134 lines
4.1 KiB
C++
134 lines
4.1 KiB
C++
/*
|
|
sonnenlicht poly ensemble
|
|
|
|
Copyright 2025 Gordon JC Pearce <gordonjcp@gjcp.net>
|
|
|
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
purpose with or without fee is hereby granted, provided that the above
|
|
copyright notice and this permission notice appear in all copies.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
|
OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
|
CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include "chorus.hpp"
|
|
|
|
#include <math.h>
|
|
#include <string.h>
|
|
|
|
#include <cstdio>
|
|
|
|
Chorus::Chorus(uint32_t xbufferSize, double xsampleRate) { // no parameters, programs, or states
|
|
|
|
bufferSize = xbufferSize;
|
|
sampleRate = xsampleRate;
|
|
|
|
lpfIn = new float[bufferSize];
|
|
lpfOut1 = new float[bufferSize];
|
|
lpfOut2 = new float[bufferSize];
|
|
ram = new float[DELAYSIZE]; // probably needs to be calculated based on sample rate
|
|
|
|
fastPhase = 0;
|
|
slowPhase = 0;
|
|
|
|
postFilter1 = new SVF(8000, 1.3);
|
|
postFilter2 = new SVF(8000, 0.54);
|
|
|
|
// lfo values taken from a rough simulation
|
|
fastOmega = 6.283 * 5.7 / sampleRate; // approximate, can be adjusted
|
|
slowOmega = 6.283 * 0.7 / sampleRate; // again approximate
|
|
|
|
// zero out the delay buffer
|
|
memset(ram, 0, sizeof(float) * DELAYSIZE);
|
|
memset(lpfIn, 0, sizeof(float) * bufferSize);
|
|
memset(lpfOut1, 0, sizeof(float) * bufferSize);
|
|
memset(lpfOut2, 0, sizeof(float) * bufferSize);
|
|
}
|
|
|
|
Chorus::~Chorus() {
|
|
delete lpfIn;
|
|
delete lpfOut1;
|
|
delete lpfOut2;
|
|
delete ram;
|
|
delete postFilter1;
|
|
delete postFilter2;
|
|
}
|
|
|
|
void Chorus::run(const float *input, float **outputs, uint32_t frames) {
|
|
// actual effects here
|
|
|
|
// now run the DSP
|
|
float out0 = 0, out120 = 0, out240 = 0, s0 = 0, s1 = 0;
|
|
float lfoMod, dly1, frac;
|
|
uint16_t tap, delay;
|
|
|
|
for (uint32_t i = 0; i < frames; i++) {
|
|
// run a step of LFO
|
|
fastPhase += fastOmega;
|
|
if (fastPhase > 6.283) fastPhase -= 6.283;
|
|
slowPhase += slowOmega;
|
|
if (slowPhase > 6.283) slowPhase -= 6.283;
|
|
|
|
ram[delayptr] = input[i];
|
|
|
|
// lowpass filter
|
|
|
|
// now we need to calculate the delay
|
|
// I don't know how long the Solina's delay lines are so I'm guessing 2-4ms for now
|
|
// normalised mod depths, from a quick simulation of the LFO block:
|
|
// 0deg 0.203 slow 0.635 fast
|
|
// 120deg 0.248 slow 0.745 fast
|
|
// 240deg 0.252 slow 0.609 fast
|
|
|
|
#define BASE 0.05
|
|
#define AMT 0.00175
|
|
|
|
// 0 degree delay line
|
|
lfoMod = 0.203 * sin(fastPhase) + 0.835 * sin(slowPhase);
|
|
dly1 = (BASE + (AMT * lfoMod)) * sampleRate;
|
|
delay = (int)dly1;
|
|
frac = dly1 - delay;
|
|
|
|
tap = delayptr - delay;
|
|
s1 = ram[(tap - 1) & 0x3ff];
|
|
s0 = ram[tap & 0x3ff];
|
|
out0 = ((s1 - s0) * frac) + s0;
|
|
|
|
// 120 degree delay line
|
|
lfoMod = 0.248 * sin(fastPhase + 2.09) + 0.745 * sin(slowPhase + 2.09);
|
|
dly1 = (BASE + (AMT * lfoMod)) * sampleRate;
|
|
delay = (int)dly1;
|
|
frac = dly1 - delay;
|
|
|
|
tap = delayptr - delay;
|
|
s1 = ram[(tap - 1) & 0x3ff];
|
|
s0 = ram[tap & 0x3ff];
|
|
out120 = ((s1 - s0) * frac) + s0;
|
|
|
|
// 240 degree delay line
|
|
lfoMod = 0.252 * sin(fastPhase + 4.18) + 0.809 * sin(slowPhase + 4.18);
|
|
dly1 = (BASE + (AMT * lfoMod)) * sampleRate;
|
|
delay = (int)dly1;
|
|
frac = dly1 - delay;
|
|
|
|
tap = delayptr - delay;
|
|
s1 = ram[(tap - 1) & 0x3ff];
|
|
s0 = ram[tap & 0x3ff];
|
|
out240 = ((s1 - s0) * frac) + s0;
|
|
|
|
lpfOut1[i] = (out0 + out120 + out240) / 3;
|
|
|
|
delayptr++;
|
|
delayptr &= 0x3ff;
|
|
}
|
|
|
|
postFilter1->runSVF(lpfOut1, lpfOut2, frames);
|
|
postFilter2->runSVF(lpfOut2, outputs[0], frames);
|
|
memcpy (outputs[1], outputs[0], frames * sizeof(float)); // only mono output for now
|
|
}
|