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.

405 lines
10KB

  1. #include <vector>
  2. #include <map>
  3. #include <queue>
  4. #include <thread>
  5. #include <mutex>
  6. #include <condition_variable>
  7. #pragma GCC diagnostic push
  8. #ifndef __clang__
  9. #pragma GCC diagnostic ignored "-Wsuggest-override"
  10. #endif
  11. #include <rtmidi/RtMidi.h>
  12. #pragma GCC diagnostic pop
  13. #include <rtmidi.hpp>
  14. #include <midi.hpp>
  15. #include <string.hpp>
  16. #include <system.hpp>
  17. #include <context.hpp>
  18. #include <engine/Engine.hpp>
  19. namespace rack {
  20. static void rtMidiErrorCallback(RtMidiError::Type type, const std::string& errorText, void* userData) {
  21. // Do nothing
  22. }
  23. struct RtMidiInputDevice : midi::InputDevice {
  24. RtMidiIn* rtMidiIn;
  25. std::string name;
  26. RtMidiInputDevice(int driverId, int deviceId) {
  27. try {
  28. rtMidiIn = new RtMidiIn((RtMidi::Api) driverId, "VCV Rack");
  29. }
  30. catch (RtMidiError& e) {
  31. throw Exception("Failed to create RtMidi input driver %d: %s", driverId, e.what());
  32. }
  33. rtMidiIn->setErrorCallback(rtMidiErrorCallback);
  34. rtMidiIn->ignoreTypes(false, false, false);
  35. rtMidiIn->setCallback(midiInputCallback, this);
  36. try {
  37. name = rtMidiIn->getPortName(deviceId);
  38. }
  39. catch (RtMidiError& e) {
  40. throw Exception("Failed to get RtMidi input device name: %s", e.what());
  41. }
  42. try {
  43. rtMidiIn->openPort(deviceId, "VCV Rack input");
  44. }
  45. catch (RtMidiError& e) {
  46. throw Exception("Failed to open RtMidi input device: %s", e.what());
  47. }
  48. }
  49. ~RtMidiInputDevice() {
  50. // This does not throw for any driver API
  51. rtMidiIn->closePort();
  52. delete rtMidiIn;
  53. }
  54. std::string getName() override {
  55. return name;
  56. }
  57. static void midiInputCallback(double timeStamp, std::vector<unsigned char>* message, void* userData) {
  58. if (!message)
  59. return;
  60. if (!userData)
  61. return;
  62. RtMidiInputDevice* midiInputDevice = (RtMidiInputDevice*) userData;
  63. if (!midiInputDevice)
  64. return;
  65. midi::Message msg;
  66. msg.bytes = std::vector<uint8_t>(message->begin(), message->end());
  67. // Don't set msg.frame from timeStamp here, because it's set in onMessage().
  68. midiInputDevice->onMessage(msg);
  69. }
  70. };
  71. struct RtMidiOutputDevice : midi::OutputDevice {
  72. RtMidiOut* rtMidiOut;
  73. std::string name;
  74. struct MessageSchedule {
  75. midi::Message message;
  76. double timestamp;
  77. bool operator<(const MessageSchedule& other) const {
  78. return timestamp > other.timestamp;
  79. }
  80. };
  81. std::priority_queue<MessageSchedule, std::vector<MessageSchedule>> messageQueue;
  82. std::thread thread;
  83. std::mutex mutex;
  84. std::condition_variable cv;
  85. bool stopped = false;
  86. RtMidiOutputDevice(int driverId, int deviceId) {
  87. try {
  88. rtMidiOut = new RtMidiOut((RtMidi::Api) driverId, "VCV Rack");
  89. }
  90. catch (RtMidiError& e) {
  91. throw Exception("Failed to create RtMidi output driver %d: %s", driverId, e.what());
  92. }
  93. rtMidiOut->setErrorCallback(rtMidiErrorCallback);
  94. try {
  95. name = rtMidiOut->getPortName(deviceId);
  96. }
  97. catch (RtMidiError& e) {
  98. throw Exception("Failed to get RtMidi output device name: %s", e.what());
  99. }
  100. try {
  101. rtMidiOut->openPort(deviceId, "VCV Rack output");
  102. }
  103. catch (RtMidiError& e) {
  104. throw Exception("Failed to get RtMidi output device name: %s", e.what());
  105. }
  106. startThread();
  107. }
  108. ~RtMidiOutputDevice() {
  109. stopThread();
  110. // This does not throw for any driver API
  111. rtMidiOut->closePort();
  112. delete rtMidiOut;
  113. }
  114. std::string getName() override {
  115. return name;
  116. }
  117. void sendMessage(const midi::Message& message) override {
  118. // If frame is undefined, send message immediately
  119. if (message.getFrame() < 0) {
  120. sendMessageNow(message);
  121. return;
  122. }
  123. // Schedule message to be sent by worker thread
  124. MessageSchedule ms;
  125. ms.message = message;
  126. int64_t deltaFrames = message.getFrame() - APP->engine->getBlockFrame();
  127. // Delay message by current Engine block size
  128. deltaFrames += APP->engine->getBlockFrames();
  129. // Compute time in next Engine block to send message
  130. double deltaTime = deltaFrames * APP->engine->getSampleTime();
  131. ms.timestamp = APP->engine->getBlockTime() + deltaTime;
  132. std::lock_guard<decltype(mutex)> lock(mutex);
  133. messageQueue.push(ms);
  134. cv.notify_one();
  135. }
  136. // Consumer thread methods
  137. void startThread() {
  138. thread = std::thread(&RtMidiOutputDevice::runThread, this);
  139. }
  140. void runThread() {
  141. std::unique_lock<decltype(mutex)> lock(mutex);
  142. while (!stopped) {
  143. if (messageQueue.empty()) {
  144. // No messages. Wait on the CV to be notified.
  145. cv.wait(lock);
  146. }
  147. else {
  148. // Get earliest message
  149. const MessageSchedule& ms = messageQueue.top();
  150. double duration = ms.timestamp - system::getTime();
  151. // If we need to wait, release the lock and wait for the timeout, or if the CV is notified.
  152. // This correctly handles MIDI messages with no timestamp, because duration will be NAN.
  153. if (duration > 0) {
  154. if (cv.wait_for(lock, std::chrono::duration<double>(duration)) != std::cv_status::timeout)
  155. continue;
  156. }
  157. // Send and remove from queue
  158. sendMessageNow(ms.message);
  159. messageQueue.pop();
  160. }
  161. }
  162. }
  163. void sendMessageNow(const midi::Message& message) {
  164. try {
  165. rtMidiOut->sendMessage(message.bytes.data(), message.bytes.size());
  166. }
  167. catch (RtMidiError& e) {
  168. // Ignore error
  169. }
  170. }
  171. void stopThread() {
  172. {
  173. std::lock_guard<decltype(mutex)> lock(mutex);
  174. stopped = true;
  175. cv.notify_one();
  176. }
  177. thread.join();
  178. }
  179. };
  180. struct RtMidiDriver : midi::Driver {
  181. int driverId;
  182. /** Just for querying MIDI driver information */
  183. RtMidiIn* rtMidiIn;
  184. RtMidiOut* rtMidiOut;
  185. std::map<int, RtMidiInputDevice*> inputDevices;
  186. std::map<int, RtMidiOutputDevice*> outputDevices;
  187. RtMidiDriver(int driverId) {
  188. this->driverId = driverId;
  189. try {
  190. rtMidiIn = new RtMidiIn((RtMidi::Api) driverId);
  191. }
  192. catch (RtMidiError& e) {
  193. throw Exception("Failed to create RtMidi input driver %d: %s", driverId, e.what());
  194. }
  195. rtMidiIn->setErrorCallback(rtMidiErrorCallback);
  196. try {
  197. rtMidiOut = new RtMidiOut((RtMidi::Api) driverId);
  198. }
  199. catch (RtMidiError& e) {
  200. throw Exception("Failed to create RtMidi output driver %d: %s", driverId, e.what());
  201. }
  202. rtMidiOut->setErrorCallback(rtMidiErrorCallback);
  203. }
  204. ~RtMidiDriver() {
  205. assert(inputDevices.empty());
  206. assert(outputDevices.empty());
  207. // This does not throw for any driver API
  208. delete rtMidiIn;
  209. delete rtMidiOut;
  210. }
  211. std::string getName() override {
  212. switch (driverId) {
  213. case RtMidi::UNSPECIFIED: return "Unspecified";
  214. case RtMidi::MACOSX_CORE: return "Core MIDI";
  215. case RtMidi::LINUX_ALSA: return "ALSA";
  216. case RtMidi::UNIX_JACK: return "JACK";
  217. case RtMidi::WINDOWS_MM: return "Windows MIDI";
  218. case RtMidi::RTMIDI_DUMMY: return "Dummy MIDI";
  219. default: return "";
  220. }
  221. }
  222. std::vector<int> getInputDeviceIds() override {
  223. // TODO The IDs unfortunately jump around in RtMidi. Is there a way to keep them constant when a MIDI device is added/removed?
  224. int count;
  225. try {
  226. count = rtMidiIn->getPortCount();
  227. }
  228. catch (RtMidiError& e) {
  229. throw Exception("Failed to get RtMidi input device count: %s", e.what());
  230. }
  231. std::vector<int> deviceIds;
  232. for (int i = 0; i < count; i++)
  233. deviceIds.push_back(i);
  234. return deviceIds;
  235. }
  236. std::string getInputDeviceName(int deviceId) override {
  237. if (deviceId < 0)
  238. return "";
  239. try {
  240. return rtMidiIn->getPortName(deviceId);
  241. }
  242. catch (RtMidiError& e) {
  243. throw Exception("Failed to get RtMidi input device name: %s", e.what());
  244. }
  245. }
  246. midi::InputDevice* subscribeInput(int deviceId, midi::Input* input) override {
  247. if (!(0 <= deviceId && deviceId < (int) rtMidiIn->getPortCount()))
  248. return NULL;
  249. RtMidiInputDevice* device = get(inputDevices, deviceId, NULL);
  250. if (!device) {
  251. try {
  252. inputDevices[deviceId] = device = new RtMidiInputDevice(driverId, deviceId);
  253. }
  254. catch (RtMidiError& e) {
  255. throw Exception("Failed to create RtMidi input device: %s", e.what());
  256. }
  257. }
  258. device->subscribe(input);
  259. return device;
  260. }
  261. void unsubscribeInput(int deviceId, midi::Input* input) override {
  262. auto it = inputDevices.find(deviceId);
  263. if (it == inputDevices.end())
  264. return;
  265. RtMidiInputDevice* device = it->second;
  266. device->unsubscribe(input);
  267. // Destroy device if nothing is subscribed anymore
  268. if (device->subscribed.empty()) {
  269. inputDevices.erase(it);
  270. try {
  271. delete device;
  272. }
  273. catch (RtMidiError& e) {
  274. throw Exception("Failed to delete RtMidi input device: %s", e.what());
  275. }
  276. }
  277. }
  278. std::vector<int> getOutputDeviceIds() override {
  279. // TODO The IDs unfortunately jump around in RtMidi. Is there a way to keep them constant when a MIDI device is added/removed?
  280. int count;
  281. try {
  282. count = rtMidiOut->getPortCount();
  283. }
  284. catch (RtMidiError& e) {
  285. throw Exception("Failed to get RtMidi output device count: %s", e.what());
  286. }
  287. std::vector<int> deviceIds;
  288. for (int i = 0; i < count; i++)
  289. deviceIds.push_back(i);
  290. return deviceIds;
  291. }
  292. std::string getOutputDeviceName(int deviceId) override {
  293. if (deviceId < 0)
  294. return "";
  295. try {
  296. return rtMidiOut->getPortName(deviceId);
  297. }
  298. catch (RtMidiError& e) {
  299. throw Exception("Failed to get RtMidi output device count: %s", e.what());
  300. }
  301. }
  302. midi::OutputDevice* subscribeOutput(int deviceId, midi::Output* output) override {
  303. if (!(0 <= deviceId && deviceId < (int) rtMidiOut->getPortCount()))
  304. return NULL;
  305. RtMidiOutputDevice* device = get(outputDevices, deviceId, NULL);
  306. if (!device) {
  307. try {
  308. outputDevices[deviceId] = device = new RtMidiOutputDevice(driverId, deviceId);
  309. }
  310. catch (RtMidiError& e) {
  311. throw Exception("Failed to create RtMidi output device: %s", e.what());
  312. }
  313. }
  314. device->subscribe(output);
  315. return device;
  316. }
  317. void unsubscribeOutput(int deviceId, midi::Output* output) override {
  318. auto it = outputDevices.find(deviceId);
  319. if (it == outputDevices.end())
  320. return;
  321. RtMidiOutputDevice* device = it->second;
  322. device->unsubscribe(output);
  323. // Destroy device if nothing is subscribed anymore
  324. if (device->subscribed.empty()) {
  325. outputDevices.erase(it);
  326. try {
  327. delete device;
  328. }
  329. catch (RtMidiError& e) {
  330. throw Exception("Failed to delete RtMidi output device: %s", e.what());
  331. }
  332. }
  333. }
  334. };
  335. void rtmidiInit() {
  336. std::vector<RtMidi::Api> rtApis;
  337. RtMidi::getCompiledApi(rtApis);
  338. for (RtMidi::Api api : rtApis) {
  339. int driverId = (int) api;
  340. midi::Driver* driver = new RtMidiDriver(driverId);
  341. midi::addDriver(driverId, driver);
  342. }
  343. }
  344. } // namespace rack