feat: implement control key (note 64) and trigger looper command (note 62)

Co-authored-by: aider (deepseek/deepseek-reasoner) <aider@aider.chat>
This commit is contained in:
Loic Coenen
2026-05-08 20:51:34 +00:00
parent 6b6f2dee3c
commit 9eb264aab8
2 changed files with 75 additions and 31 deletions

View File

@@ -40,6 +40,9 @@ static jack_port_t *midi_control_port;
static jack_port_t *midi_clock_port;
static jack_client_t *client;
/* control key mechanism note 64 acts as a modifier */
static atomic_int control_key_active = 0;
/* ---------------------------------------------------------------
* process callback runs in realtime context
* --------------------------------------------------------------- */
@@ -54,37 +57,78 @@ static int process(jack_nframes_t nframes, void *arg)
jack_midi_event_t ev;
for (jack_nframes_t i = 0; i < nevents; i++) {
if (jack_midi_event_get(&ev, midi_ctrl_buf, i) != 0) continue;
if ((ev.size >= 3) && ((ev.buffer[0] & 0xf0) == 0x90)) {
unsigned char note = ev.buffer[1];
switch (note) {
case 1: /* toggle state of channel 0 (backward compatible) */
{
int cur0 = atomic_load(&channels[0].state);
switch (cur0) {
case STATE_IDLE:
atomic_store(&channels[0].state, STATE_RECORD);
break;
case STATE_RECORD:
atomic_store(&channels[0].state, STATE_LOOPING);
break;
case STATE_LOOPING:
atomic_store(&channels[0].state, STATE_PAUSED);
break;
case STATE_PAUSED:
atomic_store(&channels[0].state, STATE_LOOPING);
break;
if (ev.size < 3) continue;
unsigned char status = ev.buffer[0];
unsigned char note = ev.buffer[1];
unsigned char vel = ev.buffer[2];
/* noteon */
if ((status & 0xf0) == 0x90 && vel > 0) {
if (note == 64) {
/* control key pressed activate modifier */
atomic_store(&control_key_active, 1);
} else {
int ck = atomic_load(&control_key_active);
if (ck) {
/* command selected by control key */
atomic_store(&control_key_active, 0);
switch (note) {
case 60: atomic_store(&cmd_add, 1); break;
case 61: atomic_store(&cmd_remove, 1); break;
case 62: /* trigger looper toggle channel 0 */
{
int cur0 = atomic_load(&channels[0].state);
switch (cur0) {
case STATE_IDLE:
atomic_store(&channels[0].state, STATE_RECORD);
break;
case STATE_RECORD:
atomic_store(&channels[0].state, STATE_LOOPING);
break;
case STATE_LOOPING:
atomic_store(&channels[0].state, STATE_PAUSED);
break;
case STATE_PAUSED:
atomic_store(&channels[0].state, STATE_LOOPING);
break;
}
}
break;
default:
break;
}
} else {
/* direct mapping (backward compatible) */
switch (note) {
case 1: /* toggle state of channel 0 */
{
int cur0 = atomic_load(&channels[0].state);
switch (cur0) {
case STATE_IDLE:
atomic_store(&channels[0].state, STATE_RECORD);
break;
case STATE_RECORD:
atomic_store(&channels[0].state, STATE_LOOPING);
break;
case STATE_LOOPING:
atomic_store(&channels[0].state, STATE_PAUSED);
break;
case STATE_PAUSED:
atomic_store(&channels[0].state, STATE_LOOPING);
break;
}
}
break;
case 60: atomic_store(&cmd_add, 1); break;
case 61: atomic_store(&cmd_remove, 1); break;
default:
break;
}
}
break;
}
case 60: /* add channel send to main thread */
atomic_store(&cmd_add, 1);
break;
case 61: /* remove channel send to main thread */
atomic_store(&cmd_remove, 1);
break;
default:
break;
}
} else if ((status & 0xf0) == 0x80 || ((status & 0xf0) == 0x90 && vel == 0)) {
/* noteoff clear control key state */
atomic_store(&control_key_active, 0);
}
}
}