feat: add address sanitizer, persistent FIFO fds, and latency test
This commit is contained in:
committed by
Loic Coenen (aider)
parent
0537263a7a
commit
dd67576c45
@@ -1,6 +1,6 @@
|
||||
CC ?= gcc
|
||||
CFLAGS ?= -Wall -Wextra -g -Isrc
|
||||
LDFLAGS ?= -ljack -lm -lsndfile -lpthread
|
||||
CFLAGS ?= -Wall -Wextra -g -Isrc -fsanitize=address -fno-omit-frame-pointer
|
||||
LDFLAGS ?= -fsanitize=address -ljack -lm -lsndfile -lpthread
|
||||
|
||||
SRC = src/main.c src/looper.c src/channel.c src/midi.c src/queue.c src/pipe.c src/ringbuffer.c src/wav.c src/log.c
|
||||
OBJ = $(SRC:.c=.o)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// cppcheck-suppress missingIncludeSystem
|
||||
#include "looper.h"
|
||||
#include "channel.h"
|
||||
#include "log.h"
|
||||
#include "midi.h"
|
||||
#include "pipe.h"
|
||||
#include "queue.h"
|
||||
@@ -10,12 +11,14 @@
|
||||
#include <jack/midiport.h>
|
||||
#include <math.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
/* Global command queues (used by midi.c and pipe.c) */
|
||||
spsc_queue_t cmd_queue;
|
||||
@@ -27,15 +30,81 @@ spsc_queue_t cmd_queue_main_fifo;
|
||||
/* writer status fd */
|
||||
static int status_fd = -1;
|
||||
|
||||
static jack_client_t *global_client = NULL;
|
||||
|
||||
/* 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;
|
||||
|
||||
/* Track previous state to avoid writing unchanged status lines */
|
||||
static atomic_int prev_state[MAX_CHANNELS][MAX_SCENES];
|
||||
|
||||
/* Unregister all ports and close the JACK client */
|
||||
static void looper_cleanup(jack_client_t *client) {
|
||||
for (int c = 0; c < MAX_CHANNELS; c++) {
|
||||
if (channels[c].audio_in) {
|
||||
jack_port_unregister(client, channels[c].audio_in);
|
||||
channels[c].audio_in = NULL;
|
||||
}
|
||||
if (channels[c].audio_out) {
|
||||
jack_port_unregister(client, channels[c].audio_out);
|
||||
channels[c].audio_out = NULL;
|
||||
}
|
||||
if (channels[c].midi_in) {
|
||||
jack_port_unregister(client, channels[c].midi_in);
|
||||
channels[c].midi_in = NULL;
|
||||
}
|
||||
if (channels[c].midi_out) {
|
||||
jack_port_unregister(client, channels[c].midi_out);
|
||||
channels[c].midi_out = NULL;
|
||||
}
|
||||
}
|
||||
if (midi_control_port) {
|
||||
jack_port_unregister(client, midi_control_port);
|
||||
midi_control_port = NULL;
|
||||
}
|
||||
if (midi_clock_port) {
|
||||
jack_port_unregister(client, midi_clock_port);
|
||||
midi_clock_port = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Signal handler: deactivate and cleanup before exit */
|
||||
static void signal_handler(int sig) {
|
||||
(void)sig;
|
||||
if (global_client) {
|
||||
looper_cleanup(global_client);
|
||||
jack_client_close(global_client);
|
||||
}
|
||||
log_close();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void looper_write_status(void) {
|
||||
if (status_fd < 0)
|
||||
return;
|
||||
char buf[256];
|
||||
char buf[4096];
|
||||
int pos = 0;
|
||||
for (int ch = 0; ch < MAX_CHANNELS; ch++) {
|
||||
if (!atomic_load(&channels[ch].active))
|
||||
continue;
|
||||
int sc_idx = atomic_load(&channels[ch].current_scene);
|
||||
int state = atomic_load(&channels[ch].scenes[sc_idx].state);
|
||||
int prev = atomic_load(&prev_state[ch][sc_idx]);
|
||||
if (state == prev)
|
||||
continue; /* unchanged, skip */
|
||||
atomic_store(&prev_state[ch][sc_idx], state);
|
||||
|
||||
const char *state_str;
|
||||
switch (state) {
|
||||
case STATE_IDLE:
|
||||
@@ -53,29 +122,17 @@ static void looper_write_status(void) {
|
||||
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(status_fd, buf, n);
|
||||
(void)ret;
|
||||
}
|
||||
int n = snprintf(buf + pos, sizeof(buf) - pos,
|
||||
"CH=%d SC=%d STATE=%s\n", ch, sc_idx, state_str);
|
||||
if (n > 0) pos += n;
|
||||
if (pos >= (int)sizeof(buf) - 128) break;
|
||||
}
|
||||
if (pos > 0) {
|
||||
int ret = write(status_fd, buf, pos);
|
||||
(void)ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
@@ -465,16 +522,28 @@ int looper_init(jack_client_t *client) {
|
||||
/* store sample rate for writer thread */
|
||||
global_sample_rate = jack_get_sample_rate(client);
|
||||
|
||||
global_client = client;
|
||||
|
||||
/* Install signal handlers for graceful shutdown */
|
||||
signal(SIGINT, signal_handler);
|
||||
signal(SIGTERM, signal_handler);
|
||||
signal(SIGQUIT, signal_handler);
|
||||
|
||||
/* 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);
|
||||
status_fd = open(STATUS_FIFO, O_RDWR | O_NONBLOCK);
|
||||
if (status_fd < 0) {
|
||||
perror("open status FIFO");
|
||||
}
|
||||
|
||||
/* initialise prev_state to -1 */
|
||||
for (int ch = 0; ch < MAX_CHANNELS; ch++)
|
||||
for (int sc = 0; sc < MAX_SCENES; sc++)
|
||||
atomic_init(&prev_state[ch][sc], -1);
|
||||
|
||||
queue_init(&cmd_queue);
|
||||
queue_init(&cmd_queue_main_midi);
|
||||
queue_init(&cmd_queue_main_fifo);
|
||||
|
||||
Reference in New Issue
Block a user