Warning: This is a highly advanced guide that involves modifying your Bluetooth stacks on Android – read this guide in its entirety and follow all instructions exactly as given.
Despite the fact that Bluetooth headsets and Bluetooth audio has become quite popular, it’s a bit of an issue for audiophiles because Bluetooth has been proven to reduce audio quality, as bits of the audio information and frequencies get lost in the air over Bluetooth streaming.
This is why some manufacturers are putting out aptX and LDAC codecs, to enhance the sound quality over the standard SBC Bluetooth codec which is supported by all headphones and most Bluetooth devices – however, devices with aptX and LDAC codecs are much more expensive because these codecs require licensing fees, which the consumer pays in the long run.
The low audio quality of the SBC Bluetooth codec is caused by artificial limitations of all current Bluetooth stacks and headphones’ configuration, and this limitation can be circumvented on any existing devices.
If you’re interested in Bluetooth audio, we’ll show you at the end of this guide how to take a Bluetooth audio log dump and inspect it to see what kind of audio quality and frequency you’re getting from your Android’s Bluetooth receiver.
The majority of this guide will focus on a few simple tweaks and ways to read your Bluetooth audio output to greatly enhance the output quality of standard SBC Bluetooth codecs – please read this entire guide carefully as its quite educational and there is a lot of different stuff to flash or tweak, depending on your device model.
At this end of this guide is a list of pre-patched Bluetooth stacks for a lot of popular Android devices – these can be flashed in recovery as you would any other flashable .zip – if none of the devices belong to you, you will have to follow the guide for modifying Bluetooth stacks on Android.
Short technical information about 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 profile: Joint Stereo, 8 bands, 16 blocks, Loudness, bitpool 2..53. This profile encodes 44.1 kHz audio with a bitrate of 328 kbps.
Bitpool parameter directly affects the bitrate within the same profile: the higher it is, the higher the bitrate, and hence the quality.
However, the bitpool parameter is not bound to a specific 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.
For example, Dual Channel encodes channels separately, using the entire bitpool for each channel. Forcing the device to use Dual Channel instead of Joint Stereo will get us almost doubled bitrate at the same maximum bitpool, 617 kbps.
To me it feels that bitpool should be an internal variable. It is an A2DP specification design fault that bitpool value is not bound to other codec parameters and only defined as a global value.
These fixed Bitpool and Bitrate values originate from recommended values for high-quality audio. But the recommendation is not an excuse to limit the profile to these values.
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 the 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.
In the new version of the specification there is no bitrate limitation. It is assumed that modern headphones released after 2015 can support bitrates up to 1000 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.
Most devices work fine on a modified Bluetooth stack with a bitrate of 507 kbps, without interrupts and crackling. But such a bitrate will never be negotiated under normal conditions, with stock Bluetooth stacks.
***Required for Testing Using Guides Below: bluetooth-dualchannel-test-ubuntu-18.04.1-desktop-amd64.iso.torrent
How to test on a PC
High bitrate SBC headphone compatibility test is the easiest to perform on the PC with a Bluetooth adapter. I’ve prepared Ubuntu image with a modified Bluetooth stack, which can be run as in a virtual machine (by connecting Bluetooth adapter as a USB device inside the virtual machine, it also works with the adapters built into the laptops) or by booting from the USB flash drive. This image uses the following profile: Dual Channel, 8 bands, 16 blocks, Loudness, bitpool 2..41, 44.1 kHz, which provides 485 kbps bitrate.
Running in a VM
- Download Virtualbox and Virtualbox Extension Pack: https://www.virtualbox.org/wiki/Downloads;
- Install Virtualbox, start it;
- Install Extension Pack using File → Preferences → Extensions;
- Create new virtual machine: Linux, Ubuntu (64-bit), 1024 RAM. Do not create a HDD.
- Navigate to virtual machine settings, in Storage choose Controller: IDE, Empty, press CD icon → Choose virtual optical disk file;
- Select downloaded bluetooth-dualchannel-test-ubuntu-18.04.1-desktop-amd64.iso;
- Save and close settings window, start virtual machine;
- Right-click USB cable icon in the bottom right, select your Bluetooth adapter;
Running on a PC
The image supports BIOS/CSM and UEFI booting.
- Burn the image to a USB flash drive using Etcher: https://www.balena.io/etcher. This operation will delete all existing files on a USB drive.
- Turn off the PC;
- Insert USB flash drive, turn on the PC and press boot order button (usually Esc or F12);
- Select your USB flash drive.
Performing the test
- (optional but recommended) Double click on “Btsnoop Dump” script on the desktop. It will start Bluetooth data capture for later analysis. Do not close terminal window.
- Switch the headphones to pairing mode;
- Click to the arrow in top right corner, select Bluetooth icon → Bluetooth Settings;
- Choose your headphones, wait until pairing is complete and close the window;
- Set Ubuntu volume to about 2/3. Also decrease volume using headset buttons as it could be very loud after pairing.
- Open “music” folder, play “testrecord1.flac”;
- (optional but recommended) Close player, close terminal window. This will stop data capture.
- (optional but recommended) Open Firefox browser, upload data dump (btsnoop_hci.btsnoop on the desktop) to https://btcodecs.valdikss.org.ru/
You can listen to other music in the music folder, or upload your own;
There should be no cracklings, audio interruption or other sound distortion in the headphones. If you hear a good high-quality sound, that means your headphones support audio with a bit rate of 485 kbps.
How to test on Android device
In order to test from Android smartphone or tablet you need to use modified Bluetooth stack, which requires root privilege.
How to capture Bluetooth data dump on Android
- Turn off Bluetooth;
- In Developer Settings, enable the “Enable Bluetooth HCI snoop log” switch;
- Turn on Bluetooth, connect to your headset using Bluetooth menu (this is important! Do not allow auto connection!);
- Play short audio sample;
- Open developer settings, disable the “Enable Bluetooth HCI snoop log” switch;
- There should be /storage/emulated/0/btsnoop_hci.log or /data/misc/bluetooth/logs/btsnoop_hci.log created. If it’s missing, open /etc/bluetooth/bt_stack.conf with a text editor and see the path in BtSnoopFileName option.
There should be no cracklings, audio interruption or other sound distortion in the headphones. If you hear a good high-quality sound with the patched library, that means your headphones support audio with a bit rate of 512 kbps.
Please carefully follow the algorithm above. Especially, if you power off the headphones or disconnect after pairing, it’s important to connect to the headphones manually from the Bluetooth settings, do not allow auto connection!
Devices which support at least 512 kbit/s SBC
- 1MORE iBFree
- JBL Everest 310
- JBL Everest 700
- Skullcandy HESH 3
- Sony WI-C400
- Sony MDR-1ABT
- Sony MDR-ZX770BT
- Sony MDR-XB650BT
- Sony MDR-XB950B1
- Sony SBH50
- Bluedio T4s (Bitpool max 39. Respond to not support Dual Channel, but work if forced, 462 kbit/s. Does not conform to A2DP specification.)
- Bluedio T5 (Respond to not support Dual Channel, but work if forced. Does not conform to A2DP specification.)
- Bluedio T6 (Respond to not support Dual Channel, but work if forced. Does not conform to A2DP specification. Adopt Max 97220 chip.)
- Marshall Major II Bluetooth
- Overdrive RealForce D1
- Edifier W830BT
- DEXP BT-250
- Logitech BT Adapter
- Noname automotive head unit (CSR8645 chip)
- Sony DSX-A400BT automotive head unit
Devices which support SBC higher than 512 kbit/s
- JBL Everest 310 (617-660 kbit/s)
- Sony WI-C400 (576 kbit/s)
- Sony MDR-ZX770BT (617-660 kbit/s)
- Marshall Major II Bluetooth (617-660 kbit/s)
- Overdrive RealForce D1 (730 kbit/s, dual channel, 4 subbands)
Devices which don’t work with higher bitrates or Dual Channel
- Harper HB-202 (cracklings; Beken BK3256 chip)
- Sony Ericsson MW600 (high frequency distortion, cracklings; device from 2009)
Why this is important: SBC 328k and 485k vs aptX
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 are mostly needed in them. 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:
The most energy lies 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 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 spectrogram from the aptX spectrogram (the brighter, the more distortion):
While the SBC 328k has introduced less distortion the signal in the range from 0 to 10 kHz, and the rest has been сut:
Bitrate 485k for SBC was enough to save the entire frequency range, without cutting off the bands.
SBC 485k on this audio sample is much better than aptX in the range of 0-15 kHz, and with a smaller but still noticeable difference – at 15-22 kHz (the darker, the less distortion):
Switching to a high-bitrate SBC, you will get a sound superior to aptX most of the time, on any headphones.
How to Modify the Bluetooth Stacks on Android 5 – 7
These modifications should be applied to stock Android bluetooth stacks Bluedroid (Android 5) and Fluoride (Android 6-7). Qualcomm-modified stack is not supported.
Replace Joint Stereo with Dual Channel in standard SBC configuration
android/platform/external/bluetooth/bluedroid/btif/co/bta_av_co.c:99
Code:
const tA2D_SBC_CIE btif_av_sbc_default_config = { BTIF_AV_SBC_DEFAULT_SAMP_FREQ, /* samp_freq */ A2D_SBC_IE_CH_MD_JOINT, /* ch_mode */ A2D_SBC_IE_BLOCKS_16, /* block_len */ A2D_SBC_IE_SUBBAND_8, /* num_subbands */ A2D_SBC_IE_ALLOC_MD_L, /* alloc_mthd */ BTA_AV_CO_SBC_MAX_BITPOOL, /* max_bitpool */ A2D_SBC_IE_MIN_BITPOOL /* min_bitpool */ };
Replace A2D_SBC_IE_CH_MD_JOINT with A2D_SBC_IE_CH_MD_DUAL.
Increase Dual Channel priority
android/platform/external/bluetooth/bluedroid/btif/co/bta_av_co.c:41
Code:
if (src_cap.ch_mode & A2D_SBC_IE_CH_MD_JOINT) pref_cap.ch_mode = A2D_SBC_IE_CH_MD_JOINT; else if (src_cap.ch_mode & A2D_SBC_IE_CH_MD_STEREO) pref_cap.ch_mode = A2D_SBC_IE_CH_MD_STEREO; else if (src_cap.ch_mode & A2D_SBC_IE_CH_MD_DUAL) pref_cap.ch_mode = A2D_SBC_IE_CH_MD_DUAL; else if (src_cap.ch_mode & A2D_SBC_IE_CH_MD_MONO) pref_cap.ch_mode = A2D_SBC_IE_CH_MD_MONO; Move if with A2D_SBC_IE_CH_MD_DUAL to the top.
- Disable or increase bitrate restriction
Android bluetooth stack has not only bitpool limit, but also bitrate limit, 328 kbit/s. If the headphones support, for example, bitpool 53 for 48 kHz, Android will decrease the bitpool down to fit into 328 kbit/s limit. This will happen AFTER codec negotiation, on the encoding stage, do not take into account bitpool value in Bluetooth SetCapabilities packet.
android/platform/external/bluetooth/bluedroid/btif/src/btif_media_task.c:172
Code:
#define DEFAULT_SBC_BITRATE 328
Replace with 512.
- (for experiments only) Disable MTU limit.
This is required for bitrates higher than ~580 kbit/s.
btif/src/btif_media_task.c:174
Code:
/* 2DH5 payload size of 679 bytes - (4 bytes L2CAP Header + 12 bytes AVDTP Header) */ #define MAX_2MBPS_AVDTP_MTU 663
How to Modify Bluetooth Stacks on Android 8 – 9
These modifications haven’t been tested, but should work.
Add Dual Channel support into A2DP SBC Source
/platform/system/bt/stack/a2dp/a2dp_sbc.cc:55
Code:
/* SBC SRC codec capabilities */ static const tA2DP_SBC_CIE a2dp_sbc_caps = { A2DP_SBC_IE_SAMP_FREQ_44, /* samp_freq */ (A2DP_SBC_IE_CH_MD_MONO | A2DP_SBC_IE_CH_MD_JOINT), /* ch_mode */ (A2DP_SBC_IE_BLOCKS_16 | A2DP_SBC_IE_BLOCKS_12 | A2DP_SBC_IE_BLOCKS_8 | A2DP_SBC_IE_BLOCKS_4), /* block_len */ A2DP_SBC_IE_SUBBAND_8, /* num_subbands */ A2DP_SBC_IE_ALLOC_MD_L, /* alloc_method */ A2DP_SBC_IE_MIN_BITPOOL, /* min_bitpool */ A2DP_SBC_MAX_BITPOOL, /* max_bitpool */ BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16 /* bits_per_sample */ };
add A2DP_SBC_IE_CH_MD_DUAL in ch_mode.
Replace Joint Stereo with Dual Channel in default config
/platform/system/bt/stack/a2dp/a2dp_sbc.cc:82
Code:
/* Default SBC codec configuration */ const tA2DP_SBC_CIE a2dp_sbc_default_config = { A2DP_SBC_IE_SAMP_FREQ_44, /* samp_freq */ A2DP_SBC_IE_CH_MD_JOINT, /* ch_mode */ A2DP_SBC_IE_BLOCKS_16, /* block_len */ A2DP_SBC_IE_SUBBAND_8, /* num_subbands */ A2DP_SBC_IE_ALLOC_MD_L, /* alloc_method */ A2DP_SBC_IE_MIN_BITPOOL, /* min_bitpool */ A2DP_SBC_MAX_BITPOOL, /* max_bitpool */ BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16 /* bits_per_sample */ };
Replace A2DP_SBC_IE_CH_MD_JOINT with A2DP_SBC_IE_CH_MD_DUAL.
Increase Dual Channel priority
/platform/system/bt/stack/a2dp/a2dp_sbc.cc:1155
Code:
static bool select_best_channel_mode(uint8_t ch_mode, tA2DP_SBC_CIE* p_result, btav_a2dp_codec_config_t* p_codec_config) { if (ch_mode & A2DP_SBC_IE_CH_MD_JOINT) { p_result->ch_mode = A2DP_SBC_IE_CH_MD_JOINT; p_codec_config->channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO; return true; } if (ch_mode & A2DP_SBC_IE_CH_MD_STEREO) { p_result->ch_mode = A2DP_SBC_IE_CH_MD_STEREO; p_codec_config->channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO; return true; } if (ch_mode & A2DP_SBC_IE_CH_MD_DUAL) { p_result->ch_mode = A2DP_SBC_IE_CH_MD_DUAL; p_codec_config->channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO; return true; } if (ch_mode & A2DP_SBC_IE_CH_MD_MONO) { p_result->ch_mode = A2DP_SBC_IE_CH_MD_MONO; p_codec_config->channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_MONO; return true; } return false; }
Move if with A2DP_SBC_IE_CH_MD_DUAL to the top.
Increase bitrate limit
/platform/system/bt/stack/a2dp/a2dp_sbc_encoder.cc:42
Code:
#define A2DP_SBC_DEFAULT_BITRATE 328
Replace with 512.
- (for experiments only) Disable MTU limit
This is required for bitrates higher than ~580 kbit/s.
/platform/system/bt/stack/a2dp/a2dp_sbc_encoder.cc:47
Code:
#define MAX_2MBPS_AVDTP_MTU 663