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.

332 lines
9.0KB

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