openairplay/airplay2-receiver

Docker / Raspi 4 - no sound

Opened this issue · 12 comments

Originally: ckdo#10

@PaulWieland commented Feb 21, 2021 •

I’m just testing out the docker image on my raspberry pi 4 and for some reason I am not getting any sound output.

The client (iOS 14.4) connects and I see what looks like audio being buffered in the container log, but I have no audio coming from the 1/8” port on the pi.

I tested aplay a wav file locally to make sure there isn’t something wrong with my pi and it seems to work just fine.

I’m really interested in this project. It would be excellent to have a basic airplay 2 receiver for multi room audio with the pi acting as a receiver and a digital signal processor for DML speakers.

UPDATE:
I ran docker run -it --rm --device /dev/snd --entrypoint /bin/bash invano/ap2-receiver to get to the containers shell, and then ran speaker-test. This is producing static on the pi's headphone jack, which tells me that the the hardware + docker are all working just fine.

@PaulWieland commented Mar 17, 2021

I believe this is some sort of a problem with pyaudio (or even portaudio), but I cannot figure it out. I can't get pyaudio to output any sound under docker on my raspberry pi 4.

I created a new docker image that only installs the libraries and then attempts to play a wav file using the example script from the pyaudio home page. This doesn't produce any sound, however aplay works just fine.
http://people.csail.mit.edu/hubert/pyaudio/

Then then tried compiling portaudio and running the test/patest_sine8. program - (which I am blindly assuming outputs a sine wave sweep) which also produces no sound.

Stuck.

@systemcrash commented Mar 17, 2021

Probably portaudio - I think portaudio is intended for macOS only. Could be wrong.

@PaulWieland commented Mar 17, 2021

Well, doesn’t the receiver script rely on pyaudio to output sound (regardless of OS)?

from what I understand pyaudio is a python wrapper for portaudio.

@systemcrash commented Mar 17, 2021

I was completely wrong XD - PortAudio is cross-platform 🤦

@PaulWieland commented Mar 31, 2021

I built a test dockerfile that does the bare minimum. It's built on the official python3 image and it uses the demo play.py file from pyaudio. No sound.

https://github.com/PaulWieland/pyaudio_test

@M-Whitaker commented Apr 5, 2021

I'm currently also having this issue @PaulWieland do you have this fork working outside of docker? Also I have tried on macOS Big Sur with no luck (however due to Big Sur being different in lots of ways I kind of expected that)

@PaulWieland commented Apr 5, 2021

@M-Whitaker I can't get a RaspberryPi to output sound correctly with pyaudio (see my pyaudio_test container in the previous reply). Docker or not doesn't seem to matter.

From what I understand, the docker container is incompatible with macOS because it expects to output to /dev/snd - which is normal for Linux but not for macOS.

The ap2 receiver does work on macOS if you run it natively using the instructions in the Readme.

@M-Whitaker commented Apr 5, 2021

Ah perfect thanks for the clarification. For information I was using the native install for macOS Big Sur as like you I noted the /dev forwarding wasn't going to work. What devices have you got this working on? as I'm slightly confused as to what is working and what shouldn't as I understand this is a POC.

@PaulWieland commented Apr 5, 2021

I'm running the receiver under 10.15.7 (I haven't updated to Big Sur yet because of compatibility issues with my company's VPN).

Tested Clients:

Music 1.0.6.10 (on Catalina)
iOS 14.4.2

This combination works perfectly fine for me.

@M-Whitaker commented Apr 5, 2021

Ok, thanks for that as said it is really not a surprise that Big Sur breaks things (I'm also running M1 but that shouldn't effect anything from what I have seen skimming the code). I have to say on the docker/portaudio front I haven't previously tried running audio applications though docker as I know getting devices & drivers to work this way can be a pain. Would be interesting to see if #9 works (i.e. not using docker) on your setup as I couldn't get past an error: amixer: Unable to find simple control 'PCM',0

@PaulWieland commented Apr 5, 2021

The program should not be trying to call amixer under darwin (macOS).

airplay2-receiver/ap2/utils.py

Line 44 in cf2d54c
if subsys == "Darwin":

Can you run a quick test to verify what python thinks your OS is?

Go to a terminal and type in python3 and press enter. Now type in the following two lines:

import platform
platform.system()

It will output your system name, which if you are on a mac should be 'Darwin'.

image

@M-Whitaker commented Apr 5, 2021 •

Sorry, this is on my PI4. (My Mistake)

@PaulWieland commented Apr 5, 2021 •

Ok, in that case you need to find out what the alsa device name is (which can be different on each machine - which is why ckdo added the --no-volume-management flag).
https://github.com/ckdo/airplay2-receiver/blob/cf2d54c6dd0534e08acbd02dafef166dff113dfd/docker/start.sh#L19

On mine it's Headphone (not PCM).

So the solution is to run it without volume management - or or to modify the get/set_volume subroutines in the utils.py script.
https://github.com/ckdo/airplay2-receiver/blob/cf2d54c6dd0534e08acbd02dafef166dff113dfd/ap2/utils.py#L48

Launch a container and jump into the shell:
docker run -it --rm --device /dev/snd --net host invano/ap2-receiver /bin/bash

Then type amixer scontrols and it should tell you what your mixer name is.
image

Then modify utils.py accordingly:

Get Volume becomes:

line_pct = subprocess.check_output(["amixer", "get", "Headphone"]).splitlines()[-1]

Set Volume becomes:

subprocess.run(["amixer", "set", "Headphone", "%d%%" % pct])

@M-Whitaker commented Apr 5, 2021

Thanks, that was a bit simple of me... Shouldn't have skipped over that. So this is now working the same as the docker container (no audio) which I assume is the same place you are on your PI4?

@PaulWieland commented Apr 5, 2021 •

Correct. The client can connect and play/pause/disconnect/reconnect/adjust volume. But there's no sound. I'm trying to get help from the port audio mailing list, because I believe this is an issue with that library. The python script is using pyaudio (which depends on portaudio).
https://listserv.cuit.columbia.edu/scripts/wa.exe?A2=2103B&L=PORTAUDIO&D=0&S=b&P=292768

My pyaudio_test was an attempt to isolate the problem.

@satrik commented Apr 16, 2021

@PaulWieland I'm testing it currently with Docker on Debian Buster (kernel 4.19) and it works if I use --privileged for the audio device. I know --privileged isn't a (nice) solution, but it helps to narrow down the error further. I will do some tests on the weekend and maybe I'll find the cause and a nice solution :)

@PaulWieland commented Apr 16, 2021

A small update. I replaced portaudio with alsa and everything works fine.

https://github.com/PaulWieland/airplay2-receiver/blob/alsaaudio/ap2/connections/audio.py

I’m sure this change breaks cross platform support but shairport-sync writes directly to alsa as well so...

What is the point of copying this issue from the original? This will only confuse people and make it harder to manage.

ckdo repo seems to have become the go-to because it's way ahead of this one. It seems @Neustradamus wants to remain the go-to. But this requires work to stay up to date. I might be wrong tho.

I think ckdo can be the working copy, and when it's turfed out the bugs, the PRs can land here. opensource is opensource. forks are forks......

Alternatively, these issues are just a reflection of current issues in master, which have only been logged in other forks.

@systemcrash Exactly. If we find and fix an issue in a fork, then do a pull request to the main. But copying issues from one repo to another isn't helping anyone; it's just creating a mess.

The best place is here.
10th March 2021, I have requested the transfer of issues, ckdo has removed my ticket...

The last solution to follow is to create ticket from original like I have done :)

Recall: The upstream is up-to-date.

@PaulWieland - --privileged seems kinda necessary, since the Docker image is taking the portaudio output all to itself, yes?

Also - which kernel are you trying on? https://www.raspberrypi.org/forums/viewtopic.php?t=282154

https://listserv.cuit.columbia.edu/scripts/wa.exe?A2=PORTAUDIO;643ac1b1.2104B&S=

Also, what happens if you tweak the latency when running on your Pi? PortAudio/portaudio#246

Try something like frames_per_buffer

        self.sink = self.pa.open(format=self.pa.get_format_from_width(2),
                                 channels=self.channel_count,
                                 rate=self.sample_rate,
                                 output=True,
                                 frames_per_buffer=2048)

Ref: https://people.csail.mit.edu/hubert/pyaudio/docs/#class-stream

@systemcrash I have spent countless hours and tried numerous things and nothing works. I tried exactly what you suggested. When compiling portaudio from source (and adjusting the latency per the issue you linked), the test programs do not produce sound correctly.

The callback method test program from pyaudio does play audio, but only after a 30 second delay.

I ditched port audio in my fork and replaced with alsaaudio (which is similar to how shairpoint-sync works) and it works flawlessly.

I suspect kernel shenanigans.

... the test programs do not produce sound correctly.

But do they produce sound?

The callback method test program from pyaudio does play audio, but only after a 30 second delay.

I read your msg on the mailing list also - would be interesting to run a trace and see where execution is for those 30 seconds.

Did you try strace with the callback method test program? This often reveals where the wait is happening. Were you to submit this to the mailing list, this would massively narrow down the efforts and likely result in a patch much more quickly than what to them is perceived as "doesn't work for me".

Since it's on linux (ensure strace is installed via your packet mgr), start simple and run:
strace <progname and its args>

(I've no RPi so I cannot do any debugging)

Does someone already have a solution? Some links are no longer available.

From my understanding of the current situation, if you are wanting to use an airplay 2 receiver this code (or what is learnt from it) is being gradually merged into a C implementation called shairport-sync (https://github.com/mikebrady/shairport-sync/tree/development) which does not use pyaudio for obvious reasons. Someone did create a fork of this codebase that removed the need for pyaudio instead using ALSA but I personally couldn't get it working on my PI. @PaulWieland may have some more info for you but this is my take 😄

This C implementation works flawlessly on my pi. This is amazing! :D