Skip to content

Commit 9ea832f

Browse files
committed
USB MIDI USermod
On-connect repaint delay ✅ Fixed delete_enabled in GUI ✅ Fixed presetsModifiedTime cache staleness ✅ Fixed Reboot cancel-then-arm loop ✅ Fixed Track LED only on Track 4 ✅ Fixed (now any button with the action) Single-LED status bytes (0x99 dropped) ✅ Fixed (use 0x90 + vel 0x02)
1 parent 32faad9 commit 9ea832f

4 files changed

Lines changed: 996 additions & 435 deletions

File tree

usermods/usermod_v2_midi/midi_usb_host.cpp

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,13 @@ static volatile bool midi_in_error_recovery_needed = false;
115115
#if 1
116116
static usb_transfer_t* midi_out_xfer = nullptr;
117117
static volatile bool midi_out_in_flight = false;
118-
static uint8_t out_rb_storage[512] __attribute__((aligned(4)));
118+
// OUT ring buffer. Holds USB-MIDI event packets (4 bytes each).
119+
// A full controller repaint queues ~150 packets (80 wipe + 64 pads +
120+
// tracks + scenes), so 512 bytes (128 packets) was too small and
121+
// dropped late packets — most noticeably the post-power-on scene 8
122+
// send, which would get dropped before the OUT thread could drain.
123+
// 2048 bytes (512 packets) gives comfortable headroom.
124+
static uint8_t out_rb_storage[2048] __attribute__((aligned(4)));
119125
static StaticRingbuffer_t out_rb_struct;
120126
static RingbufHandle_t midi_out_rb = nullptr;
121127
#endif
@@ -329,31 +335,29 @@ static void midi_client_event_cb(const usb_host_client_event_msg_t* msg, void* /
329335
// rebuilds on first boot, re-applied presets from prior disconnect,
330336
// etc.).
331337
if (midiUsermodPtr) {
332-
MIDI_LOG("connect: repainting controller (immediate + 500ms)");
333-
stateUpdated(CALL_MODE_BUTTON);
334-
335-
// 1500ms-delayed "settled" repaint. Always spawn — multiple paints
336-
// are harmless and idempotent (each just re-sends the full state).
337-
// The 1500ms delay gives WLED's setup loop time to apply the boot
338-
// preset / load the boot playlist before we paint — otherwise the
339-
// first paint sees currentPreset=0 and currentPlaylist=0 (defaults)
340-
// and the playlist parent pad shows as plain blue until the 500ms
341-
// paint kicks in. 1500ms comfortably covers WLED's setup work
342-
// including playlist loading from LittleFS.
338+
// USB_Task now starts after the SD card in WLED's task list
339+
// (the boot-blocking mount task runs first), so by the time
340+
// we get this connect callback, the segment/preset structures
341+
// are already initialized and a direct repaint is safe.
342+
// Always do an immediate repaint to handle the common case
343+
// where the boot preset was already applied, plus a 1500ms-
344+
// delayed repaint for the case where the boot playlist is
345+
// still being set up (currentPlaylist may be 0 at t=0 even
346+
// though currentPreset is set).
347+
midiUsermodPtr->requestFullRepaint();
348+
// Single delayed "settled" repaint using a static buffer (no
349+
// heap allocation, so no race with the SD-card task).
343350
struct RepaintCtx { MidiUsermod* ptr; };
344-
RepaintCtx* ctx = new RepaintCtx{ midiUsermodPtr };
351+
static RepaintCtx s_repaint_ctx;
352+
s_repaint_ctx.ptr = midiUsermodPtr;
345353
xTaskCreate(
346354
[](void* arg) {
347-
vTaskDelay(pdMS_TO_TICKS(1500));
348355
auto* c = (RepaintCtx*)arg;
349-
if (c->ptr) {
350-
MIDI_LOG("connect: settled repaint firing");
351-
c->ptr->requestFullRepaint();
352-
}
353-
delete c;
356+
vTaskDelay(pdMS_TO_TICKS(1500));
357+
if (c->ptr) c->ptr->requestFullRepaint();
354358
vTaskDelete(NULL);
355359
},
356-
"midi_repaint", 2048, ctx, 1, nullptr);
360+
"midi_repaint", 2048, &s_repaint_ctx, 1, nullptr);
357361
}
358362
} else {
359363
MIDI_LOG("event: NEW_DEV addr=%d configure failed (likely MSC or unknown device)", msg->new_dev.address);

0 commit comments

Comments
 (0)