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.

575 lines
15KB

  1. #include <algorithm>
  2. #include "plugin.hpp"
  3. namespace rack {
  4. namespace core {
  5. struct MIDI_CV : Module {
  6. enum ParamIds {
  7. NUM_PARAMS
  8. };
  9. enum InputIds {
  10. NUM_INPUTS
  11. };
  12. enum OutputIds {
  13. PITCH_OUTPUT,
  14. GATE_OUTPUT,
  15. VELOCITY_OUTPUT,
  16. AFTERTOUCH_OUTPUT,
  17. PW_OUTPUT,
  18. MOD_OUTPUT,
  19. RETRIGGER_OUTPUT,
  20. CLOCK_OUTPUT,
  21. CLOCK_DIV_OUTPUT,
  22. START_OUTPUT,
  23. STOP_OUTPUT,
  24. CONTINUE_OUTPUT,
  25. NUM_OUTPUTS
  26. };
  27. enum LightIds {
  28. NUM_LIGHTS
  29. };
  30. midi::InputQueue midiInput;
  31. bool smooth;
  32. int channels;
  33. enum PolyMode {
  34. ROTATE_MODE,
  35. REUSE_MODE,
  36. RESET_MODE,
  37. MPE_MODE,
  38. NUM_POLY_MODES
  39. };
  40. PolyMode polyMode;
  41. uint32_t clock = 0;
  42. int clockDivision;
  43. bool pedal;
  44. // Indexed by channel
  45. uint8_t notes[16];
  46. bool gates[16];
  47. uint8_t velocities[16];
  48. uint8_t aftertouches[16];
  49. std::vector<uint8_t> heldNotes;
  50. int rotateIndex;
  51. /** Pitch wheel.
  52. When MPE is disabled, only the first channel is used.
  53. [channel]
  54. */
  55. uint16_t pws[16];
  56. /** [channel] */
  57. uint8_t mods[16];
  58. dsp::ExponentialFilter pwFilters[16];
  59. dsp::ExponentialFilter modFilters[16];
  60. dsp::PulseGenerator clockPulse;
  61. dsp::PulseGenerator clockDividerPulse;
  62. dsp::PulseGenerator retriggerPulses[16];
  63. dsp::PulseGenerator startPulse;
  64. dsp::PulseGenerator stopPulse;
  65. dsp::PulseGenerator continuePulse;
  66. MIDI_CV() {
  67. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  68. configOutput(PITCH_OUTPUT, "Pitch (1V/oct)");
  69. configOutput(GATE_OUTPUT, "Gate");
  70. configOutput(VELOCITY_OUTPUT, "Velocity");
  71. configOutput(AFTERTOUCH_OUTPUT, "Aftertouch");
  72. configOutput(PW_OUTPUT, "Pitch wheel");
  73. configOutput(MOD_OUTPUT, "Mod wheel");
  74. configOutput(RETRIGGER_OUTPUT, "Retrigger");
  75. configOutput(CLOCK_OUTPUT, "Clock");
  76. configOutput(CLOCK_DIV_OUTPUT, "Clock divider");
  77. configOutput(START_OUTPUT, "Start trigger");
  78. configOutput(STOP_OUTPUT, "Stop trigger");
  79. configOutput(CONTINUE_OUTPUT, "Continue trigger");
  80. heldNotes.reserve(128);
  81. for (int c = 0; c < 16; c++) {
  82. pwFilters[c].setTau(1 / 30.f);
  83. modFilters[c].setTau(1 / 30.f);
  84. }
  85. onReset();
  86. }
  87. void onReset() override {
  88. smooth = true;
  89. channels = 1;
  90. polyMode = ROTATE_MODE;
  91. clockDivision = 24;
  92. panic();
  93. midiInput.reset();
  94. }
  95. /** Resets performance state */
  96. void panic() {
  97. for (int c = 0; c < 16; c++) {
  98. notes[c] = 60;
  99. gates[c] = false;
  100. velocities[c] = 0;
  101. aftertouches[c] = 0;
  102. pws[c] = 8192;
  103. mods[c] = 0;
  104. pwFilters[c].reset();
  105. modFilters[c].reset();
  106. }
  107. pedal = false;
  108. rotateIndex = -1;
  109. heldNotes.clear();
  110. }
  111. void process(const ProcessArgs& args) override {
  112. midi::Message msg;
  113. while (midiInput.tryPop(&msg, args.frame)) {
  114. processMessage(msg);
  115. }
  116. outputs[PITCH_OUTPUT].setChannels(channels);
  117. outputs[GATE_OUTPUT].setChannels(channels);
  118. outputs[VELOCITY_OUTPUT].setChannels(channels);
  119. outputs[AFTERTOUCH_OUTPUT].setChannels(channels);
  120. outputs[RETRIGGER_OUTPUT].setChannels(channels);
  121. for (int c = 0; c < channels; c++) {
  122. outputs[PITCH_OUTPUT].setVoltage((notes[c] - 60.f) / 12.f, c);
  123. outputs[GATE_OUTPUT].setVoltage(gates[c] ? 10.f : 0.f, c);
  124. outputs[VELOCITY_OUTPUT].setVoltage(rescale(velocities[c], 0, 127, 0.f, 10.f), c);
  125. outputs[AFTERTOUCH_OUTPUT].setVoltage(rescale(aftertouches[c], 0, 127, 0.f, 10.f), c);
  126. outputs[RETRIGGER_OUTPUT].setVoltage(retriggerPulses[c].process(args.sampleTime) ? 10.f : 0.f, c);
  127. }
  128. // Set pitch and mod wheel
  129. int wheelChannels = (polyMode == MPE_MODE) ? 16 : 1;
  130. outputs[PW_OUTPUT].setChannels(wheelChannels);
  131. outputs[MOD_OUTPUT].setChannels(wheelChannels);
  132. for (int c = 0; c < wheelChannels; c++) {
  133. float pw = ((int) pws[c] - 8192) / 8191.f;
  134. pw = clamp(pw, -1.f, 1.f);
  135. if (smooth)
  136. pw = pwFilters[c].process(args.sampleTime, pw);
  137. else
  138. pwFilters[c].out = pw;
  139. outputs[PW_OUTPUT].setVoltage(pw * 5.f);
  140. float mod = mods[c] / 127.f;
  141. mod = clamp(mod, 0.f, 1.f);
  142. if (smooth)
  143. mod = modFilters[c].process(args.sampleTime, mod);
  144. else
  145. modFilters[c].out = mod;
  146. outputs[MOD_OUTPUT].setVoltage(mod * 10.f);
  147. }
  148. outputs[CLOCK_OUTPUT].setVoltage(clockPulse.process(args.sampleTime) ? 10.f : 0.f);
  149. outputs[CLOCK_DIV_OUTPUT].setVoltage(clockDividerPulse.process(args.sampleTime) ? 10.f : 0.f);
  150. outputs[START_OUTPUT].setVoltage(startPulse.process(args.sampleTime) ? 10.f : 0.f);
  151. outputs[STOP_OUTPUT].setVoltage(stopPulse.process(args.sampleTime) ? 10.f : 0.f);
  152. outputs[CONTINUE_OUTPUT].setVoltage(continuePulse.process(args.sampleTime) ? 10.f : 0.f);
  153. }
  154. void processMessage(const midi::Message& msg) {
  155. // DEBUG("MIDI: %ld %s", msg.getFrame(), msg.toString().c_str());
  156. switch (msg.getStatus()) {
  157. // note off
  158. case 0x8: {
  159. releaseNote(msg.getNote());
  160. } break;
  161. // note on
  162. case 0x9: {
  163. if (msg.getValue() > 0) {
  164. int c = msg.getChannel();
  165. pressNote(msg.getNote(), &c);
  166. velocities[c] = msg.getValue();
  167. }
  168. else {
  169. // For some reason, some keyboards send a "note on" event with a velocity of 0 to signal that the key has been released.
  170. releaseNote(msg.getNote());
  171. }
  172. } break;
  173. // key pressure
  174. case 0xa: {
  175. // Set the aftertouches with the same note
  176. // TODO Should we handle the MPE case differently?
  177. for (int c = 0; c < 16; c++) {
  178. if (notes[c] == msg.getNote())
  179. aftertouches[c] = msg.getValue();
  180. }
  181. } break;
  182. // cc
  183. case 0xb: {
  184. processCC(msg);
  185. } break;
  186. // channel pressure
  187. case 0xd: {
  188. if (polyMode == MPE_MODE) {
  189. // Set the channel aftertouch
  190. aftertouches[msg.getChannel()] = msg.getNote();
  191. }
  192. else {
  193. // Set all aftertouches
  194. for (int c = 0; c < 16; c++) {
  195. aftertouches[c] = msg.getNote();
  196. }
  197. }
  198. } break;
  199. // pitch wheel
  200. case 0xe: {
  201. int c = (polyMode == MPE_MODE) ? msg.getChannel() : 0;
  202. pws[c] = ((uint16_t) msg.getValue() << 7) | msg.getNote();
  203. } break;
  204. case 0xf: {
  205. processSystem(msg);
  206. } break;
  207. default: break;
  208. }
  209. }
  210. void processCC(const midi::Message &msg) {
  211. switch (msg.getNote()) {
  212. // mod
  213. case 0x01: {
  214. int c = (polyMode == MPE_MODE) ? msg.getChannel() : 0;
  215. mods[c] = msg.getValue();
  216. } break;
  217. // sustain
  218. case 0x40: {
  219. if (msg.getValue() >= 64)
  220. pressPedal();
  221. else
  222. releasePedal();
  223. } break;
  224. // all notes off (panic)
  225. case 0x7b: {
  226. if (msg.getValue() == 0) {
  227. panic();
  228. }
  229. } break;
  230. default: break;
  231. }
  232. }
  233. void processSystem(const midi::Message &msg) {
  234. switch (msg.getChannel()) {
  235. // Timing
  236. case 0x8: {
  237. clockPulse.trigger(1e-3);
  238. if (clock % clockDivision == 0) {
  239. clockDividerPulse.trigger(1e-3);
  240. }
  241. clock++;
  242. } break;
  243. // Start
  244. case 0xa: {
  245. startPulse.trigger(1e-3);
  246. clock = 0;
  247. } break;
  248. // Continue
  249. case 0xb: {
  250. continuePulse.trigger(1e-3);
  251. } break;
  252. // Stop
  253. case 0xc: {
  254. stopPulse.trigger(1e-3);
  255. clock = 0;
  256. } break;
  257. default: break;
  258. }
  259. }
  260. int assignChannel(uint8_t note) {
  261. if (channels == 1)
  262. return 0;
  263. switch (polyMode) {
  264. case REUSE_MODE: {
  265. // Find channel with the same note
  266. for (int c = 0; c < channels; c++) {
  267. if (notes[c] == note)
  268. return c;
  269. }
  270. } // fallthrough
  271. case ROTATE_MODE: {
  272. // Find next available channel
  273. for (int i = 0; i < channels; i++) {
  274. rotateIndex++;
  275. if (rotateIndex >= channels)
  276. rotateIndex = 0;
  277. if (!gates[rotateIndex])
  278. return rotateIndex;
  279. }
  280. // No notes are available. Advance rotateIndex once more.
  281. rotateIndex++;
  282. if (rotateIndex >= channels)
  283. rotateIndex = 0;
  284. return rotateIndex;
  285. } break;
  286. case RESET_MODE: {
  287. for (int c = 0; c < channels; c++) {
  288. if (!gates[c])
  289. return c;
  290. }
  291. return channels - 1;
  292. } break;
  293. case MPE_MODE: {
  294. // This case is handled by querying the MIDI message channel.
  295. return 0;
  296. } break;
  297. default: return 0;
  298. }
  299. }
  300. void pressNote(uint8_t note, int* channel) {
  301. // Remove existing similar note
  302. auto it = std::find(heldNotes.begin(), heldNotes.end(), note);
  303. if (it != heldNotes.end())
  304. heldNotes.erase(it);
  305. // Push note
  306. heldNotes.push_back(note);
  307. // Determine actual channel
  308. if (polyMode == MPE_MODE) {
  309. // Channel is already decided for us
  310. }
  311. else {
  312. *channel = assignChannel(note);
  313. }
  314. // Set note
  315. notes[*channel] = note;
  316. gates[*channel] = true;
  317. retriggerPulses[*channel].trigger(1e-3);
  318. }
  319. void releaseNote(uint8_t note) {
  320. // Remove the note
  321. auto it = std::find(heldNotes.begin(), heldNotes.end(), note);
  322. if (it != heldNotes.end())
  323. heldNotes.erase(it);
  324. // Hold note if pedal is pressed
  325. if (pedal)
  326. return;
  327. // Turn off gate of all channels with note
  328. for (int c = 0; c < channels; c++) {
  329. if (notes[c] == note) {
  330. gates[c] = false;
  331. }
  332. }
  333. // Set last note if monophonic
  334. if (channels == 1) {
  335. if (note == notes[0] && !heldNotes.empty()) {
  336. uint8_t lastNote = heldNotes.back();
  337. notes[0] = lastNote;
  338. gates[0] = true;
  339. return;
  340. }
  341. }
  342. }
  343. void pressPedal() {
  344. if (pedal)
  345. return;
  346. pedal = true;
  347. }
  348. void releasePedal() {
  349. if (!pedal)
  350. return;
  351. pedal = false;
  352. // Set last note if monophonic
  353. if (channels == 1) {
  354. if (!heldNotes.empty()) {
  355. uint8_t lastNote = heldNotes.back();
  356. notes[0] = lastNote;
  357. }
  358. }
  359. // Clear notes that are not held if polyphonic
  360. else {
  361. for (int c = 0; c < channels; c++) {
  362. if (!gates[c])
  363. continue;
  364. gates[c] = false;
  365. for (uint8_t note : heldNotes) {
  366. if (notes[c] == note) {
  367. gates[c] = true;
  368. break;
  369. }
  370. }
  371. }
  372. }
  373. }
  374. void setChannels(int channels) {
  375. if (channels == this->channels)
  376. return;
  377. this->channels = channels;
  378. panic();
  379. }
  380. void setPolyMode(PolyMode polyMode) {
  381. if (polyMode == this->polyMode)
  382. return;
  383. this->polyMode = polyMode;
  384. panic();
  385. }
  386. json_t* dataToJson() override {
  387. json_t* rootJ = json_object();
  388. json_object_set_new(rootJ, "smooth", json_boolean(smooth));
  389. json_object_set_new(rootJ, "channels", json_integer(channels));
  390. json_object_set_new(rootJ, "polyMode", json_integer(polyMode));
  391. json_object_set_new(rootJ, "clockDivision", json_integer(clockDivision));
  392. // Saving/restoring pitch and mod doesn't make much sense for MPE.
  393. if (polyMode != MPE_MODE) {
  394. json_object_set_new(rootJ, "lastPitch", json_integer(pws[0]));
  395. json_object_set_new(rootJ, "lastMod", json_integer(mods[0]));
  396. }
  397. json_object_set_new(rootJ, "midi", midiInput.toJson());
  398. return rootJ;
  399. }
  400. void dataFromJson(json_t* rootJ) override {
  401. json_t* smoothJ = json_object_get(rootJ, "smooth");
  402. if (smoothJ)
  403. smooth = json_boolean_value(smoothJ);
  404. json_t* channelsJ = json_object_get(rootJ, "channels");
  405. if (channelsJ)
  406. setChannels(json_integer_value(channelsJ));
  407. json_t* polyModeJ = json_object_get(rootJ, "polyMode");
  408. if (polyModeJ)
  409. polyMode = (PolyMode) json_integer_value(polyModeJ);
  410. json_t* clockDivisionJ = json_object_get(rootJ, "clockDivision");
  411. if (clockDivisionJ)
  412. clockDivision = json_integer_value(clockDivisionJ);
  413. json_t* lastPitchJ = json_object_get(rootJ, "lastPitch");
  414. if (lastPitchJ)
  415. pws[0] = json_integer_value(lastPitchJ);
  416. json_t* lastModJ = json_object_get(rootJ, "lastMod");
  417. if (lastModJ)
  418. mods[0] = json_integer_value(lastModJ);
  419. json_t* midiJ = json_object_get(rootJ, "midi");
  420. if (midiJ)
  421. midiInput.fromJson(midiJ);
  422. }
  423. };
  424. struct MIDI_CVWidget : ModuleWidget {
  425. MIDI_CVWidget(MIDI_CV* module) {
  426. setModule(module);
  427. setPanel(Svg::load(asset::system("res/Core/MIDI-CV.svg")));
  428. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  429. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  430. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  431. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  432. addOutput(createOutput<PJ301MPort>(mm2px(Vec(4.61505, 60.1445)), module, MIDI_CV::PITCH_OUTPUT));
  433. addOutput(createOutput<PJ301MPort>(mm2px(Vec(16.214, 60.1445)), module, MIDI_CV::GATE_OUTPUT));
  434. addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.8143, 60.1445)), module, MIDI_CV::VELOCITY_OUTPUT));
  435. addOutput(createOutput<PJ301MPort>(mm2px(Vec(4.61505, 76.1449)), module, MIDI_CV::AFTERTOUCH_OUTPUT));
  436. addOutput(createOutput<PJ301MPort>(mm2px(Vec(16.214, 76.1449)), module, MIDI_CV::PW_OUTPUT));
  437. addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.8143, 76.1449)), module, MIDI_CV::MOD_OUTPUT));
  438. addOutput(createOutput<PJ301MPort>(mm2px(Vec(4.61505, 92.1439)), module, MIDI_CV::CLOCK_OUTPUT));
  439. addOutput(createOutput<PJ301MPort>(mm2px(Vec(16.214, 92.1439)), module, MIDI_CV::CLOCK_DIV_OUTPUT));
  440. addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.8143, 92.1439)), module, MIDI_CV::RETRIGGER_OUTPUT));
  441. addOutput(createOutput<PJ301MPort>(mm2px(Vec(4.61505, 108.144)), module, MIDI_CV::START_OUTPUT));
  442. addOutput(createOutput<PJ301MPort>(mm2px(Vec(16.214, 108.144)), module, MIDI_CV::STOP_OUTPUT));
  443. addOutput(createOutput<PJ301MPort>(mm2px(Vec(27.8143, 108.144)), module, MIDI_CV::CONTINUE_OUTPUT));
  444. MidiWidget* midiWidget = createWidget<MidiWidget>(mm2px(Vec(3.41891, 14.8373)));
  445. midiWidget->box.size = mm2px(Vec(33.840, 28));
  446. midiWidget->setMidiPort(module ? &module->midiInput : NULL);
  447. addChild(midiWidget);
  448. // MidiButton example
  449. // MidiButton* midiButton = createWidget<MidiButton_MIDI_DIN>(Vec(0, 0));
  450. // midiButton->setMidiPort(module ? &module->midiInput : NULL);
  451. // addChild(midiButton);
  452. }
  453. void appendContextMenu(Menu* menu) override {
  454. MIDI_CV* module = dynamic_cast<MIDI_CV*>(this->module);
  455. menu->addChild(new MenuSeparator);
  456. menu->addChild(createBoolPtrMenuItem("Smooth pitch/mod wheel", "", &module->smooth));
  457. static const std::vector<int> clockDivisions = {24 * 4, 24 * 2, 24, 24 / 2, 24 / 4, 24 / 8, 2, 1};
  458. static const std::vector<std::string> clockDivisionLabels = {"Whole", "Half", "Quarter", "8th", "16th", "32nd", "12 PPQN", "24 PPQN"};
  459. struct ClockDivisionItem : MenuItem {
  460. MIDI_CV* module;
  461. Menu* createChildMenu() override {
  462. Menu* menu = new Menu;
  463. for (size_t i = 0; i < clockDivisions.size(); i++) {
  464. menu->addChild(createCheckMenuItem(clockDivisionLabels[i], "",
  465. [=]() {return module->clockDivision == clockDivisions[i];},
  466. [=]() {module->clockDivision = clockDivisions[i];}
  467. ));
  468. }
  469. return menu;
  470. }
  471. };
  472. ClockDivisionItem* clockDivisionItem = new ClockDivisionItem;
  473. clockDivisionItem->text = "CLK/N divider";
  474. clockDivisionItem->rightText = RIGHT_ARROW;
  475. clockDivisionItem->module = module;
  476. menu->addChild(clockDivisionItem);
  477. struct ChannelItem : MenuItem {
  478. MIDI_CV* module;
  479. Menu* createChildMenu() override {
  480. Menu* menu = new Menu;
  481. for (int c = 1; c <= 16; c++) {
  482. menu->addChild(createCheckMenuItem((c == 1) ? "Monophonic" : string::f("%d", c), "",
  483. [=]() {return module->channels == c;},
  484. [=]() {module->setChannels(c);}
  485. ));
  486. }
  487. return menu;
  488. }
  489. };
  490. ChannelItem* channelItem = new ChannelItem;
  491. channelItem->text = "Polyphony channels";
  492. channelItem->rightText = string::f("%d", module->channels) + " " + RIGHT_ARROW;
  493. channelItem->module = module;
  494. menu->addChild(channelItem);
  495. menu->addChild(createIndexPtrSubmenuItem("Polyphony mode", {
  496. "Rotate",
  497. "Reuse",
  498. "Reset",
  499. "MPE",
  500. }, &module->polyMode));
  501. menu->addChild(createMenuItem("Panic", "",
  502. [=]() {module->panic();}
  503. ));
  504. // Example of using appendMidiMenu()
  505. // menu->addChild(new MenuSeparator);
  506. // appendMidiMenu(menu, &module->midiInput);
  507. }
  508. };
  509. // Use legacy slug for compatibility
  510. Model* modelMIDI_CV = createModel<MIDI_CV, MIDI_CVWidget>("MIDIToCVInterface");
  511. } // namespace core
  512. } // namespace rack