Skip to content

Instantly share code, notes, and snippets.

@peters
Last active November 17, 2024 14:22
Show Gist options
  • Save peters/26315cd7a8a31e3d192ed05ef9a79ba7 to your computer and use it in GitHub Desktop.
Save peters/26315cd7a8a31e3d192ed05ef9a79ba7 to your computer and use it in GitHub Desktop.
Connecting Sony WH-1000XM4 to Ubuntu 22.04 (Jammy)

Connecting Sony WH-1000XM4 to Ubuntu 22.04 (Jammy)

This guide will help you connect your Sony WH-1000XM4 headset to Ubuntu 22.04 using Bluetooth. Once connected, you'll be able to listen to music and use the microphone on apps like Microsoft Teams.

1. Connect to Bluetooth Headset

Steps:

  1. Open a terminal and launch bluetoothctl:

    bluetoothctl
  2. Initialize the agent and set it as the default:

    agent on
    default-agent
    
  3. Scan for Bluetooth devices:

    scan on
    

    Look for the MAC address of your Sony WH-1000XM4 headset.

  4. Stop the scanning process:

    scan off
    
  5. Pair, trust, and connect to the headset using its MAC address:

    pair XX:XX:XX:XX:XX:XX
    trust XX:XX:XX:XX:XX:XX
    connect XX:XX:XX:XX:XX:XX
    

    (Replace XX:XX:XX:XX:XX:XX with the MAC address of your headset.)

  6. Exit bluetoothctl:

    exit
    

2. Enable PipeWire for Better Audio Handling

Ubuntu 22.04 partially uses PipeWire by default. To fully utilize it for audio and Bluetooth, follow these steps:

Installation:

  1. Install and start WirePlumber:
    sudo apt install pipewire-media-session- wireplumber
    systemctl --user --now enable wireplumber.service

Configuration:

  • For ALSA:

    sudo apt install pipewire-audio-client-libraries
    sudo cp /usr/share/doc/pipewire/examples/alsa.conf.d/99-pipewire-default.conf /etc/alsa/conf.d/
  • For Bluetooth:

    sudo apt install libldacbt-{abr,enc}2 libspa-0.2-bluetooth pulseaudio-module-bluetooth-

Validation:

After rebooting your system, you can check if PipeWire is active by running:

LANG=C pactl info | grep '^Server Name'

Switch Audio Profile using CLI

image

mkdir -p ~/.audio/bin && cd ~/.audio/bin
wget https://gist.githubusercontent.com/peters/26315cd7a8a31e3d192ed05ef9a79ba7/raw/9466f91da19b53d01cac862898cfdfcb18e3137e/wh1000xm4-audio-profile.sh
chmod +x wh1000xm4-audio-profile.sh
./wh1000xm4-audio-profile.sh

For microphone testing, click here to visit the microphone test site

#!/bin/bash
# Device alias to match against
DEVICE_ALIAS="WH-1000XM4"
# Fetch the card name for the device
CARD_NAME=$(pactl list cards | awk '/Name:/ {name=$2} /device.alias = "WH-1000XM4"/ {print name}')
# If the card name is empty, exit
if [ -z "$CARD_NAME" ]; then
echo "Unable to find the card for device $DEVICE_ALIAS"
exit 1
fi
# Retrieve all supported profiles for the card
SUPPORTED_PROFILES=$(pactl list cards | awk -v card="$CARD_NAME" '/Name: / {if ($2 == card) {f=1} else {f=0}} f && /Profiles:/, /Active Profile:/ {if (!/Profiles:/ && !/Active Profile:/) print $1}')
# Declare an associative array for profiles
declare -A PROFILES
PROFILES["a2dp-sink"]="High Quality Audio Only"
PROFILES["headset-head-unit-cvsd"]="Mono audio / mic"
# Create an array of profiles that we're interested in and are supported by the device
FILTERED_PROFILES=()
for profile in "${!PROFILES[@]}"; do
if echo "$SUPPORTED_PROFILES" | grep -q "^${profile}:"; then
FILTERED_PROFILES+=("$profile (${PROFILES[$profile]})")
fi
done
if [ ${#FILTERED_PROFILES[@]} -eq 0 ]; then
echo "None of the desired profiles are supported by device $DEVICE_ALIAS"
exit 1
fi
# Display the profiles and prompt the user to choose
echo "Available profiles for $DEVICE_ALIAS:"
select opt in "${FILTERED_PROFILES[@]}"; do
for profile in "${!PROFILES[@]}"; do
if [[ $opt == "$profile (${PROFILES[$profile]})" ]]; then
pactl set-card-profile "$CARD_NAME" "$profile"
echo "Switched to profile: $opt"
exit 0
fi
done
echo "Invalid option."
done
@bugs84
Copy link

bugs84 commented Jul 10, 2024

Nice job thanks!

Just note, that profile
"headset-head-unit-msbc" (if available)
will give you better quality, than
"headset-head-unit-cvsd"

@mrk-te
Copy link

mrk-te commented Oct 16, 2024

On Ubuntu 24.04, pipewire seems to be used out of the box.

I improved the provided script to fully use the DEVICE_ALIAS variable value and get it work regardless of your system's locale for a WF-1000XM5 headset.

#!/bin/bash

# Device alias to match against
DEVICE_ALIAS="WF-1000XM5"

# Fetch the card name for the device
CARD_NAME=$(LANG=C pactl list cards | awk -v alias="$DEVICE_ALIAS" '/Name:/ {name=$2} $0 ~ "device.alias = \""alias"\"" {print name}')

# If the card name is empty, exit
if [ -z "$CARD_NAME" ]; then
    echo "Unable to find the card for device $DEVICE_ALIAS"
    exit 1
fi

# Retrieve all supported profiles for the card
SUPPORTED_PROFILES=$(LANG=C pactl list cards | awk -v card="$CARD_NAME" '/Name: / {if ($2 == card) {f=1} else {f=0}} f && /Profiles:/, /Active Profile:/ {if (!/Profiles:/ && !/Active Profile:/) print $1}')

# Declare an associative array for profiles
declare -A PROFILES
PROFILES["a2dp-sink"]="High Quality Audio Only"
PROFILES["headset-head-unit-msbc"]="Mono audio / mic MSBC"
PROFILES["headset-head-unit-cvsd"]="Mono audio / mic CVSD"

# Create an array of profiles that we're interested in and are supported by the device
FILTERED_PROFILES=()
for profile in "${!PROFILES[@]}"; do
    if echo "$SUPPORTED_PROFILES" | grep -q "^${profile}:"; then
        FILTERED_PROFILES+=("$profile (${PROFILES[$profile]})")
    fi
done

if [ ${#FILTERED_PROFILES[@]} -eq 0 ]; then
    echo "None of the desired profiles are supported by device $DEVICE_ALIAS"
    exit 1
fi

# Display the profiles and prompt the user to choose
echo "Available profiles for $DEVICE_ALIAS:"
select opt in "${FILTERED_PROFILES[@]}"; do
    for profile in "${!PROFILES[@]}"; do
        if [[ $opt == "$profile (${PROFILES[$profile]})" ]]; then
            pactl set-card-profile "$CARD_NAME" "$profile"
            echo "Switched to profile: $opt"
            exit 0
        fi
    done
    echo "Invalid option."
done

@Cyranotec
Copy link

Thank you very much

@ipeacocks
Copy link

Sometimes the OS says it connects to headphones, but the headphones themselves are not recognized, and eventually, the sound doesn't play through them. To quickly fix this, I sometimes click the "Reconnect to..." menu twice or so.

Does anyone know how to resolve this issue?

@dolikemedo
Copy link

dolikemedo commented Nov 17, 2024

Hi!
Thank for the description.
What I don't understand is why the profile changes? In A2DP in theory you would use ldac but in headset profile you get the poor quality audio BUT you would get mic. Using LDAC wouldn't allow for both?
On Android devices in theory both high quality audio and microphone usage. here chatgpt about it:
"
Android supports audio codecs like aptX and LDAC and advanced Bluetooth profiles that allow simultaneous high-quality audio playback and microphone usage. This is because Android has more flexibility in managing Bluetooth connections compared to Windows, which uses legacy drivers and profiles for compatibility.
"
edit: some chatgpting
okay so chatgpt:
The Bluetooth protocol and available profiles have bandwidth limitations. Here's why:

Bluetooth Bandwidth: Bluetooth operates in a constrained bandwidth environment (~1-3 Mbps depending on codec and connection quality).
Profile Limitations:
A2DP (Audio Streaming): Focuses on high-bitrate audio for playback, consuming most of the available bandwidth.
HFP/HSP (Microphone + Audio): Allocates bandwidth for bidirectional communication, which reduces audio playback quality to ensure reliable voice transmission.
Codec Limitations:
Codecs like LDAC, aptX-HD, and AAC optimize one-way audio quality but aren’t designed for two-way communication.
HFP with mSBC improves audio but is still limited to 16 kHz sampling, far below LDAC’s 96 kHz.

Can PipeWire Improve Both?
With PipeWire, you can balance the tradeoff more effectively:

Enable mSBC Codec in HFP:

Provides better microphone quality compared to standard HFP (8 kHz sampling).
Playback quality also improves but is still not comparable to A2DP.
Dynamic Profile Switching:

PipeWire can dynamically switch between A2DP for high-quality playback and HFP/HSP when the mic is in use.
This reduces interruptions and may provide a seamless user experience for applications like gaming or calls.

hardware limitations etc. etc.

PC Bluetooth Stack: Unlike Android, which uses customized Bluetooth implementations, Linux (and Windows) rely on generic stacks, limiting flexibility.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment