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.

278 lines
7.8KB

  1. #include "Core.hpp"
  2. #include "midi.hpp"
  3. #include <algorithm>
  4. struct QuadMIDIToCVInterface : Module {
  5. enum ParamIds {
  6. NUM_PARAMS
  7. };
  8. enum InputIds {
  9. NUM_INPUTS
  10. };
  11. enum OutputIds {
  12. ENUMS(CV_OUTPUT, 4),
  13. ENUMS(GATE_OUTPUT, 4),
  14. ENUMS(VELOCITY_OUTPUT, 4),
  15. ENUMS(AFTERTOUCH_OUTPUT, 4),
  16. NUM_OUTPUTS
  17. };
  18. enum LightIds {
  19. NUM_LIGHTS
  20. };
  21. MidiInputQueue midiInput;
  22. enum PolyMode {
  23. ROTATE_MODE,
  24. RESET_MODE,
  25. REASSIGN_MODE,
  26. UNISON_MODE,
  27. NUM_MODES
  28. };
  29. PolyMode polyMode = ROTATE_MODE;
  30. struct NoteData {
  31. uint8_t velocity = 0;
  32. uint8_t aftertouch = 0;
  33. };
  34. NoteData noteData[128];
  35. std::vector<uint8_t> heldNotes;
  36. uint8_t notes[4];
  37. bool gates[4];
  38. bool pedal;
  39. int rotateIndex;
  40. QuadMIDIToCVInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS), heldNotes(128) {
  41. onReset();
  42. }
  43. json_t *toJson() override {
  44. json_t *rootJ = json_object();
  45. json_object_set_new(rootJ, "midi", midiInput.toJson());
  46. json_object_set_new(rootJ, "polyMode", json_integer(polyMode));
  47. return rootJ;
  48. }
  49. void fromJson(json_t *rootJ) override {
  50. json_t *midiJ = json_object_get(rootJ, "midi");
  51. if (midiJ)
  52. midiInput.fromJson(midiJ);
  53. json_t *polyModeJ = json_object_get(rootJ, "polyMode");
  54. if (polyModeJ)
  55. polyMode = (PolyMode) json_integer_value(polyModeJ);
  56. }
  57. void onReset() override {
  58. for (int i = 0; i < 4; i++) {
  59. notes[i] = 60;
  60. gates[i] = false;
  61. }
  62. pedal = false;
  63. rotateIndex = 0;
  64. }
  65. void pressNote(uint8_t note) {
  66. // Remove existing similar note
  67. auto it = std::find(heldNotes.begin(), heldNotes.end(), note);
  68. if (it != heldNotes.end())
  69. heldNotes.erase(it);
  70. // Push note
  71. heldNotes.push_back(note);
  72. // Set notes and gates
  73. switch (polyMode) {
  74. case ROTATE_MODE: {
  75. } break;
  76. case RESET_MODE: {
  77. } break;
  78. case REASSIGN_MODE: {
  79. } break;
  80. case UNISON_MODE: {
  81. for (int i = 0; i < 4; i++) {
  82. notes[i] = note;
  83. gates[i] = true;
  84. }
  85. } break;
  86. default: break;
  87. }
  88. }
  89. void releaseNote(uint8_t note) {
  90. // Remove the note
  91. auto it = std::find(heldNotes.begin(), heldNotes.end(), note);
  92. if (it != heldNotes.end())
  93. heldNotes.erase(it);
  94. // Hold note if pedal is pressed
  95. if (pedal)
  96. return;
  97. // Set last note
  98. switch (polyMode) {
  99. case ROTATE_MODE: {
  100. } break;
  101. case RESET_MODE: {
  102. } break;
  103. case REASSIGN_MODE: {
  104. } break;
  105. case UNISON_MODE: {
  106. if (!heldNotes.empty()) {
  107. auto it2 = heldNotes.end();
  108. it2--;
  109. for (int i = 0; i < 4; i++) {
  110. notes[i] = *it2;
  111. gates[i] = true;
  112. }
  113. }
  114. else {
  115. for (int i = 0; i < 4; i++) {
  116. gates[i] = false;
  117. }
  118. }
  119. } break;
  120. default: break;
  121. }
  122. }
  123. void pressPedal() {
  124. pedal = true;
  125. }
  126. void releasePedal() {
  127. pedal = false;
  128. releaseNote(255);
  129. }
  130. void step() override {
  131. MidiMessage msg;
  132. while (midiInput.shift(&msg)) {
  133. processMessage(msg);
  134. }
  135. for (int i = 0; i < 4; i++) {
  136. uint8_t lastNote = notes[i];
  137. outputs[CV_OUTPUT + i].value = (lastNote - 60) / 12.f;
  138. outputs[GATE_OUTPUT + i].value = gates[i] ? 10.f : 0.f;
  139. outputs[VELOCITY_OUTPUT + i].value = rescale(noteData[lastNote].velocity, 0, 127, 0.f, 10.f);
  140. outputs[VELOCITY_OUTPUT + i].value = rescale(noteData[lastNote].aftertouch, 0, 127, 0.f, 10.f);
  141. }
  142. }
  143. void processMessage(MidiMessage msg) {
  144. switch (msg.status()) {
  145. // note off
  146. case 0x8: {
  147. releaseNote(msg.note());
  148. } break;
  149. // note on
  150. case 0x9: {
  151. if (msg.value() > 0) {
  152. noteData[msg.note()].velocity = msg.value();
  153. pressNote(msg.note());
  154. }
  155. else {
  156. releaseNote(msg.note());
  157. }
  158. } break;
  159. // channel aftertouch
  160. case 0xa: {
  161. noteData[msg.note()].aftertouch = msg.value();
  162. } break;
  163. // cc
  164. case 0xb: {
  165. processCC(msg);
  166. } break;
  167. default: break;
  168. }
  169. }
  170. void processCC(MidiMessage msg) {
  171. switch (msg.note()) {
  172. // sustain
  173. case 0x40: {
  174. if (msg.value() >= 64)
  175. pressPedal();
  176. else
  177. releasePedal();
  178. } break;
  179. default: break;
  180. }
  181. }
  182. };
  183. struct QuadMIDIToCVInterfaceWidget : ModuleWidget {
  184. QuadMIDIToCVInterfaceWidget(QuadMIDIToCVInterface *module) : ModuleWidget(module) {
  185. setPanel(SVG::load(assetGlobal("res/Core/QuadMIDIToCVInterface.svg")));
  186. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  187. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  188. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  189. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  190. addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.894335, 60.144478)), Port::OUTPUT, module, QuadMIDIToCVInterface::CV_OUTPUT + 0));
  191. addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.494659, 60.144478)), Port::OUTPUT, module, QuadMIDIToCVInterface::GATE_OUTPUT + 0));
  192. addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.094986, 60.144478)), Port::OUTPUT, module, QuadMIDIToCVInterface::VELOCITY_OUTPUT + 0));
  193. addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693935, 60.144478)), Port::OUTPUT, module, QuadMIDIToCVInterface::AFTERTOUCH_OUTPUT + 0));
  194. addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.894335, 76.144882)), Port::OUTPUT, module, QuadMIDIToCVInterface::CV_OUTPUT + 1));
  195. addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.494659, 76.144882)), Port::OUTPUT, module, QuadMIDIToCVInterface::GATE_OUTPUT + 1));
  196. addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.094986, 76.144882)), Port::OUTPUT, module, QuadMIDIToCVInterface::VELOCITY_OUTPUT + 1));
  197. addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693935, 76.144882)), Port::OUTPUT, module, QuadMIDIToCVInterface::AFTERTOUCH_OUTPUT + 1));
  198. addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.894335, 92.143906)), Port::OUTPUT, module, QuadMIDIToCVInterface::CV_OUTPUT + 2));
  199. addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.494659, 92.143906)), Port::OUTPUT, module, QuadMIDIToCVInterface::GATE_OUTPUT + 2));
  200. addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.094986, 92.143906)), Port::OUTPUT, module, QuadMIDIToCVInterface::VELOCITY_OUTPUT + 2));
  201. addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693935, 92.143906)), Port::OUTPUT, module, QuadMIDIToCVInterface::AFTERTOUCH_OUTPUT + 2));
  202. addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.894335, 108.1443)), Port::OUTPUT, module, QuadMIDIToCVInterface::CV_OUTPUT + 3));
  203. addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.494659, 108.1443)), Port::OUTPUT, module, QuadMIDIToCVInterface::GATE_OUTPUT + 3));
  204. addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.094986, 108.1443)), Port::OUTPUT, module, QuadMIDIToCVInterface::VELOCITY_OUTPUT + 3));
  205. addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693935, 108.1443)), Port::OUTPUT, module, QuadMIDIToCVInterface::AFTERTOUCH_OUTPUT + 3));
  206. MidiWidget *midiWidget = Widget::create<MidiWidget>(mm2px(Vec(3.4009969, 14.837336)));
  207. midiWidget->box.size = mm2px(Vec(44, 28));
  208. midiWidget->midiIO = &module->midiInput;
  209. addChild(midiWidget);
  210. }
  211. void appendContextMenu(Menu *menu) override {
  212. QuadMIDIToCVInterface *module = dynamic_cast<QuadMIDIToCVInterface*>(this->module);
  213. struct PolyphonyItem : MenuItem {
  214. QuadMIDIToCVInterface *module;
  215. QuadMIDIToCVInterface::PolyMode polyMode;
  216. void onAction(EventAction &e) override {
  217. module->polyMode = polyMode;
  218. }
  219. };
  220. menu->addChild(MenuEntry::create());
  221. menu->addChild(MenuLabel::create("Polyphony mode"));
  222. std::vector<std::string> polyModeNames = {"Rotate", "Reset", "Reassign", "Unison"};
  223. for (int i = 0; i < QuadMIDIToCVInterface::NUM_MODES; i++) {
  224. PolyphonyItem *item = MenuItem::create<PolyphonyItem>(polyModeNames[i], CHECKMARK(module->polyMode == i));
  225. item->module = module;
  226. item->polyMode = (QuadMIDIToCVInterface::PolyMode) i;
  227. menu->addChild(item);
  228. }
  229. }
  230. };
  231. Model *modelQuadMIDIToCVInterface = Model::create<QuadMIDIToCVInterface, QuadMIDIToCVInterfaceWidget>("Core", "QuadMIDIToCVInterface", "MIDI-4", MIDI_TAG, EXTERNAL_TAG, QUAD_TAG);