Why does pulseaudio handle the sound quality connection to a BT device

bluetoothbluezpulseaudio

I'm new to Linux and new to Bluetooth scripting too. What I have discovered so far is the following:

  1. Bluez is the default BT stack.
  2. BluezTools is a set of utilities you can use to more easily interact with Bluez
  3. DBus is the interface Bluez connects to while interacting directly with the hardware.
  4. PulseAudio is the subsystem responsible for producing audio on the system.

This makes sense. So let's say I have a set of BlueTooth headphones, what I would expect is (all after pairing and trusting), to be able to issue a command that can connect directly to a certain profile on the BT headphones.

The technical path I have in mind would be something like :

  1. Turn headphones on.
  2. Issue a BluezTools command – such as bt-audio -c
  3. Wait for the device to connect to the service I am after
  4. PulseAudio should now pick up a new output device
  5. Issue another command to change the audio from what it was to new output audio (the BT headphones).
  6. Enjoy a seamless listening experience.

This all seems logical, but the actual implementation is not like this, and I am looking for the WHY, so I can better understand the problem and try and fix it.

This is what actually happens:

  1. Turn headphones on.
  2. Issue a BluezTools command – such as bt-audio -c
  3. Wait for the device to connect to the service I am after
  4. PulseAudio should now pick up a new output device
  5. Issue a PulseAudio command to change the audio profile from telephone quality to high fidelity.

Let me expand on this a bit. The bluetooth headset offers 2 quality modes (telephone and high fidelity). Only 1 is really suitable for listening to music.

I would expect that the BT headphones expose each quality mode as a service, is this right? This assumption could be wrong, but I would expect something like

bt-audio -c highFidelityProfile

or

bt-audio -changeProfile highFidelityProfile

Instead it seems that Bluez just handles the RAW connection to the device, and from there you need to issue a : pacmd set-card-profile $INDEX a2dp

This just seems fundamentally wrong. Why is the quality control in the audio subsystem, hence requiring a different implementation for pulse or alsa, or any other sound sub system out there?

What am I missing? Why is it not possible to connect directly to a certain profile using Bluez / BluezTools etc?

Best Answer

A bluetooth connection has significant latency compared to simple wired headphones or speakers. What's more, the connection latency can vary, depending on the properties of the bluetooth receiver and maybe even radio signal strength as the user moves around.

The interface between an application and PulseAudio can be as simple as "here's some PCM audio data; play this." But it can also be more complicated; something like "Here's some PCM audio data; play this and tell me every 50 ms how far you've got, so that I can tell you to skip ahead if it looks like you're falling out of lip-synch with the video stream I'm playing. Oh, and you'll need to resample it too, since the data has a sample rate your hardware won't directly support." In the latter case, PulseAudio needs to be able to give the application some feedback from the audio device to correctly determine how far the audio data is actually been played at any given time.

As a result, it makes sense for PulseAudio to be fairly deeply involved in Bluetooth audio processing: the more intervening layers there are, the more possibilities for data to be buffered without maintaining accurate feedback, causing lip-synch to be lost.

In fact, before PulseAudio existed, there used to be an ALSA backend for Bluetooth audio, but it was deprecated. I think the problem was that ALSA's interfaces at that time were designed mainly for traditional sound cards, and dealing with a potentially variable audio latency of Bluetooth was difficult.

PulseAudio's interfaces were designed from the ground up to handle various sound devices and even switching audio streams between them while the stream is playing, so it seems to me it has a pretty advanced concept of audio latency built-in too.

Yes, it could have been implemented in BlueZ rather than as a PulseAudio module; but then, BlueZ would have had to present an audio interface for the applications. And since PulseAudio wants to handle "all" the audio on a system (in order to be able to transfer the audio you've currently playing from speakers to Bluetooth or vice versa on-the-fly), it would have to interface with PulseAudio somehow anyway.

Related Question