Browse Source

Simplify midi splitting and implement proper handling

tags/v0.5.0
ben 7 years ago
parent
commit
670614e4ad
2 changed files with 116 additions and 104 deletions
  1. +78
    -75
      src/core/MidiInterface.cpp
  2. +38
    -29
      src/core/MidiInterface.hpp

+ 78
- 75
src/core/MidiInterface.cpp View File

@@ -6,71 +6,7 @@


using namespace rack; 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<unsigned char> RtMidiInSplitter::getMessage(std::string deviceName, int id) {
std::vector<unsigned char> 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<std::string> RtMidiInSplitter::getDevices() {
/*This is a bit unneccessary */
RtMidiIn *t = new RtMidiIn(RtMidi::UNSPECIFIED, "Rack");

std::vector<std::string> 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: * MidiIO implements the shared functionality of all midi modules, namely:
@@ -81,14 +17,18 @@ std::vector<std::string> RtMidiInSplitter::getDevices() {
MidiIO::MidiIO(bool isOut) { MidiIO::MidiIO(bool isOut) {
channel = -1; channel = -1;
this->isOut = isOut; 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) { void MidiIO::setChannel(int channel) {
this->channel = channel; this->channel = channel;
} }


std::unordered_map<std::string, MidiInWrapper*> MidiIO::midiInMap = {};

json_t *MidiIO::addBaseJson(json_t *rootJ) { json_t *MidiIO::addBaseJson(json_t *rootJ) {
if (deviceName != "") { if (deviceName != "") {
json_object_set_new(rootJ, "interfaceName", json_string(deviceName.c_str())); json_object_set_new(rootJ, "interfaceName", json_string(deviceName.c_str()));
@@ -111,34 +51,97 @@ void MidiIO::baseFromJson(json_t *rootJ) {
} }


std::vector<std::string> MidiIO::getDevices() { std::vector<std::string> 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<std::string> names = {};

for (int i = 0; i < t->getPortCount(); i++) {
names.push_back(t->getPortName(i));
}

return names;
} }


void MidiIO::openDevice(std::string deviceName) { 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() { std::string MidiIO::getDeviceName() {
return deviceName; return deviceName;
} }


std::vector<unsigned char> MidiIO::getMessage() {
return midiInSplitter.getMessage(deviceName, id);
double MidiIO::getMessage(std::vector<unsigned char> *msg) {
std::vector<unsigned char> 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() { 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() { void MidiItem::onAction() {
midiModule->resetMidi(); // reset Midi values midiModule->resetMidi(); // reset Midi values
midiModule->openDevice(text); midiModule->openDevice(text);
midiModule->setDeviceName(text);
} }


void MidiChoice::onAction() { void MidiChoice::onAction() {


+ 38
- 29
src/core/MidiInterface.hpp View File

@@ -11,56 +11,63 @@ using namespace rack;
* multiple modules. A MidiIn port will be opened only once while multiple * multiple modules. A MidiIn port will be opened only once while multiple
* instances can use it simultaniously, each receiving all its incoming messages. * instances can use it simultaniously, each receiving all its incoming messages.
*/ */
struct RtMidiInSplitter {
private:
std::unordered_map<std::string, RtMidiIn*> midiInMap;
std::unordered_map<std::string, std::unordered_map<int, std::list<std::vector<unsigned char>>>> 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<unsigned char> getMessage(std::string deviceName, int id);

/* Returns Device names as string*/
std::vector<std::string> getDevices();


struct MidiInWrapper : RtMidiIn {
std::unordered_map<uint, std::list<std::vector<unsigned char>>> idMessagesMap;
std::unordered_map<uint, std::list<double>> 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<std::string, RtMidiOut> midiOuts;
//public:
// RtMidiOutSplitter();
//};

struct MidiIO { struct MidiIO {
private: private:
static RtMidiInSplitter midiInSplitter;
static std::unordered_map<std::string, MidiInWrapper *> midiInMap;
/* TODO: add for midi out*/
int id = -1; int id = -1;
std::string deviceName = ""; std::string deviceName = "";
public:
void setDeviceName(const std::string &deviceName);

// static RtMidiOutSplitter MidiOutSlpitter = RtMidiOutSplitter();
bool isOut = false;


public: public:
bool ignore_midiSysex=true;
bool ignore_midiTime=true;
bool ignore_midiSense=true;
int channel; int channel;
bool isOut = false;




MidiIO(bool isOut = false); MidiIO(bool isOut = false);


~MidiIO() {
close();
}

std::vector<std::string> getDevices(); std::vector<std::string> getDevices();

void openDevice(std::string deviceName); void openDevice(std::string deviceName);


std::string getDeviceName(); std::string getDeviceName();


void setChannel(int channel); void setChannel(int channel);


std::vector<unsigned char> getMessage();
double getMessage(std::vector<unsigned char> *msg);


bool isPortOpen(); bool isPortOpen();


@@ -68,6 +75,8 @@ public:


void baseFromJson(json_t *rootJ); void baseFromJson(json_t *rootJ);


void close();

/* called when midi port is set */ /* called when midi port is set */
virtual void resetMidi()=0; virtual void resetMidi()=0;
}; };


Loading…
Cancel
Save