RetroPie forum home
    • Recent
    • Tags
    • Popular
    • Home
    • Docs
    • Register
    • Login

    pulseaudio glitchless playback systemd setup

    Scheduled Pinned Locked Moved Ideas and Development
    pulseaudiohowtoguide
    1 Posts 1 Posters 525 Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • M
      mdx
      last edited by mdx

      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 to pi). The sound daemon can be accessed by all users that set Environment=XDG_RUNTIME_DIR=/run in their systemd scripts
      Those include kodi & emulationstation as long as they set XDG_RUNTIME_DIR=/run in their environment. Child processes (think emulators) that inherit the environment of emulationstation 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 with high-priority, cpu-limit & rlimit-nice & IO by realtime-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 or Requires=pulseaudio.service like kodi or emulationstation 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
      
      1 Reply Last reply Reply Quote 0
      • M mdx referenced this topic on
      • First post
        Last post

      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.