| @@ -33,10 +33,9 @@ float cliph(float in, float clip) { | |||
| */ | |||
| float wrapTWOPI(float n) { | |||
| float b = 1.f / TWOPI * n; | |||
| return (b - lround(b)) * TWOPI; | |||
| return (b - lroundf(b)) * TWOPI; | |||
| } | |||
| /** | |||
| * @brief Get PLL increment depending on frequency | |||
| * @param frq Frequency | |||
| @@ -6,10 +6,9 @@ | |||
| #include "dsp/resampler.hpp" | |||
| #include "DSPEffect.hpp" | |||
| #define LAMBERT_W_THRESHOLD 10e-10 | |||
| using namespace rack; | |||
| const static float TWOPI = (float) M_PI * 2; | |||
| const static float TWOPI = lroundf(M_PI * 2.); | |||
| namespace dsp { | |||
| @@ -50,13 +50,14 @@ struct DSPSystem { | |||
| protected: | |||
| #ifdef _MSC_VER | |||
| DSPPort input[NUM_IN + 1]; | |||
| DSPPort output[NUM_OUT + 1]; | |||
| DSPParam param[NUM_PARAM + 1]; | |||
| // (note) +1 to avoid "illegal zero-sized array" errors | |||
| DSPPort input[NUM_IN + 1]; | |||
| DSPPort output[NUM_OUT + 1]; | |||
| DSPParam param[NUM_PARAM + 1]; | |||
| #else | |||
| DSPPort input[NUM_IN + 1] = {}; | |||
| DSPPort output[NUM_OUT + 1] = {}; | |||
| DSPParam param[NUM_PARAM] = {}; | |||
| DSPPort input[NUM_IN] = {}; | |||
| DSPPort output[NUM_OUT] = {}; | |||
| DSPParam param[NUM_PARAM] = {}; | |||
| #endif | |||
| float sr; | |||
| @@ -18,15 +18,24 @@ | |||
| #include "Korg35Filter.hpp" | |||
| #include "DSPEffect.hpp" | |||
| #include "DSPMath.hpp" | |||
| /** | |||
| * @brief Construct a new Korg35 filter stage | |||
| * @param sr SampleRate | |||
| * @param type Lowpass / Highpass | |||
| */ | |||
| dsp::Korg35FilterStage::Korg35FilterStage(float sr, FilterType type) : DSPEffect(sr) { | |||
| this->type = type; | |||
| } | |||
| /** | |||
| * @brief Init stage | |||
| */ | |||
| void dsp::Korg35FilterStage::init() { | |||
| type = LPF1; | |||
| type = LP_STAGE; | |||
| alpha = 1.f; | |||
| beta = 1.f; | |||
| zn1 = 0; | |||
| @@ -34,19 +43,25 @@ void dsp::Korg35FilterStage::init() { | |||
| } | |||
| /** | |||
| * @brief Recompute filter parameter | |||
| */ | |||
| void dsp::Korg35FilterStage::invalidate() { | |||
| // only process in dedicated mode | |||
| if (!dedicated) return; | |||
| float wd = 2 * PI * fc; | |||
| float T = 1 / sr; | |||
| float wa = (2 / T) * tan(wd * T / 2); | |||
| float g = wa * T / 2; | |||
| float wd = TWOPI * fc; | |||
| float T = 1.f / sr; | |||
| float wa = (2.f / T) * tanf(wd * T / 2.f); | |||
| float g = wa * T / 2.f; | |||
| alpha = g / (1.f + g); | |||
| } | |||
| /** | |||
| * @brief Update filter and compute next sample | |||
| */ | |||
| void dsp::Korg35FilterStage::process() { | |||
| // v(n) | |||
| float vn = (in - zn1) * alpha; | |||
| @@ -55,80 +70,149 @@ void dsp::Korg35FilterStage::process() { | |||
| zn1 = vn + lpf; | |||
| float hpf = in - lpf; | |||
| // switch filter type | |||
| if (type == LPF1) { | |||
| if (type == LP_STAGE) { | |||
| out = lpf; | |||
| } else { | |||
| float hpf = in - lpf; | |||
| out = hpf; | |||
| } | |||
| } | |||
| /** | |||
| * @brief Init main filter | |||
| */ | |||
| void dsp::Korg35Filter::init() { | |||
| fc = sr / 2; | |||
| fc = sr / 2.f; | |||
| peak = 0.f; | |||
| lpf->init(); | |||
| /* lowpass stages */ | |||
| lpf1->init(); | |||
| lpf2->init(); | |||
| /* highpass stages */ | |||
| hpf1->init(); | |||
| hpf2->init(); | |||
| } | |||
| /** | |||
| * @brief Recompute filter parameter | |||
| */ | |||
| void dsp::Korg35Filter::invalidate() { | |||
| float frqHz = MAX_FREQUENCY / 1000.f * powf(1000.f, fc); | |||
| float wd = 2 * PI * frqHz; | |||
| float T = 1 / sr; | |||
| float wa = (2 / T) * tan(wd * T / 2); | |||
| float g = wa * T / 2; | |||
| float wd = TWOPI * frqHz; | |||
| float T = 1.f / sr; | |||
| float wa = (2.f / T) * tanf(wd * T / 2.f); | |||
| float g = wa * T / 2.f; | |||
| float G = g / (1.f + g); | |||
| // set alphas | |||
| lpf->alpha = G; | |||
| hpf1->alpha = G; | |||
| hpf2->alpha = G; | |||
| hpf2->beta = -1.f * G / (1.f + g); | |||
| lpf->beta = 1.f / (1.f + g); | |||
| if (type == HPF) { | |||
| /* HIGHPASS */ | |||
| lpf1->alpha = G; | |||
| hpf1->alpha = G; | |||
| hpf2->alpha = G; | |||
| hpf2->beta = -1.f * G / (1.f + g); | |||
| lpf1->beta = 1.f / (1.f + g); | |||
| } else { | |||
| /* LOWPASS */ | |||
| lpf1->alpha = G; | |||
| lpf2->alpha = G; | |||
| hpf1->alpha = G; | |||
| lpf2->beta = (peak - peak * G) / (1.f + g); | |||
| hpf1->beta = -1.f / (1.f + g); | |||
| } | |||
| Ga = 1.f / (1.f - peak * G + peak * G * G); | |||
| } | |||
| /** | |||
| * @brief Compute next sample for output depending on filter type | |||
| */ | |||
| void dsp::Korg35Filter::process() { | |||
| type == LPF ? processLPF() : processHPF(); | |||
| } | |||
| /** | |||
| * @brief Do the lowpass filtering and oversampling | |||
| */ | |||
| void dsp::Korg35Filter::processLPF() { | |||
| lpf1->in = in; | |||
| lpf1->process(); | |||
| float y1 = lpf1->out; | |||
| float s35h = hpf1->getFeedback() + lpf2->getFeedback(); | |||
| float u = Ga * (y1 + s35h); | |||
| //float y = peak * fastatan(sat * u * 0.1) * 10.f; | |||
| u = tanhf(sat * u * 0.1) * 10.f; | |||
| lpf2->in = u; | |||
| lpf2->process(); | |||
| float y = peak * lpf2->out; | |||
| hpf1->in = y; | |||
| hpf1->process(); | |||
| if (peak > 0) { | |||
| y *= 1.f / peak; // normalize | |||
| } | |||
| out = y; | |||
| } | |||
| /** | |||
| * @brief Do the highpass filtering and oversampling | |||
| */ | |||
| void dsp::Korg35Filter::processHPF() { | |||
| hpf1->in = in; | |||
| hpf1->process(); | |||
| float y1 = hpf1->out; | |||
| float s35h = hpf2->getFeedback() + lpf->getFeedback(); | |||
| float s35h = hpf2->getFeedback() + lpf1->getFeedback(); | |||
| float u = Ga * (y1 + s35h); | |||
| float y = peak * u; | |||
| y = tanh(sat * y); | |||
| float y = peak * fastatan(sat * u * 0.1) * 10.f; | |||
| hpf2->in = y; | |||
| hpf2->process(); | |||
| lpf->in = hpf2->out; | |||
| lpf->process(); | |||
| lpf1->in = hpf2->out; | |||
| lpf1->process(); | |||
| if (peak > 0) { | |||
| y *= 1 / peak; // normalize | |||
| y *= 1.f / peak; // normalize | |||
| } | |||
| out = y; | |||
| } | |||
| /** | |||
| * @brief Update samplerate | |||
| * @param sr SR | |||
| */ | |||
| void dsp::Korg35Filter::setSamplerate(float sr) { | |||
| DSPEffect::setSamplerate(sr); | |||
| // derive samplerate change | |||
| lpf->setSamplerate(sr); | |||
| lpf1->setSamplerate(sr); | |||
| lpf2->setSamplerate(sr); | |||
| hpf1->setSamplerate(sr); | |||
| hpf2->setSamplerate(sr); | |||
| @@ -23,20 +23,22 @@ | |||
| #include "engine.hpp" | |||
| #include "DSPMath.hpp" | |||
| #define PI 3.14159265358979323846f | |||
| namespace dsp { | |||
| /** | |||
| * @brief Represents one filter stage | |||
| */ | |||
| struct Korg35FilterStage : DSPEffect { | |||
| enum FilterType { | |||
| LPF1, // lowpass stage | |||
| HPF1 // highpass stage | |||
| LP_STAGE, // lowpass stage | |||
| HP_STAGE // highpass stage | |||
| }; | |||
| bool dedicated = false; | |||
| float fc; | |||
| FilterType type; | |||
| float fc; | |||
| float alpha, beta; | |||
| float zn1; | |||
| @@ -56,10 +58,21 @@ struct Korg35FilterStage : DSPEffect { | |||
| }; | |||
| /** | |||
| * @brief Actual Korg35 Filter Class | |||
| */ | |||
| struct Korg35Filter : DSPEffect { | |||
| static constexpr float MAX_FREQUENCY = 20000.f; | |||
| Korg35FilterStage *lpf, *hpf1, *hpf2; | |||
| enum FilterType { | |||
| LPF, // lowpass | |||
| HPF // highpass | |||
| }; | |||
| Korg35FilterStage *lpf1, *lpf2, *hpf1, *hpf2; | |||
| FilterType type; | |||
| float Ga; | |||
| float in, out; | |||
| @@ -68,16 +81,21 @@ struct Korg35Filter : DSPEffect { | |||
| float fc, peak, sat; | |||
| Korg35Filter(float sr) : DSPEffect(sr) { | |||
| lpf = new Korg35FilterStage(sr, Korg35FilterStage::LPF1); | |||
| hpf1 = new Korg35FilterStage(sr, Korg35FilterStage::HPF1); | |||
| hpf2 = new Korg35FilterStage(sr, Korg35FilterStage::HPF1); | |||
| Korg35Filter(float sr, FilterType type) : DSPEffect(sr) { | |||
| Korg35Filter::type = type; | |||
| lpf1 = new Korg35FilterStage(sr, Korg35FilterStage::LP_STAGE); | |||
| lpf2 = new Korg35FilterStage(sr, Korg35FilterStage::LP_STAGE); | |||
| hpf1 = new Korg35FilterStage(sr, Korg35FilterStage::HP_STAGE); | |||
| hpf2 = new Korg35FilterStage(sr, Korg35FilterStage::HP_STAGE); | |||
| } | |||
| void init() override; | |||
| void invalidate() override; | |||
| void process() override; | |||
| void processLPF(); | |||
| void processHPF(); | |||
| void setSamplerate(float sr) override; | |||
| }; | |||
| @@ -16,7 +16,7 @@ | |||
| #include <cmath> | |||
| #include "Horner.h" | |||
| namespace dsp { | |||
| using namespace dsp; | |||
| // fork macro to keep the tree balanced | |||
| #define Y2(d1, c12, d2) \ | |||
| @@ -32,6 +32,9 @@ namespace dsp { | |||
| #define Y7(d1, c12, d2, c23, d3, c34, d4, c45, d5, c56, d6, c67, d7) \ | |||
| Y6(d1, c12, d2, c23, Y2(d3, c34, d4), c45, d5, c56, d6, c67, d7) | |||
| namespace dsp { | |||
| class BranchPoint { | |||
| }; | |||
| @@ -67,14 +67,14 @@ void WaveShaper::process() { | |||
| return; | |||
| } | |||
| rs->doUpsample(STD_CHANNEL, in); | |||
| rs->doUpsample(STD_CHANNEL, beforeComputation(in)); | |||
| for (int i = 0; i < rs->getFactor(); i++) { | |||
| double x = rs->getUpsampled(STD_CHANNEL)[i]; | |||
| rs->data[STD_CHANNEL][i] = compute(x); | |||
| } | |||
| out = rs->getDownsampled(STD_CHANNEL); | |||
| out = afterComputation(rs->getDownsampled(STD_CHANNEL)); | |||
| } | |||
| @@ -86,6 +86,24 @@ public: | |||
| * @return Output sample | |||
| */ | |||
| virtual double compute(double x) { return x; } | |||
| /** | |||
| * @brief Virtual function called before actual oversampling is performed | |||
| * | |||
| * @param x Input sample | |||
| * @return Ouput sample | |||
| */ | |||
| virtual double beforeComputation(double x) { return x; } | |||
| /** | |||
| * @brief Virtual function called after actual oversampling is performed | |||
| * | |||
| * @param x Input sample | |||
| * @return Output sample | |||
| */ | |||
| virtual double afterComputation(double x) { return x; } | |||
| }; | |||
| @@ -47,7 +47,7 @@ struct Korg35 : LRModule { | |||
| }; | |||
| LRKnob *frqKnob, *peakKnob, *saturateKnob; | |||
| Korg35Filter *filter = new Korg35Filter(engineGetSampleRate()); | |||
| Korg35Filter *filter = new Korg35Filter(engineGetSampleRate(), Korg35Filter::LPF); | |||
| Korg35() : LRModule(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {} | |||
| @@ -55,7 +55,7 @@ struct Korg35 : LRModule { | |||
| void step() override { | |||
| filter->fc = params[FREQ_PARAM].value; | |||
| filter->peak = params[PEAK_PARAM].value; | |||
| filter->sat = params[SAT_PARAM].value; | |||
| filter->sat = quadraticBipolar(params[SAT_PARAM].value); | |||
| filter->in = inputs[FILTER_INPUT].value; | |||
| filter->invalidate(); | |||
| @@ -100,7 +100,7 @@ Korg35Widget::Korg35Widget(Korg35 *module) : LRModuleWidget(module) { | |||
| // ***** MAIN KNOBS ****** | |||
| module->frqKnob = LRKnob::create<LRBigKnob>(Vec(32.5, 74.4), module, Korg35::FREQ_PARAM, 0.f, 1.f, 1.f); | |||
| module->peakKnob = LRKnob::create<LRBigKnob>(Vec(32.5, 144.4), module, Korg35::PEAK_PARAM, 0.001f, 2.0, 0.001f); | |||
| module->saturateKnob = LRKnob::create<LRMiddleKnob>(Vec(40, 244.4), module, Korg35::SAT_PARAM, 1.f, 1.5, 0.0f); | |||
| module->saturateKnob = LRKnob::create<LRMiddleKnob>(Vec(40, 244.4), module, Korg35::SAT_PARAM, 1.f, 2.5, 1.0f); | |||
| module->frqKnob->setIndicatorColors(nvgRGBAf(0.9f, 0.9f, 0.9f, 1.0f)); | |||
| module->peakKnob->setIndicatorColors(nvgRGBAf(0.9f, 0.9f, 0.9f, 1.0f)); | |||
| @@ -31,7 +31,7 @@ void LRPanel::init() { | |||
| addChild(patinaWidgetClassic); | |||
| /* setup gradient variants */ | |||
| auto gradientDark = new LRGradientWidget(box.size, nvgRGBAf(.6f, .6f, .6f, 0.3f), nvgRGBAf(0.0f, 0.0f, 0.0f, 0.2f), Vec(100, -120)); | |||
| auto gradientDark = new LRGradientWidget(box.size, nvgRGBAf(.6f, .6f, .6f, 0.3f), nvgRGBAf(0.2f, 0.0f, 0.0f, 0.2f), Vec(50, 20)); | |||
| gradientDark->visible = false; | |||
| addChild(gradientDark); | |||
| gradients[LRGestalt::DARK] = gradientDark; | |||