Add MIDI device mode

This commit is contained in:
Frederik Walk 2023-07-17 22:50:27 +02:00
parent 753a1cd59f
commit f6a4c80703
10 changed files with 226 additions and 3 deletions

View File

@ -46,7 +46,7 @@ extern "C" {
//------------- CLASS -------------// //------------- CLASS -------------//
#define CFG_TUD_CDC (1) #define CFG_TUD_CDC (1)
#define CFG_TUD_MSC (0) #define CFG_TUD_MSC (0)
#define CFG_TUD_MIDI (0) #define CFG_TUD_MIDI (1)
#define CFG_TUD_HID (1) #define CFG_TUD_HID (1)
#define CFG_TUD_VENDOR (0) #define CFG_TUD_VENDOR (0)
@ -57,6 +57,10 @@ extern "C" {
#define CFG_TUD_HID_EP_BUFSIZE (64) #define CFG_TUD_HID_EP_BUFSIZE (64)
#define CFG_TUD_MIDI_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
#define CFG_TUD_MIDI_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
#define CFG_TUD_MIDI_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

43
include/usb/midi_driver.h Normal file
View File

@ -0,0 +1,43 @@
#ifndef _USB_MIDI_DRIVER_H_
#define _USB_MIDI_DRIVER_H_
#include "usb/usb_driver.h"
#include "device/usbd_pvt.h"
#include <stdint.h>
#define USBD_MIDI_NAME "MIDI Controller"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct __attribute((packed, aligned(1))) {
struct {
bool acoustic_bass_drum;
bool electric_bass_drum;
bool drumsticks;
bool side_stick;
} status;
struct {
uint8_t acoustic_bass_drum;
uint8_t electric_bass_drum;
uint8_t drumsticks;
uint8_t side_stick;
} velocity;
} midi_report_t;
bool receive_midi_report(void);
bool send_midi_report(usb_report_t report);
extern const tusb_desc_device_t midi_desc_device;
extern const uint8_t midi_desc_cfg[];
extern const usbd_class_driver_t midi_app_driver;
#ifdef __cplusplus
}
#endif
#endif // _USB_MIDI_DRIVER_H_

View File

@ -21,6 +21,7 @@ typedef enum {
USB_MODE_PS4_TATACON, USB_MODE_PS4_TATACON,
USB_MODE_DUALSHOCK4, USB_MODE_DUALSHOCK4,
USB_MODE_XBOX360, USB_MODE_XBOX360,
USB_MODE_MIDI,
USB_MODE_DEBUG, USB_MODE_DEBUG,
} usb_mode_t; } usb_mode_t;
@ -34,6 +35,7 @@ enum {
USBD_STR_PS3, USBD_STR_PS3,
USBD_STR_PS4, USBD_STR_PS4,
USBD_STR_XINPUT, USBD_STR_XINPUT,
USBD_STR_MIDI,
USBD_STR_RPI_RESET, USBD_STR_RPI_RESET,
}; };

View File

@ -2,6 +2,7 @@
#define _UTILS_INPUTSTATE_H_ #define _UTILS_INPUTSTATE_H_
#include "usb/hid_driver.h" #include "usb/hid_driver.h"
#include "usb/midi_driver.h"
#include "usb/usb_driver.h" #include "usb/usb_driver.h"
#include "usb/xinput_driver.h" #include "usb/xinput_driver.h"
@ -45,12 +46,14 @@ struct InputState {
hid_ps3_report_t m_ps3_report; hid_ps3_report_t m_ps3_report;
hid_ps4_report_t m_ps4_report; hid_ps4_report_t m_ps4_report;
xinput_report_t m_xinput_report; xinput_report_t m_xinput_report;
midi_report_t m_midi_report;
std::string m_debug_report; std::string m_debug_report;
usb_report_t getSwitchReport(); usb_report_t getSwitchReport();
usb_report_t getPS3InputReport(); usb_report_t getPS3InputReport();
usb_report_t getPS4InputReport(); usb_report_t getPS4InputReport();
usb_report_t getXinputReport(); usb_report_t getXinputReport();
usb_report_t getMidiReport();
usb_report_t getDebugReport(); usb_report_t getDebugReport();
public: public:

View File

@ -64,6 +64,7 @@ class Menu {
ChangeUsbModePS4Tatacon, ChangeUsbModePS4Tatacon,
ChangeUsbModeDS4, ChangeUsbModeDS4,
ChangeUsbModeXbox360, ChangeUsbModeXbox360,
ChangeUsbModeMidi,
ChangeUsbModeDebug, ChangeUsbModeDebug,
SetTriggerThresholdKaLeft, SetTriggerThresholdKaLeft,

View File

@ -42,6 +42,8 @@ static std::string modeToString(usb_mode_t mode) {
return "Dualshock 4"; return "Dualshock 4";
case USB_MODE_XBOX360: case USB_MODE_XBOX360:
return "Xbox 360"; return "Xbox 360";
case USB_MODE_MIDI:
return "MIDI";
case USB_MODE_DEBUG: case USB_MODE_DEBUG:
return "Debug"; return "Debug";
} }

98
src/usb/midi_driver.c Normal file
View File

@ -0,0 +1,98 @@
#include "usb/midi_driver.h"
#include "usb/usb_driver.h"
#include "class/midi/midi_device.h"
#include "tusb.h"
const tusb_desc_device_t midi_desc_device = {
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0200,
.bDeviceClass = 0x00,
.bDeviceSubClass = 0x00,
.bDeviceProtocol = 0x00,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = 0x1209,
.idProduct = 0x3939,
.bcdDevice = 0x0100,
.iManufacturer = USBD_STR_MANUFACTURER,
.iProduct = USBD_STR_PRODUCT,
.iSerialNumber = USBD_STR_SERIAL,
.bNumConfigurations = 1,
};
enum {
USBD_ITF_MIDI,
USBD_ITF_MIDI_STREAMING,
USBD_ITF_MAX,
};
#define EPNUM_MIDI_OUT 0x01
#define EPNUM_MIDI_IN 0x01
#define USBD_DESC_LEN (TUD_CONFIG_DESC_LEN + TUD_MIDI_DESC_LEN)
const uint8_t midi_desc_cfg[USBD_DESC_LEN] = {
TUD_CONFIG_DESCRIPTOR(1, USBD_ITF_MAX, USBD_STR_LANGUAGE, USBD_DESC_LEN, 0, USBD_MAX_POWER_MAX),
TUD_MIDI_DESCRIPTOR(USBD_ITF_MIDI, 0, EPNUM_MIDI_OUT, (0x80 | EPNUM_MIDI_IN), CFG_TUD_MIDI_EP_BUFSIZE),
};
static midi_report_t last_report = {};
bool receive_midi_report(void) {
// Read and discard incoming data to avoid blocking the sender
uint8_t packet[4];
while (tud_midi_available()) {
tud_midi_packet_read(packet);
}
return true;
}
static void write_midi_message(uint8_t status, uint8_t byte1, uint8_t byte2) {
uint8_t midi_message[3] = {status, byte1, byte2};
tud_midi_stream_write(0, midi_message, sizeof(midi_message));
}
static void set_note(uint8_t channel, bool on, uint8_t pitch, uint8_t velocity) {
uint8_t status = on ? (0x90 | channel) : (0x80 | channel);
velocity = velocity > 127 ? 127 : velocity;
write_midi_message(status, pitch, velocity);
}
bool send_midi_report(usb_report_t report) {
static uint8_t percussion_channel = 9;
midi_report_t *midi_report = (midi_report_t *)report.data;
if (midi_report->status.acoustic_bass_drum != last_report.status.acoustic_bass_drum) {
set_note(percussion_channel, midi_report->status.acoustic_bass_drum, 35,
midi_report->velocity.acoustic_bass_drum);
}
if (midi_report->status.electric_bass_drum != last_report.status.electric_bass_drum) {
set_note(percussion_channel, midi_report->status.electric_bass_drum, 36,
midi_report->velocity.electric_bass_drum);
}
if (midi_report->status.drumsticks != last_report.status.drumsticks) {
set_note(percussion_channel, midi_report->status.drumsticks, 31, midi_report->velocity.drumsticks);
}
if (midi_report->status.side_stick != last_report.status.side_stick) {
set_note(percussion_channel, midi_report->status.side_stick, 37, midi_report->velocity.side_stick);
}
memcpy(&last_report, report.data, tu_min16(report.size, sizeof(midi_report_t)));
return true;
}
const usbd_class_driver_t midi_app_driver = {
#if CFG_TUSB_DEBUG >= 2
.name = "MIDI",
#endif
.init = midid_init,
.reset = midid_reset,
.open = midid_open,
.control_xfer_cb = midid_control_xfer_cb,
.xfer_cb = midid_xfer_cb,
.sof = NULL};

View File

@ -1,6 +1,7 @@
#include "usb/usb_driver.h" #include "usb/usb_driver.h"
#include "usb/debug_driver.h" #include "usb/debug_driver.h"
#include "usb/hid_driver.h" #include "usb/hid_driver.h"
#include "usb/midi_driver.h"
#include "usb/xinput_driver.h" #include "usb/xinput_driver.h"
#include "bsp/board.h" #include "bsp/board.h"
@ -26,6 +27,7 @@ char *const usbd_desc_str[] = {
[USBD_STR_PS3] = USBD_PS3_NAME, // [USBD_STR_PS3] = USBD_PS3_NAME, //
[USBD_STR_PS4] = USBD_PS4_NAME, // [USBD_STR_PS4] = USBD_PS4_NAME, //
[USBD_STR_XINPUT] = USBD_XINPUT_NAME, // [USBD_STR_XINPUT] = USBD_XINPUT_NAME, //
[USBD_STR_MIDI] = USBD_MIDI_NAME, //
[USBD_STR_CDC] = USBD_DEBUG_CDC_NAME, // [USBD_STR_CDC] = USBD_DEBUG_CDC_NAME, //
[USBD_STR_RPI_RESET] = USBD_DEBUG_RESET_NAME, // [USBD_STR_RPI_RESET] = USBD_DEBUG_RESET_NAME, //
}; };
@ -81,6 +83,13 @@ void usb_driver_init(usb_mode_t mode) {
usbd_send_report = send_xinput_report; usbd_send_report = send_xinput_report;
usbd_receive_report = receive_xinput_report; usbd_receive_report = receive_xinput_report;
break; break;
case USB_MODE_MIDI:
usbd_desc_device = &midi_desc_device;
usbd_desc_cfg = midi_desc_cfg;
usbd_app_driver = &midi_app_driver;
usbd_send_report = send_midi_report;
usbd_receive_report = receive_midi_report;
break;
case USB_MODE_DEBUG: case USB_MODE_DEBUG:
usbd_desc_device = &debug_desc_device; usbd_desc_device = &debug_desc_device;
usbd_desc_cfg = debug_desc_cfg; usbd_desc_cfg = debug_desc_cfg;

View File

@ -10,7 +10,8 @@ InputState::InputState()
controller( controller(
{{false, false, false, false}, {false, false, false, false, false, false, false, false, false, false}}), {{false, false, false, false}, {false, false, false, false, false, false, false, false, false, false}}),
m_switch_report({}), m_ps3_report({}), m_ps4_report({}), m_switch_report({}), m_ps3_report({}), m_ps4_report({}),
m_xinput_report({0x00, sizeof(xinput_report_t), 0, 0, 0, 0, 0, 0, 0, 0, {}}) {} m_xinput_report({0x00, sizeof(xinput_report_t), 0, 0, 0, 0, 0, 0, 0, 0, {}}),
m_midi_report({{false, false, false, false}, {0, 0, 0, 0}}) {}
usb_report_t InputState::getReport(usb_mode_t mode) { usb_report_t InputState::getReport(usb_mode_t mode) {
switch (mode) { switch (mode) {
@ -24,10 +25,13 @@ usb_report_t InputState::getReport(usb_mode_t mode) {
return getPS4InputReport(); return getPS4InputReport();
case USB_MODE_XBOX360: case USB_MODE_XBOX360:
return getXinputReport(); return getXinputReport();
case USB_MODE_MIDI:
return getMidiReport();
case USB_MODE_DEBUG: case USB_MODE_DEBUG:
default:
return getDebugReport(); return getDebugReport();
} }
return getDebugReport();
} }
static uint8_t getHidHat(const InputState::Controller::DPad dpad) { static uint8_t getHidHat(const InputState::Controller::DPad dpad) {
@ -235,6 +239,58 @@ usb_report_t InputState::getXinputReport() {
return {(uint8_t *)&m_xinput_report, sizeof(xinput_report_t)}; return {(uint8_t *)&m_xinput_report, sizeof(xinput_report_t)};
} }
usb_report_t InputState::getMidiReport() {
struct state {
bool last_triggered;
bool on;
uint16_t velocity;
};
static state acoustic_bass_drum = {};
static state electric_bass_drum = {};
static state drumsticks = {};
static state side_stick = {};
auto set_state = [](state &target, const Drum::Pad &new_state) {
if (new_state.triggered && !target.last_triggered) {
target.velocity = 0;
target.on = false;
} else if (!new_state.triggered && target.last_triggered) {
target.on = true;
} else if (!new_state.triggered && !target.last_triggered) {
target.on = false;
}
if (new_state.triggered && new_state.raw > target.velocity) {
target.velocity = new_state.raw;
}
target.last_triggered = new_state.triggered;
};
set_state(acoustic_bass_drum, drum.don_left);
set_state(electric_bass_drum, drum.don_right);
set_state(drumsticks, drum.ka_left);
set_state(side_stick, drum.ka_right);
m_midi_report.status.acoustic_bass_drum = acoustic_bass_drum.on;
m_midi_report.status.electric_bass_drum = electric_bass_drum.on;
m_midi_report.status.drumsticks = drumsticks.on;
m_midi_report.status.side_stick = side_stick.on;
auto convert_range = [](uint16_t in) {
uint16_t out = in / 16;
return uint8_t(out > 127 ? 127 : out);
};
m_midi_report.velocity.acoustic_bass_drum = convert_range(acoustic_bass_drum.velocity);
m_midi_report.velocity.electric_bass_drum = convert_range(electric_bass_drum.velocity);
m_midi_report.velocity.drumsticks = convert_range(drumsticks.velocity);
m_midi_report.velocity.side_stick = convert_range(side_stick.velocity);
return {(uint8_t *)&m_midi_report, sizeof(midi_report_t)};
}
usb_report_t InputState::getDebugReport() { usb_report_t InputState::getDebugReport() {
std::stringstream out; std::stringstream out;

View File

@ -23,6 +23,7 @@ const std::map<Menu::Page, const Menu::Descriptor> Menu::descriptors = {
{"PS4 Tata", Menu::Descriptor::Action::ChangeUsbModePS4Tatacon}, // {"PS4 Tata", Menu::Descriptor::Action::ChangeUsbModePS4Tatacon}, //
{"Dualshock4", Menu::Descriptor::Action::ChangeUsbModeDS4}, // {"Dualshock4", Menu::Descriptor::Action::ChangeUsbModeDS4}, //
{"Xbox 360", Menu::Descriptor::Action::ChangeUsbModeXbox360}, // {"Xbox 360", Menu::Descriptor::Action::ChangeUsbModeXbox360}, //
{"MIDI", Menu::Descriptor::Action::ChangeUsbModeMidi}, //
{"Debug", Menu::Descriptor::Action::ChangeUsbModeDebug}}, // {"Debug", Menu::Descriptor::Action::ChangeUsbModeDebug}}, //
0, // 0, //
Menu::Page::Main}}, // Menu::Page::Main}}, //
@ -290,6 +291,10 @@ void Menu::performSelectionAction(Menu::Descriptor::Action action) {
m_store->setUsbMode(USB_MODE_XBOX360); m_store->setUsbMode(USB_MODE_XBOX360);
gotoPage(descriptor_it->second.parent); gotoPage(descriptor_it->second.parent);
break; break;
case Descriptor::Action::ChangeUsbModeMidi:
m_store->setUsbMode(USB_MODE_MIDI);
gotoPage(descriptor_it->second.parent);
break;
case Descriptor::Action::ChangeUsbModeDebug: case Descriptor::Action::ChangeUsbModeDebug:
m_store->setUsbMode(USB_MODE_DEBUG); m_store->setUsbMode(USB_MODE_DEBUG);
gotoPage(descriptor_it->second.parent); gotoPage(descriptor_it->second.parent);