Files
looper/breakup.md

5.3 KiB
Raw Permalink Blame History

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.