Audio sources are added to the server as source in the [stream] section of the configuration file /etc/snapserver.conf. Every source must be fed with a fixed sample format, that can be configured per stream (e.g. 48000:16:2).
The following notation is used in this paragraph:
<angle brackets>: the whole expression must be replaced with your specific setting[square brackets]: the whole expression is optional and can be left out[key=value]: if you leave this option out, "value" will be the default for "key"
The general format of an audio source is:
TYPE://host/path?name=<name>[&codec=<codec>][&sampleformat=<sampleformat>][&chunk_ms=<chunk ms>][&controlscript=<control script filename>[&controlscriptparams=<control script command line arguments>]]Within the [stream] section there are some global parameters valid for all sources:
sampleformat: Default sample format of the stream source, e.g.48000:16:2codec: The codec to use to save bandwith, one of:flac[default]: lossless codec, mean codec latency ~26msogg: lossy codecopus: lossy low latency codec, only supports 48kHz, if your stream has a different sample rate, automatic resampling will be applied, introducing further latecypcm: lossless, uncompresssed. No latency.
chunk_ms: Default source stream read chunk size [ms]. The server will continously read this number of milliseconds from the source into a buffer, before this buffer is passed to the encoder (thecodecabove)buffer: Buffer [ms]. The end-to-end latency, from capturing a sample on the server until the sample is played-out on the clientsend_to_muted:trueorfalse: Send audio to clients that are muted
source parameters have the form key=value, they are concatenated with an & character.
Supported parameters for all source types:
name: The mandatory source namecodec: Override the global codecsampleformat: Override the global sample formatchunk_ms: Override the globalchunk_mscontrolscript: Script to control the stream source and read and provide meta data, see stream_plugin.mdcontrolscriptparams: Control script command line arguments, must be url-encoded (use%20instead of a space " "), e.g.--mopidy-host=192.168.42.23%20--debug
Available audio source types are:
Captures audio from a named pipe
pipe:///<path/to/pipe>?name=<name>[&mode=create]mode can be create or read. Sometimes your audio source might insist in creating the pipe itself. So the pipe creation mode can by changed to "not create, but only read mode", using the mode option set to read
NOTE With newer kernels using FIFO pipes in a world writeable sticky dir (e.g. /tmp) one might also have to turn off fs.protected_fifos, as default settings have changed recently: sudo sysctl fs.protected_fifos=0.
See stackexchange for more details. You need to run this after each reboot or add it to /etc/sysctl.conf or /etc/sysctl.d/50-default.conf depending on distribution.
Launches librespot and reads audio from stdout
librespot:///<path/to/librespot>?name=<name>[&username=<my username>&password=<my password>][&devicename=Snapcast][&bitrate=320][&wd_timeout=7800][&volume=100][&onevent=""][&normalize=false][&autoplay=false][&cache=""][&disable_audio_cache=false][&killall=false][¶ms=extra-params]Note that you need to have the librespot binary on your machine and the sampleformat will be set to 44100:16:2
Parameters used to configure the librespot binary (see librespot-org options):
username: Username to sign in withpassword: Passworddevicename: Device namebitrate: Bitrate (96, 160 or 320). Defaults to 320volume: Initial volume in %, once connected [0-100]onevent: The path to a script that gets run when one of librespot's events is triggerednormalize: Enables volume normalisation for librespotautoplay: Autoplay similar songs when your music endscache: Path to a directory where files will be cacheddisable_audio_cache: Disable caching of the audio dataparams: Optional string appended to the librespot invocation. This allows for arbitrary flags to be passed to librespot, for instanceparams=--device-type%20avr. The value has to be properly URL-encoded.
Parameters introduced by Snapclient:
killall: Kill all running librespot instances before launching librespotwd_timeout: Restart librespot if it doesn't create log messages for x seconds
Add a stream source entry of type process which is briefly described further down, to /etc/snapserver.conf to launch go-librespot and read audio from stdout. Note that adding several more such stream sources with different profiles will require to have different configuration directories for go-librespot, each configured with different device name and server port accordingly:
source = process:///<path/to/go-librespot>?name=<name>&sampleformat=44100:16:2¶ms=--config_dir%20/var/lib/snapserver/.config/go-librespot/<name>&idle_threshold=2000&wd_timeout=0&log_stderr=false&controlscript=meta_go-librespot.py&controlscriptparams=--stream=<name>%20--librespot-host=127.0.0.1%20--librespot-port=24879You need to have the go-librespot binary on your machine and a configuration file located in the path passed as --config_dir in the above source example, which of course need to be accessible to the snapserver user.
go-librespot uses a sample rate of 44100 for the pipe output, you can either configure this globally for snapcast or specify it only for the go-librespot source (see example above).
Read about the options which can be used in the file config.yml in the go-librespot configuration documentation, including the linked config schema which has all the options. You will be interested in how to:
- persist your Spotify credentials either in
zeroconforinteractivemode; - match the sampling format of go-librespot's "pipe" audio backend to what your snapserver configuration expects;
- last but not least, if you need adjusting the volume, in order to align it somehow to your other snapcast sources if necessary. Surprisingly, since we're using the pipe
audio_backend, none of the volume-related settings have worked for me for raising the volume, as they would only be effective with disabled normalisation, which unfortunately led to crackling sounds due to go-librespot clipping some samples. Luckily, adjusting the volume can be achieved enabling normalisation and setting a pregain value (in dB). The track or album gain provided in the Spotify metadata will be used, to which the pregain is added (negative pregain will lower the volume).go-librespotwill silently cap the resulting normalisation factor to 1.0 in order to avoid clipping, however this would be just seen in the log and the effect of raising the volume will be limited, so you would need to try a lower (or negative) value, or maybe rather adjust the volume of other snapserver.conf sources. If interested, you can find more details in go-librespot's source of the functions GetTrackFactor / GetAlbumFactor. For me, raising the volume by usingnormalisation_pregain: 6.0had the effect that playing a track from my local MP3 collection through Mopidy and then the same track from Spotify subjectively sounded at similar volume.
Create the file /var/lib/snapserver/.config/go-librespot/<name>/config.yml (path may vary according to how snapserver is configured on your system) with the following content addressing the above 3 topics and adapt it to your needs (at least the device name):
device_name: "<name>"
device_type: "speaker"
audio_backend: "pipe"
audio_output_pipe: "/dev/stdout"
audio_output_pipe_format: "s16le"
bitrate: 320
normalisation_disabled: false
normalisation_use_album_gain: true
normalisation_pregain: 6.0
server:
enabled: true
port: 24879
credentials:
type: zeroconf
zeroconf:
persist_credentials: trueLaunches shairport-sync and reads audio from stdout
airplay:///<path/to/shairport-sync>?name=<name>[&devicename=Snapcast][&port=5000][&password=<my password>]Note that you need to have the shairport-sync binary on your machine and the sampleformat will be set to 44100:16:2
Parameters used to configure the shairport-sync binary:
devicename: Advertised nameport: RTSP listening port (5000 for Airplay 1, 7000 for Airplay 2)password: Passwordparams: Optional string appended to the shairport-sync invocation. This allows for arbitrary flags to be passed to shairport-sync, for instanceparams=--on-start=start.sh%20--on-stop=stop.sh. The value has to be properly URL-encoded.
Reads PCM audio from a file
file:///<path/to/PCM/file>?name=<name>Launches a process and reads audio from stdout
process:///<path/to/process>?name=<name>[&wd_timeout=0][&log_stderr=false][¶ms=<process arguments>]wd_timeout: kill and restart the process if there was no message logged for x seconds to stderr (0 = disabled)log_stderr: Forward stderr log messages to Snapclient loggingparams: Params to start the process with
Receives audio from a TCP socket (acting as server)
tcp://<listen IP, e.g. 127.0.0.1>:<port>?name=<name>[&mode=server]default for port (if omitted) is 4953, default for mode is server
Mopidy configuration would look like this (running GStreamer in client mode)
[audio]
output = audioresample ! audioconvert ! audio/x-raw,rate=48000,channels=2,format=S16LE ! wavenc ! tcpclientsink host=127.0.0.1Receives audio from a TCP socket (acting as client)
tcp://<server IP, e.g. 127.0.0.1>:<port>?name=<name>&mode=clientMopidy configuration would look like this (running GStreamer in server mode):
[audio]
output = audioresample ! audioconvert ! audio/x-raw,rate=48000,channels=2,format=S16LE ! wavenc ! tcpserversink host=127.0.0.1Captures audio from an alsa device
alsa:///?name=<name>&device=<alsa device>[&send_silence=false][&idle_threshold=100][&silence_threshold_percent=0.0]device: alsa device name or identifier, e.g.defaultorhw:0,0orhw:0,0,0idle_threshold: switch stream state from playing to idle after receivingidle_thresholdmilliseconds of silencesilence_threshold_percent: percent (float) of the max amplitude to be considered as silencesend_silence: forward silence to clients when stream state isidle
The output of any audio player that uses alsa can be redirected to Snapcast by using an alsa loopback device:
-
Setup the alsa loopback device by loading the kernel module:
sudo modprobe snd-aloop
The loopback device can be created during boot by adding
snd-aloopto/etc/modules -
The loopback device should show up in
aplay -laplay -l **** List of PLAYBACK Hardware Devices **** card 0: Loopback [Loopback], device 0: Loopback PCM [Loopback PCM] Subdevices: 8/8 Subdevice #0: subdevice #0 Subdevice #1: subdevice #1 Subdevice #2: subdevice #2 Subdevice #3: subdevice #3 Subdevice #4: subdevice #4 Subdevice #5: subdevice #5 Subdevice #6: subdevice #6 Subdevice #7: subdevice #7 card 0: Loopback [Loopback], device 1: Loopback PCM [Loopback PCM] Subdevices: 8/8 Subdevice #0: subdevice #0 Subdevice #1: subdevice #1 Subdevice #2: subdevice #2 Subdevice #3: subdevice #3 Subdevice #4: subdevice #4 Subdevice #5: subdevice #5 Subdevice #6: subdevice #6 Subdevice #7: subdevice #7 card 1: Intel [HDA Intel], device 0: CX20561 Analog [CX20561 Analog] Subdevices: 1/1 Subdevice #0: subdevice #0 card 1: Intel [HDA Intel], device 1: CX20561 Digital [CX20561 Digital] Subdevices: 1/1 Subdevice #0: subdevice #0 card 2: CODEC [USB Audio CODEC], device 0: USB Audio [USB Audio] Subdevices: 0/1 Subdevice #0: subdevice #0
In this example the loopback device is card 0 with devices 0 and 1, each having 8 subdevices.
The devices are addressed withhw:<card idx>,<device idx>,<subdevice num>, e.g.hw:0,0,0.
If a process plays audio usinghw:0,0,x, then the audio will be looped back tohw:0,1,x -
Configure your player to use a loopback device
For mopidy (gstreamer) in
mopidy.conf:output = audioresample ! audioconvert ! audio/x-raw,rate=48000,channels=2,format=S16LE ! alsasink device=hw:0,0,0
For mpd: in
mpd.confaudio_output_format "48000:16:2" audio_output { type "alsa" name "My ALSA Device" device "hw:0,0,0" # optional # auto_resample "no" # mixer_type "hardware" # optional # mixer_device "default" # optional # mixer_control "PCM" # optional # mixer_index "0" # optional }
For librespot (check previusly if your librespot binary is compiled with alsa backend with
./librespot --backend ?): Warning, you need to set snapserver rate to 44100source = alsa:///?name=Spotify&sampleformat=44100:16:2&device=hw:0,1,0./librespot -b 320 --disable-audio-cache --name Snapcast --initial-volume 100 --backend alsa --device hw:0,0,0
-
Configure Snapserver to capture the loopback device:
[stream] source = alsa:///?name=SomeName&device=hw:0,1,0
Direct audio capture from PipeWire, see the PipeWire Stream Usage Guide
Reads audio from a Jack server. Snapcast must be built with the cmake flag BUILD_WITH_JACK=ON to enable Jack support.
jack:///?name=<name>[sampleformat=48000:16:2][autoconnect=][autoconnect_skip=0][&send_silence=false][&idle_threshold=100]server_name: The Jack server name to connect to, leave empty for "default"autoconnect: Regular expression to match Jack ports to auto-connect to stream inputsautoconnect_skip: Skip this number of matches from the regular expressionidle_threshold: switch stream state from playing to idle after receivingidle_thresholdmilliseconds of silencesilence_threshold_percent: percent (float) of the max amplitude to be considered as silencesend_silence: forward silence to clients when stream state isidle
Each jack stream creates a separate connection to a jack server and registers
as many input ports as there are audio channels in this stream (according to
sampleformat).
You can use autoconnect to automatically connect this streams input ports
with Jack output ports. The parameter takes a regular expression and matches
against the whole Jack port name (including client name). For example, if you
have a Jack client named "system" with four output ports ("playback_1",
"playback_2", ...) and you want each output as a separate SnapCast stream, you
could either autoconnect to the exact ports, or you use an autoconnect search
term that returns all ports and use autoconnect_skip to pick the right one:
jack:///?name=Channel1&sampleformat=48000:16:1&autoconnect=system:playback_
jack:///?name=Channel2&sampleformat=48000:16:1&autoconnect=system:playback_&autoconnect_skip=1
jack:///?name=Channel3&sampleformat=48000:16:1&autoconnect=system:playback_&autoconnect_skip=2
jack:///?name=Channel4&sampleformat=48000:16:1&autoconnect=system:playback_&autoconnect_skip=3-
Currently all
jackstreams need to match the sample rate of the Jack server. -
The
chunk_msparameter is ignored for jack streams. The Jack buffer size (Frames/Period) is used instead.
Read and mix audio from other stream sources
meta:///<name of source#1>/<name of source#2>/.../<name of source#N>?name=<name>Plays audio from the active source with the highest priority, with source#1 having the highest priority and source#N the lowest.
Use codec=null for stream sources that should only serve as input for meta streams
Streaming clients connect to the server and receive configuration and audio data. The client is fully controlled from the server so clients don't have to persist any state. The [streaming_client] section has just one option currently:
initial_volume: 0-100 [percent]: The volume a streaming client gets assigned on very first connect (i.e. the client is not known to the server yet). Defaults to 100 if unset.
Snapserver supports RPC via HTTP(S) and WS(S) as well as audio streaming over WS(S). To enable HTTP and WS, the parameter enabled must be set to true (default) in the [http] section.
For HTTPS/WSS, the paramter ssl_enabled must be set to true (default: false) and the certificate and certificate_key paramters in the [ssl] section must point to a certificate file and key file in PEM format.
If you want only trusted clients being able to connect, the parameter verify_clients must be set to true and the client CA certificates must be configures as list of client_cert = entries.
Some hints on how to create a certificate and a private key are given for instance here:
A certificate, signed by a CA, and a certificate key can be created like this:
Create an X509 V3 certificate extension config file snapserver.ext, which is used to define the Subject Alternative Name (SAN) for the certificate.
Set the DNS.x values to your snapserver's IPs and domain names:
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = <IP or domain name>
DNS.2 = <another IP or domain name>
...
DNS.x = <another IP or domain name>Use OpenSSL to create the CA certificate snapcastCA.crt, the CA key snapcastCA.key, the snapserver certificate snapserver.crt, signed by the snapcastCA and the snapserver key snapserver.key:
# Create a private CA key file "snapcastCA.key"
openssl genrsa -out snapcastCA.key 4096
# Generate a root certificate "snapcastCA.crt"
openssl req -x509 -new -nodes -key snapcastCA.key -sha256 -days 3650 -out snapcastCA.crt -subj "/C=DE/ST=NRW/O=Badaix"
# Create a certificate key file "snapserver.key" for snapserver
openssl genrsa -out snapserver.key 2048
# Create a certificate signing request (CSR) "snapserver.csr"
openssl req -new -sha256 -key snapserver.key -subj "/C=DE/ST=NRW/O=Badaix/CN=Snapserver" -addext "subjectAltName=DNS:laptop.local,DNS:127.0.0.1" -out snapserver.csr
# Create a certificate file "snapserver.crt", signed by the root CA
openssl x509 -req -in snapserver.csr -CA snapcastCA.crt -CAkey snapcastCA.key -CAcreateserial -out snapserver.crt -days 3650 -sha256 -extfile snapserver.extCopy snapserver.crt and snapserver.key into the /etc/snapserver/certs/ directory and configure [ssl] in snapserver.conf with:
[ssl]
...
# Certificate file in PEM format
certificate = snapserver.crt
# Private key file in PEM format
certificate_key = snapserver.keyInstall the CA certificate snapcastCA.crt on you client's OS or browser.
To use an SSL connection to the server, the client must use the secure websockets URI: snapclient [options...] wss://<server host or IP>[:port].
To enable server authentication, the server CA certificate can be configured with --server-cert=<filename>.
If the server is confgured to authenticate the clients (verify_clients = true in snapserver.conf), you must configure the client certificate and private key with --cert=<filename> and --cert-key=<filename>.