From 6a00521656723c29e94dfc46f1d5ab6d8b4b1a76 Mon Sep 17 00:00:00 2001 From: Gordon JC Pearce Date: Fri, 19 Dec 2025 15:06:02 +0000 Subject: [PATCH] adjustable filter envelope and vca gate --- plugin/module.cpp | 42 +++++++++++++++++++++++++++++++----------- plugin/module.hpp | 23 +++++++++++++++++------ plugin/voice.cpp | 21 ++++++++++++--------- 3 files changed, 60 insertions(+), 26 deletions(-) diff --git a/plugin/module.cpp b/plugin/module.cpp index 6e82cda..4394546 100644 --- a/plugin/module.cpp +++ b/plugin/module.cpp @@ -18,6 +18,8 @@ #include "module.hpp" +#include + #include "tables.hpp" Module::Module() { @@ -26,32 +28,50 @@ Module::Module() { void Module::run(Voice* voice) { // run updates for module board -// FIXME break these out to the patch setter - a = attackTable[patchRam.env_a]; // attack time coeff looked up in table - d = decayTable[patchRam.env_d]; // decay time coeff looked up in table - r = decayTable[patchRam.env_r]; // release time coeff looked up in table - s = patchRam.env_s << 7; // scale 0x00-0x7f to 0x0000-0x3f80 + // FIXME break these out to the patch setter + a = attackTable[patchRam.env_a]; // attack time coeff looked up in table + d = decayTable[patchRam.env_d]; // decay time coeff looked up in table + r = decayTable[patchRam.env_r]; // release time coeff looked up in table + s = patchRam.env_s << 7; // scale 0x00-0x7f to 0x0000-0x3f80 + + square = (patchRam.switch2 & 0x08) ? 0.63 : 0; + saw = (patchRam.switch2 & 0x10) ? 0.8 : 0; + sub = patchRam.sub / 127.0f; + + // printf("%f %f %f %02x\n", square, saw, sub, patchRam.switch2); + // work out the "master" cutoff - vcfCutoff = patchRam.vcfFreq / 127.0f; - vcfCutoff += lfo * (patchRam.vcfLfo/127.0f); + // vcfCutoff = patchRam.vcfFreq / 127.0f; + // vcfCutoff += lfo * (patchRam.vcfLfo / 127.0f); // also needs pitch bend amount for the base level + int16_t vcf = (patchRam.vcfEnv << 7) * ((patchRam.switch2 & 0x02) ? -1 : 1); + // int16_t vca = (patchRam.vcfEnv << 7) * (patchRam.switch2 & 0x01) ? -1 : 1; + for (uint32_t i = 0; i < NUM_VOICES; i++) { switch (voice[i].envPhase) { - case 0: // release phase FIXME use an enum I guess + case 0: // release phase FIXME use an enum I guess voice[i].env = (voice[i].env * d) >> 16; // "RC" decay to zero break; - case 1: // attack phase + case 1: // attack phase voice[i].env += a; // linear attack to 0x3fff break; case 2: - voice[i].env = (((voice[i].env - s) * d) >>16 ) + s; + voice[i].env = (((voice[i].env - s) * d) >> 16) + s; break; } if (voice[i].env > 0x3fff) { voice[i].env = 0x3fff; voice[i].envPhase = 2; // flip to decay } - // per voice we need to calculate the key follow amount and envelope amount + // per voice we need to calculate the key follow amount and envelope amount + voice[i].vcfCut = (patchRam.vcfFreq << 7) + ((vcf * voice[i].env) >> 16); + + if (voice[i].vcfCut > 0x3fff) voice[i].vcfCut = 0x3fff; + if (voice[i].vcfCut < 0) voice[i].vcfCut = 0; + + + voice[i].vcaEnv = (patchRam.switch2 & 0x04) ? (voice[i].envPhase ? 0x3fff : 0) : voice[i].env; + } } diff --git a/plugin/module.hpp b/plugin/module.hpp index 3a36908..7b99f47 100644 --- a/plugin/module.hpp +++ b/plugin/module.hpp @@ -35,31 +35,37 @@ class Module { float lfo = 0, lfoTheta = 0; // precomputed values for all voices - float pw, saw, square, sub; + float pw;//, saw, square, sub; // "internal state" values for patch parameters uint16_t a, d, s, r; + float saw = 0, square = 0, sub = 0, noise = 0; + struct { 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 vcfFreq = 0x4c; // 0x3f80 uint8_t vcfReso = 0x00; - uint8_t vcfEnv = 0x2e; + uint8_t vcfEnv = 0x4e; uint8_t vcfLfo = 0; uint8_t vcfKey = 0x47; uint8_t vca = 0x28; - uint8_t env_a = 0x1b; + uint8_t env_a = 0x00; uint8_t env_d = 0x39; uint8_t env_s = 0x39; // 0x3f80 uint8_t env_r = 0x30; - uint8_t sub = 0x00; + uint8_t sub = 0x7f; uint8_t switch1 = 0x1a; uint8_t switch2 = 0x18; } patchRam; + + private: + // controls + float subRC = 0, outRC = 0, pwmRC = 0, resRC = 0, noiseRC = 0; }; class Voice { @@ -70,9 +76,14 @@ class Voice { void run(Module* m, float* buffer, uint32_t samples); uint8_t envPhase = 0; int16_t env = 0; // output amplitude + int16_t vcfCut; + int16_t vcaEnv; private: - float omega = 0, theta = 0; // phase increment and angle + // control + float vcaRC = 0, vcfRC = 0; + + float omega = 0, theta = 0; // phase increment and angle FIXME better names float delay = 0, lastpw = 0; // delay slots for antialiasing uint8_t pulseStage = 1; // pulse wave phase float subosc = 1; // sub oscillator flipflop output diff --git a/plugin/voice.cpp b/plugin/voice.cpp index 025707c..0ad1094 100644 --- a/plugin/voice.cpp +++ b/plugin/voice.cpp @@ -51,14 +51,17 @@ void Voice::run(Module* m, float* buffer, uint32_t samples) { // carry out per-voice calculations for each block of samples float out, t, fb, res; - float cut = 0.00513 + 0.0000075*env; - // printf("%f ", delay); - m->saw = 1; - m->square = 1; - m->sub = .5; - m->pw = 0.5; + //float cut = 0.00513 + 0.0000075*env; - float amp = env / 4096.0f; + // calculate cutoff frequency + float cut = 248.0f * (powf(2, (vcfCut - 0x1880) / 1143.0f)); + cut = 0.25 * 6.2832 * cut / 48000.0f; // FIXME hardcoded values + cut = cut/(1+cut); // correct tuning warp + + // printf("%f ", delay); + m->pw = 0.5; + + float amp = vcaEnv / 4096.0f; for (uint32_t i = 0; i < samples; i++) { out = delay; @@ -101,14 +104,14 @@ void Voice::run(Module* m, float* buffer, uint32_t samples) { if (fb > 1) fb = 1; if (fb < -1) fb = -1; - fb = out - (fb * 2); + fb = out - (fb * 1.7); b1 = ((fb - b1) * cut) + b1; b2 = ((b1 - b2) * cut) + b2; b3 = ((b2 - b3) * cut) + b3; b4 = ((b3 - b4) * cut) + b4; } - buffer[i] += 0.125 * amp * b4; + buffer[i] += 0.0625 * amp * b4; lastpw = m->pw; } // buffer[0] += 1;