Browse Source

Update to Rack v1 API

tags/v1.1.0
Andrew Belt 6 years ago
parent
commit
599c602f04
10 changed files with 516 additions and 487 deletions
  1. +1
    -0
      plugin.json
  2. +56
    -51
      src/ABC.cpp
  3. +0
    -2
      src/Befaco.cpp
  4. +3
    -2
      src/Befaco.hpp
  5. +29
    -25
      src/DualAtenuverter.cpp
  6. +106
    -107
      src/EvenVCO.cpp
  7. +31
    -25
      src/Mixer.cpp
  8. +145
    -130
      src/Rampage.cpp
  9. +45
    -42
      src/SlewLimiter.cpp
  10. +100
    -103
      src/SpringReverb.cpp

+ 1
- 0
plugin.json View File

@@ -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",


+ 56
- 51
src/ABC.cpp View File

@@ -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));


+ 0
- 2
src/Befaco.cpp View File

@@ -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);


+ 3
- 2
src/Befaco.hpp View File

@@ -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;
}


+ 29
- 25
src/DualAtenuverter.cpp View File

@@ -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));


+ 106
- 107
src/EvenVCO.cpp View File

@@ -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));
}
};



+ 31
- 25
src/Mixer.cpp View File

@@ -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));
}


+ 145
- 130
src/Rampage.cpp View File

@@ -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));


+ 45
- 42
src/SlewLimiter.cpp View File

@@ -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");

+ 100
- 103
src/SpringReverb.cpp View File

@@ -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));


Loading…
Cancel
Save