# Integration Plan: Carla Plugin Host (Client‑side) ## 1. Strategy Carla lives in the **client** (the TUI), not the looper engine. The client becomes a lightweight JACK client itself, capable of loading plugins via the Carla host API and bridging audio/MIDI between the looper’s JACK ports and the plugin’s ports. ## 2. Client (TUI) – new modules ### 2.1 Carla dependency - Link the client binary with `libcarla_host` (C static/shared library). - Use the official `carla_standalone.h` API. ### 2.2 New files `client/src/plugins.c` / `plugins.h` Functions (same signatures as before, but run **inside the client**): - `int plugin_load(const char *binary, const char *plugin_id, int *out_id)` - `int plugin_unload(int id)` - `int plugin_connect(int id, const char *port_name, const char *looper_port)` - `int plugin_disconnect(const char *from, const char *to)` - `void plugin_set_bypass(int id, bool bypass)` The module owns the list of loaded plugins, their Carla‑native IDs, and the mapping between looper JACK ports and plugin ports. ### 2.3 JACK client for plugin I/O - In `client/src/tui.c` (or a new `client/src/jack_io.c`), open a JACK client with `jack_client_open()`. - Register input/output ports that will be connected to the looper’s ports (usually via `jack_connect` called once at start‑up). - In the process callback, copy audio between looper ports and plugin ports (using Carla’s `process()`‑like functions). - This keeps the engine completely unaware of plugins. ## 3. TUI commands (colon‑mode) All already exist in the plan; only the implementation target changes: - `:from ` → describe a looper output port (e.g., `looper:out_0`) - `:to ` → destination port (e.g., `plugin1:in_left`) - `:addplugin ` → loads the plugin and, if `from`/`to` are set, connects them automatically - `:connect` → creates a JACK‑connection between the stored `from` and `to` (or, if one side belongs to a plugin, uses Carla’s internal connect) - `:disconnect` - `:rack` → toggles rack view (list of plugins with ports and bypass status) - `:grid` → back to the original grid view ## 4. Rack view (TUI) Identical to the original description, but the data comes from the **client’s internal Carla handle** instead of the status FIFO. ## 5. Integration Tests ### 5.1 Mock plugin (client‑side) - Create `client/tests/mock_plugin/mock_plugin.c`. - A trivial JACK client that copies input to output and adds a 1 kHz tone when the input is silent. - Compiled to `libmock_plugin.so` → the TUI’s plugin loader will load it. ### 5.2 Test infrastructure (client/tests) - Start the TUI in a headless test mode (or fork a child and feed it commands via stdin). - Observe the status output (sent to stdout or a temporary file) to verify plugin list and connections. ### 5.3 Test cases (new file `client/tests/test_plugin_client.c`) - `test_plugin_load_unload` – load mock plugin, confirm list shows 1 entry, unload, confirm list empty. - `test_plugin_connect_audio` – load plugin, connect to looper ports, inject audio from a test JACK client, verify plugin output reaches a monitor port. - `test_rack_view` – send `:rack` command, parse the printed lines, verify they match the expected layout. - (Later) `test_bypass` – load, bypass, verify audio passes unaltered. ## 6. Build System Changes - **client/makefile**: - Add `-lcarla_host` to `LDFLAGS`. - Add `plugins.c` (and optionally `jack_io.c`) to `SRCS`. - Build mock plugin as a separate target. - **engine/makefile** – no changes (engine stays pure looper). - **top‑level makefile** – no changes. ## 7. Implementation Steps (ordered) 1. **Add Carla dependency & stub tests (client side)** - Link client binary with `-lcarla_host`. - Create `client/src/plugins.h` / `client/src/plugins.c` with stub implementations. - Create `client/tests/test_plugins.c` with failing (**Red**) unit tests for: - `plugin_load` returns -1 on NULL binary - `plugin_unload` returns -1 on invalid id - (optional) `plugin_connect` returns -1 on invalid id - Add a `test` target in `client/makefile` that builds and runs `test_plugins`. - Verify the tests compile and pass (**Green**). 2. **Implement real Carla integration (client side)** - Open a private JACK client inside the TUI using `jack_client_open()`. - Implement `plugin_load` / `plugin_unload` using Carla’s `carla_new_native` etc. - Write integration tests that load a mock plugin and verify it appears in the rack. 3. **Add `:addplugin` command parsing in TUI** - When colon mode is entered, parse `:addplugin `. - Call the underlying `plugin_load` and update the internal plugin list. 4. **Implement rack view (TUI)** - Toggle between grid view and plugin‑list view. - Display plugin name, ID, bypass status. - Add `B`, `D`, `X` keybindings. 5. **Build mock plugin and write integration tests** - Create `client/tests/mock_plugin/mock_plugin.c`. - Target `libmock_plugin.so`. - Write tests in `client/tests/test_plugin_client.c`: - `test_plugin_load_unload` - `test_plugin_connect_audio` - `test_rack_view` 6. **Polish and document** - Clean up error messages, handle edge cases. - Add comments to new modules. - Update root `README` with Carla instructions.