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.

1713 lines
67KB

  1. //***********************************************************************************************
  2. //Multi-phrase 16 step sequencer module for VCV Rack by Marc Boulé
  3. //
  4. //Based on code from the Fundamental and AudibleInstruments plugins by Andrew Belt
  5. //and graphics from the Component Library by Wes Milholen
  6. //See ./LICENSE.txt for all licenses
  7. //See ./res/fonts/ for font licenses
  8. //
  9. //Module inspired by the SA-100 Stepper Acid sequencer by Transistor Sounds Labs
  10. //
  11. //Acknowledgements: please see README.md
  12. //***********************************************************************************************
  13. #include "ImpromptuModular.hpp"
  14. #include "PhraseSeqUtil.hpp"
  15. namespace rack_plugin_ImpromptuModular {
  16. struct PhraseSeq16 : Module {
  17. enum ParamIds {
  18. LEFT_PARAM,
  19. RIGHT_PARAM,
  20. LENGTH_PARAM,
  21. EDIT_PARAM,
  22. SEQUENCE_PARAM,
  23. RUN_PARAM,
  24. COPY_PARAM,
  25. PASTE_PARAM,
  26. RESET_PARAM,
  27. ENUMS(OCTAVE_PARAM, 7),
  28. GATE1_PARAM,
  29. GATE2_PARAM,
  30. SLIDE_BTN_PARAM,
  31. SLIDE_KNOB_PARAM,
  32. ATTACH_PARAM,
  33. ROTATEL_PARAM,// no longer used
  34. ROTATER_PARAM,// no longer used
  35. PASTESYNC_PARAM,// no longer used
  36. AUTOSTEP_PARAM,
  37. ENUMS(KEY_PARAMS, 12),
  38. TRANSPOSEU_PARAM,// no longer used
  39. TRANSPOSED_PARAM,// no longer used
  40. // -- 0.6.2 ^^
  41. RUNMODE_PARAM,
  42. TRAN_ROT_PARAM,
  43. ROTATE_PARAM,//no longer used
  44. GATE1_KNOB_PARAM,
  45. GATE2_KNOB_PARAM,// no longer used
  46. GATE1_PROB_PARAM,
  47. TIE_PARAM,// Legato
  48. // -- 0.6.3 ^^
  49. CPMODE_PARAM,
  50. // -- 0.6.4 ^^
  51. NUM_PARAMS
  52. };
  53. enum InputIds {
  54. WRITE_INPUT,
  55. CV_INPUT,
  56. RESET_INPUT,
  57. CLOCK_INPUT,
  58. // -- 0.6.2 ^^
  59. LEFTCV_INPUT,
  60. RIGHTCV_INPUT,
  61. RUNCV_INPUT,
  62. SEQCV_INPUT,
  63. MODECV_INPUT,
  64. // -- 0.6.3 ^^
  65. // -- 0.6.4 ^^
  66. GATE1CV_INPUT,
  67. GATE2CV_INPUT,
  68. TIEDCV_INPUT,
  69. SLIDECV_INPUT,
  70. NUM_INPUTS
  71. };
  72. enum OutputIds {
  73. CV_OUTPUT,
  74. GATE1_OUTPUT,
  75. GATE2_OUTPUT,
  76. // -- 0.6.2 ^^
  77. // -- 0.6.3 ^^
  78. NUM_OUTPUTS
  79. };
  80. enum LightIds {
  81. ENUMS(STEP_PHRASE_LIGHTS, 16 * 2),// room for GreenRed
  82. ENUMS(OCTAVE_LIGHTS, 7),// octaves 1 to 7
  83. ENUMS(KEY_LIGHTS, 12 * 2),// room for GreenRed
  84. RUN_LIGHT,
  85. RESET_LIGHT,
  86. ENUMS(GATE1_LIGHT, 2),// room for GreenRed
  87. ENUMS(GATE2_LIGHT, 2),// room for GreenRed
  88. SLIDE_LIGHT,
  89. ATTACH_LIGHT,
  90. PENDING_LIGHT,// no longer used
  91. // -- 0.6.2 ^^
  92. GATE1_PROB_LIGHT,
  93. // -- 0.6.3 ^^
  94. TIE_LIGHT,
  95. NUM_LIGHTS
  96. };
  97. enum DisplayStateIds {DISP_NORMAL, DISP_MODE, DISP_TRANSPOSE, DISP_ROTATE};
  98. // Need to save
  99. int panelTheme = 0;
  100. int expansion = 0;
  101. int pulsesPerStep;// 1 means normal gate mode, alt choices are 4, 6, 12, 24 PPS (Pulses per step)
  102. bool running;
  103. int runModeSeq[16];
  104. int runModeSong;
  105. //
  106. int sequence;
  107. int lengths[16];//1 to 16
  108. //
  109. int phrase[16];// This is the song (series of phases; a phrase is a patten number)
  110. int phrases;//1 to 16
  111. //
  112. float cv[16][16];// [-3.0 : 3.917]. First index is patten number, 2nd index is step
  113. int attributes[16][16];// First index is patten number, 2nd index is step (see enum AttributeBitMasks for details)
  114. //
  115. bool resetOnRun;
  116. bool attached;
  117. // No need to save
  118. float resetLight = 0.0f;
  119. int stepIndexEdit;
  120. int stepIndexRun;
  121. int phraseIndexEdit;
  122. int phraseIndexRun;
  123. unsigned long editingLength;// 0 when not editing length, downward step counter timer when editing length
  124. long infoCopyPaste;// 0 when no info, positive downward step counter timer when copy, negative upward when paste
  125. unsigned long editingGate;// 0 when no edit gate, downward step counter timer when edit gate
  126. float editingGateCV;// no need to initialize, this is a companion to editingGate (output this only when editingGate > 0)
  127. int editingGateKeyLight;// no need to initialize, this is a companion to editingGate (use this only when editingGate > 0)
  128. int stepIndexRunHistory;// no need to initialize
  129. int phraseIndexRunHistory;// no need to initialize
  130. int displayState;
  131. unsigned long slideStepsRemain;// 0 when no slide under way, downward step counter when sliding
  132. float slideCVdelta;// no need to initialize, this is a companion to slideStepsRemain
  133. float cvCPbuffer[16];// copy paste buffer for CVs
  134. int attributesCPbuffer[16];// copy paste buffer for attributes
  135. int lengthCPbuffer;
  136. int modeCPbuffer;
  137. int countCP;// number of steps to paste (in case CPMODE_PARAM changes between copy and paste)
  138. int transposeOffset;// no need to initialize, this is companion to displayMode = DISP_TRANSPOSE
  139. int rotateOffset;// no need to initialize, this is companion to displayMode = DISP_ROTATE
  140. long clockIgnoreOnReset;
  141. const float clockIgnoreOnResetDuration = 0.001f;// disable clock on powerup and reset for 1 ms (so that the first step plays)
  142. unsigned long clockPeriod;// counts number of step() calls upward from last clock (reset after clock processed)
  143. long tiedWarning;// 0 when no warning, positive downward step counter timer when warning
  144. int sequenceKnob = 0;
  145. int gate1Code;
  146. int gate2Code;
  147. long editingGateLength;// 0 when no info, positive downward step counter timer when gate1, negative upward when gate2
  148. long editGateLengthTimeInitMult;// multiplier for extended setting of advanced gates
  149. long editingPpqn;// 0 when no info, positive downward step counter timer when editing ppqn
  150. int ppqnCount;
  151. int lightRefreshCounter;
  152. static constexpr float EDIT_PARAM_INIT_VALUE = 1.0f;// so that module constructor is coherent with widget initialization, since module created before widget
  153. bool editingSequence;
  154. bool editingSequenceLast;
  155. SchmittTrigger resetTrigger;
  156. SchmittTrigger leftTrigger;
  157. SchmittTrigger rightTrigger;
  158. SchmittTrigger runningTrigger;
  159. SchmittTrigger clockTrigger;
  160. SchmittTrigger octTriggers[7];
  161. SchmittTrigger octmTrigger;
  162. SchmittTrigger gate1Trigger;
  163. SchmittTrigger gate1ProbTrigger;
  164. SchmittTrigger gate2Trigger;
  165. SchmittTrigger slideTrigger;
  166. SchmittTrigger lengthTrigger;
  167. SchmittTrigger keyTriggers[12];
  168. SchmittTrigger writeTrigger;
  169. SchmittTrigger attachedTrigger;
  170. SchmittTrigger copyTrigger;
  171. SchmittTrigger pasteTrigger;
  172. SchmittTrigger modeTrigger;
  173. SchmittTrigger rotateTrigger;
  174. SchmittTrigger transposeTrigger;
  175. SchmittTrigger tiedTrigger;
  176. HoldDetect modeHoldDetect;
  177. HoldDetect gate1HoldDetect;
  178. HoldDetect gate2HoldDetect;
  179. inline bool isEditingSequence(void) {return params[EDIT_PARAM].value > 0.5f;}
  180. inline bool getGate1(int seq, int step) {return getGate1a(attributes[seq][step]);}
  181. inline bool getGate1P(int seq, int step) {return getGate1Pa(attributes[seq][step]);}
  182. inline bool getGate2(int seq, int step) {return getGate2a(attributes[seq][step]);}
  183. inline bool getSlide(int seq, int step) {return getSlideA(attributes[seq][step]);}
  184. inline bool getTied(int seq, int step) {return getTiedA(attributes[seq][step]);}
  185. inline int getGate1Mode(int seq, int step) {return getGate1aMode(attributes[seq][step]);}
  186. inline int getGate2Mode(int seq, int step) {return getGate2aMode(attributes[seq][step]);}
  187. inline void setGate1Mode(int seq, int step, int gateMode) {attributes[seq][step] &= ~ATT_MSK_GATE1MODE; attributes[seq][step] |= (gateMode << gate1ModeShift);}
  188. inline void setGate2Mode(int seq, int step, int gateMode) {attributes[seq][step] &= ~ATT_MSK_GATE2MODE; attributes[seq][step] |= (gateMode << gate2ModeShift);}
  189. PhraseSeq16() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  190. onReset();
  191. }
  192. void onReset() override {
  193. pulsesPerStep = 1;
  194. running = false;
  195. runModeSong = MODE_FWD;
  196. stepIndexEdit = 0;
  197. phraseIndexEdit = 0;
  198. sequence = 0;
  199. phrases = 4;
  200. for (int i = 0; i < 16; i++) {
  201. for (int s = 0; s < 16; s++) {
  202. cv[i][s] = 0.0f;
  203. attributes[i][s] = ATT_MSK_GATE1;
  204. }
  205. runModeSeq[i] = MODE_FWD;
  206. phrase[i] = 0;
  207. lengths[i] = 16;
  208. cvCPbuffer[i] = 0.0f;
  209. attributesCPbuffer[i] = ATT_MSK_GATE1;
  210. }
  211. initRun(true);
  212. lengthCPbuffer = 16;
  213. modeCPbuffer = MODE_FWD;
  214. countCP = 16;
  215. editingLength = 0ul;
  216. editingGate = 0ul;
  217. infoCopyPaste = 0l;
  218. displayState = DISP_NORMAL;
  219. slideStepsRemain = 0ul;
  220. attached = true;
  221. clockPeriod = 0ul;
  222. tiedWarning = 0ul;
  223. editingSequence = EDIT_PARAM_INIT_VALUE > 0.5f;
  224. editingSequenceLast = editingSequence;
  225. resetOnRun = false;
  226. modeHoldDetect.reset();
  227. gate1HoldDetect.reset();
  228. gate2HoldDetect.reset();
  229. editGateLengthTimeInitMult = 1l;
  230. editingPpqn = 0l;
  231. lightRefreshCounter = 0;
  232. }
  233. void onRandomize() override {
  234. running = false;
  235. runModeSong = randomu32() % 5;
  236. stepIndexEdit = 0;
  237. phraseIndexEdit = 0;
  238. sequence = randomu32() % 16;
  239. phrases = 1 + (randomu32() % 16);
  240. for (int i = 0; i < 16; i++) {
  241. for (int s = 0; s < 16; s++) {
  242. cv[i][s] = ((float)(randomu32() % 7)) + ((float)(randomu32() % 12)) / 12.0f - 3.0f;
  243. attributes[i][s] = randomu32() & 0x1FFF;// 5 bit for normal attributes + 2 * 4 bits for advanced gate modes
  244. if (getTied(i,s)) {
  245. attributes[i][s] = ATT_MSK_TIED;// clear other attributes if tied
  246. applyTiedStep(i, s, lengths[i]);
  247. }
  248. }
  249. runModeSeq[i] = randomu32() % NUM_MODES;
  250. phrase[i] = randomu32() % 16;
  251. lengths[i] = 1 + (randomu32() % 16);
  252. cvCPbuffer[i] = 0.0f;
  253. attributesCPbuffer[i] = ATT_MSK_GATE1;
  254. }
  255. initRun(true);
  256. lengthCPbuffer = 16;
  257. modeCPbuffer = MODE_FWD;
  258. countCP = 16;
  259. editingLength = 0ul;
  260. editingGate = 0ul;
  261. infoCopyPaste = 0l;
  262. displayState = DISP_NORMAL;
  263. slideStepsRemain = 0ul;
  264. attached = true;
  265. clockPeriod = 0ul;
  266. tiedWarning = 0ul;
  267. editingSequence = isEditingSequence();
  268. editingSequenceLast = editingSequence;
  269. resetOnRun = false;
  270. modeHoldDetect.reset();
  271. gate1HoldDetect.reset();
  272. gate2HoldDetect.reset();
  273. editGateLengthTimeInitMult = 1l;
  274. editingPpqn = 0l;
  275. }
  276. void initRun(bool hard) {// run button activated or run edge in run input jack or edit mode toggled
  277. if (hard)
  278. phraseIndexRun = (runModeSong == MODE_REV ? phrases - 1 : 0);
  279. int seq = (editingSequence ? sequence : phrase[phraseIndexRun]);
  280. if (hard)
  281. stepIndexRun = (runModeSeq[seq] == MODE_REV ? lengths[seq] - 1 : 0);
  282. ppqnCount = 0;
  283. gate1Code = calcGate1Code(attributes[seq][stepIndexRun], 0, pulsesPerStep, params[GATE1_KNOB_PARAM].value);
  284. gate2Code = calcGate2Code(attributes[seq][stepIndexRun], 0, pulsesPerStep);
  285. clockIgnoreOnReset = (long) (clockIgnoreOnResetDuration * engineGetSampleRate());
  286. editingGateLength = 0l;
  287. }
  288. json_t *toJson() override {
  289. json_t *rootJ = json_object();
  290. // panelTheme
  291. json_object_set_new(rootJ, "panelTheme", json_integer(panelTheme));
  292. // expansion
  293. json_object_set_new(rootJ, "expansion", json_integer(expansion));
  294. // pulsesPerStep
  295. json_object_set_new(rootJ, "pulsesPerStep", json_integer(pulsesPerStep));
  296. // running
  297. json_object_set_new(rootJ, "running", json_boolean(running));
  298. // runModeSeq
  299. json_t *runModeSeqJ = json_array();
  300. for (int i = 0; i < 16; i++)
  301. json_array_insert_new(runModeSeqJ, i, json_integer(runModeSeq[i]));
  302. json_object_set_new(rootJ, "runModeSeq2", runModeSeqJ);// "2" appended so no break patches
  303. // runModeSong
  304. json_object_set_new(rootJ, "runModeSong", json_integer(runModeSong));
  305. // sequence
  306. json_object_set_new(rootJ, "sequence", json_integer(sequence));
  307. // lengths
  308. json_t *lengthsJ = json_array();
  309. for (int i = 0; i < 16; i++)
  310. json_array_insert_new(lengthsJ, i, json_integer(lengths[i]));
  311. json_object_set_new(rootJ, "lengths", lengthsJ);
  312. // phrase
  313. json_t *phraseJ = json_array();
  314. for (int i = 0; i < 16; i++)
  315. json_array_insert_new(phraseJ, i, json_integer(phrase[i]));
  316. json_object_set_new(rootJ, "phrase", phraseJ);
  317. // phrases
  318. json_object_set_new(rootJ, "phrases", json_integer(phrases));
  319. // CV
  320. json_t *cvJ = json_array();
  321. for (int i = 0; i < 16; i++)
  322. for (int s = 0; s < 16; s++) {
  323. json_array_insert_new(cvJ, s + (i * 16), json_real(cv[i][s]));
  324. }
  325. json_object_set_new(rootJ, "cv", cvJ);
  326. // attributes
  327. json_t *attributesJ = json_array();
  328. for (int i = 0; i < 16; i++)
  329. for (int s = 0; s < 16; s++) {
  330. json_array_insert_new(attributesJ, s + (i * 16), json_integer(attributes[i][s]));
  331. }
  332. json_object_set_new(rootJ, "attributes", attributesJ);
  333. // attached
  334. json_object_set_new(rootJ, "attached", json_boolean(attached));
  335. // resetOnRun
  336. json_object_set_new(rootJ, "resetOnRun", json_boolean(resetOnRun));
  337. return rootJ;
  338. }
  339. void fromJson(json_t *rootJ) override {
  340. // panelTheme
  341. json_t *panelThemeJ = json_object_get(rootJ, "panelTheme");
  342. if (panelThemeJ)
  343. panelTheme = json_integer_value(panelThemeJ);
  344. // expansion
  345. json_t *expansionJ = json_object_get(rootJ, "expansion");
  346. if (expansionJ)
  347. expansion = json_integer_value(expansionJ);
  348. // pulsesPerStep
  349. json_t *pulsesPerStepJ = json_object_get(rootJ, "pulsesPerStep");
  350. if (pulsesPerStepJ)
  351. pulsesPerStep = json_integer_value(pulsesPerStepJ);
  352. // running
  353. json_t *runningJ = json_object_get(rootJ, "running");
  354. if (runningJ)
  355. running = json_is_true(runningJ);
  356. // runModeSeq
  357. json_t *runModeSeqJ = json_object_get(rootJ, "runModeSeq2");// "2" appended so no break patches
  358. if (runModeSeqJ) {
  359. for (int i = 0; i < 16; i++)
  360. {
  361. json_t *runModeSeqArrayJ = json_array_get(runModeSeqJ, i);
  362. if (runModeSeqArrayJ)
  363. runModeSeq[i] = json_integer_value(runModeSeqArrayJ);
  364. }
  365. }
  366. else {// legacy
  367. runModeSeqJ = json_object_get(rootJ, "runModeSeq");
  368. if (runModeSeqJ)
  369. runModeSeq[0] = json_integer_value(runModeSeqJ);
  370. for (int i = 1; i < 16; i++)
  371. runModeSeq[i] = runModeSeq[0];
  372. }
  373. // runModeSong
  374. json_t *runModeSongJ = json_object_get(rootJ, "runModeSong");
  375. if (runModeSongJ)
  376. runModeSong = json_integer_value(runModeSongJ);
  377. // sequence
  378. json_t *sequenceJ = json_object_get(rootJ, "sequence");
  379. if (sequenceJ)
  380. sequence = json_integer_value(sequenceJ);
  381. // lengths
  382. json_t *lengthsJ = json_object_get(rootJ, "lengths");
  383. if (lengthsJ) {
  384. for (int i = 0; i < 16; i++)
  385. {
  386. json_t *lengthsArrayJ = json_array_get(lengthsJ, i);
  387. if (lengthsArrayJ)
  388. lengths[i] = json_integer_value(lengthsArrayJ);
  389. }
  390. }
  391. else {// legacy
  392. json_t *stepsJ = json_object_get(rootJ, "steps");
  393. if (stepsJ) {
  394. int steps = json_integer_value(stepsJ);
  395. for (int i = 0; i < 16; i++)
  396. lengths[i] = steps;
  397. }
  398. }
  399. // phrase
  400. json_t *phraseJ = json_object_get(rootJ, "phrase");
  401. if (phraseJ)
  402. for (int i = 0; i < 16; i++)
  403. {
  404. json_t *phraseArrayJ = json_array_get(phraseJ, i);
  405. if (phraseArrayJ)
  406. phrase[i] = json_integer_value(phraseArrayJ);
  407. }
  408. // phrases
  409. json_t *phrasesJ = json_object_get(rootJ, "phrases");
  410. if (phrasesJ)
  411. phrases = json_integer_value(phrasesJ);
  412. // CV
  413. json_t *cvJ = json_object_get(rootJ, "cv");
  414. if (cvJ) {
  415. for (int i = 0; i < 16; i++)
  416. for (int s = 0; s < 16; s++) {
  417. json_t *cvArrayJ = json_array_get(cvJ, s + (i * 16));
  418. if (cvArrayJ)
  419. cv[i][s] = json_real_value(cvArrayJ);
  420. }
  421. }
  422. // attributes
  423. json_t *attributesJ = json_object_get(rootJ, "attributes");
  424. if (attributesJ) {
  425. for (int i = 0; i < 16; i++)
  426. for (int s = 0; s < 16; s++) {
  427. json_t *attributesArrayJ = json_array_get(attributesJ, s + (i * 16));
  428. if (attributesArrayJ)
  429. attributes[i][s] = json_integer_value(attributesArrayJ);
  430. }
  431. }
  432. else {// legacy
  433. for (int i = 0; i < 16; i++)
  434. for (int s = 0; s < 16; s++)
  435. attributes[i][s] = 0;
  436. // gate1
  437. json_t *gate1J = json_object_get(rootJ, "gate1");
  438. if (gate1J) {
  439. for (int i = 0; i < 16; i++)
  440. for (int s = 0; s < 16; s++) {
  441. json_t *gate1arrayJ = json_array_get(gate1J, s + (i * 16));
  442. if (gate1arrayJ)
  443. if (!!json_integer_value(gate1arrayJ)) attributes[i][s] |= ATT_MSK_GATE1;
  444. }
  445. }
  446. // gate1Prob
  447. json_t *gate1ProbJ = json_object_get(rootJ, "gate1Prob");
  448. if (gate1ProbJ) {
  449. for (int i = 0; i < 16; i++)
  450. for (int s = 0; s < 16; s++) {
  451. json_t *gate1ProbarrayJ = json_array_get(gate1ProbJ, s + (i * 16));
  452. if (gate1ProbarrayJ)
  453. if (!!json_integer_value(gate1ProbarrayJ)) attributes[i][s] |= ATT_MSK_GATE1P;
  454. }
  455. }
  456. // gate2
  457. json_t *gate2J = json_object_get(rootJ, "gate2");
  458. if (gate2J) {
  459. for (int i = 0; i < 16; i++)
  460. for (int s = 0; s < 16; s++) {
  461. json_t *gate2arrayJ = json_array_get(gate2J, s + (i * 16));
  462. if (gate2arrayJ)
  463. if (!!json_integer_value(gate2arrayJ)) attributes[i][s] |= ATT_MSK_GATE2;
  464. }
  465. }
  466. // slide
  467. json_t *slideJ = json_object_get(rootJ, "slide");
  468. if (slideJ) {
  469. for (int i = 0; i < 16; i++)
  470. for (int s = 0; s < 16; s++) {
  471. json_t *slideArrayJ = json_array_get(slideJ, s + (i * 16));
  472. if (slideArrayJ)
  473. if (!!json_integer_value(slideArrayJ)) attributes[i][s] |= ATT_MSK_SLIDE;
  474. }
  475. }
  476. // tied
  477. json_t *tiedJ = json_object_get(rootJ, "tied");
  478. if (tiedJ) {
  479. for (int i = 0; i < 16; i++)
  480. for (int s = 0; s < 16; s++) {
  481. json_t *tiedArrayJ = json_array_get(tiedJ, s + (i * 16));
  482. if (tiedArrayJ)
  483. if (!!json_integer_value(tiedArrayJ)) attributes[i][s] |= ATT_MSK_TIED;
  484. }
  485. }
  486. }
  487. // attached
  488. json_t *attachedJ = json_object_get(rootJ, "attached");
  489. if (attachedJ)
  490. attached = json_is_true(attachedJ);
  491. // resetOnRun
  492. json_t *resetOnRunJ = json_object_get(rootJ, "resetOnRun");
  493. if (resetOnRunJ)
  494. resetOnRun = json_is_true(resetOnRunJ);
  495. // Initialize dependants after everything loaded
  496. initRun(true);
  497. editingSequence = isEditingSequence();
  498. editingSequenceLast = editingSequence;
  499. }
  500. void rotateSeq(int seqNum, bool directionRight, int seqLength) {
  501. float rotCV;
  502. int rotAttributes;
  503. int iStart = 0;
  504. int iEnd = seqLength - 1;
  505. int iRot = iStart;
  506. int iDelta = 1;
  507. if (directionRight) {
  508. iRot = iEnd;
  509. iDelta = -1;
  510. }
  511. rotCV = cv[seqNum][iRot];
  512. rotAttributes = attributes[seqNum][iRot];
  513. for ( ; ; iRot += iDelta) {
  514. if (iDelta == 1 && iRot >= iEnd) break;
  515. if (iDelta == -1 && iRot <= iStart) break;
  516. cv[seqNum][iRot] = cv[seqNum][iRot + iDelta];
  517. attributes[seqNum][iRot] = attributes[seqNum][iRot + iDelta];
  518. }
  519. cv[seqNum][iRot] = rotCV;
  520. attributes[seqNum][iRot] = rotAttributes;
  521. }
  522. // Advances the module by 1 audio frame with duration 1.0 / engineGetSampleRate()
  523. void step() override {
  524. float sampleRate = engineGetSampleRate();
  525. static const float gateTime = 0.4f;// seconds
  526. static const float copyPasteInfoTime = 0.5f;// seconds
  527. static const float editLengthTime = 2.0f;// seconds
  528. static const float tiedWarningTime = 0.7f;// seconds
  529. static const float holdDetectTime = 2.0f;// seconds
  530. static const float editGateLengthTime = 4.0f;// seconds
  531. //********** Buttons, knobs, switches and inputs **********
  532. // Notes:
  533. // * a tied step's attributes can not be modified by any of the following:
  534. // write input, oct and keyboard buttons, gate1Prob and slide buttons
  535. // however, paste, transpose, rotate obviously can, and gate1/2 can be turned back on if desired.
  536. // * Whenever cv[][] is modified or tied[] is activated for a step, call applyTiedStep(sequence,stepIndexEdit,steps)
  537. // Edit mode
  538. editingSequence = isEditingSequence();// true = editing sequence, false = editing song
  539. if (editingSequenceLast != editingSequence) {
  540. if (running)
  541. initRun(true);
  542. displayState = DISP_NORMAL;
  543. editingSequenceLast = editingSequence;
  544. }
  545. // Seq CV input
  546. if (inputs[SEQCV_INPUT].active) {
  547. sequence = (int) clamp( round(inputs[SEQCV_INPUT].value * (16.0f - 1.0f) / 10.0f), 0.0f, (16.0f - 1.0f) );
  548. }
  549. // Mode CV input
  550. if (inputs[MODECV_INPUT].active) {
  551. if (editingSequence)
  552. runModeSeq[sequence] = (int) clamp( round(inputs[MODECV_INPUT].value * ((float)NUM_MODES - 1.0f) / 10.0f), 0.0f, (float)NUM_MODES - 1.0f );
  553. }
  554. // Run button
  555. if (runningTrigger.process(params[RUN_PARAM].value + inputs[RUNCV_INPUT].value)) {
  556. running = !running;
  557. if (running)
  558. initRun(resetOnRun);
  559. displayState = DISP_NORMAL;
  560. }
  561. // Attach button
  562. if (attachedTrigger.process(params[ATTACH_PARAM].value)) {
  563. if (running)
  564. attached = !attached;
  565. displayState = DISP_NORMAL;
  566. }
  567. if (running && attached) {
  568. if (editingSequence)
  569. stepIndexEdit = stepIndexRun;
  570. else
  571. phraseIndexEdit = phraseIndexRun;
  572. }
  573. // Copy button
  574. if (copyTrigger.process(params[COPY_PARAM].value)) {
  575. if (editingSequence) {
  576. infoCopyPaste = (long) (copyPasteInfoTime * sampleRate / displayRefreshStepSkips);
  577. int sStart = stepIndexEdit;
  578. int sCount = 16;
  579. if (params[CPMODE_PARAM].value > 1.5f)// all
  580. sStart = 0;
  581. else if (params[CPMODE_PARAM].value < 0.5f)// 4
  582. sCount = 4;
  583. else// 8
  584. sCount = 8;
  585. countCP = sCount;
  586. for (int i = 0, s = sStart; i < countCP; i++, s++) {
  587. if (s >= 16) s = 0;
  588. cvCPbuffer[i] = cv[sequence][s];
  589. attributesCPbuffer[i] = attributes[sequence][s];
  590. if ((--sCount) <= 0)
  591. break;
  592. }
  593. lengthCPbuffer = lengths[sequence];
  594. modeCPbuffer = runModeSeq[sequence];
  595. }
  596. displayState = DISP_NORMAL;
  597. }
  598. // Paste button
  599. if (pasteTrigger.process(params[PASTE_PARAM].value)) {
  600. if (editingSequence) {
  601. infoCopyPaste = (long) (-1 * copyPasteInfoTime * sampleRate / displayRefreshStepSkips);
  602. int sStart = ((countCP == 16) ? 0 : stepIndexEdit);
  603. int sCount = countCP;
  604. for (int i = 0, s = sStart; i < countCP; i++, s++) {
  605. if (s >= 16)
  606. break;
  607. cv[sequence][s] = cvCPbuffer[i];
  608. attributes[sequence][s] = attributesCPbuffer[i];
  609. if ((--sCount) <= 0)
  610. break;
  611. }
  612. if (params[CPMODE_PARAM].value > 1.5f) {// all
  613. lengths[sequence] = lengthCPbuffer;
  614. runModeSeq[sequence] = modeCPbuffer;
  615. }
  616. }
  617. displayState = DISP_NORMAL;
  618. }
  619. // Length button
  620. if (lengthTrigger.process(params[LENGTH_PARAM].value)) {
  621. if (editingLength > 0ul)
  622. editingLength = 0ul;// allow user to quickly leave editing mode when re-press
  623. else
  624. editingLength = (unsigned long) (editLengthTime * sampleRate / displayRefreshStepSkips);
  625. displayState = DISP_NORMAL;
  626. }
  627. // Write input (must be before Left and Right in case route gate simultaneously to Right and Write for example)
  628. // (write must be to correct step)
  629. bool writeTrig = writeTrigger.process(inputs[WRITE_INPUT].value);
  630. if (writeTrig) {
  631. if (editingSequence) {
  632. cv[sequence][stepIndexEdit] = inputs[CV_INPUT].value;
  633. // Extra CVs from expansion panel:
  634. if (inputs[TIEDCV_INPUT].active)
  635. setTiedA(&attributes[sequence][stepIndexEdit], inputs[TIEDCV_INPUT].value > 1.0f);
  636. if (inputs[GATE1CV_INPUT].active)
  637. setGate1a(&attributes[sequence][stepIndexEdit], inputs[GATE1CV_INPUT].value > 1.0f);
  638. if (inputs[GATE2CV_INPUT].active)
  639. setGate2a(&attributes[sequence][stepIndexEdit], inputs[GATE2CV_INPUT].value > 1.0f);
  640. if (inputs[SLIDECV_INPUT].active)
  641. setSlideA(&attributes[sequence][stepIndexEdit], inputs[SLIDECV_INPUT].value > 1.0f);
  642. applyTiedStep(sequence, stepIndexEdit, lengths[sequence]);
  643. editingGate = (unsigned long) (gateTime * sampleRate / displayRefreshStepSkips);
  644. editingGateCV = cv[sequence][stepIndexEdit];
  645. editingGateKeyLight = -1;
  646. // Autostep (after grab all active inputs)
  647. if (params[AUTOSTEP_PARAM].value > 0.5f)
  648. stepIndexEdit = moveIndex(stepIndexEdit, stepIndexEdit + 1, 16);
  649. }
  650. displayState = DISP_NORMAL;
  651. }
  652. // Left and Right CV inputs and buttons
  653. int delta = 0;
  654. if (leftTrigger.process(inputs[LEFTCV_INPUT].value + params[LEFT_PARAM].value)) {
  655. delta = -1;
  656. displayState = DISP_NORMAL;
  657. }
  658. if (rightTrigger.process(inputs[RIGHTCV_INPUT].value + params[RIGHT_PARAM].value)) {
  659. delta = +1;
  660. displayState = DISP_NORMAL;
  661. }
  662. if (delta != 0) {
  663. if (editingLength > 0ul) {
  664. editingLength = (unsigned long) (editLengthTime * sampleRate / displayRefreshStepSkips);// restart editing length timer
  665. if (editingSequence) {
  666. lengths[sequence] += delta;
  667. if (lengths[sequence] > 16) lengths[sequence] = 16;
  668. if (lengths[sequence] < 1 ) lengths[sequence] = 1;
  669. }
  670. else {
  671. phrases += delta;
  672. if (phrases > 16) phrases = 16;
  673. if (phrases < 1 ) phrases = 1;
  674. }
  675. }
  676. else {
  677. if (!running || !attached) {// don't move heads when attach and running
  678. if (editingSequence) {
  679. stepIndexEdit = moveIndex(stepIndexEdit, stepIndexEdit + delta, 16);
  680. if (!getTied(sequence,stepIndexEdit)) {// play if non-tied step
  681. if (!writeTrig) {// in case autostep when simultaneous writeCV and stepCV (keep what was done in Write Input block above)
  682. editingGate = (unsigned long) (gateTime * sampleRate / displayRefreshStepSkips);
  683. editingGateCV = cv[sequence][stepIndexEdit];
  684. editingGateKeyLight = -1;
  685. }
  686. }
  687. }
  688. else
  689. phraseIndexEdit = moveIndex(phraseIndexEdit, phraseIndexEdit + delta, 16);
  690. }
  691. }
  692. }
  693. // Mode and Transpose/Rotate buttons
  694. if (modeTrigger.process(params[RUNMODE_PARAM].value)) {
  695. if (displayState != DISP_MODE)
  696. displayState = DISP_MODE;
  697. else
  698. displayState = DISP_NORMAL;
  699. //if (!running) {
  700. modeHoldDetect.start((long) (holdDetectTime * sampleRate / displayRefreshStepSkips));
  701. //}
  702. }
  703. if (transposeTrigger.process(params[TRAN_ROT_PARAM].value)) {
  704. if (editingSequence) {
  705. if (displayState == DISP_NORMAL || displayState == DISP_MODE) {
  706. displayState = DISP_TRANSPOSE;
  707. transposeOffset = 0;
  708. }
  709. else if (displayState == DISP_TRANSPOSE) {
  710. displayState = DISP_ROTATE;
  711. rotateOffset = 0;
  712. }
  713. else
  714. displayState = DISP_NORMAL;
  715. }
  716. }
  717. // Sequence knob
  718. float seqParamValue = params[SEQUENCE_PARAM].value;
  719. int newSequenceKnob = (int)roundf(seqParamValue * 7.0f);
  720. if (seqParamValue == 0.0f)// true when constructor or fromJson() occured
  721. sequenceKnob = newSequenceKnob;
  722. int deltaKnob = newSequenceKnob - sequenceKnob;
  723. if (deltaKnob != 0) {
  724. if (abs(deltaKnob) <= 3) {// avoid discontinuous step (initialize for example)
  725. if (editingLength > 0ul) {
  726. editingLength = (unsigned long) (editLengthTime * sampleRate / displayRefreshStepSkips);// restart editing length timer
  727. if (editingSequence) {
  728. lengths[sequence] += deltaKnob;
  729. if (lengths[sequence] > 16) lengths[sequence] = 16 ;
  730. if (lengths[sequence] < 1 ) lengths[sequence] = 1;
  731. }
  732. else {
  733. phrases += deltaKnob;
  734. if (phrases > 16) phrases = 16;
  735. if (phrases < 1 ) phrases = 1;
  736. }
  737. }
  738. else if (editingPpqn != 0) {
  739. pulsesPerStep = indexToPps(ppsToIndex(pulsesPerStep) + deltaKnob);// indexToPps() does clamping
  740. editingPpqn = (long) (editGateLengthTime * sampleRate / displayRefreshStepSkips);
  741. }
  742. else if (displayState == DISP_MODE) {
  743. if (editingSequence) {
  744. if (!inputs[MODECV_INPUT].active) {
  745. runModeSeq[sequence] += deltaKnob;
  746. if (runModeSeq[sequence] < 0) runModeSeq[sequence] = 0;
  747. if (runModeSeq[sequence] >= NUM_MODES) runModeSeq[sequence] = NUM_MODES - 1;
  748. }
  749. }
  750. else {
  751. runModeSong += deltaKnob;
  752. if (runModeSong < 0) runModeSong = 0;
  753. if (runModeSong >= 5) runModeSong = 5 - 1;
  754. }
  755. }
  756. else if (displayState == DISP_TRANSPOSE) {
  757. if (editingSequence) {
  758. transposeOffset += deltaKnob;
  759. if (transposeOffset > 99) transposeOffset = 99;
  760. if (transposeOffset < -99) transposeOffset = -99;
  761. // Tranpose by this number of semi-tones: deltaKnob
  762. float transposeOffsetCV = ((float)(deltaKnob))/12.0f;
  763. for (int s = 0; s < 16; s++) {
  764. cv[sequence][s] += transposeOffsetCV;
  765. }
  766. }
  767. }
  768. else if (displayState == DISP_ROTATE) {
  769. if (editingSequence) {
  770. rotateOffset += deltaKnob;
  771. if (rotateOffset > 99) rotateOffset = 99;
  772. if (rotateOffset < -99) rotateOffset = -99;
  773. if (deltaKnob > 0 && deltaKnob < 99) {// Rotate right, 99 is safety
  774. for (int i = deltaKnob; i > 0; i--)
  775. rotateSeq(sequence, true, lengths[sequence]);
  776. }
  777. if (deltaKnob < 0 && deltaKnob > -99) {// Rotate left, 99 is safety
  778. for (int i = deltaKnob; i < 0; i++)
  779. rotateSeq(sequence, false, lengths[sequence]);
  780. }
  781. }
  782. }
  783. else {// DISP_NORMAL
  784. if (editingSequence) {
  785. if (!inputs[SEQCV_INPUT].active) {
  786. sequence += deltaKnob;
  787. if (sequence < 0) sequence = 0;
  788. if (sequence >= 16) sequence = (16 - 1);
  789. }
  790. }
  791. else {
  792. phrase[phraseIndexEdit] += deltaKnob;
  793. if (phrase[phraseIndexEdit] < 0) phrase[phraseIndexEdit] = 0;
  794. if (phrase[phraseIndexEdit] >= 16) phrase[phraseIndexEdit] = (16 - 1);
  795. }
  796. }
  797. }
  798. sequenceKnob = newSequenceKnob;
  799. }
  800. // Octave buttons
  801. int newOct = -1;
  802. for (int i = 0; i < 7; i++) {
  803. if (octTriggers[i].process(params[OCTAVE_PARAM + i].value)) {
  804. newOct = 6 - i;
  805. displayState = DISP_NORMAL;
  806. }
  807. }
  808. if (newOct >= 0 && newOct <= 6) {
  809. if (editingSequence) {
  810. if (getTied(sequence,stepIndexEdit))
  811. tiedWarning = (long) (tiedWarningTime * sampleRate / displayRefreshStepSkips);
  812. else {
  813. float newCV = cv[sequence][stepIndexEdit] + 10.0f;//to properly handle negative note voltages
  814. newCV = newCV - floor(newCV) + (float) (newOct - 3);
  815. if (newCV >= -3.0f && newCV < 4.0f) {
  816. cv[sequence][stepIndexEdit] = newCV;
  817. applyTiedStep(sequence, stepIndexEdit, lengths[sequence]);
  818. }
  819. editingGate = (unsigned long) (gateTime * sampleRate / displayRefreshStepSkips);
  820. editingGateCV = cv[sequence][stepIndexEdit];
  821. editingGateKeyLight = -1;
  822. }
  823. }
  824. }
  825. // Keyboard buttons
  826. for (int i = 0; i < 12; i++) {
  827. if (keyTriggers[i].process(params[KEY_PARAMS + i].value)) {
  828. if (editingSequence) {
  829. if (editingGateLength != 0l) {
  830. int newMode = keyIndexToGateMode(i, pulsesPerStep);
  831. if (editingGateLength > 0l) {
  832. if (newMode != -1)
  833. setGate1Mode(sequence, stepIndexEdit, newMode);
  834. else
  835. editingPpqn = (long) (editGateLengthTime * sampleRate / displayRefreshStepSkips);
  836. editingGateLength = ((long) (editGateLengthTime * sampleRate / displayRefreshStepSkips) * editGateLengthTimeInitMult);
  837. }
  838. else {
  839. if (newMode != -1)
  840. setGate2Mode(sequence, stepIndexEdit, newMode);
  841. else
  842. editingPpqn = (long) (editGateLengthTime * sampleRate / displayRefreshStepSkips);
  843. editingGateLength = -1l * ((long) (editGateLengthTime * sampleRate / displayRefreshStepSkips) * editGateLengthTimeInitMult);
  844. }
  845. }
  846. else if (getTied(sequence,stepIndexEdit)) {
  847. if (params[KEY_PARAMS + i].value > 1.5f)
  848. stepIndexEdit = moveIndex(stepIndexEdit, stepIndexEdit + 1, 16);
  849. else
  850. tiedWarning = (long) (tiedWarningTime * sampleRate / displayRefreshStepSkips);
  851. }
  852. else {
  853. cv[sequence][stepIndexEdit] = floor(cv[sequence][stepIndexEdit]) + ((float) i) / 12.0f;
  854. applyTiedStep(sequence, stepIndexEdit, lengths[sequence]);
  855. editingGate = (unsigned long) (gateTime * sampleRate / displayRefreshStepSkips);
  856. editingGateCV = cv[sequence][stepIndexEdit];
  857. editingGateKeyLight = -1;
  858. if (params[KEY_PARAMS + i].value > 1.5f) {
  859. stepIndexEdit = moveIndex(stepIndexEdit, stepIndexEdit + 1, 16);
  860. editingGateKeyLight = i;
  861. }
  862. }
  863. }
  864. displayState = DISP_NORMAL;
  865. }
  866. }
  867. // Gate1, Gate1Prob, Gate2, Slide and Tied buttons
  868. if (gate1Trigger.process(params[GATE1_PARAM].value)) {
  869. if (editingSequence) {
  870. toggleGate1a(&attributes[sequence][stepIndexEdit]);
  871. //if (!running) {
  872. if (pulsesPerStep != 1) {
  873. editingGateLength = getGate1(sequence,stepIndexEdit) ? ((long) (editGateLengthTime * sampleRate / displayRefreshStepSkips) * editGateLengthTimeInitMult) : 0l;
  874. gate1HoldDetect.start((long) (holdDetectTime * sampleRate / displayRefreshStepSkips));
  875. }
  876. //}
  877. }
  878. displayState = DISP_NORMAL;
  879. }
  880. if (gate1ProbTrigger.process(params[GATE1_PROB_PARAM].value)) {
  881. if (editingSequence) {
  882. if (getTied(sequence,stepIndexEdit))
  883. tiedWarning = (long) (tiedWarningTime * sampleRate / displayRefreshStepSkips);
  884. else
  885. toggleGate1Pa(&attributes[sequence][stepIndexEdit]);
  886. }
  887. displayState = DISP_NORMAL;
  888. }
  889. if (gate2Trigger.process(params[GATE2_PARAM].value)) {
  890. if (editingSequence) {
  891. toggleGate2a(&attributes[sequence][stepIndexEdit]);
  892. //if (!running) {
  893. if (pulsesPerStep != 1) {
  894. editingGateLength = getGate2(sequence,stepIndexEdit) ? -1l * ((long) (editGateLengthTime * sampleRate / displayRefreshStepSkips) * editGateLengthTimeInitMult) : 0l;
  895. gate2HoldDetect.start((long) (holdDetectTime * sampleRate / displayRefreshStepSkips));
  896. }
  897. //}
  898. }
  899. displayState = DISP_NORMAL;
  900. }
  901. if (slideTrigger.process(params[SLIDE_BTN_PARAM].value)) {
  902. if (editingSequence) {
  903. if (getTied(sequence,stepIndexEdit))
  904. tiedWarning = (long) (tiedWarningTime * sampleRate / displayRefreshStepSkips);
  905. else
  906. toggleSlideA(&attributes[sequence][stepIndexEdit]);
  907. }
  908. displayState = DISP_NORMAL;
  909. }
  910. if (tiedTrigger.process(params[TIE_PARAM].value)) {
  911. if (editingSequence) {
  912. toggleTiedA(&attributes[sequence][stepIndexEdit]);
  913. if (getTied(sequence,stepIndexEdit)) {
  914. setGate1a(&attributes[sequence][stepIndexEdit], false);
  915. setGate2a(&attributes[sequence][stepIndexEdit], false);
  916. setSlideA(&attributes[sequence][stepIndexEdit], false);
  917. applyTiedStep(sequence, stepIndexEdit, lengths[sequence]);
  918. }
  919. }
  920. displayState = DISP_NORMAL;
  921. }
  922. //********** Clock and reset **********
  923. // Clock
  924. if (clockTrigger.process(inputs[CLOCK_INPUT].value)) {
  925. if (running && clockIgnoreOnReset == 0l) {
  926. ppqnCount++;
  927. if (ppqnCount >= pulsesPerStep)
  928. ppqnCount = 0;
  929. int newSeq = sequence;// good value when editingSequence, overwrite if not editingSequence
  930. if (ppqnCount == 0) {
  931. float slideFromCV = 0.0f;
  932. if (editingSequence) {
  933. slideFromCV = cv[sequence][stepIndexRun];
  934. moveIndexRunMode(&stepIndexRun, lengths[sequence], runModeSeq[sequence], &stepIndexRunHistory);
  935. }
  936. else {
  937. slideFromCV = cv[phrase[phraseIndexRun]][stepIndexRun];
  938. if (moveIndexRunMode(&stepIndexRun, lengths[phrase[phraseIndexRun]], runModeSeq[phrase[phraseIndexRun]], &stepIndexRunHistory)) {
  939. moveIndexRunMode(&phraseIndexRun, phrases, runModeSong, &phraseIndexRunHistory);
  940. stepIndexRun = (runModeSeq[phrase[phraseIndexRun]] == MODE_REV ? lengths[phrase[phraseIndexRun]] - 1 : 0);// must always refresh after phraseIndexRun has changed
  941. }
  942. newSeq = phrase[phraseIndexRun];
  943. }
  944. // Slide
  945. if (getSlide(newSeq, stepIndexRun)) {
  946. // activate sliding (slideStepsRemain can be reset, else runs down to 0, either way, no need to reinit)
  947. slideStepsRemain = (unsigned long) (((float)clockPeriod * pulsesPerStep) * params[SLIDE_KNOB_PARAM].value / 2.0f);// 0-T slide, where T is clock period (can be too long when user does clock gating)
  948. //slideStepsRemain = (unsigned long) (engineGetSampleRate() * params[SLIDE_KNOB_PARAM].value );// 0-2s slide
  949. float slideToCV = cv[newSeq][stepIndexRun];
  950. slideCVdelta = (slideToCV - slideFromCV)/(float)slideStepsRemain;
  951. }
  952. }
  953. else {
  954. if (!editingSequence)
  955. newSeq = phrase[phraseIndexRun];
  956. }
  957. if (gate1Code != -1 || ppqnCount == 0)
  958. gate1Code = calcGate1Code(attributes[newSeq][stepIndexRun], ppqnCount, pulsesPerStep, params[GATE1_KNOB_PARAM].value);
  959. gate2Code = calcGate2Code(attributes[newSeq][stepIndexRun], ppqnCount, pulsesPerStep);
  960. }
  961. clockPeriod = 0ul;
  962. }
  963. clockPeriod++;
  964. // Reset
  965. if (resetTrigger.process(inputs[RESET_INPUT].value + params[RESET_PARAM].value)) {
  966. //stepIndexEdit = 0;
  967. //sequence = 0;
  968. initRun(true);// must be after sequence reset
  969. resetLight = 1.0f;
  970. displayState = DISP_NORMAL;
  971. clockTrigger.reset();
  972. }
  973. //********** Outputs and lights **********
  974. // CV and gates outputs
  975. int seq = editingSequence ? (sequence) : (running ? phrase[phraseIndexRun] : phrase[phraseIndexEdit]);
  976. int step = editingSequence ? (running ? stepIndexRun : stepIndexEdit) : (stepIndexRun);
  977. if (running) {
  978. float slideOffset = (slideStepsRemain > 0ul ? (slideCVdelta * (float)slideStepsRemain) : 0.0f);
  979. outputs[CV_OUTPUT].value = cv[seq][step] - slideOffset;
  980. outputs[GATE1_OUTPUT].value = calcGate(gate1Code, clockTrigger, clockPeriod, sampleRate) ? 10.0f : 0.0f;
  981. outputs[GATE2_OUTPUT].value = calcGate(gate2Code, clockTrigger, clockPeriod, sampleRate) ? 10.0f : 0.0f;
  982. }
  983. else {// not running
  984. outputs[CV_OUTPUT].value = (editingGate > 0ul) ? editingGateCV : cv[seq][step];
  985. outputs[GATE1_OUTPUT].value = (editingGate > 0ul) ? 10.0f : 0.0f;
  986. outputs[GATE2_OUTPUT].value = (editingGate > 0ul) ? 10.0f : 0.0f;
  987. }
  988. if (slideStepsRemain > 0ul)
  989. slideStepsRemain--;
  990. lightRefreshCounter++;
  991. if (lightRefreshCounter > displayRefreshStepSkips) {
  992. lightRefreshCounter = 0;
  993. // Step/phrase lights
  994. if (infoCopyPaste != 0l) {
  995. for (int i = 0; i < 16; i++) {
  996. if ( (i >= stepIndexEdit && i < (stepIndexEdit + countCP)) || (countCP == 16) )
  997. lights[STEP_PHRASE_LIGHTS + (i<<1)].value = 0.5f;// Green when copy interval
  998. else
  999. lights[STEP_PHRASE_LIGHTS + (i<<1)].value = 0.0f; // Green (nothing)
  1000. lights[STEP_PHRASE_LIGHTS + (i<<1) + 1].value = 0.0f;// Red (nothing)
  1001. }
  1002. }
  1003. else {
  1004. for (int i = 0; i < 16; i++) {
  1005. if (editingLength > 0ul) {
  1006. // Length (green)
  1007. if (editingSequence)
  1008. lights[STEP_PHRASE_LIGHTS + (i<<1)].value = ((i < lengths[sequence]) ? 0.5f : 0.0f);
  1009. else
  1010. lights[STEP_PHRASE_LIGHTS + (i<<1)].value = ((i < phrases) ? 0.5f : 0.0f);
  1011. // Nothing (red)
  1012. lights[STEP_PHRASE_LIGHTS + (i<<1) + 1].value = 0.0f;
  1013. }
  1014. else {
  1015. // Run cursor (green)
  1016. if (editingSequence)
  1017. lights[STEP_PHRASE_LIGHTS + (i<<1)].value = ((running && (i == stepIndexRun)) ? 1.0f : 0.0f);
  1018. else {
  1019. float green = ((running && (i == phraseIndexRun)) ? 1.0f : 0.0f);
  1020. green += ((running && (i == stepIndexRun) && i != phraseIndexEdit) ? 0.1f : 0.0f);
  1021. lights[STEP_PHRASE_LIGHTS + (i<<1)].value = clamp(green, 0.0f, 1.0f);
  1022. }
  1023. // Edit cursor (red)
  1024. if (editingSequence)
  1025. lights[STEP_PHRASE_LIGHTS + (i<<1) + 1].value = (i == stepIndexEdit ? 1.0f : 0.0f);
  1026. else
  1027. lights[STEP_PHRASE_LIGHTS + (i<<1) + 1].value = (i == phraseIndexEdit ? 1.0f : 0.0f);
  1028. }
  1029. }
  1030. }
  1031. // Octave lights
  1032. float octCV = 0.0f;
  1033. if (editingSequence)
  1034. octCV = cv[sequence][stepIndexEdit];
  1035. else
  1036. octCV = cv[phrase[phraseIndexEdit]][stepIndexRun];
  1037. int octLightIndex = (int) floor(octCV + 3.0f);
  1038. for (int i = 0; i < 7; i++) {
  1039. if (!editingSequence && (!attached || !running))// no oct lights when song mode and either (detached [1] or stopped [2])
  1040. // [1] makes no sense, can't mod steps and stepping though seq that may not be playing
  1041. // [2] CV is set to 0V when not running and in song mode, so cv[][] makes no sense to display
  1042. lights[OCTAVE_LIGHTS + i].value = 0.0f;
  1043. else {
  1044. if (tiedWarning > 0l) {
  1045. bool warningFlashState = calcWarningFlash(tiedWarning, (long) (tiedWarningTime * sampleRate / displayRefreshStepSkips));
  1046. lights[OCTAVE_LIGHTS + i].value = (warningFlashState && (i == (6 - octLightIndex))) ? 1.0f : 0.0f;
  1047. }
  1048. else
  1049. lights[OCTAVE_LIGHTS + i].value = (i == (6 - octLightIndex) ? 1.0f : 0.0f);
  1050. }
  1051. }
  1052. // Keyboard lights
  1053. float cvValOffset;
  1054. if (editingSequence)
  1055. cvValOffset = cv[sequence][stepIndexEdit] + 10.0f;//to properly handle negative note voltages
  1056. else
  1057. cvValOffset = cv[phrase[phraseIndexEdit]][stepIndexRun] + 10.0f;//to properly handle negative note voltages
  1058. int keyLightIndex = (int) clamp( roundf( (cvValOffset-floor(cvValOffset)) * 12.0f ), 0.0f, 11.0f);
  1059. if (editingGateLength != 0 && editingSequence) {
  1060. int modeLightIndex = gateModeToKeyLightIndex(attributes[sequence][stepIndexEdit], editingGateLength > 0l);
  1061. for (int i = 0; i < 12; i++) {
  1062. if (i == modeLightIndex) {
  1063. lights[KEY_LIGHTS + i * 2 + 0].value = editingGateLength > 0l ? 1.0f : 0.2f;
  1064. lights[KEY_LIGHTS + i * 2 + 1].value = editingGateLength > 0l ? 0.2f : 1.0f;
  1065. }
  1066. else {
  1067. lights[KEY_LIGHTS + i * 2 + 0].value = 0.0f;
  1068. if (i == keyLightIndex)
  1069. lights[KEY_LIGHTS + i * 2 + 1].value = 0.1f;
  1070. else
  1071. lights[KEY_LIGHTS + i * 2 + 1].value = 0.0f;
  1072. }
  1073. }
  1074. }
  1075. else {
  1076. for (int i = 0; i < 12; i++) {
  1077. lights[KEY_LIGHTS + i * 2 + 0].value = 0.0f;
  1078. if (!editingSequence && (!attached || !running))// no keyboard lights when song mode and either (detached [1] or stopped [2])
  1079. // [1] makes no sense, can't mod steps and stepping though seq that may not be playing
  1080. // [2] CV is set to 0V when not running and in song mode, so cv[][] makes no sense to display
  1081. lights[KEY_LIGHTS + i * 2 + 1].value = 0.0f;
  1082. else {
  1083. if (tiedWarning > 0l) {
  1084. bool warningFlashState = calcWarningFlash(tiedWarning, (long) (tiedWarningTime * sampleRate / displayRefreshStepSkips));
  1085. lights[KEY_LIGHTS + i * 2 + 1].value = (warningFlashState && i == keyLightIndex) ? 1.0f : 0.0f;
  1086. }
  1087. else {
  1088. if (editingGate > 0ul && editingGateKeyLight != -1)
  1089. lights[KEY_LIGHTS + i * 2 + 1].value = (i == editingGateKeyLight ? ((float) editingGate / (float)(gateTime * sampleRate / displayRefreshStepSkips)) : 0.0f);
  1090. else
  1091. lights[KEY_LIGHTS + i * 2 + 1].value = (i == keyLightIndex ? 1.0f : 0.0f);
  1092. }
  1093. }
  1094. }
  1095. }
  1096. // Gate1, Gate1Prob, Gate2, Slide and Tied lights
  1097. int attributesVal = attributes[sequence][stepIndexEdit];
  1098. if (!editingSequence)
  1099. attributesVal = attributes[phrase[phraseIndexEdit]][stepIndexRun];
  1100. //
  1101. setGateLight(getGate1a(attributesVal), GATE1_LIGHT);
  1102. setGateLight(getGate2a(attributesVal), GATE2_LIGHT);
  1103. lights[GATE1_PROB_LIGHT].value = getGate1Pa(attributesVal) ? 1.0f : 0.0f;
  1104. lights[SLIDE_LIGHT].value = getSlideA(attributesVal) ? 1.0f : 0.0f;
  1105. if (tiedWarning > 0l) {
  1106. bool warningFlashState = calcWarningFlash(tiedWarning, (long) (tiedWarningTime * sampleRate / displayRefreshStepSkips));
  1107. lights[TIE_LIGHT].value = (warningFlashState) ? 1.0f : 0.0f;
  1108. }
  1109. else
  1110. lights[TIE_LIGHT].value = getTiedA(attributesVal) ? 1.0f : 0.0f;
  1111. // Attach light
  1112. lights[ATTACH_LIGHT].value = (running && attached) ? 1.0f : 0.0f;
  1113. // Reset light
  1114. lights[RESET_LIGHT].value = resetLight;
  1115. resetLight -= (resetLight / lightLambda) * engineGetSampleTime() * displayRefreshStepSkips;
  1116. // Run light
  1117. lights[RUN_LIGHT].value = running ? 1.0f : 0.0f;
  1118. if (tiedWarning > 0l)
  1119. tiedWarning--;
  1120. if (editingLength > 0ul)
  1121. editingLength--;
  1122. if (editingGate > 0ul)
  1123. editingGate--;
  1124. if (infoCopyPaste != 0l) {
  1125. if (infoCopyPaste > 0l)
  1126. infoCopyPaste --;
  1127. if (infoCopyPaste < 0l)
  1128. infoCopyPaste ++;
  1129. }
  1130. if (modeHoldDetect.process(params[RUNMODE_PARAM].value)) {
  1131. displayState = DISP_NORMAL;
  1132. editingPpqn = (long) (editGateLengthTime * sampleRate / displayRefreshStepSkips);
  1133. }
  1134. if (gate1HoldDetect.process(params[GATE1_PARAM].value)) {
  1135. toggleGate1a(&attributes[sequence][stepIndexEdit]);
  1136. editGateLengthTimeInitMult = 1;
  1137. }
  1138. if (gate2HoldDetect.process(params[GATE2_PARAM].value)) {
  1139. toggleGate2a(&attributes[sequence][stepIndexEdit]);
  1140. editGateLengthTimeInitMult = 100;
  1141. }
  1142. if (editingPpqn > 0l)
  1143. editingPpqn--;
  1144. if (editingGateLength != 0l) {
  1145. if (editingGateLength > 0l)
  1146. editingGateLength --;
  1147. if (editingGateLength < 0l)
  1148. editingGateLength ++;
  1149. }
  1150. }// lightRefreshCounter
  1151. if (clockIgnoreOnReset > 0l)
  1152. clockIgnoreOnReset--;
  1153. }// step()
  1154. void applyTiedStep(int seqNum, int indexTied, int seqLength) {
  1155. // Start on indexTied and loop until seqLength
  1156. // Called because either:
  1157. // case A: tied was activated for given step
  1158. // case B: the given step's CV was modified
  1159. // These cases are mutually exclusive
  1160. // copy previous CV over to current step if tied
  1161. if (getTied(seqNum,indexTied) && (indexTied > 0))
  1162. cv[seqNum][indexTied] = cv[seqNum][indexTied - 1];
  1163. // Affect downstream CVs of subsequent tied note chain (can be 0 length if next note is not tied)
  1164. for (int i = indexTied + 1; i < seqLength && getTied(seqNum,i); i++)
  1165. cv[seqNum][i] = cv[seqNum][indexTied];
  1166. }
  1167. int calcNewGateMode(int currentGateMode, int deltaKnob) {
  1168. return clamp(currentGateMode + deltaKnob, 0, NUM_GATES - 1);
  1169. }
  1170. inline void setGateLight(bool gateOn, int lightIndex) {
  1171. if (!gateOn) {
  1172. lights[lightIndex + 0].value = 0.0f;
  1173. lights[lightIndex + 1].value = 0.0f;
  1174. }
  1175. else if (pulsesPerStep == 1) {
  1176. lights[lightIndex + 0].value = 0.0f;
  1177. lights[lightIndex + 1].value = 1.0f;
  1178. }
  1179. else {
  1180. lights[lightIndex + 0].value = lightIndex == GATE1_LIGHT ? 1.0f : 0.2f;
  1181. lights[lightIndex + 1].value = lightIndex == GATE1_LIGHT ? 0.2f : 1.0f;
  1182. }
  1183. }
  1184. };
  1185. struct PhraseSeq16Widget : ModuleWidget {
  1186. PhraseSeq16 *module;
  1187. DynamicSVGPanel *panel;
  1188. int oldExpansion;
  1189. int expWidth = 60;
  1190. IMPort* expPorts[5];
  1191. struct SequenceDisplayWidget : TransparentWidget {
  1192. PhraseSeq16 *module;
  1193. std::shared_ptr<Font> font;
  1194. char displayStr[4];
  1195. SequenceDisplayWidget() {
  1196. font = Font::load(assetPlugin(plugin, "res/fonts/Segment14.ttf"));
  1197. }
  1198. void runModeToStr(int num) {
  1199. if (num >= 0 && num < NUM_MODES)
  1200. snprintf(displayStr, 4, "%s", modeLabels[num].c_str());
  1201. }
  1202. void draw(NVGcontext *vg) override {
  1203. NVGcolor textColor = prepareDisplay(vg, &box);
  1204. nvgFontFaceId(vg, font->handle);
  1205. //nvgTextLetterSpacing(vg, 2.5);
  1206. Vec textPos = Vec(6, 24);
  1207. nvgFillColor(vg, nvgTransRGBA(textColor, 16));
  1208. nvgText(vg, textPos.x, textPos.y, "~~~", NULL);
  1209. nvgFillColor(vg, textColor);
  1210. if (module->infoCopyPaste != 0l) {
  1211. if (module->infoCopyPaste > 0l)
  1212. snprintf(displayStr, 4, "CPY");
  1213. else
  1214. snprintf(displayStr, 4, "PST");
  1215. }
  1216. else if (module->editingLength > 0ul) {
  1217. if (module->editingSequence)
  1218. snprintf(displayStr, 4, "L%2u", (unsigned) module->lengths[module->sequence]);
  1219. else
  1220. snprintf(displayStr, 4, "L%2u", (unsigned) module->phrases);
  1221. }
  1222. else if (module->editingPpqn != 0ul) {
  1223. snprintf(displayStr, 4, "x%2u", (unsigned) module->pulsesPerStep);
  1224. }
  1225. else if (module->displayState == PhraseSeq16::DISP_MODE) {
  1226. if (module->editingSequence)
  1227. runModeToStr(module->runModeSeq[module->sequence]);
  1228. else
  1229. runModeToStr(module->runModeSong);
  1230. }
  1231. else if (module->displayState == PhraseSeq16::DISP_TRANSPOSE) {
  1232. snprintf(displayStr, 4, "+%2u", (unsigned) abs(module->transposeOffset));
  1233. if (module->transposeOffset < 0)
  1234. displayStr[0] = '-';
  1235. }
  1236. else if (module->displayState == PhraseSeq16::DISP_ROTATE) {
  1237. snprintf(displayStr, 4, ")%2u", (unsigned) abs(module->rotateOffset));
  1238. if (module->rotateOffset < 0)
  1239. displayStr[0] = '(';
  1240. }
  1241. else {// DISP_NORMAL
  1242. snprintf(displayStr, 4, " %2u", (unsigned) (module->editingSequence ?
  1243. module->sequence : module->phrase[module->phraseIndexEdit]) + 1 );
  1244. }
  1245. nvgText(vg, textPos.x, textPos.y, displayStr, NULL);
  1246. }
  1247. };
  1248. struct PanelThemeItem : MenuItem {
  1249. PhraseSeq16 *module;
  1250. int theme;
  1251. void onAction(EventAction &e) override {
  1252. module->panelTheme = theme;
  1253. }
  1254. void step() override {
  1255. rightText = (module->panelTheme == theme) ? "✔" : "";
  1256. }
  1257. };
  1258. struct ExpansionItem : MenuItem {
  1259. PhraseSeq16 *module;
  1260. void onAction(EventAction &e) override {
  1261. module->expansion = module->expansion == 1 ? 0 : 1;
  1262. }
  1263. };
  1264. struct ResetOnRunItem : MenuItem {
  1265. PhraseSeq16 *module;
  1266. void onAction(EventAction &e) override {
  1267. module->resetOnRun = !module->resetOnRun;
  1268. }
  1269. };
  1270. Menu *createContextMenu() override {
  1271. Menu *menu = ModuleWidget::createContextMenu();
  1272. MenuLabel *spacerLabel = new MenuLabel();
  1273. menu->addChild(spacerLabel);
  1274. PhraseSeq16 *module = dynamic_cast<PhraseSeq16*>(this->module);
  1275. assert(module);
  1276. MenuLabel *themeLabel = new MenuLabel();
  1277. themeLabel->text = "Panel Theme";
  1278. menu->addChild(themeLabel);
  1279. PanelThemeItem *lightItem = new PanelThemeItem();
  1280. lightItem->text = lightPanelID;// ImpromptuModular.hpp
  1281. lightItem->module = module;
  1282. lightItem->theme = 0;
  1283. menu->addChild(lightItem);
  1284. PanelThemeItem *darkItem = new PanelThemeItem();
  1285. darkItem->text = darkPanelID;// ImpromptuModular.hpp
  1286. darkItem->module = module;
  1287. darkItem->theme = 1;
  1288. menu->addChild(darkItem);
  1289. menu->addChild(new MenuLabel());// empty line
  1290. MenuLabel *settingsLabel = new MenuLabel();
  1291. settingsLabel->text = "Settings";
  1292. menu->addChild(settingsLabel);
  1293. ResetOnRunItem *rorItem = MenuItem::create<ResetOnRunItem>("Reset on Run", CHECKMARK(module->resetOnRun));
  1294. rorItem->module = module;
  1295. menu->addChild(rorItem);
  1296. menu->addChild(new MenuLabel());// empty line
  1297. MenuLabel *expansionLabel = new MenuLabel();
  1298. expansionLabel->text = "Expansion module";
  1299. menu->addChild(expansionLabel);
  1300. ExpansionItem *expItem = MenuItem::create<ExpansionItem>(expansionMenuLabel, CHECKMARK(module->expansion != 0));
  1301. expItem->module = module;
  1302. menu->addChild(expItem);
  1303. return menu;
  1304. }
  1305. void step() override {
  1306. if(module->expansion != oldExpansion) {
  1307. if (oldExpansion!= -1 && module->expansion == 0) {// if just removed expansion panel, disconnect wires to those jacks
  1308. for (int i = 0; i < 5; i++)
  1309. rack::global_ui->app.gRackWidget->wireContainer->removeAllWires(expPorts[i]);
  1310. }
  1311. oldExpansion = module->expansion;
  1312. }
  1313. box.size.x = panel->box.size.x - (1 - module->expansion) * expWidth;
  1314. Widget::step();
  1315. }
  1316. PhraseSeq16Widget(PhraseSeq16 *module) : ModuleWidget(module) {
  1317. this->module = module;
  1318. oldExpansion = -1;
  1319. // Main panel from Inkscape
  1320. panel = new DynamicSVGPanel();
  1321. panel->mode = &module->panelTheme;
  1322. panel->expWidth = &expWidth;
  1323. panel->addPanel(SVG::load(assetPlugin(plugin, "res/light/PhraseSeq16.svg")));
  1324. panel->addPanel(SVG::load(assetPlugin(plugin, "res/dark/PhraseSeq16_dark.svg")));
  1325. box.size = panel->box.size;
  1326. box.size.x = box.size.x - (1 - module->expansion) * expWidth;
  1327. addChild(panel);
  1328. // Screws
  1329. addChild(createDynamicScrew<IMScrew>(Vec(15, 0), &module->panelTheme));
  1330. addChild(createDynamicScrew<IMScrew>(Vec(15, 365), &module->panelTheme));
  1331. addChild(createDynamicScrew<IMScrew>(Vec(panel->box.size.x-30, 0), &module->panelTheme));
  1332. addChild(createDynamicScrew<IMScrew>(Vec(panel->box.size.x-30, 365), &module->panelTheme));
  1333. addChild(createDynamicScrew<IMScrew>(Vec(panel->box.size.x-30-expWidth, 0), &module->panelTheme));
  1334. addChild(createDynamicScrew<IMScrew>(Vec(panel->box.size.x-30-expWidth, 365), &module->panelTheme));
  1335. // ****** Top row ******
  1336. static const int rowRulerT0 = 48;
  1337. static const int columnRulerT0 = 15;// Length button
  1338. static const int columnRulerT1 = columnRulerT0 + 47;// Left/Right buttons
  1339. static const int columnRulerT2 = columnRulerT1 + 75;// Step/Phase lights
  1340. static const int columnRulerT3 = columnRulerT2 + 263;// Attach (also used to align rest of right side of module)
  1341. // Length button
  1342. addParam(createDynamicParam<IMBigPushButton>(Vec(columnRulerT0 + offsetCKD6b, rowRulerT0 + offsetCKD6b), module, PhraseSeq16::LENGTH_PARAM, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  1343. // Left/Right buttons
  1344. addParam(createDynamicParam<IMBigPushButton>(Vec(columnRulerT1 + offsetCKD6b, rowRulerT0 + offsetCKD6b), module, PhraseSeq16::LEFT_PARAM, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  1345. addParam(createDynamicParam<IMBigPushButton>(Vec(columnRulerT1 + 38 + offsetCKD6b, rowRulerT0 + offsetCKD6b), module, PhraseSeq16::RIGHT_PARAM, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  1346. // Step/Phrase lights
  1347. static const int spLightsSpacing = 15;
  1348. for (int i = 0; i < 16; i++) {
  1349. addChild(ModuleLightWidget::create<MediumLight<GreenRedLight>>(Vec(columnRulerT2 + spLightsSpacing * i + offsetMediumLight, rowRulerT0 + offsetMediumLight), module, PhraseSeq16::STEP_PHRASE_LIGHTS + (i*2)));
  1350. }
  1351. // Attach button and light
  1352. addParam(ParamWidget::create<TL1105>(Vec(columnRulerT3 - 4, rowRulerT0 + 2 + offsetTL1105), module, PhraseSeq16::ATTACH_PARAM, 0.0f, 1.0f, 0.0f));
  1353. addChild(ModuleLightWidget::create<MediumLight<RedLight>>(Vec(columnRulerT3 + 12 + offsetMediumLight, rowRulerT0 + offsetMediumLight), module, PhraseSeq16::ATTACH_LIGHT));
  1354. // ****** Octave and keyboard area ******
  1355. // Octave LED buttons
  1356. static const float octLightsIntY = 20.0f;
  1357. for (int i = 0; i < 7; i++) {
  1358. addParam(ParamWidget::create<LEDButton>(Vec(15 + 3, 82 + 24 + i * octLightsIntY- 4.4f), module, PhraseSeq16::OCTAVE_PARAM + i, 0.0f, 1.0f, 0.0f));
  1359. addChild(ModuleLightWidget::create<MediumLight<RedLight>>(Vec(15 + 3 + 4.4f, 82 + 24 + i * octLightsIntY), module, PhraseSeq16::OCTAVE_LIGHTS + i));
  1360. }
  1361. // Keys and Key lights
  1362. static const int keyNudgeX = 7;
  1363. static const int KeyBlackY = 103;
  1364. static const int KeyWhiteY = 141;
  1365. static const int offsetKeyLEDx = 6;
  1366. static const int offsetKeyLEDy = 16;
  1367. // Black keys and lights
  1368. addParam(ParamWidget::create<InvisibleKeySmall>( Vec(65+keyNudgeX, KeyBlackY), module, PhraseSeq16::KEY_PARAMS + 1, 0.0, 1.0, 0.0));
  1369. addChild(ModuleLightWidget::create<MediumLight<GreenRedLight>>(Vec(65+keyNudgeX+offsetKeyLEDx, KeyBlackY+offsetKeyLEDy), module, PhraseSeq16::KEY_LIGHTS + 1 * 2));
  1370. addParam(ParamWidget::create<InvisibleKeySmall>( Vec(93+keyNudgeX, KeyBlackY), module, PhraseSeq16::KEY_PARAMS + 3, 0.0, 1.0, 0.0));
  1371. addChild(ModuleLightWidget::create<MediumLight<GreenRedLight>>(Vec(93+keyNudgeX+offsetKeyLEDx, KeyBlackY+offsetKeyLEDy), module, PhraseSeq16::KEY_LIGHTS + 3 * 2));
  1372. addParam(ParamWidget::create<InvisibleKeySmall>( Vec(150+keyNudgeX, KeyBlackY), module, PhraseSeq16::KEY_PARAMS + 6, 0.0, 1.0, 0.0));
  1373. addChild(ModuleLightWidget::create<MediumLight<GreenRedLight>>(Vec(150+keyNudgeX+offsetKeyLEDx, KeyBlackY+offsetKeyLEDy), module, PhraseSeq16::KEY_LIGHTS + 6 * 2));
  1374. addParam(ParamWidget::create<InvisibleKeySmall>( Vec(178+keyNudgeX, KeyBlackY), module, PhraseSeq16::KEY_PARAMS + 8, 0.0, 1.0, 0.0));
  1375. addChild(ModuleLightWidget::create<MediumLight<GreenRedLight>>(Vec(178+keyNudgeX+offsetKeyLEDx, KeyBlackY+offsetKeyLEDy), module, PhraseSeq16::KEY_LIGHTS + 8 * 2));
  1376. addParam(ParamWidget::create<InvisibleKeySmall>( Vec(206+keyNudgeX, KeyBlackY), module, PhraseSeq16::KEY_PARAMS + 10, 0.0, 1.0, 0.0));
  1377. addChild(ModuleLightWidget::create<MediumLight<GreenRedLight>>(Vec(206+keyNudgeX+offsetKeyLEDx, KeyBlackY+offsetKeyLEDy), module, PhraseSeq16::KEY_LIGHTS + 10 * 2));
  1378. // White keys and lights
  1379. addParam(ParamWidget::create<InvisibleKeySmall>( Vec(51+keyNudgeX, KeyWhiteY), module, PhraseSeq16::KEY_PARAMS + 0, 0.0, 1.0, 0.0));
  1380. addChild(ModuleLightWidget::create<MediumLight<GreenRedLight>>(Vec(51+keyNudgeX+offsetKeyLEDx, KeyWhiteY+offsetKeyLEDy), module, PhraseSeq16::KEY_LIGHTS + 0 * 2));
  1381. addParam(ParamWidget::create<InvisibleKeySmall>( Vec(79+keyNudgeX, KeyWhiteY), module, PhraseSeq16::KEY_PARAMS + 2, 0.0, 1.0, 0.0));
  1382. addChild(ModuleLightWidget::create<MediumLight<GreenRedLight>>(Vec(79+keyNudgeX+offsetKeyLEDx, KeyWhiteY+offsetKeyLEDy), module, PhraseSeq16::KEY_LIGHTS + 2 * 2));
  1383. addParam(ParamWidget::create<InvisibleKeySmall>( Vec(107+keyNudgeX, KeyWhiteY), module, PhraseSeq16::KEY_PARAMS + 4, 0.0, 1.0, 0.0));
  1384. addChild(ModuleLightWidget::create<MediumLight<GreenRedLight>>(Vec(107+keyNudgeX+offsetKeyLEDx, KeyWhiteY+offsetKeyLEDy), module, PhraseSeq16::KEY_LIGHTS + 4 * 2));
  1385. addParam(ParamWidget::create<InvisibleKeySmall>( Vec(136+keyNudgeX, KeyWhiteY), module, PhraseSeq16::KEY_PARAMS + 5, 0.0, 1.0, 0.0));
  1386. addChild(ModuleLightWidget::create<MediumLight<GreenRedLight>>(Vec(136+keyNudgeX+offsetKeyLEDx, KeyWhiteY+offsetKeyLEDy), module, PhraseSeq16::KEY_LIGHTS + 5 * 2));
  1387. addParam(ParamWidget::create<InvisibleKeySmall>( Vec(164+keyNudgeX, KeyWhiteY), module, PhraseSeq16::KEY_PARAMS + 7, 0.0, 1.0, 0.0));
  1388. addChild(ModuleLightWidget::create<MediumLight<GreenRedLight>>(Vec(164+keyNudgeX+offsetKeyLEDx, KeyWhiteY+offsetKeyLEDy), module, PhraseSeq16::KEY_LIGHTS + 7 * 2));
  1389. addParam(ParamWidget::create<InvisibleKeySmall>( Vec(192+keyNudgeX, KeyWhiteY), module, PhraseSeq16::KEY_PARAMS + 9, 0.0, 1.0, 0.0));
  1390. addChild(ModuleLightWidget::create<MediumLight<GreenRedLight>>(Vec(192+keyNudgeX+offsetKeyLEDx, KeyWhiteY+offsetKeyLEDy), module, PhraseSeq16::KEY_LIGHTS + 9 * 2));
  1391. addParam(ParamWidget::create<InvisibleKeySmall>( Vec(220+keyNudgeX, KeyWhiteY), module, PhraseSeq16::KEY_PARAMS + 11, 0.0, 1.0, 0.0));
  1392. addChild(ModuleLightWidget::create<MediumLight<GreenRedLight>>(Vec(220+keyNudgeX+offsetKeyLEDx, KeyWhiteY+offsetKeyLEDy), module, PhraseSeq16::KEY_LIGHTS + 11 * 2));
  1393. // ****** Right side control area ******
  1394. static const int rowRulerMK0 = 101;// Edit mode row
  1395. static const int rowRulerMK1 = rowRulerMK0 + 56; // Run row
  1396. static const int rowRulerMK2 = rowRulerMK1 + 54; // Reset row
  1397. static const int columnRulerMK0 = 277;// Edit mode column
  1398. static const int columnRulerMK1 = columnRulerMK0 + 59;// Display column
  1399. static const int columnRulerMK2 = columnRulerT3;// Run mode column
  1400. // Edit mode switch
  1401. addParam(ParamWidget::create<CKSS>(Vec(columnRulerMK0 + hOffsetCKSS, rowRulerMK0 + vOffsetCKSS), module, PhraseSeq16::EDIT_PARAM, 0.0f, 1.0f, PhraseSeq16::EDIT_PARAM_INIT_VALUE));
  1402. // Sequence display
  1403. SequenceDisplayWidget *displaySequence = new SequenceDisplayWidget();
  1404. displaySequence->box.pos = Vec(columnRulerMK1-15, rowRulerMK0 + 3 + vOffsetDisplay);
  1405. displaySequence->box.size = Vec(55, 30);// 3 characters
  1406. displaySequence->module = module;
  1407. addChild(displaySequence);
  1408. // Run mode button
  1409. addParam(createDynamicParam<IMBigPushButton>(Vec(columnRulerMK2 + offsetCKD6b, rowRulerMK0 + 0 + offsetCKD6b), module, PhraseSeq16::RUNMODE_PARAM, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  1410. // Run LED bezel and light
  1411. addParam(ParamWidget::create<LEDBezel>(Vec(columnRulerMK0 + offsetLEDbezel, rowRulerMK1 + 7 + offsetLEDbezel), module, PhraseSeq16::RUN_PARAM, 0.0f, 1.0f, 0.0f));
  1412. addChild(ModuleLightWidget::create<MuteLight<GreenLight>>(Vec(columnRulerMK0 + offsetLEDbezel + offsetLEDbezelLight, rowRulerMK1 + 7 + offsetLEDbezel + offsetLEDbezelLight), module, PhraseSeq16::RUN_LIGHT));
  1413. // Sequence knob
  1414. addParam(createDynamicParam<IMBigKnobInf>(Vec(columnRulerMK1 + 1 + offsetIMBigKnob, rowRulerMK0 + 55 + offsetIMBigKnob), module, PhraseSeq16::SEQUENCE_PARAM, -INFINITY, INFINITY, 0.0f, &module->panelTheme));
  1415. // Transpose/rotate button
  1416. addParam(createDynamicParam<IMBigPushButton>(Vec(columnRulerMK2 + offsetCKD6b, rowRulerMK1 + 4 + offsetCKD6b), module, PhraseSeq16::TRAN_ROT_PARAM, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  1417. // Reset LED bezel and light
  1418. addParam(ParamWidget::create<LEDBezel>(Vec(columnRulerMK0 + offsetLEDbezel, rowRulerMK2 + 5 + offsetLEDbezel), module, PhraseSeq16::RESET_PARAM, 0.0f, 1.0f, 0.0f));
  1419. addChild(ModuleLightWidget::create<MuteLight<GreenLight>>(Vec(columnRulerMK0 + offsetLEDbezel + offsetLEDbezelLight, rowRulerMK2 + 5 + offsetLEDbezel + offsetLEDbezelLight), module, PhraseSeq16::RESET_LIGHT));
  1420. // Copy/paste buttons
  1421. addParam(ParamWidget::create<TL1105>(Vec(columnRulerMK1 - 10, rowRulerMK2 + 5 + offsetTL1105), module, PhraseSeq16::COPY_PARAM, 0.0f, 1.0f, 0.0f));
  1422. addParam(ParamWidget::create<TL1105>(Vec(columnRulerMK1 + 20, rowRulerMK2 + 5 + offsetTL1105), module, PhraseSeq16::PASTE_PARAM, 0.0f, 1.0f, 0.0f));
  1423. // Copy-paste mode switch (3 position)
  1424. addParam(ParamWidget::create<CKSSThreeInv>(Vec(columnRulerMK2 + hOffsetCKSS + 1, rowRulerMK2 - 3 + vOffsetCKSSThree), module, PhraseSeq16::CPMODE_PARAM, 0.0f, 2.0f, 2.0f)); // 0.0f is top position
  1425. // ****** Gate and slide section ******
  1426. static const int rowRulerMB0 = 214;
  1427. static const int columnRulerMBspacing = 70;
  1428. static const int columnRulerMB2 = 130;// Gate2
  1429. static const int columnRulerMB1 = columnRulerMB2 - columnRulerMBspacing;// Gate1
  1430. static const int columnRulerMB3 = columnRulerMB2 + columnRulerMBspacing;// Tie
  1431. static const int posLEDvsButton = + 25;
  1432. // Gate 1 light and button
  1433. addChild(ModuleLightWidget::create<MediumLight<GreenRedLight>>(Vec(columnRulerMB1 + posLEDvsButton + offsetMediumLight, rowRulerMB0 + 4 + offsetMediumLight), module, PhraseSeq16::GATE1_LIGHT));
  1434. addParam(createDynamicParam<IMBigPushButton>(Vec(columnRulerMB1 + offsetCKD6b, rowRulerMB0 + 4 + offsetCKD6b), module, PhraseSeq16::GATE1_PARAM, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  1435. // Gate 2 light and button
  1436. addChild(ModuleLightWidget::create<MediumLight<GreenRedLight>>(Vec(columnRulerMB2 + posLEDvsButton + offsetMediumLight, rowRulerMB0 + 4 + offsetMediumLight), module, PhraseSeq16::GATE2_LIGHT));
  1437. addParam(createDynamicParam<IMBigPushButton>(Vec(columnRulerMB2 + offsetCKD6b, rowRulerMB0 + 4 + offsetCKD6b), module, PhraseSeq16::GATE2_PARAM, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  1438. // Tie light and button
  1439. addChild(ModuleLightWidget::create<MediumLight<RedLight>>(Vec(columnRulerMB3 + posLEDvsButton + offsetMediumLight, rowRulerMB0 + 4 + offsetMediumLight), module, PhraseSeq16::TIE_LIGHT));
  1440. addParam(createDynamicParam<IMBigPushButton>(Vec(columnRulerMB3 + offsetCKD6b, rowRulerMB0 + 4 + offsetCKD6b), module, PhraseSeq16::TIE_PARAM, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  1441. // ****** Bottom two rows ******
  1442. static const int outputJackSpacingX = 54;
  1443. static const int rowRulerB0 = 323;
  1444. static const int rowRulerB1 = 269;
  1445. static const int columnRulerB0 = 22;
  1446. static const int columnRulerB1 = columnRulerB0 + outputJackSpacingX;
  1447. static const int columnRulerB2 = columnRulerB1 + outputJackSpacingX;
  1448. static const int columnRulerB3 = columnRulerB2 + outputJackSpacingX;
  1449. static const int columnRulerB4 = columnRulerB3 + outputJackSpacingX;
  1450. static const int columnRulerB7 = columnRulerMK2 + 1;
  1451. static const int columnRulerB6 = columnRulerB7 - outputJackSpacingX;
  1452. static const int columnRulerB5 = columnRulerB6 - outputJackSpacingX;
  1453. // Gate 1 probability light and button
  1454. addChild(ModuleLightWidget::create<MediumLight<RedLight>>(Vec(columnRulerB0 + posLEDvsButton + offsetMediumLight, rowRulerB1 + offsetMediumLight), module, PhraseSeq16::GATE1_PROB_LIGHT));
  1455. addParam(createDynamicParam<IMBigPushButton>(Vec(columnRulerB0 + offsetCKD6b, rowRulerB1 + offsetCKD6b), module, PhraseSeq16::GATE1_PROB_PARAM, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  1456. // Gate 1 probability knob
  1457. addParam(createDynamicParam<IMSmallKnob>(Vec(columnRulerB1 + offsetIMSmallKnob, rowRulerB1 + offsetIMSmallKnob), module, PhraseSeq16::GATE1_KNOB_PARAM, 0.0f, 1.0f, 1.0f, &module->panelTheme));
  1458. // Slide light and button
  1459. addChild(ModuleLightWidget::create<MediumLight<RedLight>>(Vec(columnRulerB2 + posLEDvsButton + offsetMediumLight, rowRulerB1 + offsetMediumLight), module, PhraseSeq16::SLIDE_LIGHT));
  1460. addParam(createDynamicParam<IMBigPushButton>(Vec(columnRulerB2 + offsetCKD6b, rowRulerB1 + offsetCKD6b), module, PhraseSeq16::SLIDE_BTN_PARAM, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  1461. // Slide knob
  1462. addParam(createDynamicParam<IMSmallKnob>(Vec(columnRulerB3 + offsetIMSmallKnob, rowRulerB1 + offsetIMSmallKnob), module, PhraseSeq16::SLIDE_KNOB_PARAM, 0.0f, 2.0f, 0.2f, &module->panelTheme));
  1463. // Autostep
  1464. addParam(ParamWidget::create<CKSS>(Vec(columnRulerB4 + hOffsetCKSS, rowRulerB1 + vOffsetCKSS), module, PhraseSeq16::AUTOSTEP_PARAM, 0.0f, 1.0f, 1.0f));
  1465. // CV in
  1466. addInput(createDynamicPort<IMPort>(Vec(columnRulerB5, rowRulerB1), Port::INPUT, module, PhraseSeq16::CV_INPUT, &module->panelTheme));
  1467. // Clock
  1468. addInput(createDynamicPort<IMPort>(Vec(columnRulerB6, rowRulerB1), Port::INPUT, module, PhraseSeq16::CLOCK_INPUT, &module->panelTheme));
  1469. // Reset
  1470. addInput(createDynamicPort<IMPort>(Vec(columnRulerB7, rowRulerB1), Port::INPUT, module, PhraseSeq16::RESET_INPUT, &module->panelTheme));
  1471. // ****** Bottom row (all aligned) ******
  1472. // CV control Inputs
  1473. addInput(createDynamicPort<IMPort>(Vec(columnRulerB0, rowRulerB0), Port::INPUT, module, PhraseSeq16::LEFTCV_INPUT, &module->panelTheme));
  1474. addInput(createDynamicPort<IMPort>(Vec(columnRulerB1, rowRulerB0), Port::INPUT, module, PhraseSeq16::RIGHTCV_INPUT, &module->panelTheme));
  1475. addInput(createDynamicPort<IMPort>(Vec(columnRulerB2, rowRulerB0), Port::INPUT, module, PhraseSeq16::SEQCV_INPUT, &module->panelTheme));
  1476. addInput(createDynamicPort<IMPort>(Vec(columnRulerB3, rowRulerB0), Port::INPUT, module, PhraseSeq16::RUNCV_INPUT, &module->panelTheme));
  1477. addInput(createDynamicPort<IMPort>(Vec(columnRulerB4, rowRulerB0), Port::INPUT, module, PhraseSeq16::WRITE_INPUT, &module->panelTheme));
  1478. // Outputs
  1479. addOutput(createDynamicPort<IMPort>(Vec(columnRulerB5, rowRulerB0), Port::OUTPUT, module, PhraseSeq16::CV_OUTPUT, &module->panelTheme));
  1480. addOutput(createDynamicPort<IMPort>(Vec(columnRulerB6, rowRulerB0), Port::OUTPUT, module, PhraseSeq16::GATE1_OUTPUT, &module->panelTheme));
  1481. addOutput(createDynamicPort<IMPort>(Vec(columnRulerB7, rowRulerB0), Port::OUTPUT, module, PhraseSeq16::GATE2_OUTPUT, &module->panelTheme));
  1482. // Expansion module
  1483. static const int rowRulerExpTop = 65;
  1484. static const int rowSpacingExp = 60;
  1485. static const int colRulerExp = 497 - 30;// PS16 is 2HP less than PS32
  1486. addInput(expPorts[0] = createDynamicPort<IMPort>(Vec(colRulerExp, rowRulerExpTop + rowSpacingExp * 0), Port::INPUT, module, PhraseSeq16::GATE1CV_INPUT, &module->panelTheme));
  1487. addInput(expPorts[1] = createDynamicPort<IMPort>(Vec(colRulerExp, rowRulerExpTop + rowSpacingExp * 1), Port::INPUT, module, PhraseSeq16::GATE2CV_INPUT, &module->panelTheme));
  1488. addInput(expPorts[2] = createDynamicPort<IMPort>(Vec(colRulerExp, rowRulerExpTop + rowSpacingExp * 2), Port::INPUT, module, PhraseSeq16::TIEDCV_INPUT, &module->panelTheme));
  1489. addInput(expPorts[3] = createDynamicPort<IMPort>(Vec(colRulerExp, rowRulerExpTop + rowSpacingExp * 3), Port::INPUT, module, PhraseSeq16::SLIDECV_INPUT, &module->panelTheme));
  1490. addInput(expPorts[4] = createDynamicPort<IMPort>(Vec(colRulerExp, rowRulerExpTop + rowSpacingExp * 4), Port::INPUT, module, PhraseSeq16::MODECV_INPUT, &module->panelTheme));
  1491. }
  1492. };
  1493. } // namespace rack_plugin_ImpromptuModular
  1494. using namespace rack_plugin_ImpromptuModular;
  1495. RACK_PLUGIN_MODEL_INIT(ImpromptuModular, PhraseSeq16) {
  1496. Model *modelPhraseSeq16 = Model::create<PhraseSeq16, PhraseSeq16Widget>("Impromptu Modular", "Phrase-Seq-16", "SEQ - Phrase-Seq-16", SEQUENCER_TAG);
  1497. return modelPhraseSeq16;
  1498. }
  1499. /*CHANGE LOG
  1500. 0.6.11:
  1501. step optimization of lights refresh
  1502. 0.6.10:
  1503. add advanced gate mode
  1504. unlock gates when tied (turn off when press tied, but allow to be turned back on)
  1505. allow main knob to also change length when length editing is active
  1506. 0.6.9:
  1507. add FW2, FW3 and FW4 run modes for sequences (but not for song)
  1508. right click on notes now does same as left click but with autostep
  1509. 0.6.8:
  1510. show length in display when editing length
  1511. 0.6.7:
  1512. allow full edit capabilities in Seq and song mode
  1513. no reset on run by default, with switch added in context menu
  1514. reset does not revert seq or song number to 1
  1515. gate 2 is off by default
  1516. fix emitted monitoring gates to depend on gate states instead of always triggering
  1517. 0.6.6:
  1518. knob bug fixes when loading patch
  1519. 0.6.5:
  1520. paste 4, 8 doesn't loop over to overwrite first steps
  1521. small adjustements to gates and CVs used in monitoring write operations
  1522. add GATE1, GATE2, TIED, SLIDE CV inputs
  1523. add MODE CV input (affects only selected sequence and in Seq mode)
  1524. add expansion panel option
  1525. 0.6.4:
  1526. turn off keyboard and oct lights when detached in song mode (makes no sense, can't mod steps and shows stepping though seq that may not be playing)
  1527. removed mode CV input, added paste 4/8/ALL option (ALL copies length and run mode also)
  1528. allow each sequence to have its own length and run mode
  1529. merged functionalities of transpose and rotate into one knob
  1530. implemented tied notes state bit for each step, and added light to tied steps
  1531. implemented 0-T slide as opposed to 0-2s slide, where T is clock period
  1532. changed copy-paste indication, now uses display rather than keyboard lights
  1533. 0.6.3:
  1534. added tie step macro button
  1535. added gate probabilities (one prob setting for all steps)
  1536. removed paste-sync
  1537. 0.6.2:
  1538. initial release of PS16
  1539. */