This post is part of a series.

The acquisition

From the previous post, it was decided that a humble Raspberry Pi would be the heart of a vintage but also modern Audiophile system.

However, it is 2021, and between a global pandemic and a global chips shortage, we can’t have everything we want. Raspberry Pis are still pretty much out-of-stock everywhere I looked.

There is an alternative, the ROCK Pi E, designed by the company Radxa based out of Shenzhen [1]. There isn’t a lot about the history of the ROCK Pi, and whether it was born out of philosophical, technical or cosmetic differences with its berry cousins. Nevertheless, the price-point for the ROCK Pi E is very good (USD$35 + shipping), and there is actually an online store that stocks them, sells them, and even ships them to your home [2]. In these strange times, this is double-plus-good.

So I got one.


The humble ROCK Pi E and the not-so-humble Schiit Modius and Magnius


The linux bit

This now gets into the technical setup of the Pi. The rest assumes you’ve already got a working Ubuntu distro onto the Pi.

Firstly, install the alsa audio driver if you don’t already have it.

$ sudo apt-get install alsa

Then, list devices to retrieve the ID of your USB-DAC

$ aplay -l
aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: Modius [Schiit Unison Modius], device 0: USB Audio [USB Audio]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 1: rockchiprk3328 [rockchip,rk3328], device 0: ff010000.i2s-rk3328-hifi rk3328-hifi-0 []
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 2: rockchiphdmi [rockchip,hdmi], device 0: ff000000.i2s-i2s-hifi i2s-hifi-0 []
  Subdevices: 1/1
  Subdevice #0: subdevice #0

If you have more than one devices listed, beware that the ID of the device can change between restarts. Annoying, I know.

To fix this, you’ll need to edit /etc/modprobe.d/alsa-base.conf, and add in the following lines so that the device IDs persist between restarts.

# add this to the end of /etc/modprobe.d/alsa-base.conf
# format:
# options snd-<name> index=<id>
options snd-Modius index=0
options snd-rockchiprk3328 index=1
options snd-rockchiphdmi index=2

Restart your device to confirm that the order of devices stay the same.

To set the DAC as the default output device, modify or create the file at /etc/asound.conf, and set the id of the default output device. In my case, it is the Schiit.

$ sudo cat > /etc/asound.conf <<EOF
defaults.pcm.card 0
defaults.ctl.card 0
EOF

To test that the default device is working:

$ speaker-test
# this will say "0 - Front Left", but it will actually play in both channels

The shairport bit

The official documentation does a very good job at the installation steps for shairport-sync and its dependencies.

References:

  • shairport-sync [3], make sure you checkout the development branch!
  • nqptp [4], this one is on the main branch

The deviation happens when configuring shairport-sync using /etc/shairport-sync.conf. Since we’ve setup the default devices using /etc/asound.conf, the default configuration for shairport-sync will respect the default device, and there isn’t any modification we need to do.

It is good to modify /etc/shairport-sync.conf and set a device name though, unless the device hostname is already very good. Some learnings I had to go through:

  • The name needs to be Siri-friendly.
  • Abbreviations and CamelCase names aren;t Siri friendly. I tried using the name “MrQuiet”, but Siri became very very confused, and would only refer to it as “Emm-Arr-Quiet”.
  • Avoid names that might conflict with artist names. If you ask Siri to “play some music on Coldplay”, don’t be surprised when Siri simply plays “Yellow” on your phone, and entirely skips checking whether you also happen have an AirPlay device called “Coldplay”.
  • Naming the speaker “Audiophile” was a good choice. But I guess I could also have trolled Siri and named the device “Alexa”.

Gotcha - missing home folder

When I started shairport-sync for the very first time, it immediately crashed and complained about a missing home folder. shairport-sync runs under its own user, and on some linux distros the default behaviour is to not create home directories.

sudo mkdir -p /home/shairport-sync
sudo mkdir -p /home/shairport-sync/.config/pulse
sudo chown -R shairport-sync:shairport-sync /home/shairport-sync

The Apple Home bit

Now that shairport-sync is running, it should appear as one of the AirPlay destinations.


You should also add it to your Home on iOS. This sporadically failed for me for seemingly random reasons. A few things appeared to have helped though:

  • On your iOS device, don’t use the 5GHz wifi.
    • Most smart devices support only 2.4GHz, presumably because of the cost of components.
    • Apple Home appears to also like when all devices are on the same wifi.
  • Devices without mobile internet seem to be reliable for adding smart home accessories.
    • Some smart-home setups use a local wifi network, hosted by the smart device, to receive initial configurations.
    • A hypothesis is that an iOS device with mobile internet would join this wifi network, freak out that it no longer has internet connection, activates mobile internet, then attempt to reach the device over mobile internet.

Another gotcha - but don’t worry, it’s just for me. Bless open source.

While trying to get shairport working on the Pi, I ran up against the error "Can't find an AAC decoder!". All other aspects of the shairport-sync server were perfect: it played nice with Apple Home, it showed up on AirPlay, it just didn’t want to play any music…

There was a week of head scratching, installing every iteration of AAC codecs I could find in ubuntu (there were many, ffmpeg, fdkaac, faac, libavcodec-dev, libavcodec-extra57, libavformat-dev, libfaac, libfdk-aac-dev, ubuntu-restricted-extras, even vlc…), and still the same error persisted.

I eventually worked up the courage to check the source-code. I was preparing to file a bug on the development branch of a one-man open source project due to niche hardware, the very least I could do is to get a bit more debug output. But the gods of Google and StackOverflow smiled upon me that afternoon, and this blessed thread appeared on my browser [5]. The second answer with 0 votes pointed the path.

The crux of the problem was that my Pi was running Ubuntu 18 + ffmpeg 3.4, while shairport-sync is developed against a Raspberry Pi 4, which has Ubuntu 20 + ffmpeg 4.x. In ffmpeg 4.x, the list of codecs do not need to be explicitly discovered, but this is still needed in ffmpeg 3.4.

A small code change, another round of make && make install, and suddenly the hills are alive with the sound of music. I went to sleep that night feeling triumphant. Even more triumphant was waking up and seeing that my PR was merged into shairport-sync already [6]. Kudos @mikebrady, you’re a hell of a guy.

So I hope, if you’ve stumbled across this… perhaps you are also trying to debug a gnarly problem. I hope the gods of Google and StackOverflow also smile upon you.

The final food for thought then

All that challenge, for an AAC codec? Why does AirPlay 2 need to use a lossy codec such as AAC?

So it turns out AirPlay 2 is not lossless. This no-compromise system is actually full of compromises. Welp.

References

  1. Rock Pi E: https://wiki.radxa.com/RockpiE
  2. Rock Pi E sold by AllNetChina: https://shop.allnetchina.cn/products/copy-of-rock-pi-e?variant=31974543392870
  3. shairport-sync build instructions for AirPlay2: https://github.com/mikebrady/shairport-sync/blob/development/BUILDFORAP2.md
  4. nqptp build instructions: https://github.com/mikebrady/nqptp
  5. The blessed StackOverflow thread: https://stackoverflow.com/questions/15801722/avcodec-find-encoderav-codec-id-h264-returns-null
  6. A small PR: https://github.com/mikebrady/shairport-sync/pull/1284