diff options
author | Marijn Suijten <marijns95@gmail.com> | 2021-03-05 23:28:57 +0100 |
---|---|---|
committer | Marijn Suijten <marijns95@gmail.com> | 2021-03-16 10:48:59 +0100 |
commit | d510ddc7fbbce22d27a3f0dc7f47712059432c71 (patch) | |
tree | 47424dfba58dc6081c531767fb203199f83590b5 | |
parent | 9c847b16a8068fc5982c9a706c76ac2b917d5c04 (diff) |
bluetooth: Perform software attenuation until HF/HS reports gain control
HF/HS hardware attenuation is optional on HFP: the peer indicates
support with the AT+BRSF command, when bit 4 is set. That does not
explicitly mandate speaker or microphone gain control; either is
dynamically detected as soon as `AT+VG[MS]=` is received. Otherwise
software attenuation is performed.
It is also optional on HSP but nothing is mentioned about feature
detection, assume it is the same as HFP: perform software attenuation
until the HF/HS peer sends an `AT+VG[MS]=` command.
When PA is a HS/HF (and the peer the AG) we attenuate both channels in
software and unconditionally keep the peer up to date with
`AT+VGM/AT+VGS` commands.
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/521>
-rw-r--r-- | src/modules/bluetooth/backend-native.c | 45 | ||||
-rw-r--r-- | src/modules/bluetooth/module-bluez5-device.c | 12 |
2 files changed, 42 insertions, 15 deletions
diff --git a/src/modules/bluetooth/backend-native.c b/src/modules/bluetooth/backend-native.c index 5a6238752..b47c36ba3 100644 --- a/src/modules/bluetooth/backend-native.c +++ b/src/modules/bluetooth/backend-native.c @@ -473,6 +473,9 @@ static void transport_put(pa_bluetooth_transport *t) pa_log_debug("Transport %s available for profile %s", t->path, pa_bluetooth_profile_to_string(t->profile)); } +static pa_volume_t set_sink_volume(pa_bluetooth_transport *t, pa_volume_t volume); +static pa_volume_t set_source_volume(pa_bluetooth_transport *t, pa_volume_t volume); + static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf) { struct hfp_config *c = t->config; @@ -480,11 +483,12 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf /* stateful negotiation */ if (c->state == 0 && sscanf(buf, "AT+BRSF=%d", &val) == 1) { - c->capabilities = val; - pa_log_info("HFP capabilities returns 0x%x", val); - rfcomm_write_response(fd, "+BRSF: %d", hfp_features); - c->state = 1; - return true; + c->capabilities = val; + pa_log_info("HFP capabilities returns 0x%x", val); + rfcomm_write_response(fd, "+BRSF: %d", hfp_features); + c->state = 1; + + return true; } else if (c->state == 1 && pa_startswith(buf, "AT+CIND=?")) { /* we declare minimal no indicators */ rfcomm_write_response(fd, "+CIND: " @@ -557,11 +561,21 @@ static void rfcomm_io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_i * RING: Sent by AG to HS to notify of an incoming call. It can safely be ignored because * it does not expect a reply. */ if (sscanf(buf, "AT+VGS=%d", &gain) == 1 || sscanf(buf, "\r\n+VGM=%d\r\n", &gain) == 1) { + if (!t->set_sink_volume) { + pa_log_debug("HS/HF peer supports speaker gain control"); + t->set_sink_volume = set_sink_volume; + } + t->sink_volume = hsp_gain_to_volume(gain); pa_hook_fire(pa_bluetooth_discovery_hook(t->device->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_SINK_VOLUME_CHANGED), t); do_reply = true; } else if (sscanf(buf, "AT+VGM=%d", &gain) == 1 || sscanf(buf, "\r\n+VGS=%d\r\n", &gain) == 1) { + if (!t->set_source_volume) { + pa_log_debug("HS/HF peer supports microphone gain control"); + t->set_source_volume = set_source_volume; + } + t->source_volume = hsp_gain_to_volume(gain); pa_hook_fire(pa_bluetooth_discovery_hook(t->device->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_SOURCE_VOLUME_CHANGED), t); do_reply = true; @@ -717,8 +731,25 @@ static DBusMessage *profile_new_connection(DBusConnection *conn, DBusMessage *m, t->acquire = sco_acquire_cb; t->release = sco_release_cb; t->destroy = transport_destroy; - t->set_sink_volume = set_sink_volume; - t->set_source_volume = set_source_volume; + + /* If PA is the HF/HS we are in control of volume attenuation and + * can always send volume commands (notifications) to keep the peer + * updated on actual volume value. + * + * If the peer is the HF/HS it is responsible for attenuation of both + * speaker and microphone gain. + * On HFP speaker/microphone gain support is reported by bit 4 in the + * `AT+BRSF=` command. Since it isn't explicitly documented whether this + * applies to speaker or microphone gain but the peer is required to send + * an initial value with `AT+VG[MS]=` either callback is hooked + * independently as soon as this command is received. + * On HSP this is not specified and is assumed to be dynamic for both + * speaker and microphone. + */ + if (is_peer_audio_gateway(p)) { + t->set_sink_volume = set_sink_volume; + t->set_source_volume = set_source_volume; + } trd = pa_xnew0(struct transport_data, 1); trd->rfcomm_fd = fd; diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c index fab951f35..449586135 100644 --- a/src/modules/bluetooth/module-bluez5-device.c +++ b/src/modules/bluetooth/module-bluez5-device.c @@ -1041,10 +1041,8 @@ static int add_source(struct userdata *u) { u->source->parent.process_msg = source_process_msg; u->source->set_state_in_io_thread = source_set_state_in_io_thread_cb; - if (u->profile == PA_BLUETOOTH_PROFILE_HSP_HS - || u->profile == PA_BLUETOOTH_PROFILE_HSP_AG - || u->profile == PA_BLUETOOTH_PROFILE_HFP_AG - || u->profile == PA_BLUETOOTH_PROFILE_HFP_HF) { + if (u->transport->set_source_volume) { + pa_log_debug("Peer supports microphone gain control"); pa_source_set_set_volume_callback(u->source, source_set_volume_cb); u->source->n_volume_steps = 16; } @@ -1222,10 +1220,8 @@ static int add_sink(struct userdata *u) { u->sink->parent.process_msg = sink_process_msg; u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb; - if (u->profile == PA_BLUETOOTH_PROFILE_HSP_HS - || u->profile == PA_BLUETOOTH_PROFILE_HSP_AG - || u->profile == PA_BLUETOOTH_PROFILE_HFP_AG - || u->profile == PA_BLUETOOTH_PROFILE_HFP_HF) { + if (u->transport->set_sink_volume) { + pa_log_debug("Peer supports speaker gain control"); pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb); u->sink->n_volume_steps = 16; } |