Browse Source

[WIP] Implement rtMidi wrapper

(Note: This misses some checks and proper error handling, but should be
good enough to test in principle)
tags/v0.5.0
ben 7 years ago
parent
commit
9647f22eee
6 changed files with 170 additions and 100 deletions
  1. +4
    -6
      src/core/MidiCCToCV.cpp
  2. +3
    -8
      src/core/MidiClockToCV.cpp
  3. +102
    -62
      src/core/MidiInterface.cpp
  4. +55
    -12
      src/core/MidiInterface.hpp
  5. +3
    -6
      src/core/MidiToCV.cpp
  6. +3
    -6
      src/core/MidiTriggerToCV.cpp

+ 4
- 6
src/core/MidiCCToCV.cpp View File

@@ -31,7 +31,7 @@ struct MIDICCToCVInterface : MidiIO, Module {
} }


~MIDICCToCVInterface() { ~MIDICCToCVInterface() {
setPortId(-1);
} }


void step(); void step();
@@ -62,22 +62,20 @@ struct MIDICCToCVInterface : MidiIO, Module {
} }


virtual void initialize() { virtual void initialize() {
setPortId(-1);
} }


}; };




void MIDICCToCVInterface::step() { void MIDICCToCVInterface::step() {
if (rtMidi->isPortOpen()) {
if (isPortOpen()) {
std::vector<unsigned char> message; std::vector<unsigned char> message;


// midiIn->getMessage returns empty vector if there are no messages in the queue // midiIn->getMessage returns empty vector if there are no messages in the queue

dynamic_cast<RtMidiIn *>(rtMidi)->getMessage(&message);
message = getMessage();
while (message.size() > 0) { while (message.size() > 0) {
processMidi(message); processMidi(message);
dynamic_cast<RtMidiIn *>(rtMidi)->getMessage(&message);
message = getMessage();
} }
} }




+ 3
- 8
src/core/MidiClockToCV.cpp View File

@@ -35,11 +35,9 @@ struct MIDIClockToCVInterface : MidiIO, Module {
bool stop = false; bool stop = false;


MIDIClockToCVInterface() : MidiIO(), Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) { MIDIClockToCVInterface() : MidiIO(), Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {
(dynamic_cast<RtMidiIn *>(rtMidi))->ignoreTypes(true, false);
} }


~MIDIClockToCVInterface() { ~MIDIClockToCVInterface() {
setPortId(-1);
} }


void step(); void step();
@@ -70,7 +68,6 @@ struct MIDIClockToCVInterface : MidiIO, Module {
} }


virtual void initialize() { virtual void initialize() {
setPortId(-1);
} }


}; };
@@ -88,17 +85,15 @@ void MIDIClockToCVInterface::step() {
* */ * */
static int ratios[] = {6, 8, 12, 16, 24, 32, 48, 96, 192}; static int ratios[] = {6, 8, 12, 16, 24, 32, 48, 96, 192};


if (rtMidi->isPortOpen()) {
if (isPortOpen()) {
std::vector<unsigned char> message; std::vector<unsigned char> message;


// midiIn->getMessage returns empty vector if there are no messages in the queue // midiIn->getMessage returns empty vector if there are no messages in the queue

dynamic_cast<RtMidiIn *>(rtMidi)->getMessage(&message);
message = getMessage();
while (message.size() > 0) { while (message.size() > 0) {
processMidi(message); processMidi(message);
dynamic_cast<RtMidiIn *>(rtMidi)->getMessage(&message);
message = getMessage();
} }

} }


if (start) { if (start) {


+ 102
- 62
src/core/MidiInterface.cpp View File

@@ -6,6 +6,72 @@


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:
* + Channel Selection (including helper for storing json) * + Channel Selection (including helper for storing json)
@@ -13,48 +79,29 @@ using namespace rack;
* + rtMidi initialisation (input or output) * + rtMidi initialisation (input or output)
*/ */
MidiIO::MidiIO(bool isOut) { MidiIO::MidiIO(bool isOut) {
portId = -1;
rtMidi = NULL;
channel = -1; channel = -1;

/*
* If isOut is set to true, creates a RtMidiOut, RtMidiIn otherwise
*/
try {
if (isOut) {
rtMidi = new RtMidiOut(RtMidi::UNSPECIFIED, "Rack");
} else {
rtMidi = new RtMidiIn(RtMidi::UNSPECIFIED, "Rack");
}
}
catch (RtMidiError &error) {
fprintf(stderr, "Failed to create RtMidiIn: %s\n", error.getMessage().c_str());
}
this->isOut = isOut;
}; };


RtMidiInSplitter MidiIO::midiInSplitter = RtMidiInSplitter();

void MidiIO::setChannel(int channel) { void MidiIO::setChannel(int channel) {
this->channel = channel; this->channel = channel;
} }


json_t *MidiIO::addBaseJson(json_t *rootJ) { json_t *MidiIO::addBaseJson(json_t *rootJ) {
if (portId >= 0) {
std::string portName = getPortName(portId);
json_object_set_new(rootJ, "portName", json_string(portName.c_str()));
if (deviceName != "") {
json_object_set_new(rootJ, "interfaceName", json_string(deviceName.c_str()));
json_object_set_new(rootJ, "channel", json_integer(channel)); json_object_set_new(rootJ, "channel", json_integer(channel));
} }
return rootJ; return rootJ;
} }


void MidiIO::baseFromJson(json_t *rootJ) { void MidiIO::baseFromJson(json_t *rootJ) {
json_t *portNameJ = json_object_get(rootJ, "portName");
json_t *portNameJ = json_object_get(rootJ, "interfaceName");
if (portNameJ) { if (portNameJ) {
std::string portName = json_string_value(portNameJ);
for (int i = 0; i < getPortCount(); i++) {
if (portName == getPortName(i)) {
setPortId(i);
break;
}
}
deviceName = json_string_value(portNameJ);
openDevice(deviceName);
} }


json_t *channelJ = json_object_get(rootJ, "channel"); json_t *channelJ = json_object_get(rootJ, "channel");
@@ -63,41 +110,35 @@ void MidiIO::baseFromJson(json_t *rootJ) {
} }
} }



int MidiIO::getPortCount() {
return rtMidi->getPortCount();
std::vector<std::string> MidiIO::getDevices() {
return midiInSplitter.getDevices();
} }


std::string MidiIO::getPortName(int portId) {
std::string portName;
try {
portName = rtMidi->getPortName(portId);
}
catch (RtMidiError &error) {
fprintf(stderr, "Failed to get Port Name: %d, %s\n", portId, error.getMessage().c_str());
}
return portName;
void MidiIO::openDevice(std::string deviceName) {
id = midiInSplitter.openDevice(deviceName);
deviceName = deviceName;
} }


void MidiIO::setPortId(int portId) {
std::string MidiIO::getDeviceName() {
return deviceName;
}


// Close port if it was previously opened
if (rtMidi->isPortOpen()) {
rtMidi->closePort();
}
this->portId = -1;
std::vector<unsigned char> MidiIO::getMessage() {
return midiInSplitter.getMessage(deviceName, id);
}


// Open new port
if (portId >= 0) {
rtMidi->openPort(portId, "Midi Interface");
}
this->portId = portId;
bool MidiIO::isPortOpen() {
return id > 0;
} }


void MidiIO::setDeviceName(const std::string &deviceName) {
MidiIO::deviceName = deviceName;
}


void MidiItem::onAction() { void MidiItem::onAction() {
midiModule->resetMidi(); // reset Midi values midiModule->resetMidi(); // reset Midi values
midiModule->setPortId(portId);
midiModule->openDevice(text);
midiModule->setDeviceName(text);
} }


void MidiChoice::onAction() { void MidiChoice::onAction() {
@@ -105,36 +146,35 @@ void MidiChoice::onAction() {
menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y)); menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y));
menu->box.size.x = box.size.x; menu->box.size.x = box.size.x;


int portCount = midiModule->getPortCount();
{ {
MidiItem *midiItem = new MidiItem(); MidiItem *midiItem = new MidiItem();
midiItem->midiModule = midiModule; midiItem->midiModule = midiModule;
midiItem->portId = -1;
midiItem->text = "No device";
midiItem->text = "";
menu->pushChild(midiItem); menu->pushChild(midiItem);
} }
for (int portId = 0; portId < portCount; portId++) {

std::vector<std::string> deviceNames = midiModule->getDevices();
for (int i = 0; i < deviceNames.size(); i++) {
MidiItem *midiItem = new MidiItem(); MidiItem *midiItem = new MidiItem();
midiItem->midiModule = midiModule; midiItem->midiModule = midiModule;
midiItem->portId = portId;
midiItem->text = midiModule->getPortName(portId);
midiItem->text = deviceNames[i];
menu->pushChild(midiItem); menu->pushChild(midiItem);
} }
} }


void MidiChoice::step() { void MidiChoice::step() {
if (midiModule->portId < 0) {
if (midiModule->getDeviceName() == "") {
text = "No Device"; text = "No Device";
return; return;
} }
std::string name = midiModule->getPortName(midiModule->portId);
std::string name = midiModule->getDeviceName();
text = ellipsize(name, 15); text = ellipsize(name, 15);
} }


void ChannelItem::onAction() { void ChannelItem::onAction() {
midiModule->resetMidi(); // reset Midi values
midiModule->setChannel(channel);
}
midiModule->resetMidi(); // reset Midi values
midiModule->setChannel(channel);
}


void ChannelChoice::onAction() { void ChannelChoice::onAction() {
Menu *menu = gScene->createMenu(); Menu *menu = gScene->createMenu();


+ 55
- 12
src/core/MidiInterface.hpp View File

@@ -1,3 +1,4 @@
#include <unordered_map>
#include "rack.hpp" #include "rack.hpp"
#include "rtmidi/RtMidi.h" #include "rtmidi/RtMidi.h"


@@ -5,25 +6,64 @@
using namespace rack; using namespace rack;




//////////////////////
// MIDI module widgets
//////////////////////
/**
* This class allows to use one instance of rtMidiIn with
* 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<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 RtMidiOutSplitter {
//private:
// std::unordered_map<std::string, RtMidiOut> midiOuts;
//public:
// RtMidiOutSplitter();
//};


struct MidiIO { struct MidiIO {
int portId;
private:
static RtMidiInSplitter midiInSplitter;
int id = -1;
std::string deviceName = "";
public:
void setDeviceName(const std::string &deviceName);

// static RtMidiOutSplitter MidiOutSlpitter = RtMidiOutSplitter();

public:
int channel; int channel;
RtMidi *rtMidi;
bool isOut = false;


MidiIO(bool isOut=false);


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


std::string getPortName(int portId);
std::vector<std::string> getDevices();
void openDevice(std::string deviceName);


void setPortId(int portId);
std::string getDeviceName();


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


std::vector<unsigned char> getMessage();

bool isPortOpen();

json_t *addBaseJson(json_t *rootJ); json_t *addBaseJson(json_t *rootJ);


void baseFromJson(json_t *rootJ); void baseFromJson(json_t *rootJ);
@@ -32,9 +72,12 @@ struct MidiIO {
virtual void resetMidi()=0; virtual void resetMidi()=0;
}; };


//////////////////////
// MIDI module widgets
//////////////////////

struct MidiItem : MenuItem { struct MidiItem : MenuItem {
MidiIO *midiModule; MidiIO *midiModule;
int portId;


void onAction(); void onAction();
}; };
@@ -63,13 +106,13 @@ struct ChannelChoice : ChoiceButton {
}; };




struct MidiToCVWidget : ModuleWidget{
struct MidiToCVWidget : ModuleWidget {
MidiToCVWidget(); MidiToCVWidget();


void step(); void step();
}; };


struct MIDICCToCVWidget : ModuleWidget{
struct MIDICCToCVWidget : ModuleWidget {
MIDICCToCVWidget(); MIDICCToCVWidget();


void step(); void step();


+ 3
- 6
src/core/MidiToCV.cpp View File

@@ -45,7 +45,6 @@ struct MIDIToCVInterface : MidiIO, Module {
} }


~MIDIToCVInterface() { ~MIDIToCVInterface() {
setPortId(-1);
}; };


void step(); void step();
@@ -67,7 +66,6 @@ struct MIDIToCVInterface : MidiIO, Module {
} }


virtual void initialize() { virtual void initialize() {
setPortId(-1);
} }


virtual void resetMidi(); virtual void resetMidi();
@@ -85,15 +83,14 @@ void MIDIToCVInterface::resetMidi() {
} }


void MIDIToCVInterface::step() { void MIDIToCVInterface::step() {
if (rtMidi->isPortOpen()) {
if (isPortOpen()) {
std::vector<unsigned char> message; std::vector<unsigned char> message;


// midiIn->getMessage returns empty vector if there are no messages in the queue // midiIn->getMessage returns empty vector if there are no messages in the queue

dynamic_cast<RtMidiIn *>(rtMidi)->getMessage(&message);
message = getMessage();
while (message.size() > 0) { while (message.size() > 0) {
processMidi(message); processMidi(message);
dynamic_cast<RtMidiIn *>(rtMidi)->getMessage(&message);
message = getMessage();
} }
} }




+ 3
- 6
src/core/MidiTriggerToCV.cpp View File

@@ -34,7 +34,6 @@ struct MIDITriggerToCVInterface : MidiIO, Module {
} }


~MIDITriggerToCVInterface() { ~MIDITriggerToCVInterface() {
setPortId(-1);
} }


void step(); void step();
@@ -65,22 +64,20 @@ struct MIDITriggerToCVInterface : MidiIO, Module {
} }


virtual void initialize() { virtual void initialize() {
setPortId(-1);
} }


}; };




void MIDITriggerToCVInterface::step() { void MIDITriggerToCVInterface::step() {
if (rtMidi->isPortOpen()) {
if (isPortOpen()) {
std::vector<unsigned char> message; std::vector<unsigned char> message;


// midiIn->getMessage returns empty vector if there are no messages in the queue // midiIn->getMessage returns empty vector if there are no messages in the queue

dynamic_cast<RtMidiIn *>(rtMidi)->getMessage(&message);
message = getMessage();
while (message.size() > 0) { while (message.size() > 0) {
processMidi(message); processMidi(message);
dynamic_cast<RtMidiIn *>(rtMidi)->getMessage(&message);
message = getMessage();
} }
} }




Loading…
Cancel
Save