diff --git a/include/gamepad.hpp b/include/gamepad.hpp index de059cd7..21f09303 100644 --- a/include/gamepad.hpp +++ b/include/gamepad.hpp @@ -13,7 +13,7 @@ const int GAMEPAD_DRIVER = -10; struct GamepadInputDevice : MidiInputDevice { int deviceId; std::vector ccs; - std::vector notes; + std::vector states; void step(); }; diff --git a/include/keyboard.hpp b/include/keyboard.hpp new file mode 100644 index 00000000..afedc38a --- /dev/null +++ b/include/keyboard.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include "util/common.hpp" +#include "midi.hpp" + + +namespace rack { + + +const int KEYBOARD_DRIVER = -11; + + +struct KeyboardInputDevice : MidiInputDevice { + int octave = 5; + void processKey(int key, bool released); +}; + + +struct KeyboardInputDriver : MidiInputDriver { + KeyboardInputDevice device; + + std::vector getDeviceIds() override; + std::string getDeviceName(int deviceId) override; + MidiInputDevice *getDevice(int deviceId) override; +}; + + +void keyboardPress(int key); +void keyboardRelease(int key); +KeyboardInputDriver *keyboardGetInputDriver(); + + +} // namespace rack diff --git a/src/gamepad.cpp b/src/gamepad.cpp index b4e4c186..43b771cc 100644 --- a/src/gamepad.cpp +++ b/src/gamepad.cpp @@ -1,5 +1,4 @@ #include "gamepad.hpp" -#include "util/common.hpp" #include @@ -34,14 +33,14 @@ void GamepadInputDevice::step() { } // Convert buttons to MIDI notes - notes.resize(numButtons); + states.resize(numButtons); for (int i = 0; i < numButtons; i++) { - bool note = !!buttons[i]; - if (note != notes[i]) { - notes[i] = note; + bool state = !!buttons[i]; + if (state != states[i]) { + states[i] = state; MidiMessage msg; - msg.cmd = ((note ? 0x9 : 0x8) << 4); + msg.cmd = ((state ? 0x9 : 0x8) << 4); msg.data1 = i; msg.data2 = 127; onMessage(msg); diff --git a/src/keyboard.cpp b/src/keyboard.cpp new file mode 100644 index 00000000..4240d850 --- /dev/null +++ b/src/keyboard.cpp @@ -0,0 +1,115 @@ +#include "keyboard.hpp" +#include + + +namespace rack { + + +void KeyboardInputDevice::processKey(int key, bool released) { + 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: { + if (!released) + octave--; + } break; + case GLFW_KEY_1: { + if (!released) + octave++; + } break; + default: break; + } + + octave = clamp(octave, 0, 9); + if (note < 0) + return; + note += 12 * octave; + + if (note > 127) + return; + + MidiMessage msg; + msg.cmd = ((!released ? 0x9 : 0x8) << 4); + msg.data1 = note; + msg.data2 = 127; + onMessage(msg); +} + + +std::vector KeyboardInputDriver::getDeviceIds() { + return {0}; +} + +std::string KeyboardInputDriver::getDeviceName(int deviceId) { + if (deviceId == 0) + return "QWERTY keyboard (US)"; + return ""; +} + +MidiInputDevice *KeyboardInputDriver::getDevice(int deviceId) { + return &device; +} + + +static KeyboardInputDriver *driver = NULL; + +void keyboardPress(int key) { + if (!driver) + return; + driver->device.processKey(key, false); +} + +void keyboardRelease(int key) { + if (!driver) + return; + driver->device.processKey(key, true); +} + +KeyboardInputDriver *keyboardGetInputDriver() { + // Lazily create driver + if (!driver) { + driver = new KeyboardInputDriver(); + } + return driver; +} + + +} // namespace rack diff --git a/src/midi.cpp b/src/midi.cpp index cf333b8b..d3c74b35 100644 --- a/src/midi.cpp +++ b/src/midi.cpp @@ -2,6 +2,7 @@ #include "rtmidi.hpp" #include "bridge.hpp" #include "gamepad.hpp" +#include "keyboard.hpp" namespace rack { @@ -45,6 +46,7 @@ std::vector MidiIO::getDriverIds() { // Add custom driverIds driverIds.push_back(BRIDGE_DRIVER); driverIds.push_back(GAMEPAD_DRIVER); + driverIds.push_back(KEYBOARD_DRIVER); return driverIds; } @@ -59,6 +61,7 @@ std::string MidiIO::getDriverName(int driverId) { case RtMidi::RTMIDI_DUMMY: return "Dummy MIDI"; case BRIDGE_DRIVER: return "Bridge"; case GAMEPAD_DRIVER: return "Gamepad"; + case KEYBOARD_DRIVER: return "Computer keyboard"; default: return "Unknown"; } } @@ -132,6 +135,9 @@ void MidiInput::setDriverId(int driverId) { else if (driverId == GAMEPAD_DRIVER) { driver = gamepadGetInputDriver(); } + else if (driverId == KEYBOARD_DRIVER) { + driver = keyboardGetInputDriver(); + } // Set driverId if (driver) { diff --git a/src/window.cpp b/src/window.cpp index 0b75c430..337d5c37 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -2,6 +2,7 @@ #include "app.hpp" #include "asset.hpp" #include "gamepad.hpp" +#include "keyboard.hpp" #include "util/color.hpp" #include @@ -262,6 +263,16 @@ void charCallback(GLFWwindow *window, unsigned int codepoint) { } void keyCallback(GLFWwindow *window, int key, int scancode, int action, int mods) { + // Keyboard MIDI driver + if (1 || glfwGetInputMode(gWindow, GLFW_LOCK_KEY_MODS) & GLFW_MOD_CAPS_LOCK) { + if (action == GLFW_PRESS) { + keyboardPress(key); + } + else if (action == GLFW_RELEASE) { + keyboardRelease(key); + } + } + if (action == GLFW_PRESS || action == GLFW_REPEAT) { if (gFocusedWidget) { // onKey