#include "dBiz.hpp" #include "dsp/functions.hpp" #include "dsp/decimator.hpp" #include "dsp/filter.hpp" namespace rack_plugin_dBiz { extern float sawTable[2048]; template struct subBank { float phase = 0.0; float freq; float pitch; Decimator sawDecimator; // For analog detuning effect float pitchSlew = 0.0f; int pitchSlewIndex = 0; float sawBuffer[OVERSAMPLE] = {}; //void setPitch(float pitchKnob, float pitchCv) void setPitch(float pitchKnob, float pitchCv) { // Compute frequency pitch = pitchKnob; const float pitchSlewAmount = 3.0f; pitch += pitchSlew * pitchSlewAmount; pitch += pitchCv; // Note C3 freq = 261.626f * powf(2.0, pitch / 12.0); // Accumulate the phase } void process(float deltaTime) { // Adjust pitch slew if (++pitchSlewIndex > 32) { const float pitchSlewTau = 100.0f; // Time constant for leaky integrator in seconds pitchSlew += (randomNormal() - pitchSlew / pitchSlewTau) * engineGetSampleTime(); pitchSlewIndex = 0; } // Advance phase float deltaPhase = clamp(freq * deltaTime, 1e-6, 0.5f); for (int i = 0; i < OVERSAMPLE; i++) { sawBuffer[i] = 1.66f * interpolateLinear(sawTable, phase * 2047.f); // Advance phase phase += deltaPhase / OVERSAMPLE; phase = eucmod(phase, 1.0f); } } float saw() { return sawDecimator.process(sawBuffer); } }; struct SuHa : Module { enum ParamIds { SUM_VOL_PARAM, VCO_PARAM, SUB1_PARAM = VCO_PARAM + 2, SUB2_PARAM = SUB1_PARAM + 2, VCO_VOL_PARAM = SUB2_PARAM + 2, SUB1_VOL_PARAM = VCO_VOL_PARAM + 2, SUB2_VOL_PARAM = SUB1_VOL_PARAM + 2, NUM_PARAMS = SUB2_VOL_PARAM + 2 }; enum InputIds { VCO_INPUT, SUB1_INPUT = VCO_INPUT + 2, SUB2_INPUT = SUB1_INPUT + 2, NUM_INPUTS = SUB2_INPUT + 2 }; enum OutputIds { SUM_OUTPUT, VCO_OUTPUT, SUB1_OUTPUT = VCO_OUTPUT + 2, SUB2_OUTPUT = SUB1_OUTPUT + 2, NUM_OUTPUTS = SUB2_OUTPUT + 2 }; enum LightIds { NUM_LIGHTS }; subBank <16,16> VCO[2]={}; subBank <16,16> SUB1[2]={}; subBank <16,16> SUB2[2]={}; SuHa() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {} void step() override; }; void SuHa::step() { int s1[2]={}; int s2[2] = {}; float sum=0.0f; for (int i=0;i<2;i++) { s1[i] = round(params[SUB1_PARAM+i].value + clamp(inputs[SUB1_INPUT+i].value, -15.0f, 15.0f)); if (s1[i]>15) s1[i]=15; if (s1[i]<=1) s1[i]=1; s2[i] = round(params[SUB2_PARAM+i].value + clamp(inputs[SUB2_INPUT+i].value, -15.0f, 15.0f)); if (s2[i]>15) s2[i]=15; if (s2[i]<=1) s2[i]=1; VCO[i].setPitch(params[VCO_PARAM+i].value,12*inputs[VCO_INPUT+i].value); SUB1[i].freq=VCO[i].freq/s1[i]; SUB2[i].freq=VCO[i].freq/s2[i]; VCO[i].process(engineGetSampleTime()); SUB1[i].process(engineGetSampleTime()); SUB2[i].process(engineGetSampleTime()); outputs[VCO_OUTPUT + i].value = 2.0f * VCO[i].saw()*params[VCO_VOL_PARAM+i].value; outputs[SUB1_OUTPUT + i].value = 2.0f * SUB1[i].saw()*params[SUB1_VOL_PARAM+i].value; outputs[SUB2_OUTPUT + i].value = 2.0f * SUB2[i].saw()*params[SUB2_VOL_PARAM+i].value; } for (int i = 0; i < 2; i++) { sum += clamp(outputs[VCO_OUTPUT + i].value + outputs[SUB1_OUTPUT + i].value + outputs[SUB2_OUTPUT + i].value,-5.0f,5.0f); } outputs[SUM_OUTPUT].value=sum*params[SUM_VOL_PARAM].value; } struct SuHaWidget : ModuleWidget { SuHaWidget(SuHa *module) : ModuleWidget(module) { setPanel(SVG::load(assetPlugin(plugin, "res/SuHa.svg"))); int KS=50; int JS = 37; float Side=7.5; 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))); /////////////////////////////////////////////////////////////////////////////////// for (int i = 0; i < 2; i++) { addParam(ParamWidget::create(Vec(Side + 6, 87 + i * KS), module, SuHa::VCO_PARAM + i, -54.0, 54.0, 0.0)); addParam(ParamWidget::create(Vec(Side + 6 + KS, 87 +i*KS), module, SuHa::SUB1_PARAM +i, 1.0, 15.0, 1.0)); addParam(ParamWidget::create(Vec(Side + 6 + 2 * KS, 87 +i*KS), module, SuHa::SUB2_PARAM +i, 1.0, 15.0, 1.0)); addParam(ParamWidget::create(Vec(Side + 15, 25 + i*30), module, SuHa::VCO_VOL_PARAM +i, 0.0, 1.0, 0.0)); addParam(ParamWidget::create(Vec(Side + 15 + KS, 25 + i*30), module, SuHa::SUB1_VOL_PARAM +i, 0.0, 1.0, 0.0)); addParam(ParamWidget::create(Vec(Side + 15 + 2 * KS, 25 + i*30), module, SuHa::SUB2_VOL_PARAM +i, 0.0, 1.0, 0.0)); addInput(Port::create(Vec(Side + 11, 215+i*JS), Port::INPUT, module, SuHa::VCO_INPUT +i)); addInput(Port::create(Vec(Side + 11 + KS, 215+i*JS), Port::INPUT, module, SuHa::SUB1_INPUT +i)); addInput(Port::create(Vec(Side + 11 + 2 * KS, 215+i*JS), Port::INPUT, module, SuHa::SUB2_INPUT +i)); addOutput(Port::create(Vec(Side + 11, 215 + 2 * JS+i*JS), Port::OUTPUT, module, SuHa::VCO_OUTPUT +i)); addOutput(Port::create(Vec(Side + 11 + KS, 215 + 2 * JS+i*JS), Port::OUTPUT, module, SuHa::SUB1_OUTPUT +i)); addOutput(Port::create(Vec(Side + 11 + 2 * KS, 215 + 2 * JS+i*JS), Port::OUTPUT, module, SuHa::SUB2_OUTPUT +i)); } addParam(ParamWidget::create(Vec(Side + 40, 180), module, SuHa::SUM_VOL_PARAM, 0.0, 1.0, 0.0)); addOutput(Port::create(Vec(Side + 80, 185), Port::OUTPUT, module, SuHa::SUM_OUTPUT)); ////////////////////////////////////////////////////////////////////////////////////////////////////////// } }; } // namespace rack_plugin_dBiz using namespace rack_plugin_dBiz; RACK_PLUGIN_MODEL_INIT(dBiz, SuHa) { // Specify the Module and ModuleWidget subclass, human-readable // author name for categorization per plugin, module slug (should never // change), human-readable module name, and any number of tags // (found in `include/tags.hpp`) separated by commas. Model *modelSuHa = Model::create("dBiz", "SuHa", "SuHa", OSCILLATOR_TAG); return modelSuHa; }