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.

252 lines
6.8KB

  1. #include "Core.hpp"
  2. #include "midi.hpp"
  3. #include "dsp/digital.hpp"
  4. #include "dsp/filter.hpp"
  5. #include <algorithm>
  6. struct MidiNoteData {
  7. uint8_t velocity = 0;
  8. uint8_t aftertouch = 0;
  9. };
  10. struct MIDIToCVInterface : Module {
  11. enum ParamIds {
  12. NUM_PARAMS
  13. };
  14. enum InputIds {
  15. NUM_INPUTS
  16. };
  17. enum OutputIds {
  18. CV_OUTPUT,
  19. GATE_OUTPUT,
  20. VELOCITY_OUTPUT,
  21. MOD_OUTPUT,
  22. PITCH_OUTPUT,
  23. AFTERTOUCH_OUTPUT,
  24. START_OUTPUT,
  25. STOP_OUTPUT,
  26. CONTINUE_OUTPUT,
  27. CLOCK_OUTPUT,
  28. CLOCK_2_OUTPUT,
  29. CLOCK_HALF_OUTPUT,
  30. NUM_OUTPUTS
  31. };
  32. enum LightIds {
  33. NUM_LIGHTS
  34. };
  35. MidiInputQueue midiInput;
  36. uint8_t mod = 0;
  37. ExponentialFilter modFilter;
  38. uint16_t pitch = 0;
  39. ExponentialFilter pitchFilter;
  40. PulseGenerator startPulse;
  41. PulseGenerator stopPulse;
  42. PulseGenerator continuePulse;
  43. PulseGenerator clockPulse;
  44. MidiNoteData noteData[128];
  45. std::list<uint8_t> heldNotes;
  46. uint8_t lastNote;
  47. bool pedal;
  48. bool gate;
  49. MIDIToCVInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  50. onReset();
  51. }
  52. void onReset() override {
  53. heldNotes.clear();
  54. pedal = false;
  55. lastNote = 60;
  56. }
  57. void pressNote(uint8_t note) {
  58. // Remove existing similar note
  59. heldNotes.remove(note);
  60. // Push note
  61. heldNotes.push_back(note);
  62. lastNote = note;
  63. gate = true;
  64. }
  65. void releaseNote(uint8_t note) {
  66. // Remove the note
  67. heldNotes.remove(note);
  68. // Hold note if pedal is pressed
  69. if (pedal)
  70. return;
  71. // Set last note
  72. if (!heldNotes.empty()) {
  73. auto it2 = heldNotes.end();
  74. it2--;
  75. lastNote = *it2;
  76. gate = true;
  77. }
  78. else {
  79. gate = false;
  80. }
  81. }
  82. void pressPedal() {
  83. pedal = true;
  84. }
  85. void releasePedal() {
  86. pedal = false;
  87. releaseNote(255);
  88. }
  89. void step() override {
  90. MidiMessage msg;
  91. while (midiInput.shift(&msg)) {
  92. processMessage(msg);
  93. }
  94. float deltaTime = engineGetSampleTime();
  95. outputs[CV_OUTPUT].value = (lastNote - 60) / 12.f;
  96. outputs[GATE_OUTPUT].value = gate ? 10.f : 0.f;
  97. outputs[VELOCITY_OUTPUT].value = rescale(noteData[lastNote].velocity, 0, 127, 0.f, 10.f);
  98. modFilter.lambda = 100.f * deltaTime;
  99. outputs[MOD_OUTPUT].value = modFilter.process(rescale(mod, 0, 127, 0.f, 10.f));
  100. pitchFilter.lambda = 100.f * deltaTime;
  101. outputs[PITCH_OUTPUT].value = pitchFilter.process(rescale(pitch, 0, 16384, -5.f, 5.f));
  102. outputs[AFTERTOUCH_OUTPUT].value = rescale(noteData[lastNote].aftertouch, 0, 127, 0.f, 10.f);
  103. outputs[START_OUTPUT].value = startPulse.process(deltaTime) ? 10.f : 0.f;
  104. outputs[STOP_OUTPUT].value = stopPulse.process(deltaTime) ? 10.f : 0.f;
  105. outputs[CONTINUE_OUTPUT].value = continuePulse.process(deltaTime) ? 10.f : 0.f;
  106. outputs[CLOCK_OUTPUT].value = clockPulse.process(deltaTime) ? 10.f : 0.f;
  107. }
  108. void processMessage(MidiMessage msg) {
  109. // debug("MIDI: %01x %01x %02x %02x", msg.status(), msg.channel(), msg.data1, msg.data2);
  110. switch (msg.status()) {
  111. // note off
  112. case 0x8: {
  113. releaseNote(msg.data1);
  114. } break;
  115. // note on
  116. case 0x9: {
  117. if (msg.data2 > 0) {
  118. uint8_t note = msg.data1 & 0x7f;
  119. noteData[note].velocity = msg.data2;
  120. pressNote(msg.data1);
  121. }
  122. else {
  123. // For some reason, some keyboards send a "note on" event with a velocity of 0 to signal that the key has been released.
  124. releaseNote(msg.data1);
  125. }
  126. } break;
  127. // channel aftertouch
  128. case 0xa: {
  129. uint8_t note = msg.data1 & 0x7f;
  130. noteData[note].aftertouch = msg.data2;
  131. } break;
  132. // cc
  133. case 0xb: {
  134. processCC(msg);
  135. } break;
  136. // pitch wheel
  137. case 0xe: {
  138. pitch = msg.data2 * 128 + msg.data1;
  139. } break;
  140. case 0xf: {
  141. processSystem(msg);
  142. } break;
  143. default: break;
  144. }
  145. }
  146. void processCC(MidiMessage msg) {
  147. switch (msg.data1) {
  148. // mod
  149. case 0x01: {
  150. mod = msg.data2;
  151. } break;
  152. // sustain
  153. case 0x40: {
  154. if (msg.data2 >= 64)
  155. pressPedal();
  156. else
  157. releasePedal();
  158. } break;
  159. default: break;
  160. }
  161. }
  162. void processSystem(MidiMessage msg) {
  163. switch (msg.channel()) {
  164. // Timing
  165. case 0x8: {
  166. clockPulse.trigger(1e-3);
  167. } break;
  168. // Start
  169. case 0xa: {
  170. startPulse.trigger(1e-3);
  171. } break;
  172. // Continue
  173. case 0xb: {
  174. continuePulse.trigger(1e-3);
  175. } break;
  176. // Stop
  177. case 0xc: {
  178. stopPulse.trigger(1e-3);
  179. } break;
  180. default: break;
  181. }
  182. }
  183. json_t *toJson() override {
  184. json_t *rootJ = json_object();
  185. json_object_set_new(rootJ, "midi", midiInput.toJson());
  186. return rootJ;
  187. }
  188. void fromJson(json_t *rootJ) override {
  189. json_t *midiJ = json_object_get(rootJ, "midi");
  190. midiInput.fromJson(midiJ);
  191. }
  192. };
  193. struct MIDIToCVInterfaceWidget : ModuleWidget {
  194. MIDIToCVInterfaceWidget(MIDIToCVInterface *module) : ModuleWidget(module) {
  195. setPanel(SVG::load(assetGlobal("res/Core/MIDIToCVInterface.svg")));
  196. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  197. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  198. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  199. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  200. addOutput(Port::create<PJ301MPort>(mm2px(Vec(4.61505, 60.1445)), Port::OUTPUT, module, MIDIToCVInterface::CV_OUTPUT));
  201. addOutput(Port::create<PJ301MPort>(mm2px(Vec(16.214, 60.1445)), Port::OUTPUT, module, MIDIToCVInterface::GATE_OUTPUT));
  202. addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.8143, 60.1445)), Port::OUTPUT, module, MIDIToCVInterface::VELOCITY_OUTPUT));
  203. addOutput(Port::create<PJ301MPort>(mm2px(Vec(4.61505, 76.1449)), Port::OUTPUT, module, MIDIToCVInterface::MOD_OUTPUT));
  204. addOutput(Port::create<PJ301MPort>(mm2px(Vec(16.214, 76.1449)), Port::OUTPUT, module, MIDIToCVInterface::PITCH_OUTPUT));
  205. addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.8143, 76.1449)), Port::OUTPUT, module, MIDIToCVInterface::AFTERTOUCH_OUTPUT));
  206. addOutput(Port::create<PJ301MPort>(mm2px(Vec(4.61505, 92.1439)), Port::OUTPUT, module, MIDIToCVInterface::START_OUTPUT));
  207. addOutput(Port::create<PJ301MPort>(mm2px(Vec(16.214, 92.1439)), Port::OUTPUT, module, MIDIToCVInterface::STOP_OUTPUT));
  208. addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.8143, 92.1439)), Port::OUTPUT, module, MIDIToCVInterface::CONTINUE_OUTPUT));
  209. addOutput(Port::create<PJ301MPort>(mm2px(Vec(4.61505, 108.144)), Port::OUTPUT, module, MIDIToCVInterface::CLOCK_OUTPUT));
  210. addOutput(Port::create<PJ301MPort>(mm2px(Vec(16.214, 108.144)), Port::OUTPUT, module, MIDIToCVInterface::CLOCK_2_OUTPUT));
  211. addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.8143, 108.144)), Port::OUTPUT, module, MIDIToCVInterface::CLOCK_HALF_OUTPUT));
  212. MidiWidget *midiWidget = Widget::create<MidiWidget>(mm2px(Vec(3.41891, 14.8373)));
  213. midiWidget->box.size = mm2px(Vec(33.840, 28));
  214. midiWidget->midiIO = &module->midiInput;
  215. addChild(midiWidget);
  216. }
  217. };
  218. Model *modelMIDIToCVInterface = Model::create<MIDIToCVInterface, MIDIToCVInterfaceWidget>("Core", "MIDIToCVInterface", "MIDI-1", MIDI_TAG, EXTERNAL_TAG);