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.

205 lines
5.1KB

  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)
  26. return;
  27. if (!userData)
  28. return;
  29. RtMidiInputDevice *midiInputDevice = (RtMidiInputDevice*) userData;
  30. if (!midiInputDevice)
  31. return;
  32. // Users have reported that some MIDI devices can send messages >3 bytes. I don't know how this is possible, so just reject the message.
  33. if (message->size() > 3)
  34. return;
  35. midi::Message msg;
  36. msg.size = message->size();
  37. for (int i = 0; i < msg.size; i++) {
  38. msg.bytes[i] = (*message)[i];
  39. }
  40. midiInputDevice->onMessage(msg);
  41. }
  42. };
  43. struct RtMidiOutputDevice : midi::OutputDevice {
  44. RtMidiOut *rtMidiOut;
  45. RtMidiOutputDevice(int driverId, int deviceId) {
  46. rtMidiOut = new RtMidiOut((RtMidi::Api) driverId, "VCV Rack");
  47. assert(rtMidiOut);
  48. rtMidiOut->openPort(deviceId, "VCV Rack output");
  49. }
  50. ~RtMidiOutputDevice() {
  51. rtMidiOut->closePort();
  52. delete rtMidiOut;
  53. }
  54. void sendMessage(midi::Message message) override {
  55. rtMidiOut->sendMessage(message.bytes, message.size);
  56. }
  57. };
  58. struct RtMidiDriver : midi::Driver {
  59. int driverId;
  60. /** Just for querying MIDI driver information */
  61. RtMidiIn *rtMidiIn;
  62. RtMidiOut *rtMidiOut;
  63. std::map<int, RtMidiInputDevice*> inputDevices;
  64. std::map<int, RtMidiOutputDevice*> outputDevices;
  65. RtMidiDriver(int driverId) {
  66. this->driverId = driverId;
  67. rtMidiIn = new RtMidiIn((RtMidi::Api) driverId);
  68. assert(rtMidiIn);
  69. rtMidiOut = new RtMidiOut((RtMidi::Api) driverId);
  70. assert(rtMidiOut);
  71. }
  72. ~RtMidiDriver() {
  73. delete rtMidiIn;
  74. delete rtMidiOut;
  75. }
  76. std::string getName() override {
  77. switch (driverId) {
  78. case RtMidi::UNSPECIFIED: return "Unspecified";
  79. case RtMidi::MACOSX_CORE: return "Core MIDI";
  80. case RtMidi::LINUX_ALSA: return "ALSA";
  81. case RtMidi::UNIX_JACK: return "JACK";
  82. case RtMidi::WINDOWS_MM: return "Windows MIDI";
  83. case RtMidi::RTMIDI_DUMMY: return "Dummy MIDI";
  84. default: return "";
  85. }
  86. }
  87. std::vector<int> getInputDeviceIds() override {
  88. // TODO The IDs unfortunately jump around in RtMidi. Is there a way to keep them constant when a MIDI device is added/removed?
  89. int count = rtMidiIn->getPortCount();
  90. std::vector<int> deviceIds;
  91. for (int i = 0; i < count; i++)
  92. deviceIds.push_back(i);
  93. return deviceIds;
  94. }
  95. std::string getInputDeviceName(int deviceId) override {
  96. if (deviceId >= 0) {
  97. return rtMidiIn->getPortName(deviceId);
  98. }
  99. return "";
  100. }
  101. midi::InputDevice *subscribeInput(int deviceId, midi::Input *input) override {
  102. if (!(0 <= deviceId && deviceId < (int) rtMidiIn->getPortCount()))
  103. return NULL;
  104. RtMidiInputDevice *device = inputDevices[deviceId];
  105. if (!device) {
  106. inputDevices[deviceId] = device = new RtMidiInputDevice(driverId, deviceId);
  107. }
  108. device->subscribe(input);
  109. return device;
  110. }
  111. void unsubscribeInput(int deviceId, midi::Input *input) override {
  112. auto it = inputDevices.find(deviceId);
  113. if (it == inputDevices.end())
  114. return;
  115. RtMidiInputDevice *device = it->second;
  116. device->unsubscribe(input);
  117. // Destroy device if nothing is subscribed anymore
  118. if (device->subscribed.empty()) {
  119. inputDevices.erase(it);
  120. delete device;
  121. }
  122. }
  123. std::vector<int> getOutputDeviceIds() override {
  124. int count = rtMidiOut->getPortCount();
  125. std::vector<int> deviceIds;
  126. for (int i = 0; i < count; i++)
  127. deviceIds.push_back(i);
  128. return deviceIds;
  129. }
  130. std::string getOutputDeviceName(int deviceId) override {
  131. if (deviceId >= 0) {
  132. return rtMidiOut->getPortName(deviceId);
  133. }
  134. return "";
  135. }
  136. midi::OutputDevice *subscribeOutput(int deviceId, midi::Output *output) override {
  137. if (!(0 <= deviceId && deviceId < (int) rtMidiOut->getPortCount()))
  138. return NULL;
  139. RtMidiOutputDevice *device = outputDevices[deviceId];
  140. if (!device) {
  141. outputDevices[deviceId] = device = new RtMidiOutputDevice(driverId, deviceId);
  142. }
  143. device->subscribe(output);
  144. return device;
  145. }
  146. void unsubscribeOutput(int deviceId, midi::Output *output) override {
  147. auto it = outputDevices.find(deviceId);
  148. if (it == outputDevices.end())
  149. return;
  150. RtMidiOutputDevice *device = it->second;
  151. device->unsubscribe(output);
  152. // Destroy device if nothing is subscribed anymore
  153. if (device->subscribed.empty()) {
  154. outputDevices.erase(it);
  155. delete device;
  156. }
  157. }
  158. };
  159. void rtmidiInit() {
  160. std::vector<RtMidi::Api> rtApis;
  161. RtMidi::getCompiledApi(rtApis);
  162. for (RtMidi::Api api : rtApis) {
  163. int driverId = (int) api;
  164. midi::Driver *driver = new RtMidiDriver(driverId);
  165. midi::addDriver(driverId, driver);
  166. }
  167. }
  168. } // namespace rack