Whole House Audio with Snapcast

Open Source based Whole House Audio with Snapcast

The Snapcast project provides an Open Source client (Snapclient) and server (Snapserver) that distributes audio over a digital network to multiple audio output devices while ensuring synchronization. This enables listening to an audio stream over an extended area, even if that area consists of multiple isolated spaces.

What Snapcast does is simple. But it does it very well, and the pervasive access to music that Snapcast enables has contributed significantly to my quality of life over the last several years.

With a little effort Snapcast makes it possible to implement a whole house audio system that rivals six figure professional installations but which, if implemented with at hand or second hand hardware, costs very little.

Many thanks to the contributors who make Snapcast possible.

Legacy solutions

Collecting and listening to music has always been a priority for me, and I like to make music part of my everyday experience.

For me the best way to achieve this is to distribute the audio I am listenting to throughout my home, so I can listen uninterrupted as I move about.

I have used several methods to accomplish this over the years.

Short range FM transmitter

UniKit UX300 FM Transmitter

When my living arrangements did not accommodate installing the necessary cables to achieve whole house audio, my solution was a short range FM transmitter. This solution was simple to set up, but offered limited range and mediocre sound quality.


Analog cabling

Once I had the means, I installed analog cabling from the analog line output on my server to my receiver/amplifier. This provided quality sound in the space where the stereo system resided, but other parts of the house were still dependent on the FM transmitter and it’s limited range and sound quality.


Architectural speakers

I had the good fortune to design and build my own house. This allow me to place architectural speakers in the main living space, driven by a stereo receiver receiving the analog feed from my server.

Definitely a step forward. But quality listening was still restricted to the one space, and managing content through the server independent from setting volume and speaker selection at the stereo receiver complicated operation.

With the new house came a new problem. The house was in a rural area, in a sheltered location, and had a steel roof. FM radio, the primary source of much of what I liked to listen to, was unusable.

The other feature incorporated in the house was extensive Cat 5e cabling. There had to be a way to distribute quality audio throughout the house without having to run parallel analog audio cabling to every room.


Why an Open solution?

At this point, everyone in the audience is screaming “Sonos”. Yes, Sonos does this; distributed, synchronized audio over a digital network.

However, with a proprietary solution:

With an Open solution, there is more work up front. But you secure a solution that:

Google has a campaign underway for Nest devices that highlights whole house audio as the key feature. If you are happy revealing intimate personal information in exchange for convenience of installation you are welcome to your choice. But this is not a solution that works for me.

Sonos appears to only stream synchronized audio sourced from a phone. Why should my home audio system fail just because I am using my phone away from home? Which, last time I checked, was the primary use of a mobile phone.

Snapserver can stream synchronized audio from your phone, but can be configured to do much more.

Finally, there is a lot of high quality audio equipment being disposed of because it is not easily integrated with digital audio sources. Snapcast let’s you redeploy these components into an easy to use, high functionality, and future proof whole house audio system.

Snapcast

The authoritative repository for Snapcast is on Github. The software is available there, as well as extensive and high quality documentation.

Installing Snapcast

Snapcast has been packaged for most linux distributions. Most of my systems are based on Debian Linux, so both the client and server was available via apt-get.

Instructions are available to build Snapcast for:

  • Linux
  • FreeBSD
  • macOS
  • Android
  • OpenWrt
  • Buildroot
  • Raspberry Pi
  • Windows

I successfully built Snapclient on piCore; a Linux distribution for Raspberry Pi.

Native Snapclient apps are available for iOS and Android


Snapcast components

There are two components to Snapcast:

  • A single server instance (Snapserver) accepts streams of audio from multiple sources, and makes them available to multiple clients

    The server also provides a web interace which controls the stream played by, and volume, of each Snapclient.

  • Multiple clients (Snapclient) receive a stream from the server, and outputs synchronized audio to an amplifier and speaker


Configuration

Configuration of the system is done at the server, through editing the file snapserver.conf.

I am not going repeat the documentation for the configuration file here. Rather, I am going to share the architecture I use, solutions to the challenges I ran into, and provide some shortcuts that are of particular value to an Alberta audience.


Audio sources

Snapserver can receive multiple audio sources. In a typical installation, there are multiple sources being processed and made available simultaneously by the Snapserver. This allows individual Snapclients to chose the audio source desired at each location.

The Snapserver primarily accepts audio in the form of a bit stream, either piped through STDOUT, or through a FIFO queue.

Making an audio source available through your Snapcast system is a matter of finding and configuring software that can handle the source files or stream, and output it as a stream of PCM audio.

The documentation provides extensive examples for this, including integration with common media hub systems.

But for my application I ended up using four source types:

Playing an MP3 audio file collection

My audio library is old and simple. It consists of a simple hierarchy of mp3 files in directories of the form:

genre 1
   artist 1
      album 1
         track 1.mp3
         track 2.mp3
         track 3.mp3
         ...
         track n.mp3
      album 2
         track 1.mp3
         ...
      album 3
         ...
      ...
      album n
   artist 2
      album 1
      album 2
      ...
genre 2
   ...

Or, specifically:

.
├── Christmas
│   ├── Academy and Chorus of St. Martin-in-the-Fields
│   │   └── Christmas with the Academy
│   │       ├── Away in a Manger.mp3
│   │       ├── Christmas Oratorio: Sinfonia.mp3
│   │       └── Tomorrow Shall Be My Dancing Day.mp3
│   ├── Allison Brown
│   │   └── Evergreen
│   │       ├── Carol And The Kings.mp3
│   │       ├── Christmas Don't Be Late.mp3
│   │       └── Two Santas.mp3
│   ├── Allison Young
├── Music
│   ├── Alison Krauss
│   │   ├── A Hundred Miles Or More - A Collection
│   │   │   ├── Away Down The River.mp3
│   │   │   ├── Baby Mine.mp3
│   │   │   ├── Down To The River To Pray.mp3
│   │   │   ├── Get Me Through December.mp3

Play lists are handled through directories containing soft links to the songs that I want included in the list. Each mp3 file is fully tagged.

The oldest file in the hierarchy is from 1998. That is 24 years of continuous use without having to re-encode or reorganize.

The following BASH code snippet starts from a directory and assembles a play list of the mp3 files in the hierarchy of directories:

cd /home/shared/served_files/Audio/Music
sudo rm -f /tmp/SnapList
sudo find ~+ -type f -follow -name *.mp3 > /tmp/SnapList
sudo chown Snapserver /tmp/SnapList
sudo chgrp Snapserver /tmp/SnapList

and then this line in snapserver.conf shuffle plays the list with mplayer:

source = process:///usr/bin/mplayer?name=All&params=-vo null -really-quiet -srate 48000 -nolirc -volume 100 -playlist /tmp/SnapList -ao pcm:file=/dev/stdout

In my system I have two instances of mplayer feeding streams to Snapserver all the time, each defined by a directory containing soft links to a collection of tracks suitable for daytime (lively) or evening (more subdued) listening.

A third mplayer instance is enabled automatically December 1, and disabled December 26. It serves Christmas music.


Playing Internet streams

Most of the audio I listen to is sourced from internet audio streams. Specifically, I am a fan of, and donor to, CKUA.

While content providers really, really, really want you to listen through their phone App, most of them provide an http based audio stream that can be accessed directly. They streams are necessary if a content creator wants to be available through music player applications, or smart speakers like Nest or HomePod.

To listen to one of these streams through Snapcast you need to find the URL. The best source I have found for this is fmstream.org.

Accessing CKUA is a challenge. They have multiple streams, which tend to be inconsistent in quality, availability and stability. The MP3 streams were the best to use for several years, but recently started dropping and skipping. I am now using the AAC stream (which disappeared for a while for some reason) and as of Q3 2022 they are good quality and reliable.

CBC also likes to mess around and change their streams on a regular basis. The URL’s is use for accessing CBC changed three times during the preparation of this article.

Secondly, you need to select the audio player client software to use. Initially, I mostly used mplayer. However, I now find I get more reliable results from cvlc, which is the command line invocation for Video Lan Client.

The final challenge is setting the “sampleformat” configuration value in relevant line in snapserver.conf correctly. One way to determine the correct settings is to use VLC (Video Lan Client) to listen to the stream; the “Media information” page then provides as starting point for the values you need to provide to Snapserver.

If a stream makes noise, but it sounds like the Chipmunks, you need to mess around with the rate component of the “sampleformat” configuration attribute.

Here are the lines from my snapserver.conf file that provide access to BBC Radio 1 (This is the BBC’s modern popular music and current chart hits broadcast, not the BBC World Service), CKUA, Radio Caroline (the original pirate radio station out of England), CBC Radio 1 and 2, and Jazz FM 91 out of Toronto:

source = process:///usr/bin/cvlc?name=BBC_1&sampleformat=48000:16:2&params=--no-video --aout afile --audiofile-file - http://stream.live.vc.bbcmedia.co.uk/bbc_radio_one
source = process:///usr/bin/cvlc?name=CKUA&sampleformat=44100:16:2&params=--no-video --aout afile --audiofile-file - http://ais-sa1.streamon.fm/7000_48k.aac
source = process:///usr/bin/cvlc?name=CAROLINE&sampleformat=44100:16:2&params=--no-video --aout afile --audiofile-file - http://sc3.radiocaroline.net:8030/listen.pls
source = process:///usr/bin/cvlc?name=CBC_1&sampleformat=44100:16:2&params=--no-video --aout afile --audiofile-file - http://cbcmp3.ic.llnwd.net/stream/cbcmp3_cbc_r1_cgy
source = process:///usr/bin/cvlc?name=CBC_2&sampleformat=44100:16:2&params=--no-video --aout afile --audiofile-file - http://cbcmp3.ic.llnwd.net/stream/cbcmp3_cbc_r2_edm
source = process:///usr/bin/mplayer?name=Jazz.FM91&params=-vo null -cache 100 -cache-min 50 -really-quiet -srate 48000 -nolirc -volume 100 -ao pcm:file=/dev/stdout https://jazzfm91.streambb.live/SB00009

Yes, I know. I am a nerd.


Airplay

The shairport program allows linux hosts to receive Airplay streams from iOS (Apple) devices. It can be used as a source for Snapcast.

Be aware that shairport imparts a significant delay to the audio stream. This means you get poor results if you try to distribute the audio stream from a video; the video and audio track end up out of sync. However, the solution is great for listening to podcasts.

The required line in snapserver.conf:

source = process:////usr/bin/shairport-sync?name=Airplay&params=-o stdout&sampleformat=44100:16:2

Complete details are provided in the documentation.


Pulse audio redirection

You can direct the output from the Pulse audio daemon on a linux desktop instance to Snapserver.

I use this is to direct the audio from web seminars into Snapcast. I can then move about the house doing other tasks while still listening.

I initially had problems getting pulse redirection to start reliably; every time I went to use it a lot of fiddling and restarting was required to get it to work. But I eventually resolved the issue.

The Snapclient documentation suggests creating the fifo queue that carries the bitstream from the pulseaudio daemon to snapserver in /tmp, i.e /tmp/snapfifo.

It turns out that “sticky” directories like /tmp have obscure usage restrictions that prevent processes that do not share a user id from opening a common file.

After creating the fifo in /run (i.e. /run/snapfifo), changing snapserver.conf, and directing the pulseaudio server to the new fifo, everything starts reliably.

The required line in snapserver.conf:

source = pipe:///run/snapfifo?name=Pulse&mode=read

Some configuration of your desktop is also required; the documentation provides the details.

One final point; the “&mode=read” argument in the snapserver.conf line is important. Otherwise the Snapserver recreates the fifo every time it starts, which causes the connection from the pulseaudio daemon to be lost.


Snapclient

Once you have your Snapserver instance up and running, multiple instances of Snapclient are used to listen to the audio streams.

Snapclient hosts can include:

Snapclient instance examples

I have up to fourteen instances of Snapclient running on my network. Here is what my Snapserver control page looks like when they are all running:
My Snapclient control page
Click to view full size

  • Snapclient runs on each of my three Linux desktops/laptop (one desktop runs two instances) and the Linux based server.


    Portable Snapclient

  • Two dedicated Raspberry Pi’s with IQaudio DigiAMP+ boards drive the architectural speakers in the great room.

  • Two dedicated Raspberry Pi’s drive Ikea Eneby speakers in the bed rooms.


    Portable Snapclient

  • A Raspberry Pi Model A powered by a USB power bank drive an Ikea Eneby speaker equipped with a battery. This used to take audio out to the deck.

  • Snaplient running on a Raspberry Pi with another IQaudio DigiAMP+ board in an outbuilding. This Pi also provides a WiFi access point and security camera.

  • The Snapclient App running on my Macbook.

  • The Snapclient App running on my iPhone and iPad.


I highly recommend Ikea Eneby speakers; I have four of them. They are affordable, simple and easy to use, look good, and have great sound quality.

Unfortunately, Ikea is discontinuing the Eneby and replacing it with the Sonos based Symfonisk line. Which are three times the cost and don’t have an analog input; they only play audio from a phone.

Isn’t capitalisim grand!

Things I learned setting up my clients:

Controlling your audio system

Snapcast only deals with synchronized distribution of audio. You need to decide how you want to control your audio system and manage your media.

A control system is not strictly necessary. My play lists are directories of soft links to the directories of mp3 files I want in the playlist. I could configure Snapserver to run an mplayer instance that shuffle plays each playlist (as per example above), and a vlc instances for every audio streams I am interested in, and select which stream I wanted listen to with snapclient. I would start and stop the server by logging into the console and issuing commands.

In my case, I use a custom built web site hosted on my home server. I am not going to share this code; it is implemented with CGI scripts that were built piecemeal over the years, some functionality is currently broken, and it is thoroughly obsolete.

The best solution for most is to install and use an established media management system. The Snapcast documentation provides explicit instructions for Music Player Daemon and Mopidy.

There is a snapcast component for Home Assistant.

Here are some considerations to keep in mind when planning your system:

Restarting Snapserver

You need to have some method to manually restart Snapserver. The culprit is not Snapserver; it is very stable. The culprit is audio stream sources or Internet access that are short of capacity, causing the audio player providing a stream to exit because it’s cache is exhausted.


Running a single web audio stream

Snapserver, when invoked, will start up a process for each source, and make them all available for selection via Snapclient.

This may be acceptable if you have fast internet. I have have crappy rural internet, so I avoid traffic that is not of immediate value.

The way I achieve this is bash scripting that comments out the lines in snapclient.conf related to streams I am not listening to, and uncomments the line for the stream I am going to listen to. This is done before invoking Snapserver.

These bash script snippets:

  1. Uncomment every line in snapserver.conf related to an internet audio stream:

    sudo sed -i '/CBC_1/s/^#//' /etc/snapserver.conf
    sudo sed -i '/CBC_2/s/^#//' /etc/snapserver.conf
    sudo sed -i '/CKUA/s/^#//' /etc/snapserver.conf
    sudo sed -i '/Jazz.FM91/s/^#//' /etc/snapserver.conf
    sudo sed -i '/BBC_1/s/^#//' /etc/snapserver.conf
    sudo sed -i '/CAROLINE/s/^#//' /etc/snapserver.conf
    
  2. Comment out every line in snapserver.conf related to an internet audio stream:

    sudo sed -i '/CBC_1/s/^/#/' /etc/snapserver.conf
    sudo sed -i '/CBC_2/s/^/#/' /etc/snapserver.conf
    sudo sed -i '/CKUA/s/^/#/' /etc/snapserver.conf
    sudo sed -i '/Jazz.FM91/s/^/#/' /etc/snapserver.conf
    sudo sed -i '/BBC_1/s/^/#/' /etc/snapserver.conf
    sudo sed -i '/CAROLINE/s/^/#/' /etc/snapserver.conf
    
  3. Uncomment the line related to the stream that is going to be listened to:

    if [ "$QUERY_STRING" == "CBC1" ]
       then
          sudo sed -i '/CBC_1/s/^#//' /etc/snapserver.conf
          fi
    
    if [ "$QUERY_STRING" == "CBC2" ]
       then
          sudo sed -i '/CBC_2/s/^#//' /etc/snapserver.conf
          fi
    
    if [ "$QUERY_STRING" == "CKUA" ]
       then
          sudo sed -i '/CKUA/s/^#//' /etc/snapserver.conf
          fi
    
    if [ "$QUERY_STRING" == "JAZZFM91" ]
       then
          sudo sed -i '/Jazz.FM91/s/^#//' /etc/snapserver.conf
          fi
    
    if [ "$QUERY_STRING" == "BBC1" ]
       then
          sudo sed -i '/BBC_1/s/^#//' /etc/snapserver.conf
          fi
    
    if [ "$QUERY_STRING" == "CAROLINE" ]
       then
          sudo sed -i '/CAROLINE/s/^#//' /etc/snapserver.conf
          fi
    

All this is run just before snapserver is started.

Conclusion

For me, Snapcast was the archetypical Open Software experience.

My journey began with “Gee, it would be nice to be able do that.”, progressed to, “This looks interesting. Since it is free and simple to install, let’s give a try.“, and concluded with “How did I live without this?“.

And because I built my system myself, and have permanent access to all of the required components (and could hack the source if I really had to) I know that rather than having to throw away and replace everything in a few years, I have another item in my my Open Source toolkit that can be adapted and sustained as long as I require it.


Welcome | Solutions | Flokk | Contact