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() {
setPortId(-1);
}

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

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

};


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

// 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) {
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;

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

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

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

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

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

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

// 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) {
processMidi(message);
dynamic_cast<RtMidiIn *>(rtMidi)->getMessage(&message);
message = getMessage();
}

}

if (start) {


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

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

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

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));
}
return 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) {
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");
@@ -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() {
midiModule->resetMidi(); // reset Midi values
midiModule->setPortId(portId);
midiModule->openDevice(text);
midiModule->setDeviceName(text);
}

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

int portCount = midiModule->getPortCount();
{
MidiItem *midiItem = new MidiItem();
midiItem->midiModule = midiModule;
midiItem->portId = -1;
midiItem->text = "No device";
midiItem->text = "";
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->midiModule = midiModule;
midiItem->portId = portId;
midiItem->text = midiModule->getPortName(portId);
midiItem->text = deviceNames[i];
menu->pushChild(midiItem);
}
}

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

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

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


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

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

@@ -5,25 +6,64 @@
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 {
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;
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);

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

bool isPortOpen();

json_t *addBaseJson(json_t *rootJ);

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

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

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

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


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

void step();
};

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

void step();


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

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

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

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

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

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

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

// 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) {
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() {
setPortId(-1);
}

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

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

};


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

// 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) {
processMidi(message);
dynamic_cast<RtMidiIn *>(rtMidi)->getMessage(&message);
message = getMessage();
}
}



Loading…
Cancel
Save