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.

385 lines
11KB

  1. #include "global_pre.hpp"
  2. #include "Core.hpp"
  3. #include "midi.hpp"
  4. #include "dsp/digital.hpp"
  5. #include "global.hpp"
  6. #include <algorithm>
  7. #ifdef USE_VST2
  8. #include "vstmidi.hpp"
  9. #endif // USE_VST2
  10. struct QuadMIDIToCVInterface : Module {
  11. enum ParamIds {
  12. NUM_PARAMS
  13. };
  14. enum InputIds {
  15. NUM_INPUTS
  16. };
  17. enum OutputIds {
  18. ENUMS(CV_OUTPUT, 4),
  19. ENUMS(GATE_OUTPUT, 4),
  20. ENUMS(VELOCITY_OUTPUT, 4),
  21. ENUMS(AFTERTOUCH_OUTPUT, 4),
  22. NUM_OUTPUTS
  23. };
  24. enum LightIds {
  25. NUM_LIGHTS
  26. };
  27. MidiInputQueue midiInput;
  28. enum PolyMode {
  29. ROTATE_MODE,
  30. REUSE_MODE,
  31. RESET_MODE,
  32. REASSIGN_MODE,
  33. UNISON_MODE,
  34. NUM_MODES
  35. };
  36. PolyMode polyMode = RESET_MODE;
  37. struct NoteData {
  38. uint8_t velocity = 0;
  39. uint8_t aftertouch = 0;
  40. };
  41. NoteData noteData[128];
  42. // cachedNotes : UNISON_MODE and REASSIGN_MODE cache all played notes. The other polyModes cache stolen notes (after the 4th one).
  43. std::vector<uint8_t> cachedNotes;
  44. uint8_t notes[4];
  45. bool gates[4];
  46. // gates set to TRUE by pedal and current gate. FALSE by pedal.
  47. bool pedalgates[4];
  48. bool pedal;
  49. int rotateIndex;
  50. int stealIndex;
  51. QuadMIDIToCVInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS), cachedNotes(128) {
  52. onReset();
  53. }
  54. json_t *toJson() override {
  55. json_t *rootJ = json_object();
  56. json_object_set_new(rootJ, "midi", midiInput.toJson());
  57. json_object_set_new(rootJ, "polyMode", json_integer(polyMode));
  58. return rootJ;
  59. }
  60. void fromJson(json_t *rootJ) override {
  61. json_t *midiJ = json_object_get(rootJ, "midi");
  62. if (midiJ)
  63. midiInput.fromJson(midiJ);
  64. json_t *polyModeJ = json_object_get(rootJ, "polyMode");
  65. if (polyModeJ)
  66. polyMode = (PolyMode) json_integer_value(polyModeJ);
  67. }
  68. void onReset() override {
  69. for (int i = 0; i < 4; i++) {
  70. notes[i] = 60;
  71. gates[i] = false;
  72. pedalgates[i] = false;
  73. }
  74. pedal = false;
  75. rotateIndex = -1;
  76. cachedNotes.clear();
  77. }
  78. int getPolyIndex(int nowIndex) {
  79. for (int i = 0; i < 4; i++) {
  80. nowIndex++;
  81. if (nowIndex > 3)
  82. nowIndex = 0;
  83. if (!(gates[nowIndex] || pedalgates[nowIndex])) {
  84. stealIndex = nowIndex;
  85. return nowIndex;
  86. }
  87. }
  88. // All taken = steal (stealIndex always rotates)
  89. stealIndex++;
  90. if (stealIndex > 3)
  91. stealIndex = 0;
  92. if ((polyMode < REASSIGN_MODE) && (gates[stealIndex]))
  93. cachedNotes.push_back(notes[stealIndex]);
  94. return stealIndex;
  95. }
  96. void pressNote(uint8_t note) {
  97. // Set notes and gates
  98. switch (polyMode) {
  99. case ROTATE_MODE: {
  100. rotateIndex = getPolyIndex(rotateIndex);
  101. } break;
  102. case REUSE_MODE: {
  103. bool reuse = false;
  104. for (int i = 0; i < 4; i++) {
  105. if (notes[i] == note) {
  106. rotateIndex = i;
  107. reuse = true;
  108. break;
  109. }
  110. }
  111. if (!reuse)
  112. rotateIndex = getPolyIndex(rotateIndex);
  113. } break;
  114. case RESET_MODE: {
  115. rotateIndex = getPolyIndex(-1);
  116. } break;
  117. case REASSIGN_MODE: {
  118. cachedNotes.push_back(note);
  119. rotateIndex = getPolyIndex(-1);
  120. } break;
  121. case UNISON_MODE: {
  122. cachedNotes.push_back(note);
  123. for (int i = 0; i < 4; i++) {
  124. notes[i] = note;
  125. gates[i] = true;
  126. pedalgates[i] = pedal;
  127. // reTrigger[i].trigger(1e-3);
  128. }
  129. return;
  130. } break;
  131. default: break;
  132. }
  133. // Set notes and gates
  134. // if (gates[rotateIndex] || pedalgates[rotateIndex])
  135. // reTrigger[rotateIndex].trigger(1e-3);
  136. notes[rotateIndex] = note;
  137. gates[rotateIndex] = true;
  138. pedalgates[rotateIndex] = pedal;
  139. }
  140. void releaseNote(uint8_t note) {
  141. // Remove the note
  142. auto it = std::find(cachedNotes.begin(), cachedNotes.end(), note);
  143. if (it != cachedNotes.end())
  144. cachedNotes.erase(it);
  145. switch (polyMode) {
  146. case REASSIGN_MODE: {
  147. for (int i = 0; i < 4; i++) {
  148. if (i < (int) cachedNotes.size()) {
  149. if (!pedalgates[i])
  150. notes[i] = cachedNotes[i];
  151. pedalgates[i] = pedal;
  152. }
  153. else {
  154. gates[i] = false;
  155. }
  156. }
  157. } break;
  158. case UNISON_MODE: {
  159. if (!cachedNotes.empty()) {
  160. uint8_t backnote = cachedNotes.back();
  161. for (int i = 0; i < 4; i++) {
  162. notes[i] = backnote;
  163. gates[i] = true;
  164. }
  165. }
  166. else {
  167. for (int i = 0; i < 4; i++) {
  168. gates[i] = false;
  169. }
  170. }
  171. } break;
  172. // default ROTATE_MODE REUSE_MODE RESET_MODE
  173. default: {
  174. for (int i = 0; i < 4; i++) {
  175. if (notes[i] == note) {
  176. if (pedalgates[i]) {
  177. gates[i] = false;
  178. }
  179. else if (!cachedNotes.empty()) {
  180. notes[i] = cachedNotes.back();
  181. cachedNotes.pop_back();
  182. }
  183. else {
  184. gates[i] = false;
  185. }
  186. }
  187. }
  188. } break;
  189. }
  190. }
  191. void pressPedal() {
  192. pedal = true;
  193. for (int i = 0; i < 4; i++) {
  194. pedalgates[i] = gates[i];
  195. }
  196. }
  197. void releasePedal() {
  198. pedal = false;
  199. // When pedal is off, recover notes for pressed keys (if any) after they were already being "cycled" out by pedal-sustained notes.
  200. for (int i = 0; i < 4; i++) {
  201. pedalgates[i] = false;
  202. if (!cachedNotes.empty()) {
  203. if (polyMode < REASSIGN_MODE) {
  204. notes[i] = cachedNotes.back();
  205. cachedNotes.pop_back();
  206. gates[i] = true;
  207. }
  208. }
  209. }
  210. if (polyMode == REASSIGN_MODE) {
  211. for (int i = 0; i < 4; i++) {
  212. if (i < (int) cachedNotes.size()) {
  213. notes[i] = cachedNotes[i];
  214. gates[i] = true;
  215. }
  216. else {
  217. gates[i] = false;
  218. }
  219. }
  220. }
  221. }
  222. void step() override {
  223. MidiMessage msg;
  224. while (midiInput.shift(&msg)) {
  225. processMessage(msg);
  226. }
  227. for (int i = 0; i < 4; i++) {
  228. uint8_t lastNote = notes[i];
  229. uint8_t lastGate = (gates[i] || pedalgates[i]);
  230. outputs[CV_OUTPUT + i].value = (lastNote - 60) / 12.f;
  231. outputs[GATE_OUTPUT + i].value = lastGate ? 10.f : 0.f;
  232. outputs[VELOCITY_OUTPUT + i].value = rescale(noteData[lastNote].velocity, 0, 127, 0.f, 10.f);
  233. outputs[AFTERTOUCH_OUTPUT + i].value = rescale(noteData[lastNote].aftertouch, 0, 127, 0.f, 10.f);
  234. }
  235. }
  236. void processMessage(MidiMessage msg) {
  237. // filter MIDI channel
  238. if ((midiInput.channel > -1) && (midiInput.channel != msg.channel()))
  239. return;
  240. switch (msg.status()) {
  241. // note off
  242. case 0x8: {
  243. releaseNote(msg.note());
  244. } break;
  245. // note on
  246. case 0x9: {
  247. if (msg.value() > 0) {
  248. noteData[msg.note()].velocity = msg.value();
  249. pressNote(msg.note());
  250. }
  251. else {
  252. releaseNote(msg.note());
  253. }
  254. } break;
  255. // channel aftertouch
  256. case 0xa: {
  257. noteData[msg.note()].aftertouch = msg.value();
  258. } break;
  259. // cc
  260. case 0xb: {
  261. processCC(msg);
  262. } break;
  263. default: break;
  264. }
  265. }
  266. void processCC(MidiMessage msg) {
  267. switch (msg.note()) {
  268. // sustain
  269. case 0x40: {
  270. if (msg.value() >= 64)
  271. pressPedal();
  272. else
  273. releasePedal();
  274. } break;
  275. default: break;
  276. }
  277. }
  278. };
  279. struct QuadMIDIToCVInterfaceWidget : ModuleWidget {
  280. QuadMIDIToCVInterfaceWidget(QuadMIDIToCVInterface *module) : ModuleWidget(module) {
  281. setPanel(SVG::load(assetGlobal("res/Core/QuadMIDIToCVInterface.svg")));
  282. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  283. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  284. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  285. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  286. addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.894335f, 60.144478f)), Port::OUTPUT, module, QuadMIDIToCVInterface::CV_OUTPUT + 0));
  287. addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.494659f, 60.144478f)), Port::OUTPUT, module, QuadMIDIToCVInterface::GATE_OUTPUT + 0));
  288. addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.094986f, 60.144478f)), Port::OUTPUT, module, QuadMIDIToCVInterface::VELOCITY_OUTPUT + 0));
  289. addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693935f, 60.144478f)), Port::OUTPUT, module, QuadMIDIToCVInterface::AFTERTOUCH_OUTPUT + 0));
  290. addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.894335f, 76.144882f)), Port::OUTPUT, module, QuadMIDIToCVInterface::CV_OUTPUT + 1));
  291. addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.494659f, 76.144882f)), Port::OUTPUT, module, QuadMIDIToCVInterface::GATE_OUTPUT + 1));
  292. addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.094986f, 76.144882f)), Port::OUTPUT, module, QuadMIDIToCVInterface::VELOCITY_OUTPUT + 1));
  293. addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693935f, 76.144882f)), Port::OUTPUT, module, QuadMIDIToCVInterface::AFTERTOUCH_OUTPUT + 1));
  294. addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.894335f, 92.143906f)), Port::OUTPUT, module, QuadMIDIToCVInterface::CV_OUTPUT + 2));
  295. addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.494659f, 92.143906f)), Port::OUTPUT, module, QuadMIDIToCVInterface::GATE_OUTPUT + 2));
  296. addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.094986f, 92.143906f)), Port::OUTPUT, module, QuadMIDIToCVInterface::VELOCITY_OUTPUT + 2));
  297. addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693935f, 92.143906f)), Port::OUTPUT, module, QuadMIDIToCVInterface::AFTERTOUCH_OUTPUT + 2));
  298. addOutput(Port::create<PJ301MPort>(mm2px(Vec(3.894335f, 108.1443f)), Port::OUTPUT, module, QuadMIDIToCVInterface::CV_OUTPUT + 3));
  299. addOutput(Port::create<PJ301MPort>(mm2px(Vec(15.494659f, 108.1443f)), Port::OUTPUT, module, QuadMIDIToCVInterface::GATE_OUTPUT + 3));
  300. addOutput(Port::create<PJ301MPort>(mm2px(Vec(27.094986f, 108.1443f)), Port::OUTPUT, module, QuadMIDIToCVInterface::VELOCITY_OUTPUT + 3));
  301. addOutput(Port::create<PJ301MPort>(mm2px(Vec(38.693935f, 108.1443f)), Port::OUTPUT, module, QuadMIDIToCVInterface::AFTERTOUCH_OUTPUT + 3));
  302. MidiWidget *midiWidget = Widget::create<MidiWidget>(mm2px(Vec(3.4009969f, 14.837336f)));
  303. midiWidget->box.size = mm2px(Vec(44, 28));
  304. midiWidget->midiIO = &module->midiInput;
  305. #ifdef USE_VST2
  306. midiWidget->midiIO->setDriverId(VST_DRIVER);
  307. midiWidget->midiIO->setDeviceId(0);
  308. #endif // USE_VST2
  309. addChild(midiWidget);
  310. }
  311. void appendContextMenu(Menu *menu) override {
  312. QuadMIDIToCVInterface *module = dynamic_cast<QuadMIDIToCVInterface*>(this->module);
  313. struct PolyphonyItem : MenuItem {
  314. QuadMIDIToCVInterface *module;
  315. QuadMIDIToCVInterface::PolyMode polyMode;
  316. void onAction(EventAction &e) override {
  317. module->polyMode = polyMode;
  318. module->onReset();
  319. }
  320. };
  321. menu->addChild(MenuEntry::create());
  322. menu->addChild(MenuLabel::create("Polyphony mode"));
  323. auto addPolyphonyItem = [&](QuadMIDIToCVInterface::PolyMode polyMode, std::string name) {
  324. PolyphonyItem *item = MenuItem::create<PolyphonyItem>(name, CHECKMARK(module->polyMode == polyMode));
  325. item->module = module;
  326. item->polyMode = polyMode;
  327. menu->addChild(item);
  328. };
  329. addPolyphonyItem(QuadMIDIToCVInterface::RESET_MODE, "Reset");
  330. addPolyphonyItem(QuadMIDIToCVInterface::ROTATE_MODE, "Rotate");
  331. addPolyphonyItem(QuadMIDIToCVInterface::REUSE_MODE, "Reuse");
  332. addPolyphonyItem(QuadMIDIToCVInterface::REASSIGN_MODE, "Reassign");
  333. addPolyphonyItem(QuadMIDIToCVInterface::UNISON_MODE, "Unison");
  334. }
  335. };
  336. RACK_PLUGIN_MODEL_INIT(Core, QuadMIDIToCVInterface) {
  337. Model *modelQuadMIDIToCVInterface = Model::create<QuadMIDIToCVInterface, QuadMIDIToCVInterfaceWidget>("Core", "QuadMIDIToCVInterface", "MIDI-4", MIDI_TAG, EXTERNAL_TAG, QUAD_TAG);
  338. return modelQuadMIDIToCVInterface;
  339. }