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.

2184 lines
85KB

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