Pi4 Tweak - HDMI_0>Video + HDMI_1>Audio

See Post #4 for updated playbook v2

UPDATED v2.1:

I have spent the past few days figuring this out due to my own inexperience and mistaken my USB connections, and I have now gotten it working and verified with another fresh install. My usecase is likely unique as I didn’t find any previous thread on the matter, and even more “gotchas” with my hardware. These are my exact steps for my hardware so it may not work for you exactly, and I provide this a reference that it can be done.

Hardware:

Pi 4b w/ 8gb
LCDWIKI 8" 1280x800 HDMI touchscreen
POE HAT
Generic Power Filter HAT
Generic “Hifiberry Digi” HAT
Samsung Soundbar

Scenario:

My Pi4 was my first Volumio build so I tinker the most with it. It was originally hooked up to a SMSL DAC via USB and outputs thru various audio HAT’s. For my daily use, I now run a Pi5 with another enclosure and I’ve been looking to make more use of Pi4 instead of a just a smart clock/weather station. The Pi4 was originally powered and networked thru POE and moving it closer to the sound bar meant I had to try a few things I wasn’t planning on, namely wifi and USB power. These 2 functions now causes the Pi4 to draw more power than before so I ran into a few gotchas:

  1. To ensure everything is powered adequately, the screen now attaches 1 USB port to power and another to the Pi for touch controls. With POE, I was able to power the screen thru the Pi4’s own USB2 port but I found doing so now might cause instability.
  2. I’m now using a OnePlus USB charger as my PSU to solely power the Pi. I’m sure the power requirements may lower if I removed the HAT’s for this application but I couldn’t be bothered for now. There are no perceptible noise or emi from this.
  3. I now have to use the built in hotspot for initial setup.
  4. ***I arrived at these steps with the help of ChatGPT. It had suggested various scripts and hacks that I have also tried to no success.
  5. I’m not sure if a OTA update will kill these tweaks.

Goal:

I simply wanted to output video-only to HDMI_0 port and audio-only to the HDMI_1 port. Due to my screen being more like 16x9 in resolution scale vs the typical 1280x780 video signal expected by the soundbar, I had to try many different things that did not work. I’ve finally tested the following steps and duplicated it again for verification. Since I’ve spent so much time on this, I hope it may help someone else in the future.

Steps:

  1. Flash Volumio on media, install, setup, activate DEV. Connect touchscreen [HDMI_0] and sound bar [HDMI_1].

  2. Install “Touch Display” and “Now Playing” plugins. Reboot and enable both.

  3. Edit userconfig.txt:

sudo nano /boot/userconfig.txt

# --- Pi4 Customization ---
arm_boost=1
max_usb_current=1
dtoverlay=vc4-kms-v3d-pi4,audio=on
disable_overscan=1
hdmi_blanking=0
config_hdmi_boost=7

# --- Force HDMI0 to native at boot ---
hdmi_force_hotplug:0=1
hdmi_ignore_edid_audio:0=1
hdmi_drive:0=1

# --- Force HDMI1 audio at boot ---
hdmi_force_hotplug:1=1
hdmi_force_edid_audio:1=1

# --- Light OC for smooth UI ---
gpu_freq=650
over_voltage=4
force_turbo=0

# --- Disable unused buses ---
dtparam=spi=off
dtparam=uart=off
  1. Reboot.

  2. Create a tiny helper script and make it executable:

sudo nano /usr/local/bin/fix-outputs.sh

#!/usr/bin/env bash
# Robustly set display layout + touch mapping for Volumio Touch Display
# Works with vc4-kms-v3d on Pi 4

export DISPLAY=:0
export XAUTHORITY=/var/lib/volumio/.Xauthority

log() { echo "[fix-outputs] $*" | systemd-cat -t fix-outputs -p info; }

# Wait for X to be up
for i in {1..30}; do
  if xrandr >/dev/null 2>&1; then break; fi
  sleep 1
done

# Ensure connectors are present
xrandr_out="$(xrandr)"
echo "$xrandr_out" | grep -q "^HDMI-1 connected" || { log "HDMI-1 not connected"; exit 0; }
echo "$xrandr_out" | grep -q "^HDMI-2 connected" || log "HDMI-2 not connected (ok if soundbar off)"

# 1) Set HDMI-1 (LCDWIKI) to native 1280x800 and primary
xrandr --output HDMI-1 --mode 1280x800 --pos 0x0 --primary --rate 60 --dpi 120

# 2) Put HDMI-2 (soundbar) to the right so outputs don't overlap
#    Choose a common stable mode on the bar (fallback 1920x1080@60)
if echo "$xrandr_out" | sed -n '/^HDMI-2 connected/,/^\S/p' | grep -q "1920x1080"; then
  xrandr --output HDMI-2 --mode 1920x1080 --pos 1280x0 --rate 60
else
  # use its preferred mode (starred) if 1080p isn't present
  pref="$(echo "$xrandr_out" | awk '/^HDMI-2 connected/{flag=1;next}/^\S/{flag=0}flag && /\*/{print $1; exit}')"
  [ -n "$pref" ] && xrandr --output HDMI-2 --mode "$pref" --pos 1280x0
fi

# 3) Prevent DPMS blanking (Volumio sometimes blanks aggressively)
xset -dpms
xset s off
xset s noblank

# 4) Map touchscreen input to HDMI-1
#    Find the first touch-capable device (adjust if you know the exact name)
touch_id="$(xinput list | awk -F'=|\\[' '/Touch|touch|Goodix|ILITEK|eGalax|WaveShare|HID/ && /pointer/ {print $2; exit}')"
if [ -n "$touch_id" ]; then
  xinput map-to-output "$touch_id" HDMI-1
  log "Mapped touch device id $touch_id to HDMI-1"
else
  log "No obvious touch device found in xinput list"
fi

log "Outputs configured."

Make it executable:

sudo chmod +x /usr/local/bin/fix-outputs.sh

  1. Create a systemd unit that runs after the X session which the Touch Display plugin launches:

sudo nano /etc/systemd/system/outputs-fix.service

[Unit]
Description=Fix display layout and touch mapping for Volumio kiosk
After=volumio-kiosk.service
Wants=volumio-kiosk.service

[Service]
Type=oneshot
User=volumio
Group=volumio
Environment=DISPLAY=:0
ExecStart=/usr/local/bin/fix-outputs.sh
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
  1. Enable and start:

sudo systemctl daemon-reload
sudo systemctl enable outputs-fix.service
sudo systemctl start outputs-fix.service

  1. Reboot

  2. ***REMEMBER TO CHANGE OUTPUT TO “HDMI 1” IN “PLAYBACK OPTIONS”

  3. Scale the UI in “Touch Display”; you’ll need to reboot again for it to apply the correct resolution.

Outcome:

I can now get pixel-perfect display out to my 1280x800 touchscreen without it stretching to match the output expected by the soundbar or display any black bar if it tried to match the scaling of the sound bar. Again, this is unique to my usecase and I wondered if I tried my non-touch screen that scales to normal video resolution expected by the sound bar, if it’d have made life easier. Audio outputs thru the other HDMI port to the sound bar and I have no issue with any format being played. I have not looked into multi-channel audio out thru HDMI_1 yet but I also don’t have any source material for it anyway for now.

I know there’s more than one way to skin a cat, so to speak…if there’s a better way to make this work, I’m all ears!

1 Like

Hey @RedEyeNinja,

Great work getting this dual HDMI setup working! The amount of troubleshooting you put into this really shows, and documenting it helps the community immensely.

I wanted to add some context about why your audio routing works the way it does, since I’ve been researching this exact area. The key principle is that Xorg itself doesn’t route audio - it’s a display server - but how you configure outputs in Xorg directly affects whether the HDMI audio hardware remains accessible to ALSA.

When you use xrandr to position HDMI-2 (your soundbar) “to the right” of HDMI-1 with a valid mode like 1920x1080, you’re keeping that HDMI port active in the display system. Even though your UI only shows on HDMI-1, keeping HDMI-2 clocked and initialized prevents the kernel from shutting down the audio device associated with that port. HDMI carries both video and audio on the same physical connection, so the driver assumes if video is off, audio isn’t needed either.

That’s why your script works - by keeping both outputs active in non-overlapping virtual space, all the hardware stays powered and available to your audio subsystem.

I wrote a similar guide for keeping HDMI audio extractors working while running the Touch Display UI on DSI panels, tackling the same underlying issue from a different angle:

[Guide] Keep HDMI audio extractor working while the Touch Display UI runs on DSI

Your dual HDMI approach and the positioning logic in your fix-outputs.sh script are solid. Nice job working through all those power and hardware gotchas too!


Just wanted to flag this for the Volumio team (@volumio) - it might be worth considering some built-in support for advanced audio routing scenarios like this. Soundbars connected via HDMI are becoming increasingly common, and users are clearly running into these edge cases.

Also tagging @gvolt - this could be a valuable reference for adding optional logic to the Touch Display plugin, especially for handling multi-output configurations where audio needs to stay on a specific DSI/eDP/HDMI port.

Kind Regards,

1 Like

@nerd

As expected, this solution works but is not optimal. After living with it for a few days, here are some observations:

  1. Audio playback is seamless and is the most reliable part of this tweak, no pops or clicks switching between sources or audio types.
  2. The touchscreen is not as responsive as before and it’s not really usable. It’s no big deal for now, as the screen is far away from me and I mainly use it to display time/weather when not playing music and PeppyMeter Basic when I’m playing music. I used to be able to tap on the weather area to change it the weather display to hourly forecast instead of daily but not anymore as that tap will now activate the touchscreen and goes into music browser.
  3. As such, swipe down functions are not reliable and I haven’t gotten thru a sequence where I can use the drop down menu to get into Manifest to shut the Pi4 down using the touchscreen.
  4. If I don’t touch the touchscreen, I can play for hours with no issues. If I touch the touchscreen, the UI crashes sometimes at both the touchscreen and web UI, requiring a reboot to recover.

I had a feeling that this may conflict with existing implementation and plugins but I was hopeful. Here’s a log after the interactions described above.

http://logs.volumio.org/volumio/QRiP4bH.html

EDIT: I got ChatGPT to review parts of the log and after more troubleshooting, I got the playbook v2 in the post below.

Did another round of troubleshooting with ChatGPT to review logs and polish the playbook to avoid conflicts with plugins. Here’s the summary I got ChatGPT to generate as the playbook v2. Again, this is very specific to my hardware, as ChatGPT identified from the logs that the touch issue is a matter of proper naming and mapping, and it is now mapped specific to my touchscreen model/name and not an universal implementation. This is an area that an official plugin approach may be more applicable to the community.

Playbook v2 — Pi 4: HDMI-0 video (touch LCD @1280×800) + HDMI-1 audio (soundbar), with reliable touch

Tested on: Raspberry Pi 4B (8 GB), Volumio (Bookworm), LCDWIKI 8" 1280×800 HDMI touch, Samsung soundbar on HDMI-1.
Goal: Pixel-perfect UI on the 1280×800 LCD via HDMI-0, audio out via HDMI-1 — no stretching. Touch works reliably.


0) Prep & install

  1. Flash Volumio, boot. Connect:
  • LCD (video + its USB touch) → HDMI-0 / USB
  • Soundbar → HDMI-1
  1. In Plugins install and enable:
  • Touch Display
  • Now Playing (optional; works with this setup)
  1. Set Playback Options → Output device = HDMI 1 (the soundbar).

1) Pi firmware boot config

Edit /boot/userconfig.txt:

# --- Pi4 Customization ---
arm_boost=1
max_usb_current=1
dtoverlay=vc4-kms-v3d-pi4,audio=on
disable_overscan=1
hdmi_blanking=0
config_hdmi_boost=7

# --- Force HDMI0 to native at boot (video; no audio on LCD) ---
hdmi_force_hotplug:0=1
hdmi_ignore_edid_audio:0=1
hdmi_drive:0=1

# --- Force HDMI1 audio at boot (soundbar) ---
hdmi_force_hotplug:1=1
hdmi_force_edid_audio:1=1

# --- Light OC for smooth UI (optional) ---
gpu_freq=650
over_voltage=4
force_turbo=0

# --- Disable unused buses (optional) ---
dtparam=spi=off
dtparam=uart=off

Reboot.


2) (Optional but recommended) Pin HDMI-0 to 1280×800 via Xorg

Create /etc/X11/xorg.conf.d/10-monitor.conf:

Section "Monitor"
    Identifier "HDMI-1"
    Option "PreferredMode" "1280x800"
EndSection

Section "Screen"
    Identifier "Screen0"
    Monitor "HDMI-1"
EndSection

Note: On KMS, the connector names are HDMI-1 (the left/primary) and HDMI-2 (right). Yes, it’s a tad confusing: HDMI-1 = HDMI-0 port physically.


3) Install utilities used by the fix script

sudo apt-get update
sudo apt-get install -y xinput x11-xserver-utils jq

4) Create the layout + touch-mapping helper

/usr/local/bin/fix-outputs.sh:

#!/usr/bin/env bash
# Robustly set display layout + touch mapping for Volumio Touch Display
# Works with vc4-kms-v3d on Pi 4

export DISPLAY=:0
export XAUTHORITY=/var/lib/volumio/.Xauthority

log() { echo "[fix-outputs] $*" | systemd-cat -t fix-outputs -p info; }

# Wait for X to be up
for i in {1..30}; do
  if xrandr >/dev/null 2>&1; then break; fi
  sleep 1
done

# Ensure connectors are present
xrandr_out="$(xrandr)"
echo "$xrandr_out" | grep -q "^HDMI-1 connected" || { log "HDMI-1 not connected"; exit 0; }
echo "$xrandr_out" | grep -q "^HDMI-2 connected" || log "HDMI-2 not connected (ok if soundbar off)"

# 1) Set HDMI-1 (LCDWIKI) to native 1280x800 and primary
xrandr --output HDMI-1 --mode 1280x800 --pos 0x0 --primary --rate 60 --dpi 120

# 2) Put HDMI-2 (soundbar) to the right so outputs don't overlap
#    Choose a common stable mode on the bar (fallback 1920x1080@60)
if echo "$xrandr_out" | sed -n '/^HDMI-2 connected/,/^\S/p' | grep -q "1920x1080"; then
  xrandr --output HDMI-2 --mode 1920x1080 --pos 1280x0 --rate 60
else
  # use its preferred mode (starred) if 1080p isn't present
  pref="$(echo "$xrandr_out" | awk '/^HDMI-2 connected/{flag=1;next}/^\S/{flag=0}flag && /\*/{print $1; exit}')"
  [ -n "$pref" ] && xrandr --output HDMI-2 --mode "$pref" --pos 1280x0
fi

# 3) Prevent DPMS blanking (ignore errors)
xset -dpms  || true
xset s off  || true
xset s noblank || true

# 4) Let kiosk / plugins finish GPU init before touching inputs
sleep 8

# 5) Map touchscreen to HDMI-1 by NAME (stable), with retries
if command -v /usr/bin/xinput >/dev/null 2>&1; then
  name=$(/usr/bin/xinput list --name-only | grep -E '^QDtech MPI1001$' | head -n1)
  if [ -z "$name" ]; then
    # Fallback: anything that looks like the panel
    name=$(/usr/bin/xinput list --name-only | grep -E 'QDtech|MPI|Touch|touch|HID' | head -n1)
  fi

  if [ -n "$name" ]; then
    echo "[fix-outputs] Touch candidate: $name" | systemd-cat -t fix-outputs -p info
    # Try up to 10 times in case the device re-appears with a new id
    for i in $(seq 1 10); do
      /usr/bin/xinput map-to-output "$name" HDMI-1 2>/dev/null && {
        echo "[fix-outputs] Touch mapped to HDMI-1 (name: $name)" | systemd-cat -t fix-outputs -p info
        break
      }
      sleep 1
    done
    # Also map any sibling 'pointer' devices that match the vendor
    /usr/bin/xinput list --name-only \
      | grep -E 'QDtech|MPI|Touch|touch|HID' \
      | while read -r dev; do /usr/bin/xinput map-to-output "$dev" HDMI-1 2>/dev/null || true; done
  else
    echo "[fix-outputs] No touch device name found" | systemd-cat -t fix-outputs -p warning
  fi
else
  echo "[fix-outputs] xinput not installed; skipping mapping" | systemd-cat -t fix-outputs -p info
fi

Make it executable:

sudo chmod +x /usr/local/bin/fix-outputs.sh

5) One-shot systemd unit (runs after kiosk/Chromium is up)

/etc/systemd/system/outputs-fix.service:

[Unit]
Description=Fix display layout and touch mapping for Volumio kiosk
After=volumio-kiosk.service now_playing.service
Wants=volumio-kiosk.service now_playing.service

[Service]
Type=oneshot
User=volumio
Group=volumio
Environment=DISPLAY=:0
Environment=XAUTHORITY=/var/lib/volumio/.Xauthority
ExecStartPre=/bin/sleep 3
ExecStart=/usr/local/bin/fix-outputs.sh
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

(Ensure a sane PATH for oneshot services)

sudo install -d -m 0755 /etc/systemd/system/outputs-fix.service.d
printf "[Service]\nEnvironment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\n" | \
  sudo tee /etc/systemd/system/outputs-fix.service.d/override.conf >/dev/null

Enable & start:

sudo systemctl daemon-reload
sudo systemctl enable outputs-fix.service
sudo systemctl start outputs-fix.service

You should see on first run:

[fix-outputs] Touch mapped to HDMI-1 (name: QDtech MPI1001)

6) Verify

DISPLAY=:0 xrandr
# Expect:
# HDMI-1 connected primary 1280x800+0+0
# HDMI-2 connected 1920x1080+1280+0

DISPLAY=:0 XAUTHORITY=/var/lib/volumio/.Xauthority xinput list | grep -E 'QDtech|MPI|Touch'
DISPLAY=:0 XAUTHORITY=/var/lib/volumio/.Xauthority \
  xinput list-props "QDtech MPI1001" | egrep -i 'Coordinate Transformation Matrix|Calibration'
# Expect a non-identity Coordinate Transformation Matrix, e.g. 0.400000 ... 0.740741 ...

In Touch Display plugin, optionally adjust UI scale; reboot once if you change it.


7) Notes & gotchas

  • Power: Touch LCDs pulling power from the Pi can be marginal, especially with HATs/Wi-Fi. If touch gets flaky or UI crashes under heavy touch use, power the LCD from a stable 5 V supply (not the Pi’s USB2 port).
  • Now Playing: This setup works fine (we start after volumio-kiosk.service and now_playing.service).
  • PeppyMeter: If you use it, it will start/stop around playback transitions; that’s unrelated to touch mapping here.
  • Cold-boot race: If mapping ever misses on cold boots, bump ExecStartPre=/bin/sleep 3 to sleep 5 in the unit and retry.
  • Different touch name: If your device isn’t matched, change the regex in the script from QDtech|MPI|Touch|touch to include your vendor string (see xinput list).

8) One-liners for manual recovery (if needed)

  • Remap touch interactively (no reboot):
DISPLAY=:0 XAUTHORITY=/var/lib/volumio/.Xauthority xinput map-to-output "QDtech MPI1001" HDMI-1
  • Re-run the fix:
sudo systemctl restart outputs-fix.service
journalctl -u outputs-fix.service --no-pager | tail -n 50

9) What “good” looks like (logs)

A healthy start shows exactly one mapping per restart:

… Starting outputs-fix.service …
[fix-outputs] Touch mapped to HDMI-1 (name: QDtech MPI1001)
… Finished outputs-fix.service …

…and xrandr shows a 3200×1080 framebuffer with:

  • HDMI-1 1280x800+0+0 primary (LCD)
  • HDMI-2 1920x1080+1280+0 (soundbar)

Recap: the correct final structure

/etc/systemd/system/
├── outputs-fix.service
└── outputs-fix.service.d/
    └── override.conf

EDIT: These fixed the touchscreen but now PeppyMeter Basic is not working, as it may display on the wrong HDMI port. Here’s a fix:

Fix PeppyMeter Basic on HDMI-1 (touch panel)

1) Make PeppyMeter start after your output mapping

Create a systemd drop-in for peppymeterbasic.service so it waits for outputs-fix.service:

sudo mkdir -p /etc/systemd/system/peppymeterbasic.service.d

cat <<'EOF' | sudo tee /etc/systemd/system/peppymeterbasic.service.d/override.conf >/dev/null
[Unit]
Wants=outputs-fix.service
After=outputs-fix.service

[Service]
# Ensure peppy has the same X env as the kiosk + our fix script
Environment=DISPLAY=:0
Environment=XAUTHORITY=/var/lib/volumio/.Xauthority

# Force SDL/Pygame to the FIRST head (HDMI-1 at 1280x800, placed at 0,0)
Environment=SDL_VIDEO_FULLSCREEN_DISPLAY=0
Environment=SDL_VIDEO_WINDOW_POS=0,0

# Keep a normal PATH so python and helpers can be found reliably
Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
EOF

sudo systemctl daemon-reload

Why: your desktop spans both heads (3200×1080). Without these, Pygame may open full-screen on the right-hand head (HDMI-2), so you never see it on the touch panel.

2) Restart PeppyMeter and verify logs

sudo systemctl restart peppymeterbasic.service
journalctl -u peppymeterbasic.service --since -2m --no-pager

Now start a track and wait ~5–10s. You should see the VU meters pop up on HDMI-1.

Optional Post-Setup Enhancements (Recommended)

  1. Grant GPU access to volumio:
sudo usermod -aG render,video volumio
sudo mkdir -p /etc/systemd/system/peppymeterbasic.service.d
cat <<'EOF' | sudo tee /etc/systemd/system/peppymeterbasic.service.d/10-groups.conf >/dev/null
[Service]
SupplementaryGroups=render video
EOF
sudo systemctl daemon-reload
  1. Fix Mesa shader cache (choose one):
  • Option A:
    sudo chown -R volumio:volumio /home/volumio/.cache
  • Option B:
    Add to same drop-in:
    Environment=MESA_SHADER_CACHE_DIR=/tmp/mesa_shader_cache

These steps remove GPU “permission denied” errors and ensure PeppyMeter uses hardware acceleration cleanly.

Hey @RedEyeNinja,

Thanks for sharing your detailed troubleshooting process and the refined playbook v2. The amount of research you put into this is really impressive, and documenting both versions helps the community understand the progression.

Generic problem

A word of caution about using AI tools like ChatGPT for Volumio-specific troubleshooting: while AI can provide helpful input and generic guidance for typical opensource Linux distributions, this needs to be treated with careful consideration. Volumio OS is a complete ecosystem built from multiple layers - firmware, hardware management, backend, UI, proprietary extensions, APIs, and a complete plugin inclusion framework that works across local players and numerous supporting distributed endpoints.

Using AI assistant

AI assistants are helpful with generic Linux trivia and standard configurations, but they fall short with ecosystem understanding. They don’t know how Volumio’s plugins interact with each other, how the kiosk service integrates with the audio backend, or how updates might affect custom configurations. Your experience with the touch responsiveness issues and UI crashes is a good example - those problems likely stem from conflicts with existing Volumio implementations that a generic AI wouldn’t anticipate.

Consideartion

Your playbook v2 improvements - especially the device-name specific mapping and the service dependencies - show the kind of real-world testing that matters. However, the touch responsiveness issues and UI crashes you’re experiencing are likely consequences of working around the official implementation rather than integrating with it.

Suggestion

The proper approach would be referencing the Touch Display plugin source where the specifics of xorg controls are officially handled:

Rather than custom scripting that potentially conflicts with the plugin’s logic, consider collaborating with @gvolt on GitHub to integrate multi-output audio routing support directly into the Touch Display plugin framework. This would benefit the entire community and avoid the kind of instability you’re seeing with the current workaround.

Kind Regards,

Hey @RedEyeNinja,

You can see the functional prototype started here:

Kind Regards,

Hey @RedEyeNinja,

The concept has been released as beta for testing. More details here.

Kind Regards,

1 Like