// cppcheck-suppress missingIncludeSystem #include "midi.h" #include "channel.h" #include #include #include extern atomic_int control_key_active; extern atomic_int cmd_add; extern atomic_int cmd_remove; extern atomic_int cmd_load; extern atomic_int cmd_save; extern atomic_int bind_channel; void midi_handle_events(void *port_buffer, jack_nframes_t nframes) { (void)nframes; jack_nframes_t nevents = jack_midi_get_event_count(port_buffer); jack_midi_event_t ev; for (jack_nframes_t i = 0; i < nevents; i++) { if (jack_midi_event_get(&ev, port_buffer, i) != 0) continue; if (ev.size < 3) continue; unsigned char status = ev.buffer[0]; unsigned char note = ev.buffer[1]; unsigned char vel = ev.buffer[2]; /* note‑on */ if ((status & 0xf0) == 0x90 && vel > 0) { if (note == 64) { atomic_store(&control_key_active, 1); } else { int ck = atomic_load(&control_key_active); if (ck) { atomic_store(&control_key_active, 0); if (note < 16) { atomic_store(&bind_channel, note); } else { switch (note) { case 60: atomic_store(&cmd_add, 1); break; case 61: atomic_store(&cmd_remove, 1); break; case 62: /* trigger looper – channel via bind_channel */ { int bch = atomic_load(&bind_channel); if (bch >= 0 && bch < MAX_CHANNELS) { int cur = atomic_load(&channels[bch].state); switch (cur) { case STATE_IDLE: atomic_store(&channels[bch].state, STATE_RECORD); break; case STATE_RECORD: atomic_store(&channels[bch].state, STATE_LOOPING); break; case STATE_LOOPING: atomic_store(&channels[bch].state, STATE_PAUSED); break; case STATE_PAUSED: atomic_store(&channels[bch].state, STATE_LOOPING); break; } } } break; case 63: /* unbind – reset bind to channel 0 */ atomic_store(&bind_channel, 0); break; case 70: /* load WAV into channel 0 */ atomic_store(&cmd_load, 1); break; case 71: /* save WAV of channel 0 */ atomic_store(&cmd_save, 1); break; default: break; } } } else { /* direct mapping */ switch (note) { case 1: /* 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; case 60: atomic_store(&cmd_add, 1); break; case 61: atomic_store(&cmd_remove, 1); break; default: break; } } } } else if ((status & 0xf0) == 0x80 || ((status & 0xf0) == 0x90 && vel == 0)) { atomic_store(&control_key_active, 0); } } }