You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

135 lines
2.6KB

  1. #include "gamepad.hpp"
  2. #include "midi.hpp"
  3. #include "string.hpp"
  4. #include "window.hpp"
  5. namespace rack {
  6. namespace gamepad {
  7. struct Driver;
  8. static const int DRIVER = -10;
  9. static Driver *driver = NULL;
  10. struct InputDevice : midi::InputDevice {
  11. int deviceId;
  12. std::vector<uint8_t> ccs;
  13. std::vector<bool> states;
  14. void step() {
  15. if (!glfwJoystickPresent(deviceId))
  16. return;
  17. // Get gamepad state
  18. int numAxes;
  19. const float *axes = glfwGetJoystickAxes(deviceId, &numAxes);
  20. int numButtons;
  21. const unsigned char *buttons = glfwGetJoystickButtons(deviceId, &numButtons);
  22. // Convert axes to MIDI CC
  23. ccs.resize(numAxes);
  24. for (int i = 0; i < numAxes; i++) {
  25. // Allow CC value to go negative, but clamp at -127 instead of -128 for symmetry
  26. int8_t cc = math::clamp((int) std::round(axes[i] * 127), -127, 127);
  27. if (cc != ccs[i]) {
  28. ccs[i] = cc;
  29. // Send MIDI message
  30. midi::Message msg;
  31. // MIDI channel 1
  32. msg.cmd = (0xb << 4) | 0;
  33. msg.data1 = i;
  34. msg.data2 = ccs[i];
  35. onMessage(msg);
  36. }
  37. }
  38. // Convert buttons to MIDI notes
  39. states.resize(numButtons);
  40. for (int i = 0; i < numButtons; i++) {
  41. bool state = !!buttons[i];
  42. if (state != states[i]) {
  43. states[i] = state;
  44. midi::Message msg;
  45. msg.cmd = ((state ? 0x9 : 0x8) << 4);
  46. msg.data1 = i;
  47. msg.data2 = 127;
  48. onMessage(msg);
  49. }
  50. }
  51. }
  52. };
  53. struct Driver : midi::Driver {
  54. InputDevice devices[16];
  55. Driver() {
  56. for (int i = 0; i < 16; i++) {
  57. devices[i].deviceId = i;
  58. }
  59. }
  60. std::string getName() override {return "Gamepad";}
  61. std::vector<int> getInputDeviceIds() override {
  62. std::vector<int> deviceIds;
  63. for (int i = 0; i < 16; i++) {
  64. if (glfwJoystickPresent(i)) {
  65. deviceIds.push_back(i);
  66. }
  67. }
  68. return deviceIds;
  69. }
  70. std::string getInputDeviceName(int deviceId) override {
  71. if (!(0 <= deviceId && deviceId < 16))
  72. return "";
  73. const char *name = glfwGetJoystickName(deviceId);
  74. if (name) {
  75. return name;
  76. }
  77. return string::f(" %d (unavailable)", deviceId + 1);
  78. }
  79. midi::InputDevice *subscribeInput(int deviceId, midi::Input *input) override {
  80. if (!(0 <= deviceId && deviceId < 16))
  81. return NULL;
  82. devices[deviceId].subscribe(input);
  83. return &devices[deviceId];
  84. }
  85. void unsubscribeInput(int deviceId, midi::Input *input) override {
  86. if (!(0 <= deviceId && deviceId < 16))
  87. return;
  88. devices[deviceId].unsubscribe(input);
  89. }
  90. };
  91. void init() {
  92. driver = new Driver;
  93. midi::addDriver(DRIVER, driver);
  94. }
  95. void step() {
  96. if (!driver)
  97. return;
  98. for (int i = 0; i < 16; i++) {
  99. if (glfwJoystickPresent(i)) {
  100. driver->devices[i].step();
  101. }
  102. }
  103. }
  104. } // namespace gamepad
  105. } // namespace rack