[pulseaudio-discuss] [RFC 1/2] bluetooth: Add MediaTransport volume management

Frédéric Danis frederic.danis at linux.intel.com
Mon Nov 12 07:19:09 PST 2012


With BlueZ 5 volume updates for HFP will go through transport interface.

Note that this is backward compatible.
---
 src/modules/bluetooth/bluetooth-util.c          |   62 +++++++++++++++----
 src/modules/bluetooth/bluetooth-util.h          |    4 ++
 src/modules/bluetooth/module-bluetooth-device.c |   75 +++++++++++++++++++++++
 3 files changed, 130 insertions(+), 11 deletions(-)

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index 272b6ce..c0d5287 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -754,21 +754,49 @@ int pa_bluetooth_transport_parse_property(pa_bluetooth_transport *t, DBusMessage
 
     dbus_message_iter_recurse(i, &variant_i);
 
-    switch (dbus_message_iter_get_arg_type(&variant_i)) {
+    if (pa_streq(key, "NREC")) {
+        dbus_bool_t value;
 
-        case DBUS_TYPE_BOOLEAN: {
+        if (dbus_message_iter_get_arg_type(&variant_i) != DBUS_TYPE_BOOLEAN) {
+            pa_log("Property value not a boolean.");
+            return -1;
+        }
 
-            dbus_bool_t value;
-            dbus_message_iter_get_basic(&variant_i, &value);
+        dbus_message_iter_get_basic(&variant_i, &value);
 
-            if (pa_streq(key, "NREC") && t->nrec != value) {
-                t->nrec = value;
-                pa_log_debug("Transport %s: Property 'NREC' changed to %s.", t->path, t->nrec ? "True" : "False");
-                pa_hook_fire(&t->hooks[PA_BLUETOOTH_TRANSPORT_HOOK_NREC_CHANGED], NULL);
-            }
+        if (t->nrec != value) {
+            t->nrec = value;
+            pa_log_debug("Transport %s: Property 'NREC' changed to %s.", t->path, t->nrec ? "True" : "False");
+            pa_hook_fire(&t->hooks[PA_BLUETOOTH_TRANSPORT_HOOK_NREC_CHANGED], NULL);
+        }
+    } else if(pa_streq(key,"OutputGain")) {
+        uint16_t value;
 
-            break;
-         }
+        if (dbus_message_iter_get_arg_type(&variant_i) != DBUS_TYPE_UINT16) {
+            pa_log("Property value not an uint16.");
+            return -1;
+        }
+
+        dbus_message_iter_get_basic(&variant_i, &value);
+
+        if (t->output_gain != value) {
+            t->output_gain = value;
+            pa_hook_fire(&t->hooks[PA_BLUETOOTH_TRANSPORT_HOOK_OUTPUT_GAIN_CHANGED], NULL);
+        }
+    } else if(pa_streq(key,"InputGain")) {
+        uint16_t value;
+
+        if (dbus_message_iter_get_arg_type(&variant_i) != DBUS_TYPE_UINT16) {
+            pa_log("Property value not an uint16.");
+            return -1;
+        }
+
+        dbus_message_iter_get_basic(&variant_i, &value);
+
+        if (t->input_gain != value) {
+            t->input_gain = value;
+            pa_hook_fire(&t->hooks[PA_BLUETOOTH_TRANSPORT_HOOK_INPUT_GAIN_CHANGED], NULL);
+        }
     }
 
     return 0;
@@ -1104,6 +1132,8 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage
     uint8_t *config = NULL;
     int size = 0;
     pa_bool_t nrec = FALSE;
+    uint16_t output_gain = 15;
+    uint16_t input_gain = 15;
     enum profile p;
     DBusMessageIter args, props;
     DBusMessage *r;
@@ -1151,6 +1181,14 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage
                 goto fail;
             dbus_message_iter_recurse(&value, &array);
             dbus_message_iter_get_fixed_array(&array, &config, &size);
+        } else if (strcasecmp(key, "OutputGain") == 0) {
+            if (var != DBUS_TYPE_UINT16)
+                goto fail;
+            dbus_message_iter_get_basic(&value, &output_gain);
+        } else if (strcasecmp(key, "InputGain") == 0) {
+            if (var != DBUS_TYPE_UINT16)
+                goto fail;
+            dbus_message_iter_get_basic(&value, &input_gain);
         }
 
         dbus_message_iter_next(&props);
@@ -1172,6 +1210,8 @@ static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage
     t = transport_new(y, path, p, config, size);
     if (nrec)
         t->nrec = nrec;
+    t->output_gain = output_gain;
+    t->input_gain = input_gain;
     pa_hashmap_put(d->transports, t->path, t);
 
     pa_log_debug("Transport %s profile %d available", t->path, t->profile);
diff --git a/src/modules/bluetooth/bluetooth-util.h b/src/modules/bluetooth/bluetooth-util.h
index 8a3f2ad..5fba105 100644
--- a/src/modules/bluetooth/bluetooth-util.h
+++ b/src/modules/bluetooth/bluetooth-util.h
@@ -67,6 +67,8 @@ enum profile {
 typedef enum pa_bluetooth_transport_hook {
     PA_BLUETOOTH_TRANSPORT_HOOK_NREC_CHANGED, /* Call data: NULL. */
     PA_BLUETOOTH_TRANSPORT_HOOK_REMOVED, /* Call data: NULL. */
+    PA_BLUETOOTH_TRANSPORT_HOOK_OUTPUT_GAIN_CHANGED, /* Call data: NULL. */
+    PA_BLUETOOTH_TRANSPORT_HOOK_INPUT_GAIN_CHANGED, /* Call data: NULL. */
     PA_BLUETOOTH_TRANSPORT_HOOK_MAX
 } pa_bluetooth_transport_hook_t;
 
@@ -78,6 +80,8 @@ struct pa_bluetooth_transport {
     uint8_t *config;
     int config_size;
     pa_bool_t nrec;
+    uint16_t output_gain;
+    uint16_t input_gain;
 
     pa_hook hooks[PA_BLUETOOTH_TRANSPORT_HOOK_MAX];
 };
diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 8c2efa4..656852d 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -124,6 +124,8 @@ struct hsp_info {
     pa_hook_slot *sink_state_changed_slot;
     pa_hook_slot *source_state_changed_slot;
     pa_hook_slot *nrec_changed_slot;
+    pa_hook_slot *output_gain_slot;
+    pa_hook_slot *input_gain_slot;
 };
 
 struct bluetooth_msg {
@@ -1423,6 +1425,19 @@ fail:
     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 }
 
+static void send_property_update(struct userdata *u, const char *name, int type, void *data)
+{
+    DBusMessage *m;
+    DBusMessageIter iter;
+
+    pa_assert_se(m = dbus_message_new_method_call("org.bluez", u->transport->path, "org.bluez.MediaTransport", "SetProperty"));
+    dbus_message_iter_init_append(m, &iter);
+    pa_assert_se(dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &name));
+    pa_dbus_append_basic_variant(&iter, DBUS_TYPE_UINT16, data);
+    pa_assert_se(dbus_connection_send(pa_dbus_connection_get(u->connection), m, NULL));
+    dbus_message_unref(m);
+}
+
 /* Run from main thread */
 static void sink_set_volume_cb(pa_sink *s) {
     DBusMessage *m;
@@ -1430,6 +1445,7 @@ static void sink_set_volume_cb(pa_sink *s) {
     pa_volume_t volume;
     struct userdata *u;
     char *k;
+    const char *name = "OutputGain";
 
     pa_assert(s);
     pa_assert(s->core);
@@ -1459,6 +1475,8 @@ static void sink_set_volume_cb(pa_sink *s) {
     pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID));
     pa_assert_se(dbus_connection_send(pa_dbus_connection_get(u->connection), m, NULL));
     dbus_message_unref(m);
+
+    send_property_update(u, name, DBUS_TYPE_UINT16, &gain);
 }
 
 /* Run from main thread */
@@ -1468,6 +1486,7 @@ static void source_set_volume_cb(pa_source *s) {
     pa_volume_t volume;
     struct userdata *u;
     char *k;
+    const char *name = "InputGain";
 
     pa_assert(s);
     pa_assert(s->core);
@@ -1497,6 +1516,8 @@ static void source_set_volume_cb(pa_source *s) {
     pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID));
     pa_assert_se(dbus_connection_send(pa_dbus_connection_get(u->connection), m, NULL));
     dbus_message_unref(m);
+
+    send_property_update(u, name, DBUS_TYPE_UINT16, &gain);
 }
 
 /* Run from main thread */
@@ -1599,6 +1620,44 @@ static pa_hook_result_t nrec_changed_cb(pa_bluetooth_transport *t, void *call_da
     return PA_HOOK_OK;
 }
 
+static pa_hook_result_t output_gain_changed_cb(pa_bluetooth_transport *t, void *call_data, struct userdata *u) {
+    pa_volume_t volume;
+    pa_cvolume v;
+
+    pa_assert(t);
+    pa_assert(u);
+
+    volume = (pa_volume_t) (t->output_gain * PA_VOLUME_NORM / HSP_MAX_GAIN);
+
+    /* increment volume by one to correct rounding errors */
+    if (volume < PA_VOLUME_NORM)
+        volume++;
+
+    pa_cvolume_set(&v, u->sample_spec.channels, volume);
+    pa_sink_volume_changed(u->sink, &v);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t input_gain_changed_cb(pa_bluetooth_transport *t, void *call_data, struct userdata *u) {
+    pa_volume_t volume;
+    pa_cvolume v;
+
+    pa_assert(t);
+    pa_assert(u);
+
+    volume = (pa_volume_t) (t->input_gain * PA_VOLUME_NORM / HSP_MAX_GAIN);
+
+    /* increment volume by one to correct rounding errors */
+    if (volume < PA_VOLUME_NORM)
+        volume++;
+
+    pa_cvolume_set(&v, u->sample_spec.channels, volume);
+    pa_source_volume_changed(u->source, &v);
+
+    return PA_HOOK_OK;
+}
+
 static void connect_ports(struct userdata *u, void *sink_or_source_new_data, pa_direction_t direction) {
     union {
         pa_sink_new_data *sink_new_data;
@@ -1748,6 +1807,9 @@ static int add_sink(struct userdata *u) {
         k = pa_sprintf_malloc("bluetooth-device@%p", (void*) u->sink);
         pa_shared_set(u->core, k, u);
         pa_xfree(k);
+
+        if (u->transport && !u->hsp.output_gain_slot)
+            u->hsp.output_gain_slot = pa_hook_connect(&u->transport->hooks[PA_BLUETOOTH_TRANSPORT_HOOK_OUTPUT_GAIN_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) output_gain_changed_cb, u);
     }
 
     return 0;
@@ -1832,6 +1894,9 @@ static int add_source(struct userdata *u) {
         k = pa_sprintf_malloc("bluetooth-device@%p", (void*) u->source);
         pa_shared_set(u->core, k, u);
         pa_xfree(k);
+
+        if (u->transport && !u->hsp.input_gain_slot)
+            u->hsp.input_gain_slot = pa_hook_connect(&u->transport->hooks[PA_BLUETOOTH_TRANSPORT_HOOK_INPUT_GAIN_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) input_gain_changed_cb, u);
     }
 
     return 0;
@@ -2057,6 +2122,16 @@ static void stop_thread(struct userdata *u) {
         u->hsp.nrec_changed_slot = NULL;
     }
 
+    if (u->hsp.output_gain_slot) {
+        pa_hook_slot_free(u->hsp.output_gain_slot);
+        u->hsp.output_gain_slot = NULL;
+    }
+
+    if (u->hsp.input_gain_slot) {
+        pa_hook_slot_free(u->hsp.input_gain_slot);
+        u->hsp.input_gain_slot = NULL;
+    }
+
     if (u->transport_removed_slot) {
         pa_hook_slot_free(u->transport_removed_slot);
         u->transport_removed_slot = NULL;
-- 
1.7.9.5



More information about the pulseaudio-discuss mailing list