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.

284 lines
6.3KB

  1. #include <list>
  2. #include <algorithm>
  3. #include "core.hpp"
  4. #include "midi.hpp"
  5. #include "dsp/digital.hpp"
  6. /*
  7. * MIDIToCVInterface converts midi note on/off events, velocity , channel aftertouch, pitch wheel and mod wheel to
  8. * CV
  9. */
  10. struct MidiValue {
  11. int val = 0; // Controller value
  12. // TransitionSmoother tSmooth;
  13. bool changed = false; // Value has been changed by midi message (only if it is in sync!)
  14. };
  15. struct MIDIToCVInterface : Module {
  16. enum ParamIds {
  17. RESET_PARAM,
  18. NUM_PARAMS
  19. };
  20. enum InputIds {
  21. NUM_INPUTS
  22. };
  23. enum OutputIds {
  24. PITCH_OUTPUT = 0,
  25. GATE_OUTPUT,
  26. VELOCITY_OUTPUT,
  27. MOD_OUTPUT,
  28. PITCHWHEEL_OUTPUT,
  29. CHANNEL_AFTERTOUCH_OUTPUT,
  30. NUM_OUTPUTS
  31. };
  32. enum LightIds {
  33. ACTIVE_LIGHT,
  34. RESET_LIGHT,
  35. NUM_LIGHTS
  36. };
  37. MidiInputQueue midiInput;
  38. std::list<int> notes;
  39. bool pedal = false;
  40. int note = 60; // C4, most modules should use 261.626 Hz
  41. int vel = 0;
  42. MidiValue mod;
  43. MidiValue afterTouch;
  44. MidiValue pitchWheel;
  45. bool gate = false;
  46. SchmittTrigger resetTrigger;
  47. MIDIToCVInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  48. pitchWheel.val = 64;
  49. // pitchWheel.tSmooth.set(0, 0);
  50. }
  51. ~MIDIToCVInterface() {
  52. };
  53. void step() override;
  54. void pressNote(int note);
  55. void releaseNote(int note);
  56. void processMidi(std::vector<unsigned char> msg);
  57. json_t *toJson() override {
  58. json_t *rootJ = json_object();
  59. // addBaseJson(rootJ);
  60. return rootJ;
  61. }
  62. void fromJson(json_t *rootJ) override {
  63. // baseFromJson(rootJ);
  64. }
  65. void onReset() override {
  66. // resetMidi();
  67. }
  68. // void resetMidi() override;
  69. };
  70. /*
  71. void MIDIToCVInterface::resetMidi() {
  72. mod.val = 0;
  73. mod.tSmooth.set(0, 0);
  74. pitchWheel.val = 64;
  75. pitchWheel.tSmooth.set(0, 0);
  76. afterTouch.val = 0;
  77. afterTouch.tSmooth.set(0, 0);
  78. vel = 0;
  79. gate = false;
  80. notes.clear();
  81. }
  82. */
  83. void MIDIToCVInterface::step() {
  84. /*
  85. if (isPortOpen()) {
  86. std::vector<unsigned char> message;
  87. // midiIn->getMessage returns empty vector if there are no messages in the queue
  88. getMessage(&message);
  89. if (message.size() > 0) {
  90. processMidi(message);
  91. }
  92. }
  93. outputs[PITCH_OUTPUT].value = ((note - 60)) / 12.0;
  94. if (resetTrigger.process(params[RESET_PARAM].value)) {
  95. resetMidi();
  96. return;
  97. }
  98. lights[RESET_LIGHT].value -= lights[RESET_LIGHT].value / 0.55 / engineGetSampleRate(); // fade out light
  99. outputs[GATE_OUTPUT].value = gate ? 10.0 : 0.0;
  100. outputs[VELOCITY_OUTPUT].value = vel / 127.0 * 10.0;
  101. int steps = int(engineGetSampleRate() / 32);
  102. if (mod.changed) {
  103. mod.tSmooth.set(outputs[MOD_OUTPUT].value, (mod.val / 127.0 * 10.0), steps);
  104. mod.changed = false;
  105. }
  106. outputs[MOD_OUTPUT].value = mod.tSmooth.next();
  107. if (pitchWheel.changed) {
  108. pitchWheel.tSmooth.set(outputs[PITCHWHEEL_OUTPUT].value, (pitchWheel.val - 64) / 64.0 * 10.0, steps);
  109. pitchWheel.changed = false;
  110. }
  111. outputs[PITCHWHEEL_OUTPUT].value = pitchWheel.tSmooth.next();
  112. outputs[CHANNEL_AFTERTOUCH_OUTPUT].value = afterTouch.val / 127.0 * 10.0;
  113. */
  114. // Lights
  115. lights[ACTIVE_LIGHT].value = midiInput.isActive() ? 1.0 : 0.0;
  116. }
  117. void MIDIToCVInterface::pressNote(int note) {
  118. // Remove existing similar note
  119. auto it = std::find(notes.begin(), notes.end(), note);
  120. if (it != notes.end())
  121. notes.erase(it);
  122. // Push note
  123. notes.push_back(note);
  124. this->note = note;
  125. gate = true;
  126. }
  127. void MIDIToCVInterface::releaseNote(int note) {
  128. // Remove the note
  129. auto it = std::find(notes.begin(), notes.end(), note);
  130. if (it != notes.end())
  131. notes.erase(it);
  132. if (pedal) {
  133. // Don't release if pedal is held
  134. gate = true;
  135. } else if (!notes.empty()) {
  136. // Play previous note
  137. auto it2 = notes.end();
  138. it2--;
  139. this->note = *it2;
  140. gate = true;
  141. } else {
  142. gate = false;
  143. }
  144. }
  145. void MIDIToCVInterface::processMidi(std::vector<unsigned char> msg) {
  146. /*
  147. int channel = msg[0] & 0xf;
  148. int status = (msg[0] >> 4) & 0xf;
  149. int data1 = msg[1];
  150. int data2 = msg[2];
  151. // fprintf(stderr, "channel %d status %d data1 %d data2 %d\n", channel, status, data1, data2);
  152. // Filter channels
  153. if (this->channel >= 0 && this->channel != channel)
  154. return;
  155. switch (status) {
  156. // note off
  157. case 0x8: {
  158. releaseNote(data1);
  159. }
  160. break;
  161. case 0x9: // note on
  162. if (data2 > 0) {
  163. pressNote(data1);
  164. this->vel = data2;
  165. } else {
  166. // For some reason, some keyboards send a "note on" event with a velocity of 0 to signal that the key has been released.
  167. releaseNote(data1);
  168. }
  169. break;
  170. case 0xb: // cc
  171. switch (data1) {
  172. case 0x01: // mod
  173. mod.val = data2;
  174. mod.changed = true;
  175. break;
  176. case 0x40: // sustain
  177. pedal = (data2 >= 64);
  178. if (!pedal) {
  179. releaseNote(-1);
  180. }
  181. break;
  182. }
  183. break;
  184. case 0xe: // pitch wheel
  185. pitchWheel.val = data2;
  186. pitchWheel.changed = true;
  187. break;
  188. case 0xd: // channel aftertouch
  189. afterTouch.val = data1;
  190. afterTouch.changed = true;
  191. break;
  192. }
  193. */
  194. }
  195. MidiToCVWidget::MidiToCVWidget() {
  196. MIDIToCVInterface *module = new MIDIToCVInterface();
  197. setModule(module);
  198. box.size = Vec(15 * 9, 380);
  199. {
  200. Panel *panel = new LightPanel();
  201. panel->box.size = box.size;
  202. addChild(panel);
  203. }
  204. float margin = 5;
  205. float labelHeight = 15;
  206. float yPos = margin;
  207. float yGap = 35;
  208. addChild(createScrew<ScrewSilver>(Vec(15, 0)));
  209. addChild(createScrew<ScrewSilver>(Vec(box.size.x - 30, 0)));
  210. addChild(createScrew<ScrewSilver>(Vec(15, 365)));
  211. addChild(createScrew<ScrewSilver>(Vec(box.size.x - 30, 365)));
  212. {
  213. Label *label = new Label();
  214. label->box.pos = Vec(box.size.x - margin - 7 * 15, margin);
  215. label->text = "MIDI to CV";
  216. addChild(label);
  217. yPos = labelHeight * 2;
  218. }
  219. addParam(createParam<LEDButton>(Vec(7 * 15, labelHeight), module, MIDIToCVInterface::RESET_PARAM, 0.0, 1.0, 0.0));
  220. addChild(createLight<SmallLight<RedLight>>(Vec(7 * 15 + 5, labelHeight + 5), module, MIDIToCVInterface::RESET_LIGHT));
  221. std::string labels[MIDIToCVInterface::NUM_OUTPUTS] = {"1V/oct", "Gate", "Velocity", "Mod Wheel", "Pitch Wheel", "Aftertouch"};
  222. for (int i = 0; i < MIDIToCVInterface::NUM_OUTPUTS; i++) {
  223. Label *label = new Label();
  224. label->box.pos = Vec(margin, yPos);
  225. label->text = labels[i];
  226. addChild(label);
  227. addOutput(createOutput<PJ3410Port>(Vec(15 * 6, yPos - 5), module, i));
  228. yPos += yGap + margin;
  229. }
  230. MidiWidget *midiWidget = construct<MIDI_DIN_MidiWidget>();
  231. midiWidget->midiIO = &module->midiInput;
  232. addChild(midiWidget);
  233. // Lights
  234. addChild(createLight<SmallLight<GreenLight>>(Vec(40, 20), module, MIDIToCVInterface::ACTIVE_LIGHT));
  235. }