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.

373 lines
9.0KB

  1. #if 0
  2. #include <list>
  3. #include <algorithm>
  4. #include "core.hpp"
  5. #include "MidiIO.hpp"
  6. #include "dsp/digital.hpp"
  7. using namespace rack;
  8. struct MIDIClockToCVInterface : MidiIO, Module {
  9. enum ParamIds {
  10. NUM_PARAMS
  11. };
  12. enum InputIds {
  13. CLOCK1_RATIO,
  14. CLOCK2_RATIO,
  15. NUM_INPUTS
  16. };
  17. enum OutputIds {
  18. CLOCK1_PULSE,
  19. CLOCK2_PULSE,
  20. CONTINUE_PULSE,
  21. START_PULSE,
  22. STOP_PULSE,
  23. NUM_OUTPUTS
  24. };
  25. int clock1ratio = 0;
  26. int clock2ratio = 0;
  27. PulseGenerator clock1Pulse;
  28. PulseGenerator clock2Pulse;
  29. PulseGenerator continuePulse;
  30. PulseGenerator startPulse;
  31. PulseGenerator stopPulse;
  32. bool tick = false;
  33. bool running = false;
  34. bool start = false;
  35. bool stop = false;
  36. bool cont = false;
  37. int c_bar = 0;
  38. /* Note this is in relation to the Midi clock's Tick (6x per 16th note).
  39. * Therefore, e.g. the 2:3 is calculated:
  40. *
  41. * 24 (Ticks per quarter note) * 2 / 3 = 16
  42. *
  43. * Implying that every 16 midi clock ticks we need to send a pulse
  44. * */
  45. const int ratios[9] = {6, 8, 12, 16, 24, 32, 48, 96, 192};
  46. const int numratios = 9;
  47. /*
  48. * Length of clock pulse
  49. */
  50. const float pulseTime = 0.005;
  51. MIDIClockToCVInterface() : MidiIO(), Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {
  52. }
  53. ~MIDIClockToCVInterface() {
  54. }
  55. void step() override;
  56. void processMidi(std::vector<unsigned char> msg);
  57. void onDeviceChange() override;
  58. void resetMidi() override;
  59. json_t *toJson() override {
  60. json_t *rootJ = json_object();
  61. addBaseJson(rootJ);
  62. json_object_set_new(rootJ, "clock1ratio", json_integer(clock1ratio));
  63. json_object_set_new(rootJ, "clock2ratio", json_integer(clock2ratio));
  64. return rootJ;
  65. }
  66. void fromJson(json_t *rootJ) override {
  67. baseFromJson(rootJ);
  68. json_t *c1rJ = json_object_get(rootJ, "clock1ratio");
  69. if (c1rJ) {
  70. clock1ratio = json_integer_value(c1rJ);
  71. }
  72. json_t *c2rJ = json_object_get(rootJ, "clock2ratio");
  73. if (c2rJ) {
  74. clock2ratio = json_integer_value(c2rJ);
  75. }
  76. }
  77. };
  78. void MIDIClockToCVInterface::step() {
  79. float sampleRate = engineGetSampleRate();
  80. if (isPortOpen()) {
  81. std::vector<unsigned char> message;
  82. // midiIn->getMessage returns empty vector if there are no messages in the queue
  83. getMessage(&message);
  84. if (message.size() > 0) {
  85. processMidi(message);
  86. }
  87. }
  88. if (inputs[CLOCK1_RATIO].active) {
  89. clock1ratio = int(clampf(inputs[CLOCK1_RATIO].value, 0.0, 10.0) * (numratios - 1) / 10);
  90. }
  91. if (inputs[CLOCK2_RATIO].active) {
  92. clock2ratio = int(clampf(inputs[CLOCK2_RATIO].value, 0.0, 10.0) * (numratios - 1) / 10);
  93. }
  94. if (start) {
  95. start = false;
  96. running = true;
  97. startPulse.trigger(pulseTime);
  98. c_bar = 0;
  99. }
  100. if (stop) {
  101. stop = false;
  102. running = false;
  103. stopPulse.trigger(pulseTime);
  104. }
  105. if (cont) {
  106. cont = false;
  107. running = true;
  108. continuePulse.trigger(pulseTime);
  109. }
  110. if (tick) {
  111. tick = false;
  112. /* Note: At least for my midi clock, the clock ticks are sent
  113. * even if the midi clock is stopped.
  114. * Therefore, we need to keep track of ticks even when the clock
  115. * is stopped. Otherwise we can run into weird timing issues.
  116. */
  117. if (running) {
  118. if (c_bar % ratios[clock1ratio] == 0) {
  119. clock1Pulse.trigger(pulseTime);
  120. }
  121. if (c_bar % ratios[clock2ratio] == 0) {
  122. clock2Pulse.trigger(pulseTime);
  123. }
  124. }
  125. c_bar++;
  126. // One "midi bar" = 4 whole notes = (6 ticks per 16th) 6 * 16 *4 = 384
  127. if (c_bar >= 384) {
  128. c_bar = 0;
  129. }
  130. }
  131. bool pulse = clock1Pulse.process(1.0 / sampleRate);
  132. outputs[CLOCK1_PULSE].value = pulse ? 10.0 : 0.0;
  133. pulse = clock2Pulse.process(1.0 / sampleRate);
  134. outputs[CLOCK2_PULSE].value = pulse ? 10.0 : 0.0;
  135. pulse = continuePulse.process(1.0 / sampleRate);
  136. outputs[CONTINUE_PULSE].value = pulse ? 10.0 : 0.0;
  137. pulse = startPulse.process(1.0 / sampleRate);
  138. outputs[START_PULSE].value = pulse ? 10.0 : 0.0;
  139. pulse = stopPulse.process(1.0 / sampleRate);
  140. outputs[STOP_PULSE].value = pulse ? 10.0 : 0.0;
  141. }
  142. void MIDIClockToCVInterface::resetMidi() {
  143. outputs[CLOCK1_PULSE].value = 0.0;
  144. outputs[CLOCK2_PULSE].value = 0.0;
  145. }
  146. void MIDIClockToCVInterface::processMidi(std::vector<unsigned char> msg) {
  147. switch (msg[0]) {
  148. case 0xfa:
  149. start = true;
  150. break;
  151. case 0xfb:
  152. cont = true;
  153. break;
  154. case 0xfc:
  155. stop = true;
  156. break;
  157. case 0xf8:
  158. tick = true;
  159. break;
  160. }
  161. }
  162. void MIDIClockToCVInterface::onDeviceChange() {
  163. setIgnores(true, false);
  164. }
  165. struct ClockRatioItem : MenuItem {
  166. int ratio;
  167. int *clockRatio;
  168. void onAction(EventAction &e) override {
  169. *clockRatio = ratio;
  170. }
  171. };
  172. struct ClockRatioChoice : ChoiceButton {
  173. int *clockRatio;
  174. const std::vector<std::string> ratioNames = {"Sixteenth note (1:4 ratio)", "Eighth note triplet (1:3 ratio)",
  175. "Eighth note (1:2 ratio)", "Quarter note triplet (2:3 ratio)",
  176. "Quarter note (tap speed)", "Half note triplet (4:3 ratio)",
  177. "Half note (2:1 ratio)", "Whole note (4:1 ratio)",
  178. "Two whole notes (8:1 ratio)"
  179. };
  180. const std::vector<std::string> ratioNames_short = {"1:4 ratio", "1:3 ratio", "1:2 ratio", "2:3 ratio", "1:1 ratio",
  181. "4:3", "2:1 ratio", "4:1 ratio", "8:1 ratio"
  182. };
  183. void onAction(EventAction &e) override {
  184. Menu *menu = gScene->createMenu();
  185. menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round();
  186. menu->box.size.x = box.size.x;
  187. for (unsigned long ratio = 0; ratio < ratioNames.size(); ratio++) {
  188. ClockRatioItem *clockRatioItem = new ClockRatioItem();
  189. clockRatioItem->ratio = ratio;
  190. clockRatioItem->clockRatio = clockRatio;
  191. clockRatioItem->text = ratioNames[ratio];
  192. menu->addChild(clockRatioItem);
  193. }
  194. }
  195. void step() override {
  196. text = ratioNames_short[*clockRatio];
  197. }
  198. };
  199. MIDIClockToCVWidget::MIDIClockToCVWidget() {
  200. MIDIClockToCVInterface *module = new MIDIClockToCVInterface();
  201. setModule(module);
  202. box.size = Vec(15 * 9, 380);
  203. {
  204. Panel *panel = new LightPanel();
  205. panel->box.size = box.size;
  206. addChild(panel);
  207. }
  208. float margin = 5;
  209. float labelHeight = 15;
  210. float yPos = margin;
  211. addChild(createScrew<ScrewSilver>(Vec(15, 0)));
  212. addChild(createScrew<ScrewSilver>(Vec(box.size.x - 30, 0)));
  213. addChild(createScrew<ScrewSilver>(Vec(15, 365)));
  214. addChild(createScrew<ScrewSilver>(Vec(box.size.x - 30, 365)));
  215. {
  216. Label *label = new Label();
  217. label->box.pos = Vec(box.size.x - margin - 7 * 15, margin);
  218. label->text = "MIDI Clk-CV";
  219. addChild(label);
  220. yPos = labelHeight * 2;
  221. }
  222. {
  223. Label *label = new Label();
  224. label->box.pos = Vec(margin, yPos);
  225. label->text = "MIDI Interface";
  226. addChild(label);
  227. yPos += labelHeight + margin;
  228. MidiChoice *midiChoice = new MidiChoice();
  229. midiChoice->midiModule = dynamic_cast<MidiIO *>(module);
  230. midiChoice->box.pos = Vec(margin, yPos);
  231. midiChoice->box.size.x = box.size.x - 10;
  232. addChild(midiChoice);
  233. yPos += midiChoice->box.size.y + margin * 4;
  234. }
  235. {
  236. Label *label = new Label();
  237. label->box.pos = Vec(margin, yPos);
  238. label->text = "Start";
  239. addChild(label);
  240. addOutput(createOutput<PJ3410Port>(Vec(15 * 6, yPos - 5), module, MIDIClockToCVInterface::START_PULSE));
  241. yPos += labelHeight + margin * 4;
  242. }
  243. {
  244. Label *label = new Label();
  245. label->box.pos = Vec(margin, yPos);
  246. label->text = "Stop";
  247. addChild(label);
  248. addOutput(createOutput<PJ3410Port>(Vec(15 * 6, yPos - 5), module, MIDIClockToCVInterface::STOP_PULSE));
  249. yPos += labelHeight + margin * 4;
  250. }
  251. {
  252. Label *label = new Label();
  253. label->box.pos = Vec(margin, yPos);
  254. label->text = "Continue";
  255. addChild(label);
  256. addOutput(createOutput<PJ3410Port>(Vec(15 * 6, yPos - 5), module, MIDIClockToCVInterface::CONTINUE_PULSE));
  257. yPos += labelHeight + margin * 6;
  258. }
  259. {
  260. addInput(createInput<PJ3410Port>(Vec(margin, yPos - 5), module, MIDIClockToCVInterface::CLOCK1_RATIO));
  261. ClockRatioChoice *ratioChoice = new ClockRatioChoice();
  262. ratioChoice->clockRatio = &module->clock1ratio;
  263. ratioChoice->box.pos = Vec(int(box.size.x / 3), yPos);
  264. ratioChoice->box.size.x = int(box.size.x / 1.5 - margin);
  265. addChild(ratioChoice);
  266. yPos += ratioChoice->box.size.y + margin * 3;
  267. }
  268. {
  269. Label *label = new Label();
  270. label->box.pos = Vec(margin, yPos);
  271. label->text = "C1 Pulse";
  272. addChild(label);
  273. addOutput(createOutput<PJ3410Port>(Vec(15 * 6, yPos - 5), module, MIDIClockToCVInterface::CLOCK1_PULSE));
  274. yPos += margin * 10;
  275. }
  276. {
  277. addInput(createInput<PJ3410Port>(Vec(margin, yPos - 5), module, MIDIClockToCVInterface::CLOCK2_RATIO));
  278. ClockRatioChoice *ratioChoice = new ClockRatioChoice();
  279. ratioChoice->clockRatio = &module->clock2ratio;
  280. ratioChoice->box.pos = Vec(int(box.size.x / 3), yPos);
  281. ratioChoice->box.size.x = int(box.size.x / 1.5 - margin);
  282. addChild(ratioChoice);
  283. yPos += ratioChoice->box.size.y + margin * 3;
  284. }
  285. {
  286. Label *label = new Label();
  287. label->box.pos = Vec(margin, yPos);
  288. label->text = "C2 Pulse";
  289. addChild(label);
  290. addOutput(createOutput<PJ3410Port>(Vec(15 * 6, yPos - 5), module, MIDIClockToCVInterface::CLOCK2_PULSE));
  291. yPos += labelHeight + margin * 3;
  292. }
  293. }
  294. void MIDIClockToCVWidget::step() {
  295. ModuleWidget::step();
  296. }
  297. #endif