pulseaudio glitchless playback systemd setup
-
In another thread I included my setup for running
kodi
&emulationstation
back to back off of systemd scripts rather than a retropie script.The setup tied in a system-wide
pulseaudio
instance, which is fully detailed below. This pulseaudio setup works with low-spec'd devices and might resolve some latency issues by prioritising audio over other processing.Initially,
pulseaudio
runs off a root systemd instance but as a specified user (mpd
- user that runs the audio player I use, you can change that topi
). The sound daemon can be accessed by all users that setEnvironment=XDG_RUNTIME_DIR=/run
in their systemd scripts
Those includekodi
&emulationstation
as long as they setXDG_RUNTIME_DIR=/run
in their environment. Child processes (think emulators) that inherit the environment ofemulationstation
should also work.The daemon will appear as running as a non-root user:
# ps -fp $(pgrep pulseaudio) UID PID PPID C STIME TTY TIME CMD mpd 950 1 19 08:42 ? 00:32:31 /usr/bin/pulseaudio --daemonize --log-target=journal --use-pid-file=yes
Chief reason for running the daemon initially by root is to gain shared memory privileges and setting real CPU- & IO- scheduling. With this setup, my RPi Zero is capable of playing 96khz/24 bit (via
mpd
) while rebuilding the retropie emulators.A system-wide instance is not generally recommended but there are clear benefits to running the sound daemon like that for performance reasons and glitchless playback. The pulseaudio instance will drop privileges as soon as it sets up privileged memory & IO priority, running as
mpd
.Privileged access to memory is achieved by
enable-memfd
&enable-shm
options.
Real-time scheduling policies for CPU are set withhigh-priority
,cpu-limit
&rlimit-nice
& IO byrealtime-scheduling
&realtime-priority
.# egrep -v '^;|^#|^$' /etc/pulse/client.conf enable-memfd = yes enable-shm = yes
# cat /etc/pulse/daemon.conf cpu-limit = no high-priority = yes nice-level = -12 rlimit-nice = -12 realtime-scheduling = yes realtime-priority = 99 rlimit-rtprio = 99 enable-shm = yes log-level = info exit-idle-time = -1 ;Disabled, the three options are specific to a sound card I use, for higher quality resampling and higher sample rate. May not work reliably with your card. Leaving them for you to test. ;default-sample-format = s24le ;default-sample-rate = 96000 ;resample-method = soxr-mq
All this results in fewer glitches during music playback, all the while the system is doing something else.
One app that can also be tuned to take advantage of a higher CPU priority is
mpd
:# cat /etc/systemd/system/mpd.service.d/10_scheduling.conf [Service] CPUSchedulingPolicy=rr CPUSchedulingPriority=50
You can verify the priorities and the scheduling classes of
mpd
's threads:# cat /etc/systemd/system/mpd.service.d/90_log_rt.conf [Service] ExecStartPost=ps H -q ${MAINPID} -o 'pid,tid,cls,rtprio,pri,comm'
The main
pulseaudio
service. It overrides the pulseaudio.service in/lib/systemd/system/pulseaudio.service
that comes installed with the daemon.# cat /etc/systemd/system/pulseaudio.service [Unit] Description=Pulseaudio sound server (userland) Conflicts=pulseaudio-system.service Requires=sound.target After=sound.target [Install] WantedBy=multi-user.target [Service] Type=forking Environment=XDG_RUNTIME_DIR=/run PIDFile=/run/pulse/pid # Enable mpd instead of pulse for shm/srbchannel with mpd User=mpd ExecStartPre=!/bin/chown mpd:audio /run ExecStartPre=/bin/mkdir -p /run/pulse ExecStartPre=/bin/chown -R mpd:audio /run/pulse ExecStart=/usr/bin/pulseaudio --daemonize --log-target=journal --use-pid-file=yes ExecStartPost=!/bin/chown root:root /run ExecStartPost=/bin/chmod 750 /run/pulse # Disabled, systemd & pulseaudio use SIGKILL signal #ExecStop=sudo /bin/chown mpd:audio /run #ExecStop=/usr/bin/pulseaudio -k #ExecStopPost=sudo /bin/chown root:root /run Restart=on-failure TimeoutStopSec=12 # Disabled, unable to connect client if enabled #PrivateTmp=true # Disabled, complains about 'sudo: effective uid is not 0' # Enabled, sudo is not in use RestrictAddressFamilies= ~AF_INET6 RuntimeDirectory=pulse RuntimeDirectoryMode=0750 LimitRTPRIO=99 LimitRTTIME=infinity LimitNICE=-20 LimitMEMLOCK=infinity
IO scheduling drop-in:
# cat /etc/systemd/system/pulseaudio.service.d/10_scheduling.conf [Service] ExecStartPost=chrt -p 51 ${MAINPID} #ExecStart= #ExecStart=prlimit --rtprio=51 --rttime=unlimited /usr/bin/pulseaudio --daemonize --log-target=journal --use-pid-file=yes #ExecStart=chrt -r 51 /usr/bin/pulseaudio --daemonize --log-target=journal --use-pid-file=yes IOSchedulingPriority=1 IOSchedulingClass=realtime
# cat /etc/systemd/system/pulseaudio.service.d/10_restart_mode.conf [Service] ExecStart= ExecStart=-/usr/bin/pulseaudio --daemonize --log-target=journal --use-pid-file=yes ExecStop= ExecStopPost= Restart=no # Disabled due to dash in ExecStart #SuccessExitStatus=0 1 2 15 SIGTERM SIGKILL
The chown in pulseaudio.service requires additional root capability:
# cat /etc/systemd/system/pulseaudio.service.d/10_limit_capabilities.conf [Service] # Needed by elevated chown # Deprecates the need for sudo CapabilityBoundingSet=CAP_CHOWN
It is possible to enable a socket rather than a service, so pulseaudio is launched on demand rather than running before when it isn't yet needed.
# systemctl disable pulseaudio.service # systemctl enable pulseaudio.socket
(Regular activation by apps that define
Wants=pulseaudio.service
orRequires=pulseaudio.service
likekodi
oremulationstation
in their systemd services will still work).It's rather for apps that can send in a UDP or TCP stream. One app like that is the roc remote sound daemon, sending its datastream on port 10001.
Users of native RTP in pulseaudio will find this useful.# cat /etc/systemd/system/pulseaudio.socket [Unit] Description=Pulseaudio sound server socket Conflicts=pulseaudio-system.socket Requires=sound.target After=sound.target [Socket] Priority=6 Backlog=5 ReusePort=true # Disabled due to usage conflict, systemd does not release the socket, causing other processes connecting to it to fail #ListenStream=%t/pulse/native #ListenStream=/tmp/pulse-server ListenStream=4713 # roc UDP port ListenDatagram=10001
This drop-in ensures the pulseaudio service isn't installed when the package is upgraded.
Useful when it's the socket that starts pulseaudio and the service isn't enabled.# cat /etc/systemd/system/pulseaudio.service.d/00_no_install.conf [Install] WantedBy=
Socket compatibility with mpd:
# cat /etc/systemd/system/pulseaudio.socket.d/10_socket-owner.conf [Socket] SocketUser=mpd SocketGroup=audio
Compatibility across restarts with roc-toolkit:
# cat /etc/systemd/system/pulseaudio.socket.d/10_flushpending.conf [Socket] ## Systemd +v274 only ## Avoid re-starting the service due to pending connections remaining in the socket when udp traffic (roc) has ceased ## Flush the socket on stopping the service to remove the packet FlushPending=true
-
Contributions to the project are always appreciated, so if you would like to support us with a donation you can do so here.
Hosting provided by Mythic-Beasts. See the Hosting Information page for more information.