@@ -1,27 +1,11 @@ | |||||
#pragma once | #pragma once | ||||
#include "bridgeprotocol.hpp" | #include "bridgeprotocol.hpp" | ||||
#include "audio.hpp" | #include "audio.hpp" | ||||
#include "midi.hpp" | |||||
namespace rack { | 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 bridgeInit(); | ||||
void bridgeDestroy(); | void bridgeDestroy(); | ||||
void bridgeAudioSubscribe(int channel, audio::IO *audio); | void bridgeAudioSubscribe(int channel, audio::IO *audio); | ||||
@@ -1,32 +1,11 @@ | |||||
#pragma once | #pragma once | ||||
#include "common.hpp" | #include "common.hpp" | ||||
#include "midi.hpp" | |||||
namespace rack { | namespace rack { | ||||
namespace gamepad { | 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 init(); | ||||
void step(); | void step(); | ||||
@@ -1,32 +1,11 @@ | |||||
#pragma once | #pragma once | ||||
#include "common.hpp" | #include "common.hpp" | ||||
#include "midi.hpp" | |||||
#include <map> | |||||
namespace rack { | namespace rack { | ||||
namespace keyboard { | 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 init(); | ||||
void press(int key); | void press(int key); | ||||
void release(int key); | void release(int key); | ||||
@@ -1,44 +1,10 @@ | |||||
#pragma once | #pragma once | ||||
#include "common.hpp" | #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 { | 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(); | void rtmidiInit(); | ||||
@@ -1,4 +1,5 @@ | |||||
#include "bridge.hpp" | #include "bridge.hpp" | ||||
#include "midi.hpp" | |||||
#include "string.hpp" | #include "string.hpp" | ||||
#include "dsp/ringbuffer.hpp" | #include "dsp/ringbuffer.hpp" | ||||
@@ -19,6 +20,9 @@ | |||||
namespace rack { | namespace rack { | ||||
struct BridgeMidiDriver; | |||||
struct BridgeClientConnection; | struct BridgeClientConnection; | ||||
static BridgeClientConnection *connections[BRIDGE_NUM_PORTS] = {}; | static BridgeClientConnection *connections[BRIDGE_NUM_PORTS] = {}; | ||||
static audio::IO *audioListeners[BRIDGE_NUM_PORTS] = {}; | static audio::IO *audioListeners[BRIDGE_NUM_PORTS] = {}; | ||||
@@ -27,6 +31,46 @@ static bool serverRunning = false; | |||||
static BridgeMidiDriver *driver = NULL; | 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 { | struct BridgeClientConnection { | ||||
int client; | int client; | ||||
bool ready = false; | 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() { | void bridgeInit() { | ||||
serverRunning = true; | serverRunning = true; | ||||
serverThread = std::thread(serverRun); | serverThread = std::thread(serverRun); | ||||
@@ -1,4 +1,5 @@ | |||||
#include "gamepad.hpp" | #include "gamepad.hpp" | ||||
#include "midi.hpp" | |||||
#include "string.hpp" | #include "string.hpp" | ||||
#include "window.hpp" | #include "window.hpp" | ||||
@@ -7,95 +8,110 @@ namespace rack { | |||||
namespace gamepad { | namespace gamepad { | ||||
struct Driver; | |||||
static const int DRIVER = -10; | static const int DRIVER = -10; | ||||
static Driver *driver = NULL; | 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() { | void init() { | ||||
@@ -1,122 +1,137 @@ | |||||
#include "keyboard.hpp" | #include "keyboard.hpp" | ||||
#include "midi.hpp" | |||||
#include "window.hpp" | #include "window.hpp" | ||||
#include <map> | |||||
namespace rack { | namespace rack { | ||||
namespace keyboard { | 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; | midi::Message msg; | ||||
msg.cmd = 0x8 << 4; | |||||
msg.cmd = 0x9 << 4; | |||||
msg.data1 = note; | msg.data1 = note; | ||||
msg.data2 = 127; | msg.data2 = 127; | ||||
onMessage(msg); | 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() { | void init() { | ||||
@@ -1,6 +1,7 @@ | |||||
#include "common.hpp" | #include "common.hpp" | ||||
#include "random.hpp" | #include "random.hpp" | ||||
#include "asset.hpp" | #include "asset.hpp" | ||||
#include "midi.hpp" | |||||
#include "rtmidi.hpp" | #include "rtmidi.hpp" | ||||
#include "keyboard.hpp" | #include "keyboard.hpp" | ||||
#include "gamepad.hpp" | #include "gamepad.hpp" | ||||
@@ -1,107 +1,190 @@ | |||||
#include "rtmidi.hpp" | #include "rtmidi.hpp" | ||||
#include "midi.hpp" | |||||
#include <map> | #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 { | 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]; | msg.cmd = (*message)[0]; | ||||
if (message->size() >= 2) | |||||
msg.data1 = (*message)[1]; | msg.data1 = (*message)[1]; | ||||
if (message->size() >= 3) | |||||
msg.data2 = (*message)[2]; | 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() { | void rtmidiInit() { | ||||