From 9c847b16a8068fc5982c9a706c76ac2b917d5c04 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Thu, 4 Mar 2021 11:30:42 +0100 Subject: [PATCH] bluetooth: Move attenuation decision to shared function Generalize the distinction between local and peer-attenuated volumes into a function, paving the way for future changes where this needs to be checked in more places and when A2DP Absolute Volume support is added. Part-of: --- src/modules/bluetooth/backend-native.c | 35 +++++++++++++++----- src/modules/bluetooth/bluez5-util.c | 28 ++++++++++++++++ src/modules/bluetooth/bluez5-util.h | 1 + src/modules/bluetooth/module-bluez5-device.c | 16 ++++----- 4 files changed, 64 insertions(+), 16 deletions(-) diff --git a/src/modules/bluetooth/backend-native.c b/src/modules/bluetooth/backend-native.c index 126cec552..5a6238752 100644 --- a/src/modules/bluetooth/backend-native.c +++ b/src/modules/bluetooth/backend-native.c @@ -145,6 +145,23 @@ static uint16_t volume_to_hsp_gain(pa_volume_t volume) { return gain; } +static bool is_peer_audio_gateway(pa_bluetooth_profile_t peer_profile) { + switch(peer_profile) { + case PA_BLUETOOTH_PROFILE_HFP_HF: + case PA_BLUETOOTH_PROFILE_HSP_HS: + return false; + case PA_BLUETOOTH_PROFILE_HFP_AG: + case PA_BLUETOOTH_PROFILE_HSP_AG: + return true; + default: + pa_assert_not_reached(); + } +} + +static bool is_pulseaudio_audio_gateway(pa_bluetooth_profile_t peer_profile) { + return !is_peer_audio_gateway(peer_profile); +} + static pa_dbus_pending* send_and_add_to_pending(pa_bluetooth_backend *backend, DBusMessage *m, DBusPendingCallNotifyFunction func, void *call_data) { @@ -595,10 +612,11 @@ static pa_volume_t set_sink_volume(pa_bluetooth_transport *t, pa_volume_t volume t->sink_volume = volume; - /* If we are in the AG role, we send a command to the head set to change - * the speaker gain. In the HS role, source and sink are swapped, so - * in this case we notify the AG that the microphone gain has changed */ - if (t->profile == PA_BLUETOOTH_PROFILE_HSP_HS || t->profile == PA_BLUETOOTH_PROFILE_HFP_HF) { + /* If we are in the AG role, we send an unsolicited result-code to the headset + * to change the speaker gain. In the HS role, source and sink are swapped, + * so in this case we notify the AG that the microphone gain has changed + * by sending a command. */ + if (is_pulseaudio_audio_gateway(t->profile)) { rfcomm_write_response(trd->rfcomm_fd, "+VGS=%d", gain); } else { rfcomm_write_command(trd->rfcomm_fd, "AT+VGM=%d", gain); @@ -619,10 +637,11 @@ static pa_volume_t set_source_volume(pa_bluetooth_transport *t, pa_volume_t volu t->source_volume = volume; - /* If we are in the AG role, we send a command to the head set to change - * the microphone gain. In the HS role, source and sink are swapped, so - * in this case we notify the AG that the speaker gain has changed */ - if (t->profile == PA_BLUETOOTH_PROFILE_HSP_HS || t->profile == PA_BLUETOOTH_PROFILE_HFP_HF) { + /* If we are in the AG role, we send an unsolicited result-code to the headset + * to change the microphone gain. In the HS role, source and sink are swapped, + * so in this case we notify the AG that the speaker gain has changed + * by sending a command. */ + if (is_pulseaudio_audio_gateway(t->profile)) { rfcomm_write_response(trd->rfcomm_fd, "+VGM=%d", gain); } else { rfcomm_write_command(trd->rfcomm_fd, "AT+VGS=%d", gain); diff --git a/src/modules/bluetooth/bluez5-util.c b/src/modules/bluetooth/bluez5-util.c index 9c3f137a1..b1df0a977 100644 --- a/src/modules/bluetooth/bluez5-util.c +++ b/src/modules/bluetooth/bluez5-util.c @@ -1738,6 +1738,34 @@ const char *pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile) { return NULL; } +/* Returns true when PA has to perform attenuation, false if this is the + * responsibility of the peer. + * + * `peer_profile` is the profile of the peer. + * + * When the peer is in the HFP/HSP Audio Gateway role (PA is in headset role) PA + * has to perform attenuation on both the incoming and outgoing stream. In the + * HandsFree/HeadSet role both are attenuated on the peer. + */ +bool pa_bluetooth_profile_should_attenuate_volume(pa_bluetooth_profile_t peer_profile) { + switch(peer_profile) { + case PA_BLUETOOTH_PROFILE_A2DP_SINK: + /* Will be set to false when A2DP absolute volume is supported */ + return true; + case PA_BLUETOOTH_PROFILE_A2DP_SOURCE: + return true; + case PA_BLUETOOTH_PROFILE_HFP_HF: + case PA_BLUETOOTH_PROFILE_HSP_HS: + return false; + case PA_BLUETOOTH_PROFILE_HFP_AG: + case PA_BLUETOOTH_PROFILE_HSP_AG: + return true; + case PA_BLUETOOTH_PROFILE_OFF: + pa_assert_not_reached(); + } + pa_assert_not_reached(); +} + static const pa_a2dp_codec *a2dp_endpoint_to_a2dp_codec(const char *endpoint) { const char *codec_name; diff --git a/src/modules/bluetooth/bluez5-util.h b/src/modules/bluetooth/bluez5-util.h index f56c4d2a5..2121d3162 100644 --- a/src/modules/bluetooth/bluez5-util.h +++ b/src/modules/bluetooth/bluez5-util.h @@ -191,6 +191,7 @@ pa_bluetooth_device* pa_bluetooth_discovery_get_device_by_address(pa_bluetooth_d pa_hook* pa_bluetooth_discovery_hook(pa_bluetooth_discovery *y, pa_bluetooth_hook_t hook); const char *pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile); +bool pa_bluetooth_profile_should_attenuate_volume(pa_bluetooth_profile_t profile); static inline bool pa_bluetooth_uuid_is_hsp_hs(const char *uuid) { return pa_streq(uuid, PA_BLUETOOTH_UUID_HSP_HS) || pa_streq(uuid, PA_BLUETOOTH_UUID_HSP_HS_ALT); diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c index 7874f6828..fab951f35 100644 --- a/src/modules/bluetooth/module-bluez5-device.c +++ b/src/modules/bluetooth/module-bluez5-device.c @@ -982,7 +982,7 @@ static void source_set_volume_cb(pa_source *s) { pa_cvolume_set(&s->real_volume, u->decoder_sample_spec.channels, volume); /* Set soft volume when in headset role */ - if (u->profile == PA_BLUETOOTH_PROFILE_HFP_AG || u->profile == PA_BLUETOOTH_PROFILE_HSP_AG) + if (pa_bluetooth_profile_should_attenuate_volume(u->profile)) pa_cvolume_set(&s->soft_volume, u->decoder_sample_spec.channels, volume); } @@ -1162,7 +1162,7 @@ static void sink_set_volume_cb(pa_sink *s) { pa_cvolume_set(&s->real_volume, u->encoder_sample_spec.channels, volume); /* Set soft volume when in headset role */ - if (u->profile == PA_BLUETOOTH_PROFILE_HFP_AG || u->profile == PA_BLUETOOTH_PROFILE_HSP_AG) + if (pa_bluetooth_profile_should_attenuate_volume(u->profile)) pa_cvolume_set(&s->soft_volume, u->encoder_sample_spec.channels, volume); } @@ -2257,10 +2257,10 @@ static pa_hook_result_t transport_sink_volume_changed_cb(pa_bluetooth_discovery volume = t->sink_volume; pa_cvolume_set(&v, u->encoder_sample_spec.channels, volume); - if (t->profile == PA_BLUETOOTH_PROFILE_HSP_HS || t->profile == PA_BLUETOOTH_PROFILE_HFP_HF) - pa_sink_volume_changed(u->sink, &v); - else + if (pa_bluetooth_profile_should_attenuate_volume(t->profile)) pa_sink_set_volume(u->sink, &v, true, true); + else + pa_sink_volume_changed(u->sink, &v); return PA_HOOK_OK; } @@ -2279,10 +2279,10 @@ static pa_hook_result_t transport_source_volume_changed_cb(pa_bluetooth_discover pa_cvolume_set(&v, u->decoder_sample_spec.channels, volume); - if (t->profile == PA_BLUETOOTH_PROFILE_HSP_HS || t->profile == PA_BLUETOOTH_PROFILE_HFP_HF) - pa_source_volume_changed(u->source, &v); - else + if (pa_bluetooth_profile_should_attenuate_volume(t->profile)) pa_source_set_volume(u->source, &v, true, true); + else + pa_source_volume_changed(u->source, &v); return PA_HOOK_OK; }