Here’s how to setup Raspberry Pi as AirPlay audio player, so we can stream music to it from Apple devices (macOS, iOS, etc.). This is possible thanks to amazing work from many different developers over the years, going back to original Shairport, which successfully reversed engineered first version of AirPlay. Shairport Sync is a fork of that project, building on the legacy.

Info

This only works as an audio player (target). Unfortunately it’s not possible to use Shairport Sync as a source and use it to stream audio to other AirPlay compatible speakers. AFAIK this can only be done from Apple systems, like iOS and macOS. I haven’t found a workaround to this yet, although PulseAudio network streams (and its modern implementation in PipeWire) look like an interesting alternative to Apple AirPlay as a whole that I may explore in the future. Building a complete FOSS multi-room audio system is a bit daunting, but I can’t resist to try.

I’m using Raspberry Pi OS Lite x64 (without desktop environment) and the same Pi I used in my earlier post.

Setting up Shairport Sync

First, let’s make sure all packages are up to date:

sudo apt update && apt list --upgradable
sudo apt upgrade -y

Now let’s install packages required to build and run Shairport Sync:

sudo apt install autoconf libtool libdaemon-dev libasound2-dev libpopt-dev libconfig-dev avahi-daemon libavahi-client-dev libssl-dev git

We can now clone the repo:

git clone https://github.com/mikebrady/shairport-sync.git

Now we can configure it with these three commands:

cd shairport-sync
autoreconf -i -f
./configure --with-alsa --with-avahi --with-ssl=openssl --with-systemd --with-metadata --sysconfdir=/etc

The flags we are using are:

FlagDescription
--with-alsaOutput to the Advanced Linux Sound Architecture (ALSA) system. This is recommended for highest quality.
--with-avahiChooses Avahi-based Zeroconf support. This is mandatory for AirPlay 2 operation.
--with-ssl=opensslUses the OpenSSL cryptography toolkit. This is mandatory for AirPlay 2 operation.
--with-systemdIncludes a script to create a Shairport Sync service that can optionally launch automatically at startup on systemd-based Linux.
--with-metadataAdds support for Shairport Sync to request metadata (track name, artist name, album name, cover art and more) and to pipe it to a compatible application.
--sysconfdir=/etcShairport Sync will look for a configuration file – shairport-sync.conf by default – when it starts up. sysconfdir defaults to /usr/local/etc directory. I prefer /etc.

Note

We need to talk about sound in Linux. Like many other aspects, this part of the system is modular and comprises of several different components that work in tandem. There is a plethora of options and possible configurations out there, but most distros nowadays ship with PipeWire.

Here’s a simplified explanation how sound is handled in Linux:

ApplicationplayingaudioPipeWireLinuAxLSkAernelHardware··

--with-alsa flag instructs Shairport Sync to use plain ALSA (Advanced Linux Sound Architecture), rather than the middleware. This setup is simple, minimal, and fitting for a headless streamer. ALSA is an integral part of Linux kernel on any distro, as it provides audio device drivers and facilitates direct access to the hardware.

On a system with a desktop environment, you probably shouldn’t use ALSA but rather your sound server (middleware). This is most likely PipeWire, but can also be PulseAudio depending on the Linux distro (or one of the more obscure options - if you are using a distro that ships them as default, then you surely must know what you’re doing). Check which one is installed on your system, then use --with-pipewire or --with-pa flags respectively.

Optionally you could add below flag to enable AirPlay version 2 support. I am going to omit it however and use version 1:

FlagDescription
--with-airplay-2Enables support for AirPlay 2, newer version of the protocol that offers better multi-room compatibility with Apple devices and integrates with Apple Home app.

Info

Why am I not using version 2 of the protocol? Not all of its features are currently supported by Shairport Sync (for example smart control when integrated with Apple Home app). Also, the implementation of v2 operates with 256Kbps AAC streams in certain scenarios, while v1 is lossless. Since I’m going to be actively listening on headphones most of the time, audio quality takes priority for me. You can find detailed explanation and also all other possible configuration flags here.

Finally we can build it and install it with these two commands:

make
sudo make install

We should be able to start the service now and enable it to start automatically on future boots:

sudo systemctl enable shairport-sync --now

Let’s check the status with:

luk@officepi:~ $ sudo systemctl status shairport-sync.service
● shairport-sync.service - Shairport Sync - AirPlay Audio Receiver
     Loaded: loaded (/lib/systemd/system/shairport-sync.service; enabled; vendor preset: enabled)
     Active: active (running) since Sat 2023-09-16 12:51:07 BST; 33s ago
   Main PID: 608 (shairport-sync)
      Tasks: 7 (limit: 779)
        CPU: 350ms
     CGroup: /system.slice/shairport-sync.service
             └─608 /usr/local/bin/shairport-sync --log-to-syslog

Sep 16 12:51:07 officepi systemd[1]: Started Shairport Sync - AirPlay Audio Receiver.

Raspberry Pi should appear now on the list of AirPlay devices. Here’s an example from iOS:

Screenshot from iOS

Changing audio output

By default Raspberry Pi should output audio via integrated headphone jack. If you have access to the desktop environment with a sound server (like PipeWire), then you can easily change the output device via GUI. Here’s how to change it on a headless system in ALSA, without a sound server.

Option 1: Modify default ALSA audio device

This is the option I chose, since I’m using the Raspberry Pi as a headless streamer and I’m going to use only one audio output for Sharepoint Sync and any other software that I may add in the future.

First, let’s check all available devices:

luk@officepi:~ $ aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: Headphones [bcm2835 Headphones], device 0: bcm2835 Headphones [bcm2835 Headphones]
  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: S3E [Schiit Modi 3E], device 0: USB Audio [USB Audio]
  Subdevices: 0/1
  Subdevice #0: subdevice #0
card 2: vc4hdmi [vc4-hdmi], device 0: MAI PCM i2s-hifi-0 [MAI PCM i2s-hifi-0]
  Subdevices: 1/1
  Subdevice #0: subdevice #0

In my case, I have a USB audio DAC that I’d like to use as a default output. That’s card number 1. Unfortunately those numbers don’t mean much, as they can change on reboots, especially when certain devices are not present at boot time and are plugged in later (like a USB DAC might be). We can work around that by using device names instead. They are in the output above, but Arch wiki also has a nice one liner to query them:

luk@officepi:~ $ aplay -l | awk -F \: '/,/{print $2}' | awk '{print $1}' | uniq
Headphones
S3E
vc4hdmi

We can now set a system-wide default by creating /etc/asound.conf:

sudo nano /etc/asound.conf

And pasting below config. Make sure to change the card name to your preferred one, in my case it’s S3E:

pcm.!default {
   type hw
   card S3E
}

ctl.!default {
   type hw
   card S3E
}

Finally, let’s reboot to apply the changes:

sudo shutdown -r now

That’s it! Shairport Sync should now play audio via your preferred device.

Option 2: Specify output device in shairport-sync.conf

It is also possible to specify output device in the config file for Shairport Sync, independently of system-wide default. This will only work if you also used --with-alsa flag earlier. If you are using PulseWire or other middleware, then Shairport Sync can’t access the hardware directly. Instead you should be able to manage the output sink in that middleware.

First we need to list all PCM interfaces. Note that we are using capital -L this time. -l lists physical devices. I’ll truncate the output to show only couple devices as an example:

luk@officepi:~ $ aplay -L
[...]
front:CARD=S3E,DEV=0
    Schiit Modi 3E, USB Audio
    Front output / input
[...]
hdmi:CARD=vc4hdmi,DEV=0
    vc4-hdmi, MAI PCM i2s-hifi-0
    HDMI Audio Output

We need to grab the card name of our preferred interface. In examples above that’s S3E from front:CARD=S3E,DEV=0 line and vc4hdmi from hdmi:CARD=vc4hdmi,DEV=0.

Tip

You can also use shairport-sync -h to list available devices. At the end of the output you should see:

luk@officepi:~ $ shairport-sync -h
[...]
Settings and options for the audio backend "alsa":
    -d output-device    set the output device, default is "default".
    -c mixer-control    set the mixer control name, default is to use no mixer.
    -m mixer-device     set the mixer device, default is the output device.
    -i mixer-index      set the mixer index, default is 0.
    hardware output devices:
      "hw:Headphones"
      "hw:S3E"
      "hw:vc4hdmi"

We can now specify output device in the config file, which should be located in /etc/shairport-sync.conf if you used the same configuration flags I mentioned earlier. Otherwise, by default it’s located in /usr/local/etc/shairport-sync.conf

Look for the “Back End Settings” section that mentions ALSA:

// Back End Settings

// These are parameters for the "alsa" audio back end.
// For this section to be operative, Shairport Sync must be built with the following configuration flag:
// --with-alsa
alsa =
{
//      output_device = "default";

Remove // to uncomment output_device line, and change it to hw: followed by your interface card:

alsa =
{
      output_device = "hw:S3E";

There’s no need to close the bracket. }; should already be at the end of ALSA section below all the comments.

Now restart the service:

sudo systemctl restart shairport-sync.service

That’s it!

Controlling volume levels

Two options again:

Option 1: Shairport Sync and its own software volume control

In its current configuration, volume changes made on the source device while streaming audio are managed in software by Shairport Sync. You can separately adjust master volume for the audio card by using alsamixer utility if it’s too low or too high. You may need to install alsa-utils package first if it’s not already installed (sudo apt install alsa-utils on Debian based distros).

Running alsamixer should then let you control volume levels for different cards and their outputs:

alsamixer

Option 2: Shairport Sync can control device volume directly

Output device must be capable of volume control for this to work. Not all devices support this, e.g. standalone DACs.

If you defined output_device in the config file for Shairport Sync, you can also add mixer_control_name = "PCM" right below it. You may need to change PCM to your output name, like Headphone or Front (without adding hw: this time). It depends on the hardware, but it should correspond to the output name shown by alsamixer. This way Shairport Sync will directly set device master volume based on your input from the streaming source.