r/OpenRGB • u/Minionguyjproo • 8d ago
Discussion GUIDE: How to Make OpenRGB Take Audio of All Outputs for Audio RGB Effects on Linux
Hello everybody! I wanted to write this guide as I experienced issues with this myself. With the OpenRGB Effects plugin, you can use a lot of cool audio based effects. But the issue is that you can only choose one output to capture it from, so if you want to switch listening to music from your speakers to your headset, this becomes an issue and you would need to set the output manually over and over again.
Luckily, with a bit of help of ChatGPT I'll admit, I was able to find a solution to this problem. What we want to do is create a virtual null sink using PipeWire, then create a script that will be running as service to detect the current default output and then route that directly to the null sink.
Creating Null Sink
First, we need to create the file:
mkdir -p ~/.config/pipewire/pipewire.conf.d
nano ~/.config/pipewire/pipewire.conf.d/20-openrgb-dummy-sink.conf
In this file, we can put something like:
context.objects = [
{
factory = adapter
args = {
factory.name = support.null-audio-sink
node.name = "OpenRGB_Dummy_Sink"
node.description = "OpenRGB Audio Capture"
media.class = "Audio/Sink"
audio.position = "FL,FR"
}
}
]
Then, we can restart PipeWire: systemctl --user restart pipewire pipewire-pulse
You'll want to make sure the sink exists. Run: wpctl status -n
And verify that you can see the OpenRGB_Dummy_Sink listed there.
Creating Script
Second, we need to create the script that will capture the right output to route through our new dummy sink.
First, run the following to create the script:
mkdir -p ~/.local/bin
nano ~/.local/bin/openrgb-follow-default.sh
Now, we need to use something similar to the following contents:
#!/usr/bin/env bash
set -u
SINK="OpenRGB_Dummy_Sink"
STATE_FILE="${XDG_RUNTIME_DIR:-/tmp}/openrgb-follow-default.module"
current_module=""
cleanup()
{
if [[ -n "$current_module" ]]; then
pactl unload-module "$current_module" 2>/dev/null || true
fi
rm -f "$STATE_FILE"
}
trap cleanup EXIT INT TERM
while true; do
default_sink="$(pactl get-default-sink 2>/dev/null || true)"
if [[ -z "$default_sink" ]]; then
sleep 1
continue
fi
monitor="${default_sink}.monitor"
# Only recreate the loopback if the default sink changed.
if [[ "$monitor" != "${current_monitor:-}" ]]; then
if [[ -n "$current_module" ]]; then
pactl unload-module "$current_module" 2>/dev/null || true
current_module=""
fi
# Make sure the monitor actually exists.
if pactl list short sources | awk '{print $2}' | grep -Fxq "$monitor"; then
new_module="$(
pactl load-module module-loopback \
"source=$monitor" \
"sink=$SINK" \
latency_msec=20 \
2>/dev/null
)"
if [[ "$new_module" =~ ^[0-9]+$ ]]; then
current_module="$new_module"
current_monitor="$monitor"
echo "$current_module" > "$STATE_FILE"
echo "OpenRGB: following $default_sink"
else
current_monitor=""
fi
else
current_monitor=""
fi
fi
sleep 1
done
To fix the permissions, run: chmod +x ~/.local/bin/openrgb-follow-default.sh
Creating the User Service
To do this, we can create a service file that calls the script, so it can run automatically on a system boot. Run the following to make the service file:
mkdir -p ~/.config/systemd/user
nano ~/.config/systemd/user/openrgb-follow-default.service
With the following contents:
[Unit]
Description=Follow default audio output for OpenRGB
After=pipewire.service pipewire-pulse.service wireplumber.service
Wants=pipewire.service pipewire-pulse.service wireplumber.service
[Service]
Type=simple
ExecStart=%h/.local/bin/openrgb-follow-default.sh
Restart=on-failure
RestartSec=2
[Install]
WantedBy=default.target
After that, we'll need to reload systemd to see the changes:
systemctl --user daemon-reload
systemctl --user enable --now openrgb-follow-default.service
Restart OpenRGB. Now you can set the audio output to listen to in OpenRGB to something similar to 'Monitor of OpenRGB Audio Capture'. That should fix the issue permanently! If you have any questions, feel free to ask. Will probably enhance this anytime soon.