Compare commits

..

23 Commits

Author SHA1 Message Date
Gordon JC Pearce
1c8f451c3a removed a lot of debugging printfs 2024-09-11 23:30:06 +01:00
Gordon JC Pearce
3980a1c966 correct parameter generation 2024-09-11 23:25:41 +01:00
Gordon JC Pearce
620914b500 saneish looking parameters 2024-09-11 21:40:11 +01:00
Gordon JC Pearce
3588c3459b fix labelling 2024-09-11 20:04:34 +01:00
Gordon JC Pearce
561acbc0fb fix uninitialised variable 2024-09-11 19:57:11 +01:00
Gordon JC Pearce
788b94993a basic patch support 2024-09-10 14:57:56 +01:00
Gordon JC Pearce
210401d6ed filter control calculations seem about right 2024-09-10 00:14:25 +01:00
Gordon JC Pearce
ca65162e3f renamed the digital part 2024-09-09 20:58:14 +01:00
Gordon JC Pearce
5db88ca4ea and add the important file I forgot 2024-09-09 20:41:05 +01:00
Gordon JC Pearce
eb86b3c7d3 begin the great digital cleanup 2024-09-09 20:40:16 +01:00
Gordon JC Pearce
357cbbac9a filter of sorts 2024-09-09 16:31:43 +01:00
Gordon JC Pearce
bde0863a15 just setting pw to 0 causes a DC offset, let's not eh 2024-09-09 15:54:08 +01:00
Gordon JC Pearce
f0400b13db better tuning, corrected range switching 2024-09-09 14:13:18 +01:00
Gordon JC Pearce
f8d07cfa3d modwheel 2024-09-09 14:05:43 +01:00
Gordon JC Pearce
af49dcf7b6 pitch computation 2024-09-09 13:34:02 +01:00
Gordon JC Pearce
88ca7a5037 LFO delay code 2024-09-09 11:34:13 +01:00
Gordon JC Pearce
6bf1cc1da1 more comments in LFO code 2024-09-09 08:51:37 +01:00
Gordon JC Pearce
1d4e6ab2bd PWM LFO working 2024-09-09 00:55:14 +01:00
Gordon JC Pearce
1f6188f751 broke digital code out to separate file 2024-09-08 22:45:12 +01:00
Gordon JC Pearce
3f63138b85 all current parameters can be set 2024-09-08 22:21:03 +01:00
Gordon JC Pearce
3c9edede8d broke out parameter code to a separate source file because it's getting a tad unruly in there 2024-09-08 21:55:46 +01:00
Gordon JC Pearce
0274204809 cleaned up some names, pwm works, knobs all present 2024-09-08 21:50:42 +01:00
Gordon JC Pearce
5f1d620a46 env/gate 2024-09-08 20:57:11 +01:00
10 changed files with 1550 additions and 375 deletions

View File

@ -27,7 +27,7 @@
#define DISTRHO_PLUGIN_IS_SYNTH 1
#define DISTRHO_PLUGIN_IS_RT_SAFE 1
#define DISTRHO_PLUGIN_WANT_PROGRAMS 0
#define DISTRHO_PLUGIN_WANT_PROGRAMS 1
enum Parameters {
paramProg,

View File

@ -10,6 +10,8 @@
NAME = chassis
FILES_DSP = \
parameters.cpp \
voicecpu.cpp \
chassis.cpp \
voice.cpp

View File

@ -18,169 +18,15 @@
#include "chassis.hpp"
#include "patches.hpp"
START_NAMESPACE_DISTRHO
Chassis::Chassis() : Plugin(parameterCount, 0, 0), sampleRate(getSampleRate()) { // one parameter, no programs, no states
Chassis::Chassis() : Plugin(parameterCount, 128, 0), sampleRate(getSampleRate()) { // one parameter, no programs, no states
}
// Initialisation functions
void Chassis::initParameter(uint32_t index, Parameter &parameter) {
switch (index) {
case paramSaw:
parameter.hints = kParameterIsAutomatable | kParameterIsBoolean;
parameter.name = "Saw";
parameter.symbol = "ch_saw";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f;
parameter.ranges.def = 127.0f;
parameter.midiCC = 17;
break;
case paramSqr:
parameter.hints = kParameterIsAutomatable | kParameterIsBoolean;
parameter.name = "Square";
parameter.symbol = "ch_sqr";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f;
parameter.ranges.def = 127.0f;
parameter.midiCC = 16;
break;
case paramSub:
parameter.hints = kParameterIsAutomatable;
parameter.name = "Sub Osc";
parameter.symbol = "ch_sub";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f;
parameter.ranges.def = 0.0f;
parameter.midiCC = 18;
break;
case paramAttack:
parameter.hints = kParameterIsAutomatable;
parameter.name = "Attack";
parameter.symbol = "ch_attack";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f;
parameter.ranges.def = 59.0f;
parameter.midiCC = 73;
break;
case paramDecay:
parameter.hints = kParameterIsAutomatable;
parameter.name = "Decay";
parameter.symbol = "ch_decay";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f;
parameter.ranges.def = 32.0f;
parameter.midiCC = 75;
break;
case paramSustain:
parameter.hints = kParameterIsAutomatable;
parameter.name = "Sustain";
parameter.symbol = "ch_sustain";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f;
parameter.ranges.def = 86.0f;
parameter.midiCC = 27;
break;
case paramRelease:
parameter.hints = kParameterIsAutomatable;
parameter.name = "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.max = 127.0f;
parameter.ranges.def = 0.0f;
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;
}
}
void Chassis::setParameterValue(uint32_t index, float value) {
// printf("setparametervalue %d\n", index);
switch (index) {
case paramSaw:
s.patchRam.switch1 &= 0xef;
s.patchRam.switch1 |= (value > 63) << 4;
break;
case paramSqr:
s.patchRam.switch1 &= 0xf7;
s.patchRam.switch1 |= (value > 63) << 3;
break;
case paramSub:
s.patchRam.sub = value;
break;
case paramAttack:
s.patchRam.env_a = value;
break;
case paramDecay:
s.patchRam.env_d = value;
break;
case paramSustain:
s.patchRam.env_s = value;
break;
case paramRelease:
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;
}
}
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;
}
void Chassis::initAudioPort(bool input, uint32_t index, AudioPort &port) {
port.groupId = kPortGroupStereo;
Plugin::initAudioPort(input, index, port);
@ -189,29 +35,39 @@ void Chassis::initAudioPort(bool input, uint32_t index, AudioPort &port) {
if (!input && index == 1) port.name = "Right Out";
}
void Chassis::initProgramName(uint32_t index, String &programName) {
programName = patchName[index & 0x7f].c_str();
}
void Chassis::loadProgram(uint32_t index) {
memmove(&s.patchRam, (uint8_t *)patchData + (index * 18), 18);
}
// Processing functions
void Chassis::activate() {
// calculate filter coefficients and stuff
printf("called activate()\n");
for (uint8_t i = 0; i < 104; i++) {
s.pitchCV[i] = (440.0f * powf(2, (i - 49) / 12.0f)) / sampleRate;
}
}
void Chassis::deactivate() {
// zero out the outputs, maybe
printf("called deactivate()\n");
// printf("%02x", assign[1]);
}
void Chassis::noteOn(uint8_t note) {
uint32_t i;
s.keyon = true;
for (i = 0; i < NUM_VOICES; i++) {
vPtr++;
if (vPtr == NUM_VOICES) vPtr = 0;
if (s.voice[vPtr].isFree()) {
// printf("voice %d is free, existing note = %d, note = %d\n", vPtr, s.voice[i].note, note);
// if it's an existing note don't reset
s.voice[vPtr].on(note, s.voice[i].note != note);
// printf("note on %d for voice %d\n", note, vPtr);
break;
}
}
@ -224,22 +80,19 @@ void Chassis::noteOn(uint8_t note) {
}
void Chassis::noteOff(uint8_t note) {
// printf("noteoff %d\n", note);
s.keyon = false;
for (uint32_t i = 0; i < NUM_VOICES; i++) {
if (s.voice[i].note == note && !s.voice[i].isFree()) {
s.voice[i].off();
// printf("note off %d for voice %d\n", note, i);
break;
}
}
}
void Chassis::doMidi(const MidiEvent *ev, uint32_t count, uint32_t timeLimit) {
// printf("doMidi() handling events from %d to %d\n", lastEvent, timeLimit);
uint32_t i;
if (count == 0) return;
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]);
if (ev[i].frame > timeLimit) break;
switch (ev[i].data[0]) {
case 0x90:
@ -254,17 +107,14 @@ void Chassis::doMidi(const MidiEvent *ev, uint32_t count, uint32_t timeLimit) {
}
void Chassis::run(const float **, float **outputs, uint32_t frames, const MidiEvent *midiEvents, uint32_t midiEventCount) {
// if (midiEventCount > 0) printf("\n--------------------\n");
uint32_t framePos = 0;
uint32_t sizeThisTime;
s.framesLeft = frames;
// printf("\n------------\ncalled run() for %d frames\n",frames);
// flatten the left channel to use as temporary storage, since
// the synth engine only generates a mono channel
bzero(outputs[0], sizeof(float) * frames);
bzero(outputs[1], sizeof(float) * frames);
// get any of the last block's worth of MIDI out of the way
lastEvent = 0;
@ -275,17 +125,29 @@ void Chassis::run(const float **, float **outputs, uint32_t frames, const MidiEv
s.blockLeft = sampleRate / 238;
doMidi(midiEvents, midiEventCount, framePos + s.blockLeft);
// printf("compute params and reset block size\n");
s.lfoDelay();
s.runLFO();
for (uint32_t i = 0; i < NUM_VOICES; i++) {
s.voice[i].gate(s);
s.voice[i].calcPitch(s);
switch (s.patchRam.switch1 & 0x07) {
case 0:
s.voice[i].omega /= 2;
break;
case 2:
s.voice[i].omega *= 2;
default:
break;
}
s.voice[i].envelope(s);
s.voice[i].calcFilter(s);
}
}
sizeThisTime = (s.framesLeft < s.blockLeft) ? s.framesLeft : s.blockLeft;
// printf("sL = %d bL = %d, calculating %d frames at %d\n", s.framesLeft, s.blockLeft, sizeThisTime, framePos);
// run each synth voice
for (uint8_t i = 0; i < NUM_VOICES; i++) {
@ -296,7 +158,6 @@ void Chassis::run(const float **, float **outputs, uint32_t frames, const MidiEv
s.framesLeft -= sizeThisTime;
s.blockLeft -= sizeThisTime;
}
// printf("and now run the rest of the process for %d frames\n\n", frames);
// copy left to right
memmove(outputs[1], outputs[0], sizeof(float) * frames);

View File

@ -32,15 +32,37 @@ START_NAMESPACE_DISTRHO
class Chassis : public Plugin {
public:
enum Parameters {
paramLFORate,
paramLFODelay,
paramVCORange,
paramVCOLFO,
paramPWMLFO,
paramPWMMode,
paramSaw,
paramSqr,
paramSub,
paramNoise,
paramHPF,
paramVCFFreq,
paramVCFReso,
paramVCFMode,
paramVCFEnv,
paramVCFLFO,
paramVCFKey,
paramEnvGate,
paramVCALevel,
paramAttack,
paramDecay,
paramSustain,
paramRelease,
paramEnvGate,
paramVCALevel,
paramModWheel,
parameterCount
};
@ -49,12 +71,12 @@ class Chassis : public Plugin {
protected:
const char *getLabel() const override { return "chassis"; }
const char *getDescription() const override {
return "MIDIVerb emulation, a tribute to Keith Barr";
return "simple polysynth";
}
const char *getMaker() const override { return "Gordonjcp"; }
const char *getLicense() const override { return "ISC"; }
uint32_t getVersion() const override { return d_version(1, 0, 0); }
int64_t getUniqueId() const override { return d_cconst('P', 'f', 'a', 'u'); }
int64_t getUniqueId() const override { return d_cconst('P', 'h', 'e', 'r'); }
// Initialisation
void initAudioPort(bool input, uint32_t index, AudioPort &port) override;
@ -63,8 +85,8 @@ class Chassis : public Plugin {
void setParameterValue(uint32_t index, float value) override;
float getParameterValue(uint32_t index) const override;
// void initProgramName(uint32_t index, String &programName) override;
// void loadProgram(uint32_t index) override;
void initProgramName(uint32_t index, String &programName) override;
void loadProgram(uint32_t index) override;
// Processing
void activate() override;
@ -79,7 +101,7 @@ class Chassis : public Plugin {
double sampleRate;
uint32_t lastEvent;
// Voice voice[NUM_VOICES];
uint8_t vPtr;
uint8_t vPtr = 0;
Synth s;

497
plugin/parameters.cpp Normal file
View File

@ -0,0 +1,497 @@
/*
Chassis polysynth framework
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 "chassis.hpp"
void Chassis::initParameter(uint32_t index, Parameter& parameter) {
switch (index) {
case paramLFORate:
parameter.hints = kParameterIsAutomatable;
parameter.name = "LFO Rate";
parameter.symbol = "ch_lforate";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f;
parameter.ranges.def = 48.0f;
parameter.midiCC = 3;
break;
case paramLFODelay:
parameter.hints = kParameterIsAutomatable;
parameter.name = "LFO Delay";
parameter.symbol = "ch_lfodelay";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f;
parameter.ranges.def = 0.0f;
parameter.midiCC = 9;
break;
case paramVCORange:
parameter.hints = kParameterIsAutomatable | kParameterIsInteger;
parameter.name = "Range";
parameter.symbol = "ch_vcorange";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 2.0f;
parameter.ranges.def = 1.0f;
parameter.midiCC = 12;
parameter.enumValues.count = 3;
parameter.enumValues.restrictedMode = true;
{
ParameterEnumerationValue* const enumValues = new ParameterEnumerationValue[3];
enumValues[0].value = 0.0f;
enumValues[0].label = "16'";
enumValues[1].value = 1.0f;
enumValues[1].label = "8'";
enumValues[2].value = 2.0f;
enumValues[2].label = "4'";
parameter.enumValues.values = enumValues;
}
break;
case paramVCOLFO:
parameter.hints = kParameterIsAutomatable;
parameter.name = "LFO";
parameter.symbol = "ch_lfo";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f;
parameter.ranges.def = 10.0f;
parameter.midiCC = 13;
break;
case paramPWMLFO:
parameter.hints = kParameterIsAutomatable;
parameter.name = "PWM";
parameter.symbol = "ch_pwm";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f;
parameter.ranges.def = 48.0f;
parameter.midiCC = 14;
break;
case paramPWMMode:
parameter.hints = kParameterIsAutomatable | kParameterIsBoolean;
parameter.name = "PWM Mode";
parameter.symbol = "ch_pwmmode";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 1.0f;
parameter.ranges.def = 1.0f;
parameter.midiCC = 15;
parameter.enumValues.count = 2;
parameter.enumValues.restrictedMode = true;
{
ParameterEnumerationValue* const enumValues = new ParameterEnumerationValue[2];
enumValues[0].value = 0.0f;
enumValues[0].label = "LFO";
enumValues[1].value = 1.0f;
enumValues[1].label = "MAN";
parameter.enumValues.values = enumValues;
}
break;
case paramSaw:
parameter.hints = kParameterIsAutomatable | kParameterIsBoolean;
parameter.name = "Saw";
parameter.symbol = "ch_saw";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 1.0f;
parameter.ranges.def = 1.0f;
parameter.midiCC = 17;
break;
case paramSqr:
parameter.hints = kParameterIsAutomatable | kParameterIsBoolean;
parameter.name = "Square";
parameter.symbol = "ch_sqr";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 1.0f;
parameter.ranges.def = 1.0f;
parameter.midiCC = 16;
break;
case paramSub:
parameter.hints = kParameterIsAutomatable;
parameter.name = "Sub Osc";
parameter.symbol = "ch_sub";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f;
parameter.ranges.def = 0.0f;
parameter.midiCC = 18;
break;
case paramNoise:
parameter.hints = kParameterIsAutomatable;
parameter.name = "Noise";
parameter.symbol = "ch_noise";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f;
parameter.ranges.def = 0.0f;
parameter.midiCC = 19;
break;
case paramHPF:
parameter.hints = kParameterIsAutomatable | kParameterIsInteger;
parameter.name = "HPF";
parameter.symbol = "ch_hpf";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 3.0f;
parameter.ranges.def = 0.0f;
parameter.midiCC = 20;
break;
case paramVCFFreq:
parameter.hints = kParameterIsAutomatable;
parameter.name = "Freq";
parameter.symbol = "ch_freq";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f;
parameter.ranges.def = 60.0f;
parameter.midiCC = 74;
break;
case paramVCFReso:
parameter.hints = kParameterIsAutomatable;
parameter.name = "Res";
parameter.symbol = "ch_reso";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f;
parameter.ranges.def = 0.0f;
parameter.midiCC = 71;
break;
case paramVCFMode:
parameter.hints = kParameterIsAutomatable | kParameterIsInteger;
parameter.name = "Polarity";
parameter.symbol = "ch_vcfmode";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 1.0f;
parameter.ranges.def = 1.0f;
parameter.midiCC = 21;
parameter.enumValues.count = 2;
parameter.enumValues.restrictedMode = true;
{
ParameterEnumerationValue* const enumValues = new ParameterEnumerationValue[2];
enumValues[0].value = 0.0f;
enumValues[0].label = "POS";
enumValues[1].value = 1.0f;
enumValues[1].label = "INV";
parameter.enumValues.values = enumValues;
}
break;
case paramVCFEnv:
parameter.hints = kParameterIsAutomatable;
parameter.name = "Env";
parameter.symbol = "ch_vcfenv";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f;
parameter.ranges.def = 46.0f;
parameter.midiCC = 22;
break;
case paramVCFLFO:
parameter.hints = kParameterIsAutomatable;
parameter.name = "LFO";
parameter.symbol = "ch_vcflfo";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f;
parameter.ranges.def = 0.0f;
parameter.midiCC = 23;
break;
case paramVCFKey:
parameter.hints = kParameterIsAutomatable;
parameter.name = "Kybd";
parameter.symbol = "ch_vcfkey";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f;
parameter.ranges.def = 71.0f;
parameter.midiCC = 24;
break;
case paramAttack:
parameter.hints = kParameterIsAutomatable;
parameter.name = "Attack";
parameter.symbol = "ch_attack";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f;
parameter.ranges.def = 27.0f;
parameter.midiCC = 73;
break;
case paramDecay:
parameter.hints = kParameterIsAutomatable;
parameter.name = "Decay";
parameter.symbol = "ch_decay";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f;
parameter.ranges.def = 57.0f;
parameter.midiCC = 75;
break;
case paramSustain:
parameter.hints = kParameterIsAutomatable;
parameter.name = "Sustain";
parameter.symbol = "ch_sustain";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f;
parameter.ranges.def = 57.0f;
parameter.midiCC = 27;
break;
case paramRelease:
parameter.hints = kParameterIsAutomatable;
parameter.name = "Release";
parameter.symbol = "ch_release";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f;
parameter.ranges.def = 48.0f;
parameter.midiCC = 72;
break;
case paramEnvGate:
parameter.hints = kParameterIsAutomatable | kParameterIsInteger; // | kParameterIsBoolean;
parameter.name = "Env-Gate";
parameter.symbol = "ch_envgate";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 1.0f;
parameter.ranges.def = 1.0f;
parameter.midiCC = 25;
parameter.enumValues.count = 2;
parameter.enumValues.restrictedMode = true;
{
ParameterEnumerationValue* const enumValues = new ParameterEnumerationValue[2];
enumValues[0].value = 0.0f;
enumValues[0].label = "ENV";
enumValues[1].value = 1.0f;
enumValues[1].label = "GATE";
parameter.enumValues.values = enumValues;
}
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;
case paramModWheel:
parameter.hints = kParameterIsAutomatable | kParameterIsHidden;
parameter.name = "Mod wheel";
parameter.symbol = "ch_modwheel";
parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f;
parameter.ranges.def = 0.0f;
parameter.midiCC = 1;
break;
}
// chorus, porta, bend range, key mode still to do
}
void Chassis::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;
switch (index) {
case paramLFORate:
s.patchRam.lfoRate = value;
break;
case paramLFODelay:
s.patchRam.lfoDelay = value;
break;
case paramVCOLFO:
s.patchRam.vcoLfo = value;
break;
case paramPWMLFO:
s.patchRam.pwmLfo = value / 1.27;
break;
case paramSub:
s.patchRam.sub = value;
break;
case paramNoise:
s.patchRam.noise = value;
break;
case paramVCFFreq:
s.patchRam.vcfFreq = value;
break;
case paramVCFReso:
s.patchRam.vcfReso = value;
break;
case paramVCFEnv:
s.patchRam.vcfEnv = value;
break;
case paramVCFLFO:
s.patchRam.vcfLfo = value;
break;
case paramVCFKey:
s.patchRam.vcfKey = value;
break;
case paramVCALevel:
s.patchRam.vca = value;
break;
case paramAttack:
s.patchRam.env_a = value;
break;
case paramDecay:
s.patchRam.env_d = value;
break;
case paramSustain:
s.patchRam.env_s = value;
break;
case paramRelease:
s.patchRam.env_r = value;
break;
// switch 1 params
case paramVCORange: // bits 0-2 of switch 1
// doesn't look great in Carla because of odd behaviour with small integer knobs
s.patchRam.switch1 &= 0xf8;
s.patchRam.switch1 |= (1 << (int)(value - 1));
break;
case paramSqr: // bit 3 of switch 1
s.patchRam.switch1 &= 0xf7;
s.patchRam.switch1 |= (value >= 0.5) << 3;
break;
case paramSaw: // bit 4 of switch 1
s.patchRam.switch1 &= 0xef;
s.patchRam.switch1 |= (value >= 0.5) << 4;
break;
// missing chorus switch
// switch 2 params
case paramPWMMode: // bit 0 of switch 2
s.patchRam.switch2 &= 0xfe;
s.patchRam.switch2 |= (value >= 0.5);
break;
case paramVCFMode: // bit 1 of switch 2
s.patchRam.switch2 &= 0xfd;
s.patchRam.switch2 |= (value >= 0.5) << 1;
break;
case paramEnvGate:
s.patchRam.switch2 &= 0xfb;
s.patchRam.switch2 |= (value >= 0.5) << 2;
break;
case paramHPF: // 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;
s.patchRam.switch2 &= 0xf3;
s.patchRam.switch2 |= (int)value << 3;
break;
case paramModWheel:
s.ff64 = (int)value << 1;
break;
}
}
float Chassis::getParameterValue(uint32_t index) const {
switch (index) {
case paramLFORate:
return s.patchRam.lfoRate;
break;
case paramLFODelay:
return s.patchRam.lfoDelay;
break;
case paramVCORange:
// FIXME this needs to be better generally
switch (s.patchRam.switch1 & 0x07) {
case 1:
return 0;
break;
case 4:
return 2;
break;
default:
return 1;
}
break;
case paramVCOLFO:
return s.patchRam.vcoLfo;
break;
case paramPWMLFO:
return s.patchRam.pwmLfo * 1.27f;
break;
case paramPWMMode:
return (s.patchRam.switch2 & 0x01) != 0;
break;
case paramSaw:
return (s.patchRam.switch1 & 0x10) != 0;
break;
case paramSqr:
return (s.patchRam.switch1 & 0x08) != 0;
case paramSub:
return s.patchRam.sub;
break;
case paramNoise:
return s.patchRam.noise;
break;
case paramHPF:
return (s.patchRam.switch2 & 0x18) >> 3;
break;
case paramVCFFreq:
return s.patchRam.vcfFreq;
break;
case paramVCFReso:
return s.patchRam.vcfReso;
break;
case paramVCFEnv:
return s.patchRam.vcfEnv;
break;
case paramVCFLFO:
return s.patchRam.vcfLfo;
break;
case paramVCFKey:
return s.patchRam.vcfKey;
break;
case paramVCFMode:
return (s.patchRam.switch2 & 0x02) != 0;
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;
case paramEnvGate:
return (s.patchRam.switch2 & 0x04) != 0;
case paramVCALevel:
return s.patchRam.vca;
break;
}
return 0;
}

157
plugin/patches.hpp Normal file
View File

@ -0,0 +1,157 @@
#pragma once
#include <stdint.h>
#include <string>
#include "DistrhoPlugin.hpp"
const std::string patchName[] = {
"Brass Set 1", "Brass Swell", "Trumpet", "Flutes", "Moving Strings", "Brass & Strings", "Choir", "Piano I",
"Organ I", "Organ II", "Combo Organ", "Calliope", "Donald Pluck", "Celeste* (1 oct.up)", "Elect. Piano I",
"Elect. Piano II", "Clock Chimes* (1 oct. up)", "Steel Drums", "Xylophone", "Brass III", "Fanfare", "String III",
"Pizzicato", "High Strings", "Bass clarinet", "English Horn", "Brass Ensemble", "Guitar", "Koto", "Dark Pluck",
"Funky I", "Synth Bass I (unison)", "Lead I", "Lead II", "Lead III", "Funky II", "Synth Bass II", "Funky III",
"Thud Wah", "Going Up", "Piano II", "Clav", "Frontier Organ", "Snare Drum (unison)", "Tom Toms (unison)",
"Timpani (unison)", "Shaker", "Synth Pad", "Sweep I", "Pluck Sweep", "Repeater", "Sweep II", "Pluck Bell",
"Dark Synth Piano", "Sustainer", "Wah Release", "Gong (play low chords)", "Resonance Funk",
"Drum Booms* (1 oct. down)", "Dust Storm", "Rocket Men", "Hand Claps", "FX Sweep", "Caverns", "Strings",
"Violin", "Chorus Vibes", "Organ 1", "Harpsichord 1", "Recorder", "Perc. Pluck", "Noise Sweep", "Space Chimes",
"Nylon Guitar", "Orchestral Pad", "Bright Pluck", "Organ Bell", "Accordion", "FX Rise 1", "FX Rise 2", "Brass",
"Helicopter", "Lute", "Chorus Funk", "Tomita", "FX Sweep 1", "Sharp Reed", "Bass Pluck", "Resonant Rise",
"Harpsichord 2", "Dark Ensemble", "Contact Wah", "Noise Sweep 2", "Glassy Wah", "Phase Ensemble", "Chorused Bell",
"Clav", "Organ 2", "Bassoon", "Auto Release Noise Sweep", "Brass Ensemble", "Ethereal", "Chorus Bell 2",
"Blizzard", "E. Piano with Tremolo", "Clarinet", "Thunder", "Reedy Organ", "Flute / Horn", "Toy Rhodes",
"Surf's Up", "OW Bass", "Piccolo", "Melodic Taps", "Meow Brass", "Violin (high)", "High Bells", "Rolling Wah",
"Ping Bell", "Brassy Organ", "Low Dark Strings", "Piccolo Trumpet", "Cello", "High Strings", "Rocket Men",
"Forbidden Planet", "Froggy", "Owgan"};
const uint8_t patchData[128][18] = {
{0x14, 0x31, 0x00, 0x66, 0x00, 0x23, 0x0d, 0x3a, 0x00, 0x56, 0x6c, 0x03, 0x31, 0x2d, 0x20, 0x00, 0x51, 0x11},
{0x06, 0x30, 0x00, 0x38, 0x00, 0x2b, 0x11, 0x1a, 0x00, 0x54, 0x4b, 0x40, 0x76, 0x26, 0x25, 0x46, 0x52, 0x19},
{0x34, 0x2d, 0x08, 0x66, 0x00, 0x37, 0x22, 0x18, 0x01, 0x3b, 0x7f, 0x05, 0x42, 0x30, 0x10, 0x00, 0x32, 0x09},
{0x3c, 0x2b, 0x01, 0x00, 0x00, 0x37, 0x20, 0x0a, 0x0b, 0x29, 0x7f, 0x17, 0x51, 0x00, 0x12, 0x00, 0x32, 0x01},
{0x3f, 0x00, 0x00, 0x27, 0x00, 0x4d, 0x14, 0x04, 0x00, 0x6f, 0x22, 0x0d, 0x57, 0x58, 0x23, 0x0e, 0x1a, 0x10},
{0x23, 0x00, 0x00, 0x38, 0x00, 0x4c, 0x11, 0x04, 0x00, 0x29, 0x4e, 0x2c, 0x42, 0x35, 0x2c, 0x17, 0x49, 0x18},
{0x3b, 0x0e, 0x0d, 0x19, 0x00, 0x3b, 0x5e, 0x02, 0x00, 0x3e, 0x7f, 0x44, 0x0b, 0x7f, 0x30, 0x00, 0x4a, 0x09},
{0x14, 0x31, 0x00, 0x50, 0x00, 0x41, 0x0c, 0x0a, 0x00, 0x1b, 0x67, 0x00, 0x42, 0x00, 0x1e, 0x56, 0x2a, 0x11},
{0x36, 0x0f, 0x00, 0x35, 0x00, 0x2b, 0x4c, 0x0e, 0x01, 0x7f, 0x64, 0x00, 0x0a, 0x52, 0x00, 0x17, 0x29, 0x1c},
{0x2c, 0x0f, 0x00, 0x35, 0x00, 0x35, 0x4c, 0x0e, 0x01, 0x55, 0x4a, 0x00, 0x0a, 0x52, 0x00, 0x3a, 0x4a, 0x1c},
{0x4b, 0x15, 0x09, 0x39, 0x00, 0x3f, 0x46, 0x04, 0x00, 0x6d, 0x60, 0x00, 0x30, 0x2b, 0x2e, 0x3d, 0x2c, 0x0d},
{0x52, 0x28, 0x0b, 0x00, 0x00, 0x57, 0x1b, 0x11, 0x00, 0x38, 0x59, 0x07, 0x7f, 0x7f, 0x06, 0x30, 0x4a, 0x0b},
{0x4c, 0x15, 0x09, 0x39, 0x00, 0x49, 0x69, 0x0f, 0x00, 0x4e, 0x52, 0x02, 0x05, 0x2b, 0x0a, 0x7f, 0x2c, 0x07},
{0x1c, 0x00, 0x00, 0x00, 0x00, 0x21, 0x18, 0x36, 0x00, 0x26, 0x60, 0x00, 0x2c, 0x00, 0x51, 0x0f, 0x2c, 0x19},
{0x3b, 0x00, 0x00, 0x00, 0x00, 0x10, 0x1f, 0x3d, 0x06, 0x23, 0x7f, 0x01, 0x55, 0x2b, 0x28, 0x00, 0x29, 0x01},
{0x00, 0x00, 0x00, 0x47, 0x00, 0x32, 0x45, 0x07, 0x00, 0x50, 0x66, 0x00, 0x44, 0x00, 0x16, 0x00, 0x49, 0x11},
{0x3b, 0x00, 0x00, 0x00, 0x16, 0x2c, 0x7f, 0x00, 0x00, 0x7f, 0x68, 0x00, 0x30, 0x00, 0x33, 0x7f, 0x44, 0x1b},
{0x21, 0x35, 0x00, 0x20, 0x09, 0x47, 0x2e, 0x1a, 0x00, 0x7f, 0x7f, 0x00, 0x1a, 0x00, 0x25, 0x21, 0x4a, 0x1b},
{0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x18, 0x36, 0x00, 0x33, 0x7f, 0x00, 0x1d, 0x1d, 0x26, 0x0f, 0x2c, 0x19},
{0x34, 0x14, 0x00, 0x23, 0x00, 0x42, 0x18, 0x0b, 0x00, 0x0c, 0x7f, 0x3a, 0x64, 0x5e, 0x25, 0x16, 0x52, 0x19},
{0x2f, 0x00, 0x00, 0x46, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x43, 0x21, 0x48, 0x68, 0x4b, 0x31, 0x32, 0x59, 0x18},
{0x30, 0x1b, 0x00, 0x66, 0x00, 0x47, 0x0e, 0x00, 0x00, 0x54, 0x49, 0x3f, 0x1f, 0x7f, 0x2d, 0x00, 0x1a, 0x10},
{0x3c, 0x12, 0x00, 0x66, 0x00, 0x42, 0x02, 0x05, 0x00, 0x2a, 0x7f, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x5a, 0x00},
{0x3a, 0x0e, 0x00, 0x66, 0x00, 0x54, 0x08, 0x02, 0x00, 0x47, 0x4d, 0x12, 0x2c, 0x7f, 0x28, 0x00, 0x0c, 0x08},
{0x34, 0x2d, 0x08, 0x00, 0x00, 0x30, 0x24, 0x19, 0x08, 0x3a, 0x68, 0x0b, 0x4b, 0x00, 0x19, 0x00, 0x29, 0x11},
{0x2f, 0x2d, 0x09, 0x66, 0x00, 0x46, 0x30, 0x07, 0x00, 0x1b, 0x7f, 0x08, 0x51, 0x1a, 0x10, 0x00, 0x2a, 0x01},
{0x34, 0x2d, 0x00, 0x66, 0x00, 0x2e, 0x2c, 0x1d, 0x01, 0x3b, 0x67, 0x10, 0x67, 0x61, 0x22, 0x1f, 0x52, 0x11},
{0x56, 0x3a, 0x00, 0x41, 0x00, 0x0c, 0x0f, 0x47, 0x00, 0x1e, 0x60, 0x00, 0x34, 0x36, 0x1f, 0x00, 0x39, 0x11},
{0x5a, 0x1c, 0x00, 0x5e, 0x00, 0x28, 0x3a, 0x1d, 0x00, 0x4b, 0x7f, 0x00, 0x38, 0x00, 0x27, 0x02, 0x4a, 0x01},
{0x1f, 0x00, 0x00, 0x57, 0x00, 0x26, 0x3d, 0x1b, 0x00, 0x47, 0x5a, 0x00, 0x34, 0x0f, 0x3f, 0x61, 0x0a, 0x19},
{0x04, 0x00, 0x00, 0x49, 0x00, 0x1f, 0x17, 0x33, 0x00, 0x29, 0x47, 0x00, 0x1e, 0x24, 0x02, 0x40, 0x59, 0x1d},
{0x3a, 0x10, 0x01, 0x39, 0x32, 0x20, 0x00, 0x42, 0x00, 0x23, 0x4b, 0x00, 0x22, 0x00, 0x24, 0x66, 0x29, 0x19},
{0x48, 0x58, 0x12, 0x00, 0x00, 0x28, 0x61, 0x24, 0x00, 0x34, 0x63, 0x00, 0x2d, 0x49, 0x00, 0x00, 0x2a, 0x1d},
{0x18, 0x58, 0x00, 0x35, 0x00, 0x2d, 0x00, 0x2b, 0x00, 0x34, 0x38, 0x01, 0x17, 0x5b, 0x4b, 0x4f, 0x39, 0x14},
{0x56, 0x47, 0x15, 0x66, 0x00, 0x3c, 0x1f, 0x12, 0x01, 0x4c, 0x7f, 0x00, 0x42, 0x30, 0x0b, 0x00, 0x32, 0x15},
{0x04, 0x00, 0x00, 0x30, 0x00, 0x05, 0x17, 0x51, 0x00, 0x29, 0x64, 0x00, 0x1e, 0x27, 0x02, 0x39, 0x4a, 0x15},
{0x5a, 0x15, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x00, 0x4e, 0x00, 0x25, 0x09, 0x16, 0x2d, 0x31, 0x1c},
{0x04, 0x00, 0x00, 0x09, 0x65, 0x26, 0x36, 0x2d, 0x00, 0x0a, 0x4f, 0x00, 0x18, 0x00, 0x02, 0x7f, 0x39, 0x1d},
{0x3e, 0x00, 0x00, 0x66, 0x08, 0x4e, 0x59, 0x22, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x66, 0x2d, 0x20, 0x1a, 0x1a},
{0x00, 0x00, 0x00, 0x64, 0x1c, 0x4f, 0x7f, 0x25, 0x00, 0x41, 0x4b, 0x00, 0x6c, 0x12, 0x7f, 0x00, 0x22, 0x13},
{0x01, 0x00, 0x00, 0x48, 0x00, 0x2f, 0x00, 0x27, 0x00, 0x05, 0x4e, 0x00, 0x62, 0x00, 0x20, 0x73, 0x2a, 0x10},
{0x42, 0x0f, 0x01, 0x66, 0x00, 0x07, 0x0b, 0x56, 0x02, 0x00, 0x78, 0x00, 0x27, 0x30, 0x0e, 0x00, 0x49, 0x15},
{0x4f, 0x0f, 0x03, 0x55, 0x00, 0x45, 0x36, 0x00, 0x00, 0x34, 0x7f, 0x00, 0x00, 0x7f, 0x00, 0x1d, 0x2a, 0x1c},
{0x34, 0x1b, 0x00, 0x00, 0x5b, 0x5e, 0x0b, 0x11, 0x00, 0x00, 0x65, 0x00, 0x19, 0x00, 0x1e, 0x40, 0x22, 0x11},
{0x3e, 0x10, 0x08, 0x00, 0x7f, 0x35, 0x04, 0x28, 0x00, 0x15, 0x65, 0x00, 0x1e, 0x0f, 0x28, 0x3a, 0x21, 0x18},
{0x00, 0x00, 0x00, 0x23, 0x7f, 0x19, 0x00, 0x36, 0x00, 0x18, 0x7f, 0x01, 0x38, 0x1a, 0x47, 0x2f, 0x21, 0x11},
{0x15, 0x47, 0x00, 0x3a, 0x7f, 0x59, 0x2d, 0x01, 0x11, 0x50, 0x7f, 0x0a, 0x09, 0x00, 0x00, 0x00, 0x22, 0x01},
{0x38, 0x00, 0x05, 0x2b, 0x00, 0x25, 0x00, 0x54, 0x00, 0x7f, 0x33, 0x00, 0x55, 0x4b, 0x3e, 0x25, 0x0a, 0x18},
{0x4e, 0x00, 0x00, 0x66, 0x1c, 0x73, 0x28, 0x43, 0x00, 0x00, 0x61, 0x00, 0x3f, 0x7f, 0x52, 0x2a, 0x0c, 0x12},
{0x44, 0x30, 0x00, 0x44, 0x00, 0x3c, 0x6f, 0x09, 0x06, 0x62, 0x2d, 0x00, 0x5b, 0x00, 0x4d, 0x7d, 0x1a, 0x08},
{0x4d, 0x14, 0x09, 0x65, 0x00, 0x61, 0x1e, 0x38, 0x00, 0x39, 0x57, 0x0e, 0x00, 0x29, 0x00, 0x2c, 0x4a, 0x17},
{0x58, 0x48, 0x0f, 0x61, 0x00, 0x42, 0x66, 0x44, 0x00, 0x46, 0x65, 0x00, 0x59, 0x35, 0x4e, 0x30, 0x4a, 0x19},
{0x33, 0x00, 0x05, 0x11, 0x00, 0x27, 0x00, 0x31, 0x00, 0x7f, 0x31, 0x00, 0x55, 0x4b, 0x35, 0x31, 0x0c, 0x18},
{0x7f, 0x00, 0x00, 0x00, 0x00, 0x38, 0x56, 0x06, 0x00, 0x47, 0x4f, 0x00, 0x2f, 0x00, 0x32, 0x7f, 0x1a, 0x1a},
{0x47, 0x00, 0x05, 0x2e, 0x00, 0x27, 0x00, 0x54, 0x00, 0x7f, 0x39, 0x00, 0x55, 0x4b, 0x46, 0x31, 0x2a, 0x18},
{0x59, 0x0e, 0x01, 0x19, 0x00, 0x48, 0x58, 0x13, 0x00, 0x42, 0x68, 0x00, 0x54, 0x00, 0x1d, 0x57, 0x0a, 0x13},
{0x7f, 0x00, 0x00, 0x66, 0x38, 0x46, 0x6b, 0x05, 0x00, 0x48, 0x5b, 0x03, 0x30, 0x7f, 0x62, 0x00, 0x49, 0x13},
{0x3b, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x7f, 0x2d, 0x00, 0x59, 0x7f, 0x00, 0x13, 0x00, 0x16, 0x00, 0x21, 0x11},
{0x00, 0x00, 0x00, 0x66, 0x7f, 0x2a, 0x00, 0x22, 0x00, 0x3a, 0x7f, 0x00, 0x24, 0x0f, 0x31, 0x2e, 0x21, 0x19},
{0x00, 0x00, 0x00, 0x00, 0x7f, 0x34, 0x55, 0x00, 0x2c, 0x5e, 0x7f, 0x58, 0x5b, 0x1c, 0x55, 0x00, 0x21, 0x11},
{0x08, 0x20, 0x00, 0x66, 0x61, 0x49, 0x72, 0x08, 0x00, 0x38, 0x7f, 0x00, 0x59, 0x7f, 0x68, 0x00, 0x24, 0x03},
{0x3b, 0x00, 0x00, 0x00, 0x7f, 0x11, 0x58, 0x36, 0x00, 0x37, 0x7f, 0x01, 0x0b, 0x00, 0x08, 0x00, 0x21, 0x05},
{0x7f, 0x41, 0x74, 0x66, 0x7f, 0x47, 0x2c, 0x53, 0x00, 0x5e, 0x7f, 0x00, 0x5e, 0x00, 0x70, 0x7f, 0x29, 0x12},
{0x00, 0x00, 0x00, 0x66, 0x7f, 0x44, 0x76, 0x00, 0x00, 0x45, 0x6b, 0x00, 0x0d, 0x26, 0x2f, 0x00, 0x21, 0x08},
{0x39, 0x2d, 0x00, 0x37, 0x00, 0x55, 0x00, 0x00, 0x00, 0x6c, 0x34, 0x3b, 0x20, 0x56, 0x28, 0x00, 0x1a, 0x18},
{0x42, 0x2d, 0x14, 0x00, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x78, 0x6e, 0x2b, 0x2d, 0x39, 0x1a, 0x00, 0x32, 0x18},
{0x48, 0x2d, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x3b, 0x00, 0x17, 0x58, 0x00, 0x59, 0x2f, 0x4a, 0x00, 0x4a, 0x19},
{0x2d, 0x2a, 0x00, 0x49, 0x00, 0x3c, 0x48, 0x0e, 0x00, 0x7f, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x61, 0x0a, 0x1d},
{0x17, 0x00, 0x00, 0x36, 0x00, 0x7f, 0x7f, 0x5a, 0x00, 0x56, 0x7d, 0x00, 0x3c, 0x00, 0x1f, 0x23, 0x2c, 0x00},
{0x4e, 0x00, 0x00, 0x08, 0x00, 0x06, 0x00, 0x2e, 0x00, 0x7f, 0x7f, 0x05, 0x15, 0x7f, 0x1e, 0x00, 0x2a, 0x11},
{0x3e, 0x26, 0x08, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x49, 0x41, 0x00, 0x10, 0x4e, 0x74, 0x00, 0x4a, 0x19},
{0x7f, 0x00, 0x00, 0x00, 0x7f, 0x02, 0x00, 0x4d, 0x00, 0x68, 0x5c, 0x0e, 0x4e, 0x6c, 0x78, 0x00, 0x21, 0x18},
{0x63, 0x00, 0x00, 0x00, 0x00, 0x46, 0x7f, 0x00, 0x00, 0x4a, 0x55, 0x00, 0x19, 0x00, 0x3c, 0x00, 0x4c, 0x18},
{0x48, 0x2d, 0x00, 0x1d, 0x00, 0x39, 0x00, 0x1a, 0x00, 0x19, 0x73, 0x00, 0x59, 0x00, 0x20, 0x00, 0x2a, 0x01},
{0x16, 0x2d, 0x00, 0x69, 0x00, 0x21, 0x00, 0x37, 0x00, 0x24, 0x00, 0x1d, 0x58, 0x32, 0x34, 0x7f, 0x1a, 0x18},
{0x00, 0x00, 0x00, 0x3a, 0x00, 0x39, 0x00, 0x28, 0x00, 0x56, 0x68, 0x00, 0x19, 0x2a, 0x2c, 0x00, 0x2a, 0x11},
{0x4e, 0x00, 0x06, 0x44, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x7f, 0x3e, 0x00, 0x00, 0x7f, 0x1a, 0x55, 0x5a, 0x19},
{0x00, 0x00, 0x00, 0x40, 0x00, 0x4d, 0x00, 0x0e, 0x00, 0x4a, 0x42, 0x08, 0x0b, 0x6e, 0x09, 0x50, 0x5a, 0x01},
{0x74, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x7f, 0x10, 0x7f, 0x40, 0x00, 0x00, 0x00, 0x7f, 0x54, 0x00, 0x44, 0x1a},
{0x6c, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x7f, 0x50, 0x17, 0x7f, 0x00, 0x00, 0x7f, 0x33, 0x47, 0x00, 0x02, 0x1a},
{0x33, 0x7f, 0x00, 0x49, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x7f, 0x48, 0x03, 0x2c, 0x33, 0x0b, 0x00, 0x52, 0x1c},
{0x6a, 0x00, 0x00, 0x30, 0x00, 0x52, 0x05, 0x5d, 0x7f, 0x00, 0x48, 0x00, 0x00, 0x23, 0x4c, 0x7f, 0x0a, 0x1b},
{0x34, 0x00, 0x02, 0x69, 0x00, 0x1d, 0x00, 0x23, 0x00, 0x56, 0x7f, 0x00, 0x30, 0x22, 0x57, 0x00, 0x2a, 0x19},
{0x4d, 0x5e, 0x00, 0x49, 0x00, 0x2f, 0x22, 0x35, 0x00, 0x41, 0x2a, 0x00, 0x0b, 0x22, 0x00, 0x4f, 0x5a, 0x1d},
{0x4e, 0x00, 0x00, 0x69, 0x00, 0x31, 0x7d, 0x0f, 0x02, 0x7f, 0x39, 0x00, 0x0e, 0x4f, 0x00, 0x00, 0x24, 0x1f},
{0x7f, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x7f, 0x10, 0x4c, 0x40, 0x00, 0x00, 0x00, 0x7f, 0x55, 0x00, 0x44, 0x18},
{0x15, 0x00, 0x00, 0x49, 0x00, 0x21, 0x00, 0x35, 0x00, 0x41, 0x37, 0x02, 0x12, 0x70, 0x00, 0x00, 0x3c, 0x1c},
{0x00, 0x00, 0x00, 0x3c, 0x00, 0x25, 0x1d, 0x1c, 0x00, 0x2b, 0x5c, 0x00, 0x2a, 0x23, 0x00, 0x00, 0x39, 0x18},
{0x34, 0x00, 0x0f, 0x00, 0x00, 0x42, 0x6b, 0x1f, 0x00, 0x49, 0x3e, 0x00, 0x34, 0x27, 0x00, 0x00, 0x11, 0x1e},
{0x00, 0x00, 0x00, 0x69, 0x00, 0x7f, 0x5f, 0x00, 0x00, 0x7f, 0x6b, 0x00, 0x2d, 0x00, 0x00, 0x23, 0x3c, 0x11},
{0x00, 0x00, 0x00, 0x39, 0x00, 0x37, 0x00, 0x00, 0x00, 0x6b, 0x43, 0x15, 0x00, 0x7f, 0x23, 0x4b, 0x5a, 0x1b},
{0x4e, 0x00, 0x00, 0x51, 0x00, 0x3d, 0x65, 0x2e, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x17},
{0x7f, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x53, 0x00, 0x68, 0x74, 0x7f, 0x4f, 0x1a, 0x53, 0x00, 0x44, 0x1a},
{0x00, 0x5e, 0x00, 0x1c, 0x00, 0x18, 0x22, 0x3d, 0x00, 0x41, 0x70, 0x06, 0x12, 0x32, 0x26, 0x00, 0x2a, 0x19},
{0x0b, 0x2d, 0x05, 0x69, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x7f, 0x63, 0x54, 0x2d, 0x39, 0x31, 0x00, 0x0a, 0x18},
{0x00, 0x0b, 0x00, 0x69, 0x00, 0x3b, 0x7f, 0x00, 0x00, 0x7f, 0x3e, 0x00, 0x3e, 0x00, 0x39, 0x65, 0x4c, 0x18},
{0x32, 0x0b, 0x00, 0x5b, 0x00, 0x20, 0x00, 0x3f, 0x00, 0x00, 0x6c, 0x00, 0x2b, 0x37, 0x00, 0x07, 0x29, 0x09},
{0x51, 0x00, 0x00, 0x68, 0x00, 0x36, 0x6a, 0x07, 0x00, 0x7f, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x1d},
{0x54, 0x26, 0x09, 0x5a, 0x00, 0x23, 0x00, 0x2b, 0x09, 0x62, 0x7f, 0x05, 0x7f, 0x6a, 0x02, 0x00, 0x29, 0x01},
{0x7f, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x00, 0x68, 0x6b, 0x00, 0x4f, 0x39, 0x53, 0x00, 0x44, 0x1a},
{0x19, 0x5e, 0x00, 0x49, 0x00, 0x2f, 0x22, 0x23, 0x00, 0x41, 0x27, 0x06, 0x44, 0x43, 0x26, 0x00, 0x5a, 0x18},
{0x45, 0x00, 0x00, 0x69, 0x00, 0x2f, 0x78, 0x00, 0x00, 0x7f, 0x3c, 0x5c, 0x32, 0x7f, 0x37, 0x3b, 0x1a, 0x18},
{0x48, 0x2d, 0x00, 0x1d, 0x00, 0x00, 0x66, 0x3e, 0x00, 0x5a, 0x1e, 0x00, 0x75, 0x00, 0x78, 0x00, 0x5a, 0x19},
{0x01, 0x00, 0x00, 0x69, 0x7f, 0x38, 0x59, 0x07, 0x26, 0x79, 0x7f, 0x4e, 0x62, 0x57, 0x4e, 0x00, 0x24, 0x00},
{0x2c, 0x00, 0x00, 0x15, 0x00, 0x16, 0x00, 0x23, 0x07, 0x6b, 0x67, 0x00, 0x41, 0x3c, 0x62, 0x7f, 0x2c, 0x10},
{0x45, 0x16, 0x09, 0x00, 0x00, 0x23, 0x00, 0x2b, 0x07, 0x62, 0x68, 0x05, 0x7f, 0x6a, 0x07, 0x00, 0x2a, 0x01},
{0x7f, 0x00, 0x00, 0x00, 0x7f, 0x02, 0x00, 0x4d, 0x00, 0x68, 0x6b, 0x00, 0x16, 0x6c, 0x78, 0x00, 0x21, 0x18},
{0x23, 0x12, 0x00, 0x49, 0x00, 0x21, 0x00, 0x35, 0x00, 0x41, 0x45, 0x03, 0x2c, 0x6d, 0x00, 0x00, 0x2a, 0x1c},
{0x22, 0x00, 0x00, 0x3b, 0x00, 0x19, 0x00, 0x2a, 0x00, 0x41, 0x16, 0x15, 0x49, 0x47, 0x30, 0x00, 0x19, 0x18},
{0x1e, 0x2d, 0x7f, 0x00, 0x00, 0x3a, 0x7f, 0x00, 0x00, 0x7f, 0x6b, 0x00, 0x59, 0x00, 0x27, 0x00, 0x24, 0x00},
{0x01, 0x00, 0x00, 0x69, 0x7f, 0x54, 0x0b, 0x00, 0x31, 0x36, 0x4b, 0x21, 0x61, 0x7f, 0x79, 0x00, 0x24, 0x0a},
{0x32, 0x00, 0x00, 0x32, 0x00, 0x3f, 0x54, 0x41, 0x00, 0x7f, 0x46, 0x7f, 0x00, 0x6b, 0x00, 0x2f, 0x39, 0x1f},
{0x46, 0x1c, 0x00, 0x0e, 0x00, 0x04, 0x00, 0x3f, 0x10, 0x62, 0x57, 0x10, 0x67, 0x6a, 0x15, 0x00, 0x3c, 0x09},
{0x63, 0x00, 0x00, 0x00, 0x7f, 0x48, 0x70, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x18, 0x00, 0x18, 0x00, 0x41, 0x00},
{0x33, 0x7f, 0x00, 0x49, 0x00, 0x2d, 0x64, 0x23, 0x00, 0x41, 0x54, 0x04, 0x5a, 0x00, 0x1b, 0x00, 0x32, 0x1c},
{0x47, 0x2d, 0x16, 0x00, 0x00, 0x59, 0x00, 0x00, 0x03, 0x78, 0x55, 0x2b, 0x2d, 0x39, 0x1a, 0x00, 0x34, 0x18},
{0x1e, 0x2d, 0x7f, 0x00, 0x00, 0x4c, 0x7f, 0x00, 0x00, 0x7f, 0x6f, 0x00, 0x16, 0x34, 0x3b, 0x00, 0x24, 0x00},
{0x24, 0x00, 0x00, 0x69, 0x00, 0x3c, 0x09, 0x00, 0x7f, 0x00, 0x4d, 0x22, 0x37, 0x7f, 0x69, 0x00, 0x2a, 0x18},
{0x00, 0x0b, 0x00, 0x68, 0x00, 0x4c, 0x7f, 0x00, 0x00, 0x7f, 0x53, 0x00, 0x17, 0x17, 0x39, 0x00, 0x4c, 0x18},
{0x10, 0x00, 0x00, 0x44, 0x00, 0x08, 0x00, 0x57, 0x00, 0x34, 0x0e, 0x00, 0x24, 0x6d, 0x00, 0x38, 0x59, 0x18},
{0x15, 0x2d, 0x05, 0x51, 0x00, 0x47, 0x00, 0x00, 0x00, 0x67, 0x28, 0x53, 0x17, 0x6d, 0x2a, 0x00, 0x19, 0x18},
{0x33, 0x7f, 0x00, 0x49, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x7f, 0x61, 0x03, 0x2c, 0x33, 0x0b, 0x00, 0x32, 0x1c},
{0x39, 0x2d, 0x15, 0x00, 0x00, 0x4b, 0x00, 0x03, 0x01, 0x2d, 0x6d, 0x30, 0x41, 0x5a, 0x22, 0x00, 0x31, 0x18},
{0x50, 0x00, 0x0a, 0x46, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x47, 0x30, 0x1b, 0x39, 0x39, 0x29, 0x00, 0x1c, 0x18},
{0x6c, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x7f, 0x50, 0x3f, 0x7f, 0x00, 0x00, 0x7f, 0x33, 0x59, 0x00, 0x02, 0x1a},
{0x32, 0x0b, 0x00, 0x2c, 0x00, 0x1d, 0x04, 0x58, 0x05, 0x5f, 0x4f, 0x00, 0x30, 0x17, 0x2a, 0x09, 0x4c, 0x01},
{0x4e, 0x00, 0x00, 0x00, 0x00, 0x37, 0x7f, 0x2b, 0x00, 0x7f, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x29, 0x17},
{0x32, 0x00, 0x00, 0x2d, 0x00, 0x26, 0x54, 0x20, 0x00, 0x7f, 0x65, 0x00, 0x31, 0x37, 0x00, 0x38, 0x39, 0x19}};

View File

@ -16,121 +16,14 @@
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
// contains the actual sound generation code
#include "voice.hpp"
#include <math.h>
#include <cstdio>
bool Voice::isFree() {
return ff10 == false;
}
void Voice::on(uint32_t key, bool reset = 0) {
// printf("======================================================================================\n");
// 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
// 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;
note = key;
if (ff11) goto h013e;
h0132:
if (ff33) goto h0149; // sustained
ff33 = false;
goto h0149;
h013e:
ff00 = true; // in a real one, voice counter needs programmed
goto h0149;
h0144:
if (!ff11) goto h0132; // unsure, copied from ff10 at start of mainloop
h0149:
// 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;
}
void Voice::off() {
bool sustain = false;
ff10 = false;
if (!sustain) { // dummy sustain
ff33 = false;
}
// printf("after note off, %d %x %x %x %x\n", note, ff07, ff10, ff11, ff33);
}
void Voice::gate(Synth &s) {
uint16_t bc, ea = env;
ff11 = ff10;
// 0509
if (!ff11) goto h0538;
// 050e
if (!ff33) goto h0563;
// 0513
if (!ff07) goto h051e;
h0517:
ff07 = false;
h051e:
bc = s.patchRam.env_s << 7; // half scale
if (ea < bc) ea = bc;
ea -= bc;
bc = ea;
ea = (ea * decay_table[s.patchRam.env_d]) >> 16;
ea += s.patchRam.env_s << 7;
// printf("returning from decay phase\n");
goto h0590;
h0538:
// printf("got to 0x0538\n");
if (!ff07) goto h054a; // note on? if not skip ahead
// 053c
if (ff08) goto h0517;
ff07 = false;
h054a:
// printf("release phase\n");
ff33 = false;
ff08 = false;
bc = ea;
ea = (ea * decay_table[s.patchRam.env_r]) >> 16;
// printf("returning from release phase\n");
goto h0590;
h0563:
// printf("attack phase\n");
ff08 = false;
ea += attack_table[s.patchRam.env_a];
if (ea & 0xc000) {
ea = 0x3fff;
ff33 = true;
ff08 = true;
}
h0590:
env = ea;
// printf("%04x %d %d %d %d %d \n", ea, ff07, ff08, ff10, ff11, ff33);
}
static inline float poly3blep0(float t) {
float t2 = t * t;
return 2 * (t * t2 - 0.5f * t2 * t2);
@ -141,14 +34,28 @@ static inline float poly3blep1(float t) {
}
void Voice::run(Synth &s, float *buffer, uint32_t samples) {
float y, out, pw = 0.5, t;
float y, out, 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 pw = s.ff4f / 32768.0f;
float fb, res = s.patchRam.vcfReso / 28.0f; // guess
float cut = 248.0f * (powf(2, (vcfenv - 0x1880) / 1143.0f));
// now radians
cut = 0.25 * 6.2832 * cut / 48000.0f;
// now correct
cut = cut / (1 + cut);
float sqr = (s.patchRam.switch1 & 0x08) ? 0.63 : 0; //? 0.175 : 0;
float saw = (s.patchRam.switch1 & 0x10) ? 0.8 : 0; //? 0.220 : 0;
float sub = (s.patchRam.sub / 127.0f); // * 0.275;
float gain = 0.5 * powf(2, (s.patchRam.vca / 64.0f) - 1);
float vcaEnv = (s.patchRam.switch2 & 0x04) ? (float)ff11 : (env / 16384.0f);
for (uint32_t i = 0; i < samples; i++) {
y = delay;
delay = 0;
@ -161,11 +68,7 @@ void Voice::run(Synth &s, float *buffer, uint32_t samples) {
while (true) {
if (pulseStage == 0) {
if (phase < pw) break;
#if 1
t = (phase - pw) / omega;
#else
t = (phase - pw) / (widthDelay - pw + freq);
#endif
t = (phase - pw) / (lastpw - pw + omega);
y -= poly3blep0(t) * sqr;
delay -= poly3blep1(t) * sqr;
pulseStage = 1;
@ -174,7 +77,7 @@ void Voice::run(Synth &s, float *buffer, uint32_t samples) {
if (pulseStage == 1) {
if (phase < 1) break;
float t = (phase - 1) / omega;
t = (phase - 1) / omega;
y += poly3blep0(t) * (saw + sqr);
delay += poly3blep1(t) * (saw + sqr);
@ -197,7 +100,26 @@ void Voice::run(Synth &s, float *buffer, uint32_t samples) {
out = y;
// widthDelay = pw;
vr58c106 += (((env / 16383.0) - vr58c106) * 0.0075);
out *= 0.707;
for (uint8_t ovs = 0; ovs < 4; ovs++) {
fb = b4;
// hard clip
if (fb > 1) fb = 1;
if (fb < -1) fb = -1;
fb = out - (fb * res);
b1 = ((fb - b1) * cut) + b1;
b2 = ((b1 - b2) * cut) + b2;
b3 = ((b2 - b3) * cut) + b3;
b4 = ((b3 - b4) * cut) + b4;
}
vr58c106 += ((vcaEnv - vr58c106) * 0.005);
lastpw = pw;
out = b4 * (0.25);
buffer[i] += (gain * out * vr58c106);
}
}

View File

@ -24,26 +24,22 @@
class Synth;
class Patch {
public:
float saw, sqr, sub;
uint16_t attack, decay, sustain, release;
};
class Voice {
public:
// uint8_t note = 72;
uint8_t note = 60;
uint8_t note = 60; // per-voice note, set to middle C at 02b1h
void on(uint32_t key, bool reset);
void off();
bool isFree();
void run(Synth &s, float *buffer, uint32_t samples);
void envelope(Synth &s);
void calcPitch(Synth &s);
void calcFilter(Synth &s);
void gate(Synth &s);
uint16_t ff71 = 0; // stores pitch + fraction
float omega;
uint16_t vcfenv;
private:
enum { ATTACK,
@ -55,66 +51,56 @@ class Voice {
K_ON,
K_SUSTAIN } keyState = K_OFF;
bool ff00 = 0; // reset bit
bool ff07 = 0; // status bit
bool ff08 = 0; // set to indicate attack phase complete
bool ff10 = 0; // note on bit
bool ff11 = 0; // sustain flag
bool ff33 = 0; // releasing flag
bool ff00 = 0; // reset DCO clock flag
bool ff07 = 0; // set to indicate attack phase
bool ff08 = 0; // set to indicate decay/sustain phase
bool ff10 = 0; // note on bit
bool ff11 = 0; // drives the "gate" signal for VCA
uint16_t ff27 = 0; // envelope for voice
bool ff33 = 0; // releasing flag
uint16_t env;
float phase = 0, omega = 260 / 48000.0, subosc = 1;
float phase = 0, subosc = 1;
// float env, target;
float delay;
uint8_t pulseStage = 0;
float pw_rc = 0;
float lastpw = 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};
float b1, b2, b3, b4;
};
class Synth {
public:
Patch patch;
Voice voice[8];
float lfo = 0, lfosw = 0.01;
float lastpw = 0;
uint32_t blockLeft;
uint32_t framesLeft = 0;
bool keyon;
uint8_t ff63 = 0;
// RAM from ff00h to ffffh is cleared to zero
// this is in the startup routine at 0280h
uint8_t ff1e = 0;
uint8_t ff34 = 0;
uint8_t ff4a = 0; // LFO flags
uint16_t ff4d = 0; // LFO output value
uint16_t ff4f = 0; // computed PWM LFO
uint16_t ff51 = 0; // computed pitch LFO
uint16_t ff53 = 0; // computed VCF LFO
uint16_t ff56 = 0; // LFO Delay envelope
uint16_t ff5a = 0; // LFO Delay holdoff
uint8_t ff64 = 0; // LFO mod sens amount
uint16_t ff65 = 0; // computed VCF bend amount
uint8_t ff6a = 0;
uint8_t ff6b = 0;
uint8_t ff6e = 0; // fractional pitch temp
uint16_t ff6f = 0; // computed pitch amount
// uint16_t ff71 = 0; // unsure, to do with pitch
// 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
@ -122,26 +108,32 @@ class Synth {
// the horrifying implication here is that we can just shunt this
// around and memcpy() values from sysex on top of it
// this is set to a "sensible" patch
// the comments indicate what the defaults are set to
// in the routine at 02c2h, in case they're important
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;
uint8_t lfoRate = 0x30; // lookup value defaults to 0x0200
uint8_t lfoDelay = 0x00;
uint8_t vcoLfo = 0x0a;
uint8_t pwmLfo = 0x30;
uint8_t noise = 0x00;
uint8_t vcfFreq = 0x3c; // 0x3f80
uint8_t vcfReso = 0x00;
uint8_t vcfEnv = 0x2e;
uint8_t vcfLfo = 0;
uint8_t vcfKey = 0x47;
uint8_t vca = 0x28;
uint8_t env_a = 0x1b;
uint8_t env_d = 0x39;
uint8_t env_s = 0x39; // 0x3f80
uint8_t env_r = 0x30;
uint8_t sub = 0x00;
uint8_t switch1 = 0x1a;
uint8_t switch2 = 0x18;
} patchRam;
float pitchCV[104];
void runLFO();
void lfoDelay();
};

631
plugin/voicecpu.cpp Normal file
View File

@ -0,0 +1,631 @@
/*
Chassis polysynth framework
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.
*/
// contains the emulation of the digital bits
// what's with the crazy private variables and all the gotos with crazy labels?
// this code emulates the uPD7811 code directly (probably inefficiently)
// to allow for documenting what the variables actually do
// they're really bitfields holding a bit for each voice
#include "voicecpu.hpp"
#include <cmath>
#include <cstdio>
#include "voice.hpp"
using namespace Digital;
bool Voice::isFree() {
return ff10 == false;
}
void Voice::on(uint32_t key, bool reset = 0) {
// this current implementation doesn't reset the voice
(void)reset;
// printf("called with key=%d\n", note);
ff10 = true; // note held from keyboard
ff07 = true; // attack phase
if (note == key) goto h0144;
note = key;
if (ff11) goto h013e;
h0132:
if (ff33) goto h0149; // sustained
ff33 = false;
goto h0149;
h013e:
ff00 = true; // in a real one, voice counter needs programmed
goto h0149;
h0144:
if (!ff11) goto h0132; // unsure, copied from ff10 at start of mainloop
h0149:
// 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;
// printf("note, key = %d, %d\n", note, key);
return;
}
void Voice::off() {
bool sustain = false;
ff10 = false;
if (!sustain) { // dummy sustain
ff33 = false;
}
}
void Synth::lfoDelay() {
// compute LFO delay
uint16_t a, bc, d, ea, tos;
// 030d: 45 11 3f ONIW $0011,$3F ; are any notes enabled
// 0310: 4e 59 JRE $036B ; no, just run LFO
if (!keyon /* ff11 */) goto h036b; // skip ahead if no notes are pressed
// 0312: 5b 1e BIT 3,$001E ; ramp-up is complete?
// 0314: ce JR $0323 ; no
if (!(ff1e & 0x08)) goto h0323;
// no not was playing so reset
bc = 0;
ff56 = 0; // delay envelope
ff5a = 0; // holdoff timer
ff1e &= 0xf1; // mask bits in flag byte
h0323:
if (!(ff1e & 0x02)) goto h0370; // compute delay envelope
if (!(ff1e & 0x04)) goto h0388; // compute holdoff timer
// 032b
bc |= 0xff00; // initial scaling value?
h032d:
tos = bc; // push bc
// 032e
// 032e: 01 49 LDAW $0049 ; DCO LFO depth
a = lfoDepthTable[patchRam.vcoLfo];
// MUL B; MOV A, EAH
a = (a * (bc >> 8)) >> 8;
// add in the modwheel amount, clamp if it exceeds 0xff
// 0333 ADDNCW $0064; MVI A, $FF
a += ff64;
if (a > 0xff) a = 0xff;
// 0338 sets up HL to store computed pitch LFO output
// 33b
bc = ff4d; // current LFO output
// 033f
ea = (bc & 0xff) * a; // MUL C
d = a; // MOV D,A
a = (ea >> 8); // MOV A,EAH
bc &= 0xff00;
bc |= a; // MOV C,A
a = d; // MOV A,D
// 0345
ea = (bc >> 8) * a; // MUL B
ea += (bc & 0xff); // EADD EA, C
// 0349
ea >>= 3; // divide by eight
// 034f
ff51 = ea; // save scaled pitch LFO
bc = tos; // pop BC, contains scaling amount
// 0352: 01 48 LDAW $0048 ; VCF LFO amount
a = patchRam.vcfLfo << 1; // amount is doubled and stored at ff48
// 0354
ea = (bc >> 8) * a; // MUL B
a = ea >> 8; // MOV A, EAH
bc = ff4d; // current LFO output
// 035b
ea = (bc & 0xff) * a; // MUL C
d = a; // MOV D,A
a = ea >> 8; // MOV A, EAH
bc &= 0xff00;
bc |= a; // MOV C,A
a = d; // MOV A,D
ea = (bc >> 8) * a; // MUL B
ea += (bc & 0xff); // EADD EA,C
ea >>= 1; // DSLR A
ff53 = ea; // save scaled VCF LFO
goto h03a1;
h036b:
ff1e |= 0x08; // set LFO flag
goto h0323;
h0370: // calculate holdoff time
ea = ff56; // holdoff time
// 0375: 70 1f 58 ff LBCD $FF58 ; add on delay amount
bc = attackTable[patchRam.lfoDelay]; // stored at ff58
// 0379
ea += bc; // DADD EA,BC
ff56 = ea; // STEAX (DE) which still holds ff56 from 0x370
a = ea >> 8; // MOV A, EAH
if (a & 0xc0) goto h0385; // OFFI A, $C0
bc &= 0xff; // MOV B, 0
goto h032d;
h0385:
ff1e |= 0x02; // stop predelay flag
h0388:
ea = ff5a; // envelope speed
// 038d: 70 1f 6c ff LBCD $FF6C ; value from "short" LFO lookup table
bc = lfoDelayTable[patchRam.lfoDelay >> 4]; // delay setting divided by 8 and saved at ff6c
// 0391 DADDNC EA, BC
if ((ea + bc) > 0xffff) goto h039a;
ea += bc;
// 0394
ff5a = ea; // STEAX (HL) hl still contains ff5a
bc |= (ea & 0xff00); // MOV A, EAH; MOV B, A
goto h032d;
h039a:
ff1e |= 0x04;
bc |= 0xff; // MVI B, $ff
goto h032d;
h03a1:
return;
}
void Voice::calcPitch(Synth &s) {
uint32_t bc, ea, a;
// 03ad
ea = 0x1818;
// add in tuning value from ff61, not implemented
// 03ba
// 03ba: 70 1f 51 ff LBCD $FF51 ; computed pitch LFO?
bc = s.ff51; // computed pitch LFO
if (s.ff4a & 0x02) {
ea -= bc;
} else {
ea += bc;
}
// 03c6
// add in bender from ff68
// 03d2: 24 6f ff LXI DE,$FF6F
// 03d5: 48 92 STEAX (DE) ; save final value
s.ff6f = ea;
// these are set, because in the uPD7811 code it loops around all six voices
// 03d7: 71 0f 00 MVIW $000F,$00 ; voice counter
// 03da: 24 71 ff LXI DE,$FF71 ; DAC pitch table for voices
// 03dd: 34 09 ff LXI HL,$FF09 ; note pitch table for voices
// 03e0: 48 82 LDEAX (DE) ; fetch
// 03e2: 2d LDAX (HL+) ; fetch note
ea = ff71;
a = note;
// 03e3 MOV B,A
bc = a << 8;
// 03e6: 01 7d LDAW $007D ; porta coefficient
a = 0x00; // set from porta coefficient ff7d
if (a != 0) goto h03f5;
// 3eb
ea = bc;
h03ec:
ff71 = ea; // STEAX (DE++)
// if we were handling all voices in this loop we'd do
// 03ee: 20 0f INRW $000F ; voice counter
// 03f0: 75 0f 06 EQIW $000F,$06 ; loop
// 03f3: ec JR $03E0 ; loop around note
// 03f4: d2 JR $0407 ; jump ahead
goto h0407;
h03f5:
// portamento down
if (ea == bc) goto h03ec; // DNE EA, BC; JR 03EC store value
if (!(ea > bc)) goto h0401; // DGT EA, BC; JR 0401
ea -= a;
if (!(ea > bc)) ea = bc;
goto h03ec;
h0401:
// portamento up
ea += a;
if (!(ea < bc)) ea = bc;
goto h03ec;
h0407:
// bit of code that outputs sub osc CV
// 0413: 71 0f 00 MVIW $000F,$00 ; reset voice counter
// 0416: 71 34 01 MVIW $0034,$01 ; voice selector, first voice
// 0419
ea = ff71; // pitch + fraction per voice
bc = s.ff6f; // tune + lfo + bend
ea += bc;
// 0424
s.ff6e = ea & 0xff; // MOV A, EAL; STAW 006e
a = ea >> 8; // mov a, EAH
// 0428
if (a <= 0x2f) goto h04a5; // GTI A,$2F; JRE $04A5
if (a >= 0x97) goto h04ac; // LTI A,$97; JRE $04AC
a -= 0x30;
h0432:
// printf("setting omega for note %d \n", a);
omega = ((s.pitchCV[a + 1] - s.pitchCV[a]) * (s.ff6e / 256.0)) + s.pitchCV[a];
// 0432 onwards calculates the address for the CV
// table at E60 and stacks it
// 043a onwards fetches the value from the divider
// table and computes a linear interpolation with the next one up
// using the fractional value stored in ff6e
// 045a onwards decides which divider to program
// 0471 unstacks the CV table address and calculates a linear
// interpolation of this and the next CV value using ff6e
// 048b onwards sends it to the correct DAC
// 0496 onwards works out which voice to do next and loops
// 04a3: 4e 30 JRE $04D5 ; calculate filter
return;
h04a5: // pitch too low
s.ff6e = 0;
a = 0;
goto h0432;
h04ac: // pitch too high
s.ff6e = 0;
a = 0x66;
goto h0432;
// 04b3 programs the dividers somehow
}
void Voice::calcFilter(Synth &s) {
// 04d5
uint16_t a, bc, ea, tos;
goto h04d5;
h04d5:
s.ff6a = 0;
// 04d8: 70 1f 3d ff LBCD $FF3D ; VCF cutoff
// 04dc: a5 DMOV EA,BC
ea = s.patchRam.vcfFreq << 7; // stored exended to two bytes
bc = s.ff53; // scaled VCF LFO
// 04e1: 59 4a BIT 1,$004A ; LFO add/sub flag
// 04e3: e7 JR $04CB
// 04e4: 74 b5 DSUBNB EA,BC ; add, skip if no borrow
// 04e6: 71 6a 01 MVIW $006A,$01 ; set a flag?
if (!(s.ff4a & 0x02)) {
ea += bc;
} else {
if ((ea - bc) < bc) {
s.ff6a = 1;
}
ea -= bc;
}
bc = s.ff65;
if (!(s.ff1e & 0x20)) {
if ((ea + bc) > 0xffff) {
s.ff6a = 0;
}
ea += bc;
} else {
if ((ea - bc) < bc) {
s.ff6a = 1;
}
ea -= bc;
}
// 04f6
tos = ea;
// 04f7: 71 0f 00 MVIW $000F,$00 ; voice counter
// 04fa: 71 34 01 MVIW $0034,$01 ; voice enable bit
// 04fd: 34 71 ff LXI HL,$FF71 ; pitch + fraction table
// 0500: 24 25 ff LXI DE,$FF25 ; release time
// 0503: b3 PUSH HL ; TOS = address of DAC note, then VCF Bias
// 0504
ea = ff27; // current envelope level
// in the real ROM the envelope code would run here
// there's something about the first voice, I'd need to emulate that a bit more
// 05a6 sets the DAC depending on the state of the ENV/GATE switch
// 05c0: a3 POP HL ; HL might have started as FF71
// hl = ff71; // this was stored on the stack back at 0503
a = s.ff6a;
s.ff6b = a;
ea = ff27;
bc = ea;
// 05c8: 01 41 LDAW $0041 ; VCF ENV MOD
a = s.patchRam.vcfEnv << 1; // stored doubled
// 05c8
ea = (bc & 0xff) * a; // MUL C
a = ea >> 8; // MOV A, EAH
bc &= 0xff00;
bc |= a; // MOV C,A
a = s.patchRam.vcfEnv << 1; // stored doubled
ea = (bc >> 8) * a; // MUL B
ea += (bc & 0xff); // EADD EA,C
bc = ea;
ea = tos; // precomputed VCF knob + LFO + bend
if (!(s.patchRam.switch2 & 0x02)) {
if ((ea + bc) > 0xffff) {
s.ff6b = 0;
}
ea += bc;
} else {
if ((ea - bc) < bc) {
s.ff6b = 1;
}
ea -= bc;
}
// 05e0
tos = ea; // save
ea = ff71; // pitch value
ea >>= 2;
bc = ea;
ea >>= 1;
ea += bc; // multiplied by 0.375
bc = 0x1680; // this is 0x3c00 * 0.375, middle C * 0.375
if (ea <= bc) goto h0620;
// 05f3
ea -= bc;
bc = ea;
// 05f6
a = s.patchRam.vcfKey << 1; // stored doubled at ff42
ea = (bc & 0xff) * a; // MUL C
a = ea >> 8; // MOV A, EAH
bc &= 0xff00;
bc |= a; // MOV C,A
a = s.patchRam.vcfKey << 1; // needs lookup table
ea = (bc >> 8) * a; // MUL B
ea += (bc & 0xff); // EADD EA,C
bc = ea;
// 0603
ea = tos; // get saved VCF back
// 0604: 74 a5 DADDNC EA,BC
// 0606: 71 6b 00 MVIW $006B,$00
if ((ea + bc) > 0xffff) {
s.ff6b = 0;
}
ea += bc;
h0609:
if (!(ea & 0xc000)) goto h063c;
ea = 0;
// 0611
if (!(s.ff6b & 0x01))
ea = 0x3fff;
goto h063c;
h0620:
bc = ea;
ea = 0x1680;
ea -= bc;
bc = ea;
// 0627
a = s.patchRam.vcfKey << 1; // stored doubled at ff42
ea = (bc & 0xff) * a; // MUL C
a = ea >> 8; // MOV A, EAH
bc &= 0xff00;
bc |= a; // MOV C,A
a = s.patchRam.vcfKey << 1; // needs lookup table
ea = (bc >> 8) * a; // MUL B
ea += (bc & 0xff); // EADD EA,C
bc = ea;
// 0634
ea = tos;
if ((ea - bc) < bc) {
s.ff6b = 1;
}
ea -= bc;
goto h0609;
h063c:
vcfenv = ea;
return;
}
void Voice::envelope(Synth &s) {
uint16_t bc, ea = env;
ff11 = ff10;
// 0509
if (!ff11) goto h0538;
// 050e
if (!ff33) goto h0563;
// 0513
if (!ff07) goto h051e;
h0517:
ff07 = false;
h051e:
bc = s.patchRam.env_s << 7; // half scale
if (ea < bc) ea = bc;
ea -= bc;
bc = ea;
ea = (ea * decayTable[s.patchRam.env_d]) >> 16;
ea += s.patchRam.env_s << 7;
// printf("returning from decay phase\n");
goto h0590;
h0538:
// printf("got to 0x0538\n");
if (!ff07) goto h054a; // note on? if not skip ahead
// 053c
if (ff08) goto h0517;
ff07 = false;
h054a:
// printf("release phase\n");
ff33 = false;
ff08 = false;
bc = ea;
ea = (ea * decayTable[s.patchRam.env_r]) >> 16;
// printf("returning from release phase\n");
goto h0590;
h0563:
// printf("attack phase\n");
ff08 = false;
ea += attackTable[s.patchRam.env_a];
if (ea & 0xc000) {
ea = 0x3fff;
ff33 = true;
ff08 = true;
}
h0590:
env = ea;
ff27 = ea;
// printf("%04x %d %d %d %d %d \n", ea, ff07, ff08, ff10, ff11, ff33);
}
void Synth::runLFO() {
// compute a loop's worth of LFO
uint16_t bc, ea;
// 074e
ea = ff4d; // lfo value
bc = lfoRateTable[patchRam.lfoRate];
// bit zero is low for rising slope, high for falling
if (!(ff4a & 0x01)) goto h078b;
// 075b DSUBNB EA, BC subtract BC from EA, skip next instruction if EA < BC
// 075d JRE 079a routine that handles flipping from down to up
ea -= bc;
if (ea < bc) goto h079a;
h075f:
ff4d = ea; // LFO output variable
// bit one seems to be used to represent negative values of LFO
if (!(ff4a & 0x02)) goto h07a2; // routine that adds on 0x2000 to ea
// 0765 LFO is negative (bit 1 is high) so invert the value of EA
// so that we have a positive-only LFO running from 0 to 0x3fff
bc = ea;
ea = 0x2000;
ea -= bc;
h076b:
bc = ea; // BC now contains an LFO range from 0 to 0x3fff, always positive
if (patchRam.switch2 & 0x01) { // LFO Manual?
bc = 0x3fff; // fixed maximum value
}
// 0771
bc = (bc * patchRam.pwmLfo) >> 7; // scale by PWM pot amount
// 077d
bc = 0x3fff - bc; // invert so pot = 0 gives 0x3fff
// test if squarewave is on or off - if it's off set PW to 0
if (!(patchRam.switch1 & 0x08)) bc = 0x0000; // square off
// final computed PWM value
ff4f = bc;
// 078a
goto h07a9;
h078b:
// BC contains rate, EA contains LFO value
ea += bc;
if (ea & 0xe000) { // if we've exceeded 0x1fff
ea = 0x1fff; // clamp
ff4a++; // increment the flags
}
goto h075f; // store in LFO output variable
h079a:
ea = 0; // output is close (enough) to zero, clamp
ff4a++; // increment the flags
goto h075f; // store in LFO output variable
h07a2: // LFO output is positive
ea += 0x2000; // add on 0x2000 to scale PWM to 0 - 0x3fff
goto h076b; // jump back to scale LFO amount
h07a9:
return;
}

91
plugin/voicecpu.hpp Normal file
View File

@ -0,0 +1,91 @@
/*
Chassis polysynth framework
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.
*/
#pragma once
#include <cstdint>
namespace Digital {
uint16_t attackTable[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 decayTable[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};
uint8_t lfoDepthTable[128] = {
0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21,
0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c,
0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x62, 0x64,
0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c,
0x80, 0x84, 0x88, 0x8c, 0x90, 0x94, 0x98, 0x9c, 0xa0, 0xa4, 0xa8, 0xac,
0xb0, 0xb4, 0xb8, 0xbc, 0xc0, 0xc4, 0xc8, 0xcc, 0xd0, 0xd4, 0xd8, 0xdc,
0xe0, 0xe4, 0xe8, 0xec, 0xf0, 0xf8, 0xff, 0xff};
uint16_t lfoRateTable[128] = {
0x0005, 0x000f, 0x0019, 0x0028, 0x0037, 0x0046, 0x0050, 0x005a, 0x0064,
0x006e, 0x0078, 0x0082, 0x008c, 0x0096, 0x00a0, 0x00aa, 0x00b4, 0x00be,
0x00c8, 0x00d2, 0x00dc, 0x00e6, 0x00f0, 0x00fa, 0x0104, 0x010e, 0x0118,
0x0122, 0x012c, 0x0136, 0x0140, 0x014a, 0x0154, 0x015e, 0x0168, 0x0172,
0x017c, 0x0186, 0x0190, 0x019a, 0x01a4, 0x01ae, 0x01b8, 0x01c2, 0x01cc,
0x01d6, 0x01e0, 0x01ea, 0x01f4, 0x01fe, 0x0208, 0x0212, 0x021c, 0x0226,
0x0230, 0x023a, 0x0244, 0x024e, 0x0258, 0x0262, 0x026c, 0x0276, 0x0280,
0x028a, 0x029a, 0x02aa, 0x02ba, 0x02ca, 0x02da, 0x02ea, 0x02fa, 0x030a,
0x031a, 0x032a, 0x033a, 0x034a, 0x035a, 0x036a, 0x037a, 0x038a, 0x039a,
0x03aa, 0x03ba, 0x03ca, 0x03da, 0x03ea, 0x03fa, 0x040a, 0x041a, 0x042a,
0x043a, 0x044a, 0x045a, 0x046a, 0x047a, 0x048a, 0x04be, 0x04f2, 0x0526,
0x055a, 0x058e, 0x05c2, 0x05f6, 0x062c, 0x0672, 0x06b8, 0x0708, 0x0758,
0x07a8, 0x07f8, 0x085c, 0x08c0, 0x0924, 0x0988, 0x09ec, 0x0a50, 0x0ab4,
0x0b18, 0x0b7c, 0x0be0, 0x0c58, 0x0cd0, 0x0d48, 0x0dde, 0x0e74, 0x0f0a,
0x0fa0, 0x1000};
uint16_t lfoDelayTable[8] = {
0xffff, 0x0419, 0x020c, 0x015e, 0x0100, 0x0100, 0x0100, 0x0100};
}; // namespace Digital