Skip to content

Making an Analog In to Airplay RPi Transmitter

snacques edited this page Apr 26, 2025 · 13 revisions

Say you have a record player in one room and some AirPlay speakers scattered about? Ever want to play from the analog out to the AirPlay speakers? Here's a guide on doing that with a Raspberry Pi. This is mostly a writeup of Issue #632; and will walk through from ground up with a blank RPi.

Get RPi Image

Go fetch the lastest Raspbian. Go with the lite version (you won't need a gui desktop or any other fancy software if it's a headless server). Burn the image to your SD card.

Set up for headless In the SD card's /boot mount.

  1. Make a blank file called ssh.
  2. Make a file called wpa_supplicant.conf and fill in your network information in this template.
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=US 
network={
    ssid="MyWifi"
    psk="MyWifiPassword"
    scan_ssid=1
}

Setup the RPi

Power up the pi! It should jump on your Wifi at first boot. You still have a few things to do. If you don't have a monitor or keyboard hooked up, you can just SSH into it if you know the IP. Username is pi password is raspberry.

  1. Run passwd and update the password.
  2. Run sudo raspi-config and update the tool.
  3. Still in the raspi-config tool set your language, local, time, etc. Also Expand the File System while you are in there.
  4. Reboot.

Update things

Log back in run:

  1. sudo apt-get update
  2. sudo apt-get upgrade
  3. sudo apt-get install git libasound2-dev
  4. sudo apt-get install tmux mosh emacs htop glances

That last install step is optional but things I can't live without.

Install OwnTone

First, just follow these instructions.

In the /etc/owntone.conf file, hunt and peck and set the following. I'm adding "10.0" to the trusted networks as that's my network prefix. Set whatever password you like and you can change that music dir if you want, just remember it as you'll need it later:

admin_password = ""
trusted_networks = { "localhost", "192.168", "10.0", "fd" }
directories = { "/home/pi/music" }

Test owntone

At this point, it should all be running. Restart the server:

sudo service owntone restart

Upload an MP3 to /home/pi/music: wget https://file-examples.com/wp-content/uploads/2017/11/file_example_MP3_5MG.mp3 In your webbrowser, go to OwnTone's web UI (typically http://owntone.local:3689/), and set it up, play the MP3 to the remote speakers. It is very important that everything is working at this point...nothing will work from here out if not.

Test Configure the Raw Audio In

  1. Make a fifo file: mkfifo -m 666 /home/pi/music/AUX
  2. Run arecord -L and pick your soundcard. Mine is hw:CARD=sndrpihifiberry,DEV=0
  3. Pipe the soundcard to the fifo file: arecord -D hw:CARD=sndrpihifiberry,DEV=0 -f cd -d 0 -t raw > /home/pi/music/AUX NOTE: If you see "Warning: rate is not accurate", "overrun!!!" messages, or the audio is off pitch or skips in step 6, please see this note about input format.
  4. Login to the server: http://owntone.local:3689
  5. You should see a "music file" called AUX. Play it, it should be the pass through from your audio device (receiver, record player, cd player, whatever).
  6. All good? Great, stop the arecord program.

PS: If you're using a HiFiBerry DAC+ADC, there's an important note on this page.

CPIPED

So the arecord is cool...cpiped wraps it nicely with some extra pizzaz like signaling the AUX queue start and stop.

Make it

  1. cd ~/
  2. mkdir bin/
  3. git clone "https://github.com/b-fitzpatrick/cpiped.git"
  4. cd cpiped/
  5. make cpiped
  6. cp cpiped ~/bin

Config it

The sound detect script uses a secondary FIFO file called .audioDetectPipe as an intermediary for the sound output (see the cat line below). We need to create this FIFO file too, or cpiped will not work:

mkfifo -m 666 /home/pi/.audioDetectPipe

Make a file at ~/bin/soundDetect.sh:

#!/bin/bash
cat /home/pi/.audioDetectPipe > /home/pi/music/AUX &
echo $! > /tmp/catPipe.pid

Make a file at ~/bin/soundAbsence.sh:

#!/bin/bash
kill $(< /tmp/catPipe.pid)
echo " " > /tmp/catPipe.pid

Now make them executable:

cd ~/bin
chmod +x *.sh

Turn on services at boot time

What's my sound card again?

Run arecord -l and you'll see something like:

**** List of CAPTURE Hardware Devices ****
card 0: sndrpihifiberry [snd_rpi_hifiberry_dacplusadcpro], device 0: HiFiBerry DAC+ADC PRO HiFi multicodec-0 [HiFiBerry DAC+ADC PRO HiFi multicodec-0]
  Subdevices: 0/1
  Subdevice #0: subdevice #0
card 1: Device [USB Audio Device], device 0: USB Audio [USB Audio]
  Subdevices: 1/1
  Subdevice #0: subdevice #0

This lists all the audio cards. For us, we want the first one which would be hw:0,0. Remember that. It's a different name than the last time when we tested it.

Next, make a file at /lib/systemd/system/cpiped.service and sub in your device if it's not -d "hw:0,0":

[Unit]
Description=cpiped
After=sound.target

[Service]
Type=forking
KillMode=none
User=pi
ExecStart=/home/pi/bin/cpiped -d "hw:0,0" -s /home/pi/bin/soundDetect.sh -e /home/pi/bin/soundAbsence.sh -t 800 -D /home/pi/.audioDetectPipe
ExecStop=/usr/bin/killall -9 cpiped
WorkingDirectory=/home/pi

[Install]
WantedBy=multi-user.target

To change the silence threshold for audio detection, you can change -t 800 to another number. cpiped has a sound range of 1 to 32767. The default is 100 which is often too low for analog audio situations like a turntable.

Then run:

  1. sudo systemctl enable owntone
  2. sudo systemctl enable cpiped

Enjoy!

So the music should stream through now. The cpiped tool will automatically open and close the stream if audio is present. You can dive in and edit the /etc/owntone.conf file if you want to set up default speakers and run alsamixer to set the input gain.

Notes for HiFiBerry DAC+ADC Pro

You can turn off the board LEDs if you want. I never got the board to work outside of slave mode.

  1. In /boot/config.txt
  2. Add: dtoverlay=hifiberry-dacplusadcpro,slave,leds_off=true
  3. Remove: dtparam=audio=on
  4. Run: sudo rpi-update

Notes for input format mismatch

Some inexpensive input cards use a fixed format that is different from the "-f cd" format that owntone is expecting from named pipes. In that case, you will need to use sox (or something similar) to resample the stream. You can view your input card configurations with arecord --dump-hw-params -D hw:1,0 substituting your hw ids. This is the example from a Plugable USB-AUDIO device:

pi@airplayer:~ $ arecord --dump-hw-params -D hw:1,0
Warning: Some sources (like microphones) may produce inaudible results
         with 8-bit sampling. Use '-f' argument to increase resolution
         e.g. '-f S16_LE'.
Recording WAVE 'stdin' : Unsigned 8 bit, Rate 8000 Hz, Mono
HW Params of device "hw:1,0":
--------------------
ACCESS:  MMAP_INTERLEAVED RW_INTERLEAVED
FORMAT:  S16_LE
SUBFORMAT:  STD
SAMPLE_BITS: 16
FRAME_BITS: 32
CHANNELS: 2
RATE: 48000
PERIOD_TIME: [1000 1000000]
PERIOD_SIZE: [48 48000]
PERIOD_BYTES: [192 192000]
PERIODS: [2 1024]
BUFFER_TIME: [2000 2000000]
BUFFER_SIZE: [96 96000]
BUFFER_BYTES: [384 384000]
TICK_TIME: ALL
--------------------
arecord: set_params:1352: Sample format non available
Available formats:
- S16_LE

In this case, the audio is being recorded at 48kHz, while owntone expects 44.1kHz in named pipes. If passed directly, the audio would be lower pitched and frequently skip due to overruns.

Here is the corresponding arecord command given the input above. Note that -f dat in arecord is a shortcut for S16 LE 48kHz. You might have to break out each parameter, although I don't think it makes any difference other than suppressing the mismatch error. For the sox portion, you need to be explicit.

arecord -D hw:CARD=Device,DEV=0 -f dat -d 0 -t raw | sox -t raw -r 48000 -e signed -b 16 -c 2 - -t raw -r 44100 -e signed -b 16 -c 2 - > /home/pi/music/AUX

If that works, use the following when you get to the soundDetech.sh section of the cpiped solution:

#!/bin/bash
sox -t raw -r 48000 -e signed -b 16 -c 2 /home/pi/.audioDetectPipe \
    -t raw -r 44100 -e signed -b 16 -c 2 - > /home/pi/music/AUX &

echo $! > /tmp/catPipe.pid