sample rate stuff for chorus

This commit is contained in:
Gordon JC Pearce 2025-12-22 11:28:24 +00:00
parent 6f7f9340f1
commit 5ea59fb299
2 changed files with 39 additions and 37 deletions

View File

@ -21,24 +21,21 @@
#include <math.h> #include <math.h>
#include <string.h> #include <string.h>
#include <cstdio>
Chorus::Chorus() { Chorus::Chorus() {
lpfOut1 = new float[bufferSize]; lpfOut1 = new float[bufferSize];
lpfOut2 = new float[bufferSize]; lpfOut2 = new float[bufferSize];
ram = new float[DELAYSIZE]; // probably needs to be calculated based on sample rate ram = new float[DELAYSIZE];
fastPhase = 0; lfoPhase = 1;
lfoSpeed = 6.283 * 10.7 / sampleRate; // plainly silly value to show if it hasn't been set
// not quite Butterworth but you'd never hear the difference // not quite Butterworth but you'd never hear the difference
// these are calculated from the real-world component values
postFilter1l = new SVF(9688, .549); postFilter1l = new SVF(9688, .549);
postFilter2l = new SVF(10377, 1.291); postFilter2l = new SVF(10377, 1.291);
postFilter1r = new SVF(9688, .549); postFilter1r = new SVF(9688, .549);
postFilter2r = new SVF(10377, 1.291); postFilter2r = new SVF(10377, 1.291);
// lfo values taken from a rough simulation
fastOmega = 6.283 * 0.7 / sampleRate; // approximate, can be adjusted
// zero out the delay buffer // zero out the delay buffer
memset(ram, 0, sizeof(float) * DELAYSIZE); memset(ram, 0, sizeof(float) * DELAYSIZE);
memset(lpfOut1, 0, sizeof(float) * bufferSize); memset(lpfOut1, 0, sizeof(float) * bufferSize);
@ -56,32 +53,33 @@ Chorus::~Chorus() {
} }
void Chorus::run(float* input, float** outputs, uint32_t frames) { void Chorus::run(float* input, float** outputs, uint32_t frames) {
// actual effects here // run highpass / bass boost and stereo chorus effect for one full block
// FIXME add highpass filter
// now run the DSP
float s0 = 0, s1 = 0; float s0 = 0, s1 = 0;
float lfoMod, dly1, frac, flt; float lfoMod, dly1, frac, flt;
uint16_t tap, delay; uint16_t tap, delay;
for (uint32_t i = 0; i < frames; i++) { for (uint32_t i = 0; i < frames; i++) {
// run a step of LFO // run a step of LFO
fastPhase += fastOmega; lfoPhase += (lfoState & 0x01) ? lfoSpeed : -lfoSpeed;
if (fastPhase > 6.283) fastPhase -= 6.283;
flt = ((hpdelay - input[i]) * hpcut) + input[i]; if (abs(lfoPhase) > 1) {
hpdelay = flt; // lfoPhase -= 2;
input[i] += (flt * hpgain); lfoState++;
}
// highpass/bass boost
flt = ((input[i] - hpDelay) * hpCut) + hpDelay;
hpDelay = flt;
input[i] += (flt * hpGain);
ram[delayptr] = input[i]; ram[delayptr] = input[i];
// delays in milliseconds
#define BASE 0.005 #define BASE 0.005
#define AMT 0.00175 #define AMT 0.00175
// FIXME make this triangle not sine, and move arithmetic out of mainloop dly1 = (BASE + (AMT * lfoPhase)) * sampleRate;
lfoMod = sin(fastPhase);
dly1 = (BASE + (AMT * lfoMod)) * sampleRate;
delay = (int)dly1; delay = (int)dly1;
frac = dly1 - delay; frac = dly1 - delay;
@ -90,7 +88,7 @@ void Chorus::run(float* input, float** outputs, uint32_t frames) {
s0 = ram[tap & 0x3ff]; s0 = ram[tap & 0x3ff];
lpfOut1[i] = ((s1 - s0) * frac) + s0; lpfOut1[i] = ((s1 - s0) * frac) + s0;
dly1 = (BASE - (AMT * lfoMod)) * sampleRate; dly1 = (BASE - (AMT * lfoPhase)) * sampleRate;
delay = (int)dly1; delay = (int)dly1;
frac = dly1 - delay; frac = dly1 - delay;
@ -99,9 +97,6 @@ void Chorus::run(float* input, float** outputs, uint32_t frames) {
s0 = ram[tap & 0x3ff]; s0 = ram[tap & 0x3ff];
lpfOut2[i] = ((s1 - s0) * frac) + s0; lpfOut2[i] = ((s1 - s0) * frac) + s0;
// lpfOut1[i] = input[i] + 1.2 * out0; //(out0 + (out120 * 0.66) + (out240 * 0.33));
// lpfOut2[i] = input[i] + 1.2 * out120; //(out0 + (out120 * 0.33) + (out240 * 0.66));
delayptr++; delayptr++;
delayptr &= 0x3ff; delayptr &= 0x3ff;
} }
@ -119,22 +114,27 @@ void Chorus::run(float* input, float** outputs, uint32_t frames) {
} }
void Chorus::setHpf(uint8_t mode) { void Chorus::setHpf(uint8_t mode) {
// the simple 1-pole lowpass has its output
// subtracted from input for the highpass
// or added for bass boost
// cutoff is calculated as
// k = 1-exp(-2pi * Fc * sampleRate)
switch (mode) { switch (mode) {
case 0x00: case 0x00:
hpcut = 0.91245; hpCut = 1 - exp(-6.283 * 720 / sampleRate);
hpgain = -1; hpGain = -1;
break; break;
case 0x08: case 0x08:
hpcut = 0.97097; hpCut = 1 - exp(-6.283 * 225 / sampleRate);
hpgain = -1; hpGain = -1;
break; break;
case 0x10: case 0x10:
hpcut = 1; hpCut = 1;
hpgain = 0; hpGain = 0;
break; break;
case 0x18: case 0x18:
hpcut = 0.99088; hpCut = 1 - exp(-6.283 * 85 / sampleRate);
hpgain = 1; hpGain = 1.707;
break; break;
} }
} }
@ -147,11 +147,11 @@ void Chorus::setChorus(uint8_t mode) {
break; break;
case 0x40: case 0x40:
gain = 1.2; gain = 1.2;
fastOmega = 6.283 * 0.5 / sampleRate; lfoSpeed = 6.283 * 0.5 / sampleRate;
break; // approximate, can be adjusted break;
case 0x00: case 0x00:
gain = 1.2; gain = 1.2;
fastOmega = 6.283 * 0.9 / sampleRate; lfoSpeed = 6.283 * 0.9 / sampleRate;
break; // approximate, can be adjusted break;
} }
} }

View File

@ -38,16 +38,18 @@ class Chorus {
void setChorus(uint8_t mode); void setChorus(uint8_t mode);
private: private:
double fastPhase = 0, fastOmega = 0; double lfoPhase = 0, lfoSpeed = 0;
uint8_t lfoState=0;
float gain = 1.2; float gain = 1.2;
uint16_t delayptr = 0; uint16_t delayptr = 0;
float* ram; float* ram;
float* lpfIn; float* lpfIn;
float *lpfOut1, *lpfOut2; float *lpfOut1, *lpfOut2;
float hpdelay=0, hpcut=0, hpgain=1; float hpDelay=0, hpCut=0, hpGain=0;
SVF *postFilter1l, *postFilter2l, *postFilter1r, *postFilter2r; SVF *postFilter1l, *postFilter2l, *postFilter1r, *postFilter2r;
}; };