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