#include #include #include #include namespace rack { namespace gamepad { struct Driver; static const int DRIVER = -10; static Driver* driver = NULL; struct InputDevice : midi::InputDevice { int deviceId; int16_t ccValues[128] = {}; std::string getName() override { const char* name = glfwGetJoystickName(deviceId); if (!name) return ""; return name; } 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 and buttons to MIDI CC // Unfortunately to support 14-bit MIDI CC, only the first 32 CCs can be used. // This could be fixed by continuing with CC 64 if more than 32 CCs are needed. int numCcs = std::min(numAxes + numButtons, 32); for (int i = 0; i < numCcs; i++) { // Allow CC value to go negative int16_t value; if (i < numAxes) { // Axis value = math::clamp((int) std::round(axes[i] * 0x3f80), -0x3f80, 0x3f80); } else { // Button value = buttons[i - numAxes] ? 0x3f80 : 0; } if (value == ccValues[i]) continue; ccValues[i] = value; // Send MSB MIDI message midi::Message msg; msg.setStatus(0xb); msg.setNote(i); // Allow 8th bit to be set to allow bipolar value hack. msg.bytes[2] = (value >> 7); onMessage(msg); // Send LSB MIDI message for axis CCs if (i < numAxes) { midi::Message msg; msg.setStatus(0xb); msg.setNote(i + 32); msg.bytes[2] = (value & 0x7f); onMessage(msg); } } } }; struct Driver : midi::Driver { InputDevice devices[16]; Driver() { for (int i = 0; i < 16; i++) { devices[i].deviceId = i; } } std::string getName() override { return "Gamepad"; } std::vector getInputDeviceIds() override { std::vector deviceIds; for (int i = 0; i < 16; i++) { if (glfwJoystickPresent(i)) { deviceIds.push_back(i); } } return deviceIds; } int getDefaultInputDeviceId() override { return 0; } std::string getInputDeviceName(int deviceId) override { if (!(0 <= deviceId && deviceId < 16)) return ""; const char* name = glfwGetJoystickName(deviceId); if (!name) return string::f("#%d (unavailable)", deviceId + 1); return name; } midi::InputDevice* subscribeInput(int deviceId, midi::Input* input) override { if (!(0 <= deviceId && deviceId < 16)) return NULL; if (!glfwJoystickPresent(deviceId)) 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); } }; void init() { driver = new Driver; midi::addDriver(DRIVER, driver); } void step() { for (int i = 0; i < 16; i++) { if (glfwJoystickPresent(i)) { driver->devices[i].step(); } } } } // namespace gamepad } // namespace rack