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.

256 lines
7.0KB

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