LFO delay code

This commit is contained in:
Gordon JC Pearce 2024-09-09 11:34:13 +01:00
parent 6bf1cc1da1
commit 88ca7a5037
3 changed files with 181 additions and 21 deletions

View File

@ -25,7 +25,6 @@ Chassis::Chassis() : Plugin(parameterCount, 0, 0), sampleRate(getSampleRate()) {
// Initialisation functions // Initialisation functions
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);
@ -49,6 +48,7 @@ void Chassis::deactivate() {
void Chassis::noteOn(uint8_t note) { void Chassis::noteOn(uint8_t note) {
uint32_t i; uint32_t i;
s.keyon = true;
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;
@ -70,6 +70,7 @@ void Chassis::noteOn(uint8_t note) {
void Chassis::noteOff(uint8_t note) { void Chassis::noteOff(uint8_t note) {
// printf("noteoff %d\n", note); // printf("noteoff %d\n", note);
s.keyon = false;
for (uint32_t i = 0; i < NUM_VOICES; i++) { for (uint32_t i = 0; i < NUM_VOICES; i++) {
if (s.voice[i].note == note && !s.voice[i].isFree()) { if (s.voice[i].note == note && !s.voice[i].isFree()) {
s.voice[i].off(); s.voice[i].off();
@ -121,6 +122,7 @@ void Chassis::run(const float **, float **outputs, uint32_t frames, const MidiEv
doMidi(midiEvents, midiEventCount, framePos + s.blockLeft); doMidi(midiEvents, midiEventCount, framePos + s.blockLeft);
// printf("compute params and reset block size\n"); // printf("compute params and reset block size\n");
s.lfoDelay();
s.runLFO(); s.runLFO();
for (uint32_t i = 0; i < NUM_VOICES; i++) { for (uint32_t i = 0; i < NUM_VOICES; i++) {

View File

@ -128,6 +128,137 @@ h0590:
// 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);
} }
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() { void Synth::runLFO() {
// compute a loop's worth of LFO // compute a loop's worth of LFO

View File

@ -26,8 +26,7 @@ class Synth;
class Voice { class Voice {
public: public:
// uint8_t note = 72; uint8_t note = 60; // per-voice note, set to middle C at 02b1h
uint8_t note = 60;
void on(uint32_t key, bool reset); void on(uint32_t key, bool reset);
@ -107,15 +106,19 @@ class Synth {
uint32_t blockLeft; uint32_t blockLeft;
uint32_t framesLeft = 0; uint32_t framesLeft = 0;
uint8_t ff1e; bool keyon;
uint8_t ff4a; // LFO flags
// 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 ff4d = 0; // LFO output value
uint16_t ff4f; // computed PWM LFO uint16_t ff4f = 0; // computed PWM LFO
uint16_t ff51; // computed pitch LFO uint16_t ff51 = 0; // computed pitch LFO
uint16_t ff53; // computed VCF LFO uint16_t ff53 = 0; // computed VCF LFO
uint16_t ff56; // LFO Delay envelope uint16_t ff56 = 0; // LFO Delay envelope
uint16_t ff5a; // LFO Delay holdoff uint16_t ff5a = 0; // LFO Delay holdoff
uint8_t ff64; // LFO mod sens amount uint8_t ff64 = 0; // LFO mod sens amount
// okay, not the greatest, this right here // okay, not the greatest, this right here
// this struct contains the bytes that make up a Juno 106 patch in // 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 // the horrifying implication here is that we can just shunt this
// around and memcpy() values from sysex on top of it // 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 { struct {
uint8_t lfoRate = 57; uint8_t lfoRate = 57; // lookup value defaults to 0x0200
uint8_t lfoDelay = 45; uint8_t lfoDelay = 45;
uint8_t vcoLfo = 0; uint8_t vcoLfo = 0;
uint8_t pwmLfo = 55; uint8_t pwmLfo = 55;
uint8_t noise = 0; uint8_t noise = 0;
uint8_t vcfFreq = 85; uint8_t vcfFreq = 85; // 0x3f80
uint8_t vcfReso = 0; uint8_t vcfReso = 0;
uint8_t vcfEnv = 0; uint8_t vcfEnv = 0;
uint8_t vcfLfo = 0; uint8_t vcfLfo = 0;
@ -138,7 +144,7 @@ class Synth {
uint8_t vca = 52; uint8_t vca = 52;
uint8_t env_a = 59; uint8_t env_a = 59;
uint8_t env_d = 32; uint8_t env_d = 32;
uint8_t env_s = 86; uint8_t env_s = 86; // 0x3f80
uint8_t env_r = 40; uint8_t env_r = 40;
uint8_t sub = 0; uint8_t sub = 0;
uint8_t switch1 = 26; uint8_t switch1 = 26;
@ -174,5 +180,26 @@ class Synth {
0x0b18, 0x0b7c, 0x0be0, 0x0c58, 0x0cd0, 0x0d48, 0x0dde, 0x0e74, 0x0f0a, 0x0b18, 0x0b7c, 0x0be0, 0x0c58, 0x0cd0, 0x0d48, 0x0dde, 0x0e74, 0x0f0a,
0x0fa0, 0x1000}; 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 runLFO();
void lfoDelay();
}; };