a few more controls, automated param mapping is a success

This commit is contained in:
Gordon JC Pearce 2024-09-08 20:48:10 +01:00
parent 9dc7f5b0ea
commit 875cf03b44
4 changed files with 235 additions and 191 deletions

View File

@ -20,116 +20,167 @@
START_NAMESPACE_DISTRHO START_NAMESPACE_DISTRHO
Chassis::Chassis() : Plugin(kParameterCount, 0, 0), sampleRate(getSampleRate()) { // one parameter, no programs, no states Chassis::Chassis() : Plugin(parameterCount, 0, 0), sampleRate(getSampleRate()) { // one parameter, no programs, no states
} }
// Initialisation functions // Initialisation functions
void Chassis::initParameter(uint32_t index, Parameter &parameter) { void Chassis::initParameter(uint32_t index, Parameter &parameter) {
switch (index) { switch (index) {
case k_saw: case paramSaw:
parameter.hints = kParameterIsAutomatable | kParameterIsBoolean; parameter.hints = kParameterIsAutomatable | kParameterIsBoolean;
parameter.name = "Saw"; parameter.name = "Saw";
parameter.symbol = "saw"; parameter.symbol = "ch_saw";
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f; parameter.ranges.max = 127.0f;
parameter.ranges.def = 127.0f; parameter.ranges.def = 127.0f;
parameter.midiCC = 17; parameter.midiCC = 17;
break; break;
case k_sqr: case paramSqr:
parameter.hints = kParameterIsAutomatable | kParameterIsBoolean; parameter.hints = kParameterIsAutomatable | kParameterIsBoolean;
parameter.name = "Square"; parameter.name = "Square";
parameter.symbol = "sqr"; parameter.symbol = "ch_sqr";
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f; parameter.ranges.max = 127.0f;
parameter.ranges.def = 0.0f; parameter.ranges.def = 127.0f;
parameter.midiCC = 16; parameter.midiCC = 16;
break; break;
case k_sub: case paramSub:
parameter.hints = kParameterIsAutomatable; parameter.hints = kParameterIsAutomatable;
parameter.name = "Sub Osc"; parameter.name = "Sub Osc";
parameter.symbol = "sub"; parameter.symbol = "ch_sub";
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f; parameter.ranges.max = 127.0f;
parameter.ranges.def = 0.0f; parameter.ranges.def = 0.0f;
parameter.midiCC = 18; parameter.midiCC = 18;
break; break;
case k_attack: case paramAttack:
parameter.hints = kParameterIsAutomatable; parameter.hints = kParameterIsAutomatable;
parameter.name = "Attack"; parameter.name = "Attack";
parameter.symbol = "attack"; parameter.symbol = "ch_attack";
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f; parameter.ranges.max = 127.0f;
parameter.ranges.def = 0.0f; parameter.ranges.def = 59.0f;
parameter.midiCC = 73; parameter.midiCC = 73;
break; break;
case k_decay: case paramDecay:
parameter.hints = kParameterIsAutomatable; parameter.hints = kParameterIsAutomatable;
parameter.name = "Decay"; parameter.name = "Decay";
parameter.symbol = "decay"; parameter.symbol = "ch_decay";
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f; parameter.ranges.max = 127.0f;
parameter.ranges.def = 0.0f; parameter.ranges.def = 32.0f;
parameter.midiCC = 75; parameter.midiCC = 75;
break; break;
case k_sustain: case paramSustain:
parameter.hints = kParameterIsAutomatable; parameter.hints = kParameterIsAutomatable;
parameter.name = "Sustain"; parameter.name = "Sustain";
parameter.symbol = "sustain"; parameter.symbol = "ch_sustain";
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f; parameter.ranges.max = 127.0f;
parameter.ranges.def = 0.0f; parameter.ranges.def = 86.0f;
parameter.midiCC = 27; parameter.midiCC = 27;
break; break;
case k_release: case paramRelease:
parameter.hints = kParameterIsAutomatable; parameter.hints = kParameterIsAutomatable;
parameter.name = "Release"; parameter.name = "Release";
parameter.symbol = "release"; parameter.symbol = "ch_release";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f;
parameter.ranges.def = 40.0f;
parameter.midiCC = 72;
break;
case paramEnvGate:
parameter.hints = kParameterIsAutomatable | kParameterIsBoolean;
parameter.name = "Env / Gate";
parameter.symbol = "ch_envgate";
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f; parameter.ranges.max = 127.0f;
parameter.ranges.def = 0.0f; parameter.ranges.def = 0.0f;
parameter.midiCC = 72; parameter.midiCC = 25;
break;
case paramVCALevel:
parameter.hints = kParameterIsAutomatable;
parameter.name = "VCA Level";
parameter.symbol = "ch_vcalevel";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f;
parameter.ranges.def = 40.0f;
parameter.midiCC = 26;
break; break;
} }
} }
void Chassis::setParameterValue(uint32_t index, float value) { void Chassis::setParameterValue(uint32_t index, float value) {
// printf("setparametervalue %d\n", index);
switch (index) { switch (index) {
case k_saw: case paramSaw:
s.patch.saw = value / 127.0f; s.patchRam.switch1 &= 0xef;
s.patchRam.switch1 |= (value > 63) << 4;
break; break;
case k_sqr: case paramSqr:
s.patch.sqr = value / 127.0f; s.patchRam.switch1 &= 0xf7;
s.patchRam.switch1 |= (value > 63) << 3;
break; break;
case k_sub: case paramSub:
s.patch.sub = value / 127.0f; s.patchRam.sub = value;
break; break;
case k_attack: case paramAttack:
s.patch.attack = attack_table[(int)value]; s.patchRam.env_a = value;
break; break;
case k_decay: case paramDecay:
s.patch.decay = decay_table[(int)value]; s.patchRam.env_d = value;
break; break;
case k_sustain: case paramSustain:
s.patch.sustain = (int)value << 7; s.patchRam.env_s = value;
break; break;
case k_release: case paramRelease:
s.patch.release = decay_table[(int)value]; s.patchRam.env_r = value;
break;
case paramEnvGate:
s.patchRam.switch2 &= 0xfb;
s.patchRam.switch2 |= (value > 63) << 2;
break;
case paramVCALevel:
s.patchRam.vca = value;
break; break;
} }
} }
/*
float Chassis::getParameterValue(uint32_t index) const { float Chassis::getParameterValue(uint32_t index) const {
// printf("getparametervalue %d\n", index);
switch (index) {
case paramSaw:
return (s.patchRam.switch1 & 0x10) ? 127.0f : 0.0f;
break;
case paramSqr:
return (s.patchRam.switch1 & 0x08) ? 127.0f : 0.0f;
break;
case paramAttack:
return s.patchRam.env_a;
break;
case paramDecay:
return s.patchRam.env_d;
break;
case paramSustain:
return s.patchRam.env_s;
break;
case paramRelease:
return s.patchRam.env_r;
break;
}
return 0; return 0;
} }
*/
void Chassis::initAudioPort(bool input, uint32_t index, AudioPort &port) { void Chassis::initAudioPort(bool input, uint32_t index, AudioPort &port) {
port.groupId = kPortGroupStereo; port.groupId = kPortGroupStereo;
Plugin::initAudioPort(input, index, port); Plugin::initAudioPort(input, index, port);
@ -137,16 +188,7 @@ void Chassis::initAudioPort(bool input, uint32_t index, AudioPort &port) {
if (!input && index == 0) port.name = "Left Out"; if (!input && index == 0) port.name = "Left Out";
if (!input && index == 1) port.name = "Right Out"; if (!input && index == 1) port.name = "Right Out";
} }
/*
void Chassis::initProgramName(uint32_t index, String &programName) {
programName = "init";
}
void Chassis::loadProgram(uint32_t index) {
prog_offset = (index & 0x3f) << 7;
program = index + 1;
}
*/
// Processing functions // Processing functions
void Chassis::activate() { void Chassis::activate() {
@ -162,7 +204,6 @@ void Chassis::deactivate() {
void Chassis::noteOn(uint8_t note) { void Chassis::noteOn(uint8_t note) {
uint32_t i; uint32_t i;
//printf("noteon %d %d\n", note, vPtr);
for (i = 0; i < NUM_VOICES; i++) { for (i = 0; i < NUM_VOICES; i++) {
vPtr++; vPtr++;
if (vPtr == NUM_VOICES) vPtr = 0; if (vPtr == NUM_VOICES) vPtr = 0;
@ -196,7 +237,6 @@ void Chassis::noteOff(uint8_t note) {
void Chassis::doMidi(const MidiEvent *ev, uint32_t count, uint32_t timeLimit) { void Chassis::doMidi(const MidiEvent *ev, uint32_t count, uint32_t timeLimit) {
// printf("doMidi() handling events from %d to %d\n", lastEvent, timeLimit); // printf("doMidi() handling events from %d to %d\n", lastEvent, timeLimit);
uint32_t i; uint32_t i;
uint8_t val = 0;
if (count == 0) return; if (count == 0) return;
for (i = lastEvent; i < count; i++) { for (i = lastEvent; i < count; i++) {
// printf("doMidi event number %d of %d %02x %02x\n", i, count, ev[i].data[0], ev[i].data[1]); // printf("doMidi event number %d of %d %02x %02x\n", i, count, ev[i].data[0], ev[i].data[1]);
@ -208,35 +248,6 @@ void Chassis::doMidi(const MidiEvent *ev, uint32_t count, uint32_t timeLimit) {
case 0x80: case 0x80:
noteOff(ev[i].data[1]); noteOff(ev[i].data[1]);
break; break;
#if 0
case 0xb0:
//printf("cc %02x %02x\n", ev[i].data[1], ev[i].data[2]);
val = ev[i].data[2]; // get control value
switch (ev[i].data[1]) {
case 16:
s.patch.sqr = val / 127.0f;
break;
case 17:
s.patch.saw = val / 127.0f;
break;
case 18:
s.patch.sub = val / 127.0f;
break;
case 73:
s.patch.attack = attack_table[val];
break;
case 75:
s.patch.decay = decay_table[val];
break;
case 72:
s.patch.release = decay_table[val];
break;
case 27:
s.patch.sustain = val << 7;
}
break;
#endif
} }
} }
lastEvent = i; lastEvent = i;

View File

@ -32,14 +32,16 @@ START_NAMESPACE_DISTRHO
class Chassis : public Plugin { class Chassis : public Plugin {
public: public:
enum Parameters { enum Parameters {
k_saw, paramSaw,
k_sqr, paramSqr,
k_sub, paramSub,
k_attack, paramAttack,
k_decay, paramDecay,
k_sustain, paramSustain,
k_release, paramRelease,
kParameterCount paramEnvGate,
paramVCALevel,
parameterCount
}; };
Chassis(); Chassis();
@ -59,7 +61,7 @@ class Chassis : public Plugin {
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;
// void initProgramName(uint32_t index, String &programName) override; // void initProgramName(uint32_t index, String &programName) override;
// void loadProgram(uint32_t index) override; // void loadProgram(uint32_t index) override;
@ -84,40 +86,6 @@ class Chassis : public Plugin {
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Chassis); DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Chassis);
}; };
uint16_t attack_table[128] = {
0x4000, 0x2000, 0x1000, 0x0aaa, 0x0800, 0x0666, 0x0555, 0x0492, 0x0400,
0x038e, 0x0333, 0x02e9, 0x02ab, 0x0276, 0x0249, 0x0222, 0x0200, 0x01e2,
0x01c7, 0x01af, 0x0199, 0x0186, 0x0174, 0x0164, 0x0155, 0x0148, 0x013b,
0x012f, 0x0124, 0x011a, 0x0111, 0x0108, 0x0100, 0x00f8, 0x00f1, 0x00ea,
0x00e4, 0x00dd, 0x00d8, 0x00d2, 0x00cd, 0x00c8, 0x00c3, 0x00bf, 0x00ba,
0x00b6, 0x00b2, 0x00ae, 0x00ab, 0x00a7, 0x00a4, 0x00a1, 0x009e, 0x009b,
0x0098, 0x0095, 0x0092, 0x0090, 0x008d, 0x008b, 0x0089, 0x0086, 0x0084,
0x0082, 0x007f, 0x007d, 0x007a, 0x0077, 0x0074, 0x0072, 0x006f, 0x006c,
0x0069, 0x0067, 0x0064, 0x0061, 0x005e, 0x005c, 0x0059, 0x0056, 0x0053,
0x0050, 0x004e, 0x004b, 0x0048, 0x0045, 0x0042, 0x0040, 0x003f, 0x003d,
0x003c, 0x003a, 0x0039, 0x0037, 0x0036, 0x0034, 0x0033, 0x0031, 0x0030,
0x002e, 0x002d, 0x002b, 0x002a, 0x0028, 0x0027, 0x0025, 0x0024, 0x0022,
0x0021, 0x0021, 0x0020, 0x0020, 0x001f, 0x001f, 0x001e, 0x001e, 0x001d,
0x001d, 0x001c, 0x001c, 0x001b, 0x001b, 0x001a, 0x0019, 0x0018, 0x0017,
0x0016, 0x0015};
uint16_t decay_table[128] = {
0x1000, 0x3000, 0x5000, 0x7000, 0x9000, 0xa000, 0xa800, 0xb000, 0xb800,
0xc000, 0xc800, 0xd000, 0xd800, 0xe000, 0xe800, 0xf000, 0xf080, 0xf100,
0xf180, 0xf200, 0xf280, 0xf300, 0xf380, 0xf400, 0xf480, 0xf500, 0xf580,
0xf600, 0xf680, 0xf700, 0xf780, 0xf800, 0xf880, 0xf900, 0xf980, 0xfa00,
0xfa80, 0xfb00, 0xfb80, 0xfc00, 0xfc80, 0xfd00, 0xfd80, 0xfe00, 0xfe0c,
0xfe18, 0xfe24, 0xfe30, 0xfe3c, 0xfe48, 0xfe54, 0xfe60, 0xfe6c, 0xfe78,
0xfe84, 0xfe90, 0xfe9c, 0xfea8, 0xfeb4, 0xfec0, 0xfecc, 0xfed8, 0xfee4,
0xfef0, 0xfefc, 0xff08, 0xff0c, 0xff10, 0xff14, 0xff18, 0xff1c, 0xff20,
0xff24, 0xff28, 0xff2c, 0xff30, 0xff34, 0xff38, 0xff3c, 0xff40, 0xff44,
0xff48, 0xff4c, 0xff50, 0xff54, 0xff58, 0xff5c, 0xff60, 0xff64, 0xff68,
0xff6c, 0xff70, 0xff74, 0xff78, 0xff7c, 0xff80, 0xff84, 0xff88, 0xff8c,
0xff90, 0xff94, 0xff98, 0xff9c, 0xffa0, 0xffa4, 0xffa8, 0xffac, 0xffb0,
0xffb4, 0xffb8, 0xffbc, 0xffc0, 0xffc4, 0xffc8, 0xffcc, 0xffd0, 0xffd4,
0xffd8, 0xffdc, 0xffe0, 0xffe4, 0xffe8, 0xffec, 0xfff0, 0xfff1, 0xfff2,
0xfff3, 0xfff4};
END_NAMESPACE_DISTRHO END_NAMESPACE_DISTRHO
#endif // _CHASSIS_HPP #endif // _CHASSIS_HPP

View File

@ -27,28 +27,37 @@ bool Voice::isFree() {
} }
void Voice::on(uint32_t key, bool reset = 0) { void Voice::on(uint32_t key, bool reset = 0) {
// printf("======================================================================================\n"); // printf("======================================================================================\n");
ff10 = true; // what's with the crazy private variables and all the gotos with crazy labels?
// this code emulates the 78C11 code directly (probably inefficiently)
// to allow for documenting what the variables actually do
// they're really bitfields holding a bit for each voice
ff07 = true; // this current implementation doesn't reset the voice
(void)reset;
ff10 = true; // note held from keyboard
ff07 = true; // attack phase
if (note == key) goto h0144; if (note == key) goto h0144;
note = key; note = key;
if (ff11) goto h013e; if (ff11) goto h013e;
h0132: h0132:
if (ff33) goto h0149; if (ff33) goto h0149; // sustained
ff33 = false; ff33 = false;
goto h0149; goto h0149;
h013e: h013e:
ff00 = true; ff00 = true; // in a real one, voice counter needs programmed
goto h0149; goto h0149;
h0144: h0144:
if (!ff11) goto h0132; if (!ff11) goto h0132; // unsure, copied from ff10 at start of mainloop
h0149: h0149:
// printf("after 0144h, %d %x %x %x %x\n", note, ff07, ff10, ff11, ff33); // printf("after 0144h, %d %x %x %x %x\n", note, ff07, ff10, ff11, ff33);
// this is in the wrong place really but is the equivalent of programming the counter
// and VCO ramp DAC
omega = (261.63 * powf(2, (note - 60) / 12.0f)) / 48000.0f; omega = (261.63 * powf(2, (note - 60) / 12.0f)) / 48000.0f;
} }
@ -63,13 +72,10 @@ void Voice::off() {
} }
void Voice::gate(Synth &s) { void Voice::gate(Synth &s) {
uint16_t bc, ea = env; uint16_t bc, ea = env;
ff11 = ff10; ff11 = ff10;
// 0509 // 0509
if (!ff11) goto h0538; if (!ff11) goto h0538;
@ -79,23 +85,19 @@ void Voice::gate(Synth &s) {
// 0513 // 0513
if (!ff07) goto h051e; if (!ff07) goto h051e;
// 0517
h0517: h0517:
//printf("got to 0517\n");
ff07 = false; ff07 = false;
h051e: h051e:
bc = s.patchRam.env_s << 7; // half scale
//printf("got to 051e\n");
bc = s.patch.sustain; // half scale
if (ea < bc) ea = bc; if (ea < bc) ea = bc;
ea -= bc; ea -= bc;
bc = ea; bc = ea;
ea = (ea * s.patch.decay) >> 16; ea = (ea * decay_table[s.patchRam.env_d]) >> 16;
ea += s.patch.sustain; ea += s.patchRam.env_s << 7;
// printf("returning from decay phase\n"); // printf("returning from decay phase\n");
goto h0590; goto h0590;
@ -110,14 +112,14 @@ void Voice::gate(Synth &s) {
ff33 = false; ff33 = false;
ff08 = false; ff08 = false;
bc = ea; bc = ea;
ea = (ea*s.patch.release)>>16; ea = (ea * decay_table[s.patchRam.env_r]) >> 16;
// printf("returning from release phase\n"); // printf("returning from release phase\n");
goto h0590; goto h0590;
h0563: h0563:
// printf("attack phase\n"); // printf("attack phase\n");
ff08 = false; ff08 = false;
ea += s.patch.attack; ea += attack_table[s.patchRam.env_a];
if (ea & 0xc000) { if (ea & 0xc000) {
ea = 0x3fff; ea = 0x3fff;
ff33 = true; ff33 = true;
@ -127,7 +129,6 @@ void Voice::gate(Synth &s) {
h0590: h0590:
env = ea; env = ea;
// printf("%04x %d %d %d %d %d \n", ea, ff07, ff08, ff10, ff11, ff33); // printf("%04x %d %d %d %d %d \n", ea, ff07, ff08, ff10, ff11, ff33);
} }
static inline float poly3blep0(float t) { static inline float poly3blep0(float t) {
@ -140,7 +141,13 @@ static inline float poly3blep1(float t) {
} }
void Voice::run(Synth &s, float *buffer, uint32_t samples) { void Voice::run(Synth &s, float *buffer, uint32_t samples) {
float y, out, pw = 0, t; float y, out, pw = 0.5, t;
float sqr = (s.patchRam.switch1 & 0x08) ? 0.175 : 0;
float saw = (s.patchRam.switch1 & 0x10) ? 0.220 : 0;
float sub = (s.patchRam.sub / 127.0f) * 0.275;
float gain = 0.5 * powf(2, (s.patchRam.vca / 64.0f) - 1);
for (uint32_t i = 0; i < samples; i++) { for (uint32_t i = 0; i < samples; i++) {
y = delay; y = delay;
@ -148,21 +155,19 @@ void Voice::run(Synth &s, float *buffer, uint32_t samples) {
phase += omega; phase += omega;
float pulseWidth = 0.5; // pwm[i]; // if(pw > 1) pw = 1;
// if(pw < 0) pw = 0;
// if(pulseWidth > 1) pulseWidth = 1;
// if(pulseWidth < 0) pulseWidth = 0;
while (true) { while (true) {
if (pulseStage == 0) { if (pulseStage == 0) {
if (phase < pulseWidth) break; if (phase < pw) break;
#if 1 #if 1
float t = (phase - pulseWidth) / omega; t = (phase - pw) / omega;
#else #else
float t = (phase - pulseWidth) / (widthDelay - pulseWidth + freq); t = (phase - pw) / (widthDelay - pw + freq);
#endif #endif
y -= poly3blep0(t) * s.patch.sqr; y -= poly3blep0(t) * sqr;
delay -= poly3blep1(t) * s.patch.sqr; delay -= poly3blep1(t) * sqr;
pulseStage = 1; pulseStage = 1;
} }
@ -170,11 +175,11 @@ void Voice::run(Synth &s, float *buffer, uint32_t samples) {
if (phase < 1) break; if (phase < 1) break;
float t = (phase - 1) / omega; float t = (phase - 1) / omega;
y += poly3blep0(t) * (s.patch.saw + s.patch.sqr); y += poly3blep0(t) * (saw + sqr);
delay += poly3blep1(t) * (s.patch.saw + s.patch.sqr); delay += poly3blep1(t) * (saw + sqr);
y -= poly3blep0(t) * (s.patch.sub * subosc); y -= poly3blep0(t) * (sub * subosc);
delay -= poly3blep1(t) * (s.patch.sub * subosc); delay -= poly3blep1(t) * (sub * subosc);
pulseStage = 0; pulseStage = 0;
phase -= 1; phase -= 1;
@ -182,18 +187,17 @@ void Voice::run(Synth &s, float *buffer, uint32_t samples) {
} }
} }
delay += s.patch.saw * (1 - (2 * phase)); delay += saw * (1 - (2 * phase));
delay += s.patch.sqr * (pulseStage ? -1.f : 1.f); delay += sqr * (pulseStage ? -1.f : 1.f);
delay += s.patch.sub * subosc; delay += sub * subosc;
// out = (2 * y) - 1; // out = (2 * y) - 1;
out = y; out = y;
// widthDelay = pulseWidth; // widthDelay = pw;
vr58c106 += (((env / 16383.0) - vr58c106) * 0.0075); vr58c106 += (((env / 16383.0) - vr58c106) * 0.0075);
buffer[i] += (0.25 * out * vr58c106); buffer[i] += (gain * out * vr58c106);
} }
} }

View File

@ -26,7 +26,7 @@ class Synth;
class Patch { class Patch {
public: public:
float saw=1, sqr, sub; float saw, sqr, sub;
uint16_t attack, decay, sustain, release; uint16_t attack, decay, sustain, release;
}; };
@ -71,6 +71,39 @@ class Voice {
float pw_rc = 0; float pw_rc = 0;
float vr58c106 = 0; float vr58c106 = 0;
uint16_t attack_table[128] = {
0x4000, 0x2000, 0x1000, 0x0aaa, 0x0800, 0x0666, 0x0555, 0x0492, 0x0400,
0x038e, 0x0333, 0x02e9, 0x02ab, 0x0276, 0x0249, 0x0222, 0x0200, 0x01e2,
0x01c7, 0x01af, 0x0199, 0x0186, 0x0174, 0x0164, 0x0155, 0x0148, 0x013b,
0x012f, 0x0124, 0x011a, 0x0111, 0x0108, 0x0100, 0x00f8, 0x00f1, 0x00ea,
0x00e4, 0x00dd, 0x00d8, 0x00d2, 0x00cd, 0x00c8, 0x00c3, 0x00bf, 0x00ba,
0x00b6, 0x00b2, 0x00ae, 0x00ab, 0x00a7, 0x00a4, 0x00a1, 0x009e, 0x009b,
0x0098, 0x0095, 0x0092, 0x0090, 0x008d, 0x008b, 0x0089, 0x0086, 0x0084,
0x0082, 0x007f, 0x007d, 0x007a, 0x0077, 0x0074, 0x0072, 0x006f, 0x006c,
0x0069, 0x0067, 0x0064, 0x0061, 0x005e, 0x005c, 0x0059, 0x0056, 0x0053,
0x0050, 0x004e, 0x004b, 0x0048, 0x0045, 0x0042, 0x0040, 0x003f, 0x003d,
0x003c, 0x003a, 0x0039, 0x0037, 0x0036, 0x0034, 0x0033, 0x0031, 0x0030,
0x002e, 0x002d, 0x002b, 0x002a, 0x0028, 0x0027, 0x0025, 0x0024, 0x0022,
0x0021, 0x0021, 0x0020, 0x0020, 0x001f, 0x001f, 0x001e, 0x001e, 0x001d,
0x001d, 0x001c, 0x001c, 0x001b, 0x001b, 0x001a, 0x0019, 0x0018, 0x0017,
0x0016, 0x0015};
uint16_t decay_table[128] = {
0x1000, 0x3000, 0x5000, 0x7000, 0x9000, 0xa000, 0xa800, 0xb000, 0xb800,
0xc000, 0xc800, 0xd000, 0xd800, 0xe000, 0xe800, 0xf000, 0xf080, 0xf100,
0xf180, 0xf200, 0xf280, 0xf300, 0xf380, 0xf400, 0xf480, 0xf500, 0xf580,
0xf600, 0xf680, 0xf700, 0xf780, 0xf800, 0xf880, 0xf900, 0xf980, 0xfa00,
0xfa80, 0xfb00, 0xfb80, 0xfc00, 0xfc80, 0xfd00, 0xfd80, 0xfe00, 0xfe0c,
0xfe18, 0xfe24, 0xfe30, 0xfe3c, 0xfe48, 0xfe54, 0xfe60, 0xfe6c, 0xfe78,
0xfe84, 0xfe90, 0xfe9c, 0xfea8, 0xfeb4, 0xfec0, 0xfecc, 0xfed8, 0xfee4,
0xfef0, 0xfefc, 0xff08, 0xff0c, 0xff10, 0xff14, 0xff18, 0xff1c, 0xff20,
0xff24, 0xff28, 0xff2c, 0xff30, 0xff34, 0xff38, 0xff3c, 0xff40, 0xff44,
0xff48, 0xff4c, 0xff50, 0xff54, 0xff58, 0xff5c, 0xff60, 0xff64, 0xff68,
0xff6c, 0xff70, 0xff74, 0xff78, 0xff7c, 0xff80, 0xff84, 0xff88, 0xff8c,
0xff90, 0xff94, 0xff98, 0xff9c, 0xffa0, 0xffa4, 0xffa8, 0xffac, 0xffb0,
0xffb4, 0xffb8, 0xffbc, 0xffc0, 0xffc4, 0xffc8, 0xffcc, 0xffd0, 0xffd4,
0xffd8, 0xffdc, 0xffe0, 0xffe4, 0xffe8, 0xffec, 0xfff0, 0xfff1, 0xfff2,
0xfff3, 0xfff4};
}; };
class Synth { class Synth {
@ -82,5 +115,33 @@ class Synth {
uint32_t blockLeft; uint32_t blockLeft;
uint32_t framesLeft = 0; uint32_t framesLeft = 0;
// okay, not the greatest, this right here
// this struct contains the bytes that make up a Juno 106 patch in
// sysex order, exactly as they'd be transmitted or received by a
// real one.
// the horrifying implication here is that we can just shunt this
// around and memcpy() values from sysex on top of it
struct {
uint8_t lfo_rate = 57;
uint8_t lfo_delay = 45;
uint8_t lfo_dco = 0;
uint8_t lfo_pwm = 55;
uint8_t noise = 0;
uint8_t vcf_cut = 85;
uint8_t vcf_res = 0;
uint8_t vcf_env = 0;
uint8_t vcf_lfo = 0;
uint8_t vcf_kyb = 108;
uint8_t vca = 52;
uint8_t env_a = 59;
uint8_t env_d = 32;
uint8_t env_s = 86;
uint8_t env_r = 40;
uint8_t sub = 0;
uint8_t switch1 = 26;
uint8_t switch2 = 24;
} patchRam;
}; };