@@ -4,6 +4,7 @@ | |||||
"author": "VCV", | "author": "VCV", | ||||
"license": "BSD-3-Clause", | "license": "BSD-3-Clause", | ||||
"authorEmail": "contact@vcvrack.com", | "authorEmail": "contact@vcvrack.com", | ||||
"pluginUrl": "https://vcvrack.com/Befaco.html", | |||||
"authorUrl": "https://vcvrack.com/", | "authorUrl": "https://vcvrack.com/", | ||||
"sourceUrl": "https://github.com/VCVRack/Befaco", | "sourceUrl": "https://github.com/VCVRack/Befaco", | ||||
"version": "1.0.0", | "version": "1.0.0", | ||||
@@ -1,5 +1,11 @@ | |||||
#include "Befaco.hpp" | #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 { | struct ABC : Module { | ||||
@@ -32,66 +38,65 @@ struct ABC : Module { | |||||
NUM_LIGHTS | 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 { | struct ABCWidget : ModuleWidget { | ||||
ABCWidget(ABC *module) : ModuleWidget(module) { | 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, 0))); | ||||
addChild(createWidget<Knurlie>(Vec(15, 365))); | 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, 162), module, ABC::OUT1_POS_LIGHT)); | ||||
addChild(createLight<MediumLight<GreenRedLight>>(Vec(37, 329), module, ABC::OUT2_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) { | void init(rack::Plugin *p) { | ||||
plugin = p; | plugin = p; | ||||
p->slug = TOSTRING(SLUG); | |||||
p->version = TOSTRING(VERSION); | |||||
p->addModel(modelEvenVCO); | p->addModel(modelEvenVCO); | ||||
p->addModel(modelRampage); | p->addModel(modelRampage); | ||||
@@ -1,4 +1,5 @@ | |||||
#include "rack0.hpp" | |||||
#include "rack.hpp" | |||||
#include "componentlibrary.hpp" | |||||
using namespace rack; | using namespace rack; | ||||
@@ -17,7 +18,7 @@ extern Model *modelDualAtenuverter; | |||||
struct Knurlie : SVGScrew { | struct Knurlie : SVGScrew { | ||||
Knurlie() { | Knurlie() { | ||||
sw->svg = SVG::load(assetPlugin(plugin, "res/Knurlie.svg")); | |||||
sw->svg = SVG::load(asset::plugin(plugin, "res/Knurlie.svg")); | |||||
sw->wrap(); | sw->wrap(); | ||||
box.size = sw->box.size; | box.size = sw->box.size; | ||||
} | } | ||||
@@ -27,43 +27,47 @@ struct DualAtenuverter : Module { | |||||
NUM_LIGHTS | 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 { | struct DualAtenuverterWidget : ModuleWidget { | ||||
DualAtenuverterWidget(DualAtenuverter *module) : ModuleWidget(module) { | 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, 0))); | ||||
addChild(createWidget<Knurlie>(Vec(15, 365))); | 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, 143), module, DualAtenuverter::OUT1_POS_LIGHT)); | ||||
addChild(createLight<MediumLight<GreenRedLight>>(Vec(33, 311), module, DualAtenuverter::OUT2_POS_LIGHT)); | addChild(createLight<MediumLight<GreenRedLight>>(Vec(33, 311), module, DualAtenuverter::OUT2_POS_LIGHT)); | ||||
@@ -1,6 +1,4 @@ | |||||
#include "Befaco.hpp" | #include "Befaco.hpp" | ||||
#include "dsp/minblep.hpp" | |||||
#include "dsp/filter.hpp" | |||||
struct EvenVCO : Module { | struct EvenVCO : Module { | ||||
@@ -35,127 +33,128 @@ struct EvenVCO : Module { | |||||
/** Whether we are past the pulse width already */ | /** Whether we are past the pulse width already */ | ||||
bool halfPhase = false; | 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 { | struct EvenVCOWidget : ModuleWidget { | ||||
EvenVCOWidget(EvenVCO *module) : ModuleWidget(module) { | 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, 0))); | ||||
addChild(createWidget<Knurlie>(Vec(15, 365))); | addChild(createWidget<Knurlie>(Vec(15, 365))); | ||||
addChild(createWidget<Knurlie>(Vec(15*6, 0))); | addChild(createWidget<Knurlie>(Vec(15*6, 0))); | ||||
addChild(createWidget<Knurlie>(Vec(15*6, 365))); | 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 | 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 { | struct MixerWidget : ModuleWidget { | ||||
MixerWidget(Mixer *module) : ModuleWidget(module) { | 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, 0))); | ||||
addChild(createWidget<Knurlie>(Vec(15, 365))); | 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)); | addChild(createLight<MediumLight<GreenRedLight>>(Vec(32.7, 310), module, Mixer::OUT_POS_LIGHT)); | ||||
} | } | ||||
@@ -1,5 +1,17 @@ | |||||
#include "Befaco.hpp" | #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 { | struct Rampage : Module { | ||||
@@ -63,158 +75,161 @@ struct Rampage : Module { | |||||
float out[2] = {}; | float out[2] = {}; | ||||
bool gate[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 { | struct RampageWidget : ModuleWidget { | ||||
RampageWidget(Rampage *module) : ModuleWidget(module) { | 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(15, 0))); | ||||
addChild(createWidget<Knurlie>(Vec(box.size.x-30, 0))); | addChild(createWidget<Knurlie>(Vec(box.size.x-30, 0))); | ||||
addChild(createWidget<Knurlie>(Vec(15, 365))); | addChild(createWidget<Knurlie>(Vec(15, 365))); | ||||
addChild(createWidget<Knurlie>(Vec(box.size.x-30, 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(132, 167), module, Rampage::COMPARATOR_LIGHT)); | ||||
addChild(createLight<SmallLight<RedLight>>(Vec(123, 174), module, Rampage::MIN_LIGHT)); | addChild(createLight<SmallLight<RedLight>>(Vec(123, 174), module, Rampage::MIN_LIGHT)); | ||||
@@ -21,61 +21,64 @@ struct SlewLimiter : Module { | |||||
float out = 0.0; | 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 { | 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, 0))); | ||||
addChild(createWidget<Knurlie>(Vec(15, 365))); | 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 <string.h> | ||||
#include "Befaco.hpp" | #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" | #include "pffft.h" | ||||
@@ -13,6 +8,7 @@ BINARY(src_SpringReverbIR_pcm); | |||||
static const size_t BLOCK_SIZE = 1024; | static const size_t BLOCK_SIZE = 1024; | ||||
struct SpringReverb : Module { | struct SpringReverb : Module { | ||||
enum ParamIds { | enum ParamIds { | ||||
WET_PARAM, | WET_PARAM, | ||||
@@ -40,131 +36,132 @@ struct SpringReverb : Module { | |||||
NUM_LIGHTS = VU1_LIGHT + 7 | 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 { | struct SpringReverbWidget : ModuleWidget { | ||||
SpringReverbWidget(SpringReverb *module) : ModuleWidget(module) { | 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, 0))); | ||||
addChild(createWidget<Knurlie>(Vec(15, 365))); | addChild(createWidget<Knurlie>(Vec(15, 365))); | ||||
addChild(createWidget<Knurlie>(Vec(15*6, 0))); | addChild(createWidget<Knurlie>(Vec(15*6, 0))); | ||||
addChild(createWidget<Knurlie>(Vec(15*6, 365))); | 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<GreenRedLight>>(Vec(55, 269), module, SpringReverb::PEAK_LIGHT)); | ||||
addChild(createLight<MediumLight<RedLight>>(Vec(55, 113), module, SpringReverb::VU1_LIGHT + 0)); | addChild(createLight<MediumLight<RedLight>>(Vec(55, 113), module, SpringReverb::VU1_LIGHT + 0)); | ||||