built-in triple chorus
This commit is contained in:
parent
24fd4718f5
commit
6bde8378ed
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
NAME = sonnenlicht
|
NAME = sonnenlicht
|
||||||
|
|
||||||
FILES_DSP = assigner.cpp generator.cpp sonnenlicht.cpp
|
FILES_DSP = assigner.cpp generator.cpp chorus.cpp svf.cpp sonnenlicht.cpp
|
||||||
include ../dpf/Makefile.plugins.mk
|
include ../dpf/Makefile.plugins.mk
|
||||||
|
|
||||||
TARGETS += vst2 vst3 jack lv2_dsp
|
TARGETS += vst2 vst3 jack lv2_dsp
|
||||||
|
|
|
@ -0,0 +1,152 @@
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
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
|
||||||
|
|
||||||
|
sampleRate = xsampleRate;
|
||||||
|
|
||||||
|
// lfo values taken from a rough simulation
|
||||||
|
|
||||||
|
fastPhase = 0;
|
||||||
|
slowPhase = 0;
|
||||||
|
|
||||||
|
preFilter = new SVF();
|
||||||
|
postFilter1 = new SVF();
|
||||||
|
postFilter2 = new SVF();
|
||||||
|
|
||||||
|
fastOmega = 6.283 * 6.8 / 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);
|
||||||
|
preFilter->setCutoff(12600, 1.3, sampleRate);
|
||||||
|
postFilter1->setCutoff(11653, 6.6, sampleRate);
|
||||||
|
postFilter2->setCutoff(5883, 1.1, sampleRate);
|
||||||
|
|
||||||
|
// calculate SVF params
|
||||||
|
// hardcoded values for now
|
||||||
|
// this is the pre-chorus filter based around TR2
|
||||||
|
// It's actually a Sallen-Key filter which is easy to realise in hardware
|
||||||
|
// however a State Variable Filter is far easier to realise in software
|
||||||
|
// simple is good, and using a little maths we can work out that for
|
||||||
|
// R55 = R56 = 22k, C54 = 1.5nF, C76 = 220pF then the filter is at
|
||||||
|
// 12.6kHz and a Q of about 1.3
|
||||||
|
//
|
||||||
|
// Here is the best writeup ever on SVFs
|
||||||
|
// https://kokkinizita.linuxaudio.org/papers/digsvfilt.pdf
|
||||||
|
}
|
||||||
|
|
||||||
|
Chorus::~Chorus() {
|
||||||
|
delete lpfIn;
|
||||||
|
delete lpfOut1;
|
||||||
|
delete lpfOut2;
|
||||||
|
delete ram;
|
||||||
|
delete preFilter;
|
||||||
|
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;
|
||||||
|
|
||||||
|
// filter the input
|
||||||
|
preFilter->runSVF(input, lpfIn, frames);
|
||||||
|
|
||||||
|
//memcpy(lpfIn, input, sizeof(float) * frames);
|
||||||
|
|
||||||
|
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] = lpfIn[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.005
|
||||||
|
#define AMT 0.0015
|
||||||
|
|
||||||
|
// 0 degree delay line
|
||||||
|
lfoMod = 0.203 * sin(fastPhase) + 0.635 * 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.609 * 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);
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef Chorus_HPP
|
||||||
|
#define Chorus_HPP
|
||||||
|
|
||||||
|
#include "svf.hpp"
|
||||||
|
|
||||||
|
// total size of delay line buffer
|
||||||
|
#define DELAYSIZE 1028
|
||||||
|
|
||||||
|
class Chorus {
|
||||||
|
public:
|
||||||
|
Chorus(uint32_t bufferSize, double sampleRate);
|
||||||
|
~Chorus();
|
||||||
|
void run(const float *input, float **outputs, uint32_t frames);
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32_t bufferSize;
|
||||||
|
double sampleRate;
|
||||||
|
double fastPhase, fastOmega;
|
||||||
|
double slowPhase, slowOmega;
|
||||||
|
double fastLfo, slowLfo;
|
||||||
|
|
||||||
|
uint16_t delayptr;
|
||||||
|
|
||||||
|
float *ram;
|
||||||
|
float *lpfIn;
|
||||||
|
float *lpfOut1, *lpfOut2;
|
||||||
|
|
||||||
|
SVF *preFilter, *postFilter1, *postFilter2;
|
||||||
|
};
|
||||||
|
#endif
|
|
@ -18,21 +18,21 @@
|
||||||
|
|
||||||
#include "sonnenlicht.hpp"
|
#include "sonnenlicht.hpp"
|
||||||
|
|
||||||
#include "assigner.hpp"
|
|
||||||
|
|
||||||
START_NAMESPACE_DISTRHO
|
START_NAMESPACE_DISTRHO
|
||||||
|
|
||||||
Sonnenlicht::Sonnenlicht() : Plugin(kParameterCount, 0, 0), fSampleRate(getSampleRate()) {
|
Sonnenlicht::Sonnenlicht() : Plugin(kParameterCount, 0, 0), fSampleRate(getSampleRate()) {
|
||||||
printf("initialiser called\n");
|
|
||||||
genny = new Generator(getBufferSize());
|
genny = new Generator(getBufferSize());
|
||||||
genny->setupGenerator(fSampleRate);
|
genny->setupGenerator(fSampleRate);
|
||||||
|
|
||||||
assigner = new Assigner(genny->voices);
|
assigner = new Assigner(genny->voices);
|
||||||
|
|
||||||
|
chorus = new Chorus(getBufferSize(), fSampleRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
Sonnenlicht::~Sonnenlicht() {
|
Sonnenlicht::~Sonnenlicht() {
|
||||||
delete assigner;
|
delete assigner;
|
||||||
delete genny;
|
delete genny;
|
||||||
|
delete chorus;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sonnenlicht::setParameterValue(uint32_t index, float value) {
|
void Sonnenlicht::setParameterValue(uint32_t index, float value) {
|
||||||
|
@ -66,8 +66,8 @@ void Sonnenlicht::run(const float**, float** outputs, uint32_t frames,
|
||||||
}
|
}
|
||||||
|
|
||||||
genny->runBlock(assigner->noteTbl, frames);
|
genny->runBlock(assigner->noteTbl, frames);
|
||||||
memcpy(outputs[0], genny->output, frames * sizeof(float));
|
|
||||||
memcpy(outputs[1], genny->output, frames * sizeof(float));
|
chorus->run(genny->output, outputs, frames);
|
||||||
}
|
}
|
||||||
|
|
||||||
Plugin* createPlugin() { return new Sonnenlicht(); }
|
Plugin* createPlugin() { return new Sonnenlicht(); }
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "DistrhoPlugin.hpp"
|
#include "DistrhoPlugin.hpp"
|
||||||
#include "assigner.hpp"
|
#include "assigner.hpp"
|
||||||
#include "generator.hpp"
|
#include "generator.hpp"
|
||||||
|
#include "chorus.hpp"
|
||||||
|
|
||||||
START_NAMESPACE_DISTRHO
|
START_NAMESPACE_DISTRHO
|
||||||
|
|
||||||
|
@ -61,6 +62,7 @@ class Sonnenlicht : public Plugin {
|
||||||
double fSampleRate;
|
double fSampleRate;
|
||||||
Assigner *assigner;
|
Assigner *assigner;
|
||||||
Generator *genny;
|
Generator *genny;
|
||||||
|
Chorus *chorus;
|
||||||
|
|
||||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Sonnenlicht);
|
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Sonnenlicht);
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
State Variable Filter
|
||||||
|
|
||||||
|
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 "svf.hpp"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
SVF::SVF() {
|
||||||
|
// zero out all values
|
||||||
|
z1 = 0;
|
||||||
|
z2 = 0;
|
||||||
|
c1 = 0;
|
||||||
|
c2 = 0;
|
||||||
|
d0 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SVF::setCutoff(float cutoff, float Q, float sampleRate) {
|
||||||
|
float F = cutoff / sampleRate;
|
||||||
|
float w = 2 * tan(3.14159 * F);
|
||||||
|
float a = w / Q;
|
||||||
|
float b = w * w;
|
||||||
|
|
||||||
|
// "corrected" SVF params, per Fons Adriaensen
|
||||||
|
c1 = (a + b) / (1 + a / 2 + b / 4);
|
||||||
|
c2 = b / (a + b);
|
||||||
|
d0 = c1 * c2 / 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SVF::runSVF(const float *input, float *output, uint32_t frames) {
|
||||||
|
float x;
|
||||||
|
for (uint32_t i = 0; i < frames; i++) {
|
||||||
|
// lowpass filter
|
||||||
|
x = input[i] - z1 - z2;
|
||||||
|
z2 += c2 * z1;
|
||||||
|
z1 += c1 * x;
|
||||||
|
output[i] = d0 * x + z2;
|
||||||
|
}
|
||||||
|
// printf("%f\n", x);
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
State Variable Filter
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SVF_HPP
|
||||||
|
#define SVF_HPP
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
class SVF {
|
||||||
|
public:
|
||||||
|
SVF();
|
||||||
|
void setCutoff(float cutoff, float Q, float sampleRate);
|
||||||
|
void runSVF(const float *input, float *output, uint32_t frames);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
private:
|
||||||
|
float c1, c2, d0;
|
||||||
|
float z1, z2;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue