Map buttons to custom CC values with reverse-map feedback
This commit is contained in:
@@ -28,6 +28,7 @@ private:
|
|||||||
static const uint8_t NUM_PADS = 10;
|
static const uint8_t NUM_PADS = 10;
|
||||||
PadMapping pad_mapping[NUM_PADS];
|
PadMapping pad_mapping[NUM_PADS];
|
||||||
bool last_switch_state[NUM_PADS];
|
bool last_switch_state[NUM_PADS];
|
||||||
|
uint8_t cc_map[NUM_PADS];
|
||||||
|
|
||||||
// SysEx reassembly buffer
|
// SysEx reassembly buffer
|
||||||
static const uint8_t SYSEX_MAX_LEN = 64;
|
static const uint8_t SYSEX_MAX_LEN = 64;
|
||||||
|
|||||||
+15
-15
@@ -6,12 +6,14 @@ AppTask::AppTask(LedStub* led, SwitchStub* sw, UsbMidiTransport* midi)
|
|||||||
|
|
||||||
// Launchpad X standard: bottom row = notes 36-45 (C2 to A2) on channel 1
|
// Launchpad X standard: bottom row = notes 36-45 (C2 to A2) on channel 1
|
||||||
const uint8_t launchpad_notes[10] = {36, 37, 38, 39, 40, 41, 42, 43, 44, 45};
|
const uint8_t launchpad_notes[10] = {36, 37, 38, 39, 40, 41, 42, 43, 44, 45};
|
||||||
|
const uint8_t cc_assignments[10] = {112, 22, 113, 25, 114, 24, 115, 26, 116, 117};
|
||||||
|
|
||||||
for (uint8_t i = 0; i < NUM_PADS; i++) {
|
for (uint8_t i = 0; i < NUM_PADS; i++) {
|
||||||
pad_mapping[i].physical_switch = i;
|
pad_mapping[i].physical_switch = i;
|
||||||
pad_mapping[i].midi_channel = 1;
|
pad_mapping[i].midi_channel = 1;
|
||||||
pad_mapping[i].midi_note = launchpad_notes[i];
|
pad_mapping[i].midi_note = launchpad_notes[i];
|
||||||
pad_mapping[i].led_index = i;
|
pad_mapping[i].led_index = i;
|
||||||
|
cc_map[i] = cc_assignments[i];
|
||||||
last_switch_state[i] = false;
|
last_switch_state[i] = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -23,7 +25,10 @@ void AppTask::begin() {
|
|||||||
process_midi_event(event);
|
process_midi_event(event);
|
||||||
});
|
});
|
||||||
|
|
||||||
Serial.println("[APP] Controller ready - Launchpad X mode (notes 36-45, ch1)");
|
Serial.println("[APP] Controller ready - CC mode");
|
||||||
|
for (uint8_t i = 0; i < NUM_PADS; i++) {
|
||||||
|
Serial.printf("[APP] Pad %d -> CC%d -> LED%d\n", i + 1, cc_map[i], i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppTask::update() {
|
void AppTask::update() {
|
||||||
@@ -92,18 +97,16 @@ void AppTask::process_midi_event(const MidiEvent& event) {
|
|||||||
Serial.printf("[APP] NOTE Ch%d ignored (not Launchpad channel 1-3)\n", midi_channel);
|
Serial.printf("[APP] NOTE Ch%d ignored (not Launchpad channel 1-3)\n", midi_channel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// CONTROL_CHANGE fallback for generic MIDI / Loopy Pro generic mode
|
// CONTROL_CHANGE: look up which pad this CC belongs to
|
||||||
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 to pad: CC2-11 (Loopy Pro), CC0-9, CC36-45
|
for (uint8_t i = 0; i < NUM_PADS; i++) {
|
||||||
if (cc_num >= 2 && cc_num < 2 + NUM_PADS) {
|
if (cc_map[i] == cc_num) {
|
||||||
led_index = cc_num - 2;
|
led_index = i;
|
||||||
} else if (cc_num < NUM_PADS) {
|
break;
|
||||||
led_index = cc_num;
|
}
|
||||||
} else if (cc_num >= 36 && cc_num < 36 + NUM_PADS) {
|
|
||||||
led_index = cc_num - 36;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (led_index < NUM_PADS) {
|
if (led_index < NUM_PADS) {
|
||||||
@@ -113,8 +116,8 @@ void AppTask::process_midi_event(const MidiEvent& event) {
|
|||||||
cc_val,
|
cc_val,
|
||||||
led_index
|
led_index
|
||||||
);
|
);
|
||||||
Serial.printf("[APP] CC -> LED: Ch%d CC%d Val%d -> LED%d\n",
|
Serial.printf("[APP] CC%d Val%d -> LED%d\n",
|
||||||
midi_channel, cc_num, cc_val, led_index);
|
cc_num, cc_val, led_index);
|
||||||
} else {
|
} else {
|
||||||
Serial.printf("[APP] CC Ch%d CC%d Val%d - no 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);
|
||||||
@@ -126,15 +129,12 @@ 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 Generic mode mirrors CC back on the same CC it received
|
uint8_t cc_num = cc_map[i];
|
||||||
// CC2-11 maps to pads 0-9
|
|
||||||
uint8_t cc_num = 2 + i;
|
|
||||||
uint8_t value = pressed ? 127 : 0;
|
uint8_t value = pressed ? 127 : 0;
|
||||||
|
|
||||||
if (pressed) {
|
if (pressed) {
|
||||||
midi_transport->send_cc(channel, cc_num, value);
|
midi_transport->send_cc(channel, cc_num, value);
|
||||||
}
|
}
|
||||||
// Don't send CC 0 on release — Loopy Pro uses velocity 0 to turn off
|
|
||||||
|
|
||||||
Serial.printf("[APP] Switch %d -> Ch%d CC%d Val%d (%s)\n",
|
Serial.printf("[APP] Switch %d -> Ch%d CC%d Val%d (%s)\n",
|
||||||
switch_id, channel, cc_num, value,
|
switch_id, channel, cc_num, value,
|
||||||
|
|||||||
Reference in New Issue
Block a user