#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; 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())); 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() { /* Note: we could also use an existing interface if one exists */ static RtMidiIn *m = new RtMidiIn(); std::vector names = {}; for (unsigned int i = 0; i < m->getPortCount(); i++) { names.push_back(m->getPortName(i)); } 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; } } } catch (RtMidiError &error) { fprintf(stderr, "Failed to create RtMidiIn: %s\n", 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][0] = ignoreSysex; midiInMap[deviceName]->ignoresMap[id][1] = ignoreTime; midiInMap[deviceName]->ignoresMap[id][2] = ignoreSense; for (auto kv : midiInMap[deviceName]->ignoresMap) { sy = sy && kv.second[0]; ti = ti && kv.second[1]; se = se && kv.second[2]; } midiInMap[deviceName]->ignoreTypes(se,ti,se); } std::string MidiIO::getDeviceName() { return deviceName; } double MidiIO::getMessage(std::vector *msg) { std::vector next_msg; MidiInWrapper *mw = midiInMap[deviceName]; if (!mw) { 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 : mw->idMessagesMap) { mw->idMessagesMap[kv.first].push_back(next_msg); mw->idStampsMap[kv.first].push_back(stamp); } } if (mw->idMessagesMap[id].size() <= 0) { *msg = next_msg; return stamp; } *msg = mw->idMessagesMap[id].front(); stamp = mw->idStampsMap[id].front(); mw->idMessagesMap[id].pop_front(); return stamp; } bool MidiIO::isPortOpen() { return id > 0; } void MidiIO::close() { MidiInWrapper *mw = midiInMap[deviceName]; if (!mw || id < 0) { //fprintf(stderr, "Trying to close already closed device!\n"); return; } setIgnores(); // reset ignore types for this instance mw->erase(id); if (mw->subscribers == 0) { mw->closePort(); midiInMap.erase(deviceName); } id = -1; deviceName = ""; } void MidiItem::onAction() { midiModule->resetMidi(); // reset Midi values midiModule->openDevice(text); } void MidiChoice::onAction() { Menu *menu = gScene->createMenu(); menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y)); 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() { midiModule->resetMidi(); // reset Midi values midiModule->setChannel(channel); } void ChannelChoice::onAction() { Menu *menu = gScene->createMenu(); menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y)); 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"; }