arkq/bluez-alsa

Audio volume does not go to 100% on Raspberry Pi

Vinchethescript opened this issue · 19 comments

Problem

Yesterday, I reinstalled bluealsa (as a sink) after a fresh Raspberry Pi OS Bookworm install on my Raspberry Pi 4, but the volume control from my phone wasn't working, even though I could see the volume changes from bluetoothctl the audio wasn't very loud (it's actually never been, but I never thought of investigating and trying to figure out why before).

Using alsamixer, though, I noticed that it does change the volume of the bluealsa capture device, even though I can't hear the difference: when the volume is set to any value except 0, you always hear it at the maximum volume; when it's 0, the audio is just muted. Also, I can control the volume just fine from alsamixer. it only changes the volume up to 0dB, which is not the maximum volume on Raspberry Pi.

Reproduction steps

Run bluealsa and bluealsa-aplay. I used these parameters: -p midi --midi-advertisement -p a2dp-source -p a2dp-sink --a2dp-volume -c sbc -c aac -c aptx -c aptx-hd -c ldac --ldac-abr --ldac-quality=high for bluealsa, and both none and --pcm=plughw:0,0 --mixer-name=PCM --mixer-index=0 for bluealsa-aplay, as amixer scontents shows that the mixer is called PCM and not Master. The volume will not change. The volume will only reach up to 0dB while the Raspberry Pi's maximum volume is 4dB. If you try to set a higher volume than that, it will crash because of an assertion error.

Setup

  • Raspberry Pi OS Bookworm (64-bit)
  • BlueALSA is at the latest version at the time of writing (v4.2.0)
  • BlueZ 5.66
  • ALSA 1.2.8
  • Built from branch master and release tag v4.2.0 (261f184 (HEAD, tag: v4.2.0) Minor version bump (release 4.2.0)).

Configure options:

  • --enable-manpages
  • --enable-aac
  • --enable-ldac
  • --with-libopenaptx
  • --enable-mp3lame
  • --enable-mpg123
  • --enable-aptx
  • --enable-aptx-hd
  • --enable-msbc
  • --enable-rfcomm
  • --enable-hcitop
  • --enable-systemd
  • --enable-cli
  • --enable-faststream
  • --enable-midi
  • --enable-ofono
  • --enable-upower
  • --enable-a2dpconf
  • --with-systemdbluealsaargs='-S -p midi --midi-advertisement -p a2dp-source -p a2dp-sink --a2dp-volume -c sbc -c aac -c aptx -c aptx-hd -c ldac --ldac-abr --ldac-quality=high'

Additional context (old)

Checking the logs (even with the --enable-debug flag on the ./configure command and recompiling), the only actually useful info I could see was:

bluealsa-aplay: [197226] D: ../../../utils/aplay/aplay.c:692: Opening ALSA playback PCM: name=default channels=2 rate=96000
bluealsa-aplay: [197226] D: ../../../utils/aplay/aplay.c:708: Opening ALSA mixer: name=default elem=Master index=0
bluealsa-aplay: [197226] D: ../../../utils/aplay/aplay.c:467: Setting up ALSA mixer volume synchronization
bluealsa-aplay: [29403] E: ../../../utils/aplay/aplay.c:349: Couldn't get ALSA mixer playback dB level: Invalid argument
bluealsa-aplay: [29339] E: ../../../utils/aplay/aplay.c:433: Couldn't set ALSA mixer playback dB level: Invalid argument

on the logs of bluealsa-aplay. Looks like the only workaround is to use the --volume=software parameter on bluealsa-aplay for now.

The audio seems to be passing through pipewire when running bluealsa-aplay as user. If I run it as root so that it won't pass through pipewire, I'll still get sound playing, but with the same issue as above, but with different logs:

bluealsa-aplay: [197569] D: ../../../utils/aplay/aplay.c:692: Opening ALSA playback PCM: name=default channels=2 rate=96000
bluealsa-aplay: [197569] D: ../../../utils/aplay/aplay.c:708: Opening ALSA mixer: name=default elem=Master index=0
bluealsa-aplay: [197569] W: ../../../utils/aplay/aplay.c:712: Couldn't open ALSA mixer: Mixer element not found

In general it is not a good idea to run bluealsa and pipewire together unless you really know what you are doing.

So, first I assume you have disabled the pipewire bluetooth support (you cannot have both pipewire and bluealsa fighting over A2DP and expect to have any kind of stable Bluetooth audio from either of them).

Second, pipewire does not like applications accessing the sound card(s) directly. You will find problems if an application sends audio to pipewire at the same time as bluealsa-aplay is sending audio to plughw

Third, assuming pipewire has installed itselt as default ALSA PCM, it appears from the logs that the pipewire ALSA pcm plugin Master control does not offer dB level settings as required by bluealsa-aplay. You can check this by running
amixer scontents

The audio only passed through PipeWire when running as user, as root it directly goes to ALSA. As soon as I noticed that PipeWire was installed, I instantly disabled and stopped it so it wouldn't give problems, as I don't need it since I only use the audio for BlueALSA. I also tried this with PulseAudio to see if PipeWire was the problem, and it persists, even after setting up the PulseAudio integration as of the wiki page (the bluepulse script/the load-module command from the wiki even fails to initialize the module-alsa-sink).

Also, on PulseAudio, I tried using bluealsa-aplay again with both --pcm=pulse and --pcm=plughw:0,0 and now both give the same error as I ran it as root before:

bluealsa-aplay: [5228] D: ../../../utils/aplay/aplay.c:692: Opening ALSA playback PCM: name=plughw:0,0 channels=2 rate=96000
bluealsa-aplay: [5228] D: ../../../utils/aplay/aplay.c:708: Opening ALSA mixer: name=default elem=Master index=0
bluealsa-aplay: [5228] W: ../../../utils/aplay/aplay.c:712: Couldn't open ALSA mixer: Mixer element not found

Opening ALSA mixer: name=default elem=Master index=0
Couldn't open ALSA mixer: Mixer element not found

I think that error message is very clear: your default mixer does not have a control called "Master". As I said in my first comment, use amixer scontents to see the available scontrols on the default mixer, and to check if they have dB scale support; and see the bluealsa-aplay manual page for how to use a different control name.

Okay, it turned out that the device name was "PCM", so I had to specify it on bluealsa-aplay. There's one issue now: if I set the volume from alsamixer to anything higher to 0dB, bluealsa-aplay will crash with an assertion error:

bluealsa-aplay: [79994] D: ../../../utils/aplay/aplay.c:692: Opening ALSA playback PCM: name=default channels=2 rate=96000
bluealsa-aplay: [79994] D: ../../../utils/aplay/aplay.c:708: Opening ALSA mixer: name=default elem=PCM index=0
bluealsa-aplay: [79994] D: ../../../utils/aplay/aplay.c:467: Setting up ALSA mixer volume synchronization
bluealsa-aplay: ../../../utils/aplay/aplay.c:369: io_worker_mixer_volume_sync_ba_pcm: Assertion `volume_db_sum <= 0LL' failed.
[1]    79993 abort      bluealsa-aplay --volume=mixer --mixer-name=PCM --mixer-index=0

The Raspberry Pi's maximum volume is at 4 dB, and 0 dB is 96% of it. Is it not possible to make it go up to 100%?

maximum volume is at 4 dB, and 0 dB is 96% of it. Is it not possible to make it go up to 100%?

That is a limitation of bluealsa-aplay's volume control logic. A dB scale represents the relative level of a quantity to some reference value, where 0dB represents the reference value. bluealsa-aplay assumes that the ALSA reference value is always "full volume". This is the first example I have seen where 100% volume is represented by a value > 0dB, and I cannot imagine what other reference value this driver uses. Unfortunately this invalidates an assumption within the bluealsa-aplay code. It looks like that assertion needs to be reconsidered, and perhaps the mapping from the AVRCP [0-127] scale to the ALSA dB scale needs to be revised. That will not happen in a hurry, if at all; so in the short term it looks liike the answer to your question is that it is not possible for bluealsa-aplay to make that control go to 100%, and if it is already set above 96% by alsamixer or whatever then that will trigger an assertion failure in bluealsa-aplay. I'm marking this issue as a feature request because perhaps it may be possible to improve this (I'm no expert in audio volume control, so can't guarantee that it is possible).

A little additional information. I've just found this: raspberrypi/linux#2982

So it seems this apparent anomaly is deliberately coded into the RPi audio driver, although the reasoning that it gives "a bit of extra boost" seems rather odd. The maximum volume is the same no matter if they choose to label it as 0dB or 4dB. How can a label make any difference? Anyway it is what it is. I wonder if this driver is unique in having this seemingly bizarre dB scale?

I don't know if only the Raspberry Pi has this 4dB, but at this point I think anyone could change this by just patching the kernel. I don't know if it can be any helpful, but in the amixer scontents command I noticed this line:

 Limits: Playback -10239 - 400

which are the minimum and maximum volume values.

Its been that way on Raspi since the early days. The 4dB is a software(or firmware) boost. I realize that it makes no difference for bluetooth, but at 0dB it produced bit perfect output on HDMI.

@Vinchethescript, please try this branch: https://github.com/arkq/bluez-alsa/tree/dbrange

This patch didn't change much, as it still reaches up to 0dB. This time, though, there's no assertion error anymore, but when I try to reach a higher value than 0dB through alsamixer or amixer sset PCM 100%, it gets back to 0dB. If I try to raise the volume using the arrow buttons, it instead lowers somehow (and I checked if it also happens when bluealsa-aplay isn't running and when audio is not playing - it doesn't).

arkq commented

This time, though, there's no assertion error anymore, but when I try to reach a higher value than 0dB through alsamixer or amixer sset PCM 100%, it gets back to 0dB.

Makes sense. I will try to test it locally then. But the approach will be to map 100% to max dB for selected mixer control.

I realize that it makes no difference for bluetooth, but at 0dB it produced bit perfect output on HDMI.

Would it make any sense to limit dB level to 0dB and do not allow this boost? My current approach would be to rescale 0-100% to "some very low arbitrary dB level" - "max dB of used mixer control". Such rescaling will allow this 4dB boost for 100% Bluetooth volume, but then it will be rather impossible to set 0dB when linked with Bluetooth controls.

For what it's worth, setting 0dB on a raspi was almost impossible to do with the graphical mixer. The only way we could do it reliably was to use amixer to set the dB to zero. So I don't see a big deal just scaling based on min and max reported from alsa.

arkq commented

@Vinchethescript I've just updated the dbrange branch. Please, try it again.

It now works great, and volume can go all the way up both when managed from my phone and from alsamixer. While testing, though, I got into another issue: if I stop the music, then change the volume from my phone after it gets marked as inactive and then play it again, the volume will get back to the previous value. If I change it through alsamixer, instead, it will set it to the value from alsamixer as soon as audio is played.

Update: it also just crashed because of an assertion error while the device was inactive:

bluealsa-aplay: [91798] D: ../../../utils/aplay/aplay.c:735: Opening ALSA playback PCM: name=default channels=2 rate=96000
bluealsa-aplay: [91798] D: ../../../utils/aplay/aplay.c:342: Opening ALSA mixer: name=default elem=PCM index=0
bluealsa-aplay: [91798] D: ../../../utils/aplay/aplay.c:510: Setting up ALSA mixer volume synchronization
bluealsa-aplay: [91798] D: ../../../utils/aplay/aplay.c:646: BT device marked as inactive: F0:39:65:CA:B4:4A
bluealsa-aplay: simple.c:270: snd_mixer_selem_has_playback_channel: Assertion `(elem)->type == SND_MIXER_ELEM_SIMPLE' failed.
[1]    91797 abort      bluealsa-aplay --volume=mixer --mixer-name=PCM --mixer-index=0

I don't know what caused this as I wasn't looking at the logs when it happened. I don't think it's related to this other issue, but I thought it was worth mentioning.

arkq commented

While testing, though, I got into another issue: if I stop the music, then change the volume from my phone after it gets marked as inactive and then play it again, the volume will get back to the previous value. If I change it through alsamixer, instead, it will set it to the value from alsamixer as soon as audio is played.

This behavior is by "design" :D. The case is that whenever the playback stops, bluealsa-aplay releases PCM and mixer, so others could use it (because bluealsa-aplay does not know for how long the playback has stopped). So when the playback mixer is released, it is not linked with Bluetooth mixer. Then, when playback starts, the mixers are linked and volume is synchronized again.

Update: it also just crashed because of an assertion error while the device was inactive:

Could you run bluealsa-aplay via gdb and in case of crash send here the backtrace?

Could you run bluealsa-aplay via gdb and in case of crash send here the backtrace?

For some reason, I can't seem to reproduce this error anymore. It happened randomly when I was not playing anything from the phone. Also, in the source code there's no file called simple.c, so I don't know where this assertion is located.

So when the playback mixer is released, it is not linked with Bluetooth mixer. Then, when playback starts, the mixers are linked and volume is synchronized again.

I see. This also means it can't set the volume as soon as the playback starts, right?

arkq commented

This also means it can't set the volume as soon as the playback starts, right?

The volume is synchronized just before the playback starts (from playback PCM to Bluetooth volume - speaker's volume is a master in that case). So, if the playback mixer has -60 dB and phone 100%, phone volume indicator should be turned down.

I see. Since the issue has been resolved, I think this can be closed. Thank you!