| @@ -4,6 +4,7 @@ | |||
| "author": "VCV", | |||
| "license": "BSD-3-Clause", | |||
| "authorEmail": "contact@vcvrack.com", | |||
| "pluginUrl": "https://vcvrack.com/Befaco.html", | |||
| "authorUrl": "https://vcvrack.com/", | |||
| "sourceUrl": "https://github.com/VCVRack/Befaco", | |||
| "version": "1.0.0", | |||
| @@ -1,5 +1,11 @@ | |||
| #include "Befaco.hpp" | |||
| #include "dsp/functions.hpp" | |||
| static float clip(float x) { | |||
| x = clamp(x, -2.f, 2.f); | |||
| return x / std::pow(1.f + std::pow(x, 24.f), 1/24.f); | |||
| } | |||
| struct ABC : Module { | |||
| @@ -32,66 +38,65 @@ struct ABC : Module { | |||
| NUM_LIGHTS | |||
| }; | |||
| ABC() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {} | |||
| void step() override; | |||
| }; | |||
| static float clip(float x) { | |||
| x = clamp(x, -2.0f, 2.0f); | |||
| return x / powf(1.0 + powf(x, 24.0), 1/24.0); | |||
| } | |||
| void ABC::step() { | |||
| float a1 = inputs[A1_INPUT].value; | |||
| float b1 = inputs[B1_INPUT].normalize(5.0) * 2.0*exponentialBipolar(80.0, params[B1_LEVEL_PARAM].value); | |||
| float c1 = inputs[C1_INPUT].normalize(10.0) * exponentialBipolar(80.0, params[C1_LEVEL_PARAM].value); | |||
| float out1 = a1 * b1 / 5.0 + c1; | |||
| float a2 = inputs[A2_INPUT].value; | |||
| float b2 = inputs[B2_INPUT].normalize(5.0) * 2.0*exponentialBipolar(80.0, params[B2_LEVEL_PARAM].value); | |||
| float c2 = inputs[C2_INPUT].normalize(10.0) * exponentialBipolar(80.0, params[C2_LEVEL_PARAM].value); | |||
| float out2 = a2 * b2 / 5.0 + c2; | |||
| // Set outputs | |||
| if (outputs[OUT1_OUTPUT].active) { | |||
| outputs[OUT1_OUTPUT].value = clip(out1 / 10.0) * 10.0; | |||
| } | |||
| else { | |||
| out2 += out1; | |||
| } | |||
| if (outputs[OUT2_OUTPUT].active) { | |||
| outputs[OUT2_OUTPUT].value = clip(out2 / 10.0) * 10.0; | |||
| ABC() { | |||
| config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); | |||
| params[B1_LEVEL_PARAM].config(-1.0, 1.0, 0.0, "B1 Level"); | |||
| params[C1_LEVEL_PARAM].config(-1.0, 1.0, 0.0, "C1 Level"); | |||
| params[B2_LEVEL_PARAM].config(-1.0, 1.0, 0.0, "B2 Level"); | |||
| params[C2_LEVEL_PARAM].config(-1.0, 1.0, 0.0, "C2 Level"); | |||
| } | |||
| // Lights | |||
| lights[OUT1_POS_LIGHT].value = fmaxf(0.0, out1 / 5.0); | |||
| lights[OUT1_NEG_LIGHT].value = fmaxf(0.0, -out1 / 5.0); | |||
| lights[OUT2_POS_LIGHT].value = fmaxf(0.0, out2 / 5.0); | |||
| lights[OUT2_NEG_LIGHT].value = fmaxf(0.0, -out2 / 5.0); | |||
| } | |||
| void step() override { | |||
| float a1 = inputs[A1_INPUT].value; | |||
| float b1 = inputs[B1_INPUT].normalize(5.f) * 2.f*dsp::exponentialBipolar(80.f, params[B1_LEVEL_PARAM].value); | |||
| float c1 = inputs[C1_INPUT].normalize(10.f) * dsp::exponentialBipolar(80.f, params[C1_LEVEL_PARAM].value); | |||
| float out1 = a1 * b1 / 5.f + c1; | |||
| float a2 = inputs[A2_INPUT].value; | |||
| float b2 = inputs[B2_INPUT].normalize(5.f) * 2.f*dsp::exponentialBipolar(80.f, params[B2_LEVEL_PARAM].value); | |||
| float c2 = inputs[C2_INPUT].normalize(10.f) * dsp::exponentialBipolar(80.f, params[C2_LEVEL_PARAM].value); | |||
| float out2 = a2 * b2 / 5.f + c2; | |||
| // Set outputs | |||
| if (outputs[OUT1_OUTPUT].active) { | |||
| outputs[OUT1_OUTPUT].value = clip(out1 / 10.f) * 10.f; | |||
| } | |||
| else { | |||
| out2 += out1; | |||
| } | |||
| if (outputs[OUT2_OUTPUT].active) { | |||
| outputs[OUT2_OUTPUT].value = clip(out2 / 10.f) * 10.f; | |||
| } | |||
| // Lights | |||
| lights[OUT1_POS_LIGHT].setBrightnessSmooth(std::max(0.f, out1 / 5.f)); | |||
| lights[OUT1_NEG_LIGHT].setBrightnessSmooth(std::max(0.f, -out1 / 5.f)); | |||
| lights[OUT2_POS_LIGHT].setBrightnessSmooth(std::max(0.f, out2 / 5.f)); | |||
| lights[OUT2_NEG_LIGHT].setBrightnessSmooth(std::max(0.f, -out2 / 5.f)); | |||
| } | |||
| }; | |||
| struct ABCWidget : ModuleWidget { | |||
| ABCWidget(ABC *module) : ModuleWidget(module) { | |||
| setPanel(SVG::load(assetPlugin(plugin, "res/ABC.svg"))); | |||
| setPanel(SVG::load(asset::plugin(plugin, "res/ABC.svg"))); | |||
| addChild(createWidget<Knurlie>(Vec(15, 0))); | |||
| addChild(createWidget<Knurlie>(Vec(15, 365))); | |||
| addParam(createParam<Davies1900hRedKnob>(Vec(45, 37), module, ABC::B1_LEVEL_PARAM, -1.0, 1.0, 0.0)); | |||
| addParam(createParam<Davies1900hWhiteKnob>(Vec(45, 107), module, ABC::C1_LEVEL_PARAM, -1.0, 1.0, 0.0)); | |||
| addParam(createParam<Davies1900hRedKnob>(Vec(45, 204), module, ABC::B2_LEVEL_PARAM, -1.0, 1.0, 0.0)); | |||
| addParam(createParam<Davies1900hWhiteKnob>(Vec(45, 274), module, ABC::C2_LEVEL_PARAM, -1.0, 1.0, 0.0)); | |||
| addInput(createPort<PJ301MPort>(Vec(7, 28), PortWidget::INPUT, module, ABC::A1_INPUT)); | |||
| addInput(createPort<PJ301MPort>(Vec(7, 70), PortWidget::INPUT, module, ABC::B1_INPUT)); | |||
| addInput(createPort<PJ301MPort>(Vec(7, 112), PortWidget::INPUT, module, ABC::C1_INPUT)); | |||
| addOutput(createPort<PJ301MPort>(Vec(7, 154), PortWidget::OUTPUT, module, ABC::OUT1_OUTPUT)); | |||
| addInput(createPort<PJ301MPort>(Vec(7, 195), PortWidget::INPUT, module, ABC::A2_INPUT)); | |||
| addInput(createPort<PJ301MPort>(Vec(7, 237), PortWidget::INPUT, module, ABC::B2_INPUT)); | |||
| addInput(createPort<PJ301MPort>(Vec(7, 279), PortWidget::INPUT, module, ABC::C2_INPUT)); | |||
| addOutput(createPort<PJ301MPort>(Vec(7, 321), PortWidget::OUTPUT, module, ABC::OUT2_OUTPUT)); | |||
| addParam(createParam<Davies1900hRedKnob>(Vec(45, 37), module, ABC::B1_LEVEL_PARAM)); | |||
| addParam(createParam<Davies1900hWhiteKnob>(Vec(45, 107), module, ABC::C1_LEVEL_PARAM)); | |||
| addParam(createParam<Davies1900hRedKnob>(Vec(45, 204), module, ABC::B2_LEVEL_PARAM)); | |||
| addParam(createParam<Davies1900hWhiteKnob>(Vec(45, 274), module, ABC::C2_LEVEL_PARAM)); | |||
| addInput(createInput<PJ301MPort>(Vec(7, 28), module, ABC::A1_INPUT)); | |||
| addInput(createInput<PJ301MPort>(Vec(7, 70), module, ABC::B1_INPUT)); | |||
| addInput(createInput<PJ301MPort>(Vec(7, 112), module, ABC::C1_INPUT)); | |||
| addOutput(createOutput<PJ301MPort>(Vec(7, 154), module, ABC::OUT1_OUTPUT)); | |||
| addInput(createInput<PJ301MPort>(Vec(7, 195), module, ABC::A2_INPUT)); | |||
| addInput(createInput<PJ301MPort>(Vec(7, 237), module, ABC::B2_INPUT)); | |||
| addInput(createInput<PJ301MPort>(Vec(7, 279), module, ABC::C2_INPUT)); | |||
| addOutput(createOutput<PJ301MPort>(Vec(7, 321), module, ABC::OUT2_OUTPUT)); | |||
| addChild(createLight<MediumLight<GreenRedLight>>(Vec(37, 162), module, ABC::OUT1_POS_LIGHT)); | |||
| addChild(createLight<MediumLight<GreenRedLight>>(Vec(37, 329), module, ABC::OUT2_POS_LIGHT)); | |||
| @@ -5,8 +5,6 @@ Plugin *plugin; | |||
| void init(rack::Plugin *p) { | |||
| plugin = p; | |||
| p->slug = TOSTRING(SLUG); | |||
| p->version = TOSTRING(VERSION); | |||
| p->addModel(modelEvenVCO); | |||
| p->addModel(modelRampage); | |||
| @@ -1,4 +1,5 @@ | |||
| #include "rack0.hpp" | |||
| #include "rack.hpp" | |||
| #include "componentlibrary.hpp" | |||
| using namespace rack; | |||
| @@ -17,7 +18,7 @@ extern Model *modelDualAtenuverter; | |||
| struct Knurlie : SVGScrew { | |||
| Knurlie() { | |||
| sw->svg = SVG::load(assetPlugin(plugin, "res/Knurlie.svg")); | |||
| sw->svg = SVG::load(asset::plugin(plugin, "res/Knurlie.svg")); | |||
| sw->wrap(); | |||
| box.size = sw->box.size; | |||
| } | |||
| @@ -27,43 +27,47 @@ struct DualAtenuverter : Module { | |||
| NUM_LIGHTS | |||
| }; | |||
| DualAtenuverter() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {} | |||
| void step() override; | |||
| }; | |||
| DualAtenuverter() { | |||
| config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); | |||
| params[ATEN1_PARAM].config(-1.0, 1.0, 0.0); | |||
| params[OFFSET1_PARAM].config(-10.0, 10.0, 0.0); | |||
| params[ATEN2_PARAM].config(-1.0, 1.0, 0.0); | |||
| params[OFFSET2_PARAM].config(-10.0, 10.0, 0.0); | |||
| } | |||
| void DualAtenuverter::step() { | |||
| float out1 = inputs[IN1_INPUT].value * params[ATEN1_PARAM].value + params[OFFSET1_PARAM].value; | |||
| float out2 = inputs[IN2_INPUT].value * params[ATEN2_PARAM].value + params[OFFSET2_PARAM].value; | |||
| out1 = clamp(out1, -10.0f, 10.0f); | |||
| out2 = clamp(out2, -10.0f, 10.0f); | |||
| void step() override { | |||
| float out1 = inputs[IN1_INPUT].value * params[ATEN1_PARAM].value + params[OFFSET1_PARAM].value; | |||
| float out2 = inputs[IN2_INPUT].value * params[ATEN2_PARAM].value + params[OFFSET2_PARAM].value; | |||
| out1 = clamp(out1, -10.f, 10.f); | |||
| out2 = clamp(out2, -10.f, 10.f); | |||
| outputs[OUT1_OUTPUT].value = out1; | |||
| outputs[OUT2_OUTPUT].value = out2; | |||
| lights[OUT1_POS_LIGHT].value = fmaxf(0.0, out1 / 5.0); | |||
| lights[OUT1_NEG_LIGHT].value = fmaxf(0.0, -out1 / 5.0); | |||
| lights[OUT2_POS_LIGHT].value = fmaxf(0.0, out2 / 5.0); | |||
| lights[OUT2_NEG_LIGHT].value = fmaxf(0.0, -out2 / 5.0); | |||
| } | |||
| outputs[OUT1_OUTPUT].value = out1; | |||
| outputs[OUT2_OUTPUT].value = out2; | |||
| lights[OUT1_POS_LIGHT].setBrightnessSmooth(std::max(0.f, out1 / 5.f)); | |||
| lights[OUT1_NEG_LIGHT].setBrightnessSmooth(std::max(0.f, -out1 / 5.f)); | |||
| lights[OUT2_POS_LIGHT].setBrightnessSmooth(std::max(0.f, out2 / 5.f)); | |||
| lights[OUT2_NEG_LIGHT].setBrightnessSmooth(std::max(0.f, -out2 / 5.f)); | |||
| } | |||
| }; | |||
| struct DualAtenuverterWidget : ModuleWidget { | |||
| DualAtenuverterWidget(DualAtenuverter *module) : ModuleWidget(module) { | |||
| setPanel(SVG::load(assetPlugin(plugin, "res/DualAtenuverter.svg"))); | |||
| setPanel(SVG::load(asset::plugin(plugin, "res/DualAtenuverter.svg"))); | |||
| addChild(createWidget<Knurlie>(Vec(15, 0))); | |||
| addChild(createWidget<Knurlie>(Vec(15, 365))); | |||
| addParam(createParam<Davies1900hWhiteKnob>(Vec(20, 33), module, DualAtenuverter::ATEN1_PARAM, -1.0, 1.0, 0.0)); | |||
| addParam(createParam<Davies1900hRedKnob>(Vec(20, 91), module, DualAtenuverter::OFFSET1_PARAM, -10.0, 10.0, 0.0)); | |||
| addParam(createParam<Davies1900hWhiteKnob>(Vec(20, 201), module, DualAtenuverter::ATEN2_PARAM, -1.0, 1.0, 0.0)); | |||
| addParam(createParam<Davies1900hRedKnob>(Vec(20, 260), module, DualAtenuverter::OFFSET2_PARAM, -10.0, 10.0, 0.0)); | |||
| addParam(createParam<Davies1900hWhiteKnob>(Vec(20, 33), module, DualAtenuverter::ATEN1_PARAM)); | |||
| addParam(createParam<Davies1900hRedKnob>(Vec(20, 91), module, DualAtenuverter::OFFSET1_PARAM)); | |||
| addParam(createParam<Davies1900hWhiteKnob>(Vec(20, 201), module, DualAtenuverter::ATEN2_PARAM)); | |||
| addParam(createParam<Davies1900hRedKnob>(Vec(20, 260), module, DualAtenuverter::OFFSET2_PARAM)); | |||
| addInput(createPort<PJ301MPort>(Vec(7, 152), PortWidget::INPUT, module, DualAtenuverter::IN1_INPUT)); | |||
| addOutput(createPort<PJ301MPort>(Vec(43, 152), PortWidget::OUTPUT, module, DualAtenuverter::OUT1_OUTPUT)); | |||
| addInput(createInput<PJ301MPort>(Vec(7, 152), module, DualAtenuverter::IN1_INPUT)); | |||
| addOutput(createOutput<PJ301MPort>(Vec(43, 152), module, DualAtenuverter::OUT1_OUTPUT)); | |||
| addInput(createPort<PJ301MPort>(Vec(7, 319), PortWidget::INPUT, module, DualAtenuverter::IN2_INPUT)); | |||
| addOutput(createPort<PJ301MPort>(Vec(43, 319), PortWidget::OUTPUT, module, DualAtenuverter::OUT2_OUTPUT)); | |||
| addInput(createInput<PJ301MPort>(Vec(7, 319), module, DualAtenuverter::IN2_INPUT)); | |||
| addOutput(createOutput<PJ301MPort>(Vec(43, 319), module, DualAtenuverter::OUT2_OUTPUT)); | |||
| addChild(createLight<MediumLight<GreenRedLight>>(Vec(33, 143), module, DualAtenuverter::OUT1_POS_LIGHT)); | |||
| addChild(createLight<MediumLight<GreenRedLight>>(Vec(33, 311), module, DualAtenuverter::OUT2_POS_LIGHT)); | |||
| @@ -1,6 +1,4 @@ | |||
| #include "Befaco.hpp" | |||
| #include "dsp/minblep.hpp" | |||
| #include "dsp/filter.hpp" | |||
| struct EvenVCO : Module { | |||
| @@ -35,127 +33,128 @@ struct EvenVCO : Module { | |||
| /** Whether we are past the pulse width already */ | |||
| bool halfPhase = false; | |||
| MinBLEP<16> triSquareMinBLEP; | |||
| MinBLEP<16> triMinBLEP; | |||
| MinBLEP<16> sineMinBLEP; | |||
| MinBLEP<16> doubleSawMinBLEP; | |||
| MinBLEP<16> sawMinBLEP; | |||
| MinBLEP<16> squareMinBLEP; | |||
| RCFilter triFilter; | |||
| EvenVCO(); | |||
| void step() override; | |||
| }; | |||
| EvenVCO::EvenVCO() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) { | |||
| triSquareMinBLEP.minblep = minblep_16_32; | |||
| triSquareMinBLEP.oversample = 32; | |||
| triMinBLEP.minblep = minblep_16_32; | |||
| triMinBLEP.oversample = 32; | |||
| sineMinBLEP.minblep = minblep_16_32; | |||
| sineMinBLEP.oversample = 32; | |||
| doubleSawMinBLEP.minblep = minblep_16_32; | |||
| doubleSawMinBLEP.oversample = 32; | |||
| sawMinBLEP.minblep = minblep_16_32; | |||
| sawMinBLEP.oversample = 32; | |||
| squareMinBLEP.minblep = minblep_16_32; | |||
| squareMinBLEP.oversample = 32; | |||
| } | |||
| void EvenVCO::step() { | |||
| // Compute frequency, pitch is 1V/oct | |||
| float pitch = 1.0 + roundf(params[OCTAVE_PARAM].value) + params[TUNE_PARAM].value / 12.0; | |||
| pitch += inputs[PITCH1_INPUT].value + inputs[PITCH2_INPUT].value; | |||
| pitch += inputs[FM_INPUT].value / 4.0; | |||
| float freq = 261.626 * powf(2.0, pitch); | |||
| freq = clamp(freq, 0.0f, 20000.0f); | |||
| // Pulse width | |||
| float pw = params[PWM_PARAM].value + inputs[PWM_INPUT].value / 5.0; | |||
| const float minPw = 0.05; | |||
| pw = rescale(clamp(pw, -1.0f, 1.0f), -1.0f, 1.0f, minPw, 1.0f - minPw); | |||
| // Advance phase | |||
| float deltaPhase = clamp(freq * engineGetSampleTime(), 1e-6f, 0.5f); | |||
| float oldPhase = phase; | |||
| phase += deltaPhase; | |||
| if (oldPhase < 0.5 && phase >= 0.5) { | |||
| float crossing = -(phase - 0.5) / deltaPhase; | |||
| triSquareMinBLEP.jump(crossing, 2.0); | |||
| doubleSawMinBLEP.jump(crossing, -2.0); | |||
| } | |||
| if (!halfPhase && phase >= pw) { | |||
| float crossing = -(phase - pw) / deltaPhase; | |||
| squareMinBLEP.jump(crossing, 2.0); | |||
| halfPhase = true; | |||
| dsp::MinBLEP<16> triSquareMinBLEP; | |||
| dsp::MinBLEP<16> triMinBLEP; | |||
| dsp::MinBLEP<16> sineMinBLEP; | |||
| dsp::MinBLEP<16> doubleSawMinBLEP; | |||
| dsp::MinBLEP<16> sawMinBLEP; | |||
| dsp::MinBLEP<16> squareMinBLEP; | |||
| dsp::RCFilter triFilter; | |||
| EvenVCO() { | |||
| config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS); | |||
| params[OCTAVE_PARAM].config(-5.0, 4.0, 0.0, "Octave", " Hz", 2, dsp::FREQ_C4); | |||
| params[TUNE_PARAM].config(-7.0, 7.0, 0.0, "Tune"); | |||
| params[PWM_PARAM].config(-1.0, 1.0, 0.0, "Pulse width"); | |||
| triSquareMinBLEP.minblep = dsp::minblep_16_32; | |||
| triSquareMinBLEP.oversample = 32; | |||
| triMinBLEP.minblep = dsp::minblep_16_32; | |||
| triMinBLEP.oversample = 32; | |||
| sineMinBLEP.minblep = dsp::minblep_16_32; | |||
| sineMinBLEP.oversample = 32; | |||
| doubleSawMinBLEP.minblep = dsp::minblep_16_32; | |||
| doubleSawMinBLEP.oversample = 32; | |||
| sawMinBLEP.minblep = dsp::minblep_16_32; | |||
| sawMinBLEP.oversample = 32; | |||
| squareMinBLEP.minblep = dsp::minblep_16_32; | |||
| squareMinBLEP.oversample = 32; | |||
| } | |||
| // Reset phase if at end of cycle | |||
| if (phase >= 1.0) { | |||
| phase -= 1.0; | |||
| float crossing = -phase / deltaPhase; | |||
| triSquareMinBLEP.jump(crossing, -2.0); | |||
| doubleSawMinBLEP.jump(crossing, -2.0); | |||
| squareMinBLEP.jump(crossing, -2.0); | |||
| sawMinBLEP.jump(crossing, -2.0); | |||
| halfPhase = false; | |||
| void step() override { | |||
| // Compute frequency, pitch is 1V/oct | |||
| float pitch = 1.f + std::round(params[OCTAVE_PARAM].value) + params[TUNE_PARAM].value / 12.f; | |||
| pitch += inputs[PITCH1_INPUT].value + inputs[PITCH2_INPUT].value; | |||
| pitch += inputs[FM_INPUT].value / 4.f; | |||
| float freq = dsp::FREQ_C4 * std::pow(2.f, pitch); | |||
| freq = clamp(freq, 0.f, 20000.f); | |||
| // Pulse width | |||
| float pw = params[PWM_PARAM].value + inputs[PWM_INPUT].value / 5.f; | |||
| const float minPw = 0.05; | |||
| pw = rescale(clamp(pw, -1.f, 1.f), -1.f, 1.f, minPw, 1.f - minPw); | |||
| // Advance phase | |||
| float deltaPhase = clamp(freq * app()->engine->getSampleTime(), 1e-6f, 0.5f); | |||
| float oldPhase = phase; | |||
| phase += deltaPhase; | |||
| if (oldPhase < 0.5 && phase >= 0.5) { | |||
| float crossing = -(phase - 0.5) / deltaPhase; | |||
| triSquareMinBLEP.jump(crossing, 2.f); | |||
| doubleSawMinBLEP.jump(crossing, -2.f); | |||
| } | |||
| if (!halfPhase && phase >= pw) { | |||
| float crossing = -(phase - pw) / deltaPhase; | |||
| squareMinBLEP.jump(crossing, 2.f); | |||
| halfPhase = true; | |||
| } | |||
| // Reset phase if at end of cycle | |||
| if (phase >= 1.f) { | |||
| phase -= 1.f; | |||
| float crossing = -phase / deltaPhase; | |||
| triSquareMinBLEP.jump(crossing, -2.f); | |||
| doubleSawMinBLEP.jump(crossing, -2.f); | |||
| squareMinBLEP.jump(crossing, -2.f); | |||
| sawMinBLEP.jump(crossing, -2.f); | |||
| halfPhase = false; | |||
| } | |||
| // Outputs | |||
| float triSquare = (phase < 0.5) ? -1.f : 1.f; | |||
| triSquare += triSquareMinBLEP.shift(); | |||
| // Integrate square for triangle | |||
| tri += 4.f * triSquare * freq * app()->engine->getSampleTime(); | |||
| tri *= (1.f - 40.f * app()->engine->getSampleTime()); | |||
| float sine = -std::cos(2*M_PI * phase); | |||
| float doubleSaw = (phase < 0.5) ? (-1.f + 4.f*phase) : (-1.f + 4.f*(phase - 0.5)); | |||
| doubleSaw += doubleSawMinBLEP.shift(); | |||
| float even = 0.55 * (doubleSaw + 1.27 * sine); | |||
| float saw = -1.f + 2.f*phase; | |||
| saw += sawMinBLEP.shift(); | |||
| float square = (phase < pw) ? -1.f : 1.f; | |||
| square += squareMinBLEP.shift(); | |||
| // Set outputs | |||
| outputs[TRI_OUTPUT].value = 5.f*tri; | |||
| outputs[SINE_OUTPUT].value = 5.f*sine; | |||
| outputs[EVEN_OUTPUT].value = 5.f*even; | |||
| outputs[SAW_OUTPUT].value = 5.f*saw; | |||
| outputs[SQUARE_OUTPUT].value = 5.f*square; | |||
| } | |||
| // Outputs | |||
| float triSquare = (phase < 0.5) ? -1.0 : 1.0; | |||
| triSquare += triSquareMinBLEP.shift(); | |||
| // Integrate square for triangle | |||
| tri += 4.0 * triSquare * freq * engineGetSampleTime(); | |||
| tri *= (1.0 - 40.0 * engineGetSampleTime()); | |||
| float sine = -cosf(2*M_PI * phase); | |||
| float doubleSaw = (phase < 0.5) ? (-1.0 + 4.0*phase) : (-1.0 + 4.0*(phase - 0.5)); | |||
| doubleSaw += doubleSawMinBLEP.shift(); | |||
| float even = 0.55 * (doubleSaw + 1.27 * sine); | |||
| float saw = -1.0 + 2.0*phase; | |||
| saw += sawMinBLEP.shift(); | |||
| float square = (phase < pw) ? -1.0 : 1.0; | |||
| square += squareMinBLEP.shift(); | |||
| // Set outputs | |||
| outputs[TRI_OUTPUT].value = 5.0*tri; | |||
| outputs[SINE_OUTPUT].value = 5.0*sine; | |||
| outputs[EVEN_OUTPUT].value = 5.0*even; | |||
| outputs[SAW_OUTPUT].value = 5.0*saw; | |||
| outputs[SQUARE_OUTPUT].value = 5.0*square; | |||
| } | |||
| }; | |||
| struct EvenVCOWidget : ModuleWidget { | |||
| EvenVCOWidget(EvenVCO *module) : ModuleWidget(module) { | |||
| setPanel(SVG::load(assetPlugin(plugin, "res/EvenVCO.svg"))); | |||
| setPanel(SVG::load(asset::plugin(plugin, "res/EvenVCO.svg"))); | |||
| addChild(createWidget<Knurlie>(Vec(15, 0))); | |||
| addChild(createWidget<Knurlie>(Vec(15, 365))); | |||
| addChild(createWidget<Knurlie>(Vec(15*6, 0))); | |||
| addChild(createWidget<Knurlie>(Vec(15*6, 365))); | |||
| addParam(createParam<BefacoBigSnapKnob>(Vec(22, 32), module, EvenVCO::OCTAVE_PARAM, -5.0, 4.0, 0.0)); | |||
| addParam(createParam<BefacoTinyKnob>(Vec(73, 131), module, EvenVCO::TUNE_PARAM, -7.0, 7.0, 0.0)); | |||
| addParam(createParam<Davies1900hRedKnob>(Vec(16, 230), module, EvenVCO::PWM_PARAM, -1.0, 1.0, 0.0)); | |||
| addParam(createParam<BefacoBigSnapKnob>(Vec(22, 32), module, EvenVCO::OCTAVE_PARAM)); | |||
| addParam(createParam<BefacoTinyKnob>(Vec(73, 131), module, EvenVCO::TUNE_PARAM)); | |||
| addParam(createParam<Davies1900hRedKnob>(Vec(16, 230), module, EvenVCO::PWM_PARAM)); | |||
| addInput(createPort<PJ301MPort>(Vec(8, 120), PortWidget::INPUT, module, EvenVCO::PITCH1_INPUT)); | |||
| addInput(createPort<PJ301MPort>(Vec(19, 157), PortWidget::INPUT, module, EvenVCO::PITCH2_INPUT)); | |||
| addInput(createPort<PJ301MPort>(Vec(48, 183), PortWidget::INPUT, module, EvenVCO::FM_INPUT)); | |||
| addInput(createPort<PJ301MPort>(Vec(86, 189), PortWidget::INPUT, module, EvenVCO::SYNC_INPUT)); | |||
| addInput(createInput<PJ301MPort>(Vec(8, 120), module, EvenVCO::PITCH1_INPUT)); | |||
| addInput(createInput<PJ301MPort>(Vec(19, 157), module, EvenVCO::PITCH2_INPUT)); | |||
| addInput(createInput<PJ301MPort>(Vec(48, 183), module, EvenVCO::FM_INPUT)); | |||
| addInput(createInput<PJ301MPort>(Vec(86, 189), module, EvenVCO::SYNC_INPUT)); | |||
| addInput(createPort<PJ301MPort>(Vec(72, 236), PortWidget::INPUT, module, EvenVCO::PWM_INPUT)); | |||
| addInput(createInput<PJ301MPort>(Vec(72, 236), module, EvenVCO::PWM_INPUT)); | |||
| addOutput(createPort<PJ301MPort>(Vec(10, 283), PortWidget::OUTPUT, module, EvenVCO::TRI_OUTPUT)); | |||
| addOutput(createPort<PJ301MPort>(Vec(87, 283), PortWidget::OUTPUT, module, EvenVCO::SINE_OUTPUT)); | |||
| addOutput(createPort<PJ301MPort>(Vec(48, 306), PortWidget::OUTPUT, module, EvenVCO::EVEN_OUTPUT)); | |||
| addOutput(createPort<PJ301MPort>(Vec(10, 327), PortWidget::OUTPUT, module, EvenVCO::SAW_OUTPUT)); | |||
| addOutput(createPort<PJ301MPort>(Vec(87, 327), PortWidget::OUTPUT, module, EvenVCO::SQUARE_OUTPUT)); | |||
| addOutput(createOutput<PJ301MPort>(Vec(10, 283), module, EvenVCO::TRI_OUTPUT)); | |||
| addOutput(createOutput<PJ301MPort>(Vec(87, 283), module, EvenVCO::SINE_OUTPUT)); | |||
| addOutput(createOutput<PJ301MPort>(Vec(48, 306), module, EvenVCO::EVEN_OUTPUT)); | |||
| addOutput(createOutput<PJ301MPort>(Vec(10, 327), module, EvenVCO::SAW_OUTPUT)); | |||
| addOutput(createOutput<PJ301MPort>(Vec(87, 327), module, EvenVCO::SQUARE_OUTPUT)); | |||
| } | |||
| }; | |||
| @@ -27,45 +27,51 @@ struct Mixer : Module { | |||
| NUM_LIGHTS | |||
| }; | |||
| Mixer() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {} | |||
| void step() override; | |||
| }; | |||
| Mixer() { | |||
| config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); | |||
| params[CH1_PARAM].config(0.0, 1.0, 0.0); | |||
| params[CH2_PARAM].config(0.0, 1.0, 0.0); | |||
| params[CH3_PARAM].config(0.0, 1.0, 0.0); | |||
| params[CH4_PARAM].config(0.0, 1.0, 0.0); | |||
| } | |||
| void step() override { | |||
| float in1 = inputs[IN1_INPUT].value * params[CH1_PARAM].value; | |||
| float in2 = inputs[IN2_INPUT].value * params[CH2_PARAM].value; | |||
| float in3 = inputs[IN3_INPUT].value * params[CH3_PARAM].value; | |||
| float in4 = inputs[IN4_INPUT].value * params[CH4_PARAM].value; | |||
| float out = in1 + in2 + in3 + in4; | |||
| outputs[OUT1_OUTPUT].value = out; | |||
| outputs[OUT2_OUTPUT].value = -out; | |||
| lights[OUT_POS_LIGHT].setBrightnessSmooth(out / 5.f); | |||
| lights[OUT_NEG_LIGHT].setBrightnessSmooth(-out / 5.f); | |||
| } | |||
| }; | |||
| void Mixer::step() { | |||
| float in1 = inputs[IN1_INPUT].value * params[CH1_PARAM].value; | |||
| float in2 = inputs[IN2_INPUT].value * params[CH2_PARAM].value; | |||
| float in3 = inputs[IN3_INPUT].value * params[CH3_PARAM].value; | |||
| float in4 = inputs[IN4_INPUT].value * params[CH4_PARAM].value; | |||
| float out = in1 + in2 + in3 + in4; | |||
| outputs[OUT1_OUTPUT].value = out; | |||
| outputs[OUT2_OUTPUT].value = -out; | |||
| lights[OUT_POS_LIGHT].setBrightnessSmooth(out / 5.0); | |||
| lights[OUT_NEG_LIGHT].setBrightnessSmooth(-out / 5.0); | |||
| } | |||
| struct MixerWidget : ModuleWidget { | |||
| MixerWidget(Mixer *module) : ModuleWidget(module) { | |||
| setPanel(SVG::load(assetPlugin(plugin, "res/Mixer.svg"))); | |||
| setPanel(SVG::load(asset::plugin(plugin, "res/Mixer.svg"))); | |||
| addChild(createWidget<Knurlie>(Vec(15, 0))); | |||
| addChild(createWidget<Knurlie>(Vec(15, 365))); | |||
| addParam(createParam<Davies1900hWhiteKnob>(Vec(19, 32), module, Mixer::CH1_PARAM, 0.0, 1.0, 0.0)); | |||
| addParam(createParam<Davies1900hWhiteKnob>(Vec(19, 85), module, Mixer::CH2_PARAM, 0.0, 1.0, 0.0)); | |||
| addParam(createParam<Davies1900hWhiteKnob>(Vec(19, 137), module, Mixer::CH3_PARAM, 0.0, 1.0, 0.0)); | |||
| addParam(createParam<Davies1900hWhiteKnob>(Vec(19, 190), module, Mixer::CH4_PARAM, 0.0, 1.0, 0.0)); | |||
| addParam(createParam<Davies1900hWhiteKnob>(Vec(19, 32), module, Mixer::CH1_PARAM)); | |||
| addParam(createParam<Davies1900hWhiteKnob>(Vec(19, 85), module, Mixer::CH2_PARAM)); | |||
| addParam(createParam<Davies1900hWhiteKnob>(Vec(19, 137), module, Mixer::CH3_PARAM)); | |||
| addParam(createParam<Davies1900hWhiteKnob>(Vec(19, 190), module, Mixer::CH4_PARAM)); | |||
| addInput(createPort<PJ301MPort>(Vec(7, 242), PortWidget::INPUT, module, Mixer::IN1_INPUT)); | |||
| addInput(createPort<PJ301MPort>(Vec(43, 242), PortWidget::INPUT, module, Mixer::IN2_INPUT)); | |||
| addInput(createInput<PJ301MPort>(Vec(7, 242), module, Mixer::IN1_INPUT)); | |||
| addInput(createInput<PJ301MPort>(Vec(43, 242), module, Mixer::IN2_INPUT)); | |||
| addInput(createPort<PJ301MPort>(Vec(7, 281), PortWidget::INPUT, module, Mixer::IN3_INPUT)); | |||
| addInput(createPort<PJ301MPort>(Vec(43, 281), PortWidget::INPUT, module, Mixer::IN4_INPUT)); | |||
| addInput(createInput<PJ301MPort>(Vec(7, 281), module, Mixer::IN3_INPUT)); | |||
| addInput(createInput<PJ301MPort>(Vec(43, 281), module, Mixer::IN4_INPUT)); | |||
| addOutput(createPort<PJ301MPort>(Vec(7, 324), PortWidget::OUTPUT, module, Mixer::OUT1_OUTPUT)); | |||
| addOutput(createPort<PJ301MPort>(Vec(43, 324), PortWidget::OUTPUT, module, Mixer::OUT2_OUTPUT)); | |||
| addOutput(createOutput<PJ301MPort>(Vec(7, 324), module, Mixer::OUT1_OUTPUT)); | |||
| addOutput(createOutput<PJ301MPort>(Vec(43, 324), module, Mixer::OUT2_OUTPUT)); | |||
| addChild(createLight<MediumLight<GreenRedLight>>(Vec(32.7, 310), module, Mixer::OUT_POS_LIGHT)); | |||
| } | |||
| @@ -1,5 +1,17 @@ | |||
| #include "Befaco.hpp" | |||
| #include "dsp/digital.hpp" | |||
| static float shapeDelta(float delta, float tau, float shape) { | |||
| float lin = sgn(delta) * 10.f / tau; | |||
| if (shape < 0.f) { | |||
| float log = sgn(delta) * 40.f / tau / (std::abs(delta) + 1.f); | |||
| return crossfade(lin, log, -shape * 0.95f); | |||
| } | |||
| else { | |||
| float exp = M_E * delta / tau; | |||
| return crossfade(lin, exp, shape * 0.90f); | |||
| } | |||
| } | |||
| struct Rampage : Module { | |||
| @@ -63,158 +75,161 @@ struct Rampage : Module { | |||
| float out[2] = {}; | |||
| bool gate[2] = {}; | |||
| SchmittTrigger trigger[2]; | |||
| PulseGenerator endOfCyclePulse[2]; | |||
| Rampage() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {} | |||
| void step() override; | |||
| }; | |||
| static float shapeDelta(float delta, float tau, float shape) { | |||
| float lin = sgn(delta) * 10.0 / tau; | |||
| if (shape < 0.0) { | |||
| float log = sgn(delta) * 40.0 / tau / (fabsf(delta) + 1.0); | |||
| return crossfade(lin, log, -shape * 0.95f); | |||
| } | |||
| else { | |||
| float exp = M_E * delta / tau; | |||
| return crossfade(lin, exp, shape * 0.90f); | |||
| dsp::SchmittTrigger trigger[2]; | |||
| dsp::PulseGenerator endOfCyclePulse[2]; | |||
| Rampage() { | |||
| config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); | |||
| params[RANGE_A_PARAM].config(0.0, 2.0, 0.0); | |||
| params[SHAPE_A_PARAM].config(-1.0, 1.0, 0.0); | |||
| params[TRIGG_A_PARAM].config(0.0, 1.0, 0.0); | |||
| params[RISE_A_PARAM].config(0.0, 1.0, 0.0); | |||
| params[FALL_A_PARAM].config(0.0, 1.0, 0.0); | |||
| params[CYCLE_A_PARAM].config(0.0, 1.0, 0.0); | |||
| params[RANGE_B_PARAM].config(0.0, 2.0, 0.0); | |||
| params[SHAPE_B_PARAM].config(-1.0, 1.0, 0.0); | |||
| params[TRIGG_B_PARAM].config(0.0, 1.0, 0.0); | |||
| params[RISE_B_PARAM].config(0.0, 1.0, 0.0); | |||
| params[FALL_B_PARAM].config(0.0, 1.0, 0.0); | |||
| params[CYCLE_B_PARAM].config(0.0, 1.0, 0.0); | |||
| params[BALANCE_PARAM].config(0.0, 1.0, 0.5); | |||
| } | |||
| } | |||
| void Rampage::step() { | |||
| for (int c = 0; c < 2; c++) { | |||
| float in = inputs[IN_A_INPUT + c].value; | |||
| if (trigger[c].process(params[TRIGG_A_PARAM + c].value * 10.0 + inputs[TRIGG_A_INPUT + c].value / 2.0)) { | |||
| gate[c] = true; | |||
| } | |||
| if (gate[c]) { | |||
| in = 10.0; | |||
| } | |||
| void step() override { | |||
| for (int c = 0; c < 2; c++) { | |||
| float in = inputs[IN_A_INPUT + c].value; | |||
| if (trigger[c].process(params[TRIGG_A_PARAM + c].value * 10.0 + inputs[TRIGG_A_INPUT + c].value / 2.0)) { | |||
| gate[c] = true; | |||
| } | |||
| if (gate[c]) { | |||
| in = 10.0; | |||
| } | |||
| float shape = params[SHAPE_A_PARAM + c].value; | |||
| float delta = in - out[c]; | |||
| float shape = params[SHAPE_A_PARAM + c].value; | |||
| float delta = in - out[c]; | |||
| // Integrator | |||
| float minTime; | |||
| switch ((int) params[RANGE_A_PARAM + c].value) { | |||
| case 0: minTime = 1e-2; break; | |||
| case 1: minTime = 1e-3; break; | |||
| default: minTime = 1e-1; break; | |||
| } | |||
| // Integrator | |||
| float minTime; | |||
| switch ((int) params[RANGE_A_PARAM + c].value) { | |||
| case 0: minTime = 1e-2; break; | |||
| case 1: minTime = 1e-3; break; | |||
| default: minTime = 1e-1; break; | |||
| } | |||
| bool rising = false; | |||
| bool falling = false; | |||
| if (delta > 0) { | |||
| // Rise | |||
| float riseCv = params[RISE_A_PARAM + c].value - inputs[EXP_CV_A_INPUT + c].value / 10.0 + inputs[RISE_CV_A_INPUT + c].value / 10.0; | |||
| riseCv = clamp(riseCv, 0.0f, 1.0f); | |||
| float rise = minTime * powf(2.0, riseCv * 10.0); | |||
| out[c] += shapeDelta(delta, rise, shape) * engineGetSampleTime(); | |||
| rising = (in - out[c] > 1e-3); | |||
| if (!rising) { | |||
| gate[c] = false; | |||
| bool rising = false; | |||
| bool falling = false; | |||
| if (delta > 0) { | |||
| // Rise | |||
| float riseCv = params[RISE_A_PARAM + c].value - inputs[EXP_CV_A_INPUT + c].value / 10.0 + inputs[RISE_CV_A_INPUT + c].value / 10.0; | |||
| riseCv = clamp(riseCv, 0.0f, 1.0f); | |||
| float rise = minTime * std::pow(2.0, riseCv * 10.0); | |||
| out[c] += shapeDelta(delta, rise, shape) * app()->engine->getSampleTime(); | |||
| rising = (in - out[c] > 1e-3); | |||
| if (!rising) { | |||
| gate[c] = false; | |||
| } | |||
| } | |||
| } | |||
| else if (delta < 0) { | |||
| // Fall | |||
| float fallCv = params[FALL_A_PARAM + c].value - inputs[EXP_CV_A_INPUT + c].value / 10.0 + inputs[FALL_CV_A_INPUT + c].value / 10.0; | |||
| fallCv = clamp(fallCv, 0.0f, 1.0f); | |||
| float fall = minTime * powf(2.0, fallCv * 10.0); | |||
| out[c] += shapeDelta(delta, fall, shape) * engineGetSampleTime(); | |||
| falling = (in - out[c] < -1e-3); | |||
| if (!falling) { | |||
| // End of cycle, check if we should turn the gate back on (cycle mode) | |||
| endOfCyclePulse[c].trigger(1e-3); | |||
| if (params[CYCLE_A_PARAM + c].value * 10.0 + inputs[CYCLE_A_INPUT + c].value >= 4.0) { | |||
| gate[c] = true; | |||
| else if (delta < 0) { | |||
| // Fall | |||
| float fallCv = params[FALL_A_PARAM + c].value - inputs[EXP_CV_A_INPUT + c].value / 10.0 + inputs[FALL_CV_A_INPUT + c].value / 10.0; | |||
| fallCv = clamp(fallCv, 0.0f, 1.0f); | |||
| float fall = minTime * std::pow(2.0, fallCv * 10.0); | |||
| out[c] += shapeDelta(delta, fall, shape) * app()->engine->getSampleTime(); | |||
| falling = (in - out[c] < -1e-3); | |||
| if (!falling) { | |||
| // End of cycle, check if we should turn the gate back on (cycle mode) | |||
| endOfCyclePulse[c].trigger(1e-3); | |||
| if (params[CYCLE_A_PARAM + c].value * 10.0 + inputs[CYCLE_A_INPUT + c].value >= 4.0) { | |||
| gate[c] = true; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| else { | |||
| gate[c] = false; | |||
| } | |||
| else { | |||
| gate[c] = false; | |||
| } | |||
| if (!rising && !falling) { | |||
| out[c] = in; | |||
| if (!rising && !falling) { | |||
| out[c] = in; | |||
| } | |||
| outputs[RISING_A_OUTPUT + c].value = (rising ? 10.0 : 0.0); | |||
| outputs[FALLING_A_OUTPUT + c].value = (falling ? 10.0 : 0.0); | |||
| lights[RISING_A_LIGHT + c].setBrightnessSmooth(rising ? 1.0 : 0.0); | |||
| lights[FALLING_A_LIGHT + c].setBrightnessSmooth(falling ? 1.0 : 0.0); | |||
| outputs[EOC_A_OUTPUT + c].value = (endOfCyclePulse[c].process(app()->engine->getSampleTime()) ? 10.0 : 0.0); | |||
| outputs[OUT_A_OUTPUT + c].value = out[c]; | |||
| lights[OUT_A_LIGHT + c].setBrightnessSmooth(out[c] / 10.0); | |||
| } | |||
| outputs[RISING_A_OUTPUT + c].value = (rising ? 10.0 : 0.0); | |||
| outputs[FALLING_A_OUTPUT + c].value = (falling ? 10.0 : 0.0); | |||
| lights[RISING_A_LIGHT + c].setBrightnessSmooth(rising ? 1.0 : 0.0); | |||
| lights[FALLING_A_LIGHT + c].setBrightnessSmooth(falling ? 1.0 : 0.0); | |||
| outputs[EOC_A_OUTPUT + c].value = (endOfCyclePulse[c].process(engineGetSampleTime()) ? 10.0 : 0.0); | |||
| outputs[OUT_A_OUTPUT + c].value = out[c]; | |||
| lights[OUT_A_LIGHT + c].setBrightnessSmooth(out[c] / 10.0); | |||
| // Logic | |||
| float balance = params[BALANCE_PARAM].value; | |||
| float a = out[0]; | |||
| float b = out[1]; | |||
| if (balance < 0.5) | |||
| b *= 2.0 * balance; | |||
| else if (balance > 0.5) | |||
| a *= 2.0 * (1.0 - balance); | |||
| outputs[COMPARATOR_OUTPUT].value = (b > a ? 10.0 : 0.0); | |||
| outputs[MIN_OUTPUT].value = std::min(a, b); | |||
| outputs[MAX_OUTPUT].value = std::max(a, b); | |||
| // Lights | |||
| lights[COMPARATOR_LIGHT].setBrightnessSmooth(outputs[COMPARATOR_OUTPUT].value / 10.0); | |||
| lights[MIN_LIGHT].setBrightnessSmooth(outputs[MIN_OUTPUT].value / 10.0); | |||
| lights[MAX_LIGHT].setBrightnessSmooth(outputs[MAX_OUTPUT].value / 10.0); | |||
| } | |||
| }; | |||
| // Logic | |||
| float balance = params[BALANCE_PARAM].value; | |||
| float a = out[0]; | |||
| float b = out[1]; | |||
| if (balance < 0.5) | |||
| b *= 2.0 * balance; | |||
| else if (balance > 0.5) | |||
| a *= 2.0 * (1.0 - balance); | |||
| outputs[COMPARATOR_OUTPUT].value = (b > a ? 10.0 : 0.0); | |||
| outputs[MIN_OUTPUT].value = fminf(a, b); | |||
| outputs[MAX_OUTPUT].value = fmaxf(a, b); | |||
| // Lights | |||
| lights[COMPARATOR_LIGHT].setBrightnessSmooth(outputs[COMPARATOR_OUTPUT].value / 10.0); | |||
| lights[MIN_LIGHT].setBrightnessSmooth(outputs[MIN_OUTPUT].value / 10.0); | |||
| lights[MAX_LIGHT].setBrightnessSmooth(outputs[MAX_OUTPUT].value / 10.0); | |||
| } | |||
| struct RampageWidget : ModuleWidget { | |||
| RampageWidget(Rampage *module) : ModuleWidget(module) { | |||
| setPanel(SVG::load(assetPlugin(plugin, "res/Rampage.svg"))); | |||
| setPanel(SVG::load(asset::plugin(plugin, "res/Rampage.svg"))); | |||
| addChild(createWidget<Knurlie>(Vec(15, 0))); | |||
| addChild(createWidget<Knurlie>(Vec(box.size.x-30, 0))); | |||
| addChild(createWidget<Knurlie>(Vec(15, 365))); | |||
| addChild(createWidget<Knurlie>(Vec(box.size.x-30, 365))); | |||
| addInput(createPort<PJ301MPort>(Vec(14, 30), PortWidget::INPUT, module, Rampage::IN_A_INPUT)); | |||
| addInput(createPort<PJ301MPort>(Vec(52, 37), PortWidget::INPUT, module, Rampage::TRIGG_A_INPUT)); | |||
| addInput(createPort<PJ301MPort>(Vec(8, 268), PortWidget::INPUT, module, Rampage::RISE_CV_A_INPUT)); | |||
| addInput(createPort<PJ301MPort>(Vec(67, 268), PortWidget::INPUT, module, Rampage::FALL_CV_A_INPUT)); | |||
| addInput(createPort<PJ301MPort>(Vec(38, 297), PortWidget::INPUT, module, Rampage::EXP_CV_A_INPUT)); | |||
| addInput(createPort<PJ301MPort>(Vec(102, 290), PortWidget::INPUT, module, Rampage::CYCLE_A_INPUT)); | |||
| addInput(createPort<PJ301MPort>(Vec(229, 30), PortWidget::INPUT, module, Rampage::IN_B_INPUT)); | |||
| addInput(createPort<PJ301MPort>(Vec(192, 37), PortWidget::INPUT, module, Rampage::TRIGG_B_INPUT)); | |||
| addInput(createPort<PJ301MPort>(Vec(176, 268), PortWidget::INPUT, module, Rampage::RISE_CV_B_INPUT)); | |||
| addInput(createPort<PJ301MPort>(Vec(237, 268), PortWidget::INPUT, module, Rampage::FALL_CV_B_INPUT)); | |||
| addInput(createPort<PJ301MPort>(Vec(207, 297), PortWidget::INPUT, module, Rampage::EXP_CV_B_INPUT)); | |||
| addInput(createPort<PJ301MPort>(Vec(143, 290), PortWidget::INPUT, module, Rampage::CYCLE_B_INPUT)); | |||
| addParam(createParam<BefacoSwitch>(Vec(94, 32), module, Rampage::RANGE_A_PARAM, 0.0, 2.0, 0.0)); | |||
| addParam(createParam<BefacoTinyKnob>(Vec(27, 90), module, Rampage::SHAPE_A_PARAM, -1.0, 1.0, 0.0)); | |||
| addParam(createParam<BefacoPush>(Vec(72, 82), module, Rampage::TRIGG_A_PARAM, 0.0, 1.0, 0.0)); | |||
| addParam(createParam<BefacoSlidePot>(Vec(16, 135), module, Rampage::RISE_A_PARAM, 0.0, 1.0, 0.0)); | |||
| addParam(createParam<BefacoSlidePot>(Vec(57, 135), module, Rampage::FALL_A_PARAM, 0.0, 1.0, 0.0)); | |||
| addParam(createParam<BefacoSwitch>(Vec(101, 238), module, Rampage::CYCLE_A_PARAM, 0.0, 1.0, 0.0)); | |||
| addParam(createParam<BefacoSwitch>(Vec(147, 32), module, Rampage::RANGE_B_PARAM, 0.0, 2.0, 0.0)); | |||
| addParam(createParam<BefacoTinyKnob>(Vec(217, 90), module, Rampage::SHAPE_B_PARAM, -1.0, 1.0, 0.0)); | |||
| addParam(createParam<BefacoPush>(Vec(170, 82), module, Rampage::TRIGG_B_PARAM, 0.0, 1.0, 0.0)); | |||
| addParam(createParam<BefacoSlidePot>(Vec(197, 135), module, Rampage::RISE_B_PARAM, 0.0, 1.0, 0.0)); | |||
| addParam(createParam<BefacoSlidePot>(Vec(238, 135), module, Rampage::FALL_B_PARAM, 0.0, 1.0, 0.0)); | |||
| addParam(createParam<BefacoSwitch>(Vec(141, 238), module, Rampage::CYCLE_B_PARAM, 0.0, 1.0, 0.0)); | |||
| addParam(createParam<Davies1900hWhiteKnob>(Vec(117, 76), module, Rampage::BALANCE_PARAM, 0.0, 1.0, 0.5)); | |||
| addOutput(createPort<PJ301MPort>(Vec(8, 326), PortWidget::OUTPUT, module, Rampage::RISING_A_OUTPUT)); | |||
| addOutput(createPort<PJ301MPort>(Vec(68, 326), PortWidget::OUTPUT, module, Rampage::FALLING_A_OUTPUT)); | |||
| addOutput(createPort<PJ301MPort>(Vec(104, 326), PortWidget::OUTPUT, module, Rampage::EOC_A_OUTPUT)); | |||
| addOutput(createPort<PJ301MPort>(Vec(102, 195), PortWidget::OUTPUT, module, Rampage::OUT_A_OUTPUT)); | |||
| addOutput(createPort<PJ301MPort>(Vec(177, 326), PortWidget::OUTPUT, module, Rampage::RISING_B_OUTPUT)); | |||
| addOutput(createPort<PJ301MPort>(Vec(237, 326), PortWidget::OUTPUT, module, Rampage::FALLING_B_OUTPUT)); | |||
| addOutput(createPort<PJ301MPort>(Vec(140, 326), PortWidget::OUTPUT, module, Rampage::EOC_B_OUTPUT)); | |||
| addOutput(createPort<PJ301MPort>(Vec(142, 195), PortWidget::OUTPUT, module, Rampage::OUT_B_OUTPUT)); | |||
| addOutput(createPort<PJ301MPort>(Vec(122, 133), PortWidget::OUTPUT, module, Rampage::COMPARATOR_OUTPUT)); | |||
| addOutput(createPort<PJ301MPort>(Vec(89, 157), PortWidget::OUTPUT, module, Rampage::MIN_OUTPUT)); | |||
| addOutput(createPort<PJ301MPort>(Vec(155, 157), PortWidget::OUTPUT, module, Rampage::MAX_OUTPUT)); | |||
| addParam(createParam<BefacoSwitch>(Vec(94, 32), module, Rampage::RANGE_A_PARAM)); | |||
| addParam(createParam<BefacoTinyKnob>(Vec(27, 90), module, Rampage::SHAPE_A_PARAM)); | |||
| addParam(createParam<BefacoPush>(Vec(72, 82), module, Rampage::TRIGG_A_PARAM)); | |||
| addParam(createParam<BefacoSlidePot>(Vec(16, 135), module, Rampage::RISE_A_PARAM)); | |||
| addParam(createParam<BefacoSlidePot>(Vec(57, 135), module, Rampage::FALL_A_PARAM)); | |||
| addParam(createParam<BefacoSwitch>(Vec(101, 238), module, Rampage::CYCLE_A_PARAM)); | |||
| addParam(createParam<BefacoSwitch>(Vec(147, 32), module, Rampage::RANGE_B_PARAM)); | |||
| addParam(createParam<BefacoTinyKnob>(Vec(217, 90), module, Rampage::SHAPE_B_PARAM)); | |||
| addParam(createParam<BefacoPush>(Vec(170, 82), module, Rampage::TRIGG_B_PARAM)); | |||
| addParam(createParam<BefacoSlidePot>(Vec(197, 135), module, Rampage::RISE_B_PARAM)); | |||
| addParam(createParam<BefacoSlidePot>(Vec(238, 135), module, Rampage::FALL_B_PARAM)); | |||
| addParam(createParam<BefacoSwitch>(Vec(141, 238), module, Rampage::CYCLE_B_PARAM)); | |||
| addParam(createParam<Davies1900hWhiteKnob>(Vec(117, 76), module, Rampage::BALANCE_PARAM)); | |||
| addInput(createInput<PJ301MPort>(Vec(14, 30), module, Rampage::IN_A_INPUT)); | |||
| addInput(createInput<PJ301MPort>(Vec(52, 37), module, Rampage::TRIGG_A_INPUT)); | |||
| addInput(createInput<PJ301MPort>(Vec(8, 268), module, Rampage::RISE_CV_A_INPUT)); | |||
| addInput(createInput<PJ301MPort>(Vec(67, 268), module, Rampage::FALL_CV_A_INPUT)); | |||
| addInput(createInput<PJ301MPort>(Vec(38, 297), module, Rampage::EXP_CV_A_INPUT)); | |||
| addInput(createInput<PJ301MPort>(Vec(102, 290), module, Rampage::CYCLE_A_INPUT)); | |||
| addInput(createInput<PJ301MPort>(Vec(229, 30), module, Rampage::IN_B_INPUT)); | |||
| addInput(createInput<PJ301MPort>(Vec(192, 37), module, Rampage::TRIGG_B_INPUT)); | |||
| addInput(createInput<PJ301MPort>(Vec(176, 268), module, Rampage::RISE_CV_B_INPUT)); | |||
| addInput(createInput<PJ301MPort>(Vec(237, 268), module, Rampage::FALL_CV_B_INPUT)); | |||
| addInput(createInput<PJ301MPort>(Vec(207, 297), module, Rampage::EXP_CV_B_INPUT)); | |||
| addInput(createInput<PJ301MPort>(Vec(143, 290), module, Rampage::CYCLE_B_INPUT)); | |||
| addOutput(createOutput<PJ301MPort>(Vec(8, 326), module, Rampage::RISING_A_OUTPUT)); | |||
| addOutput(createOutput<PJ301MPort>(Vec(68, 326), module, Rampage::FALLING_A_OUTPUT)); | |||
| addOutput(createOutput<PJ301MPort>(Vec(104, 326), module, Rampage::EOC_A_OUTPUT)); | |||
| addOutput(createOutput<PJ301MPort>(Vec(102, 195), module, Rampage::OUT_A_OUTPUT)); | |||
| addOutput(createOutput<PJ301MPort>(Vec(177, 326), module, Rampage::RISING_B_OUTPUT)); | |||
| addOutput(createOutput<PJ301MPort>(Vec(237, 326), module, Rampage::FALLING_B_OUTPUT)); | |||
| addOutput(createOutput<PJ301MPort>(Vec(140, 326), module, Rampage::EOC_B_OUTPUT)); | |||
| addOutput(createOutput<PJ301MPort>(Vec(142, 195), module, Rampage::OUT_B_OUTPUT)); | |||
| addOutput(createOutput<PJ301MPort>(Vec(122, 133), module, Rampage::COMPARATOR_OUTPUT)); | |||
| addOutput(createOutput<PJ301MPort>(Vec(89, 157), module, Rampage::MIN_OUTPUT)); | |||
| addOutput(createOutput<PJ301MPort>(Vec(155, 157), module, Rampage::MAX_OUTPUT)); | |||
| addChild(createLight<SmallLight<RedLight>>(Vec(132, 167), module, Rampage::COMPARATOR_LIGHT)); | |||
| addChild(createLight<SmallLight<RedLight>>(Vec(123, 174), module, Rampage::MIN_LIGHT)); | |||
| @@ -21,61 +21,64 @@ struct SlewLimiter : Module { | |||
| float out = 0.0; | |||
| SlewLimiter() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {} | |||
| void step() override; | |||
| }; | |||
| void ::SlewLimiter::step() { | |||
| float in = inputs[IN_INPUT].value; | |||
| float shape = params[SHAPE_PARAM].value; | |||
| // minimum and maximum slopes in volts per second | |||
| const float slewMin = 0.1; | |||
| const float slewMax = 10000.0; | |||
| // Amount of extra slew per voltage difference | |||
| const float shapeScale = 1/10.0; | |||
| // Rise | |||
| if (in > out) { | |||
| float rise = inputs[RISE_INPUT].value / 10.0 + params[RISE_PARAM].value; | |||
| float slew = slewMax * powf(slewMin / slewMax, rise); | |||
| out += slew * crossfade(1.0f, shapeScale * (in - out), shape) * engineGetSampleTime(); | |||
| if (out > in) | |||
| out = in; | |||
| } | |||
| // Fall | |||
| else if (in < out) { | |||
| float fall = inputs[FALL_INPUT].value / 10.0 + params[FALL_PARAM].value; | |||
| float slew = slewMax * powf(slewMin / slewMax, fall); | |||
| out -= slew * crossfade(1.0f, shapeScale * (out - in), shape) * engineGetSampleTime(); | |||
| if (out < in) | |||
| out = in; | |||
| SlewLimiter() { | |||
| config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS); | |||
| params[SHAPE_PARAM].config(0.0, 1.0, 0.0); | |||
| params[RISE_PARAM].config(0.0, 1.0, 0.0); | |||
| params[FALL_PARAM].config(0.0, 1.0, 0.0); | |||
| } | |||
| outputs[OUT_OUTPUT].value = out; | |||
| } | |||
| void step() override { | |||
| float in = inputs[IN_INPUT].value; | |||
| float shape = params[SHAPE_PARAM].value; | |||
| // minimum and maximum slopes in volts per second | |||
| const float slewMin = 0.1; | |||
| const float slewMax = 10000.f; | |||
| // Amount of extra slew per voltage difference | |||
| const float shapeScale = 1/10.f; | |||
| // Rise | |||
| if (in > out) { | |||
| float rise = inputs[RISE_INPUT].value / 10.f + params[RISE_PARAM].value; | |||
| float slew = slewMax * powf(slewMin / slewMax, rise); | |||
| out += slew * crossfade(1.f, shapeScale * (in - out), shape) * app()->engine->getSampleTime(); | |||
| if (out > in) | |||
| out = in; | |||
| } | |||
| // Fall | |||
| else if (in < out) { | |||
| float fall = inputs[FALL_INPUT].value / 10.f + params[FALL_PARAM].value; | |||
| float slew = slewMax * powf(slewMin / slewMax, fall); | |||
| out -= slew * crossfade(1.f, shapeScale * (out - in), shape) * app()->engine->getSampleTime(); | |||
| if (out < in) | |||
| out = in; | |||
| } | |||
| outputs[OUT_OUTPUT].value = out; | |||
| } | |||
| }; | |||
| struct SlewLimiterWidget : ModuleWidget { | |||
| SlewLimiterWidget(SlewLimiter *module) : ModuleWidget(module) { | |||
| setPanel(SVG::load(assetPlugin(plugin, "res/SlewLimiter.svg"))); | |||
| SlewLimiterWidget(::SlewLimiter *module) : ModuleWidget(module) { | |||
| setPanel(SVG::load(asset::plugin(plugin, "res/SlewLimiter.svg"))); | |||
| addChild(createWidget<Knurlie>(Vec(15, 0))); | |||
| addChild(createWidget<Knurlie>(Vec(15, 365))); | |||
| addParam(createParam<Davies1900hWhiteKnob>(Vec(27, 39), module, ::SlewLimiter::SHAPE_PARAM, 0.0, 1.0, 0.0)); | |||
| addParam(createParam<Davies1900hWhiteKnob>(Vec(27, 39), module, ::SlewLimiter::SHAPE_PARAM)); | |||
| addParam(createParam<BefacoSlidePot>(Vec(15, 102), module, ::SlewLimiter::RISE_PARAM, 0.0, 1.0, 0.0)); | |||
| addParam(createParam<BefacoSlidePot>(Vec(60, 102), module, ::SlewLimiter::FALL_PARAM, 0.0, 1.0, 0.0)); | |||
| addParam(createParam<BefacoSlidePot>(Vec(15, 102), module, ::SlewLimiter::RISE_PARAM)); | |||
| addParam(createParam<BefacoSlidePot>(Vec(60, 102), module, ::SlewLimiter::FALL_PARAM)); | |||
| addInput(createPort<PJ301MPort>(Vec(10, 273), PortWidget::INPUT, module, ::SlewLimiter::RISE_INPUT)); | |||
| addInput(createPort<PJ301MPort>(Vec(55, 273), PortWidget::INPUT, module, ::SlewLimiter::FALL_INPUT)); | |||
| addInput(createInput<PJ301MPort>(Vec(10, 273), module, ::SlewLimiter::RISE_INPUT)); | |||
| addInput(createInput<PJ301MPort>(Vec(55, 273), module, ::SlewLimiter::FALL_INPUT)); | |||
| addInput(createPort<PJ301MPort>(Vec(10, 323), PortWidget::INPUT, module, ::SlewLimiter::IN_INPUT)); | |||
| addOutput(createPort<PJ301MPort>(Vec(55, 323), PortWidget::OUTPUT, module, ::SlewLimiter::OUT_OUTPUT)); | |||
| addInput(createInput<PJ301MPort>(Vec(10, 323), module, ::SlewLimiter::IN_INPUT)); | |||
| addOutput(createOutput<PJ301MPort>(Vec(55, 323), module, ::SlewLimiter::OUT_OUTPUT)); | |||
| } | |||
| }; | |||
| Model *modelSlewLimiter = createModel<SlewLimiter, SlewLimiterWidget>("SlewLimiter"); | |||
| Model *modelSlewLimiter = createModel<::SlewLimiter, SlewLimiterWidget>("SlewLimiter"); | |||
| @@ -1,10 +1,5 @@ | |||
| #include <string.h> | |||
| #include "Befaco.hpp" | |||
| #include "dsp/functions.hpp" | |||
| #include "dsp/samplerate.hpp" | |||
| #include "dsp/ringbuffer.hpp" | |||
| #include "dsp/filter.hpp" | |||
| #include "dsp/fir.hpp" | |||
| #include "pffft.h" | |||
| @@ -13,6 +8,7 @@ BINARY(src_SpringReverbIR_pcm); | |||
| static const size_t BLOCK_SIZE = 1024; | |||
| struct SpringReverb : Module { | |||
| enum ParamIds { | |||
| WET_PARAM, | |||
| @@ -40,131 +36,132 @@ struct SpringReverb : Module { | |||
| NUM_LIGHTS = VU1_LIGHT + 7 | |||
| }; | |||
| RealTimeConvolver *convolver = NULL; | |||
| SampleRateConverter<1> inputSrc; | |||
| SampleRateConverter<1> outputSrc; | |||
| DoubleRingBuffer<Frame<1>, 16*BLOCK_SIZE> inputBuffer; | |||
| DoubleRingBuffer<Frame<1>, 16*BLOCK_SIZE> outputBuffer; | |||
| dsp::RealTimeConvolver *convolver = NULL; | |||
| dsp::SampleRateConverter<1> inputSrc; | |||
| dsp::SampleRateConverter<1> outputSrc; | |||
| dsp::DoubleRingBuffer<dsp::Frame<1>, 16*BLOCK_SIZE> inputBuffer; | |||
| dsp::DoubleRingBuffer<dsp::Frame<1>, 16*BLOCK_SIZE> outputBuffer; | |||
| RCFilter dryFilter; | |||
| PeakFilter vuFilter; | |||
| PeakFilter lightFilter; | |||
| dsp::RCFilter dryFilter; | |||
| dsp::PeakFilter vuFilter; | |||
| dsp::PeakFilter lightFilter; | |||
| SpringReverb(); | |||
| ~SpringReverb(); | |||
| void step() override; | |||
| }; | |||
| SpringReverb() { | |||
| config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); | |||
| params[WET_PARAM].config(0.0, 1.0, 0.5); | |||
| params[LEVEL1_PARAM].config(0.0, 1.0, 0.0); | |||
| params[LEVEL2_PARAM].config(0.0, 1.0, 0.0); | |||
| params[HPF_PARAM].config(0.0, 1.0, 0.5); | |||
| convolver = new dsp::RealTimeConvolver(BLOCK_SIZE); | |||
| SpringReverb::SpringReverb() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { | |||
| convolver = new RealTimeConvolver(BLOCK_SIZE); | |||
| const float *kernel = (const float*) BINARY_START(src_SpringReverbIR_pcm); | |||
| size_t kernelLen = BINARY_SIZE(src_SpringReverbIR_pcm) / sizeof(float); | |||
| convolver->setKernel(kernel, kernelLen); | |||
| } | |||
| SpringReverb::~SpringReverb() { | |||
| delete convolver; | |||
| } | |||
| void SpringReverb::step() { | |||
| float in1 = inputs[IN1_INPUT].value; | |||
| float in2 = inputs[IN2_INPUT].value; | |||
| const float levelScale = 0.030; | |||
| const float levelBase = 25.0; | |||
| float level1 = levelScale * exponentialBipolar(levelBase, params[LEVEL1_PARAM].value) * inputs[CV1_INPUT].normalize(10.0) / 10.0; | |||
| float level2 = levelScale * exponentialBipolar(levelBase, params[LEVEL2_PARAM].value) * inputs[CV2_INPUT].normalize(10.0) / 10.0; | |||
| float dry = in1 * level1 + in2 * level2; | |||
| // HPF on dry | |||
| float dryCutoff = 200.0 * powf(20.0, params[HPF_PARAM].value) * engineGetSampleTime(); | |||
| dryFilter.setCutoff(dryCutoff); | |||
| dryFilter.process(dry); | |||
| // Add dry to input buffer | |||
| if (!inputBuffer.full()) { | |||
| Frame<1> inputFrame; | |||
| inputFrame.samples[0] = dryFilter.highpass(); | |||
| inputBuffer.push(inputFrame); | |||
| const float *kernel = (const float*) BINARY_START(src_SpringReverbIR_pcm); | |||
| size_t kernelLen = BINARY_SIZE(src_SpringReverbIR_pcm) / sizeof(float); | |||
| convolver->setKernel(kernel, kernelLen); | |||
| } | |||
| ~SpringReverb() { | |||
| delete convolver; | |||
| } | |||
| if (outputBuffer.empty()) { | |||
| float input[BLOCK_SIZE] = {}; | |||
| float output[BLOCK_SIZE]; | |||
| // Convert input buffer | |||
| { | |||
| inputSrc.setRates(engineGetSampleRate(), 48000); | |||
| int inLen = inputBuffer.size(); | |||
| int outLen = BLOCK_SIZE; | |||
| inputSrc.process(inputBuffer.startData(), &inLen, (Frame<1>*) input, &outLen); | |||
| inputBuffer.startIncr(inLen); | |||
| void step() override { | |||
| float in1 = inputs[IN1_INPUT].value; | |||
| float in2 = inputs[IN2_INPUT].value; | |||
| const float levelScale = 0.030; | |||
| const float levelBase = 25.0; | |||
| float level1 = levelScale * dsp::exponentialBipolar(levelBase, params[LEVEL1_PARAM].value) * inputs[CV1_INPUT].normalize(10.0) / 10.0; | |||
| float level2 = levelScale * dsp::exponentialBipolar(levelBase, params[LEVEL2_PARAM].value) * inputs[CV2_INPUT].normalize(10.0) / 10.0; | |||
| float dry = in1 * level1 + in2 * level2; | |||
| // HPF on dry | |||
| float dryCutoff = 200.0 * std::pow(20.0, params[HPF_PARAM].value) * app()->engine->getSampleTime(); | |||
| dryFilter.setCutoff(dryCutoff); | |||
| dryFilter.process(dry); | |||
| // Add dry to input buffer | |||
| if (!inputBuffer.full()) { | |||
| dsp::Frame<1> inputFrame; | |||
| inputFrame.samples[0] = dryFilter.highpass(); | |||
| inputBuffer.push(inputFrame); | |||
| } | |||
| // Convolve block | |||
| convolver->processBlock(input, output); | |||
| // Convert output buffer | |||
| { | |||
| outputSrc.setRates(48000, engineGetSampleRate()); | |||
| int inLen = BLOCK_SIZE; | |||
| int outLen = outputBuffer.capacity(); | |||
| outputSrc.process((Frame<1>*) output, &inLen, outputBuffer.endData(), &outLen); | |||
| outputBuffer.endIncr(outLen); | |||
| if (outputBuffer.empty()) { | |||
| float input[BLOCK_SIZE] = {}; | |||
| float output[BLOCK_SIZE]; | |||
| // Convert input buffer | |||
| { | |||
| inputSrc.setRates(app()->engine->getSampleRate(), 48000); | |||
| int inLen = inputBuffer.size(); | |||
| int outLen = BLOCK_SIZE; | |||
| inputSrc.process(inputBuffer.startData(), &inLen, (dsp::Frame<1>*) input, &outLen); | |||
| inputBuffer.startIncr(inLen); | |||
| } | |||
| // Convolve block | |||
| convolver->processBlock(input, output); | |||
| // Convert output buffer | |||
| { | |||
| outputSrc.setRates(48000, app()->engine->getSampleRate()); | |||
| int inLen = BLOCK_SIZE; | |||
| int outLen = outputBuffer.capacity(); | |||
| outputSrc.process((dsp::Frame<1>*) output, &inLen, outputBuffer.endData(), &outLen); | |||
| outputBuffer.endIncr(outLen); | |||
| } | |||
| } | |||
| } | |||
| // Set output | |||
| if (outputBuffer.empty()) | |||
| return; | |||
| float wet = outputBuffer.shift().samples[0]; | |||
| float balance = clamp(params[WET_PARAM].value + inputs[MIX_CV_INPUT].value / 10.0f, 0.0f, 1.0f); | |||
| float mix = crossfade(in1, wet, balance); | |||
| outputs[WET_OUTPUT].value = clamp(wet, -10.0f, 10.0f); | |||
| outputs[MIX_OUTPUT].value = clamp(mix, -10.0f, 10.0f); | |||
| // Set lights | |||
| float lightRate = 5.0 * engineGetSampleTime(); | |||
| vuFilter.setRate(lightRate); | |||
| vuFilter.process(fabsf(wet)); | |||
| lightFilter.setRate(lightRate); | |||
| lightFilter.process(fabsf(dry*50.0)); | |||
| float vuValue = vuFilter.peak(); | |||
| for (int i = 0; i < 7; i++) { | |||
| float light = powf(1.413, i) * vuValue / 10.0 - 1.0; | |||
| lights[VU1_LIGHT + i].value = clamp(light, 0.0f, 1.0f); | |||
| // Set output | |||
| if (outputBuffer.empty()) | |||
| return; | |||
| float wet = outputBuffer.shift().samples[0]; | |||
| float balance = clamp(params[WET_PARAM].value + inputs[MIX_CV_INPUT].value / 10.0f, 0.0f, 1.0f); | |||
| float mix = crossfade(in1, wet, balance); | |||
| outputs[WET_OUTPUT].value = clamp(wet, -10.0f, 10.0f); | |||
| outputs[MIX_OUTPUT].value = clamp(mix, -10.0f, 10.0f); | |||
| // Set lights | |||
| float lightRate = 5.0 * app()->engine->getSampleTime(); | |||
| vuFilter.setRate(lightRate); | |||
| vuFilter.process(std::abs(wet)); | |||
| lightFilter.setRate(lightRate); | |||
| lightFilter.process(std::abs(dry*50.0)); | |||
| float vuValue = vuFilter.peak(); | |||
| for (int i = 0; i < 7; i++) { | |||
| float light = std::pow(1.413, i) * vuValue / 10.0 - 1.0; | |||
| lights[VU1_LIGHT + i].value = clamp(light, 0.0f, 1.0f); | |||
| } | |||
| lights[PEAK_LIGHT].value = lightFilter.peak(); | |||
| } | |||
| lights[PEAK_LIGHT].value = lightFilter.peak(); | |||
| } | |||
| }; | |||
| struct SpringReverbWidget : ModuleWidget { | |||
| SpringReverbWidget(SpringReverb *module) : ModuleWidget(module) { | |||
| setPanel(SVG::load(assetPlugin(plugin, "res/SpringReverb.svg"))); | |||
| setPanel(SVG::load(asset::plugin(plugin, "res/SpringReverb.svg"))); | |||
| addChild(createWidget<Knurlie>(Vec(15, 0))); | |||
| addChild(createWidget<Knurlie>(Vec(15, 365))); | |||
| addChild(createWidget<Knurlie>(Vec(15*6, 0))); | |||
| addChild(createWidget<Knurlie>(Vec(15*6, 365))); | |||
| addParam(createParam<BefacoBigKnob>(Vec(22, 29), module, SpringReverb::WET_PARAM, 0.0, 1.0, 0.5)); | |||
| addParam(createParam<BefacoBigKnob>(Vec(22, 29), module, SpringReverb::WET_PARAM)); | |||
| addParam(createParam<BefacoSlidePot>(Vec(12, 116), module, SpringReverb::LEVEL1_PARAM, 0.0, 1.0, 0.0)); | |||
| addParam(createParam<BefacoSlidePot>(Vec(93, 116), module, SpringReverb::LEVEL2_PARAM, 0.0, 1.0, 0.0)); | |||
| addParam(createParam<BefacoSlidePot>(Vec(12, 116), module, SpringReverb::LEVEL1_PARAM)); | |||
| addParam(createParam<BefacoSlidePot>(Vec(93, 116), module, SpringReverb::LEVEL2_PARAM)); | |||
| addParam(createParam<Davies1900hWhiteKnob>(Vec(42, 210), module, SpringReverb::HPF_PARAM, 0.0, 1.0, 0.5)); | |||
| addParam(createParam<Davies1900hWhiteKnob>(Vec(42, 210), module, SpringReverb::HPF_PARAM)); | |||
| addInput(createPort<PJ301MPort>(Vec(7, 243), PortWidget::INPUT, module, SpringReverb::CV1_INPUT)); | |||
| addInput(createPort<PJ301MPort>(Vec(88, 243), PortWidget::INPUT, module, SpringReverb::CV2_INPUT)); | |||
| addInput(createPort<PJ301MPort>(Vec(27, 281), PortWidget::INPUT, module, SpringReverb::IN1_INPUT)); | |||
| addInput(createPort<PJ301MPort>(Vec(67, 281), PortWidget::INPUT, module, SpringReverb::IN2_INPUT)); | |||
| addInput(createInput<PJ301MPort>(Vec(7, 243), module, SpringReverb::CV1_INPUT)); | |||
| addInput(createInput<PJ301MPort>(Vec(88, 243), module, SpringReverb::CV2_INPUT)); | |||
| addInput(createInput<PJ301MPort>(Vec(27, 281), module, SpringReverb::IN1_INPUT)); | |||
| addInput(createInput<PJ301MPort>(Vec(67, 281), module, SpringReverb::IN2_INPUT)); | |||
| addOutput(createPort<PJ301MPort>(Vec(7, 317), PortWidget::OUTPUT, module, SpringReverb::MIX_OUTPUT)); | |||
| addInput(createPort<PJ301MPort>(Vec(47, 324), PortWidget::INPUT, module, SpringReverb::MIX_CV_INPUT)); | |||
| addOutput(createPort<PJ301MPort>(Vec(88, 317), PortWidget::OUTPUT, module, SpringReverb::WET_OUTPUT)); | |||
| addOutput(createOutput<PJ301MPort>(Vec(7, 317), module, SpringReverb::MIX_OUTPUT)); | |||
| addInput(createInput<PJ301MPort>(Vec(47, 324), module, SpringReverb::MIX_CV_INPUT)); | |||
| addOutput(createOutput<PJ301MPort>(Vec(88, 317), module, SpringReverb::WET_OUTPUT)); | |||
| addChild(createLight<MediumLight<GreenRedLight>>(Vec(55, 269), module, SpringReverb::PEAK_LIGHT)); | |||
| addChild(createLight<MediumLight<RedLight>>(Vec(55, 113), module, SpringReverb::VU1_LIGHT + 0)); | |||