#include #include #include #pragma GCC diagnostic push #ifndef __clang__ #pragma GCC diagnostic ignored "-Wsuggest-override" #endif #include #pragma GCC diagnostic pop namespace rack { struct RtMidiInputDevice : midi::InputDevice { RtMidiIn* rtMidiIn; RtMidiInputDevice(int driverId, int deviceId) { rtMidiIn = new RtMidiIn((RtMidi::Api) driverId, "VCV Rack"); assert(rtMidiIn); rtMidiIn->ignoreTypes(false, false, false); rtMidiIn->setCallback(midiInputCallback, this); rtMidiIn->openPort(deviceId, "VCV Rack input"); } ~RtMidiInputDevice() { rtMidiIn->closePort(); delete rtMidiIn; } static void midiInputCallback(double timeStamp, std::vector* message, void* userData) { if (!message) return; if (!userData) return; RtMidiInputDevice* midiInputDevice = (RtMidiInputDevice*) userData; if (!midiInputDevice) return; // 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. if (message->size() > 3) return; midi::Message msg; msg.size = message->size(); for (int i = 0; i < msg.size; i++) { msg.bytes[i] = (*message)[i]; } midiInputDevice->onMessage(msg); } }; struct RtMidiOutputDevice : midi::OutputDevice { RtMidiOut* rtMidiOut; RtMidiOutputDevice(int driverId, int deviceId) { rtMidiOut = new RtMidiOut((RtMidi::Api) driverId, "VCV Rack"); assert(rtMidiOut); rtMidiOut->openPort(deviceId, "VCV Rack output"); } ~RtMidiOutputDevice() { rtMidiOut->closePort(); delete rtMidiOut; } void sendMessage(midi::Message message) override { rtMidiOut->sendMessage(message.bytes, message.size); } }; struct RtMidiDriver : midi::Driver { int driverId; /** Just for querying MIDI driver information */ RtMidiIn* rtMidiIn; RtMidiOut* rtMidiOut; std::map inputDevices; std::map outputDevices; RtMidiDriver(int driverId) { this->driverId = driverId; rtMidiIn = new RtMidiIn((RtMidi::Api) driverId); assert(rtMidiIn); rtMidiOut = new RtMidiOut((RtMidi::Api) driverId); assert(rtMidiOut); } ~RtMidiDriver() { delete rtMidiIn; delete rtMidiOut; } std::string getName() override { switch (driverId) { case RtMidi::UNSPECIFIED: return "Unspecified"; case RtMidi::MACOSX_CORE: return "Core MIDI"; case RtMidi::LINUX_ALSA: return "ALSA"; case RtMidi::UNIX_JACK: return "JACK"; case RtMidi::WINDOWS_MM: return "Windows MIDI"; case RtMidi::RTMIDI_DUMMY: return "Dummy MIDI"; default: return ""; } } std::vector getInputDeviceIds() override { // TODO The IDs unfortunately jump around in RtMidi. Is there a way to keep them constant when a MIDI device is added/removed? int count = rtMidiIn->getPortCount(); std::vector deviceIds; for (int i = 0; i < count; i++) deviceIds.push_back(i); return deviceIds; } std::string getInputDeviceName(int deviceId) override { if (deviceId >= 0) { return rtMidiIn->getPortName(deviceId); } return ""; } midi::InputDevice* subscribeInput(int deviceId, midi::Input* input) override { if (!(0 <= deviceId && deviceId < (int) rtMidiIn->getPortCount())) return NULL; RtMidiInputDevice* device = inputDevices[deviceId]; if (!device) { inputDevices[deviceId] = device = new RtMidiInputDevice(driverId, deviceId); } device->subscribe(input); return device; } void unsubscribeInput(int deviceId, midi::Input* input) override { auto it = inputDevices.find(deviceId); if (it == inputDevices.end()) return; RtMidiInputDevice* device = it->second; device->unsubscribe(input); // Destroy device if nothing is subscribed anymore if (device->subscribed.empty()) { inputDevices.erase(it); delete device; } } std::vector getOutputDeviceIds() override { int count = rtMidiOut->getPortCount(); std::vector deviceIds; for (int i = 0; i < count; i++) deviceIds.push_back(i); return deviceIds; } std::string getOutputDeviceName(int deviceId) override { if (deviceId >= 0) { return rtMidiOut->getPortName(deviceId); } return ""; } midi::OutputDevice* subscribeOutput(int deviceId, midi::Output* output) override { if (!(0 <= deviceId && deviceId < (int) rtMidiOut->getPortCount())) return NULL; RtMidiOutputDevice* device = outputDevices[deviceId]; if (!device) { outputDevices[deviceId] = device = new RtMidiOutputDevice(driverId, deviceId); } device->subscribe(output); return device; } void unsubscribeOutput(int deviceId, midi::Output* output) override { auto it = outputDevices.find(deviceId); if (it == outputDevices.end()) return; RtMidiOutputDevice* device = it->second; device->unsubscribe(output); // Destroy device if nothing is subscribed anymore if (device->subscribed.empty()) { outputDevices.erase(it); delete device; } } }; void rtmidiInit() { std::vector rtApis; RtMidi::getCompiledApi(rtApis); for (RtMidi::Api api : rtApis) { int driverId = (int) api; midi::Driver* driver = new RtMidiDriver(driverId); midi::addDriver(driverId, driver); } } } // namespace rack