Releases: Sendspin/sendspin-cpp-cli
Release list
sendspin-cli 0.1.6
New features
linux-armv7archive — 32-bit builds for a Pi 2, Pi 3, Pi 4 or Pi Zero 2 running a 32-bit Raspberry Pi OS. (#47)linux-armv6archive — for a Pi Zero, Pi Zero W or original Pi. These are ARM1176 and hit an illegal instruction in the ARMv7 build. Built natively in an emulated Raspbian container, so it is also the only archive here that loads on Raspberry Pi OS bookworm as well as trixie. (#44, #48)- Automatic archive selection —
scripts/get_started_linux.shreads the userland and downloads the right archive for the board it is on. It refuses rather than guesses on a pre-ARMv6 CPU or a userland it cannot identify. (#47, #48)
Fixed issues
- A replugged ALSA device now recovers mid-track. (#45, #46)
AlsaAudioSinkwas the only sink with real hardware behind it not wired intoSinkRecovery. Any error outside-EINTR/-EAGAIN,-EPIPEand-ESTRPIPE—-ENODEVincluded — counted as a failed write, so unplugging a USB DAC left the handle open on hardware that was gone and repeated an unthrottledERRORonce per retry until you restarted the process. Device loss now closes the device and reopens at the last negotiated format, behind the delay and retry budget the other sinks already use. Nothing to configure: plug it back in and the stream resumes. SENDSPIN_SERVER_URLno longer exports a stale dialled URL to the stream hooks on an-srun. (#34, fixed in 0.1.5, tracked closed here.)
Which Raspberry Pi archive do I want?
| Board | Archive |
|---|---|
| Pi 5, Pi 4, Pi 3, Pi Zero 2 on a 64-bit OS | linux-arm64 |
| Pi 2, Pi 3, Pi 4, Pi Zero 2 on a 32-bit OS | linux-armv7 |
| Pi Zero, Pi Zero W, original Pi | linux-armv6 |
Or run scripts/get_started_linux.sh and let it choose.
Install
Installation, SHA256SUMS verification and the macOS Gatekeeper situation are in the wiki — Installation, or Getting Started on a Raspberry Pi for the archives above. Each archive also carries its own BUILD-INFO.txt.
Full changelog: v0.1.5...v0.1.6
sendspin-cli 0.1.5
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_D10sOver -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_URLno 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
--interfaceas 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 pulsecan report continuous lost/regained sync with
audible artifacts (#32).-o pipewireon 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
sendspin-cli 0.1.4
Binaries for Linux (x86-64 and arm64) and macOS (Apple silicon), plus a macOS
installer .pkg, built and published by .github/workflows/release.yml from the tag
alone.
Install
Each archive is a staged cmake --install payload, so every path under its usr/ is
the path the file installs to:
sudo tar -xzf sendspin-cli-0.1.4-linux-x86_64.tar.gz --strip-components=1 -C / \
sendspin-cli-0.1.4-linux-x86_64/usr
sudo systemd-sysusers
sudo systemctl daemon-reloadsystemd-sysusers creates the unprivileged account the unit runs as, from the
declaration the payload installs alongside it; a tarball has no postinst to run it
for you, and without it systemctl start reports 217/USER.
Naming the usr member is what leaves BUILD-INFO.txt in the archive rather than
unpacking it at /. Each archive carries its own BUILD-INFO.txt at the root with
that command already filled in for its platform, the runtime packages it needs, and
what the systemd unit does. Or run it where you unpacked it, at
./<name>/usr/local/bin/sendspin-cli.
On macOS the .pkg is the shorter path to the same place. It is built from the very
payload the macOS archive holds, so the two put identical files on disk:
sudo installer -pkg sendspin-cli-0.1.4-macos-arm64.pkg -target /
sendspin-cli --versionThere is no uninstaller: remove those files and run
sudo pkgutil --forget io.github.chrisuthe.sendspin-cli.
macOS is unsigned, archive and .pkg alike
The macOS binary is ad-hoc signed — the minimum an arm64 Mach-O needs to execute
at all, applied by the linker. It carries no developer identity, spctl rejects it,
and the .pkg around it is unsigned in the same way. Unpack the archive from a
terminal rather than in Finder, whose Archive Utility sets the quarantine flag where
tar does not; if macOS refuses the binary anyway:
xattr -d com.apple.quarantine \
./sendspin-cli-0.1.4-macos-arm64/usr/local/bin/sendspin-cliA .pkg downloaded from this page is quarantined too, and Gatekeeper refuses it on a
double-click — allow it once under System Settings → Privacy & Security, or use the
installer command above, which makes no Gatekeeper assessment at all whatever the
file is flagged with.
All of that is a workaround, not a fix, and the .pkg is not the fix either. What it
changes is narrower: xcrun stapler staples a notarization ticket to a .pkg and
refuses a bare executable, so this is the shape that makes notarization possible at
all. A Developer ID signature and notarization need a paid Apple enrolment and are
still owed. Said plainly here because it is worth knowing before you download: these
are unsigned builds from a public CI runner. The full account is under macOS, and
Gatekeeper in the README linked below.
Checksums
SHA256SUMS covers the four files above. It does not cover the Source code
archives GitHub attaches on its own, which are generated by GitHub rather than built
here.
sha256sum --ignore-missing -c SHA256SUMS # Linux
shasum -a 256 --ignore-missing -c SHA256SUMS # macOSRun that from the directory you downloaded into. --ignore-missing is there because
SHA256SUMS lists all four and you have almost certainly taken one: without it, the
ones you did not download are reported as failures and the command exits non-zero on a
perfectly good file.
What this build is
sendspin-cli 0.1.4. The version of the sendspin-cpp library it is linked against
is pinned at build time and printed on the second line of sendspin-cli --version,
alongside this one on the first.
- README — build, install, run,
macOS, and Gatekeeper,
and the installer.pkg - Roadmap — what shipped and
what is owed, item by item
sendspin-cli 0.1.3
Binaries for Linux (x86-64 and arm64) and macOS (Apple silicon), plus a macOS
installer .pkg, built and published by .github/workflows/release.yml from the tag
alone.
Install
Each archive is a staged cmake --install payload, so every path under its usr/ is
the path the file installs to:
sudo tar -xzf sendspin-cli-0.1.3-linux-x86_64.tar.gz --strip-components=1 -C / \
sendspin-cli-0.1.3-linux-x86_64/usr
sudo systemd-sysusers
sudo systemctl daemon-reloadsystemd-sysusers creates the unprivileged account the unit runs as, from the
declaration the payload installs alongside it; a tarball has no postinst to run it
for you, and without it systemctl start reports 217/USER.
Naming the usr member is what leaves BUILD-INFO.txt in the archive rather than
unpacking it at /. Each archive carries its own BUILD-INFO.txt at the root with
that command already filled in for its platform, the runtime packages it needs, and
what the systemd unit does. Or run it where you unpacked it, at
./<name>/usr/local/bin/sendspin-cli.
On macOS the .pkg is the shorter path to the same place. It is built from the very
payload the macOS archive holds, so the two put identical files on disk:
sudo installer -pkg sendspin-cli-0.1.3-macos-arm64.pkg -target /
sendspin-cli --versionThere is no uninstaller: remove those files and run
sudo pkgutil --forget io.github.chrisuthe.sendspin-cli.
macOS is unsigned, archive and .pkg alike
The macOS binary is ad-hoc signed — the minimum an arm64 Mach-O needs to execute
at all, applied by the linker. It carries no developer identity, spctl rejects it,
and the .pkg around it is unsigned in the same way. Unpack the archive from a
terminal rather than in Finder, whose Archive Utility sets the quarantine flag where
tar does not; if macOS refuses the binary anyway:
xattr -d com.apple.quarantine \
./sendspin-cli-0.1.3-macos-arm64/usr/local/bin/sendspin-cliA .pkg downloaded from this page is quarantined too, and Gatekeeper refuses it on a
double-click — allow it once under System Settings → Privacy & Security, or use the
installer command above, which makes no Gatekeeper assessment at all whatever the
file is flagged with.
All of that is a workaround, not a fix, and the .pkg is not the fix either. What it
changes is narrower: xcrun stapler staples a notarization ticket to a .pkg and
refuses a bare executable, so this is the shape that makes notarization possible at
all. A Developer ID signature and notarization need a paid Apple enrolment and are
still owed. Said plainly here because it is worth knowing before you download: these
are unsigned builds from a public CI runner. The full account is under macOS, and
Gatekeeper in the README linked below.
Checksums
SHA256SUMS covers the four files above. It does not cover the Source code
archives GitHub attaches on its own, which are generated by GitHub rather than built
here.
sha256sum --ignore-missing -c SHA256SUMS # Linux
shasum -a 256 --ignore-missing -c SHA256SUMS # macOSRun that from the directory you downloaded into. --ignore-missing is there because
SHA256SUMS lists all four and you have almost certainly taken one: without it, the
ones you did not download are reported as failures and the command exits non-zero on a
perfectly good file.
What this build is
sendspin-cli 0.1.3. The version of the sendspin-cpp library it is linked against
is pinned at build time and printed on the second line of sendspin-cli --version,
alongside this one on the first.
- README — build, install, run,
macOS, and Gatekeeper,
and the installer.pkg - Roadmap — what shipped and
what is owed, item by item
sendspin-cli 0.1.0
Binaries for Linux (x86-64 and arm64) and macOS (Apple silicon), plus a macOS
installer .pkg, built and published by .github/workflows/release.yml from the tag
alone.
Install
Each archive is a staged cmake --install payload, so every path under its usr/ is
the path the file installs to:
sudo tar -xzf sendspin-cli-0.1.0-linux-x86_64.tar.gz --strip-components=1 -C / \
sendspin-cli-0.1.0-linux-x86_64/usr
sudo systemd-sysusers
sudo systemctl daemon-reloadsystemd-sysusers creates the unprivileged account the unit runs as, from the
declaration the payload installs alongside it; a tarball has no postinst to run it
for you, and without it systemctl start reports 217/USER.
Naming the usr member is what leaves BUILD-INFO.txt in the archive rather than
unpacking it at /. Each archive carries its own BUILD-INFO.txt at the root with
that command already filled in for its platform, the runtime packages it needs, and
what the systemd unit does. Or run it where you unpacked it, at
./<name>/usr/local/bin/sendspin-cli.
On macOS the .pkg is the shorter path to the same place. It is built from the very
payload the macOS archive holds, so the two put identical files on disk:
sudo installer -pkg sendspin-cli-0.1.0-macos-arm64.pkg -target /
sendspin-cli --versionThere is no uninstaller: remove those files and run
sudo pkgutil --forget io.github.chrisuthe.sendspin-cli.
macOS is unsigned, archive and .pkg alike
The macOS binary is ad-hoc signed — the minimum an arm64 Mach-O needs to execute
at all, applied by the linker. It carries no developer identity, spctl rejects it,
and the .pkg around it is unsigned in the same way. Unpack the archive from a
terminal rather than in Finder, whose Archive Utility sets the quarantine flag where
tar does not; if macOS refuses the binary anyway:
xattr -d com.apple.quarantine \
./sendspin-cli-0.1.0-macos-arm64/usr/local/bin/sendspin-cliA .pkg downloaded from this page is quarantined too, and Gatekeeper refuses it on a
double-click — allow it once under System Settings → Privacy & Security, or use the
installer command above, which makes no Gatekeeper assessment at all whatever the
file is flagged with.
All of that is a workaround, not a fix, and the .pkg is not the fix either. What it
changes is narrower: xcrun stapler staples a notarization ticket to a .pkg and
refuses a bare executable, so this is the shape that makes notarization possible at
all. A Developer ID signature and notarization need a paid Apple enrolment and are
still owed. Said plainly here because it is worth knowing before you download: these
are unsigned builds from a public CI runner. The full account is under macOS, and
Gatekeeper in the README linked below.
Checksums
SHA256SUMS covers the four files above. It does not cover the Source code
archives GitHub attaches on its own, which are generated by GitHub rather than built
here.
sha256sum --ignore-missing -c SHA256SUMS # Linux
shasum -a 256 --ignore-missing -c SHA256SUMS # macOSRun that from the directory you downloaded into. --ignore-missing is there because
SHA256SUMS lists all four and you have almost certainly taken one: without it, the
ones you did not download are reported as failures and the command exits non-zero on a
perfectly good file.
What this build is
sendspin-cli 0.1.0. The version of the sendspin-cpp library it is linked against
is pinned at build time and printed on the second line of sendspin-cli --version,
alongside this one on the first.
- README — build, install, run,
macOS, and Gatekeeper,
and the installer.pkg - Roadmap — what shipped and
what is owed, item by item