Breaking changes
- The player speaks Sendspin 1.0.0-rc1, and every connection is encrypted. A server must
speak rc1 too (aiosendspin 10 or later); an older one cannot connect at all. Every player gets
a new identity, the public key of a keypair generated on first run, so a server sees a new
player and its stored volume and grouping do not carry over. A server must pair with the
player before it can play, unless the player runs with--allow-unpaired.--idandid =
are refused with a pointer atstatus, which gainsclient id:andtrust:lines near the
top and printsoutput delay:where it printedstatic delay:. In the state file,
static-delay-msandlast-server-hashbecomeblob-*keys, automatically and one way: an
older binary reading the new file loses the delay.SENDSPIN_CLIENT_IDin hooks is now
always set, to the key-derived id. (#72, #81, #83)
New features
- Pairing from the terminal.
sendspin-cli pair-tokenprints the token to paste into a
server, and the player logs it once at startup until a server is paired, to the terminal or
journal but never to a--logfile. When a server asks to pair with a code, the player logs
the 6-digit code and shows it instatus;--pairing-code <8 digits>/pairing-code =
replaces it with a fixed one, best kept in the config file.sendspin-cli pair confirmand
pair cancelanswer an attempt that is waiting, andstatuscarries apairing:line, with
pairing code:andpairing window:while they apply. (#73) --input <backend>:<device>streams a microphone or line-in to the server through the
source role, on ALSA, CoreAudio (macOS asks for microphone permission on first use),
PipeWire, PulseAudio and PortAudio, withtoneandnullfor a device-less test.-llists
capture devices alongside outputs, andstatusshows aninput:line while one is
configured. (#74, #75, #76, #84)--input loopback[:<sink|device>]streams what this host is playing. On PipeWire and
PulseAudio it captures a sink's monitor, the player's own output included; on macOS 14.2 and
later it captures through a process tap that leaves the player's own playback out, and
refuses a tap that cannot. (#85, #86)--fixed-volumefor an output whose level is set elsewhere, such as a line-out into an
amplifier. The player plays at full gain, unmuted, and offers the server neither volume nor
mute, so a server's volume slider for it disappears rather than silently doing nothing. A
volume remembered in the state file is kept but ignored, with one log line saying so, and
returns once the flag is dropped.statusprintsplayer volume: 100 (fixed; the server cannot set it). (#88)--output-delayis the name for what--static-delayset, andoutput-delay =in a
config file likewise. The old flag and key are still accepted. (#72)
Fixed issues
-o nulland-o stdoutno longer drop the connection a second into every stream.
Neither sink reported playback, so the player never left its startup priming: the first
chunk stayed held, every chunk after it queued behind it, the inbound ring filled and the
library closed the connection, which the server reopened in a loop. Both sinks now report
what they consume against a virtual clock, so each chunk is released as it arrives. (#89)- Volume scaling at 16 and 32 bits on big-endian hosts. Any gain other than unity played
as noise on MIPS under OpenWrt, because those two paths read and wrote samples at native
width; they now assemble little-endian bytes as the 24-bit path already did. (#77) --helpno longer lists--id, which rc1 refuses;--state-dirgains the advice that
two players on one host need separate state directories, or they share one identity. (#83)
Under the hood
- sendspin-cpp is pinned to
v0.9.5, and the deprecatedpairing_window_supported
assignment is gone, since the library ignores it and the CLI already answers the pairing
window withpair confirm|cancel. Upstream changes that reach the player with the pin: the
player's quota is released atstream/clearandstream/end,client/hellogoes out only
afterserver/helloarrives, the network provider is polled only from the main loop, Opus
is decoded at 16 bits whateverstream/startsays, Opus PCM stays little-endian on
big-endian hosts, and a hard sync is measured from now after the sink starves. (#81) - CI runs a big-endian MIPS job, cross-compiling the volume code and running it under
QEMU, so the scaling fix above cannot regress unnoticed. (#77) - PortAudio and CoreAudio device lookup is shared between sink and source, so
-l,-o
and--inputresolve a device the same way on each backend. (#75, #84)
Full changelog: v0.3.0...v0.4.0
What this build is
sendspin-cli 0.4.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.
Installing, verifying SHA256SUMS and the macOS Gatekeeper situation are in the
wiki — Installation,
or Getting Started on a Raspberry Pi
for the two 32-bit ARM archives. Each archive also carries its own BUILD-INFO.txt,
with the install command already filled in for its platform.
The macOS binary and the .pkg around it are unsigned — ad-hoc signed, which is
the minimum an arm64 Mach-O needs to execute at all, and carries no developer identity
— so Gatekeeper refuses them until you say otherwise. Said here rather than only behind
a link because it is worth knowing before you download; what to do about it is under
the macOS installation guide.
- README — what it is, how to run
it, and how to contribute - Contributors — development,
testing, CI, and release guidance - Roadmap — what shipped and
what is owed, item by item