Documentation: Multi-Zone Audio Setup (Sendspin Variant) Target System: Ubuntu 24.04 (Minimal VM) on Proxmox Hardware: Wondom KAB9 (8-Channel USB Audio) Software: ALSA, Sendspin (Client), Music Assistant (Docker)
- Prerequisites & Installation Kernel Drivers (USB Audio) In minimal Ubuntu images, audio modules are often missing. These must be installed and permanently loaded.
# Install driver modules
sudo apt update
sudo apt install linux-modules-extra-$(uname -r) -y
# Load module permanently
echo "snd-usb-audio" | sudo tee -a /etc/modules
sudo modprobe snd-usb-audioRequired Packages
We follow the official installer and use uv as well as libportaudio2.
# 1. Install system dependencies
sudo apt install alsa-utils libasound2-plugins libportaudio2 curl -y
# 2. Install uv (Modern Python Package Manager)
# NOTE: Please DO NOT install uv via Snap! Sandboxing often prevents
# correct function (e.g., venv creation, access to /mnt).
if ! command -v uv &> /dev/null;
then
# Official installer
curl -LsSf https://astral.sh/uv/install.sh | sh
# Load path for the current session (installer usually makes it permanent)
if [ -f "$HOME/.local/bin/env" ]; then
source "$HOME/.local/bin/env"
elif [ -f "$HOME/.cargo/env" ]; then
source "$HOME/.cargo/env"
else
# Fallback: Set paths manually
export PATH="$HOME/.local/bin:$HOME/.cargo/bin:$PATH"
fi
fi
# 3. Install sendspin via uv
# Note: If you are working as root, this will be installed to /root/.local/bin.
uv tool install sendspin
# 4. Determine path to binary
# We need this path for the systemd service file shortly.
# Note the output! (e.g., /root/.local/bin/sendspin or /home/user/.local/bin/sendspin)
uv tool dir --bin | xargs -I{} echo {}/sendspin- Audio Configuration (ALSA)
The
/etc/asound.conffile splits the 8 physical channels of the sound card into 4 virtual stereo zones.
File: /etc/asound.conf
# 1. Hardware Abstraction with dmix (Allows sharing of the 8 channels)
pcm.card_0 {
type dmix
ipc_key 2047
ipc_key_add_uid yes
slave {
pcm "hw:0,0"
rate 48000
period_time 0
period_size 1024
buffer_size 8192
channels 8
}
bindings {
0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
}
}
# 2. Multi-Device Bridge
pcm.multi {
type multi
slaves.a {
pcm "card_0"
channels 8
}
bindings.0.slave a
bindings.0.channel 0
bindings.1.slave a
bindings.1.channel 1
bindings.2.slave a
bindings.2.channel 2
bindings.3.slave a
bindings.3.channel 3
bindings.4.slave a
bindings.4.channel 4
bindings.5.slave a
bindings.5.channel 5
bindings.6.slave a
bindings.6.channel 6
bindings.7.slave a
bindings.7.channel 7
}
# 3. Zone Definitions (Stereo Pairs)
pcm.zone1 {
type plug
slave { pcm "multi" channels 8 }
ttable.0.0 1
ttable.1.1 1
}
pcm.zone2 {
type plug
slave { pcm "multi" channels 8 }
ttable.0.2 1
ttable.1.3 1
}
pcm.zone3 {
type plug
slave { pcm "multi" channels 8 }
ttable.0.4 1
ttable.1.5 1
}
pcm.zone4 {
type plug
slave { pcm "multi" channels 8 }
ttable.0.6 1
ttable.1.7 1
}
- Player Configuration (Sendspin)
We use a systemd template to start multiple player instances in
daemonmode.
Note on Root vs. User:
If you run everything as root (typical for minimal audio VMs), you can omit the User= parameter or set it to User=root. The paths will then point to /root/.local/bin/sendspin.
For normal user installations (recommended for desktop systems), set User=your_username and adjust the paths accordingly.
Step A: Identify Audio Devices Before configuring the services, we need to know how Sendspin (via PortAudio) names the ALSA zones.
# List all available audio devices
sendspin --list-audio-devices
# Look for entries like "zone1", "zone2", etc.
# Note the exact name (or index).Step B: Create Systemd Template
Adjust the variables USER_NAME and SENDSPIN_BIN according to your installation.
File: /etc/systemd/system/sendspin@.service
[Unit]
Description=Sendspin Player %I
After=network-online.target sound.target
Wants=network-online.target
[Service]
Type=simple
# Case A: Root Installation (Standard on minimal VMs)
User=root
# Adjust path: usually /root/.local/bin/sendspin
Environment="SENDSPIN_BIN_PATH=/root/.local/bin/sendspin"
# Case B: User Installation (Remove comment and adjust)
# User=my_linux_user
# Environment="SENDSPIN_BIN_PATH=/home/my_linux_user/.local/bin/sendspin"
EnvironmentFile=/etc/sendspin.d/%i.conf
# We use bash exec to cleanly expand variables
ExecStart=/bin/bash -c 'exec ${SENDSPIN_BIN_PATH} daemon \
${SENDSPIN_CLIENT_NAME:+--name "${SENDSPIN_CLIENT_NAME}"}
${SENDSPIN_AUDIO_DEVICE:+--audio-device "${SENDSPIN_AUDIO_DEVICE}"}
--id "sendspin-%i" \
--static-delay-ms ${SENDSPIN_STATIC_DELAY_MS:-0}
${SENDSPIN_ARGS}'
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal
# Security Hardening (optional, can cause issues with root operation if too strict)
# NoNewPrivileges=true
# PrivateTmp=true
# ProtectSystem=strict
# ProtectHome=read-only
[Install]
WantedBy=multi-user.targetStep C: Instance Configuration (Example Living Room) Create the directory for configurations:
mkdir -p /etc/sendspin.d/File: /etc/sendspin.d/wohnzimmer.conf
# Friendly Name for Music Assistant
SENDSPIN_CLIENT_NAME="Wohnzimmer"
# The ALSA device (Name or Index from 'sendspin --list-audio-devices')
SENDSPIN_AUDIO_DEVICE="zone1"
# Optional: Delay in ms (if synchronization issues occur)
SENDSPIN_STATIC_DELAY_MS=0
# Optional: Additional arguments
SENDSPIN_ARGS=""- Service Management
# Load and start new instance
systemctl daemon-reload
systemctl enable --now sendspin@wohnzimmer- Integration into Music Assistant Web UI: Open Music Assistant (Port 8095).
Provider: Go to Settings -> Providers. Sendspin is the native protocol of Music Assistant. Ensure the "Sendspin" provider is enabled (if present) or that players are automatically detected.
Result: The player "Wohnzimmer" should appear in the player list.
Helpful Debugging Commands
# Check logs
journalctl -u sendspin@wohnzimmer -f
# Test sound card
speaker-test -D zone1 -c 2
# Check usage
fuser -v /dev/snd/*