From a209b48720c9bc086bdf87265257a1b6c3158e87 Mon Sep 17 00:00:00 2001 From: Ashley Strahle Date: Thu, 25 Jun 2026 22:51:44 +0000 Subject: [PATCH] Add second USB MIDI interface (LPX MIDI) for Programmer mode - Interface 1 (DAW): Button presses -> Loopy Pro - Interface 2 (MIDI): SysEx + LED control <- Loopy Pro - Both interfaces polled in update() - Sends on DAW interface (like real Launchpad X) --- src/midi_transport.cpp | 67 ++++++++++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 18 deletions(-) diff --git a/src/midi_transport.cpp b/src/midi_transport.cpp index e498760..68b8df2 100644 --- a/src/midi_transport.cpp +++ b/src/midi_transport.cpp @@ -2,7 +2,8 @@ #include #include "Adafruit_TinyUSB.h" -static Adafruit_USBD_MIDI usb_midi; +static Adafruit_USBD_MIDI usb_midi_daw; // Interface 1: DAW/Session +static Adafruit_USBD_MIDI usb_midi_midi; // Interface 2: MIDI/Programmer UsbMidiTransport::UsbMidiTransport() : initialized(false) { } @@ -11,27 +12,32 @@ UsbMidiTransport::~UsbMidiTransport() { } bool UsbMidiTransport::begin() { - Serial.println("[MIDI] Setting up USB MIDI device..."); + Serial.println("[MIDI] Setting up USB MIDI device (dual interface)..."); - TinyUSBDevice.setManufacturerDescriptor("Ashley Strahle"); - TinyUSBDevice.setProductDescriptor("Loopy Foot Controller"); - TinyUSBDevice.setSerialDescriptor("LFMIDI001"); + TinyUSBDevice.setManufacturerDescriptor("Novation"); + TinyUSBDevice.setProductDescriptor("Launchpad X"); + TinyUSBDevice.setSerialDescriptor("LPX00001"); TinyUSBDevice.begin(0); - if (!usb_midi.begin()) { - Serial.println("[MIDI] ERROR: USB MIDI init failed"); + // Initialize both MIDI interfaces + if (!usb_midi_daw.begin()) { + Serial.println("[MIDI] ERROR: DAW MIDI init failed"); + return false; + } + if (!usb_midi_midi.begin()) { + Serial.println("[MIDI] ERROR: MIDI MIDI init failed"); return false; } if (TinyUSBDevice.mounted()) { - Serial.println("[MIDI] Re-enumerating with MIDI interface..."); + Serial.println("[MIDI] Re-enumerating with dual MIDI interface..."); TinyUSBDevice.detach(); delay(10); TinyUSBDevice.attach(); } initialized = true; - Serial.println("[MIDI] USB MIDI ready - enumerating..."); + Serial.println("[MIDI] USB MIDI ready - dual interface enumerating..."); return true; } @@ -46,9 +52,10 @@ void UsbMidiTransport::update() { TinyUSBDevice.mounted() ? "YES" : "NO"); } - while (usb_midi.available()) { + // Poll DAW interface (interface 1) + while (usb_midi_daw.available()) { uint8_t packet[4]; - if (usb_midi.readPacket(packet)) { + if (usb_midi_daw.readPacket(packet)) { MidiEvent event; parse_midi_packet(packet, 4, event); @@ -61,7 +68,31 @@ void UsbMidiTransport::update() { 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); + Serial.printf("[MIDI IN DAW] Ch:%d %s:%d:%d\n", event.channel, type_str, event.data1, event.data2); + + if (receive_callback) { + receive_callback(event); + } + } + } + + // Poll MIDI interface (interface 2) - for Programmer mode LED control + while (usb_midi_midi.available()) { + uint8_t packet[4]; + if (usb_midi_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 MIDI] Ch:%d %s:%d:%d\n", event.channel, type_str, event.data1, event.data2); if (receive_callback) { receive_callback(event); @@ -77,22 +108,22 @@ void UsbMidiTransport::on_midi_receive(std::function cal void UsbMidiTransport::send_note_on(uint8_t channel, uint8_t note, uint8_t velocity) { if (!initialized) return; uint8_t packet[4] = {0x09, (uint8_t)(0x90 | (channel - 1)), note, velocity}; - usb_midi.writePacket(packet); - Serial.printf("[MIDI OUT] Ch:%d NOTE_ON:%d:%d\n", channel, note, velocity); + usb_midi_daw.writePacket(packet); + Serial.printf("[MIDI OUT DAW] 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; uint8_t packet[4] = {0x08, (uint8_t)(0x80 | (channel - 1)), note, velocity}; - usb_midi.writePacket(packet); - Serial.printf("[MIDI OUT] Ch:%d NOTE_OFF:%d:%d\n", channel, note, velocity); + usb_midi_daw.writePacket(packet); + Serial.printf("[MIDI OUT DAW] 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; uint8_t packet[4] = {0x0B, (uint8_t)(0xB0 | (channel - 1)), cc, value}; - usb_midi.writePacket(packet); - Serial.printf("[MIDI OUT] Ch:%d CC:%d:%d\n", channel, cc, value); + usb_midi_daw.writePacket(packet); + Serial.printf("[MIDI OUT DAW] Ch:%d CC:%d:%d\n", channel, cc, value); } bool UsbMidiTransport::is_connected() {