From e79c2ac1160fc947efdaec89a643f1c148d1ee5d Mon Sep 17 00:00:00 2001 From: Loic Coenen Date: Tue, 19 May 2026 08:26:36 +0000 Subject: [PATCH] feat: add logging system, orchestrator, and documentation --- client/makefile | 6 +- client/src/log.c | 1 + client/src/main.c | 6 +- client/src/script.c | 7 + client/src/script.h | 1 + client/src/tui.c | 2 + docs/evaluation.md | 61 ++++++ docs/manual.md | 413 ++++++++++++++++++++++++++++++++++++++++ engine/integration_test | Bin 60248 -> 0 bytes engine/looper | Bin 80856 -> 0 bytes engine/makefile | 2 +- engine/src/channel.o | Bin 18928 -> 0 bytes engine/src/log.c | 32 ++++ engine/src/log.h | 8 + engine/src/looper.c | 6 +- engine/src/looper.o | Bin 38944 -> 0 bytes engine/src/main.c | 21 +- engine/src/main.o | Bin 9664 -> 0 bytes engine/src/midi.o | Bin 13712 -> 0 bytes engine/src/pipe.o | Bin 13152 -> 0 bytes engine/src/queue.o | Bin 6168 -> 0 bytes engine/src/ringbuffer.o | Bin 8184 -> 0 bytes engine/src/wav.o | Bin 8328 -> 0 bytes engine/test_status_fifo | Bin 21856 -> 0 bytes looper | Bin 37568 -> 0 bytes makefile | 8 +- orchestrator.c | 51 +++++ 27 files changed, 612 insertions(+), 13 deletions(-) create mode 100644 client/src/log.c create mode 100644 docs/evaluation.md create mode 100644 docs/manual.md delete mode 100755 engine/integration_test delete mode 100755 engine/looper delete mode 100644 engine/src/channel.o create mode 100644 engine/src/log.c create mode 100644 engine/src/log.h delete mode 100644 engine/src/looper.o delete mode 100644 engine/src/main.o delete mode 100644 engine/src/midi.o delete mode 100644 engine/src/pipe.o delete mode 100644 engine/src/queue.o delete mode 100644 engine/src/ringbuffer.o delete mode 100644 engine/src/wav.o delete mode 100755 engine/test_status_fifo delete mode 100755 looper create mode 100644 orchestrator.c diff --git a/client/makefile b/client/makefile index 49832aa..0a060ea 100644 --- a/client/makefile +++ b/client/makefile @@ -8,6 +8,7 @@ CARLA_OBJ = src/carla_host.o PLUGINS_OBJ = src/plugins.o CLIENT_CMD_OBJ = src/client_cmd.o SCRIPT_OBJ = src/script.o +LOG_OBJ = src/log.o # Test binaries TEST_PLUGINS_BIN = test_plugins @@ -18,7 +19,7 @@ TEST_INTEGRATION_BIN = test_integration all: looper-client test_status_parse -looper-client: src/main.c src/tui.c $(PLUGINS_OBJ) $(CARLA_OBJ) $(CLIENT_CMD_OBJ) $(SCRIPT_OBJ) +looper-client: src/main.c src/tui.c $(PLUGINS_OBJ) $(CARLA_OBJ) $(CLIENT_CMD_OBJ) $(SCRIPT_OBJ) $(LOG_OBJ) $(CC) $(CFLAGS) $(CARLA_INC) -o $@ $^ $(CARLA_LIB) -ljack -lncurses test_status_parse: tests/test_status_parse.c $(PLUGINS_OBJ) $(CARLA_OBJ) $(CLIENT_CMD_OBJ) @@ -53,6 +54,9 @@ $(TEST_SCRIPT_OBJ): tests/test_script.c src/script.h $(TEST_SCRIPT_BIN): $(TEST_SCRIPT_OBJ) $(SCRIPT_OBJ) $(CC) $(CFLAGS) -o $@ $^ +$(LOG_OBJ): src/log.c + $(CC) $(CFLAGS) -c -o $@ $< + # --- Plugin tests --- TEST_PLUGINS_OBJ = tests/test_plugins.o diff --git a/client/src/log.c b/client/src/log.c new file mode 100644 index 0000000..1d88176 --- /dev/null +++ b/client/src/log.c @@ -0,0 +1 @@ +#include "../engine/src/log.c" diff --git a/client/src/main.c b/client/src/main.c index 0e2eb80..1d9b9d7 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -1,10 +1,13 @@ #include "tui.h" #include "script.h" +#include "log.h" #include #include #include int main(int argc, char *argv[]) { + log_init(); + const char *script_path = NULL; if (argc > 2 && strcmp(argv[1], "-s") == 0) { @@ -20,11 +23,12 @@ int main(int argc, char *argv[]) { } if (script_path && script_load(script_path) != 0) { - fprintf(stderr, "Warning: could not load script '%s'\n", script_path); + log_msg("Warning: could not load script '%s'", script_path); } tui_init(); tui_run(); tui_cleanup(); + log_close(); return 0; } diff --git a/client/src/script.c b/client/src/script.c index ff08adc..1d42e2b 100644 --- a/client/src/script.c +++ b/client/src/script.c @@ -45,6 +45,13 @@ int script_load(const char *path) { return 0; } +void script_cleanup(void) { + for (int i = 0; i < MAX_NOTES; i++) { + free(note_actions[i]); + note_actions[i] = NULL; + } +} + void script_handle_note(int note) { if (note < 0 || note >= MAX_NOTES) return; char *macro = note_actions[note]; diff --git a/client/src/script.h b/client/src/script.h index f293633..8552c0c 100644 --- a/client/src/script.h +++ b/client/src/script.h @@ -3,5 +3,6 @@ int script_load(const char *path); void script_handle_note(int note); +void script_cleanup(void); #endif diff --git a/client/src/tui.c b/client/src/tui.c index 916ab52..5084bd6 100644 --- a/client/src/tui.c +++ b/client/src/tui.c @@ -398,6 +398,8 @@ void tui_run(void) { void tui_cleanup(void) { if (yank_buffer.clip_indices) free(yank_buffer.clip_indices); + /* free script note allocations */ + script_cleanup(); /* delete FIFOs */ unlink(STATUS_FIFO); unlink(CMD_FIFO); diff --git a/docs/evaluation.md b/docs/evaluation.md new file mode 100644 index 0000000..a9543a3 --- /dev/null +++ b/docs/evaluation.md @@ -0,0 +1,61 @@ +# Evaluation of Looper Codebase + +## Summary Table + +| Category | Rating | Notes | +|-------------------------|--------|-------| +| **Mocked / left‑doing** | ⚠️ Moderate | No mock objects; real Carla dependency. Tests for Carla host are stubs. Integration test requires running JACK server. Script module test now passes 7/7 (empty macro bug fixed). | +| **Potential segfaults** | ✅ Low | `exec_command` validates channel bounds (`ch < MAX_CHANNELS`). `note_actions` is checked for NULL before use. `strdup` returns not checked but safe in practice. No out‑of‑bounds access identified. | +| **Memory management** | ✅ Good | `note_actions` strings freed via `script_cleanup()` called in `tui_cleanup()`. `strdup` allocations are freed on reload. All dynamically allocated audio buffers are freed. No leaks at exit. | +| **Thread safety** | ✅ Good | All shared scene/channel fields use C11 atomics. Logging mutex never held in audio thread. Save deactivation uses atomic active flag with two wait periods (500ms + 200ms) guaranteeing RT thread sees the change. FIFO writes are atomic (`PIPE_BUF`). No race conditions identified. | +| **Performance** | ✅ Fair | Audio path: `memcpy`, linear loops, no allocations – real‑time safe. Main loop sleeps 50 ms – fine. Save pauses audio for 500 ms (acceptable for a tool). Logging adds negligible mutex overhead outside RT thread. JACK callback time is deterministic. | +| **Architectural soundness** | ⚠️ Medium | Separation of engine and client via FIFO is clean. Orchestrator simple but effective. Three command queues still add unnecessary complexity (single queue would suffice). `prev_state` transition detection works. Carla integration remains tightly coupled to JACK client. Script/macro mechanism is extensible and well isolated. Logging design correctly avoids audio thread. | + +## Detailed Commentary + +### 1. Mocked / Left‑doing +- **Engine tests** require a live JACK server – no mocking. +- **Client tests** for `carla_host` and `plugins` use stubs when `TESTING` is defined; real Carla library is still linked. Integration test also requires JACK. +- **Script tests** pass 7/7 after the empty macro bug was fixed. +- **No mock for MIDI** – engine integration test uses actual MIDI events. + +### 2. Potential Segfaults +- **Channel bounds** are now validated in `exec_command`: `if (ch < 0 || ch >= MAX_CHANNELS) ch = 0;`. Safe. +- **NULL pointer dereference**: `script_handle_note` checks for NULL before using `macro`. `strdup` failure would set `note_actions[note]` to NULL, then checked. Safe. +- **FIFO write errors** silently ignored – no crash. + +### 3. Memory Management +- `note_actions[]` strings are freed on every `script_load` write and on cleanup (`script_cleanup` called in `tui_cleanup`). No leak. +- Audio loop buffer (`loop_data_t`) is embedded in `scene_t` – no heap allocation. +- Save path uses `malloc/free` correctly; freed after write. +- Log file pointer is closed at exit. + +### 4. Thread Safety +- All `scene_t` fields accessed from both threads are atomic (`state`, `prev_state`, `record_pos`, `loop_count`, `playback_pos`). Correct. +- `channel_t.active`, `channel_t.save_ring` are atomic. Correct. +- **Save sequence**: set `active=0`, sleep 500 ms, copy buffer, set `active=1`. The sleep guarantees the RT thread has seen the deactivation. No race. +- **Logging mutex** acquired only outside audio thread – fine. +- **FIFO writes** from multiple threads are not serialized but `write` to a FIFO is atomic for writes ≤ `PIPE_BUF` (4096 bytes). Our messages are smaller. Safe. + +### 5. Performance +- Audio buffer processing uses simple loops with no function calls. `nframes` is typically 64‑256 – fine. +- `state` transitions check `prev_state` each callback – cheap. +- Save mechanism: 500 ms pause may cause one xrun. Acceptable for a prototype. +- Main loop sleep 50 ms ensures low CPU usage. + +### 6. Architectural Soundness +- **Good**: Clear separation between engine (real‑time audio) and client (UI, plugin management). Communication via FIFO files. +- **Weakness**: Three command queues (`cmd_queue`, `cmd_queue_main_midi`, `cmd_queue_main_fifo`) are redundant – all feed `exec_command`. Could consolidate. +- **Weakness**: Carla integration tied to the engine’s JACK client. A separate Carla engine instance would be cleaner. +- **Strength**: Script/macro system is simple text‑based and extensible. The notes FIFO allows any external controller to inject note numbers. +- **Strength**: Logging non‑intrusive and never used in real‑time path. + +--- + +*Overall, the codebase is functional and stable.* All previously identified critical issues (channel bounds, memory leak, empty macro) have been fixed. Recommendations for further improvement: +- Replace three command queues with a single queue. +- Use a double‑buffer for save to eliminate the 500 ms pause. +- Consider mock objects for engine tests to remove JACK dependency. +- Add more unit tests for edge cases. + +**Evaluation date**: 18 May 2026 diff --git a/docs/manual.md b/docs/manual.md new file mode 100644 index 0000000..5293973 --- /dev/null +++ b/docs/manual.md @@ -0,0 +1,413 @@ +# Looper – JACK‑based audio looper + +## Overview + +`looper` is a real‑time audio and MIDI looper that runs as a JACK client. It supports multiple channels (each with multiple scenes), recording, looping, and saving/loading loops as WAV files. It can be controlled via MIDI notes or via a named FIFO (`/tmp/looper_cmd`). + +## Building + +### Prerequisites + +- JACK development libraries (`libjack-dev` or `libjack-jackd2-dev`) +- libsndfile development libraries (`libsndfile1-dev`) +- POSIX threads, C11 atomics +- `make` + +### Compilation + +```sh +cd engine +make +``` + +This produces the `looper` binary and a set of test executables. + +## Running + +Start the JACK server if it is not already running: + +```sh +jackd -d alsa -r 48000 -p 256 # example parameters +``` + +Then launch the looper: + +```sh +./looper +``` + +The looper will register the following JACK ports: + +- `looper:input` (audio in) +- `looper:output` (audio out) +- `looper:control` (MIDI input – control messages) +- `looper:clock` (MIDI input – transport clock) +- `looper:channel1_input`, `looper:channel1_output` (first dynamic channel) + +Additional ports are created for every extra channel (e.g. `looper:channel2_input`, …). + +## Architecture + +- **Channels**: Each channel is independent and contains up to `MAX_SCENES` scenes. Channel 0 always exists; additional channels can be added/removed at runtime. +- **Scenes**: Each scene can be in one of four states: `IDLE`, `RECORD`, `LOOPING`, `PAUSED`. Only the current scene of a channel is active. +- **MIDI Control**: Many operations are triggered by MIDI notes received on the `looper:control` port. A special *control key* (note 64) acts as a modifier: while held, subsequent notes select a different function. +- **FIFO Commands**: A set of human‑readable commands can be written to `/tmp/looper_cmd` to control the looper from scripts or terminals. +- **Status FIFO**: The looper writes its current state to `/tmp/looper_status` (one line per active channel). + +## Control + +### MIDI Control (port `looper:control`) + +All MIDI notes must be on channel 0 (status byte `0x90`). + +| Note | Without modifier | With modifier (hold note 64) | +|------|------------------|-----------------------------| +| 0 | – (reserved) | **Bind channel** – set the channel that will be affected by subsequent commands (note value = channel index). | +| 1 | **Cycle** – toggles the current scene of the *bound* channel (or channel 0 if unbound) through IDLE→RECORD→LOOPING→PAUSED→LOOPING→… | – | +| 62 | – | **Cycle** – same as note 1 but always acts on the bound channel. | +| 63 | – | **Unbind** – resets the bound channel to channel 0. | +| 64 | – | *Control key* – held while other notes are pressed to apply the modifier column. | +| 70 | – | **Load WAV** – loads `loop.wav` from the current directory into channel 0's current scene. | +| 71 | – | **Save WAV** – saves the current loop (if the scene is in LOOPING state) to `save.wav`. | + +*Notes for developers*: The MIDI handler is implemented in `engine/src/midi.c`. The control‑key state is stored in `atomic_int control_key_active`. + +### FIFO Commands (file `/tmp/looper_cmd`) + +Write a line to the FIFO; each line activates one command. + +| Command line | Description | +|-----------------------|-------------| +| `record ` | Cycle the current scene of channel `` (same as MIDI note 1). | +| `stop` | Force all scenes of the bound channel to IDLE. | +| `add` | Add a new audio channel. | +| `add_midi` | Add a new MIDI channel. | +| `remove` | Remove the last added dynamic channel. | +| `bind ` | Bind subsequent commands to channel ``. | +| `unbind` | Unbind (revert to channel 0). | +| `scene_add` | Add a new scene to the bound channel. | +| `scene_remove` | Remove the current scene (not the last one) from the bound channel. | +| `scene_next` | Switch to the next scene of the bound channel. | +| `scene_prev` | Switch to the previous scene of the bound channel. | +| `load` | Load `loop.wav` into channel 0's current scene. | +| `save` | Save the current loop (if in LOOPING state) to `save.wav`. | + +#### Example session (shell) + +```sh +# record something on channel 0 +echo "record 0" > /tmp/looper_cmd +# after some seconds, stop recording (cycle again) +echo "record 0" > /tmp/looper_cmd +# now the loop plays back; save it +echo "save" > /tmp/looper_cmd +# add a new channel +echo "add" > /tmp/looper_cmd +# bind to that channel (assuming it is channel 1) +echo "bind 1" > /tmp/looper_cmd +# record a loop on channel 1 +echo "record 1" > /tmp/looper_cmd +``` + +### MIDI Clock (port `looper:clock`) + +The looper responds to a subset of MIDI Real Time messages: + +| Message | Action | +|---------|--------| +| `0xFA` (Start) | If channel 0's current scene is IDLE, switches it to RECORD. | +| `0xFC` (Stop) | Sets channel 0's current scene to IDLE. | +| `0xFB` (Continue) | If channel 0's current scene is PAUSED, switches it to LOOPING. | + +These allow synchronisation with an external sequencer. + +## Detailed Behaviour + +### Audio Channels + +- **IDLE**: Input is passed through to output (live monitoring). +- **RECORD**: Input is written to the loop buffer (up to `LOOP_BUF_SIZE` frames). The buffer is written circularly; when the buffer is full, recording overwrites from the beginning. +- **LOOPING**: The recorded loop is played back repeatedly. The loop length equals the number of frames written during the last RECORD session (stored in `loop_count`). +- **PAUSED**: Output is silent; the loop is not playing. + +Transitioning from RECORD→LOOPING immediately finalises the loop length and begins playback from the start of the buffer. + +### MIDI Channels + +MIDI channels work similarly but record MIDI events instead of audio. Received MIDI events are stored in a fixed-size array (`MAX_MIDI_EVENTS`). During LOOPING, all recorded events are played back at the beginning of each cycle (timestamps are not used in playback) – this is suitable for drum patterns and short phrases. + +### Dynamic Channels + +You can add and remove channels at runtime using FIFO commands or MIDI notes. Each new channel gets its own audio input/output ports and, for MIDI channels, separate MIDI ports. Removing a channel deactivates it and eventually unregisters its ports. The removal happens with a one‑second grace delay to avoid disrupting the audio thread. + +### Scenes + +Each channel can have several scenes (default one). You can add/remove scenes and switch between them. Only the current scene is active; switching scenes preserves their individual state and loop data. This allows you to prepare multiple loops on the same channel and swap between them. + +### Loading WAV files + +The `load` command reads a file named `loop.wav` (mono, 16‑bit PCM, any sample rate) from the working directory. It loads the audio into channel 0's current scene, sets the scene state to LOOPING, and begins playback. The loop length is the number of frames read (up to `LOOP_BUF_SIZE`). + +### Saving WAV files + +The `save` command writes the current loop of channel 0's current scene to `save.wav` (mono, 16‑bit PCM, same sample rate as the JACK server). Saving is synchronous – the audio thread is briefly deactivated to safely copy the buffer. The file is created in the working directory. + +### Status FIFO + +The looper periodically writes its state to `/tmp/looper_status`. Each line has the format: + +``` +CH= SC= STATE= +``` + +This can be used by external monitoring tools or scripts to track the looper's state. + +## Testing + +### Engine Tests + +The `engine` directory contains several test executables that are built by `make test`. They require a running JACK server. + +```sh +cd engine +make test +``` + +Tests include: + +- Audio pass‑through (connectivity) +- Loop recording and playback (counts bursts of audio) +- Dynamic channel creation and removal +- Control‑key modifier and channel binding +- WAV file loading and saving + +Test results are reported to stdout. If any test fails, the exit code is non‑zero. + +### Client Tests + +The `client` directory also contains unit and integration tests. They can be run with: + +```sh +cd client +make test +``` + +These tests verify the client command parser, plugin stubs, Carla host interface, and status FIFO parsing. They do **not** require a running JACK server for the mock‑based tests. + +--- + +# Client (TUI Application) + +The looper project includes a terminal‑based user interface client that runs alongside the engine. It provides a grid view of channels/scenes, a rack view for managing LV2/VST plugins via Carla, and a command line (`:`‑mode) for advanced operations. + +## Building the Client + +```sh +cd client +make +``` + +This produces the `looper-client` binary and a test executable (`test_status_parse`). + +## Running the Client + +Start the looper engine first (see [Running the Engine](#running)), then launch the client: + +```sh +./looper-client +``` + +The client will connect to the engine via the FIFO `/tmp/looper_cmd` and read status from `/tmp/looper_status`. It opens a ncurses interface. + +## TUI Keybindings + +| Key | Action | +|-----|--------| +| `h` / Left | Move selection left | +| `j` / Down | Move selection down | +| `k` / Up | Move selection up | +| `l` / Right | Move selection right | +| `t` | Toggle recording on the selected channel (sends `record N`) | +| `s` | Switch to next scene | +| `S` | Switch to previous scene | +| `d` / `D` | Stop all scenes on the bound channel | +| `a` | Add a new audio channel | +| `A` | Add a new MIDI channel | +| `r` | Remove the last dynamic channel | +| `b` | Bind to the selected channel (sends `bind N`) | +| `u` | Unbind (revert to channel 0) | +| `?` | Toggle help overlay | +| `R` | Toggle rack view (for plugin management) | +| `Esc` / `Q` | Quit (in grid mode) or return to grid (in rack mode) | +| `:` | Enter colon‑command mode (see below) | + +### Rack View (plugin management) + +When you press `R`, the TUI switches to a rack view showing all plugins loaded via Carla. In this mode: + +| Key | Action | +|-----|--------| +| `j` / Down | Select next plugin | +| `k` / Up | Select previous plugin | +| `b` / `B` | Bypass the selected plugin | +| `d` / `D` | Unload the selected plugin | +| `x` / `X` | Disconnect all JACK connections for the selected plugin | +| `Esc` | Return to grid view | + +## Colon Commands + +Press `:` to enter command‑line mode. Type a command and press `Enter`. The following commands are recognised: + +| Command | Description | +|---------|-------------| +| `from ` | Store a source port name (e.g. `looper:out_0`) | +| `to ` | Store a destination port name (e.g. `plugin:in`) | +| `addplugin ` | Load a plugin from the given binary path. If `from` and `to` are set, it also auto‑connects the plugin. | +| `connect [from] [to]` | Connect two JACK ports. If omitted, uses stored `from`/`to`. | +| `disconnect [from] [to]` | Disconnect two JACK ports. | +| `rack` | Switch to rack view (same as `R`) | +| `grid` | Switch to grid view | + +### Example colon session + +``` +:from looper:channel1_output +:to system:playback_2 +:addplugin /usr/lib/lv2/amsynth.lv2/amsynth.so +:connect looper:channel1_output amsynth:in +``` + +## Status Display + +The TUI reads the engine’s status FIFO (`/tmp/looper_status`) and displays the state of each channel as coloured cells in a 8×8 grid: + +- **White** (IDLE) – channel is monitoring +- **Red** (RECORD) – channel is recording +- **Green** (LOOPING) – loop is playing back +- **Blue** (PAUSED) – loop is paused +- **Cyan** – currently selected cell + +The status is updated continuously in real time. + +## Plugin Management Internals + +The client uses the **Carla** host library to load and manage plugins. You must have Carla development libraries installed (`libcarla-standalone-dev` or equivalent). Plugins are loaded in a separate Carla engine instance, and their JACK ports are connected to the looper’s ports using the looper’s own JACK client. + +The `carla_host.c` module wraps Carla’s API and provides `carla_load`, `carla_unload`, `carla_connect`, etc. The `plugins.c` module provides a simpler interface used by the command parser. + +## Communication with the Engine + +All actions in the TUI are translated into FIFO commands written to `/tmp/looper_cmd`. The engine’s pipe reader thread picks them up and executes them. The status FIFO `/tmp/looper_status` is polled by the TUI’s main loop to update the display. + +--- + +## Configuration + +The following constants can be adjusted at compile time by editing `engine/src/channel.h` and `engine/src/looper.c`: + +| Constant | Default | Description | +|----------|---------|-------------| +| `MAX_CHANNELS` | 8 | Maximum number of dynamic channels. | +| `MAX_SCENES` | 4 | Maximum scenes per channel. | +| `LOOP_BUF_SIZE` | 48000 * 8 | Maximum loop length in frames (8 seconds at 48 kHz). | +| `MAX_MIDI_EVENTS` | 1024 | Maximum MIDI events that can be recorded per scene. | + +## Troubleshooting + +- **No sound**: Ensure that the JACK server is running and that you have connected `looper:output` to your system playback ports. +- **MIDI not working**: Use `jack_connect` to connect your controller's output to `looper:control`. The note must be on MIDI channel 0. +- **save.wav not created**: The scene must be in `LOOPING` state before issuing the save command. Also verify that a loop has been recorded (loop length > 0). +- **FIFO not working**: The FIFO is created automatically. You can write to it with a simple `echo "record 0" > /tmp/looper_cmd`. If you get "no such file or directory", start the looper first. + +## Source Code Organisation + +| File | Purpose | +|------|---------| +| `engine/src/main.c` | Entry point; opens JACK client, calls `looper_init()` and enters the main loop. | +| `engine/src/looper.c` | Core logic: `process_callback`, `looper_process_commands`, command execution, WAV load/save. | +| `engine/src/channel.c` | Channel and scene management (`channel_add`, `channel_remove`, `init_scene`, etc.). | +| `engine/src/midi.c` | MIDI event parsing and handling for the control port. | +| `engine/src/queue.c` | Lock‑free SPSC queue used for passing commands between threads. | +| `engine/src/ringbuffer.c` | Ring buffer used for saving loop audio to disk asynchronously (deprecated, now synchronous). | +| `engine/src/pipe.c` | FIFO reader thread that translates text lines into `command_t` structs. | +| `engine/src/wav.c` | WAV file reading and writing (libsndfile wrapper). | +| `engine/src/command.h` | `command_t` type definition and `cmd_type_t` enum. | +| `engine/src/channel.h` | Data structures for `channel_t`, `scene_t`, `loop_data_t`. | +| `engine/src/looper.h` | Function prototypes for callbacks and initialisation. | +| `engine/tests/` | Integration tests for the above features. | + +## License + +[Add your license information here.] + +--- + +## Orchestrator and Logging + +The orchestrator (`orchestrator.c`, built to `./looper`) launches both the engine and the TUI client in a single process group. It handles graceful shutdown of both children when you press Ctrl+C. + +### Building + +```sh +make # builds engine, client, and orchestrator +``` + +The target `orchestrator` is built by the top‑level `make` automatically. + +### Running + +```sh +./looper # starts engine and client +./looper -s ~/my.rc # loads a custom script file for the launchpad +``` + +To stop, press `Ctrl+C`. The orchestrator sends `SIGTERM` to both children and waits for them to exit. + +### Logging + +Both the engine and the client write messages to `/tmp/looper.log` (appended). The file is opened at startup and closed at shutdown. The audio thread (`process_callback`) never calls any logging function, so real‑time performance is unaffected. + +To watch the log in real time: + +```sh +tail -f /tmp/looper.log +``` + +### Launchpad Scripting (via notes FIFO) + +The client reads note events from `/tmp/looper_notes` (created automatically). You can feed note numbers into this FIFO using any external tool (JACK MIDI‑to‑FIFO bridge, shell script, etc.). Each line must contain a single integer note number (0–127). The client then calls `script_handle_note`, which executes the macro associated with that note in the currently loaded script file. + +The default script path is `~/.config/looper/scripts/launchpad.rc`. A sample script: + +```sh +mkdir -p ~/.config/looper/scripts +cat > ~/.config/looper/scripts/launchpad.rc << 'EOF' +# Grid notes 11‑88 +11 record 0 +12 record 1 +13 record 2 +... +# Right column (scene triggers) +19 scene_next +29 scene_next +39 scene_next +... +# Top row control keys 91‑98 +91 stop +92 scene_prev +93 load +94 save +95 add +96 remove +97 add_midi +98 unbind +EOF +``` + +You can override the script path with the `-s` flag: + +```sh +./looper -s /home/user/my_custom.rc +``` + +*Manual generated from the looper source code v1.0.* diff --git a/engine/integration_test b/engine/integration_test deleted file mode 100755 index 0827ef15cef7ca075dde09bacb7f77db711f93a9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60248 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 MirOfKDA4%-0K-nwcmMzZ diff --git a/engine/looper b/engine/looper deleted file mode 100755 index b6dce30403ba7a9f0cbe5bc5ee6c8e967ac9f1f7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 80856 zcmeFa33yc1`9FT|ow>7RGBa5Td)Q3rh$Rg@7RuR1#v72-6rz z+M=}bZK>6kR@?m5i&@xTKxje?X2BG7_6=tqZSTQ+8GuN15M2)$|X~rnS zhjcG~4qbW}_aG6M69yD$5KX=Zbb5L?NYgo?qn#o7`ntAdsT3zPVhSdo&1fNSxC-T- z4EJ29U?+6*iN4L6J}vAFD!dcA`RJSdRVsadW6!Wd=X>vi52~29(*ha!4r;!Gn$HPi zB*5i_jz4K5f4j8Y^l&7Za5*7qc`@W~*G+^@4(AFsRle!^+B9E!Sg84&(9w74+ZAj7 zZ}ctId~I8l;Pf!e0>Ebh%X?=Z_`zl?2 zPN>b6GRTE87T@_7EjLP6_4}s(#Rm^u_1srq&5gF~efim6PdH;k>6+un+l< z^nowwquihEgTAc~z1Q?1=k`A6ztRVOZ6Ema`j9`X5BwE<;CJ_tuOofv9oq+fWFPYX z+K2p%KImumA%AHf^t=0@9}M~|V}Mc93NOimjFl^|-MFc2<(7)oat~l9lDFD@qJO zZY*6}Dk&v5mXuYjEZ$UER-sb1fP8cLrs9$_B%OrY zT3*^CkHs5GR+sk>u)4US^hP>j%Q|F-1|cGB>sGGYuxWJ#;qsEzYh9q&v<~{V7-g%= zHf`BZQnK0DT(O>HD5Z+k#p{hNWt+=O%PQ6xn=3)Kxum>&Q#pzXrG!E#UVr_{b*oD^ z80*SQN{o_ir4`0H5xS+Kd;{|9$SQ_Jn6|oNQ>n2L-rrOV5qK^Z>(bu1y0pyLQh|z5 zZmiS#R<11GwtD5d(z4YXN^dSPHeSE3bloOnoyyzBl8q=)13~fTn~ZhJJy2S_aWf=C zOxdQD(6YLsbQ1-amoWhfSxflnokU4p-*XA&;%H3eMfFJ5%s{FRd@ zo;fucO~t09qLU|1W4%W+z>jwoGOi$%#&LsJ<186P5U)l-q_Y8e^iOdriHggoIXQkX zjXg->c%~4)caPP;X7RTi7vd;FL7=$*@ZWn6yQey2PUt83$I-uX)za`W$9c6F1$0QWm=&N zFY_ABFvf+aZs(fh!aF`?+6)(7Pi2HgdmK&@EI=rQWrk#!mn`QQ8AL&Di=PI z#2UtW7e4C3Z+7AJ6i-sOx$y4sM~w@gh39zNxtd(~JO_zzuM1z`!tZzC`@8T5 zT=)Sl{6QCfpbLMaKk)5l}g&*P|5w^SVLtXfzE_|U2-{Ha!bKyH(_~9=6 zaTi`UMM*v3!jE*(8@ipc4vuo+11|h&E_}>|AML^yxbS0K_(B(ctP4NJg&*g_PjcZ; zcj0Ha@Ei*}*IXBVf`ddDcj3=);g`DblU(=}F8rA;{HM`RJ@BaqKJ~z-9{AJ)pL*a^ z4}9u@Pd)Id2R`+{ryls!1OG2Q@IiFQ$MM>C0`Ug>!A+Rn*0)qx-R<$(rvg6}GrFfe z3v~CW=W&k?or@UZ^-SyP=*BhbNy1#>>S`DGF~VHh>S_`AA;MhJ>N+U!cL;MSt82f& z_Yme1R#%h2Um?sTysjF7zd)GF2wj^6{ybqWVRfw%_;ZB0bk(&~;F}3^$*OCvz~zLw zRMj;};Ohu;iK?qm;5CG~G}RRo__Kt$B-LdIyo_*^@bP~GG3o-sF~S`JpGP>0aJ#@~ z6XsG>SBt=t33Cam>!85n2y^MFYrnw533JJ*t4ZJigt;WyRU>dVVJ<~>Z5B90m`hPz zs|2-yzJUqOSb{-$R&7 zL|sh+e}yoYhPrA5{sLhx33Y82`16Fh6x6j!;Lj1}5>VGtfo~?vrJt_30+$o!l26wp zz^w0QzPl*#%WL9^H{!K#9bbA`(U~o0J`-=8{R(Kh^Pbya7$2^i7#&&%5DlVn_Gcg< z-e@mlF!Mx39-7J>AYxMqc6UUF){yR}It2Y#p`SLK!PtMt6UXC6{xTMTZC zJZ(P(gn9K(Q})7+xyHH_OnF_`f_6D%prJ2KR6QO~R32O#Z}5(y7vqVd6KA#{e;;2c zKznP^^FSlV%i9{3JRff;YES$ko@hNpWM>ts1UQbyYpaeLmCGBJw;?k#U_`tz^N0vY z(ar;HLs468M+}}S1YJz%WFHd4e5B=WkIkholQR6L;b9f+q&$N1?j273t90 z4lCb6%a13HFy%2?6c^(<@B1ex8tu_&C%vRz!Bj2lY>+_qbl6kFLTH?QrM8D{K-qIX z^=bMaf8yA-hw3!82v|s&0x4%JDO0o*wp1m>bV;dnq#RoY7C%`UmLorzxoJ)dyOA8|qCw6I#6SMEz0LfCdQ~ zj70q*7P8zSua3L8Cka?uh?4nIQb-s=I+PFwJ%uFdTSOr5w4pus>BvbEVN2uENf0_m z3r*B_DuE2r1wI8_?P1}ZUsK`(evm*Q0T5A$wRk8KbzUfkYbA1I}SMoK_)K)v|W$0UCq@G-NgRtnUt zJxqPk=p6$U8a0`fMs)(HG;e<7sx&RA4e`WB=(-Yn;^;i$iAFwL6ZI=_Pp?h&mr2qF z+-nc>Zrl`Vt(OSIG}M!`_Cz>Z_c)&VjO_fkL^7gv%{sF49f^dZb5R45P!PwEAHG=U* z>vjmn8{JtZ7*DkBc9G+W?z~DcRcFKU6STUaaC!x0T~PZZTO6xFyYIK7pUlC0p@XhDf0^t1S}4@8HYF08J$ zst(q3t3hJB_tWaCItvvZ_vr2>1+#Kj)tMEeyLJGfB_3}TlaM3ih9)MSM9T4yvTKC|ZJCtO>YuZDt9W$x z_fdikdqhM-JsWM}B|bZkU?|x2MWnUXZv&E7YyEcIS>VT}H%Q#A^v{W;BSdLfktBq@ zR+JTGht_zz!IL%M6dW@_Cp@HXn%wBC_S16 z9ld{Ntm{qK$Np7O?(MaF_k!-nif)dgYf^N~oU0x*f$ocnZlI#u)hpdD)Y!6yJ<=N8 z*~mmTjxFyc;g3u>rs{tNUjS1+z(J5iTWn;*B)kIRP<8>C?>*+?r9j+ zT0e>DxVnyo=skk&it)>x$%+A)e0DCo>sQIz;EmDFp(jh;nM$r4 zi){R94fO93{f(KQJrBIDA?Tlxr>^sQQzL!v=}9X&(C9fX5Gxb)8)&oKUEM;@+$MYA zS8r39PRIiq89vT@C+bhA>@#q(pQzugkV2wHOPoT2i9w32f=og@tDf;_ofpMidzkDd zo-h)zDpqv8%POl?*0wM|V<4!tsGX(6fjQcWpmt)YtSqZ-LDy41>0~mZ&LAvU-fdkB9GrKjWr8R?3r?;Y zSJfk@-WZ>{^?b^oOtB0UtXuASzwkH<3BB)0m3VwF)CG$AASN{jjcDB)ey1?-O#LG| zX;mPe=NF5(M`wkEocP0@(0z#^G#Wv7LwVsabC+cs9nAZBaPPX7}#?!pr0Pa4#9fM8c zZL+~E2D3zJ`NPM;OuyEr^s0|Rm~lLMB$n8-9VE9N=6%PvJ;n}<*T@ua%tb#Y;wcGpjSr$R zDwFmpOsrP5PkKLyW-Vy)_QCa}gAY5>){C?@NVC9c)QUC~4VBKSAB21KbC-gq>lv8X zAPAQ&%xbd6z2610dqAemLSMoMNSqqf*ecapwm~q4lDMO_@aYB$?YE_hUb}{mj};2T zASEFV-q!lXlz{8lr415y*(P#KJ}|{cjbvcwSbGFMiY?ehPg$@FXmmhd{)bqD*)L&{ z1usy%@R0bQR zW;t%-DqQFBKZ9j>I-xi=SxSo`_TaMgMVasoA@5%F*OlISc9&i z2DQCTayf}Mr5dOs=T0q0szA{$bR)Z*qC?-XSU*ZDF`aacVv3qT*Oa2G9*JaCD~k3y zgV-f|K~$rkpZ8C6*EY`M_N#GLs!%w!IOdGKYh`$V=@iYz(n#QMtebYgCnM@;-C?%p z2K$8Fb5z~=Cy*uT_cE`whZ!LM%|fe?@O>`)ny9}FX|453Nr9{DO3*ayAsBCH6!z4! zzZgRK>_1K9;33S>^ausunQiQma%J2jk67~CmL>hy1%`1qj=RMu?-2wc~4jN@Xd~w4k>m9>Fle3t3AxSr}Bc{ ztS2Z<>8OsPEIgu-$Cb9 zbpmy~VmVzfyKox)edMtSRH{|%S%2*A{s|zXbM;%vU4&KOI&jidRE-Tj3-Jm2MGI}H z(SCg@Y>~vyZNk}>mWVMdI5V2DsLt-wS=i?=TO^~K zCBgemLU1&V8%t$ai~qqC+BWSe1{jOY6aW24G62iF-++S?_9W_W%-gAGzuJ>lJHGSR ziZ)Ez2K!%I5nys2M*JfcuN;T?)t6G#>~)lZYH>O|rK-h9RE9%%7<702oE|NDSo!+l zo%VMOmVDkyrTyBT+8s-mEA4kG+9`^5e($tn6>T|b8|-2R+R~LOUO5{0A1o6~ZOVY9 z+~8D}-u$Mv^w?=hpJOw{DEax|xO+1tn{?oUJ9@U4XP`g1of1P1;)%m2(9qKA|FHth zkBG+r_{D~zSL2Bn5>I#DkLLi?ya;PT}$OJdgs}i;u(BRlIQus z|A}XQiYItol4q~xX|S6v!FcurmKq*F4jb%S0Ve84qWl`vhrfg?9i>f5SD1d>ps4DRtSSu5Dl~t9~ z%{KVai~UQC;&`MNqlk7J6|K7!=CsOU36j9VC1AD&F2OSHHu2huo+e@mmLzJerB<=l z9wthbU<(lGuE1g=Bf40Wt^6uMgD6edcU&xX_fvMqLE4MmMEA5imqQTU7q!-hkxs|L z0f){&fKKP^*Pyg<4^vRlJFutuu%w`(ztFi69Wh5WUqef<|Kq>u^ZIVDx~ps z-u-*Zv75F~y6hbxM;yHKVag(Jkh@w6Q>$O;&*CxUMG32^f>JxX2Gge-u`fkC8CU2; zOKJoS+Z!5JFdMZ;a;2fZ(D~ih0jfo6J%3pxCQwQbb2xNi5_;(*IakNl1LyLyvYo_C za1-M}tY?MHBz>$iO5)8uh}CFZNmXM5vhW!q<8!xEj)7|wB}g3KfwHKEWPJ3rcFusy zkDY-&bkh=+?CgFVMxzfcfDx(>oi1f;tAy6h^Iw%RYX6u`pa@IpNt1P>EW$#1szpZY z_M^b#jrNakpy1TFo(&emP%(`6^nzSTdJ_d%#8LiURRTr(5#Nv6AnjePbxLY#$3;?V zPgv3V(NK)xJ!E5IIY3#FsjN5%rh_RlQ$bUEn0KRs`@DbJEVlOc_;;S%y*)*qhikCx zpfYo$!=iuVzeCozhF4qFVpQCaOioVXk+sG+j)j0kEd)+SiJ;H^=s}NKEv-FVNW&^o ze;KLyF>1#iDrsmWES!zA>&MgNr#rJpArxV)omWA(lPx^&?3l_H$0qOw7LnDc24j=X z=U&HD5PUs$Bc2Dpe)!eCGYP}KqiK&lFdTi1(}m=e@}rlTE4~OR93wTdbZ% zol@zrz{sNnMaPi}>v2d#rDD(VB>jt1`qfzmJbXE{PCW83?rF9(lG=T*$e4x=A|+33L$#rI8>sMQGeRVtDYkF&?K(A zl>SN`oyhzq&j2O7A&AITBBW;Y5aA5S&g)&o^;$$jy+}{Zt@9Q!ML(y9o}4^6m0Y>L zA+KQ(sqNbtG-lp6j)tYq>g;(1_MfETb&jdg{?uMZCvw*+rwXJ97%2<*a=s+b6ceUhND~^RYwN}FPyLo<^irHO(e zUO;bA-pK`&#~i8x>KIF1mFO1S1?-g0y}k4QeuLsaIp05KiY}cICnw*VO2)FbyEn#XhUq_Sql0GX{^O(?EzAgegG%Mnmr@n%6yAidy#UNM<3e@h|~du#XktNrjyc`wc`| zWsOFe-v$D#1>4I$;M3rFKQZ-YfoTR9owvU7`bf}wyS%v9S7TS zhb>X`79JeTlsY8lW=73XC&UJ8eiY@5k5H6uqDIm1&-F>ej@KF{_9N2W`^<~U9N+rR ze%Oge5Jzu`N7mw=?&o?UU9J`-cczpe_s;sK9Oh7`J`!@SLLI9xqj&IpG*-}hk+e7| ziQ9s-l`EufNpiGE4H3JFrAywX<>aZ$#Ioh*0Nc?vv(( zqB_jrW);qPsO9) zS1suEifVNCjQ~zsh?Rj+)k>Hq<9sv{)gP;FIVHA$O4KQ{W@04mQOM8lyl~?mWtEfTpv&=I5geDf|L}4hw{j5R$ScmZJ9=R1Ny+ z)lSxSr5Ut~hq`MaSc`8hIttg}#NVsXg&t$M?2a^M4x0%m@eK&|nwQIRzViWI+ z>75xargqSG6)GlF`97I`yTHYCH<^yXgD@}nADHfOGp!@jqlzi?ADA|}na(8Bt%@n* zKQPU3Gx0TruEmNe{2!P+Zl+c;^}{XM5zUU)akT{%V65+|x6X!(h_5tep6K?3) z#$83ns6$C=CneQnxWug%$>mA!IuX!zCAe6t)oBcMC?mxf17q!B-qF_HM;07lcLfcR z6tuPfNv5E!eKwgQZH>%k#r&@MK=*FLf}{+!^`fyotvdZ73at{kr)z?CB)KP2*t zuu|-PJjzK-Ik;XcqXmp$IZBu%1BmG7+WyhY8uG56B}XQ5*r4b{YmLFWjSJm0Ag_j+ zu2!$D;XOr;JL8G>5~yZN4q@^9qyY?Ghp{QeJ=o+abUdCR6p0k-9#X2rqj=6YE;SSF zM@|eq(pg|W8GZCuW1mbc$58XtSw%-H{jx@U>_Y6~wsE0X3YSIiu+wT2CG` zADvb7s*{m5(B2Yns6=Lt#*5+7Hpxtz$_yr07_EmI<^~ey>AYx_$ji}Q{M2KN-ADfR z{xq4t9&7gLMcA14KrV`?(f$?#we~pvNsdb8j1kUOaorMQ!@Jesqe;kGJKG>%N~woq zaU2R{UB47sb&L$7+YL${7YZSdfl+~j4eB*U%!#6p*4bRvKPOa~<3c~JurMxfl-Q^R z$}2T(kcne!3tHvv2QO-IYdy!kt!nljuI=f8c4Ow`$&yL+xXZ`koUW(93I%OB z4BQH??V&)PY^R_g>jIa8M=%5JDpm?MpQOOoLjgQ?N;%kkrc1$PRA7mM%>0ys$1og6 zS<3mtxh>+Sl}_erTl}1G^gqH02NyDx1L>>=wWce%?AwKZo#Zm$*I1hEx)r@IT=+lP z4BRdp2_>mAV()k=WpsyIK{*w0bb;0Qc_{@P2|GEw<|pYnw3)-Rka2a|PUVnKmy~*S z0MB_hrDr9{bQ|yl8!0=DM>GNOJ4%%IQPVkS~o!F&E?LoKMouZ ze{4i8K;J4fXdTZ$YdqET5#<7ddbwSC)Z|X`4p4Ue1`g^hLM*ur9XshkVwXg4RKYoC zxJ|NEF}(Ayu%H2B;H-*#JY|l6hOIM!X}Xf{FzUZ%bi2}yG&^|5P?v5Bm2#v5RkTKVenK!co%PDzeBRsG*& zStrTDsG~jIl^i7~yD;vdQOh~JKyFXont$-e>6z?HV9LP%D)g#T6siVeD1&1^Lir`Q z*U!>k~yaKOlJOjE$Ht) z=JG)d1$_ci@Ph%c?RDBlxOHv;tCx)H15T z2md873*!*PLzPsX&iJgNj`9ewo$=GTLRZvwEI$RwN~l_ZXan{*Xxic!^?;6s9*EB@ zIDlI#B*SDHX}F46If%lQ1jkn$2*{0k~#Ac=w)MRguJ!<&RD1 z5TbCFtUV*CwezQ+B%OA^<+Khsty7%V0f(UIi(15K8ge-e17|G1&n#*YC$)eQhmflF z8tG1ILv?CTI*GdA5qulv$Q$qo(`e@`c&Kzcd}h@y5nsU3Fi6(_X;LXN~q z4a^kHN;{@H9JT$F9OqQ+M!qUobl?eAM|#VI0P$jN)gc@;lE$Ov&{$$WVOIL#==zo& ztX@(j-}|BNN4pv*f%eKNyB&NR5c7nGkNI&fSkBrNJuK4=C^_d*QKHU5amcM?-RH(VO%k9*+pj;7O#gHB^wGsw|@1O!i$zKUR=1ka&76R zLjDcUm@Q+G`|C?LZ-yx$L*-8Yr%%W@qhjOcGnD)IA2e{0LB~1nTDWN8#m1D$g=wQ~gqE+~ zTFC$WF;D;%<(oDb_+K0RBhg8MmMmJZNV0>>FTQx*g0l;^uD((J$ET2gV^p$MN@^mI zI(bWFG5_RfeBrusczw(4QI#QMq0|MIG5!c>EpD4Olf#j?aNeTD@cfq5HP(aJK0#+Kre zvXYhLQ8CS+fU=Tp_?IQ2*j!$6BT8m9=-^f7&x_Lj4hiqD4fM?7w{;4Urxg!i-yHEV z-|y}ogK*fv?rwgeGy9?L?p+Aaf4IB*0K#@mPudWk*@hW5!fO%o|MFHp*WJzk0lVf` z7{4HV4I#hiX&nVW!c7PdAYA=2_z_<68u$^8d%e3mfQ6_)2lOJmrxW}LpFy}D;X`;M zd>6tm;JxGn2;V*qeuQ6o5BvyUMHs+};+XfrkMJghafH7>$P;l@`0~pxgbyP;fbfM6 z!H+QTckm+|{txhTbq*I6ZRIj=US^ovV&=fGKhT6c63=(TPJFk!`!rH4@XK^;B%)LC zi{0Pd-HyB%k=VjW!3EKbt$`Y2&VX6tri>g8tngyf%y{s7-QDj&o-r>HyUm)P>GQl} zLWQJn2l{jH`83K&rcbh*^nF0z^$?Z_X=^gQ&UVrt2im}>ay3>SQt4Mkn9h0Rt3*HD z-8~&)G9A%D_?^J-N~C?>rx6pX+huI3|j-)h_<^N&bCE{|VCH?~(p)GW~I+H@}2auPD=` ze$O#438?>7q_6&6clXOZ_+L!&e}eP@uXcC8)+7DpWcncZ<|xv?!Yn)a^Xv&Y^3Or~ zGQ4v-+ReX4*hT+UNdFAdyWHv4TS@ubkv<%+w087J|6MYDAJX4I`lUU};36pl=I?Q& z=iv(*-|xZyoh1LONWT~9@gC{(lj)xz{duG>?vZ|eGJO!*+6kmz(j$FwGW{H+kHxn+ zmiI_snoPe6>DM5=FZ;G5{avJ=<>oIJcJbdnq{|jUL=QRnx@V7Xl z>+WX9z=47Tx4^Ic06+e1l`j1UV4!h%0{s6qnq+ulKEqRB;g1U;!V|~eE{ssG@m zZZ;|Y|EpDF@cfD5k9bdvo;LmJHgaOCCC-=|^jvdLAbf=m?{yRKJ}{u*}vmvNwm)#w1vRSi4;&&cN$ zFDLzP|L)#InBzKLdN^hr(gb|u@j9;4or&1H+CFYG^5Qs$SDp%zcg<#W?p7$xW)$j< z&KdV{tBe=>YhEjKMjd>Oc7$X1QAK7r{i~xNd%%((Bnv5)4gfjP=}Z6aGefs)ZuM9yiF|#_{JRc) z!wk9db$FT%r|WQ`4lmc?Ivw7q!`pOtrw+fV!$)=av<_d=;U9JQcOCkMYyCPrO^4HU zxKM|e>u{Y8Z`9#!I=oYd-_+rwI(%A(FX`})I#gQm1^Q+cbH)`WK*rCXKmY8)F?eE+ zEj)Aj#4{(JSvYyp9PMKtk2Z<$Is*-k%P$Fd7p`tzRivD!8Fh&2$D3!dpum@QZ zQ1snn9s1K2s-*8YeL|8_MHKxUoZ3|K+b>mk8Ahkk5>)hKiX8guu2MwDnfG2UMSnEz z(0>*ORe4!{6{g=6{YMzZDtoRfQ4|$@jCmA&$rTQLd#R%TVITI4h9_;q6ybRbP!fYZ z=jVH51WU%=io-So+d*hs{y(9?+g?A1@^--gLk6agowjY^6DEd{@3rw?$c8TiC~I8) zJfQ4=&3_5^eF?1Agn{sb;d`C&z5O>cUe5TJ2TJ@&#`h0kykEd~0D<+hVT3%~ZiA|N z$PF{@%S6mGAa^a|nQP&im^b4i#7(O7#=>_aP)`YOUf#D64;bNJA@Cf*4W>Utn*;Vr zSQeqYVTeR&o8KrHkGSoBgl`7pMS~Y12$C;02(fvHEZd2Q_d~=C z@7ItG(>noHS>Csa@S2DRynhutS$_)+-$nrTt^pT;Ej9os3TgZsg?Ml?y(}QhyAYzh z-i5#fygd0D^L`zCh2BB9&-G46YE57a@F>tvfHJHAKbd8C15=PeBM^8IcZ{HY-v-ux ztv|n*ob?>IMwn6YU^dA5S^cP4iQLVU7jHHzW86;sNuopz_TWAZAVh%6GN^1!jO`vjiwK_b}xg0mhgd z-}`0@Fv)BsaIOF|%oXIBBfwm9JC&X%K-{cj%6tKqno_wI2(ZHJg6Y16`Ix^LtIUr{ z7SGKDu-^PK1zaFjZysq;Hy*)WGmF5g{`~0sese9Y zTP@ZdFryT(CddkS(7cDDi}PlI?2x&TJZr_&7L!M~eCq^w%KSW2)-`{^O1P!c4x^F@x_zD1z2Rt64r2)`hX#ha}69R&7GI=y^!3bk#@<#W0 zSnz{ZKzPR8k-0>$z%v#j78q`ZSry6=x8$}JF~h@#>0#^3{yfxW-Ogm*49{$c@n4Q; z@Ygmuzm-h#Z$wh?Ho+cFVYvtp3JCVsNo@8r%mocrm3Q!i%)n<%AIE8;$&%_VZQB1F z=t3DHVj$B~l|JK1Bn2BxNjoRWYeKpb>}RJM{8E59Ugo4586FU1Y*2aML^R!E{ToC% zhW|mztdvYQF^y=8orOg($MCSt5BUbLW>6HT_rT`9-0w1f^pB_cfgQQ|fCli(p`Pab zJ3=jB4@X66^W4yOfmV;6<+tD;oqAL}sp-?7#Qy|7Lp>@o)T1IpJt{KPqhbJ?XNG!S zWT@xG81=juum&k1*Fz&iJv2hDheqgZKkT}5z&w)b2guEg>j#L$^#g?Qv4PM=`uoX& zl5zkjJWLo5yowv_;KL-J@sNI)lri3gA+GoBezbL0xauwqxN35wKq2mY4S& zf`E~4z(IjG5l2q>WP$m7vSd;CQbckn&2NNQIeZ&(egY`Or;2}A|4zm$0*KEUSOHYO zyHI2yK4$zygX@WS3>4$O4XgamnbCc~W0^>g-GFH%w{sB1rBTNp4J*?@AJ7MgBmdFR{G*$6&naF9(g~?||hP^aMivhoHsSyO@yyxmQ7* zVYAVM2Ilag<+U%T$Uy?wb}^KN1`FUbLfauVG{i#X4oYZ^)+IL=Q3^#ehCaC za@oD}6C4Li$!WXYYufxpHNY6OnhVb>2(XSw8f1XB-iH(9X z!>%VVRsgU4MYtz4PJn>@B2!KmAZ*`EN#g~G*&osN30YK?YlOH+8=C0hLE|%?02#mR z5}Fj?bHr|84$jP_dkwpe`JbFay?a6J7ogDo z9?8ziSp#8X>|fHN83K&Aw-A__vm4ST4Za_S&&ub&qVOZFgL9$woP28C1<(krhvTy7 z2vSIM@)&_HAUW%gKtAjsqekqr%;((x_W~6wVlL<9QcP?lJ#(IlPo{zMb7@oT&&=%t zh3D2qs7S>xCe1<>|0XRxU*b~vGQ1HsW&h7$rQv@Bl1=}=q1W>N3}}x(0KH!S9K>z^ zV~G3w`~v~Me*)qGe?FuI{nKfi{}{ZJ;jchy*a*Dx;U z2k%8>QPDr}6TfyFp=Y2boagx=PyuG;%+MLYWDf7gCXpWmX5c;`3NlZNT@1kf8O+Zd zqg&6lkeoR-m#sM<`ZLGnk}75d$QjxSshOvH22hF&B_mS;F?hk|STZur&&(M%$C8m5 z!QYA$jwK^A1&HxWh(_dW0Sau6B_p#0D6~12jGQCD7@K3s$ZP>7*{uZ56<~(Vv1DY9 z0CR1QB_rnv5Vtv&jLa8csU0WJ0s&Uo97{$P=8uJ)t89)XBk|mc0M^?aOGYjbt2a;P zSTb^<02oW!8!2K*UNu*+_wfU7g_L(pV%EE!qZZwXTN+8j$pR`tIEzW67w$|05MZOUH=;6%>@X<5XhJ*pih0zQs z4j1?)kc37Vg>&K6*m}<#Vki&;?`%}&Uy4~QC*^V)+26>F!bkb{D4Vw?6WPX%%mOr| zoO`Soha-0|jg}hIR?;Zo!=nBDITDb7EV zh=u3j%k(O@ktRs*H&4=iP@&Q(U#Cw)dglEmF{dZfX`4qtr6~{cqWLV1)#iB)6?#(` zn0cx2t|3-#Vn8Rv8G*~d7HKEjOp}{E8 z&@J;g@|P3v1gPQXOk-iIwCLSn4_)kgi)bl&3O1rc0f#Q~{WA?+P4p69HlAi~>KY1J z>>Hbg-hi?XUFe&ehW-k2AG*MIMNf3*mw@hnvl-*mU>nnDi7F9sX66^5CALdvodkRi zN@?i)5936-!yG$@SuhMa_JGjb*DnW~^eu>Xm=h$E&7D<2p6GeQ%w$`!niqKgrnw#f z8Lv;EAy~S0oADPjD3&yTsoL;&XE5FxcwidpISW42dd5YAri3Ss;M^r zimIYvP9eQ7Y9S=xbuEO5suPZ?I;fgHPo+Al9&l7yn7fFogGv?P9;!4fsy1pNw<=l2 zhZJ%JJngY`u69-XwrZjaHIdljwIWLD?Rchn|B`0R%&5+H)G_9eG6rxDV>B$rj5<## z;Aduey&glpIkkydVfKC?GIznYEGt5Fp5{-gd^c&H-KMY@W&i|vR>m43*jpXkQf%Z~ zG3k_=+p4{LH2nxgZv2{t9zX%*S-FG6Yc190a5M<19teNiffof?YmA3 z+3zm4F;1P^C&f0_il}1SwIjHp*cj)e?yvSb?ESReduanpag3F%O0i~#lpd$94GVOC{25TBA;4>W45A2Xi3)m5Ac(-V&H)xJGDRoP^ah8>>8eLPh;d@sT^$>fm zBleII3pgnj4=oPN0z^#ba-_N6l<~_M=O{gLMh#JOUlm+EyamzyW})hg_wMlan+*gm zM_~d@Rr2y<)1w@u^8(mW>YbX#^n}CoxWjZ%SqeDGbV(Z12M*I=he>A-aFVG^GYwN# z8l#a1G4Bb}!~9sybH&pDITe@cxI0bJl@8BOlp?D+E<6AoJ-&B!Svz{9xL-2!baBJM z6hDkByLXu%VF`ej91TQh?mGw-XRR*3)-dlf2OkE?YF=;5;L@)7sFcQD-BrnHLt%Mg z2-aeDqECHvlnh>)Z{I$W5+0pOeou4FcJFhVNk1TsygUfL1)bw#8Jecb@P;OW9G4$0 zf!7&>FcK^_j>td-OS0R2{WZl~9Jb-I*pol6pF*#052GXhx$Y1;sw}ST{6Z&oY`4CO zfyWCPd~16UCw@npV`w`cxIrHG9cK=IQQ(8s!E)moGa{qD=Ht~~NAVP;*Laimvom=z zq^eeXmBlB*lOa_Oa}Dp0wXJ7qmi&Cp^1SYTjw+Vtb?*a~iTc?wShFm0SbmYEv~((# zUt}eolu4y9hJ<>@JTA)3v~9kl7)u7oS|51l$Mv} z$&I}DrfLo+_oBd|-c>C{lB6~a?>bHIWc@pihF5exnv{lDlKIGE#r;ly81e~R@>Zs* zt$TM^Q&HM)YjFP#?-ge%qknCx@EJTota+e$XMk!GA8rrhVD`I%IWC9k-crqQwPyG> z7{b>Xe;KOC@7?a(rYWXrig5X6<2@;PPxFcG-Y@H9C+}OSkyY}&eC4f!J0$<_<TJUP7UytYNmafDOh3zO+ChXV7sqUCvSC< zH*UGs2$=(4Mx(WwOMvxFoT}2U(fq+h#sa4x4{i6}r|BIf9gdO(X0|HGHl+lxqs05X zX4-PpCm@cmL#jnY&y2)Nd?l`R-wvhxM(h()dDr_Xrt55{9J+(bpjC#Mx?5}$t-x^KN(CzR6?Mz@Q5^CI z*|;jq{%mue=DWA&U4+QZcr;@*@4rN;4E7J-K`&7f(FRE!87hkaGD9HJm?G9WGvPT%jk2uU6+ljB!^Xx_OTHu9iN*gMpb< z#>TIQe0YFy$hT6bepv0Dqx%-!(-?OT)z8SQ^%zV)Bd?a8%hP-m^Z0LTo&nIv>lY5s zN*RRV*)5ONl}?QZ%Re3e5G|vIWvn9pjJL_~s?3A30Y;NeU z8XtyV?5}U=uj(QEX>fRHTu*bk-gc9&0&i6td8lc|ebmS$t6H7VS?&8!YjAQRjCDGv z??9xtV!X1!>8M|J?B1R1t0k|yrK?u8=c}5>X|0)1FM0i<=Gmq4`b8Jd(`h{O9G<&0 zPmSWaE5+mcOB#>pxLeboP@~wpQ}o7Z7lyFw?HnwTH_ebab6+bA4Rif$Sb$Mab4QiW zI!)#K@1_db6o>0c*aKC*ES>yIoh(gqly>YvHSIc0gAZ3(Z{YbiI!iVJnp>*EWyTDh zcBsnhh;n+VSujCLdW4=u)$br|F56o{OEP8#Q=*Khl!~-FF$`@{$T;mL7aPUPbLQU&T%D?HcDqvAc@3#VgRC z4On1d9YT(OvTNj(ep;3rB>{D&C!o&s1k{`)R~%qIwIrc5gDI4BIA97adku{ppMAo75e?{kZ*B=-XF-R zLENyoL+=j?5U_Ye#vjUg3JI~HYzO{~9I_PPU@8JHkI4AL9v<(^`3-Jz@;3S}#Da1_ zCa4a`czHl35S_>qLPC-?9$pQ~@tB|-j|s~0n4mfylNSi|F9m_M%LldrxzgWSJRTF! z$73uWj|u4GF&2-<1f1hBULKFZc_M1)C&y#>7ea+` zVB~mA2KBe#H;gD8kMZ(&Okkv^61aYHJZ6*!V);Z3jEV6Cm0|ICOkk`4UW>mba0{VE2 z#p5x7nK|5foRq7M$NU*_9*@aY$785fj>p*DuqA_zAca(Wk754-$r)z?0kK0N z;7kEx))ZI}JX?SQi^np8vjix#_Auof0mfL%3CtE?lGRG!TmfcSJeCohBfwmX$1;NF z2@tnSGxek7Wce6`op)$1;M;qdvHAmqpIt z)tO&L&}8vgMsQ_6yeMhxwRkKexT-%-y6m@jEF-vDtUF-wSVnM7@ILSyw0JBdSe!Q$ zWQQys%LwXY85WOa1og2Di#wS?eJsP`v5eqGIda%;czG-%dY;Y;NJ&bf3+F&*HHR9Pu3qYyr2B&!D+%7#>DE z5@TuLz#NdLWpgh-Q}5+-d)6a2HU=DDXyfWY;d3NXg_ zCuk!r0Jo13%W+^flcTilQ-!zr;T<6?+*Zrz4=dWf*rkefqLy$A_+`dZ9i}L(jLZD7 zT81RpcQyRyYmRcw5h~td)W|TyYF_MfQXP5EIr3^EO5O^QSEF}yAa9$NH&4rxdF8FP zqFyDl%3Eb{MeQgB<|EAg?k)Xw?q7B|_tF27`z<|lzokd+E0eiDOG|L9y$Sr1`^p}< zuT15>OmndL@nY_`CUftkI`W=zmr>Ed}&^W4Vfh3iE;heD;s z)tUqohc`6EU5db1j5Z+^`jM5j!i6C;YyN2M@-Mhc59<@kWqY{v(>FN9VCw znvbVhFYj$m+JxSN!-Q}Rn&pzSv|;Z%{@uqzHPVQdjAkQ($D|D{nZh=N-X~PH#kfTG z9hdo*YksG#KjN5svESX(U91_F8vfhUI3#nI=$=M-(jz}>r(p#8UK@E@hM8COE5zA*o5H&=uEN-})`=mKrleSNWrAiED^1 z9!&FmH)x)tC?#IAAlrNeaz}==nrib;1P5s~JN(~Wq(~h#qGFO?^$u9aWbZIZcWs&t+X`B_Y4B*AO3|)Y#on`0(yv*?5pgD#(Qkj=< zkKzs|sSTUsYvXP74V*vUg0kqoNl_UP|WbUrnI_Nn=^PtBiwYW|$b zV)Lo_vro;R3)K8s)?aPc{Mo1G&;0`+=d{>%&4_K+j2Mz1LNdrX8MgK0_w&$W>&b5$ z;sZGO^{Kh8Pt8_+YPM>-W~;Vqw(3)})d*&*=R&YETm1`!C1mDGY2*b8ugq7yGGFz|eATPws}ams zUk0r+Up)x1#;ML%Z9QMz2LZV zC``O;JzssBVr9O%2dU0{^$idu=c@#g^Hl=L`6_|re3d|QzDiZOhOOtT=Kvbwny(h2 z#yay=dN(;=rHtf!l~l?3DuLvDl|XX7O5hacs~3Q5QcTTPKZ`i$t1&fSrB?Mi$K!~F z=?GF}(N%bz<6>AC{yLDG9Awl8>iOzQpn`l=Bd{QsVuD>e&;h?Znq*i#9qh-ri@EhQzgQ(Z=y}-+{IJT8jl}#s zD#AQ0BYYW!V@!a|@$#1%1^pE1@v4l0I(4W{HMznyMBei9G{0MAa*b=K2LDp!zp-S4 zQK-ScR(Zamxv}>q*L+kNdF+e|(q*-lh>ewHSw zk&?J=JUUxRYEpBD(Ru@W0p1?>a!?d8O9|TT!nJSLaD=Y zdZuF0WA@XC6Qkmq9CK8X$CKo!##d>ENjfJ3kqJMyMlbF!y&~&La~jPYhvw20 z&C_W#Tr(CqSEOkEnMSkIp}8tWGwCzR5XYYT9h%iCnvyh{7aW>(DVndO(R|?0T$iND zX@)9}Nc?i7KySW$Qf2zl9c|NKO||ErT9i}gm!KPw`mwd2s!|73{n#oS;8NH_KC5yu zMSHczVcAxo>c>IFvQ3|rg_G~mEHgFB56HqQb&GCU?W*){NwzFcM;gl?9hPd%f}=4k z@ah!Hz$=w_M@xTXU$oq+S#Y+TEVm|EGA>VJ5%M}sen^qmCCLrX7c{-ocWrl6By>l# zU-2Z89Z|*)(^y2s?V5bABELOJj<+K*OXD?E1u6%7{K~a@-C&QNWiB91fX_NP+G{+3 z=QaoW*TA)r)Ik5Bsbr;p_Jt?nLw6v}@RmBx1@)#FUYUDI*b6Mk1z+L`)fp zm@*PEWh7$CNW_$ph^a;*9=+?v0g08(>JklGxxrr`BCm*Ad0b{!Z-`p?M5`A>t$vIL z@ZAvkFiU`g602Z8a9M${ClcgJw7jM1i85+ra7dC(9wVH=AxVzN3LDwK0ct2na(L@D zJZkwc3$jt#HH01?yQ@ud$QS%Ltn$95Lhh#iz zvgA>7A&;6fdDPSg0jD%j%?VDJ?}p`5+s#))J+2P9L*>5PknX*~$REN8KYie3SCv1$ zLBNAMd=-W`hV#btMgjAPC0PHLwIF8-$%Edq(c3ShiP7ezvZLQGLor|p_R%zsD;$mj z&9Oo_3X&X|;cJwYPLr1{@j)IXU!A}h3b>bFo#|l%yYA@+Fi*!??Qz| zo->B_$aBVcGGkAA2QnJga*_Q^6^&a3DCCG=L5ux|v{=Ujqajp05boiDu+Gn1!~dH! zj?Xw8nLRv^ndHcvt9|CQO7TE;4-aH(56s2vhHu)Lw$EvBqmvm%W^KtV&wd+&QNP$; zzlzQ+Kh<6zhp%|0e|BkH7Uu7MZxfQxVj1|P#rmYh`lQABq{aHA#rmYh`lQABq{aHA z#rmYh`lQABq{aGFi`Dz&GKH|RJuL15HrW>uF;3bA zfv`6k+yal8oThkVF~*IUOjG2urs0@OQy9lIC9?ymwTOo&;-;r5-aHTI^ftHo?Fj>! zVxNt{m3`*`df9%R_`Ur{6VEMw`^y6*&QEvP`v)*SC=h&($p=XPvtdMenaAL3h-Jw~ zIeZyWv@~-bu)bLMC%_y08NR%{Um+4OBAp0iS;YT#KN{iv7|FpxaM|BOl@AOQ(*}PH z@fm>`{?8&Iy9B`50g0Z2=&UeRV^a&a>yR$lJPV(AApf~TrUMlrUI^H=2!Y?o8izPG zP|wEnLq3s&{Zkg9Ve0);=0klV3Hzr6)F+a#e@Z}oA_@Da1k@*zuzyNGeIg0_rv%g| zlCXbDKz$+!`=iJv%WKP8X)L=yH- z`2{TXi6rcw{s27c6G_-VUUliQVu8D2IsFWXW^0wN?Ym5;UL#{Fu(9t6_U8^(nZ&kgWbirX9q4-p z@lK}8tpkwZr4KVcNB;YTpWU-!715zvnDj$4nhQnTR96~@^Y!e=dQax$Bq}r!l#!?z zVNNb0TAug386)_vw|TYAjC^-3_2OiT3NXCb0b)-@=~|;$zJ`Xq0b}F>2s85{biebg z;i?=je8CCIN6G6-d80_dGh(?{f}>x6N!$cDjlEs&2IPA{1`Wbt(<=XO^k6|g|AF-P zVU=e?NtrP`!t8Oy-G9zX>cH$y1;${Mw1>l;(K#JJd79g?4F5lAMCUm~<6K1lP9wV3 zA)4qSTDwZ=(X<9PK|ICljWaXpD=zwr^2sii4$b0N_!CF&G#Amr)jj3DujS6rat|rF zGhHlCY8FQ>&t!@6b6i9t)+o6S(K!y$92Zet8qsQp=sXwE+i66%IYdP+qJ_mh)!pw9 z#a%?-(nRwVt-uv)8KBIhsgvc2SDJ$lIzZ#yJ!fTa3&5v9U^_W8sKTF8y%E zw@UEn!vN{`sA93y0Ey(ht}0Psg;#}lf8G`_*Phi18q|D7<0Mo1hJi2O>YvkKezre_&-U1DMGV*BJ%;QihE$We_SQ znqD!&-Dud81JrZZdLpnSL~>l_zKdbEr`(u4P$Jlb4{+6Z@+ca@eb>Ols0))PlUcTH zIrf?&HuFFUM_*Glw8CG!db2T2L#xbS1Z!Xdam~@cdD9l-Yz-dT5qV5o(ogJAdla%b zF(vCVx*4x%a_wr06fCC;hWGqZMXLw+Ts55)rD51a$4HBA@zJkgeubh?^1J<7#lqb1 zGWYdIaUs8jgH(PGsQea=7d(z(OMVMys{CS6kU1Qt^R(1t?uTng<#&XJRDMS$GiY*? zbd*k9FS#G9p}F4SI`giajMF)ZBNH6u_5Tz2%>lk5orAMf29SeADhC532b^kze-6?t zdl@M1T&KuVkA}AubyflM3$F%P=dy-tw?&R?hJjlAqac@6j69Dj2DM3<=V47kiKC8c zgNAAjq0ZOTYEL>pNo_=*gl(Dgw3g)#&%QV+8Hyouj5f=2qme0l4Oic55I67;)p$jX)BT}7$Yt(> z3U5-yR;|KmKBB_zp~6n7Fy_KC_O)L}n~bLV+K+UzNdHFf+C1dQS>urYRj=1LD;}zA zO|FedC#aW%oOKh`L58Pe9m*OnuR~=8^wLV^PGl)O11X+?IX5A0-+`N6M)9a_FY|su z$)^DI!i-H+w%@xJfp?Pm3@daUG6bI)*7KM@12(LPU_AJSFZSC(XjnB+#OzyxvKjFi z)-^)xH>@WmDS&aUB!vvCN|G`RYpx`P4eMfwWE$3aA~|AMi$rqNupXAAm|=~?!wJpG zu)Z#GvJGpX5a$@ycERKu*0aLZ4^wL)F2Jx>F#QdyRfr1>>s}E!%&@*MW(>#1tk8@w z%-4`){-4&q1w5|fI`7`yo!tv8-V1;Pp8y3u^uUu8Ey4M ziohT05;MIgVY~ zlAX9tQuRM)X6_4ENB+JniF;?xJ!j_3dCj>qckh}797E<`;A+?$L^~0)1X^Lv0}?gg zhpRDj8YE5TJAgEse+x*9`2&;_<^f=Kny=&7WtM=m!Mqve8_a9qu-n`KoPP5`ln2bm z03S4e9gv;o<2VkR5%jgo{4>Bu%$I<3zR)4;VG32-h}7H*bkpnwc9Z!cj-6%;j{Rl>+S_UVOB{!d+RuQ!8FPIj z$l5joR7ad%@PvCllkRnty1Pz^E%hgT5?>~qGK99>20_s!1iav){x>pk^ui;U$(A`Vy(|4Gn zB)IM!(|i?oC66Q0ZCojx?h$FV!^$i!J&v$?S$5x-!@FPhQt`qk^3&6Zdb zrlxi!2MafrkP=u}*=BC$5qD5Q^0&$>*!uWMh+(xWH7vieD3svu0x!^zUlx+Rbq)|J zqQ;4hohyG8`0L{)Rp8@j%hF0D-Vs97czEmt3TX&ynjm4R>44^7 zmSYz++$nVFbW!V$kK`S9?WIoRI-S=$!}969!2zq$N+C4*!4lZSBl1bGSzAZTHe(Nr zfenqun$XtL>J!6V9dxlRZXRAi<8PN%hwtc0b6puR9mC>&2&n@U#;QGRpQ}Cg8K;YD z5~eoS8mg~liL%9!e?{q$2QwbML)svttd}TtWRy+7fuVFd!xgu8)oVlTQcxCoS9EiS zwgl>EgEnAC8#ic(BXc}F2{Z9}-$oia!dEX`NM4}syzOqT<^@|^FSt>9!A(m2%mLnF z$9@@)W85%J0Ex|8Fj&V@$-;VAF_23y7clrfmT6R)Gtu-zV#2KsY=Zm;y_PDyYpZL0 zxP9w2f)NSt_Vb7puJmiBiU8aFaM#7C_xG^ScmOgk+~!~_6ZMMgb}~S(|2e`Bc{!$$ zYxi^6CQ9M>m-0R0A*S8Xa$TxMdnCnFzgFYg3$Q4`Qec$_Ee45ufUaSQ#kfIMyF~L+ zr_*tjW#oqNgmjTsY2KC$`Q!m@0Uv4icv%4@LjkvMd zorc|($Y7$bNyL^|fYUU*AUQCqeo8^4W*izmgu*c1qBOt z4MTZ~ns9ZvZ4GL;O}54JvkHan+9taadSYHtP17q~O?OC5uhM3_I;hq)TCHofS`nC$ zYk1<$da&n-JDou->v+sFgFk9+ka5*8wUaJ0`?`9IEb&BlBIYT9W@1;jT`?JmR8$~e zu^gM|3X@SFZCBY_Z9r)ph2v7rVNzm^oN|_g3V#%=*GAG%P56zDwQv_rt}dGWa%|CN z=FZ9fh$`dh`;p)nRkKr98Y)|hI3pI0EN}^e(<<y z;q`hf4Kk!OYI{>&5g`dHONJN%xh!6LO4F_ijYv{Bw#s1LmP|-5^(QPUT_F)6U8$yv z6;QgKc)>VX$V$I@I$ZU*sf^d!b*Nn7GR^I*W1dygB}3bKSKvrp+zhyDxjA@)U(hll z+~5F}n7vbKHzy~`$^2HiksU`dL!#Lv{cbazGbP(5-bMf$*rDiD!Xpe;iuh+HR_7-w-BW;EMtL0 zYMOu7P){e5Y7C$~Tx@0G?`Hh%z#lDZ&)_d)t`9ZVb)sbQ;Xi{QR7N#)9)~bhBfmwZ z->1W;aS)%!A@T<}n3t(4HW)P_{ewHJUK5RPh?Zx?>Qf?WT@|7tnyj~ll@+mlqRzT5 z8a^eWS&`T$!au>~nDupmwatimS%`hOT6aanfZ&IOPYDs)&?Z*AAYyL|tcga~^|WAbol2KQ%cBxB&?0ISsCh<$?s7q5Ry2oh zYZb9;qPc&WWBs>;6&h_7%@^@~AvA2gCSu{-IdLOZ)(NZtM?z&O`~L;mukRE)!z-+# zq3#wD1AkY9bx|}tDZ*JH!vEa}{WsGTE#WVmJt`Tw*=3|*2N>zc8Z(%G#o<`&2secK zP4rnlYF+Qw|6yCA;a(vwiP*8wzJ%CCWLVr5SbqjTIE14iG*0Y=Aq(*p(QrSZ*Yydk zi(?qCh@0q~)FY_*g0Q+o42EgVh?@IE!xMxachOt-iC&`kvRI81#%z6@(ANQ7yl`J= zW8&V>4bII|Pc8rqz|e^GZ_Y@{|X&?uuo$GSZVSrOkSyMl?;M6DUZZQTsc&WY8Ji8Z0#gs7!M z>^V^vy0ukwKP%Q-8$|q)Xg(&es{omw6YbI_t-S)<3v8uLGh+MOVsJ=owVn`n>=k`O zVi2XCy<(U!viPYGoc4+yKH(6GlI?_#iuS1Wq!>s_ZfC?T5^sP`NiSX_@WQ7MC)9MuKuP}>!4vY2s#n4_+zgINA6CkL0^#jHwdUxH2PxOWxvf(Dg%nhRVez7Gq z&<5`uXcgky5I%(7bWOBh6Kk)DnoFYf80JUtXbq7`^jr~}!QCpjV-u`z75-ZHia|oy zEjCHkV&NJ{q~?eig4vvdaoj1|?Y=7XYVziK|K@=^|lDIda~8peLf@J>lktSK2h^5eEcJbD6f7nw9$n5cMqZRC&myn zX@)<3wo|lGJMhu)=gpVNP1jsxH{E(}3aL5SA3#o8yt>L*0_iR;!C{`WQv>IH#aKt$nGP7Y1m86n;lwN@LA;8yE?GT1kS z&~ds1WJIl_2vPgrHFRC{0FANcqG-o_1=Eck6YZf}61u#G%CV=2k7#Ifi%dgce+}s3 zAD>b#S|^uK)|`Kr6U<{g!NmS}Negvo;k>{PqSaiNGro0M4AJ1vvCDgxnE-L5Fok)VwX$K*O8QiMG9> z_KN7Kh}+JIb>}E5t+*~)IO@_=Z(cyeu&#;PKO*ggtruYxp%qB?zJvXAO8)4H(`A!C z+(-M9KT7&20d#WThEG!3#fL!RN7YCm-J|?YP<|&Uzl%}^7v+3dtV7CintEtUi&%@PjnWL0Ds8AlVU_&^a1#K51fY!^zV$v_ zJ}1Oup_^O9+R&C(mvkLS*Kj-V$`z=Pb@LQ?RCE%YjQwJ$yA|z_k*^L7v_te)A<92R zQ+KsuhPr~v6qHy{_hxhkZg~7P8b*IYnCHc9)+}=cFC( zRri`kIw7U-f*S_|cL8d`U!2wy-bk*%I; ziCT)%Q!KbpPw`ZXxFKqNPuy|=Q)o{!7}(k(@SFm|Wce+f-cdBfuZV`jO>i2dcd{E# z|Ir;noDV-38a9#Et)R3HQqbIE+=0&Xs1;O@)-Ojj+{z~YfHLtp z*xRxuj+@1$CN6L5K1Zg_hj(Gx{yUk77==N9V~QHb4Nr>+ovnEt#x+jqYYp;Q8Y||- z_@e@8(z;KH#9o>|am18?WAmp3Vik+~8Bu$c99O2~e<)g$gbj7MFUc6QLpgA zXK)WvAFeqn8pB^TLXYF&iJeESE5~7^9S*%zS5~ylNc(?Tth*|8fc$}GcS_w>E4AG2 zf-u`Ph7+OPrbvVfM?+(+$JJjy&Z6-K(QzEJ<341l{=$9@Sz2(A=~CPV>#IYXDM6SG zBnVltQZeKt2w71-gBu{Q(})|O(Cvxi`0H1HqJBd^1VewgjNI^jLumY~#!&e5kQ8b7 zapW>9!*3eBqVclmsBr3Q{-L~iL-fBG{_t7rl{@9^yoMtJvK}*xM?X5*#@>L6DU1%| zysV9d$p|*%kM5t@w#1jnw)o&04h72)uLtOF8nwedt_ky+7`PJ#6j$*CnV8Z|dD(sc>UeVDt#bO%z4;EzOLz&ifcyJ`f^2x^O{Z=xHPt*ta;&~kxxr^hgm zWrym_7&KJ9T0*l0EgA68TVAB0^MQxXqvj<5@-n$zKr08KKcT9-BJc{Tf^PhI9e^hg z`FatWMZ|aMfV2Bv}v89G1`lf_TNHL2r?CXf~K^R4T2lg^Gmaxu)9@DW4U#`tx26s97 ze{NnK$?m)mljcwcQnFju7E_j+UJ*wLYg~Ip z6Rh-{oG)JDTa3_A5q=76^a*pHxV;|=Y1SiV%LdEM`+gAJDj$4GweQr^L$rGSHusCQd6TSTzwH(Nkj65Z$I?Sq|&3Sb}Ijg`5s6 zdsjsLvm(wn_lTw&t$T3?l|X-(4J)2r!;5eX*PP3XcD|HJ8+%jflga#IYA!cDxlqm( z@(h_Or^=O*EM`h2fRm$!ot(6bnbfp>B2~&5Xn^PmSV}Es>`Z=|E2oONvaFS63&nD0 z4nHhna}jjula&Q}&W)(%3h9$Jt`+8{?LszN!t0p{Ea?}@MPojN-}tfZ(wR~^H8*FM z<_h^4B6twZ*y*`khFS&%8k8*2SVkCryfA+vmr?u|7c<3))V!=tROaS91Kpj<%~gsS z!_F2nnPR3yjWLW!+QF^Wb z=`kdqErP8QjfsRITbr$vrwga@BN#HtOJbH-A3$WfnM_%Y-_FyxY`nt5P8a4X^GbWn z5pfKL?DS##9B1$#d7MzT*j-E-O2r9 z%-$}r1Wn5Z_Dme^92wl&Ik3HVpm(5iuzzrS|F*6DojvpAN$;b7@pl#q{is z)3m=x%mv5BisX0dXCj_XSgPP&?hik%2W@9J%b%DH=VJkQ{@ylI8m6Y zq-Ui!G2n11Gj=+aE=&CETxzBSDa{wAGe$m@FO=ponFYxtkaCNuGC~CSD=i>MJO~bL zHEJ+Ly>ZNtA}<%psX6xhGQPlPXJNMa0{1D0E{%=bkY10bfXDzu4cBtC%h=2vcasKx z=|w3cQXILmYS$ zvGK_xWA@11!xIx@`;Dwkzd<&inJ*O2*o7j(fSpP|RLN0vu@OVxPr7nD$Sfj$0fH|i zBC5_87Bj}wq2WVgc5)QHma0tW3c7pc5(=5wHOgleGR(!{iCxKwQBT7Z9c1I_LIuHx zI!H|;5ZI}5VLq3(GpAtz`I(H3sLxFdkB&N{UW)g~LJ@%Z1zfT@qLUm!GmVCs#gep1PK<3@-X;&?ncnsnHvL6S(Q`(1^?oRZ~%;lop7qedR;q}fLG$?DAIIo5+SdYAG1 z$!soL;PFk29X+JR7gQz`H3yE!=+RM7myntvHOWoxCAWmFBukT(@&ZhQtRSGLrnn&BJqrz`W&H>&S+TVDarzQ5ERa|;LFCT)FMyH2IhbwSrqreA Hd`h!A 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 diff --git a/engine/src/log.c b/engine/src/log.c new file mode 100644 index 0000000..4d196c3 --- /dev/null +++ b/engine/src/log.c @@ -0,0 +1,32 @@ +#include "log.h" +#include +#include +#include +#include + +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; +} diff --git a/engine/src/log.h b/engine/src/log.h new file mode 100644 index 0000000..fb9e107 --- /dev/null +++ b/engine/src/log.h @@ -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 diff --git a/engine/src/looper.c b/engine/src/looper.c index 62a9fec..ca814c5 100644 --- a/engine/src/looper.c +++ b/engine/src/looper.c @@ -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) { @@ -569,8 +569,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); } } diff --git a/engine/src/looper.o b/engine/src/looper.o deleted file mode 100644 index fa3b2594d158f4ed8228b02c145e7970c860e250..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 38944 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
  • J1q@3XRJ*w9P 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 j+yhI>&VZjRmJ@N1j33k_OsH~(Tc{1|1- #include #include @@ -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; } diff --git a/engine/src/main.o b/engine/src/main.o deleted file mode 100644 index c3ba94bfa145ad94d796cb442a1d1ee0df009de4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9664 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 diff --git a/engine/src/midi.o b/engine/src/midi.o deleted file mode 100644 index f824688b82c46af603b9836871d6a6a84ca83596..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13712 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 diff --git a/engine/src/pipe.o b/engine/src/pipe.o deleted file mode 100644 index 70b4a740886b7c90c51990bc5561bc400d563b9e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13152 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 OQT&fPOG*yt^8W#F&hPF3 diff --git a/engine/src/queue.o b/engine/src/queue.o deleted file mode 100644 index fd9df5325d3d4682ebd4ab0b873b1203a618c0c7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6168 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 C6fExm diff --git a/engine/src/wav.o b/engine/src/wav.o deleted file mode 100644 index 6d48fbefe91c6c388594c6c82a6483f47650882e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8328 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 diff --git a/looper b/looper deleted file mode 100755 index 03efe4f7750adc8436311fbc0717e60750839458..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37568 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 diff --git a/makefile b/makefile index 1ad841c..7ed3e75 100644 --- a/makefile +++ b/makefile @@ -2,13 +2,16 @@ SUBDIRS = engine client -.PHONY: all build clean test check format $(SUBDIRS) +.PHONY: all build clean test check format orchestrator $(SUBDIRS) -all: build +all: build orchestrator build: $(SUBDIRS) @echo "Build complete." +orchestrator: orchestrator.c + $(CC) -Wall -Wextra -std=c11 -o looper orchestrator.c + $(SUBDIRS): $(MAKE) -C $@ @@ -17,6 +20,7 @@ test: $(MAKE) -C client test clean: + rm -f looper @for dir in $(SUBDIRS); do \ echo "Cleaning $$dir..."; \ $(MAKE) -C $$dir clean; \ diff --git a/orchestrator.c b/orchestrator.c new file mode 100644 index 0000000..4e3d588 --- /dev/null +++ b/orchestrator.c @@ -0,0 +1,51 @@ +#include +#include +#include +#include +#include +#include + +static pid_t engine_pid = 0; +static pid_t client_pid = 0; + +static void cleanup(int sig) { + (void)sig; + if (engine_pid > 0) kill(engine_pid, SIGTERM); + if (client_pid > 0) kill(client_pid, SIGTERM); + while (wait(NULL) > 0); + _exit(0); +} + +int main(int argc, char *argv[]) { + signal(SIGINT, cleanup); + signal(SIGTERM, cleanup); + + engine_pid = fork(); + if (engine_pid == 0) { + execl("./engine/looper", "looper", NULL); + perror("execl engine"); + _exit(1); + } + + client_pid = fork(); + if (client_pid == 0) { + if (argc > 2 && strcmp(argv[1], "-s") == 0) { + execl("./client/looper-client", "looper-client", "-s", argv[2], NULL); + } else { + execl("./client/looper-client", "looper-client", NULL); + } + perror("execl client"); + _exit(1); + } + + int status; + pid_t exited = wait(&status); + if (exited == engine_pid) { + kill(client_pid, SIGTERM); + wait(NULL); + } else if (exited == client_pid) { + kill(engine_pid, SIGTERM); + wait(NULL); + } + return 0; +}