mirror of
https://github.com/ravinrabbid/DonCon2040.git
synced 2024-11-20 11:47:07 +01:00
Add MIDI device mode
This commit is contained in:
parent
753a1cd59f
commit
f6a4c80703
@ -46,7 +46,7 @@ extern "C" {
|
||||
//------------- CLASS -------------//
|
||||
#define CFG_TUD_CDC (1)
|
||||
#define CFG_TUD_MSC (0)
|
||||
#define CFG_TUD_MIDI (0)
|
||||
#define CFG_TUD_MIDI (1)
|
||||
#define CFG_TUD_HID (1)
|
||||
#define CFG_TUD_VENDOR (0)
|
||||
|
||||
@ -57,6 +57,10 @@ extern "C" {
|
||||
|
||||
#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
|
||||
}
|
||||
#endif
|
||||
|
43
include/usb/midi_driver.h
Normal file
43
include/usb/midi_driver.h
Normal 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_
|
@ -21,6 +21,7 @@ typedef enum {
|
||||
USB_MODE_PS4_TATACON,
|
||||
USB_MODE_DUALSHOCK4,
|
||||
USB_MODE_XBOX360,
|
||||
USB_MODE_MIDI,
|
||||
USB_MODE_DEBUG,
|
||||
} usb_mode_t;
|
||||
|
||||
@ -34,6 +35,7 @@ enum {
|
||||
USBD_STR_PS3,
|
||||
USBD_STR_PS4,
|
||||
USBD_STR_XINPUT,
|
||||
USBD_STR_MIDI,
|
||||
USBD_STR_RPI_RESET,
|
||||
};
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
#define _UTILS_INPUTSTATE_H_
|
||||
|
||||
#include "usb/hid_driver.h"
|
||||
#include "usb/midi_driver.h"
|
||||
#include "usb/usb_driver.h"
|
||||
#include "usb/xinput_driver.h"
|
||||
|
||||
@ -45,12 +46,14 @@ struct InputState {
|
||||
hid_ps3_report_t m_ps3_report;
|
||||
hid_ps4_report_t m_ps4_report;
|
||||
xinput_report_t m_xinput_report;
|
||||
midi_report_t m_midi_report;
|
||||
std::string m_debug_report;
|
||||
|
||||
usb_report_t getSwitchReport();
|
||||
usb_report_t getPS3InputReport();
|
||||
usb_report_t getPS4InputReport();
|
||||
usb_report_t getXinputReport();
|
||||
usb_report_t getMidiReport();
|
||||
usb_report_t getDebugReport();
|
||||
|
||||
public:
|
||||
|
@ -64,6 +64,7 @@ class Menu {
|
||||
ChangeUsbModePS4Tatacon,
|
||||
ChangeUsbModeDS4,
|
||||
ChangeUsbModeXbox360,
|
||||
ChangeUsbModeMidi,
|
||||
ChangeUsbModeDebug,
|
||||
|
||||
SetTriggerThresholdKaLeft,
|
||||
|
@ -42,6 +42,8 @@ static std::string modeToString(usb_mode_t mode) {
|
||||
return "Dualshock 4";
|
||||
case USB_MODE_XBOX360:
|
||||
return "Xbox 360";
|
||||
case USB_MODE_MIDI:
|
||||
return "MIDI";
|
||||
case USB_MODE_DEBUG:
|
||||
return "Debug";
|
||||
}
|
||||
|
98
src/usb/midi_driver.c
Normal file
98
src/usb/midi_driver.c
Normal 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};
|
@ -1,6 +1,7 @@
|
||||
#include "usb/usb_driver.h"
|
||||
#include "usb/debug_driver.h"
|
||||
#include "usb/hid_driver.h"
|
||||
#include "usb/midi_driver.h"
|
||||
#include "usb/xinput_driver.h"
|
||||
|
||||
#include "bsp/board.h"
|
||||
@ -26,6 +27,7 @@ char *const usbd_desc_str[] = {
|
||||
[USBD_STR_PS3] = USBD_PS3_NAME, //
|
||||
[USBD_STR_PS4] = USBD_PS4_NAME, //
|
||||
[USBD_STR_XINPUT] = USBD_XINPUT_NAME, //
|
||||
[USBD_STR_MIDI] = USBD_MIDI_NAME, //
|
||||
[USBD_STR_CDC] = USBD_DEBUG_CDC_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_receive_report = receive_xinput_report;
|
||||
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:
|
||||
usbd_desc_device = &debug_desc_device;
|
||||
usbd_desc_cfg = debug_desc_cfg;
|
||||
|
@ -10,7 +10,8 @@ InputState::InputState()
|
||||
controller(
|
||||
{{false, false, false, false}, {false, false, false, false, false, false, false, false, false, false}}),
|
||||
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) {
|
||||
switch (mode) {
|
||||
@ -24,10 +25,13 @@ usb_report_t InputState::getReport(usb_mode_t mode) {
|
||||
return getPS4InputReport();
|
||||
case USB_MODE_XBOX360:
|
||||
return getXinputReport();
|
||||
case USB_MODE_MIDI:
|
||||
return getMidiReport();
|
||||
case USB_MODE_DEBUG:
|
||||
default:
|
||||
return getDebugReport();
|
||||
}
|
||||
|
||||
return getDebugReport();
|
||||
}
|
||||
|
||||
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)};
|
||||
}
|
||||
|
||||
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() {
|
||||
std::stringstream out;
|
||||
|
||||
|
@ -23,6 +23,7 @@ const std::map<Menu::Page, const Menu::Descriptor> Menu::descriptors = {
|
||||
{"PS4 Tata", Menu::Descriptor::Action::ChangeUsbModePS4Tatacon}, //
|
||||
{"Dualshock4", Menu::Descriptor::Action::ChangeUsbModeDS4}, //
|
||||
{"Xbox 360", Menu::Descriptor::Action::ChangeUsbModeXbox360}, //
|
||||
{"MIDI", Menu::Descriptor::Action::ChangeUsbModeMidi}, //
|
||||
{"Debug", Menu::Descriptor::Action::ChangeUsbModeDebug}}, //
|
||||
0, //
|
||||
Menu::Page::Main}}, //
|
||||
@ -290,6 +291,10 @@ void Menu::performSelectionAction(Menu::Descriptor::Action action) {
|
||||
m_store->setUsbMode(USB_MODE_XBOX360);
|
||||
gotoPage(descriptor_it->second.parent);
|
||||
break;
|
||||
case Descriptor::Action::ChangeUsbModeMidi:
|
||||
m_store->setUsbMode(USB_MODE_MIDI);
|
||||
gotoPage(descriptor_it->second.parent);
|
||||
break;
|
||||
case Descriptor::Action::ChangeUsbModeDebug:
|
||||
m_store->setUsbMode(USB_MODE_DEBUG);
|
||||
gotoPage(descriptor_it->second.parent);
|
||||
|
Loading…
Reference in New Issue
Block a user