Add palette test mode (hold pad 0+9 for 2s), comprehensive MIDI handling
This commit is contained in:
+44
-29
@@ -40,21 +40,38 @@ void AppTask::update() {
|
|||||||
last_switch_state[i] = false;
|
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) {
|
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",
|
Serial.printf("[APP] MIDI IN: Type=%d Ch=%d Data1=%d Data2=%d\n",
|
||||||
event.type, event.channel, event.data1, event.data2);
|
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_note = event.data1;
|
||||||
uint8_t midi_velocity = event.data2;
|
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) {
|
if (event.type == MidiEvent::NOTE_ON || event.type == MidiEvent::NOTE_OFF) {
|
||||||
for (uint8_t i = 0; i < NUM_PADS; i++) {
|
for (uint8_t i = 0; i < NUM_PADS; i++) {
|
||||||
if (pad_mapping[i].midi_channel == midi_channel &&
|
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) {
|
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(
|
led_driver->set_led_state(
|
||||||
pad_mapping[led_index].midi_note,
|
pad_mapping[led_index].midi_note,
|
||||||
pad_mapping[led_index].midi_channel,
|
pad_mapping[led_index].midi_channel,
|
||||||
event.type == MidiEvent::NOTE_ON ? midi_velocity : 0
|
color_vel
|
||||||
);
|
);
|
||||||
|
Serial.printf("[APP] NOTE -> LED: Ch%d Note%d Vel%d -> LED%d\n",
|
||||||
Serial.printf("[APP] MIDI -> LED: Ch%d Note%d Vel%d -> LED%d\n",
|
midi_channel, midi_note, color_vel, led_index);
|
||||||
midi_channel, midi_note, midi_velocity, led_index);
|
|
||||||
} else {
|
} 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);
|
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) {
|
else if (event.type == MidiEvent::CONTROL_CHANGE) {
|
||||||
uint8_t cc_num = event.data1;
|
uint8_t cc_num = event.data1;
|
||||||
uint8_t cc_val = event.data2;
|
uint8_t cc_val = event.data2;
|
||||||
|
|
||||||
// Map CC number to pad index
|
// Map CC to pad: CC2-11 (Loopy Pro), CC0-9, CC36-45
|
||||||
// Loopy Pro: CC2=pixel0, CC3=pixel1, ... CC11=pixel9
|
|
||||||
// Also try CC 0-9 and CC 36-45 for compatibility
|
|
||||||
if (cc_num >= 2 && cc_num < 2 + NUM_PADS) {
|
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) {
|
} 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) {
|
} 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) {
|
if (led_index < NUM_PADS) {
|
||||||
|
// Use CC value directly as color velocity
|
||||||
led_driver->set_led_state(
|
led_driver->set_led_state(
|
||||||
pad_mapping[led_index].midi_note,
|
pad_mapping[led_index].midi_note,
|
||||||
pad_mapping[led_index].midi_channel,
|
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",
|
Serial.printf("[APP] CC -> LED: Ch%d CC%d Val%d -> LED%d\n",
|
||||||
midi_channel, cc_num, cc_val, led_index);
|
midi_channel, cc_num, cc_val, led_index);
|
||||||
} else {
|
} 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);
|
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++) {
|
for (uint8_t i = 0; i < NUM_PADS; i++) {
|
||||||
if (pad_mapping[i].physical_switch == switch_id) {
|
if (pad_mapping[i].physical_switch == switch_id) {
|
||||||
uint8_t channel = pad_mapping[i].midi_channel;
|
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_num = 2 + switch_id;
|
||||||
uint8_t cc_val = pressed ? 127 : 0;
|
uint8_t cc_val = pressed ? 127 : 0;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user