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.

1277 lines
47KB

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