From 987288fd41f3a67ec1f3d27f4efa3b3ca5491139 Mon Sep 17 00:00:00 2001 From: Ashley Strahle Date: Tue, 23 Jun 2026 13:10:25 +0000 Subject: [PATCH] Fix USB MIDI: use Adafruit TinyUSB Library for proper MIDI support - Add Adafruit TinyUSB Library dependency - Create tusb_config.h enabling MIDI class - USB device named 'Loopy Foot Controller' - is_connected() check before sending --- include/midi_transport.h | 2 + platformio.ini | 5 +- src/midi_transport.cpp | 138 +++++++++++++++++++++------------------ src/tusb_config.h | 21 ++++++ 4 files changed, 100 insertions(+), 66 deletions(-) create mode 100644 src/tusb_config.h diff --git a/include/midi_transport.h b/include/midi_transport.h index 1f5fdbd..c38cff0 100644 --- a/include/midi_transport.h +++ b/include/midi_transport.h @@ -40,4 +40,6 @@ public: private: std::function receive_callback; bool initialized; + + void parse_midi_packet(const uint8_t* buffer, uint32_t size, MidiEvent& event); }; diff --git a/platformio.ini b/platformio.ini index 745038a..79713ed 100644 --- a/platformio.ini +++ b/platformio.ini @@ -7,10 +7,11 @@ board = esp32-s3-devkitc-1 framework = arduino lib_deps = - lathoub/ArduinoUSBMIDI@^1.0.0 + adafruit/Adafruit TinyUSB Library@^2.0.0 + adafruit/Adafruit NeoPixel build_flags = - -DARDUINO_USB_MODE=1 + -DARDUINO_USB_MODE=0 -DARDUINO_USB_CDC_ON_BOOT=1 monitor_speed = 115200 diff --git a/src/midi_transport.cpp b/src/midi_transport.cpp index a55c54f..e810ff4 100644 --- a/src/midi_transport.cpp +++ b/src/midi_transport.cpp @@ -1,8 +1,8 @@ #include "midi_transport.h" #include -#include +#include "Adafruit_TinyUSB.h" -static USBMIDI MIDI; +static Adafruit_USBD_MIDI usb_midi; UsbMidiTransport::UsbMidiTransport() : initialized(false) { } @@ -11,95 +11,105 @@ UsbMidiTransport::~UsbMidiTransport() { } bool UsbMidiTransport::begin() { - MIDI.begin(MIDI_CHANNEL_OMNI); + Serial.println("[MIDI] Initializing USB MIDI..."); + + USBDevice.setManufacturerDescriptor("Ashley Strahle"); + USBDevice.setProductDescriptor("Loopy Foot Controller"); + USBDevice.setSerialDescriptor("LFMIDI001"); + + if (!usb_midi.begin(32)) { + Serial.println("[MIDI] ERROR: USB MIDI init failed"); + return false; + } + + USB.begin(); initialized = true; - Serial.println("[MIDI] USB MIDI initialized (ArduinoUSBMIDI)"); + Serial.println("[MIDI] USB MIDI initialized - waiting for host"); return true; } void UsbMidiTransport::update() { if (!initialized) return; - MIDI.read(); + while (usb_midi.available()) { + uint8_t packet[4]; + if (usb_midi.readPacket(packet)) { + MidiEvent event; + parse_midi_packet(packet, 4, event); + + const char* type_str = "UNK"; + switch (event.type) { + case MidiEvent::NOTE_ON: type_str = "NOTE_ON"; break; + case MidiEvent::NOTE_OFF: type_str = "NOTE_OFF"; break; + case MidiEvent::CONTROL_CHANGE: type_str = "CC"; break; + case MidiEvent::PROGRAM_CHANGE: type_str = "PC"; break; + case MidiEvent::PITCH_BEND: type_str = "PB"; break; + default: break; + } + Serial.printf("[MIDI IN] Ch:%d %s:%d:%d\n", event.channel, type_str, event.data1, event.data2); + + if (receive_callback) { + receive_callback(event); + } + } + } } void UsbMidiTransport::on_midi_receive(std::function callback) { receive_callback = callback; - - MIDI.setHandleNoteOn([this](uint8_t channel, uint8_t note, uint8_t velocity) { - MidiEvent event; - event.type = (velocity > 0) ? MidiEvent::NOTE_ON : MidiEvent::NOTE_OFF; - event.channel = channel; - event.data1 = note; - event.data2 = velocity; - event.timestamp = millis(); - Serial.printf("[MIDI IN] Ch:%d NOTE_ON:%d:%d\n", channel, note, velocity); - if (receive_callback) receive_callback(event); - }); - - MIDI.setHandleNoteOff([this](uint8_t channel, uint8_t note, uint8_t velocity) { - MidiEvent event; - event.type = MidiEvent::NOTE_OFF; - event.channel = channel; - event.data1 = note; - event.data2 = velocity; - event.timestamp = millis(); - Serial.printf("[MIDI IN] Ch:%d NOTE_OFF:%d:%d\n", channel, note, velocity); - if (receive_callback) receive_callback(event); - }); - - MIDI.setHandleControlChange([this](uint8_t channel, uint8_t cc, uint8_t value) { - MidiEvent event; - event.type = MidiEvent::CONTROL_CHANGE; - event.channel = channel; - event.data1 = cc; - event.data2 = value; - event.timestamp = millis(); - Serial.printf("[MIDI IN] Ch:%d CC:%d:%d\n", channel, cc, value); - if (receive_callback) receive_callback(event); - }); - - MIDI.setHandleProgramChange([this](uint8_t channel, uint8_t program) { - MidiEvent event; - event.type = MidiEvent::PROGRAM_CHANGE; - event.channel = channel; - event.data1 = program; - event.data2 = 0; - event.timestamp = millis(); - Serial.printf("[MIDI IN] Ch:%d PC:%d\n", channel, program); - if (receive_callback) receive_callback(event); - }); - - MIDI.setHandlePitchBend([this](uint8_t channel, int bend) { - MidiEvent event; - event.type = MidiEvent::PITCH_BEND; - event.channel = channel; - event.data1 = bend & 0x7F; - event.data2 = (bend >> 7) & 0x7F; - event.timestamp = millis(); - Serial.printf("[MIDI IN] Ch:%d PB:%d\n", channel, bend); - if (receive_callback) receive_callback(event); - }); } void UsbMidiTransport::send_note_on(uint8_t channel, uint8_t note, uint8_t velocity) { if (!initialized) return; - MIDI.sendNoteOn(note, velocity, channel); + usb_midi.sendNoteOn(note, velocity, channel); Serial.printf("[MIDI OUT] Ch:%d NOTE_ON:%d:%d\n", channel, note, velocity); } void UsbMidiTransport::send_note_off(uint8_t channel, uint8_t note, uint8_t velocity) { if (!initialized) return; - MIDI.sendNoteOff(note, velocity, channel); + usb_midi.sendNoteOff(note, velocity, channel); Serial.printf("[MIDI OUT] Ch:%d NOTE_OFF:%d:%d\n", channel, note, velocity); } void UsbMidiTransport::send_cc(uint8_t channel, uint8_t cc, uint8_t value) { if (!initialized) return; - MIDI.sendControlChange(cc, value, channel); + usb_midi.sendControlChange(cc, value, channel); Serial.printf("[MIDI OUT] Ch:%d CC:%d:%d\n", channel, cc, value); } bool UsbMidiTransport::is_connected() { - return initialized; + return initialized && usb_midi.isMounted(); +} + +void UsbMidiTransport::parse_midi_packet(const uint8_t* buffer, uint32_t size, MidiEvent& event) { + if (size < 4) return; + + uint8_t cin = buffer[0] & 0x0F; + uint8_t status = buffer[1]; + uint8_t type = status & 0xF0; + uint8_t channel = (status & 0x0F) + 1; + + event.channel = channel; + event.data1 = buffer[2]; + event.data2 = buffer[3]; + + switch (cin) { + case 0x8: event.type = MidiEvent::NOTE_OFF; break; + case 0x9: + event.type = (event.data2 > 0) ? MidiEvent::NOTE_ON : MidiEvent::NOTE_OFF; + break; + case 0xB: event.type = MidiEvent::CONTROL_CHANGE; break; + case 0xC: event.type = MidiEvent::PROGRAM_CHANGE; break; + case 0xE: event.type = MidiEvent::PITCH_BEND; break; + default: + switch (type) { + case 0x80: event.type = MidiEvent::NOTE_OFF; break; + case 0x90: event.type = (event.data2 > 0) ? MidiEvent::NOTE_ON : MidiEvent::NOTE_OFF; break; + case 0xB0: event.type = MidiEvent::CONTROL_CHANGE; break; + case 0xC0: event.type = MidiEvent::PROGRAM_CHANGE; break; + case 0xE0: event.type = MidiEvent::PITCH_BEND; break; + default: event.type = MidiEvent::NOTE_ON; break; + } + break; + } } diff --git a/src/tusb_config.h b/src/tusb_config.h new file mode 100644 index 0000000..ea8d6fa --- /dev/null +++ b/src/tusb_config.h @@ -0,0 +1,21 @@ +#ifndef TUSB_CONFIG_H +#define TUSB_CONFIG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define CFG_TUD_MIDI 1 +#define CFG_TUD_MIDI_RX_BUFSIZE 64 +#define CFG_TUD_MIDI_TX_BUFSIZE 64 + +#define CFG_TUD_CDC 0 +#define CFG_TUD_MSC 0 +#define CFG_TUD_HID 0 +#define CFG_TUD_NET 0 + +#ifdef __cplusplus +} +#endif + +#endif