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.

851 lines
27KB

  1. #include "global_pre.hpp"
  2. #include "Erratic.hpp"
  3. #include "midi.hpp"
  4. #include "dsp/digital.hpp"
  5. #include "MPEBaseWidget.hpp"
  6. #include "global_ui.hpp"
  7. struct MidiValue {
  8. int val = 0; // Controller value
  9. // TransitionSmoother tSmooth;
  10. bool changed = false; // Value has been changed by midi message (only if it is in sync!)
  11. };
  12. struct MidiNote {
  13. int pitch = 60;
  14. int vel = 0; // velocity
  15. bool gate = false;
  16. int channel ;
  17. bool noteOn, noteOff ;
  18. bool changed = false;
  19. };
  20. struct MPEPlusValue {
  21. uint16_t val = 0; // Controller value
  22. int MSB = 0 ;
  23. int LSB = 0;
  24. bool changed = false; // Value has been changed by midi message (only if it is in sync!)
  25. };
  26. struct MidiPedalValue {
  27. int val = 0; // Controller value
  28. int cc ; // need to set it
  29. bool changed = false; // Value has been changed by midi message (only if it is in sync!)
  30. };
  31. struct MPEChannel { // This contains the required info for each channel, each note in practical terms
  32. int MIDIChannel; // must initialize
  33. MidiNote note;
  34. MidiValue mod;
  35. MidiValue afterTouch;
  36. MidiValue pitchWheel;
  37. MidiValue Yaxis ;
  38. MPEPlusValue MPEPlusyAxis, MPEPluszAxis;
  39. bool changed = false;
  40. };
  41. struct QuadMPEToCV : Module {
  42. enum ParamIds {
  43. RESET_PARAM,
  44. NUM_PARAMS,
  45. BEND_RANGE_PARAM
  46. };
  47. enum InputIds {
  48. NUM_INPUTS
  49. };
  50. enum OutputIds {
  51. PITCH_OUTPUT = 0,
  52. GATE_OUTPUT = 4,
  53. VELOCITY_OUTPUT = 8,
  54. PRESSURE_OUTPUT = 12,
  55. Y_OUTPUT = 16,
  56. PEDAL_OUTPUT = 20,
  57. NUM_OUTPUTS = 21
  58. };
  59. enum LightIds {
  60. RESET_LIGHT,
  61. VELOCITY_LIGHT,
  62. PRESSURE_LIGHT,
  63. Y_AXIS_LIGHT,
  64. PEDAL_LIGHT,
  65. NUM_LIGHTS
  66. };
  67. MidiInputQueue midiInput;
  68. int polyphony = 4;
  69. std::vector<MPEChannel> mpeChannels;
  70. // MPEChannel mpeChannels[4] ; // using this here instead of a container
  71. bool noteOffReset = true; // Our default
  72. int baseMIDIChannel = 2 ;
  73. int bendRange = 48; // our default is 48 (common for ROLI), Continuum defaults to 96. This is a global parameter (for now)
  74. int globalMIDIChannel = 16; // Our default channel is 16. ROLI users will want to set this is 1
  75. bool MPEPlus = false ; // This is specially useful for Haken Continuum
  76. int YaxisCC = 74 ;
  77. bool pedal = false;
  78. // int note = 60; // C4, most modules should use 261.626 Hz
  79. int vel = 0;
  80. MidiPedalValue midiPedalOne ;
  81. SchmittTrigger resetTrigger;
  82. QuadMPEToCV() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  83. // for (int p=0; p < polyphony ; p++) {
  84. // pitchWheel[p].val = 64;
  85. // //pitchWheel[p].tSmooth.set(0, 0);
  86. // }
  87. mpeChannels.reserve(polyphony);
  88. midiPedalOne.cc = 12; // By default we use 12 (barrel i on the Haken Continuum)
  89. this->setupMIDIChannels();
  90. }
  91. ~QuadMPEToCV() {
  92. };
  93. void step() override;
  94. void pressNote(MidiNote note);
  95. void releaseNote(MidiNote note);
  96. void processMessage(MidiMessage msg) ;
  97. void setupMIDIChannels();
  98. json_t *toJson() override {
  99. json_t *rootJ = json_object();
  100. json_object_set_new(rootJ, "midi", midiInput.toJson());
  101. json_object_set_new(rootJ, "bendRange", json_integer(bendRange));
  102. json_object_set_new(rootJ, "baseMIDIChannel", json_integer(baseMIDIChannel));
  103. json_object_set_new(rootJ, "globalMidiChannel", json_integer(globalMIDIChannel));
  104. json_object_set_new(rootJ, "MPEMode", json_integer(MPEPlus));
  105. json_object_set_new(rootJ, "noteOffReset", json_boolean(noteOffReset));
  106. return rootJ;
  107. }
  108. void fromJson(json_t *rootJ) override {
  109. json_t *midiJ = json_object_get(rootJ, "midi");
  110. midiInput.fromJson(midiJ);
  111. json_t *bendRangeJ = json_object_get(rootJ, "bendRange");
  112. if (bendRangeJ) {
  113. bendRange = json_integer_value(bendRangeJ);
  114. }
  115. json_t *baseMIDIChannelJ = json_object_get(rootJ, "baseMIDIChannel");
  116. if (baseMIDIChannelJ) {
  117. baseMIDIChannel = json_integer_value(baseMIDIChannelJ);
  118. }
  119. json_t *globalMidiChannelJ = json_object_get(rootJ, "globalMidiChannel");
  120. if (globalMidiChannelJ) {
  121. globalMIDIChannel = json_integer_value(globalMidiChannelJ);
  122. }
  123. json_t *MPEModeJ = json_object_get(rootJ, "MPEMode");
  124. if (MPEModeJ) {
  125. MPEPlus = json_integer_value(MPEModeJ);
  126. }
  127. json_t *noteOffResetJ = json_object_get(rootJ, "noteOffReset");
  128. if (noteOffResetJ) {
  129. noteOffReset = json_integer_value(noteOffResetJ);
  130. }
  131. }
  132. // void fromJson(json_t *rootJ) override {
  133. // json_t *bendRangeJ = json_object_get(rootJ, "bendRange");
  134. // if (bendRangeJ)
  135. // bendRange = json_integer_value(bendRangeJ);
  136. // json_t *baseMIDIChannelJ = json_object_get(rootJ, "baseMIDIChannel");
  137. // if (baseMIDIChannelJ)
  138. // baseMIDIChannel = json_integer_value(baseMIDIChannelJ);
  139. // json_t *globalMIDIChannelJ = json_object_get(rootJ, "globalMIDIChannel");
  140. // if (globalMIDIChannelJ)
  141. // globalMIDIChannel = json_integer_value(globalMIDIChannelJ);
  142. // json_t *noteOffResetJ = json_object_get(rootJ, "noteOffReset");
  143. // if (noteOffResetJ)
  144. // noteOffReset = json_boolean_value(noteOffResetJ);
  145. // json_t *MPEPlusModeJ = json_object_get(rootJ, "MPEPlusMode");
  146. // if (MPEPlusModeJ)
  147. // MPEPlus = json_boolean_value(MPEPlusModeJ);
  148. // this->setupMIDIChannels();
  149. // }
  150. // json_t *toJson() override {
  151. // json_t *rootJ = json_object();
  152. // // Semitones
  153. // // std::cout<< "We set bendRange to " << bendRange << std::endl;
  154. // json_object_set_new(rootJ, "noteOffReset", json_boolean(noteOffReset));
  155. // json_object_set_new(rootJ, "MPEPlusMode", json_boolean(MPEPlus));
  156. // json_object_set_new(rootJ, "bendRange", json_integer(bendRange));
  157. // json_object_set_new(rootJ, "baseMIDIChannel", json_integer(baseMIDIChannel));
  158. // json_object_set_new(rootJ, "globalMIDIChannel", json_integer(globalMIDIChannel));
  159. // return rootJ;
  160. // }
  161. // void reset() override {
  162. // resetMidi();
  163. // }
  164. // void resetMidi() override;
  165. };
  166. struct QuadMPEToCVWidget : ModuleWidget {
  167. QuadMPEToCVWidget(QuadMPEToCV *module);
  168. // Reset Notes or not
  169. void appendContextMenu(Menu *menu) override {
  170. QuadMPEToCV *module = dynamic_cast<QuadMPEToCV*>(this->module);
  171. struct ResetNoteItem : MenuItem {
  172. QuadMPEToCV *module;
  173. bool resetNoteBool;
  174. void onAction(EventAction &e) override {
  175. module->noteOffReset = ! resetNoteBool;
  176. }
  177. };
  178. ResetNoteItem *item = MenuItem::create<ResetNoteItem>("Reset Note", CHECKMARK(module->noteOffReset == true));
  179. item->module = module;
  180. item->resetNoteBool = module->noteOffReset;
  181. menu->addChild(item);
  182. }
  183. };
  184. void QuadMPEToCV::setupMIDIChannels() {
  185. // std::cout << " Setting up MIDI channels with baseMIDIChannel set to " << baseMIDIChannel << std::endl;
  186. for (int p=0 ; p < polyphony ; p++) {
  187. // std::cout << " p MIDIChannel " << p << " " << p + baseMIDIChannel - 1 << std::endl;
  188. mpeChannels[p].MIDIChannel = p + baseMIDIChannel - 1; // MPE channels start at 2 onwards. We are using MIDI channel starting at 0
  189. }
  190. }
  191. // void QuadMPEToCV::resetMidi() {
  192. // for (int p=0; p < polyphony ; p++) {
  193. // mpeChannels[p].mod.val = 0;
  194. // mpeChannels[p].pitchWheel.val = 0;
  195. // mpeChannels[p].afterTouch.val = 0;
  196. // }
  197. // // mod.val = 0;
  198. // // mod.tSmooth.set(0, 0);
  199. // // pitchWheel.val = 64;
  200. // // pitchWheel.tSmooth.set(0, 0);
  201. // // afterTouch.val = 0;
  202. // // afterTouch.tSmooth.set(0, 0);
  203. // //vel = 0;
  204. // //gate = false;
  205. // //notes.clear();
  206. // }
  207. void QuadMPEToCV::step() {
  208. // if (isPortOpen()) {
  209. // std::vector<unsigned char> message;
  210. // int msgsProcessed = 0;
  211. // // midiIn->getMessage returns empty vector if there are no messages in the queue
  212. // // Original Midi to CV limits processing to 4 midi msgs, we should log how many we do at a time to look for
  213. // // potential issues, specially with MPE+
  214. // getMessage(&message);
  215. // while (msgsProcessed < 4 && message.size() > 0) {
  216. // processMidi(message);
  217. // getMessage(&message);
  218. // msgsProcessed++;
  219. // }
  220. // }
  221. MidiMessage msg;
  222. while (midiInput.shift(&msg)) {
  223. processMessage(msg);
  224. }
  225. // if (resetTrigger.process(params[RESET_PARAM].value)) {
  226. // resetMidi();
  227. // return;
  228. // }
  229. // lights[RESET_LIGHT].value -= lights[RESET_LIGHT].value / 0.55 / engineGetSampleRate(); // fade out light
  230. for (int ci=0; ci < polyphony; ci++) {
  231. if (mpeChannels[ci].changed) {
  232. if (mpeChannels[ci].note.changed) {
  233. // std::cout << "New note on ci " << ci << std::endl;
  234. // std::cout << "gate is " << mpeChannels[ci].note.gate << std::endl;
  235. outputs[GATE_OUTPUT+ci].value = mpeChannels[ci].note.gate ? 10.0 : 0.0;
  236. // std::cout << "outputs[GATE_OUTPUT+ci].value is " << outputs[GATE_OUTPUT+ci].value << std::endl;
  237. outputs[VELOCITY_OUTPUT+ci].value = mpeChannels[ci].note.vel / 127.f * 10.f;
  238. outputs[PITCH_OUTPUT+ci].value = (((mpeChannels[ci].note.pitch - 60)) / 12.0) + ((mpeChannels[ci].pitchWheel.val - 8192 ) / 8192.0 / 12.0 * (float)bendRange ) ;
  239. // std::cout << "outputs[VELOCITY_OUTPUT+ci].value is " << outputs[VELOCITY_OUTPUT+ci].value << std::endl;
  240. if (mpeChannels[ci].note.noteOff && noteOffReset) { // We reset all info when the note goes off
  241. mpeChannels[ci].note.noteOff = false;
  242. if (noteOffReset) {
  243. //std::cout << "We execute the note off reset" << std::endl;
  244. mpeChannels[ci].pitchWheel.val = 0;
  245. mpeChannels[ci].pitchWheel.changed = false;
  246. outputs[PITCH_OUTPUT+ci].value = 0 ;
  247. mpeChannels[ci].afterTouch.val = 0;
  248. mpeChannels[ci].afterTouch.changed = false;
  249. outputs[PRESSURE_OUTPUT+ci].value = 0 ;
  250. mpeChannels[ci].Yaxis.val = 0;
  251. mpeChannels[ci].Yaxis.changed = false;
  252. outputs[Y_OUTPUT+ci].value = 0 ;
  253. }
  254. }
  255. mpeChannels[ci].note.changed = false;
  256. }
  257. if (mpeChannels[ci].note.gate) {
  258. if (mpeChannels[ci].pitchWheel.changed && mpeChannels[ci].note.gate ) {
  259. // std::cout << "mpeChannels[ci].pitch is " << mpeChannels[ci].note.pitch << std::endl;
  260. outputs[PITCH_OUTPUT+ci].value = (((mpeChannels[ci].note.pitch - 60)) / 12.0) + ((mpeChannels[ci].pitchWheel.val - 8192 ) / 8192.0 / 12.0 * (float)bendRange ) ;
  261. mpeChannels[ci].pitchWheel.changed = false;
  262. // std::cout << "Setting pitch on ci " << ci << " to " << outputs[PITCH_OUTPUT+ci].value << std::endl;
  263. }
  264. if (MPEPlus) { // We process MPE+ or not here
  265. if (mpeChannels[ci].MPEPluszAxis.changed) {
  266. // Combine two 7 bit into 14bit
  267. mpeChannels[ci].MPEPluszAxis.val = ( (uint16_t)mpeChannels[ci].MPEPluszAxis.MSB << 7) | ( (uint16_t)mpeChannels[ci].MPEPluszAxis.LSB ) ;
  268. outputs[PRESSURE_OUTPUT+ci].value = mpeChannels[ci].MPEPluszAxis.val / 16384.0 * 10.f;
  269. mpeChannels[ci].MPEPluszAxis.changed = false;
  270. //std::cout << "Setting pressure on ci " << ci << " to " << outputs[PRESSURE_OUTPUT+ci].value << std::endl;
  271. }
  272. if (mpeChannels[ci].MPEPlusyAxis.changed) {
  273. // Combine two 7 bit into 14bit
  274. mpeChannels[ci].MPEPlusyAxis.val = ( (uint16_t)mpeChannels[ci].MPEPlusyAxis.MSB << 7) | ( (uint16_t)mpeChannels[ci].MPEPlusyAxis.LSB ) ;
  275. outputs[Y_OUTPUT+ci].value = mpeChannels[ci].MPEPlusyAxis.val / 16384.0 * 10.f;
  276. // std::cout << "Y axis is " << outputs[Y_OUTPUT].value << std::endl;
  277. mpeChannels[ci].MPEPlusyAxis.changed = false;
  278. }
  279. } else {
  280. if (mpeChannels[ci].afterTouch.changed ) {
  281. outputs[PRESSURE_OUTPUT+ci].value = mpeChannels[ci].afterTouch.val / 127.f * 10.f;
  282. mpeChannels[ci].afterTouch.changed = false;
  283. // std::cout << "outputs[PRESSURE_OUTPUT+ci].value is " << outputs[PRESSURE_OUTPUT+ci].value << std::endl;
  284. }
  285. if (mpeChannels[ci].Yaxis.changed ) {
  286. outputs[Y_OUTPUT+ci].value = mpeChannels[ci].Yaxis.val / 127.f * 10.f;
  287. mpeChannels[ci].Yaxis.changed = false;
  288. // std::cout << "outputs[Y_OUTPUT+ci].value is " << outputs[Y_OUTPUT+ci].value << std::endl;
  289. }
  290. }
  291. }
  292. mpeChannels[ci].changed = false;
  293. }
  294. }
  295. // Pedal
  296. if (midiPedalOne.changed) {
  297. outputs[PEDAL_OUTPUT].value = midiPedalOne.val / 127.f * 10.f ;
  298. // std::cout << " We set the output outputs[PEDAL_OUTPUT].value to " << outputs[PEDAL_OUTPUT].value << std::endl;
  299. midiPedalOne.changed = false;
  300. }
  301. /*
  302. outputs[GATE_OUTPUT].value = gate ? 10.0 : 0.0;
  303. outputs[VELOCITY_OUTPUT].value = vel / 127.0 * 10.0;
  304. // Pressure
  305. if (MPEPlus) {
  306. if (MPEPluszAxis.changed) {
  307. // Combine two 7 bit into 14bit
  308. MPEPluszAxis.val = ( (uint16_t)MPEPluszAxis.MSB << 7) | ( (uint16_t)MPEPluszAxis.LSB ) ;
  309. outputs[PRESSURE_OUTPUT].value = MPEPluszAxis.val / 16384.0 * 10.f;
  310. MPEPluszAxis.changed = false;
  311. }
  312. if (MPEPlusyAxis.changed) {
  313. // Combine two 7 bit into 14bit
  314. MPEPlusyAxis.val = ( (uint16_t)MPEPlusyAxis.MSB << 7) | ( (uint16_t)MPEPlusyAxis.LSB ) ;
  315. outputs[Y_OUTPUT].value = MPEPlusyAxis.val / 16384.0 * 10.f;
  316. std::cout << "Y axis is " << outputs[Y_OUTPUT].value << std::endl;
  317. MPEPlusyAxis.changed = false;
  318. }
  319. } else { // Standard resolution MPE
  320. if (afterTouch.changed) {
  321. outputs[PRESSURE_OUTPUT].value = afterTouch.val / 127.0 * 10;
  322. afterTouch.changed = false;
  323. }
  324. if (Yaxis.changed) {
  325. outputs[Y_OUTPUT].value = Yaxis.val / 127.0 * 10;
  326. Yaxis.changed = false;
  327. }
  328. }
  329. // 1/V incorporates pitch wheel changes
  330. if (pitchWheel.changed | this->newNote) {
  331. outputs[PITCH_OUTPUT].value = (((note - 60)) / 12.0) + ((pitchWheel.val - 8192 ) / 8192.0 / 12.0 * (float)bendRange ) ;
  332. pitchWheel.changed = false;
  333. this->newNote = false;
  334. }
  335. */
  336. }
  337. void QuadMPEToCV::processMessage(MidiMessage msg) {
  338. int8_t channel = msg.channel(); // starts at 0
  339. int8_t status = msg.status(); //(msg[0] >> 4) & 0xf;
  340. int8_t data1 = msg.data1;
  341. int8_t data2 = msg.data2;
  342. if (status == 0xb && ( data1 == 111 || data1 == 118)) {
  343. return;
  344. }
  345. // fprintf(stderr, "channel %d status %d data1 %d data2 %d\n", channel, status, data1, data2);
  346. // Filter only the channels of our polyphony, it must be within our boundaries :)
  347. // std::cout << "MIDI channel and mpeChannels[0].MIDIChannel " << channel << " " << mpeChannels[0].MIDIChannel << std::endl;
  348. // std::cout << "polyphony is " << polyphony << std::endl;
  349. // std::cout << "mpeChannels[0].MIDIChannel is " << mpeChannels[0].MIDIChannel << " and mpeChannels[polyphony].MIDIChannel "
  350. // << mpeChannels[polyphony-1].MIDIChannel << std::endl;
  351. // for (int p=0; p < polyphony ; p++ ) {
  352. // std::cout << " mpeChannels[" << p << "].MIDIChannel: " << mpeChannels[p].MIDIChannel << std::endl;
  353. // }
  354. if ( channel >= mpeChannels[0].MIDIChannel && channel <= mpeChannels[polyphony-1].MIDIChannel ) { // Only process the channel we want
  355. // std::cout << "We process" << std::endl;
  356. // std::cout << "channel is " << channel << " baseMIDIChannel is " << baseMIDIChannel << " ci is " << ci << std::endl;
  357. // start ci channel
  358. // 1 0 0
  359. // 1 1 1
  360. // 2 0 1
  361. // 2 1 2
  362. // 3 0 2
  363. // 3 1 3
  364. int ci = channel - baseMIDIChannel + 1;
  365. switch (status) {
  366. // note off
  367. case 0x8: {
  368. // std::cout << "Note off" << std::endl;
  369. mpeChannels[ci].note.gate= false;
  370. mpeChannels[ci].note.vel = data2;
  371. mpeChannels[ci].note.gate = false;
  372. mpeChannels[ci].note.noteOff = true;
  373. mpeChannels[ci].note.changed = true;
  374. mpeChannels[ci].changed = true;
  375. }
  376. break;
  377. case 0x9: // note on
  378. // std::cout << "Note on" << std::endl;
  379. // fprintf(stderr, "channel %d status %d data1 %d data2 %d\n", channel, status, data1, data2);
  380. // std::cout << "ci is " << ci << std::endl;
  381. // for (int p=0 ; p < polyphony ; p++) {
  382. // std::cout << " p MIDIChannel " << p << " " << mpeChannels[p].MIDIChannel << std::endl;
  383. // // mpeChannels[p].MIDIChannel = p + baseMIDIChannel - 1; // MPE channels start at 2 onwards. We are using MIDI channel starting at 0
  384. // }
  385. if (data2 > 0) { // note ON
  386. mpeChannels[ci].note.gate= true;
  387. mpeChannels[ci].note.pitch = data1;
  388. mpeChannels[ci].note.vel = data2;
  389. mpeChannels[ci].note.changed = true;
  390. mpeChannels[ci].changed = true;
  391. } else { // note off
  392. // For some reason, some keyboards send a "note on" event with a velocity of 0 to signal that the key has been released.
  393. mpeChannels[ci].note.gate= false;
  394. mpeChannels[ci].note.vel = 0;
  395. mpeChannels[ci].note.noteOff = true;
  396. mpeChannels[ci].note.changed = true;
  397. mpeChannels[ci].changed = true;
  398. }
  399. break;
  400. case 0xe: // pitch wheel, we combine two 7 bit in two bytes into a 14bit msg
  401. {
  402. // We want 2 bytes but variable size may change with platform, maybe we should do a more robust way
  403. uint16_t twoBytes ; // Initialize our final pitchWheel variable.
  404. // we don't need to shift the first byte because it's 7 bit (always starts with 0)
  405. twoBytes = ( (uint16_t)msg.data2 << 7) | ( (uint16_t)msg.data1 ) ;
  406. // std::cout << "Pitch wheel " << twoBytes << " on channel and -1 : " << channel << " " << channel -1 << std::endl;
  407. mpeChannels[ci].pitchWheel.val = twoBytes;
  408. mpeChannels[ci].changed = true;
  409. mpeChannels[ci].pitchWheel.changed = true;
  410. }
  411. break;
  412. }
  413. if (MPEPlus) { // Processing MPE+ data
  414. // Note from the Haken Continuum Manual:
  415. // (To avoid the glitches, the synthesizer can do synchronous 14-bit updates with output from the Continuum:
  416. // simply save the least significant data, and do not apply it until the most significant data is received.)
  417. switch (data1) {
  418. case 74: // Y axis
  419. mpeChannels[ci].MPEPlusyAxis.MSB = data2;
  420. mpeChannels[ci].MPEPlusyAxis.changed = true;
  421. mpeChannels[ci].changed = true;
  422. break;
  423. case 106:
  424. mpeChannels[ci].MPEPlusyAxis.LSB = data2;
  425. mpeChannels[ci].MPEPlusyAxis.changed = true;
  426. mpeChannels[ci].changed = true;
  427. break;
  428. case 70: // Z or Pressure
  429. mpeChannels[ci].MPEPluszAxis.MSB = data2;
  430. mpeChannels[ci].MPEPluszAxis.changed = true;
  431. mpeChannels[ci].changed = true;
  432. break;
  433. case 102:
  434. mpeChannels[ci].MPEPluszAxis.LSB = data2;
  435. mpeChannels[ci].MPEPluszAxis.changed = true;
  436. mpeChannels[ci].changed = true;
  437. break;
  438. }
  439. } else { // Non MPE+ data
  440. if (status == 0xd) { // Channel Pressure
  441. // std::cout << " We parse channel aftertouch data that is " << data1 << std::endl;
  442. mpeChannels[ci].afterTouch.val = data1;
  443. mpeChannels[ci].afterTouch.changed = true;
  444. mpeChannels[ci].changed = true;
  445. }
  446. if (status == 0xb && data1 == 0x4a) { // CC (oxb) #74 <- we should probably make this assignable if neeed.
  447. // std::cout << " We parse CC 74 data that is " << data1 << std::endl;
  448. mpeChannels[ci].Yaxis.val = data2;
  449. mpeChannels[ci].Yaxis.changed = true;
  450. mpeChannels[ci].changed = true;
  451. }
  452. } // End MPE or MPE+ switch
  453. } // End note processing
  454. if (this->globalMIDIChannel == (channel + 1) ) { // If we're on global midi channel
  455. // std::cout <<"Global channel!" << std::endl;
  456. if (data1 == midiPedalOne.cc) {
  457. // std::cout <<"Pedal One value is " << data2 << std::endl;
  458. midiPedalOne.val = data2;
  459. midiPedalOne.changed = true;
  460. }
  461. }
  462. /*
  463. switch (status) {
  464. case 0xb: // cc
  465. if (MPEPlus) { // Processing MPE+ data
  466. // Note from the Haken Continuum Manual:
  467. // (To avoid the glitches, the synthesizer can do synchronous 14-bit updates with output from the Continuum:
  468. // simply save the least significant data, and do not apply it until the most significant data is received.)
  469. switch (data1) {
  470. case 74: // Y axis
  471. MPEPlusyAxis.MSB = data2;
  472. MPEPlusyAxis.changed = true;
  473. break;
  474. case 106:
  475. MPEPlusyAxis.LSB = data2;
  476. MPEPlusyAxis.changed = true;
  477. break;
  478. case 70: // Z or Pressure
  479. MPEPluszAxis.MSB = data2;
  480. MPEPluszAxis.changed = true;
  481. break;
  482. case 102:
  483. MPEPluszAxis.LSB = data2;
  484. MPEPluszAxis.changed = true;
  485. break;
  486. }
  487. } else { // Non MPE+ data
  488. switch (data1) {
  489. case 0x01: // mod
  490. mod.val = data2;
  491. mod.changed = true;
  492. // std::cout << "mod" << std::endl;
  493. break;
  494. case 0x4a: // CC 74 <- we should probably make this assignable if neeed.
  495. Yaxis.val = data2;
  496. Yaxis.changed = true;
  497. break ;
  498. }
  499. } // End MPE or MPE+ switch
  500. if (data1== 0x40 ) {
  501. pedal = (data2 >= 64);
  502. if (!pedal) {
  503. releaseNote(-1);
  504. }
  505. } // sustain
  506. break;
  507. case 0xe: // pitch wheel, we combine two 7 bit in two bytes into a 14bit msg
  508. {
  509. int nBytes;
  510. // double stamp;
  511. nBytes = msg.size();
  512. // for ( i=0; i<nBytes; i++ )
  513. // std::cout << "Byte " << i << " = " << (int)msg[i] << ", ";
  514. // if ( nBytes > 0 )
  515. // std::cout << "stamp = " << stamp << std::endl;
  516. // We want 2 bytes but variable size may change with platform, maybe we should do a more robust way
  517. uint16_t twoBytes ; // Initialize our final pitchWheel variable.
  518. // we don't need to shift the first byte because it's 7 bit (always starts with 0)
  519. twoBytes = ( (uint16_t)msg[2] << 7) | ( (uint16_t)msg[1] ) ;
  520. // std::cout << sizeof(int) << std::endl;
  521. // std::bitset<8> msgOne(msg[1]);
  522. // std::bitset<8> msgTwo(msg[2]);
  523. // std::bitset<16> x(twoBytes);
  524. //std::cout << "msg[1] and 2 are " << msgOne << " " << msgTwo << " and shifted is " << x << std::endl;
  525. //std::cout << "twoBytes is " << (int)twoBytes << std::endl;
  526. pitchWheel.val = twoBytes;
  527. pitchWheel.changed = true;
  528. }
  529. break;
  530. case 0xd: // channel aftertouch
  531. afterTouch.val = data1;
  532. afterTouch.changed = true;
  533. break;
  534. }
  535. }
  536. // std::cout <<" midi input is on " << Global channel!"
  537. if (this->globalMIDIChannel == (channel + 1) ) {
  538. std::cout <<"Global channel!" << std::endl;
  539. if (data1 == midiPedalOne.cc) {
  540. std::cout <<"Pedal One value is " << data2 << std::endl;
  541. midiPedalOne.val = data2;
  542. midiPedalOne.changed = true;
  543. }
  544. }
  545. */
  546. }
  547. // MPEMidiWidget stuff
  548. struct QuadBendRangeItem : MenuItem {
  549. QuadMPEToCV *quadmpetocv;
  550. int bendRange ;
  551. void onAction(EventAction &e) override {
  552. // debug("We trigger action with %d", bendRange);
  553. quadmpetocv->bendRange = bendRange;
  554. }
  555. };
  556. struct QuadBendRangeChoice : LedDisplayChoice {
  557. // QuadMPEToCVWidget *quadmpetocvwidget;
  558. QuadMPEToCV *quadmpetocv;
  559. int bendRange ;
  560. void onAction(EventAction &e) override {
  561. #ifdef USE_VST2
  562. Menu *menu = rack::global_ui->ui.gScene->createMenu();
  563. #else
  564. Menu *menu = gScene->createMenu();
  565. #endif // USE_VST2
  566. menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Bend Range"));
  567. std::vector<int> bendRanges = {1,2,3,4,12,24,48,96}; // The bend range we use
  568. for (auto const& bendRangeValue: bendRanges) {
  569. QuadBendRangeItem *item = new QuadBendRangeItem();
  570. item->quadmpetocv = quadmpetocv;
  571. item->text = std::to_string(bendRangeValue);
  572. item->bendRange = bendRangeValue;
  573. menu->addChild(item);
  574. }
  575. // quadmpetocv->bendRange = bendRange;
  576. }
  577. void step() override {
  578. color = nvgRGB(0xff, 0x00, 0x00);
  579. color.a = 0.8f;
  580. text = stringf("%d", quadmpetocv->bendRange);
  581. }
  582. };
  583. struct QuadMidiChannelItem : MenuItem {
  584. QuadMPEToCV *quadmpetocv;
  585. int channel ;
  586. void onAction(EventAction &e) override {
  587. quadmpetocv->baseMIDIChannel = channel;
  588. }
  589. };
  590. struct QuadMidiChannelChoice : LedDisplayChoice {
  591. // QuadMPEToCVWidget *quadmpetocvwidget;
  592. QuadMPEToCV *quadmpetocv;
  593. int channel ;
  594. void onAction(EventAction &e) override {
  595. #ifdef USE_VST2
  596. Menu *menu = rack::global_ui->ui.gScene->createMenu();
  597. #else
  598. Menu *menu = gScene->createMenu();
  599. #endif // USE_VST2
  600. menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Midi channel"));
  601. std::vector<int> bendRanges = {1,2,3,4,12,24,48,96}; // The bend range we use
  602. for (int c=1; c <= 16 ; c++) {
  603. QuadMidiChannelItem *item = new QuadMidiChannelItem();
  604. item->quadmpetocv = quadmpetocv;
  605. item->text = std::to_string(c);
  606. item->channel = c;
  607. menu->addChild(item);
  608. }
  609. }
  610. void step() override {
  611. color = nvgRGB(0xff, 0x00, 0x00);
  612. color.a = 0.8f;
  613. text = std::to_string(quadmpetocv->baseMIDIChannel);
  614. }
  615. };
  616. struct QuadGlobalMidiChannelItem : MenuItem {
  617. QuadMPEToCV *quadmpetocv;
  618. int channel ;
  619. void onAction(EventAction &e) override {
  620. quadmpetocv->globalMIDIChannel = channel;
  621. }
  622. };
  623. struct QuadGlobalMidiChannelChoice : LedDisplayChoice {
  624. // QuadMPEToCVWidget *quadmpetocvwidget;
  625. QuadMPEToCV *quadmpetocv;
  626. int channel ;
  627. void onAction(EventAction &e) override {
  628. #ifdef USE_VST2
  629. Menu *menu = rack::global_ui->ui.gScene->createMenu();
  630. #else
  631. Menu *menu = gScene->createMenu();
  632. #endif // USE_VST2
  633. menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Global Midi channel"));
  634. for (int c=1; c <= 16 ; c++) {
  635. QuadGlobalMidiChannelItem *item = new QuadGlobalMidiChannelItem();
  636. item->quadmpetocv = quadmpetocv;
  637. item->text = std::to_string(c);
  638. item->channel = c;
  639. menu->addChild(item);
  640. }
  641. }
  642. void step() override {
  643. color = nvgRGB(0xff, 0x00, 0x00);
  644. color.a = 0.8f;
  645. text = std::to_string(quadmpetocv->globalMIDIChannel);
  646. }
  647. };
  648. struct QuadMPEModeItem : MenuItem {
  649. QuadMPEToCV *quadmpetocv;
  650. bool MPEPlus ;
  651. void onAction(EventAction &e) override {
  652. quadmpetocv->MPEPlus = MPEPlus;
  653. }
  654. };
  655. struct QuadMPEModeChoice : LedDisplayChoice {
  656. // QuadMPEToCVWidget *quadmpetocvwidget;
  657. QuadMPEToCV *quadmpetocv;
  658. bool MPEPlus ;
  659. void onAction(EventAction &e) override {
  660. #ifdef USE_VST2
  661. Menu *menu = rack::global_ui->ui.gScene->createMenu();
  662. #else
  663. Menu *menu = gScene->createMenu();
  664. #endif // USE_VST2
  665. menu->addChild(construct<MenuLabel>(&MenuLabel::text, "MPE mode"));
  666. // MPE
  667. QuadMPEModeItem *MPE = new QuadMPEModeItem();
  668. MPE->quadmpetocv = quadmpetocv;
  669. MPE->text = "MPE - Standard (ROLI, etc)";
  670. MPE->MPEPlus = false;
  671. menu->addChild(MPE);
  672. // MPE Plus
  673. QuadMPEModeItem *MPEPlus = new QuadMPEModeItem();
  674. MPEPlus->quadmpetocv = quadmpetocv;
  675. MPEPlus->text = "MPE+ - High Res for Haken Continuum";
  676. MPEPlus->MPEPlus = true;
  677. menu->addChild(MPEPlus);
  678. }
  679. void step() override {
  680. // color = nvgRGB(0xff, 0x00, 0x00);
  681. // color.a = 0.8f;
  682. if (quadmpetocv->MPEPlus) {
  683. text = "MPE+";
  684. } else {
  685. text = "MPE";
  686. }
  687. }
  688. };
  689. // We extend the midi to follow similar design
  690. struct QuadMPEMidiWidget : MPEBaseWidget {
  691. LedDisplaySeparator *hSeparators[2];
  692. LedDisplaySeparator *vSeparators[3];
  693. // LedDisplayChoice *ccChoices[4][4];
  694. QuadMPEToCV *quadmpetocv ;
  695. QuadBendRangeChoice *bendRangeChoice ;
  696. QuadMidiChannelChoice *midiChannelChoice ;
  697. QuadGlobalMidiChannelChoice *globalMidiChannelChoice ;
  698. QuadMPEModeChoice *mpeModeChoice ;
  699. QuadMPEMidiWidget() {
  700. }
  701. void initialize(QuadMPEToCV *quadmpetocv) {
  702. this->quadmpetocv = quadmpetocv;
  703. Vec pos = deviceChoice->box.getBottomLeft();
  704. for (int y = 0; y < 2; y++) {
  705. hSeparators[y] = Widget::create<LedDisplaySeparator>(pos);
  706. addChild(hSeparators[y]);
  707. }
  708. midiChannelChoice = Widget::create<QuadMidiChannelChoice>(pos);
  709. midiChannelChoice->quadmpetocv = quadmpetocv ;
  710. addChild(midiChannelChoice);
  711. globalMidiChannelChoice = Widget::create<QuadGlobalMidiChannelChoice>(pos);
  712. globalMidiChannelChoice->quadmpetocv = quadmpetocv ;
  713. addChild(globalMidiChannelChoice);
  714. bendRangeChoice = Widget::create<QuadBendRangeChoice>(pos);
  715. bendRangeChoice->quadmpetocv = quadmpetocv ;
  716. addChild(bendRangeChoice);
  717. mpeModeChoice = Widget::create<QuadMPEModeChoice>(pos);
  718. mpeModeChoice->quadmpetocv = quadmpetocv ;
  719. addChild(mpeModeChoice);
  720. for (int x = 1; x < 3; x++) {
  721. vSeparators[x] = Widget::create<LedDisplaySeparator>(pos);
  722. addChild(vSeparators[x]);
  723. }
  724. for (int x = 1; x < 3; x++) {
  725. vSeparators[x]->box.size.y = midiChannelChoice->box.size.y;
  726. }
  727. }
  728. void step() override {
  729. MPEBaseWidget::step();
  730. midiChannelChoice->box.size.x = box.size.x/4;
  731. midiChannelChoice->box.pos.x = 0;
  732. globalMidiChannelChoice->box.size.x = box.size.x/4;
  733. globalMidiChannelChoice->box.pos.x = box.size.x/4;
  734. bendRangeChoice->box.size.x = box.size.x/4;
  735. bendRangeChoice->box.pos.x = box.size.x/4 * 2 ;
  736. mpeModeChoice->box.size.x = box.size.x/4;
  737. mpeModeChoice->box.pos.x = box.size.x/4 * 3 - 5 ;
  738. for (int y = 0; y < 2; y++) {
  739. hSeparators[y]->box.size.x = box.size.x;
  740. }
  741. for (int x = 1; x < 3; x++) {
  742. vSeparators[x]->box.pos.x = box.size.x / 4 * x;
  743. }
  744. }
  745. };