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.

606 lines
16KB

  1. ///////////////////////////////////////////////////////////////////
  2. //
  3. // dBiz revisited version of
  4. //
  5. // Cartesian Sequencer Module for VCV
  6. // many thx to
  7. // Strum 2017
  8. // strum@softhome.net
  9. //
  10. ///////////////////////////////////////////////////////////////////
  11. #include "dBiz.hpp"
  12. #include "dsp/digital.hpp"
  13. using namespace std;
  14. namespace rack_plugin_dBiz {
  15. struct Bene : Module {
  16. enum ParamIds
  17. {
  18. ROOT_NOTE_PARAM,
  19. SCALE_PARAM,
  20. // QUANT_PARAM,
  21. KNOB_PARAM,
  22. NUM_PARAMS = KNOB_PARAM + 16
  23. };
  24. enum InputIds
  25. {
  26. ROOT_NOTE_INPUT,
  27. SCALE_INPUT,
  28. UP,
  29. DOWN,
  30. LEFT,
  31. RIGHT,
  32. X_PAD,
  33. Y_PAD,
  34. G_PAD,
  35. RESET,
  36. X_RESET,
  37. Y_RESET,
  38. NUM_INPUTS
  39. };
  40. enum OutputIds {
  41. UNQUANT_OUT,
  42. QUANT_OUT,
  43. ROW_OUT,
  44. COLUMN_OUT = ROW_OUT + 4,
  45. NUM_OUTPUTS = COLUMN_OUT + 4
  46. };
  47. enum LightIds
  48. {
  49. GRID_LIGHTS,
  50. NUM_LIGHTS = GRID_LIGHTS + 16
  51. };
  52. //copied & fixed these scales http://www.grantmuller.com/MidiReference/doc/midiReference/ScaleReference.html
  53. int SCALE_AEOLIAN [7] = {0, 2, 3, 5, 7, 8, 10};
  54. int SCALE_BLUES [6] = {0, 3, 5, 6, 7, 10}; //FIXED!
  55. int SCALE_CHROMATIC [12]= {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
  56. int SCALE_DIATONIC_MINOR [7] = {0, 2, 3, 5, 7, 8, 10};
  57. int SCALE_DORIAN [7] = {0, 2, 3, 5, 7, 9, 10};
  58. int SCALE_HARMONIC_MINOR [7] = {0, 2, 3, 5, 7, 8, 11};
  59. int SCALE_INDIAN [7] = {0, 1, 1, 4, 5, 8, 10};
  60. int SCALE_LOCRIAN [7] = {0, 1, 3, 5, 6, 8, 10};
  61. int SCALE_LYDIAN [7] = {0, 2, 4, 6, 7, 9, 10};
  62. int SCALE_MAJOR [7] = {0, 2, 4, 5, 7, 9, 11};
  63. int SCALE_MELODIC_MINOR [9] = {0, 2, 3, 5, 7, 8, 9, 10, 11};
  64. int SCALE_MINOR [7] = {0, 2, 3, 5, 7, 8, 10};
  65. int SCALE_MIXOLYDIAN [7] = {0, 2, 4, 5, 7, 9, 10};
  66. int SCALE_NATURAL_MINOR [7] = {0, 2, 3, 5, 7, 8, 10};
  67. int SCALE_PENTATONIC [5] = {0, 2, 4, 7, 9};
  68. int SCALE_PHRYGIAN [7] = {0, 1, 3, 5, 7, 8, 10};
  69. int SCALE_TURKISH [7] = {0, 1, 3, 5, 7, 10, 11};
  70. enum Notes
  71. {
  72. NOTE_C,
  73. NOTE_C_SHARP,
  74. NOTE_D,
  75. NOTE_D_SHARP,
  76. NOTE_E,
  77. NOTE_F,
  78. NOTE_F_SHARP,
  79. NOTE_G,
  80. NOTE_G_SHARP,
  81. NOTE_A,
  82. NOTE_A_SHARP,
  83. NOTE_B,
  84. NUM_NOTES
  85. };
  86. enum Scales
  87. {
  88. AEOLIAN,
  89. BLUES,
  90. CHROMATIC,
  91. DIATONIC_MINOR,
  92. DORIAN,
  93. HARMONIC_MINOR,
  94. INDIAN,
  95. LOCRIAN,
  96. LYDIAN,
  97. MAJOR,
  98. MELODIC_MINOR,
  99. MINOR,
  100. MIXOLYDIAN,
  101. NATURAL_MINOR,
  102. PENTATONIC,
  103. PHRYGIAN,
  104. TURKISH,
  105. NONE,
  106. NUM_SCALES
  107. };
  108. SchmittTrigger leftTrigger;
  109. SchmittTrigger rightTrigger;
  110. SchmittTrigger upTrigger;
  111. SchmittTrigger downTrigger;
  112. SchmittTrigger resetTrigger;
  113. SchmittTrigger x_resetTrigger;
  114. SchmittTrigger y_resetTrigger;
  115. SchmittTrigger button_triggers[4][4];
  116. float row_outs[4] = {0.0,0.0,0.0,0.0};
  117. float column_outs[4] = {0.0,0.0,0.0,0.0};
  118. int x_position = 0;
  119. int y_position = 0;
  120. int rootNote = 0;
  121. int curScaleVal = 0;
  122. float pitch = 0;
  123. float previousPitch = 0;
  124. Bene() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
  125. void step() override;
  126. // Quantization based on JW quantizer module!!!
  127. float closestVoltageInScale(float voltsIn)
  128. {
  129. rootNote = params[ROOT_NOTE_PARAM].value + rescale(inputs[ROOT_NOTE_INPUT].value, 0,10,0, Bene::NUM_NOTES - 1);
  130. curScaleVal =params[SCALE_PARAM].value + rescale(inputs[SCALE_INPUT].value, 0,10,0, Bene::NUM_SCALES - 1);
  131. int *curScaleArr;
  132. int notesInScale = 0;
  133. switch (curScaleVal)
  134. {
  135. case AEOLIAN:
  136. curScaleArr = SCALE_AEOLIAN;
  137. notesInScale = LENGTHOF(SCALE_AEOLIAN);
  138. break;
  139. case BLUES:
  140. curScaleArr = SCALE_BLUES;
  141. notesInScale = LENGTHOF(SCALE_BLUES);
  142. break;
  143. case CHROMATIC:
  144. curScaleArr = SCALE_CHROMATIC;
  145. notesInScale = LENGTHOF(SCALE_CHROMATIC);
  146. break;
  147. case DIATONIC_MINOR:
  148. curScaleArr = SCALE_DIATONIC_MINOR;
  149. notesInScale = LENGTHOF(SCALE_DIATONIC_MINOR);
  150. break;
  151. case DORIAN:
  152. curScaleArr = SCALE_DORIAN;
  153. notesInScale = LENGTHOF(SCALE_DORIAN);
  154. break;
  155. case HARMONIC_MINOR:
  156. curScaleArr = SCALE_HARMONIC_MINOR;
  157. notesInScale = LENGTHOF(SCALE_HARMONIC_MINOR);
  158. break;
  159. case INDIAN:
  160. curScaleArr = SCALE_INDIAN;
  161. notesInScale = LENGTHOF(SCALE_INDIAN);
  162. break;
  163. case LOCRIAN:
  164. curScaleArr = SCALE_LOCRIAN;
  165. notesInScale = LENGTHOF(SCALE_LOCRIAN);
  166. break;
  167. case LYDIAN:
  168. curScaleArr = SCALE_LYDIAN;
  169. notesInScale = LENGTHOF(SCALE_LYDIAN);
  170. break;
  171. case MAJOR:
  172. curScaleArr = SCALE_MAJOR;
  173. notesInScale = LENGTHOF(SCALE_MAJOR);
  174. break;
  175. case MELODIC_MINOR:
  176. curScaleArr = SCALE_MELODIC_MINOR;
  177. notesInScale = LENGTHOF(SCALE_MELODIC_MINOR);
  178. break;
  179. case MINOR:
  180. curScaleArr = SCALE_MINOR;
  181. notesInScale = LENGTHOF(SCALE_MINOR);
  182. break;
  183. case MIXOLYDIAN:
  184. curScaleArr = SCALE_MIXOLYDIAN;
  185. notesInScale = LENGTHOF(SCALE_MIXOLYDIAN);
  186. break;
  187. case NATURAL_MINOR:
  188. curScaleArr = SCALE_NATURAL_MINOR;
  189. notesInScale = LENGTHOF(SCALE_NATURAL_MINOR);
  190. break;
  191. case PENTATONIC:
  192. curScaleArr = SCALE_PENTATONIC;
  193. notesInScale = LENGTHOF(SCALE_PENTATONIC);
  194. break;
  195. case PHRYGIAN:
  196. curScaleArr = SCALE_PHRYGIAN;
  197. notesInScale = LENGTHOF(SCALE_PHRYGIAN);
  198. break;
  199. case TURKISH:
  200. curScaleArr = SCALE_TURKISH;
  201. notesInScale = LENGTHOF(SCALE_TURKISH);
  202. break;
  203. case NONE:
  204. return voltsIn;
  205. }
  206. float closestVal = 10.0;
  207. float closestDist = 10.0;
  208. float scaleNoteInVolts = 0;
  209. float distAway = 0;
  210. int octaveInVolts = int(floorf(voltsIn));
  211. float voltMinusOct = voltsIn - octaveInVolts;
  212. for (int i=0; i < notesInScale; i++) {
  213. scaleNoteInVolts = curScaleArr[i] / 12.0;
  214. distAway = fabs(voltMinusOct - scaleNoteInVolts);
  215. if(distAway < closestDist){
  216. closestVal = scaleNoteInVolts;
  217. closestDist = distAway;
  218. }
  219. }
  220. return octaveInVolts + rootNote/12.0 + closestVal;
  221. }
  222. };
  223. void Bene::step() {
  224. bool step_right = false;
  225. bool step_left = false;
  226. bool step_up = false;
  227. bool step_down = false;
  228. lights[GRID_LIGHTS+x_position+y_position*4].value =1.0;
  229. // handle clock inputs
  230. if (inputs[RIGHT].active)
  231. {
  232. if (rightTrigger.process(inputs[RIGHT].value))
  233. {
  234. step_right = true;
  235. }
  236. }
  237. if (inputs[LEFT].active)
  238. {
  239. if (leftTrigger.process(inputs[LEFT].value))
  240. {
  241. step_left = true;
  242. }
  243. }
  244. if (inputs[DOWN].active)
  245. {
  246. if (downTrigger.process(inputs[DOWN].value))
  247. {
  248. step_down = true;
  249. }
  250. }
  251. if (inputs[UP].active)
  252. {
  253. if (upTrigger.process(inputs[UP].value))
  254. {
  255. step_up = true;
  256. }
  257. }
  258. // resets
  259. if (resetTrigger.process(inputs[RESET].value))
  260. {
  261. lights[GRID_LIGHTS + x_position + y_position*4].value = 0.0;
  262. x_position = 0;
  263. y_position = 0;
  264. lights[GRID_LIGHTS + x_position + y_position*4].value = 1.0;
  265. step_right = false;
  266. step_left = false;
  267. step_up = false;
  268. step_down = false;
  269. }
  270. if (x_resetTrigger.process(inputs[X_RESET].value))
  271. {
  272. lights[GRID_LIGHTS + x_position + y_position*4].value = 0.0;
  273. x_position = 0;
  274. lights[GRID_LIGHTS + x_position + y_position*4].value = 1.0;
  275. step_right = false;
  276. step_left = false;
  277. step_up = false;
  278. step_down = false;
  279. }
  280. if (y_resetTrigger.process(inputs[Y_RESET].value))
  281. {
  282. lights[GRID_LIGHTS + x_position + y_position*4].value = 0.0;
  283. y_position = 0;
  284. lights[GRID_LIGHTS + x_position + y_position*4].value = 1.0;
  285. step_right = false;
  286. step_left = false;
  287. step_up = false;
  288. step_down = false;
  289. }
  290. // handle button triggers
  291. int xpad = round(inputs[X_PAD].value);
  292. int ypad = round(inputs[Y_PAD].value);
  293. bool gated = inputs[G_PAD].value > 0.0;
  294. if (gated)
  295. {
  296. for (int i = 0; i < 4; i++)
  297. {
  298. for (int j = 0; j < 4; j++)
  299. {
  300. lights[GRID_LIGHTS + x_position + y_position*4].value = 0.0;
  301. x_position = xpad-1;
  302. y_position = ypad-1;
  303. lights[GRID_LIGHTS + x_position + y_position*4].value = 1.0;
  304. }
  305. }
  306. }
  307. // change x and y
  308. if (step_right)
  309. {
  310. lights[GRID_LIGHTS + x_position + y_position*4].value = 0.0;
  311. x_position += 1;
  312. if (x_position > 3) x_position = 0;
  313. lights[GRID_LIGHTS + x_position + y_position*4].value = 1.0;
  314. }
  315. if (step_left)
  316. {
  317. lights[GRID_LIGHTS + x_position + y_position*4].value = 0.0;
  318. x_position -= 1;
  319. if (x_position < 0) x_position = 3;
  320. lights[GRID_LIGHTS + x_position + y_position*4].value = 1.0;
  321. }
  322. if (step_down)
  323. {
  324. lights[GRID_LIGHTS + x_position + y_position*4].value = 0.0;
  325. y_position += 1;
  326. if (y_position > 3) y_position = 0;
  327. lights[GRID_LIGHTS + x_position + y_position*4].value = 1.0;
  328. }
  329. if (step_up)
  330. {
  331. lights[GRID_LIGHTS + x_position + y_position*4].value = 0.0;
  332. y_position -= 1;
  333. if (y_position < 0) y_position = 3;
  334. lights[GRID_LIGHTS + x_position + y_position*4].value = 1.0;
  335. }
  336. /// set outputs
  337. int which_knob = y_position * 4 + x_position;
  338. //float main_out = params[KNOB_PARAM + which_knob].value;
  339. // int oct = round(main_out);
  340. // float left = main_out - oct;
  341. // int semi = round(left * 12);
  342. // float quant_out = oct + semi/12.0;
  343. //
  344. for (int i = 0 ; i < 4 ; i++)
  345. {
  346. float main_out = params[KNOB_PARAM + which_knob].value;
  347. float quant_out = closestVoltageInScale(params[KNOB_PARAM + which_knob].value);
  348. row_outs[i] = closestVoltageInScale(params[KNOB_PARAM + y_position * 4 + i].value);
  349. column_outs[i] = closestVoltageInScale(params[KNOB_PARAM + x_position + i * 4].value);
  350. /* roct[i] = round(row_outs[i]);
  351. rleft[i] = row_outs[i] - roct[i];
  352. rsemi[i] = round(rleft[i] * 12);
  353. rquant_out[i] = roct[i] + rsemi[i] / 12.0;
  354. coct[i] = round(column_outs[i]);
  355. cleft[i] = column_outs[i] - roct[i];
  356. csemi[i] = round(cleft[i] * 12);
  357. cquant_out[i] = coct[i] + csemi[i] / 12.0;
  358. */
  359. outputs[ROW_OUT + i].value = row_outs[i];
  360. outputs[COLUMN_OUT + i].value = column_outs[i];
  361. outputs[UNQUANT_OUT].value = main_out;
  362. outputs[QUANT_OUT].value = quant_out;
  363. }
  364. }
  365. //////////////////////////////////// Display --- Based on DTROY by Bidoo
  366. struct BeneDisplay : TransparentWidget
  367. {
  368. Bene *module;
  369. int frame = 0;
  370. shared_ptr<Font> font;
  371. string note, scale;
  372. BeneDisplay()
  373. {
  374. font = Font::load(assetPlugin(plugin, "res/DejaVuSansMono.ttf"));
  375. }
  376. void drawMessage(NVGcontext *vg, Vec pos, string note,string scale)
  377. {
  378. nvgFontSize(vg, 18);
  379. nvgFontFaceId(vg, font->handle);
  380. nvgTextLetterSpacing(vg, -2);
  381. nvgFillColor(vg, nvgRGBA(75, 199, 75, 0xff));
  382. nvgFontSize(vg, 14);
  383. nvgFillColor(vg, nvgRGBA(0xff, 0xff, 0xff, 0xff));
  384. nvgText(vg, pos.x + 8, pos.y + 23, note.c_str(), NULL);
  385. nvgText(vg, pos.x + 30, pos.y + 23, scale.c_str(), NULL);
  386. }
  387. string displayRootNote(int value)
  388. {
  389. switch (value)
  390. {
  391. case Bene::NOTE_C:
  392. return "C";
  393. case Bene::NOTE_C_SHARP:
  394. return "C#";
  395. case Bene::NOTE_D:
  396. return "D";
  397. case Bene::NOTE_D_SHARP:
  398. return "D#";
  399. case Bene::NOTE_E:
  400. return "E";
  401. case Bene::NOTE_F:
  402. return "F";
  403. case Bene::NOTE_F_SHARP:
  404. return "F#";
  405. case Bene::NOTE_G:
  406. return "G";
  407. case Bene::NOTE_G_SHARP:
  408. return "G#";
  409. case Bene::NOTE_A:
  410. return "A";
  411. case Bene::NOTE_A_SHARP:
  412. return "A#";
  413. case Bene::NOTE_B:
  414. return "B";
  415. default:
  416. return "";
  417. }
  418. }
  419. string displayScale(int value)
  420. {
  421. switch (value)
  422. {
  423. case Bene::AEOLIAN:
  424. return "Aeolian";
  425. case Bene::BLUES:
  426. return "Blues";
  427. case Bene::CHROMATIC:
  428. return "Chromatic";
  429. case Bene::DIATONIC_MINOR:
  430. return "Diat. Min.";
  431. case Bene::DORIAN:
  432. return "Dorian";
  433. case Bene::HARMONIC_MINOR:
  434. return "Harm. Min.";
  435. case Bene::INDIAN:
  436. return "Indian";
  437. case Bene::LOCRIAN:
  438. return "Locrian";
  439. case Bene::LYDIAN:
  440. return "Lydian";
  441. case Bene::MAJOR:
  442. return "Major";
  443. case Bene::MELODIC_MINOR:
  444. return "Melo. Min.";
  445. case Bene::MINOR:
  446. return "Minor";
  447. case Bene::MIXOLYDIAN:
  448. return "Mixolydian";
  449. case Bene::NATURAL_MINOR:
  450. return "Nat. Min.";
  451. case Bene::PENTATONIC:
  452. return "Pentatonic";
  453. case Bene::PHRYGIAN:
  454. return "Phrygian";
  455. case Bene::TURKISH:
  456. return "Turkish";
  457. case Bene::NONE:
  458. return "None";
  459. default:
  460. return "";
  461. }
  462. }
  463. void draw(NVGcontext *vg) override
  464. {
  465. if (++frame >= 4)
  466. {
  467. frame = 0;
  468. note = displayRootNote(module->rootNote);
  469. scale = displayScale(module->curScaleVal);
  470. }
  471. drawMessage(vg, Vec(0, 20), note, scale);
  472. }
  473. };
  474. ////////////////////////////////
  475. struct BeneWidget : ModuleWidget
  476. {
  477. BeneWidget(Bene *module) : ModuleWidget(module)
  478. {
  479. box.size = Vec(15*13, 380);
  480. int top = 20;
  481. int top2 = 35;
  482. int left = 8;
  483. int column_spacing = 35;
  484. int row_spacing = 35;
  485. {
  486. SVGPanel *panel = new SVGPanel();
  487. panel->box.size = box.size;
  488. panel->setBackground(SVG::load(assetPlugin(plugin,"res/Bene.svg")));
  489. addChild(panel);
  490. }
  491. {
  492. BeneDisplay *display = new BeneDisplay();
  493. display->module = module;
  494. display->box.pos = Vec(left, top + 105);
  495. display->box.size = Vec(250, 60);
  496. addChild(display);
  497. }
  498. addInput(Port::create<PJ301MIPort>(Vec(left, top), Port::INPUT, module, Bene::LEFT));
  499. addInput(Port::create<PJ301MIPort>(Vec(left+column_spacing, top), Port::INPUT, module, Bene::RIGHT));
  500. addInput(Port::create<PJ301MIPort>(Vec(left, top + 40), Port::INPUT, module, Bene::UP));
  501. addInput(Port::create<PJ301MIPort>(Vec(left + column_spacing, top + 40), Port::INPUT, module, Bene::DOWN));
  502. addInput(Port::create<PJ301MIPort>(Vec(left+column_spacing * 2, top), Port::INPUT, module, Bene::X_RESET));
  503. addInput(Port::create<PJ301MIPort>(Vec(left + column_spacing * 2, top + 40), Port::INPUT, module, Bene::Y_RESET));
  504. addInput(Port::create<PJ301MOrPort>(Vec(left , top+85), Port::INPUT, module, Bene::X_PAD));
  505. addInput(Port::create<PJ301MOrPort>(Vec(left + column_spacing , top + 85), Port::INPUT, module, Bene::Y_PAD));
  506. addInput(Port::create<PJ301MOrPort>(Vec(left + column_spacing * 2, top + 85), Port::INPUT, module, Bene::G_PAD));
  507. addInput(Port::create<PJ301MIPort>(Vec(left + column_spacing * 3, top ), Port::INPUT, module, Bene::RESET));
  508. addOutput(Port::create<PJ301MOPort>(Vec(left + column_spacing * 5-20, top), Port::OUTPUT, module, Bene::UNQUANT_OUT));
  509. addOutput(Port::create<PJ301MOPort>(Vec(left + column_spacing * 5-20, top+30), Port::OUTPUT, module, Bene::QUANT_OUT));
  510. for ( int i = 0 ; i < 4 ; i++)
  511. {
  512. for ( int j = 0 ; j < 4 ; j++)
  513. {
  514. addParam(ParamWidget::create<Rogan2PWhite>(Vec(left+column_spacing * i, top2 + row_spacing * j + 150 ), module, Bene::KNOB_PARAM + i + j * 4, 0.0, 2.0, 1.0));
  515. addChild(GrayModuleLightWidget::create<BigLight<OrangeLight>>(Vec(left + column_spacing * i + 8, top2 + row_spacing * j + 150 + 8), module, Bene::GRID_LIGHTS + i + j * 4));
  516. }
  517. addOutput(Port::create<PJ301MOPort>(Vec(left+column_spacing * i+5, top2 + row_spacing * 4 + 155 ), Port::OUTPUT, module, Bene::ROW_OUT + i));
  518. addOutput(Port::create<PJ301MOPort>(Vec(left+column_spacing * 4+5, top2 + row_spacing * i + 155 ), Port::OUTPUT, module, Bene::COLUMN_OUT + i));
  519. }
  520. addParam(ParamWidget::create<Rogan2PWhite>(Vec(left + column_spacing*3-5, top + 85 + row_spacing), module, Bene::ROOT_NOTE_PARAM, 0.0, Bene::NUM_NOTES - 1 + 0.1, 0));
  521. addParam(ParamWidget::create<Rogan2PWhite>(Vec(left + column_spacing*4 , top + 85 + row_spacing), module, Bene::SCALE_PARAM, 0.0, Bene::NUM_SCALES - 1 + 0.1, 0));
  522. addInput(Port::create<PJ301MPort>(Vec(column_spacing * 4-25, top + 85), Port::INPUT, module, Bene::ROOT_NOTE_INPUT));
  523. addInput(Port::create<PJ301MPort>(Vec(column_spacing * 4 +15, top + 85), Port::INPUT, module, Bene::SCALE_INPUT));
  524. addChild(Widget::create<ScrewSilver>(Vec(15, 0)));
  525. addChild(Widget::create<ScrewSilver>(Vec(box.size.x-30, 0)));
  526. addChild(Widget::create<ScrewSilver>(Vec(15, 365)));
  527. addChild(Widget::create<ScrewSilver>(Vec(box.size.x-30, 365)));
  528. }
  529. };
  530. } // namespace rack_plugin_dBiz
  531. using namespace rack_plugin_dBiz;
  532. RACK_PLUGIN_MODEL_INIT(dBiz, Bene) {
  533. Model *modelBene = Model::create<Bene, BeneWidget>("dBiz", "Bene", "Bene", SEQUENCER_TAG);
  534. return modelBene;
  535. }