diff --git a/src/SEQ3.cpp b/src/SEQ3.cpp index 034ed05..60138e1 100644 --- a/src/SEQ3.cpp +++ b/src/SEQ3.cpp @@ -7,7 +7,7 @@ struct SEQ3 : Module { RUN_PARAM, RESET_PARAM, STEPS_PARAM, - ENUMS(ROW_PARAMS, 3 * 8), + ENUMS(CV_PARAMS, 3 * 8), ENUMS(GATE_PARAMS, 8), // added in 2.0 TEMPO_CV_PARAM, @@ -25,9 +25,7 @@ struct SEQ3 : Module { }; enum OutputIds { GATE_OUTPUT, - CV1_OUTPUT, - CV2_OUTPUT, - CV3_OUTPUT, + ENUMS(CV_OUTPUTS, 3), ENUMS(STEP_OUTPUTS, 8), // added in 2.0 STEPS_OUTPUT, @@ -46,10 +44,19 @@ struct SEQ3 : Module { }; bool running = true; + + dsp::BooleanTrigger runButtonTrigger; + dsp::BooleanTrigger resetButtonTrigger; + dsp::BooleanTrigger gateTriggers[8]; + dsp::SchmittTrigger clockTrigger; - dsp::SchmittTrigger runningTrigger; + dsp::SchmittTrigger runTrigger; dsp::SchmittTrigger resetTrigger; - dsp::SchmittTrigger gateTriggers[8]; + + dsp::PulseGenerator clockPulse; + dsp::PulseGenerator runPulse; + dsp::PulseGenerator resetPulse; + /** Phase of internal LFO */ float phase = 0.f; int index = 0; @@ -57,28 +64,44 @@ struct SEQ3 : Module { SEQ3() { config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); + configParam(TEMPO_PARAM, -2.f, 4.f, 1.f, "Tempo", " bpm", 2.f, 60.f); + getParamQuantity(TEMPO_PARAM)->randomizeEnabled = false; configParam(TEMPO_CV_PARAM, 0.f, 1.f, 1.f, "Tempo CV", "%", 0, 100); + getParamQuantity(TEMPO_CV_PARAM)->randomizeEnabled = false; configButton(RUN_PARAM, "Run"); configButton(RESET_PARAM, "Reset"); configParam(STEPS_PARAM, 1.f, 8.f, 8.f, "Steps"); + getParamQuantity(STEPS_PARAM)->randomizeEnabled = false; configParam(STEPS_CV_PARAM, 0.f, 1.f, 1.f, "Steps CV", "%", 0, 100); + getParamQuantity(STEPS_CV_PARAM)->randomizeEnabled = false; paramQuantities[STEPS_PARAM]->snapEnabled = true; for (int j = 0; j < 3; j++) { for (int i = 0; i < 8; i++) { - configParam(ROW_PARAMS + 8 * j + i, -10.f, 10.f, 0.f, string::f("Row %d step %d", j + 1, i + 1), " V"); + configParam(CV_PARAMS + 8 * j + i, -10.f, 10.f, 0.f, string::f("CV %d step %d", j + 1, i + 1), " V"); } } - configInput(TEMPO_INPUT, "Clock rate"); - configInput(CLOCK_INPUT, "External clock"); + for (int i = 0; i < 8; i++) { + configButton(GATE_PARAMS + i, string::f("Step %d gate", i + 1)); + } + + configInput(TEMPO_INPUT, "Tempo"); + configInput(CLOCK_INPUT, "Clock"); + configInput(RUN_INPUT, "Run"); configInput(RESET_INPUT, "Reset"); configInput(STEPS_INPUT, "Steps"); - configOutput(GATE_OUTPUT, "Gate"); - configOutput(CV1_OUTPUT, "Row 1"); - configOutput(CV2_OUTPUT, "Row 2"); - configOutput(CV3_OUTPUT, "Row 3"); - for (int i = 0; i < 8; i++) + + for (int i = 0; i < 8; i++) { configOutput(STEP_OUTPUTS + i, string::f("Step %d", i + 1)); + } + for (int j = 0; j < 3; j++) { + configOutput(CV_OUTPUTS + j, string::f("CV %d", j + 1)); + } + configOutput(GATE_OUTPUT, "Gate"); + configOutput(STEPS_OUTPUT, "Steps"); + configOutput(CLOCK_OUTPUT, "Clock"); + configOutput(RUN_OUTPUT, "Run"); + configOutput(RESET_OUTPUT, "Reset"); configLight(CLOCK_LIGHT, "Clock trigger"); @@ -89,12 +112,118 @@ struct SEQ3 : Module { for (int i = 0; i < 8; i++) { gates[i] = true; } + index = 0; } void onRandomize() override { for (int i = 0; i < 8; i++) { - gates[i] = (random::uniform() > 0.5f); + gates[i] = random::get(); + } + } + + void rotateStates(int delta) { + // Rotate CV params + for (int j = 0; j < 3; j++) { + float cvs[8]; + for (int i = 0; i < 8; i++) { + cvs[i] = params[CV_PARAMS + 8 * j + i].getValue(); + } + for (int i = 0; i < 8; i++) { + int index = eucMod(i + delta, 8); + params[CV_PARAMS + 8 * j + index].setValue(cvs[i]); + } + } + // Rotate gates + bool gates[8]; + for (int i = 0; i < 8; i++) { + gates[i] = this->gates[i]; + } + for (int i = 0; i < 8; i++) { + int index = eucMod(i + delta, 8); + this->gates[index] = gates[i]; + } + } + + void process(const ProcessArgs& args) override { + // Run + // Use bitwise OR "|" to always evaluate both expressions + if (runButtonTrigger.process(params[RUN_PARAM].getValue()) | runTrigger.process(inputs[RUN_INPUT].getVoltage(), 0.1f, 2.f)) { + running ^= true; + runPulse.trigger(1e-3f); + } + + // Reset + if (resetButtonTrigger.process(params[RESET_PARAM].getValue()) | resetTrigger.process(inputs[RESET_INPUT].getVoltage(), 0.1f, 2.f)) { + resetPulse.trigger(1e-3f); + // Reset step index + index = 0; + } + + bool resetGate = resetPulse.process(args.sampleTime); + + // Clock + bool clock = false; + if (running) { + if (inputs[CLOCK_INPUT].isConnected()) { + // External clock + // Ignore clock while reset pulse is high + if (clockTrigger.process(inputs[CLOCK_INPUT].getVoltage(), 0.1f, 2.f) && !resetGate) { + clock = true; + } + } + else { + // Internal clock + float clockPitch = params[TEMPO_PARAM].getValue() + inputs[TEMPO_INPUT].getVoltage() * params[TEMPO_CV_PARAM].getValue(); + float clockFreq = std::pow(2.f, clockPitch); + phase += clockFreq * args.sampleTime; + if (phase >= 1.f && !resetGate) { + clock = true; + } + phase -= std::trunc(phase); + } + } + + // Get number of steps + float steps = params[STEPS_PARAM].getValue() + inputs[STEPS_INPUT].getVoltage() * params[STEPS_CV_PARAM].getValue(); + int numSteps = (int) clamp(std::round(steps), 1.f, 8.f); + + // Advance step + if (clock) { + clockPulse.trigger(1e-3f); + index++; + if (index >= numSteps) + index = 0; + } + bool clockGate = clockPulse.process(args.sampleTime); + + // Gate buttons + for (int i = 0; i < 8; i++) { + if (gateTriggers[i].process(params[GATE_PARAMS + i].getValue())) { + gates[i] ^= true; + } + lights[GATE_LIGHTS + i].setBrightness(gates[i]); } + + // Step outputs + for (int i = 0; i < 8; i++) { + outputs[STEP_OUTPUTS + i].setVoltage((index == i) ? 10.f : 0.f); + lights[STEP_LIGHTS + i].setSmoothBrightness(index == i, args.sampleTime); + } + + // Outputs + outputs[CV_OUTPUTS + 0].setVoltage(params[CV_PARAMS + 8 * 0 + index].getValue()); + outputs[CV_OUTPUTS + 1].setVoltage(params[CV_PARAMS + 8 * 1 + index].getValue()); + outputs[CV_OUTPUTS + 2].setVoltage(params[CV_PARAMS + 8 * 2 + index].getValue()); + outputs[GATE_OUTPUT].setVoltage((clockGate && gates[index]) ? 10.f : 0.f); + + outputs[STEPS_OUTPUT].setVoltage((numSteps - 1) * 1.f); + outputs[CLOCK_OUTPUT].setVoltage(clockGate ? 10.f : 0.f); + outputs[RUN_OUTPUT].setVoltage(runPulse.process(args.sampleTime) ? 10.f : 0.f); + outputs[RESET_OUTPUT].setVoltage(resetGate ? 10.f : 0.f); + + lights[CLOCK_LIGHT].setSmoothBrightness(clockGate, args.sampleTime); + lights[RUN_LIGHT].setBrightness(running); + lights[RESET_LIGHT].setSmoothBrightness(resetGate, args.sampleTime); } json_t* dataToJson() override { @@ -129,63 +258,6 @@ struct SEQ3 : Module { } } } - - void setIndex(int index) { - int numSteps = (int) clamp(std::round(params[STEPS_PARAM].getValue() + inputs[STEPS_INPUT].getVoltage()), 1.f, 8.f); - phase = 0.f; - this->index = index; - if (this->index >= numSteps) - this->index = 0; - } - - void process(const ProcessArgs& args) override { - // Run - if (runningTrigger.process(params[RUN_PARAM].getValue())) { - running = !running; - } - - bool gateIn = false; - if (running) { - if (inputs[CLOCK_INPUT].isConnected()) { - // External clock - if (clockTrigger.process(inputs[CLOCK_INPUT].getVoltage())) { - setIndex(index + 1); - } - gateIn = clockTrigger.isHigh(); - } - else { - // Internal clock - float clockTime = std::pow(2.f, params[TEMPO_PARAM].getValue() + inputs[TEMPO_INPUT].getVoltage()); - phase += clockTime * args.sampleTime; - if (phase >= 1.f) { - setIndex(index + 1); - } - gateIn = (phase < 0.5f); - } - } - - // Reset - if (resetTrigger.process(params[RESET_PARAM].getValue() + inputs[RESET_INPUT].getVoltage())) { - setIndex(0); - } - - // Gate buttons - for (int i = 0; i < 8; i++) { - if (gateTriggers[i].process(params[GATE_PARAMS + i].getValue())) { - gates[i] = !gates[i]; - } - outputs[STEP_OUTPUTS + i].setVoltage((running && gateIn && i == index && gates[i]) ? 10.f : 0.f); - lights[GATE_LIGHTS + i].setSmoothBrightness((gateIn && i == index) ? (gates[i] ? 1.f : 0.33) : (gates[i] ? 0.66 : 0.0), args.sampleTime); - } - - // Outputs - outputs[CV1_OUTPUT].setVoltage(params[ROW_PARAMS * 8 + 0 + index].getValue()); - outputs[CV2_OUTPUT].setVoltage(params[ROW_PARAMS * 8 + 1 + index].getValue()); - outputs[CV3_OUTPUT].setVoltage(params[ROW_PARAMS * 8 + 2 + index].getValue()); - outputs[GATE_OUTPUT].setVoltage((gateIn && gates[index]) ? 10.f : 0.f); - lights[RUN_LIGHT].setBrightness(running); - lights[RESET_LIGHT].setSmoothBrightness(resetTrigger.isHigh(), args.sampleTime); - } }; @@ -202,45 +274,45 @@ struct SEQ3Widget : ModuleWidget { addParam(createParamCentered(mm2px(Vec(11.753, 26.755)), module, SEQ3::TEMPO_PARAM)); addParam(createParamCentered(mm2px(Vec(32.077, 26.782)), module, SEQ3::STEPS_PARAM)); addParam(createParamCentered(mm2px(Vec(49.372, 34.066)), module, SEQ3::TEMPO_CV_PARAM)); - addParam(createLightParamCentered>>(mm2px(Vec(88.424, 33.679)), module, SEQ3::RUN_PARAM, SEQ3::RUN_LIGHT)); + addParam(createLightParamCentered>>(mm2px(Vec(88.424, 33.679)), module, SEQ3::RUN_PARAM, SEQ3::RUN_LIGHT)); addParam(createParamCentered(mm2px(Vec(62.39, 34.066)), module, SEQ3::STEPS_CV_PARAM)); - addParam(createLightParamCentered>>(mm2px(Vec(101.441, 33.679)), module, SEQ3::RESET_PARAM, SEQ3::RESET_LIGHT)); - - addParam(createParamCentered(mm2px(Vec(10.319, 46.563)), module, SEQ3::ROW_PARAMS + 8 * 0 + 0)); - addParam(createParamCentered(mm2px(Vec(23.336, 46.563)), module, SEQ3::ROW_PARAMS + 8 * 0 + 1)); - addParam(createParamCentered(mm2px(Vec(36.354, 46.563)), module, SEQ3::ROW_PARAMS + 8 * 0 + 2)); - addParam(createParamCentered(mm2px(Vec(49.371, 46.563)), module, SEQ3::ROW_PARAMS + 8 * 0 + 3)); - addParam(createParamCentered(mm2px(Vec(62.389, 46.563)), module, SEQ3::ROW_PARAMS + 8 * 0 + 4)); - addParam(createParamCentered(mm2px(Vec(75.406, 46.563)), module, SEQ3::ROW_PARAMS + 8 * 0 + 5)); - addParam(createParamCentered(mm2px(Vec(88.424, 46.563)), module, SEQ3::ROW_PARAMS + 8 * 0 + 6)); - addParam(createParamCentered(mm2px(Vec(101.441, 46.563)), module, SEQ3::ROW_PARAMS + 8 * 0 + 7)); - - addParam(createParamCentered(mm2px(Vec(10.319, 60.607)), module, SEQ3::ROW_PARAMS + 8 * 1 + 0)); - addParam(createParamCentered(mm2px(Vec(23.336, 60.607)), module, SEQ3::ROW_PARAMS + 8 * 1 + 1)); - addParam(createParamCentered(mm2px(Vec(36.354, 60.607)), module, SEQ3::ROW_PARAMS + 8 * 1 + 2)); - addParam(createParamCentered(mm2px(Vec(49.371, 60.607)), module, SEQ3::ROW_PARAMS + 8 * 1 + 3)); - addParam(createParamCentered(mm2px(Vec(62.389, 60.607)), module, SEQ3::ROW_PARAMS + 8 * 1 + 4)); - addParam(createParamCentered(mm2px(Vec(75.406, 60.607)), module, SEQ3::ROW_PARAMS + 8 * 1 + 5)); - addParam(createParamCentered(mm2px(Vec(88.424, 60.607)), module, SEQ3::ROW_PARAMS + 8 * 1 + 6)); - addParam(createParamCentered(mm2px(Vec(101.441, 60.607)), module, SEQ3::ROW_PARAMS + 8 * 1 + 7)); - - addParam(createParamCentered(mm2px(Vec(10.319, 74.605)), module, SEQ3::ROW_PARAMS + 8 * 2 + 0)); - addParam(createParamCentered(mm2px(Vec(23.336, 74.605)), module, SEQ3::ROW_PARAMS + 8 * 2 + 1)); - addParam(createParamCentered(mm2px(Vec(36.354, 74.605)), module, SEQ3::ROW_PARAMS + 8 * 2 + 2)); - addParam(createParamCentered(mm2px(Vec(49.371, 74.605)), module, SEQ3::ROW_PARAMS + 8 * 2 + 3)); - addParam(createParamCentered(mm2px(Vec(62.389, 74.605)), module, SEQ3::ROW_PARAMS + 8 * 2 + 4)); - addParam(createParamCentered(mm2px(Vec(75.406, 74.605)), module, SEQ3::ROW_PARAMS + 8 * 2 + 5)); - addParam(createParamCentered(mm2px(Vec(88.424, 74.605)), module, SEQ3::ROW_PARAMS + 8 * 2 + 6)); - addParam(createParamCentered(mm2px(Vec(101.441, 74.605)), module, SEQ3::ROW_PARAMS + 8 * 2 + 7)); - - addParam(createLightParamCentered>>(mm2px(Vec(10.319, 85.801)), module, SEQ3::GATE_PARAMS + 0, SEQ3::GATE_LIGHTS + 0)); - addParam(createLightParamCentered>>(mm2px(Vec(23.336, 85.801)), module, SEQ3::GATE_PARAMS + 1, SEQ3::GATE_LIGHTS + 1)); - addParam(createLightParamCentered>>(mm2px(Vec(36.354, 85.801)), module, SEQ3::GATE_PARAMS + 2, SEQ3::GATE_LIGHTS + 2)); - addParam(createLightParamCentered>>(mm2px(Vec(49.371, 85.801)), module, SEQ3::GATE_PARAMS + 3, SEQ3::GATE_LIGHTS + 3)); - addParam(createLightParamCentered>>(mm2px(Vec(62.389, 85.801)), module, SEQ3::GATE_PARAMS + 4, SEQ3::GATE_LIGHTS + 4)); - addParam(createLightParamCentered>>(mm2px(Vec(75.406, 85.801)), module, SEQ3::GATE_PARAMS + 5, SEQ3::GATE_LIGHTS + 5)); - addParam(createLightParamCentered>>(mm2px(Vec(88.424, 85.801)), module, SEQ3::GATE_PARAMS + 6, SEQ3::GATE_LIGHTS + 6)); - addParam(createLightParamCentered>>(mm2px(Vec(101.441, 85.801)), module, SEQ3::GATE_PARAMS + 7, SEQ3::GATE_LIGHTS + 7)); + addParam(createLightParamCentered>>(mm2px(Vec(101.441, 33.679)), module, SEQ3::RESET_PARAM, SEQ3::RESET_LIGHT)); + + addParam(createParamCentered(mm2px(Vec(10.319, 46.563)), module, SEQ3::CV_PARAMS + 8 * 0 + 0)); + addParam(createParamCentered(mm2px(Vec(23.336, 46.563)), module, SEQ3::CV_PARAMS + 8 * 0 + 1)); + addParam(createParamCentered(mm2px(Vec(36.354, 46.563)), module, SEQ3::CV_PARAMS + 8 * 0 + 2)); + addParam(createParamCentered(mm2px(Vec(49.371, 46.563)), module, SEQ3::CV_PARAMS + 8 * 0 + 3)); + addParam(createParamCentered(mm2px(Vec(62.389, 46.563)), module, SEQ3::CV_PARAMS + 8 * 0 + 4)); + addParam(createParamCentered(mm2px(Vec(75.406, 46.563)), module, SEQ3::CV_PARAMS + 8 * 0 + 5)); + addParam(createParamCentered(mm2px(Vec(88.424, 46.563)), module, SEQ3::CV_PARAMS + 8 * 0 + 6)); + addParam(createParamCentered(mm2px(Vec(101.441, 46.563)), module, SEQ3::CV_PARAMS + 8 * 0 + 7)); + + addParam(createParamCentered(mm2px(Vec(10.319, 60.607)), module, SEQ3::CV_PARAMS + 8 * 1 + 0)); + addParam(createParamCentered(mm2px(Vec(23.336, 60.607)), module, SEQ3::CV_PARAMS + 8 * 1 + 1)); + addParam(createParamCentered(mm2px(Vec(36.354, 60.607)), module, SEQ3::CV_PARAMS + 8 * 1 + 2)); + addParam(createParamCentered(mm2px(Vec(49.371, 60.607)), module, SEQ3::CV_PARAMS + 8 * 1 + 3)); + addParam(createParamCentered(mm2px(Vec(62.389, 60.607)), module, SEQ3::CV_PARAMS + 8 * 1 + 4)); + addParam(createParamCentered(mm2px(Vec(75.406, 60.607)), module, SEQ3::CV_PARAMS + 8 * 1 + 5)); + addParam(createParamCentered(mm2px(Vec(88.424, 60.607)), module, SEQ3::CV_PARAMS + 8 * 1 + 6)); + addParam(createParamCentered(mm2px(Vec(101.441, 60.607)), module, SEQ3::CV_PARAMS + 8 * 1 + 7)); + + addParam(createParamCentered(mm2px(Vec(10.319, 74.605)), module, SEQ3::CV_PARAMS + 8 * 2 + 0)); + addParam(createParamCentered(mm2px(Vec(23.336, 74.605)), module, SEQ3::CV_PARAMS + 8 * 2 + 1)); + addParam(createParamCentered(mm2px(Vec(36.354, 74.605)), module, SEQ3::CV_PARAMS + 8 * 2 + 2)); + addParam(createParamCentered(mm2px(Vec(49.371, 74.605)), module, SEQ3::CV_PARAMS + 8 * 2 + 3)); + addParam(createParamCentered(mm2px(Vec(62.389, 74.605)), module, SEQ3::CV_PARAMS + 8 * 2 + 4)); + addParam(createParamCentered(mm2px(Vec(75.406, 74.605)), module, SEQ3::CV_PARAMS + 8 * 2 + 5)); + addParam(createParamCentered(mm2px(Vec(88.424, 74.605)), module, SEQ3::CV_PARAMS + 8 * 2 + 6)); + addParam(createParamCentered(mm2px(Vec(101.441, 74.605)), module, SEQ3::CV_PARAMS + 8 * 2 + 7)); + + addParam(createLightParamCentered>>(mm2px(Vec(10.319, 85.801)), module, SEQ3::GATE_PARAMS + 0, SEQ3::GATE_LIGHTS + 0)); + addParam(createLightParamCentered>>(mm2px(Vec(23.336, 85.801)), module, SEQ3::GATE_PARAMS + 1, SEQ3::GATE_LIGHTS + 1)); + addParam(createLightParamCentered>>(mm2px(Vec(36.354, 85.801)), module, SEQ3::GATE_PARAMS + 2, SEQ3::GATE_LIGHTS + 2)); + addParam(createLightParamCentered>>(mm2px(Vec(49.371, 85.801)), module, SEQ3::GATE_PARAMS + 3, SEQ3::GATE_LIGHTS + 3)); + addParam(createLightParamCentered>>(mm2px(Vec(62.389, 85.801)), module, SEQ3::GATE_PARAMS + 4, SEQ3::GATE_LIGHTS + 4)); + addParam(createLightParamCentered>>(mm2px(Vec(75.406, 85.801)), module, SEQ3::GATE_PARAMS + 5, SEQ3::GATE_LIGHTS + 5)); + addParam(createLightParamCentered>>(mm2px(Vec(88.424, 85.801)), module, SEQ3::GATE_PARAMS + 6, SEQ3::GATE_LIGHTS + 6)); + addParam(createLightParamCentered>>(mm2px(Vec(101.441, 85.801)), module, SEQ3::GATE_PARAMS + 7, SEQ3::GATE_LIGHTS + 7)); addInput(createInputCentered(mm2px(Vec(49.371, 17.307)), module, SEQ3::TEMPO_INPUT)); addInput(createInputCentered(mm2px(Vec(62.389, 17.307)), module, SEQ3::STEPS_INPUT)); @@ -257,9 +329,9 @@ struct SEQ3Widget : ModuleWidget { addOutput(createOutputCentered(mm2px(Vec(88.424, 96.859)), module, SEQ3::STEP_OUTPUTS + 6)); addOutput(createOutputCentered(mm2px(Vec(101.441, 96.859)), module, SEQ3::STEP_OUTPUTS + 7)); - addOutput(createOutputCentered(mm2px(Vec(10.319, 113.115)), module, SEQ3::CV1_OUTPUT)); - addOutput(createOutputCentered(mm2px(Vec(23.336, 113.115)), module, SEQ3::CV2_OUTPUT)); - addOutput(createOutputCentered(mm2px(Vec(36.354, 113.115)), module, SEQ3::CV3_OUTPUT)); + addOutput(createOutputCentered(mm2px(Vec(10.319, 113.115)), module, SEQ3::CV_OUTPUTS + 0)); + addOutput(createOutputCentered(mm2px(Vec(23.336, 113.115)), module, SEQ3::CV_OUTPUTS + 1)); + addOutput(createOutputCentered(mm2px(Vec(36.354, 113.115)), module, SEQ3::CV_OUTPUTS + 2)); addOutput(createOutputCentered(mm2px(Vec(49.371, 113.115)), module, SEQ3::GATE_OUTPUT)); addOutput(createOutputCentered(mm2px(Vec(62.389, 113.115)), module, SEQ3::STEPS_OUTPUT)); addOutput(createOutputCentered(mm2px(Vec(75.406, 113.115)), module, SEQ3::CLOCK_OUTPUT)); @@ -268,17 +340,31 @@ struct SEQ3Widget : ModuleWidget { addChild(createLightCentered>(mm2px(Vec(75.406, 33.497)), module, SEQ3::CLOCK_LIGHT)); - addChild(createLightCentered>(mm2px(Vec(14.064, 93.103)), module, SEQ3::GATE_LIGHTS + 0)); - addChild(createLightCentered>(mm2px(Vec(27.084, 93.103)), module, SEQ3::GATE_LIGHTS + 1)); - addChild(createLightCentered>(mm2px(Vec(40.103, 93.103)), module, SEQ3::GATE_LIGHTS + 2)); - addChild(createLightCentered>(mm2px(Vec(53.122, 93.103)), module, SEQ3::GATE_LIGHTS + 3)); - addChild(createLightCentered>(mm2px(Vec(66.142, 93.103)), module, SEQ3::GATE_LIGHTS + 4)); - addChild(createLightCentered>(mm2px(Vec(79.161, 93.103)), module, SEQ3::GATE_LIGHTS + 5)); - addChild(createLightCentered>(mm2px(Vec(92.181, 93.103)), module, SEQ3::GATE_LIGHTS + 6)); - addChild(createLightCentered>(mm2px(Vec(105.2, 93.103)), module, SEQ3::GATE_LIGHTS + 7)); + addChild(createLightCentered>(mm2px(Vec(14.064, 93.103)), module, SEQ3::STEP_LIGHTS + 0)); + addChild(createLightCentered>(mm2px(Vec(27.084, 93.103)), module, SEQ3::STEP_LIGHTS + 1)); + addChild(createLightCentered>(mm2px(Vec(40.103, 93.103)), module, SEQ3::STEP_LIGHTS + 2)); + addChild(createLightCentered>(mm2px(Vec(53.122, 93.103)), module, SEQ3::STEP_LIGHTS + 3)); + addChild(createLightCentered>(mm2px(Vec(66.142, 93.103)), module, SEQ3::STEP_LIGHTS + 4)); + addChild(createLightCentered>(mm2px(Vec(79.161, 93.103)), module, SEQ3::STEP_LIGHTS + 5)); + addChild(createLightCentered>(mm2px(Vec(92.181, 93.103)), module, SEQ3::STEP_LIGHTS + 6)); + addChild(createLightCentered>(mm2px(Vec(105.2, 93.103)), module, SEQ3::STEP_LIGHTS + 7)); } -}; + void appendContextMenu(Menu* menu) override { + SEQ3* module = dynamic_cast(this->module); + assert(module); + + menu->addChild(new MenuSeparator); + + menu->addChild(createMenuItem("Rotate left", "", + [=]() {module->rotateStates(-1);} + )); + + menu->addChild(createMenuItem("Rotate right", "", + [=]() {module->rotateStates(1);} + )); + } +}; Model* modelSEQ3 = createModel("SEQ3");