From af49dcf7b6c3c0b1888b2224ef77b9c04447f001 Mon Sep 17 00:00:00 2001 From: Gordon JC Pearce Date: Mon, 9 Sep 2024 13:34:02 +0100 Subject: [PATCH] pitch computation --- plugin/chassis.cpp | 13 ++++ plugin/digital.cpp | 147 ++++++++++++++++++++++++++++++++++++++++----- plugin/voice.cpp | 6 +- plugin/voice.hpp | 16 +++-- 4 files changed, 160 insertions(+), 22 deletions(-) diff --git a/plugin/chassis.cpp b/plugin/chassis.cpp index cfac943..e423176 100644 --- a/plugin/chassis.cpp +++ b/plugin/chassis.cpp @@ -38,6 +38,10 @@ void Chassis::initAudioPort(bool input, uint32_t index, AudioPort &port) { void Chassis::activate() { // calculate filter coefficients and stuff printf("called activate()\n"); + + for (uint8_t i=0; i<104; i++) { + s.pitchCV[i] = (261.63 * powf(2, (i-24) / 12.0f)) / sampleRate; + } } void Chassis::deactivate() { @@ -126,6 +130,15 @@ void Chassis::run(const float **, float **outputs, uint32_t frames, const MidiEv s.runLFO(); for (uint32_t i = 0; i < NUM_VOICES; i++) { + s.voice[i].calcPitch(s); + + switch(s.patchRam.switch1 & 0x03) { + case 1: s.voice[i].omega /= 4; break; + case 2: s.voice[i].omega /= 2; + default: break; + } + + //printf("voice %d note = %02x ff71 = %04x\n",i, s.voice[i].note, s.voice[i].ff71 ); s.voice[i].gate(s); } } diff --git a/plugin/digital.cpp b/plugin/digital.cpp index c257299..bd6dee8 100644 --- a/plugin/digital.cpp +++ b/plugin/digital.cpp @@ -28,6 +28,119 @@ bool Voice::isFree() { return ff10 == false; } +void Voice::calcPitch(Synth &s) { + uint32_t bc, de, ea, a; + + // 03ad + ea = 0x1818; + + // add in tuning value from ff61, not implemented + + // 03ba + bc = s.ff51; // computed pitch LFO + if (s.ff4a & 0x02) { + ea -= bc; + } else { + ea += bc; + } + + // 03c6 + // add in bender from ff68 + + // 03d2 + s.ff6f = ea; + + ea = ff71; + a = note; + + // 03e3 + bc = a << 8; + + // 03e6 + a = 0; // set from porta coefficient ff7d + + if (a != 0) goto h03f5; + + // 3eb + ea = bc; +h03ec: + ff71 = ea; // STEAX (DE++) + + // we're not looping so we can ignore until + // 03ee inrw ff0f voice counter + // 03f0 eqiw ff0f, 06 + // 03f3 jr 03e0 + + goto h0407; + +h03f5: + 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: + ea += a; + if (!(ea < bc)) ea = bc; + goto h03ec; + +h0407: + // this outputs the Sub Osc CV + // ignore until + // 0413 mviw ff0f, 0 reset voice counter + s.ff34 = 1; // unsure what this is used for + + // 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 vocie to do next and loops + + // 04a3 + goto h04d5; + +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 + // ignore until 04d5 + +h04d5: + return; +} + void Voice::on(uint32_t key, bool reset = 0) { // what's with the crazy private variables and all the gotos with crazy labels? // this code emulates the 78C11 code directly (probably inefficiently) @@ -37,6 +150,8 @@ 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; @@ -57,7 +172,9 @@ h0144: 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; + // omega = (261.63 * powf(2, (note - 60) / 12.0f)) / 48000.0f; + // printf("note, key = %d, %d\n", note, key); + return; } void Voice::off() { @@ -152,7 +269,7 @@ h0323: // 032b bc |= 0xff00; // initial scaling value? - //printf("0323 "); + // printf("0323 "); h032d: tos = bc; // push bc @@ -178,8 +295,9 @@ h032d: 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 + bc &= 0xff00; + bc |= a; // MOV C,A + a = d; // MOV A,D // 0345 ea = (bc >> 8) * a; // MUL B @@ -203,12 +321,13 @@ h032d: 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 + 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: @@ -216,7 +335,7 @@ h036b: goto h0323; h0370: // calculate holdoff time - //printf("0370 "); + // printf("0370 "); ea = ff56; // holdoff time bc = attackTable[patchRam.lfoDelay]; // stored at ff58 // 0379 @@ -231,13 +350,13 @@ h0385: ff1e |= 0x02; // stop predelay flag h0388: - //printf("0388 "); + // 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); + // printf("---------------------------------------- %04x %04x\n", ea, bc); // 0391 DADDNC EA, BC if ((ea + bc) > 0xffff) goto h039a; @@ -255,7 +374,7 @@ h039a: goto h032d; h03a1: - //printf("LFO=%04x VCF=%04x flags=%02x holdoff=%04x envelope=%04x\n", ff51, ff53, ff1e, ff56, ff5a); + // printf("LFO=%04x VCF=%04x flags=%02x holdoff=%04x envelope=%04x\n", ff51, ff53, ff1e, ff56, ff5a); return; } diff --git a/plugin/voice.cpp b/plugin/voice.cpp index f9abc4d..63da6e9 100644 --- a/plugin/voice.cpp +++ b/plugin/voice.cpp @@ -19,10 +19,11 @@ // contains the actual sound generation code #include "voice.hpp" -#include #include +#include + static inline float poly3blep0(float t) { float t2 = t * t; return 2 * (t * t2 - 0.5f * t2 * t2); @@ -40,8 +41,7 @@ void Voice::run(Synth &s, float *buffer, uint32_t samples) { // there's a resistor on the panel board to sprag the range float pw = s.ff4f / 32768.0f; - - float sqr = 0.175; + float sqr = 0.175; float saw = (s.patchRam.switch1 & 0x10) ? 0.220 : 0; float sub = (s.patchRam.sub / 127.0f) * 0.275; diff --git a/plugin/voice.hpp b/plugin/voice.hpp index 9af2a9d..cca9bc8 100644 --- a/plugin/voice.hpp +++ b/plugin/voice.hpp @@ -29,14 +29,14 @@ class Voice { 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 gate(Synth &s); + void calcPitch(Synth &s); + uint16_t ff71 = 0; // stores pitch + fraction + + float omega; private: enum { ATTACK, @@ -57,7 +57,7 @@ class Voice { 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; @@ -111,6 +111,7 @@ class Synth { // 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 @@ -119,6 +120,9 @@ class Synth { uint16_t ff56 = 0; // LFO Delay envelope uint16_t ff5a = 0; // LFO Delay holdoff uint8_t ff64 = 0; // LFO mod sens amount + 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 @@ -200,6 +204,8 @@ class Synth { uint16_t lfoDelayTable[8] = { 0xffff, 0x0419, 0x020c, 0x015e, 0x0100, 0x0100, 0x0100, 0x0100}; + float pitchCV[104]; + void runLFO(); void lfoDelay(); };