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.

306 lines
8.8KB

  1. #include "Fundamental.hpp"
  2. #include "dsp/digital.hpp"
  3. struct SEQ3 : Module {
  4. enum ParamIds {
  5. CLOCK_PARAM,
  6. RUN_PARAM,
  7. RESET_PARAM,
  8. STEPS_PARAM,
  9. ROW1_PARAM,
  10. ROW2_PARAM = ROW1_PARAM + 8,
  11. ROW3_PARAM = ROW2_PARAM + 8,
  12. GATE_PARAM = ROW3_PARAM + 8,
  13. NUM_PARAMS = GATE_PARAM + 8
  14. };
  15. enum InputIds {
  16. CLOCK_INPUT,
  17. EXT_CLOCK_INPUT,
  18. RESET_INPUT,
  19. STEPS_INPUT,
  20. NUM_INPUTS
  21. };
  22. enum OutputIds {
  23. GATES_OUTPUT,
  24. ROW1_OUTPUT,
  25. ROW2_OUTPUT,
  26. ROW3_OUTPUT,
  27. GATE_OUTPUT,
  28. NUM_OUTPUTS = GATE_OUTPUT + 8
  29. };
  30. enum LightIds {
  31. RUNNING_LIGHT,
  32. RESET_LIGHT,
  33. GATES_LIGHT,
  34. ROW_LIGHTS,
  35. GATE_LIGHTS = ROW_LIGHTS + 3,
  36. NUM_LIGHTS = GATE_LIGHTS + 8
  37. };
  38. bool running = true;
  39. SchmittTrigger clockTrigger; // for external clock
  40. // For buttons
  41. SchmittTrigger runningTrigger;
  42. SchmittTrigger resetTrigger;
  43. SchmittTrigger gateTriggers[8];
  44. float phase = 0.0f;
  45. int index = 0;
  46. bool gateState[8] = {};
  47. float resetLight = 0.0f;
  48. float stepLights[8] = {};
  49. enum GateMode {
  50. TRIGGER,
  51. RETRIGGER,
  52. CONTINUOUS,
  53. };
  54. GateMode gateMode = TRIGGER;
  55. PulseGenerator gatePulse;
  56. SEQ3() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  57. onReset();
  58. }
  59. void step() override;
  60. json_t *toJson() override {
  61. json_t *rootJ = json_object();
  62. // running
  63. json_object_set_new(rootJ, "running", json_boolean(running));
  64. // gates
  65. json_t *gatesJ = json_array();
  66. for (int i = 0; i < 8; i++) {
  67. json_t *gateJ = json_integer((int) gateState[i]);
  68. json_array_append_new(gatesJ, gateJ);
  69. }
  70. json_object_set_new(rootJ, "gates", gatesJ);
  71. // gateMode
  72. json_t *gateModeJ = json_integer((int) gateMode);
  73. json_object_set_new(rootJ, "gateMode", gateModeJ);
  74. return rootJ;
  75. }
  76. void fromJson(json_t *rootJ) override {
  77. // running
  78. json_t *runningJ = json_object_get(rootJ, "running");
  79. if (runningJ)
  80. running = json_is_true(runningJ);
  81. // gates
  82. json_t *gatesJ = json_object_get(rootJ, "gates");
  83. if (gatesJ) {
  84. for (int i = 0; i < 8; i++) {
  85. json_t *gateJ = json_array_get(gatesJ, i);
  86. if (gateJ)
  87. gateState[i] = !!json_integer_value(gateJ);
  88. }
  89. }
  90. // gateMode
  91. json_t *gateModeJ = json_object_get(rootJ, "gateMode");
  92. if (gateModeJ)
  93. gateMode = (GateMode)json_integer_value(gateModeJ);
  94. }
  95. void onReset() override {
  96. for (int i = 0; i < 8; i++) {
  97. gateState[i] = true;
  98. }
  99. }
  100. void onRandomize() override {
  101. for (int i = 0; i < 8; i++) {
  102. gateState[i] = (randomf() > 0.5f);
  103. }
  104. }
  105. };
  106. void SEQ3::step() {
  107. const float lightLambda = 0.075f;
  108. // Run
  109. if (runningTrigger.process(params[RUN_PARAM].value)) {
  110. running = !running;
  111. }
  112. lights[RUNNING_LIGHT].value = running ? 1.0f : 0.0f;
  113. bool nextStep = false;
  114. if (running) {
  115. if (inputs[EXT_CLOCK_INPUT].active) {
  116. // External clock
  117. if (clockTrigger.process(inputs[EXT_CLOCK_INPUT].value)) {
  118. phase = 0.0f;
  119. nextStep = true;
  120. }
  121. }
  122. else {
  123. // Internal clock
  124. float clockTime = powf(2.0f, params[CLOCK_PARAM].value + inputs[CLOCK_INPUT].value);
  125. phase += clockTime * engineGetSampleTime();
  126. if (phase >= 1.0f) {
  127. phase -= 1.0f;
  128. nextStep = true;
  129. }
  130. }
  131. }
  132. // Reset
  133. if (resetTrigger.process(params[RESET_PARAM].value + inputs[RESET_INPUT].value)) {
  134. phase = 0.0f;
  135. index = 8;
  136. nextStep = true;
  137. resetLight = 1.0f;
  138. }
  139. if (nextStep) {
  140. // Advance step
  141. int numSteps = clamp(roundf(params[STEPS_PARAM].value + inputs[STEPS_INPUT].value), 1.0f, 8.0f);
  142. index += 1;
  143. if (index >= numSteps) {
  144. index = 0;
  145. }
  146. stepLights[index] = 1.0f;
  147. gatePulse.trigger(1e-3);
  148. }
  149. resetLight -= resetLight / lightLambda * engineGetSampleTime();
  150. bool pulse = gatePulse.process(engineGetSampleTime());
  151. // Gate buttons
  152. for (int i = 0; i < 8; i++) {
  153. if (gateTriggers[i].process(params[GATE_PARAM + i].value)) {
  154. gateState[i] = !gateState[i];
  155. }
  156. bool gateOn = (running && i == index && gateState[i]);
  157. if (gateMode == TRIGGER)
  158. gateOn = gateOn && pulse;
  159. else if (gateMode == RETRIGGER)
  160. gateOn = gateOn && !pulse;
  161. outputs[GATE_OUTPUT + i].value = gateOn ? 10.0f : 0.0f;
  162. stepLights[i] -= stepLights[i] / lightLambda * engineGetSampleTime();
  163. lights[GATE_LIGHTS + i].value = gateState[i] ? 1.0f - stepLights[i] : stepLights[i];
  164. }
  165. // Rows
  166. float row1 = params[ROW1_PARAM + index].value;
  167. float row2 = params[ROW2_PARAM + index].value;
  168. float row3 = params[ROW3_PARAM + index].value;
  169. bool gatesOn = (running && gateState[index]);
  170. if (gateMode == TRIGGER)
  171. gatesOn = gatesOn && pulse;
  172. else if (gateMode == RETRIGGER)
  173. gatesOn = gatesOn && !pulse;
  174. // Outputs
  175. outputs[ROW1_OUTPUT].value = row1;
  176. outputs[ROW2_OUTPUT].value = row2;
  177. outputs[ROW3_OUTPUT].value = row3;
  178. outputs[GATES_OUTPUT].value = gatesOn ? 10.0f : 0.0f;
  179. lights[RESET_LIGHT].value = resetLight;
  180. lights[GATES_LIGHT].value = gatesOn ? 1.0f : 0.0f;
  181. lights[ROW_LIGHTS].value = row1 / 10.0f;
  182. lights[ROW_LIGHTS + 1].value = row2 / 10.0f;
  183. lights[ROW_LIGHTS + 2].value = row3 / 10.0f;
  184. }
  185. SEQ3Widget::SEQ3Widget() {
  186. SEQ3 *module = new SEQ3();
  187. setModule(module);
  188. box.size = Vec(15*22, 380);
  189. {
  190. SVGPanel *panel = new SVGPanel();
  191. panel->box.size = box.size;
  192. panel->setBackground(SVG::load(assetPlugin(plugin, "res/SEQ3.svg")));
  193. addChild(panel);
  194. }
  195. addChild(createScrew<ScrewSilver>(Vec(15, 0)));
  196. addChild(createScrew<ScrewSilver>(Vec(box.size.x-30, 0)));
  197. addChild(createScrew<ScrewSilver>(Vec(15, 365)));
  198. addChild(createScrew<ScrewSilver>(Vec(box.size.x-30, 365)));
  199. addParam(createParam<RoundSmallBlackKnob>(Vec(18, 56), module, SEQ3::CLOCK_PARAM, -2.0f, 6.0f, 2.0f));
  200. addParam(createParam<LEDButton>(Vec(60, 61-1), module, SEQ3::RUN_PARAM, 0.0f, 1.0f, 0.0f));
  201. addChild(createLight<MediumLight<GreenLight>>(Vec(64.4f, 64.4f), module, SEQ3::RUNNING_LIGHT));
  202. addParam(createParam<LEDButton>(Vec(99, 61-1), module, SEQ3::RESET_PARAM, 0.0f, 1.0f, 0.0f));
  203. addChild(createLight<MediumLight<GreenLight>>(Vec(103.4f, 64.4f), module, SEQ3::RESET_LIGHT));
  204. addParam(createParam<RoundSmallBlackSnapKnob>(Vec(132, 56), module, SEQ3::STEPS_PARAM, 1.0f, 8.0f, 8.0f));
  205. addChild(createLight<MediumLight<GreenLight>>(Vec(179.4f, 64.4f), module, SEQ3::GATES_LIGHT));
  206. addChild(createLight<MediumLight<GreenLight>>(Vec(218.4f, 64.4f), module, SEQ3::ROW_LIGHTS));
  207. addChild(createLight<MediumLight<GreenLight>>(Vec(256.4f, 64.4f), module, SEQ3::ROW_LIGHTS + 1));
  208. addChild(createLight<MediumLight<GreenLight>>(Vec(295.4f, 64.4f), module, SEQ3::ROW_LIGHTS + 2));
  209. static const float portX[8] = {20, 58, 96, 135, 173, 212, 250, 289};
  210. addInput(createInput<PJ301MPort>(Vec(portX[0]-1, 98), module, SEQ3::CLOCK_INPUT));
  211. addInput(createInput<PJ301MPort>(Vec(portX[1]-1, 98), module, SEQ3::EXT_CLOCK_INPUT));
  212. addInput(createInput<PJ301MPort>(Vec(portX[2]-1, 98), module, SEQ3::RESET_INPUT));
  213. addInput(createInput<PJ301MPort>(Vec(portX[3]-1, 98), module, SEQ3::STEPS_INPUT));
  214. addOutput(createOutput<PJ301MPort>(Vec(portX[4]-1, 98), module, SEQ3::GATES_OUTPUT));
  215. addOutput(createOutput<PJ301MPort>(Vec(portX[5]-1, 98), module, SEQ3::ROW1_OUTPUT));
  216. addOutput(createOutput<PJ301MPort>(Vec(portX[6]-1, 98), module, SEQ3::ROW2_OUTPUT));
  217. addOutput(createOutput<PJ301MPort>(Vec(portX[7]-1, 98), module, SEQ3::ROW3_OUTPUT));
  218. for (int i = 0; i < 8; i++) {
  219. addParam(createParam<RoundSmallBlackKnob>(Vec(portX[i]-2, 157), module, SEQ3::ROW1_PARAM + i, 0.0f, 10.0f, 0.0f));
  220. addParam(createParam<RoundSmallBlackKnob>(Vec(portX[i]-2, 198), module, SEQ3::ROW2_PARAM + i, 0.0f, 10.0f, 0.0f));
  221. addParam(createParam<RoundSmallBlackKnob>(Vec(portX[i]-2, 240), module, SEQ3::ROW3_PARAM + i, 0.0f, 10.0f, 0.0f));
  222. addParam(createParam<LEDButton>(Vec(portX[i]+2, 278-1), module, SEQ3::GATE_PARAM + i, 0.0f, 1.0f, 0.0f));
  223. addChild(createLight<MediumLight<GreenLight>>(Vec(portX[i]+6.4f, 281.4f), module, SEQ3::GATE_LIGHTS + i));
  224. addOutput(createOutput<PJ301MPort>(Vec(portX[i]-1, 307), module, SEQ3::GATE_OUTPUT + i));
  225. }
  226. }
  227. struct SEQ3GateModeItem : MenuItem {
  228. SEQ3 *seq3;
  229. SEQ3::GateMode gateMode;
  230. void onAction(EventAction &e) override {
  231. seq3->gateMode = gateMode;
  232. }
  233. void step() override {
  234. rightText = CHECKMARK(seq3->gateMode == gateMode);
  235. }
  236. };
  237. Menu *SEQ3Widget::createContextMenu() {
  238. Menu *menu = ModuleWidget::createContextMenu();
  239. MenuLabel *spacerLabel = new MenuLabel();
  240. menu->addChild(spacerLabel);
  241. SEQ3 *seq3 = dynamic_cast<SEQ3*>(module);
  242. assert(seq3);
  243. MenuLabel *modeLabel = new MenuLabel();
  244. modeLabel->text = "Gate Mode";
  245. menu->addChild(modeLabel);
  246. SEQ3GateModeItem *triggerItem = new SEQ3GateModeItem();
  247. triggerItem->text = "Trigger";
  248. triggerItem->seq3 = seq3;
  249. triggerItem->gateMode = SEQ3::TRIGGER;
  250. menu->addChild(triggerItem);
  251. SEQ3GateModeItem *retriggerItem = new SEQ3GateModeItem();
  252. retriggerItem->text = "Retrigger";
  253. retriggerItem->seq3 = seq3;
  254. retriggerItem->gateMode = SEQ3::RETRIGGER;
  255. menu->addChild(retriggerItem);
  256. SEQ3GateModeItem *continuousItem = new SEQ3GateModeItem();
  257. continuousItem->text = "Continuous";
  258. continuousItem->seq3 = seq3;
  259. continuousItem->gateMode = SEQ3::CONTINUOUS;
  260. menu->addChild(continuousItem);
  261. return menu;
  262. }