Search code examples
pythonraspberry-pisudopyaudiopulseaudio

Running part of python program with/without sudo


I am trying to control some LEDs on my Raspberry Pi Zero 2w with rpi_ws281x using some audio from pyaudio as input. One of them needs sudo, the other only works without sudo...

I tried to import rpi_ws281x in a script without sudo. That crashes because the 'permission to open \dev\mem is denied'. So the program has to be run with sudo privileges in order to be able to control the ledstrip. However, using pyaudio in that script when called with sudo, I get this error: OSError: [Errno -9996] Invalid input device (no default output device).

To verify that the problem lies with using sudo, I made two minimal working examples using either rpi_ws281x or pyaudio. The first one only works with sudo, the second only without.

Is there any solution to this? I'm not an expert on pulseaudio, so maybe it is possible to have it accept connections even when pyaudio is run by a program that has been run with sudo.

Otherwise, I guess it should be possible to split the program into two parts. One is run with sudo, the other without. But how would they then communicate efficiently (given that about 20 updates per second are needed)? Thanks in advance!

If needed, here is the entire error that pyaudio spits out when run with sudo:

ALSA lib pcm_dsnoop.c:638:(snd_pcm_dsnoop_open) unable to open slave
ALSA lib pcm_dmix.c:1075:(snd_pcm_dmix_open) unable to open slave
ALSA lib pcm.c:2660:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear
ALSA lib pcm.c:2660:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lfe
ALSA lib pcm.c:2660:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side
ALSA lib confmisc.c:1281:(snd_func_refer) Unable to find definition 'cards.USB-Audio.pcm.hdmi.0:CARD=1,AES0=4,AES1=130,AES2=0,AES3=2'
ALSA lib conf.c:4745:(_snd_config_evaluate) function snd_func_refer returned error: No such file or directory
ALSA lib conf.c:5233:(snd_config_expand) Evaluate error: No such file or directory
ALSA lib pcm.c:2660:(snd_pcm_open_noupdate) Unknown PCM hdmi
ALSA lib confmisc.c:1281:(snd_func_refer) Unable to find definition 'cards.USB-Audio.pcm.hdmi.0:CARD=1,AES0=4,AES1=130,AES2=0,AES3=2'
ALSA lib conf.c:4745:(_snd_config_evaluate) function snd_func_refer returned error: No such file or directory
ALSA lib conf.c:5233:(snd_config_expand) Evaluate error: No such file or directory
ALSA lib pcm.c:2660:(snd_pcm_open_noupdate) Unknown PCM hdmi
ALSA lib pcm.c:2660:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.modem
ALSA lib pcm.c:2660:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.modem
ALSA lib pcm.c:2660:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.phoneline
ALSA lib pcm.c:2660:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.phoneline
Cannot connect to server socket err = No such file or directory
Cannot connect to server request channel
jack server is not running or cannot be started
JackShmReadWritePtr::~JackShmReadWritePtr - Init not done for -1, skipping unlock
JackShmReadWritePtr::~JackShmReadWritePtr - Init not done for -1, skipping unlock
Cannot connect to server socket err = No such file or directory
Cannot connect to server request channel
jack server is not running or cannot be started
JackShmReadWritePtr::~JackShmReadWritePtr - Init not done for -1, skipping unlock
JackShmReadWritePtr::~JackShmReadWritePtr - Init not done for -1, skipping unlock
ALSA lib pcm_oss.c:377:(_snd_pcm_oss_open) Unknown field port
ALSA lib pcm_oss.c:377:(_snd_pcm_oss_open) Unknown field port
ALSA lib pulse.c:242:(pulse_connect) PulseAudio: Unable to connect: Access denied

ALSA lib pulse.c:242:(pulse_connect) PulseAudio: Unable to connect: Access denied

ALSA lib pcm_a52.c:823:(_snd_pcm_a52_open) a52 is only for playback
ALSA lib pcm_usb_stream.c:486:(_snd_pcm_usb_stream_open) Invalid type for card
ALSA lib pcm_usb_stream.c:486:(_snd_pcm_usb_stream_open) Invalid type for card
ALSA lib pcm_dmix.c:1075:(snd_pcm_dmix_open) unable to open slave
ALSA lib pulse.c:242:(pulse_connect) PulseAudio: Unable to connect: Access denied

ALSA lib pulse.c:242:(pulse_connect) PulseAudio: Unable to connect: Access denied

Cannot connect to server socket err = No such file or directory
Cannot connect to server request channel
jack server is not running or cannot be started
JackShmReadWritePtr::~JackShmReadWritePtr - Init not done for -1, skipping unlock
JackShmReadWritePtr::~JackShmReadWritePtr - Init not done for -1, skipping unlock
Recording
Traceback (most recent call last):
  File "/home/pi/Documents/test.py", line 15, in <module>
    stream = p.open(format=sample_format,
  File "/home/pi/environments/flask-env/lib/python3.9/site-packages/pyaudio.py", line 754, in open
    stream = Stream(self, *args, **kwargs)
  File "/home/pi/environments/flask-env/lib/python3.9/site-packages/pyaudio.py", line 445, in __init__
    self._stream = pa.open(**arguments)
OSError: [Errno -9996] Invalid input device (no default output device)

Solution

  • Okay, so I solved the issue after a non-zero amount of googling and messing up my pulseaudio installation enough times to warrant a reinstall more often than I would like to admit.

    rpi_ws281x requires the PWM pin (GPIO 18 on the Pi Zero 2w) for which it needs sudo privileges. So the solution is to run pulseaudio as a system service, instead of a user service.

    Now, the Pulseaudio creators warn against doing this, but in this case it is alright, since the Pi (in this case) is not really meant to have a user logged in anyway.

    Setting up pulseaudio as a system service, is not very complicated (see this and this):

    First, disable user mode (if it was enabled):

    sudo systemctl --global disable pulseaudio.service pulseaudio.socket

    Create a systemd file (/etc/systemd/system/pulseaudio.service):

    [Unit]
    Description=PulseAudio Daemon
     
    [Install]
    WantedBy=multi-user.target
     
    [Service]
    Type=simple
    PrivateTmp=true
    ExecStart=/usr/bin/pulseaudio --system --realtime --disallow-exit --no-cpu-limit 
    

    Add the pulse and pi user to the required groups:

    sudo usermod -a -G audio pulse #add pulse to audio group
    sudo usermod -a -G pulse-access pi #add pi to pulse-access group
    sudo usermod -a -G pulse-access root #add root to pulse-access group
    

    And finally, start the service:

    sudo systemctl enable pulseaudio.service
    sudo systemctl start pulseaudio.service
    

    Now, scripts run with sudo should be able to interact with pulseaudio. I have only verified this with PyAudio, but it should work for others too.