#include "midi_transport.h" #include #include "USB.h" #include "USBMIDI.h" static USBMIDI usb_midi; UsbMidiTransport::UsbMidiTransport() : initialized(false) { } UsbMidiTransport::~UsbMidiTransport() { } bool UsbMidiTransport::begin() { Serial.println("[MIDI] Setting up USB MIDI device..."); if (!usb_midi.begin()) { Serial.println("[MIDI] ERROR: USB MIDI init failed"); return false; } USB.begin(); initialized = true; Serial.println("[MIDI] USB MIDI ready - enumerating..."); return true; } void UsbMidiTransport::update() { if (!initialized) return; static uint32_t last_status = 0; uint32_t now = millis(); if (now - last_status > 5000) { last_status = now; Serial.printf("[MIDI] USB ready: %s\n", USB.ready() ? "YES" : "NO"); } 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; } void UsbMidiTransport::send_note_on(uint8_t channel, uint8_t note, uint8_t velocity) { if (!initialized) return; usb_midi.noteOn(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; usb_midi.noteOff(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; usb_midi.controlChange(cc, value, channel); Serial.printf("[MIDI OUT] Ch:%d CC:%d:%d\n", channel, cc, value); } bool UsbMidiTransport::is_connected() { return initialized && USB.ready(); } 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; } }