Logo

🛠️ PipeWire on DietPi (Headless Raspberry Pi + HiFiBerry ADC)

  • Author
    by Admin User
    a day ago
  • This guide sets up PipeWire as system services tied to the dietpi user, allowing multiple applications (e.g. InterScribe agent + monitoring tools) to access HiFiBerry ADC simultaneously with low latency.

    HiFiBerry ADC devices, when accessed directly through ALSA, can sometimes exhibit quirks or limited compatibility—especially with applications like the InterScribe Agent that expect multi-client audio access. ALSA by itself does not natively allow multiple programs to capture from the same device at once, and some applications may not work reliably with certain ALSA plugins. PipeWire acts as a unified sound server that greatly improves compatibility: it enables simultaneous access from multiple apps, provides a modern replacement for both PulseAudio and JACK, and avoids many of the limitations and device-specific issues found with direct ALSA access.

    By default, DietPi doesn’t run systemd --user sessions, so we run PipeWire as system services for the dietpi user.


    1. Install required packages

    sudo apt update
    sudo apt install -y pipewire pipewire-pulse wireplumber pipewire-bin pulseaudio-utils rtkit dbus-user-session libasound2-plugins
    
    • libasound2-plugins is required so ALSA apps can see/use pcm.pulse → PipeWire.

    2. Create runtime + DBus session service

    PipeWire expects /run/user/<UID> with correct ownership and permissions. We create a helper service to provide this for dietpi.

    sudo tee /etc/systemd/system/dietpi-user-runtime.service >/dev/null <<'EOF'
    [Unit]
    Description=Create /run/user and session bus for dietpi
    After=systemd-logind.service
    Wants=systemd-logind.service
    
    [Service]
    Type=simple
    User=dietpi
    Group=audio
    PermissionsStartOnly=yes
    ExecStartPre=/bin/mkdir -p /run/user/1000
    ExecStartPre=/bin/chown dietpi:audio /run/user/1000
    ExecStartPre=/bin/chmod 700 /run/user/1000
    Environment=XDG_RUNTIME_DIR=/run/user/1000
    ExecStart=/usr/bin/dbus-daemon --session --address=unix:path=/run/user/1000/bus --nofork
    Restart=always
    RestartSec=2
    
    [Install]
    WantedBy=multi-user.target
    EOF
    
    sudo systemctl daemon-reload
    sudo systemctl enable --now dietpi-user-runtime.service
    

    Verify:

    ls -ld /run/user/1000      # expect: drwx------ dietpi audio
    ls -l  /run/user/1000/bus  # should be a socket
    

    3. Create PipeWire services for dietpi

    PipeWire

    sudo tee /etc/systemd/system/pipewire-dietpi.service >/dev/null <<'EOF'
    [Unit]
    Description=PipeWire for dietpi (headless)
    After=dietpi-user-runtime.service
    Requires=dietpi-user-runtime.service
    
    [Service]
    Type=simple
    User=dietpi
    Group=audio
    Environment=HOME=/home/dietpi
    Environment=TMPDIR=/tmp
    Environment=XDG_RUNTIME_DIR=/run/user/1000
    Environment=DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
    ExecStartPre=-/bin/rm -f /run/user/1000/pipewire-0 /run/user/1000/pipewire-0.lock
    ExecStart=/usr/bin/pipewire
    Restart=always
    RestartSec=2
    LimitRTPRIO=95
    LimitMEMLOCK=infinity
    
    [Install]
    WantedBy=multi-user.target
    EOF
    

    WirePlumber

    sudo tee /etc/systemd/system/wireplumber-dietpi.service >/dev/null <<'EOF'
    [Unit]
    Description=WirePlumber (session manager) for dietpi
    After=pipewire-dietpi.service
    Requires=pipewire-dietpi.service
    
    [Service]
    Type=simple
    User=dietpi
    Group=audio
    Environment=HOME=/home/dietpi
    Environment=TMPDIR=/tmp
    Environment=XDG_RUNTIME_DIR=/run/user/1000
    Environment=DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
    ExecStart=/usr/bin/wireplumber
    Restart=always
    RestartSec=2
    
    [Install]
    WantedBy=multi-user.target
    EOF
    

    PulseAudio shim

    sudo tee /etc/systemd/system/pipewire-pulse-dietpi.service >/dev/null <<'EOF'
    [Unit]
    Description=PipeWire PulseAudio compatibility for dietpi
    After=pipewire-dietpi.service
    Requires=pipewire-dietpi.service
    
    [Service]
    Type=simple
    User=dietpi
    Group=audio
    Environment=HOME=/home/dietpi
    Environment=TMPDIR=/tmp
    Environment=XDG_RUNTIME_DIR=/run/user/1000
    Environment=DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
    ExecStartPre=-/bin/rm -f /run/user/1000/pulse/native
    ExecStart=/usr/bin/pipewire-pulse
    Restart=always
    RestartSec=2
    
    [Install]
    WantedBy=multi-user.target
    EOF
    

    Enable them:

    sudo systemctl daemon-reload
    sudo systemctl enable --now pipewire-dietpi wireplumber-dietpi pipewire-pulse-dietpi
    

    4. Configure InterScribe Agent audio settings

    To ensure the InterScribe Agent uses the correct audio server, you must configure /etc/default/interscribe-agent.

    Preferred method: Set the PULSE_SERVER environment variable. This makes the agent independent of the system's default source and more robust, as it connects directly to the PulseAudio (PipeWire) server socket.

    Example /etc/default/interscribe-agent:

    # Recommended: Point directly to the PulseAudio (PipeWire) server socket
    PULSE_SERVER=unix:/run/user/1000/pulse/native
    
    # Alternative (not recommended): Use PULSE_SOURCE to select a specific input device by name
    # PULSE_SOURCE=alsa_input.platform-soc_107c000000_sound.stereo-fallback
    

    Note: Using PULSE_SERVER is recommended. PULSE_SOURCE can be used to select a specific input if needed, but may be less robust if device names change.


    5. Expose a pipewire ALSA device

    To make pipewire appear in ALSA device lists:

    sudo tee /etc/asound.conf >/dev/null <<'EOF'
    pcm.pipewire {
        type pulse
        hint.description "PipeWire Sound Server"
    }
    ctl.pipewire {
        type pulse
    }
    
    pcm.!default {
        type pulse
        fallback "hw:CARD=sndrpihifiberry,DEV=0"
        hint.description "PipeWire (Default)"
    }
    ctl.!default {
        type pulse
        fallback "hw:CARD=sndrpihifiberry,DEV=0"
    }
    EOF
    

    Now apps that enumerate ALSA devices will show pipewire as an option.


    6. Export environment in your shell

    PipeWire needs these environment variables in your dietpi session. Add them to ~/.profile so they persist:

    export XDG_RUNTIME_DIR=/run/user/1000
    export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
    

    Load them immediately:

    source ~/.profile
    

    7. Verify PipeWire

    Check the PulseAudio shim is active:

    pactl info | grep "Server Name"
    # Expect: Server Name: PulseAudio (on PipeWire 0.3.x)
    

    List inputs:

    pactl list short sources
    # You should see your HiFiBerry ADC as alsa_input.platform-soc_107c000000_sound.stereo-fallback
    

    Set HiFiBerry as default:

    pactl set-default-source alsa_input.platform-soc_107c000000_sound.stereo-fallback
    

    Test recording:

    parecord /tmp/test.wav   # Ctrl+C after a few seconds
    aplay /tmp/test.wav
    

    8. Low-latency tuning (optional)

    Create config override:

    mkdir -p ~/.config/pipewire/pipewire.conf.d
    nano ~/.config/pipewire/pipewire.conf.d/10-lowlatency.conf
    

    Contents:

    context.properties = {
        default.clock.rate          = 48000
        default.clock.quantum       = 128
        default.clock.min-quantum   = 32
        default.clock.max-quantum   = 2048
    }
    

    Restart services:

    sudo systemctl restart pipewire-dietpi wireplumber-dietpi pipewire-pulse-dietpi
    

    12. [Optional] Enable System D-Bus + RTKit (Cleaner Logs + Realtime Scheduling)

    On DietPi, PipeWire and WirePlumber may log warnings like:

    Failed to connect to system bus: No such file or directory
    Realtime scheduling disabled: insufficient realtime privileges
    

    This happens because DietPi doesn’t run the system D-Bus or RTKit by default. Audio still works, but logs may look noisy and realtime scheduling isn’t used.

    ✅ Enable system D-Bus and RTKit

    sudo apt update
    sudo apt install -y dbus rtkit
    sudo systemctl enable --now dbus
    

    Verify the socket:

    ls -l /run/dbus/system_bus_socket
    

    Restart PipeWire services:

    sudo systemctl restart dietpi-user-runtime pipewire-dietpi wireplumber-dietpi pipewire-pulse-dietpi
    

    After this, the “system bus” and “realtime scheduling disabled” warnings should disappear, and PipeWire can make use of RTKit for low-latency scheduling.


    10. Troubleshooting

    🧪 Quick health check (copy/paste)

    Run these as the dietpi user (not root):

    # Ensure your shell points to the correct runtime/bus
    export XDG_RUNTIME_DIR=/run/user/1000
    export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
    
    echo "== runtime =="
    ls -ld /run/user/1000 || echo "MISSING: /run/user/1000"
    ls -l  /run/user/1000/bus          || echo "MISSING: bus socket"
    ls -l  /run/user/1000/pulse/native || echo "MISSING: pulse/native"
    
    echo "== services =="
    systemctl --no-pager -l status dietpi-user-runtime pipewire-dietpi wireplumber-dietpi pipewire-pulse-dietpi || true
    
    echo "== pactl =="
    pactl --server="$XDG_RUNTIME_DIR/pulse/native" info || echo "pactl failed"
    

    If /run/user/1000 is missing or the bus/pulse/native sockets are missing, see the items below.


    /run/user/1000 missing (or wrong owner/permissions)

    /run is a tmpfs and resets on boot. Recreate and restart in order:

    # replace 1000 with your actual UID if different
    id -u dietpi
    sudo systemctl stop pipewire-dietpi pipewire-pulse-dietpi wireplumber-dietpi || true
    sudo mkdir -p /run/user/1000
    sudo chown dietpi:audio /run/user/1000
    sudo chmod 700 /run/user/1000
    sudo systemctl restart dietpi-user-runtime
    sudo systemctl start pipewire-dietpi wireplumber-dietpi pipewire-pulse-dietpi
    

    Check why the shim didn’t create it:

    sudo journalctl -u dietpi-user-runtime -n 80 --no-pager
    

    (Optional hardening) Create a tmpfiles rule so the directory exists early at boot:

    echo "d /run/user/1000 0700 dietpi audio -" | sudo tee /etc/tmpfiles.d/dietpi-user.conf
    sudo systemd-tmpfiles --create /etc/tmpfiles.d/dietpi-user.conf
    

    PipeWire won’t start (status=245/254) or WirePlumber exits (status=69)

    Common causes & fixes:

    # 1) Ownership/permissions on the runtime dir
    ls -ld /run/user/1000
    sudo chown dietpi:audio /run/user/1000
    sudo chmod 700 /run/user/1000
    
    # 2) Stale lock/socket files
    sudo rm -f /run/user/1000/pipewire-0 /run/user/1000/pipewire-0.lock /run/user/1000/pulse/native
    
    # 3) Ensure a machine-id exists for D-Bus
    [ -s /etc/machine-id ] || sudo systemd-machine-id-setup
    
    # 4) Kill legacy pulseaudio if it’s running (rare on DietPi)
    pgrep -a pulseaudio || true
    sudo killall pulseaudio 2>/dev/null || true
    
    # 5) Restart in order
    sudo systemctl restart dietpi-user-runtime
    sudo systemctl restart pipewire-dietpi wireplumber-dietpi pipewire-pulse-dietpi
    
    # 6) Foreground debug (shows the exact error)
    sudo -u dietpi \
      XDG_RUNTIME_DIR=/run/user/1000 \
      DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus \
      PIPEWIRE_DEBUG=4 \
      /usr/bin/pipewire
    

    Also inspect logs:

    sudo journalctl -u pipewire-dietpi -u wireplumber-dietpi -u pipewire-pulse-dietpi -n 120 --no-pager
    

    pactl says “Connection refused”

    • Make sure pipewire-pulse-dietpi is active:
      systemctl --no-pager -l status pipewire-pulse-dietpi
      
    • Ensure your shell has the right env set:
      export XDG_RUNTIME_DIR=/run/user/1000
      export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
      pactl --server="$XDG_RUNTIME_DIR/pulse/native" info
      
    • If you must run with sudo, target the dietpi runtime:
      sudo -u dietpi XDG_RUNTIME_DIR=/run/user/1000 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus pactl info
      

    PipeWire not listed in ALSA apps

    • Confirm the plugin package is installed:
      dpkg -l | grep libasound2-plugins
      
    • Ensure /etc/asound.conf defines pcm.pipewire and ctl.pipewire as shown above.
    • Restart apps (some cache ALSA device lists).

    View live logs

    Follow everything at once:

    sudo journalctl -u dietpi-user-runtime -u pipewire-dietpi -u wireplumber-dietpi -u pipewire-pulse-dietpi -f
    

    11. [Optional] Using Multiple Input Devices (Without Changing Default)

    By default, PipeWire will expose all connected capture devices (HiFiBerry, USB mics, webcams, etc.). Only one device is set as the default input, but you can still access others explicitly without changing defaults.

    🔹 1. List available input devices

    pactl list short sources
    

    Example output:

    alsa_input.platform-soc_107c000000_sound.stereo-fallback
    alsa_input.usb-Generic_AB13X_USB_Audio_20210926172016-00.mono-fallback
    
    • The first is the HiFiBerry Studio ADC (default).
    • The second is a USB microphone.

    🔹 2. Use another device temporarily

    Instead of changing the default, run your app with the desired source:

    PULSE_SOURCE=alsa_input.usb-Generic_AB13X_USB_Audio_20210926172016-00.mono-fallback my-app
    

    This launches my-app using the USB mic while the system keeps HiFiBerry as default.


    🔹 3. Test with parecord

    parecord --device=alsa_input.usb-Generic_AB13X_USB_Audio_20210926172016-00.mono-fallback /tmp/usb-test.wav
    aplay /tmp/usb-test.wav
    

    🔹 4. Make a friendly ALSA alias (optional)

    If your app only enumerates ALSA devices and doesn’t understand PipeWire names, you can define an alias:

    sudo tee -a /etc/asound.conf >/dev/null <<'EOF'
    
    pcm.usbmic {
        type pulse
        device "alsa_input.usb-Generic_AB13X_USB_Audio_20210926172016-00.mono-fallback"
        hint.description "USB Microphone (via PipeWire)"
    }
    EOF
    

    Now the USB mic will appear in ALSA device lists as:

    usbmic   USB Microphone (via PipeWire)
    

    12. Common Log Messages (Safe to Ignore)

    When using ALSA and PipeWire together, you may occasionally see warnings like:

    ALSA lib pcm_dsnoop.c:566:(snd_pcm_dsnoop_open) unable to open slave
    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
    ALSA lib pcm_oss.c:397:(_snd_pcm_oss_open) Cannot open device /dev/dsp
    

    These messages appear because:

    • dsnoop warnings → the ALSA dsnoop plugin only supports capture. Apps probing playback may trigger these harmless errors.
    • jack server is not running → some apps probe JACK even if you don’t use it. PipeWire can handle JACK apps if you install pipewire-jack, but otherwise these warnings can be ignored.
    • /dev/dsp OSS errors → legacy OSS interface is not available on modern systems, so apps that still try will log a failure, but ALSA/PipeWire continues working fine.

    ✅ None of these indicate a problem with PipeWire or HiFiBerry. Your audio will still flow normally — you can safely ignore these logs.


    🔇 Silencing JACK Warnings

    If you want to prevent JACK errors from appearing in logs, set this environment variable in your session:

    export JACK_NO_START_SERVER=1
    

    To make it persistent for the dietpi user, add it to ~/.profile:

    echo 'export JACK_NO_START_SERVER=1' >> ~/.profile
    

    Then reload:

    source ~/.profile
    

    This tells libraries that probe JACK not to try auto-starting a JACK server, removing the “jack server is not running” spam.

    We respect your privacy.

    TLDR: We use cookies for language selection, theme, and analytics. Learn more.