- Rust 100%
|
|
||
|---|---|---|
| control | ||
| patchbay | ||
| recorder | ||
| studio-core | ||
| .gitignore | ||
| AGENTS.md | ||
| Cargo.lock | ||
| Cargo.toml | ||
| LICENSE | ||
| README.md | ||
| TASKS.md | ||
tui-studio
A modular TUI-based multitrack recording environment for Linux, inspired by the studios of my youth — a mixer, a couple of ADATs or DA-88s and a way to plug in FX. Built on Pipewire for audio transport.
Project structure
| Crate | Description |
|---|---|
studio-core |
Shared types, metering utilities, IPC protocol messages |
recorder |
Multitrack recorder TUI binary (the application) |
control |
Remote control panel (separate binary, connects over TCP) |
patchbay |
Virtual patchbay TUI (separate binary, connects over TCP) |
Dependencies
- Pipewire — audio capture
- ratatui + crossterm — terminal UI
- hound — 24-bit WAV writing
- serde + toml — project file serialization
- serde + serde_json — IPC protocol
Getting started
Start the recorder:
cargo run --release -p recorder
Start with a project and remote control:
# Terminal 1 — recorder (set control_port in config.toml first)
cargo run --release -p recorder -- -p myproject
# Terminal 2 — control panel
cargo run --release -p control
Recorder CLI flags
| Flag | Default | Description |
|---|---|---|
-r |
48000 | Sample rate (Hz) |
-b |
24 | Bit depth |
-p |
untitled | Project name |
-o |
./recordings | Output directory |
--bpm |
120 | Project BPM |
Track count comes from the project file (start with 0, add tracks with +).
Config file
Settings like count-in bars and control panel port are read from
~/.config/tui-studio/config.toml:
count_in_bars = 1
control_port = 18726
If the file doesn't exist, defaults are used. Create it to customize.
| Setting | Default | Description |
|---|---|---|
count_in_bars |
1 | Count-in bars before recording (0 = disabled) |
control_port |
0 | Enable IPC control panel on this port (0 = disabled) |
Control panel CLI flags
| Flag | Default | Description |
|---|---|---|
--connect |
127.0.0.1:18726 | Recorder address |
Patchbay
A visual routing matrix that shows all available Pipewire audio sources and recorder track inputs, with the ability to make and break connections.
# Terminal 1 — recorder (set control_port in config.toml first)
cargo run --release -p recorder -- -p myproject
# Terminal 2 — patchbay
cargo run --release -p patchbay
Patchbay CLI flags
| Flag | Default | Description |
|---|---|---|
--connect |
127.0.0.1:18726 | Recorder address |
Keybindings (recorder)
| Key | Action |
|---|---|
Space |
Toggle recording |
h / l |
Select previous / next track |
g / G |
Jump to first / last track |
Enter |
Arm / disarm selected track |
a / d |
Arm all / disarm all |
i |
Open input source selection popup |
r |
Rename selected track |
? |
Toggle help popup |
+ / - |
Add / remove track |
q / Esc |
Quit |
Keybindings (control panel)
| Key | Action |
|---|---|
Space |
Toggle recording |
h / j / Left / Up |
Select previous track (wraps) |
l / k / Right / Down |
Select next track (wraps) |
g / G |
First / last track |
Enter |
Arm / disarm selected track |
a / d |
Arm all / disarm all |
i |
Open source selection popup |
r |
Rename selected track |
? |
Toggle help popup |
+ / - |
Add / remove track |
q / Esc |
Quit |
Keybindings (patchbay)
| Key | Action |
|---|---|
j / k / Down / Up |
Select prev / next source |
h / l / Left / Right |
Select prev / next track |
g / G |
First / last source |
Enter |
Connect / disconnect selected source to selected track |
d |
Disconnect selected track |
? |
Toggle help popup |
q / Esc |
Quit |
IPC protocol
The recorder and control panel communicate over TCP using length-prefixed JSON messages. Each message is a 4-byte little-endian length followed by that many bytes of JSON.
Messages are defined in studio-core::ipc:
| Direction | Message | Fields |
|---|---|---|
| Control → Recorder | ControlCommand |
RecordToggle, ArmToggle(idx), ArmAll, DisarmAll, AddTrack, RemoveTrack, SelectTrackSource {track, source}, RenameTrack {track, name}, or Shutdown |
| Recorder → Control | RecorderStatus |
recording: bool, elapsed_secs: f64, tracks: [{name, armed, meter_peak, input}], sources: [{node_id, port_id, name, node_name}] |
License
GNU General Public License v3.0 or later.