Files
looper/client/PLAN.md

137 lines
8.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Plan: Refactor TUI into Standalone FIFOClient Binary
## Goal
Extract the TUI from the existing monolithic codebase into a separate `looper-client` binary that communicates with the engine **only** via the FIFO pipe (`/tmp/looper_cmd`).
The TUI must **not** link to any engine source files (`engine.c`, `dispatcher.c`, `carla.c`, etc.) or use their headers beyond shared type definitions (e.g., `command.h`). All state is maintained by the engine; the TUI sends commands and assumes they succeed.
## Background
- The looper engine runs as a separate JACK client and listens for commands on:
- FIFO pipe (`/tmp/looper_cmd`) textbased commands
- `looper:control` MIDI noteon events
- The current TUI uses a local `Engine` / `AppState` / `dispatcher` that does **not** talk to the real looper. It was designed for unit testing.
## Task 1 Create a new `client/` directory structure
- Keep existing `client/src/tui.c` and `client/src/tui.h`.
- Remove all `#include` directives that reference engine internal headers:
- `engine.h`
- `dispatcher.h`
- `wav_io.h`
- `transport.h`
- `carla.h`
- Remove any `Engine*`, `AppState`, `DispatchFn`, `Clip`, `MidiClip` usage.
- **Keep** the `FuzzySearch` struct, `draw_rack_view()`, `handle_rack_view()`, `list_wav_files()`, `load_sample_callback`, mouse handling, etc. they **are not removed**.
However, every call to engine internals (e.g., `carla_get_available_plugins`, `dispatcher_get_state`, `g_dispatch`, `carla.h` functions) **must be replaced** with a stub that does nothing (or prints a debug message) until the engine implements the corresponding FIFO commands.
This keeps the UI code compilable and preserves the structure for future implementation.
## Task 2 Implement `send_command()` and open FIFO
- Add a function:
```c
int send_command(const char *cmd_line);
```
This function:
- Opens `/tmp/looper_cmd` with `O_WRONLY`.
- Writes the command string (e.g., `"record 0\n"`).
- Appends a newline if missing.
- Closes the file descriptor.
- Returns 0 on success, -1 on error (prints diagnostic to stderr).
- The TUI should call `send_command()` for every user action that should affect the engine.
## Task 3 Map TUI keys to FIFO commands
Replace each `g_dispatch(action)` with a direct FIFO command string.
**Proposed mapping (simplified can be refined later):**
| TUI key/action | FIFO command |
|--------------------------------------------|-----------------------------------------------------|
| `'t'` (trigger clip at selected cell) | `record <channel>\n` (channel = `selected_col`) |
| `'d'` (reset clip) | `stop\n` (global stop) |
| `'s'` (trigger scene current row) | `"scene_next\n"` (or `"scene_add\n"`? TBD) |
| `' '` (toggle transport play/pause) | No corresponding FIFO command yet. Omit for now. |
| `'S'` (stop transport) | `"stop\n"` |
| `'q'` (cycle quantize) | No FIFO equivalent ignore. |
| `'x'` (reset transport) | `"stop\n"` |
| `'N'` (play next scene) | `"scene_next\n"` |
| `'P'` (play previous scene) | `"scene_prev\n"` |
| `'u'` (undo) | No FIFO equivalent ignore. |
| `Ctrl+R` (redo) | Ignore. |
| `'v'`, `'V'` (visual mode) | Keep visual selection logic but send commands only for `'d'` and `'y'` actions. |
| `'y'` (yank) | Do nothing (local clipboard only). |
| `'p'` (paste) | For each pasted cell send `"record <ch>\n"`. |
| `'m'` (move mode) | No effect on engine local navigation. |
| `'z'` (zoom grid selector) | Local navigation only. |
| `'G'` (toggle audio/MIDI grid) | No FIFO command ignore. |
| `'-'` / `'='` (volume) | No FIFO command ignore. |
| `\t` (switch to rack view) | Remove entirely. |
| `':'` command mode | Keep for `:q` (quit) and `:rack` commands (the latter can be removed). |
| Escape / `'Q'` | Quit the TUI (no command sent). |
**Channel binding:**
- When the user moves selection to a new column, send `"bind <col>\n"` to ensure subsequent commands affect the correct channel. This can be done in the navigation switch cases.
## Task 4 Remove all references to `Engine` and `dispatcher`
- Delete the lines:
- `static Engine *g_engine = NULL;`
- `static DispatchFn g_dispatch = NULL;`
- Replace calls like `g_dispatch(action)` with `send_command(formatted_string)`.
- Remove `dispatcher_get_state()` calls the TUI will no longer query the engine state. Update `draw_cell()` to display only static info (clip index) or a fixed colour (e.g., all green). The statedependent colouring is not available without feedback from the engine. For now, show all cells as idle (white) or use a placeholder.
- Remove the line `AppState state; dispatcher_get_state(&state);` inside draw functions.
## Task 5 Simplify the `tui.h` header
- Replace the function signatures:
```c
void tui_init(void); // no Engine* argument
void tui_run(void); // no Engine* argument
void tui_cleanup(void);
```
- Remove `#include "engine.h"` and `#include "dispatcher.h"`.
- Remove the `Engine*` parameter from the init and run functions.
## Task 6 Create `client/main.c`
- Write a simple `main()` that:
- Optionally opens the FIFO for writing just to check it exists.
- Calls `tui_init()`.
- Calls `tui_run()`.
- Calls `tui_cleanup()`.
- Returns 0.
## Task 7 Write `client/makefile`
- Target `looper-client`:
- Compile `src/tui.c` and `src/main.c` (or `src/client.c` if split).
- **Do not** link to any engine `.o` files.
- Link only with `-lncurses` (and `-lm` if needed).
- Example:
```makefile
CC ?= gcc
CFLAGS ?= -Wall -Wextra -g -I../engine/src
LDFLAGS ?= -lncurses -lm
looper-client: src/tui.c src/main.c
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
```
## Task 8 Remove dead code and unnecessary helpers
- Delete `utils.c` / `utils.h` if they existed only for TUI but keep all WAVrelated, rackrelated, and fuzzysearch code (they are now stubs).
- Remove `clip_state_to_string()`, `transport_state_to_string()`, `quantize_mode_to_string()`, `clock_source_to_string()` they are no longer used for display because we have no `AppState`.
Replace them with static strings that show placeholder text (e.g., `"N/A"`).
- Remove `state_to_color()` instead use a fixed colour pair (e.g., all cells white) or remove colour entirely, because we have no clip state.
- Remove the `mouse` callback if it relied on `dispatcher_get_state` but keep the function body as a noop.
## Task 9 Test the new client
- Build `looper-client` and verify it compiles without engine object files.
- Start the engine (`./looper` in `engine/`).
- Run `./looper-client` and press keys that should generate FIFO commands. Use `cat /tmp/looper_cmd` in another terminal to verify output.
- Check that commands like `record 2`, `stop`, `bind 3` appear.
## Notes / Future Improvements
- **State feedback:** The TUI currently shows clip state colours. To restore that, a separate FIFO (or shared memory) for engine>client status could be added. Not part of this plan.
- **MIDI grid / rack view:** These depend on engine features not yet exposed via FIFO. They are removed; can be readded later.
- **Transport commands:** The engine does not have a dedicated transport play/pause command via FIFO; it relies on MIDI notes. Future FIFO extension needed.
This plan produces a clean, minimal client that interfaces only through the named pipe.
````