From c9909de77ce39fc5bc095ce3a2628bff3e0ef99a Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Fri, 3 Nov 2017 11:32:05 -0400 Subject: [PATCH] Added Mutes and Unity --- res/{8VERT.svg => 8vert.svg} | 0 res/Mutes.svg | 1274 ++++++++++++++++++++++++ res/Unity.svg | 1791 ++++++++++++++++++++++++++++++++++ src/{8VERT.cpp => 8vert.cpp} | 12 +- src/ADSR.cpp | 4 +- src/Fundamental.cpp | 4 +- src/Fundamental.hpp | 13 +- src/Mutes.cpp | 137 +++ src/SEQ3.cpp | 14 +- src/Scope.cpp | 8 +- src/Unity.cpp | 160 +++ 11 files changed, 3395 insertions(+), 22 deletions(-) rename res/{8VERT.svg => 8vert.svg} (100%) create mode 100644 res/Mutes.svg create mode 100644 res/Unity.svg rename src/{8VERT.cpp => 8vert.cpp} (93%) create mode 100644 src/Mutes.cpp create mode 100644 src/Unity.cpp diff --git a/res/8VERT.svg b/res/8vert.svg similarity index 100% rename from res/8VERT.svg rename to res/8vert.svg diff --git a/res/Mutes.svg b/res/Mutes.svg new file mode 100644 index 0000000..640902c --- /dev/null +++ b/res/Mutes.svg @@ -0,0 +1,1274 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/Unity.svg b/res/Unity.svg new file mode 100644 index 0000000..68d9790 --- /dev/null +++ b/res/Unity.svg @@ -0,0 +1,1791 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/8VERT.cpp b/src/8vert.cpp similarity index 93% rename from src/8VERT.cpp rename to src/8vert.cpp index adde761..355520f 100644 --- a/src/8VERT.cpp +++ b/src/8vert.cpp @@ -1,7 +1,7 @@ #include "Fundamental.hpp" -struct _8VERT : Module { +struct _8vert : Module { enum ParamIds { NUM_PARAMS = 8 }; @@ -15,11 +15,11 @@ struct _8VERT : Module { NUM_LIGHTS = 16 }; - _8VERT() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {} + _8vert() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {} void step() override; }; -void _8VERT::step() { +void _8vert::step() { float lastIn = 10.0; for (int i = 0; i < 8; i++) { lastIn = inputs[i].normalize(lastIn); @@ -31,14 +31,14 @@ void _8VERT::step() { } -_8VERTWidget::_8VERTWidget() { - _8VERT *module = new _8VERT(); +_8vertWidget::_8vertWidget() { + _8vert *module = new _8vert(); setModule(module); box.size = Vec(8 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT); SVGPanel *panel = new SVGPanel(); panel->box.size = box.size; - panel->setBackground(SVG::load(assetPlugin(plugin, "res/8VERT.svg"))); + panel->setBackground(SVG::load(assetPlugin(plugin, "res/8vert.svg"))); addChild(panel); addChild(createScrew(Vec(15, 0))); diff --git a/src/ADSR.cpp b/src/ADSR.cpp index fbaed53..6fe9b8e 100644 --- a/src/ADSR.cpp +++ b/src/ADSR.cpp @@ -133,7 +133,7 @@ ADSRWidget::ADSRWidget() { addOutput(createOutput(Vec(87, 320), module, ADSR::ENVELOPE_OUTPUT)); addChild(createLight>(Vec(94, 41), module, ADSR::ATTACK_LIGHT)); - addChild(createLight>(Vec(94, 108), module, ADSR::DECAY_LIGHT)); + addChild(createLight>(Vec(94, 109), module, ADSR::DECAY_LIGHT)); addChild(createLight>(Vec(94, 175), module, ADSR::SUSTAIN_LIGHT)); - addChild(createLight>(Vec(94, 241), module, ADSR::RELEASE_LIGHT)); + addChild(createLight>(Vec(94, 242), module, ADSR::RELEASE_LIGHT)); } diff --git a/src/Fundamental.cpp b/src/Fundamental.cpp index 9b0367d..e2f7290 100644 --- a/src/Fundamental.cpp +++ b/src/Fundamental.cpp @@ -18,7 +18,9 @@ void init(rack::Plugin *p) { p->addModel(createModel("Fundamental", "Fundamental", "Delay", "Delay")); p->addModel(createModel("Fundamental", "Fundamental", "ADSR", "ADSR")); p->addModel(createModel("Fundamental", "Fundamental", "VCMixer", "VC Mixer")); - p->addModel(createModel<_8VERTWidget>("Fundamental", "Fundamental", "8VERT", "8VERT")); + p->addModel(createModel<_8vertWidget>("Fundamental", "Fundamental", "8vert", "8vert")); + p->addModel(createModel("Fundamental", "Fundamental", "Unity", "Unity")); + p->addModel(createModel("Fundamental", "Fundamental", "Mutes", "Mutes")); p->addModel(createModel("Fundamental", "Fundamental", "Scope", "Scope")); p->addModel(createModel("Fundamental", "Fundamental", "SEQ3", "SEQ-3")); } diff --git a/src/Fundamental.hpp b/src/Fundamental.hpp index 74aa0c2..4f85f83 100644 --- a/src/Fundamental.hpp +++ b/src/Fundamental.hpp @@ -46,8 +46,17 @@ struct VCMixerWidget : ModuleWidget { VCMixerWidget(); }; -struct _8VERTWidget : ModuleWidget { - _8VERTWidget(); +struct _8vertWidget : ModuleWidget { + _8vertWidget(); +}; + +struct UnityWidget : ModuleWidget { + UnityWidget(); + Menu *createContextMenu() override; +}; + +struct MutesWidget : ModuleWidget { + MutesWidget(); }; struct ScopeWidget : ModuleWidget { diff --git a/src/Mutes.cpp b/src/Mutes.cpp new file mode 100644 index 0000000..f323e25 --- /dev/null +++ b/src/Mutes.cpp @@ -0,0 +1,137 @@ +#include "Fundamental.hpp" +#include "dsp/digital.hpp" + + +struct Mutes : Module { + enum ParamIds { + MUTE_PARAM, + NUM_PARAMS = MUTE_PARAM + 10 + }; + enum InputIds { + IN_INPUT, + NUM_INPUTS = IN_INPUT + 10 + }; + enum OutputIds { + OUT_OUTPUT, + NUM_OUTPUTS = OUT_OUTPUT + 10 + }; + enum LightIds { + MUTE_LIGHT, + NUM_LIGHTS = MUTE_LIGHT + 10 + }; + + bool state[10]; + SchmittTrigger muteTrigger[10]; + + Mutes() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { + reset(); + } + void step() override; + + void reset() override { + for (int i = 0; i < 10; i++) { + state[i] = true; + } + } + void randomize() override { + for (int i = 0; i < 10; i++) { + state[i] = (randomf() < 0.5); + } + } + + json_t *toJson() override { + json_t *rootJ = json_object(); + // states + json_t *statesJ = json_array(); + for (int i = 0; i < 8; i++) { + json_t *stateJ = json_boolean(state[i]); + json_array_append_new(statesJ, stateJ); + } + json_object_set_new(rootJ, "states", statesJ); + return rootJ; + } + void fromJson(json_t *rootJ) override { + // states + json_t *statesJ = json_object_get(rootJ, "states"); + if (statesJ) { + for (int i = 0; i < 8; i++) { + json_t *stateJ = json_array_get(statesJ, i); + if (stateJ) + state[i] = json_boolean_value(stateJ); + } + } + } +}; + +void Mutes::step() { + for (int i = 0; i < 10; i++) { + if (muteTrigger[i].process(params[MUTE_PARAM + i].value)) + state[i] ^= true; + float in = inputs[IN_INPUT + i].value; + outputs[OUT_OUTPUT + i].value = state[i] ? in : 0.0; + lights[MUTE_LIGHT + i].setBrightness(state[i] ? 0.9 : 0.0); + } +} + + +template +struct MuteLight : BASE { + MuteLight() { + this->box.size = mm2px(Vec(6.0, 6.0)); + } +}; + +MutesWidget::MutesWidget() { + Mutes *module = new Mutes(); + setModule(module); + setPanel(SVG::load(assetPlugin(plugin, "res/Mutes.svg"))); + + addChild(createScrew(Vec(15, 0))); + addChild(createScrew(Vec(box.size.x - 30, 0))); + addChild(createScrew(Vec(15, 365))); + addChild(createScrew(Vec(box.size.x - 30, 365))); + + addParam(createParam(mm2px(Vec(16.57, 18.165)), module, Mutes::MUTE_PARAM + 0, 0.0, 1.0, 0.0)); + addParam(createParam(mm2px(Vec(16.57, 28.164)), module, Mutes::MUTE_PARAM + 1, 0.0, 1.0, 0.0)); + addParam(createParam(mm2px(Vec(16.57, 38.164)), module, Mutes::MUTE_PARAM + 2, 0.0, 1.0, 0.0)); + addParam(createParam(mm2px(Vec(16.57, 48.165)), module, Mutes::MUTE_PARAM + 3, 0.0, 1.0, 0.0)); + addParam(createParam(mm2px(Vec(16.57, 58.164)), module, Mutes::MUTE_PARAM + 4, 0.0, 1.0, 0.0)); + addParam(createParam(mm2px(Vec(16.57, 68.165)), module, Mutes::MUTE_PARAM + 5, 0.0, 1.0, 0.0)); + addParam(createParam(mm2px(Vec(16.57, 78.164)), module, Mutes::MUTE_PARAM + 6, 0.0, 1.0, 0.0)); + addParam(createParam(mm2px(Vec(16.57, 88.164)), module, Mutes::MUTE_PARAM + 7, 0.0, 1.0, 0.0)); + addParam(createParam(mm2px(Vec(16.57, 98.165)), module, Mutes::MUTE_PARAM + 8, 0.0, 1.0, 0.0)); + addParam(createParam(mm2px(Vec(16.57, 108.166)), module, Mutes::MUTE_PARAM + 9, 0.0, 1.0, 0.0)); + + addInput(createInput(mm2px(Vec(4.214, 17.81)), module, Mutes::IN_INPUT + 0)); + addInput(createInput(mm2px(Vec(4.214, 27.809)), module, Mutes::IN_INPUT + 1)); + addInput(createInput(mm2px(Vec(4.214, 37.809)), module, Mutes::IN_INPUT + 2)); + addInput(createInput(mm2px(Vec(4.214, 47.81)), module, Mutes::IN_INPUT + 3)); + addInput(createInput(mm2px(Vec(4.214, 57.81)), module, Mutes::IN_INPUT + 4)); + addInput(createInput(mm2px(Vec(4.214, 67.809)), module, Mutes::IN_INPUT + 5)); + addInput(createInput(mm2px(Vec(4.214, 77.81)), module, Mutes::IN_INPUT + 6)); + addInput(createInput(mm2px(Vec(4.214, 87.81)), module, Mutes::IN_INPUT + 7)); + addInput(createInput(mm2px(Vec(4.214, 97.809)), module, Mutes::IN_INPUT + 8)); + addInput(createInput(mm2px(Vec(4.214, 107.809)), module, Mutes::IN_INPUT + 9)); + + addOutput(createOutput(mm2px(Vec(28.214, 17.81)), module, Mutes::OUT_OUTPUT + 0)); + addOutput(createOutput(mm2px(Vec(28.214, 27.809)), module, Mutes::OUT_OUTPUT + 1)); + addOutput(createOutput(mm2px(Vec(28.214, 37.809)), module, Mutes::OUT_OUTPUT + 2)); + addOutput(createOutput(mm2px(Vec(28.214, 47.81)), module, Mutes::OUT_OUTPUT + 3)); + addOutput(createOutput(mm2px(Vec(28.214, 57.809)), module, Mutes::OUT_OUTPUT + 4)); + addOutput(createOutput(mm2px(Vec(28.214, 67.809)), module, Mutes::OUT_OUTPUT + 5)); + addOutput(createOutput(mm2px(Vec(28.214, 77.81)), module, Mutes::OUT_OUTPUT + 6)); + addOutput(createOutput(mm2px(Vec(28.214, 87.81)), module, Mutes::OUT_OUTPUT + 7)); + addOutput(createOutput(mm2px(Vec(28.214, 97.809)), module, Mutes::OUT_OUTPUT + 8)); + addOutput(createOutput(mm2px(Vec(28.214, 107.809)), module, Mutes::OUT_OUTPUT + 9)); + + addChild(createLight>(mm2px(Vec(17.32, 18.915)), module, Mutes::MUTE_LIGHT + 0)); + addChild(createLight>(mm2px(Vec(17.32, 28.916)), module, Mutes::MUTE_LIGHT + 1)); + addChild(createLight>(mm2px(Vec(17.32, 38.915)), module, Mutes::MUTE_LIGHT + 2)); + addChild(createLight>(mm2px(Vec(17.32, 48.915)), module, Mutes::MUTE_LIGHT + 3)); + addChild(createLight>(mm2px(Vec(17.32, 58.916)), module, Mutes::MUTE_LIGHT + 4)); + addChild(createLight>(mm2px(Vec(17.32, 68.916)), module, Mutes::MUTE_LIGHT + 5)); + addChild(createLight>(mm2px(Vec(17.32, 78.915)), module, Mutes::MUTE_LIGHT + 6)); + addChild(createLight>(mm2px(Vec(17.32, 88.916)), module, Mutes::MUTE_LIGHT + 7)); + addChild(createLight>(mm2px(Vec(17.32, 98.915)), module, Mutes::MUTE_LIGHT + 8)); + addChild(createLight>(mm2px(Vec(17.32, 108.915)), module, Mutes::MUTE_LIGHT + 9)); +} diff --git a/src/SEQ3.cpp b/src/SEQ3.cpp index 720f9c8..24eb5e2 100644 --- a/src/SEQ3.cpp +++ b/src/SEQ3.cpp @@ -228,14 +228,14 @@ SEQ3Widget::SEQ3Widget() { addParam(createParam(Vec(18, 56), module, SEQ3::CLOCK_PARAM, -2.0, 6.0, 2.0)); addParam(createParam(Vec(60, 61-1), module, SEQ3::RUN_PARAM, 0.0, 1.0, 0.0)); - addChild(createLight>(Vec(65, 65), module, SEQ3::RUNNING_LIGHT)); + addChild(createLight>(Vec(64.4, 64.4), module, SEQ3::RUNNING_LIGHT)); addParam(createParam(Vec(99, 61-1), module, SEQ3::RESET_PARAM, 0.0, 1.0, 0.0)); - addChild(createLight>(Vec(104, 65), module, SEQ3::RESET_LIGHT)); + addChild(createLight>(Vec(103.4, 64.4), module, SEQ3::RESET_LIGHT)); addParam(createParam(Vec(132, 56), module, SEQ3::STEPS_PARAM, 1.0, 8.0, 8.0)); - addChild(createLight>(Vec(180, 65), module, SEQ3::GATES_LIGHT)); - addChild(createLight>(Vec(219, 65), module, SEQ3::ROW_LIGHTS)); - addChild(createLight>(Vec(257, 65), module, SEQ3::ROW_LIGHTS + 1)); - addChild(createLight>(Vec(296, 65), module, SEQ3::ROW_LIGHTS + 2)); + addChild(createLight>(Vec(179.4, 64.4), module, SEQ3::GATES_LIGHT)); + addChild(createLight>(Vec(218.4, 64.4), module, SEQ3::ROW_LIGHTS)); + addChild(createLight>(Vec(256.4, 64.4), module, SEQ3::ROW_LIGHTS + 1)); + addChild(createLight>(Vec(295.4, 64.4), module, SEQ3::ROW_LIGHTS + 2)); static const float portX[8] = {20, 58, 96, 135, 173, 212, 250, 289}; addInput(createInput(Vec(portX[0]-1, 98), module, SEQ3::CLOCK_INPUT)); @@ -252,7 +252,7 @@ SEQ3Widget::SEQ3Widget() { addParam(createParam(Vec(portX[i]-2, 198), module, SEQ3::ROW2_PARAM + i, 0.0, 10.0, 0.0)); addParam(createParam(Vec(portX[i]-2, 240), module, SEQ3::ROW3_PARAM + i, 0.0, 10.0, 0.0)); addParam(createParam(Vec(portX[i]+2, 278-1), module, SEQ3::GATE_PARAM + i, 0.0, 1.0, 0.0)); - addChild(createLight>(Vec(portX[i]+7, 282), module, SEQ3::GATE_LIGHTS + i)); + addChild(createLight>(Vec(portX[i]+6.4, 281.4), module, SEQ3::GATE_LIGHTS + i)); addOutput(createOutput(Vec(portX[i]-1, 307), module, SEQ3::GATE_OUTPUT + i)); } } diff --git a/src/Scope.cpp b/src/Scope.cpp index 86809a4..186e21a 100644 --- a/src/Scope.cpp +++ b/src/Scope.cpp @@ -338,8 +338,8 @@ ScopeWidget::ScopeWidget() { addInput(createInput(Vec(63, 319), module, Scope::Y_INPUT)); addInput(createInput(Vec(154, 319), module, Scope::TRIG_INPUT)); - addChild(createLight>(Vec(104, 251), module, Scope::PLOT_LIGHT)); - addChild(createLight>(Vec(104, 296), module, Scope::LISSAJOUS_LIGHT)); - addChild(createLight>(Vec(150, 251), module, Scope::INTERNAL_LIGHT)); - addChild(createLight>(Vec(150, 296), module, Scope::EXTERNAL_LIGHT)); + addChild(createLight>(Vec(104, 251), module, Scope::PLOT_LIGHT)); + addChild(createLight>(Vec(104, 296), module, Scope::LISSAJOUS_LIGHT)); + addChild(createLight>(Vec(150, 251), module, Scope::INTERNAL_LIGHT)); + addChild(createLight>(Vec(150, 296), module, Scope::EXTERNAL_LIGHT)); } diff --git a/src/Unity.cpp b/src/Unity.cpp new file mode 100644 index 0000000..938e28e --- /dev/null +++ b/src/Unity.cpp @@ -0,0 +1,160 @@ +#include "Fundamental.hpp" + + +struct Unity : Module { + enum ParamIds { + AVG1_PARAM, + AVG2_PARAM, + NUM_PARAMS + }; + enum InputIds { + IN1_INPUT, + IN2_INPUT = IN1_INPUT + 6, + NUM_INPUTS = IN2_INPUT + 6 + }; + enum OutputIds { + MIX1_OUTPUT, + INV1_OUTPUT, + MIX2_OUTPUT, + INV2_OUTPUT, + NUM_OUTPUTS + }; + enum LightIds { + VU1_LIGHT, + VU2_LIGHT = VU1_LIGHT + 5, + NUM_LIGHTS = VU2_LIGHT + 5 + }; + + bool merge = false; + + Unity() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {} + void step() override; + + void reset() override { + merge = false; + } + + json_t *toJson() override { + json_t *rootJ = json_object(); + // merge + json_object_set_new(rootJ, "merge", json_boolean(merge)); + return rootJ; + } + + void fromJson(json_t *rootJ) override { + // merge + json_t *mergeJ = json_object_get(rootJ, "merge"); + if (mergeJ) + merge = json_boolean_value(mergeJ); + } +}; + +void Unity::step() { + float mix[2] = {}; + int count[2] = {}; + + for (int i = 0; i < 2; i++) { + // Inputs + for (int j = 0; j < 6; j++) { + mix[i] += inputs[IN1_INPUT + 6*i + j].value; + if (inputs[IN1_INPUT + 6*i + j].active) + count[i]++; + } + } + + // Combine + if (merge) { + mix[0] += mix[1]; + mix[1] = mix[0]; + count[0] += count[1]; + count[1] = count[0]; + } + + for (int i = 0; i < 2; i++) { + // Params + if ((int) params[AVG1_PARAM + i].value == 1 && count[i] > 0) + mix[i] /= count[i]; + + // Outputs + outputs[MIX1_OUTPUT + 2*i].value = mix[i]; + outputs[INV1_OUTPUT + 2*i].value = -mix[i]; + // Lights + float dB = logf(fabsf(mix[i] / 10.0)) / logf(20.0) * 10.0; + for (int j = 0; j < 5; j++) { + float b = clampf(dB / 3.0 + 1 + j, 0.0, 1.0); + lights[VU1_LIGHT + 5*i + j].setBrightnessSmooth(b); + } + } +} + + +UnityWidget::UnityWidget() { + Unity *module = new Unity(); + setModule(module); + setPanel(SVG::load(assetPlugin(plugin, "res/Unity.svg"))); + + addChild(createScrew(Vec(15, 0))); + addChild(createScrew(Vec(box.size.x - 30, 0))); + addChild(createScrew(Vec(15, 365))); + addChild(createScrew(Vec(box.size.x - 30, 365))); + + addParam(createParam(mm2px(Vec(12.867, 52.961)), module, Unity::AVG1_PARAM, 0.0, 1.0, 0.0)); + addParam(createParam(mm2px(Vec(12.867, 107.006)), module, Unity::AVG2_PARAM, 0.0, 1.0, 0.0)); + + addInput(createInput(mm2px(Vec(2.361, 17.144)), module, Unity::IN1_INPUT + 0)); + addInput(createInput(mm2px(Vec(19.907, 17.144)), module, Unity::IN1_INPUT + 1)); + addInput(createInput(mm2px(Vec(2.361, 28.145)), module, Unity::IN1_INPUT + 2)); + addInput(createInput(mm2px(Vec(19.907, 28.145)), module, Unity::IN1_INPUT + 3)); + addInput(createInput(mm2px(Vec(2.361, 39.145)), module, Unity::IN1_INPUT + 4)); + addInput(createInput(mm2px(Vec(19.907, 39.145)), module, Unity::IN1_INPUT + 5)); + addInput(createInput(mm2px(Vec(2.361, 71.145)), module, Unity::IN2_INPUT + 0)); + addInput(createInput(mm2px(Vec(19.907, 71.145)), module, Unity::IN2_INPUT + 1)); + addInput(createInput(mm2px(Vec(2.361, 82.145)), module, Unity::IN2_INPUT + 2)); + addInput(createInput(mm2px(Vec(19.907, 82.145)), module, Unity::IN2_INPUT + 3)); + addInput(createInput(mm2px(Vec(2.361, 93.144)), module, Unity::IN2_INPUT + 4)); + addInput(createInput(mm2px(Vec(19.907, 93.144)), module, Unity::IN2_INPUT + 5)); + + addOutput(createOutput(mm2px(Vec(2.361, 54.15)), module, Unity::MIX1_OUTPUT)); + addOutput(createOutput(mm2px(Vec(19.907, 54.15)), module, Unity::INV1_OUTPUT)); + addOutput(createOutput(mm2px(Vec(2.361, 108.144)), module, Unity::MIX2_OUTPUT)); + addOutput(createOutput(mm2px(Vec(19.907, 108.144)), module, Unity::INV2_OUTPUT)); + + addChild(createLight>(mm2px(Vec(13.652, 19.663)), module, Unity::VU1_LIGHT + 0)); + addChild(createLight>(mm2px(Vec(13.652, 25.163)), module, Unity::VU1_LIGHT + 1)); + addChild(createLight>(mm2px(Vec(13.652, 30.663)), module, Unity::VU1_LIGHT + 2)); + addChild(createLight>(mm2px(Vec(13.652, 36.162)), module, Unity::VU1_LIGHT + 3)); + addChild(createLight>(mm2px(Vec(13.652, 41.662)), module, Unity::VU1_LIGHT + 4)); + addChild(createLight>(mm2px(Vec(13.652, 73.663)), module, Unity::VU2_LIGHT + 0)); + addChild(createLight>(mm2px(Vec(13.652, 79.163)), module, Unity::VU2_LIGHT + 1)); + addChild(createLight>(mm2px(Vec(13.652, 84.663)), module, Unity::VU2_LIGHT + 2)); + addChild(createLight>(mm2px(Vec(13.652, 90.162)), module, Unity::VU2_LIGHT + 3)); + addChild(createLight>(mm2px(Vec(13.652, 95.662)), module, Unity::VU2_LIGHT + 4)); +} + + +struct UnityMergeItem : MenuItem { + Unity *unity; + void onAction(EventAction &e) override { + unity->merge ^= true; + } + void step() override { + rightText = (unity->merge) ? "✔" : ""; + } +}; + +Menu *UnityWidget::createContextMenu() { + Menu *menu = ModuleWidget::createContextMenu(); + + MenuLabel *spacerLabel = new MenuLabel(); + menu->pushChild(spacerLabel); + + Unity *unity = dynamic_cast(module); + assert(unity); + + UnityMergeItem *mergeItem = new UnityMergeItem(); + mergeItem->text = "Merge channels 1 & 2"; + mergeItem->unity = unity; + menu->pushChild(mergeItem); + + return menu; +}