@@ -1,27 +1,11 @@ | |||
#pragma once | |||
#include "bridgeprotocol.hpp" | |||
#include "audio.hpp" | |||
#include "midi.hpp" | |||
namespace rack { | |||
struct BridgeMidiInputDevice : midi::InputDevice { | |||
}; | |||
struct BridgeMidiDriver : midi::Driver { | |||
BridgeMidiInputDevice devices[16]; | |||
std::string getName() override {return "Bridge";} | |||
std::vector<int> getInputDeviceIds() override; | |||
std::string getInputDeviceName(int deviceId) override; | |||
midi::InputDevice *subscribeInput(int deviceId, midi::Input *input) override; | |||
void unsubscribeInput(int deviceId, midi::Input *input) override; | |||
}; | |||
void bridgeInit(); | |||
void bridgeDestroy(); | |||
void bridgeAudioSubscribe(int channel, audio::IO *audio); | |||
@@ -1,32 +1,11 @@ | |||
#pragma once | |||
#include "common.hpp" | |||
#include "midi.hpp" | |||
namespace rack { | |||
namespace gamepad { | |||
struct InputDevice : midi::InputDevice { | |||
int deviceId; | |||
std::vector<uint8_t> ccs; | |||
std::vector<bool> states; | |||
void step(); | |||
}; | |||
struct Driver : midi::Driver { | |||
InputDevice devices[16]; | |||
Driver(); | |||
std::string getName() override {return "Gamepad";} | |||
std::vector<int> getInputDeviceIds() override; | |||
std::string getInputDeviceName(int deviceId) override; | |||
midi::InputDevice *subscribeInput(int deviceId, midi::Input *input) override; | |||
void unsubscribeInput(int deviceId, midi::Input *input) override; | |||
}; | |||
void init(); | |||
void step(); | |||
@@ -1,32 +1,11 @@ | |||
#pragma once | |||
#include "common.hpp" | |||
#include "midi.hpp" | |||
#include <map> | |||
namespace rack { | |||
namespace keyboard { | |||
struct InputDevice : midi::InputDevice { | |||
int octave = 5; | |||
std::map<int, int> pressedNotes; | |||
void onKeyPress(int key); | |||
void onKeyRelease(int key); | |||
}; | |||
struct Driver : midi::Driver { | |||
InputDevice device; | |||
std::string getName() override {return "Computer keyboard";} | |||
std::vector<int> getInputDeviceIds() override; | |||
std::string getInputDeviceName(int deviceId) override; | |||
midi::InputDevice *subscribeInput(int deviceId, midi::Input *input) override; | |||
void unsubscribeInput(int deviceId, midi::Input *input) override; | |||
}; | |||
void init(); | |||
void press(int key); | |||
void release(int key); | |||
@@ -1,44 +1,10 @@ | |||
#pragma once | |||
#include "common.hpp" | |||
#include "midi.hpp" | |||
#include <map> | |||
#pragma GCC diagnostic push | |||
#ifndef __clang__ | |||
#pragma GCC diagnostic ignored "-Wsuggest-override" | |||
#endif | |||
#include <rtmidi/RtMidi.h> | |||
#pragma GCC diagnostic pop | |||
namespace rack { | |||
struct RtMidiInputDevice : midi::InputDevice { | |||
RtMidiIn *rtMidiIn; | |||
RtMidiInputDevice(int driverId, int deviceId); | |||
~RtMidiInputDevice(); | |||
}; | |||
struct RtMidiDriver : midi::Driver { | |||
int driverId; | |||
/** Just for querying MIDI driver information */ | |||
RtMidiIn *rtMidiIn; | |||
RtMidiOut *rtMidiOut; | |||
std::map<int, RtMidiInputDevice*> devices; | |||
RtMidiDriver(int driverId); | |||
~RtMidiDriver(); | |||
std::string getName() override; | |||
std::vector<int> getInputDeviceIds() override; | |||
std::string getInputDeviceName(int deviceId) override; | |||
midi::InputDevice *subscribeInput(int deviceId, midi::Input *input) override; | |||
void unsubscribeInput(int deviceId, midi::Input *input) override; | |||
}; | |||
void rtmidiInit(); | |||
@@ -1,4 +1,5 @@ | |||
#include "bridge.hpp" | |||
#include "midi.hpp" | |||
#include "string.hpp" | |||
#include "dsp/ringbuffer.hpp" | |||
@@ -19,6 +20,9 @@ | |||
namespace rack { | |||
struct BridgeMidiDriver; | |||
struct BridgeClientConnection; | |||
static BridgeClientConnection *connections[BRIDGE_NUM_PORTS] = {}; | |||
static audio::IO *audioListeners[BRIDGE_NUM_PORTS] = {}; | |||
@@ -27,6 +31,46 @@ static bool serverRunning = false; | |||
static BridgeMidiDriver *driver = NULL; | |||
struct BridgeMidiInputDevice : midi::InputDevice { | |||
}; | |||
struct BridgeMidiDriver : midi::Driver { | |||
BridgeMidiInputDevice devices[16]; | |||
std::string getName() override {return "Bridge";} | |||
std::vector<int> getInputDeviceIds() override { | |||
std::vector<int> deviceIds; | |||
for (int i = 0; i < 16; i++) { | |||
deviceIds.push_back(i); | |||
} | |||
return deviceIds; | |||
} | |||
std::string getInputDeviceName(int deviceId) override { | |||
if (deviceId < 0) | |||
return ""; | |||
return string::f("Port %d", deviceId + 1); | |||
} | |||
midi::InputDevice *subscribeInput(int deviceId, midi::Input *input) override { | |||
if (!(0 <= deviceId && deviceId < 16)) | |||
return NULL; | |||
devices[deviceId].subscribe(input); | |||
return &devices[deviceId]; | |||
} | |||
void unsubscribeInput(int deviceId, midi::Input *input) override { | |||
if (!(0 <= deviceId && deviceId < 16)) | |||
return; | |||
devices[deviceId].unsubscribe(input); | |||
} | |||
}; | |||
struct BridgeClientConnection { | |||
int client; | |||
bool ready = false; | |||
@@ -372,36 +416,6 @@ static void serverRun() { | |||
} | |||
std::vector<int> BridgeMidiDriver::getInputDeviceIds() { | |||
std::vector<int> deviceIds; | |||
for (int i = 0; i < 16; i++) { | |||
deviceIds.push_back(i); | |||
} | |||
return deviceIds; | |||
} | |||
std::string BridgeMidiDriver::getInputDeviceName(int deviceId) { | |||
if (deviceId < 0) | |||
return ""; | |||
return string::f("Port %d", deviceId + 1); | |||
} | |||
midi::InputDevice *BridgeMidiDriver::subscribeInput(int deviceId, midi::Input *input) { | |||
if (!(0 <= deviceId && deviceId < 16)) | |||
return NULL; | |||
devices[deviceId].subscribe(input); | |||
return &devices[deviceId]; | |||
} | |||
void BridgeMidiDriver::unsubscribeInput(int deviceId, midi::Input *input) { | |||
if (!(0 <= deviceId && deviceId < 16)) | |||
return; | |||
devices[deviceId].unsubscribe(input); | |||
} | |||
void bridgeInit() { | |||
serverRunning = true; | |||
serverThread = std::thread(serverRun); | |||
@@ -1,4 +1,5 @@ | |||
#include "gamepad.hpp" | |||
#include "midi.hpp" | |||
#include "string.hpp" | |||
#include "window.hpp" | |||
@@ -7,95 +8,110 @@ namespace rack { | |||
namespace gamepad { | |||
struct Driver; | |||
static const int DRIVER = -10; | |||
static Driver *driver = NULL; | |||
void InputDevice::step() { | |||
if (!glfwJoystickPresent(deviceId)) | |||
return; | |||
// Get gamepad state | |||
int numAxes; | |||
const float *axes = glfwGetJoystickAxes(deviceId, &numAxes); | |||
int numButtons; | |||
const unsigned char *buttons = glfwGetJoystickButtons(deviceId, &numButtons); | |||
// Convert axes to MIDI CC | |||
ccs.resize(numAxes); | |||
for (int i = 0; i < numAxes; i++) { | |||
// Allow CC value to go negative, but clamp at -127 instead of -128 for symmetry | |||
int8_t cc = math::clamp((int) std::round(axes[i] * 127), -127, 127); | |||
if (cc != ccs[i]) { | |||
ccs[i] = cc; | |||
// Send MIDI message | |||
midi::Message msg; | |||
// MIDI channel 1 | |||
msg.cmd = (0xb << 4) | 0; | |||
msg.data1 = i; | |||
msg.data2 = ccs[i]; | |||
onMessage(msg); | |||
struct InputDevice : midi::InputDevice { | |||
int deviceId; | |||
std::vector<uint8_t> ccs; | |||
std::vector<bool> states; | |||
void step() { | |||
if (!glfwJoystickPresent(deviceId)) | |||
return; | |||
// Get gamepad state | |||
int numAxes; | |||
const float *axes = glfwGetJoystickAxes(deviceId, &numAxes); | |||
int numButtons; | |||
const unsigned char *buttons = glfwGetJoystickButtons(deviceId, &numButtons); | |||
// Convert axes to MIDI CC | |||
ccs.resize(numAxes); | |||
for (int i = 0; i < numAxes; i++) { | |||
// Allow CC value to go negative, but clamp at -127 instead of -128 for symmetry | |||
int8_t cc = math::clamp((int) std::round(axes[i] * 127), -127, 127); | |||
if (cc != ccs[i]) { | |||
ccs[i] = cc; | |||
// Send MIDI message | |||
midi::Message msg; | |||
// MIDI channel 1 | |||
msg.cmd = (0xb << 4) | 0; | |||
msg.data1 = i; | |||
msg.data2 = ccs[i]; | |||
onMessage(msg); | |||
} | |||
} | |||
} | |||
// Convert buttons to MIDI notes | |||
states.resize(numButtons); | |||
for (int i = 0; i < numButtons; i++) { | |||
bool state = !!buttons[i]; | |||
if (state != states[i]) { | |||
states[i] = state; | |||
midi::Message msg; | |||
msg.cmd = ((state ? 0x9 : 0x8) << 4); | |||
msg.data1 = i; | |||
msg.data2 = 127; | |||
onMessage(msg); | |||
// Convert buttons to MIDI notes | |||
states.resize(numButtons); | |||
for (int i = 0; i < numButtons; i++) { | |||
bool state = !!buttons[i]; | |||
if (state != states[i]) { | |||
states[i] = state; | |||
midi::Message msg; | |||
msg.cmd = ((state ? 0x9 : 0x8) << 4); | |||
msg.data1 = i; | |||
msg.data2 = 127; | |||
onMessage(msg); | |||
} | |||
} | |||
} | |||
} | |||
}; | |||
Driver::Driver() { | |||
for (int i = 0; i < 16; i++) { | |||
devices[i].deviceId = i; | |||
struct Driver : midi::Driver { | |||
InputDevice devices[16]; | |||
Driver() { | |||
for (int i = 0; i < 16; i++) { | |||
devices[i].deviceId = i; | |||
} | |||
} | |||
} | |||
std::vector<int> Driver::getInputDeviceIds() { | |||
std::vector<int> deviceIds; | |||
for (int i = 0; i < 16; i++) { | |||
if (glfwJoystickPresent(i)) { | |||
deviceIds.push_back(i); | |||
std::string getName() override {return "Gamepad";} | |||
std::vector<int> getInputDeviceIds() override { | |||
std::vector<int> deviceIds; | |||
for (int i = 0; i < 16; i++) { | |||
if (glfwJoystickPresent(i)) { | |||
deviceIds.push_back(i); | |||
} | |||
} | |||
return deviceIds; | |||
} | |||
return deviceIds; | |||
} | |||
std::string Driver::getInputDeviceName(int deviceId) { | |||
if (!(0 <= deviceId && deviceId < 16)) | |||
return ""; | |||
std::string getInputDeviceName(int deviceId) override { | |||
if (!(0 <= deviceId && deviceId < 16)) | |||
return ""; | |||
const char *name = glfwGetJoystickName(deviceId); | |||
if (name) { | |||
return name; | |||
const char *name = glfwGetJoystickName(deviceId); | |||
if (name) { | |||
return name; | |||
} | |||
return string::f(" %d (unavailable)", deviceId + 1); | |||
} | |||
return string::f(" %d (unavailable)", deviceId + 1); | |||
} | |||
midi::InputDevice *Driver::subscribeInput(int deviceId, midi::Input *input) { | |||
if (!(0 <= deviceId && deviceId < 16)) | |||
return NULL; | |||
midi::InputDevice *subscribeInput(int deviceId, midi::Input *input) override { | |||
if (!(0 <= deviceId && deviceId < 16)) | |||
return NULL; | |||
devices[deviceId].subscribe(input); | |||
return &devices[deviceId]; | |||
} | |||
devices[deviceId].subscribe(input); | |||
return &devices[deviceId]; | |||
} | |||
void Driver::unsubscribeInput(int deviceId, midi::Input *input) { | |||
if (!(0 <= deviceId && deviceId < 16)) | |||
return; | |||
void unsubscribeInput(int deviceId, midi::Input *input) override { | |||
if (!(0 <= deviceId && deviceId < 16)) | |||
return; | |||
devices[deviceId].unsubscribe(input); | |||
} | |||
devices[deviceId].unsubscribe(input); | |||
} | |||
}; | |||
void init() { | |||
@@ -1,122 +1,137 @@ | |||
#include "keyboard.hpp" | |||
#include "midi.hpp" | |||
#include "window.hpp" | |||
#include <map> | |||
namespace rack { | |||
namespace keyboard { | |||
static const int DRIVER = -11; | |||
static Driver *driver = NULL; | |||
struct Driver; | |||
void InputDevice::onKeyPress(int key) { | |||
int note = -1; | |||
switch (key) { | |||
case GLFW_KEY_Z: note = 0; break; | |||
case GLFW_KEY_S: note = 1; break; | |||
case GLFW_KEY_X: note = 2; break; | |||
case GLFW_KEY_D: note = 3; break; | |||
case GLFW_KEY_C: note = 4; break; | |||
case GLFW_KEY_V: note = 5; break; | |||
case GLFW_KEY_G: note = 6; break; | |||
case GLFW_KEY_B: note = 7; break; | |||
case GLFW_KEY_H: note = 8; break; | |||
case GLFW_KEY_N: note = 9; break; | |||
case GLFW_KEY_J: note = 10; break; | |||
case GLFW_KEY_M: note = 11; break; | |||
case GLFW_KEY_COMMA: note = 12; break; | |||
case GLFW_KEY_L: note = 13; break; | |||
case GLFW_KEY_PERIOD: note = 14; break; | |||
case GLFW_KEY_SEMICOLON: note = 15; break; | |||
case GLFW_KEY_SLASH: note = 16; break; | |||
case GLFW_KEY_Q: note = 12; break; | |||
case GLFW_KEY_2: note = 13; break; | |||
case GLFW_KEY_W: note = 14; break; | |||
case GLFW_KEY_3: note = 15; break; | |||
case GLFW_KEY_E: note = 16; break; | |||
case GLFW_KEY_R: note = 17; break; | |||
case GLFW_KEY_5: note = 18; break; | |||
case GLFW_KEY_T: note = 19; break; | |||
case GLFW_KEY_6: note = 20; break; | |||
case GLFW_KEY_Y: note = 21; break; | |||
case GLFW_KEY_7: note = 22; break; | |||
case GLFW_KEY_U: note = 23; break; | |||
case GLFW_KEY_I: note = 24; break; | |||
case GLFW_KEY_9: note = 25; break; | |||
case GLFW_KEY_O: note = 26; break; | |||
case GLFW_KEY_0: note = 27; break; | |||
case GLFW_KEY_P: note = 28; break; | |||
case GLFW_KEY_LEFT_BRACKET: note = 29; break; | |||
case GLFW_KEY_EQUAL: note = 30; break; | |||
case GLFW_KEY_RIGHT_BRACKET: note = 31; break; | |||
case GLFW_KEY_GRAVE_ACCENT: { | |||
octave--; | |||
} break; | |||
case GLFW_KEY_1: { | |||
octave++; | |||
} break; | |||
default: break; | |||
} | |||
octave = math::clamp(octave, 0, 9); | |||
if (note < 0) | |||
return; | |||
note += 12 * octave; | |||
if (note > 127) | |||
return; | |||
static const int DRIVER = -11; | |||
static Driver *driver = NULL; | |||
midi::Message msg; | |||
msg.cmd = 0x9 << 4; | |||
msg.data1 = note; | |||
msg.data2 = 127; | |||
onMessage(msg); | |||
pressedNotes[key] = note; | |||
} | |||
struct InputDevice : midi::InputDevice { | |||
int octave = 5; | |||
std::map<int, int> pressedNotes; | |||
void onKeyPress(int key) { | |||
int note = -1; | |||
switch (key) { | |||
case GLFW_KEY_Z: note = 0; break; | |||
case GLFW_KEY_S: note = 1; break; | |||
case GLFW_KEY_X: note = 2; break; | |||
case GLFW_KEY_D: note = 3; break; | |||
case GLFW_KEY_C: note = 4; break; | |||
case GLFW_KEY_V: note = 5; break; | |||
case GLFW_KEY_G: note = 6; break; | |||
case GLFW_KEY_B: note = 7; break; | |||
case GLFW_KEY_H: note = 8; break; | |||
case GLFW_KEY_N: note = 9; break; | |||
case GLFW_KEY_J: note = 10; break; | |||
case GLFW_KEY_M: note = 11; break; | |||
case GLFW_KEY_COMMA: note = 12; break; | |||
case GLFW_KEY_L: note = 13; break; | |||
case GLFW_KEY_PERIOD: note = 14; break; | |||
case GLFW_KEY_SEMICOLON: note = 15; break; | |||
case GLFW_KEY_SLASH: note = 16; break; | |||
case GLFW_KEY_Q: note = 12; break; | |||
case GLFW_KEY_2: note = 13; break; | |||
case GLFW_KEY_W: note = 14; break; | |||
case GLFW_KEY_3: note = 15; break; | |||
case GLFW_KEY_E: note = 16; break; | |||
case GLFW_KEY_R: note = 17; break; | |||
case GLFW_KEY_5: note = 18; break; | |||
case GLFW_KEY_T: note = 19; break; | |||
case GLFW_KEY_6: note = 20; break; | |||
case GLFW_KEY_Y: note = 21; break; | |||
case GLFW_KEY_7: note = 22; break; | |||
case GLFW_KEY_U: note = 23; break; | |||
case GLFW_KEY_I: note = 24; break; | |||
case GLFW_KEY_9: note = 25; break; | |||
case GLFW_KEY_O: note = 26; break; | |||
case GLFW_KEY_0: note = 27; break; | |||
case GLFW_KEY_P: note = 28; break; | |||
case GLFW_KEY_LEFT_BRACKET: note = 29; break; | |||
case GLFW_KEY_EQUAL: note = 30; break; | |||
case GLFW_KEY_RIGHT_BRACKET: note = 31; break; | |||
case GLFW_KEY_GRAVE_ACCENT: { | |||
octave--; | |||
} break; | |||
case GLFW_KEY_1: { | |||
octave++; | |||
} break; | |||
default: break; | |||
} | |||
octave = math::clamp(octave, 0, 9); | |||
if (note < 0) | |||
return; | |||
note += 12 * octave; | |||
if (note > 127) | |||
return; | |||
void InputDevice::onKeyRelease(int key) { | |||
auto it = pressedNotes.find(key); | |||
if (it != pressedNotes.end()) { | |||
int note = it->second; | |||
midi::Message msg; | |||
msg.cmd = 0x8 << 4; | |||
msg.cmd = 0x9 << 4; | |||
msg.data1 = note; | |||
msg.data2 = 127; | |||
onMessage(msg); | |||
pressedNotes.erase(it); | |||
pressedNotes[key] = note; | |||
} | |||
} | |||
void onKeyRelease(int key) { | |||
auto it = pressedNotes.find(key); | |||
if (it != pressedNotes.end()) { | |||
int note = it->second; | |||
midi::Message msg; | |||
msg.cmd = 0x8 << 4; | |||
msg.data1 = note; | |||
msg.data2 = 127; | |||
onMessage(msg); | |||
pressedNotes.erase(it); | |||
} | |||
} | |||
}; | |||
std::vector<int> Driver::getInputDeviceIds() { | |||
return {0}; | |||
} | |||
std::string Driver::getInputDeviceName(int deviceId) { | |||
if (deviceId == 0) | |||
return "QWERTY keyboard (US)"; | |||
return ""; | |||
} | |||
struct Driver : midi::Driver { | |||
InputDevice device; | |||
std::string getName() override {return "Computer keyboard";} | |||
midi::InputDevice *Driver::subscribeInput(int deviceId, midi::Input *input) { | |||
if (deviceId != 0) | |||
return NULL; | |||
std::vector<int> getInputDeviceIds() override { | |||
return {0}; | |||
} | |||
device.subscribe(input); | |||
return &device; | |||
} | |||
std::string getInputDeviceName(int deviceId) override { | |||
if (deviceId == 0) | |||
return "QWERTY keyboard (US)"; | |||
return ""; | |||
} | |||
void Driver::unsubscribeInput(int deviceId, midi::Input *input) { | |||
if (deviceId != 0) | |||
return; | |||
midi::InputDevice *subscribeInput(int deviceId, midi::Input *input) override { | |||
if (deviceId != 0) | |||
return NULL; | |||
device.unsubscribe(input); | |||
} | |||
device.subscribe(input); | |||
return &device; | |||
} | |||
void unsubscribeInput(int deviceId, midi::Input *input) override { | |||
if (deviceId != 0) | |||
return; | |||
device.unsubscribe(input); | |||
} | |||
}; | |||
void init() { | |||
@@ -1,6 +1,7 @@ | |||
#include "common.hpp" | |||
#include "random.hpp" | |||
#include "asset.hpp" | |||
#include "midi.hpp" | |||
#include "rtmidi.hpp" | |||
#include "keyboard.hpp" | |||
#include "gamepad.hpp" | |||
@@ -1,107 +1,190 @@ | |||
#include "rtmidi.hpp" | |||
#include "midi.hpp" | |||
#include <map> | |||
#pragma GCC diagnostic push | |||
#ifndef __clang__ | |||
#pragma GCC diagnostic ignored "-Wsuggest-override" | |||
#endif | |||
#include <rtmidi/RtMidi.h> | |||
#pragma GCC diagnostic pop | |||
namespace rack { | |||
static void midiInputCallback(double timeStamp, std::vector<unsigned char> *message, void *userData) { | |||
if (!message) return; | |||
if (!userData) return; | |||
struct RtMidiInputDevice : midi::InputDevice { | |||
RtMidiIn *rtMidiIn; | |||
RtMidiInputDevice(int driverId, int deviceId) { | |||
rtMidiIn = new RtMidiIn((RtMidi::Api) driverId, "VCV Rack"); | |||
assert(rtMidiIn); | |||
rtMidiIn->ignoreTypes(false, false, false); | |||
rtMidiIn->setCallback(midiInputCallback, this); | |||
rtMidiIn->openPort(deviceId, "VCV Rack input"); | |||
} | |||
~RtMidiInputDevice() { | |||
rtMidiIn->closePort(); | |||
delete rtMidiIn; | |||
} | |||
static void midiInputCallback(double timeStamp, std::vector<unsigned char> *message, void *userData) { | |||
if (!message) return; | |||
if (!userData) return; | |||
RtMidiInputDevice *midiInputDevice = (RtMidiInputDevice*) userData; | |||
if (!midiInputDevice) return; | |||
if (message->size() < 3) return; | |||
RtMidiInputDevice *midiInputDevice = (RtMidiInputDevice*) userData; | |||
if (!midiInputDevice) return; | |||
midi::Message msg; | |||
if (message->size() >= 1) | |||
midi::Message msg; | |||
msg.cmd = (*message)[0]; | |||
if (message->size() >= 2) | |||
msg.data1 = (*message)[1]; | |||
if (message->size() >= 3) | |||
msg.data2 = (*message)[2]; | |||
midiInputDevice->onMessage(msg); | |||
} | |||
}; | |||
midiInputDevice->onMessage(msg); | |||
} | |||
RtMidiInputDevice::RtMidiInputDevice(int driverId, int deviceId) { | |||
rtMidiIn = new RtMidiIn((RtMidi::Api) driverId, "VCV Rack"); | |||
assert(rtMidiIn); | |||
rtMidiIn->ignoreTypes(false, false, false); | |||
rtMidiIn->setCallback(midiInputCallback, this); | |||
rtMidiIn->openPort(deviceId, "VCV Rack input"); | |||
} | |||
struct RtMidiOutputDevice : midi::OutputDevice { | |||
RtMidiOut *rtMidiOut; | |||
RtMidiInputDevice::~RtMidiInputDevice() { | |||
rtMidiIn->closePort(); | |||
delete rtMidiIn; | |||
} | |||
RtMidiOutputDevice(int driverId, int deviceId) { | |||
rtMidiOut = new RtMidiOut((RtMidi::Api) driverId, "VCV Rack"); | |||
assert(rtMidiOut); | |||
rtMidiOut->openPort(deviceId, "VCV Rack output"); | |||
} | |||
~RtMidiOutputDevice() { | |||
rtMidiOut->closePort(); | |||
delete rtMidiOut; | |||
} | |||
RtMidiDriver::RtMidiDriver(int driverId) { | |||
this->driverId = driverId; | |||
rtMidiIn = new RtMidiIn((RtMidi::Api) driverId); | |||
assert(rtMidiIn); | |||
rtMidiOut = new RtMidiOut((RtMidi::Api) driverId); | |||
assert(rtMidiOut); | |||
} | |||
void sendMessage(midi::Message message) override { | |||
unsigned char msg[3]; | |||
msg[0] = message.cmd; | |||
msg[0] = message.data1; | |||
msg[0] = message.data2; | |||
rtMidiOut->sendMessage(msg, 3); | |||
} | |||
}; | |||
struct RtMidiDriver : midi::Driver { | |||
int driverId; | |||
/** Just for querying MIDI driver information */ | |||
RtMidiIn *rtMidiIn; | |||
RtMidiOut *rtMidiOut; | |||
std::map<int, RtMidiInputDevice*> inputDevices; | |||
std::map<int, RtMidiOutputDevice*> outputDevices; | |||
RtMidiDriver(int driverId) { | |||
this->driverId = driverId; | |||
rtMidiIn = new RtMidiIn((RtMidi::Api) driverId); | |||
assert(rtMidiIn); | |||
rtMidiOut = new RtMidiOut((RtMidi::Api) driverId); | |||
assert(rtMidiOut); | |||
} | |||
RtMidiDriver::~RtMidiDriver() { | |||
delete rtMidiIn; | |||
delete rtMidiOut; | |||
} | |||
~RtMidiDriver() { | |||
delete rtMidiIn; | |||
delete rtMidiOut; | |||
} | |||
std::string RtMidiDriver::getName() { | |||
switch (driverId) { | |||
case RtMidi::UNSPECIFIED: return "Unspecified"; | |||
case RtMidi::MACOSX_CORE: return "Core MIDI"; | |||
case RtMidi::LINUX_ALSA: return "ALSA"; | |||
case RtMidi::UNIX_JACK: return "JACK"; | |||
case RtMidi::WINDOWS_MM: return "Windows MIDI"; | |||
case RtMidi::RTMIDI_DUMMY: return "Dummy MIDI"; | |||
default: return ""; | |||
std::string getName() override { | |||
switch (driverId) { | |||
case RtMidi::UNSPECIFIED: return "Unspecified"; | |||
case RtMidi::MACOSX_CORE: return "Core MIDI"; | |||
case RtMidi::LINUX_ALSA: return "ALSA"; | |||
case RtMidi::UNIX_JACK: return "JACK"; | |||
case RtMidi::WINDOWS_MM: return "Windows MIDI"; | |||
case RtMidi::RTMIDI_DUMMY: return "Dummy MIDI"; | |||
default: return ""; | |||
} | |||
} | |||
std::vector<int> getInputDeviceIds() override { | |||
// TODO The IDs unfortunately jump around in RtMidi. Is there a way to keep them constant when a MIDI device is added/removed? | |||
int count = rtMidiIn->getPortCount(); | |||
std::vector<int> deviceIds; | |||
for (int i = 0; i < count; i++) | |||
deviceIds.push_back(i); | |||
return deviceIds; | |||
} | |||
} | |||
std::vector<int> RtMidiDriver::getInputDeviceIds() { | |||
// TODO The IDs unfortunately jump around in RtMidi. Is there a way to keep them constant when a MIDI device is added/removed? | |||
int count = rtMidiIn->getPortCount(); | |||
std::vector<int> deviceIds; | |||
for (int i = 0; i < count; i++) | |||
deviceIds.push_back(i); | |||
return deviceIds; | |||
} | |||
std::string getInputDeviceName(int deviceId) override { | |||
if (deviceId >= 0) { | |||
return rtMidiIn->getPortName(deviceId); | |||
} | |||
return ""; | |||
} | |||
std::string RtMidiDriver::getInputDeviceName(int deviceId) { | |||
if (deviceId >= 0) { | |||
return rtMidiIn->getPortName(deviceId); | |||
midi::InputDevice *subscribeInput(int deviceId, midi::Input *input) override { | |||
if (!(0 <= deviceId && deviceId < (int) rtMidiIn->getPortCount())) | |||
return NULL; | |||
RtMidiInputDevice *device = inputDevices[deviceId]; | |||
if (!device) { | |||
inputDevices[deviceId] = device = new RtMidiInputDevice(driverId, deviceId); | |||
} | |||
device->subscribe(input); | |||
return device; | |||
} | |||
return ""; | |||
} | |||
midi::InputDevice *RtMidiDriver::subscribeInput(int deviceId, midi::Input *input) { | |||
if (!(0 <= deviceId && deviceId < (int) rtMidiIn->getPortCount())) | |||
return NULL; | |||
RtMidiInputDevice *device = devices[deviceId]; | |||
if (!device) { | |||
devices[deviceId] = device = new RtMidiInputDevice(driverId, deviceId); | |||
void unsubscribeInput(int deviceId, midi::Input *input) override { | |||
auto it = inputDevices.find(deviceId); | |||
if (it == inputDevices.end()) | |||
return; | |||
RtMidiInputDevice *device = it->second; | |||
device->unsubscribe(input); | |||
// Destroy device if nothing is subscribed anymore | |||
if (device->subscribed.empty()) { | |||
inputDevices.erase(it); | |||
delete device; | |||
} | |||
} | |||
device->subscribe(input); | |||
return device; | |||
} | |||
std::vector<int> getOutputDeviceIds() override { | |||
int count = rtMidiOut->getPortCount(); | |||
std::vector<int> deviceIds; | |||
for (int i = 0; i < count; i++) | |||
deviceIds.push_back(i); | |||
return deviceIds; | |||
} | |||
std::string getOutputDeviceName(int deviceId) override { | |||
if (deviceId >= 0) { | |||
return rtMidiOut->getPortName(deviceId); | |||
} | |||
return ""; | |||
} | |||
void RtMidiDriver::unsubscribeInput(int deviceId, midi::Input *input) { | |||
auto it = devices.find(deviceId); | |||
if (it == devices.end()) | |||
return; | |||
RtMidiInputDevice *device = it->second; | |||
device->unsubscribe(input); | |||
midi::OutputDevice *subscribeOutput(int deviceId, midi::Output *output) override { | |||
if (!(0 <= deviceId && deviceId < (int) rtMidiOut->getPortCount())) | |||
return NULL; | |||
RtMidiOutputDevice *device = outputDevices[deviceId]; | |||
if (!device) { | |||
outputDevices[deviceId] = device = new RtMidiOutputDevice(driverId, deviceId); | |||
} | |||
// Destroy device if nothing is subscribed anymore | |||
if (device->subscribed.empty()) { | |||
devices.erase(it); | |||
delete device; | |||
device->subscribe(output); | |||
return device; | |||
} | |||
} | |||
void unsubscribeOutput(int deviceId, midi::Output *output) override { | |||
auto it = outputDevices.find(deviceId); | |||
if (it == outputDevices.end()) | |||
return; | |||
RtMidiOutputDevice *device = it->second; | |||
device->unsubscribe(output); | |||
// Destroy device if nothing is subscribed anymore | |||
if (device->subscribed.empty()) { | |||
outputDevices.erase(it); | |||
delete device; | |||
} | |||
} | |||
}; | |||
void rtmidiInit() { | |||