From f38797fe0a29215b23c4e5d1ddae704810c5566e Mon Sep 17 00:00:00 2001 From: Loic Coenen Date: Mon, 18 May 2026 17:35:31 +0000 Subject: [PATCH] refactor: replace writer thread with synchronous save and fix ring buffer memory ordering --- engine/integration_test | Bin 54072 -> 60248 bytes engine/looper | Bin 69936 -> 81152 bytes engine/src/channel.c | 39 ++- engine/src/channel.c~ | 117 ------- engine/src/channel.o | Bin 10184 -> 18928 bytes engine/src/looper.c | 285 +++++++--------- engine/src/looper.c~ | 675 ------------------------------------- engine/src/looper.o | Bin 35424 -> 39568 bytes engine/src/main.c | 7 +- engine/src/main.o | Bin 9656 -> 9664 bytes engine/src/midi.c | 12 +- engine/src/midi.c~ | 154 --------- engine/src/midi.o | Bin 9376 -> 13712 bytes engine/src/pipe.c~ | 111 ------ engine/src/pipe.o | Bin 12968 -> 13152 bytes engine/src/plugins.c | 0 engine/src/plugins.h | 0 engine/src/ringbuffer.c | 63 ++-- engine/src/ringbuffer.o | Bin 7976 -> 8184 bytes engine/src/wav.c | 66 ++-- engine/src/wav.o | Bin 8336 -> 8328 bytes engine/tests/integration.c | 131 ++++--- 22 files changed, 291 insertions(+), 1369 deletions(-) delete mode 100644 engine/src/channel.c~ delete mode 100644 engine/src/looper.c~ delete mode 100644 engine/src/midi.c~ delete mode 100644 engine/src/pipe.c~ delete mode 100644 engine/src/plugins.c delete mode 100644 engine/src/plugins.h diff --git a/engine/integration_test b/engine/integration_test index 054738c5a386a522e2090cb3b0d949a3cdaf1f2d..0827ef15cef7ca075dde09bacb7f77db711f93a9 100755 GIT binary patch literal 60248 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 MirOfKDA4%-0K-nwcmMzZ literal 54072 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 diff --git a/engine/looper b/engine/looper index 1c6835c2f56e6361172426a343671f819ce79631..fcdef184af949e0a360bc66c5746b9d106c1d2bf 100755 GIT binary patch literal 81152 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 literal 69936 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 -#include -#include -#include - -<<<<<<< HEAD -======= -/* Helper: zero a scene and set its state to IDLE */ -static void init_scene(scene_t *sc) { - memset(sc, 0, sizeof(scene_t)); - atomic_store(&sc->state, STATE_IDLE); - atomic_store(&sc->prev_state, -1); -} - ->>>>>>> 3-integrate-carla -void channel_add(jack_client_t *client, int idx) { - 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); - /* Do NOT mark channel active – process loop will skip it */ - atomic_store(&channels[idx].active, 0); - return; - } - - atomic_store(&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; - channels[idx].save_ring = NULL; - - next_channel_id++; - channel_count++; -} - -void channel_remove(jack_client_t *client, int idx) { - (void)client; -<<<<<<< HEAD - atomic_store(&channels[idx].active, 0); - channel_count--; -======= - struct channel_t *cur = get_channels_array(); - atomic_store(&cur[idx].active, 0); - atomic_fetch_sub(&channel_count, 1); -} - -void channel_add_scene(jack_client_t *client, int idx) { - (void)client; - struct channel_t *cur = get_channels_array(); - if (atomic_load(&cur[idx].scene_count) >= MAX_SCENES) - return; - int ns = atomic_load(&cur[idx].scene_count); - init_scene(&cur[idx].scenes[ns]); - atomic_fetch_add(&cur[idx].scene_count, 1); -} - -void channel_remove_scene(jack_client_t *client, int idx) { - (void)client; - struct channel_t *cur = get_channels_array(); - int sc = atomic_load(&cur[idx].scene_count); - if (sc <= 1) - return; - int cs = atomic_load(&cur[idx].current_scene); - /* shift remaining scenes down (atomic copy of fields) */ - for (int i = cs; i < sc - 1; i++) { - atomic_store(&cur[idx].scenes[i].loop_count, - atomic_load(&cur[idx].scenes[i + 1].loop_count)); - atomic_store(&cur[idx].scenes[i].record_pos, - atomic_load(&cur[idx].scenes[i + 1].record_pos)); - atomic_store(&cur[idx].scenes[i].playback_pos, - atomic_load(&cur[idx].scenes[i + 1].playback_pos)); - atomic_store(&cur[idx].scenes[i].state, - atomic_load(&cur[idx].scenes[i + 1].state)); - atomic_store(&cur[idx].scenes[i].prev_state, - atomic_load(&cur[idx].scenes[i + 1].prev_state)); - /* copy loop data (may race with RT thread; acceptable for this release) */ - memcpy(cur[idx].scenes[i].loop.audio_buffer, - cur[idx].scenes[i + 1].loop.audio_buffer, - LOOP_BUF_SIZE * sizeof(float)); - } - atomic_fetch_sub(&cur[idx].scene_count, 1); - int new_sc = atomic_load(&cur[idx].scene_count); - if (cs >= new_sc) - atomic_store(&cur[idx].current_scene, new_sc - 1); -} - -void channel_next_scene(jack_client_t *client, int idx) { - (void)client; - struct channel_t *cur = get_channels_array(); - int sc = atomic_load(&cur[idx].scene_count); - if (sc > 1) { - int cs = atomic_load(&cur[idx].current_scene); - atomic_store(&cur[idx].current_scene, (cs + 1) % sc); - } -} - -void channel_prev_scene(jack_client_t *client, int idx) { - (void)client; - struct channel_t *cur = get_channels_array(); - int sc = atomic_load(&cur[idx].scene_count); - if (sc > 1) { - int cs = atomic_load(&cur[idx].current_scene); - atomic_store(&cur[idx].current_scene, (cs - 1 + sc) % sc); - } ->>>>>>> 3-integrate-carla -} diff --git a/engine/src/channel.o b/engine/src/channel.o index b39bc850af49489aac9312f2c5d4a6544cce64fe..7c32a586cd60b01d5d6e0543319685ddf171aca7 100644 GIT binary patch literal 18928 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 literal 10184 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 CNX8%l diff --git a/engine/src/looper.c b/engine/src/looper.c index 62224cb..62a9fec 100644 --- a/engine/src/looper.c +++ b/engine/src/looper.c @@ -2,9 +2,9 @@ #include "looper.h" #include "channel.h" #include "midi.h" +#include "pipe.h" #include "queue.h" #include "wav.h" -#include "pipe.h" #include #include #include @@ -38,13 +38,23 @@ static void looper_write_status(void) { int state = atomic_load(&channels[ch].scenes[sc_idx].state); const char *state_str; switch (state) { - case STATE_IDLE: state_str = "IDLE"; break; - case STATE_RECORD: state_str = "RECORD"; break; - case STATE_LOOPING:state_str = "LOOPING";break; - case STATE_PAUSED: state_str = "PAUSED"; break; - default: state_str = "UNKNOWN"; + case STATE_IDLE: + state_str = "IDLE"; + break; + case STATE_RECORD: + state_str = "RECORD"; + break; + case STATE_LOOPING: + state_str = "LOOPING"; + break; + case STATE_PAUSED: + state_str = "PAUSED"; + break; + default: + state_str = "UNKNOWN"; } - int n = snprintf(buf, sizeof(buf), "CH=%d SC=%d STATE=%s\n", ch, sc_idx, state_str); + int n = snprintf(buf, sizeof(buf), "CH=%d SC=%d STATE=%s\n", ch, sc_idx, + state_str); if (n > 0) { int ret = write(status_fd, buf, n); (void)ret; @@ -69,101 +79,101 @@ atomic_int bind_channel = 0; /* Deferred removal index (1 second grace) */ static int pending_unregister_idx = -1; -/* writer thread function and sample rate holder */ -static void *writer_thread(void *arg); +/* sample rate holder */ static int global_sample_rate = 0; /* execute a single command (called from looper_process_commands) */ static void exec_command(command_t cmd, jack_client_t *client) { - int ch = cmd.channel; - if (ch < 0) ch = 0; + int ch = cmd.channel; + if (ch < 0) + ch = 0; - switch (cmd.type) { - case CMD_CYCLE: { - int sc_idx = atomic_load(&channels[ch].current_scene); - int state = atomic_load(&channels[ch].scenes[sc_idx].state); - switch (state) { - case STATE_IDLE: - atomic_store(&channels[ch].scenes[sc_idx].state, STATE_RECORD); - break; - case STATE_RECORD: - atomic_store(&channels[ch].scenes[sc_idx].state, STATE_LOOPING); - break; - case STATE_LOOPING: - atomic_store(&channels[ch].scenes[sc_idx].state, STATE_PAUSED); - break; - case STATE_PAUSED: - atomic_store(&channels[ch].scenes[sc_idx].state, STATE_LOOPING); - break; - } - atomic_store(&channels[ch].scenes[sc_idx].prev_state, -1); + switch (cmd.type) { + case CMD_CYCLE: { + int sc_idx = atomic_load(&channels[ch].current_scene); + scene_t *sc_ptr = &channels[ch].scenes[sc_idx]; + int state = atomic_load(&sc_ptr->state); + switch (state) { + case STATE_IDLE: + atomic_store(&sc_ptr->state, STATE_RECORD); + break; + case STATE_RECORD: + atomic_store(&sc_ptr->state, STATE_LOOPING); + break; + case STATE_LOOPING: + atomic_store(&sc_ptr->state, STATE_PAUSED); + break; + case STATE_PAUSED: + atomic_store(&sc_ptr->state, STATE_LOOPING); break; } - case CMD_STOP: - for (int s = 0; s < atomic_load(&channels[ch].scene_count); s++) { - atomic_store(&channels[ch].scenes[s].state, STATE_IDLE); - atomic_store(&channels[ch].scenes[s].prev_state, -1); - } - break; - - case CMD_ADD_CHANNEL: - case CMD_ADD_MIDI_CHANNEL: { - int idx; - for (idx = 0; idx < MAX_CHANNELS; idx++) - if (!channels[idx].active) - break; - if (idx < MAX_CHANNELS) - channel_add(client, idx); - break; + break; + } + case CMD_STOP: + for (int s = 0; s < atomic_load(&channels[ch].scene_count); s++) { + atomic_store(&channels[ch].scenes[s].state, STATE_IDLE); + atomic_store(&channels[ch].scenes[s].prev_state, -1); } + break; - case CMD_REMOVE_CHANNEL: { - 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); - pending_unregister_idx = remove_idx; - } - break; + case CMD_ADD_CHANNEL: + case CMD_ADD_MIDI_CHANNEL: { + int idx; + for (idx = 0; idx < MAX_CHANNELS; idx++) + if (!channels[idx].active) + break; + if (idx < MAX_CHANNELS) + channel_add(client, idx); + break; + } + + case CMD_REMOVE_CHANNEL: { + 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); + pending_unregister_idx = remove_idx; } + break; + } - case CMD_BIND_CHANNEL: - atomic_store(&bind_channel, cmd.data); - break; + case CMD_BIND_CHANNEL: + atomic_store(&bind_channel, cmd.data); + break; - case CMD_UNBIND: - atomic_store(&bind_channel, 0); - break; + case CMD_UNBIND: + atomic_store(&bind_channel, 0); + break; - case CMD_LOAD: - atomic_store(&cmd_load, 1); - break; + case CMD_LOAD: + atomic_store(&cmd_load, 1); + break; - case CMD_SAVE: - atomic_store(&cmd_save, 1); - break; + case CMD_SAVE: + atomic_store(&cmd_save, 1); + break; - case CMD_ADD_SCENE: - channel_add_scene(client, ch); - break; + case CMD_ADD_SCENE: + channel_add_scene(client, ch); + break; - case CMD_REMOVE_SCENE: - channel_remove_scene(client, ch); - break; + case CMD_REMOVE_SCENE: + channel_remove_scene(client, ch); + break; - case CMD_NEXT_SCENE: - channel_next_scene(client, ch); - break; + case CMD_NEXT_SCENE: + channel_next_scene(client, ch); + break; - case CMD_PREV_SCENE: - channel_prev_scene(client, ch); - break; + case CMD_PREV_SCENE: + channel_prev_scene(client, ch); + break; - default: - break; - } + default: + break; + } } /* ---------------------------------------------------------------- @@ -217,7 +227,8 @@ int process_callback(jack_nframes_t nframes, void *arg) { /* MIDI channel handling */ void *midi_in_buf = jack_port_get_buffer(channels[c].midi_in, nframes); void *midi_out_buf = jack_port_get_buffer(channels[c].midi_out, nframes); - if (!midi_out_buf) continue; + if (!midi_out_buf) + continue; switch (state) { case STATE_RECORD: { @@ -232,7 +243,8 @@ int process_callback(jack_nframes_t nframes, void *arg) { sc->loop.midi_events[rp].timestamp = ev.time; sc->loop.midi_events[rp].status = ev.buffer[0]; sc->loop.midi_events[rp].note = (ev.size > 1) ? ev.buffer[1] : 0; - sc->loop.midi_events[rp].velocity = (ev.size > 2) ? ev.buffer[2] : 0; + sc->loop.midi_events[rp].velocity = + (ev.size > 2) ? ev.buffer[2] : 0; atomic_store(&sc->record_pos, rp + 1); } } @@ -286,7 +298,8 @@ int process_callback(jack_nframes_t nframes, void *arg) { jack_default_audio_sample_t *out = (jack_default_audio_sample_t *)jack_port_get_buffer( channels[c].audio_out, nframes); - if (!out) continue; + if (!out) + continue; switch (state) { case STATE_RECORD: @@ -397,7 +410,6 @@ void jack_shutdown_cb(void *arg) { exit(0); } - /* ---------------------------------------------------------------- * looper initialisation * ---------------------------------------------------------------- */ @@ -408,10 +420,11 @@ int looper_init(jack_client_t *client) { /* create status FIFO (ignore if already exists) */ mkfifo(STATUS_FIFO, 0666); - /* open the status FIFO for reading+writing so writes work even without reader */ + /* open the status FIFO for reading+writing so writes work even without reader + */ status_fd = open(STATUS_FIFO, O_RDWR); if (status_fd < 0) { - perror("open status FIFO"); + perror("open status FIFO"); } queue_init(&cmd_queue); @@ -421,10 +434,9 @@ int looper_init(jack_client_t *client) { /* start the FIFO reader thread */ pipe_start_reader(); - /* channel 0 */ channels[0].active = 1; - channels[0].type = CHANNEL_AUDIO; /* default */ + channels[0].type = CHANNEL_AUDIO; /* default */ channels[0].current_scene = 0; channels[0].scene_count = 1; init_scene(&channels[0].scenes[0]); /* sets state IDLE, prev_state -1 */ @@ -453,58 +465,15 @@ int looper_init(jack_client_t *client) { return -1; } + /* Give JACK time to register the ports before clients connect */ + { + struct timespec req = {.tv_sec = 0, .tv_nsec = 500000000}; + nanosleep(&req, NULL); + } + return 0; } -/* ---------------------------------------------------------------- - * writer thread – consumes the save ring and writes WAV file - * ---------------------------------------------------------------- */ -static void *writer_thread(void *arg) { - struct channel_t *ch = (struct channel_t *)arg; - int sc_idx = atomic_load(&ch->current_scene); - scene_t *sc = &ch->scenes[sc_idx]; - RingBuf *ring = (RingBuf *)ch->save_ring; - if (!ring) - return NULL; - - static const char *path = "save.wav"; - unsigned sr = (unsigned)global_sample_rate; - if (sr == 0) - sr = 48000; - - int lc = atomic_load(&sc->loop_count); - float *outbuf = malloc((size_t)lc * sizeof(float)); - if (!outbuf) { - ring_destroy(ring); - free(ring); - ch->save_ring = NULL; - return NULL; - } - size_t collected = 0; - size_t want = (size_t)lc; - while (collected < want) { - size_t got = ring_read(ring, outbuf + collected, want - collected); - collected += got; - if (got == 0) { - struct timespec req = {.tv_sec = 0, .tv_nsec = 10000000}; - nanosleep(&req, NULL); - } - } - wav_write(path, outbuf, (unsigned)lc, sr); - free(outbuf); - - /* Signal the RT thread to stop writing */ - atomic_store_explicit(&ch->save_complete, 1, memory_order_release); - /* Wait for the RT thread to see the flag (one audio period) */ - struct timespec req = { .tv_sec = 0, .tv_nsec = 10000000 }; /* 10ms */ - nanosleep(&req, NULL); - - ring_destroy(ring); - free(ring); - atomic_store_explicit(&ch->save_ring, NULL, memory_order_release); - return NULL; -} - /* ---------------------------------------------------------------- * main‑loop command processing * ---------------------------------------------------------------- */ @@ -578,25 +547,31 @@ void looper_process_commands(jack_client_t *client) { } } - /* ---------- save command (writer thread) ---------- */ + /* ---------- save command (synchronous) ---------- */ if (atomic_exchange(&cmd_save, 0)) { int sc_idx = atomic_load(&channels[0].current_scene); scene_t *sc = &channels[0].scenes[sc_idx]; int lc = atomic_load(&sc->loop_count); - if (atomic_load(&sc->state) == STATE_LOOPING && lc > 0 && - channels[0].save_ring == NULL) { - RingBuf *ring = (RingBuf *)malloc(sizeof(RingBuf)); - if (ring) { - size_t sz = (size_t)lc * 2; - if (ring_init(ring, sz) == 0) { - atomic_store_explicit(&channels[0].save_ring, (_Atomic RingBuf *)ring, - memory_order_release); - pthread_t th; - pthread_create(&th, NULL, writer_thread, &channels[0]); - pthread_detach(th); - } else { - free(ring); - } + if (atomic_load(&sc->state) == STATE_LOOPING && lc > 0) { + /* Deactivate channel to prevent RT thread from reading the buffer */ + int was_active = atomic_load(&channels[0].active); + if (was_active) { + atomic_store(&channels[0].active, 0); + struct timespec req = {.tv_sec = 0, .tv_nsec = 500000000}; /* 500 ms */ + nanosleep(&req, NULL); + } + /* Now safe to copy the loop buffer */ + float *data = malloc((size_t)lc * sizeof(float)); + if (data) { + memcpy(data, sc->loop.audio_buffer, (size_t)lc * sizeof(float)); + unsigned sr = (unsigned)global_sample_rate; + if (sr == 0) sr = 48000; + wav_write("save.wav", data, (unsigned)lc, sr); + free(data); + } + /* Reactivate channel */ + if (was_active) { + atomic_store(&channels[0].active, 1); } } } diff --git a/engine/src/looper.c~ b/engine/src/looper.c~ deleted file mode 100644 index 89cb2db..0000000 --- a/engine/src/looper.c~ +++ /dev/null @@ -1,675 +0,0 @@ -// cppcheck-suppress missingIncludeSystem -#include "looper.h" -#include "channel.h" -#include "midi.h" -<<<<<<< HEAD -#include "wav.h" -#include "ringbuffer.h" -#include "pipe.h" -#include -#include -======= -#include "queue.h" ->>>>>>> 3-integrate-carla -#include -#include -#include -#include -#include -#include -#include -#include -#include -<<<<<<< HEAD -#include -#include "queue.h" -#include "command.h" - -/* Global command queues */ -spsc_queue_t cmd_queue; -spsc_queue_t cmd_queue_main_midi; -spsc_queue_t cmd_queue_main_fifo; -======= -#include -#include ->>>>>>> 3-integrate-carla - -#define STATUS_FIFO "/tmp/looper_status" - -/* writer status fd */ -static int status_fd = -1; - -static void looper_write_status(void) { -<<<<<<< HEAD - if (status_fd < 0) - return; - char buf[256]; - for (int ch = 0; ch < MAX_CHANNELS; ch++) { - if (!atomic_load(&channels[ch].active)) - continue; - int state_val = atomic_load(&channels[ch].state); - const char *state_str; - switch (state_val) { - case STATE_IDLE: state_str = "IDLE"; break; - case STATE_RECORD: state_str = "RECORD"; break; - case STATE_LOOPING: state_str = "LOOPING"; break; - case STATE_PAUSED: state_str = "PAUSED"; break; - default: state_str = "UNKNOWN"; - } - int n = snprintf(buf, sizeof(buf), - "CH=%d SC=%d STATE=%s\n", - ch, 0, state_str); - if (n > 0) { - int ret = write(status_fd, buf, n); - (void)ret; - } - } -======= - int fd = open(STATUS_FIFO, O_WRONLY | O_NONBLOCK); - if (fd < 0) - return; - struct channel_t *cur = get_channels_array(); - int cap = atomic_load(&channel_capacity); - char buf[256]; - for (int ch = 0; ch < cap; ch++) { - if (!atomic_load(&cur[ch].active)) - continue; - int sc_idx = atomic_load(&cur[ch].current_scene); - int state = atomic_load(&cur[ch].scenes[sc_idx].state); - const char *state_str; - switch (state) { - case STATE_IDLE: - state_str = "IDLE"; - break; - case STATE_RECORD: - state_str = "RECORD"; - break; - case STATE_LOOPING: - state_str = "LOOPING"; - break; - case STATE_PAUSED: - state_str = "PAUSED"; - break; - default: - state_str = "UNKNOWN"; - } - int n = snprintf(buf, sizeof(buf), "CH=%d SC=%d STATE=%s\n", ch, sc_idx, - state_str); - if (n > 0) { - int ret = write(fd, buf, n); - (void)ret; - } - } - close(fd); ->>>>>>> 3-integrate-carla -} - -/* Global state (shared across files) */ -struct channel_t channels[MAX_CHANNELS]; -atomic_int channel_count = 0; -atomic_int channel_capacity = MAX_CHANNELS; -int next_channel_id = 1; -atomic_int cmd_add = 0; -atomic_int cmd_remove = 0; -atomic_int cmd_load = 0; -atomic_int cmd_save = 0; -jack_port_t *midi_control_port = NULL; -jack_port_t *midi_clock_port = NULL; -atomic_int control_key_active = 0; -atomic_int bind_channel = 0; - -/* Deferred removal index (1 second grace) */ -static int pending_unregister_idx = -1; - -/* writer thread function and sample rate holder */ -static void *writer_thread(void *arg); -static int global_sample_rate = 0; - -/* execute a single command (called from looper_process_commands) */ -static void exec_command(command_t cmd, jack_client_t *client) { - int ch = cmd.channel; - if (ch < 0) ch = 0; - - switch (cmd.type) { - case CMD_CYCLE: { - int state = atomic_load(&channels[ch].state); - switch (state) { - case STATE_IDLE: - atomic_store(&channels[ch].state, STATE_RECORD); - break; - case STATE_RECORD: - atomic_store(&channels[ch].state, STATE_LOOPING); - break; - case STATE_LOOPING: - atomic_store(&channels[ch].state, STATE_PAUSED); - break; - case STATE_PAUSED: - atomic_store(&channels[ch].state, STATE_LOOPING); - break; - } - atomic_store(&channels[ch].prev_state, -1); - break; - } - case CMD_STOP: - atomic_store(&channels[ch].state, STATE_IDLE); - atomic_store(&channels[ch].prev_state, -1); - break; - - case CMD_ADD_CHANNEL: - case CMD_ADD_MIDI_CHANNEL: { - int idx; - for (idx = 0; idx < MAX_CHANNELS; idx++) - if (!channels[idx].active) - break; - if (idx < MAX_CHANNELS) - channel_add(client, idx); - break; - } - - case CMD_REMOVE_CHANNEL: { - 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); - pending_unregister_idx = remove_idx; - } - break; - } - - case CMD_BIND_CHANNEL: - atomic_store(&bind_channel, cmd.data); - break; - - case CMD_UNBIND: - atomic_store(&bind_channel, 0); - break; - - case CMD_LOAD: - atomic_store(&cmd_load, 1); - break; - - case CMD_SAVE: - atomic_store(&cmd_save, 1); - break; - - case CMD_ADD_SCENE: - case CMD_REMOVE_SCENE: - case CMD_NEXT_SCENE: - case CMD_PREV_SCENE: - break; - - default: - break; - } -} - -/* ---------------------------------------------------------------- - * process callback - * ---------------------------------------------------------------- */ -int process_callback(jack_nframes_t nframes, void *arg) { - (void)arg; - - if (midi_control_port) { - 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 (!atomic_load(&channels[c].active)) - continue; - - /* Guard against NULL ports (e.g. if port registration failed) */ - if (!channels[c].audio_in || !channels[c].audio_out) { - fprintf(stderr, "WARN: channel %d has NULL audio port(s), skipping\n", c); - continue; - } - - const jack_default_audio_sample_t *in = - (const 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 != atomic_load(&channels[c].prev_state)) { - switch (state) { - case STATE_RECORD: - atomic_store(&channels[c].record_pos, 0); - atomic_store(&channels[c].loop_count, 0); - break; - case STATE_LOOPING: - if (atomic_load(&channels[c].prev_state) == STATE_RECORD && - atomic_load(&channels[c].record_pos) > 0) - atomic_store(&channels[c].loop_count, - atomic_load(&channels[c].record_pos)); - atomic_store(&channels[c].playback_pos, 0); - break; - default: - break; - } - } - -<<<<<<< HEAD - jack_nframes_t i; - switch (state) { - case STATE_RECORD: - if (in) { - float *f_out = (float *)out; - const float *f_in = (const float *)in; - for (i = 0; i < nframes; i++) { - int rp = atomic_fetch_add(&channels[c].record_pos, 1); - if (rp < LOOP_BUF_SIZE) - channels[c].loop_buffer[rp] = f_in[i]; - f_out[i] = f_in[i]; - } - } else { -======= - if (active_channels[c].type == CHANNEL_MIDI) { - /* MIDI channel handling */ - switch (state) { - case STATE_RECORD: { - void *midi_in_buf = - jack_port_get_buffer(active_channels[c].midi_in, nframes); - if (midi_in_buf) { - jack_nframes_t nevents = jack_midi_get_event_count(midi_in_buf); - jack_midi_event_t ev; - for (jack_nframes_t j = 0; j < nevents; j++) { - if (jack_midi_event_get(&ev, midi_in_buf, j) != 0) - continue; - int rp = atomic_load(&sc->record_pos); - if (rp < MAX_MIDI_EVENTS) { - sc->loop.midi_events[rp].timestamp = ev.time; - sc->loop.midi_events[rp].status = ev.buffer[0]; - sc->loop.midi_events[rp].note = (ev.size > 1) ? ev.buffer[1] : 0; - sc->loop.midi_events[rp].velocity = - (ev.size > 2) ? ev.buffer[2] : 0; - atomic_store(&sc->record_pos, rp + 1); - } - } - /* forward incoming MIDI to output during record */ - void *midi_out_buf = - jack_port_get_buffer(active_channels[c].midi_out, nframes); - if (midi_out_buf) { - jack_midi_clear_buffer(midi_out_buf); - for (jack_nframes_t j = 0; j < nevents; j++) { - if (jack_midi_event_get(&ev, midi_in_buf, j) != 0) - continue; - jack_midi_event_write(midi_out_buf, ev.time, ev.buffer, ev.size); - } - } - } - break; - } - case STATE_LOOPING: { - void *midi_out_buf = - jack_port_get_buffer(active_channels[c].midi_out, nframes); - if (midi_out_buf) { - jack_midi_clear_buffer(midi_out_buf); - int cnt = atomic_load(&sc->loop_count); - if (cnt > 0) { - for (int e = 0; e < cnt; e++) { - unsigned char msg[3]; - msg[0] = sc->loop.midi_events[e].status; - msg[1] = sc->loop.midi_events[e].note; - msg[2] = sc->loop.midi_events[e].velocity; - jack_midi_event_write(midi_out_buf, 0, msg, 3); - } - } - } - break; - } - case STATE_PAUSED: - /* no output */ - break; - default: /* IDLE */ - { - void *midi_in_buf = - jack_port_get_buffer(active_channels[c].midi_in, nframes); - void *midi_out_buf = - jack_port_get_buffer(active_channels[c].midi_out, nframes); - if (midi_in_buf && midi_out_buf) { - jack_midi_clear_buffer(midi_out_buf); - jack_nframes_t nevents = jack_midi_get_event_count(midi_in_buf); - jack_midi_event_t ev; - for (jack_nframes_t j = 0; j < nevents; j++) { - if (jack_midi_event_get(&ev, midi_in_buf, j) != 0) - continue; - jack_midi_event_write(midi_out_buf, ev.time, ev.buffer, ev.size); - } - } - } break; - } - if (state == STATE_LOOPING) { - atomic_store(&sc->loop_count, atomic_load(&sc->record_pos)); - } - } else { - /* audio channel handling */ - jack_nframes_t i; - switch (state) { - case STATE_RECORD: - if (in) { - float *f_out = (float *)out; - const float *f_in = (const float *)in; - for (i = 0; i < nframes; i++) { - int rp = atomic_load(&sc->record_pos); - if (rp < LOOP_BUF_SIZE) { - sc->loop.audio_buffer[rp] = f_in[i]; - atomic_store(&sc->record_pos, rp + 1); - } - f_out[i] = f_in[i]; - } - } else { - memset(out, 0, sizeof(jack_default_audio_sample_t) * nframes); - } - break; - - case STATE_LOOPING: { - int loop_cnt = atomic_load(&sc->loop_count); - if (loop_cnt > 0) { - float *outf = (float *)out; - int pp = atomic_load(&sc->playback_pos); - for (i = 0; i < nframes; i++) { - outf[i] = sc->loop.audio_buffer[pp]; - pp = (pp + 1) % loop_cnt; - } - atomic_store(&sc->playback_pos, pp); - } else { - memset(out, 0, sizeof(jack_default_audio_sample_t) * nframes); - } - break; - } - - case STATE_PAUSED: ->>>>>>> 3-integrate-carla - memset(out, 0, sizeof(jack_default_audio_sample_t) * nframes); - } - break; - - case STATE_LOOPING: - int lc = atomic_load(&channels[c].loop_count); - if (lc > 0) { - float *outf = (float *)out; - for (i = 0; i < nframes; i++) { - int pp = atomic_load(&channels[c].playback_pos); - outf[i] = channels[c].loop_buffer[pp]; - atomic_store(&channels[c].playback_pos, (pp + 1) % lc); - } - } 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; - } - - // push loop output into save ring if saving (atomic load) - RingBuf *r = (RingBuf *)atomic_load_explicit(&channels[c].save_ring, - memory_order_acquire); - if (r != NULL) { - if (state == STATE_LOOPING && atomic_load(&channels[c].loop_count) > 0) { - const float *outf = (const float *)out; - ring_write(r, outf, nframes); - } - } - - atomic_store(&channels[c].prev_state, state); - } - - /* MIDI clock events – affect channel 0 only */ - if (midi_clock_port) { - 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) { - /* store sample rate for writer thread */ - global_sample_rate = jack_get_sample_rate(client); - - /* create status FIFO (ignore if already exists) */ - mkfifo(STATUS_FIFO, 0666); - - /* open the status FIFO for reading+writing so writes work even without reader */ - status_fd = open(STATUS_FIFO, O_RDWR); - if (status_fd < 0) { - perror("open status FIFO"); - } - - queue_init(&cmd_queue); - queue_init(&cmd_queue_main_midi); - queue_init(&cmd_queue_main_fifo); - - /* start the FIFO reader thread */ - pipe_start_reader(); - - - /* channel 0 */ - channels[0].active = 1; - atomic_store(&channels[0].state, STATE_IDLE); - atomic_store(&channels[0].prev_state, -1); - channels[0].loop_count = 0; - atomic_store(&channels[0].record_pos, 0); - atomic_store(&channels[0].playback_pos, 0); - atomic_store_explicit(&channels[0].save_ring, NULL, memory_order_release); - - 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; -} - -/* ---------------------------------------------------------------- - * writer thread – consumes the save ring and writes WAV file - * ---------------------------------------------------------------- */ -static void *writer_thread(void *arg) { - struct channel_t *ch = (struct channel_t *)arg; - RingBuf *ring = (RingBuf *)ch->save_ring; - if (!ring) - return NULL; - - static const char *path = "save.wav"; - unsigned sr = (unsigned)global_sample_rate; - if (sr == 0) - sr = 48000; - - int lc = atomic_load(&ch->loop_count); - float *outbuf = malloc((size_t)lc * sizeof(float)); - if (!outbuf) { - ring_destroy(ring); - free(ring); - ch->save_ring = NULL; - return NULL; - } - size_t collected = 0; - size_t want = (size_t)lc; - while (collected < want) { - size_t got = ring_read(ring, outbuf + collected, want - collected); - collected += got; - if (got == 0) { - struct timespec req = {.tv_sec = 0, .tv_nsec = 10000000}; - nanosleep(&req, NULL); - } - } - wav_write(path, outbuf, (unsigned)lc, sr); - free(outbuf); - - ring_destroy(ring); - free(ring); - atomic_store_explicit(&ch->save_ring, NULL, memory_order_release); - return NULL; -} - -/* ---------------------------------------------------------------- - * main‑loop command processing - * ---------------------------------------------------------------- */ -void looper_process_commands(jack_client_t *client) { - /* process commands from the three queues FIRST */ - command_t cmd; - while (queue_pop(&cmd_queue, &cmd)) - exec_command(cmd, client); - while (queue_pop(&cmd_queue_main_midi, &cmd)) - exec_command(cmd, client); - while (queue_pop(&cmd_queue_main_fifo, &cmd)) - exec_command(cmd, client); - - /* Unregister any ports that were marked for deferred removal. - By now the real‑time thread has had at least one full cycle - to see the `active = 0` store. */ - if (pending_unregister_idx != -1) { - int idx = pending_unregister_idx; - if (channels[idx].audio_in) - jack_port_unregister(client, channels[idx].audio_in); - if (channels[idx].audio_out) - jack_port_unregister(client, channels[idx].audio_out); - pending_unregister_idx = -1; - } - - /* ---------- add channel ---------- */ - 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); - } - } - - /* ---------- remove channel ---------- */ - 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) { - /* Mark inactive now; ports will be unregistered next round */ - channel_remove(client, remove_idx); - pending_unregister_idx = remove_idx; - } - } - - /* ---------- load command ---------- */ - if (atomic_exchange(&cmd_load, 0)) { - float *buf = NULL; - unsigned frames = 0; - printf("LOAD: wav_read called\n"); - if (wav_read("loop.wav", &buf, &frames) == 0 && frames > 0) { - printf("LOAD: success, frames=%u\n", frames); - if (frames > LOOP_BUF_SIZE) - frames = LOOP_BUF_SIZE; - memcpy(channels[0].loop_buffer, buf, frames * sizeof(float)); - atomic_store(&channels[0].loop_count, (int)frames); - atomic_store(&channels[0].record_pos, 0); - atomic_store(&channels[0].playback_pos, 0); - atomic_store(&channels[0].state, STATE_LOOPING); - atomic_store(&channels[0].prev_state, -1); - free(buf); - } else { - fprintf(stderr, "Failed to load loop.wav\n"); - printf("LOAD: FAILED\n"); - } - } - - /* ---------- save command (writer thread) ---------- */ - if (atomic_exchange(&cmd_save, 0)) { - int lc = atomic_load(&channels[0].loop_count); - if (atomic_load(&channels[0].state) == STATE_LOOPING && lc > 0 && - channels[0].save_ring == NULL) { - RingBuf *ring = (RingBuf *)malloc(sizeof(RingBuf)); - if (ring) { - size_t sz = (size_t)lc * 2; - if (ring_init(ring, sz) == 0) { - atomic_store_explicit(&channels[0].save_ring, (_Atomic RingBuf *)ring, - memory_order_release); - pthread_t th; - pthread_create(&th, NULL, writer_thread, &channels[0]); - pthread_detach(th); - } else { - free(ring); - } - } - } - } - - /* write current state to status FIFO */ - looper_write_status(); -} diff --git a/engine/src/looper.o b/engine/src/looper.o index 413d19bbacf66e9e490d31ef512794e9e1b09e2a..ded87055932bd9bea4c171f1a8994cafbd46c477 100644 GIT binary patch literal 39568 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 iW~6gz8RQg~B+j^zpQ(|#%H$|u^J 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 literal 35424 zcmb`Q30zIjANNnY7H*MU+)9+Rp)4s&U2Z6(6rp`jrEbX*rBc^TiXyc5S<8|o5s4y_ zrJ_*Q_$j3*OC;MfbM8zXqx=8=pXc?=>*by^@AI9{eCIoJ=FBS`u!V{rYMjV&{=LO*Skpna36}hHBph?&}Fz{mE5QzNiIL84wT^v=#7Bl$CQAq zfc^xs(c^p4=?|-b0{oapVPUxWvoJsA4q#D*@Kb{4h;}x52zSJJCTM4)jc|wJ$qRSH zczv6vbGHhep^P8}hKIY=kamA-OdH5q@XKc8!#w{U zm;doH>=rJ+lqQWdj;RDzQHAi6+Mp$%vs~9oVc@x2w?Yvvznou+%&S4ZSu8P%j=fk^ zA*_JJ3y zy=}_EJy2B#Xg6TutZlFt!5$n^;mKk=rKVPJsFV`{FxB){xDR~)yY@tiJV6w|c~%J= zsU#hVjtq~E$d;fs$dq#Eoxhs$w^%PARwLj<|IwtYK)ohb@$cfq$;#){mC``~jswqO zQ24NHU94jBV9+=aO{!TD?7)^a%Lq8hd`?8-U$Tqu(oMJ&u!ztJP6M`{zEVc&?jdTi`I{ajV zo*MJ^fekTxanbY%I%68z7o_uy!kP7jgLhklYR`%eWX!S+QJ!VSfWjuTxUeG#7ytpzb~gQFo_7Ko^` zYw5rh7K9sI(3oCGu%h3AArMsv*B0P{F5CfgbTsUz`{2-D%74$6VCto3xl&FYj3VeF zIN#9-P8eiA|21{P**4&V=U^adSNiT8n!^#Fu1`DgBPxt$Iy}gUNv+b&LhBG zxWN)&h$W={?+Wk|4iw-9EAZ)MMI^ndpszmQnh#ePaQ}bs3xtH8OoS^#;kc)R>ILfP zDX8Q%C?*gX{uxsm#VRHW`xsoCufSyi-SZNxZ4bLL%wb>zx}d2590Fq$j%_eTe}K4v z#?k|q&&dM2@Brw-Q>FAu5*((QionPJ&`gDkK%Z)36)W;nT0nFn3*qNR5TlJi_YTwT{G{diCWu5+=qa8>$=r6eidlg?EP6|i^~ zSL`Mb7Y9YCD7t1bp^b6OaZnp;lP+jvlMIN2fCgPP2_tct_7Pu->UtWuf1fvS+=B}l zC$4+Yyashe6~a%7mGGTzH;!~7BfPi3k-x)BjsZ?85KrwEr`P^uR1-ni1S#+k*Y&<& zt1zTxT<9Xm^f6ek(V=48 zv$4q~DeT{T&;{ThybG)YyLDh&#El9sqIm9Tt_@lCw^WZ)Bwl8ti{OTl-De@_LAqrE zOSl9s5?;sXw*002=lls)x%6d#(Jx?$D_l85(JcgbMzEpa&S*{t=#8%R04w?)809G6 z!v?rSH?=0BZ_>XH7~aEGO7XTW*E%>gpCSDaTBq zbvTXS3eSq3D9p66&QWnOS3o(|IeKFPrzlqRX7DN8-6jnzO*%oIr)UDtDpqtd_@wVP zxLr4}gWxnKh=Ch$&cqY}K>_{Ak9i9AqYB|C#frHEiguS`1M^ba4AcS63RsMuso?=| z5li^Pn(DF!m}w0UfWUip3-#h*97Q7wvGes~8S2U|^GJGlJfi+#ZMG7JGd z?5;pe@6or+Q7sEVsrLD(6W&%vHWM^O7N!#4e~IukC!@q?8$tgb)^ z&*J~9EbVG3r-<%DPEjn=8*s{2mU3>w7J>U**teX@Sj8@RP7OE+0{j~v1Se1_r$lI8 zLMqh}N_9-7M&cm2#p|x7jf;+g`fqY+H^B1;U?4oB(Zdoj+Xx?%T!2mKilH!8B5hG@ z=_qiKZP%I3Q^36)Qxq}=?|1jSHmIn(=ksAGnvMeo+!coNCJw^?2TyxyRnMM|{@c@G ze|y?2^f3 z4HcpaB+|nfs#KB!wbd z*;Olj5v5N;F#7*n=S8*@K~MRGTret$E^Hpe6{JAU6pU=W+rP)M4@Ba^;DhMt4!iTp)g{VR(#M*E%s?Z4v^V9_? zaOW?Vg*S?LMn(bV#P#5QTu<(=1io@h=}5!)md4nCO5h%MYYN=uZl#ZL@gof|ekR*BF6GNkQPS|O1f0LI{+++=`zAQY5J4C zN+mL{V&It-96Hcn^aKx{%HWw*w+?vr)q@GRUFbFe&(NTWaG3-5=!(EIv~Ck{Hwq8H z#Ha{7iv#^Zw}g)t`)o8ral5Z*gMfG4SF~C*Z191u+u-?kRCpy= z+{ObY5Y~{&6$2A3oWpJ5JYLD|K6Sv=d}+4_b;6^<*x(SszNw=Z8NzPg+P|rzYlIPV z{-03=YX(@EFvg9qU;q9s;z1-_`2JP|4ug=<`(Q&^n9t)-)*Q3>*5=f_`STZ8Sk0yu z%&=X=F{f;;=333Sv!cwnCK_(+MP~G;%?ulkiAJy-EDMv}3~Q??Y*#O5e}8vBHn8(@ z4rW`~&YQ<}4sr7iUS@ic?A#dR4+Xwe|&)$EntZr+q@fIlxNz>jkE z3vl(N{t~jZFt?za0~!zA)m`lzO3iZi_H%b*^8(nwH8=1fxG&WELKT@oH>^PvBg)vG^+el6T}cW&s!T1&3@AQ$mj`eNS{zbz<^*OAM5u+%ul zoPB!Jpf=a6g1HVd<5f43qpPJ4KKbd2MO6b(Mdo@zH^Mf0efk zsCr?$sHHV~^RKh^UlNsm-kA8~>c~ci)&Wr#S8hkWd|NI3e)jW|;#VG2ew~wMFNpTb za^$};gShq_{Ie1gLxxhEFl8tP+HRfgm}Ytx&~8)Kd{zcQT+VH zY1bbG%nRxDKJ(p<=upP?w5jJO zOEn)FSdqR*TxY)hBaT<$Q}35~N@vE|JRVT+T+OBFxL2%`_MzZc*KW*dS|*tx{YlYn zLOxY&-n&8F>weu#$?+5HBb*YX<(0ZTon+@-nell4 z^j*@soU4a=s&(|;bSLA~aEAffn%AEg=0B}Hw8waY{X&EJh7N}IOF~n8E*~gPx%n+e z+HTjgBhybxJL)|^m2Z{F^>gN?Wa|Iytlp0n`@psKPzuinHDc>U6j_|7@@!!nIbRl?Jn>I%2C z1>dqPY(A8eaNp?D{o(oQ3z}vO`0U$is~V>?v0uji&*j@L)bqne<#_% z*DP-teJ??KdtGCaru*w@j~A~Qb7?7m%)U&EBCn@u_cZThSlFA)ic4QRkmaKOd7u8B zlWnrmOLxgH&n&XtB(-72EZG4&J0yG0Id$`-ftl=p?^2&q>eR06TP~{`T57)DLfofj z(97-PWp`a%;C-W)o{D-&xx4GA`1ISQz500_u-Tt)RliI(;;qe=SGyLhHQ+vZdAjfL z(z;1M4dWg5?TK-2URUWbVD$d1Z#xFf2$>WTIMq9)s6#@(TCU;L`Js!SZc@L! z^iI+t(?0*aRb5adbI{y=_Q>61PUh8Rg%91__F>>z@#cWy&G&!p_bSP&9r2||)->97 zh}y=cvMK#^_C;)4JXE=EgImg}MFmr($6vhB@NGur+U+~)7JlPpJ!zObyZ^Xb=T4mo zwfSbRDRwQK?LSdFsgrFZJ$|a!r}YLa)<^7IoZfqz>{@HD%a`6-?(MrzU9(wkP6d)#tTk^wPe|vC!MLXr{?LuPfJNuS*Rv__wwx_4o|e_M^St%-p7T<3m4>OJ?zN zd}}S&ZqmrXV{93ro z$Iv&OLrZp?QT`I1)!NxBH)EgC;UM?bF9Ku37T+j#%@5Gji+?!QN6&6)n1)K?!nVHe zjgvf&FNtz(GssNw_nDY`a$wD)*7ZxPvcorrt2jFz`4~U@+mxVDj)m2m`?}R@exI^u z+v)prd<;_U+-GY4P}SRX?`&?mb3#Mz)PEP=Q^?fX(|&eN;oG!xFMs_!$NNV&V!)YS zWBUn`3==0F)T%bUoAbi;k%N4EZOFX`OR9fuT~fOHfrHaj(_@$8?OVtDu8aK8as1fs zb~}%EWA%-W*Vqs6Szi=yB6VZ4(z2Krxd(R4Yu3{ro+mdt{Fu`)3Wmg zuUOtMU#8~k?76w~Vu{qGvjH~SpZIyKzp`gc%Vn+kFQ4QZKk6m@>g)KcPk6TmSXH{6mW1Hwtk`n7OnzMI| zx}{xj)2JEokvg>XvDCcD)-!B%=j%^jV*XBN-Wr|qUDBJx2&TQ8>;uPUrbF<%p<8MS7U*exrc^V(agau+J3+%qbdI-cWl%uH)#yH^I=!O-EA zn$M~qISfQYHQ4g4+O$G5YIlDzCY=Vf1jvy}hKjf6&`F&h(JtxdGv( z6{ZvYmDOJdXH60euD*P2_-yO-t*V3C!uT)#y`pltO>sigS4&@`yt(6TPE1|0?(xQ} zns@R{69oYVT3ScqFRE$|v!7_X;ETSN#;U3UWX*Ji{VAN%3a=i{a;J}W5vYun^n zZ864e^cGv6la9{&k5^y!WP7g8xnMbO`;v~Oi;TAZ3cYOeOmo!jAt%Bo#eQY2`*}EI z|3Z%^m8F-5W%Lekd)YDY?wPclU)TG;7QA#_67*th)!FDZH};&JW|y`wcbZI<+u#!} z8K<3M+7rvXN)XwTCa=1smSiN9Xo0o&ZY{wDOpAs$}I&pAO{fO&B zTbs{@rT1&p$lrYQ+VW0~{GD~#>+2fx&u-STy|hc#=a%e?{=4;ZPgXZ>Ilbp_*pv~P z8Pi;Hk1gUIQ*}GOXTrzR@p9>X-i6PcWnw>7YJR9?TF&_BO_87WKDm+Xu%EYERXN@# zXWG%>){dM;g@qrEv`e(SGWz6hrax%F_psfQ&(Hp59JB9bo$>1R;!mekPkf)AysN@% zU(QYizXOpKf=oq07U$KojNvbqw#Af>$kML8^|Qet!AYOD!S$ShXY4nZk&{f{3~rb@ zNX7Q(h^g1c>C5yVo{%s_cKDYQ5|^#l|5h%&bEJG=h)+Y?q32F!1K+Q`@kRZhl-`46 z$rloL+S2d8xyY9*D;{>Wfp@Ihu%wnj30V*GcHpzeXN?e?h=YCh8?5N9GV{|tnU3S2^ zw4B^0&kK&-DV^^5F8k9w+nt){r+hfFBSN>{q(CCGI-~#T@^{iVmQB4QCs7z<=g#`& z`NBGR+`!Grre2kI3~LR83dC9Uro#*hL)u@gda0N5@vXy{(fOA38~d++BJq)u$Y(3; zvRQ03r+W5@J|QY63s=w2*wcBnVqUo3o5L$2BeMg1vd%@`OC6Ivq*uU2uc|dZ-*$8e zUS(~pvH&^rRtSA)hia< z_RZXQX-L?==e3NlsBjy0oVp?VdzWVGVt1+c8f$Z>{Lr6OxTWadwMx^n=KU@kedf4l z>doN70@ZKUt?Rf}c_}VGhHjFvHyR6i3qCHPs9y9Z{un44&7&{GSgmlw4XzCHbrBa!TZ*zj^kaS9YEAxk~WBsq8_fe%P2OhfDL5YwX1b zOs$ZVX#?zdU}FA}`$_pvdD>pW{deD|$FI3eR9Sr1xk6KH+hS*rT3c(k7p}n{V`_%n8KNeo(#w0*(s8Z6Uw0`!omtoS z`0`X2rDEHXw|z&LM$Ol8o%L~qv%NraICuT#wuUNOBmass3tL|<-jH#!|C%hW(%@T8 zD~=0>-09chJ7}y~oLOo?FMddyilkN5NM*YjA7!1-CGEQ1ca}}fdFP$Asat=H8CB!g zchlV|alc>IR|UEE``Z4{;mF%NeZ~&&uW@;y%XzniWIx0BMp z8Z~pTjxLdmWqK-qxtIdm3!M z_;ncTLT63AWKN@Z!@~>hCM6oFI;V5=zMmZJZKzb6aC z=Jt;!%ihWlZbteIc)3Agi}m5i^Ywzm^ZP|^8IoBLc0_gWF^k-!S^K}*tzFUKSu!o) zXu^bctJOR6jy=l@6okzfQoA`g&bZ`5Z?{af!03^^)1|M5oDcX|{eA2%wZUF7 zvEO6*>9-h1x_v5RUV4b07Q$;GmzszEV|(Q6-O))a<+CMpUa!?w*HkYU zcKubTR;%6lCr472{a902d)z;|TE@N5>%{4g`!20wzwBi?`sJa}8Piyf4qj$c%M^Q? zA8(wlp?J}7T;`O-B_qG<6s6 zL*JN8nmTrp`l7{yOB{Q>>mzf{N^oRT?zMn3UzNTXxyRL)@)~nx^(Jjj4_j!TxhG`C z;CoA22Kl}mHAKgM`W@-dN(UgSZrH*n zvMjy+=aY3ZHrb9CesYA}$M>D3*L<|xTva+E6t{iUSi0;{W&Et?#e1`ZZmI4(qtrC@ zRrOt8u`+R|qx`w1C(CvDN;*}M$G+$2&*c3xq{Cjic67w2mm3uB zpqSt)2t2-gPi5<{xZxEAsa2tBx)UtLJC{BGHa$(RqBFkYNqdmVG`nxtT=JHDV6h&} zKejskefHtBgjc`wa_eICs$NZLZ~y4g{!m_agGGAK>BRDT3(t$MnBlr^r66NwAIIEb z2fccqe}1%M-Yw_WhuQaUOx(UfPxj2J89`T;`nB%b-&E;vLFrwv>)s+YcQgK1Y3F`t zD!fw0NpYRGy*=|?(JU%I{?--~ok4X^W=^QQ`}U1%x}FBLYmS`8^;`S;Ke}B%Xnd9a zs)mrx>u)3v#jSd~bJ>l!ITLvQ7^{DKA2?^R_QbX86ih7EX!tZ%=l2TF*`6l&1-X~8aFAq_?o0g56YfsG}zbbvi(^#R(7gYli#)jo|_X!{a*AWYmaR?3q~dW`k0&iT zt}e4^`SMq#(q&`$_ea+b@=5E=>73tsQDcs|VIThyH61syt+Q3EjWUz+zNjqxX((w| zG|0=Q^~~5QS$7-C9MdjbpEL2vyJu@ZsqEPCy6+72AqRDA-?8pZT=mOg-*%gN18s#I z)%{C{*;WpcXgpV~v3wr?4NLO0-OVIV6Tk2CEQi@U+sHn;vU^(}({`O7^L2+UJlxW% zHv82Nk9CD(wHAy|G0xd-V!d=!dWnhuuPwK>Zx73i2(q%NAD|???R>uLl7Y#0#;50Z ztc$!j_~dXa@sg7TRm!Y@_v%Jdme0O3XwBLVC`fD@#>B}N6u<{ zB)`59x6LGKVUym)J+manElg8?F>#uQ;M?_+R;dXY?Ne_C>6!Ys z-U_eiJLI(S(=pOVGhdF}D4*Y%xcH3|TRU8F*w94{^|?+@dT$eWvOBJBpX{cfzBI)^ zPF{JvmGW<~6G^S!UF)?bG$D4+i{T62`CnrzypHLs?7iiS_}&TLtqYV&EuKa?ujjpc zQZdr|Sm^ySbKD>HcrlnB~^hB~F56tmLX2 z_Cr1^?5jBW^Pi30;uCh}r|sP>c;9)>x8PXg$*A&O5lu<+ca47KRo5K3@O(;uQ@i)T zl#krBy=pQ6@8YWDTq4)se624f={$3V+MW+%*5~Y>P-}ejKvLN7TN5nq?{_k|<0UnD zh*n2x>zCFMnF06T9eV%j*i~cCFy+t7Pt}mb3jT|9ipFNNTp3*%Y?sY%5zWwt3dlQ=Jqwe#tt6jk_#Z~3%uY;ddw9X|pC(N%uG6fFOr5|opqKK$;f(9P$c7*H6PKNAdv7{i`vAwl=rpZ2hSKp%ENx-URZ6a0fZ3g`&dfpXEKXl5_^DEF9C>V2yP{UFA~9*is0@d_-YY+iwK@1f@g@}2So4_BKUa`yclrU zFY)wyauh{hZ$Lb2McDrBaL#6QU0(>s_4UN-S|3!^;!0$iN^9D|)#2Ku=>27wnwDEBaTS4Y=? zl`Eb7-6&ck$dTs-UM6v)0-bqYy84vsN;k(>f^>uXh@^$n8FOgsqP;-bsuRG@)=rAV|N~;t1cwK?J_wBFy81zV2%ron3j}ppc8V zzZ@JlunX6@r3pAiVCO z?*2Rw1D>0EP!Q#TBC*naWw1N&%zdS6;2H`<$5YsILOr?(P>(K*qC?k29}!Y;TxgBo zDspsnp*-lm1KsJ(!z04mAMDeGp(3->SNhWZ1YbO&z>7#h0YMbq!;W;3K<_|$Ac3aA zaR8dt-BvuDsFffN0j|OZxVZ;|SBch89zpKzLPC!pe`o)IU_W>FKnlD&gj$K-eCmp( zaNq@_>H`A;8Fh6JURr?Gqyr}OQaDN-g)cr)fhgAtOmjHoM1?B=o3r}2tGoH zsnv+Xbpu@LV1H5=_HZ?e`C*3rUw|FpGLp^`nR8}5r@S_fqz}`5n=z5VLzH-|CV77n~3}O3xjJg>{ZbE2DX>U zXEHdOVeiS{stkS*ap*s@zp5GbY7F~25xk9|qs6cv1pfRPCa6CK{KN4vN1W`hjRaQ) ze|Z2?2Em6Rp2Ohq&pWVw0fR%=ra}>rZX*u;f!{P?`v!(R9G|$}PKG_RT?0`EVgIo| z+K5Aaru|~V9@i@n!COS|v9drIBwR1naTCFhh~P~k_%w7&M(Rh3;G^Vw_D4W)7!R1l z(ft+7>wtgQ|B;B3^~Q+c&k2t8CDE-TspE?{S?@Lx{3?U%GW={{a2*DhM7O47z4Jxz z1jL~~dJLU|BJ5u?xH`lBvj{H90!AP~eJ$`0$61rX^%;B#gBvjTImF3!wTR$D&;^LJ z=ZN5|Mes+6L;socQVYYLdA@&QaCp84fPXMaq5B!AGamfIaR7f8Oee@EFt`ze!+R-g zKNWGB-VFOm4Eq3vJ-jYpoe&W`g2ACbN#Gw$ zafn0TCxd@jXB)#FK2ySc7sGxEgJ&@8;jIm}KP=*c=2aW@q!J!U(o(0org3F+FrzzrOJUI+|HBcVw&tq_= z{vv|I-|T_OM}+<=hCOqB4r6d;z0nNLv`=JkW;}N>IP??W4`d@w#EM~xD}XEHc*9&i@HS0fJVhCTEj)&o-&gAWIJ90y(K zG)OQ$a2Iob#9{eS3?9O;hxew~zJ|e>I;!wy6(p!L8vMidI*7x1nQ@-L;B1C{0)xZz zYaBY?cQf>7G3;M6?4dt64y_D(U55P^(jH%T;LS5gI6nBgV@hy*-Ek&3Zf`Wfg{l;F z5^?AsbNuHs?3we-6$XcKFh+5>$IuxMIIg#bVXw;IEO=6ZgxiI$v*QSkb=(P#ucu*% zL;socQZ|Fn2FI|@SrIx<2#)Q)Fm$*K9Vzs-A*|Pe!TT{dGoCsO4kfWZ*rgK;BaEj# ziYGqr;cXRKO4D}e_JV>OUMdUFeHt46G>20=eh!7I?c%fu_<-jQOt3oG2RP}W9jK3% zNHj5Ga9FPny6>fbH3Q_aKP%8cHz(}x!jlZ7xx!7C{QzWdNALm^hZO{0it5GdcU-Rt z(hn!>olxU85qvugHb@D=O_x7!P`!Hy{s7f`nBePBN1Y+KCF0i!o{aptNASL=A72w( z6Y0Dscs0`LBzO$U!{3R43C9Or?x}tRKY`-FCiqz?VR`s7H85eFHer^6-_yZ_`3Iyk zm*DVwHJI?Y#`gI_Ot}*F@rZ9EIM&%t@DnJ1gy5%;4*Z@DCR}f_FiTw_cmgy4={CWg zP`|+M<6y!%(Wt#o3C>01u$|yaX#BwMNT(mcpCS&wcVpUrM|>>7uONN+eH_#N zH9Eg05quVEmnp%mP`{fK9DZL5(;R}+f4L4g{QeFm9G|-=??!N4r0+xUXUP9Rf>$8> zP=dP)tE1Kvyb#4_6T$IwWC6kDQN4)-4?;R=1pkEMc9h`5(0K~K?}G`)p%Uqr5d0)+ z*FA!xVMu)-I0xzXLE{VSd!W2B!P`(Cey_*$#~As;CU_g_-_Zmgiugo=FGT*C6Z|UT z3keQ?5*Vfx1ivN36#N-qm~gx1BR|&@yavTVK=5c3&piZxBb24Uk3!H1>kmNo`2^2H z^|lk-4z)`RwFB$;!5xr#6Z|X64<`6ONLPd4<51p!;Dsn}LU2#iFBSxMM(uJScs+`n zC&6uzKRkllq4AHuAHnTig6t9qdl#ht55cFQxSb%l71F;*@U6(cl;C(hQ%!JXWdD-j z+mJu+3BCvQ*LQ+XLHS-Nez?8GDBqvpkA>Y%sSzB$Hvp3!!DEpARDydWdkcaeME)-( z_*oP;SAyg3F9Hev6OEH_g0oRRp5T|zc-T$wXUNaP1jqAWF2Q5ac~nGjVRR_!F2Ql! z9}~O@)%%9vn^3zx6FeB@#Za7ZKh8z{-iP4Z(0o3W;L0ekNpMZ1Z%A+*q=Ua-!}S)T zdgl}NBa!`bg7cBRH^I$Neigw_qy9=E_&F4}OoF?ie#s?xILhB9cnlhc_Xs`-`T2<8 zBT)Vs!Oc+I{w4ShbY3(OTni19R)VLYxV00!AMz8gk8u3)bxsVm8}n7DUKxUaLVorp zcqFQ~AHi8D{zC}97WuDB@CZ~dz7Au5P9QrjVQ+)%7ZdzGYQG=B@jSeS;CLN^&r4h{ zUWe=_?DwHO{{9r(D`|A{_Ht-kKtJ(4GCVMp!SOvZ`)P_klh_*@GoM+V3D z+VEH)gR`+Q*hVtADuZu99LL`q<##gd)fo1fguM|Ox5pUv>J0m{g#8i}|00I{NQQkG zVK0N?|A1ky!LWZ$*!M>IZyENmztGpU)F;ATAL;*Q*ux=+-&5{|+K1z_7U_>5co53l z6I>IG=P-i%pm|_F!Kb3@&NYHRMmmiImqPP!U*s3|6Mr9p=TXd0qw@BIy*P@455W(h zSg#{^GCCi&6MPTyA77W?VfZ{J5b-j?{tD86OmH!jZz1?46t`aT!h-NH)R#i>8A;@?DY{9chX8mPEkW+*Vu%Cd=-#WrR7uib)zXtAlj%t9O;}1fdjE^S4v7eI&j{USC^u3V2KVknC z%?}$0`%lPz2VozC`sDz@;rrh(oh5Vzq38Hzggw@&B{E{Wc6!Ew8Z=0jzIpGNb82I4rLdPsi?VUKm@5FG0`5IR9f$B(e@1swxv z9bqp(_In6^8u5!F_-#TT_t#^B~ViRAUN)?dP3(A(vgK1 zX^?On>Jir_xFuS=PemNJ3&(#h!EyYZ369ruD+zu4_a)JUJ-$xi_hGP~<)}UQ{T9-n zd_o8NQ%2O=jC7t8_PAa6eHE;ab$$^#gHXO7`oaaU*0og;(B9gYKj zjxY>y9M8>Yy)l-sABNYB1lLAe_UqIO7^XnGD@%dFt=$NDV_B~;bb$${Y z>nNb}498&&(osbm#{ui;6CCSIBXp{ejtyat$Ac@u@puR#bo!xrES9jxIy(uDbq*0a z)=1|fVUOEYMsVD&8bT)l>AWNCvCa>IV;y<4p2cylL^`@6xEbPP{H+O&_3?W+Sbr2O z2vQJXkK-0ea2&TJ#IcSS(m71nW1T#LW1SL02jADdA$TLw|3T`@T-Il_U9hK zu|IW$PBYT~K-lAcq5pUW^goUp?w7u3osQepj20225y$q}pNRy={>&zHjLzI202c$DxYQ`G9or`&2j%uhI45GhwfS*2hw4 zJ&x@c(`Ow;jYJ&Bc^tAgW7xx`q8hTdC%6vc0fbI2(!uXp;d*hLlNmbjl8obgh|p<5 zI_C&`9Jd<;$8p2&S>bjKMC;vmggw^zNpP&w2d%rI|4?EQYOgBdSbrwcH)7bscH#EU zA~s%r@*15yr@KiyU7pk7%=yF57XK;8v;^$XC z5yx?kMf2wf^koL-`1`L3h?DU*BRKYR5uq~#U4Oj@d)(gD1jjnDgw8ailSbHMonr*Y zIt7G|8`8N$*khe11jjlp1Yd*ZhYy54oMT`bh`t2E@zfMz%81|-5uc4Xj`Iw-1JYuG z-$6V`gib7BkLQ7%1jqBhVM1r4uv)5su*W*L2#$3g6FOU9!$Csfrq?;>5>9h8-FH1_ zpdt9c*IU?3cx$MI4va!u%sD7OhTs+`uS;+TlpjZM0m>T@`~b>NA^2I8HzoKDl;;w> z3gs;c{uJe{30{x#b_9Qi^2-SR1Ld6wj{iQ~gWv;EzxWb-6ykvd*F`*p;PAgug=wvD zL-#N2ubGJBafJJ8KH|}Y{Sw6a1c(3A7)%MmO_zQU;yVbw2JuvaMSv0lLgpD|4Og^39czxwWaQvJng5dahQ5wPVbH7Uj$IoM*5*&Y@ zMd=2wS;=#D0b5>>aO;Klg53R_b$Q^2Jt$o_XPz^q>k=GH=>`P|GxSlos|)sY!B0y# z`+5L4N`I$6Xiv?wSt$t0gG!=^vsCezyYrUIzGq5}~fUm!n6J z^GbIVjsN!z8x)0oE=@>GM|twsl+keMvWCk!xTKlhMT9$HGlW}xxcC4Gu1jE9^w-m@PGF9fPdH2eZ9dc_%g3Se$+2&{Y~c?3j~qoAfIO_9Ikuq* zF7FHvG7|P5BX}L?$^W)tLJQKK%ufbpJ;g6;k+3|vt)=4!`v{lE9GcNC;se5BR0pbm zG+rnoPMA?vfcI2?5vsokU0`AV!LqnKuHO-`p6X|#3pbwEfEk@|d7KXdJ4A@-Lyu9t z`q&U{N!yix_vC-X24MkQ9{LX-LE-wb|B--^ny{ptHvFV0GvTJ|leFClj?fGXBg+M- zdR4~!+o+m zWhgAr8yku!Ux>@2<9O2>EvV2i#JDTgSoQ`g-(`SywD5wAIDpGx{1EKGN0H?DtB=a# z{>S#X{W$*~>_APjyb~(lfeq1?EZ+opPyIK2ldwP*DhK-yK6=LGasTyHH!Na55q^UI vNghqi&>nt8K$e#UjGX%bqPTEk0tvq-Bh39h*5TJ(J(X`n{*&z| #include #include -#include #include +#include int main(int argc, char *argv[]) { (void)argc; @@ -43,7 +43,10 @@ int main(int argc, char *argv[]) { while (1) { looper_process_commands(client); - { struct timespec ts = { .tv_sec = 0, .tv_nsec = 50000000 }; nanosleep(&ts, NULL); } /* check commands every 50 ms */ + { + struct timespec ts = {.tv_sec = 0, .tv_nsec = 50000000}; + nanosleep(&ts, NULL); + } /* check commands every 50 ms */ } jack_client_close(client); diff --git a/engine/src/main.o b/engine/src/main.o index 3cee390fd30cd789163f955c58849982e32bfd29..c3ba94bfa145ad94d796cb442a1d1ee0df009de4 100644 GIT binary patch delta 310 zcmdnteZYHy1`~tAMy;Prj8itNFkfY2ep7#H^B>k2#`@n35WonfE9R8;ALKmbAmEx_ z{@F~rqfoF#|*8Uxd9PBgQG;P{GZPKpG0?tr!S!7RQzpH5zw_tJKc_NIFFLqOd5z>Q zF^h2F@Fnk@wjI3IqkZP=?TyxnuI^dvQ_C;getZ^g$orz`&>M$4P6qQeChL5?^8XuC zk(Oiy*rl7!Pqf5QpKKe$K;tnl4J5sAo*dkq^dn*!sJL*drk#;NI+bk{7^w2 E0O?YADF6Tf diff --git a/engine/src/midi.c b/engine/src/midi.c index cb5819b..a5ee78e 100644 --- a/engine/src/midi.c +++ b/engine/src/midi.c @@ -57,16 +57,20 @@ void midi_handle_events(void *port_buffer, jack_nframes_t nframes) { int cur = atomic_load(&channels[bch].scenes[sc_idx].state); switch (cur) { case STATE_IDLE: - atomic_store(&channels[bch].scenes[sc_idx].state, STATE_RECORD); + atomic_store(&channels[bch].scenes[sc_idx].state, + STATE_RECORD); break; case STATE_RECORD: - atomic_store(&channels[bch].scenes[sc_idx].state, STATE_LOOPING); + atomic_store(&channels[bch].scenes[sc_idx].state, + STATE_LOOPING); break; case STATE_LOOPING: - atomic_store(&channels[bch].scenes[sc_idx].state, STATE_PAUSED); + atomic_store(&channels[bch].scenes[sc_idx].state, + STATE_PAUSED); break; case STATE_PAUSED: - atomic_store(&channels[bch].scenes[sc_idx].state, STATE_LOOPING); + atomic_store(&channels[bch].scenes[sc_idx].state, + STATE_LOOPING); break; } } diff --git a/engine/src/midi.c~ b/engine/src/midi.c~ deleted file mode 100644 index 75e83b7..0000000 --- a/engine/src/midi.c~ +++ /dev/null @@ -1,154 +0,0 @@ -// 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; -<<<<<<< HEAD - 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; -======= - case 63: { - command_t cmd = {.type = CMD_UNBIND, .channel = -1, .data = 0}; - queue_push(&cmd_queue, cmd); - } break; - case 65: { - command_t cmd = {.type = CMD_STOP, .channel = -1, .data = 0}; - queue_push(&cmd_queue, cmd); - } break; - case 66: { - command_t cmd = { - .type = CMD_ADD_MIDI_CHANNEL, .channel = -1, .data = 0}; - queue_push(&cmd_queue_main_midi, cmd); - } break; - case 67: { - command_t cmd = { - .type = CMD_NEXT_SCENE, .channel = -1, .data = 0}; - queue_push(&cmd_queue_main_midi, cmd); - } break; - case 68: { - command_t cmd = { - .type = CMD_PREV_SCENE, .channel = -1, .data = 0}; - queue_push(&cmd_queue_main_midi, cmd); - } break; - case 69: { - command_t cmd = {.type = CMD_ADD_SCENE, .channel = -1, .data = 0}; - queue_push(&cmd_queue_main_midi, cmd); - } break; - case 70: { - command_t cmd = { - .type = CMD_REMOVE_SCENE, .channel = -1, .data = 0}; - queue_push(&cmd_queue_main_midi, cmd); - } break; ->>>>>>> 3-integrate-carla - 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/engine/src/midi.o b/engine/src/midi.o index d30a2d17a1d49828d3cb882dfeefda22c4c1b66f..f824688b82c46af603b9836871d6a6a84ca83596 100644 GIT binary patch literal 13712 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 literal 9376 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 -#include -#include -#include -#include -#include -#include -#include -#include - -#define FIFO_PATH "/tmp/looper_cmd" -#define LINE_MAX 256 - -/* forward‑declare the global queues (defined in looper.c) */ -extern spsc_queue_t cmd_queue; -extern spsc_queue_t cmd_queue_main_fifo; - -static void *pipe_thread_func(void *arg) { - (void)arg; - char line[LINE_MAX]; - - while (1) { - FILE *fifo = fopen(FIFO_PATH, "r"); - if (!fifo) { - perror("fopen fifo"); - return NULL; - } - - while (fgets(line, sizeof(line), fifo)) { - /* strip newline */ - size_t len = strlen(line); - if (len > 0 && line[len - 1] == '\n') - line[len - 1] = '\0'; - - if (strcmp(line, "add") == 0) { - command_t cmd = {.type = CMD_ADD_CHANNEL, .channel = -1, .data = 0}; - queue_push(&cmd_queue, cmd); - } else if (strcmp(line, "add_midi") == 0) { -<<<<<<< HEAD - command_t cmd = {.type = CMD_ADD_MIDI_CHANNEL, .channel = -1, .data = 0}; - queue_push(&cmd_queue, cmd); -======= - command_t cmd = { - .type = CMD_ADD_MIDI_CHANNEL, .channel = -1, .data = 0}; - queue_push(&cmd_queue_main_fifo, cmd); ->>>>>>> 3-integrate-carla - } else if (strcmp(line, "remove") == 0) { - command_t cmd = {.type = CMD_REMOVE_CHANNEL, .channel = -1, .data = 0}; - queue_push(&cmd_queue, cmd); - } else if (strncmp(line, "record ", 7) == 0) { - int ch = atoi(line + 7); - command_t cmd = {.type = CMD_CYCLE, .channel = ch, .data = 0}; - queue_push(&cmd_queue, cmd); - } else if (strcmp(line, "stop") == 0) { - command_t cmd = {.type = CMD_STOP, .channel = -1, .data = 0}; - queue_push(&cmd_queue, cmd); - } else if (strncmp(line, "bind ", 5) == 0) { - int ch = atoi(line + 5); - command_t cmd = {.type = CMD_BIND_CHANNEL, .channel = -1, .data = ch}; - queue_push(&cmd_queue, cmd); - } else if (strcmp(line, "unbind") == 0) { - command_t cmd = {.type = CMD_UNBIND, .channel = -1, .data = 0}; - queue_push(&cmd_queue, cmd); - } else if (strcmp(line, "scene_add") == 0) { - command_t cmd = {.type = CMD_ADD_SCENE, .channel = -1, .data = 0}; - queue_push(&cmd_queue, cmd); - } else if (strcmp(line, "scene_remove") == 0) { - command_t cmd = {.type = CMD_REMOVE_SCENE, .channel = -1, .data = 0}; - queue_push(&cmd_queue, cmd); - } else if (strcmp(line, "scene_next") == 0) { - command_t cmd = {.type = CMD_NEXT_SCENE, .channel = -1, .data = 0}; - queue_push(&cmd_queue, cmd); - } else if (strcmp(line, "scene_prev") == 0) { - command_t cmd = {.type = CMD_PREV_SCENE, .channel = -1, .data = 0}; - queue_push(&cmd_queue, cmd); - } else if (strcmp(line, "load") == 0) { - command_t cmd = {.type = CMD_LOAD, .channel = -1, .data = 0}; - queue_push(&cmd_queue, cmd); - } else if (strcmp(line, "save") == 0) { - command_t cmd = {.type = CMD_SAVE, .channel = -1, .data = 0}; - queue_push(&cmd_queue, cmd); - } - /* ignore unknown lines */ - } - /* EOF – all writers closed, reopen for next connection */ - fclose(fifo); - { - struct timespec ts = {.tv_sec = 0, .tv_nsec = 50000000}; - nanosleep(&ts, NULL); - } /* small pause before retrying */ - } - return NULL; /* unreachable */ -} - -int pipe_start_reader(void) { - /* create FIFO if it doesn't exist */ - if (mkfifo(FIFO_PATH, 0666) != 0 && errno != EEXIST) { - perror("mkfifo"); - return -1; - } - pthread_t tid; - if (pthread_create(&tid, NULL, pipe_thread_func, NULL) != 0) { - perror("pthread_create"); - return -1; - } - pthread_detach(tid); /* we don't need to join */ - return 0; -} diff --git a/engine/src/pipe.o b/engine/src/pipe.o index 74d014adee3e7521ccaf9b895058bcddab6acbb5..70b4a740886b7c90c51990bc5561bc400d563b9e 100644 GIT binary patch literal 13152 zcmbuF30#fK|Hq#eOO(nMLUCr^rF9*{NM zV6{;#(7{)KRT6(NVZ`G(@lg^`FQr%z*$rw9dIj|(@I?s)wW1i2!_F9^ih|5bqiR7( zSrAES)Dv(7tu(40)&YzYzZIU2X8|Wcfn~9GV$X46ud~f;I1h^cKn2}@x&r=>RRH_{ z=UO1mRv>9w0^3C=d%&j0Uc-G4`iaYtoZ>_j^}skbsvPu3{P+ zQ{uhn&w2+l9Xnr3hWQ!zl)$5a6IG-b<@*85#qp9k9Z$($hdxI#-osixNP~$2PMK#w zAe|l>Znt-Iu=C}m684c2(WLavg9Hvq=1c)maa56bBwH$;iD*+652PO-aIS9TfL>Vu z+;b8vTK-~Pz%Lv-aPj?O%A9zM7OeXAK_u>m!WA(S#lHR%ef<3V1tKor*Nt%pM?!UX zFLytN=jH}KTwgCYFGeKr^;<3g3ExlTrp^S4{rnkMFCjP#62g=TPwP7)G%uZW3v@F) zYE&KO)4STBdR|-7LQ%H;fjlRLVHr;HcjoP0xA(~gPKKe0sooa35WB=zH~4Qd1GThn z)rCZF5L7<8@A1j<*~bv(%*)oz?`q|@gw85^zM(nx-u@Tsk1C}d7TfC0u8o(iSQw?6 z#EqP2rktzkI4|_q-^;a}EmP7AQ=BVn77ycmR-CcTYT~QvIou%~`**ZoNpERxjZnK; zv0{l*QWf9dY2{8OYf<~U+*560bblNF*|V+V&byYEY-iBZ01|}3e9=Ntwf#RH+1&2$P z%sn)H{|%#%=yX}R5_OXvCEDwR(IeJ<96WqPa2Ri@Ndj+stzES8I=g5?S=&iXqklbr z_|TfA@1|YSS!}S!C8cimix0=H%6^oao-*v3;ZxgjbsEt}K0Uv@GQBSKPNwLAdvJmO zT!m)64V7u#qGh6{b!*nOGT*i(@4WwskU4^DdR7Y`*c~p{v8YM+2o85nikGk4TRA>3 zl^4DE^tqP4#mD9IUVps&*JYjOXER>;Y;DfFaGjDxxP0Uua$itG`D=Q+o$TGoSk0fe|}*9d$&{ zkD6fVVb>-%`8ww%ssW0kJCSpD=Z2oSnf0z=$(*KgwRGi5W$p7qtGLw~^Wz%Y-c8yy zsbAcS>)9(_v?=eck2>d-c1R_0{725Tgm)j-7{>)YyRo_QguQLY0q3BVh1sDs>AyJ} zj9KOCbKPs{RsGfPs`ie&_V)gUdhe3(X$iMap0L(;HUDgFZn?p;wohiss!!X^^Bxs@ zCg?xB<#oAWOweyuHN&i5Jh&C)ee#vy;HkXXHAfFM_L;1#F!+XlzZ+YW6ZDnSr(~Ay zjL{x+qkfqAriOsqG4DMxlav)26J`0S>hs^KUic&zaJY~+IJ~~(ddio;fgRE+F|AWO z=ZBeaHS@3eBFKJ;{W)W$rV(;~Zj_50kkm8ofWfD8YB~M)6wTD^(bC_!*sMMwy*ez` zp?#X6;iQ_=b9L9bl}s#rcGGB5Mcll_>W(9HR4o@|ZI$7vG;MgF$>VqToXMG%a-+}u zIiLH;x$kp%5U`o?unnziPkJAv7JsL3+?-QdTguMrOs(rZ)@;+!Nn^(6j=XgLP{ZPB z1zKUdR9=~;1{x*~m&)F&&U=3h@~7TGPrL7}Q90Rgw7cTM9$S@Nw%?p^ zz6KiJI39=5;kGau)GS*x>cIT#V1}~d9*o9>fnRIw~0!bJ9JVEF4P7sjDO!| z{l>!eixNM1x_|9*;~mNNHGTi6lUlJh;$Umx%!EY=h0k`MndWqJZSJBy55pQgpIJ3O zTAOiQy0%AvtX}v8&~K9NgAd!UCJmqL-!Ub?x-k;1>>Qed6>)`gybvTx!Yg1yZ zWZ|)*^=`jpS)Qf2sp%}~aB~xj!FGF0;xwn9daN|=ZM2MfpUwTA$X3`%snp7DG~byr zW=8ikMtOkG>yA%6a1rbof5m)=CEk#Mr+b z<9KS;{zbjcYwT!^EPGX2<>-E0vufKJ%bE!(n%A$KZTf6k@G;_;ersxZ;gYfZqSvn# zR?V0F((9hV<}+vS_PBi5aJ-ULT~51EAF)e)lvDKS%bUW6YIO|RrslN5I48`qXERT9 zs$u4hp~VI>rl5Yr&$Fv=g9Z%egVO{KSB3X<1%l|i;M?F*9opQ<`yZud~_7uo3A!o$v&+zMr0&Je2?=0@F}?z-;j=Q#!` z|LnYLZ=iK0NA}2rn6RS*oApNK&+yG(pRc04;6c1-leCk9Y5tj6iRwGfetKkcg?nYj zGG$e@zLV#jI4SA1q8l0U!)bKYco!TG0yU==Sh-~sau|`0C3IXQ(g-b2rY9ctvuq%P&%FKQF!IZ9=XZ;jZt=+&^HMv7K)+X6;5UMf2g zm%ZPp=TqN@)9cH$)oct?jazQMJh^zyqTt{OT+6#|tS*$M}_hF04!+*SFz!(05sz{*um%U1EMYiY#gn2V`TRA(K{ni|r^i5gIrylVTH;bV;2 z?E{`%)EH&5@|ucdpfTQM#uuR!za zrc2#lyiBlm&Z(^|e-$z~rKm~1*o;XZJ9k$~IN|jQnby|)hs_ynaXGtw>z1yYb^GO? zne}#Qyz(!pv@BmNpAC)ODw5>uT$&>*?rC(b1ozBN=~i7^;I0 zT&n{Wm=~(GB`H<;9z8b$@le3v2iko2Zp5UIQna+0LU$RP z?m)txGhoj$%psWK^U^u+be9I>kY!%8?`~+3OlOq;J|n}n=HCof0ov9H*%(nyhJgWp zYcgn#r_abRaNGT@$uMxY{H@6_eZGZ&$S{iE@WFt<=L>CW0DO;+>4L-8f8W#VcfpOi z;Iq5nPF--gkNIA`%evtHUGNoM@bE5p65xHo^~Kkfy~hCC(}n&(7rd|wex(b}_*O%ud zBq6!ZArT)w9_*PZ9(BZ&ks zk(dj&mjV&qZ*sXH7@;56$B)kwd-(|&Y6FV1ZUW%Y6SNoFC`rPukKo&^PjHJ4Oq1f^ z1p#aLIS=O$I7+bIp3>tHu^4fvhocAUqX`b*^I=|wI9z7Ic;I+}_O~Pfa|OeWHmp}d zLmukkXv2IM;!qFoSIjjCJ@h{ee4x!B^e}d;4+jr2jZV5BnhXmAgQv-QO%bQ>2OB~U4~@YGnhT*P z+m%A-N&i`do^014LJtouS?>%}Oz27f{}6i8zbp(K7#+V7;;_B2s2vmm8%gL%|Bi&7 z^e-UvWc&ey9{P7?y)#S%p(p+SMd(TY7YRM-|0bb_{=Ja@Cxo8#KLjphfYH}?EaI>j z>33Px@a?=*jqF2tDaPk|lm4a9(gn5) zuFX2fHNoMJWtf{0d?3NC39dqLU&QhKgXj5GgdWbp*nb4UhY&oG@)HWqK}#j{fKK97?jVY_%WnEN%8e4&Qgl+K)jCPB`D5jiU%P5dx}p&TpG0-_d_g- zM~>pQh~sq*)-OOh6-u9v{12sgGs=&rIQ$h4nl8mlk)K~F?uqgD!S$pW+^f7g78<;*}J~_eTT8 zTaf-8#o;~+8eXU2IKxqU2cQ&lJPtJ}o{R3csTALZxH-jFp*+5iu>V(*a2Rh&kNs?* zxE#uFr+5+avzOxCQ9m4|INs==r8wNTLMx~EQ`)? zMk2ieN-@Xlp&=9>h5C6c#pfaa`V`0G--P0d$PZqB;(8MiUqb0;A-;^_a32i~KOe9k z{M?G9^n1{KnMm;ui0`8KJ=Bi}D83x&Pf#4s%NHoV75TYIaoqlg6kmk$O%#_$_2O}i z+v|t?cSrq%xedzqqxff}SEu+ol-Hv8TcpR&RqQ_%=`1OIDvHyV;uXkGAjOZP{2Gc6 zMEMko@0472=68xmp?Y&D{wwOoVv6H9`k?hEZWnUTs8QS$otQy!2jrJWac9I=P<$ea zb34UfAbybI&rrSBC_V@2Ur}5i)hmY|m!zM&nZ^cF~8OmO&f6PM8Y&k-bL`w{NlpyBu>Ij|o`2k<_GpRp)EhT>)@Kc3>2 zD6dU%Jpb!aJQn5g`#M~22IBbr3g!n9H>doZMR~k_!TO7cllu{P?keK*QHu3VtRyrN{3Fms4C5>G8P1@#rBQM(GU@kD@pp zM{yL#^VKGb+ao_)D2~_7sT6lZ`Ygm@77ZNRoQu zBay&|r!9tmchYv_iFu5+YhWOwE%K9O*t2c|*B}qBmk|8LN?UTwb9IIPiqa-d^F%zM zhX53nC`3XJAFn|0&YRTucnJkq0P4qw5Q7)tAkX*n^%V#~C_+E6KpXs|rNzDfIGENyJiNb1qs!j{C%HdS4`FrJq OQT&fPOG*yt^8W#F&hPF3 literal 12968 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 JDc+&${|9^R#6|!B diff --git a/engine/src/plugins.c b/engine/src/plugins.c deleted file mode 100644 index e69de29..0000000 diff --git a/engine/src/plugins.h b/engine/src/plugins.h deleted file mode 100644 index e69de29..0000000 diff --git a/engine/src/ringbuffer.c b/engine/src/ringbuffer.c index ea57ae6..0343df0 100644 --- a/engine/src/ringbuffer.c +++ b/engine/src/ringbuffer.c @@ -8,19 +8,18 @@ static inline size_t load_tail(const RingBuf *r) { return atomic_load_explicit(&r->tail, memory_order_relaxed); } static inline void store_head(RingBuf *r, size_t v) { - atomic_store_explicit(&r->head, v, memory_order_relaxed); + atomic_store_explicit(&r->head, v, memory_order_release); // release after data written } static inline void store_tail(RingBuf *r, size_t v) { - atomic_store_explicit(&r->tail, v, memory_order_relaxed); + atomic_store_explicit(&r->tail, v, memory_order_release); // release after data read } int ring_init(RingBuf *r, size_t capacity) { r->buf = (float *)malloc(capacity * sizeof(float)); - if (!r->buf) - return -1; + if (!r->buf) return -1; r->capacity = capacity; - store_head(r, 0); - store_tail(r, 0); + atomic_init(&r->head, 0); + atomic_init(&r->tail, 0); return 0; } @@ -30,47 +29,37 @@ void ring_destroy(RingBuf *r) { r->capacity = 0; } -static size_t ring_readable(const RingBuf *r) { - size_t h = load_head(r); - size_t t = load_tail(r); - if (h >= t) - return h - t; - else - return r->capacity - (t - h); -} - -static size_t ring_writeable(const RingBuf *r) { - return r->capacity - 1 - ring_readable(r); -} - size_t ring_write(RingBuf *r, const float *data, size_t count) { - size_t avail = ring_writeable(r); - if (count > avail) - count = avail; - if (count == 0) - return 0; - size_t head = load_head(r); + size_t tail = load_tail(r); // producer reads consumer's tail (relaxed is fine) + size_t head = load_head(r); // own head size_t cap = r->capacity; + size_t used = (head >= tail) ? (head - tail) : (cap - (tail - head)); + size_t avail = cap - 1 - used; + if (count > avail) count = avail; + if (count == 0) return 0; + + size_t pos = head; for (size_t i = 0; i < count; ++i) { - r->buf[head] = data[i]; - head = (head + 1) % cap; + r->buf[pos] = data[i]; + pos = (pos + 1) % cap; } - store_head(r, head); + store_head(r, pos); // release – makes data visible to consumer return count; } size_t ring_read(RingBuf *r, float *data, size_t count) { - size_t avail = ring_readable(r); - if (count > avail) - count = avail; - if (count == 0) - return 0; - size_t tail = load_tail(r); + size_t head = atomic_load_explicit(&r->head, memory_order_acquire); // acquire – see producer's writes + size_t tail = load_tail(r); // own tail size_t cap = r->capacity; + size_t used = (head >= tail) ? (head - tail) : (cap - (tail - head)); + if (count > used) count = used; + if (count == 0) return 0; + + size_t pos = tail; for (size_t i = 0; i < count; ++i) { - data[i] = r->buf[tail]; - tail = (tail + 1) % cap; + data[i] = r->buf[pos]; + pos = (pos + 1) % cap; } - store_tail(r, tail); + store_tail(r, pos); // release – makes consumer's tail visible to producer return count; } diff --git a/engine/src/ringbuffer.o b/engine/src/ringbuffer.o index 72e5b7217bd380e5a0c6ff8fd5cadfb891d304b9..d4046ebad4e1e3e6d9745d07d4dceef0cbb125c6 100644 GIT binary patch literal 8184 zcmb_g3s{UO#d;fVIE?IT1;!vvt*IR3~hjz^PG0q!9bw|H9EDFT($0MHh83~0** z^=+oFIaHR%EKiAWt~&6o$Eb74mAXtBKdr-pp3r-nvIL&>Hqc&9k^j-ilo#O3B`hn z1l6-=e}#%5e@gZ0KlM1WHsSCFGgM6{#LL7!g;bY&0m8R-b1DN`jnG zl75JL&8OrqpFbzo*ppv+|554#iFlifc;Zh1<7cNEYF+5On3a3g-D}pM0jnSLO>9>; z6g0Fgo#uYR#%gMcUWz}z=JpnAmEg&Y2PLh$y|+%Df9-l`P*_@1Rb{hQg-WO3%n;WD z%~hovs&D6%L>N`HEH<|OGqe2eh%A@sg*V&VEO(9+7fBlz?m4z?nXUWJ!nOp{MLr8o zp0+oBW}FlL`-Gu`##Z?0UA{Xk+VSMKogS45VLP`KKUpHzR@UxfzL+Etn)#a<_;=sT z?l|mmY?(`{)#>?D3ZuLDk*$aJ-<=T>+%T}Iz`Lw#a6@KhL+EnZFvqlaEoViB)XrOH z@4Cpj$g;Jh`quL&{;}KRht09GOCD|Ow4~NEs{On|KU>tk!Yk=|v^Zeua9LBqmhLCd z-EKNZZ~H4QGo&-|*Z4E3y4u>^zV69ecegK`Fm2-IJ$jN{3)g*FRsFaBq1w^jW%kH- z{4eHfH`e|7a7k6n%=p08(A<3!r=45X5gdNXtL@xE?S-kALmO8gFI-t%*by@GkLQ6l zXGLWv1+7j)%9HcAem~o%R1l&(H_$cic3JTW#rqXk2E5qk(IKw-rZV8_-(Ovvu6mHN zyDagC_ndb4T!>{q?RK~{;iAvCnOi((PG8q*YU{C-8`i@ zPQAKbudf=s@W}du^|twJpq2}N*6R9q7W@(#y#9#M!J@Cd+H2(LYcp;gdb!QI@Ds_X z%HjU`f%ot4PIa)zlU}diz2u$i2iM&ie0SY`oli5)U&$Qu@2&035hs@pwqSI&-?lLpZNg5;K2D zY4epP-vwPiPA$1xEPH9%#oiu!Z=hw4*yGubHoaD%BehR zp@-*zZ$4Qgwsh)Pu;TU)%`ZaqOaJvM!0(#hBJ03GOWUTNnXdJCV4%I-(y>qkEv^uH zq#Rr;)9J6qcfTv%zGyi6i`@XVT&FmqqY2L521%a?S`r!$6( zRqjh|+spTLAMeslSZkPTSnln3XxDLFTgAp_7gr^(l$>!fVy|xBl=nQg@(;JT?e4%x9YwL>6{sytWvXkdqBKNpAG;4VdZ2WM2{DMwx z?-$AIs!e11{r2aez2?GTtg%3`-jn}yn zF+%sfNfvF1wrv)F?}(Ue-!;zY@;4oAot}@ZY(C1(l6TGc?OM*7UnZIvcrHz?dPA3T5(|7`iWgQsELkl3a>PT6 zEZbWK3yptybj9h}-djQ0VkTGsxDmp_@{wQtzJfZLk&Aa>&55f zezl`AiubSCd~bG?w@-$XbGn(&o)z6&M%Bn4d~Vw;Gu~VI*ALHJ)>u7h_pukQy6Y6{ z=l4ORU;5KUUo_`Qy`3K=SSD<1oAgP)^{ek24$mHz7;dF6)RWvXbb9(@9bR>0yD@ufC1Guus?@|ZWvxQH@fkf#5p$DMEdf}&%K^rJu~s5{`erH?ISNun;n=vG5Esl zBW{YpdQ$r-_PMsEFWQ1Eo0gi4yOPVllhS$q_NAOrWq(%N-dCMYYUB;PXVtQ;JL~)G z>>>x9tmMgee!5UQsdIF@TkV6j@8vCJ!x-_My#0dgZCum5S1%WohXRg%h*xcZHc_jLA2mqq} z! z{#6Q3!uUf9pMmjC3h%tWml7zJ^GARA+IPRB}zKYXkm?8?d z$N8V2{2a!9>M4EVr;);mpL-O(8pkE`PwGJKC+7G)LU0`%cMQf!JxScj6i(v$PqVRo~_onc#G4D&^+9bPx3xu91~NQ z%Y+fo%B1Qxn)IY1iO^9dN|7;+Q9_xJag0bzWE?p(N)(ZVuEXMZka2xsM1)k7+)FH! z3gecEdYedM;zYy*;*c(a;l>SoWc>2wqBsZ{7cUbz!Y8Z)cM-?@vbZG21ZjMNNGeOk zu|&~fVySSs2oL;!Y;3?Dm5S^K;?4ti=>Mj0S&15VzeQV{=o10oP~B5t0Nv}5tsdsl z_aGv)e-Mt3TLf`bj?W9ila`J@76i!WKeb2qYR)G~3aZBs^1KwBuNAL#? zu}AqMmn1&PKL)VA@(;#}PEK;5lGa+l`?9Y`!GOqlrRHA8C-%7@;OvP4ZbR@#;7|%DcBI{b?I|@T1>4g& zu_JgfbkI_vmZ`>`%pcLG%EX)n0rE-P7vcD3R6yGPG}!hve=T@|6d*^0<_~@Q5_>X# zeZ@xl3iR;~MBd}Z&_@t$kEWU4`v5VU(4PW8>(q_rNezd~sW1DYA!?KFHJ0iQZT}yM C6fExm literal 7976 zcmbuD2|Scr8^GToOA-=VR3sI*Fe(z#;ErB}w76|*8jO;C3|U(!HMWS>8*SQdw`f&S ziCbw=x^*KGx)ps=+P+k}zH{F9oM}3r?)|=d&+j*L&htOdcAj(IWo8B6%|(I3p>X3+ z4%8P3Q;Iq;;LDiD40)6urAv9y(kFD%b1p4SsPUsydki-LleCoIVOdJ2@@v4T9rWM> zVG9_7k5`8uJxYhslqj`8XIIPr%1=_M>fiE3OCzhkomUl`low2_otA24f*vNvE~DGz zzUA;sg8!dWEl{}*;FK-X#YP2MdWES8EiHrD&{EI-OrOf@Pp>JBR5{%RTI*Prd4a4w zwcz+XtRuAj=ptOMHq(N z-4^J=j+KE+P)R#}hggjnjpBE-_5~wg$K=^g1447ESf?|=(E$k{uc>q3N*He`AZJ=y zOPiL_=}urd3p!+3@ZeE{yFg35Du6y1Uuh{|^qI3(K~LbfN2oHHHgWpQETEB*Q+c2!aQ%Fy_N_FaEp)oh8dQxcnge&EhACL8dn0|ZNrC26ywTKB zc%#_|`fQ|=N;zj4_4kn-AU6Q`EgZ&=9lt_+1e|*|mh>LXcs2~5%CGqIbtQhO^8dQ7 zW1WAoF0=@c&BaGx5pZ2#sp@h|{U__Hg5jBU!CJ`fC0#ujQ6@)kPnc00($`VvoZZ+@ z?Y4nede*E`dd)L28I!Rt+9k!cPg?5tiFDSCd{x7t#Zs*?^sM-#;op7lc+sG$UFGd8 zops8ubUnH6Z#<9IQ#5~Z{k{Fu+DW?8X-B7zW3M-rlvL-uZ5}y0+m?3n(A9V}`Ow$Im2T{=x|yQ867%9NhTio$WL?~fbN zIQ513i)T5@?={`skTNQUH|t5L_K>Z|%0!7{>L0(~vHWdk&x8N6x3KU$ty4Iu#`TQy z@oiZRF;(}H{C`cX)-Gtnyr#(B;rX0RY;ukS4e_?%m%mMe; z`i+O)XbK9H;v;Wc1?{PDIPm-9+S#J$Mq z>`L~SM;X}U4pz#&d&hOZL@d}p@7IGKlJ=C-&l(b{pOuWYeRcLsT-VD7t|=3*=jLaR zkH7QVT+f3WoEl7*L|I*W`1*dicASlHe}lhEurhm0Was28dKlqnQuOx7%u0`PuGZ?s zt*3jdyZ#sw;LH2TNcY;o{X>?ds*QiV?9tH1^rN{4CS}n~DzP0v(N?3Mje ze?r*gf~0}m`CDG6_!X?O?^Eboc=en@d9c8G_pC94IcM5hL!V|9DwVeN@)2e|J+@N+ z-ABEDHv~T}&;2E^+3HB=&lhrgI3CSf5x3Xf?n-l(|EZ|WeY2vduO_8Hp;H%#?R)hs?@>X|S>NN7D>cGxz;rz2@t(84@ott4f zN;TI0P#_(bKk~w`XDXem@`o??pEs;PN9nfRVypc(L(@+@u(*2Ta9+8{%!l&BU z7zEl>hMNvcwLK zzkUlT{z)(QDg7zgNLu79^{M;(Vei7b#Y=DBxTHMPK3nnC(E)0b4qesJ%?C1b)}-y$ zyz_2-^xkJJduH1xyC|(q$@G2gZan+Go|3|qeP>U_XDyxkcGr^?D&bt^jdr1xZ|4=f z&asZ$Rp)IIeWI0fA@%tEu-(~5U4`4W>Ki1b&$*JfC%>%UmY9L*lZv~R*-XewdzhnU zWutbxKKA~$3C9P`e4*-)obK%4kf0!py)nnr?LkWImZv`@ls~uGyy$(0L8jghPfP6! z%d~3B33T#&orEL#^-QhAvobbk2Rrz2Abkfpc`lo!d4!y}5H? z{0;T7UVUTb;sPw)M|?ExY|&eG2-~! za^q)>C6V_yy`Bzf$?i(Io|<~VtVc@Jgr@t~F57gBd}V$4`O1D<7SaN0va_?Tp~++q zFH=KHYjaC;OT#hTG1lC1W4RQ(Y7D_g34DM8xU@K>km0;JNbU(3Y&vX5abjmsoMZ-*Q-3H*}Hhsx$lAO~$c{KE5MW}3k2Gw^}>Pap)1sR`(9z=z=|1m-S?+oJmhQzO7&Q)EsTJC^@eq*UZZ*;tWM zla1vEij=y%C>twMy=7xOfO#>m=NO>gAN1N_UA8EH=Bxk~20q>CFGKoAkUn#E09*YH zeJRopM0H@!0${MdFs`#A7=VU3B@PH#>=WtlFA|&kQbD0YKfw}_(2pHSgaJWRgd|if zVj(+ZC^Qp@fu_(WNW?f1EE5MvM2sj9L`a0bfdbzpfdYT%gV7EM36N01!l0l~Ul#a@ zA|&F_7|LHPl6S#QLf;@yCnj5X1}O@jE%5frw2W`?hHvn3sD7lMALKZ!KlXoFj%&+t za4TUNJR$O@1fKxN;3+X-v?!_{>LJHE5a|=V50VW~55uD^WQYag&`&QkhMhGUs0enJq>C-^Ls{~>}KBYuqFR*0V=_z5JJ5?meWUm^HiB;O=>9Lnt3(BFal z(}X^Lt_2eM_#DO(96qmLTS552&;26=?}75XK=^sVx}how?ugd^h~PVspJu{8J`Z~E zK>-@B!*N!mMk0>uhT~ch9LMDo+!@84M)=3?MPh>cqPR(fpO45-7Qyup-$!udhB`|4 z$GnQ*`1#aC_`z{s5*)|H{|~_W^u*3VkKb)@J@J$g8bE#cd^AJy2!iA1)^`MVN3uD= z6Oatw7h%KxQ&^FjhtKjDJg(@- zGK5EPd`=b-9G}~j1jpCWPJ-j>w3OiZdT%5+zHcxW1en8l;`@&wI(L}k`zMd!`2J6s zN5lk6gg&5`h}nJ#9*IRkLUV~IT0)uo2_-_x+$SP}GH0lt$R~2KARxp)6wEPWp^uMP z6!q1#P%I2tEc)6cC?G_HO+Xwxgc!W60@*h-I9L<{LWYD&MCRb9s2TGPi{!;2k>+9I z&@ho$5`$uimI(aC!e9|9)IaPDfjul0zJ9Pf9vYB;FQKwxHRgR1UXoZ}HlfLmG7*69 zQP5Tu$^B6euQ$li?GRClnvbq?89)Ov{%F9V&%bLA-(wk{I58Aw5sGgqe^H=VNQLR( z@4fEUpNZCQh<@t9^(%o74ujY41Xy?Rx1bIA0OsWzw#N+q-Xm)Q=dw^-E>=W6sp|=N zclj6Uu?E;4<_}}z_&EOnz`D!70>w8X0%CjCJ4N|{L3i;RQG8Wo597mo8n(yr;rj+u z;o=D}hOHgRlfDrjop>Kc?QR%?m@s{sflAsVSBuP&=xkLOw>idpik0X3;o!eLj)x4OMq>6``3bgfVP8q z*x>%b-#ZPhR@~QH!85XnAZW`zDsd #include #include -#include int wav_read(const char *path, float **buffer, unsigned *frames) { - SF_INFO info; - info.format = 0; - SNDFILE *sf = sf_open(path, SFM_READ, &info); - if (!sf) return -1; + SF_INFO info; + info.format = 0; + SNDFILE *sf = sf_open(path, SFM_READ, &info); + if (!sf) + return -1; - /* We need mono 16-bit PCM; refuse anything else */ - if (info.channels != 1 || info.samplerate <= 0) { - sf_close(sf); - return -1; - } - - unsigned total = (info.frames > (sf_count_t)LOOP_BUF_SIZE) ? LOOP_BUF_SIZE : (unsigned)info.frames; - float *buf = (float*)malloc(total * sizeof(float)); - if (!buf) { sf_close(sf); return -1; } - - sf_count_t nread = sf_readf_float(sf, buf, total); + /* We need mono 16-bit PCM; refuse anything else */ + if (info.channels != 1 || info.samplerate <= 0) { sf_close(sf); - *buffer = buf; - *frames = (unsigned)nread; - return 0; + return -1; + } + + unsigned total = (info.frames > (sf_count_t)LOOP_BUF_SIZE) + ? LOOP_BUF_SIZE + : (unsigned)info.frames; + float *buf = (float *)malloc(total * sizeof(float)); + if (!buf) { + sf_close(sf); + return -1; + } + + sf_count_t nread = sf_readf_float(sf, buf, total); + sf_close(sf); + *buffer = buf; + *frames = (unsigned)nread; + return 0; } -int wav_write(const char *path, const float *data, unsigned frames, unsigned sample_rate) { - SF_INFO info; - info.samplerate = sample_rate; - info.channels = 1; - info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; - SNDFILE *sf = sf_open(path, SFM_WRITE, &info); - if (!sf) return -1; +int wav_write(const char *path, const float *data, unsigned frames, + unsigned sample_rate) { + SF_INFO info; + info.samplerate = sample_rate; + info.channels = 1; + info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; + SNDFILE *sf = sf_open(path, SFM_WRITE, &info); + if (!sf) + return -1; - sf_writef_float(sf, data, frames); - sf_close(sf); - return 0; + sf_writef_float(sf, data, frames); + sf_close(sf); + return 0; } diff --git a/engine/src/wav.o b/engine/src/wav.o index ba7818da38a39b3a319c7dd70b40895bae969bc7..6d48fbefe91c6c388594c6c82a6483f47650882e 100644 GIT binary patch delta 903 zcmV;219<$9L5M++C;SIR4$sxYdAMSE$+s{h9~J<&Gx91;q-^EYqZ8&BpAfgflMO046c3p|O+ zj9^a8oe80q#$5TOWb8uYK?wf!q?2`ccKH*gVjn}n(^NfiYazI<{t+e(?^TO1BCH4} z!iy+~NJOL}iXuuPG7)7F6%kbtxrmyGx`>8|84-W8B3dHmM6^XLiC7k~qHr@BG@4%u zjt6?M0487#*1$U00NdaaxD2j<9k2_Y0#AcIun%4UFM^lBE8sP74ZIEB0q=qv;1lpE z_>8}@cr=xZXp87Jh@(;Tqv|-Jn(_J%X>G0Iw=(^1@~KmHr%w>>HjEqka5XGDNV>@T zZM=U~bUn1nJ=E!@r4J*FN4nCae+bN`PWvJ77y6h@1~XujCwxknX2OSsX>p_Hy4AMy zsnPC#n~a{l7k&=-Oh7wN$MRp5l>Xww7uUt< ztENP*L(|(dK}C+N@^thHRdxDmGTz6iw?2P5`twkJftK3g?ttw{+P)ori1yqXvRiDA zwAj8I93XqNL~YElSK3%t$Fn`G{665u$#mm+Esf9m*f^Qrj?ZFyj52okCfe2Wu&=mN zI^C%lJMD6=nenD}2Bquw*VSxsANC7p7jg60mZyZLV)YN}a#u?M000000000-0RXdW z1PuXy`2YX_000000RR9100000c${62yA8rX5JdORey77WSO^JW5<~!^2uDN+D8Psu zDlpEW0!p9&DsVq^Kt#fw`3SMW8|`RTqrHm=VrA)6DfQ^Iy(qsm#we7((Q2eqBd^38 z`ir0eb7|?%!NBIEH@YRpo=8gK3nphQI#dNnLoYe3NNTbRgEcd69Z9?_9JDFOOvN-O zTViS`eYjr{*fViW`%1hc+M_m1?A8|aT;HCw{Ampic0*LVjPAS7oA`wv(Q4&!WQ4x`fsEWu$)I`)ptGJHz$I`QY=bLc2Rs9w1-oDmyaZkbuYuRWn_wTj3*H0ogB#!z z@G1C=zp`*Nm5FGH=;XxFDELuzoKTH${l~O6M)6yjemB|FDLd0A@OSISZM|6a%Z`#R z@_zHTe~PY$R@sL-**5fH`0+@m4f=<`Z0n@w1An29*_2@hY_gb73Db!AurN(-^jtSv zhCVgg{qLwu&pz-!3w$P^ougy9uS!aPap})->7kNR6u7-8ec|ID%B`yLy1!9@x*HqT zh3OkniQEcJ@6rSnS+dH}(Nn5A(NmN0K199qf6>yPhw=-w)C~6rY){kn&G1987y8KV z1bdb3yTK8%ixp}U4ZEd{HPucJu=4wW8^_a)=e0CG??U5vem6Xe?Mcd*;hSJrFT%d! zPRVqqV(7Hbxjf}f?F~w|HrG_6dni#fB zjM#u$04uNr3$Ov*k4>0(uYLyZ;GCRu|1lznN+u%4m}e*SWW1`4NmKs9nu(19c~^YE z{-99<3u)PJ!$4Ew2fY&2Gl|%yIS^iP_SB1#Y+VPniQ7LH7151wM|+Pc*yvhw61RI#T>f3aQ489YtFwLg=?j??8bh-; z25A)m=#!xv#{prJX&g5J@ROk&KLKEq;T%5!Ig=qBKLOa2VI4mSa0LJW00000&y%Sg zHvtHf;T=B#K$9UJKLOyAVIDsLSCgS0KLK--;T}H$c#|O?KLMzdVIMyM5R;)FKLP*> Tlj0vf0f3VsAU^>>lVKny?VpRa diff --git a/engine/tests/integration.c b/engine/tests/integration.c index 27a5eea..f0fa3c1 100644 --- a/engine/tests/integration.c +++ b/engine/tests/integration.c @@ -11,6 +11,7 @@ #include #include #include +#include /* static variables for passthrough test */ static jack_port_t *passthrough_output_port = NULL; @@ -333,20 +334,12 @@ static int test_looper_looping(void) { return 1; } - /* first note‑on: IDLE -> RECORD */ - if (send_jack_note_on("looper:control", 1, 127) != 0) { - jack_client_close(client); - kill(pid, SIGTERM); waitpid(pid, NULL, 0); - return 1; - } - safe_usleep(500000); /* allow state to change (500ms) */ - + /* connect audio and activate immediately */ int sr = jack_get_sample_rate(client); - continuous_sine = 0; /* disable continuous tone */ - beep_remaining = (int)(0.1f * sr); /* 0.1 second beep */ + continuous_sine = 0; + beep_remaining = (int)(3.0f * sr); /* 3 sec beep – covers entire recording */ bursts = 0; prev_above = 0; - passthrough_output_port = audio_out; passthrough_input_port = audio_in; passthrough_phase = 0.0f; @@ -355,7 +348,6 @@ static int test_looper_looping(void) { passthrough_total_samples = 0; passthrough_sum_sq = 0.0; passthrough_done = 0; - jack_set_process_callback(client, passthrough_process, NULL); if (jack_activate(client)) { jack_client_close(client); @@ -363,19 +355,23 @@ static int test_looper_looping(void) { return 1; } - safe_usleep(150000); /* let beep start */ - - /* ensure beep is fully captured */ - safe_usleep(800000); /* 0.8s after start of beep */ + /* first note‑on: IDLE -> RECORD */ + if (send_jack_note_on("looper:control", 1, 127) != 0) { + jack_client_close(client); + kill(pid, SIGTERM); waitpid(pid, NULL, 0); + return 1; + } + safe_usleep(3000000); /* 3s to capture beep */ + /* second note‑on: RECORD -> LOOPING */ if (send_jack_note_on("looper:control", 1, 127) != 0) { jack_client_close(client); kill(pid, SIGTERM); waitpid(pid, NULL, 0); return 1; } - /* wait enough time for several loops (4 seconds to be safe) */ - safe_usleep(4000000); + /* wait for several loop repetitions */ + safe_usleep(8000000); /* 8 seconds listen */ jack_deactivate(client); jack_client_close(client); @@ -504,7 +500,7 @@ static int test_control_key_modifier(void) { /* Wait for looper to enter RECORD and detect audio */ int sr = jack_get_sample_rate(client); continuous_sine = 0; - beep_remaining = (int)(0.1f * sr); /* 0.1 second beep */ + beep_remaining = (int)(0.5f * sr); /* 0.5 sec beep (was 0.1) */ bursts = 0; prev_above = 0; passthrough_output_port = audio_out; @@ -517,26 +513,30 @@ static int test_control_key_modifier(void) { passthrough_done = 0; jack_set_process_callback(client, passthrough_process, NULL); if (jack_activate(client)) { + fprintf(stderr, " FAIL: cannot activate test client\n"); jack_client_close(client); kill(pid, SIGTERM); waitpid(pid, NULL, 0); return 1; } - safe_usleep(200000); /* allow beep */ - /* send note 62 again under control key to move RECORD->LOOPING */ + safe_usleep(500000); /* allow beep to start */ + + /* second note 64 + note 62 to move RECORD → LOOPING */ if (send_jack_note_on("looper:control", 64, 127) != 0) { + jack_deactivate(client); jack_client_close(client); kill(pid, SIGTERM); waitpid(pid, NULL, 0); fprintf(stderr, " FAIL: control key re‑send\n"); return 1; } - safe_usleep(200000); + safe_usleep(500000); if (send_jack_note_on("looper:control", 62, 127) != 0) { + jack_deactivate(client); jack_client_close(client); kill(pid, SIGTERM); waitpid(pid, NULL, 0); fprintf(stderr, " FAIL: send note 62 for loop\n"); return 1; } - safe_usleep(2000000); + safe_usleep(5000000); /* listen for 5 seconds */ jack_deactivate(client); jack_client_close(client); kill(pid, SIGTERM); @@ -619,7 +619,7 @@ static int test_bind_channel(void) { /* Wait and detect bursts as before */ int sr = jack_get_sample_rate(client); continuous_sine = 0; - beep_remaining = (int)(0.1f * sr); + beep_remaining = (int)(0.5f * sr); /* 0.5 sec beep (was 0.1) */ bursts = 0; prev_above = 0; passthrough_output_port = audio_out; @@ -632,26 +632,30 @@ static int test_bind_channel(void) { passthrough_done = 0; jack_set_process_callback(client, passthrough_process, NULL); if (jack_activate(client)) { + fprintf(stderr, " FAIL: cannot activate test client\n"); jack_client_close(client); kill(pid, SIGTERM); waitpid(pid, NULL, 0); return 1; } - safe_usleep(200000); /* allow beep */ - /* send control+note62 again to move RECORD->LOOPING */ + safe_usleep(500000); /* allow beep to start */ + + /* second note 64 + note 62 to move RECORD → LOOPING */ if (send_jack_note_on("looper:control", 64, 127) != 0) { + jack_deactivate(client); jack_client_close(client); kill(pid, SIGTERM); waitpid(pid, NULL, 0); fprintf(stderr, " FAIL: control key for loop\n"); return 1; } - safe_usleep(200000); + safe_usleep(500000); if (send_jack_note_on("looper:control", 62, 127) != 0) { + jack_deactivate(client); jack_client_close(client); kill(pid, SIGTERM); waitpid(pid, NULL, 0); fprintf(stderr, " FAIL: toggle for loop\n"); return 1; } - safe_usleep(2000000); + safe_usleep(5000000); /* listen for 5 seconds */ jack_deactivate(client); jack_client_close(client); kill(pid, SIGTERM); @@ -749,7 +753,7 @@ static int test_bind_unbind(void) { /* Wait for beep and loop */ int sr = jack_get_sample_rate(client); continuous_sine = 0; - beep_remaining = (int)(0.1f * sr); + beep_remaining = (int)(0.5f * sr); /* 0.5 sec beep (was 0.1) */ bursts = 0; prev_above = 0; passthrough_output_port = audio_out; @@ -766,7 +770,7 @@ static int test_bind_unbind(void) { kill(pid, SIGTERM); waitpid(pid, NULL, 0); return 1; } - safe_usleep(200000); /* allow beep */ + safe_usleep(1000000); /* allow recording to start before we set beep */ /* second control+62 -> loop */ if (send_jack_note_on("looper:control", 64, 127) != 0) { jack_client_close(client); @@ -774,7 +778,7 @@ static int test_bind_unbind(void) { fprintf(stderr, " FAIL: control key for loop\n"); return 1; } - safe_usleep(200000); + safe_usleep(2000000); /* give time to record beep */ if (send_jack_note_on("looper:control", 62, 127) != 0) { jack_client_close(client); kill(pid, SIGTERM); waitpid(pid, NULL, 0); @@ -882,38 +886,17 @@ static int test_remove_channel(void) { * Helper: generate a simple 440 Hz WAV file for load tests * ------------------------------------------------------------ */ static int generate_test_wav(const char *path, unsigned sample_rate, unsigned duration_frames) { - int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); - if (fd < 0) return -1; - unsigned data_bytes = duration_frames * 2; - unsigned file_size = 44 + data_bytes; - unsigned char header[44]; - memset(header, 0, 44); - memcpy(header, "RIFF", 4); - unsigned chunk_size = file_size - 8; - header[4] = chunk_size & 0xff; header[5] = (chunk_size>>8)&0xff; - header[6] = (chunk_size>>16)&0xff; header[7] = (chunk_size>>24)&0xff; - memcpy(header+8, "WAVE", 4); - memcpy(header+12, "fmt ", 4); - header[16]=16; header[17]=0; header[18]=0; header[19]=0; - header[20]=1; header[21]=0; /* PCM */ - header[22]=1; header[23]=0; /* mono */ - header[24]= sample_rate & 0xff; header[25]=(sample_rate>>8)&0xff; - header[26]=(sample_rate>>16)&0xff; header[27]=(sample_rate>>24)&0xff; - unsigned br = sample_rate * 2; - header[28]= br & 0xff; header[29]=(br>>8)&0xff; - header[30]=(br>>16)&0xff; header[31]=(br>>24)&0xff; - header[32]=2; header[33]=0; - header[34]=16; header[35]=0; - memcpy(header+36, "data", 4); - header[40]= data_bytes & 0xff; header[41]=(data_bytes>>8)&0xff; - header[42]=(data_bytes>>16)&0xff; header[43]=(data_bytes>>24)&0xff; - if (write(fd, header, 44) != 44) { close(fd); return -1; } + SF_INFO info; + info.samplerate = sample_rate; + info.channels = 1; + info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; + SNDFILE *sf = sf_open(path, SFM_WRITE, &info); + if (!sf) return -1; for (unsigned i = 0; i < duration_frames; i++) { float sample = sinf(2.0f * (float)M_PI * 440.0f * i / sample_rate); - int16_t s = (int16_t)(sample * 32767); - if (write(fd, &s, 2) != 2) { close(fd); return -1; } + sf_writef_float(sf, &sample, 1); } - close(fd); + sf_close(sf); return 0; } @@ -1062,15 +1045,17 @@ static int test_wav_save(void) { kill(pid, SIGTERM); waitpid(pid, NULL, 0); return 1; } - safe_usleep(800000); - /* stop recording (cycle again) */ + safe_usleep(3000000); /* record for 3s (ensure enough beep) */ + + /* Send second record command to transition RECORD → LOOPING */ if (send_fifo_command("record 0") != 0) { jack_deactivate(client); jack_client_close(client); kill(pid, SIGTERM); waitpid(pid, NULL, 0); return 1; } - safe_usleep(500000); + safe_usleep(1000000); /* give time for state change and loop_count to be set */ + /* save */ if (send_fifo_command("save") != 0) { jack_deactivate(client); @@ -1078,16 +1063,28 @@ static int test_wav_save(void) { kill(pid, SIGTERM); waitpid(pid, NULL, 0); return 1; } - safe_usleep(2000000); - /* check save.wav */ - int fd = open("save.wav", O_RDONLY); - if (fd < 0) { + safe_usleep(8000000); /* wait for synchronous save to complete (8s) */ + + /* check save.wav with retries */ + int saved = 0; + for (int retry = 0; retry < 5; retry++) { + int fd = open("save.wav", O_RDONLY); + if (fd >= 0) { + saved = 1; + close(fd); + break; + } + safe_usleep(1000000); + } + if (!saved) { jack_deactivate(client); jack_client_close(client); kill(pid, SIGTERM); waitpid(pid, NULL, 0); fprintf(stderr, " FAIL: save.wav not created\n"); return 1; } + /* verify header */ + int fd = open("save.wav", O_RDONLY); unsigned char hdr[44]; if (read(fd, hdr, 44) != 44) { close(fd); unlink("save.wav");