X HAT and HDMI Audio

I am trying to stream the Picade 2020 using a capture card, so I need HDMI audio as well as from the speakers built-in connected to the X HAT concurrently.

None of the EmulationStation configuration of changing the audio card and/or the audio device seems to work, and even so I would assume would only output to one or the other.

How do I configure this? I am willing to update and recompile the overlay or the asound.conf, but it’s not clear to me what to change or how to get that information.

I found a similar article that has no resolution: PicadeHAT audio from the picade AND hdmi, is this possible

@gadgetoid - Would you be able to point me in the right direction? Would I need to recompile the overlay, change the asound.conf or both?

@gadgetoid - Please. I am about to deep-dive into both of these (device tree compilation and ALSA) and it would save me a lot of time if I knew I could ignore one or the other. Thank you!

Alright, after a lot of trial and error, I’ve figured it out as well as resolved a few other issues that cropped up after switching to PulseAudio. Enjoy!

Here’s why PulseAudio was chosen, as described by ChatGPT when we were troubleshooting:

Short version: ALSA can’t reliably play to two different hardware devices at once.

  • Single-device design: ALSA’s default targets one card. dmix mixes apps, but only within one card.
  • Multi-device plugins are fragile: tee/multi duplicate bytes, but don’t do per-device resampling, clock drift correction, or latency alignment. One sink stalls/drifts → late audio, xruns, or silence.
  • Clock mismatch: HDMI and the HiFiBerry DAC run off independent clocks and often use different sample rates/buffer sizes. ALSA won’t keep them in sync.
  • Control/UX: ES’s slider tends to control an ALSA mixer on one card, not two simultaneously; with ALSA-only dual-output hacks, volume control breaks or affects only one device.
  • Boot timing: Early services (e.g., splash) race devices; ALSA-only duplication is more timing-sensitive than a managed audio server.

PulseAudio (and PipeWire) solve this by creating a virtual sink with:

  • Per-sink resampling
  • Drift/latency handling
  • Unified default/volume control that ES can target
  • More robust startup behavior across services and sessions

Note the twitch system mentioned in the guide is for an EmulationStation system that will play Twitch streams and has nothing to do with the original goal of getting simultaneous HDMI audio out to make the Picade Twitch-streamable (which works now). If you’re interested in getting this set up, first follow this: Verifying...

And here’s the final step-by-step document I put together based on all the findings:

🚀 Step‑by‑step setup to get both Picade X-HAT (HiFiBerry DAC) audio and HDMI audio working simultaneously

1) 🔊 Enable audio devices

  • Edit '/boot/config.txt' and ensure:
dtparam=audio=on
dtoverlay=picade

2) 🔁 Route ALSA to PulseAudio

  • Edit '/etc/asound.conf':
pcm.!default { type pulse }
ctl.!default { type pulse }

3) 🧹 Clean up old PulseAudio files (if needed)

rm -rf /run/user/$(id -u)/pulse
rm -rf ~/.config/pulse/*

4) 🧩 Create the combined sink script

  • File: '/home/pi/Picade Audio Scripts/set-combined-audio-sink.sh'
#!/bin/bash
# Give PulseAudio time to start and detect sinks
#sleep 2

# Remove any previous combined sink (ignore errors)
pactl unload-module module-combine-sink 2>/dev/null

# Create a new combined sink for HDMI and Picade X-HAT
pactl load-module module-combine-sink sink_name=combined \
  slaves=alsa_output.platform-bcm2835_audio.digital-stereo,alsa_output.platform-soc_sound.stereo-fallback

# Set the combined sink as default
pactl set-default-sink combined

# Set combined sink volume to 50%
# pactl set-sink-volume combined 50%
  • Make executable:
chmod +x "/home/pi/Picade Audio Scripts/set-combined-audio-sink.sh"

5) 🖥️ Create the HDMI audio sink script

  • File: '/home/pi/Picade Audio Scripts/set-hdmi-audio-sink.sh'
#!/bin/bash
# Give PulseAudio time to start and detect sinks
#sleep 2
pactl set-default-sink alsa_output.platform-bcm2835_audio.digital-stereo
  • Make executable:
chmod +x "/home/pi/Picade Audio Scripts/set-hdmi-audio-sink.sh"

6) 🎧 Create the Picade audio sink script

  • File: '/home/pi/Picade Audio Scripts/set-picade-audio-sink.sh'
#!/bin/bash
# Give PulseAudio time to start and detect sinks
#sleep 2
pactl set-default-sink alsa_output.platform-soc_sound.stereo-fallback
  • Make executable:
chmod +x "/home/pi/Picade Audio Scripts/set-picade-audio-sink.sh"

7) 🛠️ Add the systemd user service

  • File: '/home/pi/.config/systemd/user/combined-sink.service'
[Unit]
Description=Set PulseAudio Combined Sink
After=pulseaudio.service
Wants=pulseaudio.service

[Service]
Type=oneshot
ExecStart="/home/pi/Picade Audio Scripts/set-combined-audio-sink.sh"
RemainAfterExit=yes

[Install]
WantedBy=default.target

8) ✅ Enable the service and user lingering

systemctl --user daemon-reload
systemctl --user enable combined-sink.service
systemctl --user start combined-sink.service
sudo loginctl enable-linger pi

9) 🔄 Reboot and test

sudo reboot

After boot:

pactl info | grep "Default Sink"        # Expect: combined
pactl list short sinks | grep combined  # combined present
paplay /usr/share/sounds/alsa/Front_Center.wav

10) 🎛️ Configure EmulationStation volume slider

  • EmulationStation → Main Menu → Sound Settings:
    • Audio Card: Default
    • Audio Device: Master
    • Adjust the Volume Slider. Back out of the menu to save, go back in to verify it saved.
    • Confirm audio on a game with a scraped video

11) 🖼️ Make the splash use PulseAudio combined sink (if using a video with audio)

  • File: '/opt/retropie/supplementary/splashscreen/asplashscreen.sh'
  • Change CMD to route VLC via PulseAudio:
CMD="env PULSE_SERVER=/run/user/1000/pulse/native PULSE_SINK=combined vlc --intf dummy --quiet --no-video-title-show --play-and-exit --aout=pulse"
  • In do_start(), add a short delay at the top:
# Wait a moment to ensure PulseAudio and all sinks are loaded
sleep 2
  • Before launching VLC, add a readiness wait:
# Wait for PulseAudio 'combined' sink to be ready (max 10s)
for i in 1 2 3 4 5 6 7 8 9 10; do
  if PULSE_SERVER=/run/user/1000/pulse/native pactl list short sinks 2>/dev/null | grep -q 'combined'; then
    break
  fi
  sleep 1
done
sleep 0.3

12) 📺 Twitch: route MPV to the combined sink and remove sudo (if configured with a ‘twitch’ system)

  • File: '/home/pi/.twitch/twitch.sh' (ensure MPV target):
/home/pi/.venv/bin/streamlink $twitchUrl "720p60,720p,480p,best" --twitch-low-latency --twitch-disable-ads --player=mpv --player-args="--audio-device=pulse/combined"
  • File: '/home/pi/.emulationstation/es_systems.cfg' (twitch system):
<command>bash /home/pi/.twitch/twitch.sh %ROM%</command>
  • Launch the Twitch system in EmulationStation and start a stream; verify audio plays from both HDMI and Picade X‑HAT outputs. 🎧🖥️

13) 🔎 Reboot and test (final verification)

  • Reboot:
sudo reboot
  • On boot, if a splash screen is enabled, listen for audio to confirm VLC is routed via the combined sink. If silent, revisit step 11 timing.
  • In EmulationStation, open Main Menu → Sound Settings and verify the volume level persisted across reboot

14) 🧯 Troubleshooting (if needed)

  • If PulseAudio won’t start after changes: repeat step 3
  • If splash audio is late, increase the sleep in asplashscreen.sh to 3–5s

Something very important missed:

If ever updating RetroPie (usually via the script/menu), the 'asplashscreen.sh' will be overwritten! Keep a copy of changes to 'asplashscreen.sh.custom' or similar and copy over after updates.