Multiroom audio: Music Assistant + Sendspin, six rooms¶
Server: MA 2.9.5 container (host net) at ma.fmm.house. Reverse-proxy rules: Base URL = https://ma.fmm.house, TCP port stays 8095 (setting 443 makes MA try to BIND 443 — crash), streamserver publish IP must remain a raw LAN IP.
Players (all Sendspin, sample-accurate sync):
- av (Living Room): sendspin-go binary as a systemd USER unit under the
desktop user (pipewire-native; agent-manageable without sudo).
- bcm / fm (Pis, Albert's Room / Foyer): same Go binary as system units;
needed libopus0 (not fully static); audio device by ALSA name.
bcm got a bookworm→trixie dist-upgrade first (worth it: box was music-only).
- Satellite1 (Kitchen): FutureProofHomes v0.2.0 firmware = native sendspin.
XMOS audio firmware only latches on COLD BOOT (unplug/replug!).
- Sonocotta HiFi-ESP32 ×2 (Bathrooms): vendor's official ESPHome sendspin
configs wrapped with our substitutions; compiled in our ESPHome builder.
- Go-player wedge: survives own crashes but not server restarts (discover-loop,
no reconnect) → watchdog timer restarts the unit when it holds no TCP
connection to :8927. Systemd Restart= can't catch healthy-but-disconnected.
- Old snapcast clients everywhere had pointed at the DEAD Pi server for days —
remove on sight.
Client apps (added later)¶
- Desktop Linux: the official desktop companion app (AppImage; AUR lags releases). It's a Tauri tray app — desktop machines only. Headless boxes (av's Living Room service, the Pis) stay on sendspin-go system/user units; a GUI companion has no business there.
- Phone/desktop app connectivity: companion apps connect DIRECTLY to the
server (web/api :8095 + stream :8097) — they do not ride the reverse proxy
by default. If ufw only admits traefik, direct clients fail while the web
UI works: open 8095+8097 to the LAN. In the app, either use
http://<lan-ip>:8095(plain, LAN-only) or the fullhttps://ma.<domain>URL — scheme REQUIRED;host:443without https:// makes the app speak plaintext ws:// at a TLS port and fail.
Audio ownership (the rule that ended the debugging)¶
One pipewire per machine owns the audio hardware; the player service joins THAT session as a user unit. On av the gaming/guest session owns the MOTU — running the player under a second (lingering) user's pipewire seized devices out from under it and silently broke desktop audio. Masking the second user's pipewire units (sockets included) makes the mistake unrepeatable. Displays get the web UI; native companion apps only on real desktops. Phone-as-player registers only while the app is foregrounded — by design. USB interfaces can wedge after ownership fights: replug is the fix.