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.

734 lines
17KB

  1. #include <assert.h>
  2. #include <list>
  3. #include <algorithm>
  4. #include "rtmidi/RtMidi.h"
  5. #include "core.hpp"
  6. #include "gui.hpp"
  7. #include "../../include/engine.hpp"
  8. using namespace rack;
  9. /**
  10. * MidiIO implements the shared functionality of all midi modules, namely:
  11. * + Channel Selection (including helper for storing json)
  12. * + Interface Selection (including helper for storing json)
  13. * + rtMidi initialisation (input or output)
  14. */
  15. struct MidiIO {
  16. int portId = -1;
  17. RtMidi *rtMidi = NULL;
  18. /** Filter MIDI channel
  19. -1 means all MIDI channels
  20. */
  21. int channel = -1;
  22. /*
  23. * If isOut is set to true, creates a RtMidiOut, RtMidiIn otherwise
  24. */
  25. MidiIO(bool isOut = false) {
  26. try {
  27. if (isOut) {
  28. rtMidi = new RtMidiOut(RtMidi::UNSPECIFIED, "Rack");
  29. } else {
  30. rtMidi = new RtMidiIn(RtMidi::UNSPECIFIED, "Rack");
  31. }
  32. }
  33. catch (RtMidiError &error) {
  34. fprintf(stderr, "Failed to create RtMidiIn: %s\n", error.getMessage().c_str());
  35. }
  36. }
  37. ~MidiIO() {}
  38. int getPortCount();
  39. std::string getPortName(int portId);
  40. // -1 will close the port
  41. void setPortId(int portId);
  42. void setChannel(int channel) {
  43. this->channel = channel;
  44. }
  45. json_t *addBaseJson(json_t *rootJ) {
  46. if (portId >= 0) {
  47. std::string portName = getPortName(portId);
  48. json_object_set_new(rootJ, "portName", json_string(portName.c_str()));
  49. json_object_set_new(rootJ, "channel", json_integer(channel));
  50. }
  51. return rootJ;
  52. }
  53. void baseFromJson(json_t *rootJ) {
  54. json_t *portNameJ = json_object_get(rootJ, "portName");
  55. if (portNameJ) {
  56. std::string portName = json_string_value(portNameJ);
  57. for (int i = 0; i < getPortCount(); i++) {
  58. if (portName == getPortName(i)) {
  59. setPortId(i);
  60. break;
  61. }
  62. }
  63. }
  64. json_t *channelJ = json_object_get(rootJ, "channel");
  65. if (channelJ) {
  66. setChannel(json_integer_value(channelJ));
  67. }
  68. }
  69. virtual void resetMidi()=0; // called when midi port is set
  70. };
  71. int MidiIO::getPortCount() {
  72. return rtMidi->getPortCount();
  73. }
  74. std::string MidiIO::getPortName(int portId) {
  75. std::string portName;
  76. try {
  77. portName = rtMidi->getPortName(portId);
  78. }
  79. catch (RtMidiError &error) {
  80. fprintf(stderr, "Failed to get Port Name: %d, %s\n", portId, error.getMessage().c_str());
  81. }
  82. return portName;
  83. }
  84. void MidiIO::setPortId(int portId) {
  85. resetMidi(); // reset Midi values
  86. // Close port if it was previously opened
  87. if (rtMidi->isPortOpen()) {
  88. rtMidi->closePort();
  89. }
  90. this->portId = -1;
  91. // Open new port
  92. if (portId >= 0) {
  93. rtMidi->openPort(portId, "Midi Interface");
  94. }
  95. this->portId = portId;
  96. }
  97. struct MidiItem : MenuItem {
  98. MidiIO *midiModule;
  99. int portId;
  100. void onAction() {
  101. midiModule->setPortId(portId);
  102. }
  103. };
  104. struct MidiChoice : ChoiceButton {
  105. MidiIO *midiModule;
  106. void onAction() {
  107. Menu *menu = gScene->createMenu();
  108. menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y));
  109. menu->box.size.x = box.size.x;
  110. int portCount = midiModule->getPortCount();
  111. {
  112. MidiItem *midiItem = new MidiItem();
  113. midiItem->midiModule = midiModule;
  114. midiItem->portId = -1;
  115. midiItem->text = "No device";
  116. menu->pushChild(midiItem);
  117. }
  118. for (int portId = 0; portId < portCount; portId++) {
  119. MidiItem *midiItem = new MidiItem();
  120. midiItem->midiModule = midiModule;
  121. midiItem->portId = portId;
  122. midiItem->text = midiModule->getPortName(portId);
  123. menu->pushChild(midiItem);
  124. }
  125. }
  126. void step() {
  127. if (midiModule->portId < 0) {
  128. text = "No Device";
  129. return;
  130. }
  131. std::string name = midiModule->getPortName(midiModule->portId);
  132. text = ellipsize(name, 15);
  133. }
  134. };
  135. struct ChannelItem : MenuItem {
  136. MidiIO *midiModule;
  137. int channel;
  138. void onAction() {
  139. midiModule->setChannel(channel);
  140. }
  141. };
  142. struct ChannelChoice : ChoiceButton {
  143. MidiIO *midiModule;
  144. void onAction() {
  145. Menu *menu = gScene->createMenu();
  146. menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y));
  147. menu->box.size.x = box.size.x;
  148. {
  149. ChannelItem *channelItem = new ChannelItem();
  150. channelItem->midiModule = midiModule;
  151. channelItem->channel = -1;
  152. channelItem->text = "All";
  153. menu->pushChild(channelItem);
  154. }
  155. for (int channel = 0; channel < 16; channel++) {
  156. ChannelItem *channelItem = new ChannelItem();
  157. channelItem->midiModule = midiModule;
  158. channelItem->channel = channel;
  159. channelItem->text = stringf("%d", channel + 1);
  160. menu->pushChild(channelItem);
  161. }
  162. }
  163. void step() {
  164. text = (midiModule->channel >= 0) ? stringf("%d", midiModule->channel + 1) : "All";
  165. }
  166. };
  167. /*
  168. * MIDIToCVInterface converts midi note on/off events, velocity , channel aftertouch, pitch wheel and mod wheel to
  169. * CV
  170. */
  171. struct MIDIToCVInterface : MidiIO, Module {
  172. enum ParamIds {
  173. RESET_PARAM,
  174. NUM_PARAMS
  175. };
  176. enum InputIds {
  177. NUM_INPUTS
  178. };
  179. enum OutputIds {
  180. PITCH_OUTPUT = 0,
  181. GATE_OUTPUT,
  182. VELOCITY_OUTPUT,
  183. MOD_OUTPUT,
  184. PITCHWHEEL_OUTPUT,
  185. CHANNEL_AFTERTOUCH_OUTPUT,
  186. NUM_OUTPUTS
  187. };
  188. std::list<int> notes;
  189. bool pedal = false;
  190. int note = 60; // C4, most modules should use 261.626 Hz
  191. int mod = 0;
  192. int vel = 0;
  193. int afterTouch = 0;
  194. int pitchWheel = 64;
  195. bool retrigger = false;
  196. bool retriggered = false;
  197. float lights[NUM_OUTPUTS];
  198. SchmittTrigger resetTrigger;
  199. float resetLight = 0.0;
  200. MIDIToCVInterface() : MidiIO(), Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {
  201. }
  202. ~MIDIToCVInterface() {
  203. setPortId(-1);
  204. };
  205. void step();
  206. void pressNote(int note);
  207. void releaseNote(int note);
  208. void processMidi(std::vector<unsigned char> msg);
  209. virtual json_t *toJson() {
  210. json_t *rootJ = json_object();
  211. addBaseJson(rootJ);
  212. return rootJ;
  213. }
  214. virtual void fromJson(json_t *rootJ) {
  215. baseFromJson(rootJ);
  216. }
  217. virtual void initialize() {
  218. setPortId(-1);
  219. }
  220. virtual void resetMidi();
  221. void updateLights();
  222. };
  223. void MIDIToCVInterface::resetMidi(){
  224. mod = 0;
  225. pitchWheel = 64;
  226. afterTouch = 0;
  227. vel = 0;
  228. resetLight = 1.0;
  229. outputs[GATE_OUTPUT].value = 0.0;
  230. notes.clear();
  231. updateLights();
  232. }
  233. void MIDIToCVInterface::updateLights() {
  234. lights[GATE_OUTPUT] = outputs[GATE_OUTPUT].value/10;
  235. lights[MOD_OUTPUT] = mod / 127.0;
  236. lights[PITCHWHEEL_OUTPUT] = pitchWheel / 127.0;
  237. lights[CHANNEL_AFTERTOUCH_OUTPUT] = afterTouch / 127.0;
  238. lights[VELOCITY_OUTPUT] = vel / 127.0;
  239. }
  240. void MIDIToCVInterface::step() {
  241. if (rtMidi->isPortOpen()) {
  242. std::vector<unsigned char> message;
  243. // midiIn->getMessage returns empty vector if there are no messages in the queue
  244. dynamic_cast<RtMidiIn *>(rtMidi)->getMessage(&message);
  245. while (message.size() > 0) {
  246. processMidi(message);
  247. dynamic_cast<RtMidiIn *>(rtMidi)->getMessage(&message);
  248. }
  249. }
  250. outputs[PITCH_OUTPUT].value = ((note - 60)) / 12.0;
  251. bool gate = pedal || !notes.empty();
  252. if (retrigger && retriggered) {
  253. gate = false;
  254. retriggered = false;
  255. }
  256. if (resetTrigger.process(params[RESET_PARAM].value)) {
  257. resetMidi();
  258. return;
  259. }
  260. if (resetLight > 0) {
  261. resetLight -= resetLight/0.55/gSampleRate; // fade out light
  262. }
  263. outputs[GATE_OUTPUT].value = gate ? 10.0 : 0.0;
  264. outputs[MOD_OUTPUT].value = mod / 127.0 * 10.0;
  265. outputs[PITCHWHEEL_OUTPUT].value = (pitchWheel - 64) / 64.0 * 10.0;
  266. outputs[CHANNEL_AFTERTOUCH_OUTPUT].value = afterTouch / 127.0 * 10.0;
  267. outputs[VELOCITY_OUTPUT].value = vel / 127.0 * 10.0;
  268. updateLights();
  269. }
  270. void MIDIToCVInterface::pressNote(int note) {
  271. // Remove existing similar note
  272. auto it = std::find(notes.begin(), notes.end(), note);
  273. if (it != notes.end())
  274. notes.erase(it);
  275. // Push note
  276. notes.push_back(note);
  277. this->note = note;
  278. retriggered = true;
  279. }
  280. void MIDIToCVInterface::releaseNote(int note) {
  281. // Remove the note
  282. auto it = std::find(notes.begin(), notes.end(), note);
  283. if (it != notes.end())
  284. notes.erase(it);
  285. if (pedal) {
  286. // Don't release if pedal is held
  287. } else if (!notes.empty()) {
  288. // Play previous note
  289. auto it2 = notes.end();
  290. it2--;
  291. this->note = *it2;
  292. retriggered = true;
  293. }
  294. }
  295. void MIDIToCVInterface::processMidi(std::vector<unsigned char> msg) {
  296. int channel = msg[0] & 0xf;
  297. int status = (msg[0] >> 4) & 0xf;
  298. int data1 = msg[1];
  299. int data2 = msg[2];
  300. //fprintf(stderr, "channel %d status %d data1 %d data2 %d\n", channel, status, data1,data2);
  301. // Filter channels
  302. if (this->channel >= 0 && this->channel != channel)
  303. return;
  304. switch (status) {
  305. // note off
  306. case 0x8: {
  307. releaseNote(data1);
  308. }
  309. break;
  310. case 0x9: // note on
  311. if (data2 > 0) {
  312. pressNote(data1);
  313. this->vel = data2;
  314. } else {
  315. // For some reason, some keyboards send a "note on" event with a velocity of 0 to signal that the key has been released.
  316. releaseNote(data1);
  317. }
  318. break;
  319. case 0xb: // cc
  320. switch (data1) {
  321. case 0x01: // mod
  322. this->mod = data2;
  323. break;
  324. case 0x40: // sustain
  325. pedal = (data2 >= 64);
  326. releaseNote(-1);
  327. break;
  328. }
  329. break;
  330. case 0xe: // pitch wheel
  331. this->pitchWheel = data2;
  332. break;
  333. case 0xd: // channel aftertouch
  334. this->afterTouch = data1;
  335. break;
  336. }
  337. }
  338. MidiToCVWidget::MidiToCVWidget() {
  339. MIDIToCVInterface *module = new MIDIToCVInterface();
  340. setModule(module);
  341. box.size = Vec(15 * 9, 380);
  342. {
  343. Panel *panel = new LightPanel();
  344. panel->box.size = box.size;
  345. addChild(panel);
  346. }
  347. float margin = 5;
  348. float labelHeight = 15;
  349. float yPos = margin;
  350. float yGap = 35;
  351. addChild(createScrew<ScrewSilver>(Vec(margin, 0)));
  352. addChild(createScrew<ScrewSilver>(Vec(box.size.x - 15 - margin, 0)));
  353. addChild(createScrew<ScrewSilver>(Vec(margin, 365)));
  354. addChild(createScrew<ScrewSilver>(Vec(box.size.x - 15 - margin, 365)));
  355. {
  356. Label *label = new Label();
  357. label->box.pos = Vec(box.size.x - margin - 7 * 15, margin);
  358. label->text = "MIDI to CV";
  359. addChild(label);
  360. yPos = labelHeight * 2;
  361. }
  362. addParam(createParam<LEDButton>(Vec(7 * 15, labelHeight), module, MIDIToCVInterface::RESET_PARAM, 0.0, 1.0, 0.0));
  363. addChild(createValueLight<SmallLight<RedValueLight>>(Vec(7*15+5, labelHeight+5), &module->resetLight));
  364. {
  365. Label *label = new Label();
  366. label->box.pos = Vec(margin, yPos);
  367. label->text = "MIDI Interface";
  368. addChild(label);
  369. yPos += labelHeight + margin;
  370. MidiChoice *midiChoice = new MidiChoice();
  371. midiChoice->midiModule = dynamic_cast<MidiIO *>(module);
  372. midiChoice->box.pos = Vec(margin, yPos);
  373. midiChoice->box.size.x = box.size.x - 10;
  374. addChild(midiChoice);
  375. yPos += midiChoice->box.size.y + margin;
  376. }
  377. {
  378. Label *label = new Label();
  379. label->box.pos = Vec(margin, yPos);
  380. label->text = "Channel";
  381. addChild(label);
  382. yPos += labelHeight + margin;
  383. ChannelChoice *channelChoice = new ChannelChoice();
  384. channelChoice->midiModule = dynamic_cast<MidiIO *>(module);
  385. channelChoice->box.pos = Vec(margin, yPos);
  386. channelChoice->box.size.x = box.size.x - 10;
  387. addChild(channelChoice);
  388. yPos += channelChoice->box.size.y + margin + 15;
  389. }
  390. std::string labels[MIDIToCVInterface::NUM_OUTPUTS] = {"1V/oct", "Gate", "Velocity", "Mod Wheel",
  391. "Pitch Wheel", "Aftertouch"};
  392. for (int i = 0; i < MIDIToCVInterface::NUM_OUTPUTS; i++) {
  393. Label *label = new Label();
  394. label->box.pos = Vec(margin, yPos);
  395. label->text = labels[i];
  396. addChild(label);
  397. addOutput(createOutput<PJ3410Port>(Vec(15 * 6, yPos - 5), module, i));
  398. if (i != MIDIToCVInterface::PITCH_OUTPUT) {
  399. addChild(createValueLight<SmallLight<GreenValueLight>>(Vec(15 * 7.5, yPos - 5), &module->lights[i]));
  400. }
  401. yPos += yGap + margin;
  402. }
  403. }
  404. void MidiToCVWidget::step() {
  405. // Assume QWERTY
  406. #define MIDI_KEY(key, midi) if (glfwGetKey(gWindow, key)) printf("%d\n", midi);
  407. // MIDI_KEY(GLFW_KEY_Z, 48);
  408. ModuleWidget::step();
  409. }
  410. /*
  411. * MIDIToCVInterface converts midi note on/off events, velocity , channel aftertouch, pitch wheel and mod weel to
  412. * CV
  413. */
  414. struct MIDICCToCVInterface : MidiIO, Module {
  415. enum ParamIds {
  416. NUM_PARAMS
  417. };
  418. enum InputIds {
  419. NUM_INPUTS
  420. };
  421. enum OutputIds {
  422. NUM_OUTPUTS = 16
  423. };
  424. int cc[NUM_OUTPUTS];
  425. int ccNum[NUM_OUTPUTS];
  426. bool ccNumInited[NUM_OUTPUTS];
  427. float lights[NUM_OUTPUTS];
  428. MIDICCToCVInterface() : MidiIO(), Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {
  429. for (int i = 0; i < NUM_OUTPUTS; i++) {
  430. cc[i] = 0;
  431. ccNum[i] = i;
  432. }
  433. }
  434. ~MIDICCToCVInterface() {
  435. setPortId(-1);
  436. }
  437. void step();
  438. void processMidi(std::vector<unsigned char> msg);
  439. virtual void resetMidi();
  440. virtual json_t *toJson() {
  441. json_t *rootJ = json_object();
  442. addBaseJson(rootJ);
  443. for (int i = 0; i < NUM_OUTPUTS; i++) {
  444. json_object_set_new(rootJ, std::to_string(i).c_str(), json_integer(ccNum[i]));
  445. }
  446. return rootJ;
  447. }
  448. virtual void fromJson(json_t *rootJ) {
  449. baseFromJson(rootJ);
  450. for (int i = 0; i < NUM_OUTPUTS; i++) {
  451. json_t *ccNumJ = json_object_get(rootJ, std::to_string(i).c_str());
  452. if (ccNumJ) {
  453. ccNum[i] = json_integer_value(ccNumJ);
  454. ccNumInited[i] = true;
  455. }
  456. }
  457. }
  458. virtual void initialize() {
  459. setPortId(-1);
  460. }
  461. };
  462. void MIDICCToCVInterface::step() {
  463. if (rtMidi->isPortOpen()) {
  464. std::vector<unsigned char> message;
  465. // midiIn->getMessage returns empty vector if there are no messages in the queue
  466. dynamic_cast<RtMidiIn *>(rtMidi)->getMessage(&message);
  467. while (message.size() > 0) {
  468. processMidi(message);
  469. dynamic_cast<RtMidiIn *>(rtMidi)->getMessage(&message);
  470. }
  471. }
  472. for (int i = 0; i < NUM_OUTPUTS; i++) {
  473. outputs[i].value = cc[i] / 127.0 * 10.0;
  474. lights[i] = 2.0 * outputs[i].value / 10.0 - 1.0;
  475. }
  476. }
  477. void MIDICCToCVInterface::resetMidi() {
  478. for (int i =0 ; i< NUM_OUTPUTS; i++){
  479. cc[i] = 0;
  480. }
  481. };
  482. void MIDICCToCVInterface::processMidi(std::vector<unsigned char> msg) {
  483. int channel = msg[0] & 0xf;
  484. int status = (msg[0] >> 4) & 0xf;
  485. int data1 = msg[1];
  486. int data2 = msg[2];
  487. //fprintf(stderr, "channel %d status %d data1 %d data2 %d\n", channel, status, data1,data2);
  488. // Filter channels
  489. if (this->channel >= 0 && this->channel != channel)
  490. return;
  491. if (status == 0xb) {
  492. for (int i = 0; i < NUM_OUTPUTS; i++) {
  493. if (data1 == ccNum[i]) {
  494. this->cc[i] = data2;
  495. }
  496. }
  497. }
  498. }
  499. struct CCTextField : TextField {
  500. void draw(NVGcontext *vg);
  501. int *ccNum;
  502. bool *inited;
  503. };
  504. void CCTextField::draw(NVGcontext *vg) {
  505. // Note: this might not be the best way to do this.
  506. // Text field should have a virtual "onTextChange" function or something.
  507. // draw() is triggered way more frequently
  508. if (text.size() > 0) {
  509. if (*inited) {
  510. *inited = false;
  511. text = std::to_string(*ccNum);
  512. }
  513. try {
  514. *ccNum = std::stoi(text, NULL, 10);
  515. // Only allow valid cc numbers
  516. if (*ccNum < 0 || *ccNum > 127) {
  517. text = "";
  518. begin = 0;
  519. end = text.size();
  520. }
  521. } catch (...) {
  522. text = "";
  523. begin = 0;
  524. end = text.size();
  525. }
  526. };
  527. TextField::draw(vg);
  528. }
  529. MIDICCToCVWidget::MIDICCToCVWidget() {
  530. MIDICCToCVInterface *module = new MIDICCToCVInterface();
  531. setModule(module);
  532. box.size = Vec(16 * 15, 380);
  533. {
  534. Panel *panel = new LightPanel();
  535. panel->box.size = box.size;
  536. addChild(panel);
  537. }
  538. float margin = 5;
  539. float labelHeight = 15;
  540. float yPos = margin;
  541. addChild(createScrew<ScrewSilver>(Vec(margin, 0)));
  542. addChild(createScrew<ScrewSilver>(Vec(box.size.x - 15 - margin, 0)));
  543. addChild(createScrew<ScrewSilver>(Vec(margin, 365)));
  544. addChild(createScrew<ScrewSilver>(Vec(box.size.x - 15 - margin, 365)));
  545. {
  546. Label *label = new Label();
  547. label->box.pos = Vec(box.size.x - margin - 11 * 15, margin);
  548. label->text = "MIDI CC to CV";
  549. addChild(label);
  550. yPos = labelHeight * 2;
  551. }
  552. {
  553. Label *label = new Label();
  554. label->box.pos = Vec(margin, yPos);
  555. label->text = "MIDI Interface";
  556. addChild(label);
  557. MidiChoice *midiChoice = new MidiChoice();
  558. midiChoice->midiModule = dynamic_cast<MidiIO *>(module);
  559. midiChoice->box.pos = Vec((box.size.x - 10) / 2 + margin, yPos);
  560. midiChoice->box.size.x = (box.size.x / 2.0) - margin;
  561. addChild(midiChoice);
  562. yPos += midiChoice->box.size.y + margin;
  563. }
  564. {
  565. Label *label = new Label();
  566. label->box.pos = Vec(margin, yPos);
  567. label->text = "Channel";
  568. addChild(label);
  569. ChannelChoice *channelChoice = new ChannelChoice();
  570. channelChoice->midiModule = dynamic_cast<MidiIO *>(module);
  571. channelChoice->box.pos = Vec((box.size.x - 10) / 2 + margin, yPos);
  572. channelChoice->box.size.x = (box.size.x / 2.0) - margin;
  573. addChild(channelChoice);
  574. yPos += channelChoice->box.size.y + margin * 3;
  575. }
  576. for (int i = 0; i < MIDICCToCVInterface::NUM_OUTPUTS; i++) {
  577. CCTextField *ccNumChoice = new CCTextField();
  578. ccNumChoice->ccNum = &module->ccNum[i];
  579. ccNumChoice->inited = &module->ccNumInited[i];
  580. ccNumChoice->text = std::to_string(module->ccNum[i]);
  581. ccNumChoice->box.pos = Vec(11 + (i % 4) * (63), yPos);
  582. ccNumChoice->box.size.x = 29;
  583. addChild(ccNumChoice);
  584. yPos += labelHeight + margin;
  585. addOutput(createOutput<PJ3410Port>(Vec((i % 4) * (63) + 10, yPos + 5), module, i));
  586. addChild(createValueLight<SmallLight<GreenValueLight>>(Vec((i % 4) * (63) + 32, yPos + 5), &module->lights[i]));
  587. if ((i + 1) % 4 == 0) {
  588. yPos += 47 + margin;
  589. } else {
  590. yPos -= labelHeight + margin;
  591. }
  592. }
  593. }
  594. void MIDICCToCVWidget::step() {
  595. // Assume QWERTY
  596. #define MIDI_KEY(key, midi) if (glfwGetKey(gWindow, key)) printf("%d\n", midi);
  597. // MIDI_KEY(GLFW_KEY_Z, 48);
  598. ModuleWidget::step();
  599. }