This script enables stereo playback using two Ultimate Ears HyperBoom Bluetooth speakers on a Linux Debian 12 system. It mimics the "Party Up" feature of the Ultimate Ears mobile application, creating a virtual device for synchronized playback.
- Combine two HyperBoom speakers for stereo sound.
- Utilizes PipeWire and PipeWire-Pulse for virtual device management.
- Supports custom speaker identification via their human-readable names.
- Linux Debian 12 (or a compatible distribution).
- Installed dependencies:
- pipewire
- pipewire-pulse
- Two paired and connected Ultimate Ears HyperBoom speakers.
- Ensure both speakers are paired and connected via Bluetooth.
- Set the speakers to the same volume.
- Edit the script to configure the following variables:
- Virtual device name and description:
VIRTUAL_SINK_NAME="HyperBoom_Left" VIRTUAL_SINK_DESCRIPTION="HyperBoom_Right"
- Speaker Names: Set the human-readable names of your two HyperBoom speakers.
These names should match their Bluetooth device names.
LEFT_SPEAKER="HyperBoom_Left" RIGHT_SPEAKER="HyperBoom_Right"
- Virtual device name and description:
- Save the changes to the script file.
- Make the script executable:
chmod +x hyperboom-duo-stereo.bash
- Run the script:
./hyperboom-duo-stereo.bash
- Follow any on-screen instructions to finalize the setup.
The script does not make permanent changes, thus you have to execute it every time Pipewire is restarted.
- Ensure both speakers are powered on, paired, and connected before running the script.
- Verify that pipewire and pipewire-pulse are installed and running on your system.
- Check the human-readable names of your speakers using the Bluetooth settings or tools like bluetoothctl.
On my system, there were synchronization problems occuring quite regularly. The following tip helped me solve them.
The following instructions were taken from Low latency guide for Linux using Pipewire (on Reddit)
Create/edit ~/.config/pipewire/pipewire-pulse.conf
and put the following lines
into it:
context.modules = [
{ name = libpipewire-module-rt
args = {
nice.level = -20
rt.prio = 99
}
flags = [ ifexists nofail ]
}
{ name = libpipewire-module-protocol-native }
{ name = libpipewire-module-client-node }
{ name = libpipewire-module-adapter }
{ name = libpipewire-module-metadata }
{ name = libpipewire-module-protocol-pulse
args = {
}
}
]
pulse.properties = {
server.address = [
"unix:native"
]
pulse.min.req = 64/48000
pulse.default.req = 64/48000
pulse.min.quantum = 64/48000
vm.overrides = {
pulse.min.quantum = 1024/48000
}
}
Restarting Pipewire
systemctl --user restart wireplumber pipewire pipewire-pulse
This script is distributed under the GNU GPL3 License. See the LICENSE file for more details.
Inspired by the "Party Up" feature of the Ultimate Ears mobile application, this script brings similar functionality to Linux users.
It is possible to use the Party Up under Linux with the following steps:
- on your smartphone, place the two speakers in Party Up using the Ultimate Ears application.
- under Linux, connect to the main speakers (the one which does not have a full circle illuminated).
- set the main speakers to use the Bluetooth channel used by Linux.
- Done!
One problem is to be forced to use the application.
But the main problem is that the Party Up mode does not work correctly for my two Hyperboom speakers. It seems to be buggy in stereo mode.
When using Party Up stereo mode on my two Hyperboom speakers, only one channel is used, making it a mono mode which ignores one channel.
graph LR;
L((Left)) --> BT1IN_FL;
L --> BT1IN_FR;
R((Right)) --x BT1;
subgraph BT1[Hyperboom #1]
BT1IN_FL[Left channel];
BT1IN_FR[Right channel];
BT1IN_FL --> OL1[\Left\];
BT1IN_FR --> OL2[/Left/];
end
BT1IN_FL --> BT2IN_FL;
BT1IN_FL --> BT2IN_FR;
subgraph BT2[Hyperboom #2]
BT2IN_FL[Left channel];
BT2IN_FR[Right channel];
BT2IN_FL --> OR1[\Left\];
BT2IN_FR --> OR2[/Left/];
end
The Party Up duo mode is not really helpful to use my Hyperboom speakers as a stereo set.
graph LR;
L((Left)) --> BT1IN_FL;
R((Right)) --> BT1IN_FR;
subgraph BT1[Hyperboom #1]
BT1IN_FL[Left channel];
BT1IN_FR[Right channel];
BT1IN_FL --> OL1[\Left\];
BT1IN_FR --> OL2[/Right/];
end
BT1IN_FL --> BT2IN_FL;
BT1IN_FR --> BT2IN_FR;
subgraph BT2[Hyperboom #2]
BT2IN_FL[Left channel];
BT2IN_FR[Right channel];
BT2IN_FL --> OR1[\Left\];
BT2IN_FR --> OR2[/Right/];
end
graph LR;
L((Left))-->VSIN1;
R((Right))-->VSIN2;
subgraph VS[Virtual sink]
VSIN1[playback_1] --> VSOUT1[capture_1];
VSIN2[playback_2] --> VSOUT2[capture_2];
end
VSOUT1 --> BT1IN_FL;
VSOUT1 --> BT1IN_FR;
subgraph BT1[Hyperboom #1]
BT1IN_FL[capture_FL];
BT1IN_FR[capture_FR];
BT1IN_FL --> OL1[\Left\];
BT1IN_FR --> OL2[/Left/];
end
VSOUT2 --> BT2IN_FL;
VSOUT2 --> BT2IN_FR;
subgraph BT2[Hyperboom #2]
BT2IN_FL[capture_FL];
BT2IN_FR[capture_FR];
BT2IN_FL --> OR1[\Right\];
BT2IN_FR --> OR2[/Right/];
end