From 5341cb676aa0e3c304f914e51cc0818ee43c070c Mon Sep 17 00:00:00 2001 From: Loic Coenen Date: Thu, 14 May 2026 14:56:11 +0000 Subject: [PATCH] feat: add status FIFO and parse status line in client --- client/makefile | 2 +- client/src/tui.c | 22 ++++++++++++++++++++++ engine/src/looper.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 1 deletion(-) diff --git a/client/makefile b/client/makefile index 3d03497..5eb60bf 100644 --- a/client/makefile +++ b/client/makefile @@ -4,7 +4,7 @@ CFLAGS = -Wall -Wextra -Wpedantic -std=c11 all: test_status_parse test_status_parse: tests/test_status_parse.c - $(CC) $(CFLAGS) -I../src -o test_status_parse tests/test_status_parse.c ../src/tui.c -lncurses + $(CC) $(CFLAGS) -Isrc -o test_status_parse tests/test_status_parse.c src/tui.c -lncurses test: test_status_parse ./test_status_parse diff --git a/client/src/tui.c b/client/src/tui.c index 28d4214..cef5bc3 100644 --- a/client/src/tui.c +++ b/client/src/tui.c @@ -63,6 +63,28 @@ typedef struct { } FuzzySearch; static FuzzySearch fuzzy_search = {0}; +/* ---------- Parse status line from engine status FIFO ---------- */ +typedef enum { STATE_IDLE, STATE_RECORD, STATE_LOOPING, STATE_PAUSED } ChannelState; + +bool parse_status_line(const char *line, int *ch, int *scene, ChannelState *state) { + int sta; + if (sscanf(line, "CH=%d SC=%d STATE=%d", ch, scene, &sta) == 3) { + if (sta >= 0 && sta <= 3) { + *state = (ChannelState)sta; + return true; + } + } + /* try text-based format */ + char state_str[32]; + if (sscanf(line, "CH=%d SC=%d STATE=%31s", ch, scene, state_str) != 3) + return false; + if (strcmp(state_str, "IDLE") == 0) { *state = STATE_IDLE; return true; } + if (strcmp(state_str, "RECORD") == 0) { *state = STATE_RECORD; return true; } + if (strcmp(state_str, "LOOPING") == 0) { *state = STATE_LOOPING; return true; } + if (strcmp(state_str, "PAUSED") == 0) { *state = STATE_PAUSED; return true; } + return false; +} + /* ---------- State to color (dummy: all white) ---------- */ static int state_to_color(ClipState s) { (void)s; return COLOR_EMPTY; } diff --git a/engine/src/looper.c b/engine/src/looper.c index 0ce3d66..8ce993c 100644 --- a/engine/src/looper.c +++ b/engine/src/looper.c @@ -5,6 +5,9 @@ #include "midi.h" #include "queue.h" #include +#include +#include +#include #include #include #include @@ -12,6 +15,39 @@ #include #include +#define STATUS_FIFO "/tmp/looper_status" + +static void looper_write_status(void) { + 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); +} + /* Global state (shared across files) */ struct channel_t *_Atomic channels = NULL; atomic_int channel_capacity = 0; @@ -393,6 +429,9 @@ void jack_shutdown_cb(void *arg) { * looper initialisation * ---------------------------------------------------------------- */ int looper_init(jack_client_t *client) { + /* create status FIFO (ignore if already exists) */ + mkfifo(STATUS_FIFO, 0666); + queue_init(&cmd_queue); queue_init(&cmd_queue_main_midi); queue_init(&cmd_queue_main_fifo); @@ -631,4 +670,7 @@ void looper_process_commands(jack_client_t *client) { pending_old = NULL; } } + + /* write current state to status FIFO */ + looper_write_status(); }