Solving Bluetooth Audio Delay on Ubuntu 20.04

For quite some time I’ve been frustrated at the state of Ubuntu’s support for Bluetooth audio. As a result, I’ve always gone with wired headphones. Now maybe its something about me but I’ve not had the best of luck with this. Headphones last a few months before some plug or wire breaks. In the worst case scenario the headphone jack on my laptop starts giving issues… so wireless is great. If it works.

I had to replace my headset recently and bought a HAVIT H2590BT, a fairly entry-level thing, and it worked fine for listening to music but as soon as I wanted more “real time” audio there were problems. I first noticed this on Duolingo and a bit of debugging showed that the problem was a delay in the audio. This became rather embarasing when doing a call with colleagues.

Turns out that Bluetooth has audio profiles that affect the operation of the headset. A2DP focuses on giving best audio quality, whereas HFP and HSP are more focused on real-time responsiveness. Unfortunately with the standard PulseAudio (13.99.1) on my Ubuntu 20.04 I could only connect to the headphones using A2DP. I came across posts on askubuntu.com from 2015 onwards talking about this issue and some suggested switching profiles but I couldn’t seem to get that right.

Then I found this post from @normankev141. Unfortunately the plugin that he suggested has been deprecated by the author, who suggested moving to PipeWire. I switched to PipeWire using the instructions from this askubuntu post, rebooted and now I’ve got a much richer selection of profiles:

$ pactl list cards
[...]
Card #54
Name: bluez_card.C5_78_21_3A_9F_DB
Driver: module-bluez5-device.c
Owner Module: n/a
[...]
Profiles:
    off: Off (sinks: 0, sources: 0, priority: 0, available: yes)
    a2dp-sink: High Fidelity Playback (A2DP Sink) (sinks: 1, sources: 0, priority: 0, available: yes)
    headset-head-unit: Headset Head Unit (HSP/HFP) (sinks: 1, sources: 1, priority: 0, available: yes)
    a2dp-sink-sbc: High Fidelity Playback (A2DP Sink, codec SBC) (sinks: 1, sources: 0, priority: 0, available: yes)
    headset-head-unit-cvsd: Headset Head Unit (HSP/HFP, codec CVSD) (sinks: 1, sources: 1, priority: 0,    available: yes)
Active Profile: a2dp-sink-sbc

I can now switch profiles with pactl set-card-profile bluez_card.C5_78_21_3A_9F_DB a2dp-sink or pactl set-card-profile bluez_card.C5_78_21_3A_9F_DB headset-head-unit. I’ve made these two aliases for my shell:

alias goodaudio="pactl set-card-profile $(pactl list cards |grep 'Name: bluez' |awk '{print $2}') a2dp-sink"
alias headset="pactl set-card-profile $(pactl list cards |grep 'Name: bluez' |awk '{print $2}') headset-head-unit"

I haven’t yet got around to linking these to some kind of Gnome utility so that I can toggle the profiles yet. Its on the TODO list.