// 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 MultiFilter { float q; float freq; float smpRate; float hp = 0.0f,bp = 0.0f,lp = 0.0f,mem1 = 0.0f,mem2 = 0.0f; void setParams(float freq, float q, float smpRate) { this->freq = freq; this->q=q; this->smpRate=smpRate; } void calcOutput(float sample) { float g = tan(pi*freq/smpRate); float R = 1.0f/(2.0f*q); hp = (sample - (2.0f*R + g)*mem1 - mem2)/(1.0f + 2.0f*R*g + g*g); bp = g*hp + mem1; lp = g*bp + mem2; mem1 = g*hp + bp; mem2 = g*bp + lp; } }; struct PERCO : Module { enum ParamIds { CUTOFF_PARAM, Q_PARAM, CMOD_PARAM, NUM_PARAMS }; enum InputIds { IN, CUTOFF_INPUT, Q_INPUT, NUM_INPUTS }; enum OutputIds { OUT_LP, OUT_BP, OUT_HP, NUM_OUTPUTS }; enum LightIds { LEARN_LIGHT, NUM_LIGHTS }; MultiFilter filter; PERCO() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { } void step() override; }; void PERCO::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 = 10.0f * clamp(params[Q_PARAM].value + inputs[Q_INPUT].value / 5.0f, 0.1f, 1.0f); filter.setParams(cfreq,q,engineGetSampleRate()); float in = inputs[IN].value/5.0f; //normalise to -1/+1 we consider VCV Rack standard is #+5/-5V on VCO1 //filtering filter.calcOutput(in); outputs[OUT_LP].value = filter.lp * 5.0f; outputs[OUT_HP].value = filter.hp * 5.0f; outputs[OUT_BP].value = filter.bp * 5.0f; } struct PERCOWidget : ModuleWidget { PERCOWidget(PERCO *module) : ModuleWidget(module) { setPanel(SVG::load(assetPlugin(plugin, "res/PERCO.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, PERCO::CUTOFF_PARAM, 0.0f, 1.0f, 1.0f)); addParam(ParamWidget::create(Vec(12, 143), module, PERCO::Q_PARAM, 0.1f, 1.0f, 0.1f)); addParam(ParamWidget::create(Vec(71, 143), module, PERCO::CMOD_PARAM, -1.0f, 1.0f, 0.0f)); addInput(Port::create(Vec(10, 276), Port::INPUT, module, PERCO::IN)); addInput(Port::create(Vec(48, 276), Port::INPUT, module, PERCO::CUTOFF_INPUT)); addInput(Port::create(Vec(85, 276), Port::INPUT, module, PERCO::Q_INPUT)); addOutput(Port::create(Vec(10, 320), Port::OUTPUT, module, PERCO::OUT_LP)); addOutput(Port::create(Vec(48, 320), Port::OUTPUT, module, PERCO::OUT_BP)); addOutput(Port::create(Vec(85, 320), Port::OUTPUT, module, PERCO::OUT_HP)); } }; } // namespace rack_plugin_Bidoo using namespace rack_plugin_Bidoo; RACK_PLUGIN_MODEL_INIT(Bidoo, PERCO) { Model *modelPERCO = Model::create("Bidoo", "pErCO", "pErCO filter", FILTER_TAG); return modelPERCO; }