refactor: improve TUI polling, FIFO reliability, and add stress tests

This commit is contained in:
Loic Coenen
2026-05-23 12:29:13 +00:00
committed by Loic Coenen (aider)
parent 7c289e1496
commit d6bd31fed5
6 changed files with 418 additions and 122 deletions

View File

@@ -4,6 +4,7 @@
#include <stdatomic.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
/* Helper: zero a scene and set its state to IDLE */
void init_scene(scene_t *sc) {
@@ -14,8 +15,9 @@ void init_scene(scene_t *sc) {
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);
pid_t pid = getpid();
snprintf(in_name, sizeof(in_name), "ch%din_%d", next_channel_id, (int)pid);
snprintf(out_name, sizeof(out_name), "ch%dout_%d", next_channel_id, (int)pid);
/* Always register audio ports (needed for pass-through even for MIDI
* channels?) */
@@ -34,10 +36,10 @@ void channel_add(jack_client_t *client, int idx) {
/* If this is a MIDI channel, register MIDI ports */
if (channels[idx].type == CHANNEL_MIDI) {
char midi_in_name[64], midi_out_name[64];
snprintf(midi_in_name, sizeof(midi_in_name), "channel%d_midi_in",
next_channel_id);
snprintf(midi_out_name, sizeof(midi_out_name), "channel%d_midi_out",
next_channel_id);
snprintf(midi_in_name, sizeof(midi_in_name), "ch%dmidiin_%d",
next_channel_id, (int)pid);
snprintf(midi_out_name, sizeof(midi_out_name), "ch%dmidiout_%d",
next_channel_id, (int)pid);
channels[idx].midi_in = jack_port_register(
client, midi_in_name, JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
channels[idx].midi_out = jack_port_register(

View File

@@ -5,7 +5,7 @@
#include <jack/jack.h>
#include <stdatomic.h>
#define MAX_SCENES 4
#define MAX_SCENES 8
#define LOOP_BUF_SIZE (5 * 48000)
#define MAX_MIDI_EVENTS 1024
#define MAX_CHANNELS 16

View File

@@ -8,7 +8,7 @@ static FILE *logfile = NULL;
static pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;
void log_init(void) {
logfile = fopen("/tmp/looper.log", "a");
logfile = fopen("./looper.log", "a");
if (!logfile)
logfile = stderr;
setbuf(logfile, NULL);

View File

@@ -96,23 +96,30 @@ static void exec_command(command_t cmd, jack_client_t *client) {
// Save the desired scene (may have been set by CMD_SET_SCENE)
int requested_scene = atomic_load(&channels[ch].current_scene);
// Clamp requested_scene to valid range
if (requested_scene < 0) requested_scene = 0;
if (requested_scene >= MAX_SCENES) requested_scene = MAX_SCENES - 1;
// Auto-create channel if it doesn't exist
if (!channels[ch].active) {
channel_add(client, ch);
// Add scenes up to the requested scene
int sc_count = atomic_load(&channels[ch].scene_count);
while (sc_count <= requested_scene) {
channel_add_scene(client, ch);
sc_count = atomic_load(&channels[ch].scene_count);
}
// Restore the requested scene (channel_add resets to 0)
atomic_store(&channels[ch].current_scene, requested_scene);
// Give JACK time to register ports
struct timespec req = {.tv_sec = 0, .tv_nsec = 200000000};
nanosleep(&req, NULL);
}
// Ensure enough scenes exist to satisfy requested_scene
int sc_count = atomic_load(&channels[ch].scene_count);
while (requested_scene >= sc_count && sc_count < MAX_SCENES) {
channel_add_scene(client, ch);
sc_count = atomic_load(&channels[ch].scene_count);
}
// Clamp requested_scene if MAX_SCENES prevents adding enough scenes
if (requested_scene >= sc_count) requested_scene = sc_count - 1;
// Restore the requested scene (channel_add or add_scene may have reset current_scene)
atomic_store(&channels[ch].current_scene, requested_scene);
// Give JACK time to register ports if we created something
struct timespec req = {.tv_sec = 0, .tv_nsec = 200000000};
nanosleep(&req, NULL);
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);
@@ -197,8 +204,8 @@ static void exec_command(command_t cmd, jack_client_t *client) {
case CMD_SET_SCENE: {
int sc = cmd.data;
// Allow setting the scene even if channel is not yet active
if (sc >= 0 && sc < MAX_SCENES) {
// Allow any scene index; scenes will be added by CMD_CYCLE if needed
if (sc >= 0) {
atomic_store(&channels[ch].current_scene, sc);
}
break;