| @@ -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() { | |||