Serve a raw PCM audio stream to any Squeezelite / Squeezebox client over the SlimProto protocol.
3
stars
14
commits
Rust
primary language
Sep 5, 2026
updated

Serve a raw PCM audio stream to any Squeezelite / Squeezebox client over the SlimProto protocol.
squeezed takes a raw PCM S16LE audio stream from stdin, a FIFO, a unix socket, or a TCP socket, and turns it into a Squeezebox server. Point any Squeezelite instance at it — or let them auto-discover it — and the audio comes out the other end, in sync across as many players as you like.
┌────────────┐ PCM ┌───────────────────────────────────┐ SlimProto ┌──────────────┐
│ ffmpeg / │ ───────► │ squeezed │ ◄──────────► │ squeezelite │
│ any PCM │ stdin / │ • SlimProto control (tcp :3483) │ (tcp) │ (player 1) │
│ producer │ fifo / │ • HTTP audio stream (tcp :9000) │ HTTP audio ├──────────────┤
│ │ unix / │ • UDP discovery (udp :3483) │ ───────────► │ squeezelite │
└────────────┘ tcp └───────────────────────────────────┘ │ (player 2) │
└──────────────┘
stdin, a named pipe, a unix socket, or a TCP listener.sudo squeezed driver install adds a "Squeezed" device to System Settings → Sound; anything your Mac plays to it streams to your Squeezelite players, volume keys included. See Virtual audio device (macOS).-s host.Prebuilt binaries are published for Linux and macOS (x86_64 and arm64) plus FreeBSD/NetBSD (amd64) on every tagged release.
You'll also want a PCM producer (ffmpeg) and a player (squeezelite) — both are in most package managers (brew install ffmpeg squeezelite, apt install ffmpeg squeezelite, …).
apt)Packages are hosted on Gemfury:
echo "deb [trusted=yes] https://apt.fury.io/tsiry/ /" \
| sudo tee /etc/apt/sources.list.d/squeezed.list
sudo apt-get update
sudo apt-get install squeezed
dnf)sudo tee /etc/yum.repos.d/squeezed.repo >/dev/null <<'EOF'
[fury]
name=Gemfury Private Repo
baseurl=https://yum.fury.io/tsiry/
enabled=1
gpgcheck=0
EOF
sudo dnf install squeezed
# From the flake (installs into your profile)
nix profile install github:tsirysndr/squeezed
# Or run without installing
nix run github:tsirysndr/squeezed -- --help
brew install tsirysndr/tap/squeezed
The @tsiry/squeezed package
downloads the prebuilt binary for your platform from GitHub Releases on install:
npm install -g @tsiry/squeezed
Grab a tarball for your platform from the releases page:
# Example: Linux x86_64 — pick the asset matching your OS/arch
curl -fsSL -o squeezed.tar.gz \
https://github.com/tsirysndr/squeezed/releases/latest/download/squeezed-<version>-linux-amd64.tar.gz
tar -xzf squeezed.tar.gz
sudo install -m 0755 squeezed /usr/local/bin/squeezed
Needs a recent Rust toolchain:
git clone https://github.com/tsirysndr/squeezed
cd squeezed
cargo build --release
# binary at ./target/release/squeezed
Optionally drop it on your PATH:
install -m755 target/release/squeezed /usr/local/bin/squeezed
Pipe from ffmpeg, play with squeezelite.
Terminal 1 — start the server and feed it audio from ffmpeg:
ffmpeg -re -i song.flac -f s16le -ar 44100 -ac 2 - | squeezed
-re streams at real-time rate (so playback isn't a fast-forward blur).-f s16le -ar 44100 -ac 2 - emits raw signed 16-bit little-endian stereo PCM to stdout, which squeezed reads from stdin.Terminal 2 — start a player. It auto-discovers squeezed:
squeezelite -n Living-Room
…or skip discovery and point it straight at the server:
squeezelite -n Living-Room -s 127.0.0.1
That's it — song.flac now plays through squeezelite. Start more squeezelite instances (on this or other machines) and they'll all play in sync.
Tip: to verify the plumbing without an audio device, have squeezelite decode to stdout:
squeezelite -s 127.0.0.1 -o - -a 16 > out.pcm
Select the source with --source (or [input] source in the TOML file).
Best for pipelines. EOF on stdin cleanly shuts the server down.
ffmpeg -re -i input.mp3 -f s16le -ar 44100 -ac 2 - | squeezed --source stdin
The server stays up across producers — when one writer closes, squeezed waits for the next.
mkfifo /tmp/squeezed.fifo
squeezed --source fifo --path /tmp/squeezed.fifo &
# feed it whenever you like; the server keeps running between tracks
ffmpeg -re -i track1.flac -f s16le -ar 44100 -ac 2 - > /tmp/squeezed.fifo
ffmpeg -re -i track2.flac -f s16le -ar 44100 -ac 2 - > /tmp/squeezed.fifo
Like a FIFO, but connection-oriented. squeezed creates (and cleans up) the socket.
squeezed --source unix --path /tmp/squeezed.sock &
ffmpeg -re -i input.wav -f s16le -ar 44100 -ac 2 - | socat - UNIX-CONNECT:/tmp/squeezed.sock
Feed audio over the network. squeezed accepts one writer at a time and waits for the next.
squeezed --source tcp --tcp-bind 0.0.0.0:4711 &
# from anywhere on the network:
ffmpeg -re -i input.opus -f s16le -ar 44100 -ac 2 - | nc server-host 4711
Turn your whole Mac into the PCM source: install a Core Audio driver that adds a "Squeezed" output device to System Settings → Sound → Output. Everything the Mac plays to it — Music, Spotify, Safari, anything — is captured by squeezed and streamed to your players, in sync. The device has a working volume slider, and the volume/mute keys apply to what the players hear.
# one-time: install the driver bundle to /Library/Audio/Plug-Ins/HAL
# (restarts coreaudiod — audio apps may hiccup for a second)
sudo squeezed driver install
# check it took
squeezed driver status
# run the server against the virtual device
squeezed --source virtual
# now pick "Squeezed" in System Settings → Sound → Output and press play
The driver is compiled into the squeezed binary itself — there is nothing else to download. Remove it any time with sudo squeezed driver uninstall.
Notes:
--source virtual and the driver subcommand are unavailable.--sample-rate (supported: 44100, 48000, 88200, 96000 Hz — the default 44100 is fine for almost everything).--latency-kb pre-buffer (default 30 KB ≈ 170 ms at 44.1k/16/2). Lower it for lip-sync with video; raise it (up to 255) if audio stutters over WiFi.To keep the virtual source running in the background, install the example LaunchAgent from contrib/launchd/com.tsirysndr.squeezed.plist (edit the binary path inside to match your install — /opt/homebrew/bin for Homebrew on Apple Silicon):
cp contrib/launchd/com.tsirysndr.squeezed.plist ~/Library/LaunchAgents/
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.tsirysndr.squeezed.plist
It starts at login, restarts on crash, and logs to /tmp/squeezed.log. Stop it with:
launchctl bootout gui/$(id -u)/com.tsirysndr.squeezed
It's a LaunchAgent (per-user), not a system LaunchDaemon, because capturing from the audio device has to happen inside your login session.
Options can come from CLI flags, a TOML file (--config), or both. Precedence, lowest to highest:
built-in defaults < --config file < command-line flags
Copy squeezed.example.toml and edit. Every key is optional; a partial file only overrides what it sets.
[input]
source = "fifo" # stdin | fifo | unix | tcp | virtual (macOS)
path = "/tmp/squeezed.fifo" # for fifo/unix
# bind = "0.0.0.0:4711" # for tcp
[audio]
sample_rate = 44100 # 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 88200, 96000, ...
channels = 2 # 1 or 2
bits = 16 # 8, 16, 24 or 32
[server]
bind_ip = "0.0.0.0"
slim_port = 3483 # SlimProto control + UDP discovery
http_port = 9000 # HTTP audio stream
discovery = true # answer UDP discovery
sync = true # multiroom sync: keep all players sample-aligned
name = "squeezed" # device/server name advertised to clients
# buffer_bytes = 4194304 # rolling PCM retention window (~23s @ 44.1k/16/2)
Run it:
squeezed --config squeezed.example.toml
# override individual values on top of the file:
squeezed --config squeezed.example.toml --http-port 9001 --name Kitchen
| Flag | TOML key | Default | Description |
|---|---|---|---|
-c, --config <FILE> | — | — | Load a TOML config file (flags still win). |
-s, --source <SRC> | input.source | stdin | stdin | fifo | unix | tcp | virtual (macOS). |
--path <PATH> | input.path | — | Path for fifo / unix. |
--tcp-bind <ADDR> | input.bind | 0.0.0.0:4711 | Bind address for tcp. |
--sample-rate <HZ> | audio.sample_rate | 44100 | PCM sample rate. |
--channels <N> | audio.channels | 2 | 1 (mono) or 2 (stereo). |
--bits <BITS> | audio.bits | 16 | 8, 16, 24 or 32. |
--bind-ip <IP> | server.bind_ip | 0.0.0.0 | Interface to listen on. |
--slim-port <PORT> | server.slim_port | 3483 | SlimProto control + UDP discovery port. |
--http-port <PORT> | server.http_port | 9000 | HTTP audio port. |
--discovery <BOOL> | server.discovery | true | Answer UDP discovery. |
--sync <BOOL> | server.sync | true | Continuously align connected players (multiroom sync). |
--name <NAME> | server.name | squeezed | Device/server name. |
--buffer-bytes <N> | server.buffer_bytes | 4194304 | Rolling PCM retention window. |
--latency-kb <KB> | server.latency_kb | 30 | Player pre-buffer before playback ≈ end-to-end latency (1..=255). Lower for video sync, raise for flaky WiFi. |
Logging verbosity is controlled by the SQUEEZED_LOG environment variable (error, warn, info, debug, trace; default info):
SQUEEZED_LOG=debug squeezed --source stdin < /dev/null
The stream must be raw, signed, little-endian PCM matching the configured sample_rate / channels / bits. That's exactly what ffmpeg's -f s16le (or s24le/s32le), -ar <rate>, -ac <n> produce. Mismatched format = wrong-speed or noisy playback, so keep the ffmpeg flags and the [audio] settings in agreement.
| bits | ffmpeg format |
|---|---|
| 16 | -f s16le |
| 24 | -f s24le |
| 32 | -f s32le |
Supported sample rates: 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 88200, 96000, 176400, 192000, 352800, 384000 Hz.
Example — 48 kHz / 24-bit:
ffmpeg -re -i hi-res.flac -f s24le -ar 48000 -ac 2 - | squeezed --sample-rate 48000 --bits 24
With discovery = true (default), squeezed answers SlimProto UDP discovery on the SlimProto port, so squeezelite finds it with no -s. Because Squeezelite hard-codes discovery to port 3483, auto-discovery only works while slim_port is left at the default; with a custom port, point clients at the server explicitly (-s host:port).
Start players in as many rooms as you like and squeezed keeps them sample-aligned — the same audio comes out of every room at the same instant, and stays that way even though each device runs on its own crystal clock.
squeezelite -n Kitchen -s 192.168.1.10 &
squeezelite -n Living-Room -s 192.168.1.10 &
squeezelite -n Bedroom -s 192.168.1.10 &
For each connected player, squeezed:
strm 't' timing probe once per second; the player replies with its clock and echoes the server timestamp. That round trip (kept from the lowest-latency sample) yields the offset between the player's clock and the server's, NTP-style.strm 'a' skip-ahead by exactly the lag. Skip-ahead is the only nudge used, because squeezelite advances its play counter by the skipped amount — so the position model never drifts from reality.The result: a player that starts to fall behind is continuously pulled back into alignment. In a stress test with a player deliberately running 4 % slow, its lag was held bounded (re-corrected every few seconds) instead of growing without limit; real devices drift by only parts-per-million, so the residual error is well under a millisecond.
Turn it off with --sync false (or sync = false) for plain simultaneous playback without correction.
Notes
- Each player needs a unique MAC (real devices have one; multiple
squeezeliteinstances on a single host must be started with-m <mac>).squeezeduses the MAC to correlate a player's control and audio connections.- Sync tracks steady-state drift; it does not attempt sample-accurate cross-fade on join. A late joiner snaps into alignment within a few seconds of starting.
Re-broadcast an internet radio stream:
ffmpeg -re -i https://stream.example.com/radio.mp3 -f s16le -ar 44100 -ac 2 - | squeezed
Play a whole folder, gaplessly, over a FIFO:
mkfifo /tmp/squeezed.fifo
squeezed --source fifo --path /tmp/squeezed.fifo &
for f in ~/Music/*.flac; do
ffmpeg -re -i "$f" -f s16le -ar 44100 -ac 2 -
done > /tmp/squeezed.fifo
Stream your desktop/system audio (PipeWire/PulseAudio via ffmpeg):
ffmpeg -f pulse -i default -f s16le -ar 44100 -ac 2 - | squeezed
Stream microphone/line-in on macOS (avfoundation):
ffmpeg -f avfoundation -i ":0" -f s16le -ar 44100 -ac 2 - | squeezed
Generate a test tone:
ffmpeg -re -f lavfi -i "sine=frequency=440:sample_rate=44100" -ac 2 -f s16le - | squeezed
-s <server-ip> to squeezelite. Discovery needs slim_port at its default 3483 and UDP broadcast reachable on your LAN.bind … failed: Address already in use — something already owns the port (often a running Logitech Media Server on 3483). Pick another with --slim-port / --http-port (and then point players with -s host:port).-re on ffmpeg; without it, ffmpeg pushes PCM as fast as it can. -re paces it to real time.[audio]. Make -f s16le -ar <rate> -ac <n> agree with bits / sample_rate / channels.squeezed driver status. If the bundle is installed but the device isn't visible, restart coreaudiod (sudo launchctl kickstart -kp system/com.apple.audio.coreaudiod) or log out and back in.--source virtual plays silence (macOS) — the virtual device only carries what is routed to it: pick "Squeezed" as the output in System Settings → Sound (or in the app you're playing from), and check it isn't muted / volume isn't at zero.SQUEEZED_LOG=debug.:3483) accepts each Squeezelite connection, reads its HELO, and replies with a strm "start" command describing the raw-PCM format and telling the client to fetch audio over HTTP (with its MAC in the URL, to correlate the two connections).:9000) streams the shared PCM buffer to each connected player, recording the byte position where each stream began.:3483) answers SlimProto discovery so clients find the server automatically.The SlimProto implementation is derived from the rockbox-slim crate.
MIT © Tsiry Sandratraina
14 commits
Rust
93.5%
JavaScript
3.3%
Nix
3.2%
Serve a raw PCM audio stream to any Squeezelite / Squeezebox client over the SlimProto protocol.
3
stars
14
commits
Rust
primary language
Sep 5, 2026
updated

Serve a raw PCM audio stream to any Squeezelite / Squeezebox client over the SlimProto protocol.
squeezed takes a raw PCM S16LE audio stream from stdin, a FIFO, a unix socket, or a TCP socket, and turns it into a Squeezebox server. Point any Squeezelite instance at it — or let them auto-discover it — and the audio comes out the other end, in sync across as many players as you like.
┌────────────┐ PCM ┌───────────────────────────────────┐ SlimProto ┌──────────────┐
│ ffmpeg / │ ───────► │ squeezed │ ◄──────────► │ squeezelite │
│ any PCM │ stdin / │ • SlimProto control (tcp :3483) │ (tcp) │ (player 1) │
│ producer │ fifo / │ • HTTP audio stream (tcp :9000) │ HTTP audio ├──────────────┤
│ │ unix / │ • UDP discovery (udp :3483) │ ───────────► │ squeezelite │
└────────────┘ tcp └───────────────────────────────────┘ │ (player 2) │
└──────────────┘
stdin, a named pipe, a unix socket, or a TCP listener.sudo squeezed driver install adds a "Squeezed" device to System Settings → Sound; anything your Mac plays to it streams to your Squeezelite players, volume keys included. See Virtual audio device (macOS).-s host.Prebuilt binaries are published for Linux and macOS (x86_64 and arm64) plus FreeBSD/NetBSD (amd64) on every tagged release.
You'll also want a PCM producer (ffmpeg) and a player (squeezelite) — both are in most package managers (brew install ffmpeg squeezelite, apt install ffmpeg squeezelite, …).
apt)Packages are hosted on Gemfury:
echo "deb [trusted=yes] https://apt.fury.io/tsiry/ /" \
| sudo tee /etc/apt/sources.list.d/squeezed.list
sudo apt-get update
sudo apt-get install squeezed
dnf)sudo tee /etc/yum.repos.d/squeezed.repo >/dev/null <<'EOF'
[fury]
name=Gemfury Private Repo
baseurl=https://yum.fury.io/tsiry/
enabled=1
gpgcheck=0
EOF
sudo dnf install squeezed
# From the flake (installs into your profile)
nix profile install github:tsirysndr/squeezed
# Or run without installing
nix run github:tsirysndr/squeezed -- --help
brew install tsirysndr/tap/squeezed
The @tsiry/squeezed package
downloads the prebuilt binary for your platform from GitHub Releases on install:
npm install -g @tsiry/squeezed
Grab a tarball for your platform from the releases page:
# Example: Linux x86_64 — pick the asset matching your OS/arch
curl -fsSL -o squeezed.tar.gz \
https://github.com/tsirysndr/squeezed/releases/latest/download/squeezed-<version>-linux-amd64.tar.gz
tar -xzf squeezed.tar.gz
sudo install -m 0755 squeezed /usr/local/bin/squeezed
Needs a recent Rust toolchain:
git clone https://github.com/tsirysndr/squeezed
cd squeezed
cargo build --release
# binary at ./target/release/squeezed
Optionally drop it on your PATH:
install -m755 target/release/squeezed /usr/local/bin/squeezed
Pipe from ffmpeg, play with squeezelite.
Terminal 1 — start the server and feed it audio from ffmpeg:
ffmpeg -re -i song.flac -f s16le -ar 44100 -ac 2 - | squeezed
-re streams at real-time rate (so playback isn't a fast-forward blur).-f s16le -ar 44100 -ac 2 - emits raw signed 16-bit little-endian stereo PCM to stdout, which squeezed reads from stdin.Terminal 2 — start a player. It auto-discovers squeezed:
squeezelite -n Living-Room
…or skip discovery and point it straight at the server:
squeezelite -n Living-Room -s 127.0.0.1
That's it — song.flac now plays through squeezelite. Start more squeezelite instances (on this or other machines) and they'll all play in sync.
Tip: to verify the plumbing without an audio device, have squeezelite decode to stdout:
squeezelite -s 127.0.0.1 -o - -a 16 > out.pcm
Select the source with --source (or [input] source in the TOML file).
Best for pipelines. EOF on stdin cleanly shuts the server down.
ffmpeg -re -i input.mp3 -f s16le -ar 44100 -ac 2 - | squeezed --source stdin
The server stays up across producers — when one writer closes, squeezed waits for the next.
mkfifo /tmp/squeezed.fifo
squeezed --source fifo --path /tmp/squeezed.fifo &
# feed it whenever you like; the server keeps running between tracks
ffmpeg -re -i track1.flac -f s16le -ar 44100 -ac 2 - > /tmp/squeezed.fifo
ffmpeg -re -i track2.flac -f s16le -ar 44100 -ac 2 - > /tmp/squeezed.fifo
Like a FIFO, but connection-oriented. squeezed creates (and cleans up) the socket.
squeezed --source unix --path /tmp/squeezed.sock &
ffmpeg -re -i input.wav -f s16le -ar 44100 -ac 2 - | socat - UNIX-CONNECT:/tmp/squeezed.sock
Feed audio over the network. squeezed accepts one writer at a time and waits for the next.
squeezed --source tcp --tcp-bind 0.0.0.0:4711 &
# from anywhere on the network:
ffmpeg -re -i input.opus -f s16le -ar 44100 -ac 2 - | nc server-host 4711
Turn your whole Mac into the PCM source: install a Core Audio driver that adds a "Squeezed" output device to System Settings → Sound → Output. Everything the Mac plays to it — Music, Spotify, Safari, anything — is captured by squeezed and streamed to your players, in sync. The device has a working volume slider, and the volume/mute keys apply to what the players hear.
# one-time: install the driver bundle to /Library/Audio/Plug-Ins/HAL
# (restarts coreaudiod — audio apps may hiccup for a second)
sudo squeezed driver install
# check it took
squeezed driver status
# run the server against the virtual device
squeezed --source virtual
# now pick "Squeezed" in System Settings → Sound → Output and press play
The driver is compiled into the squeezed binary itself — there is nothing else to download. Remove it any time with sudo squeezed driver uninstall.
Notes:
--source virtual and the driver subcommand are unavailable.--sample-rate (supported: 44100, 48000, 88200, 96000 Hz — the default 44100 is fine for almost everything).--latency-kb pre-buffer (default 30 KB ≈ 170 ms at 44.1k/16/2). Lower it for lip-sync with video; raise it (up to 255) if audio stutters over WiFi.To keep the virtual source running in the background, install the example LaunchAgent from contrib/launchd/com.tsirysndr.squeezed.plist (edit the binary path inside to match your install — /opt/homebrew/bin for Homebrew on Apple Silicon):
cp contrib/launchd/com.tsirysndr.squeezed.plist ~/Library/LaunchAgents/
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.tsirysndr.squeezed.plist
It starts at login, restarts on crash, and logs to /tmp/squeezed.log. Stop it with:
launchctl bootout gui/$(id -u)/com.tsirysndr.squeezed
It's a LaunchAgent (per-user), not a system LaunchDaemon, because capturing from the audio device has to happen inside your login session.
Options can come from CLI flags, a TOML file (--config), or both. Precedence, lowest to highest:
built-in defaults < --config file < command-line flags
Copy squeezed.example.toml and edit. Every key is optional; a partial file only overrides what it sets.
[input]
source = "fifo" # stdin | fifo | unix | tcp | virtual (macOS)
path = "/tmp/squeezed.fifo" # for fifo/unix
# bind = "0.0.0.0:4711" # for tcp
[audio]
sample_rate = 44100 # 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 88200, 96000, ...
channels = 2 # 1 or 2
bits = 16 # 8, 16, 24 or 32
[server]
bind_ip = "0.0.0.0"
slim_port = 3483 # SlimProto control + UDP discovery
http_port = 9000 # HTTP audio stream
discovery = true # answer UDP discovery
sync = true # multiroom sync: keep all players sample-aligned
name = "squeezed" # device/server name advertised to clients
# buffer_bytes = 4194304 # rolling PCM retention window (~23s @ 44.1k/16/2)
Run it:
squeezed --config squeezed.example.toml
# override individual values on top of the file:
squeezed --config squeezed.example.toml --http-port 9001 --name Kitchen
| Flag | TOML key | Default | Description |
|---|---|---|---|
-c, --config <FILE> | — | — | Load a TOML config file (flags still win). |
-s, --source <SRC> | input.source | stdin | stdin | fifo | unix | tcp | virtual (macOS). |
--path <PATH> | input.path | — | Path for fifo / unix. |
--tcp-bind <ADDR> | input.bind | 0.0.0.0:4711 | Bind address for tcp. |
--sample-rate <HZ> | audio.sample_rate | 44100 | PCM sample rate. |
--channels <N> | audio.channels | 2 | 1 (mono) or 2 (stereo). |
--bits <BITS> | audio.bits | 16 | 8, 16, 24 or 32. |
--bind-ip <IP> | server.bind_ip | 0.0.0.0 | Interface to listen on. |
--slim-port <PORT> | server.slim_port | 3483 | SlimProto control + UDP discovery port. |
--http-port <PORT> | server.http_port | 9000 | HTTP audio port. |
--discovery <BOOL> | server.discovery | true | Answer UDP discovery. |
--sync <BOOL> | server.sync | true | Continuously align connected players (multiroom sync). |
--name <NAME> | server.name | squeezed | Device/server name. |
--buffer-bytes <N> | server.buffer_bytes | 4194304 | Rolling PCM retention window. |
--latency-kb <KB> | server.latency_kb | 30 | Player pre-buffer before playback ≈ end-to-end latency (1..=255). Lower for video sync, raise for flaky WiFi. |
Logging verbosity is controlled by the SQUEEZED_LOG environment variable (error, warn, info, debug, trace; default info):
SQUEEZED_LOG=debug squeezed --source stdin < /dev/null
The stream must be raw, signed, little-endian PCM matching the configured sample_rate / channels / bits. That's exactly what ffmpeg's -f s16le (or s24le/s32le), -ar <rate>, -ac <n> produce. Mismatched format = wrong-speed or noisy playback, so keep the ffmpeg flags and the [audio] settings in agreement.
| bits | ffmpeg format |
|---|---|
| 16 | -f s16le |
| 24 | -f s24le |
| 32 | -f s32le |
Supported sample rates: 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 88200, 96000, 176400, 192000, 352800, 384000 Hz.
Example — 48 kHz / 24-bit:
ffmpeg -re -i hi-res.flac -f s24le -ar 48000 -ac 2 - | squeezed --sample-rate 48000 --bits 24
With discovery = true (default), squeezed answers SlimProto UDP discovery on the SlimProto port, so squeezelite finds it with no -s. Because Squeezelite hard-codes discovery to port 3483, auto-discovery only works while slim_port is left at the default; with a custom port, point clients at the server explicitly (-s host:port).
Start players in as many rooms as you like and squeezed keeps them sample-aligned — the same audio comes out of every room at the same instant, and stays that way even though each device runs on its own crystal clock.
squeezelite -n Kitchen -s 192.168.1.10 &
squeezelite -n Living-Room -s 192.168.1.10 &
squeezelite -n Bedroom -s 192.168.1.10 &
For each connected player, squeezed:
strm 't' timing probe once per second; the player replies with its clock and echoes the server timestamp. That round trip (kept from the lowest-latency sample) yields the offset between the player's clock and the server's, NTP-style.strm 'a' skip-ahead by exactly the lag. Skip-ahead is the only nudge used, because squeezelite advances its play counter by the skipped amount — so the position model never drifts from reality.The result: a player that starts to fall behind is continuously pulled back into alignment. In a stress test with a player deliberately running 4 % slow, its lag was held bounded (re-corrected every few seconds) instead of growing without limit; real devices drift by only parts-per-million, so the residual error is well under a millisecond.
Turn it off with --sync false (or sync = false) for plain simultaneous playback without correction.
Notes
- Each player needs a unique MAC (real devices have one; multiple
squeezeliteinstances on a single host must be started with-m <mac>).squeezeduses the MAC to correlate a player's control and audio connections.- Sync tracks steady-state drift; it does not attempt sample-accurate cross-fade on join. A late joiner snaps into alignment within a few seconds of starting.
Re-broadcast an internet radio stream:
ffmpeg -re -i https://stream.example.com/radio.mp3 -f s16le -ar 44100 -ac 2 - | squeezed
Play a whole folder, gaplessly, over a FIFO:
mkfifo /tmp/squeezed.fifo
squeezed --source fifo --path /tmp/squeezed.fifo &
for f in ~/Music/*.flac; do
ffmpeg -re -i "$f" -f s16le -ar 44100 -ac 2 -
done > /tmp/squeezed.fifo
Stream your desktop/system audio (PipeWire/PulseAudio via ffmpeg):
ffmpeg -f pulse -i default -f s16le -ar 44100 -ac 2 - | squeezed
Stream microphone/line-in on macOS (avfoundation):
ffmpeg -f avfoundation -i ":0" -f s16le -ar 44100 -ac 2 - | squeezed
Generate a test tone:
ffmpeg -re -f lavfi -i "sine=frequency=440:sample_rate=44100" -ac 2 -f s16le - | squeezed
-s <server-ip> to squeezelite. Discovery needs slim_port at its default 3483 and UDP broadcast reachable on your LAN.bind … failed: Address already in use — something already owns the port (often a running Logitech Media Server on 3483). Pick another with --slim-port / --http-port (and then point players with -s host:port).-re on ffmpeg; without it, ffmpeg pushes PCM as fast as it can. -re paces it to real time.[audio]. Make -f s16le -ar <rate> -ac <n> agree with bits / sample_rate / channels.squeezed driver status. If the bundle is installed but the device isn't visible, restart coreaudiod (sudo launchctl kickstart -kp system/com.apple.audio.coreaudiod) or log out and back in.--source virtual plays silence (macOS) — the virtual device only carries what is routed to it: pick "Squeezed" as the output in System Settings → Sound (or in the app you're playing from), and check it isn't muted / volume isn't at zero.SQUEEZED_LOG=debug.:3483) accepts each Squeezelite connection, reads its HELO, and replies with a strm "start" command describing the raw-PCM format and telling the client to fetch audio over HTTP (with its MAC in the URL, to correlate the two connections).:9000) streams the shared PCM buffer to each connected player, recording the byte position where each stream began.:3483) answers SlimProto discovery so clients find the server automatically.The SlimProto implementation is derived from the rockbox-slim crate.
MIT © Tsiry Sandratraina
14 commits
Rust
93.5%
JavaScript
3.3%
Nix
3.2%