summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarijn Suijten <marijns95@gmail.com>2021-03-05 23:28:57 +0100
committerMarijn Suijten <marijns95@gmail.com>2021-03-16 10:48:59 +0100
commitd510ddc7fbbce22d27a3f0dc7f47712059432c71 (patch)
tree47424dfba58dc6081c531767fb203199f83590b5
parent9c847b16a8068fc5982c9a706c76ac2b917d5c04 (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.c45
-rw-r--r--src/modules/bluetooth/module-bluez5-device.c12
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;
}