Pull to refresh

Bluetooth stack modifications to improve audio quality on headphones without AAC, aptX, or LDAC codecs

Reading time 7 min
Views 65K
Before reading this article, it is recommended to read the previous one: Audio over Bluetooth: most detailed information about profiles, codecs, and devices / по-русски

Some wireless headphone users note low sound quality and lack of high frequencies when using the standard Bluetooth SBC codec, which is supported by all headphones and other Bluetooth audio devices. A common recommendation to get better sound quality is to buy devices and headphones with aptX or LDAC codecs support. These codecs require licensing fees, that's why devices with them are more expensive.

It turns out that the low quality of SBC is caused by artificial limitations of all current Bluetooth stacks and headphones' configuration, and this limitation can be circumvented on any existing device with software modification only.

SBC codec

SBC has lots of different parameters that are negotiated during the connection setup phase:

  • Audio channel type and number: Joint Stereo, Stereo, Dual Channel, Mono;
  • Number of frequency bands: 4 or 8;
  • Number of audio blocks in one packet: 4, 8, 12, 16;
  • Quantization bit allocation algorithm: Loudness, SNR;
  • Maximum and minimum bit pool used in quantization process: usually 2-53.

The decoder is required to support any combination of these parameters. Encoder may implement only a part of them.
Existing Bluetooth stacks usually negotiate the following set of options which I call «profile»: Joint Stereo, 8 bands, 16 blocks, Loudness, bitpool 2..53. This profile encodes 44.1 kHz stereo audio with 328 kbps bitrate.
Bitpool is a parameter that changes encoding bitrate: the higher it is, the higher the bitrate, and hence the quality. But exact bitpool value corresponds to exact bitrate only within exact profile.
The bitrate is also significantly affected by other parameters: audio channel type, number of frequency bands, number of audio blocks. You can increase the bitrate indirectly by negotiating non-standard profiles, without changing the bitpool.

$ bitrate = \frac{8 \times frame\_length \times sample\_rate}{subbands \times blocks} $

Bitrate calculation formula

For example, Dual Channel mode encodes channels separately, using individual bitpool for each channel, unlike Stereo or Joint Stereo, which use bitpool for both channels. Forcing the device to use Dual Channel instead of Joint Stereo will get us almost doubled bitrate of 617 kbps, with the same bitpool value of 53.
To me it feels that bitpool should be an internal variable. I assume that it is an A2DP specification design fault that bitpool value is not bound to other codec parameters and only defined as an independent negotiated variable.

Fixed bitpool and bitrate values originated from recommended profile for high quality audio. But the recommendation should not be the reason to set the limit on these parameters.

Bluetooth SBC profiles table

A2DP specification v1.2, which was active from 2007 to 2015, requires all decoders to work correctly with bitrates up to 512 kbps:

The decoder of the SNK shall support all possible bitpool values that do not result in excess of the maximum bit rate. This profile limits the available maximum bit rate to 320kb/s for mono, and 512kb/s for two-channel modes.

No bitrate limit stated in the new version of the specification. It is assumed that modern headphones with EDR support released after 2015 can support bitrates up to 730 kbps.

For some reason, all currently tested Bluetooth stacks (Linux (PulseAudio), Android, Blackberry and macOS) have artificial restrictions of maximum bitpool parameter, which directly affects the maximum bitrate. But this is not the biggest problem, almost all headphones also limit the maximum bitpool value to 53.
As I've already seen in my tests, most devices work fine on a modified Bluetooth stack with a bitrate of 551 kbps, without interrupts and crackling. But such a bitrate will never be negotiated under normal conditions, with stock Bluetooth stacks.

Bluetooth stack modification

Every A2DP-compatible Bluetooth stack should support Dual Channel mode, but there's no way to force usage of this mode.

Let's add a switch to the user interface! I made patches for Android 8.1 and Android 9, which add full support for Dual Channel into the stack and into developer menu, and handle Dual Channel mode as an additional «HD Audio» codec like aptX, AAC or LDAC by adding a tick to the Bluetooth device settings Here's what it looks like:


Android 9 patch
Android 8.1 patch

This checkbox toggles Dual Channel mode which is configured to use 551 kbps for EDR 3 Mb/s devices and 452 kbps for EDR 2 Mb/s devices.

This patchset has been merged into the following alternative firmwares:
  • LineageOS 15.1 (since 31 March, 2019) and 16.0 (since May 13, 2019)
  • Resurrection Remix (since May 14, 2019)
  • crDroid (since May 13, 2019)

Where did 551 and 452 kbps come from?

Bluetooth time division technology is designed to efficiently transmit large fixed-size packets. Data transfer occurs in slots, the largest number of slots sent in one transmission is 5. There are also transfer modes using 1 or 3 slots, but not 2 or 4. You can transfer up to 679 bytes in 5 slots, at a connection speed of 2 Mbps, and up to 1021 bytes at a speed of 3 Mbps. In 3 slots maximum amount of data is 367 and 552 bytes, respectively.


If we want to transfer less data than 679 or 1021 bytes but more than 367 or 552 bytes, the transfer will still take 5 slots, and the transmission will take the same amount of time, which reduces the transmission efficiency.


44100 Hz audio encoded using SBC in Dual Channel mode with bitpool = 38, 16 blocks in a frame, 8 frequency bands, produces audio frame of 164 bytes, with 452 kb/s bitrate.
Audio payload should be encapsulated into L2CAP and AVDTP transmission protocols, which deduct 16 bytes of overhead from the audio payload.

$\begin{align*} framelength &= 4 + \frac{subbands \times channels}{2} + \\ & \begin{cases} \frac{blocks \times channels \times bitpool}{8} & \text{if mono or dual channel mode} \\ \frac{subbands+blocks \times bitpool}{8} & \text{if joint stereo mode} \\ \frac{blocks \times bitpool}{8} & \text{if stereo mode} \\ \end{cases} \end{align*}$

One 5-slot audio transmission can contain up to 4 audio frames:
679 (EDR 2 mbit/s DH5) - 4 (L2CAP) - 12 (AVDTP/RTP) - 1 (SBC header) - (164*4) = 6

A single packet transmits up to 11.7 ms of audio data, which will be transmitted in 3.75 ms, and we have 6 unused bytes left in the packet.
If you slightly raise the bitpool, 4 audio frames can no longer be packed into a single transmission. You'll have to send 3 frames at a time, which reduces transmission efficiency, reduces the amount of audio transmitted in one packet, and will increase chance for audio stutter under poor radio conditions.

551 kbps bitrate for EDR 3 Mbps was selected using the same principle: with Bitpool 47, 16 blocks per frame, 8 frequency bands, the frame size is 200 bytes, with a bit rate of 551 kbps. Single transmission can bundle up to 5 frames or 14.6 ms of music.

The algorithm for calculating all the SBC parameters is quite complicated, you can easily make a mistake if you try to calculate all of them manually, so I made an interactive calculator to help those who interested:

What is that all for?

Contrary to popular belief of aptX sound quality, in some cases it can produce worse audio quality than SBC with a standard 328k bitrate.

SBC dynamically allocates quantization bits for frequency bands, acting on a «bottom-to-top» basis. If the whole bitrate was used for the lower and middle frequencies, the upper frequencies are «cut off» (silenced).
aptX quantizes frequency bands with the same number of bits constantly, which makes it a constant bitrate codec: 352 kbps for 44.1 kHz, 384 kbps for 48 kHz. It can't «transfer bits» to frequencies that need them most. Unlike SBC, aptX will not «cut» frequencies, but will add quantization noise to them, reducing the dynamic range of audio, and sometimes introducing crackles. SBC, on the contrary, «eats the details» — discards the quietest areas.
On average, compared to SBC 328k, aptX makes less distortion in music with a wide frequency range, but on music with a narrow frequency range and a wide dynamic range SBC 328k sometimes wins.

Let us consider a special case, a piano recording. Here's a spectrogram:

Most energy locates in the 0-4 kHz frequencies, and lasts up to 10 kHz.
The spectrogram of the file aptX file looks like this:

Here is SBC 328k:

It can be seen that the SBC 328k periodically completely cut off the range above 16 kHz, and used all available bitrates for the frequency ranges below this value. However, aptX introduced more distortions into the frequency spectrum audible by the human ear, which can be seen on the subtracted original audio spectrogram from the aptX spectrogram (the brighter, the more distortion):

SBC 328k has introduced less distortion to the signal in the range from 0 to 10 kHz, and the rest frequences had been сut:

485k bitrate was enough for SBC to save the entire frequency range, without cutting off the bands.

SBC 485k produces much better results in the range of 0-15 kHz on this sample than aptX, and a smaller but still noticeable difference at 15-22 kHz (the darker, the less distortion):

Archive with original audio and SBC/aptX-encoded files.

By switching to a high-bitrate SBC you will get sound which is superior to aptX most of the time, on any headphones. On headphones with EDR 3 Mb/s support, 551 kb/s SBC produces sound that is very close to aptX HD.

Can we go even further?

Android patchset has an additional option to increase bitrate for EDR 2 mbps devices even further. You can bump the bitrate from 452 kbps to 595 kbps, at the cost of reducing the stability of the transmission in case of congested radio conditions.
Just set the persist.bluetooth.sbc_hd_higher_bitrate variable to 1:
# setprop persist.bluetooth.sbc_hd_higher_bitrate 1

Extreme bit rate patch is currently merged only in LineageOS 15.1, but not in 16.0.

Compatibility with the devices

SBC Dual Channel is supported by almost all headphones, speakers and car head units. This is no wonder — the standard mandates its support in any decoding devices. There are a small number of devices on which this mode causes problems, but these are very rare cases.
More details on compatible devices can be found at 4pda and xda-developers.

Sound difference comparison

I made a web service that encodes audio to SBC (as well as to aptX and aptX HD) in real time, right in the browser. You can compare the sound of different SBC profiles and other codecs without actually transmitting audio via Bluetooth using this service, on any wired headphones, speakers, and on your favorite music. You can also change the encoding parameters directly during audio playback.

Contacting Android developers

I tried to contact many Bluetooth stack developers from Google, asking them to consider including my patches to the main Android branch—AOSP, but did not receive a single answer. My patches in Gerrit code review system for Android have not received any comments from anyone involved in the development as well.
I would be glad if anyone could tell Google developers about this implementation of SBC HD for Android. The gerrit patchset is already out of date (this is one of the earliest revisions), but I will update it if developers are interested in my changes (it's not easy for me to update it, I don't have Android Q compatible devices).


Users of LineageOS, Resurrection Remix and crDroid firmwares can enhance Bluetooth audio quality by ticking a checkbox in Bluetooth device settings. Linux users can also get a higher SBC bitrate by installing the patch from Pali Rohár, which among other things, adds support for the aptX, aptX HD and FastStream codecs.
Comments 3
Comments Comments 3