A2DP sink volume depends on method of connecting to bluez
I stream audio from PulseAudio to Bose QuietComfort 35 headphones connected to bluez5
on a linux box. The device can be connected to the host via one of two "methods":
- "auto": device-initiated connection to the host after powering on the device
- "manual": host-initiated connection to the device by running
bluetoothctl connect <BD_ADDR>
These two connection methods do not give precisely the same device configuration. In particular, each has its own volume setting. The volume for the auto-connected device is much lower than that for the manually connected device. These independent volume settings are preserved for subsequent connections; that is, when disconnecting and reconnecting using the same method, the volume is perceptually the same.
Whether auto- or manually connected, pulseaudio
creates and routes audio to an A2DP sink corresponding to the device, driven by module-bluez5-device
. I am using SBC-family codecs and switching among them has no effect on the volume.
The pulseaudio configuration (pactl list
) is identical after auto- and manual connection, except for the absence of HW_VOLUME_CTRL
from the A2DP sink's flags after auto-connection (details below).
The transport properties also differ slightly after auto- and manual connection. First, the path of the audio fd
changes (it is a child of the device after auto-connection, whereas it is a child of a stream endpoint after manual connection). Second, a Volume
property is present only after manual connection.
auto-connection (device-initiated):
$ dbus-send --system --dest=org.bluez --print-reply=literal /org/bluez/hci0/dev_04_52_C7_11_22_33/fd21 org.freedesktop.DBus.Properties.GetAll string:org.bluez.MediaTransport1
array [
dict entry(
Device variant /org/bluez/hci0/dev_04_52_C7_11_22_33 )
dict entry(
UUID variant 0000110a-0000-1000-8000-00805f9b34fb )
dict entry(
Codec variant byte 0
)
dict entry(
Configuration variant array of bytes [
21 15 02 35
]
)
dict entry(
State variant active )
dict entry(
Delay variant uint16 1500
)
]
manual connection (host-initiated):
$ dbus-send --system --dest=org.bluez --print-reply=literal /org/bluez/hci0/dev_04_52_C7_11_22_33/sep1/fd19 org.freedesktop.DBus.Properties.GetAll string:org.bluez.MediaTransport1
array [
dict entry(
Device variant /org/bluez/hci0/dev_04_52_C7_11_22_33 )
dict entry(
UUID variant 0000110a-0000-1000-8000-00805f9b34fb )
dict entry(
Codec variant byte 0
)
dict entry(
Configuration variant array of bytes [
21 15 02 35
]
)
dict entry(
State variant active )
dict entry(
Volume variant uint16 5
)
]
Here is the diff
(<auto, >manual):
< Delay variant uint16 1500
---
> Volume variant uint16 5
The diff
shows that the Volume
parameter is only present for manual connection. The value of the volume parameter is roughly equal to the volume percentage shown by alsamixer
. Changing the volume in alsamixer
changes the volume shown by dbus
. Changing the headset volume (using the on-device controls) does not influence the volume shown by dbus
.
I think the volume should be independent of the connection method. Can this be configured?
I have discussed this with bluez
developers (issue 335), but it does not seem to be a bluez
issue. Additional bluetooth-related information is included in that issue (HCI and device information, bluetoothd
and btmon
logs).
Version information:
pulseaudio 15.0
bluez: 5.64
Bose QC35 firmware: v3.0.3
linux 5.16.0-6 (amd64)
$ pactl info
Server String: /run/user/1000/pulse/native
Library Protocol Version: 35
Server Protocol Version: 35
Is Local: yes
Client Index: 636520
Tile Size: 65472
User Name: $USER
Host Name: $HOSTNAME
Server Name: pulseaudio
Server Version: 15.0
Default Sample Specification: s16le 2ch 44100Hz
Default Channel Map: front-left,front-right
Default Sink: bluez_sink.04_52_C7_11_22_33.a2dp_sink
Default Source: alsa_input.pci-0000_00_1b.0.analog-stereo
Cookie: 6e49:be5a
$ pactl list #excerpt
Module #9
Name: module-bluez5-discover
Argument:
Usage counter: n/a
Properties:
module.author = "João Paulo Rechi Vita"
module.description = "Detect available BlueZ 5 Bluetooth audio devices and load BlueZ 5 Bluetooth audio drivers"
module.version = "15.0"
Module #35
Name: module-bluez5-device
Argument: path=/org/bluez/hci0/dev_04_52_C7_11_22_33 autodetect_mtu=0 output_rate_refresh_interval_ms=500 avrcp_absolute_volume=1
Usage counter: 1
Properties:
module.author = "João Paulo Rechi Vita"
module.description = "BlueZ 5 Bluetooth audio sink and source"
module.version = "15.0"
-----
Sink #16
State: RUNNING
Name: bluez_sink.04_52_C7_11_22_33.a2dp_sink
Description: Bose QuietComfort 35
Driver: module-bluez5-device.c
Sample Specification: s16le 2ch 44100Hz
Channel Map: front-left,front-right
Owner Module: 35
Mute: no
Volume: front-left: 2580 / 4% / -84.29 dB, front-right: 2580 / 4% / -84.29 dB
balance 0.00
Base Volume: 65536 / 100% / 0.00 dB
Monitor Source: bluez_sink.04_52_C7_11_22_33.a2dp_sink.monitor
Latency: 39619 usec, configured 39512 usec
Flags: HARDWARE HW_VOLUME_CTRL DECIBEL_VOLUME LATENCY
Properties:
bluetooth.protocol = "a2dp_sink"
bluetooth.codec = "sbc"
device.description = "Bose QuietComfort 35"
device.string = "04:52:C7:11:22:33"
device.api = "bluez"
device.class = "sound"
device.bus = "bluetooth"
device.form_factor = "headphone"
bluez.path = "/org/bluez/hci0/dev_04_52_C7_11_22_33"
bluez.class = "0x240418"
bluez.alias = "Bose QuietComfort 35"
device.icon_name = "audio-headphones-bluetooth"
Ports:
headphone-output: Headphone (type: Headphones, priority: 0, available)
Active Port: headphone-output
Formats:
pcm
-----
Source #17
State: IDLE
Name: bluez_sink.04_52_C7_11_22_33.a2dp_sink.monitor
Description: Monitor of Bose QuietComfort 35
Driver: module-bluez5-device.c
Sample Specification: s16le 2ch 44100Hz
Channel Map: front-left,front-right
Owner Module: 35
Mute: no
Volume: front-left: 65536 / 100% / 0.00 dB, front-right: 65536 / 100% / 0.00 dB
balance 0.00
Base Volume: 65536 / 100% / 0.00 dB
Monitor of Sink: bluez_sink.04_52_C7_11_22_33.a2dp_sink
Latency: 0 usec, configured 39512 usec
Flags: DECIBEL_VOLUME LATENCY
Properties:
device.description = "Monitor of Bose QuietComfort 35"
device.class = "monitor"
device.string = "04:52:C7:11:22:33"
device.api = "bluez"
device.bus = "bluetooth"
device.form_factor = "headphone"
bluez.path = "/org/bluez/hci0/dev_04_52_C7_11_22_33"
bluez.class = "0x240418"
bluez.alias = "Bose QuietComfort 35"
device.icon_name = "audio-headphones-bluetooth"
Formats:
pcm
-----
Sink Input #0
Driver: protocol-native.c
Owner Module: 10
Client: 159493
Sink: 16
Sample Specification: s16le 2ch 44100Hz
Channel Map: front-left,front-right
Format: pcm, format.sample_format = "\"s16le\"" format.rate = "44100" format.channels = "2" format.channel_map = "\"front-left,front-right\""
Corked: no
Mute: no
Volume: front-left: 65536 / 100% / 0.00 dB, front-right: 65536 / 100% / 0.00 dB
balance 0.00
Buffer Latency: 224263 usec
Sink Latency: 53707 usec
Resample method: n/a
Properties:
media.role = "music"
media.icon_name = "audio-x-generic"
media.name = "playback"
application.name = "C* Music Player"
native-protocol.peer = "UNIX socket client"
native-protocol.version = "35"
application.version = "v2.9.1"
application.process.id = "176703"
application.process.user = "$USER"
application.process.host = "$HOSTNAME"
application.process.binary = "cmus"
application.language = "C"
window.x11.display = ":0"
application.process.session_id = "2"
module-stream-restore.id = "sink-input-by-media-role:music"
-----
Card #16
Name: bluez_card.04_52_C7_11_22_33
Driver: module-bluez5-device.c
Owner Module: 35
Properties:
device.description = "Bose QuietComfort 35"
device.string = "04:52:C7:11:22:33"
device.api = "bluez"
device.class = "sound"
device.bus = "bluetooth"
device.form_factor = "headphone"
bluez.path = "/org/bluez/hci0/dev_04_52_C7_11_22_33"
bluez.class = "0x240418"
bluez.alias = "Bose QuietComfort 35"
device.icon_name = "audio-headphones-bluetooth"
bluetooth.codec = "sbc"
Profiles:
headset_head_unit: Headset Head Unit (HSP) (sinks: 1, sources: 1, priority: 30, available: no)
a2dp_sink: High Fidelity Playback (A2DP Sink) (sinks: 1, sources: 0, priority: 40, available: yes)
handsfree_head_unit: Handsfree Head Unit (HFP) (sinks: 1, sources: 1, priority: 30, available: yes)
off: Off (sinks: 0, sources: 0, priority: 0, available: yes)
Active Profile: a2dp_sink
Ports:
headphone-output: Headphone (type: Headphones, priority: 0, latency offset: 0 usec, available)
Part of profile(s): headset_head_unit, a2dp_sink, handsfree_head_unit
headphone-input: Bluetooth Input (type: Bluetooth, priority: 0, latency offset: 0 usec, availability unknown)
Part of profile(s): headset_head_unit, handsfree_head_unit
Differences between the output of pactl list
after auto-connection (<) and manual connection (>), excluding auto-renumbering of sinks, cards, etc.:
# A2DP Sink flags:
< Latency: 48573 usec, configured 39512 usec
< Flags: HARDWARE DECIBEL_VOLUME LATENCY
---
> Latency: 39619 usec, configured 39512 usec
> Flags: HARDWARE HW_VOLUME_CTRL DECIBEL_VOLUME LATENCY
# Sink Input #0:
< Buffer Latency: 220975 usec
< Sink Latency: 47639 usec
---
> Buffer Latency: 224263 usec
> Sink Latency: 53707 usec