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.

239 lines
6.4KB

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