Browse Source

Bandit initial commit

Fix bypass polyphony
pull/55/head
hemmer 6 months ago
parent
commit
9c050c8c9f
7 changed files with 2205 additions and 25 deletions
  1. +14
    -0
      plugin.json
  2. +150
    -0
      res/components/VCVBezelBig.svg
  3. +1739
    -0
      res/panels/Bandit.svg
  4. +208
    -0
      src/Bandit.cpp
  5. +92
    -25
      src/Bypass.cpp
  6. +1
    -0
      src/plugin.cpp
  7. +1
    -0
      src/plugin.hpp

+ 14
- 0
plugin.json View File

@@ -341,6 +341,20 @@
"tags": [
"Hardware clone",
"Mixer",
"Polyphonic",
"Utility"
]
},
{
"slug": "Bandit",
"name": "Bandit",
"description": "A spectral processing playground.",
"tags": [
"Equalizer",
"Filter",
"Hardware clone",
"Mixer",
"Polyphonic",
"Utility"
]
}


+ 150
- 0
res/components/VCVBezelBig.svg View File

@@ -0,0 +1,150 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 25.2.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->

<svg
version="1.0"
id="svg64581"
x="0px"
y="0px"
width="34.015747"
height="34.015747"
viewBox="0 0 34.015747 34.015748"
enable-background="new 0 0 36.02325 36.0188"
xml:space="preserve"
sodipodi:docname="VCVBezelBig.svg"
inkscape:version="1.3.2 (091e20e, 2023-11-25)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs7" />
<sodipodi:namedview
bordercolor="#666666"
borderopacity="1.0"
fit-margin-bottom="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-top="0"
id="base"
inkscape:current-layer="svg64581"
inkscape:cx="-101.01525"
inkscape:cy="10.101525"
inkscape:document-units="mm"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:window-height="1301"
inkscape:window-maximized="0"
inkscape:window-width="2560"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:zoom="1.979899"
pagecolor="#ffffff"
showgrid="false"
inkscape:showpageshadow="2"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1">
</sodipodi:namedview>
<g
id="g7"
transform="matrix(0.94421282,0,0,0.94421046,3.5880087e-4,9.4421046e-5)">
<g
id="g5959-5_106_"
transform="translate(301.93513,1189.951)">
<path
id="path5961-3_112_"
inkscape:connector-curvature="0"
d="m -265.91,-1171.937 c 0,9.9474 -8.06683,18.0115 -18.01407,18.0115 -9.94595,0 -18.01144,-8.0641 -18.01144,-18.0115 0,-9.9513 8.06549,-18.0141 18.01141,-18.0141 9.94726,10e-5 18.0141,8.0628 18.0141,18.0141" />
</g>
<g
id="g5959-5_105_"
transform="translate(301.93513,1189.951)">
<linearGradient
id="path5961-3_1_"
gradientUnits="userSpaceOnUse"
x1="-590.1001"
y1="-2191.8545"
x2="-557.04907"
y2="-2191.8545"
gradientTransform="rotate(90,-938.70655,-1537.0705)">
<stop
offset="0"
style="stop-color:#787878"
id="stop1" />
<stop
offset="1"
style="stop-color:#474747"
id="stop2" />
</linearGradient>
<path
id="path5961-3_111_"
inkscape:connector-curvature="0"
fill="url(#path5961-3_1_)"
d="m -300.44827,-1171.9395 c 0,-9.1261 7.40079,-16.5244 16.52676,-16.5244 9.12473,0 16.52429,7.3984 16.52429,16.5244 0,9.1297 -7.39957,16.5268 -16.52432,16.5268 -9.12595,0 -16.52673,-7.3971 -16.52673,-16.5268"
style="fill:url(#path5961-3_1_)" />
</g>
<g
opacity="0.21"
id="g4">
<g
id="g5959-5_104_"
transform="translate(301.93513,1189.951)">
<linearGradient
id="path5961-3_2_"
gradientUnits="userSpaceOnUse"
x1="-299.58844"
y1="-1111.948"
x2="-268.70374"
y2="-1111.948"
gradientTransform="rotate(-90,-314.02952,-1142.0548)">
<stop
offset="0.00559"
style="stop-color:#6B6B6B"
id="stop3" />
<stop
offset="1"
style="stop-color:#DEDEDE"
id="stop4" />
</linearGradient>
<path
id="path5961-3_110_"
inkscape:connector-curvature="0"
fill="url(#path5961-3_2_)"
d="m -283.92389,-1156.496 c -8.52792,0 -15.44122,-6.9156 -15.44122,-15.4434 0,-8.5267 6.9133,-15.4412 15.44122,-15.4412 8.53131,0 15.44348,6.9145 15.44348,15.4412 0,8.5278 -6.91214,15.4434 -15.44348,15.4434"
style="fill:url(#path5961-3_2_)" />
</g>
</g>
<g
id="g6">
<g
id="g5959-5_103_"
transform="translate(301.93513,1189.951)">
<linearGradient
id="path5961-3_3_"
gradientUnits="userSpaceOnUse"
x1="-298.98605"
y1="-1111.948"
x2="-269.30612"
y2="-1111.948"
gradientTransform="rotate(-90,-314.02952,-1142.0548)">
<stop
offset="0.00559"
style="stop-color:#5B5B5B"
id="stop5" />
<stop
offset="1"
style="stop-color:#6C6C6C"
id="stop6" />
</linearGradient>
<path
id="path5961-3_109_"
inkscape:connector-curvature="0"
fill="url(#path5961-3_3_)"
d="m -283.92386,-1157.0984 c -8.19525,0 -14.83887,-6.6459 -14.83887,-14.8409 0,-8.1941 6.64362,-14.839 14.83887,-14.839 8.19852,0 14.84106,6.6449 14.84106,14.8388 1e-5,8.1952 -6.64251,14.8411 -14.84106,14.8411"
style="fill:url(#path5961-3_3_)" />
</g>
</g>
</g>
</svg>

+ 1739
- 0
res/panels/Bandit.svg
File diff suppressed because it is too large
View File


+ 208
- 0
src/Bandit.cpp View File

@@ -0,0 +1,208 @@
#include "plugin.hpp"

using namespace simd;

struct Bandit : Module {
enum ParamId {
LOW_GAIN_PARAM,
LOW_MID_GAIN_PARAM,
HIGH_MID_GAIN_PARAM,
HIGH_GAIN_PARAM,
PARAMS_LEN
};
enum InputId {
LOW_INPUT,
LOW_MID_INPUT,
HIGH_MID_INPUT,
HIGH_INPUT,
LOW_RETURN_INPUT,
LOW_MID_RETURN_INPUT,
HIGH_MID_RETURN_INPUT,
HIGH_RETURN_INPUT,
LOW_CV_INPUT,
LOW_MID_CV_INPUT,
HIGH_MID_CV_INPUT,
HIGH_CV_INPUT,
ALL_INPUT,
ALL_CV_INPUT,
INPUTS_LEN
};
enum OutputId {
LOW_OUTPUT,
LOW_MID_OUTPUT,
HIGH_MID_OUTPUT,
HIGH_OUTPUT,
MIX_OUTPUT,
OUTPUTS_LEN
};
enum LightId {
ENUMS(MIX_CLIP_LIGHT, 3),
ENUMS(MIX_LIGHT, 3),
LIGHTS_LEN
};

dsp::TBiquadFilter<float_4> filterLow[4], filterLowMid[4], filterHighMid[4], filterHigh[4];

Bandit() {
config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN);
configParam(LOW_GAIN_PARAM, -1.f, 1.f, 0.f, "Low gain");
configParam(LOW_MID_GAIN_PARAM, -1.f, 1.f, 0.f, "Low mid gain");
configParam(HIGH_MID_GAIN_PARAM, -1.f, 1.f, 0.f, "High mid gain");
configParam(HIGH_GAIN_PARAM, -1.f, 1.f, 0.f, "High gain");

configInput(LOW_INPUT, "Low");
configInput(LOW_MID_INPUT, "Low mid");
configInput(HIGH_MID_INPUT, "High mid");
configInput(HIGH_INPUT, "High");
configInput(LOW_RETURN_INPUT, "Low return");
configInput(LOW_MID_RETURN_INPUT, "Low mid return");
configInput(HIGH_MID_RETURN_INPUT, "High mid return");
configInput(HIGH_RETURN_INPUT, "High return");
configInput(LOW_CV_INPUT, "Low CV");
configInput(LOW_MID_CV_INPUT, "Low mid CV");
configInput(HIGH_MID_CV_INPUT, "High mid CV");
configInput(HIGH_CV_INPUT, "High CV");
configInput(ALL_INPUT, "All");
configInput(ALL_CV_INPUT, "All CV");

configOutput(LOW_OUTPUT, "Low");
configOutput(LOW_MID_OUTPUT, "Low mid");
configOutput(HIGH_MID_OUTPUT, "High mid");
configOutput(HIGH_OUTPUT, "High");
configOutput(MIX_OUTPUT, "Mix");

onSampleRateChange();
}

void onSampleRateChange() override {
const float sr = APP->engine->getSampleRate();
const float lowFc = 300.f / sr;
const float lowMidFc = 750.f / sr;
const float highMidFc = 1500.f / sr;
const float highFc = 5000.f / sr;
const float Q = 1.f, V = 1.f;

for (int i = 0; i < 4; ++i) {
filterLow[i].setParameters(dsp::TBiquadFilter<float_4>::Type::LOWPASS, lowFc, Q, V);
filterLowMid[i].setParameters(dsp::TBiquadFilter<float_4>::Type::BANDPASS, lowMidFc, Q, V);
filterHighMid[i].setParameters(dsp::TBiquadFilter<float_4>::Type::BANDPASS, highMidFc, Q, V);
filterHigh[i].setParameters(dsp::TBiquadFilter<float_4>::Type::HIGHPASS, highFc, Q, V);
}
}

void processBypass(const ProcessArgs& args) override {
const int maxPolyphony = std::max({1, inputs[ALL_INPUT].getChannels(), inputs[LOW_INPUT].getChannels(),
inputs[LOW_MID_INPUT].getChannels(), inputs[HIGH_MID_INPUT].getChannels(),
inputs[HIGH_INPUT].getChannels()});


for (int c = 0; c < maxPolyphony; c += 4) {
const float_4 inLow = inputs[LOW_INPUT].getPolyVoltageSimd<float_4>(c);
const float_4 inLowMid = inputs[LOW_MID_INPUT].getPolyVoltageSimd<float_4>(c);
const float_4 inHighMid = inputs[HIGH_MID_INPUT].getPolyVoltageSimd<float_4>(c);
const float_4 inHigh = inputs[HIGH_INPUT].getPolyVoltageSimd<float_4>(c);
const float_4 inAll = inputs[ALL_INPUT].getPolyVoltageSimd<float_4>(c);

// bypass sums all inputs to the output
outputs[MIX_OUTPUT].setVoltageSimd<float_4>(inLow + inLowMid + inHighMid + inHigh + inAll, c);
}

outputs[MIX_OUTPUT].setChannels(maxPolyphony);
}


void process(const ProcessArgs& args) override {

const int maxPolyphony = std::max({1, inputs[ALL_INPUT].getChannels(), inputs[LOW_INPUT].getChannels(),
inputs[LOW_MID_INPUT].getChannels(), inputs[HIGH_MID_INPUT].getChannels(),
inputs[HIGH_INPUT].getChannels()});


for (int c = 0; c < maxPolyphony; c += 4) {

const float_4 inLow = inputs[LOW_INPUT].getPolyVoltageSimd<float_4>(c);
const float_4 inLowMid = inputs[LOW_MID_INPUT].getPolyVoltageSimd<float_4>(c);
const float_4 inHighMid = inputs[HIGH_MID_INPUT].getPolyVoltageSimd<float_4>(c);
const float_4 inHigh = inputs[HIGH_INPUT].getPolyVoltageSimd<float_4>(c);
const float_4 inAll = inputs[ALL_INPUT].getPolyVoltageSimd<float_4>(c);

const float_4 lowGain = params[LOW_GAIN_PARAM].getValue() * inputs[LOW_CV_INPUT].getNormalPolyVoltageSimd<float_4>(10.f, c) / 10.f;
const float_4 outLow = filterLow[c / 4].process((inLow + inAll) * lowGain);
outputs[LOW_OUTPUT].setVoltageSimd<float_4>(outLow, c);

const float_4 lowMidGain = params[LOW_MID_GAIN_PARAM].getValue() * inputs[LOW_MID_CV_INPUT].getNormalPolyVoltageSimd<float_4>(10.f, c) / 10.f;
const float_4 outLowMid = filterLowMid[c / 4].process((inLowMid + inAll) * lowMidGain);
outputs[LOW_MID_OUTPUT].setVoltageSimd<float_4>(outLowMid, c);

const float_4 highMidGain = params[HIGH_MID_GAIN_PARAM].getValue() * inputs[HIGH_MID_CV_INPUT].getNormalPolyVoltageSimd<float_4>(10.f, c) / 10.f;
const float_4 outHighMid = filterHighMid[c / 4].process((inHighMid + inAll) * highMidGain);
outputs[HIGH_MID_OUTPUT].setVoltageSimd<float_4>(outHighMid, c);

const float_4 highGain = params[HIGH_GAIN_PARAM].getValue() * inputs[HIGH_CV_INPUT].getNormalPolyVoltageSimd<float_4>(10.f, c) / 10.f;
const float_4 outHigh = filterHigh[c / 4].process((inHigh + inAll) * highGain);
outputs[HIGH_OUTPUT].setVoltageSimd<float_4>(outHigh, c);

const float_4 fxReturnSum = inputs[LOW_RETURN_INPUT].getPolyVoltageSimd<float_4>(c) +
inputs[LOW_MID_RETURN_INPUT].getPolyVoltageSimd<float_4>(c) +
inputs[HIGH_MID_RETURN_INPUT].getPolyVoltageSimd<float_4>(c) +
inputs[HIGH_RETURN_INPUT].getPolyVoltageSimd<float_4>(c);

outputs[MIX_OUTPUT].setVoltageSimd<float_4>(fxReturnSum, c);
}

outputs[LOW_OUTPUT].setChannels(maxPolyphony);

if (maxPolyphony == 1) {
lights[MIX_LIGHT + 0].setBrightness(0.f);
lights[MIX_LIGHT + 1].setBrightnessSmooth(outputs[MIX_OUTPUT].getVoltageRMS(), args.sampleTime);
lights[MIX_LIGHT + 2].setBrightness(0.f);
}
else {
lights[MIX_LIGHT + 0].setBrightness(0.f);
lights[MIX_LIGHT + 1].setBrightness(0.f);
lights[MIX_LIGHT + 2].setBrightnessSmooth(outputs[MIX_OUTPUT].getVoltageRMS(), args.sampleTime);
}
}
};


struct BanditWidget : ModuleWidget {
BanditWidget(Bandit* module) {
setModule(module);
setPanel(createPanel(asset::plugin(pluginInstance, "res/panels/Bandit.svg")));

addChild(createWidget<Knurlie>(Vec(RACK_GRID_WIDTH, 0)));
addChild(createWidget<Knurlie>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));

addParam(createParam<BefacoSlidePot>(mm2px(Vec(3.062, 51.365)), module, Bandit::LOW_GAIN_PARAM));
addParam(createParam<BefacoSlidePot>(mm2px(Vec(13.23, 51.365)), module, Bandit::LOW_MID_GAIN_PARAM));
addParam(createParam<BefacoSlidePot>(mm2px(Vec(23.398, 51.365)), module, Bandit::HIGH_MID_GAIN_PARAM));
addParam(createParam<BefacoSlidePot>(mm2px(Vec(33.566, 51.365)), module, Bandit::HIGH_GAIN_PARAM));

addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(5.038, 14.5)), module, Bandit::LOW_INPUT));
addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(15.178, 14.5)), module, Bandit::LOW_MID_INPUT));
addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(25.253, 14.5)), module, Bandit::HIGH_MID_INPUT));
addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(35.328, 14.5)), module, Bandit::HIGH_INPUT));
addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(5.045, 40.34)), module, Bandit::LOW_RETURN_INPUT));
addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(15.118, 40.34)), module, Bandit::LOW_MID_RETURN_INPUT));
addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(25.19, 40.338)), module, Bandit::HIGH_MID_RETURN_INPUT));
addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(35.263, 40.34)), module, Bandit::HIGH_RETURN_INPUT));
addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(5.038, 101.229)), module, Bandit::LOW_CV_INPUT));
addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(15.113, 101.229)), module, Bandit::LOW_MID_CV_INPUT));
addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(25.187, 101.231)), module, Bandit::HIGH_MID_CV_INPUT));
addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(35.263, 101.229)), module, Bandit::HIGH_CV_INPUT));
addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(10.075, 113.502)), module, Bandit::ALL_INPUT));
addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(20.15, 113.5)), module, Bandit::ALL_CV_INPUT));

addOutput(createOutputCentered<BefacoOutputPort>(mm2px(Vec(5.045, 27.248)), module, Bandit::LOW_OUTPUT));
addOutput(createOutputCentered<BefacoOutputPort>(mm2px(Vec(15.118, 27.256)), module, Bandit::LOW_MID_OUTPUT));
addOutput(createOutputCentered<BefacoOutputPort>(mm2px(Vec(25.19, 27.256)), module, Bandit::HIGH_MID_OUTPUT));
addOutput(createOutputCentered<BefacoOutputPort>(mm2px(Vec(35.263, 27.256)), module, Bandit::HIGH_OUTPUT));
addOutput(createOutputCentered<BefacoOutputPort>(mm2px(Vec(30.225, 113.5)), module, Bandit::MIX_OUTPUT));

addChild(createLightCentered<MediumLight<RedGreenBlueLight>>(mm2px(Vec(37.781, 111.125)), module, Bandit::MIX_CLIP_LIGHT));
addChild(createLightCentered<MediumLight<RedGreenBlueLight>>(mm2px(Vec(37.781, 115.875)), module, Bandit::MIX_LIGHT));
}
};

Model* modelBandit = createModel<Bandit, BanditWidget>("Bandit");

+ 92
- 25
src/Bypass.cpp View File

@@ -8,6 +8,7 @@ struct Bypass : Module {
FX_GAIN_PARAM,
LAUNCH_MODE_PARAM,
LAUNCH_BUTTON_PARAM,
SLEW_TIME_PARAM,
PARAMS_LEN
};
enum InputId {
@@ -37,13 +38,13 @@ struct Bypass : Module {
HARD_MODE,
SOFT_MODE
};
LatchMode latchMode = LatchMode::MOMENTARY_MODE;
ReturnMode returnMode = ReturnMode::HARD_MODE;
ParamQuantity* launchParam;
ParamQuantity* launchParam, * slewTimeParam;
dsp::SchmittTrigger launchCvTrigger;
dsp::BooleanTrigger launchButtonTrigger;
dsp::BooleanTrigger latchTrigger;
dsp::SlewLimiter clickFilter;
bool launchButtonHeld = false;

Bypass() {
config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN);
@@ -52,6 +53,9 @@ struct Bypass : Module {
configSwitch(LAUNCH_MODE_PARAM, 0.f, 1.f, 0.f, "Launch Mode", {"Latch (Toggle)", "Gate (Momentary)"});
launchParam = configButton(LAUNCH_BUTTON_PARAM, "Launch");

slewTimeParam = configParam(SLEW_TIME_PARAM, .005f, 0.05f, 0.01f, "Slew time", "s");


configInput(IN_L_INPUT, "Left");
configInput(IN_R_INPUT, "Right");
configInput(FROM_FX_L_INPUT, "From FX L");
@@ -64,30 +68,32 @@ struct Bypass : Module {
configOutput(OUT_R_OUTPUT, "Right");

configBypass(IN_L_INPUT, OUT_L_OUTPUT);
configBypass(IN_R_INPUT, OUT_R_OUTPUT);
configBypass(IN_R_INPUT, OUT_R_OUTPUT);


clickFilter.rise = 1 / 0.01f; // 0.01 ms
clickFilter.fall = 1 / 0.01f; // 0.01 ms
}

bool active = false;
void process(const ProcessArgs& args) override {

const int maxChannels = std::max(inputs[IN_L_INPUT].getChannels(), inputs[IN_R_INPUT].getChannels());
// slew time in secs (so take inverse for lambda)
clickFilter.rise = clickFilter.fall = 1.0 / params[SLEW_TIME_PARAM].getValue();

const int maxInputChannels = std::max({1, inputs[IN_L_INPUT].getChannels(), inputs[IN_R_INPUT].getChannels()});
const int maxFxReturnChannels = std::max({1, inputs[FROM_FX_L_INPUT].getChannels(), inputs[FROM_FX_R_INPUT].getChannels()});

latchMode = (LatchMode) params[LAUNCH_MODE_PARAM].getValue();
const LatchMode latchMode = (LatchMode) params[LAUNCH_MODE_PARAM].getValue();
const ReturnMode returnMode = (ReturnMode) params[MODE_PARAM].getValue();


const bool launchCvTriggered = launchCvTrigger.process(inputs[LAUNCH_INPUT].getVoltage());
const bool launchButtonPressed = launchButtonTrigger.process(params[LAUNCH_BUTTON_PARAM].getValue());
const bool launchButtonPressed = launchButtonTrigger.process(launchButtonHeld);

// logical or (high if either high)
const float launchValue = std::max(launchCvTrigger.isHigh(), launchButtonTrigger.isHigh());
if (latchMode == LatchMode::TOGGLE_MODE) {
const bool risingEdge = launchCvTriggered || launchButtonPressed;

// TODO: sometimes misses?
if (risingEdge) {
active = !active;
}
@@ -96,36 +102,91 @@ struct Bypass : Module {
const float fxGain = std::pow(10, params[FX_GAIN_PARAM].getValue() / 20.0f);
const float sendActive = clickFilter.process(args.sampleTime, (latchMode == LatchMode::TOGGLE_MODE) ? active : launchValue);

for (int c = 0; c < maxChannels; c += 4) {

for (int c = 0; c < maxInputChannels; c += 4) {
const float_4 inL = inputs[IN_L_INPUT].getPolyVoltageSimd<float_4>(c);
const float_4 inR = inputs[IN_R_INPUT].getNormalPolyVoltageSimd<float_4>(inL, c);

// we start be assuming that FXs can be polyphonic, but recognise that often they are not
outputs[TOFX_L_OUTPUT].setVoltageSimd<float_4>(inL * fxGain * sendActive, c);
outputs[TOFX_R_OUTPUT].setVoltageSimd<float_4>(inR * fxGain * sendActive, c);
}
// fx send polyphony is set by input polyphony
outputs[TOFX_L_OUTPUT].setChannels(maxInputChannels);
outputs[TOFX_R_OUTPUT].setChannels(maxInputChannels);

float_4 dryLeft, dryRight;
for (int c = 0; c < maxFxReturnChannels; c += 4) {

const bool fxMonophonic = (maxInputChannels == 1);
if (fxMonophonic) {
// if the return fx is monophonic, mix down dry inputs to monophonic also
dryLeft = inputs[IN_L_INPUT].getVoltageSum();
dryRight = inputs[IN_R_INPUT].isConnected() ? inputs[IN_R_INPUT].getVoltageSum() : inputs[IN_L_INPUT].getVoltageSum();
}
else {
// if the return fx is polyphonic, then we don't need to do anything special
dryLeft = inputs[IN_L_INPUT].getPolyVoltageSimd<float_4>(c);
dryRight = inputs[IN_R_INPUT].getNormalPolyVoltageSimd<float_4>(dryLeft, c);
}

const float_4 fxLeftReturn = inputs[FROM_FX_L_INPUT].getPolyVoltageSimd<float_4>(c);
const float_4 fxRightReturn = inputs[FROM_FX_R_INPUT].getPolyVoltageSimd<float_4>(c);

if (returnMode == ReturnMode::HARD_MODE) {
outputs[OUT_L_OUTPUT].setVoltageSimd<float_4>(inL * (1 - sendActive) + sendActive * fxLeftReturn, c);
outputs[OUT_R_OUTPUT].setVoltageSimd<float_4>(inR * (1 - sendActive) + sendActive * fxRightReturn, c);
outputs[OUT_L_OUTPUT].setVoltageSimd<float_4>(dryLeft * (1 - sendActive) + sendActive * fxLeftReturn, c);
outputs[OUT_R_OUTPUT].setVoltageSimd<float_4>(dryRight * (1 - sendActive) + sendActive * fxRightReturn, c);
}
else {
outputs[OUT_L_OUTPUT].setVoltageSimd<float_4>(inL * (1 - sendActive) + fxLeftReturn, c);
outputs[OUT_R_OUTPUT].setVoltageSimd<float_4>(inR * (1 - sendActive) + fxRightReturn, c);
outputs[OUT_L_OUTPUT].setVoltageSimd<float_4>(dryLeft * (1 - sendActive) + fxLeftReturn, c);
outputs[OUT_R_OUTPUT].setVoltageSimd<float_4>(dryRight * (1 - sendActive) + fxRightReturn, c);
}
}
// output polyphony is set by fx return polyphony
outputs[OUT_L_OUTPUT].setChannels(maxFxReturnChannels);
outputs[OUT_R_OUTPUT].setChannels(maxFxReturnChannels);

outputs[OUT_R_OUTPUT].setVoltage(sendActive);
lights[LAUNCH_LED].setSmoothBrightness(sendActive, args.sampleTime);
}
};

lights[LAUNCH_LED].setBrightness(sendActive);
/** From VCV Free */
struct VCVBezelBig : app::SvgSwitch {
VCVBezelBig() {
addFrame(Svg::load(asset::plugin(pluginInstance, "res/components/VCVBezelBig.svg")));
}
};

template <typename TBase>
struct VCVBezelLightBig : TBase {
VCVBezelLightBig() {
this->borderColor = color::WHITE_TRANSPARENT;
this->bgColor = color::WHITE_TRANSPARENT;
this->box.size = mm2px(math::Vec(9, 9));
}
};

using BefacoRedLightButton = LightButton<BefacoButton, VeryLargeSimpleLight<TRedLight<app::ModuleLightWidget>>>;
struct RecordButton : LightButton<VCVBezelBig, VCVBezelLightBig<RedLight>> {
// Instead of using onAction() which is called on mouse up, handle on mouse down
void onDragStart(const event::DragStart& e) override {
Bypass* module = dynamic_cast<Bypass*>(this->module);
if (e.button == GLFW_MOUSE_BUTTON_LEFT) {
if (module) {
module->launchButtonHeld = true;
}
}

LightButton::onDragStart(e);
}

void onDragEnd(const event::DragEnd& e) override {
Bypass* module = dynamic_cast<Bypass*>(this->module);
if (e.button == GLFW_MOUSE_BUTTON_LEFT) {
if (module) {
module->launchButtonHeld = false;
}
}
}
};

struct BypassWidget : ModuleWidget {

@@ -142,7 +203,7 @@ struct BypassWidget : ModuleWidget {
addParam(createParamCentered<BefacoTinyKnobWhite>(mm2px(Vec(10.0, 78.903)), module, Bypass::FX_GAIN_PARAM));
addParam(createParam<CKSSNarrow>(mm2px(Vec(13.8, 91.6)), module, Bypass::LAUNCH_MODE_PARAM));

launchParam = createLightParamCentered<BefacoRedLightButton>(mm2px(Vec(10.0, 111.287)), module, Bypass::LAUNCH_BUTTON_PARAM, Bypass::LAUNCH_LED);
launchParam = createLightParamCentered<RecordButton>(mm2px(Vec(10.0, 111.287)), module, Bypass::LAUNCH_BUTTON_PARAM, Bypass::LAUNCH_LED);
addParam(launchParam);

addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(15.016, 15.03)), module, Bypass::IN_R_INPUT));
@@ -157,16 +218,22 @@ struct BypassWidget : ModuleWidget {
addOutput(createOutputCentered<BefacoOutputPort>(mm2px(Vec(14.957, 53.824)), module, Bypass::OUT_R_OUTPUT));
}

void draw(const DrawArgs& args) override {
// for context menu
struct SlewTimeSider : ui::Slider {
explicit SlewTimeSider(ParamQuantity* q_) {
quantity = q_;
this->box.size.x = 200.0f;
}
};

void appendContextMenu(Menu* menu) override {
Bypass* module = dynamic_cast<Bypass*>(this->module);
assert(module);

if (module != nullptr) {
launchParam->momentary = module->latchMode == Bypass::LatchMode::MOMENTARY_MODE;
launchParam->latch = !launchParam->momentary;
}
menu->addChild(new MenuSeparator());

menu->addChild(new SlewTimeSider(module->slewTimeParam));

ModuleWidget::draw(args);
}
};



+ 1
- 0
src/plugin.cpp View File

@@ -32,4 +32,5 @@ void init(rack::Plugin *p) {
p->addModel(modelVoltio);
p->addModel(modelOctaves);
p->addModel(modelBypass);
p->addModel(modelBandit);
}

+ 1
- 0
src/plugin.hpp View File

@@ -33,6 +33,7 @@ extern Model* modelMidiThing;
extern Model* modelVoltio;
extern Model* modelOctaves;
extern Model* modelBypass;
extern Model* modelBandit;

struct Knurlie : SvgScrew {
Knurlie() {


Loading…
Cancel
Save