Full setup guide to enable automatic communication mode (noise suppression, voice focus, chime) on Linux with PipeWire/WirePlumber.
- Ubuntu 24.04 (Noble) with PipeWire 1.0.5 and WirePlumber 0.4.17
- Bose QC Ultra 2 Earbuds paired via Bluetooth
- BlueZ 5.x
# Enable deb-src in /etc/apt/sources.list.d/ubuntu.sources
# Change "Types: deb" to "Types: deb deb-src" on both stanzas
sudo apt update
sudo apt build-dep pipewire
cd /tmp && apt source pipewireFind the } else if (!mm_is_available(backend->modemmanager)) { line (around line 1111) and add these handlers immediately before it:
} else if (spa_strstartswith(buf, "AT+NREC=")) {
rfcomm_send_reply(rfcomm, "OK");
} else if (spa_strstartswith(buf, "AT+BTRH?")) {
rfcomm_send_reply(rfcomm, "OK");
} else if (spa_strstartswith(buf, "AT+CCWA=")) {
rfcomm_send_reply(rfcomm, "OK");
} else if (!mm_is_available(backend->modemmanager)) {What this does:
AT+NREC=0: Earbuds say "I'll handle noise reduction." Responding OK tells them to keep their DSP active. Without this, the earbuds send raw unprocessed mic audio.AT+BTRH?: Response and Hold status query. OK means "no held calls."AT+CCWA=1: Call Waiting enable. OK acknowledges the request.
All three are part of the HFP Service Level Connection setup. Without OK responses, the handshake is incomplete.
In sco_acquire_cb, replace:
if (optional || t->fd > 0)
sock = t->fd;
else
sock = sco_do_connect(t);With:
if (optional || t->fd > 0) {
sock = t->fd;
} else {
#ifdef HAVE_BLUEZ_5_BACKEND_HFP_NATIVE
if (td->rfcomm && td->rfcomm->slc_configured &&
t->profile == SPA_BT_PROFILE_HFP_HF) {
rfcomm_send_reply(td->rfcomm, "+CIEV: 3,1");
rfcomm_send_reply(td->rfcomm, "RING");
}
#endif
sock = sco_do_connect(t);
}This sends an incoming-call indicator and RING just before the SCO link opens, which makes the earbuds play the "entering call" chime.
In sco_release_cb, after the existing rfcomm_hfp_ag_set_cind(td->rfcomm, false); line, add:
if (td->rfcomm && td->rfcomm->slc_configured &&
t->profile == SPA_BT_PROFILE_HFP_HF) {
rfcomm_send_reply(td->rfcomm, "+CIEV: 3,0");
}This resets the call setup indicator when switching back to A2DP.
cd /tmp/pipewire-1.0.5
meson setup build
ninja -C build spa/plugins/bluez5/libspa-bluez5.so
sudo cp build/spa/plugins/bluez5/libspa-bluez5.so /usr/lib/x86_64-linux-gnu/spa-0.2/bluez5/libspa-bluez5.so
systemctl --user restart pipewire wireplumberIn GNOME Settings > Sound, set the Bose earbuds as the default output device. This is required for WirePlumber's built-in auto-switch policy to work.
WirePlumber monitors audio streams with media.role = "Communication" or from known applications (Chrome, Firefox, Zoom, Telegram, etc.). When such a stream opens, it automatically switches from A2DP to HFP. When the stream stops, it restores A2DP after 2 seconds.
| Change | Effect |
|---|---|
AT+NREC=0 -> OK |
Earbuds keep their noise reduction DSP active |
AT+BTRH? / AT+CCWA=1 -> OK |
Completes HFP handshake without a real modem |
| RING on SCO acquire | Earbuds play chime when switching to HFP |
| +CIEV:3,0 on SCO release | Clean call-ended state when switching back to A2DP |
| Default sink = Bluetooth | WirePlumber auto-switches profiles for communication apps |
After setup, connect the earbuds and open a voice app (Google Meet, Zoom). You should hear:
- The chime when the app starts using the mic (A2DP -> HFP switch)
- Good voice quality with noise suppression active
- Return to high-quality A2DP audio when the call ends
- The patch only builds
libspa-bluez5.so(the bluetooth SPA plugin), not all of PipeWire - System package updates to
libspa-0.2-bluetoothwill overwrite the patched library -- you'll need to rebuild after updates - No changes to BlueZ config, D-Bus policies, or WirePlumber lua scripts are required