feat: add Carla plugin host stubs and integration plan

This commit is contained in:
Loic Coenen
2026-05-14 22:11:01 +00:00
committed by Loic Coenen (aider)
parent 5cffec86e7
commit dafc7fe46b
10 changed files with 246 additions and 142 deletions

View File

@@ -0,0 +1,110 @@
# Integration Plan: Carla Plugin Host (Clientside)
## 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 loopers JACK ports and the plugins 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 Carlanative 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 loopers ports (usually via `jack_connect` called once at startup).
- In the process callback, copy audio between looper ports and plugin ports (using Carlas `process()`like functions).
- This keeps the engine completely unaware of plugins.
## 3. TUI commands (colonmode)
All already exist in the plan; only the implementation target changes:
- `:from <port>` → describe a looper output port (e.g., `looper:out_0`)
- `:to <port>` → destination port (e.g., `plugin1:in_left`)
- `:addplugin <path>` → loads the plugin and, if `from`/`to` are set, connects them automatically
- `:connect` → creates a JACKconnection between the stored `from` and `to` (or, if one side belongs to a plugin, uses Carlas 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 **clients internal Carla handle** instead of the status FIFO.
## 5. Integration Tests
### 5.1 Mock plugin (clientside)
- 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 TUIs 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).
- **toplevel 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 Carlas `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 <path>`.
- Call the underlying `plugin_load` and update the internal plugin list.
4. **Implement rack view (TUI)**
- Toggle between grid view and pluginlist 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.