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.

468 lines
15KB

  1. #include "JWModules.hpp"
  2. #include "dsp/digital.hpp"
  3. namespace rack_plugin_JW_Modules {
  4. struct GridSeq : Module,QuantizeUtils {
  5. enum ParamIds {
  6. RUN_PARAM,
  7. CLOCK_PARAM,
  8. RESET_PARAM,
  9. CELL_NOTE_PARAM,
  10. CELL_GATE_PARAM = CELL_NOTE_PARAM + 16,
  11. RND_NOTES_PARAM = CELL_GATE_PARAM + 16,
  12. ROOT_NOTE_PARAM,
  13. SCALE_PARAM,
  14. RND_GATES_PARAM,
  15. RIGHT_MOVE_BTN_PARAM,
  16. LEFT_MOVE_BTN_PARAM,
  17. DOWN_MOVE_BTN_PARAM,
  18. UP_MOVE_BTN_PARAM,
  19. RND_MOVE_BTN_PARAM,
  20. REP_MOVE_BTN_PARAM,
  21. VOLT_MAX_PARAM,
  22. NUM_PARAMS
  23. };
  24. enum InputIds {
  25. CLOCK_INPUT,
  26. EXT_CLOCK_INPUT,
  27. RESET_INPUT,
  28. RIGHT_INPUT, LEFT_INPUT, DOWN_INPUT, UP_INPUT,
  29. REPEAT_INPUT,
  30. RND_DIR_INPUT,
  31. RND_NOTES_INPUT,
  32. RND_GATES_INPUT,
  33. VOLT_MAX_INPUT,
  34. NUM_INPUTS
  35. };
  36. enum OutputIds {
  37. GATES_OUTPUT,
  38. CELL_OUTPUT,
  39. NUM_OUTPUTS
  40. };
  41. enum LightIds {
  42. RUNNING_LIGHT,
  43. RESET_LIGHT,
  44. GATES_LIGHT,
  45. STEPS_LIGHT = GATES_LIGHT+ 16,
  46. NUM_LIGHTS = STEPS_LIGHT + 16
  47. };
  48. SchmittTrigger rightTrigger;
  49. SchmittTrigger leftTrigger;
  50. SchmittTrigger downTrigger;
  51. SchmittTrigger upTrigger;
  52. SchmittTrigger repeatTrigger;
  53. SchmittTrigger rndPosTrigger;
  54. SchmittTrigger runningTrigger;
  55. SchmittTrigger resetTrigger;
  56. SchmittTrigger rndNotesTrigger;
  57. SchmittTrigger rndGatesTrigger;
  58. SchmittTrigger gateTriggers[16];
  59. int index = 0;
  60. int posX = 0;
  61. int posY = 0;
  62. float phase = 0.0;
  63. float noteParamMax = 10.0;
  64. bool gateState[16] = {};
  65. bool running = true;
  66. bool ignoreGateOnPitchOut = false;
  67. enum GateMode { TRIGGER, RETRIGGER, CONTINUOUS };
  68. GateMode gateMode = TRIGGER;
  69. PulseGenerator gatePulse;
  70. GridSeq() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
  71. void step() override;
  72. json_t *toJson() override {
  73. json_t *rootJ = json_object();
  74. json_object_set_new(rootJ, "running", json_boolean(running));
  75. json_object_set_new(rootJ, "ignoreGateOnPitchOut", json_boolean(ignoreGateOnPitchOut));
  76. // gates
  77. json_t *gatesJ = json_array();
  78. for (int i = 0; i < 16; i++) {
  79. json_t *gateJ = json_integer((int) gateState[i]);
  80. json_array_append_new(gatesJ, gateJ);
  81. }
  82. json_object_set_new(rootJ, "gates", gatesJ);
  83. // gateMode
  84. json_t *gateModeJ = json_integer((int) gateMode);
  85. json_object_set_new(rootJ, "gateMode", gateModeJ);
  86. return rootJ;
  87. }
  88. void fromJson(json_t *rootJ) override {
  89. json_t *runningJ = json_object_get(rootJ, "running");
  90. if (runningJ)
  91. running = json_is_true(runningJ);
  92. json_t *ignoreGateOnPitchOutJ = json_object_get(rootJ, "ignoreGateOnPitchOut");
  93. if (ignoreGateOnPitchOutJ)
  94. ignoreGateOnPitchOut = json_is_true(ignoreGateOnPitchOutJ);
  95. // gates
  96. json_t *gatesJ = json_object_get(rootJ, "gates");
  97. if (gatesJ) {
  98. for (int i = 0; i < 16; i++) {
  99. json_t *gateJ = json_array_get(gatesJ, i);
  100. if (gateJ)
  101. gateState[i] = !!json_integer_value(gateJ);
  102. }
  103. }
  104. // gateMode
  105. json_t *gateModeJ = json_object_get(rootJ, "gateMode");
  106. if (gateModeJ)
  107. gateMode = (GateMode)json_integer_value(gateModeJ);
  108. }
  109. void reset() override {
  110. for (int i = 0; i < 16; i++) {
  111. gateState[i] = true;
  112. }
  113. }
  114. void randomize() override {
  115. randomizeGateStates();
  116. }
  117. void randomizeGateStates() {
  118. for (int i = 0; i < 16; i++) {
  119. gateState[i] = (randomUniform() > 0.50);
  120. }
  121. }
  122. float getOneRandomNote(){
  123. return randomUniform() * noteParamMax;
  124. }
  125. void randomizeNotesOnly(){
  126. for (int i = 0; i < 16; i++) {
  127. params[CELL_NOTE_PARAM + i].value = getOneRandomNote();
  128. }
  129. }
  130. float closestVoltageInScaleWrapper(float voltsIn){
  131. float totalMax = clampfjw(params[VOLT_MAX_PARAM].value+inputs[VOLT_MAX_INPUT].value, 0.0, 10.0);
  132. float voltsScaled = rescalefjw(voltsIn, 0, noteParamMax, 0, totalMax);
  133. int rootNote = params[ROOT_NOTE_PARAM].value;
  134. int scale = params[SCALE_PARAM].value;
  135. return closestVoltageInScale(voltsScaled, rootNote, scale);
  136. }
  137. void handleMoveRight(){ posX = posX == 3 ? 0 : posX + 1; }
  138. void handleMoveLeft(){ posX = posX == 0 ? 3 : posX - 1; }
  139. void handleMoveDown(){ posY = posY == 3 ? 0 : posY + 1; }
  140. void handleMoveUp(){ posY = posY == 0 ? 3 : posY - 1; }
  141. };
  142. ///////////////////////////////////////////////////////////////////////////////////////////////////
  143. // STEP
  144. ///////////////////////////////////////////////////////////////////////////////////////////////////
  145. void GridSeq::step() {
  146. const float lightLambda = 0.05;
  147. // Run
  148. if (runningTrigger.process(params[RUN_PARAM].value)) {
  149. running = !running;
  150. }
  151. lights[RUNNING_LIGHT].value = running ? 1.0 : 0.0;
  152. bool nextStep = false;
  153. if (resetTrigger.process(params[RESET_PARAM].value + inputs[RESET_INPUT].value)) {
  154. phase = 0.0;
  155. posX = 0;
  156. posY = 0;
  157. index = 0;
  158. nextStep = true;
  159. lights[RESET_LIGHT].value = 1.0;
  160. }
  161. if(running){
  162. if (rndNotesTrigger.process(inputs[RND_NOTES_INPUT].value)) {
  163. randomizeNotesOnly();
  164. }
  165. if (rndGatesTrigger.process(inputs[RND_GATES_INPUT].value)) {
  166. randomizeGateStates();
  167. }
  168. if (repeatTrigger.process(inputs[REPEAT_INPUT].value + params[REP_MOVE_BTN_PARAM].value)) {
  169. nextStep = true;
  170. }
  171. if (rndPosTrigger.process(inputs[RND_DIR_INPUT].value + params[RND_MOVE_BTN_PARAM].value)) {
  172. nextStep = true;
  173. switch(int(4 * randomUniform())){
  174. case 0:handleMoveRight();break;
  175. case 1:handleMoveLeft();break;
  176. case 2:handleMoveDown();break;
  177. case 3:handleMoveUp();break;
  178. }
  179. }
  180. if (rightTrigger.process(inputs[RIGHT_INPUT].value + params[RIGHT_MOVE_BTN_PARAM].value)) {
  181. nextStep = true;
  182. handleMoveRight();
  183. }
  184. if (leftTrigger.process(inputs[LEFT_INPUT].value + params[LEFT_MOVE_BTN_PARAM].value)) {
  185. nextStep = true;
  186. handleMoveLeft();
  187. }
  188. if (downTrigger.process(inputs[DOWN_INPUT].value + params[DOWN_MOVE_BTN_PARAM].value)) {
  189. nextStep = true;
  190. handleMoveDown();
  191. }
  192. if (upTrigger.process(inputs[UP_INPUT].value + params[UP_MOVE_BTN_PARAM].value)) {
  193. nextStep = true;
  194. handleMoveUp();
  195. }
  196. }
  197. if (nextStep) {
  198. index = posX + (posY * 4);
  199. lights[STEPS_LIGHT + index].value = 1.0;
  200. gatePulse.trigger(1e-3);
  201. }
  202. lights[RESET_LIGHT].value -= lights[RESET_LIGHT].value / lightLambda / engineGetSampleRate();
  203. bool pulse = gatePulse.process(1.0 / engineGetSampleRate());
  204. // Gate buttons
  205. for (int i = 0; i < 16; i++) {
  206. if (gateTriggers[i].process(params[CELL_GATE_PARAM + i].value)) {
  207. gateState[i] = !gateState[i];
  208. }
  209. bool gateOn = (running && i == index && gateState[i]);
  210. if (gateMode == TRIGGER)
  211. gateOn = gateOn && pulse;
  212. else if (gateMode == RETRIGGER)
  213. gateOn = gateOn && !pulse;
  214. if(lights[STEPS_LIGHT + i].value > 0){ lights[STEPS_LIGHT + i].value -= lights[STEPS_LIGHT + i].value / lightLambda / engineGetSampleRate(); }
  215. lights[GATES_LIGHT + i].value = gateState[i] ? 1.0 - lights[STEPS_LIGHT + i].value : lights[STEPS_LIGHT + i].value;
  216. }
  217. // Cells
  218. bool gatesOn = (running && gateState[index]);
  219. if (gateMode == TRIGGER)
  220. gatesOn = gatesOn && pulse;
  221. else if (gateMode == RETRIGGER)
  222. gatesOn = gatesOn && !pulse;
  223. // Outputs
  224. if(gatesOn || ignoreGateOnPitchOut) {
  225. outputs[CELL_OUTPUT].value = closestVoltageInScaleWrapper(params[CELL_NOTE_PARAM + index].value);
  226. }
  227. outputs[GATES_OUTPUT].value = gatesOn ? 10.0 : 0.0;
  228. }
  229. struct GridSeqWidget : ModuleWidget {
  230. std::vector<ParamWidget*> seqKnobs;
  231. std::vector<ParamWidget*> gateButtons;
  232. GridSeqWidget(GridSeq *module);
  233. ~GridSeqWidget(){
  234. seqKnobs.clear();
  235. gateButtons.clear();
  236. }
  237. Menu *createContextMenu() override;
  238. };
  239. struct RandomizeNotesOnlyButton : SmallButton {
  240. void onMouseDown(EventMouseDown &e) override {
  241. SmallButton::onMouseDown(e);
  242. GridSeqWidget *gsw = this->getAncestorOfType<GridSeqWidget>();
  243. GridSeq *gs = dynamic_cast<GridSeq*>(gsw->module);
  244. for (int i = 0; i < 16; i++) {
  245. if(e.button == 0){
  246. gsw->seqKnobs[i]->setValue(gs->getOneRandomNote());
  247. } else if(e.button == 1){
  248. //right click this to update the knobs (if randomized by cv in)
  249. gsw->seqKnobs[i]->setValue(module->params[GridSeq::CELL_NOTE_PARAM + i].value);
  250. }
  251. }
  252. }
  253. };
  254. struct RandomizeGatesOnlyButton : SmallButton {
  255. void onMouseDown(EventMouseDown &e) override {
  256. SmallButton::onMouseDown(e);
  257. GridSeqWidget *gsw = this->getAncestorOfType<GridSeqWidget>();
  258. for (int i = 0; i < 16; i++) {
  259. gsw->gateButtons[i]->setValue(randomUniform() > 0.5);
  260. }
  261. }
  262. };
  263. GridSeqWidget::GridSeqWidget(GridSeq *module) : ModuleWidget(module) {
  264. box.size = Vec(RACK_GRID_WIDTH*20, RACK_GRID_HEIGHT);
  265. {
  266. SVGPanel *panel = new SVGPanel();
  267. panel->box.size = box.size;
  268. panel->setBackground(SVG::load(assetPlugin(plugin, "res/GridSeq.svg")));
  269. addChild(panel);
  270. }
  271. addChild(Widget::create<Screw_J>(Vec(16, 1)));
  272. addChild(Widget::create<Screw_J>(Vec(16, 365)));
  273. addChild(Widget::create<Screw_W>(Vec(box.size.x-29, 1)));
  274. addChild(Widget::create<Screw_W>(Vec(box.size.x-29, 365)));
  275. ///// RUN /////
  276. addParam(ParamWidget::create<TinyButton>(Vec(27, 90), module, GridSeq::RUN_PARAM, 0.0, 1.0, 0.0));
  277. addChild(ModuleLightWidget::create<SmallLight<MyBlueValueLight>>(Vec(27+3.75, 90+3.75), module, GridSeq::RUNNING_LIGHT));
  278. ///// RESET /////
  279. addParam(ParamWidget::create<TinyButton>(Vec(27, 138), module, GridSeq::RESET_PARAM, 0.0, 1.0, 0.0));
  280. addChild(ModuleLightWidget::create<SmallLight<MyBlueValueLight>>(Vec(27+3.75, 138+3.75), module, GridSeq::RESET_LIGHT));
  281. addInput(Port::create<PJ301MPort>(Vec(22, 160), Port::INPUT, module, GridSeq::RESET_INPUT));
  282. ///// DIR CONTROLS /////
  283. addParam(ParamWidget::create<RightMoveButton>(Vec(70, 30), module, GridSeq::RIGHT_MOVE_BTN_PARAM, 0.0, 1.0, 0.0));
  284. addParam(ParamWidget::create<LeftMoveButton>(Vec(103, 30), module, GridSeq::LEFT_MOVE_BTN_PARAM, 0.0, 1.0, 0.0));
  285. addParam(ParamWidget::create<DownMoveButton>(Vec(137, 30), module, GridSeq::DOWN_MOVE_BTN_PARAM, 0.0, 1.0, 0.0));
  286. addParam(ParamWidget::create<UpMoveButton>(Vec(172, 30), module, GridSeq::UP_MOVE_BTN_PARAM, 0.0, 1.0, 0.0));
  287. addParam(ParamWidget::create<RndMoveButton>(Vec(215, 30), module, GridSeq::RND_MOVE_BTN_PARAM, 0.0, 1.0, 0.0));
  288. addParam(ParamWidget::create<RepMoveButton>(Vec(255, 30), module, GridSeq::REP_MOVE_BTN_PARAM, 0.0, 1.0, 0.0));
  289. addInput(Port::create<PJ301MPort>(Vec(70, 52), Port::INPUT, module, GridSeq::RIGHT_INPUT));
  290. addInput(Port::create<PJ301MPort>(Vec(103, 52), Port::INPUT, module, GridSeq::LEFT_INPUT));
  291. addInput(Port::create<PJ301MPort>(Vec(137, 52), Port::INPUT, module, GridSeq::DOWN_INPUT));
  292. addInput(Port::create<PJ301MPort>(Vec(172, 52), Port::INPUT, module, GridSeq::UP_INPUT));
  293. addInput(Port::create<PJ301MPort>(Vec(212, 52), Port::INPUT, module, GridSeq::RND_DIR_INPUT));
  294. addInput(Port::create<PJ301MPort>(Vec(253, 52), Port::INPUT, module, GridSeq::REPEAT_INPUT));
  295. ///// NOTE AND SCALE CONTROLS /////
  296. NoteKnob *noteKnob = dynamic_cast<NoteKnob*>(ParamWidget::create<NoteKnob>(Vec(70, 315), module, GridSeq::ROOT_NOTE_PARAM, 0.0, QuantizeUtils::NUM_NOTES-1, QuantizeUtils::NOTE_C));
  297. CenteredLabel* const noteLabel = new CenteredLabel;
  298. noteLabel->box.pos = Vec(41, 178);
  299. noteLabel->text = "note here";
  300. noteKnob->connectLabel(noteLabel);
  301. addChild(noteLabel);
  302. addParam(noteKnob);
  303. ScaleKnob *scaleKnob = dynamic_cast<ScaleKnob*>(ParamWidget::create<ScaleKnob>(Vec(108, 315), module, GridSeq::SCALE_PARAM, 0.0, QuantizeUtils::NUM_SCALES-1, QuantizeUtils::MINOR));
  304. CenteredLabel* const scaleLabel = new CenteredLabel;
  305. scaleLabel->box.pos = Vec(61, 178);
  306. scaleLabel->text = "scale here";
  307. scaleKnob->connectLabel(scaleLabel);
  308. addChild(scaleLabel);
  309. addParam(scaleKnob);
  310. addParam(ParamWidget::create<RandomizeGatesOnlyButton>(Vec(196, 315), module, GridSeq::RND_GATES_PARAM, 0.0, 1.0, 0.0));
  311. addInput(Port::create<TinyPJ301MPort>(Vec(201, 345), Port::INPUT, module, GridSeq::RND_GATES_INPUT));
  312. addParam(ParamWidget::create<RandomizeNotesOnlyButton>(Vec(250, 315), module, GridSeq::RND_NOTES_PARAM, 0.0, 1.0, 0.0));
  313. addInput(Port::create<TinyPJ301MPort>(Vec(255, 345), Port::INPUT, module, GridSeq::RND_NOTES_INPUT));
  314. addParam(ParamWidget::create<JwSmallSnapKnob>(Vec(146, 315), module, GridSeq::VOLT_MAX_PARAM, 0.0, 10.0, 5.0));
  315. addInput(Port::create<TinyPJ301MPort>(Vec(152, 345), Port::INPUT, module, GridSeq::VOLT_MAX_INPUT));
  316. //// MAIN SEQUENCER KNOBS ////
  317. int boxSize = 55;
  318. for (int y = 0; y < 4; y++) {
  319. for (int x = 0; x < 4; x++) {
  320. int knobX = x * boxSize + 75;
  321. int knobY = y * boxSize + 105;
  322. int idx = (x+(y*4));
  323. module->gateState[idx] = true; //start with all gates on
  324. //maybe someday put note labels in each cell
  325. ParamWidget *cellNoteKnob = ParamWidget::create<SmallWhiteKnob>(Vec(knobX, knobY), module, GridSeq::CELL_NOTE_PARAM + idx, 0.0, module->noteParamMax, 3.0);
  326. addParam(cellNoteKnob);
  327. seqKnobs.push_back(cellNoteKnob);
  328. ParamWidget *cellGateButton = ParamWidget::create<LEDButton>(Vec(knobX+22, knobY-15), module, GridSeq::CELL_GATE_PARAM + idx, 0.0, 1.0, 0.0);
  329. addParam(cellGateButton);
  330. gateButtons.push_back(cellGateButton);
  331. addChild(ModuleLightWidget::create<SmallLight<MyBlueValueLight>>(Vec(knobX+27.5, knobY-9.5), module, GridSeq::GATES_LIGHT + idx));
  332. }
  333. }
  334. ///// OUTPUTS /////
  335. addOutput(Port::create<PJ301MPort>(Vec(22, 233), Port::OUTPUT, module, GridSeq::GATES_OUTPUT));
  336. addOutput(Port::create<PJ301MPort>(Vec(22, 295), Port::OUTPUT, module, GridSeq::CELL_OUTPUT));
  337. }
  338. struct GridSeqPitchMenuItem : MenuItem {
  339. GridSeq *gridSeq;
  340. void onAction(EventAction &e) override {
  341. gridSeq->ignoreGateOnPitchOut = !gridSeq->ignoreGateOnPitchOut;
  342. }
  343. void step() override {
  344. rightText = (gridSeq->ignoreGateOnPitchOut) ? "✔" : "";
  345. }
  346. };
  347. struct GridSeqGateModeItem : MenuItem {
  348. GridSeq *gridSeq;
  349. GridSeq::GateMode gateMode;
  350. void onAction(EventAction &e) override {
  351. gridSeq->gateMode = gateMode;
  352. }
  353. void step() override {
  354. rightText = (gridSeq->gateMode == gateMode) ? "✔" : "";
  355. }
  356. };
  357. Menu *GridSeqWidget::createContextMenu() {
  358. Menu *menu = ModuleWidget::createContextMenu();
  359. MenuLabel *spacerLabel = new MenuLabel();
  360. menu->addChild(spacerLabel);
  361. GridSeq *gridSeq = dynamic_cast<GridSeq*>(module);
  362. assert(gridSeq);
  363. MenuLabel *modeLabel = new MenuLabel();
  364. modeLabel->text = "Gate Mode";
  365. menu->addChild(modeLabel);
  366. GridSeqGateModeItem *triggerItem = new GridSeqGateModeItem();
  367. triggerItem->text = "Trigger";
  368. triggerItem->gridSeq = gridSeq;
  369. triggerItem->gateMode = GridSeq::TRIGGER;
  370. menu->addChild(triggerItem);
  371. GridSeqGateModeItem *retriggerItem = new GridSeqGateModeItem();
  372. retriggerItem->text = "Retrigger";
  373. retriggerItem->gridSeq = gridSeq;
  374. retriggerItem->gateMode = GridSeq::RETRIGGER;
  375. menu->addChild(retriggerItem);
  376. GridSeqGateModeItem *continuousItem = new GridSeqGateModeItem();
  377. continuousItem->text = "Continuous";
  378. continuousItem->gridSeq = gridSeq;
  379. continuousItem->gateMode = GridSeq::CONTINUOUS;
  380. menu->addChild(continuousItem);
  381. MenuLabel *spacerLabel2 = new MenuLabel();
  382. menu->addChild(spacerLabel2);
  383. GridSeqPitchMenuItem *pitchMenuItem = new GridSeqPitchMenuItem();
  384. pitchMenuItem->text = "Ignore Gate for V/OCT Out";
  385. pitchMenuItem->gridSeq = gridSeq;
  386. menu->addChild(pitchMenuItem);
  387. return menu;
  388. }
  389. } // namespace rack_plugin_JW_Modules
  390. using namespace rack_plugin_JW_Modules;
  391. RACK_PLUGIN_MODEL_INIT(JW_Modules, GridSeq) {
  392. Model *modelGridSeq = Model::create<GridSeq, GridSeqWidget>("JW-Modules", "GridSeq", "GridSeq", SEQUENCER_TAG);
  393. return modelGridSeq;
  394. }