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.

317 lines
9.1KB

  1. #include "Erratic.hpp"
  2. #include "MPEToCV.hpp"
  3. MPEToCV::MPEToCV() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {
  4. pitchWheel.val = 64;
  5. // pitchWheel.tSmooth.set(0, 0);
  6. midiPedalOne.cc = 12; // By default we use 12 (barrel i on the Haken Continuum)
  7. }
  8. void MPEToCV::step() {
  9. MidiMessage msg;
  10. while (midiInput.shift(&msg)) {
  11. Ddebug("Processmsg");
  12. processMessage(msg);
  13. }
  14. // if (resetTrigger.process(params[RESET_PARAM].value)) {
  15. // resetMidi();
  16. // return;
  17. // }
  18. // lights[RESET_LIGHT].value -= lights[RESET_LIGHT].value / 0.55 / engineGetSampleRate(); // fade out light
  19. outputs[GATE_OUTPUT].value = gate ? 10.0 : 0.0;
  20. outputs[VELOCITY_OUTPUT].value = vel / 127.0 * 10.0;
  21. /* NOTE: I'll leave out value smoothing for after touch for now. I currently don't
  22. * have an after touch capable device around and I assume it would require different
  23. * smoothing*/
  24. // Pressure
  25. if (MPEPlus) {
  26. if (MPEPluszAxis.changed) {
  27. // Combine two 7 bit into 14bit
  28. MPEPluszAxis.val = ( (uint16_t)MPEPluszAxis.MSB << 7) | ( (uint16_t)MPEPluszAxis.LSB ) ;
  29. outputs[PRESSURE_OUTPUT].value = MPEPluszAxis.val / 16384.0 * 10.f;
  30. MPEPluszAxis.changed = false;
  31. }
  32. if (MPEPlusyAxis.changed) {
  33. // Combine two 7 bit into 14bit
  34. MPEPlusyAxis.val = ( (uint16_t)MPEPlusyAxis.MSB << 7) | ( (uint16_t)MPEPlusyAxis.LSB ) ;
  35. outputs[Y_OUTPUT].value = MPEPlusyAxis.val / 16384.0 * 10.f;
  36. //std::cout << "Y axis is " << outputs[Y_OUTPUT].value << std::endl;
  37. MPEPlusyAxis.changed = false;
  38. }
  39. } else { // Standard resolution MPE
  40. if (afterTouch.changed) {
  41. outputs[PRESSURE_OUTPUT].value = afterTouch.val / 127.0 * 10;
  42. afterTouch.changed = false;
  43. }
  44. if (Yaxis.changed) {
  45. outputs[Y_OUTPUT].value = Yaxis.val / 127.0 * 10;
  46. Yaxis.changed = false;
  47. }
  48. }
  49. // Pedal
  50. if (midiPedalOne.changed) {
  51. outputs[PEDAL_OUTPUT].value = midiPedalOne.val / 127.f * 10.f ;
  52. midiPedalOne.changed = false;
  53. }
  54. // 1/V incorporates pitch wheel changes
  55. if (pitchWheel.changed && gate) {
  56. outputs[PITCH_OUTPUT].value = (((note - 60)) / 12.0) + ((pitchWheel.val - 8192 ) / 8192.0 / 12.0 * (float)bendRange ) ;
  57. Ddebug("%d",(pitchWheel.val - 8192 ));
  58. Ddebug("note=%d, pitchWheel.val=%d, bendRange=%d",note, pitchWheel.val, bendRange );
  59. Ddebug("outputs[PITCH_OUTPUT].value is %f",outputs[PITCH_OUTPUT].value);
  60. pitchWheel.changed = false;
  61. this->newNote = false;
  62. }
  63. if (resetNoteNow) {
  64. this->note = 60;
  65. this->pitchWheel.val = 8192;
  66. outputs[PITCH_OUTPUT].value = 0 ;
  67. resetNoteNow = false;
  68. }
  69. }
  70. // Currently only support one note
  71. void MPEToCV::pressNote(int note) {
  72. // Remove existing similar note
  73. // auto it = std::find(notes.begin(), notes.end(), note);
  74. // if (it != notes.end())
  75. // notes.erase(it);
  76. // // Push note
  77. // notes.push_back(note);
  78. this->note = note;
  79. gate = true;
  80. this->newNote = true;
  81. }
  82. void MPEToCV::releaseNote(int note) {
  83. gate = false;
  84. if (noteOffReset) {
  85. Ddebug("We execute the note off reset");
  86. resetNoteNow = true;
  87. afterTouch.val = 0;
  88. afterTouch.changed = true;
  89. Yaxis.val = 0;
  90. Yaxis.changed = true;
  91. }
  92. // // Remove the note
  93. // auto it = std::find(notes.begin(), notes.end(), note);
  94. // if (it != notes.end())
  95. // notes.erase(it);
  96. // if (pedal) {
  97. // // Don't release if pedal is held
  98. // gate = true;
  99. // } else if (!notes.empty()) {
  100. // // Play previous note
  101. // auto it2 = notes.end();
  102. // it2--;
  103. // this->note = *it2;
  104. // gate = true;
  105. // } else {
  106. // gate = false;
  107. // }
  108. }
  109. void MPEToCV::processMessage(MidiMessage msg) {
  110. //Ddebug("MIDI: %01x %01x %02x %02x", msg.status(), msg.channel(), msg.data1, msg.data2);
  111. int8_t channel = msg.channel();
  112. int8_t status = msg.status();
  113. int8_t data1 = msg.data1;
  114. int8_t data2 = msg.data2;
  115. // fprintf(stderr, "channel %d status %d data1 %d data2 %d\n", channel, status, data1, data2);
  116. // Filter channels
  117. if (this->channel == (channel + 1) ) { // Only process the channel we want
  118. switch (status) {
  119. // note off
  120. case 0x8: {
  121. releaseNote(data1);
  122. }
  123. break;
  124. case 0x9: // note on
  125. if (data2 > 0) {
  126. pressNote(data1);
  127. this->vel = data2;
  128. } else {
  129. // For some reason, some keyboards send a "note on" event with a velocity of 0 to signal that the key has been released.
  130. releaseNote(data1);
  131. }
  132. break;
  133. case 0xb: // cc
  134. if (MPEPlus) { // Processing MPE+ data
  135. // Note from the Haken Continuum Manual:
  136. // (To avoid the glitches, the synthesizer can do synchronous 14-bit updates with output from the Continuum:
  137. // simply save the least significant data, and do not apply it until the most significant data is received.)
  138. switch (data1) {
  139. case 74: // Y axis
  140. MPEPlusyAxis.MSB = data2;
  141. MPEPlusyAxis.changed = true;
  142. break;
  143. case 106:
  144. MPEPlusyAxis.LSB = data2;
  145. MPEPlusyAxis.changed = true;
  146. break;
  147. case 70: // Z or Pressure
  148. MPEPluszAxis.MSB = data2;
  149. MPEPluszAxis.changed = true;
  150. break;
  151. case 102:
  152. MPEPluszAxis.LSB = data2;
  153. MPEPluszAxis.changed = true;
  154. break;
  155. }
  156. } else { // Non MPE+ data
  157. switch (data1) {
  158. case 0x01: // mod
  159. mod.val = data2;
  160. mod.changed = true;
  161. // std::cout << "mod" << std::endl;
  162. break;
  163. case 0x4a: // CC 74 <- we should probably make this assignable if neeed.
  164. Yaxis.val = data2;
  165. Yaxis.changed = true;
  166. break ;
  167. }
  168. } // End MPE or MPE+ switch
  169. if (data1== 0x40 ) {
  170. pedal = (data2 >= 64);
  171. if (!pedal) {
  172. releaseNote(-1);
  173. }
  174. } // sustain
  175. break;
  176. case 0xe: // pitch wheel, we combine two 7 bit in two bytes into a 14bit msg
  177. {
  178. // We want 2 bytes but variable size may change with platform, maybe we should do a more robust way
  179. uint16_t twoBytes ; // Initialize our final pitchWheel variable.
  180. // we don't need to shift the first byte because it's 7 bit (always starts with 0)
  181. twoBytes = ( (uint16_t)msg.data2 << 7) | ( (uint16_t)msg.data1 ) ;
  182. pitchWheel.val = twoBytes;
  183. Ddebug("pitchWheel.val=%d",pitchWheel.val);
  184. pitchWheel.changed = true;
  185. }
  186. break;
  187. case 0xd: // channel aftertouch
  188. afterTouch.val = data1;
  189. afterTouch.changed = true;
  190. break;
  191. }
  192. }
  193. // std::cout <<" midi input is on " << Global channel!"
  194. if (this->globalChannel == (channel + 1) ) {
  195. //std::cout <<"Global channel!" << std::endl;
  196. if (data1 == midiPedalOne.cc) {
  197. //std::cout <<"Pedal One value is " << data2 << std::endl;
  198. midiPedalOne.val = data2;
  199. midiPedalOne.changed = true;
  200. }
  201. }
  202. }
  203. struct MPEToCVWidget : ModuleWidget {
  204. MPEToCVWidget(MPEToCV *module);
  205. // Reset Notes or not
  206. void appendContextMenu(Menu *menu) override {
  207. MPEToCV *module = dynamic_cast<MPEToCV*>(this->module);
  208. struct ResetNoteItem : MenuItem {
  209. MPEToCV *module;
  210. bool resetNoteBool;
  211. void onAction(EventAction &e) override {
  212. //Ddebug("Set resetNoteBool to %d",!resetNoteBool);
  213. module->noteOffReset = ! resetNoteBool;
  214. }
  215. };
  216. ResetNoteItem *item = MenuItem::create<ResetNoteItem>("Reset Note", CHECKMARK(module->noteOffReset == true));
  217. item->module = module;
  218. item->resetNoteBool = module->noteOffReset;
  219. menu->addChild(item);
  220. }
  221. };
  222. MPEToCVWidget::MPEToCVWidget(MPEToCV *module) : ModuleWidget(module) {
  223. // MPEToCV *module = new MPEToCV();
  224. // setModule(module);
  225. box.size = Vec(15 * 10, 380);
  226. Vec pos = Vec();
  227. MPEMidiWidget *mpemidiWidget ;
  228. // bendRangeChoice = LedDisplayChoice;
  229. // {
  230. Panel *panel = new LightPanel();
  231. panel->box.size = box.size;
  232. addChild(panel);
  233. // }
  234. float margin = 5;
  235. float labelHeight = 15;
  236. float yPos = margin;
  237. float yGap = 25;
  238. // float xPos = 0; // Not sure how to initialize it.
  239. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  240. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  241. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  242. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  243. {
  244. Label *label = new Label();
  245. label->box.pos = Vec(box.size.x - margin - 7 * 15, margin);
  246. label->text = "MPE to CV";
  247. label->color = nvgRGB(0x00, 0x00, 0x00);
  248. addChild(label);
  249. yPos = labelHeight * 2;
  250. }
  251. mpemidiWidget = Widget::create<MPEMidiWidget>(mm2px(Vec(3.41891, 14.8373)));
  252. mpemidiWidget->initialize(module);
  253. mpemidiWidget->box.size = mm2px(Vec(44, 28));
  254. // box.size = mm2px(Vec(44, 28));
  255. mpemidiWidget->midiIO = &module->midiInput;
  256. addChild(mpemidiWidget);
  257. pos = mpemidiWidget->box.getBottomLeft();
  258. std::string labels[MPEToCV::NUM_OUTPUTS] = {"1V/oct", "Gate", "Velocity", "Pressure", "Y axis","Pedal"
  259. };
  260. yPos = mpemidiWidget->box.pos.y + mpemidiWidget->box.size.y + 5*margin ;
  261. for (int i = 0; i < MPEToCV::NUM_OUTPUTS; i++) {
  262. Label *label = new Label();
  263. label->box.pos = Vec(margin, yPos);
  264. label->text = labels[i];
  265. label->color = nvgRGB(0x00, 0x00, 0x00);
  266. addChild(label);
  267. addOutput(Port::create<PJ3410Port>(Vec(15 * 6, yPos - 5), Port::OUTPUT, module, i));
  268. yPos += yGap + 2*margin;
  269. }
  270. };
  271. RACK_PLUGIN_MODEL_INIT(ErraticInstruments, MPEToCV) {
  272. Model *modelMPEToCV = Model::create<MPEToCV, MPEToCVWidget>("Erratic Instruments", "MPEToCV", "MPE to CV", MIDI_TAG, EXTERNAL_TAG);
  273. return modelMPEToCV;
  274. }