// Based on Will Pirkle's courses & Vadim Zavalishin's book #include "Bidoo.hpp" #include "BidooComponents.hpp" #include "dsp/decimator.hpp" using namespace std; namespace rack_plugin_Bidoo { #define pi 3.14159265359 struct FilterStage { float mem = 0.0; float Filter(float sample, float freq, float smpRate, float gain, int mode) { float g = tan(pi*freq/smpRate); float G = g/(1.0 + g); float out; if (mode == 0) { out = (sample - mem) * G + mem; } else { out = (tanh(sample*gain)/tanh(gain) - mem) * G + mem; } mem = out + (sample - mem) * G ; return out; } }; struct LadderFilter { FilterStage stage1; FilterStage stage2; FilterStage stage3; FilterStage stage4; float q; float freq; float smpRate; int mode = 0; float gain = 1.0f; void setParams(float freq, float q, float smpRate, float gain, int mode) { this->freq = freq; this->q=q; this->smpRate=smpRate; this->mode = mode; this->gain = gain; } float calcOutput(float sample) { float g = tan(pi*freq/smpRate); float G = g/(1.0f + g); G = G*G*G*G; float S1 = stage1.mem/(1.0f + g); float S2 = stage2.mem/(1.0f + g); float S3 = stage3.mem/(1.0f + g); float S4 = stage4.mem/(1.0f + g); float S = G*G*G*S1 + G*G*S2 + G*S3 + S4; return stage4.Filter(stage3.Filter(stage2.Filter(stage1.Filter((sample - q*S)/(1.0f + q*G), freq,smpRate,gain,mode),freq,smpRate,gain,mode),freq,smpRate,gain,mode),freq,smpRate,gain,mode); } }; struct LIMBO : Module { enum ParamIds { CUTOFF_PARAM, Q_PARAM, CMOD_PARAM, MUG_PARAM, MODE_PARAM, NUM_PARAMS }; enum InputIds { IN_L, IN_R, CUTOFF_INPUT, Q_INPUT, MUG_INPUT, NUM_INPUTS }; enum OutputIds { OUT_L, OUT_R, NUM_OUTPUTS }; enum LightIds { LEARN_LIGHT, NUM_LIGHTS }; LadderFilter lFilter,rFilter; LIMBO() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { } void step() override; }; void LIMBO::step() { float cfreq = pow(2.0f,rescale(clamp(params[CUTOFF_PARAM].value + params[CMOD_PARAM].value * inputs[CUTOFF_INPUT].value / 5.0f,0.0f,1.0f),0.0f,1.0f,4.5f,13.0f)); float q = 3.5f * clamp(params[Q_PARAM].value + inputs[Q_INPUT].value / 5.0f, 0.0f, 1.0f); float g = pow(2.0f,rescale(clamp(params[MUG_PARAM].value + inputs[MUG_INPUT].value / 5.0f,0.0f,1.0f),0.0f,1.0f,0.0f,3.0f)); int mode = (int)params[MODE_PARAM].value; lFilter.setParams(cfreq,q,engineGetSampleRate(),g/3,mode); rFilter.setParams(cfreq,q,engineGetSampleRate(),g/3,mode); float inL = inputs[IN_L].value/5.0f; //normalise to -1/+1 we consider VCV Rack standard is #+5/-5V on VCO1 float inR = inputs[IN_R].value/5.0f; inL = lFilter.calcOutput(inL)*5.0f*(mode == 0 ? g : 1); inR = rFilter.calcOutput(inR)*5.0f*(mode == 0 ? g : 1); outputs[OUT_L].value = inL; outputs[OUT_R].value = inR; } struct LIMBOWidget : ModuleWidget { LIMBOWidget(LIMBO *module) : ModuleWidget(module) { setPanel(SVG::load(assetPlugin(plugin, "res/LIMBO.svg"))); addChild(Widget::create(Vec(RACK_GRID_WIDTH, 0))); addChild(Widget::create(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); addChild(Widget::create(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); addChild(Widget::create(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); addParam(ParamWidget::create(Vec(31, 61), module, LIMBO::CUTOFF_PARAM, 0.0f, 1.0f, 1.0f)); addParam(ParamWidget::create(Vec(12, 143), module, LIMBO::Q_PARAM, 0.0f, 1.0f, 0.0f)); addParam(ParamWidget::create(Vec(71, 143), module, LIMBO::MUG_PARAM, 0.0f, 1.0f, 0.0f)); addParam(ParamWidget::create(Vec(12, 208), module, LIMBO::CMOD_PARAM, -1.0f, 1.0f, 0.0f)); addParam(ParamWidget::create(Vec(83, 217), module, LIMBO::MODE_PARAM, 0.0f, 1.0f, 0.0f)); addInput(Port::create(Vec(12, 280), Port::INPUT, module, LIMBO::CUTOFF_INPUT)); addInput(Port::create(Vec(47, 280), Port::INPUT, module, LIMBO::Q_INPUT)); addInput(Port::create(Vec(82, 280), Port::INPUT, module, LIMBO::MUG_INPUT)); addInput(Port::create(Vec(24, 319), Port::INPUT, module, LIMBO::IN_L)); addInput(Port::create(Vec(24, 339), Port::INPUT, module, LIMBO::IN_R)); addOutput(Port::create(Vec(95, 319), Port::OUTPUT, module, LIMBO::OUT_L)); addOutput(Port::create(Vec(95, 339), Port::OUTPUT, module, LIMBO::OUT_R)); } }; } // namespace rack_plugin_Bidoo using namespace rack_plugin_Bidoo; RACK_PLUGIN_MODEL_INIT(Bidoo, LIMBO) { Model *modelLIMBO = Model::create("Bidoo", "lIMbO", "lIMbO filter", FILTER_TAG); return modelLIMBO; }