#include #include #include "rtmidi/RtMidi.h" #include "core.hpp" #include "MidiIO.hpp" using namespace rack; /** * MidiIO implements the shared functionality of all midi modules, namely: * + Channel Selection (including helper for storing json) * + Interface Selection (including helper for storing json) * + rtMidi initialisation (input or output) */ MidiIO::MidiIO(bool isOut) { channel = -1; this->isOut = isOut; // TODO // Support MIDI out assert(!isOut); }; 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())); json_object_set_new(rootJ, "channel", json_integer(channel)); } return rootJ; } void MidiIO::baseFromJson(json_t *rootJ) { json_t *portNameJ = json_object_get(rootJ, "interfaceName"); if (portNameJ) { openDevice(json_string_value(portNameJ)); } json_t *channelJ = json_object_get(rootJ, "channel"); if (channelJ) { setChannel(json_integer_value(channelJ)); } } std::vector MidiIO::getDevices() { std::vector names = {}; if (isOut) { // TODO return names; } RtMidiIn *m; try { m = new RtMidiIn(); } catch (RtMidiError &error) { log(WARN, "Failed to create RtMidiIn: %s", error.getMessage().c_str()); return names; } for (unsigned int i = 0; i < m->getPortCount(); i++) { names.push_back(m->getPortName(i)); } if (!isPortOpen()) delete (m); return names; } void MidiIO::openDevice(std::string deviceName) { if (this->id > 0 || this->deviceName != "") { close(); } MidiInWrapper *mw = midiInMap[deviceName]; if (!mw) { try { mw = new MidiInWrapper(); midiInMap[deviceName] = mw; for (unsigned int i = 0; i < mw->getPortCount(); i++) { if (deviceName == mw->getPortName(i)) { mw->openPort(i); break; } } if (!mw->isPortOpen()) { log(WARN, "Failed to create RtMidiIn: No such device %s", deviceName.c_str()); this->deviceName = ""; this->id = -1; return; } } catch (RtMidiError &error) { log(WARN, "Failed to create RtMidiIn: %s", error.getMessage().c_str()); this->deviceName = ""; this->id = -1; return; } } this->deviceName = deviceName; id = midiInMap[deviceName]->add(); onDeviceChange(); } void MidiIO::setIgnores(bool ignoreSysex, bool ignoreTime, bool ignoreSense) { bool sy = true, ti = true, se = true; midiInMap[deviceName]->ignoresMap[id].midiSysex = ignoreSysex; midiInMap[deviceName]->ignoresMap[id].midiTime = ignoreTime; midiInMap[deviceName]->ignoresMap[id].midiSense = ignoreSense; for (auto kv : midiInMap[deviceName]->ignoresMap) { sy = sy && kv.second.midiSysex; ti = ti && kv.second.midiTime; se = se && kv.second.midiSense; } midiInMap[deviceName]->ignoreTypes(se, ti, se); } std::string MidiIO::getDeviceName() { return deviceName; } double MidiIO::getMessage(std::vector *msg) { MidiMessage next_msg = MidiMessage(); MidiInWrapper *mw = midiInMap[deviceName]; if (!mw) { log(WARN, "Device not opened!: %s", deviceName.c_str()); return 0; } next_msg.timeStamp = mw->getMessage(&next_msg.bytes); if (next_msg.bytes.size() > 0) { for (auto &kv : mw->idMessagesMap) { kv.second.push_back(next_msg); } } if (mw->idMessagesMap[id].size() > 0) { next_msg = mw->idMessagesMap[id].front(); mw->idMessagesMap[id].pop_front(); } *msg = next_msg.bytes; return next_msg.timeStamp; } bool MidiIO::isPortOpen() { return id > 0; } void MidiIO::close() { MidiInWrapper *mw = midiInMap[deviceName]; if (!mw || id < 0) { //log(WARN, "Trying to close already closed device!"); return; } setIgnores(); // reset ignore types for this instance mw->erase(id); if (mw->idMessagesMap.size() == 0) { mw->closePort(); midiInMap.erase(deviceName); delete (mw); } id = -1; deviceName = ""; } void MidiItem::onAction(EventAction &e) { midiModule->resetMidi(); // reset Midi values midiModule->openDevice(text); } void MidiChoice::onAction(EventAction &e) { Menu *menu = gScene->createMenu(); menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); menu->box.size.x = box.size.x; { MidiItem *midiItem = new MidiItem(); midiItem->midiModule = midiModule; midiItem->text = ""; menu->pushChild(midiItem); } std::vector deviceNames = midiModule->getDevices(); for (unsigned int i = 0; i < deviceNames.size(); i++) { MidiItem *midiItem = new MidiItem(); midiItem->midiModule = midiModule; midiItem->text = deviceNames[i]; menu->pushChild(midiItem); } } void MidiChoice::step() { if (midiModule->getDeviceName() == "") { text = "No Device"; return; } std::string name = midiModule->getDeviceName(); text = ellipsize(name, 15); } void ChannelItem::onAction(EventAction &e) { midiModule->resetMidi(); // reset Midi values midiModule->setChannel(channel); } void ChannelChoice::onAction(EventAction &e) { Menu *menu = gScene->createMenu(); menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); menu->box.size.x = box.size.x; { ChannelItem *channelItem = new ChannelItem(); channelItem->midiModule = midiModule; channelItem->channel = -1; channelItem->text = "All"; menu->pushChild(channelItem); } for (int channel = 0; channel < 16; channel++) { ChannelItem *channelItem = new ChannelItem(); channelItem->midiModule = midiModule; channelItem->channel = channel; channelItem->text = stringf("%d", channel + 1); menu->pushChild(channelItem); } } void ChannelChoice::step() { text = (midiModule->channel >= 0) ? stringf("%d", midiModule->channel + 1) : "All"; }