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.

202 lines
5.0KB

  1. #include "rtmidi.hpp"
  2. #include "midi.hpp"
  3. #include <map>
  4. #pragma GCC diagnostic push
  5. #ifndef __clang__
  6. #pragma GCC diagnostic ignored "-Wsuggest-override"
  7. #endif
  8. #include <rtmidi/RtMidi.h>
  9. #pragma GCC diagnostic pop
  10. namespace rack {
  11. struct RtMidiInputDevice : midi::InputDevice {
  12. RtMidiIn *rtMidiIn;
  13. RtMidiInputDevice(int driverId, int deviceId) {
  14. rtMidiIn = new RtMidiIn((RtMidi::Api) driverId, "VCV Rack");
  15. assert(rtMidiIn);
  16. rtMidiIn->ignoreTypes(false, false, false);
  17. rtMidiIn->setCallback(midiInputCallback, this);
  18. rtMidiIn->openPort(deviceId, "VCV Rack input");
  19. }
  20. ~RtMidiInputDevice() {
  21. rtMidiIn->closePort();
  22. delete rtMidiIn;
  23. }
  24. static void midiInputCallback(double timeStamp, std::vector<unsigned char> *message, void *userData) {
  25. if (!message) return;
  26. if (!userData) return;
  27. RtMidiInputDevice *midiInputDevice = (RtMidiInputDevice*) userData;
  28. if (!midiInputDevice) return;
  29. if (message->size() < 3) return;
  30. midi::Message msg;
  31. msg.cmd = (*message)[0];
  32. msg.data1 = (*message)[1];
  33. msg.data2 = (*message)[2];
  34. midiInputDevice->onMessage(msg);
  35. }
  36. };
  37. struct RtMidiOutputDevice : midi::OutputDevice {
  38. RtMidiOut *rtMidiOut;
  39. RtMidiOutputDevice(int driverId, int deviceId) {
  40. rtMidiOut = new RtMidiOut((RtMidi::Api) driverId, "VCV Rack");
  41. assert(rtMidiOut);
  42. rtMidiOut->openPort(deviceId, "VCV Rack output");
  43. }
  44. ~RtMidiOutputDevice() {
  45. rtMidiOut->closePort();
  46. delete rtMidiOut;
  47. }
  48. void sendMessage(midi::Message message) override {
  49. unsigned char msg[3];
  50. msg[0] = message.cmd;
  51. msg[1] = message.data1;
  52. msg[2] = message.data2;
  53. rtMidiOut->sendMessage(msg, 3);
  54. }
  55. };
  56. struct RtMidiDriver : midi::Driver {
  57. int driverId;
  58. /** Just for querying MIDI driver information */
  59. RtMidiIn *rtMidiIn;
  60. RtMidiOut *rtMidiOut;
  61. std::map<int, RtMidiInputDevice*> inputDevices;
  62. std::map<int, RtMidiOutputDevice*> outputDevices;
  63. RtMidiDriver(int driverId) {
  64. this->driverId = driverId;
  65. rtMidiIn = new RtMidiIn((RtMidi::Api) driverId);
  66. assert(rtMidiIn);
  67. rtMidiOut = new RtMidiOut((RtMidi::Api) driverId);
  68. assert(rtMidiOut);
  69. }
  70. ~RtMidiDriver() {
  71. delete rtMidiIn;
  72. delete rtMidiOut;
  73. }
  74. std::string getName() override {
  75. switch (driverId) {
  76. case RtMidi::UNSPECIFIED: return "Unspecified";
  77. case RtMidi::MACOSX_CORE: return "Core MIDI";
  78. case RtMidi::LINUX_ALSA: return "ALSA";
  79. case RtMidi::UNIX_JACK: return "JACK";
  80. case RtMidi::WINDOWS_MM: return "Windows MIDI";
  81. case RtMidi::RTMIDI_DUMMY: return "Dummy MIDI";
  82. default: return "";
  83. }
  84. }
  85. std::vector<int> getInputDeviceIds() override {
  86. // TODO The IDs unfortunately jump around in RtMidi. Is there a way to keep them constant when a MIDI device is added/removed?
  87. int count = rtMidiIn->getPortCount();
  88. std::vector<int> deviceIds;
  89. for (int i = 0; i < count; i++)
  90. deviceIds.push_back(i);
  91. return deviceIds;
  92. }
  93. std::string getInputDeviceName(int deviceId) override {
  94. if (deviceId >= 0) {
  95. return rtMidiIn->getPortName(deviceId);
  96. }
  97. return "";
  98. }
  99. midi::InputDevice *subscribeInput(int deviceId, midi::Input *input) override {
  100. if (!(0 <= deviceId && deviceId < (int) rtMidiIn->getPortCount()))
  101. return NULL;
  102. RtMidiInputDevice *device = inputDevices[deviceId];
  103. if (!device) {
  104. inputDevices[deviceId] = device = new RtMidiInputDevice(driverId, deviceId);
  105. }
  106. device->subscribe(input);
  107. return device;
  108. }
  109. void unsubscribeInput(int deviceId, midi::Input *input) override {
  110. auto it = inputDevices.find(deviceId);
  111. if (it == inputDevices.end())
  112. return;
  113. RtMidiInputDevice *device = it->second;
  114. device->unsubscribe(input);
  115. // Destroy device if nothing is subscribed anymore
  116. if (device->subscribed.empty()) {
  117. inputDevices.erase(it);
  118. delete device;
  119. }
  120. }
  121. std::vector<int> getOutputDeviceIds() override {
  122. int count = rtMidiOut->getPortCount();
  123. std::vector<int> deviceIds;
  124. for (int i = 0; i < count; i++)
  125. deviceIds.push_back(i);
  126. return deviceIds;
  127. }
  128. std::string getOutputDeviceName(int deviceId) override {
  129. if (deviceId >= 0) {
  130. return rtMidiOut->getPortName(deviceId);
  131. }
  132. return "";
  133. }
  134. midi::OutputDevice *subscribeOutput(int deviceId, midi::Output *output) override {
  135. if (!(0 <= deviceId && deviceId < (int) rtMidiOut->getPortCount()))
  136. return NULL;
  137. RtMidiOutputDevice *device = outputDevices[deviceId];
  138. if (!device) {
  139. outputDevices[deviceId] = device = new RtMidiOutputDevice(driverId, deviceId);
  140. }
  141. device->subscribe(output);
  142. return device;
  143. }
  144. void unsubscribeOutput(int deviceId, midi::Output *output) override {
  145. auto it = outputDevices.find(deviceId);
  146. if (it == outputDevices.end())
  147. return;
  148. RtMidiOutputDevice *device = it->second;
  149. device->unsubscribe(output);
  150. // Destroy device if nothing is subscribed anymore
  151. if (device->subscribed.empty()) {
  152. outputDevices.erase(it);
  153. delete device;
  154. }
  155. }
  156. };
  157. void rtmidiInit() {
  158. std::vector<RtMidi::Api> rtApis;
  159. RtMidi::getCompiledApi(rtApis);
  160. for (RtMidi::Api api : rtApis) {
  161. int driverId = (int) api;
  162. midi::Driver *driver = new RtMidiDriver(driverId);
  163. midi::addDriver(driverId, driver);
  164. }
  165. }
  166. } // namespace rack