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.

2107 lines
78KB

  1. //***********************************************************************************************
  2. //Multi-phrase 16 step sequencer module for VCV Rack by Marc Boulé
  3. //
  4. //Based on code from the Fundamental and AudibleInstruments plugins by Andrew Belt
  5. //and graphics from the Component Library by Wes Milholen
  6. //See ./LICENSE.txt for all licenses
  7. //See ./res/fonts/ for font licenses
  8. //
  9. //Module inspired by the SA-100 Stepper Acid sequencer by Transistor Sounds Labs
  10. //
  11. //Acknowledgements: please see README.md
  12. //***********************************************************************************************
  13. #include "PhraseSeqUtil.hpp"
  14. namespace rack_plugin_ImpromptuModular {
  15. struct PhraseSeq16 : Module {
  16. enum ParamIds {
  17. KEYNOTE_PARAM,// 0.6.12 replaces unused
  18. KEYGATE_PARAM,// 0.6.12 replaces unused
  19. KEYGATE2_PARAM,// no longer unused
  20. EDIT_PARAM,
  21. SEQUENCE_PARAM,
  22. RUN_PARAM,
  23. COPY_PARAM,
  24. PASTE_PARAM,
  25. RESET_PARAM,
  26. ENUMS(OCTAVE_PARAM, 7),
  27. GATE1_PARAM,
  28. GATE2_PARAM,
  29. SLIDE_BTN_PARAM,
  30. SLIDE_KNOB_PARAM,
  31. ATTACH_PARAM,
  32. ROTATEL_PARAM,// no longer used
  33. ROTATER_PARAM,// no longer used
  34. PASTESYNC_PARAM,// no longer used
  35. AUTOSTEP_PARAM,
  36. ENUMS(KEY_PARAMS, 12),
  37. TRANSPOSEU_PARAM,// no longer used
  38. TRANSPOSED_PARAM,// no longer used
  39. // -- 0.6.2 ^^
  40. RUNMODE_PARAM,
  41. TRAN_ROT_PARAM,
  42. ROTATE_PARAM,//no longer used
  43. GATE1_KNOB_PARAM,
  44. GATE2_KNOB_PARAM,// no longer used
  45. GATE1_PROB_PARAM,
  46. TIE_PARAM,// Legato
  47. // -- 0.6.3 ^^
  48. CPMODE_PARAM,
  49. // -- 0.6.4 ^^
  50. // -- 0.6.10 ^^
  51. ENUMS(STEP_PHRASE_PARAMS, 16),
  52. NUM_PARAMS
  53. };
  54. enum InputIds {
  55. WRITE_INPUT,
  56. CV_INPUT,
  57. RESET_INPUT,
  58. CLOCK_INPUT,
  59. // -- 0.6.2 ^^
  60. LEFTCV_INPUT,
  61. RIGHTCV_INPUT,
  62. RUNCV_INPUT,
  63. SEQCV_INPUT,
  64. MODECV_INPUT,
  65. // -- 0.6.3 ^^
  66. // -- 0.6.4 ^^
  67. GATE1CV_INPUT,
  68. GATE2CV_INPUT,
  69. TIEDCV_INPUT,
  70. SLIDECV_INPUT,
  71. NUM_INPUTS
  72. };
  73. enum OutputIds {
  74. CV_OUTPUT,
  75. GATE1_OUTPUT,
  76. GATE2_OUTPUT,
  77. // -- 0.6.2 ^^
  78. // -- 0.6.3 ^^
  79. NUM_OUTPUTS
  80. };
  81. enum LightIds {
  82. ENUMS(STEP_PHRASE_LIGHTS, 16 * 3),// room for GreenRedWhite
  83. ENUMS(OCTAVE_LIGHTS, 7),// octaves 1 to 7
  84. ENUMS(KEY_LIGHTS, 12 * 2),// room for GreenRed
  85. RUN_LIGHT,
  86. RESET_LIGHT,
  87. ENUMS(GATE1_LIGHT, 2),// room for GreenRed
  88. ENUMS(GATE2_LIGHT, 2),// room for GreenRed
  89. SLIDE_LIGHT,
  90. ATTACH_LIGHT,
  91. PENDING_LIGHT,// no longer used
  92. ENUMS(GATE1_PROB_LIGHT, 2),// room for GreenRed
  93. TIE_LIGHT,
  94. KEYNOTE_LIGHT,
  95. ENUMS(KEYGATE_LIGHT, 2),// room for GreenRed
  96. NUM_LIGHTS
  97. };
  98. // Constants
  99. enum DisplayStateIds {DISP_NORMAL, DISP_MODE, DISP_LENGTH, DISP_TRANSPOSE, DISP_ROTATE};
  100. // Need to save
  101. int panelTheme = 0;
  102. int expansion = 0;
  103. bool autoseq;
  104. bool autostepLen;
  105. bool holdTiedNotes;
  106. int seqCVmethod;// 0 is 0-10V, 1 is C4-D5#, 2 is TrigIncr
  107. int pulsesPerStep;// 1 means normal gate mode, alt choices are 4, 6, 12, 24 PPS (Pulses per step)
  108. bool running;
  109. SeqAttributes sequences[16];
  110. int runModeSong;
  111. int sequence;
  112. int phrase[16];// This is the song (series of phases; a phrase is a patten number)
  113. int phrases;//1 to 16
  114. float cv[16][16];// [-3.0 : 3.917]. First index is patten number, 2nd index is step
  115. StepAttributes attributes[16][16];// First index is patten number, 2nd index is step (see enum AttributeBitMasks for details)
  116. bool resetOnRun;
  117. bool attached;
  118. // No need to save
  119. int stepIndexEdit;
  120. int stepIndexRun;
  121. int phraseIndexEdit;
  122. int phraseIndexRun;
  123. long infoCopyPaste;// 0 when no info, positive downward step counter timer when copy, negative upward when paste
  124. unsigned long editingGate;// 0 when no edit gate, downward step counter timer when edit gate
  125. float editingGateCV;// no need to initialize, this is a companion to editingGate (output this only when editingGate > 0)
  126. int editingGateKeyLight;// no need to initialize, this is a companion to editingGate (use this only when editingGate > 0)
  127. unsigned long editingType;// similar to editingGate, but just for showing remanent gate type (nothing played); uses editingGateKeyLight
  128. unsigned long stepIndexRunHistory;
  129. unsigned long phraseIndexRunHistory;
  130. int displayState;
  131. unsigned long slideStepsRemain;// 0 when no slide under way, downward step counter when sliding
  132. float slideCVdelta;// no need to initialize, this is a companion to slideStepsRemain
  133. float cvCPbuffer[16];// copy paste buffer for CVs
  134. StepAttributes attribCPbuffer[16];
  135. SeqAttributes seqAttribCPbuffer;
  136. bool seqCopied;
  137. int phraseCPbuffer[16];
  138. int countCP;// number of steps to paste (in case CPMODE_PARAM changes between copy and paste)
  139. int startCP;
  140. long clockIgnoreOnReset;
  141. unsigned long clockPeriod;// counts number of step() calls upward from last clock (reset after clock processed)
  142. long tiedWarning;// 0 when no warning, positive downward step counter timer when warning
  143. long attachedWarning;// 0 when no warning, positive downward step counter timer when warning
  144. int gate1Code;
  145. int gate2Code;
  146. long revertDisplay;
  147. long editingGateLength;// 0 when no info, positive when gate1, negative when gate2
  148. long lastGateEdit;
  149. long editingPpqn;// 0 when no info, positive downward step counter timer when editing ppqn
  150. int ppqnCount;
  151. unsigned int lightRefreshCounter = 0;
  152. float resetLight = 0.0f;
  153. int sequenceKnob = 0;
  154. Trigger resetTrigger;
  155. Trigger leftTrigger;
  156. Trigger rightTrigger;
  157. Trigger runningTrigger;
  158. Trigger clockTrigger;
  159. Trigger octTriggers[7];
  160. Trigger octmTrigger;
  161. Trigger gate1Trigger;
  162. Trigger gate1ProbTrigger;
  163. Trigger gate2Trigger;
  164. Trigger slideTrigger;
  165. Trigger keyTriggers[12];
  166. Trigger writeTrigger;
  167. Trigger attachedTrigger;
  168. Trigger copyTrigger;
  169. Trigger pasteTrigger;
  170. Trigger modeTrigger;
  171. Trigger rotateTrigger;
  172. Trigger transposeTrigger;
  173. Trigger tiedTrigger;
  174. Trigger stepTriggers[16];
  175. Trigger keyNoteTrigger;
  176. Trigger keyGateTrigger;
  177. Trigger seqCVTrigger;
  178. HoldDetect modeHoldDetect;
  179. inline bool isEditingSequence(void) {return params[EDIT_PARAM].value > 0.5f;}
  180. PhraseSeq16() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  181. onReset();
  182. }
  183. void onReset() override {
  184. autoseq = false;
  185. autostepLen = false;
  186. holdTiedNotes = true;
  187. seqCVmethod = 0;
  188. pulsesPerStep = 1;
  189. running = true;
  190. runModeSong = MODE_FWD;
  191. stepIndexEdit = 0;
  192. phraseIndexEdit = 0;
  193. sequence = 0;
  194. phrases = 4;
  195. for (int i = 0; i < 16; i++) {
  196. for (int s = 0; s < 16; s++) {
  197. cv[i][s] = 0.0f;
  198. attributes[i][s].init();
  199. }
  200. sequences[i].init(16, MODE_FWD);
  201. phrase[i] = 0;
  202. cvCPbuffer[i] = 0.0f;
  203. attribCPbuffer[i].init();
  204. phraseCPbuffer[i] = 0;
  205. }
  206. initRun();
  207. seqAttribCPbuffer.init(16, MODE_FWD);
  208. seqCopied = true;
  209. countCP = 16;
  210. startCP = 0;
  211. editingGate = 0ul;
  212. editingType = 0ul;
  213. infoCopyPaste = 0l;
  214. displayState = DISP_NORMAL;
  215. slideStepsRemain = 0ul;
  216. attached = false;
  217. clockPeriod = 0ul;
  218. tiedWarning = 0ul;
  219. attachedWarning = 0l;
  220. revertDisplay = 0l;
  221. resetOnRun = false;
  222. editingGateLength = 0l;
  223. lastGateEdit = 1l;
  224. editingPpqn = 0l;
  225. clockIgnoreOnReset = (long) (clockIgnoreOnResetDuration * engineGetSampleRate());
  226. }
  227. void onRandomize() override {
  228. if (isEditingSequence()) {
  229. for (int s = 0; s < 16; s++) {
  230. cv[sequence][s] = ((float)(randomu32() % 7)) + ((float)(randomu32() % 12)) / 12.0f - 3.0f;
  231. attributes[sequence][s].randomize();
  232. // if (attributes[sequence][s].getTied()) {
  233. // activateTiedStep(sequence, s);
  234. // }
  235. }
  236. sequences[sequence].randomize(16, NUM_MODES - 1);
  237. }
  238. }
  239. void initRun() {// run button activated or run edge in run input jack
  240. phraseIndexRun = (runModeSong == MODE_REV ? phrases - 1 : 0);
  241. phraseIndexRunHistory = 0;
  242. int seq = (isEditingSequence() ? sequence : phrase[phraseIndexRun]);
  243. stepIndexRun = (sequences[seq].getRunMode() == MODE_REV ? sequences[seq].getLength() - 1 : 0);
  244. stepIndexRunHistory = 0;
  245. ppqnCount = 0;
  246. gate1Code = calcGate1Code(attributes[seq][stepIndexRun], 0, pulsesPerStep, params[GATE1_KNOB_PARAM].value);
  247. gate2Code = calcGate2Code(attributes[seq][stepIndexRun], 0, pulsesPerStep);
  248. slideStepsRemain = 0ul;
  249. }
  250. json_t *toJson() override {
  251. json_t *rootJ = json_object();
  252. // panelTheme
  253. json_object_set_new(rootJ, "panelTheme", json_integer(panelTheme));
  254. // expansion
  255. json_object_set_new(rootJ, "expansion", json_integer(expansion));
  256. // autostepLen
  257. json_object_set_new(rootJ, "autostepLen", json_boolean(autostepLen));
  258. // autoseq
  259. json_object_set_new(rootJ, "autoseq", json_boolean(autoseq));
  260. // holdTiedNotes
  261. json_object_set_new(rootJ, "holdTiedNotes", json_boolean(holdTiedNotes));
  262. // seqCVmethod
  263. json_object_set_new(rootJ, "seqCVmethod", json_integer(seqCVmethod));
  264. // pulsesPerStep
  265. json_object_set_new(rootJ, "pulsesPerStep", json_integer(pulsesPerStep));
  266. // running
  267. json_object_set_new(rootJ, "running", json_boolean(running));
  268. // runModeSong
  269. json_object_set_new(rootJ, "runModeSong3", json_integer(runModeSong));
  270. // sequence
  271. json_object_set_new(rootJ, "sequence", json_integer(sequence));
  272. // phrase
  273. json_t *phraseJ = json_array();
  274. for (int i = 0; i < 16; i++)
  275. json_array_insert_new(phraseJ, i, json_integer(phrase[i]));
  276. json_object_set_new(rootJ, "phrase", phraseJ);
  277. // phrases
  278. json_object_set_new(rootJ, "phrases", json_integer(phrases));
  279. // CV
  280. json_t *cvJ = json_array();
  281. for (int i = 0; i < 16; i++)
  282. for (int s = 0; s < 16; s++) {
  283. json_array_insert_new(cvJ, s + (i * 16), json_real(cv[i][s]));
  284. }
  285. json_object_set_new(rootJ, "cv", cvJ);
  286. // attributes
  287. json_t *attributesJ = json_array();
  288. for (int i = 0; i < 16; i++)
  289. for (int s = 0; s < 16; s++) {
  290. json_array_insert_new(attributesJ, s + (i * 16), json_integer(attributes[i][s].getAttribute()));
  291. }
  292. json_object_set_new(rootJ, "attributes", attributesJ);
  293. // attached
  294. json_object_set_new(rootJ, "attached", json_boolean(attached));
  295. // resetOnRun
  296. json_object_set_new(rootJ, "resetOnRun", json_boolean(resetOnRun));
  297. // stepIndexEdit
  298. json_object_set_new(rootJ, "stepIndexEdit", json_integer(stepIndexEdit));
  299. // phraseIndexEdit
  300. json_object_set_new(rootJ, "phraseIndexEdit", json_integer(phraseIndexEdit));
  301. // sequences
  302. json_t *sequencesJ = json_array();
  303. for (int i = 0; i < 16; i++)
  304. json_array_insert_new(sequencesJ, i, json_integer(sequences[i].getSeqAttrib()));
  305. json_object_set_new(rootJ, "sequences", sequencesJ);
  306. return rootJ;
  307. }
  308. void fromJson(json_t *rootJ) override {
  309. // panelTheme
  310. json_t *panelThemeJ = json_object_get(rootJ, "panelTheme");
  311. if (panelThemeJ)
  312. panelTheme = json_integer_value(panelThemeJ);
  313. // expansion
  314. json_t *expansionJ = json_object_get(rootJ, "expansion");
  315. if (expansionJ)
  316. expansion = json_integer_value(expansionJ);
  317. // autostepLen
  318. json_t *autostepLenJ = json_object_get(rootJ, "autostepLen");
  319. if (autostepLenJ)
  320. autostepLen = json_is_true(autostepLenJ);
  321. // autoseq
  322. json_t *autoseqJ = json_object_get(rootJ, "autoseq");
  323. if (autoseqJ)
  324. autoseq = json_is_true(autoseqJ);
  325. // holdTiedNotes
  326. json_t *holdTiedNotesJ = json_object_get(rootJ, "holdTiedNotes");
  327. if (holdTiedNotesJ)
  328. holdTiedNotes = json_is_true(holdTiedNotesJ);
  329. else
  330. holdTiedNotes = false;// legacy
  331. // seqCVmethod
  332. json_t *seqCVmethodJ = json_object_get(rootJ, "seqCVmethod");
  333. if (seqCVmethodJ)
  334. seqCVmethod = json_integer_value(seqCVmethodJ);
  335. // pulsesPerStep
  336. json_t *pulsesPerStepJ = json_object_get(rootJ, "pulsesPerStep");
  337. if (pulsesPerStepJ)
  338. pulsesPerStep = json_integer_value(pulsesPerStepJ);
  339. // running
  340. json_t *runningJ = json_object_get(rootJ, "running");
  341. if (runningJ)
  342. running = json_is_true(runningJ);
  343. // sequences
  344. json_t *sequencesJ = json_object_get(rootJ, "sequences");
  345. if (sequencesJ) {
  346. for (int i = 0; i < 16; i++)
  347. {
  348. json_t *sequencesArrayJ = json_array_get(sequencesJ, i);
  349. if (sequencesArrayJ)
  350. sequences[i].setSeqAttrib(json_integer_value(sequencesArrayJ));
  351. }
  352. }
  353. else {// legacy
  354. int lengths[16];//1 to 16
  355. int runModeSeq[16];
  356. int transposeOffsets[16];
  357. // runModeSeq
  358. json_t *runModeSeqJ = json_object_get(rootJ, "runModeSeq3");
  359. if (runModeSeqJ) {
  360. for (int i = 0; i < 16; i++)
  361. {
  362. json_t *runModeSeqArrayJ = json_array_get(runModeSeqJ, i);
  363. if (runModeSeqArrayJ)
  364. runModeSeq[i] = json_integer_value(runModeSeqArrayJ);
  365. }
  366. }
  367. else {// legacy
  368. runModeSeqJ = json_object_get(rootJ, "runModeSeq2");
  369. if (runModeSeqJ) {
  370. for (int i = 0; i < 16; i++)
  371. {
  372. json_t *runModeSeqArrayJ = json_array_get(runModeSeqJ, i);
  373. if (runModeSeqArrayJ) {
  374. runModeSeq[i] = json_integer_value(runModeSeqArrayJ);
  375. if (runModeSeq[i] >= MODE_PEN)// this mode was not present in version runModeSeq2
  376. runModeSeq[i]++;
  377. }
  378. }
  379. }
  380. else {// legacy
  381. runModeSeqJ = json_object_get(rootJ, "runModeSeq");
  382. if (runModeSeqJ) {
  383. runModeSeq[0] = json_integer_value(runModeSeqJ);
  384. if (runModeSeq[0] >= MODE_PEN)// this mode was not present in original version
  385. runModeSeq[0]++;
  386. }
  387. for (int i = 1; i < 16; i++)// there was only one global runModeSeq in original version
  388. runModeSeq[i] = runModeSeq[0];
  389. }
  390. }
  391. // lengths
  392. json_t *lengthsJ = json_object_get(rootJ, "lengths");
  393. if (lengthsJ) {
  394. for (int i = 0; i < 16; i++)
  395. {
  396. json_t *lengthsArrayJ = json_array_get(lengthsJ, i);
  397. if (lengthsArrayJ)
  398. lengths[i] = json_integer_value(lengthsArrayJ);
  399. }
  400. }
  401. else {// legacy
  402. json_t *stepsJ = json_object_get(rootJ, "steps");
  403. if (stepsJ) {
  404. int steps = json_integer_value(stepsJ);
  405. for (int i = 0; i < 16; i++)
  406. lengths[i] = steps;
  407. }
  408. }
  409. // transposeOffsets
  410. json_t *transposeOffsetsJ = json_object_get(rootJ, "transposeOffsets");
  411. if (transposeOffsetsJ) {
  412. for (int i = 0; i < 16; i++)
  413. {
  414. json_t *transposeOffsetsArrayJ = json_array_get(transposeOffsetsJ, i);
  415. if (transposeOffsetsArrayJ)
  416. transposeOffsets[i] = json_integer_value(transposeOffsetsArrayJ);
  417. }
  418. }
  419. // now write into new object
  420. for (int i = 0; i < 16; i++) {
  421. sequences[i].init(lengths[i], runModeSeq[i]);
  422. sequences[i].setTranspose(transposeOffsets[i]);
  423. }
  424. }
  425. // runModeSong
  426. json_t *runModeSongJ = json_object_get(rootJ, "runModeSong3");
  427. if (runModeSongJ)
  428. runModeSong = json_integer_value(runModeSongJ);
  429. else {// legacy
  430. runModeSongJ = json_object_get(rootJ, "runModeSong");
  431. if (runModeSongJ) {
  432. runModeSong = json_integer_value(runModeSongJ);
  433. if (runModeSong >= MODE_PEN)// this mode was not present in original version
  434. runModeSong++;
  435. }
  436. }
  437. // sequence
  438. json_t *sequenceJ = json_object_get(rootJ, "sequence");
  439. if (sequenceJ)
  440. sequence = json_integer_value(sequenceJ);
  441. // phrase
  442. json_t *phraseJ = json_object_get(rootJ, "phrase");
  443. if (phraseJ)
  444. for (int i = 0; i < 16; i++)
  445. {
  446. json_t *phraseArrayJ = json_array_get(phraseJ, i);
  447. if (phraseArrayJ)
  448. phrase[i] = json_integer_value(phraseArrayJ);
  449. }
  450. // phrases
  451. json_t *phrasesJ = json_object_get(rootJ, "phrases");
  452. if (phrasesJ)
  453. phrases = json_integer_value(phrasesJ);
  454. // CV
  455. json_t *cvJ = json_object_get(rootJ, "cv");
  456. if (cvJ) {
  457. for (int i = 0; i < 16; i++)
  458. for (int s = 0; s < 16; s++) {
  459. json_t *cvArrayJ = json_array_get(cvJ, s + (i * 16));
  460. if (cvArrayJ)
  461. cv[i][s] = json_number_value(cvArrayJ);
  462. }
  463. }
  464. // attributes
  465. json_t *attributesJ = json_object_get(rootJ, "attributes");
  466. if (attributesJ) {
  467. for (int i = 0; i < 16; i++)
  468. for (int s = 0; s < 16; s++) {
  469. json_t *attributesArrayJ = json_array_get(attributesJ, s + (i * 16));
  470. if (attributesArrayJ)
  471. attributes[i][s].setAttribute((unsigned short)json_integer_value(attributesArrayJ));
  472. }
  473. }
  474. else {// legacy
  475. for (int i = 0; i < 16; i++)
  476. for (int s = 0; s < 16; s++)
  477. attributes[i][s].setAttribute(0u);
  478. // gate1
  479. json_t *gate1J = json_object_get(rootJ, "gate1");
  480. if (gate1J) {
  481. for (int i = 0; i < 16; i++)
  482. for (int s = 0; s < 16; s++) {
  483. json_t *gate1arrayJ = json_array_get(gate1J, s + (i * 16));
  484. if (gate1arrayJ)
  485. if (!!json_integer_value(gate1arrayJ)) attributes[i][s].setGate1(true);
  486. }
  487. }
  488. // gate1Prob
  489. json_t *gate1ProbJ = json_object_get(rootJ, "gate1Prob");
  490. if (gate1ProbJ) {
  491. for (int i = 0; i < 16; i++)
  492. for (int s = 0; s < 16; s++) {
  493. json_t *gate1ProbarrayJ = json_array_get(gate1ProbJ, s + (i * 16));
  494. if (gate1ProbarrayJ)
  495. if (!!json_integer_value(gate1ProbarrayJ)) attributes[i][s].setGate1P(true);
  496. }
  497. }
  498. // gate2
  499. json_t *gate2J = json_object_get(rootJ, "gate2");
  500. if (gate2J) {
  501. for (int i = 0; i < 16; i++)
  502. for (int s = 0; s < 16; s++) {
  503. json_t *gate2arrayJ = json_array_get(gate2J, s + (i * 16));
  504. if (gate2arrayJ)
  505. if (!!json_integer_value(gate2arrayJ)) attributes[i][s].setGate2(true);
  506. }
  507. }
  508. // slide
  509. json_t *slideJ = json_object_get(rootJ, "slide");
  510. if (slideJ) {
  511. for (int i = 0; i < 16; i++)
  512. for (int s = 0; s < 16; s++) {
  513. json_t *slideArrayJ = json_array_get(slideJ, s + (i * 16));
  514. if (slideArrayJ)
  515. if (!!json_integer_value(slideArrayJ)) attributes[i][s].setSlide(true);
  516. }
  517. }
  518. // tied
  519. json_t *tiedJ = json_object_get(rootJ, "tied");
  520. if (tiedJ) {
  521. for (int i = 0; i < 16; i++)
  522. for (int s = 0; s < 16; s++) {
  523. json_t *tiedArrayJ = json_array_get(tiedJ, s + (i * 16));
  524. if (tiedArrayJ)
  525. if (!!json_integer_value(tiedArrayJ)) attributes[i][s].setTied(true);
  526. }
  527. }
  528. }
  529. // attached
  530. json_t *attachedJ = json_object_get(rootJ, "attached");
  531. if (attachedJ)
  532. attached = json_is_true(attachedJ);
  533. // resetOnRun
  534. json_t *resetOnRunJ = json_object_get(rootJ, "resetOnRun");
  535. if (resetOnRunJ)
  536. resetOnRun = json_is_true(resetOnRunJ);
  537. // stepIndexEdit
  538. json_t *stepIndexEditJ = json_object_get(rootJ, "stepIndexEdit");
  539. if (stepIndexEditJ)
  540. stepIndexEdit = json_integer_value(stepIndexEditJ);
  541. // phraseIndexEdit
  542. json_t *phraseIndexEditJ = json_object_get(rootJ, "phraseIndexEdit");
  543. if (phraseIndexEditJ)
  544. phraseIndexEdit = json_integer_value(phraseIndexEditJ);
  545. // Initialize dependants after everything loaded
  546. initRun();
  547. }
  548. void rotateSeq(int seqNum, bool directionRight, int seqLength) {
  549. float rotCV;
  550. StepAttributes rotAttributes;
  551. int iStart = 0;
  552. int iEnd = seqLength - 1;
  553. int iRot = iStart;
  554. int iDelta = 1;
  555. if (directionRight) {
  556. iRot = iEnd;
  557. iDelta = -1;
  558. }
  559. rotCV = cv[seqNum][iRot];
  560. rotAttributes = attributes[seqNum][iRot];
  561. for ( ; ; iRot += iDelta) {
  562. if (iDelta == 1 && iRot >= iEnd) break;
  563. if (iDelta == -1 && iRot <= iStart) break;
  564. cv[seqNum][iRot] = cv[seqNum][iRot + iDelta];
  565. attributes[seqNum][iRot] = attributes[seqNum][iRot + iDelta];
  566. }
  567. cv[seqNum][iRot] = rotCV;
  568. attributes[seqNum][iRot] = rotAttributes;
  569. }
  570. void step() override {
  571. float sampleRate = engineGetSampleRate();
  572. static const float gateTime = 0.4f;// seconds
  573. static const float revertDisplayTime = 0.7f;// seconds
  574. static const float warningTime = 0.7f;// seconds
  575. static const float holdDetectTime = 2.0f;// seconds
  576. static const float editGateLengthTime = 3.5f;// seconds
  577. //********** Buttons, knobs, switches and inputs **********
  578. // Edit mode
  579. bool editingSequence = isEditingSequence();// true = editing sequence, false = editing song
  580. // Run button
  581. if (runningTrigger.process(params[RUN_PARAM].value + inputs[RUNCV_INPUT].value)) {// no input refresh here, don't want to introduce startup skew
  582. running = !running;
  583. if (running) {
  584. if (resetOnRun)
  585. initRun();
  586. if (resetOnRun || clockIgnoreOnRun)
  587. clockIgnoreOnReset = (long) (clockIgnoreOnResetDuration * sampleRate);
  588. }
  589. displayState = DISP_NORMAL;
  590. }
  591. if ((lightRefreshCounter & userInputsStepSkipMask) == 0) {
  592. // Seq CV input
  593. if (inputs[SEQCV_INPUT].active) {
  594. if (seqCVmethod == 0) {// 0-10 V
  595. int newSeq = (int)( inputs[SEQCV_INPUT].value * (16.0f - 1.0f) / 10.0f + 0.5f );
  596. sequence = clamp(newSeq, 0, 16 - 1);
  597. }
  598. else if (seqCVmethod == 1) {// C4-D5#
  599. int newSeq = (int)( (inputs[SEQCV_INPUT].value) * 12.0f + 0.5f );
  600. sequence = clamp(newSeq, 0, 16 - 1);
  601. }
  602. else {// TrigIncr
  603. if (seqCVTrigger.process(inputs[SEQCV_INPUT].value))
  604. sequence = clamp(sequence + 1, 0, 16 - 1);
  605. }
  606. }
  607. // Mode CV input
  608. if (inputs[MODECV_INPUT].active) {
  609. if (editingSequence)
  610. sequences[sequence].setRunMode((int) clamp( round(inputs[MODECV_INPUT].value * ((float)NUM_MODES - 1.0f - 1.0f) / 10.0f), 0.0f, (float)NUM_MODES - 1.0f - 1.0f ));
  611. }
  612. // Attach button
  613. if (attachedTrigger.process(params[ATTACH_PARAM].value)) {
  614. attached = !attached;
  615. displayState = DISP_NORMAL;
  616. }
  617. if (running && attached) {
  618. if (editingSequence)
  619. stepIndexEdit = stepIndexRun;
  620. else
  621. phraseIndexEdit = phraseIndexRun;
  622. }
  623. // Copy button
  624. if (copyTrigger.process(params[COPY_PARAM].value)) {
  625. startCP = editingSequence ? stepIndexEdit : phraseIndexEdit;
  626. countCP = 16;
  627. if (params[CPMODE_PARAM].value > 1.5f)// all
  628. startCP = 0;
  629. else if (params[CPMODE_PARAM].value < 0.5f)// 4
  630. countCP = min(4, 16 - startCP);
  631. else// 8
  632. countCP = min(8, 16 - startCP);
  633. if (editingSequence) {
  634. for (int i = 0, s = startCP; i < countCP; i++, s++) {
  635. cvCPbuffer[i] = cv[sequence][s];
  636. attribCPbuffer[i] = attributes[sequence][s];
  637. }
  638. seqAttribCPbuffer.setSeqAttrib(sequences[sequence].getSeqAttrib());
  639. seqCopied = true;
  640. }
  641. else {
  642. for (int i = 0, p = startCP; i < countCP; i++, p++)
  643. phraseCPbuffer[i] = phrase[p];
  644. seqCopied = false;// so that a cross paste can be detected
  645. }
  646. infoCopyPaste = (long) (revertDisplayTime * sampleRate / displayRefreshStepSkips);
  647. displayState = DISP_NORMAL;
  648. }
  649. // Paste button
  650. if (pasteTrigger.process(params[PASTE_PARAM].value)) {
  651. infoCopyPaste = (long) (-1 * revertDisplayTime * sampleRate / displayRefreshStepSkips);
  652. startCP = 0;
  653. if (countCP <= 8) {
  654. startCP = editingSequence ? stepIndexEdit : phraseIndexEdit;
  655. countCP = min(countCP, 16 - startCP);
  656. }
  657. // else nothing to do for ALL
  658. if (editingSequence) {
  659. if (seqCopied) {// non-crossed paste (seq vs song)
  660. for (int i = 0, s = startCP; i < countCP; i++, s++) {
  661. cv[sequence][s] = cvCPbuffer[i];
  662. attributes[sequence][s] = attribCPbuffer[i];
  663. }
  664. if (params[CPMODE_PARAM].value > 1.5f) {// all
  665. sequences[sequence].setSeqAttrib(seqAttribCPbuffer.getSeqAttrib());
  666. }
  667. }
  668. else {// crossed paste to seq (seq vs song)
  669. if (params[CPMODE_PARAM].value > 1.5f) { // ALL (init steps)
  670. for (int s = 0; s < 16; s++) {
  671. //cv[sequence][s] = 0.0f;
  672. //attributes[sequence][s].init();
  673. attributes[sequence][s].toggleGate1();
  674. }
  675. sequences[sequence].setTranspose(0);
  676. sequences[sequence].setRotate(0);
  677. }
  678. else if (params[CPMODE_PARAM].value < 0.5f) {// 4 (randomize CVs)
  679. for (int s = 0; s < 16; s++)
  680. cv[sequence][s] = ((float)(randomu32() % 7)) + ((float)(randomu32() % 12)) / 12.0f - 3.0f;
  681. sequences[sequence].setTranspose(0);
  682. sequences[sequence].setRotate(0);
  683. }
  684. else {// 8 (randomize gate 1)
  685. for (int s = 0; s < 16; s++)
  686. if ( (randomu32() & 0x1) != 0)
  687. attributes[sequence][s].toggleGate1();
  688. }
  689. startCP = 0;
  690. countCP = 16;
  691. infoCopyPaste *= 2l;
  692. }
  693. }
  694. else {
  695. if (!seqCopied) {// non-crossed paste (seq vs song)
  696. for (int i = 0, p = startCP; i < countCP; i++, p++)
  697. phrase[p] = phraseCPbuffer[i];
  698. }
  699. else {// crossed paste to song (seq vs song)
  700. if (params[CPMODE_PARAM].value > 1.5f) { // ALL (init phrases)
  701. for (int p = 0; p < 16; p++)
  702. phrase[p] = 0;
  703. }
  704. else if (params[CPMODE_PARAM].value < 0.5f) {// 4 (phrases increase from 1 to 16)
  705. for (int p = 0; p < 16; p++)
  706. phrase[p] = p;
  707. }
  708. else {// 8 (randomize phrases)
  709. for (int p = 0; p < 16; p++)
  710. phrase[p] = randomu32() % 16;
  711. }
  712. startCP = 0;
  713. countCP = 16;
  714. infoCopyPaste *= 2l;
  715. }
  716. }
  717. displayState = DISP_NORMAL;
  718. }
  719. // Write input (must be before Left and Right in case route gate simultaneously to Right and Write for example)
  720. // (write must be to correct step)
  721. bool writeTrig = writeTrigger.process(inputs[WRITE_INPUT].value);
  722. if (writeTrig) {
  723. if (editingSequence) {
  724. if (!attributes[sequence][stepIndexEdit].getTied()) {
  725. cv[sequence][stepIndexEdit] = inputs[CV_INPUT].value;
  726. propagateCVtoTied(sequence, stepIndexEdit);
  727. }
  728. editingGate = (unsigned long) (gateTime * sampleRate / displayRefreshStepSkips);
  729. editingGateCV = inputs[CV_INPUT].value;// cv[sequence][stepIndexEdit];
  730. editingGateKeyLight = -1;
  731. // Autostep (after grab all active inputs)
  732. if (params[AUTOSTEP_PARAM].value > 0.5f) {
  733. stepIndexEdit = moveIndex(stepIndexEdit, stepIndexEdit + 1, autostepLen ? sequences[sequence].getLength() : 16);
  734. if (stepIndexEdit == 0 && autoseq && !inputs[SEQCV_INPUT].active)
  735. sequence = moveIndex(sequence, sequence + 1, 16);
  736. }
  737. }
  738. displayState = DISP_NORMAL;
  739. }
  740. // Left and Right CV inputs
  741. int delta = 0;
  742. if (leftTrigger.process(inputs[LEFTCV_INPUT].value)) {
  743. delta = -1;
  744. if (displayState != DISP_LENGTH)
  745. displayState = DISP_NORMAL;
  746. }
  747. if (rightTrigger.process(inputs[RIGHTCV_INPUT].value)) {
  748. delta = +1;
  749. if (displayState != DISP_LENGTH)
  750. displayState = DISP_NORMAL;
  751. }
  752. if (delta != 0) {
  753. if (displayState == DISP_LENGTH) {
  754. if (editingSequence) {
  755. sequences[sequence].setLength(clamp(sequences[sequence].getLength() + delta, 1, 16));
  756. }
  757. else {
  758. phrases = clamp(phrases + delta, 1, 16);
  759. }
  760. }
  761. else {
  762. if (!running || !attached) {// don't move heads when attach and running
  763. if (editingSequence) {
  764. stepIndexEdit = moveIndex(stepIndexEdit, stepIndexEdit + delta, 16);
  765. if (!attributes[sequence][stepIndexEdit].getTied()) {// play if non-tied step
  766. if (!writeTrig) {// in case autostep when simultaneous writeCV and stepCV (keep what was done in Write Input block above)
  767. editingGate = (unsigned long) (gateTime * sampleRate / displayRefreshStepSkips);
  768. editingGateCV = cv[sequence][stepIndexEdit];
  769. editingGateKeyLight = -1;
  770. }
  771. }
  772. }
  773. else {
  774. phraseIndexEdit = moveIndex(phraseIndexEdit, phraseIndexEdit + delta, 16);
  775. if (!running)
  776. phraseIndexRun = phraseIndexEdit;
  777. }
  778. }
  779. }
  780. }
  781. // Step button presses
  782. int stepPressed = -1;
  783. for (int i = 0; i < 16; i++) {
  784. if (stepTriggers[i].process(params[STEP_PHRASE_PARAMS + i].value))
  785. stepPressed = i;
  786. }
  787. if (stepPressed != -1) {
  788. if (displayState == DISP_LENGTH) {
  789. if (editingSequence)
  790. sequences[sequence].setLength(stepPressed + 1);
  791. else
  792. phrases = stepPressed + 1;
  793. revertDisplay = (long) (revertDisplayTime * sampleRate / displayRefreshStepSkips);
  794. }
  795. else {
  796. if (!running || !attached) {// not running or detached
  797. if (editingSequence) {
  798. stepIndexEdit = stepPressed;
  799. if (!attributes[sequence][stepIndexEdit].getTied()) {// play if non-tied step
  800. editingGate = (unsigned long) (gateTime * sampleRate / displayRefreshStepSkips);
  801. editingGateCV = cv[sequence][stepIndexEdit];
  802. editingGateKeyLight = -1;
  803. }
  804. }
  805. else {
  806. phraseIndexEdit = stepPressed;
  807. if (!running)
  808. phraseIndexRun = stepPressed;
  809. }
  810. }
  811. else if (attached)
  812. attachedWarning = (long) (warningTime * sampleRate / displayRefreshStepSkips);
  813. displayState = DISP_NORMAL;
  814. }
  815. }
  816. // Mode/Length button
  817. if (modeTrigger.process(params[RUNMODE_PARAM].value)) {
  818. if (editingPpqn != 0l)
  819. editingPpqn = 0l;
  820. if (displayState == DISP_NORMAL || displayState == DISP_TRANSPOSE || displayState == DISP_ROTATE)
  821. displayState = DISP_LENGTH;
  822. else if (displayState == DISP_LENGTH)
  823. displayState = DISP_MODE;
  824. else
  825. displayState = DISP_NORMAL;
  826. modeHoldDetect.start((long) (holdDetectTime * sampleRate / displayRefreshStepSkips));
  827. }
  828. // Transpose/Rotate button
  829. if (transposeTrigger.process(params[TRAN_ROT_PARAM].value)) {
  830. if (editingSequence) {
  831. if (displayState == DISP_NORMAL || displayState == DISP_MODE || displayState == DISP_LENGTH) {
  832. displayState = DISP_TRANSPOSE;
  833. }
  834. else if (displayState == DISP_TRANSPOSE) {
  835. displayState = DISP_ROTATE;
  836. }
  837. else
  838. displayState = DISP_NORMAL;
  839. }
  840. }
  841. // Sequence knob
  842. float seqParamValue = params[SEQUENCE_PARAM].value;
  843. int newSequenceKnob = (int)roundf(seqParamValue * 7.0f);
  844. if (seqParamValue == 0.0f)// true when constructor or fromJson() occured
  845. sequenceKnob = newSequenceKnob;
  846. int deltaKnob = newSequenceKnob - sequenceKnob;
  847. if (deltaKnob != 0) {
  848. if (abs(deltaKnob) <= 3) {// avoid discontinuous step (initialize for example)
  849. // any changes in here should may also require right click behavior to be updated in the knob's onMouseDown()
  850. if (editingPpqn != 0) {
  851. pulsesPerStep = indexToPps(ppsToIndex(pulsesPerStep) + deltaKnob);// indexToPps() does clamping
  852. editingPpqn = (long) (editGateLengthTime * sampleRate / displayRefreshStepSkips);
  853. }
  854. else if (displayState == DISP_MODE) {
  855. if (editingSequence) {
  856. if (!inputs[MODECV_INPUT].active) {
  857. sequences[sequence].setRunMode(clamp(sequences[sequence].getRunMode() + deltaKnob, 0, (NUM_MODES - 1 - 1)));
  858. }
  859. }
  860. else {
  861. runModeSong = clamp(runModeSong + deltaKnob, 0, 6 - 1);
  862. }
  863. }
  864. else if (displayState == DISP_LENGTH) {
  865. if (editingSequence) {
  866. sequences[sequence].setLength(clamp(sequences[sequence].getLength() + deltaKnob, 1, 16));
  867. }
  868. else {
  869. phrases = clamp(phrases + deltaKnob, 1, 16);
  870. }
  871. }
  872. else if (displayState == DISP_TRANSPOSE) {
  873. if (editingSequence) {
  874. sequences[sequence].setTranspose(clamp(sequences[sequence].getTranspose() + deltaKnob, -99, 99));
  875. float transposeOffsetCV = ((float)(deltaKnob))/12.0f;// Tranpose by deltaKnob number of semi-tones
  876. for (int s = 0; s < 16; s++) {
  877. cv[sequence][s] += transposeOffsetCV;
  878. }
  879. }
  880. }
  881. else if (displayState == DISP_ROTATE) {
  882. if (editingSequence) {
  883. int slength = sequences[sequence].getLength();
  884. sequences[sequence].setRotate(clamp(sequences[sequence].getRotate() + deltaKnob, -99, 99));
  885. if (deltaKnob > 0 && deltaKnob < 201) {// Rotate right, 201 is safety
  886. for (int i = deltaKnob; i > 0; i--) {
  887. rotateSeq(sequence, true, slength);
  888. if (stepIndexEdit < slength)
  889. stepIndexEdit = moveIndex(stepIndexEdit, stepIndexEdit + 1, slength);
  890. }
  891. }
  892. if (deltaKnob < 0 && deltaKnob > -201) {// Rotate left, 201 is safety
  893. for (int i = deltaKnob; i < 0; i++) {
  894. rotateSeq(sequence, false, slength);
  895. if (stepIndexEdit < slength)
  896. stepIndexEdit = moveIndex(stepIndexEdit, stepIndexEdit - 1, slength);
  897. }
  898. }
  899. }
  900. }
  901. else {// DISP_NORMAL
  902. if (editingSequence) {
  903. if (!inputs[SEQCV_INPUT].active) {
  904. sequence = clamp(sequence + deltaKnob, 0, 16 - 1);
  905. }
  906. }
  907. else {
  908. if (!attached || (attached && !running))
  909. phrase[phraseIndexEdit] = clamp(phrase[phraseIndexEdit] + deltaKnob, 0, 16 - 1);
  910. else
  911. attachedWarning = (long) (warningTime * sampleRate / displayRefreshStepSkips);
  912. }
  913. }
  914. }
  915. sequenceKnob = newSequenceKnob;
  916. }
  917. // Octave buttons
  918. int newOct = -1;
  919. for (int i = 0; i < 7; i++) {
  920. if (octTriggers[i].process(params[OCTAVE_PARAM + i].value)) {
  921. newOct = 6 - i;
  922. displayState = DISP_NORMAL;
  923. }
  924. }
  925. if (newOct >= 0 && newOct <= 6) {
  926. if (editingSequence) {
  927. if (attributes[sequence][stepIndexEdit].getTied())
  928. tiedWarning = (long) (warningTime * sampleRate / displayRefreshStepSkips);
  929. else {
  930. cv[sequence][stepIndexEdit] = applyNewOct(cv[sequence][stepIndexEdit], newOct);
  931. propagateCVtoTied(sequence, stepIndexEdit);
  932. editingGate = (unsigned long) (gateTime * sampleRate / displayRefreshStepSkips);
  933. editingGateCV = cv[sequence][stepIndexEdit];
  934. editingGateKeyLight = -1;
  935. }
  936. }
  937. }
  938. // Keyboard buttons
  939. for (int i = 0; i < 12; i++) {
  940. if (keyTriggers[i].process(params[KEY_PARAMS + i].value)) {
  941. if (editingSequence) {
  942. if (editingGateLength != 0l) {
  943. int newMode = keyIndexToGateMode(i, pulsesPerStep);
  944. if (newMode != -1) {
  945. editingPpqn = 0l;
  946. attributes[sequence][stepIndexEdit].setGateMode(newMode, editingGateLength > 0l);
  947. if (params[KEY_PARAMS + i].value > 1.5f) {// if right-click
  948. stepIndexEdit = moveIndex(stepIndexEdit, stepIndexEdit + 1, 16);
  949. editingType = (unsigned long) (gateTime * sampleRate / displayRefreshStepSkips);
  950. editingGateKeyLight = i;
  951. if (windowIsModPressed())
  952. attributes[sequence][stepIndexEdit].setGateMode(newMode, editingGateLength > 0l);
  953. }
  954. }
  955. else
  956. editingPpqn = (long) (editGateLengthTime * sampleRate / displayRefreshStepSkips);
  957. }
  958. else if (attributes[sequence][stepIndexEdit].getTied()) {
  959. if (params[KEY_PARAMS + i].value > 1.5f)// if right-click
  960. stepIndexEdit = moveIndex(stepIndexEdit, stepIndexEdit + 1, 16);
  961. else
  962. tiedWarning = (long) (warningTime * sampleRate / displayRefreshStepSkips);
  963. }
  964. else {
  965. float newCV = floor(cv[sequence][stepIndexEdit]) + ((float) i) / 12.0f;
  966. cv[sequence][stepIndexEdit] = newCV;
  967. propagateCVtoTied(sequence, stepIndexEdit);
  968. editingGate = (unsigned long) (gateTime * sampleRate / displayRefreshStepSkips);
  969. editingGateCV = cv[sequence][stepIndexEdit];
  970. editingGateKeyLight = -1;
  971. if (params[KEY_PARAMS + i].value > 1.5f) {// if right-click
  972. stepIndexEdit = moveIndex(stepIndexEdit, stepIndexEdit + 1, 16);
  973. editingGateKeyLight = i;
  974. if (windowIsModPressed())
  975. cv[sequence][stepIndexEdit] = newCV;
  976. }
  977. }
  978. }
  979. displayState = DISP_NORMAL;
  980. }
  981. }
  982. // Keyboard mode (note or gate type)
  983. if (keyNoteTrigger.process(params[KEYNOTE_PARAM].value)) {
  984. editingGateLength = 0l;
  985. }
  986. if (keyGateTrigger.process(params[KEYGATE_PARAM].value)) {
  987. if (editingGateLength == 0l) {
  988. editingGateLength = lastGateEdit;
  989. }
  990. else {
  991. editingGateLength *= -1l;
  992. lastGateEdit = editingGateLength;
  993. }
  994. }
  995. // Gate1, Gate1Prob, Gate2, Slide and Tied buttons
  996. if (gate1Trigger.process(params[GATE1_PARAM].value + inputs[GATE1CV_INPUT].value)) {
  997. if (editingSequence) {
  998. attributes[sequence][stepIndexEdit].toggleGate1();
  999. }
  1000. displayState = DISP_NORMAL;
  1001. }
  1002. if (gate1ProbTrigger.process(params[GATE1_PROB_PARAM].value)) {
  1003. if (editingSequence) {
  1004. if (attributes[sequence][stepIndexEdit].getTied())
  1005. tiedWarning = (long) (warningTime * sampleRate / displayRefreshStepSkips);
  1006. else
  1007. attributes[sequence][stepIndexEdit].toggleGate1P();
  1008. }
  1009. displayState = DISP_NORMAL;
  1010. }
  1011. if (gate2Trigger.process(params[GATE2_PARAM].value + inputs[GATE2CV_INPUT].value)) {
  1012. if (editingSequence) {
  1013. attributes[sequence][stepIndexEdit].toggleGate2();
  1014. }
  1015. displayState = DISP_NORMAL;
  1016. }
  1017. if (slideTrigger.process(params[SLIDE_BTN_PARAM].value + inputs[SLIDECV_INPUT].value)) {
  1018. if (editingSequence) {
  1019. if (attributes[sequence][stepIndexEdit].getTied())
  1020. tiedWarning = (long) (warningTime * sampleRate / displayRefreshStepSkips);
  1021. else
  1022. attributes[sequence][stepIndexEdit].toggleSlide();
  1023. }
  1024. displayState = DISP_NORMAL;
  1025. }
  1026. if (tiedTrigger.process(params[TIE_PARAM].value + inputs[TIEDCV_INPUT].value)) {
  1027. if (editingSequence) {
  1028. if (attributes[sequence][stepIndexEdit].getTied()) {
  1029. deactivateTiedStep(sequence, stepIndexEdit);
  1030. }
  1031. else {
  1032. activateTiedStep(sequence, stepIndexEdit);
  1033. }
  1034. }
  1035. displayState = DISP_NORMAL;
  1036. }
  1037. }// userInputs refresh
  1038. //********** Clock and reset **********
  1039. // Clock
  1040. if (running && clockIgnoreOnReset == 0l) {
  1041. if (clockTrigger.process(inputs[CLOCK_INPUT].value)) {
  1042. ppqnCount++;
  1043. if (ppqnCount >= pulsesPerStep)
  1044. ppqnCount = 0;
  1045. int newSeq = sequence;// good value when editingSequence, overwrite if not editingSequence
  1046. if (ppqnCount == 0) {
  1047. float slideFromCV = 0.0f;
  1048. if (editingSequence) {
  1049. slideFromCV = cv[sequence][stepIndexRun];
  1050. moveIndexRunMode(&stepIndexRun, sequences[sequence].getLength(), sequences[sequence].getRunMode(), &stepIndexRunHistory);
  1051. }
  1052. else {
  1053. slideFromCV = cv[phrase[phraseIndexRun]][stepIndexRun];
  1054. if (moveIndexRunMode(&stepIndexRun, sequences[phrase[phraseIndexRun]].getLength(), sequences[phrase[phraseIndexRun]].getRunMode(), &stepIndexRunHistory)) {
  1055. moveIndexRunMode(&phraseIndexRun, phrases, runModeSong, &phraseIndexRunHistory);
  1056. stepIndexRun = (sequences[phrase[phraseIndexRun]].getRunMode() == MODE_REV ? sequences[phrase[phraseIndexRun]].getLength() - 1 : 0);// must always refresh after phraseIndexRun has changed
  1057. }
  1058. newSeq = phrase[phraseIndexRun];
  1059. }
  1060. // Slide
  1061. if (attributes[newSeq][stepIndexRun].getSlide()) {
  1062. slideStepsRemain = (unsigned long) (((float)clockPeriod * pulsesPerStep) * params[SLIDE_KNOB_PARAM].value / 2.0f);
  1063. if (slideStepsRemain != 0ul) {
  1064. float slideToCV = cv[newSeq][stepIndexRun];
  1065. slideCVdelta = (slideToCV - slideFromCV)/(float)slideStepsRemain;
  1066. }
  1067. }
  1068. else
  1069. slideStepsRemain = 0ul;
  1070. }
  1071. else {
  1072. if (!editingSequence)
  1073. newSeq = phrase[phraseIndexRun];
  1074. }
  1075. if (gate1Code != -1 || ppqnCount == 0)
  1076. gate1Code = calcGate1Code(attributes[newSeq][stepIndexRun], ppqnCount, pulsesPerStep, params[GATE1_KNOB_PARAM].value);
  1077. gate2Code = calcGate2Code(attributes[newSeq][stepIndexRun], ppqnCount, pulsesPerStep);
  1078. clockPeriod = 0ul;
  1079. }
  1080. clockPeriod++;
  1081. }
  1082. // Reset
  1083. if (resetTrigger.process(inputs[RESET_INPUT].value + params[RESET_PARAM].value)) {
  1084. initRun();// must be after sequence reset
  1085. resetLight = 1.0f;
  1086. displayState = DISP_NORMAL;
  1087. clockIgnoreOnReset = (long) (clockIgnoreOnResetDuration * sampleRate);
  1088. clockTrigger.reset();
  1089. if (inputs[SEQCV_INPUT].active && seqCVmethod == 2)
  1090. sequence = 0;
  1091. }
  1092. //********** Outputs and lights **********
  1093. // CV and gates outputs
  1094. int seq = editingSequence ? (sequence) : (running ? phrase[phraseIndexRun] : phrase[phraseIndexEdit]);
  1095. int step = editingSequence ? (running ? stepIndexRun : stepIndexEdit) : (stepIndexRun);
  1096. if (running) {
  1097. bool muteGate1 = !editingSequence && ((params[GATE1_PARAM].value + inputs[GATE1CV_INPUT].value) > 0.5f);// live mute
  1098. bool muteGate2 = !editingSequence && ((params[GATE2_PARAM].value + inputs[GATE2CV_INPUT].value) > 0.5f);// live mute
  1099. float slideOffset = (slideStepsRemain > 0ul ? (slideCVdelta * (float)slideStepsRemain) : 0.0f);
  1100. outputs[CV_OUTPUT].value = cv[seq][step] - slideOffset;
  1101. bool retriggingOnReset = (clockIgnoreOnReset != 0l && retrigGatesOnReset);
  1102. outputs[GATE1_OUTPUT].value = (calcGate(gate1Code, clockTrigger, clockPeriod, sampleRate) && !muteGate1 && !retriggingOnReset) ? 10.0f : 0.0f;
  1103. outputs[GATE2_OUTPUT].value = (calcGate(gate2Code, clockTrigger, clockPeriod, sampleRate) && !muteGate2 && !retriggingOnReset) ? 10.0f : 0.0f;
  1104. }
  1105. else {// not running
  1106. outputs[CV_OUTPUT].value = (editingGate > 0ul) ? editingGateCV : cv[seq][step];
  1107. outputs[GATE1_OUTPUT].value = (editingGate > 0ul) ? 10.0f : 0.0f;
  1108. outputs[GATE2_OUTPUT].value = (editingGate > 0ul) ? 10.0f : 0.0f;
  1109. }
  1110. if (slideStepsRemain > 0ul)
  1111. slideStepsRemain--;
  1112. lightRefreshCounter++;
  1113. if (lightRefreshCounter >= displayRefreshStepSkips) {
  1114. lightRefreshCounter = 0;
  1115. // Step/phrase lights
  1116. for (int i = 0; i < 16; i++) {
  1117. float red = 0.0f;
  1118. float green = 0.0f;
  1119. float white = 0.0f;
  1120. if (infoCopyPaste != 0l) {
  1121. if (i >= startCP && i < (startCP + countCP))
  1122. green = 0.5f;
  1123. }
  1124. else if (displayState == DISP_LENGTH) {
  1125. if (editingSequence) {
  1126. if (i < (sequences[sequence].getLength() - 1))
  1127. green = 0.1f;
  1128. else if (i == (sequences[sequence].getLength() - 1))
  1129. green = 1.0f;
  1130. }
  1131. else {
  1132. if (i < phrases - 1)
  1133. green = 0.1f;
  1134. else
  1135. green = (i == phrases - 1) ? 1.0f : 0.0f;
  1136. }
  1137. }
  1138. else if (displayState == DISP_TRANSPOSE) {
  1139. red = 0.5f;
  1140. }
  1141. else if (displayState == DISP_ROTATE) {
  1142. red = (i == stepIndexEdit ? 1.0f : (i < sequences[sequence].getLength() ? 0.2f : 0.0f));
  1143. }
  1144. else {// normal led display (i.e. not length)
  1145. // Run cursor (green)
  1146. if (editingSequence)
  1147. green = ((running && (i == stepIndexRun)) ? 1.0f : 0.0f);
  1148. else {
  1149. green = ((running && (i == phraseIndexRun)) ? 1.0f : 0.0f);
  1150. green += ((running && (i == stepIndexRun) && i != phraseIndexEdit) ? 0.1f : 0.0f);
  1151. green = clamp(green, 0.0f, 1.0f);
  1152. }
  1153. // Edit cursor (red)
  1154. if (editingSequence)
  1155. red = (i == stepIndexEdit ? 1.0f : 0.0f);
  1156. else
  1157. red = (i == phraseIndexEdit ? 1.0f : 0.0f);
  1158. bool gate = false;
  1159. if (editingSequence)
  1160. gate = attributes[sequence][i].getGate1();
  1161. else if (!editingSequence && attached)
  1162. gate = attributes[phrase[phraseIndexRun]][i].getGate1();
  1163. white = ((green == 0.0f && red == 0.0f && gate && displayState != DISP_MODE) ? 0.04f : 0.0f);
  1164. if (editingSequence && white != 0.0f) {
  1165. green = 0.02f; white = 0.0f;
  1166. }
  1167. //if (white != 0.0f && attributes[sequence][i].getGate1P()) white = 0.01f;
  1168. }
  1169. setGreenRed(STEP_PHRASE_LIGHTS + i * 3, green, red);
  1170. lights[STEP_PHRASE_LIGHTS + i * 3 + 2].value = white;
  1171. }
  1172. // Octave lights
  1173. float octCV = 0.0f;
  1174. if (editingSequence)
  1175. octCV = cv[sequence][stepIndexEdit];
  1176. else
  1177. octCV = cv[phrase[phraseIndexEdit]][stepIndexRun];
  1178. int octLightIndex = (int) floor(octCV + 3.0f);
  1179. for (int i = 0; i < 7; i++) {
  1180. if (!editingSequence && (!attached || !running))// no oct lights when song mode and either (detached [1] or stopped [2])
  1181. // [1] makes no sense, can't mod steps and stepping though seq that may not be playing
  1182. // [2] CV is set to 0V when not running and in song mode, so cv[][] makes no sense to display
  1183. lights[OCTAVE_LIGHTS + i].value = 0.0f;
  1184. else {
  1185. if (tiedWarning > 0l) {
  1186. bool warningFlashState = calcWarningFlash(tiedWarning, (long) (warningTime * sampleRate / displayRefreshStepSkips));
  1187. lights[OCTAVE_LIGHTS + i].value = (warningFlashState && (i == (6 - octLightIndex))) ? 1.0f : 0.0f;
  1188. }
  1189. else
  1190. lights[OCTAVE_LIGHTS + i].value = (i == (6 - octLightIndex) ? 1.0f : 0.0f);
  1191. }
  1192. }
  1193. // Keyboard lights
  1194. float cvValOffset;
  1195. if (editingSequence)
  1196. cvValOffset = cv[sequence][stepIndexEdit] + 10.0f;//to properly handle negative note voltages
  1197. else
  1198. cvValOffset = cv[phrase[phraseIndexEdit]][stepIndexRun] + 10.0f;//to properly handle negative note voltages
  1199. int keyLightIndex = clamp( (int)((cvValOffset-floor(cvValOffset)) * 12.0f + 0.5f), 0, 11);
  1200. if (editingPpqn != 0) {
  1201. for (int i = 0; i < 12; i++) {
  1202. if (keyIndexToGateMode(i, pulsesPerStep) != -1) {
  1203. setGreenRed(KEY_LIGHTS + i * 2, 1.0f, 1.0f);
  1204. }
  1205. else {
  1206. setGreenRed(KEY_LIGHTS + i * 2, 0.0f, 0.0f);
  1207. }
  1208. }
  1209. }
  1210. else if (editingGateLength != 0l && editingSequence) {
  1211. int modeLightIndex = gateModeToKeyLightIndex(attributes[sequence][stepIndexEdit], editingGateLength > 0l);
  1212. for (int i = 0; i < 12; i++) {
  1213. float green = editingGateLength > 0l ? 1.0f : 0.2f;
  1214. float red = editingGateLength > 0l ? 0.2f : 1.0f;
  1215. if (editingType > 0ul) {
  1216. if (i == editingGateKeyLight) {
  1217. float dimMult = ((float) editingType / (float)(gateTime * sampleRate / displayRefreshStepSkips));
  1218. setGreenRed(KEY_LIGHTS + i * 2, green * dimMult, red * dimMult);
  1219. }
  1220. else
  1221. setGreenRed(KEY_LIGHTS + i * 2, 0.0f, 0.0f);
  1222. }
  1223. else {
  1224. if (i == modeLightIndex) {
  1225. setGreenRed(KEY_LIGHTS + i * 2, green, red);
  1226. }
  1227. else { // show dim note if gatetype is different than note
  1228. setGreenRed(KEY_LIGHTS + i * 2, 0.0f, (i == keyLightIndex ? 0.1f : 0.0f));
  1229. }
  1230. }
  1231. }
  1232. }
  1233. else {
  1234. for (int i = 0; i < 12; i++) {
  1235. lights[KEY_LIGHTS + i * 2 + 0].value = 0.0f;
  1236. if (!editingSequence && (!attached || !running))// no keyboard lights when song mode and either (detached [1] or stopped [2])
  1237. // [1] makes no sense, can't mod steps and stepping though seq that may not be playing
  1238. // [2] CV is set to 0V when not running and in song mode, so cv[][] makes no sense to display
  1239. lights[KEY_LIGHTS + i * 2 + 1].value = 0.0f;
  1240. else {
  1241. if (tiedWarning > 0l) {
  1242. bool warningFlashState = calcWarningFlash(tiedWarning, (long) (warningTime * sampleRate / displayRefreshStepSkips));
  1243. lights[KEY_LIGHTS + i * 2 + 1].value = (warningFlashState && i == keyLightIndex) ? 1.0f : 0.0f;
  1244. }
  1245. else {
  1246. if (editingGate > 0ul && editingGateKeyLight != -1)
  1247. lights[KEY_LIGHTS + i * 2 + 1].value = (i == editingGateKeyLight ? ((float) editingGate / (float)(gateTime * sampleRate / displayRefreshStepSkips)) : 0.0f);
  1248. else
  1249. lights[KEY_LIGHTS + i * 2 + 1].value = (i == keyLightIndex ? 1.0f : 0.0f);
  1250. }
  1251. }
  1252. }
  1253. }
  1254. // Key mode light (note or gate type)
  1255. lights[KEYNOTE_LIGHT].value = editingGateLength == 0l ? 10.0f : 0.0f;
  1256. if (editingGateLength == 0l)
  1257. setGreenRed(KEYGATE_LIGHT, 0.0f, 0.0f);
  1258. else if (editingGateLength > 0l)
  1259. setGreenRed(KEYGATE_LIGHT, 1.0f, 0.2f);
  1260. else
  1261. setGreenRed(KEYGATE_LIGHT, 0.2f, 1.0f);
  1262. // Gate1, Gate1Prob, Gate2, Slide and Tied lights
  1263. if (!editingSequence && (!attached || !running)) {// no oct lights when song mode and either (detached [1] or stopped [2])
  1264. // [1] makes no sense, can't mod steps and stepping though seq that may not be playing
  1265. // [2] CV is set to 0V when not running and in song mode, so cv[][] makes no sense to display
  1266. setGateLight(false, GATE1_LIGHT);
  1267. setGateLight(false, GATE2_LIGHT);
  1268. setGreenRed(GATE1_PROB_LIGHT, 0.0f, 0.0f);
  1269. lights[SLIDE_LIGHT].value = 0.0f;
  1270. lights[TIE_LIGHT].value = 0.0f;
  1271. }
  1272. else {
  1273. StepAttributes attributesVal = attributes[sequence][stepIndexEdit];
  1274. if (!editingSequence)
  1275. attributesVal = attributes[phrase[phraseIndexEdit]][stepIndexRun];
  1276. //
  1277. setGateLight(attributesVal.getGate1(), GATE1_LIGHT);
  1278. setGateLight(attributesVal.getGate2(), GATE2_LIGHT);
  1279. setGreenRed(GATE1_PROB_LIGHT, attributesVal.getGate1P() ? 1.0f : 0.0f, attributesVal.getGate1P() ? 1.0f : 0.0f);
  1280. lights[SLIDE_LIGHT].value = attributesVal.getSlide() ? 1.0f : 0.0f;
  1281. if (tiedWarning > 0l) {
  1282. bool warningFlashState = calcWarningFlash(tiedWarning, (long) (warningTime * sampleRate / displayRefreshStepSkips));
  1283. lights[TIE_LIGHT].value = (warningFlashState) ? 1.0f : 0.0f;
  1284. }
  1285. else
  1286. lights[TIE_LIGHT].value = attributesVal.getTied() ? 1.0f : 0.0f;
  1287. }
  1288. // Attach light
  1289. if (attachedWarning > 0l) {
  1290. bool warningFlashState = calcWarningFlash(attachedWarning, (long) (warningTime * sampleRate / displayRefreshStepSkips));
  1291. lights[ATTACH_LIGHT].value = (warningFlashState) ? 1.0f : 0.0f;
  1292. }
  1293. else
  1294. lights[ATTACH_LIGHT].value = (attached ? 1.0f : 0.0f);
  1295. // Reset light
  1296. lights[RESET_LIGHT].value = resetLight;
  1297. resetLight -= (resetLight / lightLambda) * engineGetSampleTime() * displayRefreshStepSkips;
  1298. // Run light
  1299. lights[RUN_LIGHT].value = running ? 1.0f : 0.0f;
  1300. if (editingGate > 0ul)
  1301. editingGate--;
  1302. if (editingType > 0ul)
  1303. editingType--;
  1304. if (infoCopyPaste != 0l) {
  1305. if (infoCopyPaste > 0l)
  1306. infoCopyPaste --;
  1307. if (infoCopyPaste < 0l)
  1308. infoCopyPaste ++;
  1309. }
  1310. if (editingPpqn > 0l)
  1311. editingPpqn--;
  1312. if (tiedWarning > 0l)
  1313. tiedWarning--;
  1314. if (attachedWarning > 0l)
  1315. attachedWarning--;
  1316. if (modeHoldDetect.process(params[RUNMODE_PARAM].value)) {
  1317. displayState = DISP_NORMAL;
  1318. editingPpqn = (long) (editGateLengthTime * sampleRate / displayRefreshStepSkips);
  1319. }
  1320. if (revertDisplay > 0l) {
  1321. if (revertDisplay == 1)
  1322. displayState = DISP_NORMAL;
  1323. revertDisplay--;
  1324. }
  1325. }// lightRefreshCounter
  1326. if (clockIgnoreOnReset > 0l)
  1327. clockIgnoreOnReset--;
  1328. }// step()
  1329. inline void setGreenRed(int id, float green, float red) {
  1330. lights[id + 0].value = green;
  1331. lights[id + 1].value = red;
  1332. }
  1333. inline void propagateCVtoTied(int seqn, int stepn) {
  1334. for (int i = stepn + 1; i < 16; i++) {
  1335. if (!attributes[seqn][i].getTied())
  1336. break;
  1337. cv[seqn][i] = cv[seqn][i - 1];
  1338. }
  1339. }
  1340. void activateTiedStep(int seqn, int stepn) {
  1341. attributes[seqn][stepn].setTied(true);
  1342. if (stepn > 0)
  1343. propagateCVtoTied(seqn, stepn - 1);
  1344. if (holdTiedNotes) {// new method
  1345. attributes[seqn][stepn].setGate1(true);
  1346. for (int i = max(stepn, 1); i < 16 && attributes[seqn][i].getTied(); i++) {
  1347. attributes[seqn][i].setGate1Mode(attributes[seqn][i - 1].getGate1Mode());
  1348. attributes[seqn][i - 1].setGate1Mode(5);
  1349. attributes[seqn][i - 1].setGate1(true);
  1350. }
  1351. }
  1352. else {// old method
  1353. if (stepn > 0) {
  1354. attributes[seqn][stepn] = attributes[seqn][stepn - 1];
  1355. attributes[seqn][stepn].setTied(true);
  1356. }
  1357. }
  1358. }
  1359. void deactivateTiedStep(int seqn, int stepn) {
  1360. attributes[seqn][stepn].setTied(false);
  1361. if (holdTiedNotes) {// new method
  1362. int lastGateType = attributes[seqn][stepn].getGate1Mode();
  1363. for (int i = stepn + 1; i < 16 && attributes[seqn][i].getTied(); i++)
  1364. lastGateType = attributes[seqn][i].getGate1Mode();
  1365. if (stepn > 0)
  1366. attributes[seqn][stepn - 1].setGate1Mode(lastGateType);
  1367. }
  1368. //else old method, nothing to do
  1369. }
  1370. inline void setGateLight(bool gateOn, int lightIndex) {
  1371. if (!gateOn) {
  1372. lights[lightIndex + 0].value = 0.0f;
  1373. lights[lightIndex + 1].value = 0.0f;
  1374. }
  1375. else if (editingGateLength == 0l) {
  1376. lights[lightIndex + 0].value = 0.0f;
  1377. lights[lightIndex + 1].value = 1.0f;
  1378. }
  1379. else {
  1380. lights[lightIndex + 0].value = lightIndex == GATE1_LIGHT ? 1.0f : 0.2f;
  1381. lights[lightIndex + 1].value = lightIndex == GATE1_LIGHT ? 0.2f : 1.0f;
  1382. }
  1383. }
  1384. };
  1385. struct PhraseSeq16Widget : ModuleWidget {
  1386. PhraseSeq16 *module;
  1387. DynamicSVGPanel *panel;
  1388. int oldExpansion;
  1389. int expWidth = 60;
  1390. IMPort* expPorts[5];
  1391. struct SequenceDisplayWidget : TransparentWidget {
  1392. PhraseSeq16 *module;
  1393. std::shared_ptr<Font> font;
  1394. char displayStr[4];
  1395. SequenceDisplayWidget() {
  1396. font = Font::load(assetPlugin(plugin, "res/fonts/Segment14.ttf"));
  1397. }
  1398. void runModeToStr(int num) {
  1399. if (num >= 0 && num < (NUM_MODES - 1))
  1400. snprintf(displayStr, 4, "%s", modeLabels[num].c_str());
  1401. }
  1402. void draw(NVGcontext *vg) override {
  1403. NVGcolor textColor = prepareDisplay(vg, &box, 18);
  1404. nvgFontFaceId(vg, font->handle);
  1405. bool editingSequence = module->isEditingSequence();
  1406. Vec textPos = Vec(6, 24);
  1407. nvgFillColor(vg, nvgTransRGBA(textColor, displayAlpha));
  1408. nvgText(vg, textPos.x, textPos.y, "~~~", NULL);
  1409. nvgFillColor(vg, textColor);
  1410. if (module->infoCopyPaste != 0l) {
  1411. if (module->infoCopyPaste > 0l)
  1412. snprintf(displayStr, 4, "CPY");
  1413. else {
  1414. float cpMode = module->params[PhraseSeq16::CPMODE_PARAM].value;
  1415. if (editingSequence && !module->seqCopied) {// cross paste to seq
  1416. if (cpMode > 1.5f)// All = toggle gate 1
  1417. snprintf(displayStr, 4, "TG1");
  1418. else if (cpMode < 0.5f)// 4 = random CV
  1419. snprintf(displayStr, 4, "RCV");
  1420. else// 8 = random gate 1
  1421. snprintf(displayStr, 4, "RG1");
  1422. }
  1423. else if (!editingSequence && module->seqCopied) {// cross paste to song
  1424. if (cpMode > 1.5f)// All = init
  1425. snprintf(displayStr, 4, "CLR");
  1426. else if (cpMode < 0.5f)// 4 = increase by 1
  1427. snprintf(displayStr, 4, "INC");
  1428. else// 8 = random phrases
  1429. snprintf(displayStr, 4, "RPH");
  1430. }
  1431. else
  1432. snprintf(displayStr, 4, "PST");
  1433. }
  1434. }
  1435. else if (module->editingPpqn != 0ul) {
  1436. snprintf(displayStr, 4, "x%2u", (unsigned) module->pulsesPerStep);
  1437. }
  1438. else if (module->displayState == PhraseSeq16::DISP_MODE) {
  1439. if (editingSequence)
  1440. runModeToStr(module->sequences[module->sequence].getRunMode());
  1441. else
  1442. runModeToStr(module->runModeSong);
  1443. }
  1444. else if (module->displayState == PhraseSeq16::DISP_LENGTH) {
  1445. if (editingSequence)
  1446. snprintf(displayStr, 4, "L%2u", (unsigned) module->sequences[module->sequence].getLength());
  1447. else
  1448. snprintf(displayStr, 4, "L%2u", (unsigned) module->phrases);
  1449. }
  1450. else if (module->displayState == PhraseSeq16::DISP_TRANSPOSE) {
  1451. snprintf(displayStr, 4, "+%2u", (unsigned) abs(module->sequences[module->sequence].getTranspose()));
  1452. if (module->sequences[module->sequence].getTranspose() < 0)
  1453. displayStr[0] = '-';
  1454. }
  1455. else if (module->displayState == PhraseSeq16::DISP_ROTATE) {
  1456. snprintf(displayStr, 4, ")%2u", (unsigned) abs(module->sequences[module->sequence].getRotate()));
  1457. if (module->sequences[module->sequence].getRotate() < 0)
  1458. displayStr[0] = '(';
  1459. }
  1460. else {// DISP_NORMAL
  1461. snprintf(displayStr, 4, " %2u", (unsigned) (editingSequence ?
  1462. module->sequence : module->phrase[module->phraseIndexEdit]) + 1 );
  1463. }
  1464. nvgText(vg, textPos.x, textPos.y, displayStr, NULL);
  1465. }
  1466. };
  1467. struct PanelThemeItem : MenuItem {
  1468. PhraseSeq16 *module;
  1469. int theme;
  1470. void onAction(EventAction &e) override {
  1471. module->panelTheme = theme;
  1472. }
  1473. void step() override {
  1474. rightText = (module->panelTheme == theme) ? "✔" : "";
  1475. }
  1476. };
  1477. struct ExpansionItem : MenuItem {
  1478. PhraseSeq16 *module;
  1479. void onAction(EventAction &e) override {
  1480. module->expansion = module->expansion == 1 ? 0 : 1;
  1481. }
  1482. };
  1483. struct ResetOnRunItem : MenuItem {
  1484. PhraseSeq16 *module;
  1485. void onAction(EventAction &e) override {
  1486. module->resetOnRun = !module->resetOnRun;
  1487. }
  1488. };
  1489. struct AutoStepLenItem : MenuItem {
  1490. PhraseSeq16 *module;
  1491. void onAction(EventAction &e) override {
  1492. module->autostepLen = !module->autostepLen;
  1493. }
  1494. };
  1495. struct AutoseqItem : MenuItem {
  1496. PhraseSeq16 *module;
  1497. void onAction(EventAction &e) override {
  1498. module->autoseq = !module->autoseq;
  1499. }
  1500. };
  1501. struct HoldTiedItem : MenuItem {
  1502. PhraseSeq16 *module;
  1503. void onAction(EventAction &e) override {
  1504. module->holdTiedNotes = !module->holdTiedNotes;
  1505. }
  1506. };
  1507. struct SeqCVmethodItem : MenuItem {
  1508. PhraseSeq16 *module;
  1509. void onAction(EventAction &e) override {
  1510. module->seqCVmethod++;
  1511. if (module->seqCVmethod > 2)
  1512. module->seqCVmethod = 0;
  1513. }
  1514. void step() override {
  1515. if (module->seqCVmethod == 0)
  1516. text = "Seq CV in: <0-10V>, C4-D5#, Trig-Incr";
  1517. else if (module->seqCVmethod == 1)
  1518. text = "Seq CV in: 0-10V, <C4-D5#>, Trig-Incr";
  1519. else
  1520. text = "Seq CV in: 0-10V, C4-D5#, <Trig-Incr>";
  1521. }
  1522. };
  1523. Menu *createContextMenu() override {
  1524. Menu *menu = ModuleWidget::createContextMenu();
  1525. MenuLabel *spacerLabel = new MenuLabel();
  1526. menu->addChild(spacerLabel);
  1527. PhraseSeq16 *module = dynamic_cast<PhraseSeq16*>(this->module);
  1528. assert(module);
  1529. MenuLabel *themeLabel = new MenuLabel();
  1530. themeLabel->text = "Panel Theme";
  1531. menu->addChild(themeLabel);
  1532. PanelThemeItem *lightItem = new PanelThemeItem();
  1533. lightItem->text = lightPanelID;// ImpromptuModular.hpp
  1534. lightItem->module = module;
  1535. lightItem->theme = 0;
  1536. menu->addChild(lightItem);
  1537. PanelThemeItem *darkItem = new PanelThemeItem();
  1538. darkItem->text = darkPanelID;// ImpromptuModular.hpp
  1539. darkItem->module = module;
  1540. darkItem->theme = 1;
  1541. menu->addChild(darkItem);
  1542. menu->addChild(new MenuLabel());// empty line
  1543. MenuLabel *settingsLabel = new MenuLabel();
  1544. settingsLabel->text = "Settings";
  1545. menu->addChild(settingsLabel);
  1546. ResetOnRunItem *rorItem = MenuItem::create<ResetOnRunItem>("Reset on run", CHECKMARK(module->resetOnRun));
  1547. rorItem->module = module;
  1548. menu->addChild(rorItem);
  1549. AutoStepLenItem *astlItem = MenuItem::create<AutoStepLenItem>("AutoStep write bounded by seq length", CHECKMARK(module->autostepLen));
  1550. astlItem->module = module;
  1551. menu->addChild(astlItem);
  1552. AutoseqItem *aseqItem = MenuItem::create<AutoseqItem>("AutoSeq when writing via CV inputs", CHECKMARK(module->autoseq));
  1553. aseqItem->module = module;
  1554. menu->addChild(aseqItem);
  1555. HoldTiedItem *holdItem = MenuItem::create<HoldTiedItem>("Hold tied notes", CHECKMARK(module->holdTiedNotes));
  1556. holdItem->module = module;
  1557. menu->addChild(holdItem);
  1558. SeqCVmethodItem *seqcvItem = MenuItem::create<SeqCVmethodItem>("Seq CV in: ", "");
  1559. seqcvItem->module = module;
  1560. menu->addChild(seqcvItem);
  1561. menu->addChild(new MenuLabel());// empty line
  1562. MenuLabel *expansionLabel = new MenuLabel();
  1563. expansionLabel->text = "Expansion module";
  1564. menu->addChild(expansionLabel);
  1565. ExpansionItem *expItem = MenuItem::create<ExpansionItem>(expansionMenuLabel, CHECKMARK(module->expansion != 0));
  1566. expItem->module = module;
  1567. menu->addChild(expItem);
  1568. return menu;
  1569. }
  1570. void step() override {
  1571. if(module->expansion != oldExpansion) {
  1572. if (oldExpansion!= -1 && module->expansion == 0) {// if just removed expansion panel, disconnect wires to those jacks
  1573. for (int i = 0; i < 5; i++)
  1574. RACK_PLUGIN_UI_RACKWIDGET->wireContainer->removeAllWires(expPorts[i]);
  1575. }
  1576. oldExpansion = module->expansion;
  1577. }
  1578. box.size.x = panel->box.size.x - (1 - module->expansion) * expWidth;
  1579. Widget::step();
  1580. }
  1581. struct SequenceKnob : IMBigKnobInf {
  1582. SequenceKnob() {};
  1583. void onMouseDown(EventMouseDown &e) override {// from ParamWidget.cpp
  1584. PhraseSeq16* module = dynamic_cast<PhraseSeq16*>(this->module);
  1585. if (e.button == 1) {
  1586. // same code structure below as in sequence knob in main step()
  1587. if (module->editingPpqn != 0) {
  1588. module->pulsesPerStep = 1;
  1589. //editingPpqn = (long) (editGateLengthTime * sampleRate / displayRefreshStepSkips);
  1590. }
  1591. else if (module->displayState == PhraseSeq16::DISP_MODE) {
  1592. if (module->isEditingSequence()) {
  1593. if (!module->inputs[PhraseSeq16::MODECV_INPUT].active) {
  1594. module->sequences[module->sequence].setRunMode(MODE_FWD);
  1595. }
  1596. }
  1597. else {
  1598. module->runModeSong = MODE_FWD;
  1599. }
  1600. }
  1601. else if (module->displayState == PhraseSeq16::DISP_LENGTH) {
  1602. if (module->isEditingSequence()) {
  1603. module->sequences[module->sequence].setLength(16);
  1604. }
  1605. else {
  1606. module->phrases = 4;
  1607. }
  1608. }
  1609. else if (module->displayState == PhraseSeq16::DISP_TRANSPOSE) {
  1610. // nothing
  1611. }
  1612. else if (module->displayState == PhraseSeq16::DISP_ROTATE) {
  1613. // nothing
  1614. }
  1615. else {// DISP_NORMAL
  1616. if (module->isEditingSequence()) {
  1617. if (!module->inputs[PhraseSeq16::SEQCV_INPUT].active) {
  1618. module->sequence = 0;
  1619. }
  1620. }
  1621. else {
  1622. module->phrase[module->phraseIndexEdit] = 0;
  1623. }
  1624. }
  1625. }
  1626. ParamWidget::onMouseDown(e);
  1627. }
  1628. };
  1629. // void onHoverKey(EventHoverKey &e) override {// https://www.glfw.org/docs/latest/group__keys.html
  1630. // PhraseSeq16* module = dynamic_cast<PhraseSeq16*>(this->module);
  1631. // if (e.key == GLFW_KEY_SPACE) {
  1632. // if (module->isEditingSequence()) {
  1633. // module->attributes[module->sequence][module->stepIndexEdit].toggleGate1();
  1634. // }
  1635. // e.consumed = true;
  1636. // }
  1637. // else
  1638. // ModuleWidget::onHoverKey(e);
  1639. // }
  1640. PhraseSeq16Widget(PhraseSeq16 *module) : ModuleWidget(module) {
  1641. this->module = module;
  1642. oldExpansion = -1;
  1643. // Main panel from Inkscape
  1644. panel = new DynamicSVGPanel();
  1645. panel->mode = &module->panelTheme;
  1646. panel->expWidth = &expWidth;
  1647. panel->addPanel(SVG::load(assetPlugin(plugin, "res/light/PhraseSeq16.svg")));
  1648. panel->addPanel(SVG::load(assetPlugin(plugin, "res/dark/PhraseSeq16_dark.svg")));
  1649. box.size = panel->box.size;
  1650. box.size.x = box.size.x - (1 - module->expansion) * expWidth;
  1651. addChild(panel);
  1652. // Screws
  1653. addChild(createDynamicScrew<IMScrew>(Vec(15, 0), &module->panelTheme));
  1654. addChild(createDynamicScrew<IMScrew>(Vec(15, 365), &module->panelTheme));
  1655. addChild(createDynamicScrew<IMScrew>(Vec(panel->box.size.x-30, 0), &module->panelTheme));
  1656. addChild(createDynamicScrew<IMScrew>(Vec(panel->box.size.x-30, 365), &module->panelTheme));
  1657. addChild(createDynamicScrew<IMScrew>(Vec(panel->box.size.x-30-expWidth, 0), &module->panelTheme));
  1658. addChild(createDynamicScrew<IMScrew>(Vec(panel->box.size.x-30-expWidth, 365), &module->panelTheme));
  1659. // ****** Top row ******
  1660. static const int rowRulerT0 = 48;
  1661. static const int columnRulerT0 = 18;// Step LED buttons
  1662. static const int columnRulerT3 = 400;// Attach (also used to align rest of right side of module)
  1663. // Step/Phrase LED buttons
  1664. int posX = columnRulerT0;
  1665. static int spacingSteps = 20;
  1666. static int spacingSteps4 = 4;
  1667. for (int x = 0; x < 16; x++) {
  1668. // First row
  1669. addParam(createParam<LEDButton>(Vec(posX, rowRulerT0 - 7 + 3 - 4.4f), module, PhraseSeq16::STEP_PHRASE_PARAMS + x, 0.0f, 1.0f, 0.0f));
  1670. addChild(createLight<MediumLight<GreenRedWhiteLight>>(Vec(posX + 4.4f, rowRulerT0 - 7 + 3), module, PhraseSeq16::STEP_PHRASE_LIGHTS + (x * 3)));
  1671. // step position to next location and handle groups of four
  1672. posX += spacingSteps;
  1673. if ((x + 1) % 4 == 0)
  1674. posX += spacingSteps4;
  1675. }
  1676. // Attach button and light
  1677. addParam(createDynamicParam<IMPushButton>(Vec(columnRulerT3 - 4, rowRulerT0 - 6 + 2 + offsetTL1105), module, PhraseSeq16::ATTACH_PARAM, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  1678. addChild(createLight<MediumLight<RedLight>>(Vec(columnRulerT3 + 12 + offsetMediumLight, rowRulerT0 - 6 + offsetMediumLight), module, PhraseSeq16::ATTACH_LIGHT));
  1679. // Key mode LED buttons
  1680. static const int rowRulerKM = rowRulerT0 + 26;
  1681. static const int colRulerKM = columnRulerT0 + 113;
  1682. addParam(createParam<LEDButton>(Vec(colRulerKM + 112, rowRulerKM - 4.4f), module, PhraseSeq16::KEYNOTE_PARAM, 0.0f, 1.0f, 0.0f));
  1683. addChild(createLight<MediumLight<RedLight>>(Vec(colRulerKM + 112 + 4.4f, rowRulerKM), module, PhraseSeq16::KEYNOTE_LIGHT));
  1684. addParam(createParam<LEDButton>(Vec(colRulerKM, rowRulerKM - 4.4f), module, PhraseSeq16::KEYGATE_PARAM, 0.0f, 1.0f, 0.0f));
  1685. addChild(createLight<MediumLight<GreenRedLight>>(Vec(colRulerKM + 4.4f, rowRulerKM), module, PhraseSeq16::KEYGATE_LIGHT));
  1686. // ****** Octave and keyboard area ******
  1687. // Octave LED buttons
  1688. static const float octLightsIntY = 20.0f;
  1689. for (int i = 0; i < 7; i++) {
  1690. addParam(createParam<LEDButton>(Vec(15 + 3, 82 + 24 + i * octLightsIntY- 4.4f), module, PhraseSeq16::OCTAVE_PARAM + i, 0.0f, 1.0f, 0.0f));
  1691. addChild(createLight<MediumLight<RedLight>>(Vec(15 + 3 + 4.4f, 82 + 24 + i * octLightsIntY), module, PhraseSeq16::OCTAVE_LIGHTS + i));
  1692. }
  1693. // Keys and Key lights
  1694. static const int keyNudgeX = 7;
  1695. static const int KeyBlackY = 103;
  1696. static const int KeyWhiteY = 141;
  1697. static const int offsetKeyLEDx = 6;
  1698. static const int offsetKeyLEDy = 16;
  1699. // Black keys and lights
  1700. addParam(createParam<InvisibleKeySmall>( Vec(65+keyNudgeX, KeyBlackY), module, PhraseSeq16::KEY_PARAMS + 1, 0.0, 1.0, 0.0));
  1701. addChild(createLight<MediumLight<GreenRedLight>>(Vec(65+keyNudgeX+offsetKeyLEDx, KeyBlackY+offsetKeyLEDy), module, PhraseSeq16::KEY_LIGHTS + 1 * 2));
  1702. addParam(createParam<InvisibleKeySmall>( Vec(93+keyNudgeX, KeyBlackY), module, PhraseSeq16::KEY_PARAMS + 3, 0.0, 1.0, 0.0));
  1703. addChild(createLight<MediumLight<GreenRedLight>>(Vec(93+keyNudgeX+offsetKeyLEDx, KeyBlackY+offsetKeyLEDy), module, PhraseSeq16::KEY_LIGHTS + 3 * 2));
  1704. addParam(createParam<InvisibleKeySmall>( Vec(150+keyNudgeX, KeyBlackY), module, PhraseSeq16::KEY_PARAMS + 6, 0.0, 1.0, 0.0));
  1705. addChild(createLight<MediumLight<GreenRedLight>>(Vec(150+keyNudgeX+offsetKeyLEDx, KeyBlackY+offsetKeyLEDy), module, PhraseSeq16::KEY_LIGHTS + 6 * 2));
  1706. addParam(createParam<InvisibleKeySmall>( Vec(178+keyNudgeX, KeyBlackY), module, PhraseSeq16::KEY_PARAMS + 8, 0.0, 1.0, 0.0));
  1707. addChild(createLight<MediumLight<GreenRedLight>>(Vec(178+keyNudgeX+offsetKeyLEDx, KeyBlackY+offsetKeyLEDy), module, PhraseSeq16::KEY_LIGHTS + 8 * 2));
  1708. addParam(createParam<InvisibleKeySmall>( Vec(206+keyNudgeX, KeyBlackY), module, PhraseSeq16::KEY_PARAMS + 10, 0.0, 1.0, 0.0));
  1709. addChild(createLight<MediumLight<GreenRedLight>>(Vec(206+keyNudgeX+offsetKeyLEDx, KeyBlackY+offsetKeyLEDy), module, PhraseSeq16::KEY_LIGHTS + 10 * 2));
  1710. // White keys and lights
  1711. addParam(createParam<InvisibleKeySmall>( Vec(51+keyNudgeX, KeyWhiteY), module, PhraseSeq16::KEY_PARAMS + 0, 0.0, 1.0, 0.0));
  1712. addChild(createLight<MediumLight<GreenRedLight>>(Vec(51+keyNudgeX+offsetKeyLEDx, KeyWhiteY+offsetKeyLEDy), module, PhraseSeq16::KEY_LIGHTS + 0 * 2));
  1713. addParam(createParam<InvisibleKeySmall>( Vec(79+keyNudgeX, KeyWhiteY), module, PhraseSeq16::KEY_PARAMS + 2, 0.0, 1.0, 0.0));
  1714. addChild(createLight<MediumLight<GreenRedLight>>(Vec(79+keyNudgeX+offsetKeyLEDx, KeyWhiteY+offsetKeyLEDy), module, PhraseSeq16::KEY_LIGHTS + 2 * 2));
  1715. addParam(createParam<InvisibleKeySmall>( Vec(107+keyNudgeX, KeyWhiteY), module, PhraseSeq16::KEY_PARAMS + 4, 0.0, 1.0, 0.0));
  1716. addChild(createLight<MediumLight<GreenRedLight>>(Vec(107+keyNudgeX+offsetKeyLEDx, KeyWhiteY+offsetKeyLEDy), module, PhraseSeq16::KEY_LIGHTS + 4 * 2));
  1717. addParam(createParam<InvisibleKeySmall>( Vec(136+keyNudgeX, KeyWhiteY), module, PhraseSeq16::KEY_PARAMS + 5, 0.0, 1.0, 0.0));
  1718. addChild(createLight<MediumLight<GreenRedLight>>(Vec(136+keyNudgeX+offsetKeyLEDx, KeyWhiteY+offsetKeyLEDy), module, PhraseSeq16::KEY_LIGHTS + 5 * 2));
  1719. addParam(createParam<InvisibleKeySmall>( Vec(164+keyNudgeX, KeyWhiteY), module, PhraseSeq16::KEY_PARAMS + 7, 0.0, 1.0, 0.0));
  1720. addChild(createLight<MediumLight<GreenRedLight>>(Vec(164+keyNudgeX+offsetKeyLEDx, KeyWhiteY+offsetKeyLEDy), module, PhraseSeq16::KEY_LIGHTS + 7 * 2));
  1721. addParam(createParam<InvisibleKeySmall>( Vec(192+keyNudgeX, KeyWhiteY), module, PhraseSeq16::KEY_PARAMS + 9, 0.0, 1.0, 0.0));
  1722. addChild(createLight<MediumLight<GreenRedLight>>(Vec(192+keyNudgeX+offsetKeyLEDx, KeyWhiteY+offsetKeyLEDy), module, PhraseSeq16::KEY_LIGHTS + 9 * 2));
  1723. addParam(createParam<InvisibleKeySmall>( Vec(220+keyNudgeX, KeyWhiteY), module, PhraseSeq16::KEY_PARAMS + 11, 0.0, 1.0, 0.0));
  1724. addChild(createLight<MediumLight<GreenRedLight>>(Vec(220+keyNudgeX+offsetKeyLEDx, KeyWhiteY+offsetKeyLEDy), module, PhraseSeq16::KEY_LIGHTS + 11 * 2));
  1725. // ****** Right side control area ******
  1726. static const int rowRulerMK0 = 101;// Edit mode row
  1727. static const int rowRulerMK1 = rowRulerMK0 + 56; // Run row
  1728. static const int rowRulerMK2 = rowRulerMK1 + 54; // Reset row
  1729. static const int columnRulerMK0 = 277;// Edit mode column
  1730. static const int columnRulerMK1 = columnRulerMK0 + 59;// Display column
  1731. static const int columnRulerMK2 = columnRulerT3;// Run mode column
  1732. // Edit mode switch
  1733. addParam(createParam<CKSSNoRandom>(Vec(columnRulerMK0 + hOffsetCKSS, rowRulerMK0 + vOffsetCKSS), module, PhraseSeq16::EDIT_PARAM, 0.0f, 1.0f, 1.0f));
  1734. // Sequence display
  1735. SequenceDisplayWidget *displaySequence = new SequenceDisplayWidget();
  1736. displaySequence->box.pos = Vec(columnRulerMK1-15, rowRulerMK0 + 3 + vOffsetDisplay);
  1737. displaySequence->box.size = Vec(55, 30);// 3 characters
  1738. displaySequence->module = module;
  1739. addChild(displaySequence);
  1740. // Len/mode button
  1741. addParam(createDynamicParam<IMBigPushButton>(Vec(columnRulerMK2 + offsetCKD6b, rowRulerMK0 + 0 + offsetCKD6b), module, PhraseSeq16::RUNMODE_PARAM, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  1742. // Run LED bezel and light
  1743. addParam(createParam<LEDBezel>(Vec(columnRulerMK0 + offsetLEDbezel, rowRulerMK1 + 7 + offsetLEDbezel), module, PhraseSeq16::RUN_PARAM, 0.0f, 1.0f, 0.0f));
  1744. addChild(createLight<MuteLight<GreenLight>>(Vec(columnRulerMK0 + offsetLEDbezel + offsetLEDbezelLight, rowRulerMK1 + 7 + offsetLEDbezel + offsetLEDbezelLight), module, PhraseSeq16::RUN_LIGHT));
  1745. // Sequence knob
  1746. addParam(createDynamicParam<SequenceKnob>(Vec(columnRulerMK1 + 1 + offsetIMBigKnob, rowRulerMK0 + 55 + offsetIMBigKnob), module, PhraseSeq16::SEQUENCE_PARAM, -INFINITY, INFINITY, 0.0f, &module->panelTheme));
  1747. // Transpose/rotate button
  1748. addParam(createDynamicParam<IMBigPushButton>(Vec(columnRulerMK2 + offsetCKD6b, rowRulerMK1 + 4 + offsetCKD6b), module, PhraseSeq16::TRAN_ROT_PARAM, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  1749. // Reset LED bezel and light
  1750. addParam(createParam<LEDBezel>(Vec(columnRulerMK0 + offsetLEDbezel, rowRulerMK2 + 5 + offsetLEDbezel), module, PhraseSeq16::RESET_PARAM, 0.0f, 1.0f, 0.0f));
  1751. addChild(createLight<MuteLight<GreenLight>>(Vec(columnRulerMK0 + offsetLEDbezel + offsetLEDbezelLight, rowRulerMK2 + 5 + offsetLEDbezel + offsetLEDbezelLight), module, PhraseSeq16::RESET_LIGHT));
  1752. // Copy/paste buttons
  1753. addParam(createDynamicParam<IMPushButton>(Vec(columnRulerMK1 - 10, rowRulerMK2 + 5 + offsetTL1105), module, PhraseSeq16::COPY_PARAM, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  1754. addParam(createDynamicParam<IMPushButton>(Vec(columnRulerMK1 + 20, rowRulerMK2 + 5 + offsetTL1105), module, PhraseSeq16::PASTE_PARAM, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  1755. // Copy-paste mode switch (3 position)
  1756. addParam(createParam<CKSSThreeInvNoRandom>(Vec(columnRulerMK2 + hOffsetCKSS + 1, rowRulerMK2 - 3 + vOffsetCKSSThree), module, PhraseSeq16::CPMODE_PARAM, 0.0f, 2.0f, 2.0f)); // 0.0f is top position
  1757. // ****** Gate and slide section ******
  1758. static const int rowRulerMB0 = 214;
  1759. static const int columnRulerMBspacing = 70;
  1760. static const int columnRulerMB2 = 130;// Gate2
  1761. static const int columnRulerMB1 = columnRulerMB2 - columnRulerMBspacing;// Gate1
  1762. static const int columnRulerMB3 = columnRulerMB2 + columnRulerMBspacing;// Tie
  1763. static const int posLEDvsButton = + 25;
  1764. // Gate 1 light and button
  1765. addChild(createLight<MediumLight<GreenRedLight>>(Vec(columnRulerMB1 + posLEDvsButton + offsetMediumLight, rowRulerMB0 + 4 + offsetMediumLight), module, PhraseSeq16::GATE1_LIGHT));
  1766. addParam(createDynamicParam<IMBigPushButton>(Vec(columnRulerMB1 + offsetCKD6b, rowRulerMB0 + 4 + offsetCKD6b), module, PhraseSeq16::GATE1_PARAM, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  1767. // Gate 2 light and button
  1768. addChild(createLight<MediumLight<GreenRedLight>>(Vec(columnRulerMB2 + posLEDvsButton + offsetMediumLight, rowRulerMB0 + 4 + offsetMediumLight), module, PhraseSeq16::GATE2_LIGHT));
  1769. addParam(createDynamicParam<IMBigPushButton>(Vec(columnRulerMB2 + offsetCKD6b, rowRulerMB0 + 4 + offsetCKD6b), module, PhraseSeq16::GATE2_PARAM, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  1770. // Tie light and button
  1771. addChild(createLight<MediumLight<RedLight>>(Vec(columnRulerMB3 + posLEDvsButton + offsetMediumLight, rowRulerMB0 + 4 + offsetMediumLight), module, PhraseSeq16::TIE_LIGHT));
  1772. addParam(createDynamicParam<IMBigPushButton>(Vec(columnRulerMB3 + offsetCKD6b, rowRulerMB0 + 4 + offsetCKD6b), module, PhraseSeq16::TIE_PARAM, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  1773. // ****** Bottom two rows ******
  1774. static const int outputJackSpacingX = 54;
  1775. static const int rowRulerB0 = 323;
  1776. static const int rowRulerB1 = 269;
  1777. static const int columnRulerB0 = 22;
  1778. static const int columnRulerB1 = columnRulerB0 + outputJackSpacingX;
  1779. static const int columnRulerB2 = columnRulerB1 + outputJackSpacingX;
  1780. static const int columnRulerB3 = columnRulerB2 + outputJackSpacingX;
  1781. static const int columnRulerB4 = columnRulerB3 + outputJackSpacingX;
  1782. static const int columnRulerB7 = columnRulerMK2 + 1;
  1783. static const int columnRulerB6 = columnRulerB7 - outputJackSpacingX;
  1784. static const int columnRulerB5 = columnRulerB6 - outputJackSpacingX;
  1785. // Gate 1 probability light and button
  1786. addChild(createLight<MediumLight<GreenRedLight>>(Vec(columnRulerB0 + posLEDvsButton + offsetMediumLight, rowRulerB1 + offsetMediumLight), module, PhraseSeq16::GATE1_PROB_LIGHT));
  1787. addParam(createDynamicParam<IMBigPushButton>(Vec(columnRulerB0 + offsetCKD6b, rowRulerB1 + offsetCKD6b), module, PhraseSeq16::GATE1_PROB_PARAM, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  1788. // Gate 1 probability knob
  1789. addParam(createDynamicParam<IMSmallKnob>(Vec(columnRulerB1 + offsetIMSmallKnob, rowRulerB1 + offsetIMSmallKnob), module, PhraseSeq16::GATE1_KNOB_PARAM, 0.0f, 1.0f, 1.0f, &module->panelTheme));
  1790. // Slide light and button
  1791. addChild(createLight<MediumLight<RedLight>>(Vec(columnRulerB2 + posLEDvsButton + offsetMediumLight, rowRulerB1 + offsetMediumLight), module, PhraseSeq16::SLIDE_LIGHT));
  1792. addParam(createDynamicParam<IMBigPushButton>(Vec(columnRulerB2 + offsetCKD6b, rowRulerB1 + offsetCKD6b), module, PhraseSeq16::SLIDE_BTN_PARAM, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  1793. // Slide knob
  1794. addParam(createDynamicParam<IMSmallKnob>(Vec(columnRulerB3 + offsetIMSmallKnob, rowRulerB1 + offsetIMSmallKnob), module, PhraseSeq16::SLIDE_KNOB_PARAM, 0.0f, 2.0f, 0.2f, &module->panelTheme));
  1795. // Autostep
  1796. addParam(createParam<CKSSNoRandom>(Vec(columnRulerB4 + hOffsetCKSS, rowRulerB1 + vOffsetCKSS), module, PhraseSeq16::AUTOSTEP_PARAM, 0.0f, 1.0f, 1.0f));
  1797. // CV in
  1798. addInput(createDynamicPort<IMPort>(Vec(columnRulerB5, rowRulerB1), Port::INPUT, module, PhraseSeq16::CV_INPUT, &module->panelTheme));
  1799. // Clock
  1800. addInput(createDynamicPort<IMPort>(Vec(columnRulerB6, rowRulerB1), Port::INPUT, module, PhraseSeq16::CLOCK_INPUT, &module->panelTheme));
  1801. // Reset
  1802. addInput(createDynamicPort<IMPort>(Vec(columnRulerB7, rowRulerB1), Port::INPUT, module, PhraseSeq16::RESET_INPUT, &module->panelTheme));
  1803. // ****** Bottom row (all aligned) ******
  1804. // CV control Inputs
  1805. addInput(createDynamicPort<IMPort>(Vec(columnRulerB0, rowRulerB0), Port::INPUT, module, PhraseSeq16::LEFTCV_INPUT, &module->panelTheme));
  1806. addInput(createDynamicPort<IMPort>(Vec(columnRulerB1, rowRulerB0), Port::INPUT, module, PhraseSeq16::RIGHTCV_INPUT, &module->panelTheme));
  1807. addInput(createDynamicPort<IMPort>(Vec(columnRulerB2, rowRulerB0), Port::INPUT, module, PhraseSeq16::SEQCV_INPUT, &module->panelTheme));
  1808. addInput(createDynamicPort<IMPort>(Vec(columnRulerB3, rowRulerB0), Port::INPUT, module, PhraseSeq16::RUNCV_INPUT, &module->panelTheme));
  1809. addInput(createDynamicPort<IMPort>(Vec(columnRulerB4, rowRulerB0), Port::INPUT, module, PhraseSeq16::WRITE_INPUT, &module->panelTheme));
  1810. // Outputs
  1811. addOutput(createDynamicPort<IMPort>(Vec(columnRulerB5, rowRulerB0), Port::OUTPUT, module, PhraseSeq16::CV_OUTPUT, &module->panelTheme));
  1812. addOutput(createDynamicPort<IMPort>(Vec(columnRulerB6, rowRulerB0), Port::OUTPUT, module, PhraseSeq16::GATE1_OUTPUT, &module->panelTheme));
  1813. addOutput(createDynamicPort<IMPort>(Vec(columnRulerB7, rowRulerB0), Port::OUTPUT, module, PhraseSeq16::GATE2_OUTPUT, &module->panelTheme));
  1814. // Expansion module
  1815. static const int rowRulerExpTop = 65;
  1816. static const int rowSpacingExp = 60;
  1817. static const int colRulerExp = 497 - 30;// PS16 is 2HP less than PS32
  1818. addInput(expPorts[0] = createDynamicPort<IMPort>(Vec(colRulerExp, rowRulerExpTop + rowSpacingExp * 0), Port::INPUT, module, PhraseSeq16::GATE1CV_INPUT, &module->panelTheme));
  1819. addInput(expPorts[1] = createDynamicPort<IMPort>(Vec(colRulerExp, rowRulerExpTop + rowSpacingExp * 1), Port::INPUT, module, PhraseSeq16::GATE2CV_INPUT, &module->panelTheme));
  1820. addInput(expPorts[2] = createDynamicPort<IMPort>(Vec(colRulerExp, rowRulerExpTop + rowSpacingExp * 2), Port::INPUT, module, PhraseSeq16::TIEDCV_INPUT, &module->panelTheme));
  1821. addInput(expPorts[3] = createDynamicPort<IMPort>(Vec(colRulerExp, rowRulerExpTop + rowSpacingExp * 3), Port::INPUT, module, PhraseSeq16::SLIDECV_INPUT, &module->panelTheme));
  1822. addInput(expPorts[4] = createDynamicPort<IMPort>(Vec(colRulerExp, rowRulerExpTop + rowSpacingExp * 4), Port::INPUT, module, PhraseSeq16::MODECV_INPUT, &module->panelTheme));
  1823. }
  1824. };
  1825. } // namespace rack_plugin_ImpromptuModular
  1826. using namespace rack_plugin_ImpromptuModular;
  1827. RACK_PLUGIN_MODEL_INIT(ImpromptuModular, PhraseSeq16) {
  1828. Model *modelPhraseSeq16 = Model::create<PhraseSeq16, PhraseSeq16Widget>("Impromptu Modular", "Phrase-Seq-16", "SEQ - Phrase-Seq-16", SEQUENCER_TAG);
  1829. return modelPhraseSeq16;
  1830. }
  1831. /*CHANGE LOG
  1832. 0.6.16:
  1833. add gate status feedback in steps (white lights)
  1834. 0.6.15:
  1835. add right-click menu option to bound AutoStep writes by sequence lengths
  1836. 0.6.14:
  1837. rotate offsets are now persistent and stored in the sequencer
  1838. allow ctrl-right-click of notes to copy note/gate-type over to next step (not just move to next step)
  1839. 0.6.13:
  1840. fix run mode bug (history not reset when hard reset)
  1841. fix slide bug when reset happens during a slide and run stays on
  1842. add live mute on Gate1 and Gate2 buttons in song mode
  1843. fix initRun() timing bug when turn off-and-then-on running button (it was resetting ppqnCount)
  1844. allow pulsesPerStep setting of 1 and all even values from 2 to 24, and allow all gate types that work in these
  1845. add two extra modes for Seq CV input (right-click menu): note-voltage-levels and trigger-increment
  1846. fix tied bug that prevented correct tied propagation when editing beyond sequence length less than 16
  1847. implement held tied notes option
  1848. clear all attributes (gates, gatep, tied, slide) when cross-paste to seq ALL (CVs not affected)
  1849. implement right-click initialization on main knob
  1850. 0.6.12:
  1851. input refresh optimization
  1852. add buttons for note vs advanced-gate selection (remove timeout method)
  1853. transposition amount stays persistent and is saved (reset to 0 on module init or paste ALL)
  1854. 0.6.11:
  1855. step optimization of lights refresh
  1856. change behavior of extra CV inputs (Gate1, Gate2, Tied, Slide), such that they act when triggered and not when write
  1857. implement copy-paste in song mode
  1858. implement cross paste trick for init and randomize seq/song
  1859. remove length and arrow buttons and make steps with LED buttons
  1860. add AutoSeq option when writing via CV inputs
  1861. 0.6.10:
  1862. add advanced gate mode
  1863. unlock gates when tied (turn off when press tied, but allow to be turned back on)
  1864. allow main knob to also change length when length editing is active
  1865. 0.6.9:
  1866. add FW2, FW3 and FW4 run modes for sequences (but not for song)
  1867. right click on notes now does same as left click but with autostep
  1868. 0.6.8:
  1869. show length in display when editing length
  1870. 0.6.7:
  1871. allow full edit capabilities in Seq and song mode
  1872. no reset on run by default, with switch added in context menu
  1873. reset does not revert seq or song number to 1
  1874. gate 2 is off by default
  1875. fix emitted monitoring gates to depend on gate states instead of always triggering
  1876. 0.6.6:
  1877. knob bug fixes when loading patch
  1878. 0.6.5:
  1879. paste 4, 8 doesn't loop over to overwrite first steps
  1880. small adjustements to gates and CVs used in monitoring write operations
  1881. add GATE1, GATE2, TIED, SLIDE CV inputs
  1882. add MODE CV input (affects only selected sequence and in Seq mode)
  1883. add expansion panel option
  1884. 0.6.4:
  1885. turn off keyboard and oct lights when detached in song mode (makes no sense, can't mod steps and shows stepping though seq that may not be playing)
  1886. removed mode CV input, added paste 4/8/ALL option (ALL copies length and run mode also)
  1887. allow each sequence to have its own length and run mode
  1888. merged functionalities of transpose and rotate into one knob
  1889. implemented tied notes state bit for each step, and added light to tied steps
  1890. implemented 0-T slide as opposed to 0-2s slide, where T is clock period
  1891. changed copy-paste indication, now uses display rather than keyboard lights
  1892. 0.6.3:
  1893. added tie step macro button
  1894. added gate probabilities (one prob setting for all steps)
  1895. removed paste-sync
  1896. 0.6.2:
  1897. initial release of PS16
  1898. */