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.

300 lines
6.9KB

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