Writ

A2DP Bluetooth

2021-12-30

I’ve used a series of Sony noise-cancelling headsets over the years, and consistently have issues getting pulseaudio to connect them as a2dp. They always seem to connect with the phone-quality audio, and need to be coerced to full quality.

I made a post on Stackexchange about this a few years back, but completely forgot about it, and wanted to add more substance here.

Availability

The first issue can be found by doing pacmd list-cards, and seeing if the a2dp profile (not sink) is "available". If you see:

a2dp_sink: High Fidelity Playback (A2DP Sink) (priority 40, available: no)

Then your headset is probably sneakily connected to something else which is using its high fidelity profile. In my case, I have a bluetooth transceiver attached to my TV, which had activated without my noticing and prevented me from using a2dp on my laptop. Android tends to be able to override this, but i don’t care enough to find out how on Linux. The point is, make sure it’s available.

Script

The ultimate issue is that your OS needs to select the a2dp profile on the headset for output, and it doesn’t always do this. In Mint, the audio settings doesn’t give feedback on if it worked or not, when you try manually.

This script is borne of frustration with each step (connection, receiving profiles, etc) being a bit flaky. Once you’re sure your device is paired and not connected to other devices, use this.

usage headset.sh <mac:address:of:your:headset>

The only modification that may be wise is to hardcode your headset’s MAC (discoverable through the bluetooth menu, among other places) into the first line (MAC=), instead of passing it in each time.

#!/bin/bash

MAC=$1
IDPA=$(echo "${MAC}" | sed -e 's/:/_/g')
CARD="bluez_card.${IDPA}"
SINK="bluez_sink.${IDPA}"

$(pactl list | grep -qi 'Active Profile: a2dp_sink')
a2dpUsed=$?

# may need to restart bluetooth multiple times to "unstick" it. Not sure why.
# this loops until a2dp is _actually used_ on the given MAC.
while [ ${a2dpUsed} -ne 0 ];
do
    #
    echo "Restarting bluetooth."
    rfkill unblock bluetooth
    sudo service bluetooth restart
    sudo hciconfig hci0 up

    # reconnect
    echo -e "power on\nconnect ${MAC}" | bluetoothctl

    #
    echo "Waiting for headset to be connected..."
    btConnected=1

    while [ ${btConnected} -gt 0 ];
    do

        sleep .1
        $(bluetoothctl <<< "info ${MAC}" | grep -qi "Connected: yes")
        btConnected=$?
    done

    #
    echo "Bluetooth connected, waiting for profiles to register"

    cardFound=1
    while [ ${cardFound} -ne 0 ];
    do
        $(pactl list | grep -qi "${CARD}")
        cardFound=$?
    done

    #
    echo "Setting bluetooth a2dp profile"
    pactl set-card-profile ${CARD} a2dp_sink
    $(pactl list | grep -qi 'Active Profile: a2dp_sink')
    a2dpUsed=$?
done

echo "a2dp is working."

pacmd set-default-sink ${SINK}

inputs=($(pacmd list-sink-inputs | grep index | awk '{print $2}'))
for i in ${inputs[*]};
do
	pacmd move-sink-input ${i} ${SINK} &> /dev/null
done

echo "default device changed to a2dp device."
All site content protected by CC-BY-4.0 license