// // main.cpp // // Piano LED Visualizer on RP2040 / RP2350. // // The board is a USB MIDI device: the PC is the host and owns the piano // connection and everything else. From the PC's side this enumerates as an // ordinary ALSA MIDI port. See PIANO-LED-CIRCLE-PLAN.md section 2. // #include "pianoleds.h" #include "picostrip.h" #include "pico/stdlib.h" #include "tusb.h" static CPicoLEDStrip s_Strip (LED_COUNT, WS2812_PIN); static CPianoLEDs s_PianoLEDs (s_Strip); // Number of MIDI bytes carried by a USB MIDI event packet, indexed by its // Code Index Number (USB MIDI 1.0, table 4-1). 0 means "not a message we // forward". static const uint8_t s_CINLength[16] = { 0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1 }; // -------------------------------------------------------------------------- // TinyUSB device callbacks // -------------------------------------------------------------------------- extern "C" void tud_mount_cb (void) { // Fresh session: nothing should still be lit from the last one. s_PianoLEDs.AllOff (); } extern "C" void tud_umount_cb (void) { // Host went away, possibly mid-chord. Do not leave keys lit. s_PianoLEDs.AllOff (); } extern "C" void tud_suspend_cb (bool remote_wakeup_en) { (void) remote_wakeup_en; s_PianoLEDs.AllOff (); } extern "C" void tud_resume_cb (void) { s_PianoLEDs.AllOff (); } // -------------------------------------------------------------------------- static void PollMIDI (void) { uint8_t Packet[4]; while (tud_midi_available ()) { if (!tud_midi_packet_read (Packet)) { break; } // Packet[0] is cable number (high nibble) and Code Index Number // (low nibble); the message itself is in Packet[1..3]. unsigned nLength = s_CINLength[Packet[0] & 0x0F]; if (nLength == 0) { continue; } s_PianoLEDs.OnMIDIPacket (&Packet[1], nLength); } } int main (void) { stdio_init_all (); if (!s_Strip.Initialize ()) { // Nothing sensible left to do; make the failure visible rather // than sitting dark and looking like a power problem. while (true) { tight_loop_contents (); } } s_PianoLEDs.Initialize (); tusb_init (); while (true) { tud_task (); PollMIDI (); // Rendering blocks for the strip's frame time, so it runs here // rather than in the USB callback. Returns immediately when // nothing has changed. s_PianoLEDs.Update (); } return 0; }