Commit 4e8c7fe4 authored by Marijn Suijten's avatar Marijn Suijten 🦀
Browse files

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.
parent 118ffca8
......@@ -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;
......
......@@ -52,6 +52,7 @@
#define PA_BLUETOOTH_UUID_HFP_HF "0000111e-0000-1000-8000-00805f9b34fb"
#define PA_BLUETOOTH_UUID_HFP_AG "0000111f-0000-1000-8000-00805f9b34fb"
#define A2DP_MAX_GAIN 127
#define HSP_MAX_GAIN 15
typedef struct pa_bluetooth_transport pa_bluetooth_transport;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment