Play audio on two Bluetooth headphones with PipeWire
pasystray can choose an output for an application, but it cannot itself duplicate audio to two outputs. On a PipeWire system, create a virtual combined sink that forwards its audio to both Bluetooth headphones, then select that sink in pasystray.
This is useful for listening with two pairs of headphones from one computer. Both devices must be connected at the same time.
Find the Bluetooth sink names
Run:
pactl list short sinks
For example, this system has these two Bluetooth sinks:
88 bluez_output.40:58:99:55:FD:82 PipeWire float32le 2ch 48000Hz SUSPENDED
119 bluez_output.7C:E9:13:59:03:80 PipeWire float32le 2ch 48000Hz SUSPENDED
SUSPENDED is normal when a device is connected but not currently playing audio. The important values are the two bluez_output... names, not the numeric IDs at the beginning of the lines.
To confirm that PipeWire is providing the PulseAudio-compatible server, run:
pactl info | grep "Server Name"
It should report PipeWire.
Create the combined sink
Use the sink names from the previous command:
pactl load-module module-combine-sink \
sink_name=dual_bluetooth \
sink_properties=device.description=Dual_Bluetooth \
sinks=bluez_output.40:58:99:55:FD:82,bluez_output.7C:E9:13:59:03:80
PipeWire should print a module ID. Check that the new virtual output exists:
pactl list short sinks
It should include dual_bluetooth.
In pasystray, choose Dual_Bluetooth from the default-sink menu, or route an individual application’s stream to it. To make it the default output from a terminal:
pactl set-default-sink dual_bluetooth
New applications will then send audio to both headphones. Existing applications may need to be moved to dual_bluetooth in pasystray or restarted.
Make it persistent
The pactl load-module command only lasts until PipeWire is restarted. To load the virtual sink whenever pipewire-pulse starts, create this user configuration file:
~/.config/pipewire/pipewire-pulse.conf.d/dual-bluetooth.conf
with these contents:
pulse.cmd = [
{ cmd = "load-module"
args = "module-combine-sink sink_name=dual_bluetooth sink_properties=device.description=Dual_Bluetooth sinks=bluez_output.40:58:99:55:FD:82,bluez_output.7C:E9:13:59:03:80"
flags = [ ]
}
]
Restart the user PulseAudio-compatibility service after creating the file:
systemctl --user restart pipewire-pulse
Connect both headphones, then verify the combined sink again with:
pactl list short sinks
The Bluetooth sink names normally contain the headphones’ MAC addresses, so they remain the same across reconnects. If a device is removed and paired again, check its name and update the configuration if its MAC address changed.
Remove the temporary sink
For a sink created only with pactl load-module, find its module ID:
pactl list short modules | grep combine
Then unload it:
pactl unload-module <module-id>
For the persistent version, remove or disable the dual-bluetooth.conf drop-in and restart pipewire-pulse.
Bluetooth synchronization caveat
This duplicates the audio; it does not make two Bluetooth devices perfectly synchronized. Each headset has its own codec latency, buffer, and clock. The sound can therefore have a small offset or gradually drift between the headphones, which is especially noticeable when both listeners are close together.
PipeWire’s documented module-combine-sink is the appropriate module for this setup. It is generally fine for two people listening separately, but it is not a replacement for purpose-built synchronized multi-headphone hardware.