Focus on Launchpad X mode: NOTE 36-45 ch1-3, send NOTE_ON/OFF for buttons

This commit is contained in:
2026-06-24 06:43:16 +00:00
parent 4f6ff39af4
commit fee8ab5b94
+41 -57
View File
@@ -23,7 +23,7 @@ void AppTask::begin() {
process_midi_event(event); process_midi_event(event);
}); });
Serial.println("[APP] Controller ready"); Serial.println("[APP] Controller ready - Launchpad X mode (notes 36-45, ch1)");
} }
void AppTask::update() { void AppTask::update() {
@@ -40,35 +40,6 @@ 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) {
@@ -80,32 +51,41 @@ 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;
// NOTE_ON/NOTE_OFF on channels 1-3 (Launchpad standard) // Launchpad X: NOTE_ON/NOTE_OFF on channels 1-3
// ch1 = static, ch2 = flashing, ch3 = pulsing
// Notes 36-45 (C2-A2) map to pads 0-9
// Velocity 1-127 = color palette index
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++) { // Only handle channels 1-3 (Launchpad channels)
if (pad_mapping[i].midi_channel == midi_channel && if (midi_channel >= 1 && midi_channel <= 3) {
pad_mapping[i].midi_note == midi_note) { for (uint8_t i = 0; i < NUM_PADS; i++) {
led_index = pad_mapping[i].led_index; if (pad_mapping[i].midi_channel == midi_channel &&
break; pad_mapping[i].midi_note == midi_note) {
led_index = pad_mapping[i].led_index;
break;
}
}
if (led_index < NUM_PADS) {
uint8_t color_vel = (event.type == MidiEvent::NOTE_ON) ? midi_velocity : 0;
led_driver->set_led_state(
pad_mapping[led_index].midi_note,
pad_mapping[led_index].midi_channel,
color_vel
);
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] NOTE Ch%d Note%d Vel%d - no mapping\n",
midi_channel, midi_note, midi_velocity);
} }
}
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,
color_vel
);
Serial.printf("[APP] NOTE -> LED: Ch%d Note%d Vel%d -> LED%d\n",
midi_channel, midi_note, color_vel, led_index);
} else { } else {
Serial.printf("[APP] NOTE Ch%d Note%d Vel%d - no mapping\n", Serial.printf("[APP] NOTE Ch%d ignored (not Launchpad channel 1-3)\n", midi_channel);
midi_channel, midi_note, midi_velocity);
} }
} }
// CONTROL_CHANGE on any channel (generic MIDI / Loopy Pro) // CONTROL_CHANGE fallback for generic MIDI / Loopy Pro generic mode
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;
@@ -120,7 +100,6 @@ void AppTask::process_midi_event(const MidiEvent& event) {
} }
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,
@@ -139,13 +118,18 @@ 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;
uint8_t cc_num = 2 + switch_id; // Loopy Pro Launchpad mode expects NOTE_ON/NOTE_OFF on notes 36-45
uint8_t cc_val = pressed ? 127 : 0; uint8_t note = pad_mapping[i].midi_note;
uint8_t velocity = pressed ? 127 : 0;
midi_transport->send_cc(channel, cc_num, cc_val); if (pressed) {
midi_transport->send_note_on(channel, note, velocity);
} else {
midi_transport->send_note_off(channel, note, velocity);
}
Serial.printf("[APP] Switch %d -> Ch%d CC%d Val%d (%s)\n", Serial.printf("[APP] Switch %d -> Ch%d Note%d Vel%d (%s)\n",
switch_id, channel, cc_num, cc_val, switch_id, channel, note, velocity,
pressed ? "PRESS" : "RELEASE"); pressed ? "PRESS" : "RELEASE");
break; break;
} }