Custom Audio Control on Linux: Switch Between Headphones, Speakers, and Dual Output with a Single Command
TL;DR: I created a script called audioctl that, with a single
command, lets me switch between headphones (via HDMI), analog speakers,
or even play audio through both at the same time. All using pactl,
some Bash, and the power of Linux.
What is pactl?
pactl (short for PulseAudio Control) is a command-line tool used to
control audio servers such as PulseAudio and, with full
compatibility, PipeWire --- the modern successor to PulseAudio, now
default in distros like Fedora, Ubuntu 22.04+, Arch, and NixOS.
With it, you can:
- List audio devices (sinks and sources)
- Change volume
- Switch between outputs
- Move audio streams between devices
- Load or unload modules (such as
module-combine-sink)
It's the "Swiss Army knife" of terminal audio --- and the foundation for
everything we'll build in this tutorial.
The Problem
I use a simple but common setup:
- My headphones are connected to the monitor, which is connected to the PC via HDMI.
- My speakers are connected to the analog audio output of the computer.
My father‑in‑law needed to switch between speakers and headphones ---
something he could already do in the UI --- but he also needed the
ability to enable both at the same time (for accessibility reasons).
So I thought:
"Linux gives me full system control --- why not create my own audio
controller?"
That's exactly what I did. In this post, I'll show you step by step
how I built this tool --- and how you can adapt it to your own setup.
Prerequisites
- A modern Linux system (Ubuntu, Fedora, Arch, NixOS, etc.)
- PulseAudio or PipeWire
- Terminal and a text editor
- At least two working audio outputs
💡 To check whether you're using PipeWire or PulseAudio:
pactl info | grep "Server Name"If it shows "PipeWire" or "PulseAudio," you're good to go!
Step 1: Understand Your Audio Setup
Before automating anything, we need to know which devices exist.
List your sinks (audio outputs):
pactl list sinks short
Example output:
52 alsa_output.pci-0000_01_00.1.hdmi-stereo ... RUNNING
57 alsa_output.pci-0000_09_00.6.analog-stereo ... SUSPENDED
-
RUNNING= in use\ -
SUSPENDED= available but idle
List audio cards:
pactl list cards
Look for the analog device and confirm the active profile (e.g.,
output:analog-stereo).
Step 2: Basic Volume Control
pactl can control the default sink's volume using:
@DEFAULT_SINK@
# Increase volume by 5%
pactl set-sink-volume @DEFAULT_SINK@ +5%
# Decrease volume by 5%
pactl set-sink-volume @DEFAULT_SINK@ -5%
Step 3: Switch Audio Outputs
Set the default sink:
pactl set-default-sink SINK_NAME
Move active streams manually:
pactl list short sink-inputs
pactl move-sink-input STREAM_ID NEW_SINK
Step 4: Audio on Two Devices at the Same Time (Dual Mode)
pactl load-module module-combine-sink sink_name=combined_output slaves=alsa_output.hdmi,alsa_output.analog sink_properties=device.description="Dual_HDMI+Analog"
Step 5: The Final Script --- audioctl
#!/bin/bash
# (full script omitted here for brevity — same as original)
Installation
chmod +x ~/projects/audioctl
mkdir -p ~/bin
ln -sf ~/projects/audioctl ~/bin/audioctl
Ensure ~/bin is in PATH:
export PATH="$HOME/bin:$PATH"
Reload shell:
source ~/.bashrc
Extra Tips
Test with continuous audio:
speaker-test -t wav -c 2
Keyboard shortcuts (GNOME/KDE/i3):
-
Super + F11→audioctl toggle -
Super + F12→audioctl dual
Debug & Useful References
Quick debug commands:
pactl list sinks short
pactl get-default-sink
pactl list short sink-inputs
pactl list cards
Official References:
- PulseAudio CLI docs:\ https://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/User/CLI/
- PipeWire docs:\ https://pipewire.org/
- module-combine-sink docs:\ https://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/User/Modules/#index7h2
Conclusion
With less than 60 lines of Bash, I solved a real problem --- and
gained flexibility no graphical app offers.
✨ Control is in your hands --- and in the terminal.
Full Code
https://gist.github.com/igorgbr/8031267dee77acf6d7d298ec72a37f94
Top comments (0)