(K)Ubuntu 22.04 and Bluetooth Headphones

This article is somewhat an update of the one named Linux and Bluetooth Headphones. In that article I had tested on (K)Ubuntu 20.04, but this article is about version 22.04.

Just like in the above mentioned article, I recommend that you install and enable PipeWire. It is a lot easier to do in (K)Ubuntu 22.04. The TL;DR version would be:

First, check to see which audio server you're currently on. This command most probably will return 'pulseaudio':

$ pactl info | grep 'Server Name'
Server Name: pulseaudio

So... make sure you install PipeWire:

$ sudo apt update
$ sudo apt install pipewire
$ sudo apt install gstreamer1.0-pipewire libpipewire-0.3-{0,dev,modules} libspa-0.2-{bluetooth,dev,jack,modules} pipewire{,-{audio-client-libraries,pulse,bin,tests}}
$ sudo apt install wireplumber gir1.2-wp-0.4 libwireplumber-0.4-{0,dev}

Disable PulseAudio and enable PipeWire instead:

$ systemctl --user --now disable pulseaudio.{socket,service}
$ systemctl --user mask pulseaudio

$ sudo cp -vRa /usr/share/pipewire /etc/

$ systemctl --user --now enable pipewire{,-pulse}.{socket,service}

Now, you should see PipeWire in here:

$ pactl info | grep 'Server Name'
Server Name: PulseAudio (on PipeWire 0.3.48)

For even more details, read this excellent article.

I tested with EDIFIER NeoBuds Pro and not only that switching between A2DP and HSP/HFP works automagically but, more than that, LDAC codec also works (which previously only worked for SONY WH-1000XM3):

$ pactl list
(output omitted)
Card #5323
        Name: bluez_card.FC_E8_06_76_05_2C
        Driver: module-bluez5-device.c
        Owner Module: n/a
        Properties:
                api.bluez5.address = "FC:E8:06:76:05:2C"
                api.bluez5.class = "0x240404"
                api.bluez5.connection = "disconnected"
                api.bluez5.device = ""
                api.bluez5.icon = "audio-headset"
                api.bluez5.path = "/org/bluez/hci0/dev_FC_E8_06_76_05_2C"
                bluez5.auto-connect = "[ hfp_hf hsp_hs a2dp_sink ]"
                bluez5.profile = "off"
                device.alias = "EDIFIER NeoBuds Pro"
                device.api = "bluez5"
                device.bus = "bluetooth"
                device.description = "EDIFIER NeoBuds Pro"
                device.form_factor = "headset"
                device.name = "bluez_card.FC_E8_06_76_05_2C"
                device.string = "FC:E8:06:76:05:2C"
                media.class = "Audio/Device"
                factory.id = "14"
                client.id = "34"
                object.id = "132"
                object.serial = "5323"
        Profiles:
                off: Off (sinks: 0, sources: 0, priority: 0, available: yes)
                a2dp-sink: High Fidelity Playback (A2DP Sink) (sinks: 1, sources: 0, priority: 16, available: yes)
                headset-head-unit: Headset Head Unit (HSP/HFP) (sinks: 1, sources: 1, priority: 1, available: yes)
                a2dp-sink-sbc: High Fidelity Playback (A2DP Sink, codec SBC) (sinks: 1, sources: 0, priority: 18, available: yes)
                a2dp-sink-sbc_xq: High Fidelity Playback (A2DP Sink, codec SBC-XQ) (sinks: 1, sources: 0, priority: 17, available: yes)
                a2dp-sink-ldac: High Fidelity Playback (A2DP Sink, codec LDAC) (sinks: 1, sources: 0, priority: 19, available: yes)
                headset-head-unit-cvsd: Headset Head Unit (HSP/HFP, codec CVSD) (sinks: 1, sources: 1, priority: 2, available: yes)
                headset-head-unit-msbc: Headset Head Unit (HSP/HFP, codec mSBC) (sinks: 1, sources: 1, priority: 3, available: yes)
        Active Profile: a2dp-sink-ldac
        Ports:
                headset-input: Headset (type: Headset, priority: 0, latency offset: 0 usec, available)
                        Properties:
                                port.type = "headset"
                        Part of profile(s): headset-head-unit, headset-head-unit-cvsd, headset-head-unit-msbc
                headset-output: Headset (type: Headset, priority: 0, latency offset: 0 usec, available)
                        Properties:
                                port.type = "headset"
                        Part of profile(s): a2dp-sink, headset-head-unit, a2dp-sink-sbc, a2dp-sink-sbc_xq …
more ...

Linux and Bluetooth Headphones

For newer versions of Linux, like (K)Ubuntu 22.04 (which have PipeWire already installed or even enabled by default), please go directly to (K)Ubuntu 22.04 and Bluetooth Headphones

Connecting a set of bluetooth headphones to your Linux computer is a simple thing, if you just want to listen to music. But what about when you want to use the microphone that is, most probably, embedded in those headphones?

How I tested?

All the below information was tested on Kubuntu 20.04 with the following headphones:

How to check if your microphone is working or not?

You can test from an application that makes use of the microphone like Skype or 8x8 Meet.

For example, in this screenshot, it doesn't work. Just go to https://8x8.vc/someRandomString and click the up arrow next to the microphone symbol to see the list of available microphones your computer has. You might see the headphones (in this particular example, the EDIFIER NeoBuds Pro are conected) but only the laptop's internal microphone is detected.

the mic doesn't show up

From the command line, you can run this command:

$ pactl list

At the end of the output, you should see something like this:

Card #31
  Name: bluez_card.FC_E8_06_76_05_2C
  Driver: module-bluez5-device.c
  Owner Module: 56
  Properties:
    device.description = "EDIFIER NeoBuds Pro"
    device.string = "FC:E8:06:76:05:2C"
    device.api = "bluez"
    device.class = "sound"
    device.bus = "bluetooth"
    device.form_factor = "headset"
    bluez.path = "/org/bluez/hci0/dev_FC_E8_06_76_05_2C"
    bluez.class = "0x240404"
    bluez.alias = "EDIFIER NeoBuds Pro"
    device.icon_name = "audio-headset-bluetooth"
    device.intended_roles = "phone"
  Profiles:
    a2dp_sink: High Fidelity Playback (A2DP Sink) (sinks: 1, sources: 0, priority: 40, available: yes)
    headset_head_unit: Headset Head Unit (HSP/HFP) (sinks: 1, sources: 1, priority: 30, available: no)
    off: Off (sinks: 0, sources: 0, priority: 0, available: yes)
  Active Profile: a2dp_sink
  Ports:
    headset-output: Headset (priority: 0, latency offset: 0 usec, available)
      Part of profile(s): a2dp_sink, headset_head_unit
    headset-input: Headset (priority: 0, latency offset: 0 usec, not available)
      Part of profile(s): headset_head_unit

Notice the Profiles section and the fact that the profile headset_head_unit is not available and, also, that the Active Profile is set to a2dp_sink.

The easy way: making HSP work

This only works for JBL TUNE205BT.

This is easy, as it doesn't require any hacks or additional software. Just edit the file /etc/pulse/default.pa and add auto_switch=2 to the line load-module module-bluetooth-discover. In the end, the file should look like this in that zone:

### Automatically load driver modules for Bluetooth hardware
.ifexists module-bluetooth-policy.so
#load-module module-bluetooth-policy
load-module module-bluetooth-policy auto_switch=2
.endif

Restart bluetooth and kill pulseaudio (it should respawn by itself):

$ sudo systemctl restart bluetooth
$ pulseaudio -k

Now, not only that the HSP profile will appear as available (notice the Profiles section and, also, the Active Profile)...

$ pactl list
Card #32
  Name: bluez_card.B8_F6_53_03_D4_1C
  Driver: module-bluez5-device.c
  Owner Module: 57
  Properties:
    device.description = "JBL TUNE205BT"
    device.string = "B8:F6:53:03:D4:1C"
    device.api = "bluez"
    device.class = "sound"
    device.bus = "bluetooth …
more ...