Saturday 19 August 2023

Part I: Comparison of Bluetooth Fidelity - SBC, aptX, AAC, LDAC (Android 10 source)

Blue ring around power/volume knob = Bluetooth input.

I've been wanting to do this for awhile. The last time I had a look at Bluetooth playback quality was way back in the days of the Oppo Sonica DAC in 2017.

Bluetooth has been our ubiquitous wireless audio transmission technology for more than 2 decades now; Bluetooth 1.0 was released in 1999 but it took a few years for the number of supported devices to grow and costs to come down. Since then, with subsequent versions, we have seen an increase in bandwidth, codecs, transmission range, and power efficiency.

Currently, we are at Bluetooth 5.X (version 5.0 standard released 2016) with the potential for up to 2Mbps transfer rate and distance maximally up to 240m (800ft)! These are clearly ideal numbers. Bluetooth transmits in the 2.4GHz bandwidth like WiFi but can "hop" (Frequency Hopping Spread Spectrum) between 79 channels, 1MHz wide, so there are usually no issues with coexistence. 

Given the 2Mbps limitation, lossless audio transmission would be a problem. Lossless 16/44.1 stereo is already just over 1.4Mbps uncompressed. Yes, we could losslessly compress but compression ratios are variable. Due to inevitable errors in transmission plus protocol overhead, in practice, at most we should think of Bluetooth 5 as operating around 1Mbps reliably under reasonable, if not close to ideal, conditions with transmitter/receiver nearby.

So, within that ~1Mbps data rate, our audio devices will need to compress the data, with lossy techniques for efficient packet transmission. Furthermore, another restriction is that the codec complexity should not be too high since we are typically using low-power mobile devices. This latter limitation over time should not be as much of an issue since computing energy efficiency and speed will get better.

Let's look at some common Bluetooth codecs and specs we find in devices these days:


On my test bench, I still have the AIYIMA A08 PRO tested a few weeks back. That little amp with its Qualcomm QCC5125 BT5.1 chipset would I think be a good example of a modern, inexpensive, wireless audio device we can test.

As you can see, each codec is different when it comes to maximum bitrate and sampling rate. Some, like aptX-Adaptive and LDAC can handle 24-bits and higher sample rate to 96kHz, putting them into the "lossy higher-res audio" camp I suppose although that's a bit of an oxymoron; after all, how "hi-res" can lossy be? This hasn't stopped the Japan Audio Society from "certifying" LDAC to be "High-Resolution Audio Wireless" though. Certifications like these sound good and can be used for advertising purposes.

Each of the codecs will have some variation in bitrates; for example, the lowest common denominator SBC (low-complexity Sub-Band Codec) is supported universally and can go up to 345kbps as its "High Quality" profile at 48kHz, throttling down a little to 328kbps for 44.1kHz. While AAC can go up to 320kbps, typically with most devices (like Apple's products), audio data is transmitted at 256kbps. In practice, I have not seen AAC Bluetooth go above 16/44.1 even though the compression format can handle more bits and higher sample rates.

Let's run some tests...

With that little preamble, let me load up 3 test signals and run 'em through the meat grinders lossy codecs to see what happens. To start, here are the signals as played back losslessly from the Topping D10s DAC over RCA cables to the AIYIMA A08 PRO into a 4Ω load. The Topping was connected to a Raspberry Pi 4 "Touch". I standardized the output level to 1Vrms with the 1kHz tone (0.25W). I subsequently left the volume knob alone so there is no change in amplifier settings.

We can consider the hi-res Topping D10s DAC output as the "gold standard" towards which ideal Bluetooth playback would try to approach:

For "1/10 Decade Multitone 32", I turned on the red peak reading to make sure no unexpected excess noise showing up across about 5 minutes of data capture.

As you can see, I'm using 3 test signals starting with a simple -3dBFS 1kHz tone, then moving to my typical triple-tone, and finally the highly complex 1/10 Decade Multitone 32. The signals are 24/44.1 with dithering in the 24th bit so as to extract the best resolution from codecs that can handle 24-bits. I chose 44.1kHz as this is still the most common digital samplerate.

As the signal gets more complex, we can see how well the lossy codecs negotiate this. For a visual illustration, here's what the waveforms look like, 140ms segment of each test signal:

Peak level -3dBFS for all 3 signals.

The source for the Bluetooth signal is my Huawei P30 Pro phone circa 2019, running a version of Android 10. I can easily go into Settings > Systems & Update > Developer options and change the Bluetooth codec used and some parameters like 16/24-bit depth and bitrate where possible like with LDAC.

Where there are codec options, I have made sure to set sample rate to 44.1kHz and bit depth to 24-bits to optimally correlate with the test signals.

Bluetooth codec selection on Android phone.

Of the codecs in the table above, the only one I could not test out was aptX-Adaptive which is only available if the source device uses a compatible Qualcomm chipset. As far as I can tell, aptX-Adaptive should be supported by the AIYIMA's QCC5125.

Let's get started. Here's Bluetooth playback with the default, "universal", SBC codec:


As you can see, clearly the SBC process has affected the noise floor of the output. THD+N and TD+N are in the mid -60dB's which isn't great but certainly is fine and enjoyable for non-critical listening. Various distortion tones in the 1kHz test that are not simple harmonic products. 

Notice in the "1/10 Decade Multitone 32" graph that I've put the cursor at the peak level of the tones from 300Hz+. As you can see, the frequency response is accentuated in the bass region below 200Hz. I'm using the Android app "USB Audio Player PRO" and have double checked that all EQ settings are off, aiming for "bit perfect". Likewise I don't see any audio EQ/DSP turned on in OS settings. Despite this, the Huawei OS insists on turning on the bass boost every time I get a notification from Facebook, WhatsApp or other app until I reboot the phone! I decided to not fight with the system for these measurements. In the specific frequency response section below, I rebooted and turned off all notifications for "flat" measurements.

Notice with SBC the noise floor increases gradually above 2kHz with a steeper rise above 10kHz.

Now compare the SBC graphs with AAC:

Measurement Note: Due to the noise at the base of the primary signal, I did not use the usual AES17-2015 setting in REW for the 1kHz THD+N calculation. This would inaccurately notch out too much (1 octave span) and the result better than it should be.

Yikes, that's ugly! Notice that there's a bit of frequency "smearing" around the test tones. This level of distortion makes the typical picosecond jitter sidebands look like microscopic wrinkles!

Notice the excess noise in the low frequencies with the complex Multitone 32. It's typical to see a worsening between the 1kHz THD+N and triple-tone TD+N but this large change from -41.9 to -35.9dB suggests that the noise/distortions are getting worse than usual with signal complexity. Clearly, there's more distortion using AAC than even just SBC with Android 10! On the other hand, notice that the high frequencies look pretty good with relatively lower noise than SBC.

AAC is a more complex codec than SBC so I suspect what happened is that the Android 10 encoding algorithm on my phone sacrificed quality for lower computational load, better battery life and maybe lower latency.

Let's move on to a couple of the common aptX variants - the original aptX and aptX-HD:



As you can see, numerically the THD+N and TD+N improved using aptX-HD. This is mainly on account of a significantly lower noise floor. It looks like aptX is adding a bit of dithering hence stabilizing the noise floor at around -100dBFS for most of the audible spectrum as seen in the -3dBFS 1kHz test signal. In comparison, the aptX-HD version gives us a bit more dynamic range by about 10dB.

Interesting that good 'ol SBC still holds its own against aptX and HD although with a complex signal like the "1/10 Decade Multitone 32", aptX-HD (running at 529kbps for 44.1kHz sample rate) is the best thus far with less distortion and noise.

Next, let's consider the "family" of LDAC settings; because I'm using a 44.1kHz signal, these will be at 303, 606, and 909kbps (instead of the 48kHz bitrates of 330, 660, and 990kbps). Here's 303kbps:


What is interesting about the lower bitrate 303kbps setting is that THD+N and TD+N are already pretty good and comparable to aptX-HD. Where it falls apart is with the complex Multitone 32 signal where we can see that frequency extension doesn't go all the way up to 20kHz and with the relatively low bitrate, noise is clearly elevated at the higher frequencies.

Let's double the bitrate and see - here's 606kbps:


With more bits, we're not seeing much change in the 1kHz THD+N or triple-tone TD+N which suggests that for lower-complexity signals within the most important parts of the audible spectrum (<10kHz), LDAC 303kbps does a pretty good job already. However, those extra bits certainly do become useful when we look at the Multitone 32 as we approach and go beyond 10kHz. Notice that frequency response has extended and we can see the multitone component >20kHz now.

And let's go even higher with LDAC bandwidth of 909kbps:
Notice there tends to be a cluster of distortion around the 650-675Hz tone, also seen at LDAC 606kbps setting above.

At >900kbps, we now see an improvement in the 1kHz THD+N and triple-tone TD+N by about 3dB over LDAC 606kbps. The Multitone 32 shows further improvements with reduction in high-frequency distortion (most of the anomalies are above 15kHz now).

Frequency response comparisons...

As I mentioned above, the Android OS on my phone kept turning on +6dB bass boost as I was testing. Anyhow, I rebooted and prevented the phone from running any background apps to grab some flat frequency response sweeps. The slight 0.5dB bass bump in these graphs is just from the AIYIMA amp itself.

Since there are a number of codecs here, let's split this up into 2 charts; beginning with SBC, AAC, aptX, and aptX-HD compared to the Topping USB DAC (grey, dashed line):


Nothing unusual with the frequency response other than the wireless codecs tending to roll off a little earlier. Notice that AAC's curve is a bit "rough" looking, this is correlated to the poor quality Android AAC implementation.

Next, here's LDAC - we'll keep the Topping D10s and SBC curves overlaid for comparison:


The most notable anomaly is the early roll-off with LDAC 303kbps, cut off around 16.5kHz. For most of us older guys this will not be a problem, but might be noticeable by the younger audiophiles (like those below 30 years old). Otherwise, notice the LDAC 606 and 909kbps settings extend fully to 20kHz like the Topping DAC without the same amount of SBC/AAC/aptX roll-off.

The other anomaly is the rough patch with LDAC 303kbps just before it rolls off. This is beyond 14kHz so should not be objectionable even if undesirable.




Summary... What have we learned from this?

Okay, so after looking at the graphs and data, what have we learned about the Bluetooth codecs using the AIYIMA A08 PRO as receiver and player, and an Android 10 phone sending the encoded 24/44.1 test signals?

1. These are all lossy codecs and therefore degradations can be found. Obviously stick with a lossless high-resolution wired DAC if you want best fidelity. Lossy formats take advantage of human psychoacoustics so even though the degradation can be measured, how much it impacts audibility will be variable and depends on the listener's hearing abilities. Be mindful of these differences when testing or demoing wireless headphones - if you hear distortions especially with an Android source using AAC, the headphones might be fine but the codec is just not the best choice for the transmitting device.

2. Notice that even an inexpensive amplifier like the AIYIMA A08 PRO can easily show us the differences between these codecs. Furthermore, the amplifier volume was low, only pushing 0.25W into 4Ω with the 1kHz test tone. This is less than the potential dynamic range of the device. The effect of these lossy codecs therefore degrade resolution more than the fidelity of a modern Class D chip amplifier.

As I mentioned, the Huawei phone seems to like adding +6dB bass boost to the Bluetooth signal. Be mindful of stuff like this when listening to different devices. Audio OS subsystems can be a bit murky which is why it's nice to have "bit-perfect" audio drivers like ASIO when running tests.

3. SBC as the default universal codec doesn't look/sound too bad. Since I have no way to double check the data rate, I assume the results shown here are with 328kbps which is the "high quality" profile for 44.1kHz sample rate. With low complexity content, the distortion isn't bad but watch out for degradation with complex signals and higher frequency content. This is typical with lossy codecs, like the high-frequency anomalies we hear with low bitrate MP3.

4. Android AAC encoding has been known to be of poor quality. Indeed, we're seeing this again with the Huawei P30 Pro running Android 10. Given that Android is up to version 13 now, maybe there is hope that the newer versions might have better fidelity? Let's have a look at this next time maybe.

5. AptX looks alright as a 16-bit codec; a smoother dithered noise floor, but as usual, more noise at higher frequencies. AptX-HD is an improvement, and capable of more dynamic range. With the updated Qualcomm hardware and optimizations, I would imagine aptX-Adaptive to be similar to aptX-HD in performance despite 25% lower bitrate. Hard to imagine great resolution with a 24/96 signal though. Maybe at some point I'll get a chance to test it out.

[Update: see this interesting thread on aptX-Adaptive. Looks like there could be many versions of "Adaptive" with marketing terms like "Snapdragon Sound" that don't necessarily imply specific specs. Newer versions with potential bitrates up to 1Mbps for "aptX-Lossless" and lossy 24/96. Looks like a bit of a mess, leaving it up to consumers to decipher the capabilities of their device. Not the way to do this, Qualcomm, especially after all the hullabaloo about doing lossless 16/44.1 back in 2021.]

6. As for LDAC, unless you're experiencing bandwidth issues and need to stay at 303/330kbps, I suggest using the higher 606/660 or 909/990kbps options. At the lowest 303/330kbps data rate, frequency response goes to 16.5kHz which could be audible for younger listeners, and the noise level from 5kHz up is a bit higher even than SBC. LDAC 606/660 is a good balance with excellent frequency extension and high-frequency distortions mostly above 10kHz. LDAC 909/990kbps with its high bitrate performs very well and even though it still shows high-frequency distortions in complex signals above 15kHz, this would be difficult to perceive and thus making it the best quality codec among the ones tested.

7. None of these lossy codecs should be called "High Resolution Audio", IMO. After all these decades, the only meaningful definition of "standard resolution" is CD Audio. Anything legitimately "high resolution" therefore must be capable of better than lossless 16/44.1 without compromises. Who cares if the codec can accept 24/96 data if it ultimately still increases the noise level below 20kHz? None of these wireless codecs can surpass CD resolution so it's a little silly for the Japan Audio Society to hand out their certification badges claiming "Hi-Res Audio Wireless" as if it's meaningful.

Maybe calling SBC and aptX "Standard Quality Wireless" would make more sense and then legitimately certifying that aptX-HD and LDAC >600kbps are "High Quality Wireless" codecs making it more of a statement about perceptual quality rather than the word "resolution" which has a certain objective meaning when used. It's like in the video world where the word "definition" has a certain meaning: "Standard Definition" = 480 lines, "High Definition" = 720P/1080i/P, and "Ultra High Definition" = "4K", 2160P, or more.

Given a choice then, using the system here - Android 10 phone, AIYIMA A08 PRO amplifier with supported codecs - I would rank the codecs for sound quality from best to least favored in this order:

1. LDAC 909/990kbps 
2. LDAC 606/660kbps = aptX-HD 
3. aptX = LDAC 303/330kbps
4. SBC
5. AAC

In practice, I rarely use the highest LDAC 909/990 setting because transmission range is limited and audio dropouts are not uncommon even within 5 feet of my phone depending on the environment (eg. walls nearby, someone in the kitchen turns on microwave, etc.) so depending on the Bluetooth device compatibility, I'd most commonly use LDAC 606/660 or aptX-HD for Android. Obviously the quality of your device's radio transmitter/receiver plays a huge role.

The big disappointment here is clearly the poor AAC performance with my Android phone. Apple devices use AAC 256kbps as a standard, and I see that I have some Android 11 and 13 devices here as well. In Part II next time, let's focus on AAC performance - the iPhone and iPad better be much better than Android in the follow-up tests!

One last thing for completeness. Don't forget that there are other issues with Bluetooth beyond the scope of this article. For example, latency can be problematic if watching videos with potential lip-sync issues. We discussed a bit about latency and audio system measurements a while back - as a rule of thumb <50ms would be good. There are codecs which cater to low latency like aptX-LL (~40ms) and LLAC (~30ms). Although not as popular, LHDC (400/560/900kbps up to 24/96, LLAC is part of this family) is also available with some devices. A few weeks back with the Hidizs AP80 PRO-X review, I mentioned UAT (Ultra Audio Transmission) which is supposed to handle up to 24/192 at 1.2Mbps, all lossy of course. Given the risk of playback disruption with LDAC at >900kbps already, I wonder how stable continuous >1Mbps transmission would be using current Bluetooth technology! So far, I have not tried any UAT headphones.

Waiting in the wings is LC3 (Low Complexity Communication Codec), part of the Bluetooth LE (Low Energy) Audio specification in Bluetooth 5.2. There's also LC3plus which recently obtained the JAS "High Resolution Audio Wireless" logo also. The promise of better battery life, very low latency and higher perceived sound quality using even lower bitrates (Bluetooth LE has even lower bitrate than Bluetooth Classic) are good characteristics. We will see how well this actually sounds once more BT LE Audio devices come out.

Ultimately, until something major changes with Bluetooth bitrates, these are all lossy codecs with some relative differences trying to balance quality, reliability, latency, compatibility, computational complexity and power utilization. Since they all take advantage of various human psychoacoustic properties to reduce audible anomalies (like the equal-loudness contour of human hearing, and auditory masking), it's best to just listen and enjoy rather than obsess too much about absolute "fidelity" given current technological limitations with the hope that one's ears perform within typical physiological assumptions!

Personally, I appreciate the convenience of Bluetooth headphones and use them regularly even if I know that objectively the sound is obviously not of the highest "audiophile" fidelity.

As usual, it's about enjoying the music! Hope that's what you're doing... :-)

--------------------

In response to Mikhail's comment, I thought I'd include the contents of my Huawei phone's config file - here's /vendor/etc/a2dp_audio_policy_configuration.xml:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- Copyright (C) 2015 The Android Open Source Project
     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at
          http://www.apache.org/licenses/LICENSE-2.0
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License. -->
<audioPolicyConfiguration version="1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
    <!-- version section contains a “version” tag in the form “major.minor” e.g version=”1.0” -->
    <!-- Global configuration Decalaration -->
    <globalConfiguration speaker_drc_enabled="false"/>

    <!-- Modules section:
        There is one section per audio HW module present on the platform.
        Each module section will contains two mandatory tags for audio HAL “halVersion” and “name”.
        The module names are the same as in current .conf file:
                “primary”, “A2DP”, “remote_submix”, “USB”
        Each module will contain the following sections:
        “devicePorts”: a list of device descriptors for all input and output devices accessible via this
        module.
        This contains both permanently attached devices and removable devices.
        “mixPorts”: listing all output and input streams exposed by the audio HAL
        “routes”: list of possible connections between input and output devices or between stream and
        devices.
            "route": is defined by an attribute:
                -"type": <mux|mix> means all sources are mutual exclusive (mux) or can be mixed (mix)
                -"sink": the sink involved in this route
                -"sources": all the sources than can be connected to the sink via vis route
        “attachedDevices”: permanently attached devices.
        The attachedDevices section is a list of devices names. The names correspond to device names
        defined in <devicePorts> section.
        “defaultOutputDevice”: device to be used by default when no policy rule applies -->
    <modules>
        <!-- Primary Audio HAL -->
        <module name="primary" halVersion="3.0">
            <attachedDevices>
                <item>Earpiece</item>
                <item>Speaker</item>
                <item>Built-In Mic</item>
                <item>Built-In Back Mic</item>
            </attachedDevices>
            <defaultOutputDevice>Speaker</defaultOutputDevice>
            <mixPorts>
                <mixPort name="primary out" role="source" flags="AUDIO_OUTPUT_FLAG_PRIMARY">
                    <profile name="" format="AUDIO_FORMAT_PCM_32_BIT"
                             samplingRates="48000"
                             channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
                </mixPort>
                <mixPort name="primary in" role="sink">
                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                             samplingRates="8000,11025,16000,22050,24000,32000,44100,48000"
                             channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_VOICE_CALL,AUDIO_CHANNEL_IN_VOICE_CALL_STEREO"/>
                </mixPort>
            </mixPorts>
            <devicePorts>
                <!-- Output devices declaration, i.e. Sink DEVICE PORT -->
                <devicePort tagName="Earpiece" type="AUDIO_DEVICE_OUT_EARPIECE" role="sink">
                </devicePort>
                <devicePort tagName="Speaker"  type="AUDIO_DEVICE_OUT_SPEAKER" role="sink">
                </devicePort>
                <devicePort tagName="Wired Headset" type="AUDIO_DEVICE_OUT_WIRED_HEADSET" role="sink">
                </devicePort>
                <devicePort tagName="Dgtl Dock Headset" type="AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET" role="sink">
                </devicePort>
                <devicePort tagName="Wired Headphones" type="AUDIO_DEVICE_OUT_WIRED_HEADPHONE" role="sink">
                </devicePort>
                <devicePort tagName="BT SCO" type="AUDIO_DEVICE_OUT_BLUETOOTH_SCO" role="sink">
                </devicePort>
                <devicePort tagName="BT SCO Headset" type="AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET" role="sink">
                </devicePort>
                <devicePort tagName="BT SCO Carkit" type="AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT" role="sink">
                </devicePort>
                <devicePort tagName="Aux Dgtl" type="AUDIO_DEVICE_OUT_AUX_DIGITAL"  role="sink">
                </devicePort>
                <devicePort tagName="FM" type="AUDIO_DEVICE_OUT_FM" role="sink">
                </devicePort>
                <devicePort tagName="Built-In Mic" type="AUDIO_DEVICE_IN_BUILTIN_MIC" role="source">
                </devicePort>
                <devicePort tagName="Built-In Back Mic" type="AUDIO_DEVICE_IN_BACK_MIC" role="source">
                </devicePort>
                <devicePort tagName="Wired Headset Mic" type="AUDIO_DEVICE_IN_WIRED_HEADSET" role="source">
                </devicePort>
                <devicePort tagName="BT SCO Headset Mic" type="AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET" role="source">
                </devicePort>
                <devicePort tagName="FM Tuner" type="AUDIO_DEVICE_IN_FM_TUNER" role="source">
                </devicePort>
            </devicePorts>
            <!-- route declaration, i.e. list all available sources for a given sink -->
            <routes>
                <route type="mix" sink="Earpiece"
                       sources="primary out"/>
                <route type="mix" sink="Speaker"
                       sources="primary out"/>
                <route type="mix" sink="Wired Headset"
                       sources="primary out"/>
                <route type="mix" sink="Wired Headphones"
                       sources="primary out"/>
                <route type="mix" sink="Dgtl Dock Headset"
                       sources="primary out"/>
                <route type="mix" sink="BT SCO"
                       sources="primary out"/>
                <route type="mix" sink="BT SCO Headset"
                       sources="primary out"/>
                <route type="mix" sink="BT SCO Carkit"
                       sources="primary out"/>
                <route type="mix" sink="FM"
                       sources="primary out"/>
                <route type="mix" sink="Aux Dgtl"
                       sources="primary out"/>
                <route type="mix" sink="primary in"
                       sources="Built-In Mic,Built-In Back Mic,Wired Headset Mic,BT SCO Headset Mic,FM Tuner"/>
            </routes>
        </module>
        <!-- A2dp Audio HAL -->
        <xi:include href="a2dp_audio_policy_configuration.xml"/>
        <!-- Usb Audio HAL -->
        <xi:include href="usb_audio_policy_configuration.xml"/>
        <!-- Remote Submix Audio HAL -->
        <xi:include href="r_submix_audio_policy_configuration.xml"/>
        <!-- Hearing aid Audio HAL -->

        <!-- <xi:include href="hearing_aid_audio_policy_configuration.xml"/> -->

    </modules>

    <!-- End of Modules section -->

    <!-- Volume section -->

    <xi:include href="audio_policy_volumes.xml"/>

    <xi:include href="default_volume_tables.xml"/>

    <!-- End of Volume section -->

</audioPolicyConfiguration>

And I see in the config file, a reference to /vendor/etc/a2dp_audio_policy_configuration.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!-- A2dp Audio HAL Audio Policy Configuration file -->
<module name="a2dp" halVersion="2.0">
    <mixPorts>
        <mixPort name="a2dp output" role="source">
            <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                     samplingRates="44100,48000,96000"
                     channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
            <profile name="" format="AUDIO_FORMAT_PCM_24_BIT_PACKED"
                     samplingRates="44100,48000,96000"
                     channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
        </mixPort>
    </mixPorts>
    <devicePorts>
        <devicePort tagName="BT A2DP Out" type="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP" role="sink">
            <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                     samplingRates="44100,48000,96000"
                     channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
            <profile name="" format="AUDIO_FORMAT_PCM_24_BIT_PACKED"
                     samplingRates="44100,48000,96000"
                     channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
        </devicePort>
        <devicePort tagName="BT A2DP Headphones" type="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES" role="sink">
            <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                     samplingRates="44100,48000,96000"
                     channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
            <profile name="" format="AUDIO_FORMAT_PCM_24_BIT_PACKED"
                     samplingRates="44100,48000,96000"
                     channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
        </devicePort>
        <devicePort tagName="BT A2DP Speaker" type="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER" role="sink">
            <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                     samplingRates="44100,48000,96000"
                     channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
            <profile name="" format="AUDIO_FORMAT_PCM_24_BIT_PACKED"
                     samplingRates="44100,48000,96000"
                     channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
        </devicePort>
    </devicePorts>
    <routes>
        <route type="mix" sink="BT A2DP Out"
               sources="a2dp output"/>
        <route type="mix" sink="BT A2DP Headphones"
               sources="a2dp output"/>
        <route type="mix" sink="BT A2DP Speaker"
               sources="a2dp output"/>
    </routes>
</module>

I don't see any reference to the codecs like aptX, LDAC, AAC, etc...

15 comments:

  1. Hi Archimago! Thanks for taking time doing these tests! A bit more details on the Bluetooth codecs implementation on Android. On most modern phones the encoder is "offloaded," that is, implemented in the chipset. You can check if it's offloaded on your phone by looking at the audio policy configuration file using the ADB tool (https://developer.android.com/tools/adb) and the command `adb shell cat /vendor/etc/audio_policy_configuration.xml` (I hope you don't need to root your phone for that). If the configuration file contains lines like `encodedFormats="AUDIO_FORMAT_LDAC AUDIO_FORMAT_APTX AUDIO_FORMAT_APTX_HD AUDIO_FORMAT_AAC AUDIO_FORMAT_SBC"`, that means BT codecs are offloaded. See an example for Pixel 4 here: https://cs.android.com/android/platform/superproject/main/+/main:device/google/coral/audio/audio_policy_configuration.xml;l=137?q=f:audio_policy_configuration.xml

    Your Huawei P30 Pro uses HiSilicon Kirin SoC. I would suspect that chipsets by Qualcomm and other vendors might have different codec implementations. If you have an opportunity, you can try an Android phone implemented on a Qualcomm SoC, or some other chipset like Exynos by Samsung (https://semiconductor.samsung.com/us/processor/showcase/smartphone/) or by MediaTek (https://www.androidauthority.com/best-mediatek-smartphones-906107/).

    Regarding USB, note that often times USB on Android phones is hooked via the main DSP. This is to reduce latency when using wired headsets. The DSP typically works at 48 kHz. Thus, even if you connect a USB card which only supports 44.1 kHz, and send audio in 44.1 kHz, the DSP will still do all the processing at 48 kHz and as the final step will SRC to 44.1 kHz when sending audio to the USB device. Whether or not USB is handled by the DSP can also be checked by looking inside the audio_policy_configuration.xml file. If you see `AUDIO_DEVICE_OUT_USB_DEVICE` listed under `module name="primary"` node, as on the Pixel 4 file I linked above, that means that the DSP handles USB.

    The only way to work around that is to use the USB Audio Player with their custom driver which connects to USB via the Linux kernel directly. However, use of this driver is likely prohibited in newer Android releases.

    ReplyDelete
    Replies
    1. Hey there Mikhail,
      Thanks for the detailed note on this. I had a look at the audio_policy_configuration file over ADB and you'll see I dumped this to the post as an addendum. Here's the Huawei a2dp_audio_policy_configuration file as well if that's useful.

      I'm not seeing the codecs listed like with the Pixel... Does this mean the Huawei P30 Pro is not hardware offloading; presumably just using Android's software encoding?

      Thanks also on the USB note. As you can see, for the measurements I just used bit-perfect with a Raspberry Pi 4 rather than my Android device. I'm sure I could have utilized USB Audio Player Pro's "HiRes Direct driver" as well.

      Delete
    2. BTW, yes I have Exynos tablets with Android 11 and 13 that I can look at the AAC implementation with. Unfortunately no Qualcomm devices here currently... Maybe down the road as I refresh my machine here at home ;-).

      Delete
    3. Thanks Archimago! Indeed, on your phone encoding for BT is done in software. And from the existence of this line: ``, I can conclude that the USB path is *not* handled by the DSP but rather by Linux kernel directly. Thus, there are likely less resamplings in the pipeline.

      Delete
    4. Ups, sorry, part of my comment was eaten during submission. I was pointing to the line in the config which includes usb_audio_policy_configuration.xml file. This file is used in the case when the Android audio framework open USB via Linux kernel.

      Delete
    5. Awesome thanks for the confirmation.

      Delete
  2. Neutron music player also uses its own custom driver which bypasses Android's USB limitations.

    ReplyDelete
    Replies
    1. Thanks julian,
      I've not tried Neutron. Good to see there are options! Hope it works well for you.

      Delete
  3. To me, Bluetooth on Android has two annoying limitations -- that is, two beyond the often mediocre sound. First, on most devices, it's impossible to specify which codec one wants used. Indeed, it can be difficult for a nontechnical person to find out which codecs a device supports, and the list doesn't seem to correspond to Bluetooth version numbers. Second, few devices indicate which codec actually is in use. This mess creates a jumble of claims and uncertainty and makes Bluetooth always the last choice for me -- though at the gym and on an airplane, I find LDAC (at an unknown data rate) satisfactory, even enjoyable.

    ReplyDelete
    Replies
    1. P.S. I specified Android only because that is what I use. I didn't mean to suggest that iOS is better or worse.

      Delete
    2. I hear ya Roth...

      Unless the user bothers to rummage through stuff like the "Develop options" menu, it's all rather opaque. I know that on my Samsung Android 13 tablet, at least there's slider in the main menu for LDAC and "HDaudio" (aptX) or something like that which at least is something.

      Delete
  4. I'm a bit confused on what this test tells us about real-world codec performance? These codecs are all optimized for human perception. A multitone test is nice to test the linearity of an amplifier, but bears basically no information on how well a codec that deals with human audience performs, as far as I can tell.

    I feel that auditory masking is a very good example for how this test isn't very sensible: in the presence of a similarly loud tone at a lower frequency, I really can't make out fine differences in the loudnesses of most of the higher tones.

    Am I missing something here? It seems like your test is for how linear a system codec + amplifier are, but that's not really what we care about.

    ReplyDelete
    Replies
    1. Yes, correct MM. The idea here is more a demonstration of the changes in codecs and bitrates, and how this affects objective fidelity. As you noted, human ears are not linear devices and subjective perception will "mask" many deviations and distortions.

      On a basic level, doing this on my phone tells me which codec is the most accurate. Hence I'll likely avoid AAC with my device; if I am able to hear the distortions, it would likely be with AAC.

      On a more academic level, it points to the kinds of hearing tests we can try out individually or corporately. I know that a number of folks have complained about the sound of AAC in Android already even without the objective data so I think some listeners already know there's a bit of a problem with Android AAC.

      Ultimately, as an audiophile, it's still good I think to strive towards making wireless BT audio technically as "hi-fi", and linear as possible.

      Delete
  5. Ackchewally, SBC does not max out at 345 kbps, it can up to 730 kbps with only a software update on the transmitter side, which may already be included in newer versions of Android (don't have one myself, still on Oreo 8.1 so I can't do a quick check).
    http://soundexpert.org/articles/-/blogs/audio-quality-of-sbc-xq-bluetooth-audio-codec

    ReplyDelete
    Replies
    1. Cool donjoe,
      That higher bitrate SBC XQ looks good! Haven't checked if my Pixel Pro 8 capable of this but this 529/551kbps option would be fantastic to have so long as the headphones able to decode.

      Delete