github Sendspin/sendspin-cpp-cli v0.1.5
sendspin-cli 0.1.5

3 hours ago

sendspin-cli 0.1.5, linked against sendspin-cpp v0.7.2 — --version prints both.
Binaries for Linux (x86-64 and arm64) and macOS (Apple silicon), plus a macOS
installer .pkg. Install steps are on the wiki:
Installation for every
way in, Getting Started on Linux
for the one-command version — or read each archive's own BUILD-INFO.txt. SHA256SUMS
covers the four files above, and the macOS builds are unsigned — read the
macOS install notes
before downloading one.

Native PulseAudio and PipeWire outputs (#27)

-o pulse plays through the PulseAudio server's default sink; -o pipewire lets
the PipeWire graph route the stream. Both take a device after a colon, named
exactly as -l prints it — and the server resolves that name at every stream, so
a sink that appears after startup is picked up by the next track without a restart:

sendspin-cli -o pulse                    # the server's default sink
sendspin-cli -o pulse:alsa_output.pci-0000_00_1f.3.analog-stereo
sendspin-cli -o pipewire                 # let the graph route it
sendspin-cli -o pipewire:alsa_output.usb-Topping_D10s

Over -o alsa:pulse (ALSA's plugin PCM) this buys enumerable sinks and nodes, a
stream the host mixer shows as sendspin-cli and can route per-application, and
sync feedback that answers for the server's real playout rather than the plugin's
own buffering — and this is a synchronised multi-room player, so that is not
cosmetic.

A working command line changes meaning on this upgrade: -o pulse and
-o pipewire used to fall through to ALSA's plugin PCMs of the same names; the
native backends now claim them. -o alsa:pulse and -o alsa:pipewire are the way
back, on every build with the ALSA backend.

--buffer-ms <ms> (10–2000, default 100) sizes these queues like every other
backend's: the PulseAudio server queue (tlength), the PipeWire ring. The short
tour of -o is under the wiki's
Choosing an output;
the full one is in the
README.

Stream hooks: --hook-start and --hook-stop (#28)

Each runs a shell command when a stream starts and when it stops — the amplifier
relay, the light, the notification. The command goes through /bin/sh -c, so pipes
and && work, and the event's facts arrive in the environment:

sendspin-cli -o hw:1,0 \
  --hook-start 'amixer -c 1 set Master unmute' \
  --hook-stop  'amixer -c 1 set Master mute'
Variable Carries
SENDSPIN_EVENT start or stop
SENDSPIN_SERVER_ID the connected server's id
SENDSPIN_SERVER_NAME its friendly name
SENDSPIN_SERVER_URL the URL this run dialled — set only on an -s run
SENDSPIN_CLIENT_ID this player's id, when --id chose one
SENDSPIN_CLIENT_NAME this player's friendly name (-n)

A stop event carries the same server facts as the start it pairs with, and an
unknown is left unset rather than exported empty, so [ -n "$SENDSPIN_SERVER_ID" ]
means what it says. The vocabulary is the Python sendspin-cli's, so a hook script
written against one player runs unchanged against the other.

Hooks never block playback; they run one at a time, in event order, with the newest
event superseding one still waiting (#41), and the command text never reaches the
log at any level — a hook command routinely carries a credential (#40). A player
stopped mid-stream runs its stop hook on the way out, so systemctl stop leaves
the amplifier off. The full contract is on the wiki's
Controlling the Player page.

Identity flags: --id, --manufacturer, --product-name (#29)

--id is the stable id a server files this player's volume, group and pairing
under. Without it the id is derived from the network interface MAC — so two
players on one host share it, and each server-side setting lands on whichever
connected last. A dual-mono pair needs its own --id per instance, with its own
--port and --state-dir. --manufacturer and --product-name set what the
server displays for the device.

Pin a format: --audio-format (#31)

--audio-format <codec:rate:depth:channels> — e.g. flac:48000:24:2 — moves a
preferred format to the front of what the device probe derived, which is where a
spec-following server picks. It is a reorder, not a narrowing: everything else the
device takes is still offered behind it, and a pin the device cannot take refuses
to start rather than playing something else (#38). Codecs are flac, opus and
pcm; opus only at 48 kHz / 16-bit.

Also in this release

  • SENDSPIN_SERVER_URL no longer exports a stale dialled URL when a server dials
    in after an outbound attempt dropped or failed: a lost connection clears it,
    and a discovery dial exports it only for the server it dialled (#42).
  • The roadmap records --interface as item 25, gated on a sendspin-cpp bind
    address (#39).

Known issues

  • A mid-session format change (a queue moving, say, from a 48 kHz track to a
    192 kHz one) can leave the stream desynced until a manual pause/play (#30).
  • Under pipewire-pulse, -o pulse can report continuous lost/regained sync with
    audible artifacts (#32). -o pipewire on the same host is clean — on PipeWire
    hosts, prefer the native -o pipewire.

Both are on the wiki's
Troubleshooting page,
beside the rest of the it-starts-and-makes-no-sound ladder.

More

  • Wiki — install, configure, control,
    run as a service, troubleshoot
  • README — the deep
    reference behind those pages, and the copy every archive carries offline
  • Roadmap
    what shipped and what is owed, item by item

Don't miss a new sendspin-cpp-cli release

NewReleases is sending notifications on new releases.