···2233## Project overview
4455-Rockbox Zig is a modern wrapper around the [Rockbox](https://www.rockbox.org) open-source audio player firmware. It adds Rust/Zig services on top of the C firmware to expose gRPC, GraphQL, HTTP, and MPD APIs, a Typesense-backed search engine, Chromecast/AirPlay/Snapcast output sinks, and a desktop/web UI.
55+Rockbox Zig is a modern wrapper around the [Rockbox](https://www.rockbox.org) open-source audio player firmware. It adds Rust/Zig services on top of the C firmware to expose gRPC, GraphQL, HTTP, and MPD APIs, a Typesense-backed search engine, Chromecast/AirPlay/Snapcast/Squeezelite output sinks, and a desktop/web UI.
6677The binary is called **`rockboxd`**. It is a single executable built by Zig that links:
88- The Rockbox C firmware (compiled by Make into `build-lib/libfirmware.a` and friends)
···1818build-lib/ Out-of-tree Make build directory (generated; do not edit)
1919crates/ Rust workspace
2020 airplay/ ALAC encoder + RAOP/RTP sender (AirPlay 1 output)
2121+ slim/ Slim Protocol + HTTP broadcast server (Squeezelite multi-room output)
2122 cli/ Entry point compiled to librockbox_cli.a (staticlib)
2223 server/ gRPC / HTTP server
2324 settings/ load_settings() — reads settings.toml, applies sinks
···8788audio_output = "airplay"
8889airplay_host = "192.168.1.x" # RAOP receiver IP
8990airplay_port = 5000 # optional, default 5000
9191+9292+audio_output = "squeezelite"
9393+squeezelite_port = 3483 # optional, Slim Protocol port (default 3483)
9494+squeezelite_http_port = 9999 # optional, HTTP PCM stream port (default 9999)
9595+```
9696+9797+Run one or more squeezelite clients pointing at rockboxd for multi-room:
9898+```sh
9999+squeezelite -s localhost -n "Living Room"
100100+squeezelite -s localhost -n "Kitchen"
90101```
9110292103## PCM sink architecture
···9510696107| Enum constant | Value | Implementation file |
97108|--------------------|-------|---------------------------------------------|
9898-| `PCM_SINK_BUILTIN` | 0 | `firmware/target/hosted/sdl/pcm-sdl.c` |
9999-| `PCM_SINK_FIFO` | 1 | `firmware/target/hosted/pcm-fifo.c` |
100100-| `PCM_SINK_AIRPLAY` | 2 | `firmware/target/hosted/pcm-airplay.c` |
109109+| `PCM_SINK_BUILTIN` | 0 | `firmware/target/hosted/sdl/pcm-sdl.c` |
110110+| `PCM_SINK_FIFO` | 1 | `firmware/target/hosted/pcm-fifo.c` |
111111+| `PCM_SINK_AIRPLAY` | 2 | `firmware/target/hosted/pcm-airplay.c` |
112112+| `PCM_SINK_SQUEEZELITE` | 3 | `firmware/target/hosted/pcm-squeezelite.c` |
101113102114`crates/settings/src/lib.rs:load_settings()` reads `audio_output` and calls `pcm::switch_sink()`.
103115···120132 - `rtsp.rs` — synchronous RTSP client: ANNOUNCE (SDP) → SETUP → RECORD
121133- `pcm_airplay_connect()` is called once per `sink_dma_start()` (idempotent if already connected).
122134- The `rockbox-airplay` rlib must be force-included in `librockbox_cli.a` via the `use rockbox_airplay::_link_airplay as _` shim in `crates/cli/src/lib.rs`.
135135+136136+### Squeezelite sink (Slim Protocol + HTTP broadcast)
137137+- `crates/slim/` implements a Slim Protocol TCP server and an HTTP PCM broadcast server, both in pure Rust.
138138+ - `slimproto.rs` — accepts squeezelite connections; sends `STRM 's'` pointing at the HTTP port; replies to every `STMt` heartbeat with `audg` to prevent squeezelite's 36-second watchdog from firing.
139139+ - `http.rs` — concurrent HTTP server (one thread per client); each client gets an independent `BroadcastReceiver` cursor into the shared buffer, enabling true multi-room playback.
140140+ - `lib.rs` — `BroadcastBuffer`: sequence-numbered chunks, per-reader cursors, 4 MB cap with oldest-first eviction; lagging readers skip forward rather than blocking the writer.
141141+- `firmware/target/hosted/pcm-squeezelite.c` paces the DMA loop to real time using `CLOCK_MONOTONIC`. **Use `int64_t` for the nanosecond diff** — unsigned subtraction wraps catastrophically when `tv_nsec` rolls over.
142142+- The `rockbox-slim` rlib must be force-included via `use rockbox_slim::_link_slim as _` in `crates/cli/src/lib.rs`.
143143+- **Slim Protocol framing**: client→server is `opcode[4] + u32_t length BE + payload`; server→client is `u16_t length BE + opcode[4] + payload` (length does NOT include the 2-byte length field itself).
144144+- **ASCII-encoded PCM fields in STRM**: squeezelite subtracts `'0'` from `pcm_sample_size`, `pcm_sample_rate`, `pcm_channels`, `pcm_endianness`. Correct values: `'1'` (16-bit), `'3'` (44100 Hz), `'2'` (stereo), `'1'` (little-endian).
123145124146## Key cross-cutting concerns
125147···175197# Verify AirPlay symbols are present
176198nm zig/zig-out/bin/rockboxd | grep pcm_airplay
177199200200+# Verify squeezelite symbols are present
201201+nm zig/zig-out/bin/rockboxd | grep pcm_squeezelite
202202+178203# Verify a crate is in the staticlib
179204ar t target/release/librockbox_cli.a | grep airplay
205205+ar t target/release/librockbox_cli.a | grep slim
206206+207207+# Multi-room squeezelite test
208208+squeezelite -s localhost -n "Room 1"
209209+squeezelite -s localhost -n "Room 2"
180210```