From 9c050c8c9f75c8ab730f51793d957d5f30fd146f Mon Sep 17 00:00:00 2001
From: hemmer <915048+hemmer@users.noreply.github.com>
Date: Thu, 17 Oct 2024 19:06:44 +0100
Subject: [PATCH] Bandit initial commit
Fix bypass polyphony
---
plugin.json | 14 +
res/components/VCVBezelBig.svg | 150 +++
res/panels/Bandit.svg | 1739 ++++++++++++++++++++++++++++++++
src/Bandit.cpp | 208 ++++
src/Bypass.cpp | 117 ++-
src/plugin.cpp | 1 +
src/plugin.hpp | 1 +
7 files changed, 2205 insertions(+), 25 deletions(-)
create mode 100644 res/components/VCVBezelBig.svg
create mode 100644 res/panels/Bandit.svg
create mode 100644 src/Bandit.cpp
diff --git a/plugin.json b/plugin.json
index e123313..7fafb0b 100644
--- a/plugin.json
+++ b/plugin.json
@@ -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"
]
}
diff --git a/res/components/VCVBezelBig.svg b/res/components/VCVBezelBig.svg
new file mode 100644
index 0000000..e74ade8
--- /dev/null
+++ b/res/components/VCVBezelBig.svg
@@ -0,0 +1,150 @@
+
+
+
+
diff --git a/res/panels/Bandit.svg b/res/panels/Bandit.svg
new file mode 100644
index 0000000..c93ed8c
--- /dev/null
+++ b/res/panels/Bandit.svg
@@ -0,0 +1,1739 @@
+
+
diff --git a/src/Bandit.cpp b/src/Bandit.cpp
new file mode 100644
index 0000000..4b4f5b9
--- /dev/null
+++ b/src/Bandit.cpp
@@ -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 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::Type::LOWPASS, lowFc, Q, V);
+ filterLowMid[i].setParameters(dsp::TBiquadFilter::Type::BANDPASS, lowMidFc, Q, V);
+ filterHighMid[i].setParameters(dsp::TBiquadFilter::Type::BANDPASS, highMidFc, Q, V);
+ filterHigh[i].setParameters(dsp::TBiquadFilter::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(c);
+ const float_4 inLowMid = inputs[LOW_MID_INPUT].getPolyVoltageSimd(c);
+ const float_4 inHighMid = inputs[HIGH_MID_INPUT].getPolyVoltageSimd(c);
+ const float_4 inHigh = inputs[HIGH_INPUT].getPolyVoltageSimd(c);
+ const float_4 inAll = inputs[ALL_INPUT].getPolyVoltageSimd(c);
+
+ // bypass sums all inputs to the output
+ outputs[MIX_OUTPUT].setVoltageSimd(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(c);
+ const float_4 inLowMid = inputs[LOW_MID_INPUT].getPolyVoltageSimd(c);
+ const float_4 inHighMid = inputs[HIGH_MID_INPUT].getPolyVoltageSimd(c);
+ const float_4 inHigh = inputs[HIGH_INPUT].getPolyVoltageSimd(c);
+ const float_4 inAll = inputs[ALL_INPUT].getPolyVoltageSimd(c);
+
+ const float_4 lowGain = params[LOW_GAIN_PARAM].getValue() * inputs[LOW_CV_INPUT].getNormalPolyVoltageSimd(10.f, c) / 10.f;
+ const float_4 outLow = filterLow[c / 4].process((inLow + inAll) * lowGain);
+ outputs[LOW_OUTPUT].setVoltageSimd(outLow, c);
+
+ const float_4 lowMidGain = params[LOW_MID_GAIN_PARAM].getValue() * inputs[LOW_MID_CV_INPUT].getNormalPolyVoltageSimd(10.f, c) / 10.f;
+ const float_4 outLowMid = filterLowMid[c / 4].process((inLowMid + inAll) * lowMidGain);
+ outputs[LOW_MID_OUTPUT].setVoltageSimd(outLowMid, c);
+
+ const float_4 highMidGain = params[HIGH_MID_GAIN_PARAM].getValue() * inputs[HIGH_MID_CV_INPUT].getNormalPolyVoltageSimd(10.f, c) / 10.f;
+ const float_4 outHighMid = filterHighMid[c / 4].process((inHighMid + inAll) * highMidGain);
+ outputs[HIGH_MID_OUTPUT].setVoltageSimd(outHighMid, c);
+
+ const float_4 highGain = params[HIGH_GAIN_PARAM].getValue() * inputs[HIGH_CV_INPUT].getNormalPolyVoltageSimd(10.f, c) / 10.f;
+ const float_4 outHigh = filterHigh[c / 4].process((inHigh + inAll) * highGain);
+ outputs[HIGH_OUTPUT].setVoltageSimd(outHigh, c);
+
+ const float_4 fxReturnSum = inputs[LOW_RETURN_INPUT].getPolyVoltageSimd(c) +
+ inputs[LOW_MID_RETURN_INPUT].getPolyVoltageSimd(c) +
+ inputs[HIGH_MID_RETURN_INPUT].getPolyVoltageSimd(c) +
+ inputs[HIGH_RETURN_INPUT].getPolyVoltageSimd(c);
+
+ outputs[MIX_OUTPUT].setVoltageSimd(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(Vec(RACK_GRID_WIDTH, 0)));
+ addChild(createWidget(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
+
+ addParam(createParam(mm2px(Vec(3.062, 51.365)), module, Bandit::LOW_GAIN_PARAM));
+ addParam(createParam(mm2px(Vec(13.23, 51.365)), module, Bandit::LOW_MID_GAIN_PARAM));
+ addParam(createParam(mm2px(Vec(23.398, 51.365)), module, Bandit::HIGH_MID_GAIN_PARAM));
+ addParam(createParam(mm2px(Vec(33.566, 51.365)), module, Bandit::HIGH_GAIN_PARAM));
+
+ addInput(createInputCentered(mm2px(Vec(5.038, 14.5)), module, Bandit::LOW_INPUT));
+ addInput(createInputCentered(mm2px(Vec(15.178, 14.5)), module, Bandit::LOW_MID_INPUT));
+ addInput(createInputCentered(mm2px(Vec(25.253, 14.5)), module, Bandit::HIGH_MID_INPUT));
+ addInput(createInputCentered(mm2px(Vec(35.328, 14.5)), module, Bandit::HIGH_INPUT));
+ addInput(createInputCentered(mm2px(Vec(5.045, 40.34)), module, Bandit::LOW_RETURN_INPUT));
+ addInput(createInputCentered(mm2px(Vec(15.118, 40.34)), module, Bandit::LOW_MID_RETURN_INPUT));
+ addInput(createInputCentered(mm2px(Vec(25.19, 40.338)), module, Bandit::HIGH_MID_RETURN_INPUT));
+ addInput(createInputCentered(mm2px(Vec(35.263, 40.34)), module, Bandit::HIGH_RETURN_INPUT));
+ addInput(createInputCentered(mm2px(Vec(5.038, 101.229)), module, Bandit::LOW_CV_INPUT));
+ addInput(createInputCentered(mm2px(Vec(15.113, 101.229)), module, Bandit::LOW_MID_CV_INPUT));
+ addInput(createInputCentered(mm2px(Vec(25.187, 101.231)), module, Bandit::HIGH_MID_CV_INPUT));
+ addInput(createInputCentered(mm2px(Vec(35.263, 101.229)), module, Bandit::HIGH_CV_INPUT));
+ addInput(createInputCentered(mm2px(Vec(10.075, 113.502)), module, Bandit::ALL_INPUT));
+ addInput(createInputCentered(mm2px(Vec(20.15, 113.5)), module, Bandit::ALL_CV_INPUT));
+
+ addOutput(createOutputCentered(mm2px(Vec(5.045, 27.248)), module, Bandit::LOW_OUTPUT));
+ addOutput(createOutputCentered(mm2px(Vec(15.118, 27.256)), module, Bandit::LOW_MID_OUTPUT));
+ addOutput(createOutputCentered(mm2px(Vec(25.19, 27.256)), module, Bandit::HIGH_MID_OUTPUT));
+ addOutput(createOutputCentered(mm2px(Vec(35.263, 27.256)), module, Bandit::HIGH_OUTPUT));
+ addOutput(createOutputCentered(mm2px(Vec(30.225, 113.5)), module, Bandit::MIX_OUTPUT));
+
+ addChild(createLightCentered>(mm2px(Vec(37.781, 111.125)), module, Bandit::MIX_CLIP_LIGHT));
+ addChild(createLightCentered>(mm2px(Vec(37.781, 115.875)), module, Bandit::MIX_LIGHT));
+ }
+};
+
+Model* modelBandit = createModel("Bandit");
\ No newline at end of file
diff --git a/src/Bypass.cpp b/src/Bypass.cpp
index 542cb50..10a4b2d 100644
--- a/src/Bypass.cpp
+++ b/src/Bypass.cpp
@@ -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(c);
const float_4 inR = inputs[IN_R_INPUT].getNormalPolyVoltageSimd(inL, c);
+ // we start be assuming that FXs can be polyphonic, but recognise that often they are not
outputs[TOFX_L_OUTPUT].setVoltageSimd(inL * fxGain * sendActive, c);
outputs[TOFX_R_OUTPUT].setVoltageSimd(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(c);
+ dryRight = inputs[IN_R_INPUT].getNormalPolyVoltageSimd(dryLeft, c);
+ }
const float_4 fxLeftReturn = inputs[FROM_FX_L_INPUT].getPolyVoltageSimd(c);
const float_4 fxRightReturn = inputs[FROM_FX_R_INPUT].getPolyVoltageSimd(c);
if (returnMode == ReturnMode::HARD_MODE) {
- outputs[OUT_L_OUTPUT].setVoltageSimd(inL * (1 - sendActive) + sendActive * fxLeftReturn, c);
- outputs[OUT_R_OUTPUT].setVoltageSimd(inR * (1 - sendActive) + sendActive * fxRightReturn, c);
+ outputs[OUT_L_OUTPUT].setVoltageSimd(dryLeft * (1 - sendActive) + sendActive * fxLeftReturn, c);
+ outputs[OUT_R_OUTPUT].setVoltageSimd(dryRight * (1 - sendActive) + sendActive * fxRightReturn, c);
}
else {
- outputs[OUT_L_OUTPUT].setVoltageSimd(inL * (1 - sendActive) + fxLeftReturn, c);
- outputs[OUT_R_OUTPUT].setVoltageSimd(inR * (1 - sendActive) + fxRightReturn, c);
+ outputs[OUT_L_OUTPUT].setVoltageSimd(dryLeft * (1 - sendActive) + fxLeftReturn, c);
+ outputs[OUT_R_OUTPUT].setVoltageSimd(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
+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>>;
+struct RecordButton : LightButton> {
+ // 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(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(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(mm2px(Vec(10.0, 78.903)), module, Bypass::FX_GAIN_PARAM));
addParam(createParam(mm2px(Vec(13.8, 91.6)), module, Bypass::LAUNCH_MODE_PARAM));
- launchParam = createLightParamCentered(mm2px(Vec(10.0, 111.287)), module, Bypass::LAUNCH_BUTTON_PARAM, Bypass::LAUNCH_LED);
+ launchParam = createLightParamCentered(mm2px(Vec(10.0, 111.287)), module, Bypass::LAUNCH_BUTTON_PARAM, Bypass::LAUNCH_LED);
addParam(launchParam);
addInput(createInputCentered(mm2px(Vec(15.016, 15.03)), module, Bypass::IN_R_INPUT));
@@ -157,16 +218,22 @@ struct BypassWidget : ModuleWidget {
addOutput(createOutputCentered(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(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);
}
};
diff --git a/src/plugin.cpp b/src/plugin.cpp
index 48745f7..1b48a6e 100644
--- a/src/plugin.cpp
+++ b/src/plugin.cpp
@@ -32,4 +32,5 @@ void init(rack::Plugin *p) {
p->addModel(modelVoltio);
p->addModel(modelOctaves);
p->addModel(modelBypass);
+ p->addModel(modelBandit);
}
diff --git a/src/plugin.hpp b/src/plugin.hpp
index 1c8a4fa..70ad26f 100644
--- a/src/plugin.hpp
+++ b/src/plugin.hpp
@@ -33,6 +33,7 @@ extern Model* modelMidiThing;
extern Model* modelVoltio;
extern Model* modelOctaves;
extern Model* modelBypass;
+extern Model* modelBandit;
struct Knurlie : SvgScrew {
Knurlie() {