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.

390 lines
12KB

  1. /******************************************************************************
  2. * Copyright 2017-2018 Valerio Orlandini / Sonus Dept. <sonusdept@gmail.com>
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. *****************************************************************************/
  17. #include "sonusmodular.hpp"
  18. namespace rack_plugin_SonusModular {
  19. #define TWO_1_12 1.059463094359295
  20. #define TWO_1_24 1.029302236643492
  21. #define TWO_1_72 1.009673533228511
  22. enum Intervals
  23. {
  24. CHROMATIC,
  25. QUARTER_TONES,
  26. TWELFTH_TONES
  27. };
  28. typedef struct
  29. {
  30. bool gate;
  31. float pitch;
  32. } step_info_t;
  33. struct Micromacro : Module
  34. {
  35. enum ParamIds
  36. {
  37. BPM,
  38. STEPS,
  39. BASE_OCTAVE,
  40. BASE_NOTE,
  41. TUNE,
  42. INTERVALS,
  43. CLOCK_SOURCE,
  44. PLAY,
  45. RESET,
  46. PITCH_1_1,
  47. GATE_1_1,
  48. PITCH_2_1,
  49. GATE_2_1,
  50. PITCH_3_1,
  51. GATE_3_1,
  52. PITCH_4_1,
  53. GATE_4_1,
  54. PITCH_5_1,
  55. GATE_5_1,
  56. PITCH_6_1,
  57. GATE_6_1,
  58. PITCH_7_1,
  59. GATE_7_1,
  60. PITCH_8_1,
  61. GATE_8_1,
  62. PITCH_9_1,
  63. GATE_9_1,
  64. PITCH_10_1,
  65. GATE_10_1,
  66. PITCH_11_1,
  67. GATE_11_1,
  68. PITCH_12_1,
  69. GATE_12_1,
  70. PITCH_13_1,
  71. GATE_13_1,
  72. PITCH_14_1,
  73. GATE_14_1,
  74. PITCH_15_1,
  75. GATE_15_1,
  76. PITCH_16_1,
  77. GATE_16_1,
  78. PITCH_1_2,
  79. GATE_1_2,
  80. PITCH_2_2,
  81. GATE_2_2,
  82. PITCH_3_2,
  83. GATE_3_2,
  84. PITCH_4_2,
  85. GATE_4_2,
  86. PITCH_5_2,
  87. GATE_5_2,
  88. PITCH_6_2,
  89. GATE_6_2,
  90. PITCH_7_2,
  91. GATE_7_2,
  92. PITCH_8_2,
  93. GATE_8_2,
  94. PITCH_9_2,
  95. GATE_9_2,
  96. PITCH_10_2,
  97. GATE_10_2,
  98. PITCH_11_2,
  99. GATE_11_2,
  100. PITCH_12_2,
  101. GATE_12_2,
  102. PITCH_13_2,
  103. GATE_13_2,
  104. PITCH_14_2,
  105. GATE_14_2,
  106. PITCH_15_2,
  107. GATE_15_2,
  108. PITCH_16_2,
  109. GATE_16_2,
  110. PITCH_1_3,
  111. GATE_1_3,
  112. PITCH_2_3,
  113. GATE_2_3,
  114. PITCH_3_3,
  115. GATE_3_3,
  116. PITCH_4_3,
  117. GATE_4_3,
  118. PITCH_5_3,
  119. GATE_5_3,
  120. PITCH_6_3,
  121. GATE_6_3,
  122. PITCH_7_3,
  123. GATE_7_3,
  124. PITCH_8_3,
  125. GATE_8_3,
  126. PITCH_9_3,
  127. GATE_9_3,
  128. PITCH_10_3,
  129. GATE_10_3,
  130. PITCH_11_3,
  131. GATE_11_3,
  132. PITCH_12_3,
  133. GATE_12_3,
  134. PITCH_13_3,
  135. GATE_13_3,
  136. PITCH_14_3,
  137. GATE_14_3,
  138. PITCH_15_3,
  139. GATE_15_3,
  140. PITCH_16_3,
  141. GATE_16_3,
  142. NUM_PARAMS
  143. };
  144. enum InputIds
  145. {
  146. EXT_CLOCK,
  147. CV_RESET,
  148. NUM_INPUTS
  149. };
  150. enum OutputIds
  151. {
  152. GATE_OUT_1,
  153. PITCH_OUT_1,
  154. GATE_OUT_2,
  155. PITCH_OUT_2,
  156. GATE_OUT_3,
  157. PITCH_OUT_3,
  158. NUM_OUTPUTS
  159. };
  160. enum LightIds
  161. {
  162. PLAY_LED,
  163. STEP_LED_1,
  164. STEP_LED_2,
  165. STEP_LED_3,
  166. STEP_LED_4,
  167. STEP_LED_5,
  168. STEP_LED_6,
  169. STEP_LED_7,
  170. STEP_LED_8,
  171. STEP_LED_9,
  172. STEP_LED_10,
  173. STEP_LED_11,
  174. STEP_LED_12,
  175. STEP_LED_13,
  176. STEP_LED_14,
  177. STEP_LED_15,
  178. STEP_LED_16,
  179. NUM_LIGHTS
  180. };
  181. Micromacro() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
  182. void step() override;
  183. step_info_t steps[16][3];
  184. int base_note;
  185. float base_freq;
  186. unsigned int current_step = 0;
  187. unsigned int total_steps;
  188. unsigned int type;
  189. bool paused;
  190. bool clock_source_ext = false;
  191. float time_elapsed = 0.0;
  192. float step_duration;
  193. float ext_clock_last_value;
  194. float play_last_value;
  195. float reset_last_value[2];
  196. float tune;
  197. };
  198. void Micromacro::step()
  199. {
  200. step_duration = 15.0 / params[BPM].value;
  201. params[CLOCK_SOURCE].value != 1.0 ? clock_source_ext = false : clock_source_ext = true;
  202. time_elapsed += engineGetSampleTime();
  203. total_steps = (unsigned int)(roundf(params[STEPS].value));
  204. tune = params[TUNE].value;
  205. // Distance in semitones from middle A
  206. base_note = (roundf(params[BASE_OCTAVE].value) * 12.0) + roundf(params[BASE_NOTE].value) - 9.0;
  207. base_freq = tune * powf(TWO_1_12, base_note);
  208. type = (unsigned int)(params[INTERVALS].value);
  209. params[PLAY].value != 1.0 ? paused = true : paused = false;
  210. play_last_value = params[PLAY].value;
  211. for (unsigned int s = 0; s < 16; s++)
  212. {
  213. s == current_step ? lights[STEP_LED_1 + s].value = 1.0 : lights[STEP_LED_1 + s].value = 0.0;
  214. params[GATE_1_1 + (s * 2)].value == 0.0 ? steps[s][0].gate = false : steps[s][0].gate = true;
  215. params[GATE_1_2 + (s * 2)].value == 0.0 ? steps[s][1].gate = false : steps[s][1].gate = true;
  216. params[GATE_1_3 + (s * 2)].value == 0.0 ? steps[s][2].gate = false : steps[s][2].gate = true;
  217. switch (type)
  218. {
  219. case CHROMATIC:
  220. steps[s][0].pitch = base_freq * powf(TWO_1_12, roundf(params[PITCH_1_1 + (s * 2)].value));
  221. steps[s][1].pitch = base_freq * powf(TWO_1_12, roundf(params[PITCH_1_2 + (s * 2)].value));
  222. steps[s][2].pitch = base_freq * powf(TWO_1_12, roundf(params[PITCH_1_3 + (s * 2)].value));
  223. break;
  224. case QUARTER_TONES:
  225. steps[s][0].pitch = base_freq * powf(TWO_1_24, roundf(params[PITCH_1_1 + (s * 2)].value));
  226. steps[s][1].pitch = base_freq * powf(TWO_1_24, roundf(params[PITCH_1_2 + (s * 2)].value));
  227. steps[s][2].pitch = base_freq * powf(TWO_1_24, roundf(params[PITCH_1_3 + (s * 2)].value));
  228. break;
  229. case TWELFTH_TONES:
  230. steps[s][0].pitch = base_freq * powf(TWO_1_72, roundf(params[PITCH_1_1 + (s * 2)].value));
  231. steps[s][1].pitch = base_freq * powf(TWO_1_72, roundf(params[PITCH_1_2 + (s * 2)].value));
  232. steps[s][2].pitch = base_freq * powf(TWO_1_72, roundf(params[PITCH_1_3 + (s * 2)].value));
  233. break;
  234. }
  235. }
  236. if (!paused)
  237. {
  238. for (unsigned int t = 0; t < 3; t++)
  239. {
  240. outputs[PITCH_OUT_1 + (unsigned int)(t * 2)].value = clamp(-1.0f * log2(440.0 / steps[current_step][t].pitch), -4.0f, 4.0f);
  241. outputs[GATE_OUT_1 + (unsigned int)(t * 2)].value = 0.0;
  242. }
  243. lights[PLAY_LED].value = 1.0;
  244. if (!clock_source_ext)
  245. {
  246. if (time_elapsed >= step_duration)
  247. {
  248. time_elapsed = 0.0;
  249. if (++current_step >= total_steps)
  250. {
  251. current_step = 0;
  252. }
  253. for (unsigned int t = 0; t < 3; t++)
  254. {
  255. if (steps[current_step][t].gate)
  256. {
  257. outputs[GATE_OUT_1 + (unsigned int)(t * 2)].value = 5.0;
  258. }
  259. else
  260. {
  261. outputs[GATE_OUT_1 + (unsigned int)(t * 2)].value = 0.0;
  262. }
  263. }
  264. }
  265. }
  266. else
  267. {
  268. if (inputs[EXT_CLOCK].value != 0.0 && ext_clock_last_value == 0.0)
  269. {
  270. if (++current_step >= total_steps)
  271. {
  272. current_step = 0;
  273. }
  274. for (unsigned int t = 0; t < 3; t++)
  275. {
  276. if (steps[current_step][t].gate)
  277. {
  278. outputs[GATE_OUT_1 + (unsigned int)(t * 2)].value = 5.0;
  279. }
  280. else
  281. {
  282. outputs[GATE_OUT_1 + (unsigned int)(t * 2)].value = 0.0;
  283. }
  284. }
  285. }
  286. ext_clock_last_value = inputs[EXT_CLOCK].value;
  287. }
  288. }
  289. else
  290. {
  291. lights[PLAY_LED].value = 0.0;
  292. }
  293. if ((inputs[CV_RESET].value != 0.0 && reset_last_value[0] == 0.0) || (params[RESET].value == 1.0 && reset_last_value[1] == 0.0))
  294. {
  295. current_step = 0;
  296. }
  297. reset_last_value[0] = inputs[CV_RESET].value;
  298. reset_last_value[1] = params[RESET].value;
  299. }
  300. struct MicromacroWidget : ModuleWidget
  301. {
  302. MicromacroWidget(Micromacro *module);
  303. ParamWidget *play_button;
  304. ParamWidget *clock_select;
  305. };
  306. MicromacroWidget::MicromacroWidget(Micromacro *module) : ModuleWidget(module)
  307. {
  308. box.size = Vec(15 * 54, 380);
  309. {
  310. SVGPanel *panel = new SVGPanel();
  311. panel->box.size = box.size;
  312. panel->setBackground(SVG::load(assetPlugin(plugin, "res/micromacro.svg")));
  313. addChild(panel);
  314. }
  315. addChild(Widget::create<SonusScrew>(Vec(0, 0)));
  316. addChild(Widget::create<SonusScrew>(Vec(box.size.x - 15, 0)));
  317. addChild(Widget::create<SonusScrew>(Vec(0, 365)));
  318. addChild(Widget::create<SonusScrew>(Vec(box.size.x - 15, 365)));
  319. addParam(ParamWidget::create<SonusBigKnob>(Vec(45, 46.5), module, Micromacro::BPM, 20.0, 220.0, 120.0));
  320. addParam(ParamWidget::create<SonusBigSnapKnob>(Vec(133, 46.5), module, Micromacro::STEPS, 1.0, 16.0, 16.0));
  321. addParam(ParamWidget::create<SonusBigSnapKnob>(Vec(223, 46.5), module, Micromacro::BASE_OCTAVE, -3.0, 3.0, 0));
  322. addParam(ParamWidget::create<SonusBigSnapKnob>(Vec(311, 46.5), module, Micromacro::BASE_NOTE, 0.0, 11.0, 0.0));
  323. addParam(ParamWidget::create<SonusBigKnob>(Vec(399, 46.5), module, Micromacro::TUNE, 432.0, 448.0, 440.0));
  324. addParam(ParamWidget::create<NKK>(Vec(486, 49), module, Micromacro::INTERVALS, 0.0, 2.0, 0.0));
  325. addInput(Port::create<PJ301MPort>(Vec(592, 59), Port::INPUT, module, Micromacro::EXT_CLOCK));
  326. clock_select = ParamWidget::create<CKSS>(Vec(635, 61.5), module, Micromacro::CLOCK_SOURCE, 0.0, 1.0, 0.0);
  327. addParam(clock_select);
  328. clock_select->randomizable = false;
  329. addInput(Port::create<PJ301MPort>(Vec(688, 50.5), Port::INPUT, module, Micromacro::CV_RESET));
  330. addParam(ParamWidget::create<CKD6>(Vec(728, 50.5), module, Micromacro::RESET, 0.0, 1.0, 0.0));
  331. play_button = ParamWidget::create<SonusLedButton>(Vec(728, 86.5), module, Micromacro::PLAY, 0.0, 1.0, 1.0);
  332. addParam(play_button);
  333. play_button->randomizable = false;
  334. for (unsigned int s = 0; s < 16; s++)
  335. {
  336. addChild(ModuleLightWidget::create<MediumLight<GreenLight>>(Vec(23.5 + (s * 44.0), 127), module, Micromacro::STEP_LED_1 + s));
  337. }
  338. for (unsigned int t = 0; t < 3; t++)
  339. {
  340. addOutput(Port::create<PJ301MPort>(Vec(730, 154 + (t * 70.0)), Port::OUTPUT, module, Micromacro::GATE_OUT_1 + (t * 2)));
  341. addOutput(Port::create<PJ301MPort>(Vec(770, 154 + (t * 70.0)), Port::OUTPUT, module, Micromacro::PITCH_OUT_1 + (t * 2)));
  342. for (unsigned int s = 0; s < 16; s++)
  343. {
  344. addParam(ParamWidget::create<SonusSnapKnob>(Vec(10.0 + (s * 44.0), 149.0 + (t * 70.0)), module, Micromacro::PITCH_1_1 + (s * 2) + (t * 32), 0.0, 17.0, 0.0));
  345. addParam(ParamWidget::create<SonusLedButton>(Vec(14.0 + (s * 44.0), 185.5 + (t * 70.0)), module, Micromacro::GATE_1_1 + (s * 2) + (t * 32), 0.0, 1.0, 1.0));
  346. }
  347. }
  348. addChild(ModuleLightWidget::create<MediumLight<GreenLight>>(Vec(757, 27), module, Micromacro::PLAY_LED));
  349. }
  350. } // namespace rack_plugin_SonusModular
  351. using namespace rack_plugin_SonusModular;
  352. RACK_PLUGIN_MODEL_INIT(SonusModular, Micromacro) {
  353. Model *modelMicromacro = Model::create<Micromacro, MicromacroWidget>("Sonus Modular", "Micromacro", "Micromacro | Pitch Quantized Sequencer", SEQUENCER_TAG);
  354. return modelMicromacro;
  355. }