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.

397 lines
9.0KB

  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. struct MidiInterface : Module {
  10. enum ParamIds {
  11. NUM_PARAMS
  12. };
  13. enum InputIds {
  14. NUM_INPUTS
  15. };
  16. enum OutputIds {
  17. PITCH_OUTPUT,
  18. GATE_OUTPUT,
  19. MOD_OUTPUT,
  20. PITCHWHEEL_OUTPUT,
  21. NUM_OUTPUTS
  22. };
  23. int portId = -1;
  24. RtMidiIn *midiIn = NULL;
  25. std::list<int> notes;
  26. /** Filter MIDI channel
  27. -1 means all MIDI channels
  28. */
  29. int channel = -1;
  30. bool pedal = false;
  31. int note = 60; // C4, most modules should use 261.626 Hz
  32. int mod = 0;
  33. int pitchWheel = 64;
  34. bool retrigger = false;
  35. bool retriggered = false;
  36. MidiInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {
  37. try {
  38. midiIn = new RtMidiIn(RtMidi::UNSPECIFIED, "VCVRack");
  39. }
  40. catch ( RtMidiError &error ) {
  41. fprintf(stderr, "Failed to create RtMidiIn: %s\n", error.getMessage().c_str());
  42. }
  43. }
  44. ~MidiInterface() {
  45. setPortId(-1);
  46. }
  47. void step();
  48. int getPortCount();
  49. std::string getPortName(int portId);
  50. // -1 will close the port
  51. void setPortId(int portId);
  52. void setChannel(int channel) {
  53. this->channel = channel;
  54. }
  55. void pressNote(int note);
  56. void releaseNote(int note);
  57. void processMidi(std::vector<unsigned char> msg);
  58. json_t *toJson() {
  59. json_t *rootJ = json_object();
  60. if (portId >= 0) {
  61. std::string portName = getPortName(portId);
  62. json_object_set_new(rootJ, "portName", json_string(portName.c_str()));
  63. json_object_set_new(rootJ, "channel", json_integer(channel));
  64. }
  65. return rootJ;
  66. }
  67. void fromJson(json_t *rootJ) {
  68. json_t *portNameJ = json_object_get(rootJ, "portName");
  69. if (portNameJ) {
  70. std::string portName = json_string_value(portNameJ);
  71. for (int i = 0; i < getPortCount(); i++) {
  72. if (portName == getPortName(i)) {
  73. setPortId(i);
  74. break;
  75. }
  76. }
  77. }
  78. json_t *channelJ = json_object_get(rootJ, "channel");
  79. if (channelJ) {
  80. setChannel(json_integer_value(channelJ));
  81. }
  82. }
  83. void initialize() {
  84. setPortId(-1);
  85. }
  86. };
  87. void MidiInterface::step() {
  88. if (midiIn->isPortOpen()) {
  89. std::vector<unsigned char> message;
  90. // midiIn->getMessage returns empty vector if there are no messages in the queue
  91. double stamp = midiIn->getMessage( &message );
  92. while (message.size() > 0) {
  93. processMidi(message);
  94. stamp = midiIn->getMessage( &message );
  95. }
  96. }
  97. outputs[PITCH_OUTPUT].value = ((note - 60)) / 12.0;
  98. bool gate = pedal || !notes.empty();
  99. if (retrigger && retriggered) {
  100. gate = false;
  101. retriggered = false;
  102. }
  103. outputs[GATE_OUTPUT].value = gate ? 10.0 : 0.0;
  104. outputs[MOD_OUTPUT].value = mod / 127.0 * 10.0;
  105. outputs[PITCHWHEEL_OUTPUT].value = (pitchWheel - 64) / 64.0 * 10.0;
  106. }
  107. int MidiInterface::getPortCount() {
  108. return midiIn->getPortCount();
  109. }
  110. std::string MidiInterface::getPortName(int portId) {
  111. std::string portName;
  112. try {
  113. portName = midiIn->getPortName(portId);
  114. }
  115. catch ( RtMidiError &error ) {
  116. fprintf(stderr, "Failed to get Port Name: %d, %s\n", portId, error.getMessage().c_str());
  117. }
  118. return portName;
  119. }
  120. void MidiInterface::setPortId(int portId) {
  121. // Close port if it was previously opened
  122. if (midiIn->isPortOpen()) {
  123. midiIn->closePort();
  124. }
  125. this->portId = -1;
  126. // Open new port
  127. if (portId >= 0) {
  128. midiIn->openPort(portId, "Midi Interface");
  129. }
  130. this->portId = portId;
  131. }
  132. void MidiInterface::pressNote(int note) {
  133. // Remove existing similar note
  134. auto it = std::find(notes.begin(), notes.end(), note);
  135. if (it != notes.end())
  136. notes.erase(it);
  137. // Push note
  138. notes.push_back(note);
  139. this->note = note;
  140. retriggered = true;
  141. }
  142. void MidiInterface::releaseNote(int note) {
  143. // Remove the note
  144. auto it = std::find(notes.begin(), notes.end(), note);
  145. if (it != notes.end())
  146. notes.erase(it);
  147. if (pedal) {
  148. // Don't release if pedal is held
  149. }
  150. else if (!notes.empty()) {
  151. // Play previous note
  152. auto it2 = notes.end();
  153. it2--;
  154. this->note = *it2;
  155. retriggered = true;
  156. }
  157. }
  158. void MidiInterface::processMidi(std::vector<unsigned char> msg) {
  159. int channel = msg[0] & 0xf;
  160. int status = (msg[0] >> 4) & 0xf;
  161. int data1 = msg[1];
  162. int data2 = msg[2];
  163. //fprintf(stderr, "channel %d status %d data1 %d data2 %d\n", channel, status, data1,data2);
  164. // Filter channels
  165. if (this->channel >= 0 && this->channel != channel)
  166. return;
  167. switch (status) {
  168. // note off
  169. case 0x8: {
  170. releaseNote(data1);
  171. } break;
  172. case 0x9: // note on
  173. if (data2 > 0) {
  174. pressNote(data1);
  175. }
  176. else {
  177. // For some reason, some keyboards send a "note on" event with a velocity of 0 to signal that the key has been released.
  178. releaseNote(data1);
  179. }
  180. break;
  181. case 0xb: // cc
  182. switch (data1) {
  183. case 0x01: // mod
  184. this->mod = data2;
  185. break;
  186. case 0x40: // sustain
  187. pedal = (data2 >= 64);
  188. releaseNote(-1);
  189. break;
  190. }
  191. break;
  192. case 0xe: // pitch wheel
  193. this->pitchWheel = data2;
  194. break;
  195. }
  196. }
  197. struct MidiItem : MenuItem {
  198. MidiInterface *midiInterface;
  199. int portId;
  200. void onAction() {
  201. midiInterface->setPortId(portId);
  202. }
  203. };
  204. struct MidiChoice : ChoiceButton {
  205. MidiInterface *midiInterface;
  206. void onAction() {
  207. Menu *menu = gScene->createMenu();
  208. menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y));
  209. menu->box.size.x = box.size.x;
  210. int portCount = midiInterface->getPortCount();
  211. {
  212. MidiItem *midiItem = new MidiItem();
  213. midiItem->midiInterface = midiInterface;
  214. midiItem->portId = -1;
  215. midiItem->text = "No device";
  216. menu->pushChild(midiItem);
  217. }
  218. for (int portId = 0; portId < portCount; portId++) {
  219. MidiItem *midiItem = new MidiItem();
  220. midiItem->midiInterface = midiInterface;
  221. midiItem->portId = portId;
  222. midiItem->text = midiInterface->getPortName(portId);
  223. menu->pushChild(midiItem);
  224. }
  225. }
  226. void step() {
  227. std::string name = midiInterface->getPortName(midiInterface->portId);
  228. text = ellipsize(name, 8);
  229. }
  230. };
  231. struct ChannelItem : MenuItem {
  232. MidiInterface *midiInterface;
  233. int channel;
  234. void onAction() {
  235. midiInterface->setChannel(channel);
  236. }
  237. };
  238. struct ChannelChoice : ChoiceButton {
  239. MidiInterface *midiInterface;
  240. void onAction() {
  241. Menu *menu = gScene->createMenu();
  242. menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y));
  243. menu->box.size.x = box.size.x;
  244. {
  245. ChannelItem *channelItem = new ChannelItem();
  246. channelItem->midiInterface = midiInterface;
  247. channelItem->channel = -1;
  248. channelItem->text = "All";
  249. menu->pushChild(channelItem);
  250. }
  251. for (int channel = 0; channel < 16; channel++) {
  252. ChannelItem *channelItem = new ChannelItem();
  253. channelItem->midiInterface = midiInterface;
  254. channelItem->channel = channel;
  255. channelItem->text = stringf("%d", channel + 1);
  256. menu->pushChild(channelItem);
  257. }
  258. }
  259. void step() {
  260. text = (midiInterface->channel >= 0) ? stringf("%d", midiInterface->channel + 1) : "All";
  261. }
  262. };
  263. MidiInterfaceWidget::MidiInterfaceWidget() {
  264. MidiInterface *module = new MidiInterface();
  265. setModule(module);
  266. box.size = Vec(15 * 6, 380);
  267. {
  268. Panel *panel = new LightPanel();
  269. panel->box.size = box.size;
  270. addChild(panel);
  271. }
  272. float margin = 5;
  273. float labelHeight = 15;
  274. float yPos = margin;
  275. {
  276. Label *label = new Label();
  277. label->box.pos = Vec(margin, yPos);
  278. label->text = "MIDI device";
  279. addChild(label);
  280. yPos += labelHeight + margin;
  281. MidiChoice *midiChoice = new MidiChoice();
  282. midiChoice->midiInterface = dynamic_cast<MidiInterface*>(module);
  283. midiChoice->box.pos = Vec(margin, yPos);
  284. midiChoice->box.size.x = box.size.x - 10;
  285. addChild(midiChoice);
  286. yPos += midiChoice->box.size.y + margin;
  287. }
  288. {
  289. Label *label = new Label();
  290. label->box.pos = Vec(margin, yPos);
  291. label->text = "Channel";
  292. addChild(label);
  293. yPos += labelHeight + margin;
  294. ChannelChoice *channelChoice = new ChannelChoice();
  295. channelChoice->midiInterface = dynamic_cast<MidiInterface*>(module);
  296. channelChoice->box.pos = Vec(margin, yPos);
  297. channelChoice->box.size.x = box.size.x - 10;
  298. addChild(channelChoice);
  299. yPos += channelChoice->box.size.y + margin;
  300. }
  301. {
  302. Label *label = new Label();
  303. label->box.pos = Vec(margin, yPos);
  304. label->text = "1V/oct";
  305. addChild(label);
  306. yPos += labelHeight + margin;
  307. addOutput(createOutput<PJ3410Port>(Vec(28, yPos), module, MidiInterface::PITCH_OUTPUT));
  308. yPos += 37 + margin;
  309. }
  310. {
  311. Label *label = new Label();
  312. label->box.pos = Vec(margin, yPos);
  313. label->text = "Gate";
  314. addChild(label);
  315. yPos += labelHeight + margin;
  316. addOutput(createOutput<PJ3410Port>(Vec(28, yPos), module, MidiInterface::GATE_OUTPUT));
  317. yPos += 37 + margin;
  318. }
  319. {
  320. Label *label = new Label();
  321. label->box.pos = Vec(margin, yPos);
  322. label->text = "Mod Wheel";
  323. addChild(label);
  324. yPos += labelHeight + margin;
  325. addOutput(createOutput<PJ3410Port>(Vec(28, yPos), module, MidiInterface::MOD_OUTPUT));
  326. yPos += 37 + margin;
  327. }
  328. {
  329. Label *label = new Label();
  330. label->box.pos = Vec(margin, yPos);
  331. label->text = "Pitch Wheel";
  332. addChild(label);
  333. yPos += labelHeight + margin;
  334. addOutput(createOutput<PJ3410Port>(Vec(28, yPos), module, MidiInterface::PITCHWHEEL_OUTPUT));
  335. yPos += 37 + margin;
  336. }
  337. }
  338. void MidiInterfaceWidget::step() {
  339. // Assume QWERTY
  340. #define MIDI_KEY(key, midi) if (glfwGetKey(gWindow, key)) printf("%d\n", midi);
  341. // MIDI_KEY(GLFW_KEY_Z, 48);
  342. ModuleWidget::step();
  343. }