#include "midi_transport.h" #include #include "Adafruit_TinyUSB.h" static Adafruit_USBD_MIDI 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; } initialized = true; 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 mounted: %s\n", TinyUSBDevice.mounted() ? "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; 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); } 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); } 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); } bool UsbMidiTransport::is_connected() { return initialized && TinyUSBDevice.mounted(); } 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; } }