From 96295fdb4c8e99fd644f8e9257401c4ccbb7985a Mon Sep 17 00:00:00 2001 From: Loic Coenen Date: Fri, 8 May 2026 21:03:10 +0000 Subject: [PATCH] refactor: split monolithic main.c into modular source files Co-authored-by: aider (deepseek/deepseek-reasoner) --- makefile | 16 ++- src/channel.c | 38 ++++++ src/channel.h | 39 ++++++ src/looper.c | 208 ++++++++++++++++++++++++++++++ src/looper.h | 18 +++ src/main.c | 341 ++------------------------------------------------ src/midi.c | 91 ++++++++++++++ src/midi.h | 8 ++ 8 files changed, 423 insertions(+), 336 deletions(-) create mode 100644 src/channel.c create mode 100644 src/channel.h create mode 100644 src/looper.c create mode 100644 src/looper.h create mode 100644 src/midi.c create mode 100644 src/midi.h diff --git a/makefile b/makefile index b2049a0..0556ab9 100644 --- a/makefile +++ b/makefile @@ -1,9 +1,15 @@ CC ?= gcc -CFLAGS ?= -Wall -Wextra -g -LDFLAGS ?= -ljack +CFLAGS ?= -Wall -Wextra -g -Isrc +LDFLAGS ?= -ljack -lm -looper: src/main.c - $(CC) $(CFLAGS) -o looper src/main.c $(LDFLAGS) +SRC = src/main.c src/looper.c src/channel.c src/midi.c +OBJ = $(SRC:.c=.o) + +looper: $(OBJ) + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) + +src/%.o: src/%.c + $(CC) $(CFLAGS) -c -o $@ $< integration: looper tests/integration.c $(CC) $(CFLAGS) -o integration_test tests/integration.c -ljack -lm @@ -13,4 +19,4 @@ test: integration .PHONY: clean integration test clean: - rm -f looper integration_test + rm -f looper integration_test src/*.o diff --git a/src/channel.c b/src/channel.c new file mode 100644 index 0000000..9673317 --- /dev/null +++ b/src/channel.c @@ -0,0 +1,38 @@ +#include +#include +#include +#include +#include "channel.h" + +void channel_add(jack_client_t *client, int idx) +{ + channels[idx].active = 1; + atomic_store(&channels[idx].state, STATE_IDLE); + channels[idx].prev_state = -1; + channels[idx].loop_count = 0; + channels[idx].record_pos = 0; + channels[idx].playback_pos = 0; + + char in_name[64], out_name[64]; + snprintf(in_name, sizeof(in_name), "channel%d_input", next_channel_id); + snprintf(out_name, sizeof(out_name), "channel%d_output", next_channel_id); + + channels[idx].audio_in = jack_port_register(client, in_name, + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsInput, 0); + channels[idx].audio_out = jack_port_register(client, out_name, + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput, 0); + if (!channels[idx].audio_in || !channels[idx].audio_out) + fprintf(stderr, "Failed to register ports for channel %d\n", next_channel_id); + next_channel_id++; + channel_count++; +} + +void channel_remove(jack_client_t *client, int idx) +{ + jack_port_unregister(client, channels[idx].audio_in); + jack_port_unregister(client, channels[idx].audio_out); + channels[idx].active = 0; + channel_count--; +} diff --git a/src/channel.h b/src/channel.h new file mode 100644 index 0000000..d6f0d06 --- /dev/null +++ b/src/channel.h @@ -0,0 +1,39 @@ +#ifndef CHANNEL_H +#define CHANNEL_H + +#include +#include + +#define LOOP_BUF_SIZE (5 * 48000) +#define MAX_CHANNELS 16 + +typedef enum { + STATE_IDLE, + STATE_RECORD, + STATE_LOOPING, + STATE_PAUSED +} looper_state; + +struct channel_t { + atomic_int state; + int prev_state; + float loop_buffer[LOOP_BUF_SIZE]; + int loop_count; + int record_pos; + int playback_pos; + int active; + jack_port_t *audio_in; + jack_port_t *audio_out; +}; + +/* Globals declared in looper.c */ +extern struct channel_t channels[MAX_CHANNELS]; +extern atomic_int channel_count; +extern int next_channel_id; +extern atomic_int cmd_add; +extern atomic_int cmd_remove; + +void channel_add(jack_client_t *client, int idx); +void channel_remove(jack_client_t *client, int idx); + +#endif diff --git a/src/looper.c b/src/looper.c new file mode 100644 index 0000000..5a8afaa --- /dev/null +++ b/src/looper.c @@ -0,0 +1,208 @@ +#include +#include +#include +#include +#include +#include +#include +#include "looper.h" +#include "channel.h" +#include "midi.h" + +/* Global state (shared across files) */ +struct channel_t channels[MAX_CHANNELS]; +atomic_int channel_count = 0; +int next_channel_id = 1; +atomic_int cmd_add = 0; +atomic_int cmd_remove = 0; +jack_port_t *midi_control_port = NULL; +jack_port_t *midi_clock_port = NULL; +atomic_int control_key_active = 0; + +/* ---------------------------------------------------------------- + * process callback + * ---------------------------------------------------------------- */ +int process_callback(jack_nframes_t nframes, void *arg) +{ + (void)arg; + + void *midi_ctrl_buf = jack_port_get_buffer(midi_control_port, nframes); + if (midi_ctrl_buf) { + midi_handle_events(midi_ctrl_buf, nframes); + } + + /* process each active channel */ + for (int c = 0; c < MAX_CHANNELS; c++) { + if (!channels[c].active) continue; + + jack_default_audio_sample_t *in = (jack_default_audio_sample_t *) + jack_port_get_buffer(channels[c].audio_in, nframes); + jack_default_audio_sample_t *out = (jack_default_audio_sample_t *) + jack_port_get_buffer(channels[c].audio_out, nframes); + if (!out) continue; + + int state = atomic_load(&channels[c].state); + + if (state != channels[c].prev_state) { + switch (state) { + case STATE_RECORD: + channels[c].record_pos = 0; + channels[c].loop_count = 0; + break; + case STATE_LOOPING: + if (channels[c].record_pos > 0) + channels[c].loop_count = channels[c].record_pos; + channels[c].playback_pos = 0; + break; + default: + break; + } + } + + jack_nframes_t i; + switch (state) { + case STATE_RECORD: + if (in) { + for (i = 0; i < nframes; i++) { + if (channels[c].record_pos < LOOP_BUF_SIZE) + channels[c].loop_buffer[channels[c].record_pos++] = ((const float *)in)[i]; + ((float *)out)[i] = ((const float *)in)[i]; + } + } else { + memset(out, 0, sizeof(jack_default_audio_sample_t) * nframes); + } + break; + + case STATE_LOOPING: + if (channels[c].loop_count > 0) { + float *outf = (float *)out; + for (i = 0; i < nframes; i++) { + outf[i] = channels[c].loop_buffer[channels[c].playback_pos]; + channels[c].playback_pos = (channels[c].playback_pos + 1) % channels[c].loop_count; + } + } else { + memset(out, 0, sizeof(jack_default_audio_sample_t) * nframes); + } + break; + + case STATE_PAUSED: + memset(out, 0, sizeof(jack_default_audio_sample_t) * nframes); + break; + + default: /* IDLE */ + if (in) { + memcpy(out, in, sizeof(jack_default_audio_sample_t) * nframes); + } else { + memset(out, 0, sizeof(jack_default_audio_sample_t) * nframes); + } + break; + } + + channels[c].prev_state = state; + } + + /* MIDI clock events – affect channel 0 only */ + void *midi_clock_buf = jack_port_get_buffer(midi_clock_port, nframes); + if (midi_clock_buf) { + jack_nframes_t n_clock_events = jack_midi_get_event_count(midi_clock_buf); + jack_midi_event_t cev; + for (jack_nframes_t j = 0; j < n_clock_events; j++) { + if (jack_midi_event_get(&cev, midi_clock_buf, j) != 0) continue; + if (cev.size >= 1) { + unsigned char msg = cev.buffer[0]; + switch (msg) { + case 0xFA: { + int s = atomic_load(&channels[0].state); + if (s == STATE_IDLE) atomic_store(&channels[0].state, STATE_RECORD); + break; + } + case 0xFC: + atomic_store(&channels[0].state, STATE_IDLE); + break; + case 0xFB: { + int s = atomic_load(&channels[0].state); + if (s == STATE_PAUSED) atomic_store(&channels[0].state, STATE_LOOPING); + break; + } + default: + break; + } + } + } + } + + return 0; +} + +/* ---------------------------------------------------------------- + * shutdown callback + * ---------------------------------------------------------------- */ +void jack_shutdown_cb(void *arg) +{ + (void)arg; + fprintf(stderr, "JACK shutdown\n"); + exit(0); +} + +/* ---------------------------------------------------------------- + * looper initialisation + * ---------------------------------------------------------------- */ +int looper_init(jack_client_t *client) +{ + /* channel 0 */ + channels[0].active = 1; + atomic_store(&channels[0].state, STATE_IDLE); + channels[0].prev_state = -1; + channels[0].loop_count = 0; + channels[0].record_pos = 0; + channels[0].playback_pos = 0; + + channels[0].audio_in = jack_port_register(client, "input", + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsInput, 0); + channels[0].audio_out = jack_port_register(client, "output", + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput, 0); + if (!channels[0].audio_in || !channels[0].audio_out) { + fprintf(stderr, "Could not create audio ports for channel 0\n"); + return -1; + } + channel_count = 1; + + midi_control_port = jack_port_register(client, "control", + JACK_DEFAULT_MIDI_TYPE, + JackPortIsInput, 0); + midi_clock_port = jack_port_register(client, "clock", + JACK_DEFAULT_MIDI_TYPE, + JackPortIsInput, 0); + if (!midi_control_port || !midi_clock_port) { + fprintf(stderr, "Could not create MIDI ports\n"); + return -1; + } + + return 0; +} + +/* ---------------------------------------------------------------- + * main‑loop command processing + * ---------------------------------------------------------------- */ +void looper_process_commands(jack_client_t *client) +{ + if (atomic_exchange(&cmd_add, 0)) { + int idx; + for (idx = 0; idx < MAX_CHANNELS; idx++) + if (!channels[idx].active) break; + if (idx < MAX_CHANNELS) { + channel_add(client, idx); + } + } + + if (atomic_exchange(&cmd_remove, 0)) { + int remove_idx = -1; + for (int idx = 1; idx < MAX_CHANNELS; idx++) + if (channels[idx].active) remove_idx = idx; + if (remove_idx != -1) { + channel_remove(client, remove_idx); + } + } +} diff --git a/src/looper.h b/src/looper.h new file mode 100644 index 0000000..7fb68d4 --- /dev/null +++ b/src/looper.h @@ -0,0 +1,18 @@ +#ifndef LOOPER_H +#define LOOPER_H + +#include + +/* Initialisation – must be called after setting process callback */ +int looper_init(jack_client_t *client); + +/* Process callback – to be called by JACK */ +int process_callback(jack_nframes_t nframes, void *arg); + +/* Shutdown callback */ +void jack_shutdown_cb(void *arg); + +/* Main‑loop command processing (add/remove channels) */ +void looper_process_commands(jack_client_t *client); + +#endif diff --git a/src/main.c b/src/main.c index 60e588b..06704ca 100644 --- a/src/main.c +++ b/src/main.c @@ -1,256 +1,8 @@ #include #include #include -#include #include -#include -#include -#include - -#define LOOP_BUF_SIZE (5 * 48000) /* 5 seconds at 48 kHz, mono */ -#define MAX_CHANNELS 16 - -typedef enum { - STATE_IDLE, - STATE_RECORD, - STATE_LOOPING, - STATE_PAUSED -} looper_state; - -/* per‑channel state */ -struct channel_t { - atomic_int state; - int prev_state; - float loop_buffer[LOOP_BUF_SIZE]; - int loop_count; - int record_pos; - int playback_pos; - int active; /* 1 = channel in use */ - jack_port_t *audio_in; - jack_port_t *audio_out; -}; - -static struct channel_t channels[MAX_CHANNELS]; -static atomic_int channel_count = 0; /* number of active channels */ -static int next_channel_id = 1; /* for port naming */ -static atomic_int cmd_add = 0; /* set by MIDI note 60 in process() */ -static atomic_int cmd_remove = 0; /* set by MIDI note 61 */ - -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 real‑time context - * --------------------------------------------------------------- */ -static int process(jack_nframes_t nframes, void *arg) -{ - (void)arg; - - /* handle MIDI commands on the global control port */ - void *midi_ctrl_buf = jack_port_get_buffer(midi_control_port, nframes); - if (midi_ctrl_buf) { - jack_nframes_t nevents = jack_midi_get_event_count(midi_ctrl_buf); - 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) 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) { - /* 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; - } - } - } - } else if ((status & 0xf0) == 0x80 || ((status & 0xf0) == 0x90 && vel == 0)) { - /* note‑off – clear control key state */ - atomic_store(&control_key_active, 0); - } - } - } - - /* process each active channel */ - for (int c = 0; c < MAX_CHANNELS; c++) { - if (!channels[c].active) continue; - - jack_default_audio_sample_t *in = (jack_default_audio_sample_t *) - jack_port_get_buffer(channels[c].audio_in, nframes); - jack_default_audio_sample_t *out = (jack_default_audio_sample_t *) - jack_port_get_buffer(channels[c].audio_out, nframes); - if (!out) continue; /* safety */ - - int state = atomic_load(&channels[c].state); - - /* transition initialisation */ - if (state != channels[c].prev_state) { - switch (state) { - case STATE_RECORD: - channels[c].record_pos = 0; - channels[c].loop_count = 0; - break; - case STATE_LOOPING: - if (channels[c].record_pos > 0) - channels[c].loop_count = channels[c].record_pos; - channels[c].playback_pos = 0; - break; - default: - break; - } - } - - jack_nframes_t i; - - switch (state) { - case STATE_RECORD: - if (in) { - const float *inf = (const float *)in; - float *outf = (float *)out; - for (i = 0; i < nframes; i++) { - if (channels[c].record_pos < LOOP_BUF_SIZE) { - channels[c].loop_buffer[channels[c].record_pos] = inf[i]; - channels[c].record_pos++; - } - outf[i] = inf[i]; /* monitor input */ - } - } else { - memset(out, 0, sizeof(jack_default_audio_sample_t) * nframes); - } - break; - - case STATE_LOOPING: - if (channels[c].loop_count > 0) { - float *outf = (float *)out; - for (i = 0; i < nframes; i++) { - outf[i] = channels[c].loop_buffer[channels[c].playback_pos]; - channels[c].playback_pos = - (channels[c].playback_pos + 1) % channels[c].loop_count; - } - } else { - memset(out, 0, sizeof(jack_default_audio_sample_t) * nframes); - } - break; - - case STATE_PAUSED: - memset(out, 0, sizeof(jack_default_audio_sample_t) * nframes); - break; - - default: /* IDLE */ - if (in) { - memcpy(out, in, sizeof(jack_default_audio_sample_t) * nframes); - } else { - memset(out, 0, sizeof(jack_default_audio_sample_t) * nframes); - } - break; - } - - channels[c].prev_state = state; - } - - /* ----- MIDI clock events (still affect channel 0 only) ----- */ - void *midi_clock_buf = jack_port_get_buffer(midi_clock_port, nframes); - if (midi_clock_buf) { - jack_nframes_t n_clock_events = jack_midi_get_event_count(midi_clock_buf); - jack_midi_event_t cev; - for (jack_nframes_t j = 0; j < n_clock_events; j++) { - if (jack_midi_event_get(&cev, midi_clock_buf, j) != 0) continue; - if (cev.size >= 1) { - unsigned char msg = cev.buffer[0]; - switch (msg) { - case 0xFA: { - int s = atomic_load(&channels[0].state); - if (s == STATE_IDLE) - atomic_store(&channels[0].state, STATE_RECORD); - break; - } - case 0xFC: - atomic_store(&channels[0].state, STATE_IDLE); - break; - case 0xFB: { - int s = atomic_load(&channels[0].state); - if (s == STATE_PAUSED) - atomic_store(&channels[0].state, STATE_LOOPING); - break; - } - default: - break; - } - } - } - } - - return 0; -} - -static void jack_shutdown(void *arg) -{ - (void)arg; - fprintf(stderr, "JACK shutdown\n"); - exit(0); -} +#include "looper.h" int main(int argc, char *argv[]) { @@ -260,7 +12,7 @@ int main(int argc, char *argv[]) jack_options_t options = JackNullOption; jack_status_t status; - client = jack_client_open(client_name, options, &status); + jack_client_t *client = jack_client_open(client_name, options, &status); if (client == NULL) { fprintf(stderr, "jack_client_open() failed, status = 0x%2.0x\n", status); if (status & JackServerFailed) @@ -271,99 +23,26 @@ int main(int argc, char *argv[]) if (status & JackNameNotUnique) client_name = jack_get_client_name(client); - jack_set_process_callback(client, process, NULL); - jack_on_shutdown(client, jack_shutdown, NULL); + jack_set_process_callback(client, process_callback, NULL); + jack_on_shutdown(client, jack_shutdown_cb, NULL); - /* ------------------ channel 0 (the default channel) ------------------ */ - channels[0].active = 1; - atomic_store(&channels[0].state, STATE_IDLE); - channels[0].prev_state = -1; - channels[0].loop_count = 0; - channels[0].record_pos = 0; - channels[0].playback_pos = 0; - - channels[0].audio_in = jack_port_register(client, "input", - JACK_DEFAULT_AUDIO_TYPE, - JackPortIsInput, 0); - channels[0].audio_out = jack_port_register(client, "output", - JACK_DEFAULT_AUDIO_TYPE, - JackPortIsOutput, 0); - if (!channels[0].audio_in || !channels[0].audio_out) { - fprintf(stderr, "Could not create audio ports for channel 0\n"); - return 1; - } - channel_count = 1; - - /* MIDI control & clock ports (shared across all channels) */ - midi_control_port = jack_port_register(client, "control", - JACK_DEFAULT_MIDI_TYPE, - JackPortIsInput, 0); - midi_clock_port = jack_port_register(client, "clock", - JACK_DEFAULT_MIDI_TYPE, - JackPortIsInput, 0); - if (!midi_control_port || !midi_clock_port) { - fprintf(stderr, "Could not create MIDI ports\n"); + if (looper_init(client) != 0) { + fprintf(stderr, "looper initialisation failed\n"); + jack_client_close(client); return 1; } if (jack_activate(client)) { fprintf(stderr, "Cannot activate client\n"); + jack_client_close(client); return 1; } fprintf(stderr, "looper running (client name '%s')\n", client_name); while (1) { - /* process pending add‑channel / remove‑channel commands - * (only safe outside the real‑time callback) */ - if (atomic_exchange(&cmd_add, 0)) { - int idx; - for (idx = 0; idx < MAX_CHANNELS; idx++) - if (!channels[idx].active) break; - if (idx < MAX_CHANNELS) { - channels[idx].active = 1; - atomic_store(&channels[idx].state, STATE_IDLE); - channels[idx].prev_state = -1; - channels[idx].loop_count = 0; - channels[idx].record_pos = 0; - channels[idx].playback_pos = 0; - - char in_name[64], out_name[64]; - snprintf(in_name, sizeof(in_name), - "channel%d_input", next_channel_id); - snprintf(out_name, sizeof(out_name), - "channel%d_output", next_channel_id); - - channels[idx].audio_in = - jack_port_register(client, in_name, - JACK_DEFAULT_AUDIO_TYPE, - JackPortIsInput, 0); - channels[idx].audio_out = - jack_port_register(client, out_name, - JACK_DEFAULT_AUDIO_TYPE, - JackPortIsOutput, 0); - if (!channels[idx].audio_in || !channels[idx].audio_out) - fprintf(stderr, "Failed to register ports for channel %d\n", - next_channel_id); - next_channel_id++; - channel_count++; - } - } - - if (atomic_exchange(&cmd_remove, 0)) { - /* find the highest active channel index >0 */ - int remove_idx = -1; - for (int idx = 1; idx < MAX_CHANNELS; idx++) - if (channels[idx].active) remove_idx = idx; - if (remove_idx != -1) { - jack_port_unregister(client, channels[remove_idx].audio_in); - jack_port_unregister(client, channels[remove_idx].audio_out); - channels[remove_idx].active = 0; - channel_count--; - } - } - - sleep(1); + looper_process_commands(client); + usleep(50000); /* check commands every 50 ms */ } jack_client_close(client); diff --git a/src/midi.c b/src/midi.c new file mode 100644 index 0000000..095fc3d --- /dev/null +++ b/src/midi.c @@ -0,0 +1,91 @@ +#include +#include +#include +#include "midi.h" +#include "channel.h" + +extern atomic_int control_key_active; +extern atomic_int cmd_add; +extern atomic_int cmd_remove; + +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); + switch (note) { + case 60: atomic_store(&cmd_add, 1); break; + case 61: atomic_store(&cmd_remove, 1); break; + case 62: /* trigger looper – 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 */ + 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); + } + } +} diff --git a/src/midi.h b/src/midi.h new file mode 100644 index 0000000..08b5eeb --- /dev/null +++ b/src/midi.h @@ -0,0 +1,8 @@ +#ifndef MIDI_H +#define MIDI_H + +#include + +void midi_handle_events(void *port_buffer, jack_nframes_t nframes); + +#endif