diff --git a/plugin/chassis.cpp b/plugin/chassis.cpp index 9b206bb..cfac943 100644 --- a/plugin/chassis.cpp +++ b/plugin/chassis.cpp @@ -25,7 +25,6 @@ Chassis::Chassis() : Plugin(parameterCount, 0, 0), sampleRate(getSampleRate()) { // Initialisation functions - void Chassis::initAudioPort(bool input, uint32_t index, AudioPort &port) { port.groupId = kPortGroupStereo; Plugin::initAudioPort(input, index, port); @@ -49,6 +48,7 @@ void Chassis::deactivate() { 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; @@ -70,6 +70,7 @@ 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(); @@ -121,7 +122,8 @@ void Chassis::run(const float **, float **outputs, uint32_t frames, const MidiEv doMidi(midiEvents, midiEventCount, framePos + s.blockLeft); // printf("compute params and reset block size\n"); - s.runLFO(); + s.lfoDelay(); + s.runLFO(); for (uint32_t i = 0; i < NUM_VOICES; i++) { s.voice[i].gate(s); diff --git a/plugin/digital.cpp b/plugin/digital.cpp index 8fb5486..c257299 100644 --- a/plugin/digital.cpp +++ b/plugin/digital.cpp @@ -128,6 +128,137 @@ h0590: // printf("%04x %d %d %d %d %d \n", ea, ff07, ff08, ff10, ff11, ff33); } +void Synth::lfoDelay() { + // compute LFO delay + + uint16_t a, bc, d, de, ea, tos; + + // 030d + if (!keyon /* ff11 */) goto h036b; // skip ahead if no notes are pressed + + // 0312 + if (!(ff1e & 0x08)) goto h0323; // a note is running, don't reset + + // 0315 no note is running, reset it all + 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? + + //printf("0323 "); + +h032d: + tos = bc; // push bc + + // 032e + a = lfoDepthTable[patchRam.vcoLfo]; + + // MUL B; MOV A, EAH + a = (a * (bc >> 8)) >> 8; + + // 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 + + // printf("-----------------------%02x %04x\n", a, bc); + + // 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 + 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 + //printf("0370 "); + ea = ff56; // holdoff time + 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: + //printf("0388 "); + ea = ff5a; // envelope speed + + // 038d + bc = lfoDelayTable[patchRam.lfoDelay >> 4]; // delay setting divided by 8 and saved at ff6c + + //printf("---------------------------------------- %04x %04x\n", ea, bc); + + // 0391 DADDNC EA, BC + if ((ea + bc) > 0xffff) goto h039a; + ea += bc; + + // 394 + 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: + //printf("LFO=%04x VCF=%04x flags=%02x holdoff=%04x envelope=%04x\n", ff51, ff53, ff1e, ff56, ff5a); + return; +} + void Synth::runLFO() { // compute a loop's worth of LFO @@ -187,13 +318,13 @@ h078b: 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 + 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 +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; diff --git a/plugin/voice.hpp b/plugin/voice.hpp index fb119dc..9af2a9d 100644 --- a/plugin/voice.hpp +++ b/plugin/voice.hpp @@ -26,8 +26,7 @@ class Synth; 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); @@ -107,15 +106,19 @@ class Synth { uint32_t blockLeft; uint32_t framesLeft = 0; - uint8_t ff1e; - uint8_t ff4a; // LFO flags + bool keyon; + + // RAM from ff00h to ffffh is cleared to zero + // this is in the startup routine at 0280h + uint8_t ff1e = 0; + uint8_t ff4a = 0; // LFO flags uint16_t ff4d = 0; // LFO output value - uint16_t ff4f; // computed PWM LFO - uint16_t ff51; // computed pitch LFO - uint16_t ff53; // computed VCF LFO - uint16_t ff56; // LFO Delay envelope - uint16_t ff5a; // LFO Delay holdoff - uint8_t ff64; // LFO mod sens amount + 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 // okay, not the greatest, this right here // this struct contains the bytes that make up a Juno 106 patch in @@ -124,13 +127,16 @@ 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 lfoRate = 57; + uint8_t lfoRate = 57; // lookup value defaults to 0x0200 uint8_t lfoDelay = 45; uint8_t vcoLfo = 0; uint8_t pwmLfo = 55; uint8_t noise = 0; - uint8_t vcfFreq = 85; + uint8_t vcfFreq = 85; // 0x3f80 uint8_t vcfReso = 0; uint8_t vcfEnv = 0; uint8_t vcfLfo = 0; @@ -138,7 +144,7 @@ class Synth { uint8_t vca = 52; uint8_t env_a = 59; uint8_t env_d = 32; - uint8_t env_s = 86; + uint8_t env_s = 86; // 0x3f80 uint8_t env_r = 40; uint8_t sub = 0; uint8_t switch1 = 26; @@ -174,5 +180,26 @@ class Synth { 0x0b18, 0x0b7c, 0x0be0, 0x0c58, 0x0cd0, 0x0d48, 0x0dde, 0x0e74, 0x0f0a, 0x0fa0, 0x1000}; + 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 lfoDelayTable[8] = { + 0xffff, 0x0419, 0x020c, 0x015e, 0x0100, 0x0100, 0x0100, 0x0100}; + void runLFO(); + void lfoDelay(); };