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.

1200 lines
45KB

  1. #include "global_pre.hpp"
  2. #include "Bidoo.hpp"
  3. #include "dsp/digital.hpp"
  4. #include "BidooComponents.hpp"
  5. #include "global_ui.hpp"
  6. #include <ctime>
  7. #include <iostream>
  8. #include <vector>
  9. #include <random>
  10. #include <algorithm>
  11. using namespace std;
  12. namespace rack_plugin_Bidoo {
  13. struct Step {
  14. int index = 0;
  15. int number = 0;
  16. bool skip = false;
  17. bool skipParam = false;
  18. bool slide = false;
  19. int pulses = 1;
  20. int pulsesParam = 1;
  21. float pitch = 3.0f;
  22. int type = 2;
  23. };
  24. struct Pattern {
  25. int playMode = 0;
  26. int countMode = 0;
  27. int numberOfSteps = 8;
  28. int numberOfStepsParam = 8;
  29. int rootNote = 0;
  30. int rootNoteParam = 0;
  31. int scale = 0;
  32. int scaleParam = 0;
  33. float gateTime = 0.5f;
  34. float slideTime = 0.2f;
  35. float sensitivity = 1.0f;
  36. int currentStep = 0;
  37. int currentPulse = 0;
  38. bool forward = true;
  39. std::vector<Step> steps {16};
  40. void Update(int playMode, int countMode, int numberOfSteps, int numberOfStepsParam, int rootNote, int scale, float gateTime, float slideTime, float sensitivity, std::vector<char> skips, std::vector<char> slides, std::vector<Param> pulses, std::vector<Param> pitches, std::vector<Param> types) {
  41. this->playMode = playMode;
  42. this->countMode = countMode;
  43. this->numberOfSteps = numberOfSteps;
  44. this->numberOfStepsParam = numberOfStepsParam;
  45. this->rootNote = rootNote;
  46. this->scale = scale;
  47. this->rootNoteParam = rootNote;
  48. this->scaleParam = scale;
  49. this->gateTime = gateTime;
  50. this->slideTime = slideTime;
  51. this->sensitivity = sensitivity;
  52. int pCount = 0;
  53. for (int i = 0; i < 16; i++) {
  54. steps[i].index = i%8;
  55. steps[i].number = i;
  56. if (((countMode == 0) && (i < numberOfSteps)) || ((countMode == 1) && (pCount < numberOfSteps))) {
  57. steps[i].skip = (skips[i%8] == 't');
  58. } else {
  59. steps[i].skip = true;
  60. }
  61. steps[i].skipParam = (skips[i%8] == 't');
  62. steps[i].slide = (slides[i%8] == 't');
  63. if ((countMode == 1) && ((pCount + (int)pulses[i%8].value) >= numberOfSteps)) {
  64. steps[i].pulses = max(numberOfSteps - pCount, 0);
  65. } else {
  66. steps[i].pulses = (int)pulses[i%8].value;
  67. }
  68. steps[i].pulsesParam = (int)pulses[i%8].value;
  69. pCount = pCount + steps[i].pulses;
  70. steps[i].pitch = pitches[i%8].value;
  71. steps[i].type = (int)types[i%8].value;
  72. }
  73. }
  74. std::tuple<int, int> GetNextStep(bool reset)
  75. {
  76. if (reset) {
  77. if (playMode != 1) {
  78. currentStep = GetFirstStep();
  79. } else {
  80. currentStep = GetLastStep();
  81. }
  82. currentPulse = 0;
  83. return std::make_tuple(currentStep,currentPulse);
  84. } else {
  85. if (currentPulse < steps[currentStep].pulses - 1) {
  86. currentPulse++;
  87. return std::make_tuple(steps[currentStep%16].index,currentPulse);
  88. } else {
  89. if (playMode == 0) {
  90. currentStep = GetNextStepForward(currentStep);
  91. currentPulse = 0;
  92. return std::make_tuple(steps[currentStep%16].index,currentPulse);
  93. } else if (playMode == 1) {
  94. currentStep = GetNextStepBackward(currentStep);
  95. currentPulse = 0;
  96. return std::make_tuple(steps[currentStep%16].index,currentPulse);
  97. } else if (playMode == 2) {
  98. if (currentStep == GetLastStep()) {
  99. forward = false;
  100. }
  101. if (currentStep == GetFirstStep()) {
  102. forward = true;
  103. }
  104. if (forward) {
  105. currentStep = GetNextStepForward(currentStep);
  106. } else {
  107. currentStep = GetNextStepBackward(currentStep);
  108. }
  109. currentPulse = 0;
  110. return std::make_tuple(steps[currentStep%16].index,currentPulse);
  111. } else if (playMode == 3) {
  112. std::vector<Step> tmp (steps.size());
  113. auto it = std::copy_if (steps.begin(), steps.end(), tmp.begin(), [](Step i){return !(i.skip);} );
  114. tmp.resize(std::distance(tmp.begin(),it)); // shrink container to new size
  115. Step tmpStep = *select_randomly(tmp.begin(), tmp.end());
  116. currentPulse = 0;
  117. currentStep = tmpStep.number;
  118. return std::make_tuple(steps[currentStep%16].index,currentPulse);
  119. } else if (playMode == 4) {
  120. int next = GetNextStepForward(currentStep);
  121. int prev = GetNextStepBackward(currentStep);
  122. vector<Step> subPattern;
  123. subPattern.push_back(steps[prev]);
  124. subPattern.push_back(steps[next]);
  125. Step choice = *select_randomly(subPattern.begin(), subPattern.end());
  126. currentPulse = 0;
  127. currentStep = choice.number;
  128. return std::make_tuple(steps[currentStep%16].index,currentPulse);
  129. } else {
  130. return std::make_tuple(0,0);
  131. }
  132. }
  133. }
  134. }
  135. Step CurrentStep() {
  136. return this->steps[currentStep];
  137. }
  138. int GetFirstStep()
  139. {
  140. for (int i = 0; i < 16; i++) {
  141. if (!steps[i].skip) {
  142. return i;
  143. }
  144. }
  145. return 0;
  146. }
  147. int GetLastStep()
  148. {
  149. for (int i = 15; i >= 0 ; i--) {
  150. if (!steps[i].skip) {
  151. return i;
  152. }
  153. }
  154. return 15;
  155. }
  156. int GetNextStepForward(int pos)
  157. {
  158. for (int i = pos + 1; i < pos + 16; i++) {
  159. if (!steps[i%16].skip) {
  160. return i%16;
  161. }
  162. }
  163. return pos;
  164. }
  165. int GetNextStepBackward(int pos)
  166. {
  167. for (int i = pos - 1; i > pos - 16; i--) {
  168. if (!steps[i%16 + (i<0?16:0)].skip) {
  169. return i%16 + (i<0?16:0);
  170. }
  171. }
  172. return pos;
  173. }
  174. template<typename Iter, typename RandomGenerator> Iter select_randomly(Iter start, Iter end, RandomGenerator& g) {
  175. std::uniform_int_distribution<> dis(0, std::distance(start, end) - 1);
  176. std::advance(start, dis(g));
  177. return start;
  178. }
  179. template<typename Iter> Iter select_randomly(Iter start, Iter end) {
  180. static std::random_device rd;
  181. static std::mt19937 gen(rd());
  182. return select_randomly(start, end, gen);
  183. }
  184. };
  185. struct DTROY : Module {
  186. enum ParamIds {
  187. CLOCK_PARAM,
  188. RUN_PARAM,
  189. RESET_PARAM,
  190. STEPS_PARAM,
  191. SLIDE_TIME_PARAM,
  192. GATE_TIME_PARAM,
  193. ROOT_NOTE_PARAM,
  194. SCALE_PARAM,
  195. PLAY_MODE_PARAM,
  196. COUNT_MODE_PARAM,
  197. PATTERN_PARAM,
  198. SENSITIVITY_PARAM,
  199. TRIG_COUNT_PARAM = SENSITIVITY_PARAM + 8,
  200. TRIG_TYPE_PARAM = TRIG_COUNT_PARAM + 8,
  201. TRIG_PITCH_PARAM = TRIG_TYPE_PARAM + 8,
  202. TRIG_SLIDE_PARAM = TRIG_PITCH_PARAM + 8,
  203. TRIG_SKIP_PARAM = TRIG_SLIDE_PARAM + 8,
  204. NUM_PARAMS = TRIG_SKIP_PARAM + 8
  205. };
  206. enum InputIds {
  207. CLOCK_INPUT,
  208. EXT_CLOCK_INPUT,
  209. RESET_INPUT,
  210. STEPS_INPUT,
  211. SLIDE_TIME_INPUT,
  212. GATE_TIME_INPUT,
  213. ROOT_NOTE_INPUT,
  214. SCALE_INPUT,
  215. EXTGATE1_INPUT,
  216. EXTGATE2_INPUT,
  217. PATTERN_INPUT,
  218. NUM_INPUTS
  219. };
  220. enum OutputIds {
  221. GATE_OUTPUT,
  222. PITCH_OUTPUT,
  223. NUM_OUTPUTS
  224. };
  225. enum LightIds {
  226. RUNNING_LIGHT,
  227. RESET_LIGHT,
  228. GATE_LIGHT,
  229. STEPS_LIGHTS = GATE_LIGHT + 8,
  230. SLIDES_LIGHTS = STEPS_LIGHTS + 8,
  231. SKIPS_LIGHTS = SLIDES_LIGHTS + 8,
  232. NUM_LIGHTS = SKIPS_LIGHTS + 8
  233. };
  234. //copied from http://www.grantmuller.com/MidiReference/doc/midiReference/ScaleReference.html
  235. int SCALE_AEOLIAN [7] = {0, 2, 3, 5, 7, 8, 10};
  236. int SCALE_BLUES [9] = {0, 2, 3, 4, 5, 7, 9, 10, 11};
  237. int SCALE_CHROMATIC [12]= {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
  238. int SCALE_DIATONIC_MINOR [7] = {0, 2, 3, 5, 7, 8, 10};
  239. int SCALE_DORIAN [7] = {0, 2, 3, 5, 7, 9, 10};
  240. int SCALE_HARMONIC_MINOR [7] = {0, 2, 3, 5, 7, 8, 11};
  241. int SCALE_INDIAN [7] = {0, 1, 1, 4, 5, 8, 10};
  242. int SCALE_LOCRIAN [7] = {0, 1, 3, 5, 6, 8, 10};
  243. int SCALE_LYDIAN [7] = {0, 2, 4, 6, 7, 9, 10};
  244. int SCALE_MAJOR [7] = {0, 2, 4, 5, 7, 9, 11};
  245. int SCALE_MELODIC_MINOR [9] = {0, 2, 3, 5, 7, 8, 9, 10, 11};
  246. int SCALE_MINOR [7] = {0, 2, 3, 5, 7, 8, 10};
  247. int SCALE_MIXOLYDIAN [7] = {0, 2, 4, 5, 7, 9, 10};
  248. int SCALE_NATURAL_MINOR [7] = {0, 2, 3, 5, 7, 8, 10};
  249. int SCALE_PENTATONIC [5] = {0, 2, 4, 7, 9};
  250. int SCALE_PHRYGIAN [7] = {0, 1, 3, 5, 7, 8, 10};
  251. int SCALE_TURKISH [7] = {0, 1, 3, 5, 7, 10, 11};
  252. enum Notes {
  253. NOTE_C,
  254. NOTE_C_SHARP,
  255. NOTE_D,
  256. NOTE_D_SHARP,
  257. NOTE_E,
  258. NOTE_F,
  259. NOTE_F_SHARP,
  260. NOTE_G,
  261. NOTE_G_SHARP,
  262. NOTE_A,
  263. NOTE_A_SHARP,
  264. NOTE_B,
  265. NUM_NOTES
  266. };
  267. enum Scales {
  268. AEOLIAN,
  269. BLUES,
  270. CHROMATIC,
  271. DIATONIC_MINOR,
  272. DORIAN,
  273. HARMONIC_MINOR,
  274. INDIAN,
  275. LOCRIAN,
  276. LYDIAN,
  277. MAJOR,
  278. MELODIC_MINOR,
  279. MINOR,
  280. MIXOLYDIAN,
  281. NATURAL_MINOR,
  282. PENTATONIC,
  283. PHRYGIAN,
  284. TURKISH,
  285. NONE,
  286. NUM_SCALES
  287. };
  288. bool running = true;
  289. SchmittTrigger clockTrigger;
  290. SchmittTrigger runningTrigger;
  291. SchmittTrigger resetTrigger;
  292. SchmittTrigger slideTriggers[8];
  293. SchmittTrigger skipTriggers[8];
  294. SchmittTrigger playModeTrigger;
  295. SchmittTrigger countModeTrigger;
  296. SchmittTrigger PatternTrigger;
  297. float phase = 0.0f;
  298. int index = 0;
  299. bool reStart = true;
  300. int pulse = 0;
  301. int rootNote = 0;
  302. int curScaleVal = 0;
  303. float pitch = 0.0f;
  304. float previousPitch = 0.0f;
  305. clock_t tCurrent;
  306. clock_t tLastTrig;
  307. clock_t tPreviousTrig;
  308. std::vector<char> slideState = {'f','f','f','f','f','f','f','f'};
  309. std::vector<char> skipState = {'f','f','f','f','f','f','f','f'};
  310. int playMode = 0; // 0 forward, 1 backward, 2 pingpong, 3 random, 4 brownian
  311. int countMode = 0; // 0 steps, 1 pulses
  312. int numSteps = 8;
  313. int selectedPattern = 0;
  314. int playedPattern = 0;
  315. bool pitchMode = false;
  316. bool pitchQuantizeMode = true;
  317. bool updateFlag = false;
  318. bool first = true;
  319. bool loadedFromJson = false;
  320. int copyPattern = -1;
  321. Pattern patterns[16];
  322. DTROY() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  323. }
  324. void UpdatePattern() {
  325. std::vector<Param> pulses(&params[TRIG_COUNT_PARAM],&params[TRIG_COUNT_PARAM + 8]);
  326. std::vector<Param> pitches(&params[TRIG_PITCH_PARAM],&params[TRIG_PITCH_PARAM + 8]);
  327. std::vector<Param> types(&params[TRIG_TYPE_PARAM],&params[TRIG_TYPE_PARAM + 8]);
  328. patterns[selectedPattern].Update(playMode, countMode, numSteps, roundf(params[STEPS_PARAM].value), roundf(params[ROOT_NOTE_PARAM].value), roundf(params[SCALE_PARAM].value), params[GATE_TIME_PARAM].value, params[SLIDE_TIME_PARAM].value, params[SENSITIVITY_PARAM].value , skipState, slideState, pulses, pitches, types);
  329. }
  330. void step() override;
  331. // persistence, random & init
  332. json_t *toJson() override {
  333. json_t *rootJ = json_object();
  334. json_object_set_new(rootJ, "running", json_boolean(running));
  335. json_object_set_new(rootJ, "playMode", json_integer(playMode));
  336. json_object_set_new(rootJ, "countMode", json_integer(countMode));
  337. json_object_set_new(rootJ, "pitchMode", json_boolean(pitchMode));
  338. json_object_set_new(rootJ, "pitchQuantizeMode", json_boolean(pitchQuantizeMode));
  339. json_object_set_new(rootJ, "selectedPattern", json_integer(selectedPattern));
  340. json_object_set_new(rootJ, "playedPattern", json_integer(playedPattern));
  341. json_t *trigsJ = json_array();
  342. for (int i = 0; i < 8; i++) {
  343. json_t *trigJ = json_array();
  344. json_t *trigJSlide = json_boolean(slideState[i] == 't');
  345. json_array_append_new(trigJ, trigJSlide);
  346. json_t *trigJSkip = json_boolean(skipState[i] == 't');
  347. json_array_append_new(trigJ, trigJSkip);
  348. json_array_append_new(trigsJ, trigJ);
  349. }
  350. json_object_set_new(rootJ, "trigs", trigsJ);
  351. for (int i = 0; i<16; i++) {
  352. json_t *patternJ = json_object();
  353. json_object_set_new(patternJ, "playMode", json_integer(patterns[i].playMode));
  354. json_object_set_new(patternJ, "countMode", json_integer(patterns[i].countMode));
  355. json_object_set_new(patternJ, "numSteps", json_integer(patterns[i].numberOfStepsParam));
  356. json_object_set_new(patternJ, "rootNote", json_integer(patterns[i].rootNote));
  357. json_object_set_new(patternJ, "scale", json_integer(patterns[i].scale));
  358. json_object_set_new(patternJ, "gateTime", json_real(patterns[i].gateTime));
  359. json_object_set_new(patternJ, "slideTime", json_real(patterns[i].slideTime));
  360. json_object_set_new(patternJ, "sensitivity", json_real(patterns[i].sensitivity));
  361. for (int j = 0; j < 16; j++) {
  362. json_t *stepJ = json_object();
  363. json_object_set_new(stepJ, "index", json_integer(patterns[i].steps[j].index));
  364. json_object_set_new(stepJ, "number", json_integer(patterns[i].steps[j].number));
  365. json_object_set_new(stepJ, "skip", json_integer((int)patterns[i].steps[j].skip));
  366. json_object_set_new(stepJ, "skipParam", json_integer((int)patterns[i].steps[j].skipParam));
  367. json_object_set_new(stepJ, "slide", json_integer((int)patterns[i].steps[j].slide));
  368. json_object_set_new(stepJ, "pulses", json_integer(patterns[i].steps[j].pulses));
  369. json_object_set_new(stepJ, "pulsesParam", json_integer(patterns[i].steps[j].pulsesParam));
  370. json_object_set_new(stepJ, "pitch", json_real(patterns[i].steps[j].pitch));
  371. json_object_set_new(stepJ, "type", json_integer(patterns[i].steps[j].type));
  372. json_object_set_new(patternJ, ("step" + to_string(j)).c_str() , stepJ);
  373. }
  374. json_object_set_new(rootJ, ("pattern" + to_string(i)).c_str(), patternJ);
  375. }
  376. return rootJ;
  377. }
  378. void fromJson(json_t *rootJ) override {
  379. json_t *runningJ = json_object_get(rootJ, "running");
  380. if (runningJ)
  381. running = json_is_true(runningJ);
  382. json_t *playModeJ = json_object_get(rootJ, "playMode");
  383. if (playModeJ)
  384. playMode = json_integer_value(playModeJ);
  385. json_t *countModeJ = json_object_get(rootJ, "countMode");
  386. if (countModeJ)
  387. countMode = json_integer_value(countModeJ);
  388. json_t *selectedPatternJ = json_object_get(rootJ, "selectedPattern");
  389. if (selectedPatternJ)
  390. selectedPattern = json_integer_value(selectedPatternJ);
  391. json_t *playedPatternJ = json_object_get(rootJ, "playedPattern");
  392. if (playedPatternJ)
  393. playedPattern = json_integer_value(playedPatternJ);
  394. json_t *pitchModeJ = json_object_get(rootJ, "pitchMode");
  395. if (pitchModeJ)
  396. pitchMode = json_is_true(pitchModeJ);
  397. json_t *pitchQuantizeModeJ = json_object_get(rootJ, "pitchQuantizeMode");
  398. if (pitchQuantizeModeJ)
  399. pitchQuantizeMode = json_is_true(pitchQuantizeModeJ);
  400. json_t *trigsJ = json_object_get(rootJ, "trigs");
  401. if (trigsJ) {
  402. for (int i = 0; i < 8; i++) {
  403. json_t *trigJ = json_array_get(trigsJ, i);
  404. if (trigJ)
  405. {
  406. slideState[i] = json_is_true(json_array_get(trigJ, 0)) ? 't' : 'f';
  407. skipState[i] = json_is_true(json_array_get(trigJ, 1)) ? 't' : 'f';
  408. }
  409. }
  410. }
  411. for (int i=0; i<16;i++) {
  412. json_t *patternJ = json_object_get(rootJ, ("pattern" + to_string(i)).c_str());
  413. if (patternJ){
  414. json_t *playModeJ = json_object_get(patternJ, "playMode");
  415. if (playModeJ)
  416. patterns[i].playMode = json_integer_value(playModeJ);
  417. json_t *countModeJ = json_object_get(patternJ, "countMode");
  418. if (countModeJ)
  419. patterns[i].countMode = json_integer_value(countModeJ);
  420. json_t *numStepsJ = json_object_get(patternJ, "numSteps");
  421. if (numStepsJ)
  422. patterns[i].numberOfStepsParam = json_integer_value(numStepsJ);
  423. json_t *rootNoteJ = json_object_get(patternJ, "rootNote");
  424. if (rootNoteJ)
  425. patterns[i].rootNote = json_integer_value(rootNoteJ);
  426. json_t *scaleJ = json_object_get(patternJ, "scale");
  427. if (scaleJ)
  428. patterns[i].scale = json_integer_value(scaleJ);
  429. json_t *gateTimeJ = json_object_get(patternJ, "gateTime");
  430. if (gateTimeJ)
  431. patterns[i].gateTime = json_real_value(gateTimeJ);
  432. json_t *slideTimeJ = json_object_get(patternJ, "slideTime");
  433. if (slideTimeJ)
  434. patterns[i].slideTime = json_real_value(slideTimeJ);
  435. json_t *sensitivityJ = json_object_get(patternJ, "sensitivity");
  436. if (sensitivityJ)
  437. patterns[i].sensitivity = json_real_value(sensitivityJ);
  438. for (int j = 0; j < 16; j++) {
  439. json_t *stepJ = json_object_get(patternJ, ("step" + to_string(j)).c_str());
  440. if (stepJ) {
  441. json_t *indexJ= json_object_get(stepJ, "index");
  442. if (indexJ)
  443. patterns[i].steps[j].index = json_integer_value(indexJ);
  444. json_t *numberJ= json_object_get(stepJ, "numer");
  445. if (numberJ)
  446. patterns[i].steps[j].number = json_integer_value(numberJ);
  447. json_t *skipJ= json_object_get(stepJ, "skip");
  448. if (skipJ)
  449. patterns[i].steps[j].skip = !!json_integer_value(skipJ);
  450. json_t *skipParamJ= json_object_get(stepJ, "skipParam");
  451. if (skipParamJ)
  452. patterns[i].steps[j].skipParam = !!json_integer_value(skipParamJ);
  453. json_t *slideJ= json_object_get(stepJ, "slide");
  454. if (slideJ)
  455. patterns[i].steps[j].slide = !!json_integer_value(slideJ);
  456. json_t *pulsesJ= json_object_get(stepJ, "pulses");
  457. if (pulsesJ)
  458. patterns[i].steps[j].pulses = json_integer_value(pulsesJ);
  459. json_t *pulsesParamJ= json_object_get(stepJ, "pulsesParam");
  460. if (pulsesParamJ)
  461. patterns[i].steps[j].pulsesParam = json_integer_value(pulsesParamJ);
  462. json_t *pitchJ= json_object_get(stepJ, "pitch");
  463. if (pitchJ)
  464. patterns[i].steps[j].pitch = json_real_value(pitchJ);
  465. json_t *typeJ= json_object_get(stepJ, "type");
  466. if (typeJ)
  467. patterns[i].steps[j].type = json_integer_value(typeJ);
  468. }
  469. }
  470. }
  471. }
  472. updateFlag = true;
  473. loadedFromJson = true;
  474. }
  475. void randomize() override {
  476. randomizeSlidesSkips();
  477. }
  478. void randomizeSlidesSkips() {
  479. for (int i = 0; i < 8; i++) {
  480. slideState[i] = (randomUniform() > 0.8f) ? 't' : 'f';
  481. skipState[i] = (randomUniform() > 0.85f) ? 't' : 'f';
  482. }
  483. }
  484. void reset() override {
  485. for (int i = 0; i < 8; i++) {
  486. slideState[i] = 'f';
  487. skipState[i] = 'f';
  488. }
  489. }
  490. // Quantization inspired from https://github.com/jeremywen/JW-Modules
  491. float getOneRandomNoteInScale(){
  492. rootNote = clamp((int)(patterns[playedPattern].rootNote + inputs[ROOT_NOTE_INPUT].value), 0, NUM_NOTES-1);
  493. curScaleVal = clamp((int)(patterns[playedPattern].scale + inputs[SCALE_INPUT].value), 0, NUM_SCALES-1);
  494. int *curScaleArr;
  495. int notesInScale = 0;
  496. switch(curScaleVal){
  497. case AEOLIAN: curScaleArr = SCALE_AEOLIAN; notesInScale=LENGTHOF(SCALE_AEOLIAN); break;
  498. case BLUES: curScaleArr = SCALE_BLUES; notesInScale=LENGTHOF(SCALE_BLUES); break;
  499. case CHROMATIC: curScaleArr = SCALE_CHROMATIC; notesInScale=LENGTHOF(SCALE_CHROMATIC); break;
  500. case DIATONIC_MINOR: curScaleArr = SCALE_DIATONIC_MINOR;notesInScale=LENGTHOF(SCALE_DIATONIC_MINOR); break;
  501. case DORIAN: curScaleArr = SCALE_DORIAN; notesInScale=LENGTHOF(SCALE_DORIAN); break;
  502. case HARMONIC_MINOR: curScaleArr = SCALE_HARMONIC_MINOR;notesInScale=LENGTHOF(SCALE_HARMONIC_MINOR); break;
  503. case INDIAN: curScaleArr = SCALE_INDIAN; notesInScale=LENGTHOF(SCALE_INDIAN); break;
  504. case LOCRIAN: curScaleArr = SCALE_LOCRIAN; notesInScale=LENGTHOF(SCALE_LOCRIAN); break;
  505. case LYDIAN: curScaleArr = SCALE_LYDIAN; notesInScale=LENGTHOF(SCALE_LYDIAN); break;
  506. case MAJOR: curScaleArr = SCALE_MAJOR; notesInScale=LENGTHOF(SCALE_MAJOR); break;
  507. case MELODIC_MINOR: curScaleArr = SCALE_MELODIC_MINOR; notesInScale=LENGTHOF(SCALE_MELODIC_MINOR); break;
  508. case MINOR: curScaleArr = SCALE_MINOR; notesInScale=LENGTHOF(SCALE_MINOR); break;
  509. case MIXOLYDIAN: curScaleArr = SCALE_MIXOLYDIAN; notesInScale=LENGTHOF(SCALE_MIXOLYDIAN); break;
  510. case NATURAL_MINOR: curScaleArr = SCALE_NATURAL_MINOR; notesInScale=LENGTHOF(SCALE_NATURAL_MINOR); break;
  511. case PENTATONIC: curScaleArr = SCALE_PENTATONIC; notesInScale=LENGTHOF(SCALE_PENTATONIC); break;
  512. case PHRYGIAN: curScaleArr = SCALE_PHRYGIAN; notesInScale=LENGTHOF(SCALE_PHRYGIAN); break;
  513. case TURKISH: curScaleArr = SCALE_TURKISH; notesInScale=LENGTHOF(SCALE_TURKISH); break;
  514. }
  515. if(curScaleVal == NONE){
  516. return randomUniform() * 6.0f;
  517. } else {
  518. float voltsOut = 0.0f;
  519. int rndOctaveInVolts = int(5.0f * randomUniform());
  520. voltsOut += rndOctaveInVolts;
  521. voltsOut += rootNote / 12.0f;
  522. voltsOut += curScaleArr[int(notesInScale * randomUniform())] / 12.0f;
  523. return voltsOut;
  524. }
  525. }
  526. float closestVoltageInScale(float voltsIn){
  527. rootNote = clamp((int)(patterns[playedPattern].rootNote + inputs[ROOT_NOTE_INPUT].value), 0, DTROY::NUM_NOTES-1);
  528. curScaleVal = clamp((int)(patterns[playedPattern].scale + inputs[SCALE_INPUT].value), 0, DTROY::NUM_SCALES-1);
  529. int *curScaleArr;
  530. int notesInScale = 0;
  531. switch(curScaleVal){
  532. case AEOLIAN: curScaleArr = SCALE_AEOLIAN; notesInScale=LENGTHOF(SCALE_AEOLIAN); break;
  533. case BLUES: curScaleArr = SCALE_BLUES; notesInScale=LENGTHOF(SCALE_BLUES); break;
  534. case CHROMATIC: curScaleArr = SCALE_CHROMATIC; notesInScale=LENGTHOF(SCALE_CHROMATIC); break;
  535. case DIATONIC_MINOR: curScaleArr = SCALE_DIATONIC_MINOR;notesInScale=LENGTHOF(SCALE_DIATONIC_MINOR); break;
  536. case DORIAN: curScaleArr = SCALE_DORIAN; notesInScale=LENGTHOF(SCALE_DORIAN); break;
  537. case HARMONIC_MINOR: curScaleArr = SCALE_HARMONIC_MINOR;notesInScale=LENGTHOF(SCALE_HARMONIC_MINOR); break;
  538. case INDIAN: curScaleArr = SCALE_INDIAN; notesInScale=LENGTHOF(SCALE_INDIAN); break;
  539. case LOCRIAN: curScaleArr = SCALE_LOCRIAN; notesInScale=LENGTHOF(SCALE_LOCRIAN); break;
  540. case LYDIAN: curScaleArr = SCALE_LYDIAN; notesInScale=LENGTHOF(SCALE_LYDIAN); break;
  541. case MAJOR: curScaleArr = SCALE_MAJOR; notesInScale=LENGTHOF(SCALE_MAJOR); break;
  542. case MELODIC_MINOR: curScaleArr = SCALE_MELODIC_MINOR; notesInScale=LENGTHOF(SCALE_MELODIC_MINOR); break;
  543. case MINOR: curScaleArr = SCALE_MINOR; notesInScale=LENGTHOF(SCALE_MINOR); break;
  544. case MIXOLYDIAN: curScaleArr = SCALE_MIXOLYDIAN; notesInScale=LENGTHOF(SCALE_MIXOLYDIAN); break;
  545. case NATURAL_MINOR: curScaleArr = SCALE_NATURAL_MINOR; notesInScale=LENGTHOF(SCALE_NATURAL_MINOR); break;
  546. case PENTATONIC: curScaleArr = SCALE_PENTATONIC; notesInScale=LENGTHOF(SCALE_PENTATONIC); break;
  547. case PHRYGIAN: curScaleArr = SCALE_PHRYGIAN; notesInScale=LENGTHOF(SCALE_PHRYGIAN); break;
  548. case TURKISH: curScaleArr = SCALE_TURKISH; notesInScale=LENGTHOF(SCALE_TURKISH); break;
  549. case NONE: return voltsIn;
  550. }
  551. float closestVal = 10.0f;
  552. float closestDist = 10.0f;
  553. int octaveInVolts = int(voltsIn);
  554. for (int i = 0; i < notesInScale; i++) {
  555. float scaleNoteInVolts = octaveInVolts + (((pitchQuantizeMode ? rootNote : 0.0f) + curScaleArr[i]) / 12.0f);
  556. float distAway = fabs(voltsIn - scaleNoteInVolts);
  557. if(distAway < closestDist) {
  558. closestVal = scaleNoteInVolts;
  559. closestDist = distAway;
  560. }
  561. }
  562. closestVal += pitchQuantizeMode ? 0.0f : (rootNote / 12.0f);
  563. return closestVal;
  564. }
  565. };
  566. void DTROY::step() {
  567. const float lightLambda = 0.075f;
  568. // Run
  569. if (runningTrigger.process(params[RUN_PARAM].value)) {
  570. running = !running;
  571. }
  572. lights[RUNNING_LIGHT].value = running ? 1.0f : 0.0f;
  573. bool nextStep = false;
  574. // Phase calculation
  575. if (running) {
  576. if (inputs[EXT_CLOCK_INPUT].active) {
  577. tCurrent = clock();
  578. float clockTime = powf(2.0f, params[CLOCK_PARAM].value + inputs[CLOCK_INPUT].value);
  579. if (tLastTrig && tPreviousTrig) {
  580. phase = float(tCurrent - tLastTrig) / float(tLastTrig - tPreviousTrig);
  581. }
  582. else {
  583. phase += clockTime / engineGetSampleRate();
  584. }
  585. // External clock
  586. if (clockTrigger.process(inputs[EXT_CLOCK_INPUT].value)) {
  587. tPreviousTrig = tLastTrig;
  588. tLastTrig = clock();
  589. phase = 0.0f;
  590. nextStep = true;
  591. }
  592. }
  593. else {
  594. // Internal clock
  595. float clockTime = powf(2.0f, params[CLOCK_PARAM].value + inputs[CLOCK_INPUT].value);
  596. phase += clockTime / engineGetSampleRate();
  597. if (phase >= 1.0f) {
  598. phase--;
  599. nextStep = true;
  600. }
  601. }
  602. }
  603. // Reset
  604. if (resetTrigger.process(params[RESET_PARAM].value + inputs[RESET_INPUT].value)) {
  605. phase = 0.0f;
  606. reStart = true;
  607. nextStep = true;
  608. lights[RESET_LIGHT].value = 1.0f;
  609. }
  610. //patternNumber
  611. playedPattern = clamp((inputs[PATTERN_INPUT].active ? (int)(rescale(inputs[PATTERN_INPUT].value,0.0f,10.0f,1.0f,16.1f)) : (int)(params[PATTERN_PARAM].value)) - 1, 0, 15);
  612. // Update Pattern
  613. if ((updateFlag) || (!loadedFromJson)) {
  614. // Trigs Update
  615. for (int i = 0; i < 8; i++) {
  616. if (slideTriggers[i].process(params[TRIG_SLIDE_PARAM + i].value)) {
  617. slideState[i] = slideState[i] == 't' ? 'f' : 't';
  618. }
  619. if (skipTriggers[i].process(params[TRIG_SKIP_PARAM + i].value)) {
  620. skipState[i] = skipState[i] == 't' ? 'f' : 't';
  621. }
  622. }
  623. // playMode
  624. if (playModeTrigger.process(params[PLAY_MODE_PARAM].value)) {
  625. playMode = (((int)playMode + 1) % 5);
  626. }
  627. // countMode
  628. if (countModeTrigger.process(params[COUNT_MODE_PARAM].value)) {
  629. countMode = (((int)countMode + 1) % 2);
  630. }
  631. // numSteps
  632. numSteps = clamp((int)(params[STEPS_PARAM].value + inputs[STEPS_INPUT].value), 1, 16);
  633. UpdatePattern();
  634. if (!loadedFromJson) {
  635. loadedFromJson = true;
  636. updateFlag = true;
  637. }
  638. }
  639. // Steps && Pulses Management
  640. if (nextStep) {
  641. // Advance step
  642. previousPitch = closestVoltageInScale(patterns[playedPattern].CurrentStep().pitch);
  643. auto nextT = patterns[playedPattern].GetNextStep(reStart);
  644. index = std::get<0>(nextT);
  645. pulse = std::get<1>(nextT);
  646. if (reStart) { reStart = false; }
  647. lights[STEPS_LIGHTS+index%8].value = 1.0f;
  648. }
  649. // Lights
  650. for (int i = 0; i < 8; i++) {
  651. lights[STEPS_LIGHTS + i].value -= lights[STEPS_LIGHTS + i].value / lightLambda / engineGetSampleRate();
  652. lights[SLIDES_LIGHTS + i].value = slideState[i] == 't' ? 1.0f - lights[STEPS_LIGHTS + i].value : lights[STEPS_LIGHTS + i].value;
  653. lights[SKIPS_LIGHTS + i].value = skipState[i]== 't' ? 1.0f - lights[STEPS_LIGHTS + i].value : lights[STEPS_LIGHTS + i].value;
  654. }
  655. lights[RESET_LIGHT].value -= lights[RESET_LIGHT].value / lightLambda / engineGetSampleRate();
  656. // Caclulate Outputs
  657. bool gateOn = running && (!patterns[playedPattern].CurrentStep().skip);
  658. float gateValue = 0.0f;
  659. if (gateOn){
  660. if (patterns[playedPattern].CurrentStep().type == 0) {
  661. gateOn = false;
  662. }
  663. else if (((patterns[playedPattern].CurrentStep().type == 1) && (pulse == 0))
  664. || (patterns[playedPattern].CurrentStep().type == 2)
  665. || ((patterns[playedPattern].CurrentStep().type == 3) && (pulse == patterns[playedPattern].CurrentStep().pulses))) {
  666. float gateCoeff = clamp(patterns[playedPattern].gateTime - 0.02f + inputs[GATE_TIME_INPUT].value /10.0f, 0.0f, 0.99f);
  667. gateOn = phase < gateCoeff;
  668. gateValue = 10.0f;
  669. }
  670. else if (patterns[playedPattern].CurrentStep().type == 3) {
  671. gateOn = true;
  672. gateValue = 10.0f;
  673. }
  674. else if (patterns[playedPattern].CurrentStep().type == 4) {
  675. gateOn = true;
  676. gateValue = inputs[EXTGATE1_INPUT].value;
  677. }
  678. else if (patterns[playedPattern].CurrentStep().type == 5) {
  679. gateOn = true;
  680. gateValue = inputs[EXTGATE2_INPUT].value;
  681. }
  682. else {
  683. gateOn = false;
  684. gateValue = 0.0f;
  685. }
  686. }
  687. //pitch management
  688. pitch = closestVoltageInScale(patterns[playedPattern].CurrentStep().pitch * patterns[playedPattern].sensitivity);
  689. if (patterns[playedPattern].CurrentStep().slide) {
  690. if (pulse == 0) {
  691. float slideCoeff = clamp(patterns[playedPattern].slideTime - 0.01f + inputs[SLIDE_TIME_INPUT].value /10.0f, -0.1f, 0.99f);
  692. pitch = pitch - (1.0f - powf(phase, slideCoeff)) * (pitch - previousPitch);
  693. }
  694. }
  695. // Update Outputs
  696. outputs[GATE_OUTPUT].value = gateOn ? gateValue : 0.0f;
  697. outputs[PITCH_OUTPUT].value = pitchMode ? pitch : (gateOn ? pitch : 0.0f);
  698. }
  699. struct DTROYDisplay : TransparentWidget {
  700. DTROY *module;
  701. int frame = 0;
  702. shared_ptr<Font> font;
  703. string note, scale, steps, playMode, selectedPattern, playedPattern;
  704. DTROYDisplay() {
  705. font = Font::load(assetPlugin(plugin, "res/DejaVuSansMono.ttf"));
  706. }
  707. void drawMessage(NVGcontext *vg, Vec pos, string note, string playMode, string selectedPattern, string playedPattern, string steps, string scale) {
  708. nvgFontSize(vg, 18.0f);
  709. nvgFontFaceId(vg, font->handle);
  710. nvgTextLetterSpacing(vg, -2.0f);
  711. nvgFillColor(vg, YELLOW_BIDOO);
  712. nvgText(vg, pos.x + 4.0f, pos.y + 8.0f, playMode.c_str(), NULL);
  713. nvgFontSize(vg, 14.0f);
  714. nvgFillColor(vg, YELLOW_BIDOO);
  715. nvgText(vg, pos.x + 91.0f, pos.y + 7.0f, selectedPattern.c_str(), NULL);
  716. nvgText(vg, pos.x + 31.0f, pos.y + 7.0f, steps.c_str(), NULL);
  717. nvgText(vg, pos.x + 3.0f, pos.y + 23.0f, note.c_str(), NULL);
  718. nvgText(vg, pos.x + 25.0f, pos.y + 23.0f, scale.c_str(), NULL);
  719. nvgFillColor(vg, YELLOW_BIDOO);
  720. nvgText(vg, pos.x + 116.0f, pos.y + 7.0f, playedPattern.c_str(), NULL);
  721. }
  722. string displayRootNote(int value) {
  723. switch(value){
  724. case DTROY::NOTE_C: return "C";
  725. case DTROY::NOTE_C_SHARP: return "C#";
  726. case DTROY::NOTE_D: return "D";
  727. case DTROY::NOTE_D_SHARP: return "D#";
  728. case DTROY::NOTE_E: return "E";
  729. case DTROY::NOTE_F: return "F";
  730. case DTROY::NOTE_F_SHARP: return "F#";
  731. case DTROY::NOTE_G: return "G";
  732. case DTROY::NOTE_G_SHARP: return "G#";
  733. case DTROY::NOTE_A: return "A";
  734. case DTROY::NOTE_A_SHARP: return "A#";
  735. case DTROY::NOTE_B: return "B";
  736. default: return "";
  737. }
  738. }
  739. string displayScale(int value) {
  740. switch(value){
  741. case DTROY::AEOLIAN: return "Aeolian";
  742. case DTROY::BLUES: return "Blues";
  743. case DTROY::CHROMATIC: return "Chromatic";
  744. case DTROY::DIATONIC_MINOR: return "Diatonic Minor";
  745. case DTROY::DORIAN: return "Dorian";
  746. case DTROY::HARMONIC_MINOR: return "Harmonic Minor";
  747. case DTROY::INDIAN: return "Indian";
  748. case DTROY::LOCRIAN: return "Locrian";
  749. case DTROY::LYDIAN: return "Lydian";
  750. case DTROY::MAJOR: return "Major";
  751. case DTROY::MELODIC_MINOR: return "Melodic Minor";
  752. case DTROY::MINOR: return "Minor";
  753. case DTROY::MIXOLYDIAN: return "Mixolydian";
  754. case DTROY::NATURAL_MINOR: return "Natural Minor";
  755. case DTROY::PENTATONIC: return "Pentatonic";
  756. case DTROY::PHRYGIAN: return "Phrygian";
  757. case DTROY::TURKISH: return "Turkish";
  758. case DTROY::NONE: return "None";
  759. default: return "";
  760. }
  761. }
  762. string displayPlayMode(int value) {
  763. switch(value){
  764. case 0: return "►";
  765. case 1: return "◄";
  766. case 2: return "►◄";
  767. case 3: return "►*";
  768. case 4: return "►?";
  769. default: return "";
  770. }
  771. }
  772. void draw(NVGcontext *vg) override {
  773. if (++frame >= 4) {
  774. frame = 0;
  775. note = displayRootNote(module->patterns[module->selectedPattern].rootNote);
  776. steps = (module->patterns[module->selectedPattern].countMode == 0 ? "steps:" : "pulses:" ) + to_string(module->patterns[module->selectedPattern].numberOfStepsParam);
  777. playMode = displayPlayMode(module->patterns[module->selectedPattern].playMode);
  778. scale = displayScale(module->patterns[module->selectedPattern].scale);
  779. selectedPattern = "P" + to_string(module->selectedPattern + 1);
  780. playedPattern = "P" + to_string(module->playedPattern + 1);
  781. }
  782. drawMessage(vg, Vec(0, 20), note, playMode, selectedPattern, playedPattern, steps, scale);
  783. }
  784. };
  785. struct DTROYWidget : ModuleWidget {
  786. ParamWidget *stepsParam, *scaleParam, *rootNoteParam, *sensitivityParam,
  787. *gateTimeParam, *slideTimeParam, *playModeParam, *countModeParam, *patternParam,
  788. *pitchParams[8], *pulseParams[8], *typeParams[8], *slideParams[8], *skipParams[8];
  789. DTROYWidget(DTROY *module);
  790. Menu *createContextMenu() override;
  791. };
  792. struct DTROYPatternRoundBlackSnapKnob : RoundBlackSnapKnob {
  793. void onChange(EventChange &e) override {
  794. RoundBlackSnapKnob::onChange(e);
  795. DTROY *module = dynamic_cast<DTROY*>(this->module);
  796. DTROYWidget *parent = dynamic_cast<DTROYWidget*>(this->parent);
  797. int target = clamp((int)(value) - 1, 0, 15);
  798. if (module && parent && (target != module->selectedPattern) && module->updateFlag)
  799. {
  800. module->updateFlag = false;
  801. module->selectedPattern = value - 1;
  802. parent->stepsParam->setValue(module->patterns[target].numberOfStepsParam);
  803. parent->rootNoteParam->setValue(module->patterns[target].rootNote);
  804. parent->scaleParam->setValue(module->patterns[target].scale);
  805. parent->gateTimeParam->setValue(module->patterns[target].gateTime);
  806. parent->slideTimeParam->setValue(module->patterns[target].slideTime);
  807. parent->sensitivityParam->setValue(module->patterns[target].sensitivity);
  808. module->playMode = module->patterns[module->selectedPattern].playMode;
  809. module->countMode = module->patterns[module->selectedPattern].countMode;
  810. for (int i = 0; i < 8; i++) {
  811. parent->pitchParams[i]->setValue(module->patterns[target].steps[i].pitch);
  812. parent->pulseParams[i]->setValue(module->patterns[target].steps[i].pulsesParam);
  813. parent->typeParams[i]->setValue(module->patterns[target].steps[i].type);
  814. module->skipState[i] = module->patterns[target].steps[i].skipParam ? 't' : 'f';
  815. module->slideState[i] = module->patterns[target].steps[i].slide ? 't' : 'f';
  816. }
  817. module->updateFlag = true;
  818. }
  819. }
  820. };
  821. DTROYWidget::DTROYWidget(DTROY *module) : ModuleWidget(module) {
  822. setPanel(SVG::load(assetPlugin(plugin, "res/DTROY.svg")));
  823. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  824. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  825. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  826. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  827. {
  828. DTROYDisplay *display = new DTROYDisplay();
  829. display->module = module;
  830. display->box.pos = Vec(20.0f, 253.0f);
  831. display->box.size = Vec(250.0f, 60.0f);
  832. addChild(display);
  833. }
  834. addParam(ParamWidget::create<RoundBlackKnob>(Vec(17.0f, 52.0f), module, DTROY::CLOCK_PARAM, -2.0f, 6.0f, 2.0f));
  835. addParam(ParamWidget::create<LEDButton>(Vec(61.0f, 56.0f), module, DTROY::RUN_PARAM, 0.0f, 1.0f, 0.0f));
  836. addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(Vec(67.0f, 62.0f), module, DTROY::RUNNING_LIGHT));
  837. addParam(ParamWidget::create<LEDButton>(Vec(99.0f, 56.0f), module, DTROY::RESET_PARAM, 0.0f, 1.0f, 0.0f));
  838. addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(Vec(105.0f, 62.0f), module, DTROY::RESET_LIGHT));
  839. stepsParam = ParamWidget::create<BidooBlueSnapKnob>(Vec(132.0f, 52.0f), module, DTROY::STEPS_PARAM, 1.0f, 16.0f, 8.0f);
  840. addParam(stepsParam);
  841. static const float portX0[4] = {20.0f, 58.0f, 96.0f, 135.0f};
  842. addInput(Port::create<PJ301MPort>(Vec(portX0[0], 90.0f), Port::INPUT, module, DTROY::CLOCK_INPUT));
  843. addInput(Port::create<PJ301MPort>(Vec(portX0[1], 90.0f), Port::INPUT, module, DTROY::EXT_CLOCK_INPUT));
  844. addInput(Port::create<PJ301MPort>(Vec(portX0[2], 90.0f), Port::INPUT, module, DTROY::RESET_INPUT));
  845. addInput(Port::create<PJ301MPort>(Vec(portX0[3], 90.0f), Port::INPUT, module, DTROY::STEPS_INPUT));
  846. rootNoteParam = ParamWidget::create<BidooBlueSnapKnob>(Vec(portX0[0]-2.0f, 140.0f), module, DTROY::ROOT_NOTE_PARAM, 0.0f, DTROY::NUM_NOTES-0.9f, 0.0f);
  847. addParam(rootNoteParam);
  848. scaleParam = ParamWidget::create<BidooBlueSnapKnob>(Vec(portX0[1]-2.0f, 140.0f), module, DTROY::SCALE_PARAM, 0.0f, DTROY::NUM_SCALES-0.9f, 0.0f);
  849. addParam(scaleParam);
  850. gateTimeParam = ParamWidget::create<BidooBlueKnob>(Vec(portX0[2]-2.0f, 140.0f), module, DTROY::GATE_TIME_PARAM, 0.1f, 1.0f, 0.5f);
  851. addParam(gateTimeParam);
  852. slideTimeParam = ParamWidget::create<BidooBlueKnob>(Vec(portX0[3]-2.0f, 140.0f), module, DTROY::SLIDE_TIME_PARAM , 0.1f, 1.0f, 0.2f);
  853. addParam(slideTimeParam);
  854. addInput(Port::create<PJ301MPort>(Vec(portX0[0], 180.0f), Port::INPUT, module, DTROY::ROOT_NOTE_INPUT));
  855. addInput(Port::create<PJ301MPort>(Vec(portX0[1], 180.0f), Port::INPUT, module, DTROY::SCALE_INPUT));
  856. addInput(Port::create<PJ301MPort>(Vec(portX0[2], 180.0f), Port::INPUT, module, DTROY::GATE_TIME_INPUT));
  857. addInput(Port::create<PJ301MPort>(Vec(portX0[3], 180.0f), Port::INPUT, module, DTROY::SLIDE_TIME_INPUT));
  858. playModeParam = ParamWidget::create<BlueCKD6>(Vec(portX0[0]-1.0f, 230.0f), module, DTROY::PLAY_MODE_PARAM, 0.0f, 4.0f, 0.0f);
  859. addParam(playModeParam);
  860. countModeParam = ParamWidget::create<BlueCKD6>(Vec(portX0[1]-1.0f, 230.0f), module, DTROY::COUNT_MODE_PARAM, 0.0f, 4.0f, 0.0f);
  861. addParam(countModeParam);
  862. addInput(Port::create<PJ301MPort>(Vec(portX0[2], 232.0f), Port::INPUT, module, DTROY::PATTERN_INPUT));
  863. patternParam = ParamWidget::create<DTROYPatternRoundBlackSnapKnob>(Vec(portX0[3]-1,230.0f), module, DTROY::PATTERN_PARAM, 1.0f, 16.0f, 1.0f);
  864. addParam(patternParam);
  865. static const float portX1[8] = {200.0f, 238.0f, 276.0f, 315.0f, 353.0f, 392.0f, 430.0f, 469.0f};
  866. sensitivityParam = ParamWidget::create<BidooBlueTrimpot>(Vec(portX1[6]+21.0f, 31.0f), module, DTROY::SENSITIVITY_PARAM, 0.1f, 1.0f, 1.0f);
  867. addParam(sensitivityParam);
  868. for (int i = 0; i < 8; i++) {
  869. pitchParams[i] = ParamWidget::create<BidooBlueKnob>(Vec(portX1[i]-3.0f, 52.0f), module, DTROY::TRIG_PITCH_PARAM + i, 0.0f, 10.0f, 3.0f);
  870. addParam(pitchParams[i]);
  871. pulseParams[i] = ParamWidget::create<BidooSlidePotLong>(Vec(portX1[i]+2.0f, 103.0f), module, DTROY::TRIG_COUNT_PARAM + i, 1.0f, 8.0f, 1.0f);
  872. addParam(pulseParams[i]);
  873. typeParams[i] = ParamWidget::create<BidooSlidePotShort>(Vec(portX1[i]+2.0f, 220.0f), module, DTROY::TRIG_TYPE_PARAM + i, 0.0f, 5.0f, 2.0f);
  874. addParam(typeParams[i]);
  875. slideParams[i] = ParamWidget::create<LEDButton>(Vec(portX1[i]+2.0f, 313.0f), module, DTROY::TRIG_SLIDE_PARAM + i, 0.0f, 1.0f, 0.0f);
  876. addParam(slideParams[i]);
  877. addChild(ModuleLightWidget::create<SmallLight<BlueLight>>(Vec(portX1[i]+8.0f, 319.0f), module, DTROY::SLIDES_LIGHTS + i));
  878. skipParams[i] = ParamWidget::create<LEDButton>(Vec(portX1[i]+2.0f, 338.0f), module, DTROY::TRIG_SKIP_PARAM + i, 0.0f, 1.0f, 0.0f);
  879. addParam(skipParams[i]);
  880. addChild(ModuleLightWidget::create<SmallLight<BlueLight>>(Vec(portX1[i]+8.0f, 344.0f), module, DTROY::SKIPS_LIGHTS + i));
  881. }
  882. addInput(Port::create<PJ301MPort>(Vec(portX0[0], 331.0f), Port::INPUT, module, DTROY::EXTGATE1_INPUT));
  883. addInput(Port::create<PJ301MPort>(Vec(portX0[1], 331.0f), Port::INPUT, module, DTROY::EXTGATE2_INPUT));
  884. addOutput(Port::create<PJ301MPort>(Vec(portX0[2]-1, 331.0f), Port::OUTPUT, module, DTROY::GATE_OUTPUT));
  885. addOutput(Port::create<PJ301MPort>(Vec(portX0[3]-1, 331.0f), Port::OUTPUT, module, DTROY::PITCH_OUTPUT));
  886. }
  887. struct DTROYRandPitchItem : MenuItem {
  888. DTROYWidget *dtroyWidget;
  889. void onAction(EventAction &e) override {
  890. for (int i = 0; i < 8; i++){
  891. int index = DTROY::TRIG_PITCH_PARAM + i;
  892. auto it = std::find_if(dtroyWidget->params.begin(), dtroyWidget->params.end(), [&index](const ParamWidget* m) -> bool { return m->paramId == index; });
  893. if (it != dtroyWidget->params.end())
  894. {
  895. auto index = std::distance(dtroyWidget->params.begin(), it);
  896. dtroyWidget->params[index]->randomize();
  897. }
  898. }
  899. }
  900. };
  901. struct DTROYRandGatesItem : MenuItem {
  902. DTROYWidget *dtroyWidget;
  903. void onAction(EventAction &e) override {
  904. for (int i = 0; i < 8; i++){
  905. int index = DTROY::TRIG_COUNT_PARAM + i;
  906. auto it = std::find_if(dtroyWidget->params.begin(), dtroyWidget->params.end(), [&index](const ParamWidget* m) -> bool { return m->paramId == index; });
  907. if (it != dtroyWidget->params.end())
  908. {
  909. auto index = std::distance(dtroyWidget->params.begin(), it);
  910. dtroyWidget->params[index]->randomize();
  911. }
  912. }
  913. for (int i = 0; i < 8; i++){
  914. int index = DTROY::TRIG_TYPE_PARAM + i;
  915. auto it = std::find_if(dtroyWidget->params.begin(), dtroyWidget->params.end(), [&index](const ParamWidget* m) -> bool { return m->paramId == index; });
  916. if (it != dtroyWidget->params.end())
  917. {
  918. auto index = std::distance(dtroyWidget->params.begin(), it);
  919. dtroyWidget->params[index]->randomize();
  920. }
  921. }
  922. }
  923. };
  924. struct DTROYRandSlideSkipItem : MenuItem {
  925. DTROY *dtroyModule;
  926. void onAction(EventAction &e) override {
  927. dtroyModule->randomizeSlidesSkips();
  928. }
  929. };
  930. struct DTROYPitchModeItem : MenuItem {
  931. DTROY *dtroyModule;
  932. void onAction(EventAction &e) override {
  933. dtroyModule->pitchMode = !dtroyModule->pitchMode;
  934. }
  935. void step() override {
  936. rightText = dtroyModule->pitchMode ? "✔" : "";
  937. MenuItem::step();
  938. }
  939. };
  940. struct DTROYPitchQuantizeModeItem : MenuItem {
  941. DTROY *dtroyModule;
  942. void onAction(EventAction &e) override {
  943. dtroyModule->pitchQuantizeMode = !dtroyModule->pitchQuantizeMode;
  944. }
  945. void step() override {
  946. rightText = dtroyModule->pitchQuantizeMode ? "✔" : "";
  947. MenuItem::step();
  948. }
  949. };
  950. struct DisconnectMenuItem : MenuItem {
  951. ModuleWidget *moduleWidget;
  952. void onAction(EventAction &e) override {
  953. moduleWidget->disconnect();
  954. }
  955. };
  956. struct ResetMenuItem : MenuItem {
  957. DTROYWidget *dtroyWidget;
  958. DTROY *dtroy;
  959. void onAction(EventAction &e) override {
  960. for (int i = 0; i < DTROY::NUM_PARAMS; i++){
  961. if (i != DTROY::PATTERN_PARAM) {
  962. auto it = std::find_if(dtroyWidget->params.begin(), dtroyWidget->params.end(), [&i](const ParamWidget* m) -> bool { return m->paramId == i; });
  963. if (it != dtroyWidget->params.end())
  964. {
  965. auto index = std::distance(dtroyWidget->params.begin(), it);
  966. dtroyWidget->params[index]->setValue(dtroyWidget->params[index]->defaultValue);
  967. }
  968. }
  969. }
  970. dtroy->updateFlag = false;
  971. dtroy->reset();
  972. dtroy->playMode = 0;
  973. dtroy->countMode = 0;
  974. dtroy->updateFlag = true;
  975. }
  976. };
  977. struct RandomizeMenuItem : MenuItem {
  978. ModuleWidget *moduleWidget;
  979. void onAction(EventAction &e) override {
  980. moduleWidget->randomize();
  981. }
  982. };
  983. struct CloneMenuItem : MenuItem {
  984. ModuleWidget *moduleWidget;
  985. void onAction(EventAction &e) override {
  986. RACK_PLUGIN_UI_RACKWIDGET->cloneModule(moduleWidget);
  987. }
  988. };
  989. struct DeleteMenuItem : MenuItem {
  990. ModuleWidget *moduleWidget;
  991. void onAction(EventAction &e) override {
  992. RACK_PLUGIN_UI_RACKWIDGET->deleteModule(moduleWidget);
  993. moduleWidget->finalizeEvents();
  994. delete moduleWidget;
  995. }
  996. };
  997. struct DTROYCopyItem : MenuItem {
  998. DTROY *dtroyModule;
  999. void onAction(EventAction &e) override {
  1000. dtroyModule->copyPattern = dtroyModule->selectedPattern;
  1001. }
  1002. };
  1003. struct DTROYPasteItem : MenuItem {
  1004. DTROY *dtroyModule;
  1005. DTROYWidget *dtroyWidget;
  1006. void onAction(EventAction &e) override {
  1007. if (dtroyModule && dtroyWidget && (dtroyModule->copyPattern != dtroyModule->selectedPattern) && dtroyModule->updateFlag)
  1008. {
  1009. dtroyModule->updateFlag = false;
  1010. dtroyWidget->stepsParam->setValue(dtroyModule->patterns[dtroyModule->copyPattern].numberOfStepsParam);
  1011. dtroyWidget->rootNoteParam->setValue(dtroyModule->patterns[dtroyModule->copyPattern].rootNote);
  1012. dtroyWidget->scaleParam->setValue(dtroyModule->patterns[dtroyModule->copyPattern].scale);
  1013. dtroyWidget->gateTimeParam->setValue(dtroyModule->patterns[dtroyModule->copyPattern].gateTime);
  1014. dtroyWidget->slideTimeParam->setValue(dtroyModule->patterns[dtroyModule->copyPattern].slideTime);
  1015. dtroyWidget->sensitivityParam->setValue(dtroyModule->patterns[dtroyModule->copyPattern].sensitivity);
  1016. dtroyModule->playMode = dtroyModule->patterns[dtroyModule->copyPattern].playMode;
  1017. dtroyModule->countMode = dtroyModule->patterns[dtroyModule->copyPattern].countMode;
  1018. for (int i = 0; i < 8; i++) {
  1019. dtroyWidget->pitchParams[i]->setValue(dtroyModule->patterns[dtroyModule->copyPattern].steps[i].pitch);
  1020. dtroyWidget->pulseParams[i]->setValue(dtroyModule->patterns[dtroyModule->copyPattern].steps[i].pulsesParam);
  1021. dtroyWidget->typeParams[i]->setValue(dtroyModule->patterns[dtroyModule->copyPattern].steps[i].type);
  1022. dtroyModule->skipState[i] = dtroyModule->patterns[dtroyModule->copyPattern].steps[i].skipParam ? 't' : 'f';
  1023. dtroyModule->slideState[i] = dtroyModule->patterns[dtroyModule->copyPattern].steps[i].slide ? 't' : 'f';
  1024. }
  1025. dtroyModule->updateFlag = true;
  1026. }
  1027. }
  1028. };
  1029. Menu *DTROYWidget::createContextMenu() {
  1030. DTROYWidget *dtroyWidget = dynamic_cast<DTROYWidget*>(this);
  1031. assert(dtroyWidget);
  1032. DTROY *dtroyModule = dynamic_cast<DTROY*>(module);
  1033. assert(dtroyModule);
  1034. Menu *menu = rack::global_ui->ui.gScene->createMenu();
  1035. MenuLabel *menuLabel = new MenuLabel();
  1036. menuLabel->text = model->author + " " + model->name;
  1037. menu->addChild(menuLabel);
  1038. ResetMenuItem *resetItem = new ResetMenuItem();
  1039. resetItem->text = "Initialize";
  1040. resetItem->rightText = "+I";
  1041. resetItem->dtroyWidget = this;
  1042. resetItem->dtroy = dtroyModule;
  1043. menu->addChild(resetItem);
  1044. DisconnectMenuItem *disconnectItem = new DisconnectMenuItem();
  1045. disconnectItem->text = "Disconnect cables";
  1046. disconnectItem->moduleWidget = this;
  1047. menu->addChild(disconnectItem);
  1048. CloneMenuItem *cloneItem = new CloneMenuItem();
  1049. cloneItem->text = "Duplicate";
  1050. cloneItem->rightText = "+D";
  1051. cloneItem->moduleWidget = this;
  1052. menu->addChild(cloneItem);
  1053. DeleteMenuItem *deleteItem = new DeleteMenuItem();
  1054. deleteItem->text = "Delete";
  1055. deleteItem->rightText = "Backspace/Delete";
  1056. deleteItem->moduleWidget = this;
  1057. menu->addChild(deleteItem);
  1058. MenuLabel *spacerLabel = new MenuLabel();
  1059. menu->addChild(spacerLabel);
  1060. DTROYRandPitchItem *randomizePitchItem = new DTROYRandPitchItem();
  1061. randomizePitchItem->text = "Randomize pitch";
  1062. randomizePitchItem->dtroyWidget = dtroyWidget;
  1063. menu->addChild(randomizePitchItem);
  1064. DTROYRandGatesItem *randomizeGatesItem = new DTROYRandGatesItem();
  1065. randomizeGatesItem->text = "Randomize gates";
  1066. randomizeGatesItem->dtroyWidget = dtroyWidget;
  1067. menu->addChild(randomizeGatesItem);
  1068. DTROYRandSlideSkipItem *randomizeSlideSkipItem = new DTROYRandSlideSkipItem();
  1069. randomizeSlideSkipItem->text = "Randomize slides and skips";
  1070. randomizeSlideSkipItem->dtroyModule = dtroyModule;
  1071. menu->addChild(randomizeSlideSkipItem);
  1072. MenuLabel *spacerLabel2 = new MenuLabel();
  1073. menu->addChild(spacerLabel2);
  1074. DTROYCopyItem *copyItem = new DTROYCopyItem();
  1075. copyItem->text = "Copy pattern";
  1076. copyItem->dtroyModule = dtroyModule;
  1077. menu->addChild(copyItem);
  1078. DTROYPasteItem *pasteItem = new DTROYPasteItem();
  1079. pasteItem->text = "Paste pattern";
  1080. pasteItem->dtroyModule = dtroyModule;
  1081. pasteItem->dtroyWidget = dtroyWidget;
  1082. menu->addChild(pasteItem);
  1083. MenuLabel *spacerLabel3 = new MenuLabel();
  1084. menu->addChild(spacerLabel3);
  1085. DTROYPitchModeItem *pitchModeItem = new DTROYPitchModeItem();
  1086. pitchModeItem->text = "Pitch mode continuous (vs. triggered)";
  1087. pitchModeItem->dtroyModule = dtroyModule;
  1088. menu->addChild(pitchModeItem);
  1089. DTROYPitchQuantizeModeItem *pitchQuantizeModeItem = new DTROYPitchQuantizeModeItem();
  1090. pitchQuantizeModeItem->text = "Pitch full quantize";
  1091. pitchQuantizeModeItem->dtroyModule = dtroyModule;
  1092. menu->addChild(pitchQuantizeModeItem);
  1093. return menu;
  1094. }
  1095. } // namespace rack_plugin_Bidoo
  1096. using namespace rack_plugin_Bidoo;
  1097. RACK_PLUGIN_MODEL_INIT(Bidoo, DTROY) {
  1098. Model *modelDTROY = Model::create<DTROY, DTROYWidget>("Bidoo", "dTrOY", "dTrOY sequencer", SEQUENCER_TAG);
  1099. return modelDTROY;
  1100. }