diff --git a/plugins/community/repos/LindenbergResearch/design/Korg35VCF.afdesign b/plugins/community/repos/LindenbergResearch/design/Korg35VCF.afdesign
index c1fe5fb8..edafa745 100644
Binary files a/plugins/community/repos/LindenbergResearch/design/Korg35VCF.afdesign and b/plugins/community/repos/LindenbergResearch/design/Korg35VCF.afdesign differ
diff --git a/plugins/community/repos/LindenbergResearch/res/panels/Korg35VCF.svg b/plugins/community/repos/LindenbergResearch/res/panels/Korg35VCF.svg
index 9e9c82c0..f7ad59f8 100644
--- a/plugins/community/repos/LindenbergResearch/res/panels/Korg35VCF.svg
+++ b/plugins/community/repos/LindenbergResearch/res/panels/Korg35VCF.svg
@@ -19,10 +19,7 @@
-
+
-
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
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);
diff --git a/plugins/community/repos/LindenbergResearch/src/dsp/Korg35Filter.hpp b/plugins/community/repos/LindenbergResearch/src/dsp/Korg35Filter.hpp
index 7e555dfe..5fcc2e38 100644
--- a/plugins/community/repos/LindenbergResearch/src/dsp/Korg35Filter.hpp
+++ b/plugins/community/repos/LindenbergResearch/src/dsp/Korg35Filter.hpp
@@ -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;
};
diff --git a/plugins/community/repos/LindenbergResearch/src/dsp/LambertW.cpp b/plugins/community/repos/LindenbergResearch/src/dsp/LambertW.cpp
index 04a6041c..dbca084b 100644
--- a/plugins/community/repos/LindenbergResearch/src/dsp/LambertW.cpp
+++ b/plugins/community/repos/LindenbergResearch/src/dsp/LambertW.cpp
@@ -16,7 +16,7 @@
#include
#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 {
};
diff --git a/plugins/community/repos/LindenbergResearch/src/dsp/WaveShaper.cpp b/plugins/community/repos/LindenbergResearch/src/dsp/WaveShaper.cpp
index fa8ea350..2e583c72 100644
--- a/plugins/community/repos/LindenbergResearch/src/dsp/WaveShaper.cpp
+++ b/plugins/community/repos/LindenbergResearch/src/dsp/WaveShaper.cpp
@@ -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));
}
diff --git a/plugins/community/repos/LindenbergResearch/src/dsp/WaveShaper.hpp b/plugins/community/repos/LindenbergResearch/src/dsp/WaveShaper.hpp
index aa4ebb92..60c42402 100644
--- a/plugins/community/repos/LindenbergResearch/src/dsp/WaveShaper.hpp
+++ b/plugins/community/repos/LindenbergResearch/src/dsp/WaveShaper.hpp
@@ -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; }
};
diff --git a/plugins/community/repos/LindenbergResearch/src/modules/Korg35.cpp b/plugins/community/repos/LindenbergResearch/src/modules/Korg35.cpp
index 78bb8b73..0d74e4ee 100644
--- a/plugins/community/repos/LindenbergResearch/src/modules/Korg35.cpp
+++ b/plugins/community/repos/LindenbergResearch/src/modules/Korg35.cpp
@@ -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(Vec(32.5, 74.4), module, Korg35::FREQ_PARAM, 0.f, 1.f, 1.f);
module->peakKnob = LRKnob::create(Vec(32.5, 144.4), module, Korg35::PEAK_PARAM, 0.001f, 2.0, 0.001f);
- module->saturateKnob = LRKnob::create(Vec(40, 244.4), module, Korg35::SAT_PARAM, 1.f, 1.5, 0.0f);
+ module->saturateKnob = LRKnob::create(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));
diff --git a/plugins/community/repos/LindenbergResearch/src/widgets/LRPanel.cpp b/plugins/community/repos/LindenbergResearch/src/widgets/LRPanel.cpp
index 9fc5e681..0d4630ab 100644
--- a/plugins/community/repos/LindenbergResearch/src/widgets/LRPanel.cpp
+++ b/plugins/community/repos/LindenbergResearch/src/widgets/LRPanel.cpp
@@ -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;