2026-05-14 14:29:57 +02:00
2026-05-14 14:29:57 +02:00
2026-05-14 14:29:57 +02:00
2026-05-14 14:29:57 +02:00
2026-05-14 14:29:57 +02:00
2026-05-14 14:29:57 +02:00
2026-05-14 14:29:57 +02:00
2026-05-14 14:29:57 +02:00
2026-05-14 14:29:57 +02:00

rpi-sendspin

A sendspin client and server with native PipeWire node selection. Audio is routed through pw-cat, so any PipeWire source or sink can be targeted by name — no ALSA/PulseAudio compatibility layer needed.

Requirements

  • Go 1.22+
  • PipeWire with pw-cat and pw-dump in $PATH
  • libopus and libopusfile (for the sendspin dependency)

Build host (dev packages, only needed to compile):

# Arch
sudo pacman -S pipewire opus opusfile

# Debian/Ubuntu
sudo apt install libopus-dev libopusfile-dev libasound2-dev

Raspberry Pi (runtime only, no dev packages needed):

sudo apt install libopus0 libopusfile0 libasound2 pipewire

Build

make          # builds bin/sendspin-client and bin/sendspin-server (native)
make rpi      # cross-compiles for RPi via Docker → bin/*-armhf and bin/*-arm64
make clean

make rpi requires Docker. It builds a Debian image with ARM cross-compilers and the ARM versions of libopus/libasound, then copies the four binaries into bin/. No toolchain installation needed on the host.

Usage

Server

Captures audio from a PipeWire source node and streams it to connected clients.

# List available capture sources
./bin/sendspin-server --list-sources

# Stream from a specific source
./bin/sendspin-server --source alsa_input.usb-JBL_Quantum810-00.pro-input-0

# Options
./bin/sendspin-server --help
  -source    string   PipeWire source node name
  -port      int      listen port (default 8927)
  -name      string   server name (default "Sendspin Server")
  -rate      int      capture sample rate (default 48000)
  -channels  int      capture channels (default 2)

Client

Connects to a sendspin server and plays audio on a PipeWire sink.

# List available playback sinks
./bin/sendspin-client --list-devices

# Play on a specific sink (omit --device to use the system default)
./bin/sendspin-client --server rpi.local:8927 --device alsa_output.usb-JBL_Quantum810-00.pro-output-0

# Options
./bin/sendspin-client --help
  -server   string   server address (default "localhost:8927")
  -device   string   PipeWire sink node name (default: system default)
  -name     string   player name (default: hostname)
  -volume   int      initial volume 0-100 (default 80)

Running as a systemd user service

Example unit files are in dist/systemd/. Both run as a user service so they have access to the PipeWire session without any special privileges.

# 1. Copy the binary
install -Dm755 bin/sendspin-client-arm64 ~/.local/bin/sendspin-client

# 2. Edit the unit file — set --server, --device, --name to your values
cp dist/systemd/sendspin-client.service ~/.config/systemd/user/

# 3. Enable and start
systemctl --user daemon-reload
systemctl --user enable --now sendspin-client

# Logs
journalctl --user -u sendspin-client -f

The unit files use %H (hostname) for --name and %h (home dir) for the binary path. Replace the placeholder --device / --source values — use --list-devices / --list-sources to find the right node name first.

Architecture

PipeWire source node
       │
  pw-cat --capture
       │ int32 PCM
  sendspin server  ──── network ────  sendspin client
                                           │ int32 PCM
                                      pw-cat --playback
                                           │
                                   PipeWire sink node

internal/pipewire/source.go wraps pw-cat --capture as an AudioSource.
internal/pipewire/output.go wraps pw-cat --playback as an Output.
internal/pipewire/nodes.go parses pw-dump JSON to enumerate nodes.

Description
No description provided
Readme 518 KiB
Languages
Go 81.1%
HTML 18.2%
Makefile 0.7%