From af14c2f7599cd7236353d234c6bddf80e0991182 Mon Sep 17 00:00:00 2001 From: Ashley Strahle Date: Thu, 2 Jul 2026 07:27:26 +0000 Subject: [PATCH] Replace MIDI clock with internal BPM timer for pixel 6 pulse Revert: - Remove midi_clock.h/cpp (was causing cross-core crash) - Remove real-time MIDI message parsing from midi_transport.cpp New approach: - Pixel 6 (and all active LEDs) pulse to internal BPM timer - Default 120 BPM (500ms beat interval) - 'bpm ' serial command to change tempo (20-300) - Only calls FastLED.show() when actively pulsing (avoids USB/WS2812 interference) - 33ms update rate (30fps) - Inactive LEDs left untouched (no unnecessary show() calls) --- include/led_stub.h | 2 ++ include/midi_clock.h | 19 ---------------- src/led_stub.cpp | 30 +++++++++++++++---------- src/main.cpp | 11 +++++++++ src/midi_clock.cpp | 51 ------------------------------------------ src/midi_transport.cpp | 15 ------------- 6 files changed, 31 insertions(+), 97 deletions(-) delete mode 100644 include/midi_clock.h delete mode 100644 src/midi_clock.cpp diff --git a/include/led_stub.h b/include/led_stub.h index 8c0ea1c..1a8c442 100644 --- a/include/led_stub.h +++ b/include/led_stub.h @@ -33,6 +33,7 @@ private: static const uint8_t NUM_LEDS = 10; LedState led_states[NUM_LEDS]; bool initialized; + uint32_t beat_interval_ms = 500; public: DefaultLedStub(); @@ -44,4 +45,5 @@ public: void update() override; void set_mux(PixelStompMux* mux); + void set_bpm(uint16_t bpm); }; diff --git a/include/midi_clock.h b/include/midi_clock.h deleted file mode 100644 index edafe94..0000000 --- a/include/midi_clock.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once -#include - -class MidiClock { -public: - static void tick(); - static void start(); - static void stop(); - static float get_phase(); - static float get_pulse(); - static bool is_running(); - -private: - static uint32_t last_tick_time; - static uint32_t tick_count; - static float tick_interval_ms; - static bool running; - static const uint8_t TICKS_PER_BEAT = 24; -}; diff --git a/src/led_stub.cpp b/src/led_stub.cpp index 3e9ac27..1ad105a 100644 --- a/src/led_stub.cpp +++ b/src/led_stub.cpp @@ -1,7 +1,7 @@ #include "led_stub.h" #include "pixel_stomp_mux.h" -#include "midi_clock.h" #include +#include static PixelStompMux* mux_ptr = nullptr; @@ -300,40 +300,46 @@ void DefaultLedStub::flash_activity() { // LED feedback is handled directly by set_led_state(). } +void DefaultLedStub::set_bpm(uint16_t bpm) { + if (bpm == 0) bpm = 120; + beat_interval_ms = 60000 / bpm; + Serial.printf("[LED] Pulse BPM set to %d (%dms)\n", bpm, beat_interval_ms); +} + void DefaultLedStub::update() { if (!initialized || !mux_ptr) return; uint32_t now = millis(); static uint32_t last_pulse_update = 0; - if (now - last_pulse_update < 20) return; + if (now - last_pulse_update < 33) return; last_pulse_update = now; - bool clock_running = MidiClock::is_running(); - float pulse = 1.0f; - if (clock_running) { - pulse = MidiClock::get_pulse(); - } + float phase = (now % beat_interval_ms) / (float)beat_interval_ms; + float pulse = sinf(M_PI * phase); + bool active = false; for (int i = 0; i < NUM_LEDS; i++) { if (led_states[i].active) { - float brightness = clock_running ? (0.5f + 0.5f * pulse) : 1.0f; + active = true; + float brightness = 0.5f + 0.5f * pulse; uint32_t base = pad_base_colors[i]; uint8_t r = (uint8_t)(((base >> 16) & 0xFF) * brightness); uint8_t g = (uint8_t)(((base >> 8) & 0xFF) * brightness); uint8_t b = (uint8_t)((base & 0xFF) * brightness); mux_ptr->set_led_color(i, r, g, b); - } else if (clock_running && i == 6) { + } else if (i == 6) { + active = true; float brightness = 0.3f + 0.7f * pulse; uint32_t base = pad_base_colors[6]; uint8_t r = (uint8_t)(((base >> 16) & 0xFF) * brightness); uint8_t g = (uint8_t)(((base >> 8) & 0xFF) * brightness); uint8_t b = (uint8_t)((base & 0xFF) * brightness); mux_ptr->set_led_color(6, r, g, b); - } else { - apply_pad_color(i, 0); } } - mux_ptr->show(); + if (active) { + mux_ptr->show(); + } } diff --git a/src/main.cpp b/src/main.cpp index ad42692..17222dd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -89,6 +89,13 @@ void handle_serial_command(const String& cmd) { } else if (cmd == "exp") { Serial.printf("[CMD] EXP ADC=%d MIDI=%d\n", exp_pedal.get_raw(), exp_pedal.get_value()); + } else if (cmd.startsWith("bpm ")) { + uint16_t bpm = cmd.substring(4).toInt(); + if (bpm >= 20 && bpm <= 300) { + led_driver.set_bpm(bpm); + } else { + Serial.println("[CMD] BPM must be 20-300"); + } } else if (cmd == "usb") { Serial.printf("[CMD] USB mounted: %s\n", TinyUSBDevice.mounted() ? "YES" : "NO"); Serial.printf("[CMD] USB ready: %s\n", TinyUSBDevice.ready() ? "YES" : "NO"); @@ -133,6 +140,7 @@ void handle_serial_command(const String& cmd) { Serial.println(" red/green/blue - solid colour"); Serial.println(" pixel0/pixel1 - single pixel test"); Serial.println(" exp - expression pedal ADC/MIDI value"); + Serial.println(" bpm - set LED pulse tempo (20-300, default 120)"); Serial.println(" usb - USB connection status and descriptor info"); Serial.println(" gpiotest - raw GPIO pin diagnostic"); Serial.println(" rawled - bit-bang WS2812 (no library)"); @@ -356,6 +364,9 @@ void setup() { Serial.println("[INIT] Initializing Expression Pedal..."); exp_pedal.begin(); + Serial.println("[INIT] Setting LED pulse tempo..."); + led_driver.set_bpm(120); + Serial.println("[INIT] Registering MIDI callbacks..."); controller.begin(); diff --git a/src/midi_clock.cpp b/src/midi_clock.cpp deleted file mode 100644 index f02be0c..0000000 --- a/src/midi_clock.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "midi_clock.h" -#include -#include - -uint32_t MidiClock::last_tick_time = 0; -uint32_t MidiClock::tick_count = 0; -float MidiClock::tick_interval_ms = 0; -bool MidiClock::running = false; - -void MidiClock::tick() { - uint32_t now = millis(); - if (last_tick_time != 0) { - float interval = now - last_tick_time; - if (tick_interval_ms == 0) { - tick_interval_ms = interval; - } else { - tick_interval_ms = tick_interval_ms * 0.9f + interval * 0.1f; - } - } - last_tick_time = now; - tick_count++; - running = true; -} - -void MidiClock::start() { - tick_count = 0; - last_tick_time = 0; - tick_interval_ms = 0; - running = true; -} - -void MidiClock::stop() { - running = false; -} - -float MidiClock::get_phase() { - if (!running || tick_interval_ms == 0) return 0; - uint32_t now = millis(); - float time_since_tick = now - last_tick_time; - float sub_phase = time_since_tick / tick_interval_ms; - if (sub_phase > 1.0f) sub_phase = 1.0f; - return ((tick_count % TICKS_PER_BEAT) + sub_phase) / (float)TICKS_PER_BEAT; -} - -float MidiClock::get_pulse() { - return sinf(M_PI * get_phase()); -} - -bool MidiClock::is_running() { - return running; -} diff --git a/src/midi_transport.cpp b/src/midi_transport.cpp index 4d645c9..8276b18 100644 --- a/src/midi_transport.cpp +++ b/src/midi_transport.cpp @@ -1,5 +1,4 @@ #include "midi_transport.h" -#include "midi_clock.h" #include #include "Adafruit_TinyUSB.h" @@ -52,20 +51,6 @@ void UsbMidiTransport::update() { while (usb_midi.available()) { uint8_t packet[4]; if (usb_midi.readPacket(packet)) { - uint8_t cin = packet[0] & 0x0F; - - if (cin == 0x0F) { - uint8_t rt = packet[1]; - if (rt == 0xF8) { - MidiClock::tick(); - } else if (rt == 0xFA || rt == 0xFB) { - MidiClock::start(); - } else if (rt == 0xFC) { - MidiClock::stop(); - } - continue; - } - MidiEvent event; parse_midi_packet(packet, 4, event);