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
4.9KB

  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. assert(message->size() <= 3);
  33. midi::Message msg;
  34. msg.size = message->size();
  35. for (int i = 0; i < msg.size; i++) {
  36. msg.bytes[i] = (*message)[i];
  37. }
  38. midiInputDevice->onMessage(msg);
  39. }
  40. };
  41. struct RtMidiOutputDevice : midi::OutputDevice {
  42. RtMidiOut *rtMidiOut;
  43. RtMidiOutputDevice(int driverId, int deviceId) {
  44. rtMidiOut = new RtMidiOut((RtMidi::Api) driverId, "VCV Rack");
  45. assert(rtMidiOut);
  46. rtMidiOut->openPort(deviceId, "VCV Rack output");
  47. }
  48. ~RtMidiOutputDevice() {
  49. rtMidiOut->closePort();
  50. delete rtMidiOut;
  51. }
  52. void sendMessage(midi::Message message) override {
  53. rtMidiOut->sendMessage(message.bytes, message.size);
  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