Compare commits

..

No commits in common. "cleanup" and "main" have entirely different histories.

12 changed files with 124 additions and 366 deletions

View File

@ -9,7 +9,6 @@
include dpf/Makefile.base.mk include dpf/Makefile.base.mk
CFLAGS += -ggdb
all: plugins gen all: plugins gen
plugins: plugins:

View File

@ -12,10 +12,9 @@ NAME = peacock
FILES_DSP = \ FILES_DSP = \
peacock.cpp \ peacock.cpp \
controls.cpp \ controls.cpp \
assigner.cpp \ ic1.cpp \
oscillator.cpp \ oscillator.cpp \
filter.cpp \ ic29.cpp
voiceboard.cpp
include ../dpf/Makefile.plugins.mk include ../dpf/Makefile.plugins.mk

View File

@ -16,7 +16,6 @@
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "assigner.hpp"
#include "peacock.hpp" #include "peacock.hpp"
void Peacock::initParameter(uint32_t index, Parameter& parameter) { void Peacock::initParameter(uint32_t index, Parameter& parameter) {
@ -291,127 +290,24 @@ void Peacock::initParameter(uint32_t index, Parameter& parameter) {
parameter.midiCC = 26; parameter.midiCC = 26;
break; break;
case p_vcoBend: case p_modWheel:
parameter.hints = kParameterIsAutomatable | kParameterIsInteger; parameter.hints = kParameterIsAutomatable | kParameterIsHidden;
parameter.name = "VCO Bend"; parameter.name = "Mod wheel";
parameter.symbol = "pfau_vcobend"; parameter.symbol = "pfau_modwheel";
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f; parameter.ranges.max = 127.0f;
parameter.ranges.def = 43.0f; parameter.ranges.def = 0.0f;
parameter.midiCC = 1;
break; break;
case p_vcfBend: case p_holdPedal:
parameter.hints = kParameterIsAutomatable | kParameterIsInteger; parameter.hints = kParameterIsAutomatable | kParameterIsHidden;
parameter.name = "VCF Bend"; parameter.name = "Hold Pedal";
parameter.symbol = "pfau_vcfbend"; parameter.symbol = "pfau_holdpedal";
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f; parameter.ranges.max = 127.0f;
parameter.ranges.def = 43.0f; parameter.ranges.def = 0.0f;
parameter.midiCC = 64;
break; break;
} }
// chorus, porta, bend range, key mode still to do // chorus, porta, bend range, key mode still to do
} }
void Peacock::setParameterValue(uint32_t index, float value) {
// should be trapped by host, but let's be safe
if (value < 0.0f) value = 0.0f;
if (value > 127.0f) value = 127.0f;
#if 0
switch (index) {
case p_lfoRate:
ic29.patchRam.lfoRate = value;
break;
case p_lfoDelay:
ic29.patchRam.lfoDelay = value;
break;
case p_vcoLfoMod:
ic29.patchRam.vcoLfoMod = value;
break;
case p_pwmLfoMod:
ic29.patchRam.pwmLfoMod = value / 1.27;
break;
case p_subLevel:
ic29.patchRam.subLevel = value;
break;
case p_noiseLevel:
ic29.patchRam.noiseLevel = value;
break;
case p_vcfCutoff:
ic29.patchRam.vcfCutoff = value;
break;
case p_vcfReso:
ic29.patchRam.vcfReso = value;
break;
case p_vcfEnvMod:
ic29.patchRam.vcfEnvMod = value;
break;
case p_vcfLFoMod:
ic29.patchRam.vcfLfoMod = value;
break;
case p_vcfKeyTrk:
ic29.patchRam.vcfKeyTrk = value;
break;
case p_vcaLevel:
ic29.patchRam.vcaLevel = value;
break;
case p_attack:
ic29.patchRam.attack = value;
break;
case p_decay:
ic29.patchRam.decay = value;
break;
case p_sustain:
ic29.patchRam.sustain = value;
break;
case p_release:
ic29.patchRam.release = value;
break;
// switch 1 params
case p_vcoRange: // bits 0-2 of switch 1
// doesn't look great in Carla because of odd behaviour with small integer knobs
ic29.patchRam.switch1 &= 0xf8;
ic29.patchRam.switch1 |= (1 << (uint8_t)(value - 1));
break;
case p_sqrOn: // bit 3 of switch 1
ic29.patchRam.switch1 &= 0xf7;
ic29.patchRam.switch1 |= (value >= 0.5) << 3;
break;
case p_sawOn: // bit 4 of switch 1
ic29.patchRam.switch1 &= 0xef;
ic29.patchRam.switch1 |= (value >= 0.5) << 4;
break;
// missing chorus switch
// switch 2 params
case p_pwmMode: // bit 0 of switch 2
ic29.patchRam.switch2 &= 0xfe;
ic29.patchRam.switch2 |= (value >= 0.5);
break;
case p_vcfEnvPol: // bit 1 of switch 2
ic29.patchRam.switch2 &= 0xfd;
ic29.patchRam.switch2 |= (value >= 0.5) << 1;
break;
case p_vcaEnvGate:
ic29.patchRam.switch2 &= 0xfb;
ic29.patchRam.switch2 |= (value >= 0.5) << 2;
break;
case p_hpfMode: // bits 3-4 of switch 2
// doesn't look great in Carla because of odd behaviour with small integer knobs
if (value > 3) value = 3;
ic29.patchRam.switch2 &= 0xf3;
ic29.patchRam.switch2 |= (uint8_t)value << 3;
break;
case p_vcoBend:
ic29.vcoBend = (uint8_t)value;
break;
}
#endif
}

View File

@ -1,38 +0,0 @@
/*
Peacock-8 VA polysynth
Copyright 2024 Gordon JC Pearce <gordonjcp@gjcp.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "voiceboard.hpp"
void Voice::filter(float *buffer, uint32_t pos, uint32_t frames) {
for (uint32_t i = 0; i < frames; i++) {
for (uint8_t ovs = 0; ovs < 4; ovs++) {
fb = b4;
// hard clip
if (fb > 1) fb = 1;
if (fb < -1) fb = -1;
fb = buffer[i+pos] - (fb * reso);
b1 = ((fb - b1) * cut) + b1;
b2 = ((b1 - b2) * cut) + b2;
b3 = ((b2 - b3) * cut) + b3;
b4 = ((b3 - b4) * cut) + b4;
}
buffer[i+pos] = b4;
}
}

View File

@ -16,13 +16,14 @@
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "assigner.hpp" #include "ic1.hpp"
#include "voiceboard.hpp" #include "ic29.hpp"
#include "peacock.hpp" #include "peacock.hpp"
Assigner ic1;
void Assigner::printMap() { void Assigner::printMap() {
return;
printf("note memory:\n"); printf("note memory:\n");
for (uint8_t i = 0; i < NUM_VOICES; i++) printf("%02x ", voicemap[i]); for (uint8_t i = 0; i < NUM_VOICES; i++) printf("%02x ", voicemap[i]);
printf("\n"); printf("\n");
@ -34,7 +35,7 @@ Assigner::Assigner() {
// set up the assigner // set up the assigner
for (uint8_t i = 0; i < NUM_VOICES; i++) { for (uint8_t i = 0; i < NUM_VOICES; i++) {
voicemap[i] = 0x80 + i; voicemap[i] = 0x80 + i;
keymap[i] = 0xff; keymap[i] = 0x3c | 0x80;
} }
} }
@ -48,18 +49,6 @@ void Assigner::handleMidi(const MidiEvent *ev) {
case 0x90: case 0x90:
noteOn(ev->data[1]); noteOn(ev->data[1]);
break; break;
case 0xb0:
switch (ev->data[1]) {
case 0x01:
_ic29->modWheel = ev->data[2];
break;
default:
break;
}
break; // nothing to do here except in special cases where we don't expect the host to pass on controls
case 0xe0: // pitch bend;
_ic29->pitchBend = ev->data[2] << 7 | ev->data[1];
break;
default: default:
d_debug("unhandled MIDI event, status %02x value %02x\n", ev->data[0], ev->data[1]); d_debug("unhandled MIDI event, status %02x value %02x\n", ev->data[0], ev->data[1]);
break; break;
@ -74,8 +63,7 @@ void Assigner::noteOff(uint8_t note) {
memmove(keymap + i, keymap + i + 1, NUM_VOICES - i - 1); memmove(keymap + i, keymap + i + 1, NUM_VOICES - i - 1);
voicemap[NUM_VOICES - 1] = voice; voicemap[NUM_VOICES - 1] = voice;
keymap[NUM_VOICES - 1] = note | 0x80; keymap[NUM_VOICES - 1] = note | 0x80;
_ic29->voiceOff(voice); ic29.voiceOff(voice);
printMap();
} }
} }
} }
@ -91,8 +79,7 @@ void Assigner::noteOn(uint8_t note) {
memmove(keymap + 1, keymap, i); memmove(keymap + 1, keymap, i);
keymap[0] = note; // show note as on keymap[0] = note; // show note as on
voicemap[0] = voice; voicemap[0] = voice;
_ic29->voiceOn(voice, note); ic29.voiceOn(voice, note);
printMap();
return; return;
} }
} }
@ -106,8 +93,7 @@ void Assigner::noteOn(uint8_t note) {
memmove(keymap + 1, keymap, i); memmove(keymap + 1, keymap, i);
keymap[0] = note; // show note as on keymap[0] = note; // show note as on
voicemap[0] = voice; voicemap[0] = voice;
_ic29->voiceOn(voice, note); ic29.voiceOn(voice, note);
printMap();
return; return;
} }
} }

View File

@ -18,27 +18,20 @@
#pragma once #pragma once
#include "DistrhoPlugin.hpp" #include "peacock.hpp"
#define NUM_VOICES 8
#include "voiceboard.hpp"
class Assigner { class Assigner {
public: public:
Assigner(); Assigner();
void setVoiceBoard(Synth& s) {
_ic29 = &s;
}
void handleMidi(const MidiEvent *ev); void handleMidi(const MidiEvent *ev);
private: private:
void noteOn(uint8_t note); void noteOn(uint8_t note);
void noteOff(uint8_t note); void noteOff(uint8_t note);
void printMap(); void printMap();
Synth* _ic29;
uint8_t keymap[NUM_VOICES]; uint8_t keymap[NUM_VOICES];
uint8_t voicemap[NUM_VOICES]; uint8_t voicemap[NUM_VOICES];
}; };
extern Assigner ic1;

View File

@ -16,16 +16,20 @@
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "voiceboard.hpp" #include "ic29.hpp"
#include <math.h>
#include "DistrhoPlugin.hpp"
#include "ic29tables.hpp" #include "ic29tables.hpp"
#define DEBUG Synth ic29;
Synth::Synth() { Synth::Synth() {
d_debug("initialising synth\n");
envAtk = 0x00;
envDcy = 0x1f;
envStn = 0x00;
envRls = 0x1f;
portaCoeff = 0x0;
lfo.speed = 0x06;
} }
void Synth::buildTables(double sampleRate) { void Synth::buildTables(double sampleRate) {
@ -35,64 +39,42 @@ void Synth::buildTables(double sampleRate) {
// on the real synth the tuning knob is tweaked a little off to pull it in // on the real synth the tuning knob is tweaked a little off to pull it in
pitchTable[i] = 260.15f * powf(2, (i - 36) / 12.0f) / sampleRate; pitchTable[i] = 260.15f * powf(2, (i - 36) / 12.0f) / sampleRate;
} }
// precompute a table of values to map the filter value to a filter coefficient
// the ROM preset for adjusting the VCF scale and centre presets cutoff to $31
// and key scale to $7f, which corresponds to C4 = 248Hz and C6 = 992Hz, B3 and B5
for (uint16_t i = 0; i < 256; i++) {
}
} }
void Synth::run() { void Synth::run() {
// handle a "loop" worth of envelopes, pitch calculations, etc // handle a "loop" worth of envelopes, pitch calculations, etc
// callled once every 4.3ms block of samples // callled once every 4.3ms block of samples
lfo.run(); ic29.lfo.run();
masterPitch = 0x1818; masterPitch = 0x1818;
uint16_t vcoLfoDepth = lfoDepthTable[patchRam.vcoLfoMod] + modWheel;
vcoLfoDepth = (vcoLfoDepth < 0xff) ? vcoLfoDepth : 0xff;
masterPitch += (lfo.lfoOut * vcoLfoDepth) >> 11;
if (pitchBend < 0x0100) pitchBend = 0x0100;
masterPitch += (((pitchBend >> 8) - 0x20) * vcoBend) / 1.281;
// need to calculate VCF "base" setting // need to calculate VCF "base" setting
// need to calculate PWM
// various on/off switches // various on/off switches
// PWM is bit 0 sw2, 0 = fixed 1 = lfo // PWM is bit 0 sw2, 0 = fixed 1 = lfo
// 0 sets EA to 0x3fff, 1 adds // 0 sets EA to 0x3fff, 1 adds
uint16_t pwmVal = 0x2000 - lfo.lfoOut; uint16_t pwmVal = 0x2000 - ic29.lfo.lfoOut;
if (patchRam.switch2 & 0x01) pwmVal = 0x3fff; if (0) pwmVal = 0x3fff;
pwm = 0.5 - pwmVal / 32768.0f * (patchRam.pwmLfoMod / 106.0f);
// generate the voices, then ic29.pwm = pwmVal / 40960.0f * (1); // 0.5 is knob val
for (uint32_t i = 0; i < bufferSize; i++) {
tr21 = (tr21 * 519) + 3;
// noise[i] = (1 - (tr21 & 0x00ffffff) / 8388608.0f) * (patchRam.noiseLevel * 0.0063);
}
// printf("updating\n");
for (uint8_t i = 0; i < NUM_VOICES; i++) { for (uint8_t i = 0; i < NUM_VOICES; i++) {
voices[i].update(); ic29.voices[i].update();
} }
} }
void Synth::voiceOn(uint8_t voice, uint8_t note) { void Synth::voiceOn(uint8_t voice, uint8_t note) {
// enable synth voice, start it all running // enable synth voice, start it all running
voice &= 0x7f; voice &= 0x7f;
ic29.voices[voice].on(note);
d_debug("voiceOn %02x %02x", voice, note);
voices[voice].on(note);
} }
void Synth::voiceOff(uint8_t voice) { void Synth::voiceOff(uint8_t voice) {
// enable synth voice, start it all running // enable synth voice, start it all running
voice &= 0x7f; voice &= 0x7f;
voices[voice].off(); ic29.voices[voice].off();
} }
LFO::LFO() { LFO::LFO() {
@ -110,7 +92,7 @@ void LFO::run() {
// slightly different from the real synth code which does not use signed // slightly different from the real synth code which does not use signed
// variables, since the CPU doesn't support them // variables, since the CPU doesn't support them
lfoOut += phase ? lfoRateTable[rate] : -lfoRateTable[rate]; lfoOut += phase ? lfoRateTable[speed] : -lfoRateTable[speed];
if (lfoOut > 0x1fff) { if (lfoOut > 0x1fff) {
lfoOut = 0x1fff; lfoOut = 0x1fff;
phase = 0; phase = 0;
@ -123,35 +105,30 @@ void LFO::run() {
// printf("lfoOut=%04x\n", lfoOut); // printf("lfoOut=%04x\n", lfoOut);
} }
uint16_t Envelope::atk = 0x10;
uint16_t Envelope::dcy = 0x3f;
uint16_t Envelope::stn = 0x3f;
uint16_t Envelope::rls = 0x1f;
Envelope::Envelope() { Envelope::Envelope() {
level = 0; level = 0;
phase = ENV_RLS; phase = ENV_IDLE;
} }
void Envelope::run() { void Envelope::run() {
uint16_t temp = stn << 7; uint16_t tempStn = ic29.envStn << 7;
switch (phase) { switch (phase) {
case ENV_ATK: case ENV_ATK:
level += atkTable[atk]; level += atkTable[ic29.envAtk];
if (level > 0x3fff) { if (level > 0x3fff) {
level = 0x3fff; level = 0x3fff;
phase = ENV_DCY; phase = ENV_DCY;
} }
break; break;
case ENV_DCY: case ENV_DCY:
if (level > temp) { if (level > tempStn) {
level = (((level - temp) * dcyTable[dcy]) >> 16) + temp; level = (((level - tempStn) * dcyTable[ic29.envDcy]) >> 16) + tempStn;
} else { } else {
level = temp; level = tempStn;
} }
break; break;
case ENV_RLS: case ENV_RLS:
level = (level * dcyTable[rls]) >> 16; level = (level * dcyTable[ic29.envRls]) >> 16;
break; break;
case ENV_IDLE: case ENV_IDLE:
default: default:
@ -160,10 +137,10 @@ void Envelope::run() {
} }
Voice::Voice() { Voice::Voice() {
subosc = .1;
} }
void Voice::calcPitch() { void Voice::calcPitch() {
uint16_t ea;
uint16_t target = note << 8; uint16_t target = note << 8;
// Portamento is a linear change of pitch - it'll take twice as long // Portamento is a linear change of pitch - it'll take twice as long
@ -188,17 +165,17 @@ void Voice::calcPitch() {
pitch = target; pitch = target;
} }
ea = pitch + ic29.masterPitch; pitch += ic29.masterPitch;
if (ea < 0x3000) ea = 0x3000; // lowest note if (pitch < 0x3000) pitch = 0x3000; // lowest note
if (ea > 0x9700) ea = 0x6700; // highest note if (pitch > 0x9700) pitch = 0x6700; // highest note
ea -= 0x3000; pitch -= 0x3000;
// interpolate between the two table values // interpolate between the two table values
double o1 = ic29.pitchTable[ea >> 8]; double o1 = ic29.pitchTable[pitch >> 8];
double o2 = ic29.pitchTable[(ea >> 8) + 1]; double o2 = ic29.pitchTable[(pitch >> 8) + 1];
double frac = (ea & 0xff) / 256.0f; double frac = (pitch & 0xff) / 256.0f;
omega = ((o2 - o1) * frac) + o1; omega = ((o2 - o1) * frac) + o1;
} }
@ -207,32 +184,20 @@ void Voice::update() {
// calculate the once-per-block values // calculate the once-per-block values
env.run(); env.run();
calcPitch(); calcPitch();
// printf("env=%04x\n", env.level); pw = 0.5 - ic29.pwm;
/*
pw = (patchRam.switch1 & 0x08) ? ic29.pwm : 0.0f;
saw = (ic29.patchRam.switch1 & 0x10) ? 1.0f : 0.0f;
sub = ic29.patchRam.subLevel / 128.0f;
ic29.lfo.rate = ic29.patchRam.lfoRate;
*/
// do filter values // do filter values
} }
void Voice::on(uint8_t key) { void Voice::on(uint8_t key) {
d_debug("Voice on key %02x\n", key);
voiceState = V_ON; voiceState = V_ON;
if (note != key) {
phase = 0;
}
note = key; note = key;
if (env.inRelease()) {
env.on(); // FIXME move to synth update code env.on(); // FIXME move to synth update code
}
} }
void Voice::off() { void Voice::off() {
d_debug("Voice off\n");
// sustain - I need to rethink this bit FIXME // sustain - I need to rethink this bit FIXME
voiceState = V_OFF; voiceState = V_OFF;
if (!ic29.sustained) {
env.off(); env.off();
}
} }

View File

@ -18,15 +18,14 @@
#pragma once #pragma once
#include <cstdint> #include "peacock.hpp"
#define NUM_VOICES 8
class LFO { class LFO {
public: public:
LFO(); LFO();
void run(); void run();
int16_t lfoOut; int16_t lfoOut;
uint8_t rate; uint8_t speed;
private: private:
uint8_t uint8_t
@ -42,7 +41,6 @@ class LFO {
class Envelope { class Envelope {
public: public:
Envelope(); Envelope();
void run(); void run();
void on() { void on() {
phase = ENV_ATK; phase = ENV_ATK;
@ -56,49 +54,41 @@ class Envelope {
return phase == ENV_RLS; return phase == ENV_RLS;
} }
static uint16_t atk, dcy, stn, rls;
uint16_t level; uint16_t level;
private: // private:
static const uint16_t atkTable[128];
static const uint16_t dcyTable[128];
enum { enum {
ENV_ATK, ENV_ATK,
ENV_DCY, ENV_DCY,
ENV_RLS, ENV_RLS,
ENV_IDLE ENV_IDLE
} phase; } phase;
static const uint16_t atkTable[128];
static const uint16_t dcyTable[128];
}; };
class Voice { class Voice {
public: public:
Voice(); Voice();
uint8_t note = 0x3c; // middle C uint8_t note = 0x3c; // middle C
void run(float *buffer, uint32_t pos, uint32_t samples); void run(float *buffer, uint32_t samples);
void filter(float *buffer, uint32_t pos, uint32_t samples);
void update(); void update();
void on(uint8_t note); void on(uint8_t note);
void off(); void off();
// private: private:
Envelope env; // calculated envelope value Envelope env; // calculated envelope value
uint16_t pitch = 0x1818; // calculated pitch value with porta and master pitch etc uint16_t pitch = 0x1818; // calculated pitch value with porta and master pitch etc
float delay = 0; // delay slot for polyblep float delay; // delay slot for polyblep
bool pulseStage = 0; bool pulseStage;
float pw = 0, lastpw = 0, pwrc = 0; float pw, lastpw, pwrc;
float subosc = -1; float subosc = -1;
float sub = 0, saw = 0;
float phase = 0, omega = 0; float phase = 0, omega = 0;
enum { V_DONE, enum { V_DONE,
V_OFF, V_OFF,
V_ON } voiceState; V_ON } voiceState;
void calcPitch(); void calcPitch();
float fb = 0, b1 = 0, b2 = 0, b3 = 0, b4 = 0, cut = 0.1, reso = 4;
}; };
class Synth { class Synth {
@ -111,53 +101,27 @@ class Synth {
void voiceOn(uint8_t voice, uint8_t note); void voiceOn(uint8_t voice, uint8_t note);
void voiceOff(uint8_t voice); void voiceOff(uint8_t voice);
void sustainSwitch(uint8_t val); void sustainSwitch(uint8_t val);
void pitchBend(int16_t bend);
void modWheel(uint8_t wheel);
void buildTables(double sampleRate); void buildTables(double sampleRate);
double sampleRate; double sampleRate;
uint32_t bufferSize;
uint16_t masterPitch; // sum of bend and LFO, plus any other pitch-setting value uint16_t masterPitch; // sum of bend and LFO, plus any other pitch-setting value
// protected: // protected:
uint16_t envAtk, envDcy, envStn, envRls; uint16_t envAtk, envDcy, envStn, envRls;
int8_t portaCoeff; int8_t portaCoeff;
bool sustained; bool sustained;
double pitchTable[104]; double pitchTable[104];
double filterTable[256];
float *noise;
// private: // private:
uint8_t vcoBend = 42; int16_t lfoPitch;
uint8_t vcfBend = 42; int16_t bendPitch;
Voice voices[NUM_VOICES]; Voice voices[NUM_VOICES];
LFO lfo; LFO lfo;
uint16_t pitchBend = 0x2000;
uint8_t modWheel = 0x00;
float pwm; float pwm;
uint32_t tr21;
struct {
uint8_t lfoRate = 0x3f;
uint8_t lfoDelay = 0x00;
uint8_t vcoLfoMod = 0x00;
uint8_t pwmLfoMod = 0x27;
uint8_t noiseLevel = 0x00;
uint8_t vcfCutoff = 0x4d;
uint8_t vcfReso = 0x14;
uint8_t vcfEnvMod = 0x04;
uint8_t vcfLfoMod = 0x00;
uint8_t vcfKeyTrk = 0x6f;
uint8_t vcaLevel = 0x22;
uint8_t attack = 0x0d;
uint8_t decay = 0x57;
uint8_t sustain = 0x58;
uint8_t release = 0x23;
uint8_t subLevel = 0x0e;
uint8_t switch1 = 0x1a;
uint8_t switch2 = 0x10;
} patchRam;
const static uint16_t lfoDelayTable[8];
const static uint8_t lfoDepthTable[128];
const static uint8_t portaTable[128];
}; };
// global
extern Synth ic29;

View File

@ -18,6 +18,8 @@
#pragma once #pragma once
#include "ic29.hpp"
const uint16_t Envelope::atkTable[128] = { const uint16_t Envelope::atkTable[128] = {
0x4000, 0x2000, 0x1000, 0x0aaa, 0x0800, 0x0666, 0x0555, 0x0492, 0x0400, 0x4000, 0x2000, 0x1000, 0x0aaa, 0x0800, 0x0666, 0x0555, 0x0492, 0x0400,
0x038e, 0x0333, 0x02e9, 0x02ab, 0x0276, 0x0249, 0x0222, 0x0200, 0x01e2, 0x038e, 0x0333, 0x02e9, 0x02ab, 0x0276, 0x0249, 0x0222, 0x0200, 0x01e2,
@ -52,7 +54,7 @@ const uint16_t Envelope::dcyTable[128] = {
0xffd8, 0xffdc, 0xffe0, 0xffe4, 0xffe8, 0xffec, 0xfff0, 0xfff1, 0xfff2, 0xffd8, 0xffdc, 0xffe0, 0xffe4, 0xffe8, 0xffec, 0xfff0, 0xfff1, 0xfff2,
0xfff3, 0xfff4}; 0xfff3, 0xfff4};
const uint8_t Synth::lfoDepthTable[128] = { extern const uint8_t lfoDepthTable[128] = {
0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21,
@ -65,27 +67,6 @@ const uint8_t Synth::lfoDepthTable[128] = {
0xb0, 0xb4, 0xb8, 0xbc, 0xc0, 0xc4, 0xc8, 0xcc, 0xd0, 0xd4, 0xd8, 0xdc, 0xb0, 0xb4, 0xb8, 0xbc, 0xc0, 0xc4, 0xc8, 0xcc, 0xd0, 0xd4, 0xd8, 0xdc,
0xe0, 0xe4, 0xe8, 0xec, 0xf0, 0xf8, 0xff, 0xff}; 0xe0, 0xe4, 0xe8, 0xec, 0xf0, 0xf8, 0xff, 0xff};
const uint16_t Synth::lfoDelayTable[8] = {
0xffff, 0x0419, 0x020c, 0x015e, 0x0100, 0x0100, 0x0100, 0x0100};
const uint8_t Synth::portaTable[128] = {
0x00, 0xff, 0xf7, 0xef, 0xe7, 0xdf, 0xd7, 0xcf,
0xc7, 0xbf, 0xb7, 0xaf, 0xa7, 0x9f, 0x97, 0x8f,
0x87, 0x7f, 0x77, 0x6f, 0x67, 0x5f, 0x57, 0x4f,
0x47, 0x3f, 0x3d, 0x3b, 0x39, 0x37, 0x35, 0x33,
0x31, 0x2f, 0x2d, 0x2b, 0x29, 0x27, 0x25, 0x23,
0x21, 0x1f, 0x1d, 0x1b, 0x19, 0x17, 0x15, 0x13,
0x12, 0x11, 0x10, 0x10, 0x10, 0x10, 0x0f, 0x0f,
0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d,
0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a,
0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x08,
0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07,
0x07, 0x06, 0x06, 0x06, 0x06, 0x06, 0x05, 0x05,
0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04,
0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02,
0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
const uint16_t LFO::lfoRateTable[128] = { const uint16_t LFO::lfoRateTable[128] = {
0x0005, 0x000f, 0x0019, 0x0028, 0x0037, 0x0046, 0x0050, 0x005a, 0x0064, 0x0005, 0x000f, 0x0019, 0x0028, 0x0037, 0x0046, 0x0050, 0x005a, 0x0064,
0x006e, 0x0078, 0x0082, 0x008c, 0x0096, 0x00a0, 0x00aa, 0x00b4, 0x00be, 0x006e, 0x0078, 0x0082, 0x008c, 0x0096, 0x00a0, 0x00aa, 0x00b4, 0x00be,
@ -102,3 +83,24 @@ const uint16_t LFO::lfoRateTable[128] = {
0x07a8, 0x07f8, 0x085c, 0x08c0, 0x0924, 0x0988, 0x09ec, 0x0a50, 0x0ab4, 0x07a8, 0x07f8, 0x085c, 0x08c0, 0x0924, 0x0988, 0x09ec, 0x0a50, 0x0ab4,
0x0b18, 0x0b7c, 0x0be0, 0x0c58, 0x0cd0, 0x0d48, 0x0dde, 0x0e74, 0x0f0a, 0x0b18, 0x0b7c, 0x0be0, 0x0c58, 0x0cd0, 0x0d48, 0x0dde, 0x0e74, 0x0f0a,
0x0fa0, 0x1000}; 0x0fa0, 0x1000};
extern const uint16_t lfoDelayTable[8] = {
0xffff, 0x0419, 0x020c, 0x015e, 0x0100, 0x0100, 0x0100, 0x0100};
extern const uint8_t portaTable[128] = {
0x00, 0xff, 0xf7, 0xef, 0xe7, 0xdf, 0xd7, 0xcf,
0xc7, 0xbf, 0xb7, 0xaf, 0xa7, 0x9f, 0x97, 0x8f,
0x87, 0x7f, 0x77, 0x6f, 0x67, 0x5f, 0x57, 0x4f,
0x47, 0x3f, 0x3d, 0x3b, 0x39, 0x37, 0x35, 0x33,
0x31, 0x2f, 0x2d, 0x2b, 0x29, 0x27, 0x25, 0x23,
0x21, 0x1f, 0x1d, 0x1b, 0x19, 0x17, 0x15, 0x13,
0x12, 0x11, 0x10, 0x10, 0x10, 0x10, 0x0f, 0x0f,
0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d,
0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a,
0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x08,
0x08, 0x08, 0x08, 0x08, 0x07, 0x07, 0x07, 0x07,
0x07, 0x06, 0x06, 0x06, 0x06, 0x06, 0x05, 0x05,
0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04,
0x03, 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02,
0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};

View File

@ -16,7 +16,7 @@
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "voiceboard.hpp" #include "ic29.hpp"
static inline float poly3blep0(float t) { static inline float poly3blep0(float t) {
float t2 = t * t; float t2 = t * t;
@ -27,12 +27,13 @@ static inline float poly3blep1(float t) {
return -poly3blep0(1 - t); return -poly3blep0(1 - t);
} }
void Voice::run(float *buffer, uint32_t pos, uint32_t samples) { void Voice::run(float *buffer, uint32_t samples) {
// generate a full block of samples for the oscillator // generate a full block of samples for the oscillator
float y, t; float y, out, t;
float saw = 0;
float gain = env.level / 16384.0;
if (subosc > 0) subosc = sub; else subosc = -sub;
// this uses an adaptation of Mystran's Polyblep oscillator // this uses an adaptation of Mystran's Polyblep oscillator
@ -64,14 +65,14 @@ void Voice::run(float *buffer, uint32_t pos, uint32_t samples) {
} }
delay += saw * (0.8 - (1.6 * phase)); // magic numbers observed on oscilloscope from real synth delay += saw * (0.8 - (1.6 * phase)); // magic numbers observed on oscilloscope from real synth
delay += (0.63 - (pwrc * 1.26)) + (pulseStage ? -0.63f : 0.63f); // add in the scaled pulsewidth to restore DC level delay += (0.63 - (pw * 1.26)) + (pulseStage ? -0.63f : 0.63f); // add in the scaled pulsewidth to restore DC level
// the DC correction is important because the hardware synth is AC-coupled effectively high-passing // the DC correction is important because the hardware synth is AC-coupled effectively high-passing
// the signal at about 10Hz or so, preventing any PWM rumble from leaking through! // the signal at about 10Hz or so, preventing any PWM rumble from leaking through!
delay += subosc; delay += subosc;
//delay += ic29.noise[i+pos];
out = y * 0.15;
lastpw = pwrc; lastpw = pwrc;
buffer[i + pos] = y * 0.707; buffer[i] += out * gain;
} }
} }

View File

@ -18,18 +18,18 @@
#include "peacock.hpp" #include "peacock.hpp"
#include "assigner.hpp" #include "ic1.hpp"
#include "voiceboard.hpp" #include "ic29.hpp"
START_NAMESPACE_DISTRHO START_NAMESPACE_DISTRHO
Peacock::Peacock() : Plugin(paramCount, 0, 0), sampleRate(getSampleRate()) { Peacock::Peacock() : Plugin(paramCount, 0, 0), sampleRate(getSampleRate()) {
printf("peacock constructor\n"); printf("peacock constructor\n");
ic1.setVoiceBoard(ic29);
ic29.buildTables(getSampleRate()); ic29.buildTables(getSampleRate());
ic29.bufferSize = getBufferSize();
} }
void Peacock::runMidi(const MidiEvent *ev, uint32_t count, uint32_t timeLimit) { void Peacock::runMidi(const MidiEvent *ev, uint32_t count, uint32_t timeLimit) {
// handle MIDI events, starting at lastEvent and continuing until timeLimit // handle MIDI events, starting at lastEvent and continuing until timeLimit
uint32_t i; uint32_t i;
@ -51,6 +51,7 @@ void Peacock::initAudioPort(bool input, uint32_t index, AudioPort &port) {
if (!input && index == 1) port.name = "Right Out"; if (!input && index == 1) port.name = "Right Out";
} }
void Peacock::run(const float **, float **outputs, uint32_t frames, const MidiEvent *midiEvents, uint32_t midiEventCount) { void Peacock::run(const float **, float **outputs, uint32_t frames, const MidiEvent *midiEvents, uint32_t midiEventCount) {
// calculate an entire jack period's worth of samples // calculate an entire jack period's worth of samples
// harder than it sounds because for short jack periods there may be many // harder than it sounds because for short jack periods there may be many
@ -71,7 +72,7 @@ void Peacock::run(const float **, float **outputs, uint32_t frames, const MidiEv
runMidi(midiEvents, midiEventCount, blockLeft); runMidi(midiEvents, midiEventCount, blockLeft);
// generate a buffer's worth of samples // generate a buffer's worth of samples
memset(outputs[1], 0, sizeof(float) * frames); memset(outputs[0], 0, sizeof(float) * frames);
while (framePos < frames) { while (framePos < frames) {
if (blockLeft == 0) { if (blockLeft == 0) {
@ -85,11 +86,7 @@ void Peacock::run(const float **, float **outputs, uint32_t frames, const MidiEv
// run every synth voice into the buffer here FIXME // run every synth voice into the buffer here FIXME
for (uint8_t i = 0; i < NUM_VOICES; i++) { for (uint8_t i = 0; i < NUM_VOICES; i++) {
//ic29->voices[i].run(outputs[0], framePos, sizeThisTime); ic29.voices[i].run(outputs[0] + framePos, sizeThisTime);
//ic29->voices[i].filter(outputs[0], framePos, sizeThisTime);
for (uint8_t j=0; j<sizeThisTime; j++) {
// outputs[1][framePos+j] += outputs[0][framePos+j] * ic29->voices[i].env.level/16384.0;
}
} }
framePos += sizeThisTime; // move along the frame framePos += sizeThisTime; // move along the frame
@ -97,7 +94,7 @@ void Peacock::run(const float **, float **outputs, uint32_t frames, const MidiEv
blockLeft -= sizeThisTime; blockLeft -= sizeThisTime;
} }
// output processing goes here // output processing goes here
memcpy(outputs[0], outputs[1], sizeof(float) * frames); memcpy(outputs[1], outputs[0], sizeof(float) * frames);
} }
Plugin *createPlugin() { return new Peacock(); } Plugin *createPlugin() { return new Peacock(); }

View File

@ -23,9 +23,6 @@
#include "DistrhoPlugin.hpp" #include "DistrhoPlugin.hpp"
#include "assigner.hpp"
#include "voiceboard.hpp"
START_NAMESPACE_DISTRHO START_NAMESPACE_DISTRHO
class Peacock : public Plugin { class Peacock : public Plugin {
@ -55,8 +52,8 @@ class Peacock : public Plugin {
p_sustain, p_sustain,
p_release, p_release,
p_chorus, // 0, 1, 2 p_chorus, // 0, 1, 2
p_vcoBend, p_modWheel,
p_vcfBend, p_holdPedal,
paramCount paramCount
}; };
@ -77,7 +74,7 @@ class Peacock : public Plugin {
void initAudioPort(bool input, uint32_t index, AudioPort &port) override; void initAudioPort(bool input, uint32_t index, AudioPort &port) override;
void initParameter(uint32_t index, Parameter &parameter) override; void initParameter(uint32_t index, Parameter &parameter) override;
void setParameterValue(uint32_t index, float value) override; // void setParameterValue(uint32_t index, float value) override;
// float getParameterValue(uint32_t index) const override; // float getParameterValue(uint32_t index) const override;
// Processing // Processing
@ -90,9 +87,6 @@ class Peacock : public Plugin {
uint32_t blockLeft = 0; uint32_t blockLeft = 0;
uint32_t lastEvent = 0; uint32_t lastEvent = 0;
Assigner ic1;
Synth ic29;
void runMidi(const MidiEvent *ev, uint32_t count, uint32_t timeLimit); void runMidi(const MidiEvent *ev, uint32_t count, uint32_t timeLimit);
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Peacock); DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Peacock);