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.

579 lines
16KB

  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, "1V/octave pitch");
  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. // Song Position Pointer
  236. case 0x2: {
  237. int32_t pos = int32_t(msg.getNote()) | (int32_t(msg.getValue()) << 7);
  238. clock = pos * 6;
  239. } break;
  240. // Timing
  241. case 0x8: {
  242. clockPulse.trigger(1e-3);
  243. if (clock % clockDivision == 0) {
  244. clockDividerPulse.trigger(1e-3);
  245. }
  246. clock++;
  247. } break;
  248. // Start
  249. case 0xa: {
  250. startPulse.trigger(1e-3);
  251. clock = 0;
  252. } break;
  253. // Continue
  254. case 0xb: {
  255. continuePulse.trigger(1e-3);
  256. } break;
  257. // Stop
  258. case 0xc: {
  259. stopPulse.trigger(1e-3);
  260. } break;
  261. default: break;
  262. }
  263. }
  264. int assignChannel(uint8_t note) {
  265. if (channels == 1)
  266. return 0;
  267. switch (polyMode) {
  268. case REUSE_MODE: {
  269. // Find channel with the same note
  270. for (int c = 0; c < channels; c++) {
  271. if (notes[c] == note)
  272. return c;
  273. }
  274. } // fallthrough
  275. case ROTATE_MODE: {
  276. // Find next available channel
  277. for (int i = 0; i < channels; i++) {
  278. rotateIndex++;
  279. if (rotateIndex >= channels)
  280. rotateIndex = 0;
  281. if (!gates[rotateIndex])
  282. return rotateIndex;
  283. }
  284. // No notes are available. Advance rotateIndex once more.
  285. rotateIndex++;
  286. if (rotateIndex >= channels)
  287. rotateIndex = 0;
  288. return rotateIndex;
  289. } break;
  290. case RESET_MODE: {
  291. for (int c = 0; c < channels; c++) {
  292. if (!gates[c])
  293. return c;
  294. }
  295. return channels - 1;
  296. } break;
  297. case MPE_MODE: {
  298. // This case is handled by querying the MIDI message channel.
  299. return 0;
  300. } break;
  301. default: return 0;
  302. }
  303. }
  304. void pressNote(uint8_t note, int* channel) {
  305. // Remove existing similar note
  306. auto it = std::find(heldNotes.begin(), heldNotes.end(), note);
  307. if (it != heldNotes.end())
  308. heldNotes.erase(it);
  309. // Push note
  310. heldNotes.push_back(note);
  311. // Determine actual channel
  312. if (polyMode == MPE_MODE) {
  313. // Channel is already decided for us
  314. }
  315. else {
  316. *channel = assignChannel(note);
  317. }
  318. // Set note
  319. notes[*channel] = note;
  320. gates[*channel] = true;
  321. retriggerPulses[*channel].trigger(1e-3);
  322. }
  323. void releaseNote(uint8_t note) {
  324. // Remove the note
  325. auto it = std::find(heldNotes.begin(), heldNotes.end(), note);
  326. if (it != heldNotes.end())
  327. heldNotes.erase(it);
  328. // Hold note if pedal is pressed
  329. if (pedal)
  330. return;
  331. // Turn off gate of all channels with note
  332. for (int c = 0; c < channels; c++) {
  333. if (notes[c] == note) {
  334. gates[c] = false;
  335. }
  336. }
  337. // Set last note if monophonic
  338. if (channels == 1) {
  339. if (note == notes[0] && !heldNotes.empty()) {
  340. uint8_t lastNote = heldNotes.back();
  341. notes[0] = lastNote;
  342. gates[0] = true;
  343. return;
  344. }
  345. }
  346. }
  347. void pressPedal() {
  348. if (pedal)
  349. return;
  350. pedal = true;
  351. }
  352. void releasePedal() {
  353. if (!pedal)
  354. return;
  355. pedal = false;
  356. // Set last note if monophonic
  357. if (channels == 1) {
  358. if (!heldNotes.empty()) {
  359. uint8_t lastNote = heldNotes.back();
  360. notes[0] = lastNote;
  361. }
  362. }
  363. // Clear notes that are not held if polyphonic
  364. else {
  365. for (int c = 0; c < channels; c++) {
  366. if (!gates[c])
  367. continue;
  368. gates[c] = false;
  369. for (uint8_t note : heldNotes) {
  370. if (notes[c] == note) {
  371. gates[c] = true;
  372. break;
  373. }
  374. }
  375. }
  376. }
  377. }
  378. void setChannels(int channels) {
  379. if (channels == this->channels)
  380. return;
  381. this->channels = channels;
  382. panic();
  383. }
  384. void setPolyMode(PolyMode polyMode) {
  385. if (polyMode == this->polyMode)
  386. return;
  387. this->polyMode = polyMode;
  388. panic();
  389. }
  390. json_t* dataToJson() override {
  391. json_t* rootJ = json_object();
  392. json_object_set_new(rootJ, "smooth", json_boolean(smooth));
  393. json_object_set_new(rootJ, "channels", json_integer(channels));
  394. json_object_set_new(rootJ, "polyMode", json_integer(polyMode));
  395. json_object_set_new(rootJ, "clockDivision", json_integer(clockDivision));
  396. // Saving/restoring pitch and mod doesn't make much sense for MPE.
  397. if (polyMode != MPE_MODE) {
  398. json_object_set_new(rootJ, "lastPitch", json_integer(pws[0]));
  399. json_object_set_new(rootJ, "lastMod", json_integer(mods[0]));
  400. }
  401. json_object_set_new(rootJ, "midi", midiInput.toJson());
  402. return rootJ;
  403. }
  404. void dataFromJson(json_t* rootJ) override {
  405. json_t* smoothJ = json_object_get(rootJ, "smooth");
  406. if (smoothJ)
  407. smooth = json_boolean_value(smoothJ);
  408. json_t* channelsJ = json_object_get(rootJ, "channels");
  409. if (channelsJ)
  410. setChannels(json_integer_value(channelsJ));
  411. json_t* polyModeJ = json_object_get(rootJ, "polyMode");
  412. if (polyModeJ)
  413. polyMode = (PolyMode) json_integer_value(polyModeJ);
  414. json_t* clockDivisionJ = json_object_get(rootJ, "clockDivision");
  415. if (clockDivisionJ)
  416. clockDivision = json_integer_value(clockDivisionJ);
  417. json_t* lastPitchJ = json_object_get(rootJ, "lastPitch");
  418. if (lastPitchJ)
  419. pws[0] = json_integer_value(lastPitchJ);
  420. json_t* lastModJ = json_object_get(rootJ, "lastMod");
  421. if (lastModJ)
  422. mods[0] = json_integer_value(lastModJ);
  423. json_t* midiJ = json_object_get(rootJ, "midi");
  424. if (midiJ)
  425. midiInput.fromJson(midiJ);
  426. }
  427. };
  428. struct MIDI_CVWidget : ModuleWidget {
  429. MIDI_CVWidget(MIDI_CV* module) {
  430. setModule(module);
  431. setPanel(Svg::load(asset::system("res/Core/MIDI_CV.svg")));
  432. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  433. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  434. addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  435. addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  436. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.905, 64.347)), module, MIDI_CV::PITCH_OUTPUT));
  437. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(20.248, 64.347)), module, MIDI_CV::GATE_OUTPUT));
  438. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(32.591, 64.347)), module, MIDI_CV::VELOCITY_OUTPUT));
  439. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.905, 80.603)), module, MIDI_CV::AFTERTOUCH_OUTPUT));
  440. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(20.248, 80.603)), module, MIDI_CV::PW_OUTPUT));
  441. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(32.591, 80.603)), module, MIDI_CV::MOD_OUTPUT));
  442. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.905, 96.859)), module, MIDI_CV::CLOCK_OUTPUT));
  443. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(20.248, 96.707)), module, MIDI_CV::CLOCK_DIV_OUTPUT));
  444. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(32.591, 96.859)), module, MIDI_CV::RETRIGGER_OUTPUT));
  445. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(7.905, 113.115)), module, MIDI_CV::START_OUTPUT));
  446. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(20.248, 113.115)), module, MIDI_CV::STOP_OUTPUT));
  447. addOutput(createOutputCentered<PJ301MPort>(mm2px(Vec(32.591, 112.975)), module, MIDI_CV::CONTINUE_OUTPUT));
  448. MidiDisplay* display = createWidget<MidiDisplay>(mm2px(Vec(0.0, 13.048)));
  449. display->box.size = mm2px(Vec(40.64, 29.012));
  450. display->setMidiPort(module ? &module->midiInput : NULL);
  451. addChild(display);
  452. // MidiButton example
  453. // MidiButton* midiButton = createWidget<MidiButton_MIDI_DIN>(Vec(0, 0));
  454. // midiButton->setMidiPort(module ? &module->midiInput : NULL);
  455. // addChild(midiButton);
  456. }
  457. void appendContextMenu(Menu* menu) override {
  458. MIDI_CV* module = dynamic_cast<MIDI_CV*>(this->module);
  459. menu->addChild(new MenuSeparator);
  460. menu->addChild(createBoolPtrMenuItem("Smooth pitch/mod wheel", "", &module->smooth));
  461. static const std::vector<int> clockDivisions = {24 * 4, 24 * 2, 24, 24 / 2, 24 / 4, 24 / 8, 2, 1};
  462. static const std::vector<std::string> clockDivisionLabels = {"Whole", "Half", "Quarter", "8th", "16th", "32nd", "12 PPQN", "24 PPQN"};
  463. struct ClockDivisionItem : MenuItem {
  464. MIDI_CV* module;
  465. Menu* createChildMenu() override {
  466. Menu* menu = new Menu;
  467. for (size_t i = 0; i < clockDivisions.size(); i++) {
  468. menu->addChild(createCheckMenuItem(clockDivisionLabels[i], "",
  469. [=]() {return module->clockDivision == clockDivisions[i];},
  470. [=]() {module->clockDivision = clockDivisions[i];}
  471. ));
  472. }
  473. return menu;
  474. }
  475. };
  476. ClockDivisionItem* clockDivisionItem = new ClockDivisionItem;
  477. clockDivisionItem->text = "CLK/N divider";
  478. clockDivisionItem->rightText = RIGHT_ARROW;
  479. clockDivisionItem->module = module;
  480. menu->addChild(clockDivisionItem);
  481. struct ChannelItem : MenuItem {
  482. MIDI_CV* module;
  483. Menu* createChildMenu() override {
  484. Menu* menu = new Menu;
  485. for (int c = 1; c <= 16; c++) {
  486. menu->addChild(createCheckMenuItem((c == 1) ? "Monophonic" : string::f("%d", c), "",
  487. [=]() {return module->channels == c;},
  488. [=]() {module->setChannels(c);}
  489. ));
  490. }
  491. return menu;
  492. }
  493. };
  494. ChannelItem* channelItem = new ChannelItem;
  495. channelItem->text = "Polyphony channels";
  496. channelItem->rightText = string::f("%d", module->channels) + " " + RIGHT_ARROW;
  497. channelItem->module = module;
  498. menu->addChild(channelItem);
  499. menu->addChild(createIndexPtrSubmenuItem("Polyphony mode", {
  500. "Rotate",
  501. "Reuse",
  502. "Reset",
  503. "MPE",
  504. }, &module->polyMode));
  505. menu->addChild(createMenuItem("Panic", "",
  506. [=]() {module->panic();}
  507. ));
  508. // Example of using appendMidiMenu()
  509. // menu->addChild(new MenuSeparator);
  510. // appendMidiMenu(menu, &module->midiInput);
  511. }
  512. };
  513. // Use legacy slug for compatibility
  514. Model* modelMIDI_CV = createModel<MIDI_CV, MIDI_CVWidget>("MIDIToCVInterface");
  515. } // namespace core
  516. } // namespace rack