feat: add logging system, orchestrator, and documentation

This commit is contained in:
Loic Coenen
2026-06-06 12:48:58 +00:00
committed by Loic Coenen (aider)
16 changed files with 739 additions and 30 deletions

View File

@@ -2,7 +2,7 @@ CC ?= gcc
CFLAGS ?= -Wall -Wextra -g -Isrc
LDFLAGS ?= -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 = 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)
looper: $(OBJ)

32
engine/src/log.c Normal file
View File

@@ -0,0 +1,32 @@
#include "log.h"
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <pthread.h>
static FILE *logfile = NULL;
static pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;
void log_init(void) {
logfile = fopen("/tmp/looper.log", "a");
if (!logfile)
logfile = stderr;
setbuf(logfile, NULL);
}
void log_msg(const char *fmt, ...) {
if (!logfile) return;
pthread_mutex_lock(&log_mutex);
va_list args;
va_start(args, fmt);
vfprintf(logfile, fmt, args);
va_end(args);
fputc('\n', logfile);
pthread_mutex_unlock(&log_mutex);
}
void log_close(void) {
if (logfile && logfile != stderr)
fclose(logfile);
logfile = NULL;
}

8
engine/src/log.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef LOG_H
#define LOG_H
void log_init(void);
void log_msg(const char *fmt, ...);
void log_close(void);
#endif

View File

@@ -85,7 +85,7 @@ 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)
if (ch < 0 || ch >= MAX_CHANNELS)
ch = 0;
switch (cmd.type) {
@@ -570,8 +570,10 @@ void looper_process_commands(jack_client_t *client) {
wav_write("save.wav", data, (unsigned)lc, sr);
free(data);
}
/* Reactivate channel */
/* Reactivate channel use a shorter sleep to reduce xrun risk */
if (was_active) {
struct timespec req = {.tv_sec = 0, .tv_nsec = 200000000}; /* 200 ms */
nanosleep(&req, NULL);
atomic_store(&channels[0].active, 1);
}
}

View File

@@ -1,5 +1,6 @@
// cppcheck-suppress missingIncludeSystem
#include "looper.h"
#include "log.h"
#include <jack/jack.h>
#include <stdio.h>
#include <stdlib.h>
@@ -9,15 +10,20 @@
int main(int argc, char *argv[]) {
(void)argc;
(void)argv;
log_init();
log_msg("looper engine starting");
const char *client_name = "looper";
jack_options_t options = JackNullOption;
jack_status_t status;
jack_client_t *client = jack_client_open(client_name, options, &status);
if (client == NULL) {
fprintf(stderr, "jack_client_open() failed, status = 0x%2.0x\n", status);
log_msg("jack_client_open() failed, status = 0x%2.0x", status);
if (status & JackServerFailed)
fprintf(stderr, "Unable to connect to JACK server\n");
log_msg("Unable to connect to JACK server");
log_close();
return 1;
}
@@ -28,27 +34,30 @@ int main(int argc, char *argv[]) {
jack_on_shutdown(client, jack_shutdown_cb, NULL);
if (looper_init(client) != 0) {
fprintf(stderr, "looper initialisation failed\n");
log_msg("looper initialisation failed");
jack_client_close(client);
log_close();
return 1;
}
if (jack_activate(client)) {
fprintf(stderr, "Cannot activate client\n");
log_msg("Cannot activate client");
jack_client_close(client);
log_close();
return 1;
}
fprintf(stderr, "looper running (client name '%s')\n", client_name);
log_msg("looper running (client name '%s')", client_name);
while (1) {
looper_process_commands(client);
{
struct timespec ts = {.tv_sec = 0, .tv_nsec = 50000000};
nanosleep(&ts, NULL);
} /* check commands every 50 ms */
}
}
jack_client_close(client);
log_close();
return 0;
}