You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

406 lines
17KB

  1. #include "plugin.hpp"
  2. struct SEQ3 : Module {
  3. enum ParamIds {
  4. TEMPO_PARAM,
  5. RUN_PARAM,
  6. RESET_PARAM,
  7. TRIG_PARAM,
  8. ENUMS(CV_PARAMS, 3 * 8),
  9. ENUMS(GATE_PARAMS, 8),
  10. // added in 2.0
  11. TEMPO_CV_PARAM,
  12. STEPS_CV_PARAM,
  13. NUM_PARAMS
  14. };
  15. enum InputIds {
  16. TEMPO_INPUT,
  17. CLOCK_INPUT,
  18. RESET_INPUT,
  19. STEPS_INPUT,
  20. // added in 2.0
  21. RUN_INPUT,
  22. NUM_INPUTS
  23. };
  24. enum OutputIds {
  25. TRIG_OUTPUT,
  26. ENUMS(CV_OUTPUTS, 3),
  27. ENUMS(STEP_OUTPUTS, 8),
  28. // added in 2.0
  29. STEPS_OUTPUT,
  30. CLOCK_OUTPUT,
  31. RUN_OUTPUT,
  32. RESET_OUTPUT,
  33. NUM_OUTPUTS
  34. };
  35. enum LightIds {
  36. CLOCK_LIGHT,
  37. RUN_LIGHT,
  38. RESET_LIGHT,
  39. ENUMS(GATE_LIGHTS, 8),
  40. ENUMS(STEP_LIGHTS, 8 * 2),
  41. NUM_LIGHTS
  42. };
  43. bool running = true;
  44. bool clockPassthrough = false;
  45. dsp::BooleanTrigger runButtonTrigger;
  46. dsp::BooleanTrigger resetButtonTrigger;
  47. dsp::BooleanTrigger gateTriggers[8];
  48. dsp::SchmittTrigger runTrigger;
  49. dsp::SchmittTrigger clockTrigger;
  50. dsp::SchmittTrigger resetTrigger;
  51. dsp::PulseGenerator runPulse;
  52. dsp::PulseGenerator clockPulse;
  53. dsp::PulseGenerator resetPulse;
  54. /** Phase of internal LFO */
  55. float phase = 0.f;
  56. int index = 0;
  57. bool gates[8] = {};
  58. SEQ3() {
  59. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  60. configParam(TEMPO_PARAM, -2.f, 4.f, 1.f, "Tempo", " bpm", 2.f, 60.f);
  61. getParamQuantity(TEMPO_PARAM)->randomizeEnabled = false;
  62. configParam(TEMPO_CV_PARAM, 0.f, 1.f, 1.f, "Tempo CV", "%", 0, 100);
  63. getParamQuantity(TEMPO_CV_PARAM)->randomizeEnabled = false;
  64. configButton(RUN_PARAM, "Run");
  65. configButton(RESET_PARAM, "Reset");
  66. configParam(TRIG_PARAM, 1.f, 8.f, 8.f, "Steps");
  67. getParamQuantity(TRIG_PARAM)->randomizeEnabled = false;
  68. configParam(STEPS_CV_PARAM, 0.f, 1.f, 1.f, "Steps CV", "%", 0, 100);
  69. getParamQuantity(STEPS_CV_PARAM)->randomizeEnabled = false;
  70. paramQuantities[TRIG_PARAM]->snapEnabled = true;
  71. for (int j = 0; j < 3; j++) {
  72. for (int i = 0; i < 8; i++) {
  73. configParam(CV_PARAMS + 8 * j + i, -10.f, 10.f, 0.f, string::f("CV %d step %d", j + 1, i + 1), " V");
  74. }
  75. }
  76. for (int i = 0; i < 8; i++) {
  77. configButton(GATE_PARAMS + i, string::f("Step %d trigger", i + 1));
  78. }
  79. configInput(TEMPO_INPUT, "Tempo");
  80. configInput(CLOCK_INPUT, "Clock");
  81. configInput(RUN_INPUT, "Run");
  82. configInput(RESET_INPUT, "Reset");
  83. configInput(STEPS_INPUT, "Steps");
  84. for (int i = 0; i < 8; i++) {
  85. configOutput(STEP_OUTPUTS + i, string::f("Step %d", i + 1));
  86. }
  87. for (int j = 0; j < 3; j++) {
  88. configOutput(CV_OUTPUTS + j, string::f("CV %d", j + 1));
  89. }
  90. configOutput(TRIG_OUTPUT, "Trigger");
  91. configOutput(STEPS_OUTPUT, "Steps");
  92. configOutput(CLOCK_OUTPUT, "Clock");
  93. configOutput(RUN_OUTPUT, "Run");
  94. configOutput(RESET_OUTPUT, "Reset");
  95. configLight(CLOCK_LIGHT, "Clock trigger");
  96. onReset();
  97. }
  98. void onReset() override {
  99. clockPassthrough = false;
  100. for (int i = 0; i < 8; i++) {
  101. gates[i] = true;
  102. }
  103. index = 0;
  104. }
  105. void onRandomize() override {
  106. for (int i = 0; i < 8; i++) {
  107. gates[i] = random::get<bool>();
  108. }
  109. }
  110. void rotateStates(int delta) {
  111. // Rotate CV params
  112. for (int j = 0; j < 3; j++) {
  113. float cvs[8];
  114. for (int i = 0; i < 8; i++) {
  115. cvs[i] = params[CV_PARAMS + 8 * j + i].getValue();
  116. }
  117. for (int i = 0; i < 8; i++) {
  118. int index = eucMod(i + delta, 8);
  119. params[CV_PARAMS + 8 * j + index].setValue(cvs[i]);
  120. }
  121. }
  122. // Rotate gates
  123. bool gates[8];
  124. for (int i = 0; i < 8; i++) {
  125. gates[i] = this->gates[i];
  126. }
  127. for (int i = 0; i < 8; i++) {
  128. int index = eucMod(i + delta, 8);
  129. this->gates[index] = gates[i];
  130. }
  131. }
  132. void process(const ProcessArgs& args) override {
  133. // Toggle run
  134. bool runButtonTriggered = runButtonTrigger.process(params[RUN_PARAM].getValue());
  135. bool runTriggered = runTrigger.process(inputs[RUN_INPUT].getVoltage(), 0.1f, 2.f);
  136. if (runButtonTriggered || runTriggered) {
  137. running ^= true;
  138. runPulse.trigger(1e-3f);
  139. }
  140. bool runGate = runPulse.process(args.sampleTime);
  141. int oldIndex = index;
  142. // Reset to step 1
  143. bool resetButtonTriggered = resetButtonTrigger.process(params[RESET_PARAM].getValue());
  144. bool resetTriggered = resetTrigger.process(inputs[RESET_INPUT].getVoltage(), 0.1f, 2.f);
  145. if (resetButtonTriggered || resetTriggered) {
  146. resetPulse.trigger(1e-3f);
  147. // Reset step index
  148. index = 0;
  149. // Reset phase
  150. phase = 0.f;
  151. }
  152. bool resetGate = resetPulse.process(args.sampleTime);
  153. // Clock
  154. bool clock = false;
  155. bool clockGate = false;
  156. if (running) {
  157. if (inputs[CLOCK_INPUT].isConnected()) {
  158. // External clock
  159. bool clockTriggered = clockTrigger.process(inputs[CLOCK_INPUT].getVoltage(), 0.1f, 2.f);
  160. // Ignore clock while reset pulse is high
  161. if (clockTriggered && !resetGate) {
  162. clock = true;
  163. }
  164. clockGate = clockTrigger.isHigh();
  165. }
  166. else {
  167. // Internal clock
  168. float clockPitch = params[TEMPO_PARAM].getValue() + inputs[TEMPO_INPUT].getVoltage() * params[TEMPO_CV_PARAM].getValue();
  169. float clockFreq = dsp::exp2_taylor5(clockPitch);
  170. phase += clockFreq * args.sampleTime;
  171. if (phase >= 1.f && !resetGate) {
  172. clock = true;
  173. }
  174. phase -= std::trunc(phase);
  175. clockGate = (phase < 0.5f);
  176. }
  177. }
  178. // Get number of steps
  179. float steps = params[TRIG_PARAM].getValue() + inputs[STEPS_INPUT].getVoltage() * params[STEPS_CV_PARAM].getValue();
  180. int numSteps = (int) clamp(std::round(steps), 1.f, 8.f);
  181. // Advance step when clocked
  182. if (clock) {
  183. index++;
  184. if (index >= numSteps)
  185. index = 0;
  186. }
  187. // Trigger pulse if step was changed
  188. if (index != oldIndex) {
  189. clockPulse.trigger(1e-3f);
  190. }
  191. // Unless we're passing the clock gate, generate a pulse
  192. if (!clockPassthrough) {
  193. clockGate = clockPulse.process(args.sampleTime);
  194. }
  195. // Gate buttons
  196. for (int i = 0; i < 8; i++) {
  197. if (gateTriggers[i].process(params[GATE_PARAMS + i].getValue())) {
  198. gates[i] ^= true;
  199. }
  200. lights[GATE_LIGHTS + i].setBrightness(gates[i]);
  201. }
  202. // Step outputs
  203. for (int i = 0; i < 8; i++) {
  204. outputs[STEP_OUTPUTS + i].setVoltage((index == i) ? 10.f : 0.f);
  205. lights[STEP_LIGHTS + 2 * i + 0].setSmoothBrightness(index == i, args.sampleTime);
  206. lights[STEP_LIGHTS + 2 * i + 1].setBrightness(i >= numSteps);
  207. }
  208. // Outputs
  209. outputs[CV_OUTPUTS + 0].setVoltage(params[CV_PARAMS + 8 * 0 + index].getValue());
  210. outputs[CV_OUTPUTS + 1].setVoltage(params[CV_PARAMS + 8 * 1 + index].getValue());
  211. outputs[CV_OUTPUTS + 2].setVoltage(params[CV_PARAMS + 8 * 2 + index].getValue());
  212. outputs[TRIG_OUTPUT].setVoltage((clockGate && gates[index]) ? 10.f : 0.f);
  213. outputs[STEPS_OUTPUT].setVoltage((numSteps - 1) * 1.f);
  214. outputs[CLOCK_OUTPUT].setVoltage(clockGate ? 10.f : 0.f);
  215. outputs[RUN_OUTPUT].setVoltage(runGate ? 10.f : 0.f);
  216. outputs[RESET_OUTPUT].setVoltage(resetGate ? 10.f : 0.f);
  217. lights[CLOCK_LIGHT].setSmoothBrightness(clockGate, args.sampleTime);
  218. lights[RUN_LIGHT].setBrightness(running);
  219. lights[RESET_LIGHT].setSmoothBrightness(resetGate, args.sampleTime);
  220. }
  221. json_t* dataToJson() override {
  222. json_t* rootJ = json_object();
  223. // running
  224. json_object_set_new(rootJ, "running", json_boolean(running));
  225. // gates
  226. json_t* gatesJ = json_array();
  227. for (int i = 0; i < 8; i++) {
  228. json_array_insert_new(gatesJ, i, json_integer((int) gates[i]));
  229. }
  230. json_object_set_new(rootJ, "gates", gatesJ);
  231. // clockPassthrough
  232. json_object_set_new(rootJ, "clockPassthrough", json_boolean(clockPassthrough));
  233. return rootJ;
  234. }
  235. void dataFromJson(json_t* rootJ) override {
  236. // running
  237. json_t* runningJ = json_object_get(rootJ, "running");
  238. if (runningJ)
  239. running = json_is_true(runningJ);
  240. // gates
  241. json_t* gatesJ = json_object_get(rootJ, "gates");
  242. if (gatesJ) {
  243. for (int i = 0; i < 8; i++) {
  244. json_t* gateJ = json_array_get(gatesJ, i);
  245. if (gateJ)
  246. gates[i] = !!json_integer_value(gateJ);
  247. }
  248. }
  249. // clockPassthrough
  250. json_t* clockPassthroughJ = json_object_get(rootJ, "clockPassthrough");
  251. if (clockPassthroughJ)
  252. clockPassthrough = json_is_true(clockPassthroughJ);
  253. else
  254. clockPassthrough = true;
  255. }
  256. };
  257. struct SEQ3Widget : ModuleWidget {
  258. SEQ3Widget(SEQ3* module) {
  259. setModule(module);
  260. setPanel(createPanel(asset::plugin(pluginInstance, "res/SEQ3.svg"), asset::plugin(pluginInstance, "res/SEQ3-dark.svg")));
  261. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  262. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  263. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  264. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  265. addParam(createParamCentered<RoundLargeBlackKnob>(mm2px(Vec(11.753, 26.755)), module, SEQ3::TEMPO_PARAM));
  266. addParam(createParamCentered<RoundLargeBlackKnob>(mm2px(Vec(32.077, 26.782)), module, SEQ3::TRIG_PARAM));
  267. addParam(createParamCentered<Trimpot>(mm2px(Vec(49.372, 34.066)), module, SEQ3::TEMPO_CV_PARAM));
  268. addParam(createLightParamCentered<VCVLightButton<MediumSimpleLight<WhiteLight>>>(mm2px(Vec(88.424, 33.679)), module, SEQ3::RUN_PARAM, SEQ3::RUN_LIGHT));
  269. addParam(createParamCentered<Trimpot>(mm2px(Vec(62.39, 34.066)), module, SEQ3::STEPS_CV_PARAM));
  270. addParam(createLightParamCentered<VCVLightButton<MediumSimpleLight<WhiteLight>>>(mm2px(Vec(101.441, 33.679)), module, SEQ3::RESET_PARAM, SEQ3::RESET_LIGHT));
  271. addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(10.319, 46.563)), module, SEQ3::CV_PARAMS + 8 * 0 + 0));
  272. addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(23.336, 46.563)), module, SEQ3::CV_PARAMS + 8 * 0 + 1));
  273. addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(36.354, 46.563)), module, SEQ3::CV_PARAMS + 8 * 0 + 2));
  274. addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(49.371, 46.563)), module, SEQ3::CV_PARAMS + 8 * 0 + 3));
  275. addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(62.389, 46.563)), module, SEQ3::CV_PARAMS + 8 * 0 + 4));
  276. addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(75.406, 46.563)), module, SEQ3::CV_PARAMS + 8 * 0 + 5));
  277. addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(88.424, 46.563)), module, SEQ3::CV_PARAMS + 8 * 0 + 6));
  278. addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(101.441, 46.563)), module, SEQ3::CV_PARAMS + 8 * 0 + 7));
  279. addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(10.319, 60.607)), module, SEQ3::CV_PARAMS + 8 * 1 + 0));
  280. addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(23.336, 60.607)), module, SEQ3::CV_PARAMS + 8 * 1 + 1));
  281. addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(36.354, 60.607)), module, SEQ3::CV_PARAMS + 8 * 1 + 2));
  282. addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(49.371, 60.607)), module, SEQ3::CV_PARAMS + 8 * 1 + 3));
  283. addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(62.389, 60.607)), module, SEQ3::CV_PARAMS + 8 * 1 + 4));
  284. addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(75.406, 60.607)), module, SEQ3::CV_PARAMS + 8 * 1 + 5));
  285. addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(88.424, 60.607)), module, SEQ3::CV_PARAMS + 8 * 1 + 6));
  286. addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(101.441, 60.607)), module, SEQ3::CV_PARAMS + 8 * 1 + 7));
  287. addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(10.319, 74.605)), module, SEQ3::CV_PARAMS + 8 * 2 + 0));
  288. addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(23.336, 74.605)), module, SEQ3::CV_PARAMS + 8 * 2 + 1));
  289. addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(36.354, 74.605)), module, SEQ3::CV_PARAMS + 8 * 2 + 2));
  290. addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(49.371, 74.605)), module, SEQ3::CV_PARAMS + 8 * 2 + 3));
  291. addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(62.389, 74.605)), module, SEQ3::CV_PARAMS + 8 * 2 + 4));
  292. addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(75.406, 74.605)), module, SEQ3::CV_PARAMS + 8 * 2 + 5));
  293. addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(88.424, 74.605)), module, SEQ3::CV_PARAMS + 8 * 2 + 6));
  294. addParam(createParamCentered<RoundBlackKnob>(mm2px(Vec(101.441, 74.605)), module, SEQ3::CV_PARAMS + 8 * 2 + 7));
  295. addParam(createLightParamCentered<VCVLightBezel<WhiteLight>>(mm2px(Vec(10.319, 85.801)), module, SEQ3::GATE_PARAMS + 0, SEQ3::GATE_LIGHTS + 0));
  296. addParam(createLightParamCentered<VCVLightBezel<WhiteLight>>(mm2px(Vec(23.336, 85.801)), module, SEQ3::GATE_PARAMS + 1, SEQ3::GATE_LIGHTS + 1));
  297. addParam(createLightParamCentered<VCVLightBezel<WhiteLight>>(mm2px(Vec(36.354, 85.801)), module, SEQ3::GATE_PARAMS + 2, SEQ3::GATE_LIGHTS + 2));
  298. addParam(createLightParamCentered<VCVLightBezel<WhiteLight>>(mm2px(Vec(49.371, 85.801)), module, SEQ3::GATE_PARAMS + 3, SEQ3::GATE_LIGHTS + 3));
  299. addParam(createLightParamCentered<VCVLightBezel<WhiteLight>>(mm2px(Vec(62.389, 85.801)), module, SEQ3::GATE_PARAMS + 4, SEQ3::GATE_LIGHTS + 4));
  300. addParam(createLightParamCentered<VCVLightBezel<WhiteLight>>(mm2px(Vec(75.406, 85.801)), module, SEQ3::GATE_PARAMS + 5, SEQ3::GATE_LIGHTS + 5));
  301. addParam(createLightParamCentered<VCVLightBezel<WhiteLight>>(mm2px(Vec(88.424, 85.801)), module, SEQ3::GATE_PARAMS + 6, SEQ3::GATE_LIGHTS + 6));
  302. addParam(createLightParamCentered<VCVLightBezel<WhiteLight>>(mm2px(Vec(101.441, 85.801)), module, SEQ3::GATE_PARAMS + 7, SEQ3::GATE_LIGHTS + 7));
  303. addInput(createInputCentered<ThemedPJ301MPort>(mm2px(Vec(49.371, 17.307)), module, SEQ3::TEMPO_INPUT));
  304. addInput(createInputCentered<ThemedPJ301MPort>(mm2px(Vec(62.389, 17.307)), module, SEQ3::STEPS_INPUT));
  305. addInput(createInputCentered<ThemedPJ301MPort>(mm2px(Vec(75.406, 17.42)), module, SEQ3::CLOCK_INPUT));
  306. addInput(createInputCentered<ThemedPJ301MPort>(mm2px(Vec(88.424, 17.42)), module, SEQ3::RUN_INPUT));
  307. addInput(createInputCentered<ThemedPJ301MPort>(mm2px(Vec(101.441, 17.42)), module, SEQ3::RESET_INPUT));
  308. addOutput(createOutputCentered<ThemedPJ301MPort>(mm2px(Vec(10.319, 96.859)), module, SEQ3::STEP_OUTPUTS + 0));
  309. addOutput(createOutputCentered<ThemedPJ301MPort>(mm2px(Vec(23.336, 96.859)), module, SEQ3::STEP_OUTPUTS + 1));
  310. addOutput(createOutputCentered<ThemedPJ301MPort>(mm2px(Vec(36.354, 96.859)), module, SEQ3::STEP_OUTPUTS + 2));
  311. addOutput(createOutputCentered<ThemedPJ301MPort>(mm2px(Vec(49.371, 96.859)), module, SEQ3::STEP_OUTPUTS + 3));
  312. addOutput(createOutputCentered<ThemedPJ301MPort>(mm2px(Vec(62.389, 96.859)), module, SEQ3::STEP_OUTPUTS + 4));
  313. addOutput(createOutputCentered<ThemedPJ301MPort>(mm2px(Vec(75.406, 96.859)), module, SEQ3::STEP_OUTPUTS + 5));
  314. addOutput(createOutputCentered<ThemedPJ301MPort>(mm2px(Vec(88.424, 96.859)), module, SEQ3::STEP_OUTPUTS + 6));
  315. addOutput(createOutputCentered<ThemedPJ301MPort>(mm2px(Vec(101.441, 96.859)), module, SEQ3::STEP_OUTPUTS + 7));
  316. addOutput(createOutputCentered<ThemedPJ301MPort>(mm2px(Vec(10.319, 113.115)), module, SEQ3::CV_OUTPUTS + 0));
  317. addOutput(createOutputCentered<ThemedPJ301MPort>(mm2px(Vec(23.336, 113.115)), module, SEQ3::CV_OUTPUTS + 1));
  318. addOutput(createOutputCentered<ThemedPJ301MPort>(mm2px(Vec(36.354, 113.115)), module, SEQ3::CV_OUTPUTS + 2));
  319. addOutput(createOutputCentered<ThemedPJ301MPort>(mm2px(Vec(49.371, 113.115)), module, SEQ3::TRIG_OUTPUT));
  320. addOutput(createOutputCentered<ThemedPJ301MPort>(mm2px(Vec(62.389, 113.115)), module, SEQ3::STEPS_OUTPUT));
  321. addOutput(createOutputCentered<ThemedPJ301MPort>(mm2px(Vec(75.406, 113.115)), module, SEQ3::CLOCK_OUTPUT));
  322. addOutput(createOutputCentered<ThemedPJ301MPort>(mm2px(Vec(88.424, 113.115)), module, SEQ3::RUN_OUTPUT));
  323. addOutput(createOutputCentered<ThemedPJ301MPort>(mm2px(Vec(101.441, 113.115)), module, SEQ3::RESET_OUTPUT));
  324. addChild(createLightCentered<SmallLight<YellowLight>>(mm2px(Vec(75.406, 33.497)), module, SEQ3::CLOCK_LIGHT));
  325. addChild(createLightCentered<TinyLight<YellowRedLight<>>>(mm2px(Vec(14.064, 93.103)), module, SEQ3::STEP_LIGHTS + 2 * 0));
  326. addChild(createLightCentered<TinyLight<YellowRedLight<>>>(mm2px(Vec(27.084, 93.103)), module, SEQ3::STEP_LIGHTS + 2 * 1));
  327. addChild(createLightCentered<TinyLight<YellowRedLight<>>>(mm2px(Vec(40.103, 93.103)), module, SEQ3::STEP_LIGHTS + 2 * 2));
  328. addChild(createLightCentered<TinyLight<YellowRedLight<>>>(mm2px(Vec(53.122, 93.103)), module, SEQ3::STEP_LIGHTS + 2 * 3));
  329. addChild(createLightCentered<TinyLight<YellowRedLight<>>>(mm2px(Vec(66.142, 93.103)), module, SEQ3::STEP_LIGHTS + 2 * 4));
  330. addChild(createLightCentered<TinyLight<YellowRedLight<>>>(mm2px(Vec(79.161, 93.103)), module, SEQ3::STEP_LIGHTS + 2 * 5));
  331. addChild(createLightCentered<TinyLight<YellowRedLight<>>>(mm2px(Vec(92.181, 93.103)), module, SEQ3::STEP_LIGHTS + 2 * 6));
  332. addChild(createLightCentered<TinyLight<YellowRedLight<>>>(mm2px(Vec(105.2, 93.103)), module, SEQ3::STEP_LIGHTS + 2 * 7));
  333. }
  334. void appendContextMenu(Menu* menu) override {
  335. SEQ3* module = dynamic_cast<SEQ3*>(this->module);
  336. assert(module);
  337. menu->addChild(new MenuSeparator);
  338. menu->addChild(createBoolPtrMenuItem("Clock passthrough", "", &module->clockPassthrough));
  339. menu->addChild(new MenuSeparator);
  340. menu->addChild(createMenuItem("Rotate left", "",
  341. [=]() {module->rotateStates(-1);}
  342. ));
  343. menu->addChild(createMenuItem("Rotate right", "",
  344. [=]() {module->rotateStates(1);}
  345. ));
  346. }
  347. };
  348. Model* modelSEQ3 = createModel<SEQ3, SEQ3Widget>("SEQ3");