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.

413 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* that = (RtMidiInputDevice*) userData;
  63. if (!that)
  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. that->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. system::setThreadName("RtMidi output");
  142. std::unique_lock<decltype(mutex)> lock(mutex);
  143. while (!stopped) {
  144. if (messageQueue.empty()) {
  145. // No messages. Wait on the CV to be notified.
  146. cv.wait(lock);
  147. }
  148. else {
  149. // Get earliest message
  150. const MessageSchedule& ms = messageQueue.top();
  151. double duration = ms.timestamp - system::getTime();
  152. // If we need to wait, release the lock and wait for the timeout, or if the CV is notified.
  153. // This correctly handles MIDI messages with no timestamp, because duration will be NAN.
  154. if (duration > 0) {
  155. if (cv.wait_for(lock, std::chrono::duration<double>(duration)) != std::cv_status::timeout)
  156. continue;
  157. }
  158. // Send and remove from queue
  159. sendMessageNow(ms.message);
  160. messageQueue.pop();
  161. }
  162. }
  163. }
  164. void sendMessageNow(const midi::Message& message) {
  165. try {
  166. rtMidiOut->sendMessage(message.bytes.data(), message.bytes.size());
  167. }
  168. catch (RtMidiError& e) {
  169. // Ignore error
  170. }
  171. }
  172. void stopThread() {
  173. {
  174. std::lock_guard<decltype(mutex)> lock(mutex);
  175. stopped = true;
  176. cv.notify_one();
  177. }
  178. if (thread.joinable())
  179. thread.join();
  180. }
  181. };
  182. struct RtMidiDriver : midi::Driver {
  183. int driverId;
  184. /** Just for querying MIDI driver information */
  185. RtMidiIn* rtMidiIn;
  186. RtMidiOut* rtMidiOut;
  187. std::map<int, RtMidiInputDevice*> inputDevices;
  188. std::map<int, RtMidiOutputDevice*> outputDevices;
  189. RtMidiDriver(int driverId) {
  190. this->driverId = driverId;
  191. try {
  192. rtMidiIn = new RtMidiIn((RtMidi::Api) driverId);
  193. }
  194. catch (RtMidiError& e) {
  195. throw Exception("Failed to create RtMidi input driver %d: %s", driverId, e.what());
  196. }
  197. rtMidiIn->setErrorCallback(rtMidiErrorCallback);
  198. try {
  199. rtMidiOut = new RtMidiOut((RtMidi::Api) driverId);
  200. }
  201. catch (RtMidiError& e) {
  202. throw Exception("Failed to create RtMidi output driver %d: %s", driverId, e.what());
  203. }
  204. rtMidiOut->setErrorCallback(rtMidiErrorCallback);
  205. }
  206. ~RtMidiDriver() {
  207. assert(inputDevices.empty());
  208. assert(outputDevices.empty());
  209. // This does not throw for any driver API
  210. delete rtMidiIn;
  211. delete rtMidiOut;
  212. }
  213. std::string getName() override {
  214. switch (driverId) {
  215. case RtMidi::UNSPECIFIED: return "Unspecified";
  216. case RtMidi::MACOSX_CORE: return "Core MIDI";
  217. case RtMidi::LINUX_ALSA: return "ALSA";
  218. case RtMidi::UNIX_JACK: return "JACK";
  219. case RtMidi::WINDOWS_MM: return "Windows MIDI";
  220. case RtMidi::RTMIDI_DUMMY: return "Dummy MIDI";
  221. default: return "";
  222. }
  223. }
  224. std::vector<int> getInputDeviceIds() override {
  225. // TODO The IDs unfortunately jump around in RtMidi. Is there a way to keep them constant when a MIDI device is added/removed?
  226. int count;
  227. try {
  228. count = rtMidiIn->getPortCount();
  229. }
  230. catch (RtMidiError& e) {
  231. throw Exception("Failed to get RtMidi input device count: %s", e.what());
  232. }
  233. std::vector<int> deviceIds;
  234. for (int i = 0; i < count; i++)
  235. deviceIds.push_back(i);
  236. return deviceIds;
  237. }
  238. std::string getInputDeviceName(int deviceId) override {
  239. if (deviceId < 0)
  240. return "";
  241. try {
  242. return rtMidiIn->getPortName(deviceId);
  243. }
  244. catch (RtMidiError& e) {
  245. throw Exception("Failed to get RtMidi input device name: %s", e.what());
  246. }
  247. }
  248. midi::InputDevice* subscribeInput(int deviceId, midi::Input* input) override {
  249. if (!(0 <= deviceId && deviceId < (int) rtMidiIn->getPortCount()))
  250. return NULL;
  251. RtMidiInputDevice* device = get(inputDevices, deviceId, NULL);
  252. if (!device) {
  253. try {
  254. inputDevices[deviceId] = device = new RtMidiInputDevice(driverId, deviceId);
  255. }
  256. catch (RtMidiError& e) {
  257. throw Exception("Failed to create RtMidi input device: %s", e.what());
  258. }
  259. }
  260. device->subscribe(input);
  261. return device;
  262. }
  263. void unsubscribeInput(int deviceId, midi::Input* input) override {
  264. auto it = inputDevices.find(deviceId);
  265. if (it == inputDevices.end())
  266. return;
  267. RtMidiInputDevice* device = it->second;
  268. device->unsubscribe(input);
  269. // Destroy device if nothing is subscribed anymore
  270. if (device->subscribed.empty()) {
  271. inputDevices.erase(it);
  272. try {
  273. delete device;
  274. }
  275. catch (RtMidiError& e) {
  276. throw Exception("Failed to delete RtMidi input device: %s", e.what());
  277. }
  278. }
  279. }
  280. std::vector<int> getOutputDeviceIds() override {
  281. // TODO The IDs unfortunately jump around in RtMidi. Is there a way to keep them constant when a MIDI device is added/removed?
  282. int count;
  283. try {
  284. count = rtMidiOut->getPortCount();
  285. }
  286. catch (RtMidiError& e) {
  287. throw Exception("Failed to get RtMidi output device count: %s", e.what());
  288. }
  289. std::vector<int> deviceIds;
  290. for (int i = 0; i < count; i++)
  291. deviceIds.push_back(i);
  292. return deviceIds;
  293. }
  294. std::string getOutputDeviceName(int deviceId) override {
  295. if (deviceId < 0)
  296. return "";
  297. try {
  298. return rtMidiOut->getPortName(deviceId);
  299. }
  300. catch (RtMidiError& e) {
  301. throw Exception("Failed to get RtMidi output device count: %s", e.what());
  302. }
  303. }
  304. midi::OutputDevice* subscribeOutput(int deviceId, midi::Output* output) override {
  305. if (!(0 <= deviceId && deviceId < (int) rtMidiOut->getPortCount()))
  306. return NULL;
  307. RtMidiOutputDevice* device = get(outputDevices, deviceId, NULL);
  308. if (!device) {
  309. try {
  310. outputDevices[deviceId] = device = new RtMidiOutputDevice(driverId, deviceId);
  311. }
  312. catch (RtMidiError& e) {
  313. throw Exception("Failed to create RtMidi output device: %s", e.what());
  314. }
  315. }
  316. device->subscribe(output);
  317. return device;
  318. }
  319. void unsubscribeOutput(int deviceId, midi::Output* output) override {
  320. auto it = outputDevices.find(deviceId);
  321. if (it == outputDevices.end())
  322. return;
  323. RtMidiOutputDevice* device = it->second;
  324. device->unsubscribe(output);
  325. // Destroy device if nothing is subscribed anymore
  326. if (device->subscribed.empty()) {
  327. outputDevices.erase(it);
  328. try {
  329. delete device;
  330. }
  331. catch (RtMidiError& e) {
  332. throw Exception("Failed to delete RtMidi output device: %s", e.what());
  333. }
  334. }
  335. }
  336. };
  337. void rtmidiInit() {
  338. std::vector<RtMidi::Api> rtApis;
  339. RtMidi::getCompiledApi(rtApis);
  340. for (RtMidi::Api api : rtApis) {
  341. int driverId = (int) api;
  342. try {
  343. midi::Driver* driver = new RtMidiDriver(driverId);
  344. midi::addDriver(driverId, driver);
  345. }
  346. catch (Exception& e) {
  347. WARN("Could not create RtMidiDriver %d", api);
  348. }
  349. }
  350. }
  351. } // namespace rack