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.

443 lines
12KB

  1. ///////////////////////////////////////////////////
  2. // dBiz Utility
  3. //
  4. ///////////////////////////////////////////////////
  5. #include "dBiz.hpp"
  6. #include <ctime>
  7. #include <iostream>
  8. #include <vector>
  9. #include <random>
  10. #include <algorithm>
  11. using namespace std;
  12. namespace rack_plugin_dBiz {
  13. /////added fine out /////////////////////////////////////////////////
  14. struct Utility : Module {
  15. enum ParamIds
  16. {
  17. LINK_A_PARAM,
  18. LINK_B_PARAM,
  19. ROOT_NOTE_PARAM,
  20. SCALE_PARAM,
  21. OCTAVE_SHIFT,
  22. SEMITONE_SHIFT = OCTAVE_SHIFT + 3,
  23. FINE_SHIFT = SEMITONE_SHIFT + 3,
  24. AMOUNT_PARAM = FINE_SHIFT + 3,
  25. NUM_PARAMS = AMOUNT_PARAM + 3
  26. };
  27. enum InputIds {
  28. ROOT_NOTE_INPUT,
  29. SCALE_INPUT,
  30. OCTAVE_INPUT,
  31. OCTAVE_CVINPUT=OCTAVE_INPUT+3,
  32. SEMITONE_CVINPUT=OCTAVE_CVINPUT+3,
  33. FINE_CVINPUT=SEMITONE_CVINPUT+3,
  34. AMOUNT_CVINPUT=FINE_CVINPUT+3,
  35. NUM_INPUTS=AMOUNT_CVINPUT+3
  36. };
  37. enum OutputIds {
  38. A_OUTPUT,
  39. B_OUTPUT,
  40. C_OUTPUT,
  41. NUM_OUTPUTS
  42. };
  43. enum LighIds {
  44. AMOUNT_LIGHT,
  45. NUM_LIGHTS=AMOUNT_LIGHT+3
  46. };
  47. //copied & fixed these scales http://www.grantmuller.com/MidiReference/doc/midiReference/ScaleReference.html
  48. int SCALE_AEOLIAN [7] = {0, 2, 3, 5, 7, 8, 10};
  49. int SCALE_BLUES [6] = {0, 3, 5, 6, 7, 10}; //FIXED!
  50. int SCALE_CHROMATIC [12]= {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
  51. int SCALE_DIATONIC_MINOR [7] = {0, 2, 3, 5, 7, 8, 10};
  52. int SCALE_DORIAN [7] = {0, 2, 3, 5, 7, 9, 10};
  53. int SCALE_HARMONIC_MINOR [7] = {0, 2, 3, 5, 7, 8, 11};
  54. int SCALE_INDIAN [7] = {0, 1, 1, 4, 5, 8, 10};
  55. int SCALE_LOCRIAN [7] = {0, 1, 3, 5, 6, 8, 10};
  56. int SCALE_LYDIAN [7] = {0, 2, 4, 6, 7, 9, 10};
  57. int SCALE_MAJOR [7] = {0, 2, 4, 5, 7, 9, 11};
  58. int SCALE_MELODIC_MINOR [9] = {0, 2, 3, 5, 7, 8, 9, 10, 11};
  59. int SCALE_MINOR [7] = {0, 2, 3, 5, 7, 8, 10};
  60. int SCALE_MIXOLYDIAN [7] = {0, 2, 4, 5, 7, 9, 10};
  61. int SCALE_NATURAL_MINOR [7] = {0, 2, 3, 5, 7, 8, 10};
  62. int SCALE_PENTATONIC [5] = {0, 2, 4, 7, 9};
  63. int SCALE_PHRYGIAN [7] = {0, 1, 3, 5, 7, 8, 10};
  64. int SCALE_TURKISH [7] = {0, 1, 3, 5, 7, 10, 11};
  65. enum Notes
  66. {
  67. NOTE_C,
  68. NOTE_C_SHARP,
  69. NOTE_D,
  70. NOTE_D_SHARP,
  71. NOTE_E,
  72. NOTE_F,
  73. NOTE_F_SHARP,
  74. NOTE_G,
  75. NOTE_G_SHARP,
  76. NOTE_A,
  77. NOTE_A_SHARP,
  78. NOTE_B,
  79. NUM_NOTES
  80. };
  81. enum Scales
  82. {
  83. AEOLIAN,
  84. BLUES,
  85. CHROMATIC,
  86. DIATONIC_MINOR,
  87. DORIAN,
  88. HARMONIC_MINOR,
  89. INDIAN,
  90. LOCRIAN,
  91. LYDIAN,
  92. MAJOR,
  93. MELODIC_MINOR,
  94. MINOR,
  95. MIXOLYDIAN,
  96. NATURAL_MINOR,
  97. PENTATONIC,
  98. PHRYGIAN,
  99. TURKISH,
  100. NONE,
  101. NUM_SCALES
  102. };
  103. int rootNote = 0;
  104. int curScaleVal = 0;
  105. float octave_out[3] {};
  106. float semitone_out[3] {};
  107. float fine_out[3] {};
  108. Utility() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
  109. void step() override;
  110. // Quantization based on JW quantizer module!!!
  111. float closestVoltageInScale(float voltsIn)
  112. {
  113. rootNote = params[ROOT_NOTE_PARAM].value + rescale(inputs[ROOT_NOTE_INPUT].value, 0,10,0, Utility::NUM_NOTES - 1);
  114. curScaleVal = params[SCALE_PARAM].value + rescale(inputs[SCALE_INPUT].value, 0,10,0, Utility::NUM_SCALES - 1);
  115. int *curScaleArr;
  116. int notesInScale = 0;
  117. switch (curScaleVal)
  118. {
  119. case AEOLIAN:
  120. curScaleArr = SCALE_AEOLIAN;
  121. notesInScale = LENGTHOF(SCALE_AEOLIAN);
  122. break;
  123. case BLUES:
  124. curScaleArr = SCALE_BLUES;
  125. notesInScale = LENGTHOF(SCALE_BLUES);
  126. break;
  127. case CHROMATIC:
  128. curScaleArr = SCALE_CHROMATIC;
  129. notesInScale = LENGTHOF(SCALE_CHROMATIC);
  130. break;
  131. case DIATONIC_MINOR:
  132. curScaleArr = SCALE_DIATONIC_MINOR;
  133. notesInScale = LENGTHOF(SCALE_DIATONIC_MINOR);
  134. break;
  135. case DORIAN:
  136. curScaleArr = SCALE_DORIAN;
  137. notesInScale = LENGTHOF(SCALE_DORIAN);
  138. break;
  139. case HARMONIC_MINOR:
  140. curScaleArr = SCALE_HARMONIC_MINOR;
  141. notesInScale = LENGTHOF(SCALE_HARMONIC_MINOR);
  142. break;
  143. case INDIAN:
  144. curScaleArr = SCALE_INDIAN;
  145. notesInScale = LENGTHOF(SCALE_INDIAN);
  146. break;
  147. case LOCRIAN:
  148. curScaleArr = SCALE_LOCRIAN;
  149. notesInScale = LENGTHOF(SCALE_LOCRIAN);
  150. break;
  151. case LYDIAN:
  152. curScaleArr = SCALE_LYDIAN;
  153. notesInScale = LENGTHOF(SCALE_LYDIAN);
  154. break;
  155. case MAJOR:
  156. curScaleArr = SCALE_MAJOR;
  157. notesInScale = LENGTHOF(SCALE_MAJOR);
  158. break;
  159. case MELODIC_MINOR:
  160. curScaleArr = SCALE_MELODIC_MINOR;
  161. notesInScale = LENGTHOF(SCALE_MELODIC_MINOR);
  162. break;
  163. case MINOR:
  164. curScaleArr = SCALE_MINOR;
  165. notesInScale = LENGTHOF(SCALE_MINOR);
  166. break;
  167. case MIXOLYDIAN:
  168. curScaleArr = SCALE_MIXOLYDIAN;
  169. notesInScale = LENGTHOF(SCALE_MIXOLYDIAN);
  170. break;
  171. case NATURAL_MINOR:
  172. curScaleArr = SCALE_NATURAL_MINOR;
  173. notesInScale = LENGTHOF(SCALE_NATURAL_MINOR);
  174. break;
  175. case PENTATONIC:
  176. curScaleArr = SCALE_PENTATONIC;
  177. notesInScale = LENGTHOF(SCALE_PENTATONIC);
  178. break;
  179. case PHRYGIAN:
  180. curScaleArr = SCALE_PHRYGIAN;
  181. notesInScale = LENGTHOF(SCALE_PHRYGIAN);
  182. break;
  183. case TURKISH:
  184. curScaleArr = SCALE_TURKISH;
  185. notesInScale = LENGTHOF(SCALE_TURKISH);
  186. break;
  187. case NONE:
  188. return voltsIn;
  189. }
  190. float closestVal = 10.0;
  191. float closestDist = 10.0;
  192. float scaleNoteInVolts = 0;
  193. float distAway = 0;
  194. int octaveInVolts = int(floorf(voltsIn));
  195. float voltMinusOct = voltsIn - octaveInVolts;
  196. for (int i=0; i < notesInScale; i++) {
  197. scaleNoteInVolts = curScaleArr[i] / 12.0;
  198. distAway = fabs(voltMinusOct - scaleNoteInVolts);
  199. if(distAway < closestDist){
  200. closestVal = scaleNoteInVolts;
  201. closestDist = distAway;
  202. }
  203. }
  204. return octaveInVolts + rootNote/12.0 + closestVal;
  205. }
  206. };
  207. /////////////////////////////////////////////////////
  208. void Utility::step() {
  209. if(params[LINK_A_PARAM].value ==1.0 )
  210. inputs[OCTAVE_INPUT + 1].value = inputs[OCTAVE_INPUT + 0].value;
  211. if (params[LINK_B_PARAM].value == 1.0)
  212. inputs[OCTAVE_INPUT + 2].value = inputs[OCTAVE_INPUT + 1].value;
  213. for (int i = 0; i < 3; i++)
  214. {
  215. octave_out[i] = inputs[OCTAVE_INPUT + i].value + round(params[OCTAVE_SHIFT + i].value) + round(inputs[OCTAVE_CVINPUT + i].value / 2);
  216. semitone_out[i] = octave_out[i] + round(params[SEMITONE_SHIFT + i].value) * (1.0 / 12.0) + round(inputs[SEMITONE_CVINPUT + i].value / 2) * (1.0 / 12.0);
  217. fine_out[i] = semitone_out[i] + (params[FINE_SHIFT + i].value) * (1.0 / 12.0) + (inputs[FINE_CVINPUT + i].value / 2) * (1.0 / 2.0);
  218. }
  219. float out_a = closestVoltageInScale(fine_out[0]);
  220. float out_b = closestVoltageInScale(fine_out[1]);
  221. float out_c = closestVoltageInScale(fine_out[2]);
  222. outputs[A_OUTPUT].value = out_a;
  223. outputs[B_OUTPUT].value = out_b;
  224. outputs[C_OUTPUT].value = out_c;
  225. }
  226. struct UtilityDisplay : TransparentWidget
  227. {
  228. Utility *module;
  229. int frame = 0;
  230. shared_ptr<Font> font;
  231. string note, scale;
  232. UtilityDisplay()
  233. {
  234. font = Font::load(assetPlugin(plugin, "res/DejaVuSansMono.ttf"));
  235. }
  236. void drawMessage(NVGcontext *vg, Vec pos, string note,string scale)
  237. {
  238. nvgFontSize(vg, 18);
  239. nvgFontFaceId(vg, font->handle);
  240. nvgTextLetterSpacing(vg, -2);
  241. nvgFillColor(vg, nvgRGBA(75, 199, 75, 0xff));
  242. nvgFontSize(vg, 14);
  243. nvgFillColor(vg, nvgRGBA(0xff, 0xff, 0xff, 0xff));
  244. nvgText(vg, pos.x + 8, pos.y + 23, note.c_str(), NULL);
  245. nvgText(vg, pos.x + 30, pos.y + 23, scale.c_str(), NULL);
  246. }
  247. string displayRootNote(int value)
  248. {
  249. switch (value)
  250. {
  251. case Utility::NOTE_C:
  252. return "C";
  253. case Utility::NOTE_C_SHARP:
  254. return "C#";
  255. case Utility::NOTE_D:
  256. return "D";
  257. case Utility::NOTE_D_SHARP:
  258. return "D#";
  259. case Utility::NOTE_E:
  260. return "E";
  261. case Utility::NOTE_F:
  262. return "F";
  263. case Utility::NOTE_F_SHARP:
  264. return "F#";
  265. case Utility::NOTE_G:
  266. return "G";
  267. case Utility::NOTE_G_SHARP:
  268. return "G#";
  269. case Utility::NOTE_A:
  270. return "A";
  271. case Utility::NOTE_A_SHARP:
  272. return "A#";
  273. case Utility::NOTE_B:
  274. return "B";
  275. default:
  276. return "";
  277. }
  278. }
  279. string displayScale(int value)
  280. {
  281. switch (value)
  282. {
  283. case Utility::AEOLIAN:
  284. return "Aeolian";
  285. case Utility::BLUES:
  286. return "Blues";
  287. case Utility::CHROMATIC:
  288. return "Chromatic";
  289. case Utility::DIATONIC_MINOR:
  290. return "Diat. Min.";
  291. case Utility::DORIAN:
  292. return "Dorian";
  293. case Utility::HARMONIC_MINOR:
  294. return "Harm. Min.";
  295. case Utility::INDIAN:
  296. return "Indian";
  297. case Utility::LOCRIAN:
  298. return "Locrian";
  299. case Utility::LYDIAN:
  300. return "Lydian";
  301. case Utility::MAJOR:
  302. return "Major";
  303. case Utility::MELODIC_MINOR:
  304. return "Melo. Min.";
  305. case Utility::MINOR:
  306. return "Minor";
  307. case Utility::MIXOLYDIAN:
  308. return "Mixolydian";
  309. case Utility::NATURAL_MINOR:
  310. return "Nat. Min.";
  311. case Utility::PENTATONIC:
  312. return "Pentatonic";
  313. case Utility::PHRYGIAN:
  314. return "Phrygian";
  315. case Utility::TURKISH:
  316. return "Turkish";
  317. case Utility::NONE:
  318. return "None";
  319. default:
  320. return "";
  321. }
  322. }
  323. void draw(NVGcontext *vg) override
  324. {
  325. if (++frame >= 4)
  326. {
  327. frame = 0;
  328. note = displayRootNote(module->rootNote);
  329. scale = displayScale(module->curScaleVal);
  330. }
  331. drawMessage(vg, Vec(0, 20), note, scale);
  332. }
  333. };
  334. //////////////////////////////////////////////////////////////////
  335. struct UtilityWidget : ModuleWidget
  336. {
  337. UtilityWidget(Utility *module) : ModuleWidget(module)
  338. {
  339. box.size = Vec(15*8, 380);
  340. {
  341. SVGPanel *panel = new SVGPanel();
  342. panel->box.size = box.size;
  343. panel->setBackground(SVG::load(assetPlugin(plugin,"res/Utility.svg")));
  344. addChild(panel);
  345. }
  346. {
  347. UtilityDisplay *display = new UtilityDisplay();
  348. display->module = module;
  349. display->box.pos = Vec(10, + 240);
  350. display->box.size = Vec(250, 60);
  351. addChild(display);
  352. }
  353. //Screw
  354. addChild(Widget::create<ScrewSilver>(Vec(15, 0)));
  355. addChild(Widget::create<ScrewSilver>(Vec(box.size.x-30, 0)));
  356. addChild(Widget::create<ScrewSilver>(Vec(15, 365)));
  357. addChild(Widget::create<ScrewSilver>(Vec(box.size.x-30, 365)));
  358. int knob=35;
  359. //
  360. for (int i=0;i<3;i++)
  361. {
  362. addParam(ParamWidget::create<FlatASnap>(Vec(10+knob*i, 20), module, Utility::OCTAVE_SHIFT+i, -4.5, 4.5, 0.0));
  363. addParam(ParamWidget::create<FlatASnap>(Vec(10+knob*i, 60), module, Utility::SEMITONE_SHIFT+i, -5.0 ,5.0, 0.0));
  364. addParam(ParamWidget::create<FlatA>(Vec(10+knob*i, 100), module, Utility::FINE_SHIFT+i, -1, 1, 0.0));
  365. addInput(Port::create<PJ301MIPort>(Vec(12.5+knob*i, 100+knob*1.3), Port::INPUT, module, Utility::OCTAVE_INPUT+i));
  366. addInput(Port::create<PJ301MCPort>(Vec(12.5+knob*i, 130+knob*1.3), Port::INPUT, module, Utility::OCTAVE_CVINPUT+i));
  367. addInput(Port::create<PJ301MCPort>(Vec(12.5+knob*i, 160+knob*1.3), Port::INPUT, module, Utility::SEMITONE_CVINPUT+i));
  368. addInput(Port::create<PJ301MCPort>(Vec(12.5+knob*i, 190+knob*1.3), Port::INPUT, module, Utility::FINE_CVINPUT+i));
  369. }
  370. addParam(ParamWidget::create<Trimpot>(Vec(65,304), module, Utility::ROOT_NOTE_PARAM, 0.0, Utility::NUM_NOTES - 1 + 0.1, 0));
  371. addParam(ParamWidget::create<Trimpot>(Vec(90,304), module, Utility::SCALE_PARAM, 0.0, Utility::NUM_SCALES - 1 + 0.1, 0));
  372. addInput(Port::create<PJ301MPort>(Vec(10,300), Port::INPUT, module, Utility::ROOT_NOTE_INPUT));
  373. addInput(Port::create<PJ301MPort>(Vec(37,300), Port::INPUT, module, Utility::SCALE_INPUT));
  374. addOutput(Port::create<PJ301MOPort>(Vec(12.5,340), Port::OUTPUT, module, Utility::A_OUTPUT));
  375. addOutput(Port::create<PJ301MOPort>(Vec(12.5+knob*1,340), Port::OUTPUT, module, Utility::B_OUTPUT));
  376. addOutput(Port::create<PJ301MOPort>(Vec(12.5+knob*2,340), Port::OUTPUT, module, Utility::C_OUTPUT));
  377. addParam(ParamWidget::create<CKSSS>(Vec(39,150), module, Utility::LINK_A_PARAM, 0.0, 1.0, 0.0));
  378. addParam(ParamWidget::create<CKSSS>(Vec(74.5, 150), module, Utility::LINK_B_PARAM, 0.0, 1.0, 0.0));
  379. }
  380. };
  381. } // namespace rack_plugin_dBiz
  382. using namespace rack_plugin_dBiz;
  383. RACK_PLUGIN_MODEL_INIT(dBiz, Utility) {
  384. Model *modelUtility = Model::create<Utility, UtilityWidget>("dBiz", "Utility", "Utility", QUANTIZER_TAG);
  385. return modelUtility;
  386. }