LDAC shows only 96KHz as sample rate | support for other sampling rates
Problem
$ bluealsa-cli info /org/bluealsa/hci0/dev_XXX/a2dpsrc/sink
Device: /org/bluez/hci0/dev_XXX
Sequence: 0
Transport: A2DP-source
Mode: sink
Running: true
Format: S32_LE
Channels: 2
Sampling: 96000 Hz <<< support for other rates?
Available codecs: SBC LDAC
Selected codec: LDAC
Delay: 200.1 ms
DelayAdjustment: 0.0 ms
SoftVolume: true
Volume: L: 127 R: 127
Muted: L: false R: false
Additional context
On Windows, all sample rate families (44.1, 48, 88.2 and 96 KHz) are supported using the same earbud (Sony WF-1000XM5) and bluetooth adapter. Is this possible with bluealsa?
On Windows, all sample rate families (44.1, 48, 88.2 and 96 KHz) are supported using the same earbud
BlueALSA also supports these sampling frequencies (LDAC supports also 176.4kHz and 192kHz, but these sampling rates are not supported by used encoder/decoder). However, there is no easy (user friendly) way to change it. If you want to use different sampling frequency you will have to use bluealsa-ctl and (most likely) a2dpconf tools. bluealsa-ctl will allow you to change codec configuration, while a2dpconf will allow you to "select" desire configuration.
The a2dpconf decodes codec configuration blob. You will see it next to codec name by adding -v option to bluealsa-cli info .... Then you can use the a2dpconf to decode it (this is the decoded version of "capabilities", shown next to available codecs):
$ a2dpconf ldac:2D010000AA003F07
LDAC <hex:2d010000aa003f07> {
vendor-id:32 = 0x0000012d [Sony Corporation]
vendor-codec-id:16 = 0x00aa
<reserved>:2
sampling-frequency:6 = 192000 176400 96000 88200 48000 44100
<reserved>:5
channel-mode:3 = Stereo DualChannel Mono
}
Then, you will have to select, particular configuration, out of these "capabilities", e.g.:
$ a2dpconf ldac:2D010000AA001001
LDAC <hex:2d010000aa001001> {
vendor-id:32 = 0x0000012d [Sony Corporation]
vendor-codec-id:16 = 0x00aa
<reserved>:2
sampling-frequency:6 = 48000
<reserved>:5
channel-mode:3 = Stereo
}
Finally, use this selected configuration (out of possible values, only one was selected) to update codec configuration:
bluealsa-cli codec /org/bluealsa/hci0/dev_XXX/a2dpsrc/sink ldac:2D010000AA001001
Unfortunately (as of now), selected configuration will not be persistent. BlueALSA will select "default" configuration on every Bluetooth reconnection.
That's it :)
I know that it's VERY user unfriendly, but as of now there are no other ways. The internal algorithm for selecting sampling frequencies, selects the greatest available one. So, in case of LDAC (in most cases) it selects 96kHz.
Assuming you are using ALSA applications, then you can avoid the need to use bluealsa-cli after every connection by defining a PCM with the desired codec configuration in ~/.asoundrc, for example:
pcm.earbuds {
type plug
slave.pcm {
type bluealsa
device XX:XX:XX:XX:XX:XX
profile a2dp
codec ldac:2D010000AA001001
}
}
Then use that PCM instead of bluealsa:XX:XX:XX:XX:XX:XX as in:
aplay -D earbuds music.wav
Sincerely appreciate your prompt and super useful response!
I just need to let the application which is using the ALSA backend, select from supported sampling rates. So in the example above, if I choose the codec ldac:2D010000AA003F07 the application should be able to pick any one of those sampling rates?
if I choose the codec ldac:2D010000AA003F07 the application should be able to pick any one of those sampling rates?
Not exactly. You have to choose specific configuration (when decoding configuration blob with a2dpconf you can not see more than one value next to each property). Otherwise, BlueALSA will not reconfigure codec (you will see error message).
Thanks for the explanation. Closing the issue.
How can I see the other codecs e.g. LDAC:2d010000aa000401? I can only see the one which shows all sampling frequencies LDAC:2d010000aa003c07:
$ bluealsa-cli -v info /org/bluealsa/hci0/dev_XXX/a2dpsrc/sink
Device: /org/bluez/hci0/dev_XXX
Sequence: 0
Transport: A2DP-source
Mode: sink
Running: false
Format: S32_LE
Channels: 2
Sampling: 96000 Hz
Available codecs: SBC:3fff0235 LDAC:2d010000aa003c07
Selected codec: LDAC:2d010000aa000401
Delay: 200.0 ms
DelayAdjustment: 0.0 ms
SoftVolume: true
Volume: L: 127 R: 127
$ ./a2dpconf LDAC:2d010000aa003c07
LDAC <hex:2d010000aa003c07> {
vendor-id:32 = 0x0000012d [Sony Corporation]
vendor-codec-id:16 = 0x00aa
<reserved>:2
sampling-frequency:6 = 96000 88200 48000 44100
<reserved>:5
channel-mode:3 = Stereo DualChannel Mono
}
How can I see the other codecs
I guess you mean "how can I create the configuration blob that chooses a specific sample frequency?"
Unfortunately the answer is "by understanding the semantics of the bit sequence represented by the hex blob, and creating the appropriate bit sequence by hand".
It would be great if there was a tool to do this, but I do not know of any (perhaps someone else can advise if they know of any Linux tools for this?.)
As an example, starting with the full capabilities blob given by the "Available codecs:" above:
$ ./a2dpconf LDAC:2d010000aa003c07
LDAC <hex:2d010000aa003c07> {
vendor-id:32 = 0x0000012d [Sony Corporation]
vendor-codec-id:16 = 0x00aa
<reserved>:2
sampling-frequency:6 = 96000 88200 48000 44100
<reserved>:5
channel-mode:3 = Stereo DualChannel Mono
}
We see that the first 32 bits identify the capabilities as being for a Sony codec. That is "2d010000", so we just copy that.
LDAC:2d010000
The next 16 bits ("aa00") identifies the codec ac LDAC, so again we copy that
LDAC:2d010000aa0
The next 8 bits("3c") have the first 2 unused and the last 6 indicating the frequency. Each bit represents 1 supported frequency, and in this example 4 of the possible 6 are available. So we will need to guess which bit represents which frequency (or we could just look at the bluez-alsa source code here https://github.com/arkq/bluez-alsa/blob/adf9334e30553c9ce7f4f868b75ff6da92d4d857/src/shared/a2dp-codecs.h#L558-L567. Suppose we want to use a sampling rate of 48000; that is bit 4 or 10 hex for the full 8 bits:
LDAC:2d010000aa0010
The final 8 bits have 5 unused and the the last 3 each indicating a channel mode. Again by guesswork or looking at the bluez-alsa code we select stereo which is bit 0, so 01 hex for the full 8 bits:
LDAC:2d010000aa001001
It would be great if there was a tool to do this, but I do not know of any (perhaps someone else can advise if they know of any Linux tools for this?.)
I think that the a2dpconf could be extended/improved a bit. When creating the LDAC config for 48000 Stereo I had to use calculator to convert hex to binary and binary to hex, and that was annoying to be honest :D
One improvement might be in terms of display format. I think that I will try to change current format to be more Wireshark-like (where each bit is shown in case of bitmap fields). The other improvement might be a support for command like options to set particular bitmaps to desired values (in order to create desired configurations or capabilities). There is a set of common fields and some codecs have unique fields, but I think that the options "space" is countable :)
Amazing explanation @borine!! But phew...bitwise operations for selecting codec sampling? :D @arkq a practical enhancement would be allowing all supported sampling rates at the same time, as displayed by the full capabilities blob. The downstream ALSA application can then simply use the requested frequency. A common use case is e.g. streaming from Tidal where sampling rates can vary by track. Not sure if that's doable though...
@arkq a practical enhancement would be allowing all supported sampling rates at the same time, as displayed by the full capabilities blob. The downstream ALSA application can then simply use the requested frequency.
Hmmm... Maybe the "simplest" ideas are the best :D There are some drawbacks there, though. The reconfiguration step is not as fast as for HW PCMs, so there might be some initial lag when starting to play audio with different sampling frequency (or number of channels) than currently selected. So, maybe such feature indeed would be nice, but not as a default option, but as an optional one. It should be doable (I think) :)
but as an optional one. It should be doable (I think) :)
Tell me more, I'm keen to try it! --enable-sample-rates :)
Lag isn't a problem, it doesn't need to be as fast as HW PCMs, (no one is thinking about gapless etc in this context I guess). It just needs to support all supported sampling rates without having to reconfigure everytime using a2dpconf, that's pretty much it!
@arkq should we change this to an enhancement? Wondering if you see this happening in the short or long term?
I'm trying to make a PoC for that. Maybe I will squeeze it in an upcoming 4.3.0 release (month or two from now).
I'm trying to make a PoC for that. Maybe I will squeeze it in an upcoming 4.3.0 release (month or two from now).
Awesome! Looking forward to it.
I'm still working on the PoC (some internal refactoring is required), but in the meantime I've pushed to master a change which by default selects sampling rate up to 48 kHz.
@dstagger I've pushed a PoC here https://github.com/arkq/bluez-alsa/tree/auto-codec I've added support for SBC, AAC and LDAC codecs. I've tested it only with SBC and AAC, though. Now, when opening PCM, the ALSA plugin will check for supported channels and sampling rates and if currently selected one does not match with requested one, the reconfiguration is triggered. This might cause a delay in playback up to 1 second (it depends on headset response time). Please, test it on your side a share any thoughts or encountered issues :D In the final version, this feature will be behind a configuration flag for bluealsa PCM. Also, I will have to rework other parts, so this feature will integrate well with custom A2DP configurations, and with HFP profiles.
I think that now the reconfiguration feature is ready. I've updated the https://github.com/arkq/bluez-alsa/tree/auto-codec branch with latest changes.
I've tested it a bit on my side and it seems that it works pretty well. In fact it works so well that I'm thinking about pushing it to master as a default option and maybe even without any "feature" flag for "bluealsa" PCM. Please, test it on your side, I will also test and on my for few days (maybe 1-2 weeks) and if there will be no usage troubles, we are ready to go. @borine you have always great ideas, what do you think about this (pushing it as a default setup)?
Also, before implementing this feature, I had to refactor some internals within BlueALSA service. As an outcome of that, right now it is possible to select number of channels and sampling frequency without having a PhD in astrophysics in order to do bit-flags manipulations (this change is already in master):
# list supported channels and sampling rates
$ bluealsactl codec /org/bluealsa/hci0/dev_B8_27_EB_CA_A4_E7/a2dpsrc/sink -vv
Available codecs:
SBC:ffff02fa [channels: 1 2 2 2] [sampling: 16000 32000 44100 48000]
AAC:ccffff835b60 [channels: 1 2 6 8] [sampling: 8000 11025 ... 48000 64000 88200 96000]
FastStream:0a00000001000323 [channels: 2] [sampling: 44100 48000]
Selected codec:
SBC:211502fa [channels: 2] [sampling: 44100]
# select desired number of channels and sampling rate (other options selected to default)
$ bluealsactl codec /org/bluealsa/hci0/dev_B8_27_EB_CA_A4_E7/a2dpsrc/sink AAC -c 1 -s 16000
$ bluealsactl codec /org/bluealsa/hci0/dev_B8_27_EB_CA_A4_E7/a2dpsrc/sink -vv
Available codecs:
SBC:ffff02fa [channels: 1 2 2 2] [sampling: 16000 32000 44100 48000]
AAC:ccffff835b60 [channels: 1 2 6 8] [sampling: 8000 11025 ... 48000 64000 88200 96000]
FastStream:0a00000001000323 [channels: 2] [sampling: 44100 48000]
Selected codec:
AAC:041008835b60 [channels: 1] [sampling: 16000]
EDIT: I've just found that one bit does not work right - passing custom configuration blob to ALSA PCM in fact conflicts with auto-reconfiguration.... I will have to figure out how to fix that.
@arkq The auto-codec branch looks good as a default setup, and feels more "alsa-like" in that the application gets to choose the best config for its own purpose without needing any external effort from the user. It will present difficulty for my "multi-client" branch which I will need to investigate, but that must not prevent or delay merging here.
@arkq do I need to checkout the auto-codec branch and recompile everything to test this? It's giving a 404 though... I also see that you've an issue with the reconfiguration here Just to sure that it's ready for testing as I've only fully working environment and avoid messing with it as much as possible :D
do I need to checkout the auto-codec
This feature has been merged into the master, so the auto-codec branch was removed.
I also see that you've an issue with the reconfiguration https://github.com/arkq/bluez-alsa/issues/710#issuecomment-2248753846 Just to sure that it's ready for testing as I've only fully working environment and avoid messing with it as much as possible :D
Yes, I think that it's ready for testing. Pull the latest code from master branch and test it please. I've checked it on my side and it works quite well. However, this feature introduces a "drastic" change for the whole ecosystem. Previous behavior was that once the Bluetooth configuration was established, all audio streams ware transmitted with the same setup (unless modified by user). So, if it worked for the first time, it was like 99.999% chance that it will work for future playbacks. Now, every playback can reconfigure A2DP connection. So, it is possible that due to some bug with "not very tested" configuration (e.g. mono with 11025 Hz), all of the sudden playback will not work either due to BlueALSA issue (I've found some bugs with AAC due to this :D), or remote Bluetooth device. So, it would be very much appreciated for users to test this feature as much as possible :D However, I think that the benefits are worth the effort, i.e. less resampling for playing non-standard (something else than stereo 48kHz) audio.
@arkq Apologies for my late response. How do I enable the auto rate change in my existing setup? I've currently v4.2.0-5-g9d73103 installed
You need at least 4.3.0 or better yet 4.3.1 (bug fix release). Then auto rate/channels selection should work out of the box (no special configuration required) . However, I'd recommend to use latest master, because in master there is another fix which is kind of related to auto-selection (deadlock fix).
Do I need to uninstall 4.2? Or just run the following on the existing installation:
git clone https://github.com/arkq/bluez-alsa.git
cd bluez-alsa
autoreconf --install --force
mkdir build
cd build
../configure --enable-ldac --enable-systemd --enable-cli
make
sudo make install
Uninstall and install new one. See the migration guide: https://github.com/arkq/bluez-alsa/wiki/Migrating-from-release-4.3.1-or-earlier
Yes had figured that out :) How do I select the codec that supports multiple rates?
$ bluealsactl -vv info /org/bluealsa/hci0/dev_AC_80_0A_05_F0_E7/a2dpsrc/sink
Device: /org/bluez/hci0/dev_AC_80_0A_05_F0_E7
Sequence: 1
Transport: A2DP-source
Mode: sink
Running: true
Format: S32_LE
Channels: 2
ChannelMap: FL FR
Rate: 96000 Hz
Available codecs:
SBC:3fff0235 [channels: 1 2] [rate: 44100 48000]
LDAC:2d010000aa003c07 [channels: 1 2] [rate: 44100 48000 88200 96000]
Selected codec:
LDAC:2d010000aa000401 [channels: 2] [rate: 96000]
Delay: 201.4 ms
ClientDelay: 0.0 ms
SoftVolume: true
Volume: 127 127
Mute: off off
bluealsactl codec /org/bluealsa/hci0/dev_AC_80_0A_05_F0_E7/a2dpsrc/sink LDAC:2d010000aa003c07
bluealsactl: E: CMD "codec": Couldn't select BlueALSA PCM Codec: Invalid sample rate
LDAC is already selected and transport supports these options: channels: 1 2 rate: 44100 48000 88200 96000; simply play something (with rate different than 96k) using bluealsa ALSA PCM and you will see that the configuration will automatically change.
Hmm seems like it has an issue with alsaloop. I'm piping the source player output to a loopback device which I created like this:
$ cat /etc/modprobe.d/alsa-aloop.conf
options snd-aloop index=1 pcm_substreams=1 id=Loopback
which is used by the alsaloop daemon as
$ cat alsahook.sh
#!/bin/bash
nohup alsaloop -C hw:Loopback,1 -P bluealsa -t 500000 -S 0 > /dev/null 2>&1 &
I've configured the player to use Loopback as the playback device. When the audio sample rate changes, it seems I need to stop the alsaloop daemon and restart it after the playeback starts for bluez-alsa to pickup the changes.
Yes, the configuration is changed only when the PCM is reconfigured. It can be opened all the time but hw params need to be changed. I have never been using alsaloop with BlueALSA, so I'm not sure whether there is a possibility to do that... But I guess that it could be done properly with some patches to alsaloop...
So just to conclude, rates changes are working!! I just tried between 88.2 and 96 as I'm not worried about lower sampling rates with LDAC :) Amazing job!