diff --git a/src/core/MidiInterface.cpp b/src/core/MidiInterface.cpp index e763a052..de523244 100644 --- a/src/core/MidiInterface.cpp +++ b/src/core/MidiInterface.cpp @@ -6,71 +6,7 @@ using namespace rack; -// note this is currently not thread safe but can be easily archieved by adding a mutex -RtMidiInSplitter::RtMidiInSplitter() { - midiInMap = {}; - deviceIdMessagesMap = {}; -} - -int RtMidiInSplitter::openDevice(std::string deviceName) { - int id; - - if (!midiInMap[deviceName]) { - try { - RtMidiIn *t = new RtMidiIn(RtMidi::UNSPECIFIED, "Rack"); - t->ignoreTypes(true, false); // TODO: make this optional! - midiInMap[deviceName] = t; - for (int i = 0; i < t->getPortCount(); i++) { - if (deviceName == t->getPortName(i)) { - t->openPort(i); - break; - } - } - } - catch (RtMidiError &error) { - fprintf(stderr, "Failed to create RtMidiIn: %s\n", error.getMessage().c_str()); - } - id = 0; - deviceIdMessagesMap[deviceName] = {}; - } else { - id = deviceIdMessagesMap[deviceName].size(); - } - - deviceIdMessagesMap[deviceName][id] = {}; - return id; -} - -std::vector RtMidiInSplitter::getMessage(std::string deviceName, int id) { - std::vector next_msg, ret; - midiInMap[deviceName]->getMessage(&next_msg); - - if (next_msg.size() > 0) { - for (int i = 0; i < deviceIdMessagesMap[deviceName].size(); i++) { - deviceIdMessagesMap[deviceName][i].push_back(next_msg); - } - } - if (deviceIdMessagesMap[deviceName][id].size() == 0){ - return next_msg; - } - - ret = deviceIdMessagesMap[deviceName][id].front(); - deviceIdMessagesMap[deviceName][id].pop_front(); - return ret; -} - -std::vector RtMidiInSplitter::getDevices() { - /*This is a bit unneccessary */ - RtMidiIn *t = new RtMidiIn(RtMidi::UNSPECIFIED, "Rack"); - - std::vector names = {}; - - for (int i = 0; i < t->getPortCount(); i++) { - names.push_back(t->getPortName(i)); - } - - return names; -} /** * MidiIO implements the shared functionality of all midi modules, namely: @@ -81,14 +17,18 @@ std::vector RtMidiInSplitter::getDevices() { MidiIO::MidiIO(bool isOut) { channel = -1; this->isOut = isOut; -}; -RtMidiInSplitter MidiIO::midiInSplitter = RtMidiInSplitter(); + if (isOut) { + fprintf(stderr, "Midi Out is currently not supported (will be added soon)"); + } +}; void MidiIO::setChannel(int channel) { this->channel = channel; } +std::unordered_map MidiIO::midiInMap = {}; + json_t *MidiIO::addBaseJson(json_t *rootJ) { if (deviceName != "") { json_object_set_new(rootJ, "interfaceName", json_string(deviceName.c_str())); @@ -111,34 +51,97 @@ void MidiIO::baseFromJson(json_t *rootJ) { } std::vector MidiIO::getDevices() { - return midiInSplitter.getDevices(); + /* Note: we could also use an existing interface if one exists */ + static RtMidiIn *t = new RtMidiIn(RtMidi::UNSPECIFIED, "Rack"); + + std::vector names = {}; + + for (int i = 0; i < t->getPortCount(); i++) { + names.push_back(t->getPortName(i)); + } + + return names; } void MidiIO::openDevice(std::string deviceName) { - id = midiInSplitter.openDevice(deviceName); - deviceName = deviceName; + + if (!midiInMap[deviceName]) { + try { + MidiInWrapper *t = new MidiInWrapper(); + midiInMap[deviceName] = t; + + + for (int i = 0; i < t->getPortCount(); i++) { + if (deviceName == t->getPortName(i)) { + t->openPort(i); + break; + } + } + } + catch (RtMidiError &error) { + fprintf(stderr, "Failed to create RtMidiIn: %s\n", error.getMessage().c_str()); + return; + } + } + + this->deviceName = deviceName; + + midiInMap[deviceName]->ignoreTypes(ignore_midiSysex, ignore_midiTime, ignore_midiSense); + + id = midiInMap[deviceName]->add(); } std::string MidiIO::getDeviceName() { return deviceName; } -std::vector MidiIO::getMessage() { - return midiInSplitter.getMessage(deviceName, id); +double MidiIO::getMessage(std::vector *msg) { + std::vector next_msg; + + MidiInWrapper *m = midiInMap[deviceName]; + + if (!m) { + fprintf(stderr, "Device not opened!: %s\n", deviceName.c_str()); + return 0; + } + + double stamp = midiInMap[deviceName]->getMessage(&next_msg); + + if (next_msg.size() > 0) { + for (auto kv : m->idMessagesMap) { + m->idMessagesMap[kv.first].push_back(next_msg); + m->idStampsMap[kv.first].push_back(stamp); + } + } + + if (m->idMessagesMap[id].size() <= 0) { + *msg = next_msg; + return stamp; + } + + *msg = m->idMessagesMap[id].front(); + stamp = m->idStampsMap[id].front(); + m->idMessagesMap[id].pop_front(); + return stamp; } bool MidiIO::isPortOpen() { - return id > 0; + return midiInMap[deviceName] != NULL; } -void MidiIO::setDeviceName(const std::string &deviceName) { - MidiIO::deviceName = deviceName; +void MidiIO::close() { + midiInMap[deviceName]->erase(id); + + if (midiInMap[deviceName]->subscribers == 0) { + midiInMap[deviceName]->closePort(); + midiInMap.erase(deviceName); + } } + void MidiItem::onAction() { midiModule->resetMidi(); // reset Midi values midiModule->openDevice(text); - midiModule->setDeviceName(text); } void MidiChoice::onAction() { diff --git a/src/core/MidiInterface.hpp b/src/core/MidiInterface.hpp index c9753266..8c42b0dd 100644 --- a/src/core/MidiInterface.hpp +++ b/src/core/MidiInterface.hpp @@ -11,56 +11,63 @@ using namespace rack; * multiple modules. A MidiIn port will be opened only once while multiple * instances can use it simultaniously, each receiving all its incoming messages. */ -struct RtMidiInSplitter { -private: - std::unordered_map midiInMap; - std::unordered_map>>> deviceIdMessagesMap; -public: - RtMidiInSplitter(); - - /* Returns an Id which uniquely identifies the caller in combination with the interface name */ - int openDevice(std::string interfaceName); - - /* Returns the next message in queue for given device & id*/ - std::vector getMessage(std::string deviceName, int id); - - /* Returns Device names as string*/ - std::vector getDevices(); +struct MidiInWrapper : RtMidiIn { + std::unordered_map>> idMessagesMap; + std::unordered_map> idStampsMap; + uint uuid_c = 0; + uint subscribers = 0; + + MidiInWrapper() : RtMidiIn() { + idMessagesMap = {}; + idStampsMap = {}; + }; + + uint add() { + uint id = ++uuid_c; + subscribers++; + idMessagesMap[id] = {}; + idStampsMap[id] = {}; + return id; + } + + void erase(uint id) { + subscribers--; + idMessagesMap.erase(id); + idStampsMap.erase(id); + } }; -//struct RtMidiOutSplitter { -//private: -// std::unordered_map midiOuts; -//public: -// RtMidiOutSplitter(); -//}; - struct MidiIO { private: - static RtMidiInSplitter midiInSplitter; + static std::unordered_map midiInMap; + /* TODO: add for midi out*/ int id = -1; std::string deviceName = ""; -public: - void setDeviceName(const std::string &deviceName); - -// static RtMidiOutSplitter MidiOutSlpitter = RtMidiOutSplitter(); + bool isOut = false; public: + bool ignore_midiSysex=true; + bool ignore_midiTime=true; + bool ignore_midiSense=true; int channel; - bool isOut = false; MidiIO(bool isOut = false); + ~MidiIO() { + close(); + } + std::vector getDevices(); + void openDevice(std::string deviceName); std::string getDeviceName(); void setChannel(int channel); - std::vector getMessage(); + double getMessage(std::vector *msg); bool isPortOpen(); @@ -68,6 +75,8 @@ public: void baseFromJson(json_t *rootJ); + void close(); + /* called when midi port is set */ virtual void resetMidi()=0; };