diff --git a/src/app_task.cpp b/src/app_task.cpp index b000725..76ab7e6 100644 --- a/src/app_task.cpp +++ b/src/app_task.cpp @@ -40,21 +40,38 @@ void AppTask::update() { last_switch_state[i] = false; } } + + // Test mode: hold pad 0 + pad 9 for 2s to cycle palette + static uint32_t test_hold_start = 0; + static bool test_mode_active = false; + bool p0 = switch_driver->is_pressed(0); + bool p9 = switch_driver->is_pressed(9); + + if (p0 && p9) { + if (test_hold_start == 0) test_hold_start = millis(); + else if (!test_mode_active && millis() - test_hold_start > 2000) { + test_mode_active = true; + run_palette_test(); + } + } else { + test_hold_start = 0; + test_mode_active = false; + } +} + +void AppTask::run_palette_test() { + Serial.println("[APP] PALETTE TEST: cycling through all 127 colors on pad 0"); + Serial.println("[APP] Hold pad 0 + pad 9 for 2s to re-run"); + + for (uint8_t v = 1; v <= 127; v++) { + led_driver->set_led_state(36, 1, v); + vTaskDelay(pdMS_TO_TICKS(80)); + } + led_driver->set_led_state(36, 1, 0); + Serial.println("[APP] Palette test complete"); } void AppTask::process_midi_event(const MidiEvent& event) { - // Visual indicator: blink LED 0 on ANY MIDI receive - if (led_driver && event.type != MidiEvent::SYSEX) { - static uint32_t last_blink = 0; - uint32_t now = millis(); - if (now - last_blink > 100) { - last_blink = now; - led_driver->set_led_state(36, 1, 127); // Brief flash on pad 0 - vTaskDelay(pdMS_TO_TICKS(10)); - led_driver->set_led_state(36, 1, 0); - } - } - Serial.printf("[APP] MIDI IN: Type=%d Ch=%d Data1=%d Data2=%d\n", event.type, event.channel, event.data1, event.data2); @@ -63,7 +80,7 @@ void AppTask::process_midi_event(const MidiEvent& event) { uint8_t midi_note = event.data1; uint8_t midi_velocity = event.data2; - // Handle NOTE_ON/NOTE_OFF for LED control (Launchpad standard) + // NOTE_ON/NOTE_OFF on channels 1-3 (Launchpad standard) if (event.type == MidiEvent::NOTE_ON || event.type == MidiEvent::NOTE_OFF) { for (uint8_t i = 0; i < NUM_PADS; i++) { if (pad_mapping[i].midi_channel == midi_channel && @@ -74,46 +91,45 @@ void AppTask::process_midi_event(const MidiEvent& event) { } if (led_index < NUM_PADS) { + uint8_t color_vel = (event.type == MidiEvent::NOTE_ON) ? midi_velocity : 0; + // For flashing/pulsing channels, just use velocity as color led_driver->set_led_state( pad_mapping[led_index].midi_note, pad_mapping[led_index].midi_channel, - event.type == MidiEvent::NOTE_ON ? midi_velocity : 0 + color_vel ); - - Serial.printf("[APP] MIDI -> LED: Ch%d Note%d Vel%d -> LED%d\n", - midi_channel, midi_note, midi_velocity, led_index); + Serial.printf("[APP] NOTE -> LED: Ch%d Note%d Vel%d -> LED%d\n", + midi_channel, midi_note, color_vel, led_index); } else { - Serial.printf("[APP] MIDI Ch%d Note%d Vel%d - no LED mapping\n", + Serial.printf("[APP] NOTE Ch%d Note%d Vel%d - no mapping\n", midi_channel, midi_note, midi_velocity); } } - // Handle CC for LED control (Loopy Pro uses CC) + // CONTROL_CHANGE on any channel (generic MIDI / Loopy Pro) else if (event.type == MidiEvent::CONTROL_CHANGE) { uint8_t cc_num = event.data1; uint8_t cc_val = event.data2; - // Map CC number to pad index - // Loopy Pro: CC2=pixel0, CC3=pixel1, ... CC11=pixel9 - // Also try CC 0-9 and CC 36-45 for compatibility + // Map CC to pad: CC2-11 (Loopy Pro), CC0-9, CC36-45 if (cc_num >= 2 && cc_num < 2 + NUM_PADS) { - led_index = cc_num - 2; // CC 2-11 -> pads 0-9 + led_index = cc_num - 2; } else if (cc_num < NUM_PADS) { - led_index = cc_num; // CC 0-9 + led_index = cc_num; } else if (cc_num >= 36 && cc_num < 36 + NUM_PADS) { - led_index = cc_num - 36; // CC 36-45 + led_index = cc_num - 36; } if (led_index < NUM_PADS) { + // Use CC value directly as color velocity led_driver->set_led_state( pad_mapping[led_index].midi_note, pad_mapping[led_index].midi_channel, - cc_val // CC value = velocity/color + cc_val ); - Serial.printf("[APP] CC -> LED: Ch%d CC%d Val%d -> LED%d\n", midi_channel, cc_num, cc_val, led_index); } else { - Serial.printf("[APP] CC Ch%d CC%d Val%d - no LED mapping\n", + Serial.printf("[APP] CC Ch%d CC%d Val%d - no mapping\n", midi_channel, cc_num, cc_val); } } @@ -123,7 +139,6 @@ void AppTask::process_switch_event(uint8_t switch_id, bool pressed) { for (uint8_t i = 0; i < NUM_PADS; i++) { if (pad_mapping[i].physical_switch == switch_id) { uint8_t channel = pad_mapping[i].midi_channel; - // Loopy Pro expects CC2 for pixel0, CC3 for pixel1, etc. uint8_t cc_num = 2 + switch_id; uint8_t cc_val = pressed ? 127 : 0;