bluetooth: Update source software volume on AVRCP SetAbsoluteVolume

The A2DP spec mandates that the audio rendering device - the device
receiving audio, in our case a `pa_source` - is responsible for
performing attenuation:

AVRCP v1.6.2, §5.8:
    The SetAbsoluteVolume command is used to set an absolute volume to be used by the rendering device.

BlueZ models this call as a change of the `Volume` property on the
`org.bluez.MediaTransport1` interface.  Supporting Absolute Volume is
optional but BlueZ unconditionally reports feature category 2 in its
profile, mandating support.  Hence remote devices (ie. a phone) playing
back audio to a machine running PulseAudio assume volume is to be
changed through SetAbsoluteVolume, without performing any local
attenuation.

Future changes will implement this feature the other way around: setting
an initial value for the `Volume` property as well as propagating
`pa_source` volume changes back to the peer.

Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/239>
This commit is contained in:
Marijn Suijten 2021-03-27 10:53:07 +01:00 committed by PulseAudio Marge Bot
parent c098a75d10
commit 710a35cdc3
2 changed files with 45 additions and 0 deletions

View file

@ -101,6 +101,19 @@
" </interface>" \
"</node>"
static pa_volume_t a2dp_gain_to_volume(uint16_t gain) {
pa_volume_t volume = (pa_volume_t) ((
gain * PA_VOLUME_NORM
/* Round to closest by adding half the denominator */
+ A2DP_MAX_GAIN / 2
) / A2DP_MAX_GAIN);
if (volume > PA_VOLUME_NORM)
volume = PA_VOLUME_NORM;
return volume;
}
struct pa_bluetooth_discovery {
PA_REFCNT_DECLARE;
@ -496,6 +509,24 @@ void pa_bluetooth_transport_set_state(pa_bluetooth_transport *t, pa_bluetooth_tr
}
}
static void pa_bluetooth_transport_remote_volume_changed(pa_bluetooth_transport *t, uint16_t gain) {
pa_volume_t volume;
pa_assert(t);
volume = a2dp_gain_to_volume(gain);
/* increment volume by one to correct rounding errors */
if (volume < PA_VOLUME_NORM)
volume++;
if (t->source_volume == volume)
return;
t->source_volume = volume;
pa_hook_fire(pa_bluetooth_discovery_hook(t->device->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_SOURCE_VOLUME_CHANGED), t);
}
void pa_bluetooth_transport_put(pa_bluetooth_transport *t) {
pa_assert(t);
@ -679,6 +710,8 @@ static void parse_transport_property(pa_bluetooth_transport *t, DBusMessageIter
if (key == NULL)
return;
pa_log_debug("Transport property %s changed", key);
dbus_message_iter_recurse(i, &variant_i);
switch (dbus_message_iter_get_arg_type(&variant_i)) {
@ -701,6 +734,17 @@ static void parse_transport_property(pa_bluetooth_transport *t, DBusMessageIter
break;
}
case DBUS_TYPE_UINT16: {
uint16_t value;
dbus_message_iter_get_basic(&variant_i, &value);
if (pa_streq(key, "Volume")) {
if (t->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE)
pa_bluetooth_transport_remote_volume_changed(t, value);
}
break;
}
}
return;