summaryrefslogtreecommitdiffstats
path: root/src/modules
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules')
-rw-r--r--src/modules/bluetooth/bluetooth-util.c864
-rw-r--r--src/modules/bluetooth/bluetooth-util.h81
-rw-r--r--src/modules/bluetooth/ipc.c2
-rw-r--r--src/modules/bluetooth/ipc.h7
-rw-r--r--src/modules/bluetooth/module-bluetooth-device.c1829
-rw-r--r--src/modules/bluetooth/module-bluetooth-discover.c839
-rw-r--r--src/modules/bluetooth/rtp.h46
-rw-r--r--src/modules/bluetooth/sbc.c375
-rw-r--r--src/modules/bluetooth/sbc.h1
-rw-r--r--src/modules/bluetooth/sbc_math.h2
-rw-r--r--src/modules/bluetooth/sbc_primitives.c469
-rw-r--r--src/modules/bluetooth/sbc_primitives.h74
-rw-r--r--src/modules/bluetooth/sbc_primitives_mmx.c319
-rw-r--r--src/modules/bluetooth/sbc_primitives_mmx.h40
-rw-r--r--src/modules/bluetooth/sbc_primitives_neon.c245
-rw-r--r--src/modules/bluetooth/sbc_primitives_neon.h40
-rw-r--r--src/modules/bluetooth/sbc_tables.h436
-rw-r--r--src/modules/module-card-restore.c13
-rw-r--r--src/modules/module-combine.c2
-rw-r--r--src/modules/module-device-restore.c15
-rw-r--r--src/modules/module-ladspa-sink.c2
-rw-r--r--src/modules/module-pipe-sink.c4
-rw-r--r--src/modules/module-position-event-sounds.c12
-rw-r--r--src/modules/module-raop-sink.c65
-rw-r--r--src/modules/module-remap-sink.c2
-rw-r--r--src/modules/module-sine.c2
-rw-r--r--src/modules/module-stream-restore.c73
-rw-r--r--src/modules/rtp/module-rtp-recv.c2
-rw-r--r--src/modules/rtp/module-rtp-send.c2
29 files changed, 4063 insertions, 1800 deletions
diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
new file mode 100644
index 00000000..7855c2ef
--- /dev/null
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -0,0 +1,864 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2008 Joao Paulo Rechi Vita
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/core-util.h>
+#include <modules/dbus-util.h>
+
+#include "bluetooth-util.h"
+
+enum mode {
+ MODE_FIND,
+ MODE_GET,
+ MODE_DISCOVER
+};
+
+struct pa_bluetooth_discovery {
+ DBusConnection *connection;
+ PA_LLIST_HEAD(pa_dbus_pending, pending);
+
+ enum mode mode;
+
+ /* If mode == MODE_FIND look for a specific device by its address.
+ If mode == MODE_GET look for a specific device by its path. */
+ const char *looking_for;
+ pa_bluetooth_device *found_device;
+
+ /* If looking_for is NULL we do long-time discovery */
+ pa_hashmap *devices;
+ pa_bluetooth_device_callback_t callback;
+ struct userdata *userdata;
+};
+
+static pa_bluetooth_uuid *uuid_new(const char *uuid) {
+ pa_bluetooth_uuid *u;
+
+ u = pa_xnew(pa_bluetooth_uuid, 1);
+ u->uuid = pa_xstrdup(uuid);
+ PA_LLIST_INIT(pa_bluetooth_uuid, u);
+
+ return u;
+}
+
+static void uuid_free(pa_bluetooth_uuid *u) {
+ pa_assert(u);
+
+ pa_xfree(u->uuid);
+ pa_xfree(u);
+}
+
+static pa_bluetooth_device* device_new(const char *path) {
+ pa_bluetooth_device *d;
+
+ d = pa_xnew(pa_bluetooth_device, 1);
+
+ d->device_info_valid = d->audio_sink_info_valid = d->headset_info_valid = 0;
+
+ d->data = NULL;
+
+ d->name = NULL;
+ d->path = pa_xstrdup(path);
+ d->paired = -1;
+ d->alias = NULL;
+ d->device_connected = -1;
+ PA_LLIST_HEAD_INIT(pa_bluetooth_uuid, d->uuids);
+ d->address = NULL;
+ d->class = -1;
+ d->trusted = -1;
+
+ d->audio_sink_connected = -1;
+
+ d->headset_connected = -1;
+
+ return d;
+}
+
+void pa_bluetooth_device_free(pa_bluetooth_device *d) {
+ pa_bluetooth_uuid *u;
+
+ pa_assert(d);
+
+ while ((u = d->uuids)) {
+ PA_LLIST_REMOVE(pa_bluetooth_uuid, d->uuids, u);
+ uuid_free(u);
+ }
+
+ pa_xfree(d->name);
+ pa_xfree(d->path);
+ pa_xfree(d->alias);
+ pa_xfree(d->address);
+ pa_xfree(d);
+}
+
+static pa_bool_t device_is_loaded(pa_bluetooth_device *d) {
+ pa_assert(d);
+
+ return d->device_info_valid && d->audio_sink_info_valid && d->headset_info_valid;
+}
+
+static pa_bool_t device_is_audio(pa_bluetooth_device *d) {
+ pa_assert(d);
+
+ pa_assert(d->device_info_valid);
+ pa_assert(d->audio_sink_info_valid);
+ pa_assert(d->headset_info_valid);
+
+ return d->device_info_valid > 0 &&
+ (d->audio_sink_info_valid > 0 || d->headset_info_valid > 0);
+}
+
+static int parse_device_property(pa_bluetooth_discovery *y, pa_bluetooth_device *d, DBusMessageIter *i) {
+ const char *key;
+ DBusMessageIter variant_i;
+
+ pa_assert(y);
+ pa_assert(d);
+ pa_assert(i);
+
+ if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) {
+ pa_log("Property name not a string.");
+ return -1;
+ }
+
+ dbus_message_iter_get_basic(i, &key);
+
+ if (!dbus_message_iter_next(i)) {
+ pa_log("Property value missing");
+ return -1;
+ }
+
+ if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_VARIANT) {
+ pa_log("Property value not a variant.");
+ return -1;
+ }
+
+ dbus_message_iter_recurse(i, &variant_i);
+
+/* pa_log_debug("Parsing property org.bluez.Device.%s", key); */
+
+ switch (dbus_message_iter_get_arg_type(&variant_i)) {
+
+ case DBUS_TYPE_STRING: {
+
+ const char *value;
+ dbus_message_iter_get_basic(&variant_i, &value);
+
+ if (pa_streq(key, "Name")) {
+ pa_xfree(d->name);
+ d->name = pa_xstrdup(value);
+ } else if (pa_streq(key, "Alias")) {
+ pa_xfree(d->alias);
+ d->alias = pa_xstrdup(value);
+ } else if (pa_streq(key, "Address")) {
+ pa_xfree(d->address);
+ d->address = pa_xstrdup(value);
+ }
+
+/* pa_log_debug("Value %s", value); */
+
+ break;
+ }
+
+ case DBUS_TYPE_BOOLEAN: {
+
+ dbus_bool_t value;
+ dbus_message_iter_get_basic(&variant_i, &value);
+
+ if (pa_streq(key, "Paired"))
+ d->paired = !!value;
+ else if (pa_streq(key, "Connected"))
+ d->device_connected = !!value;
+ else if (pa_streq(key, "Trusted"))
+ d->trusted = !!value;
+
+/* pa_log_debug("Value %s", pa_yes_no(value)); */
+
+ break;
+ }
+
+ case DBUS_TYPE_UINT32: {
+
+ uint32_t value;
+ dbus_message_iter_get_basic(&variant_i, &value);
+
+ if (pa_streq(key, "Class"))
+ d->class = (int) value;
+
+/* pa_log_debug("Value %u", (unsigned) value); */
+
+ break;
+ }
+
+ case DBUS_TYPE_ARRAY: {
+
+ DBusMessageIter ai;
+ dbus_message_iter_recurse(&variant_i, &ai);
+
+ if (dbus_message_iter_get_arg_type(&ai) == DBUS_TYPE_STRING &&
+ pa_streq(key, "UUIDs")) {
+
+ while (dbus_message_iter_get_arg_type(&ai) != DBUS_TYPE_INVALID) {
+ pa_bluetooth_uuid *node;
+ const char *value;
+
+ dbus_message_iter_get_basic(&ai, &value);
+ node = uuid_new(value);
+ PA_LLIST_PREPEND(pa_bluetooth_uuid, d->uuids, node);
+
+ if (!dbus_message_iter_next(&ai))
+ break;
+ }
+ }
+
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int parse_audio_property(pa_bluetooth_discovery *u, int *connected, DBusMessageIter *i) {
+ const char *key;
+ DBusMessageIter variant_i;
+
+ pa_assert(u);
+ pa_assert(connected);
+ pa_assert(i);
+
+ if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) {
+ pa_log("Property name not a string.");
+ return -1;
+ }
+
+ dbus_message_iter_get_basic(i, &key);
+
+ if (!dbus_message_iter_next(i)) {
+ pa_log("Property value missing");
+ return -1;
+ }
+
+ if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_VARIANT) {
+ pa_log("Property value not a variant.");
+ return -1;
+ }
+
+ dbus_message_iter_recurse(i, &variant_i);
+
+/* pa_log_debug("Parsing property org.bluez.{AudioSink|Headset}.%s", key); */
+
+ switch (dbus_message_iter_get_arg_type(&variant_i)) {
+
+ case DBUS_TYPE_BOOLEAN: {
+
+ dbus_bool_t value;
+ dbus_message_iter_get_basic(&variant_i, &value);
+
+ if (pa_streq(key, "Connected"))
+ *connected = !!value;
+
+/* pa_log_debug("Value %s", pa_yes_no(value)); */
+
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static void run_callback(pa_bluetooth_discovery *y, pa_bluetooth_device *d, pa_bool_t good) {
+ pa_assert(y);
+ pa_assert(d);
+
+ if (y->mode != MODE_DISCOVER)
+ return;
+
+ if (!device_is_loaded(d))
+ return;
+
+ if (!device_is_audio(d))
+ return;
+
+ y->callback(y->userdata, d, good);
+
+}
+
+static void get_properties_reply(DBusPendingCall *pending, void *userdata) {
+ DBusMessage *r;
+ DBusMessageIter arg_i, element_i;
+ pa_dbus_pending *p;
+ pa_bluetooth_device *d;
+ pa_bluetooth_discovery *y;
+ int valid;
+
+ pa_assert_se(p = userdata);
+ pa_assert_se(y = p->context_data);
+ pa_assert_se(r = dbus_pending_call_steal_reply(pending));
+
+/* pa_log_debug("Got %s.GetProperties response for %s", */
+/* dbus_message_get_interface(p->message), */
+/* dbus_message_get_path(p->message)); */
+
+ d = p->call_data;
+
+ valid = dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR ? -1 : 1;
+
+ if (dbus_message_is_method_call(p->message, "org.bluez.Device", "GetProperties"))
+ d->device_info_valid = valid;
+ else if (dbus_message_is_method_call(p->message, "org.bluez.Headset", "GetProperties"))
+ d->headset_info_valid = valid;
+ else if (dbus_message_is_method_call(p->message, "org.bluez.AudioSink", "GetProperties"))
+ d->audio_sink_info_valid = valid;
+
+ if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
+
+ if (!dbus_message_is_error(r, DBUS_ERROR_UNKNOWN_METHOD))
+ pa_log("Error from GetProperties reply: %s", dbus_message_get_error_name(r));
+
+ goto finish;
+ }
+
+ if (!dbus_message_iter_init(r, &arg_i)) {
+ pa_log("GetProperties reply has no arguments.");
+ goto finish;
+ }
+
+ if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) {
+ pa_log("GetProperties argument is not an array.");
+ goto finish;
+ }
+
+ dbus_message_iter_recurse(&arg_i, &element_i);
+ while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
+
+ if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter dict_i;
+
+ dbus_message_iter_recurse(&element_i, &dict_i);
+
+ if (dbus_message_has_interface(p->message, "org.bluez.Device")) {
+ if (parse_device_property(y, d, &dict_i) < 0)
+ goto finish;
+
+ } else if (dbus_message_has_interface(p->message, "org.bluez.Headset")) {
+ if (parse_audio_property(y, &d->headset_connected, &dict_i) < 0)
+ goto finish;
+
+ } else if (dbus_message_has_interface(p->message, "org.bluez.AudioSink")) {
+ if (parse_audio_property(y, &d->audio_sink_connected, &dict_i) < 0)
+ goto finish;
+ }
+ }
+
+ if (!dbus_message_iter_next(&element_i))
+ break;
+ }
+
+finish:
+ run_callback(y, d, TRUE);
+
+ dbus_message_unref(r);
+
+ PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
+ pa_dbus_pending_free(p);
+}
+
+static pa_dbus_pending* send_and_add_to_pending(pa_bluetooth_discovery *y, pa_bluetooth_device *d, DBusMessage *m, DBusPendingCallNotifyFunction func) {
+ pa_dbus_pending *p;
+ DBusPendingCall *call;
+
+ pa_assert(y);
+ pa_assert(m);
+
+ pa_assert_se(dbus_connection_send_with_reply(y->connection, m, &call, -1));
+
+ p = pa_dbus_pending_new(m, call, y, d);
+ PA_LLIST_PREPEND(pa_dbus_pending, y->pending, p);
+ dbus_pending_call_set_notify(call, func, p, NULL);
+
+ return p;
+}
+
+static void found_device(pa_bluetooth_discovery *y, const char* path) {
+ DBusMessage *m;
+ pa_bluetooth_device *d;
+
+ pa_assert(y);
+ pa_assert(path);
+
+ d = device_new(path);
+
+ if (y->mode == MODE_DISCOVER) {
+ pa_assert(y->devices);
+ pa_hashmap_put(y->devices, d->path, d);
+ } else {
+ pa_assert(!y->found_device);
+ y->found_device = d;
+ }
+
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Device", "GetProperties"));
+ send_and_add_to_pending(y, d, m, get_properties_reply);
+
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Headset", "GetProperties"));
+ send_and_add_to_pending(y, d, m, get_properties_reply);
+
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.AudioSink", "GetProperties"));
+ send_and_add_to_pending(y, d, m, get_properties_reply);
+}
+
+static void list_devices_reply(DBusPendingCall *pending, void *userdata) {
+ DBusError e;
+ DBusMessage *r;
+ char **paths = NULL;
+ int num = -1;
+ pa_dbus_pending *p;
+ pa_bluetooth_discovery *y;
+
+ pa_assert(pending);
+
+ dbus_error_init(&e);
+
+ pa_assert_se(p = userdata);
+ pa_assert_se(y = p->context_data);
+ pa_assert_se(r = dbus_pending_call_steal_reply(pending));
+
+ if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
+ pa_log("Error from ListDevices reply: %s", dbus_message_get_error_name(r));
+ goto end;
+ }
+
+ if (!dbus_message_get_args(r, &e, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &paths, &num, DBUS_TYPE_INVALID)) {
+ pa_log("org.bluez.Adapter.ListDevices returned an error: '%s'\n", e.message);
+ dbus_error_free(&e);
+ } else {
+ int i;
+
+ for (i = 0; i < num; ++i)
+ found_device(y, paths[i]);
+ }
+
+end:
+ if (paths)
+ dbus_free_string_array (paths);
+
+ dbus_message_unref(r);
+
+ PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
+ pa_dbus_pending_free(p);
+}
+
+static void find_device_reply(DBusPendingCall *pending, void *userdata) {
+ DBusError e;
+ DBusMessage *r;
+ char *path = NULL;
+ pa_dbus_pending *p;
+ pa_bluetooth_discovery *y;
+
+ pa_assert(pending);
+
+ dbus_error_init(&e);
+
+ pa_assert_se(p = userdata);
+ pa_assert_se(y = p->context_data);
+ pa_assert_se(r = dbus_pending_call_steal_reply(pending));
+
+ if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
+ pa_log("Error from FindDevice reply: %s", dbus_message_get_error_name(r));
+ goto end;
+ }
+
+ if (!dbus_message_get_args(r, &e, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
+ pa_log("org.bluez.Adapter.FindDevice returned an error: '%s'\n", e.message);
+ dbus_error_free(&e);
+ } else
+ found_device(y, path);
+
+end:
+ dbus_message_unref(r);
+
+ PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
+ pa_dbus_pending_free(p);
+}
+
+static void found_adapter(pa_bluetooth_discovery *y, const char *path) {
+ DBusMessage *m;
+
+ if (y->mode == MODE_FIND) {
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Adapter", "FindDevice"));
+
+ pa_assert_se(dbus_message_append_args(m,
+ DBUS_TYPE_STRING, &y->looking_for,
+ DBUS_TYPE_INVALID));
+
+ send_and_add_to_pending(y, NULL, m, find_device_reply);
+
+ } else {
+ pa_assert(y->mode == MODE_DISCOVER);
+
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Adapter", "ListDevices"));
+ send_and_add_to_pending(y, NULL, m, list_devices_reply);
+ }
+}
+
+static void list_adapters_reply(DBusPendingCall *pending, void *userdata) {
+ DBusError e;
+ DBusMessage *r;
+ char **paths = NULL;
+ int num = -1;
+ pa_dbus_pending *p;
+ pa_bluetooth_discovery *y;
+
+ pa_assert(pending);
+
+ dbus_error_init(&e);
+
+ pa_assert_se(p = userdata);
+ pa_assert_se(y = p->context_data);
+ pa_assert_se(r = dbus_pending_call_steal_reply(pending));
+
+ if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
+ pa_log("Error from ListAdapters reply: %s", dbus_message_get_error_name(r));
+ goto end;
+ }
+
+ if (!dbus_message_get_args(r, &e, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &paths, &num, DBUS_TYPE_INVALID)) {
+ pa_log("org.bluez.Manager.ListAdapters returned an error: %s", e.message);
+ dbus_error_free(&e);
+ } else {
+ int i;
+
+ for (i = 0; i < num; ++i)
+ found_adapter(y, paths[i]);
+ }
+
+end:
+ if (paths)
+ dbus_free_string_array (paths);
+
+ dbus_message_unref(r);
+
+ PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
+ pa_dbus_pending_free(p);
+}
+
+static void list_adapters(pa_bluetooth_discovery *y) {
+ DBusMessage *m;
+ pa_assert(y);
+
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", "/", "org.bluez.Manager", "ListAdapters"));
+ send_and_add_to_pending(y, NULL, m, list_adapters_reply);
+}
+
+static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *userdata) {
+ DBusError err;
+ pa_bluetooth_discovery *y;
+
+ pa_assert(bus);
+ pa_assert(m);
+
+ pa_assert_se(y = userdata);
+
+ dbus_error_init(&err);
+
+ pa_log_debug("dbus: interface=%s, path=%s, member=%s\n",
+ dbus_message_get_interface(m),
+ dbus_message_get_path(m),
+ dbus_message_get_member(m));
+
+ if (dbus_message_is_signal(m, "org.bluez.Adapter", "DeviceRemoved")) {
+ const char *path;
+ pa_bluetooth_device *d;
+
+ if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
+ pa_log("Failed to parse org.bluez.Adapter.DeviceRemoved: %s", err.message);
+ goto fail;
+ }
+
+ pa_log_debug("Device %s removed", path);
+
+ if ((d = pa_hashmap_remove(y->devices, path))) {
+
+ pa_assert_se(y->mode == MODE_DISCOVER);
+ run_callback(y, d, FALSE);
+
+ pa_bluetooth_device_free(d);
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+
+ } else if (dbus_message_is_signal(m, "org.bluez.Adapter", "DeviceCreated")) {
+ const char *path;
+
+ if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
+ pa_log("Failed to parse org.bluez.Adapter.DeviceCreated: %s", err.message);
+ goto fail;
+ }
+
+ pa_log_debug("Device %s created", path);
+
+ found_device(y, path);
+ return DBUS_HANDLER_RESULT_HANDLED;
+
+ } else if (dbus_message_is_signal(m, "org.bluez.Manager", "AdapterAdded")) {
+ const char *path;
+
+ if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
+ pa_log("Failed to parse org.bluez.Manager.AdapterAdded: %s", err.message);
+ goto fail;
+ }
+
+ pa_log_debug("Adapter %s created", path);
+
+ found_adapter(y, path);
+ return DBUS_HANDLER_RESULT_HANDLED;
+
+ } else if (dbus_message_is_signal(m, "org.bluez.Headset", "PropertyChanged") ||
+ dbus_message_is_signal(m, "org.bluez.AudioSink", "PropertyChanged") ||
+ dbus_message_is_signal(m, "org.bluez.Device", "PropertyChanged")) {
+
+ pa_bluetooth_device *d;
+
+ if ((d = pa_hashmap_get(y->devices, dbus_message_get_path(m)))) {
+ DBusMessageIter arg_i;
+
+ if (!dbus_message_iter_init(m, &arg_i)) {
+ pa_log("Failed to parse PropertyChanged: %s", err.message);
+ goto fail;
+ }
+
+ if (dbus_message_has_interface(m, "org.bluez.Device")) {
+ if (parse_device_property(y, d, &arg_i) < 0)
+ goto fail;
+
+ } else if (dbus_message_has_interface(m, "org.bluez.Headset")) {
+ if (parse_audio_property(y, &d->headset_connected, &arg_i) < 0)
+ goto fail;
+
+ } else if (dbus_message_has_interface(m, "org.bluez.AudioSink")) {
+ if (parse_audio_property(y, &d->audio_sink_connected, &arg_i) < 0)
+ goto fail;
+ }
+
+ pa_assert_se(y->mode == MODE_DISCOVER);
+ run_callback(y, d, TRUE);
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+fail:
+ dbus_error_free(&err);
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+pa_bluetooth_device* pa_bluetooth_find_device(DBusConnection *c, const char* address) {
+ pa_bluetooth_discovery y;
+
+ memset(&y, 0, sizeof(y));
+ y.mode = MODE_FIND;
+ y.looking_for = address;
+ y.connection = c;
+ PA_LLIST_HEAD_INIT(pa_dbus_pending, y.pending);
+
+ list_adapters(&y);
+
+ pa_dbus_sync_pending_list(&y.pending);
+ pa_assert(!y.pending);
+
+ if (y.found_device) {
+ pa_assert(device_is_loaded(y.found_device));
+
+ if (!device_is_audio(y.found_device)) {
+ pa_bluetooth_device_free(y.found_device);
+ return NULL;
+ }
+ }
+
+ return y.found_device;
+}
+
+pa_bluetooth_device* pa_bluetooth_get_device(DBusConnection *c, const char* path) {
+ pa_bluetooth_discovery y;
+
+ memset(&y, 0, sizeof(y));
+ y.mode = MODE_GET;
+ y.connection = c;
+ PA_LLIST_HEAD_INIT(pa_dbus_pending, y.pending);
+
+ found_device(&y, path);
+
+ pa_dbus_sync_pending_list(&y.pending);
+ pa_assert(!y.pending);
+
+ if (y.found_device) {
+ pa_assert(device_is_loaded(y.found_device));
+
+ if (!device_is_audio(y.found_device)) {
+ pa_bluetooth_device_free(y.found_device);
+ return NULL;
+ }
+ }
+
+ return y.found_device;
+}
+
+pa_bluetooth_discovery* pa_bluetooth_discovery_new(DBusConnection *c, pa_bluetooth_device_callback_t cb, struct userdata *u) {
+ DBusError err;
+ pa_bluetooth_discovery *y;
+
+ pa_assert(c);
+ pa_assert(cb);
+
+ dbus_error_init(&err);
+
+ y = pa_xnew0(pa_bluetooth_discovery, 1);
+ y->mode = MODE_DISCOVER;
+ y->connection = c;
+ y->callback = cb;
+ y->userdata = u;
+ y->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+ PA_LLIST_HEAD_INIT(pa_dbus_pending, y->pending);
+
+ /* dynamic detection of bluetooth audio devices */
+ if (!dbus_connection_add_filter(c, filter_cb, y, NULL)) {
+ pa_log_error("Failed to add filter function");
+ goto fail;
+ }
+
+ if (pa_dbus_add_matches(
+ c, &err,
+ "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterAdded'",
+ "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'",
+ "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceCreated'",
+ "type='signal',sender='org.bluez',interface='org.bluez.Device',member='PropertyChanged'",
+ "type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'",
+ "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'", NULL) < 0) {
+ pa_log("Failed to add D-Bus matches: %s", err.message);
+ goto fail;
+ }
+
+ list_adapters(y);
+
+ return y;
+
+fail:
+ dbus_error_free(&err);
+ return NULL;
+}
+
+void pa_bluetooth_discovery_free(pa_bluetooth_discovery *y) {
+ pa_bluetooth_device *d;
+
+ pa_assert(y);
+
+ pa_dbus_free_pending_list(&y->pending);
+
+ if (y->devices) {
+ while ((d = pa_hashmap_steal_first(y->devices))) {
+ run_callback(y, d, FALSE);
+ pa_bluetooth_device_free(d);
+ }
+
+ pa_hashmap_free(y->devices, NULL, NULL);
+ }
+
+ if (y->connection) {
+ pa_dbus_remove_matches(y->connection,
+ "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterAdded'",
+ "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterRemoved'",
+ "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'",
+ "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceCreated'",
+ "type='signal',sender='org.bluez',interface='org.bluez.Device',member='PropertyChanged'",
+ "type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'",
+ "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'", NULL);
+
+ dbus_connection_remove_filter(y->connection, filter_cb, y);
+ }
+}
+
+void pa_bluetooth_discovery_sync(pa_bluetooth_discovery *y) {
+ pa_assert(y);
+
+ pa_dbus_sync_pending_list(&y->pending);
+}
+
+const char*pa_bluetooth_get_form_factor(uint32_t class) {
+ unsigned i;
+ const char *r;
+
+ static const char * const table[] = {
+ [1] = "headset",
+ [2] = "hands-free",
+ [4] = "microphone",
+ [5] = "external-speakers",
+ [6] = "headphones",
+ [7] = "portable",
+ [8] = "car",
+ [10] = "hifi"
+ };
+
+ if (((class >> 8) & 31) != 4)
+ return NULL;
+
+ if ((i = (class >> 2) & 63) > PA_ELEMENTSOF(table))
+ r = NULL;
+ else
+ r = table[i];
+
+ if (!r)
+ pa_log_debug("Unknown Bluetooth minor device class %u", i);
+
+ return r;
+}
+
+char *pa_bluetooth_cleanup_name(const char *name) {
+ char *t, *s, *d;
+ pa_bool_t space = FALSE;
+
+ pa_assert(name);
+
+ while ((*name >= 1 && *name <= 32) || *name >= 127)
+ name++;
+
+ t = pa_xstrdup(name);
+
+ for (s = d = t; *s; s++) {
+
+ if (*s <= 32 || *s >= 127 || *s == '_') {
+ space = TRUE;
+ continue;
+ }
+
+ if (space) {
+ *(d++) = ' ';
+ space = FALSE;
+ }
+
+ *(d++) = *s;
+ }
+
+ *d = 0;
+
+ return t;
+}
diff --git a/src/modules/bluetooth/bluetooth-util.h b/src/modules/bluetooth/bluetooth-util.h
new file mode 100644
index 00000000..2c3ec649
--- /dev/null
+++ b/src/modules/bluetooth/bluetooth-util.h
@@ -0,0 +1,81 @@
+#ifndef foobluetoothutilhfoo
+#define foobluetoothutilhfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2008 Joao Paulo Rechi Vita
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <dbus/dbus.h>
+
+#include <pulsecore/llist.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+
+typedef struct pa_bluetooth_uuid pa_bluetooth_uuid;
+typedef struct pa_bluetooth_device pa_bluetooth_device;
+typedef struct pa_bluetooth_discovery pa_bluetooth_discovery;
+
+struct userdata;
+
+struct pa_bluetooth_uuid {
+ char *uuid;
+ PA_LLIST_FIELDS(pa_bluetooth_uuid);
+};
+
+struct pa_bluetooth_device {
+ void *data; /* arbitrary information for the one owning the discovery object */
+
+ int device_info_valid; /* 0: no results yet; 1: good results; -1: bad results ... */
+ int audio_sink_info_valid; /* ... same here ... */
+ int headset_info_valid; /* ... and here */
+
+ /* Device information */
+ char *name;
+ char *path;
+ int paired;
+ char *alias;
+ int device_connected;
+ PA_LLIST_HEAD(pa_bluetooth_uuid, uuids);
+ char *address;
+ int class;
+ int trusted;
+
+ /* AudioSink information */
+ int audio_sink_connected;
+
+ /* Headset information */
+ int headset_connected;
+};
+
+void pa_bluetooth_device_free(pa_bluetooth_device *d);
+
+pa_bluetooth_device* pa_bluetooth_get_device(DBusConnection *c, const char* path);
+pa_bluetooth_device* pa_bluetooth_find_device(DBusConnection *c, const char* address);
+
+typedef void (*pa_bluetooth_device_callback_t)(struct userdata *u, pa_bluetooth_device *d, pa_bool_t good);
+pa_bluetooth_discovery* pa_bluetooth_discovery_new(DBusConnection *c, pa_bluetooth_device_callback_t cb, struct userdata *u);
+void pa_bluetooth_discovery_free(pa_bluetooth_discovery *d);
+void pa_bluetooth_discovery_sync(pa_bluetooth_discovery *d);
+
+const char*pa_bluetooth_get_form_factor(uint32_t class);
+
+char *pa_bluetooth_cleanup_name(const char *name);
+
+#endif
diff --git a/src/modules/bluetooth/ipc.c b/src/modules/bluetooth/ipc.c
index 67785309..f14c92c4 100644
--- a/src/modules/bluetooth/ipc.c
+++ b/src/modules/bluetooth/ipc.c
@@ -2,7 +2,7 @@
*
* BlueZ - Bluetooth protocol stack for Linux
*
- * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
diff --git a/src/modules/bluetooth/ipc.h b/src/modules/bluetooth/ipc.h
index 0e985c3a..4203150b 100644
--- a/src/modules/bluetooth/ipc.h
+++ b/src/modules/bluetooth/ipc.h
@@ -2,7 +2,7 @@
*
* BlueZ - Bluetooth protocol stack for Linux
*
- * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -98,8 +98,6 @@ typedef struct {
#define BT_NEW_STREAM 2
#define BT_START_STREAM 3
#define BT_STOP_STREAM 4
-#define BT_SUSPEND_STREAM 5
-#define BT_RESUME_STREAM 6
#define BT_CONTROL 7
#define BT_CAPABILITIES_TRANSPORT_A2DP 0
@@ -120,7 +118,7 @@ struct bt_get_capabilities_req {
} __attribute__ ((packed));
/**
- * SBC Codec parameters as per A2DP profile 1.0 § 4.3
+ * SBC Codec parameters as per A2DP profile 1.0 § 4.3
*/
#define BT_A2DP_CODEC_SBC 0x00
@@ -198,7 +196,6 @@ typedef struct {
uint16_t sampling_rate;
} __attribute__ ((packed)) pcm_capabilities_t;
-
struct bt_get_capabilities_rsp {
bt_audio_msg_header_t h;
uint8_t data[0]; /* First codec_capabilities_t */
diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 11ed7d33..d909d70d 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -1,22 +1,22 @@
/***
- This file is part of PulseAudio.
+ This file is part of PulseAudio.
- Copyright 2008 Joao Paulo Rechi Vita
+ Copyright 2008 Joao Paulo Rechi Vita
- PulseAudio is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published
- by the Free Software Foundation; either version 2 of the License,
- or (at your option) any later version.
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
- PulseAudio is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
- You should have received a copy of the GNU Lesser General Public License
- along with PulseAudio; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- USA.
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
***/
#ifdef HAVE_CONFIG_H
@@ -44,14 +44,14 @@
#include <pulsecore/time-smoother.h>
#include <pulsecore/rtclock.h>
-#include "../dbus-util.h"
+#include <modules/dbus-util.h>
+
#include "module-bluetooth-device-symdef.h"
#include "ipc.h"
#include "sbc.h"
#include "rtp.h"
+#include "bluetooth-util.h"
-#define DEFAULT_SINK_NAME "bluetooth_sink"
-#define BUFFER_SIZE 2048
#define MAX_BITPOOL 64
#define MIN_BITPOOL 2U
#define SOL_SCO 17
@@ -63,30 +63,52 @@ PA_MODULE_DESCRIPTION("Bluetooth audio sink and source");
PA_MODULE_VERSION(PACKAGE_VERSION);
PA_MODULE_LOAD_ONCE(FALSE);
PA_MODULE_USAGE(
- "sink_name=<name of the device> "
+ "name=<name for the card/sink/source, to be prefixed> "
+ "card_name=<name for the card> "
+ "sink_name=<name for the sink> "
+ "source_name=<name for the source> "
"address=<address of the device> "
"profile=<a2dp|hsp> "
"rate=<sample rate> "
"channels=<number of channels> "
"path=<device object path>");
-struct bt_a2dp {
+static const char* const valid_modargs[] = {
+ "name",
+ "card_name",
+ "sink_name",
+ "source_name",
+ "address",
+ "profile",
+ "rate",
+ "channels",
+ "path",
+ NULL
+};
+
+struct a2dp_info {
sbc_capabilities_t sbc_capabilities;
sbc_t sbc; /* Codec data */
pa_bool_t sbc_initialized; /* Keep track if the encoder is initialized */
size_t codesize; /* SBC codesize */
- unsigned samples; /* Number of encoded samples */
- uint8_t buffer[BUFFER_SIZE]; /* Codec transfer buffer */
- size_t count; /* Codec transfer buffer counter */
- unsigned total_samples; /* Cumulative number of codec samples */
+ void* buffer; /* Codec transfer buffer */
+ size_t buffer_size; /* Size of the buffer */
+
uint16_t seq_num; /* Cumulative packet sequence */
- unsigned frame_count; /* Current frames in buffer*/
+};
+
+enum profile {
+ PROFILE_A2DP,
+ PROFILE_HSP,
+ PROFILE_OFF
};
struct userdata {
pa_core *core;
pa_module *module;
+
+ pa_card *card;
pa_sink *sink;
pa_source *source;
@@ -95,113 +117,145 @@ struct userdata {
pa_rtpoll_item *rtpoll_item;
pa_thread *thread;
- uint64_t offset;
- pa_smoother *smoother;
+ uint64_t read_index, write_index;
+ pa_usec_t started_at;
+ pa_smoother *read_smoother;
+
+ pa_memchunk write_memchunk;
- char *name;
- char *addr;
- char *profile;
- pa_sample_spec ss;
+ pa_sample_spec sample_spec;
- int audioservice_fd;
+ int service_fd;
int stream_fd;
- uint8_t transport;
- char *strtransport;
size_t link_mtu;
size_t block_size;
- pa_usec_t latency;
- struct bt_a2dp a2dp;
- char *path;
- pa_dbus_connection *conn;
-};
+ struct a2dp_info a2dp;
+ pa_dbus_connection *connection;
-static const char* const valid_modargs[] = {
- "sink_name",
- "address",
- "profile",
- "rate",
- "channels",
- "path",
- NULL
+ enum profile profile;
+
+ pa_modargs *modargs;
+
+ pa_bluetooth_device *device;
+
+ int write_type, read_type;
};
-static int bt_audioservice_send(int sk, const bt_audio_msg_header_t *msg) {
- int e;
- const char *type, *name;
- uint16_t length;
+static int service_send(int fd, const bt_audio_msg_header_t *msg) {
+ size_t length;
+ ssize_t r;
+
+ pa_assert(fd >= 0);
+ pa_assert(msg);
length = msg->length ? msg->length : BT_SUGGESTED_BUFFER_SIZE;
- type = bt_audio_strtype(msg->type);
- name = bt_audio_strname(msg->name);
- pa_log_debug("sending: %s -> %s", type, name);
- if (send(sk, msg, length, 0) > 0)
- e = 0;
- else {
- e = -errno;
- pa_log_error("Error sending data to audio service: %s(%d)", pa_cstrerror(errno), errno);
- }
- return e;
+
+ pa_log_debug("Sending %s -> %s",
+ pa_strnull(bt_audio_strtype(msg->type)),
+ pa_strnull(bt_audio_strname(msg->name)));
+
+ if ((r = send(fd, msg, length, 0)) == (ssize_t) length)
+ return 0;
+
+ if (r < 0)
+ pa_log_error("Error sending data to audio service: %s", pa_cstrerror(errno));
+ else
+ pa_log_error("Short send()");
+
+ return -1;
}
-static int bt_audioservice_recv(int sk, bt_audio_msg_header_t *inmsg, uint16_t expected_length) {
- int e;
- const char *type, *name;
- uint16_t length;
+static int service_recv(int fd, bt_audio_msg_header_t *msg, size_t expected_length) {
+ size_t length;
+ ssize_t r;
+
+ pa_assert(fd >= 0);
+ pa_assert(msg);
length = expected_length ? expected_length : BT_SUGGESTED_BUFFER_SIZE;
- pa_log_debug("trying to receive msg from audio service...");
- if (recv(sk, inmsg, length, 0) > 0) {
- type = bt_audio_strtype(inmsg->type);
- name = bt_audio_strname(inmsg->name);
- if (type && name) {
- pa_log_debug("Received: %s <- %s", type, name);
- e = 0;
+ pa_log_debug("Trying to receive message from audio service...");
+
+ r = recv(fd, msg, length, 0);
+
+ if (r > 0 && (r == (ssize_t) length || expected_length <= 0)) {
+
+ if ((size_t) r < sizeof(*msg)) {
+ pa_log_error("Packet read too small.");
+ return -1;
}
- else {
- e = -EINVAL;
- pa_log_error("Bogus message type %d name %d received from audio service",
- inmsg->type, inmsg->name);
+
+ if (r != msg->length) {
+ pa_log_error("Size read doesn't match header size.");
+ return -1;
}
+
+ pa_log_debug("Received %s <- %s",
+ pa_strnull(bt_audio_strtype(msg->type)),
+ pa_strnull(bt_audio_strname(msg->name)));
+ return 0;
}
- else {
- e = -errno;
- pa_log_error("Error receiving data from audio service: %s(%d)", pa_cstrerror(errno), errno);
- }
- return e;
+ if (r < 0)
+ pa_log_error("Error receiving data from audio service: %s", pa_cstrerror(errno));
+ else
+ pa_log_error("Short recv()");
+
+ return -1;
}
-static int bt_audioservice_expect(int sk, bt_audio_msg_header_t *rsp, uint8_t expected_name, uint16_t expected_length) {
- int e = bt_audioservice_recv(sk, rsp, expected_length);
+static int service_expect(int fd, bt_audio_msg_header_t *rsp, uint8_t expected_name, size_t expected_length) {
+ int r;
- if (e < 0) {
- if (rsp->name != expected_name) {
- e = -EINVAL;
- pa_log_error("Bogus message %s received while %s was expected",
- bt_audio_strname(rsp->name),
- bt_audio_strname(expected_name));
- }
+ pa_assert(fd >= 0);
+ pa_assert(rsp);
+
+ if ((r = service_recv(fd, rsp, expected_length)) < 0)
+ return r;
+
+ if (rsp->name != expected_name) {
+ pa_log_error("Bogus message %s received while %s was expected",
+ pa_strnull(bt_audio_strname(rsp->name)),
+ pa_strnull(bt_audio_strname(expected_name)));
+ return -1;
}
if (rsp->type == BT_ERROR) {
- bt_audio_error_t *error = (void *) rsp;
- pa_log_error("%s failed : %s(%d)", bt_audio_strname(rsp->name), pa_cstrerror(error->posix_errno), error->posix_errno);
- return -error->posix_errno;
+ bt_audio_error_t *error = (bt_audio_error_t *) rsp;
+ pa_log_error("%s failed: %s", pa_strnull(bt_audio_strname(rsp->name)), pa_cstrerror(error->posix_errno));
+ return -1;
}
- return e;
+ return 0;
}
-static int bt_parsecaps(struct userdata *u, struct bt_get_capabilities_rsp *rsp) {
- uint16_t bytes_left = rsp->h.length - sizeof(*rsp);
- codec_capabilities_t *codec = (void *) rsp->data;
+static int parse_caps(struct userdata *u, const struct bt_get_capabilities_rsp *rsp) {
+ uint16_t bytes_left;
+ const codec_capabilities_t *codec;
+
+ pa_assert(u);
+ pa_assert(rsp);
- u->transport = codec->transport;
+ bytes_left = rsp->h.length - sizeof(*rsp);
- if (codec->transport != BT_CAPABILITIES_TRANSPORT_A2DP)
+ if (bytes_left < sizeof(codec_capabilities_t)) {
+ pa_log_error("Packet too small to store codec information.");
+ return -1;
+ }
+
+ codec = (codec_capabilities_t *) rsp->data; /** ALIGNMENT? **/
+
+ pa_log_debug("Payload size is %lu %lu", (unsigned long) bytes_left, (unsigned long) sizeof(*codec));
+
+ if ((u->profile == PROFILE_A2DP && codec->transport != BT_CAPABILITIES_TRANSPORT_A2DP) ||
+ (u->profile == PROFILE_HSP && codec->transport != BT_CAPABILITIES_TRANSPORT_SCO)) {
+ pa_log_error("Got capabilities for wrong codec.");
+ return -1;
+ }
+
+ if (u->profile != PROFILE_A2DP)
return 0;
while (bytes_left > 0) {
@@ -209,96 +263,98 @@ static int bt_parsecaps(struct userdata *u, struct bt_get_capabilities_rsp *rsp)
break;
bytes_left -= codec->length;
- codec = (void *) (codec + codec->length);
+ codec = (const codec_capabilities_t*) ((const uint8_t*) codec + codec->length);
}
if (bytes_left <= 0 || codec->length != sizeof(u->a2dp.sbc_capabilities))
- return -EINVAL;
+ return -1;
- memcpy(&u->a2dp.sbc_capabilities, codec, codec->length);
+ pa_assert(codec->type == BT_A2DP_CODEC_SBC);
+ memcpy(&u->a2dp.sbc_capabilities, codec, sizeof(u->a2dp.sbc_capabilities));
return 0;
}
-static int bt_getcaps(struct userdata *u) {
- int e;
+static int get_caps(struct userdata *u) {
union {
- bt_audio_msg_header_t rsp;
struct bt_get_capabilities_req getcaps_req;
struct bt_get_capabilities_rsp getcaps_rsp;
uint8_t buf[BT_SUGGESTED_BUFFER_SIZE];
} msg;
- memset(msg.buf, 0, BT_SUGGESTED_BUFFER_SIZE);
+ pa_assert(u);
+
+ memset(&msg, 0, sizeof(msg));
msg.getcaps_req.h.type = BT_REQUEST;
msg.getcaps_req.h.name = BT_GET_CAPABILITIES;
msg.getcaps_req.h.length = sizeof(msg.getcaps_req);
- strncpy(msg.getcaps_req.device, u->addr, 18);
- if (pa_streq(u->profile, "a2dp"))
+ pa_strlcpy(msg.getcaps_req.device, u->device->address, sizeof(msg.getcaps_req.device));
+ if (u->profile == PROFILE_A2DP)
msg.getcaps_req.transport = BT_CAPABILITIES_TRANSPORT_A2DP;
- else if (pa_streq(u->profile, "hsp"))
- msg.getcaps_req.transport = BT_CAPABILITIES_TRANSPORT_SCO;
else {
- pa_log_error("Invalid profile argument: %s", u->profile);
- return -1;
+ pa_assert(u->profile == PROFILE_HSP);
+ msg.getcaps_req.transport = BT_CAPABILITIES_TRANSPORT_SCO;
}
msg.getcaps_req.flags = BT_FLAG_AUTOCONNECT;
- e = bt_audioservice_send(u->audioservice_fd, &msg.getcaps_req.h);
- if (e < 0) {
- pa_log_error("Failed to send GETCAPABILITIES_REQ");
- return e;
- }
+ if (service_send(u->service_fd, &msg.getcaps_req.h) < 0)
+ return -1;
- e = bt_audioservice_expect(u->audioservice_fd, &msg.rsp, BT_GET_CAPABILITIES, 0);
- if (e < 0) {
- pa_log_error("Failed to expect for GETCAPABILITIES_RSP");
- return e;
- }
+ if (service_expect(u->service_fd, &msg.getcaps_rsp.h, BT_GET_CAPABILITIES, 0) < 0)
+ return -1;
- return bt_parsecaps(u, &msg.getcaps_rsp);
+ return parse_caps(u, &msg.getcaps_rsp);
}
-static uint8_t default_bitpool(uint8_t freq, uint8_t mode) {
+static uint8_t a2dp_default_bitpool(uint8_t freq, uint8_t mode) {
+
switch (freq) {
case BT_SBC_SAMPLING_FREQ_16000:
case BT_SBC_SAMPLING_FREQ_32000:
return 53;
+
case BT_SBC_SAMPLING_FREQ_44100:
+
switch (mode) {
case BT_A2DP_CHANNEL_MODE_MONO:
case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL:
return 31;
+
case BT_A2DP_CHANNEL_MODE_STEREO:
case BT_A2DP_CHANNEL_MODE_JOINT_STEREO:
return 53;
+
default:
pa_log_warn("Invalid channel mode %u", mode);
return 53;
}
+
case BT_SBC_SAMPLING_FREQ_48000:
+
switch (mode) {
case BT_A2DP_CHANNEL_MODE_MONO:
case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL:
return 29;
+
case BT_A2DP_CHANNEL_MODE_STEREO:
case BT_A2DP_CHANNEL_MODE_JOINT_STEREO:
return 51;
+
default:
pa_log_warn("Invalid channel mode %u", mode);
return 51;
}
+
default:
pa_log_warn("Invalid sampling freq %u", freq);
return 53;
}
}
-static int bt_a2dp_init(struct userdata *u) {
- sbc_capabilities_t *cap = &u->a2dp.sbc_capabilities;
- uint8_t max_bitpool, min_bitpool;
- unsigned i;
+static int setup_a2dp(struct userdata *u) {
+ sbc_capabilities_t *cap;
+ int i;
static const struct {
uint32_t rate;
@@ -310,32 +366,59 @@ static int bt_a2dp_init(struct userdata *u) {
{ 48000U, BT_SBC_SAMPLING_FREQ_48000 }
};
+ pa_assert(u);
+ pa_assert(u->profile == PROFILE_A2DP);
+
+ cap = &u->a2dp.sbc_capabilities;
+
/* Find the lowest freq that is at least as high as the requested
* sampling rate */
- for (i = 0; i < PA_ELEMENTSOF(freq_table); i++)
- if (freq_table[i].rate >= u->ss.rate || i == PA_ELEMENTSOF(freq_table)-1 ) {
- u->ss.rate = freq_table[i].rate;
+ for (i = 0; (unsigned) i < PA_ELEMENTSOF(freq_table); i++)
+ if (freq_table[i].rate >= u->sample_spec.rate && (cap->frequency & freq_table[i].cap)) {
+ u->sample_spec.rate = freq_table[i].rate;
cap->frequency = freq_table[i].cap;
break;
}
- if (u->ss.channels >= 2) {
+ if ((unsigned) i >= PA_ELEMENTSOF(freq_table)) {
+ for (; i >= 0; i--) {
+ if (cap->frequency & freq_table[i].cap) {
+ u->sample_spec.rate = freq_table[i].rate;
+ cap->frequency = freq_table[i].cap;
+ break;
+ }
+ }
+
+ if (i < 0) {
+ pa_log("Not suitable sample rate");
+ return -1;
+ }
+ }
+
+ if (u->sample_spec.channels <= 1) {
+ if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) {
+ cap->channel_mode = BT_A2DP_CHANNEL_MODE_MONO;
+ u->sample_spec.channels = 1;
+ } else
+ u->sample_spec.channels = 2;
+ }
+
+ if (u->sample_spec.channels >= 2) {
+ u->sample_spec.channels = 2;
+
if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO)
cap->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO;
else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO)
cap->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO;
else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL)
cap->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL;
-
- u->ss.channels = 2;
- } else {
- if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_MONO)
+ else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) {
cap->channel_mode = BT_A2DP_CHANNEL_MODE_MONO;
- }
-
- if (!cap->channel_mode) {
- pa_log_error("No supported channel modes");
- return -1;
+ u->sample_spec.channels = 1;
+ } else {
+ pa_log("No supported channel modes");
+ return -1;
+ }
}
if (cap->block_length & BT_A2DP_BLOCK_LENGTH_16)
@@ -365,17 +448,18 @@ static int bt_a2dp_init(struct userdata *u) {
else if (cap->allocation_method & BT_A2DP_ALLOCATION_SNR)
cap->allocation_method = BT_A2DP_ALLOCATION_SNR;
- min_bitpool = (uint8_t) PA_MAX(MIN_BITPOOL, cap->min_bitpool);
- max_bitpool = (uint8_t) PA_MIN(default_bitpool(cap->frequency, cap->channel_mode), cap->max_bitpool);
-
- cap->min_bitpool = (uint8_t) min_bitpool;
- cap->max_bitpool = (uint8_t) max_bitpool;
+ cap->min_bitpool = (uint8_t) PA_MAX(MIN_BITPOOL, cap->min_bitpool);
+ cap->max_bitpool = (uint8_t) PA_MIN(a2dp_default_bitpool(cap->frequency, cap->channel_mode), cap->max_bitpool);
return 0;
}
-static void bt_a2dp_setup(struct bt_a2dp *a2dp) {
- sbc_capabilities_t active_capabilities = a2dp->sbc_capabilities;
+static void setup_sbc(struct a2dp_info *a2dp) {
+ sbc_capabilities_t *active_capabilities;
+
+ pa_assert(a2dp);
+
+ active_capabilities = &a2dp->sbc_capabilities;
if (a2dp->sbc_initialized)
sbc_reinit(&a2dp->sbc, 0);
@@ -383,42 +467,63 @@ static void bt_a2dp_setup(struct bt_a2dp *a2dp) {
sbc_init(&a2dp->sbc, 0);
a2dp->sbc_initialized = TRUE;
- if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_16000)
- a2dp->sbc.frequency = SBC_FREQ_16000;
-
- if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_32000)
- a2dp->sbc.frequency = SBC_FREQ_32000;
-
- if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_44100)
- a2dp->sbc.frequency = SBC_FREQ_44100;
-
- if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_48000)
- a2dp->sbc.frequency = SBC_FREQ_48000;
-
- if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_MONO)
- a2dp->sbc.mode = SBC_MODE_MONO;
-
- if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL)
- a2dp->sbc.mode = SBC_MODE_DUAL_CHANNEL;
-
- if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_STEREO)
- a2dp->sbc.mode = SBC_MODE_STEREO;
+ switch (active_capabilities->frequency) {
+ case BT_SBC_SAMPLING_FREQ_16000:
+ a2dp->sbc.frequency = SBC_FREQ_16000;
+ break;
+ case BT_SBC_SAMPLING_FREQ_32000:
+ a2dp->sbc.frequency = SBC_FREQ_32000;
+ break;
+ case BT_SBC_SAMPLING_FREQ_44100:
+ a2dp->sbc.frequency = SBC_FREQ_44100;
+ break;
+ case BT_SBC_SAMPLING_FREQ_48000:
+ a2dp->sbc.frequency = SBC_FREQ_48000;
+ break;
+ default:
+ pa_assert_not_reached();
+ }
- if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO)
- a2dp->sbc.mode = SBC_MODE_JOINT_STEREO;
+ switch (active_capabilities->channel_mode) {
+ case BT_A2DP_CHANNEL_MODE_MONO:
+ a2dp->sbc.mode = SBC_MODE_MONO;
+ break;
+ case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL:
+ a2dp->sbc.mode = SBC_MODE_DUAL_CHANNEL;
+ break;
+ case BT_A2DP_CHANNEL_MODE_STEREO:
+ a2dp->sbc.mode = SBC_MODE_STEREO;
+ break;
+ case BT_A2DP_CHANNEL_MODE_JOINT_STEREO:
+ a2dp->sbc.mode = SBC_MODE_JOINT_STEREO;
+ break;
+ default:
+ pa_assert_not_reached();
+ }
- a2dp->sbc.allocation = (uint8_t) (active_capabilities.allocation_method == BT_A2DP_ALLOCATION_SNR ? SBC_AM_SNR : SBC_AM_LOUDNESS);
+ switch (active_capabilities->allocation_method) {
+ case BT_A2DP_ALLOCATION_SNR:
+ a2dp->sbc.allocation = SBC_AM_SNR;
+ break;
+ case BT_A2DP_ALLOCATION_LOUDNESS:
+ a2dp->sbc.allocation = SBC_AM_LOUDNESS;
+ break;
+ default:
+ pa_assert_not_reached();
+ }
- switch (active_capabilities.subbands) {
+ switch (active_capabilities->subbands) {
case BT_A2DP_SUBBANDS_4:
a2dp->sbc.subbands = SBC_SB_4;
break;
case BT_A2DP_SUBBANDS_8:
a2dp->sbc.subbands = SBC_SB_8;
break;
+ default:
+ pa_assert_not_reached();
}
- switch (active_capabilities.block_length) {
+ switch (active_capabilities->block_length) {
case BT_A2DP_BLOCK_LENGTH_4:
a2dp->sbc.blocks = SBC_BLK_4;
break;
@@ -431,83 +536,82 @@ static void bt_a2dp_setup(struct bt_a2dp *a2dp) {
case BT_A2DP_BLOCK_LENGTH_16:
a2dp->sbc.blocks = SBC_BLK_16;
break;
+ default:
+ pa_assert_not_reached();
}
- a2dp->sbc.bitpool = active_capabilities.max_bitpool;
+ a2dp->sbc.bitpool = active_capabilities->max_bitpool;
a2dp->codesize = (uint16_t) sbc_get_codesize(&a2dp->sbc);
- a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload);
}
-static int bt_setconf(struct userdata *u) {
- int e;
+static int set_conf(struct userdata *u) {
union {
- bt_audio_msg_header_t rsp;
struct bt_set_configuration_req setconf_req;
struct bt_set_configuration_rsp setconf_rsp;
uint8_t buf[BT_SUGGESTED_BUFFER_SIZE];
} msg;
- if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
- e = bt_a2dp_init(u);
- if (e < 0) {
- pa_log_error("a2dp_init error");
- return e;
- }
- u->ss.format = PA_SAMPLE_S16LE;
- }
- else {
- u->ss.format = PA_SAMPLE_S16LE;
- u->ss.channels = 1;
- u->ss.rate = 8000;
+ if (u->profile == PROFILE_A2DP ) {
+ u->sample_spec.format = PA_SAMPLE_S16LE;
+
+ if (setup_a2dp(u) < 0)
+ return -1;
+ } else {
+ pa_assert(u->profile == PROFILE_HSP);
+
+ u->sample_spec.format = PA_SAMPLE_S16LE;
+ u->sample_spec.channels = 1;
+ u->sample_spec.rate = 8000;
}
- memset(msg.buf, 0, BT_SUGGESTED_BUFFER_SIZE);
+ memset(&msg, 0, sizeof(msg));
msg.setconf_req.h.type = BT_REQUEST;
msg.setconf_req.h.name = BT_SET_CONFIGURATION;
msg.setconf_req.h.length = sizeof(msg.setconf_req);
- strncpy(msg.setconf_req.device, u->addr, 18);
- msg.setconf_req.codec.transport = u->transport;
- if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
- memcpy(&msg.setconf_req.codec, &u->a2dp.sbc_capabilities,
- sizeof(u->a2dp.sbc_capabilities));
- msg.setconf_req.h.length += msg.setconf_req.codec.length
- - sizeof(msg.setconf_req.codec);
+ pa_strlcpy(msg.setconf_req.device, u->device->address, sizeof(msg.setconf_req.device));
+ msg.setconf_req.access_mode = u->profile == PROFILE_A2DP ? BT_CAPABILITIES_ACCESS_MODE_WRITE : BT_CAPABILITIES_ACCESS_MODE_READWRITE;
+
+ msg.setconf_req.codec.transport = u->profile == PROFILE_A2DP ? BT_CAPABILITIES_TRANSPORT_A2DP : BT_CAPABILITIES_TRANSPORT_SCO;
+
+ if (u->profile == PROFILE_A2DP) {
+ memcpy(&msg.setconf_req.codec, &u->a2dp.sbc_capabilities, sizeof(u->a2dp.sbc_capabilities));
+ msg.setconf_req.h.length += msg.setconf_req.codec.length - sizeof(msg.setconf_req.codec);
}
- msg.setconf_req.access_mode = BT_CAPABILITIES_ACCESS_MODE_WRITE;
- e = bt_audioservice_send(u->audioservice_fd, &msg.setconf_req.h);
- if (e < 0) {
- pa_log_error("Failed to send BT_SETCONFIGURATION_REQ");
- return e;
+ if (service_send(u->service_fd, &msg.setconf_req.h) < 0)
+ return -1;
+
+ if (service_expect(u->service_fd, &msg.setconf_rsp.h, BT_SET_CONFIGURATION, sizeof(msg.setconf_rsp)) < 0)
+ return -1;
+
+ if ((u->profile == PROFILE_A2DP && msg.setconf_rsp.transport != BT_CAPABILITIES_TRANSPORT_A2DP) ||
+ (u->profile == PROFILE_HSP && msg.setconf_rsp.transport != BT_CAPABILITIES_TRANSPORT_SCO)) {
+ pa_log("Transport doesn't match what we requested.");
+ return -1;
}
- e = bt_audioservice_expect(u->audioservice_fd, &msg.rsp, BT_SET_CONFIGURATION, sizeof(msg.setconf_rsp));
- if (e < 0) {
- pa_log_error("Failed to expect BT_SETCONFIGURATION_RSP");
- return e;
+ if ((u->profile == PROFILE_A2DP && msg.setconf_rsp.access_mode != BT_CAPABILITIES_ACCESS_MODE_WRITE) ||
+ (u->profile == PROFILE_HSP && msg.setconf_rsp.access_mode != BT_CAPABILITIES_ACCESS_MODE_READWRITE)) {
+ pa_log("Access mode doesn't match what we requested.");
+ return -1;
}
- u->transport = msg.setconf_rsp.transport;
- u->strtransport = (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP ? pa_xstrdup("A2DP") : pa_xstrdup("SCO"));
u->link_mtu = msg.setconf_rsp.link_mtu;
/* setup SBC encoder now we agree on parameters */
- if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
- bt_a2dp_setup(&u->a2dp);
+ if (u->profile == PROFILE_A2DP) {
+ setup_sbc(&u->a2dp);
u->block_size = u->a2dp.codesize;
- pa_log_info("sbc parameters:\n\tallocation=%u\n\tsubbands=%u\n\tblocks=%u\n\tbitpool=%u\n",
- u->a2dp.sbc.allocation, u->a2dp.sbc.subbands, u->a2dp.sbc.blocks, u->a2dp.sbc.bitpool);
- }
- else
+ pa_log_info("SBC parameters:\n\tallocation=%u\n\tsubbands=%u\n\tblocks=%u\n\tbitpool=%u\n",
+ u->a2dp.sbc.allocation, u->a2dp.sbc.subbands, u->a2dp.sbc.blocks, u->a2dp.sbc.bitpool);
+ } else
u->block_size = u->link_mtu;
return 0;
}
-static int bt_getstreamfd(struct userdata *u) {
- int e;
-// uint32_t period_count = io->buffer_size / io->period_size;
+static int setup_stream_fd(struct userdata *u) {
union {
bt_audio_msg_header_t rsp;
struct bt_start_stream_req start_req;
@@ -516,77 +620,68 @@ static int bt_getstreamfd(struct userdata *u) {
uint8_t buf[BT_SUGGESTED_BUFFER_SIZE];
} msg;
+ pa_assert(u);
+ pa_assert(u->stream_fd < 0);
+
memset(msg.buf, 0, BT_SUGGESTED_BUFFER_SIZE);
msg.start_req.h.type = BT_REQUEST;
msg.start_req.h.name = BT_START_STREAM;
msg.start_req.h.length = sizeof(msg.start_req);
- e = bt_audioservice_send(u->audioservice_fd, &msg.start_req.h);
- if (e < 0) {
- pa_log_error("Failed to send BT_STREAMSTART_REQ");
- return e;
- }
-
- e = bt_audioservice_expect(u->audioservice_fd, &msg.rsp, BT_START_STREAM, sizeof(msg.start_rsp));
- if (e < 0) {
- pa_log_error("Failed to expect BT_STREAMSTART_RSP");
- return e;
- }
+ if (service_send(u->service_fd, &msg.start_req.h) < 0)
+ return -1;
- e = bt_audioservice_expect(u->audioservice_fd, &msg.rsp, BT_NEW_STREAM, sizeof(msg.streamfd_ind));
- if (e < 0) {
- pa_log_error("Failed to expect BT_STREAMFD_IND");
- return e;
- }
+ if (service_expect(u->service_fd, &msg.rsp, BT_START_STREAM, sizeof(msg.start_rsp)) < 0)
+ return -1;
- if (u->stream_fd >= 0)
- pa_close(u->stream_fd);
+ if (service_expect(u->service_fd, &msg.rsp, BT_NEW_STREAM, sizeof(msg.streamfd_ind)) < 0)
+ return -1;
- u->stream_fd = bt_audio_service_get_data_fd(u->audioservice_fd);
- if (u->stream_fd < 0) {
- pa_log_error("Failed to get data fd: %s (%d)",pa_cstrerror(errno), errno);
- return -errno;
+ if ((u->stream_fd = bt_audio_service_get_data_fd(u->service_fd)) < 0) {
+ pa_log("Failed to get stream fd from audio service.");
+ return -1;
}
-// if (setsockopt(u->stream_fd, SOL_SCO, SCO_TXBUFS, &period_count, sizeof(period_count)) == 0)
-// return 0;
-// if (setsockopt(u->stream_fd, SOL_SCO, SO_SNDBUF, &period_count, sizeof(period_count)) == 0)
-// return 0;
-// /* FIXME : handle error codes */
+/* setsockopt(u->stream_fd, SOL_SCO, SCO_TXBUFS, &period_count, sizeof(period_count)); */
+/* setsockopt(u->stream_fd, SOL_SCO, SCO_SNDBUF, &period_count, sizeof(period_count)); */
+
pa_make_fd_nonblock(u->stream_fd);
-// pa_make_socket_low_delay(u->stream_fd);
+ pa_make_socket_low_delay(u->stream_fd);
return 0;
}
static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
struct userdata *u = PA_SINK(o)->userdata;
+ pa_assert(u->sink == PA_SINK(o));
pa_log_debug("got message: %d", code);
switch (code) {
case PA_SINK_MESSAGE_SET_STATE:
+
switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
+
case PA_SINK_SUSPENDED:
pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
- pa_smoother_pause(u->smoother, pa_rtclock_usec());
break;
+
case PA_SINK_IDLE:
case PA_SINK_RUNNING:
- if (u->sink->thread_info.state == PA_SINK_SUSPENDED)
- pa_smoother_resume(u->smoother, pa_rtclock_usec());
+ if (!PA_SINK_IS_OPENED(u->sink->thread_info.state))
+ u->started_at = pa_rtclock_usec();
+
break;
+
case PA_SINK_UNLINKED:
case PA_SINK_INIT:
+ case PA_SINK_INVALID_STATE:
;
}
break;
case PA_SINK_MESSAGE_GET_LATENCY: {
- pa_usec_t w, r;
-/* r = pa_smoother_get(u->smoother, pa_rtclock_usec()); */
-/* /\* w = pa_bytes_to_usec(u->offset + (uint64_t) u->memchunk.length, &u->sink->sample_spec); *\/ */
- *((pa_usec_t*) data) = /*w > r ? w - r :*/ 0;
+ *((pa_usec_t*) data) = 0;
return 0;
}
@@ -595,129 +690,254 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
return pa_sink_process_msg(o, code, data, offset, chunk);
}
-static int sco_process_render(struct userdata *u) {
- void *p;
+static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+ struct userdata *u = PA_SOURCE(o)->userdata;
+
+ pa_assert(u->source == PA_SOURCE(o));
+
+ pa_log_debug("got message: %d", code);
+ switch (code) {
+
+ case PA_SOURCE_MESSAGE_SET_STATE:
+
+ switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) {
+
+ case PA_SOURCE_SUSPENDED:
+ pa_smoother_pause(u->read_smoother, pa_rtclock_usec());
+ break;
+
+ case PA_SOURCE_IDLE:
+ case PA_SOURCE_RUNNING:
+ if (u->source->thread_info.state == PA_SOURCE_SUSPENDED)
+ pa_smoother_resume(u->read_smoother, pa_rtclock_usec());
+ break;
+
+ case PA_SOURCE_UNLINKED:
+ case PA_SOURCE_INIT:
+ case PA_SOURCE_INVALID_STATE:
+ ;
+ }
+ break;
+
+ case PA_SOURCE_MESSAGE_GET_LATENCY: {
+ *((pa_usec_t*) data) = 0;
+ return 0;
+ }
+
+ }
+
+ return pa_source_process_msg(o, code, data, offset, chunk);
+}
+
+static int hsp_process_render(struct userdata *u) {
int ret = 0;
pa_memchunk memchunk;
- pa_sink_render_full(u->sink, u->block_size, &memchunk);
+ pa_assert(u);
+ pa_assert(u->profile == PROFILE_HSP);
+ pa_assert(u->sink);
- p = pa_memblock_acquire(memchunk.memblock);
+ pa_sink_render_full(u->sink, u->block_size, &memchunk);
for (;;) {
ssize_t l;
+ const void *p;
- l = pa_loop_write(u->stream_fd, p, memchunk.length, NULL);
- pa_log_debug("Memblock written to socket: %li bytes", (long) l);
+ p = (const uint8_t*) pa_memblock_acquire(memchunk.memblock) + memchunk.index;
+ l = pa_write(u->stream_fd, p, memchunk.length, &u->write_type);
+ pa_memblock_release(memchunk.memblock);
+
+ pa_log_debug("Memblock written to socket: %lli bytes", (long long) l);
pa_assert(l != 0);
- if (l > 0) {
- u->offset += (uint64_t) l;
- break;
+ if (l < 0) {
+ if (errno == EINTR)
+ continue;
+ else {
+ pa_log_error("Failed to write data to SCO socket: %s", pa_cstrerror(errno));
+ ret = -1;
+ break;
+ }
+ } else {
+ pa_assert((size_t) l <= memchunk.length);
+
+ memchunk.index += (size_t) l;
+ memchunk.length -= (size_t) l;
+
+ u->write_index += (uint64_t) l;
+
+ if (memchunk.length <= 0)
+ break;
}
+ }
+
+ pa_memblock_unref(memchunk.memblock);
+
+ return ret;
+}
- if (errno == EINTR)
- pa_log_debug("EINTR");
- else if (errno == EAGAIN)
- pa_log_debug("EAGAIN");
- else {
- pa_log_error("Failed to write data to FIFO: %s", pa_cstrerror(errno));
- ret = -1;
+static int hsp_process_push(struct userdata *u) {
+ int ret = 0;
+ pa_memchunk memchunk;
+
+ pa_assert(u);
+ pa_assert(u->profile == PROFILE_HSP);
+ pa_assert(u->source);
+
+ memchunk.memblock = pa_memblock_new(u->core->mempool, u->block_size);
+ memchunk.index = memchunk.length = 0;
+
+ for (;;) {
+ ssize_t l;
+ void *p;
+
+ p = pa_memblock_acquire(memchunk.memblock);
+ l = pa_read(u->stream_fd, p, pa_memblock_get_length(memchunk.memblock), &u->read_type);
+ pa_memblock_release(memchunk.memblock);
+
+ if (l <= 0) {
+ if (l < 0 && errno == EINTR)
+ continue;
+ else {
+ pa_log_error("Failed to read data from SCO socket: %s", ret < 0 ? pa_cstrerror(errno) : "EOF");
+ ret = -1;
+ break;
+ }
+ } else {
+ memchunk.length = (size_t) l;
+ u->read_index += (uint64_t) l;
+
+ pa_source_post(u->source, &memchunk);
break;
}
}
- pa_memblock_release(memchunk.memblock);
pa_memblock_unref(memchunk.memblock);
return ret;
}
static int a2dp_process_render(struct userdata *u) {
+ size_t frame_size;
+ struct a2dp_info *a2dp;
+ struct rtp_header *header;
+ struct rtp_payload *payload;
+ size_t left;
+ void *d;
+ const void *p;
+ unsigned frame_count;
int written;
-
- struct bt_a2dp *a2dp = &u->a2dp;
- struct rtp_header *header = (void *) a2dp->buffer;
- struct rtp_payload *payload = (void *) (a2dp->buffer + sizeof(*header));
+ uint64_t writing_at;
pa_assert(u);
+ pa_assert(u->profile == PROFILE_A2DP);
+ pa_assert(u->sink);
- do {
- /* Render some data */
- int frame_size, encoded;
- void *p;
- pa_memchunk memchunk;
+ a2dp = &u->a2dp;
- pa_sink_render_full(u->sink, u->block_size, &memchunk);
+ if (a2dp->buffer_size < u->link_mtu) {
+ a2dp->buffer_size = 2*u->link_mtu;
+ pa_xfree(a2dp->buffer);
+ a2dp->buffer = pa_xmalloc(a2dp->buffer_size);
+ }
- p = pa_memblock_acquire(memchunk.memblock);
+ header = (struct rtp_header*) a2dp->buffer;
+ payload = (struct rtp_payload*) ((uint8_t*) a2dp->buffer + sizeof(*header));
+ d = (uint8_t*) a2dp->buffer + sizeof(*header) + sizeof(*payload);
+ left = a2dp->buffer_size - sizeof(*header) - sizeof(*payload);
- frame_size = (uint16_t) sbc_get_frame_length(&a2dp->sbc);
- pa_log_debug("SBC frame_size: %d", frame_size);
+ frame_size = sbc_get_frame_length(&a2dp->sbc);
+ frame_count = 0;
- encoded = sbc_encode(&a2dp->sbc, p, (int) a2dp->codesize, a2dp->buffer + a2dp->count,
- (int) (sizeof(a2dp->buffer) - a2dp->count), &written);
- pa_log_debug("SBC: encoded: %d; written: %d", encoded, written);
+ writing_at = u->write_index;
- pa_memblock_release(memchunk.memblock);
- pa_memblock_unref(memchunk.memblock);
+ do {
+ int encoded;
+
+ if (!u->write_memchunk.memblock)
+ pa_sink_render_full(u->sink, u->block_size, &u->write_memchunk);
+
+ p = (const uint8_t*) pa_memblock_acquire(u->write_memchunk.memblock) + u->write_memchunk.index;
+ encoded = sbc_encode(&a2dp->sbc,
+ (void*) p, u->write_memchunk.length,
+ d, left,
+ &written);
+ pa_memblock_release(u->write_memchunk.memblock);
if (encoded <= 0) {
pa_log_error("SBC encoding error (%d)", encoded);
return -1;
}
- a2dp->count += (size_t) written;
- a2dp->frame_count++;
- a2dp->samples += (unsigned) encoded / frame_size;
- a2dp->total_samples += (unsigned) encoded / frame_size;
+ pa_assert(written >= 0);
- } while (a2dp->count + (size_t) written <= u->link_mtu);
+ pa_assert((size_t) encoded <= u->write_memchunk.length);
+ pa_assert((size_t) written <= left);
+
+/* pa_log_debug("SBC: encoded: %d; written: %d", encoded, written); */
+
+ u->write_memchunk.index += encoded;
+ u->write_memchunk.length -= encoded;
+
+ if (u->write_memchunk.length <= 0) {
+ pa_memblock_unref(u->write_memchunk.memblock);
+ pa_memchunk_reset(&u->write_memchunk);
+ }
+
+ u->write_index += encoded;
+
+ d = (uint8_t*) d + written;
+ left -= written;
+
+ frame_count++;
+
+ } while ((uint8_t*) d - (uint8_t*) a2dp->buffer + written < (ptrdiff_t) u->link_mtu);
/* write it to the fifo */
memset(a2dp->buffer, 0, sizeof(*header) + sizeof(*payload));
- payload->frame_count = a2dp->frame_count;
+ payload->frame_count = frame_count;
header->v = 2;
header->pt = 1;
- header->sequence_number = htons(a2dp->seq_num);
- header->timestamp = htonl(a2dp->total_samples);
+ header->sequence_number = htons(a2dp->seq_num++);
+ header->timestamp = htonl(writing_at / frame_size);
header->ssrc = htonl(1);
+ p = a2dp->buffer;
+ left = (uint8_t*) d - (uint8_t*) a2dp->buffer;
+
for (;;) {
ssize_t l;
- l = pa_loop_write(u->stream_fd, a2dp->buffer, a2dp->count, NULL);
- pa_log_debug("avdtp_write: requested %lu bytes; written %li bytes", (unsigned long) a2dp->count, (long) l);
+ l = pa_write(u->stream_fd, p, left, &u->write_type);
+/* pa_log_debug("write: requested %lu bytes; written %li bytes; mtu=%li", (unsigned long) left, (long) l, (unsigned long) u->link_mtu); */
pa_assert(l != 0);
- if (l > 0)
- break;
+ if (l < 0) {
+ if (errno == EINTR)
+ continue;
+ else {
+ pa_log_error("Failed to write data to socket: %s", pa_cstrerror(errno));
+ return -1;
+ }
+ } else {
+ pa_assert((size_t) l <= left);
- if (errno == EINTR)
- pa_log_debug("EINTR");
- else if (errno == EAGAIN)
- pa_log_debug("EAGAIN");
- else {
- pa_log_error("Failed to write data to FIFO: %s", pa_cstrerror(errno));
- return -1;
+ d = (uint8_t*) d + l;
+ left -= l;
+
+ if (left <= 0)
+ break;
}
}
- u->offset += a2dp->codesize*a2dp->frame_count;
-
- /* Reset buffer of data to send */
- a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload);
- a2dp->frame_count = 0;
- a2dp->samples = 0;
- a2dp->seq_num++;
-
return 0;
}
static void thread_func(void *userdata) {
struct userdata *u = userdata;
+ pa_bool_t do_write = FALSE, writable = FALSE;
pa_assert(u);
@@ -729,59 +949,93 @@ static void thread_func(void *userdata) {
pa_thread_mq_install(&u->thread_mq);
pa_rtpoll_install(u->rtpoll);
- pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec());
+ pa_smoother_set_time_offset(u->read_smoother, pa_rtclock_usec());
for (;;) {
- int ret, l;
struct pollfd *pollfd;
- uint64_t n;
- pa_usec_t usec;
-
- if (PA_SINK_IS_OPENED(u->sink->thread_info.state))
- if (u->sink->thread_info.rewind_requested)
- pa_sink_process_rewind(u->sink, 0);
+ int ret;
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
- if (PA_SINK_IS_OPENED(u->sink->thread_info.state) && pollfd->revents) {
- if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
- if ((l = a2dp_process_render(u)) < 0)
- goto fail;
- } else {
- if ((l = sco_process_render(u)) < 0)
+ if (u->source && PA_SOURCE_IS_LINKED(u->source->thread_info.state)) {
+
+ if (pollfd->revents & POLLIN) {
+
+ if (hsp_process_push(u) < 0)
goto fail;
+
+ /* We just read something, so we are supposed to write something, too */
+ do_write = TRUE;
}
- pollfd->revents = 0;
-
- /* feed the time smoother */
- n = u->offset;
- if (ioctl(u->stream_fd, SIOCOUTQ, &l) >= 0 && l > 0)
- n -= (uint64_t) l;
- usec = pa_bytes_to_usec(n, &u->sink->sample_spec);
- if (usec > u->latency)
- usec -= u->latency;
- else
- usec = 0;
- pa_smoother_put(u->smoother, pa_rtclock_usec(), usec);
}
+ if (u->sink && PA_SINK_IS_LINKED(u->sink->thread_info.state)) {
+
+ if (u->sink->thread_info.rewind_requested)
+ pa_sink_process_rewind(u->sink, 0);
+
+ if (pollfd->revents & POLLOUT)
+ writable = TRUE;
+
+ if ((!u->source || !PA_SOURCE_IS_LINKED(u->source->thread_info.state)) && !do_write && writable) {
+ pa_usec_t time_passed;
+ uint64_t should_have_written;
+
+ /* Hmm, there is no input stream we could synchronize
+ * to. So let's do things by time */
+
+ time_passed = pa_rtclock_usec() - u->started_at;
+ should_have_written = pa_usec_to_bytes(time_passed, &u->sink->sample_spec);
+
+ do_write = u->write_index <= should_have_written ;
+/* pa_log_debug("Time has come: %s", pa_yes_no(do_write)); */
+ }
+
+ if (writable && do_write) {
+
+ if (u->profile == PROFILE_A2DP) {
+ if (a2dp_process_render(u) < 0)
+ goto fail;
+ } else {
+ if (hsp_process_render(u) < 0)
+ goto fail;
+ }
+
+ do_write = FALSE;
+ writable = FALSE;
+ }
+
+ if ((!u->source || !PA_SOURCE_IS_LINKED(u->source->thread_info.state)) && !do_write) {
+ pa_usec_t time_passed, next_write_at, sleep_for;
+
+ /* Hmm, there is no input stream we could synchronize
+ * to. So let's estimate when we need to wake up the latest */
+
+ time_passed = pa_rtclock_usec() - u->started_at;
+ next_write_at = pa_bytes_to_usec(u->write_index, &u->sink->sample_spec);
+ sleep_for = time_passed < next_write_at ? next_write_at - time_passed : 0;
+
+/* pa_log("Sleeping for %lu; time passed %lu, next write at %lu", (unsigned long) sleep_for, (unsigned long) time_passed, (unsigned long)next_write_at); */
+
+ pa_rtpoll_set_timer_relative(u->rtpoll, sleep_for);
+ }
+ } else
+ pa_rtpoll_set_timer_disabled(u->rtpoll);
+
/* Hmm, nothing to do. Let's sleep */
- pa_log_debug("IO thread going to sleep");
- pollfd->events = (short) (PA_SINK_IS_OPENED(u->sink->thread_info.state) ? POLLOUT : 0);
- if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) {
- pa_log_error("rtpoll_run < 0");
+ pollfd->events = (short) (((u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state) && !writable) ? POLLOUT : 0) |
+ (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state) ? POLLIN : 0));
+
+ if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
goto fail;
- }
- pa_log_debug("IO thread waking up");
- if (ret == 0) {
- pa_log_debug("rtpoll_run == 0");
+ if (ret == 0)
goto finish;
- }
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
- if (pollfd->revents & ~POLLOUT) {
- pa_log_error("FIFO shutdown.");
+
+ if (pollfd->revents & ~(POLLOUT|POLLIN)) {
+ pa_log_error("FD error.");
goto fail;
}
}
@@ -796,324 +1050,654 @@ finish:
pa_log_debug("IO thread shutting down");
}
-static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *msg, void *userdata) {
- DBusMessageIter arg_i;
- DBusError err;
- const char *value;
- struct userdata *u;
+/* static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *msg, void *userdata) { */
+/* DBusMessageIter arg_i; */
+/* DBusError err; */
+/* const char *value; */
+/* struct userdata *u; */
- pa_assert(bus);
- pa_assert(msg);
- pa_assert(userdata);
- u = userdata;
+/* pa_assert(bus); */
+/* pa_assert(msg); */
+/* pa_assert(userdata); */
+/* u = userdata; */
+
+/* pa_log_debug("dbus: interface=%s, path=%s, member=%s\n", */
+/* dbus_message_get_interface(msg), */
+/* dbus_message_get_path(msg), */
+/* dbus_message_get_member(msg)); */
+
+/* dbus_error_init(&err); */
- pa_log_debug("dbus: interface=%s, path=%s, member=%s\n",
- dbus_message_get_interface(msg),
- dbus_message_get_path(msg),
- dbus_message_get_member(msg));
+/* if (!dbus_message_has_path(msg, u->path)) */
+/* goto done; */
+
+/* if (dbus_message_is_signal(msg, "org.bluez.Headset", "PropertyChanged") || */
+/* dbus_message_is_signal(msg, "org.bluez.AudioSink", "PropertyChanged")) { */
+
+/* struct device *d; */
+/* const char *profile; */
+/* DBusMessageIter variant_i; */
+/* dbus_uint16_t gain; */
- dbus_error_init(&err);
+/* if (!dbus_message_iter_init(msg, &arg_i)) { */
+/* pa_log("dbus: message has no parameters"); */
+/* goto done; */
+/* } */
- if (dbus_message_is_signal(msg, "org.bluez.Headset", "PropertyChanged") ||
- dbus_message_is_signal(msg, "org.bluez.AudioSink", "PropertyChanged")) {
+/* if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_STRING) { */
+/* pa_log("Property name not a string."); */
+/* goto done; */
+/* } */
- struct device *d;
- const char *profile;
- DBusMessageIter variant_i;
- dbus_uint16_t gain;
+/* dbus_message_iter_get_basic(&arg_i, &value); */
- if (!dbus_message_iter_init(msg, &arg_i)) {
- pa_log("dbus: message has no parameters");
- goto done;
- }
+/* if (!dbus_message_iter_next(&arg_i)) { */
+/* pa_log("Property value missing"); */
+/* goto done; */
+/* } */
- if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_STRING) {
- pa_log("Property name not a string.");
- goto done;
- }
+/* if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_VARIANT) { */
+/* pa_log("Property value not a variant."); */
+/* goto done; */
+/* } */
- dbus_message_iter_get_basic(&arg_i, &value);
+/* dbus_message_iter_recurse(&arg_i, &variant_i); */
- if (!dbus_message_iter_next(&arg_i)) {
- pa_log("Property value missing");
- goto done;
- }
+/* if (dbus_message_iter_get_arg_type(&variant_i) != DBUS_TYPE_UINT16) { */
+/* dbus_message_iter_get_basic(&variant_i, &gain); */
- if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_VARIANT) {
- pa_log("Property value not a variant.");
- goto done;
- }
+/* if (pa_streq(value, "SpeakerGain")) { */
+/* pa_log("spk gain: %d", gain); */
+/* pa_cvolume_set(&u->sink->virtual_volume, 1, (pa_volume_t) (gain * PA_VOLUME_NORM / 15)); */
+/* pa_subscription_post(u->sink->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, u->sink->index); */
+/* } else { */
+/* pa_log("mic gain: %d", gain); */
+/* if (!u->source) */
+/* goto done; */
- dbus_message_iter_recurse(&arg_i, &variant_i);
+/* pa_cvolume_set(&u->source->virtual_volume, 1, (pa_volume_t) (gain * PA_VOLUME_NORM / 15)); */
+/* pa_subscription_post(u->source->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, u->source->index); */
+/* } */
+/* } */
+/* } */
- if (dbus_message_iter_get_arg_type(&variant_i) != DBUS_TYPE_UINT16) {
- dbus_message_iter_get_basic(&variant_i, &gain);
+/* done: */
+/* dbus_error_free(&err); */
+/* return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; */
+/* } */
- if (pa_streq(value, "SpeakerGain")) {
- pa_log("spk gain: %d", gain);
- pa_cvolume_set(&u->sink->virtual_volume, 1, (pa_volume_t) (gain * PA_VOLUME_NORM / 15));
- pa_subscription_post(u->sink->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, u->sink->index);
- } else {
- pa_log("mic gain: %d", gain);
- if (!u->source)
- goto done;
+/* static int sink_get_volume_cb(pa_sink *s) { */
+/* struct userdata *u = s->userdata; */
+/* pa_assert(u); */
- pa_cvolume_set(&u->source->virtual_volume, 1, (pa_volume_t) (gain * PA_VOLUME_NORM / 15));
- pa_subscription_post(u->source->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, u->source->index);
- }
- }
+/* /\* refresh? *\/ */
+
+/* return 0; */
+/* } */
+
+/* static int source_get_volume_cb(pa_source *s) { */
+/* struct userdata *u = s->userdata; */
+/* pa_assert(u); */
+
+/* /\* refresh? *\/ */
+
+/* return 0; */
+/* } */
+
+/* static int sink_set_volume_cb(pa_sink *s) { */
+/* DBusError e; */
+/* DBusMessage *m, *r; */
+/* DBusMessageIter it, itvar; */
+/* dbus_uint16_t vol; */
+/* const char *spkgain = "SpeakerGain"; */
+/* struct userdata *u = s->userdata; */
+/* pa_assert(u); */
+
+/* dbus_error_init(&e); */
+
+/* vol = ((float) pa_cvolume_max(&s->virtual_volume) / PA_VOLUME_NORM) * 15; */
+/* pa_log_debug("set headset volume: %d", vol); */
+
+/* pa_assert_se(m = dbus_message_new_method_call("org.bluez", u->path, "org.bluez.Headset", "SetProperty")); */
+/* dbus_message_iter_init_append(m, &it); */
+/* dbus_message_iter_append_basic(&it, DBUS_TYPE_STRING, &spkgain); */
+/* dbus_message_iter_open_container(&it, DBUS_TYPE_VARIANT, DBUS_TYPE_UINT16_AS_STRING, &itvar); */
+/* dbus_message_iter_append_basic(&itvar, DBUS_TYPE_UINT16, &vol); */
+/* dbus_message_iter_close_container(&it, &itvar); */
+
+/* r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->conn), m, -1, &e); */
+
+/* finish: */
+/* if (m) */
+/* dbus_message_unref(m); */
+/* if (r) */
+/* dbus_message_unref(r); */
+
+/* dbus_error_free(&e); */
+
+/* return 0; */
+/* } */
+
+/* static int source_set_volume_cb(pa_source *s) { */
+/* dbus_uint16_t vol; */
+/* struct userdata *u = s->userdata; */
+/* pa_assert(u); */
+
+/* vol = ((float)pa_cvolume_max(&s->virtual_volume) / PA_VOLUME_NORM) * 15; */
+
+/* pa_log_debug("set headset mic volume: %d (not implemented yet)", vol); */
+
+/* return 0; */
+/* } */
+
+static char *get_name(const char *type, pa_modargs *ma, const char *device_id, pa_bool_t *namereg_fail) {
+ char *t;
+ const char *n;
+
+ pa_assert(type);
+ pa_assert(ma);
+ pa_assert(device_id);
+ pa_assert(namereg_fail);
+
+ t = pa_sprintf_malloc("%s_name", type);
+ n = pa_modargs_get_value(ma, t, NULL);
+ pa_xfree(t);
+
+ if (n) {
+ *namereg_fail = TRUE;
+ return pa_xstrdup(n);
}
-done:
- dbus_error_free(&err);
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ if ((n = pa_modargs_get_value(ma, "name", NULL)))
+ *namereg_fail = TRUE;
+ else {
+ n = device_id;
+ *namereg_fail = FALSE;
+ }
+
+ return pa_sprintf_malloc("bluez_%s.%s", type, n);
}
-static int sink_get_volume_cb(pa_sink *s) {
- struct userdata *u = s->userdata;
+static int add_sink(struct userdata *u) {
+ pa_sink_new_data data;
+ pa_bool_t b;
+
+ pa_sink_new_data_init(&data);
+ data.driver = __FILE__;
+ data.module = u->module;
+ pa_sink_new_data_set_sample_spec(&data, &u->sample_spec);
+ pa_proplist_sets(data.proplist, "bluetooth.protocol", u->profile == PROFILE_A2DP ? "a2dp" : "sco");
+ data.card = u->card;
+ data.name = get_name("sink", u->modargs, u->device->address, &b);
+ data.namereg_fail = b;
+
+ u->sink = pa_sink_new(u->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
+ pa_sink_new_data_done(&data);
+
+ if (!u->sink) {
+ pa_log_error("Failed to create sink");
+ return -1;
+ }
+
+ u->sink->userdata = u;
+ u->sink->parent.process_msg = sink_process_msg;
+/* u->sink->get_volume = sink_get_volume_cb; */
+/* u->sink->set_volume = sink_set_volume_cb; */
+
+ pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+ pa_sink_set_rtpoll(u->sink, u->rtpoll);
+
+ return 0;
+}
+
+static int add_source(struct userdata *u) {
+ pa_source_new_data data;
+ pa_bool_t b;
+
+ pa_source_new_data_init(&data);
+ data.driver = __FILE__;
+ data.module = u->module;
+ pa_source_new_data_set_sample_spec(&data, &u->sample_spec);
+ pa_proplist_sets(data.proplist, "bluetooth.protocol", u->profile == PROFILE_A2DP ? "a2dp" : "sco");
+ data.card = u->card;
+ data.name = get_name("source", u->modargs, u->device->address, &b);
+ data.namereg_fail = b;
+
+ u->source = pa_source_new(u->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
+ pa_source_new_data_done(&data);
+
+ if (!u->source) {
+ pa_log_error("Failed to create source");
+ return -1;
+ }
+
+ u->source->userdata = u;
+ u->source->parent.process_msg = source_process_msg;
+/* u->source->get_volume = source_get_volume_cb; */
+/* u->source->set_volume = source_set_volume_cb; */
+
+ pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
+ pa_source_set_rtpoll(u->source, u->rtpoll);
+
+ return 0;
+}
+
+static int init_bt(struct userdata *u) {
pa_assert(u);
- /* refresh? */
+ /* connect to the bluez audio service */
+ if ((u->service_fd = bt_audio_service_open()) < 0) {
+ pa_log_error("Couldn't connect to bluetooth audio service");
+ return -1;
+ }
+ pa_log_debug("Connected to the bluetooth audio service");
return 0;
}
-static int source_get_volume_cb(pa_source *s) {
- struct userdata *u = s->userdata;
+static void shutdown_bt(struct userdata *u) {
+ pa_assert(u);
+
+ if (u->stream_fd <= 0) {
+ pa_close(u->stream_fd);
+ u->stream_fd = -1;
+ }
+
+ u->write_type = u->read_type = 0;
+}
+
+static int setup_bt(struct userdata *u) {
pa_assert(u);
- /* refresh? */
+ if (get_caps(u) < 0)
+ return -1;
+
+ pa_log_debug("Got device capabilities");
+
+ if (set_conf(u) < 0)
+ return -1;
+
+ pa_log_debug("Connection to the device configured");
+
+ if (setup_stream_fd(u) < 0)
+ return -1;
+
+ pa_log_debug("Got the stream socket");
return 0;
}
-static int sink_set_volume_cb(pa_sink *s) {
- DBusError e;
- DBusMessage *m, *r;
- DBusMessageIter it, itvar;
- dbus_uint16_t vol;
- const char *spkgain = "SpeakerGain";
- struct userdata *u = s->userdata;
+static int init_profile(struct userdata *u) {
+ int r = 0;
pa_assert(u);
- dbus_error_init(&e);
+ if (setup_bt(u) < 0)
+ return -1;
- vol = ((float) pa_cvolume_max(&s->virtual_volume) / PA_VOLUME_NORM) * 15;
- pa_log_debug("set headset volume: %d", vol);
+ if (u->profile == PROFILE_A2DP ||
+ u->profile == PROFILE_HSP)
+ if (add_sink(u) < 0)
+ r = -1;
- pa_assert_se(m = dbus_message_new_method_call("org.bluez", u->path, "org.bluez.Headset", "SetProperty"));
- dbus_message_iter_init_append(m, &it);
- dbus_message_iter_append_basic(&it, DBUS_TYPE_STRING, &spkgain);
- dbus_message_iter_open_container(&it, DBUS_TYPE_VARIANT, DBUS_TYPE_UINT16_AS_STRING, &itvar);
- dbus_message_iter_append_basic(&itvar, DBUS_TYPE_UINT16, &vol);
- dbus_message_iter_close_container(&it, &itvar);
+ if (u->profile == PROFILE_HSP)
+ if (add_source(u) < 0)
+ r = -1;
- r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->conn), m, -1, &e);
+ return r;
+}
-finish:
- if (m)
- dbus_message_unref(m);
- if (r)
- dbus_message_unref(r);
+static void stop_thread(struct userdata *u) {
+ pa_assert(u);
+
+ if (u->thread) {
+ pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+ pa_thread_free(u->thread);
+ u->thread = NULL;
+ }
+
+ if (u->rtpoll_item) {
+ pa_rtpoll_item_free(u->rtpoll_item);
+ u->rtpoll_item = NULL;
+ }
- dbus_error_free(&e);
+ if (u->sink) {
+ pa_sink_unref(u->sink);
+ u->sink = NULL;
+ }
+
+ if (u->source) {
+ pa_source_unref(u->source);
+ u->source = NULL;
+ }
+}
+
+static int start_thread(struct userdata *u) {
+ struct pollfd *pollfd;
+
+ pa_assert(u);
+ pa_assert(!u->thread);
+ pa_assert(!u->rtpoll_item);
+
+ u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
+ pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+ pollfd->fd = u->stream_fd;
+ pollfd->events = pollfd->revents = 0;
+
+ if (!(u->thread = pa_thread_new(thread_func, u))) {
+ pa_log_error("Failed to create IO thread");
+ stop_thread(u);
+ return -1;
+ }
+
+ if (u->sink)
+ pa_sink_put(u->sink);
+
+ if (u->source)
+ pa_source_put(u->source);
return 0;
}
-static int source_set_volume_cb(pa_source *s) {
- dbus_uint16_t vol;
- struct userdata *u = s->userdata;
+static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
+ struct userdata *u;
+ enum profile *d;
+ pa_queue *inputs = NULL, *outputs = NULL;
+
+ pa_assert(c);
+ pa_assert(new_profile);
+ pa_assert_se(u = c->userdata);
+
+ d = PA_CARD_PROFILE_DATA(new_profile);
+
+ if (u->sink) {
+ inputs = pa_sink_move_all_start(u->sink);
+ pa_sink_unlink(u->sink);
+ }
+
+ if (u->source) {
+ outputs = pa_source_move_all_start(u->source);
+ pa_source_unlink(u->source);
+ }
+
+ stop_thread(u);
+ shutdown_bt(u);
+
+ if (u->write_memchunk.memblock) {
+ pa_memblock_unref(u->write_memchunk.memblock);
+ pa_memchunk_reset(&u->write_memchunk);
+ }
+
+ u->profile = *d;
+ init_profile(u);
+
+ if (u->sink || u->source)
+ start_thread(u);
+
+ if (inputs) {
+ if (u->sink)
+ pa_sink_move_all_finish(u->sink, inputs, FALSE);
+ else
+ pa_sink_move_all_fail(inputs);
+ }
+
+ if (outputs) {
+ if (u->source)
+ pa_source_move_all_finish(u->source, outputs, FALSE);
+ else
+ pa_source_move_all_fail(outputs);
+ }
+
+ return 0;
+}
+
+static int add_card(struct userdata *u, const char * default_profile) {
+ pa_card_new_data data;
+ pa_bool_t b;
+ pa_card_profile *p;
+ enum profile *d;
+ const char *ff;
+ char *n;
+
+ pa_card_new_data_init(&data);
+ data.driver = __FILE__;
+ data.module = u->module;
+
+ n = pa_bluetooth_cleanup_name(u->device->name);
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, n);
+ pa_xfree(n);
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device->address);
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "bluez");
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "sound");
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CONNECTOR, "bluetooth");
+ if ((ff = pa_bluetooth_get_form_factor(u->device->class)))
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_FORM_FACTOR, ff);
+ pa_proplist_sets(data.proplist, "bluez.path", u->device->path);
+ pa_proplist_setf(data.proplist, "bluez.class", "0x%06x", (unsigned) u->device->class);
+ pa_proplist_sets(data.proplist, "bluez.name", u->device->name);
+ data.name = get_name("card", u->modargs, u->device->address, &b);
+ data.namereg_fail = b;
+
+ data.profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
+ if (u->device->audio_sink_info_valid > 0) {
+ p = pa_card_profile_new("a2dp", "A2DP Advanced Audio Distribution Profile", sizeof(enum profile));
+ p->priority = 10;
+ p->n_sinks = 1;
+ p->n_sources = 0;
+ p->max_sink_channels = 2;
+ p->max_source_channels = 0;
+
+ d = PA_CARD_PROFILE_DATA(p);
+ *d = PROFILE_A2DP;
+
+ pa_hashmap_put(data.profiles, p->name, p);
+ }
+
+ if (u->device->headset_info_valid > 0) {
+ p = pa_card_profile_new("hsp", "HSP/HFP Headset/Hands-Free Profile", sizeof(enum profile));
+ p->priority = 20;
+ p->n_sinks = 1;
+ p->n_sources = 1;
+ p->max_sink_channels = 1;
+ p->max_source_channels = 1;
+
+ d = PA_CARD_PROFILE_DATA(p);
+ *d = PROFILE_HSP;
+
+ pa_hashmap_put(data.profiles, p->name, p);
+ }
+
+ pa_assert(!pa_hashmap_isempty(data.profiles));
+
+ p = pa_card_profile_new("off", "Off", sizeof(enum profile));
+ d = PA_CARD_PROFILE_DATA(p);
+ *d = PROFILE_OFF;
+ pa_hashmap_put(data.profiles, p->name, p);
+
+ if (default_profile) {
+ if (pa_hashmap_get(data.profiles, default_profile))
+ pa_card_new_data_set_profile(&data, default_profile);
+ else
+ pa_log_warn("Profile '%s' not valid or not supported by device.", default_profile);
+ }
+
+ u->card = pa_card_new(u->core, &data);
+ pa_card_new_data_done(&data);
+
+ if (!u->card) {
+ pa_log("Failed to allocate card.");
+ return -1;
+ }
+
+ u->card->userdata = u;
+ u->card->set_profile = card_set_profile;
+
+ d = PA_CARD_PROFILE_DATA(u->card->active_profile);
+ u->profile = *d;
+
+ return 0;
+}
+
+static int setup_dbus(struct userdata *u) {
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ u->connection = pa_dbus_bus_get(u->core, DBUS_BUS_SYSTEM, &error);
+ if (dbus_error_is_set(&error) || (!u->connection)) {
+ pa_log("Failed to get D-Bus connection: %s", error.message);
+ dbus_error_free(&error);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int find_device(struct userdata *u, const char *address, const char *path) {
pa_assert(u);
- vol = ((float)pa_cvolume_max(&s->virtual_volume) / PA_VOLUME_NORM) * 15;
+ if (!address && !path) {
+ pa_log_error("Failed to get device address/path from module arguments.");
+ return -1;
+ }
+
+ if (path) {
+ if (!(u->device = pa_bluetooth_get_device(pa_dbus_connection_get(u->connection), path))) {
+ pa_log_error("%s is not a valid BlueZ audio device.", path);
+ return -1;
+ }
- pa_log_debug("set headset mic volume: %d (not implemented yet)", vol);
+ if (address && !(pa_streq(u->device->address, address))) {
+ pa_log_error("Passed path %s and address %s don't match.", path, address);
+ return -1;
+ }
+ } else {
+ if (!(u->device = pa_bluetooth_find_device(pa_dbus_connection_get(u->connection), address))) {
+ pa_log_error("%s is not known.", address);
+ return -1;
+ }
+ }
return 0;
}
int pa__init(pa_module* m) {
- int e;
pa_modargs *ma;
uint32_t channels;
- pa_sink_new_data data;
- struct pollfd *pollfd;
struct userdata *u;
- DBusError err;
- char *tmp;
+ const char *address, *path;
pa_assert(m);
- dbus_error_init(&err);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log_error("Failed to parse module arguments");
+ goto fail;
+ }
m->userdata = u = pa_xnew0(struct userdata, 1);
u->module = m;
u->core = m->core;
- u->audioservice_fd = -1;
+ u->service_fd = -1;
u->stream_fd = -1;
- u->transport = (uint8_t) -1;
- u->offset = 0;
- u->latency = 0;
- u->a2dp.sbc_initialized = FALSE;
- u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE, 10);
+ u->read_smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE, 10);
u->rtpoll = pa_rtpoll_new();
pa_thread_mq_init(&u->thread_mq, u->core->mainloop, u->rtpoll);
- u->rtpoll_item = NULL;
- u->ss = m->core->default_sample_spec;
+ u->sample_spec = m->core->default_sample_spec;
+ u->modargs = ma;
- if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
- pa_log_error("Failed to parse module arguments");
- goto fail;
- }
- if (!(u->name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME)))) {
- pa_log_error("Failed to get device name from module arguments");
- goto fail;
- }
- if (!(u->addr = pa_xstrdup(pa_modargs_get_value(ma, "address", NULL)))) {
- pa_log_error("Failed to get device address from module arguments");
- goto fail;
- }
- if (!(u->profile = pa_xstrdup(pa_modargs_get_value(ma, "profile", NULL)))) {
- pa_log_error("Failed to get profile from module arguments");
- goto fail;
- }
- if (pa_modargs_get_value_u32(ma, "rate", &u->ss.rate) < 0) {
+ if (pa_modargs_get_value_u32(ma, "rate", &u->sample_spec.rate) < 0 ||
+ u->sample_spec.rate <= 0 || u->sample_spec.rate > PA_RATE_MAX) {
pa_log_error("Failed to get rate from module arguments");
goto fail;
}
- u->path = pa_xstrdup(pa_modargs_get_value(ma, "path", NULL));
- channels = u->ss.channels;
- if (pa_modargs_get_value_u32(ma, "channels", &channels) < 0) {
+ channels = u->sample_spec.channels;
+ if (pa_modargs_get_value_u32(ma, "channels", &channels) < 0 ||
+ channels <= 0 || channels > PA_CHANNELS_MAX) {
pa_log_error("Failed to get channels from module arguments");
goto fail;
}
- u->ss.channels = (uint8_t) channels;
+ u->sample_spec.channels = (uint8_t) channels;
- /* connect to the bluez audio service */
- u->audioservice_fd = bt_audio_service_open();
- if (u->audioservice_fd <= 0) {
- pa_log_error("Couldn't connect to bluetooth audio service");
+ if (setup_dbus(u) < 0)
goto fail;
- }
- pa_log_debug("Connected to the bluetooth audio service");
- /* queries device capabilities */
- e = bt_getcaps(u);
- if (e < 0) {
- pa_log_error("Failed to get device capabilities");
- goto fail;
- }
- pa_log_debug("Got device capabilities");
+ address = pa_modargs_get_value(ma, "address", NULL);
+ path = pa_modargs_get_value(ma, "path", NULL);
- /* configures the connection */
- e = bt_setconf(u);
- if (e < 0) {
- pa_log_error("Failed to set config");
+ if (find_device(u, address, path) < 0)
goto fail;
- }
- pa_log_debug("Connection to the device configured");
- /* gets the device socket */
- e = bt_getstreamfd(u);
- if (e < 0) {
- pa_log_error("Failed to get stream fd (%d)", e);
- goto fail;
- }
- pa_log_debug("Got the device socket");
+ pa_assert(u->device);
- /* create sink */
- pa_sink_new_data_init(&data);
- data.driver = __FILE__;
- data.module = m;
- pa_sink_new_data_set_name(&data, u->name);
- pa_sink_new_data_set_sample_spec(&data, &u->ss);
- pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->name);
- pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Bluetooth %s '%s' (%s)", u->strtransport, u->name, u->addr);
- pa_proplist_sets(data.proplist, "bluetooth.protocol", u->profile);
- pa_proplist_setf(data.proplist, PA_PROP_DEVICE_API, "bluez");
- pa_proplist_setf(data.proplist, PA_PROP_DEVICE_CLASS, "sound");
- pa_proplist_setf(data.proplist, PA_PROP_DEVICE_CONNECTOR, "bluetooth");
-/* pa_proplist_setf(data.proplist, PA_PROP_DEVICE_FORM_FACTOR, "headset"); /\*FIXME*\/ */
-/* pa_proplist_setf(data.proplist, PA_PROP_DEVICE_VENDOR_PRODUCT_ID, "product_id"); /\*FIXME*\/ */
-/* pa_proplist_setf(data.proplist, PA_PROP_DEVICE_SERIAL, "serial"); /\*FIXME*\/ */
- u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
- pa_sink_new_data_done(&data);
- if (!u->sink) {
- pa_log_error("Failed to create sink");
+ /* Add the card structure. This will also initialize the default profile */
+ if (add_card(u, pa_modargs_get_value(ma, "profile", NULL)) < 0)
goto fail;
- }
- u->sink->userdata = u;
- u->sink->parent.process_msg = sink_process_msg;
- pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
- pa_sink_set_rtpoll(u->sink, u->rtpoll);
- u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
- pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
- pollfd->fd = u->stream_fd;
- pollfd->events = pollfd->revents = 0;
-
- /* start rt thread */
- if (!(u->thread = pa_thread_new(thread_func, u))) {
- pa_log_error("Failed to create IO thread");
- goto fail;
- }
- pa_sink_put(u->sink);
- if (!u->path)
- goto end;
-
- /* connect to the bus */
- u->conn = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &err);
- if (dbus_error_is_set(&err) || (u->conn == NULL) ) {
- pa_log("Failed to get D-Bus connection: %s", err.message);
+ /* Connect to the BT service and query capabilities */
+ if (init_bt(u) < 0)
goto fail;
- }
- /* monitor property changes */
- if (!dbus_connection_add_filter(pa_dbus_connection_get(u->conn), filter_cb, u, NULL)) {
- pa_log_error("Failed to add filter function");
+ if (init_profile(u) < 0)
goto fail;
- }
-
- if (pa_streq(u->profile, "hsp")) {
-
- tmp = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged',path='%s'", u->path);
- dbus_bus_add_match(pa_dbus_connection_get(u->conn), tmp, &err);
- pa_xfree(tmp);
- if (dbus_error_is_set(&err)) {
- pa_log_error("Unable to subscribe to org.bluez.Headset signals: %s: %s", err.name, err.message);
- goto fail;
- }
- } else {
-
- tmp = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged',path='%s'", u->path);
- dbus_bus_add_match(pa_dbus_connection_get(u->conn), tmp, &err);
- pa_xfree(tmp);
- if (dbus_error_is_set(&err)) {
- pa_log_error("Unable to subscribe to org.bluez.AudioSink signals: %s: %s", err.name, err.message);
- goto fail;
- }
- }
- u->sink->get_volume = sink_get_volume_cb;
- u->sink->set_volume = sink_set_volume_cb;
- if (u->source) {
- u->source->get_volume = source_get_volume_cb;
- u->source->set_volume = source_set_volume_cb;
- }
+/* if (u->path) { */
+/* DBusError err; */
+/* dbus_error_init(&err); */
+/* char *t; */
+
+
+/* if (!dbus_connection_add_filter(pa_dbus_connection_get(u->conn), filter_cb, u, NULL)) { */
+/* pa_log_error("Failed to add filter function"); */
+/* goto fail; */
+/* } */
+
+/* if (u->transport == BT_CAPABILITIES_TRANSPORT_SCO || */
+/* u->transport == BT_CAPABILITIES_TRANSPORT_ANY) { */
+/* t = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged',path='%s'", u->path); */
+/* dbus_bus_add_match(pa_dbus_connection_get(u->conn), t, &err); */
+/* pa_xfree(t); */
+
+/* if (dbus_error_is_set(&err)) { */
+/* pa_log_error("Unable to subscribe to org.bluez.Headset signals: %s: %s", err.name, err.message); */
+/* goto fail; */
+/* } */
+/* } */
+
+/* if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP || */
+/* u->transport == BT_CAPABILITIES_TRANSPORT_ANY) { */
+/* t = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged',path='%s'", u->path); */
+/* dbus_bus_add_match(pa_dbus_connection_get(u->conn), t, &err); */
+/* pa_xfree(t); */
+
+/* if (dbus_error_is_set(&err)) { */
+/* pa_log_error("Unable to subscribe to org.bluez.AudioSink signals: %s: %s", err.name, err.message); */
+/* goto fail; */
+/* } */
+/* } */
+/* } */
+
+ if (start_thread(u) < 0)
+ goto fail;
-end:
- pa_modargs_free(ma);
return 0;
fail:
- if (ma)
- pa_modargs_free(ma);
-
pa__done(m);
return -1;
}
+int pa__get_n_used(pa_module *m) {
+ struct userdata *u;
+
+ pa_assert(m);
+ pa_assert_se(u = m->userdata);
+
+ return
+ (u->sink ? pa_sink_linked_by(u->sink) : 0) +
+ (u->source ? pa_source_linked_by(u->source) : 0);
+}
+
void pa__done(pa_module *m) {
struct userdata *u;
pa_assert(m);
@@ -1121,63 +1705,72 @@ void pa__done(pa_module *m) {
if (!(u = m->userdata))
return;
- if (u->conn) {
- DBusError error;
- char *tmp;
- dbus_error_init(&error);
+ if (u->sink)
+ pa_sink_unlink(u->sink);
- if (pa_streq(u->profile, "hsp")) {
- tmp = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged',path='%s'", u->path);
- dbus_bus_remove_match(pa_dbus_connection_get(u->conn), tmp, &error);
- pa_xfree(tmp);
- dbus_error_free(&error);
- } else {
- tmp = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged',path='%s'", u->path);
- dbus_bus_remove_match(pa_dbus_connection_get(u->conn), tmp, &error);
- pa_xfree(tmp);
- dbus_error_free(&error);
- }
+ if (u->source)
+ pa_source_unlink(u->source);
- dbus_connection_remove_filter(pa_dbus_connection_get(u->conn), filter_cb, u);
+ stop_thread(u);
- pa_dbus_connection_unref(u->conn);
- }
+ if (u->connection) {
+/* DBusError error; */
+/* char *t; */
- if (u->path)
- pa_xfree(u->path);
+/* if (u->transport == BT_CAPABILITIES_TRANSPORT_SCO || */
+/* u->transport == BT_CAPABILITIES_TRANSPORT_ANY) { */
- if (u->sink)
- pa_sink_unlink(u->sink);
+/* t = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged',path='%s'", u->path); */
+/* dbus_error_init(&error); */
+/* dbus_bus_remove_match(pa_dbus_connection_get(u->conn), t, &error); */
+/* dbus_error_free(&error); */
+/* pa_xfree(t); */
+/* } */
- if (u->thread) {
- pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
- pa_thread_free(u->thread);
+/* if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP || */
+/* u->transport == BT_CAPABILITIES_TRANSPORT_ANY) { */
+
+/* t = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged',path='%s'", u->path); */
+/* dbus_error_init(&error); */
+/* dbus_bus_remove_match(pa_dbus_connection_get(u->conn), t, &error); */
+/* dbus_error_free(&error); */
+/* pa_xfree(t); */
+/* } */
+
+/* dbus_connection_remove_filter(pa_dbus_connection_get(u->conn), filter_cb, u); */
+ pa_dbus_connection_unref(u->connection);
}
- if (u->sink)
- pa_sink_unref(u->sink);
+ if (u->card)
+ pa_card_free(u->card);
pa_thread_mq_done(&u->thread_mq);
- if (u->rtpoll_item)
- pa_rtpoll_item_free(u->rtpoll_item);
-
if (u->rtpoll)
pa_rtpoll_free(u->rtpoll);
- if (u->smoother)
- pa_smoother_free(u->smoother);
-
- pa_xfree(u->name);
- pa_xfree(u->addr);
- pa_xfree(u->profile);
- pa_xfree(u->strtransport);
+ if (u->read_smoother)
+ pa_smoother_free(u->read_smoother);
if (u->stream_fd >= 0)
pa_close(u->stream_fd);
- if (u->audioservice_fd >= 0)
- pa_close(u->audioservice_fd);
+ if (u->service_fd >= 0)
+ pa_close(u->service_fd);
+
+ if (u->device)
+ pa_bluetooth_device_free(u->device);
+
+ if (u->write_memchunk.memblock)
+ pa_memblock_unref(u->write_memchunk.memblock);
+
+ if (u->a2dp.buffer)
+ pa_xfree(u->a2dp.buffer);
+
+ sbc_finish(&u->a2dp.sbc);
+
+ if (u->modargs)
+ pa_modargs_free(u->modargs);
pa_xfree(u);
}
diff --git a/src/modules/bluetooth/module-bluetooth-discover.c b/src/modules/bluetooth/module-bluetooth-discover.c
index 1bc05c02..f9540b32 100644
--- a/src/modules/bluetooth/module-bluetooth-discover.c
+++ b/src/modules/bluetooth/module-bluetooth-discover.c
@@ -1,22 +1,22 @@
/***
- This file is part of PulseAudio.
+ This file is part of PulseAudio.
- Copyright 2008 Joao Paulo Rechi Vita
+ Copyright 2008 Joao Paulo Rechi Vita
- PulseAudio is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published
- by the Free Software Foundation; either version 2 of the License,
- or (at your option) any later version.
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
- PulseAudio is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
- You should have received a copy of the GNU Lesser General Public License
- along with PulseAudio; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- USA.
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
***/
#ifdef HAVE_CONFIG_H
@@ -29,817 +29,138 @@
#include <pulse/xmalloc.h>
#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
#include <pulsecore/modargs.h>
#include <pulsecore/macro.h>
#include <pulsecore/llist.h>
#include <pulsecore/core-util.h>
+#include <modules/dbus-util.h>
-#include "../dbus-util.h"
#include "module-bluetooth-discover-symdef.h"
+#include "bluetooth-util.h"
PA_MODULE_AUTHOR("Joao Paulo Rechi Vita");
PA_MODULE_DESCRIPTION("Detect available bluetooth audio devices and load bluetooth audio drivers");
PA_MODULE_VERSION(PACKAGE_VERSION);
-PA_MODULE_USAGE("");
-
-struct module {
- char *profile;
- uint32_t index;
- PA_LLIST_FIELDS(struct module);
-};
-
-struct uuid {
- char *uuid;
- PA_LLIST_FIELDS(struct uuid);
-};
+PA_MODULE_USAGE("async=<Asynchronous initialization?>");
-struct dbus_pending {
- char *path;
- char *profile;
- DBusPendingCall *pending;
- PA_LLIST_FIELDS(struct dbus_pending);
-};
-
-struct device {
- char *name;
- char *object_path;
- int paired;
- char *alias;
- int connected;
- PA_LLIST_HEAD(struct uuid, uuid_list);
- char *address;
- int class;
- int trusted;
- PA_LLIST_HEAD(struct module, module_list);
- PA_LLIST_FIELDS(struct device);
+static const char* const valid_modargs[] = {
+ "async",
+ NULL
};
struct userdata {
pa_module *module;
- pa_dbus_connection *conn;
- dbus_int32_t dbus_data_slot;
- PA_LLIST_HEAD(struct device, device_list);
- PA_LLIST_HEAD(struct dbus_pending, dbus_pending_list);
+ pa_core *core;
+ pa_dbus_connection *connection;
+ pa_bluetooth_discovery *discovery;
};
-static struct module *module_new(const char *profile, pa_module *pa_m) {
- struct module *m;
-
- m = pa_xnew(struct module, 1);
- m->profile = pa_xstrdup(profile);
- m->index = pa_m->index;
- PA_LLIST_INIT(struct module, m);
-
- return m;
-}
-
-static void module_free(struct module *m) {
- pa_assert(m);
-
- pa_xfree(m->profile);
- pa_xfree(m);
-}
-
-static struct module* module_find(struct device *d, const char *profile) {
- struct module *m;
-
- for (m = d->module_list; m; m = m->next)
- if (pa_streq(m->profile, profile))
- return m;
-
- return NULL;
-}
-
-static struct uuid *uuid_new(const char *uuid) {
- struct uuid *node;
-
- node = pa_xnew(struct uuid, 1);
- node->uuid = pa_xstrdup(uuid);
- PA_LLIST_INIT(struct uuid, node);
-
- return node;
-}
-
-static void uuid_free(struct uuid *uuid) {
- pa_assert(uuid);
-
- pa_xfree(uuid->uuid);
- pa_xfree(uuid);
-}
-
-static struct dbus_pending *dbus_pending_new(struct userdata *u, DBusPendingCall *pending, const char *path, const char *profile) {
- struct dbus_pending *node;
-
- pa_assert(pending);
-
- node = pa_xnew(struct dbus_pending, 1);
- node->pending = pending;
- node->path = pa_xstrdup(path);
- node->profile = pa_xstrdup(profile);
- PA_LLIST_INIT(struct dbus_pending, node);
- dbus_pending_call_set_data(pending, u->dbus_data_slot, node, NULL);
-
- return node;
-}
-
-static void dbus_pending_free(struct dbus_pending *pending) {
- pa_assert(pending);
-
- pa_xfree(pending->path);
- pa_xfree(pending->profile);
- dbus_pending_call_cancel(pending->pending);
- dbus_pending_call_unref(pending->pending);
- pa_xfree(pending);
-}
-
-static struct device *device_new(const char *object_path) {
- struct device *node;
-
- node = pa_xnew(struct device, 1);
- node->name = NULL;
- node->object_path = pa_xstrdup(object_path);
- node->paired = -1;
- node->alias = NULL;
- node->connected = -1;
- PA_LLIST_HEAD_INIT(struct uuid, node->uuid_list);
- node->address = NULL;
- node->class = -1;
- node->trusted = -1;
- PA_LLIST_HEAD_INIT(struct module, node->module_list);
- PA_LLIST_INIT(struct device, node);
-
- return node;
-}
-
-static void device_free(struct device *device) {
- struct module *m;
- struct uuid *i;
-
- pa_assert(device);
-
- while ((m = device->module_list)) {
- PA_LLIST_REMOVE(struct module, device->module_list, m);
- module_free(m);
- }
-
- while ((i = device->uuid_list)) {
- PA_LLIST_REMOVE(struct uuid, device->uuid_list, i);
- uuid_free(i);
- }
-
- pa_xfree(device->name);
- pa_xfree(device->object_path);
- pa_xfree(device->alias);
- pa_xfree(device->address);
- pa_xfree(device);
-}
-
-static struct device* device_find(struct userdata *u, const char *path) {
- struct device *i;
-
- for (i = u->device_list; i; i = i->next)
- if (pa_streq(i->object_path, path))
- return i;
-
- return NULL;
-}
-
-static int parse_device_property(struct userdata *u, struct device *d, DBusMessageIter *i) {
- const char *key;
- DBusMessageIter variant_i;
-
+static void load_module_for_device(struct userdata *u, pa_bluetooth_device *d, pa_bool_t good) {
pa_assert(u);
pa_assert(d);
- pa_assert(i);
- if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) {
- pa_log("Property name not a string.");
- return -1;
- }
-
- dbus_message_iter_get_basic(i, &key);
-
- if (!dbus_message_iter_next(i)) {
- pa_log("Property value missing");
- return -1;
- }
-
- if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_VARIANT) {
- pa_log("Property value not a variant.");
- return -1;
- }
-
- dbus_message_iter_recurse(i, &variant_i);
-
- pa_log_debug("Parsing device property %s", key);
-
- switch (dbus_message_iter_get_arg_type(&variant_i)) {
-
- case DBUS_TYPE_STRING: {
-
- const char *value;
- dbus_message_iter_get_basic(&variant_i, &value);
-
- if (pa_streq(key, "Name")) {
- pa_xfree(d->name);
- d->name = pa_xstrdup(value);
- } else if (pa_streq(key, "Alias")) {
- pa_xfree(d->alias);
- d->alias = pa_xstrdup(value);
- } else if (pa_streq(key, "Address")) {
- pa_xfree(d->address);
- d->address = pa_xstrdup(value);
- }
-
- break;
- }
+ if (good &&
+ d->device_connected > 0 &&
+ (d->audio_sink_connected > 0 || d->headset_connected > 0)) {
- case DBUS_TYPE_BOOLEAN: {
+ if (((uint32_t) PA_PTR_TO_UINT(d->data))-1 == PA_INVALID_INDEX) {
+ pa_module *m = NULL;
+ char *args;
- dbus_bool_t value;
- dbus_message_iter_get_basic(&variant_i, &value);
+ /* Oh, awesome, a new device has shown up and been connected! */
- if (pa_streq(key, "Paired"))
- d->paired = !!value;
- else if (pa_streq(key, "Connected"))
- d->connected = !!value;
- else if (pa_streq(key, "Trusted"))
- d->trusted = !!value;
+ args = pa_sprintf_malloc("address=\"%s\" path=\"%s\"", d->address, d->path);
+ pa_log_debug("Loading module-bluetooth-device %s", args);
+ m = pa_module_load(u->module->core, "module-bluetooth-device", args);
+ pa_xfree(args);
- break;
+ if (m)
+ d->data = PA_UINT_TO_PTR((uint32_t) (m->index+1));
+ else
+ pa_log_debug("Failed to load module for device %s", d->path);
}
- case DBUS_TYPE_UINT32: {
-
- uint32_t value;
- dbus_message_iter_get_basic(&variant_i, &value);
-
- if (pa_streq(key, "Class"))
- d->class = (int) value;
-
- break;
- }
-
- case DBUS_TYPE_ARRAY: {
-
- DBusMessageIter ai;
- dbus_message_iter_recurse(&variant_i, &ai);
-
- if (dbus_message_iter_get_arg_type(&ai) == DBUS_TYPE_STRING &&
- pa_streq(key, "UUIDs")) {
-
- while (dbus_message_iter_get_arg_type(&ai) != DBUS_TYPE_INVALID) {
- struct uuid *node;
- const char *value;
+ } else {
- dbus_message_iter_get_basic(&ai, &value);
- node = uuid_new(value);
- PA_LLIST_PREPEND(struct uuid, d->uuid_list, node);
+ if (((uint32_t) PA_PTR_TO_UINT(d->data))-1 != PA_INVALID_INDEX) {
- if (!dbus_message_iter_next(&ai))
- break;
- }
- }
+ /* Hmm, disconnection? Then let's unload our module */
- break;
+ pa_log_debug("Unloading module for %s", d->path);
+ pa_module_unload_request_by_index(u->core, (uint32_t) (PA_PTR_TO_UINT(d->data))-1, TRUE);
+ d->data = NULL;
}
}
-
- return 0;
}
-static int get_device_properties(struct userdata *u, struct device *d) {
- DBusError e;
- DBusMessage *m = NULL, *r = NULL;
- DBusMessageIter arg_i, element_i;
- int ret = -1;
-
- pa_assert(u);
- pa_assert(d);
-
- dbus_error_init(&e);
-
- pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->object_path, "org.bluez.Device", "GetProperties"));
-
- r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->conn), m, -1, &e);
-
- if (!r) {
- pa_log("org.bluez.Device.GetProperties failed: %s", e.message);
- goto finish;
- }
-
- if (!dbus_message_iter_init(r, &arg_i)) {
- pa_log("org.bluez.Device.GetProperties reply has no arguments");
- goto finish;
- }
-
- if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) {
- pa_log("org.bluez.Device.GetProperties argument is not an array");
- goto finish;
- }
-
- dbus_message_iter_recurse(&arg_i, &element_i);
- while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
-
- if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
- DBusMessageIter dict_i;
-
- dbus_message_iter_recurse(&element_i, &dict_i);
-
- if (parse_device_property(u, d, &dict_i) < 0)
- goto finish;
- }
-
- if (!dbus_message_iter_next(&element_i))
- break;
- }
-
- ret = 0;
-
-finish:
- if (m)
- dbus_message_unref(m);
- if (r)
- dbus_message_unref(r);
-
- dbus_error_free(&e);
-
- return ret;
-}
-
-static void load_module_for_device(struct userdata *u, struct device *d, const char *profile) {
- char *args;
- pa_module *pa_m;
- struct module *m;
-
- pa_assert(u);
- pa_assert(d);
-
- get_device_properties(u, d);
- args = pa_sprintf_malloc("sink_name=\"%s\" address=\"%s\" profile=\"%s\" path=\"%s\"", d->name, d->address, profile, d->object_path);
- pa_m = pa_module_load(u->module->core, "module-bluetooth-device", args);
- pa_xfree(args);
-
- if (!pa_m) {
- pa_log_debug("Failed to load module for device %s", d->object_path);
- return;
- }
-
- m = module_new(profile, pa_m);
- PA_LLIST_PREPEND(struct module, d->module_list, m);
-}
-
-static void unload_module_for_device(struct userdata *u, struct device *d, const char *profile) {
- struct module *m;
-
- pa_assert(u);
- pa_assert(d);
-
- if (!(m = module_find(d, profile)))
- return;
-
- pa_module_unload_request_by_index(u->module->core, m->index, TRUE);
-
- PA_LLIST_REMOVE(struct module, d->module_list, m);
- module_free(m);
-}
-
-static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *msg, void *userdata) {
- DBusMessageIter arg_i;
+static int setup_dbus(struct userdata *u) {
DBusError err;
- const char *value;
- struct userdata *u;
-
- pa_assert(bus);
- pa_assert(msg);
- pa_assert(userdata);
- u = userdata;
dbus_error_init(&err);
- pa_log_debug("dbus: interface=%s, path=%s, member=%s\n",
- dbus_message_get_interface(msg),
- dbus_message_get_path(msg),
- dbus_message_get_member(msg));
-
- if (dbus_message_is_signal(msg, "org.bluez.Adapter", "DeviceRemoved")) {
-
- if (!dbus_message_iter_init(msg, &arg_i))
- pa_log("dbus: message has no parameters");
- else if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_OBJECT_PATH)
- pa_log("dbus: argument is not object path");
- else {
- struct device *d;
-
- dbus_message_iter_get_basic(&arg_i, &value);
- pa_log_debug("hcid: device %s removed", value);
-
- if ((d = device_find(u, value))) {
- PA_LLIST_REMOVE(struct device, u->device_list, d);
- device_free(d);
- }
- }
-
- } else if (dbus_message_is_signal(msg, "org.bluez.Headset", "PropertyChanged") ||
- dbus_message_is_signal(msg, "org.bluez.AudioSink", "PropertyChanged")) {
-
- struct device *d;
- const char *profile;
- DBusMessageIter variant_i;
- dbus_bool_t connected;
-
- if (!dbus_message_iter_init(msg, &arg_i)) {
- pa_log("dbus: message has no parameters");
- goto done;
- }
-
- if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_STRING) {
- pa_log("Property name not a string.");
- goto done;
- }
-
- dbus_message_iter_get_basic(&arg_i, &value);
-
- if (!pa_streq(value, "Connected"))
- goto done;
-
- if (!dbus_message_iter_next(&arg_i)) {
- pa_log("Property value missing");
- goto done;
- }
-
- if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_VARIANT) {
- pa_log("Property value not a variant.");
- goto done;
- }
-
- dbus_message_iter_recurse(&arg_i, &variant_i);
-
- if (dbus_message_iter_get_arg_type(&variant_i) != DBUS_TYPE_BOOLEAN) {
- pa_log("Property value not a boolean.");
- goto done;
- }
-
- dbus_message_iter_get_basic(&variant_i, &connected);
-
- if (dbus_message_is_signal(msg, "org.bluez.Headset", "PropertyChanged"))
- profile = "hsp";
- else
- profile = "a2dp";
-
- d = device_find(u, dbus_message_get_path(msg));
-
- if (connected) {
- if (!d) {
- d = device_new(dbus_message_get_path(msg));
- PA_LLIST_PREPEND(struct device, u->device_list, d);
- }
-
- load_module_for_device(u, d, profile);
- } else if (d)
- unload_module_for_device(u, d, profile);
- }
-
-done:
- dbus_error_free(&err);
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-}
-
-
-
-static void get_properties_reply(DBusPendingCall *pending, void *user_data) {
- struct userdata *u;
- DBusMessage *r;
- dbus_bool_t connected;
- DBusMessageIter arg_i, element_i;
- DBusMessageIter variant_i;
- struct device *d;
- struct dbus_pending *p;
-
- pa_assert(u = user_data);
-
- r = dbus_pending_call_steal_reply(pending);
- if (!r)
- goto end;
-
- if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
- pa_log("Error from GetProperties reply: %s", dbus_message_get_error_name(r));
- goto end;
- }
-
- if (!dbus_message_iter_init(r, &arg_i)) {
- pa_log("%s GetProperties reply has no arguments", p->profile);
- goto end;
- }
-
- if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) {
- pa_log("%s GetProperties argument is not an array", p->profile);
- goto end;
- }
-
- connected = FALSE;
- dbus_message_iter_recurse(&arg_i, &element_i);
- while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
-
- if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
- DBusMessageIter dict_i;
- const char *key;
-
- dbus_message_iter_recurse(&element_i, &dict_i);
-
- if (dbus_message_iter_get_arg_type(&dict_i) != DBUS_TYPE_STRING) {
- pa_log("Property name not a string.");
- goto end;
- }
+ u->connection = pa_dbus_bus_get(u->core, DBUS_BUS_SYSTEM, &err);
- dbus_message_iter_get_basic(&dict_i, &key);
-
- if (!dbus_message_iter_next(&dict_i)) {
- pa_log("Property value missing");
- goto end;
- }
-
- if (dbus_message_iter_get_arg_type(&dict_i) != DBUS_TYPE_VARIANT) {
- pa_log("Property value not a variant.");
- goto end;
- }
-
- dbus_message_iter_recurse(&dict_i, &variant_i);
-
- switch (dbus_message_iter_get_arg_type(&variant_i)) {
-
- case DBUS_TYPE_BOOLEAN: {
-
- dbus_bool_t value;
- dbus_message_iter_get_basic(&variant_i, &value);
-
- if (pa_streq(key, "Connected")) {
- connected = value;
- goto endloop;
- }
-
- break;
- }
- }
- }
-
- if (!dbus_message_iter_next(&element_i))
- break;
- }
-
-endloop:
- if (connected) {
- p = dbus_pending_call_get_data(pending, u->dbus_data_slot);
- pa_log_debug("%s: %s connected", p->path, p->profile);
- d = device_find(u, p->path);
-
- if (!d) {
- d = device_new(p->path);
- PA_LLIST_PREPEND(struct device, u->device_list, d);
- }
-
- load_module_for_device(u, d, p->profile);
- }
-
- dbus_message_unref(r);
-
-end:
- p = dbus_pending_call_get_data(pending, u->dbus_data_slot);
- PA_LLIST_REMOVE(struct dbus_pending, u->dbus_pending_list, p);
- dbus_pending_free(p);
-}
-
-static void list_devices_reply(DBusPendingCall *pending, void *user_data) {
- DBusMessage *r, *m;
- DBusPendingCall *call;
- DBusError e;
- char **paths = NULL;
- int i, num = -1;
- struct dbus_pending *p;
- struct userdata *u;
-
- pa_assert(u = user_data);
- dbus_error_init(&e);
-
- r = dbus_pending_call_steal_reply(pending);
- if (!r) {
- pa_log("Failed to get ListDevices reply");
- goto end;
- }
-
- if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
- pa_log("Error from ListDevices reply: %s", dbus_message_get_error_name(r));
- goto end;
- }
-
- if (!dbus_message_get_args(r, &e, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &paths, &num, DBUS_TYPE_INVALID)) {
- pa_log("org.bluez.Adapter.ListDevices returned an error: '%s'\n", e.message);
- dbus_error_free(&e);
- } else {
- for (i = 0; i < num; ++i) {
- pa_assert_se(m = dbus_message_new_method_call("org.bluez", paths[i], "org.bluez.Headset", "GetProperties"));
- if (dbus_connection_send_with_reply(pa_dbus_connection_get(u->conn), m, &call, -1)) {
- p = dbus_pending_new(u, call, paths[i], "hsp");
- PA_LLIST_PREPEND(struct dbus_pending, u->dbus_pending_list, p);
- dbus_pending_call_set_notify(call, get_properties_reply, u, NULL);
- } else {
- pa_log("Failed to send GetProperties");
- }
-
- dbus_message_unref(m);
-
- pa_assert_se(m = dbus_message_new_method_call("org.bluez", paths[i], "org.bluez.AudioSink", "GetProperties"));
- if (dbus_connection_send_with_reply(pa_dbus_connection_get(u->conn), m, &call, -1)) {
- p = dbus_pending_new(u, call, paths[i], "a2dp");
- PA_LLIST_PREPEND(struct dbus_pending, u->dbus_pending_list, p);
- dbus_pending_call_set_notify(call, get_properties_reply, u, NULL);
- } else {
- pa_log("Failed to send GetProperties");
- }
-
- dbus_message_unref(m);
- }
+ if (dbus_error_is_set(&err) || !u->connection) {
+ pa_log("Failed to get D-Bus connection: %s", err.message);
+ dbus_error_free(&err);
+ return -1;
}
- if (paths)
- dbus_free_string_array (paths);
- dbus_message_unref(r);
-
-end:
- p = dbus_pending_call_get_data(pending, u->dbus_data_slot);
- PA_LLIST_REMOVE(struct dbus_pending, u->dbus_pending_list, p);
- dbus_pending_free(p);
+ return 0;
}
-static void list_adapters_reply(DBusPendingCall *pending, void *user_data) {
- DBusMessage *r, *m;
- DBusPendingCall *call;
- DBusError e;
- char **paths = NULL;
- int i, num = -1;
- struct dbus_pending *p;
+int pa__init(pa_module* m) {
struct userdata *u;
+ pa_modargs *ma;
+ pa_bool_t async = FALSE;
- pa_assert(u = user_data);
- dbus_error_init(&e);
+ pa_assert(m);
- r = dbus_pending_call_steal_reply(pending);
- if (!r) {
- pa_log("Failed to get ListAdapters reply");
- goto end;
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log("Failed to parse module arguments");
+ goto fail;
}
- if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
- pa_log("Error from ListAdapters reply: %s", dbus_message_get_error_name(r));
- goto end;
+ if (pa_modargs_get_value_boolean(ma, "async", &async) < 0) {
+ pa_log("Failed to parse tsched argument.");
+ goto fail;
}
- if (!dbus_message_get_args(r, &e, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &paths, &num, DBUS_TYPE_INVALID)) {
- pa_log("org.bluez.Manager.ListAdapters returned an error: '%s'\n", e.message);
- dbus_error_free(&e);
- } else {
- for (i = 0; i < num; ++i) {
- pa_assert_se(m = dbus_message_new_method_call("org.bluez", paths[i], "org.bluez.Adapter", "ListDevices"));
- if (dbus_connection_send_with_reply(pa_dbus_connection_get(u->conn), m, &call, -1)) {
- p = dbus_pending_new(u, call, NULL, NULL);
- PA_LLIST_PREPEND(struct dbus_pending, u->dbus_pending_list, p);
- dbus_pending_call_set_notify(call, list_devices_reply, u, NULL);
- } else {
- pa_log("Failed to send ListDevices");
- }
-
- dbus_message_unref(m);
- }
- }
+ m->userdata = u = pa_xnew0(struct userdata, 1);
+ u->module = m;
+ u->core = m->core;
- if (paths)
- dbus_free_string_array (paths);
- dbus_message_unref(r);
+ if (setup_dbus(u) < 0)
+ goto fail;
-end:
- p = dbus_pending_call_get_data(pending, u->dbus_data_slot);
- PA_LLIST_REMOVE(struct dbus_pending, u->dbus_pending_list, p);
- dbus_pending_free(p);
-}
+ if (!(u->discovery = pa_bluetooth_discovery_new(pa_dbus_connection_get(u->connection), load_module_for_device, u)))
+ goto fail;
-static void lookup_devices(struct userdata *u) {
- DBusMessage *m;
- DBusPendingCall *call;
- struct dbus_pending *p;
+ if (!async)
+ pa_bluetooth_discovery_sync(u->discovery);
- pa_assert(u);
+ return 0;
- pa_assert_se(m = dbus_message_new_method_call("org.bluez", "/", "org.bluez.Manager", "ListAdapters"));
- if (dbus_connection_send_with_reply(pa_dbus_connection_get(u->conn), m, &call, -1)) {
- p = dbus_pending_new(u, call, NULL, NULL);
- PA_LLIST_PREPEND(struct dbus_pending, u->dbus_pending_list, p);
- dbus_pending_call_set_notify(call, list_adapters_reply, u, NULL);
- } else {
- pa_log("Failed to send ListAdapters");
- }
+fail:
+ pa__done(m);
- dbus_message_unref(m);
+ return -1;
}
void pa__done(pa_module* m) {
struct userdata *u;
- struct device *i;
- struct dbus_pending *p;
pa_assert(m);
if (!(u = m->userdata))
return;
- while ((p = u->dbus_pending_list)) {
- PA_LLIST_REMOVE(struct dbus_pending, u->dbus_pending_list, p);
- dbus_pending_free(p);
- }
-
- while ((i = u->device_list)) {
- PA_LLIST_REMOVE(struct device, u->device_list, i);
- device_free(i);
- }
-
- if (u->dbus_data_slot != -1) {
- dbus_pending_call_free_data_slot(&u->dbus_data_slot);
- }
-
- if (u->conn) {
- DBusError error;
- dbus_error_init(&error);
-
- dbus_bus_remove_match(pa_dbus_connection_get(u->conn), "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'", &error);
- dbus_error_free(&error);
+ if (u->discovery)
+ pa_bluetooth_discovery_free(u->discovery);
- dbus_bus_remove_match(pa_dbus_connection_get(u->conn), "type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'", &error);
- dbus_error_free(&error);
-
- dbus_bus_remove_match(pa_dbus_connection_get(u->conn), "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'", &error);
- dbus_error_free(&error);
-
- dbus_connection_remove_filter(pa_dbus_connection_get(u->conn), filter_cb, u);
-
- pa_dbus_connection_unref(u->conn);
- }
+ if (u->connection)
+ pa_dbus_connection_unref(u->connection);
pa_xfree(u);
}
-
-int pa__init(pa_module* m) {
- DBusError err;
- struct userdata *u;
-
- pa_assert(m);
- dbus_error_init(&err);
-
- m->userdata = u = pa_xnew(struct userdata, 1);
- u->dbus_data_slot = -1;
- u->module = m;
- PA_LLIST_HEAD_INIT(struct device, u->device_list);
- PA_LLIST_HEAD_INIT(DBusPendingCall, u->dbus_pending_list);
-
- /* connect to the bus */
- u->conn = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &err);
- if (dbus_error_is_set(&err) || (u->conn == NULL) ) {
- pa_log("Failed to get D-Bus connection: %s", err.message);
- goto fail;
- }
-
- if (!dbus_pending_call_allocate_data_slot(&u->dbus_data_slot))
- goto fail;
-
- /* dynamic detection of bluetooth audio devices */
- if (!dbus_connection_add_filter(pa_dbus_connection_get(u->conn), filter_cb, u, NULL)) {
- pa_log_error("Failed to add filter function");
- goto fail;
- }
-
- dbus_bus_add_match(pa_dbus_connection_get(u->conn), "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'", &err);
- if (dbus_error_is_set(&err)) {
- pa_log_error("Unable to subscribe to org.bluez.Adapter signals: %s: %s", err.name, err.message);
- goto fail;
- }
-
- dbus_bus_add_match(pa_dbus_connection_get(u->conn), "type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'", &err);
- if (dbus_error_is_set(&err)) {
- pa_log_error("Unable to subscribe to org.bluez.Headset signals: %s: %s", err.name, err.message);
- goto fail;
- }
-
- dbus_bus_add_match(pa_dbus_connection_get(u->conn), "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'", &err);
- if (dbus_error_is_set(&err)) {
- pa_log_error("Unable to subscribe to org.bluez.AudioSink signals: %s: %s", err.name, err.message);
- goto fail;
- }
-
- lookup_devices(u);
-
- return 0;
-
-fail:
- dbus_error_free(&err);
- pa__done(m);
-
- return -1;
-}
diff --git a/src/modules/bluetooth/rtp.h b/src/modules/bluetooth/rtp.h
index 690bd43a..14573628 100644
--- a/src/modules/bluetooth/rtp.h
+++ b/src/modules/bluetooth/rtp.h
@@ -2,7 +2,7 @@
*
* BlueZ - Bluetooth protocol stack for Linux
*
- * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
*
*
* This library is free software; you can redistribute it and/or
@@ -24,13 +24,13 @@
#if __BYTE_ORDER == __LITTLE_ENDIAN
struct rtp_header {
- uint8_t cc:4;
- uint8_t x:1;
- uint8_t p:1;
- uint8_t v:2;
+ unsigned cc:4;
+ unsigned x:1;
+ unsigned p:1;
+ unsigned v:2;
- uint8_t pt:7;
- uint8_t m:1;
+ unsigned pt:7;
+ unsigned m:1;
uint16_t sequence_number;
uint32_t timestamp;
@@ -39,23 +39,23 @@ struct rtp_header {
} __attribute__ ((packed));
struct rtp_payload {
- uint8_t frame_count:4;
- uint8_t rfa0:1;
- uint8_t is_last_fragment:1;
- uint8_t is_first_fragment:1;
- uint8_t is_fragmented:1;
+ unsigned frame_count:4;
+ unsigned rfa0:1;
+ unsigned is_last_fragment:1;
+ unsigned is_first_fragment:1;
+ unsigned is_fragmented:1;
} __attribute__ ((packed));
#elif __BYTE_ORDER == __BIG_ENDIAN
struct rtp_header {
- uint8_t v:2;
- uint8_t p:1;
- uint8_t x:1;
- uint8_t cc:4;
+ unsigned v:2;
+ unsigned p:1;
+ unsigned x:1;
+ unsigned cc:4;
- uint8_t m:1;
- uint8_t pt:7;
+ unsigned m:1;
+ unsigned pt:7;
uint16_t sequence_number;
uint32_t timestamp;
@@ -64,11 +64,11 @@ struct rtp_header {
} __attribute__ ((packed));
struct rtp_payload {
- uint8_t is_fragmented:1;
- uint8_t is_first_fragment:1;
- uint8_t is_last_fragment:1;
- uint8_t rfa0:1;
- uint8_t frame_count:4;
+ unsigned is_fragmented:1;
+ unsigned is_first_fragment:1;
+ unsigned is_last_fragment:1;
+ unsigned rfa0:1;
+ unsigned frame_count:4;
} __attribute__ ((packed));
#else
diff --git a/src/modules/bluetooth/sbc.c b/src/modules/bluetooth/sbc.c
index 651981fa..29258d05 100644
--- a/src/modules/bluetooth/sbc.c
+++ b/src/modules/bluetooth/sbc.c
@@ -46,6 +46,7 @@
#include "sbc_tables.h"
#include "sbc.h"
+#include "sbc_primitives.h"
#define SBC_SYNCWORD 0x9C
@@ -76,13 +77,16 @@ struct sbc_frame {
uint8_t joint;
/* only the lower 4 bits of every element are to be used */
- uint8_t scale_factor[2][8];
+ uint32_t scale_factor[2][8];
/* raw integer subband samples in the frame */
+ int32_t SBC_ALIGNED sb_sample_f[16][2][8];
- int32_t sb_sample_f[16][2][8];
- int32_t sb_sample[16][2][8]; /* modified subband samples */
- int16_t pcm_sample[2][16*8]; /* original pcm audio samples */
+ /* modified subband samples */
+ int32_t SBC_ALIGNED sb_sample[16][2][8];
+
+ /* original pcm audio samples */
+ int16_t SBC_ALIGNED pcm_sample[2][16*8];
};
struct sbc_decoder_state {
@@ -91,16 +95,6 @@ struct sbc_decoder_state {
int offset[2][16];
};
-struct sbc_encoder_state {
- int subbands;
- int position[2];
- int16_t X[2][256];
- void (*sbc_analyze_4b_4s)(int16_t *pcm, int16_t *x,
- int32_t *out, int out_stride);
- void (*sbc_analyze_4b_8s)(int16_t *pcm, int16_t *x,
- int32_t *out, int out_stride);
-};
-
/*
* Calculates the CRC-8 of the first len bits in data
*/
@@ -368,7 +362,7 @@ static void sbc_calculate_bits(const struct sbc_frame *frame, int (*bits)[8])
static int sbc_unpack_frame(const uint8_t *data, struct sbc_frame *frame,
size_t len)
{
- int consumed;
+ unsigned int consumed;
/* Will copy the parts of the header that are relevant to crc
* calculation here */
uint8_t crc_header[11] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
@@ -653,180 +647,41 @@ static int sbc_synthesize_audio(struct sbc_decoder_state *state,
}
}
-static inline void _sbc_analyze_four(const int16_t *in, int32_t *out)
-{
- FIXED_A t1[4];
- FIXED_T t2[4];
- int i = 0, hop = 0;
-
- /* rounding coefficient */
- t1[0] = t1[1] = t1[2] = t1[3] =
- (FIXED_A) 1 << (SBC_PROTO_FIXED4_SCALE - 1);
-
- /* low pass polyphase filter */
- for (hop = 0; hop < 40; hop += 8) {
- t1[0] += (FIXED_A) in[hop] * _sbc_proto_fixed4[hop];
- t1[1] += (FIXED_A) in[hop + 1] * _sbc_proto_fixed4[hop + 1];
- t1[2] += (FIXED_A) in[hop + 2] * _sbc_proto_fixed4[hop + 2];
- t1[1] += (FIXED_A) in[hop + 3] * _sbc_proto_fixed4[hop + 3];
- t1[0] += (FIXED_A) in[hop + 4] * _sbc_proto_fixed4[hop + 4];
- t1[3] += (FIXED_A) in[hop + 5] * _sbc_proto_fixed4[hop + 5];
- t1[3] += (FIXED_A) in[hop + 7] * _sbc_proto_fixed4[hop + 7];
- }
-
- /* scaling */
- t2[0] = t1[0] >> SBC_PROTO_FIXED4_SCALE;
- t2[1] = t1[1] >> SBC_PROTO_FIXED4_SCALE;
- t2[2] = t1[2] >> SBC_PROTO_FIXED4_SCALE;
- t2[3] = t1[3] >> SBC_PROTO_FIXED4_SCALE;
-
- /* do the cos transform */
- for (i = 0, hop = 0; i < 4; hop += 8, i++) {
- out[i] = ((FIXED_A) t2[0] * cos_table_fixed_4[0 + hop] +
- (FIXED_A) t2[1] * cos_table_fixed_4[1 + hop] +
- (FIXED_A) t2[2] * cos_table_fixed_4[2 + hop] +
- (FIXED_A) t2[3] * cos_table_fixed_4[5 + hop]) >>
- (SBC_COS_TABLE_FIXED4_SCALE - SCALE_OUT_BITS);
- }
-}
-
-static void sbc_analyze_4b_4s(int16_t *pcm, int16_t *x,
- int32_t *out, int out_stride)
-{
- int i;
-
- /* Input 4 x 4 Audio Samples */
- for (i = 0; i < 16; i += 4) {
- x[64 + i] = x[0 + i] = pcm[15 - i];
- x[65 + i] = x[1 + i] = pcm[14 - i];
- x[66 + i] = x[2 + i] = pcm[13 - i];
- x[67 + i] = x[3 + i] = pcm[12 - i];
- }
-
- /* Analyze four blocks */
- _sbc_analyze_four(x + 12, out);
- out += out_stride;
- _sbc_analyze_four(x + 8, out);
- out += out_stride;
- _sbc_analyze_four(x + 4, out);
- out += out_stride;
- _sbc_analyze_four(x, out);
-}
-
-static inline void _sbc_analyze_eight(const int16_t *in, int32_t *out)
-{
- FIXED_A t1[8];
- FIXED_T t2[8];
- int i, hop;
-
- /* rounding coefficient */
- t1[0] = t1[1] = t1[2] = t1[3] = t1[4] = t1[5] = t1[6] = t1[7] =
- (FIXED_A) 1 << (SBC_PROTO_FIXED8_SCALE-1);
-
- /* low pass polyphase filter */
- for (hop = 0; hop < 80; hop += 16) {
- t1[0] += (FIXED_A) in[hop] * _sbc_proto_fixed8[hop];
- t1[1] += (FIXED_A) in[hop + 1] * _sbc_proto_fixed8[hop + 1];
- t1[2] += (FIXED_A) in[hop + 2] * _sbc_proto_fixed8[hop + 2];
- t1[3] += (FIXED_A) in[hop + 3] * _sbc_proto_fixed8[hop + 3];
- t1[4] += (FIXED_A) in[hop + 4] * _sbc_proto_fixed8[hop + 4];
- t1[3] += (FIXED_A) in[hop + 5] * _sbc_proto_fixed8[hop + 5];
- t1[2] += (FIXED_A) in[hop + 6] * _sbc_proto_fixed8[hop + 6];
- t1[1] += (FIXED_A) in[hop + 7] * _sbc_proto_fixed8[hop + 7];
- t1[0] += (FIXED_A) in[hop + 8] * _sbc_proto_fixed8[hop + 8];
- t1[5] += (FIXED_A) in[hop + 9] * _sbc_proto_fixed8[hop + 9];
- t1[6] += (FIXED_A) in[hop + 10] * _sbc_proto_fixed8[hop + 10];
- t1[7] += (FIXED_A) in[hop + 11] * _sbc_proto_fixed8[hop + 11];
- t1[7] += (FIXED_A) in[hop + 13] * _sbc_proto_fixed8[hop + 13];
- t1[6] += (FIXED_A) in[hop + 14] * _sbc_proto_fixed8[hop + 14];
- t1[5] += (FIXED_A) in[hop + 15] * _sbc_proto_fixed8[hop + 15];
- }
-
- /* scaling */
- t2[0] = t1[0] >> SBC_PROTO_FIXED8_SCALE;
- t2[1] = t1[1] >> SBC_PROTO_FIXED8_SCALE;
- t2[2] = t1[2] >> SBC_PROTO_FIXED8_SCALE;
- t2[3] = t1[3] >> SBC_PROTO_FIXED8_SCALE;
- t2[4] = t1[4] >> SBC_PROTO_FIXED8_SCALE;
- t2[5] = t1[5] >> SBC_PROTO_FIXED8_SCALE;
- t2[6] = t1[6] >> SBC_PROTO_FIXED8_SCALE;
- t2[7] = t1[7] >> SBC_PROTO_FIXED8_SCALE;
-
- /* do the cos transform */
- for (i = 0, hop = 0; i < 8; hop += 16, i++) {
- out[i] = ((FIXED_A) t2[0] * cos_table_fixed_8[0 + hop] +
- (FIXED_A) t2[1] * cos_table_fixed_8[1 + hop] +
- (FIXED_A) t2[2] * cos_table_fixed_8[2 + hop] +
- (FIXED_A) t2[3] * cos_table_fixed_8[3 + hop] +
- (FIXED_A) t2[4] * cos_table_fixed_8[4 + hop] +
- (FIXED_A) t2[5] * cos_table_fixed_8[9 + hop] +
- (FIXED_A) t2[6] * cos_table_fixed_8[10 + hop] +
- (FIXED_A) t2[7] * cos_table_fixed_8[11 + hop]) >>
- (SBC_COS_TABLE_FIXED8_SCALE - SCALE_OUT_BITS);
- }
-}
-
-static void sbc_analyze_4b_8s(int16_t *pcm, int16_t *x,
- int32_t *out, int out_stride)
-{
- int i;
-
- /* Input 4 x 8 Audio Samples */
- for (i = 0; i < 32; i += 8) {
- x[128 + i] = x[0 + i] = pcm[31 - i];
- x[129 + i] = x[1 + i] = pcm[30 - i];
- x[130 + i] = x[2 + i] = pcm[29 - i];
- x[131 + i] = x[3 + i] = pcm[28 - i];
- x[132 + i] = x[4 + i] = pcm[27 - i];
- x[133 + i] = x[5 + i] = pcm[26 - i];
- x[134 + i] = x[6 + i] = pcm[25 - i];
- x[135 + i] = x[7 + i] = pcm[24 - i];
- }
-
- /* Analyze four blocks */
- _sbc_analyze_eight(x + 24, out);
- out += out_stride;
- _sbc_analyze_eight(x + 16, out);
- out += out_stride;
- _sbc_analyze_eight(x + 8, out);
- out += out_stride;
- _sbc_analyze_eight(x, out);
-}
-
static int sbc_analyze_audio(struct sbc_encoder_state *state,
struct sbc_frame *frame)
{
int ch, blk;
+ int16_t *x;
switch (frame->subbands) {
case 4:
- for (ch = 0; ch < frame->channels; ch++)
+ for (ch = 0; ch < frame->channels; ch++) {
+ x = &state->X[ch][state->position - 16 +
+ frame->blocks * 4];
for (blk = 0; blk < frame->blocks; blk += 4) {
state->sbc_analyze_4b_4s(
- &frame->pcm_sample[ch][blk * 4],
- &state->X[ch][state->position[ch]],
+ x,
frame->sb_sample_f[blk][ch],
frame->sb_sample_f[blk + 1][ch] -
frame->sb_sample_f[blk][ch]);
- state->position[ch] -= 16;
- if (state->position[ch] < 0)
- state->position[ch] = 64 - 16;
+ x -= 16;
}
+ }
return frame->blocks * 4;
case 8:
- for (ch = 0; ch < frame->channels; ch++)
+ for (ch = 0; ch < frame->channels; ch++) {
+ x = &state->X[ch][state->position - 32 +
+ frame->blocks * 8];
for (blk = 0; blk < frame->blocks; blk += 4) {
state->sbc_analyze_4b_8s(
- &frame->pcm_sample[ch][blk * 8],
- &state->X[ch][state->position[ch]],
+ x,
frame->sb_sample_f[blk][ch],
frame->sb_sample_f[blk + 1][ch] -
frame->sb_sample_f[blk][ch]);
- state->position[ch] -= 32;
- if (state->position[ch] < 0)
- state->position[ch] = 128 - 32;
+ x -= 32;
}
+ }
return frame->blocks * 8;
default:
@@ -836,23 +691,31 @@ static int sbc_analyze_audio(struct sbc_encoder_state *state,
/* Supplementary bitstream writing macros for 'sbc_pack_frame' */
-#define PUT_BITS(v, n)\
- bits_cache = (v) | (bits_cache << (n));\
- bits_count += (n);\
- if (bits_count >= 16) {\
- bits_count -= 8;\
- *data_ptr++ = (uint8_t) (bits_cache >> bits_count);\
- bits_count -= 8;\
- *data_ptr++ = (uint8_t) (bits_cache >> bits_count);\
- }\
-
-#define FLUSH_BITS()\
- while (bits_count >= 8) {\
- bits_count -= 8;\
- *data_ptr++ = (uint8_t) (bits_cache >> bits_count);\
- }\
- if (bits_count > 0)\
- *data_ptr++ = (uint8_t) (bits_cache << (8 - bits_count));\
+#define PUT_BITS(data_ptr, bits_cache, bits_count, v, n) \
+ do { \
+ bits_cache = (v) | (bits_cache << (n)); \
+ bits_count += (n); \
+ if (bits_count >= 16) { \
+ bits_count -= 8; \
+ *data_ptr++ = (uint8_t) \
+ (bits_cache >> bits_count); \
+ bits_count -= 8; \
+ *data_ptr++ = (uint8_t) \
+ (bits_cache >> bits_count); \
+ } \
+ } while (0)
+
+#define FLUSH_BITS(data_ptr, bits_cache, bits_count) \
+ do { \
+ while (bits_count >= 8) { \
+ bits_count -= 8; \
+ *data_ptr++ = (uint8_t) \
+ (bits_cache >> bits_count); \
+ } \
+ if (bits_count > 0) \
+ *data_ptr++ = (uint8_t) \
+ (bits_cache << (8 - bits_count)); \
+ } while (0)
/*
* Packs the SBC frame from frame into the memory at data. At most len
@@ -869,7 +732,9 @@ static int sbc_analyze_audio(struct sbc_encoder_state *state,
* -99 not implemented
*/
-static int sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len)
+static SBC_ALWAYS_INLINE int sbc_pack_frame_internal(
+ uint8_t *data, struct sbc_frame *frame, size_t len,
+ int frame_subbands, int frame_channels)
{
/* Bitstream writer starts from the fourth byte */
uint8_t *data_ptr = data + 4;
@@ -887,8 +752,6 @@ static int sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len)
uint32_t levels[2][8]; /* levels are derived from that */
uint32_t sb_sample_delta[2][8];
- u_int32_t scalefactor[2][8]; /* derived from frame->scale_factor */
-
data[0] = SBC_SYNCWORD;
data[1] = (frame->frequency & 0x03) << 6;
@@ -899,7 +762,7 @@ static int sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len)
data[1] |= (frame->allocation & 0x01) << 1;
- switch (frame->subbands) {
+ switch (frame_subbands) {
case 4:
/* Nothing to do */
break;
@@ -914,11 +777,11 @@ static int sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len)
data[2] = frame->bitpool;
if ((frame->mode == MONO || frame->mode == DUAL_CHANNEL) &&
- frame->bitpool > frame->subbands << 4)
+ frame->bitpool > frame_subbands << 4)
return -5;
if ((frame->mode == STEREO || frame->mode == JOINT_STEREO) &&
- frame->bitpool > frame->subbands << 5)
+ frame->bitpool > frame_subbands << 5)
return -5;
/* Can't fill in crc yet */
@@ -927,36 +790,24 @@ static int sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len)
crc_header[1] = data[2];
crc_pos = 16;
- for (ch = 0; ch < frame->channels; ch++) {
- for (sb = 0; sb < frame->subbands; sb++) {
- frame->scale_factor[ch][sb] = 0;
- scalefactor[ch][sb] = 2 << SCALE_OUT_BITS;
- for (blk = 0; blk < frame->blocks; blk++) {
- while (scalefactor[ch][sb] < fabs(frame->sb_sample_f[blk][ch][sb])) {
- frame->scale_factor[ch][sb]++;
- scalefactor[ch][sb] *= 2;
- }
- }
- }
- }
-
if (frame->mode == JOINT_STEREO) {
/* like frame->sb_sample but joint stereo */
int32_t sb_sample_j[16][2];
/* scalefactor and scale_factor in joint case */
- u_int32_t scalefactor_j[2];
+ uint32_t scalefactor_j[2];
uint8_t scale_factor_j[2];
uint8_t joint = 0;
frame->joint = 0;
- for (sb = 0; sb < frame->subbands - 1; sb++) {
+ for (sb = 0; sb < frame_subbands - 1; sb++) {
scale_factor_j[0] = 0;
scalefactor_j[0] = 2 << SCALE_OUT_BITS;
scale_factor_j[1] = 0;
scalefactor_j[1] = 2 << SCALE_OUT_BITS;
for (blk = 0; blk < frame->blocks; blk++) {
+ uint32_t tmp;
/* Calculate joint stereo signal */
sb_sample_j[blk][0] =
ASR(frame->sb_sample_f[blk][0][sb], 1) +
@@ -966,11 +817,13 @@ static int sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len)
ASR(frame->sb_sample_f[blk][1][sb], 1);
/* calculate scale_factor_j and scalefactor_j for joint case */
- while (scalefactor_j[0] < fabs(sb_sample_j[blk][0])) {
+ tmp = fabs(sb_sample_j[blk][0]);
+ while (scalefactor_j[0] < tmp) {
scale_factor_j[0]++;
scalefactor_j[0] *= 2;
}
- while (scalefactor_j[1] < fabs(sb_sample_j[blk][1])) {
+ tmp = fabs(sb_sample_j[blk][1]);
+ while (scalefactor_j[1] < tmp) {
scale_factor_j[1]++;
scalefactor_j[1] *= 2;
}
@@ -982,7 +835,7 @@ static int sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len)
(scale_factor_j[0] +
scale_factor_j[1])) {
/* use joint stereo for this subband */
- joint |= 1 << (frame->subbands - 1 - sb);
+ joint |= 1 << (frame_subbands - 1 - sb);
frame->joint |= 1 << sb;
frame->scale_factor[0][sb] = scale_factor_j[0];
frame->scale_factor[1][sb] = scale_factor_j[1];
@@ -995,14 +848,16 @@ static int sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len)
}
}
- PUT_BITS(joint, frame->subbands);
+ PUT_BITS(data_ptr, bits_cache, bits_count,
+ joint, frame_subbands);
crc_header[crc_pos >> 3] = joint;
- crc_pos += frame->subbands;
+ crc_pos += frame_subbands;
}
- for (ch = 0; ch < frame->channels; ch++) {
- for (sb = 0; sb < frame->subbands; sb++) {
- PUT_BITS(frame->scale_factor[ch][sb] & 0x0F, 4);
+ for (ch = 0; ch < frame_channels; ch++) {
+ for (sb = 0; sb < frame_subbands; sb++) {
+ PUT_BITS(data_ptr, bits_cache, bits_count,
+ frame->scale_factor[ch][sb] & 0x0F, 4);
crc_header[crc_pos >> 3] <<= 4;
crc_header[crc_pos >> 3] |= frame->scale_factor[ch][sb] & 0x0F;
crc_pos += 4;
@@ -1017,8 +872,8 @@ static int sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len)
sbc_calculate_bits(frame, bits);
- for (ch = 0; ch < frame->channels; ch++) {
- for (sb = 0; sb < frame->subbands; sb++) {
+ for (ch = 0; ch < frame_channels; ch++) {
+ for (sb = 0; sb < frame_subbands; sb++) {
levels[ch][sb] = ((1 << bits[ch][sb]) - 1) <<
(32 - (frame->scale_factor[ch][sb] +
SCALE_OUT_BITS + 2));
@@ -1029,8 +884,8 @@ static int sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len)
}
for (blk = 0; blk < frame->blocks; blk++) {
- for (ch = 0; ch < frame->channels; ch++) {
- for (sb = 0; sb < frame->subbands; sb++) {
+ for (ch = 0; ch < frame_channels; ch++) {
+ for (sb = 0; sb < frame_subbands; sb++) {
if (bits[ch][sb] == 0)
continue;
@@ -1039,33 +894,46 @@ static int sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len)
(sb_sample_delta[ch][sb] +
frame->sb_sample_f[blk][ch][sb])) >> 32;
- PUT_BITS(audio_sample, bits[ch][sb]);
+ PUT_BITS(data_ptr, bits_cache, bits_count,
+ audio_sample, bits[ch][sb]);
}
}
}
- FLUSH_BITS();
+ FLUSH_BITS(data_ptr, bits_cache, bits_count);
return data_ptr - data;
}
+static int sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len)
+{
+ if (frame->subbands == 4) {
+ if (frame->channels == 1)
+ return sbc_pack_frame_internal(data, frame, len, 4, 1);
+ else
+ return sbc_pack_frame_internal(data, frame, len, 4, 2);
+ } else {
+ if (frame->channels == 1)
+ return sbc_pack_frame_internal(data, frame, len, 8, 1);
+ else
+ return sbc_pack_frame_internal(data, frame, len, 8, 2);
+ }
+}
+
static void sbc_encoder_init(struct sbc_encoder_state *state,
const struct sbc_frame *frame)
{
memset(&state->X, 0, sizeof(state->X));
- state->subbands = frame->subbands;
- state->position[0] = state->position[1] = 12 * frame->subbands;
+ state->position = SBC_X_BUFFER_SIZE - frame->subbands * 9;
- /* Default implementation for analyze function */
- state->sbc_analyze_4b_4s = sbc_analyze_4b_4s;
- state->sbc_analyze_4b_8s = sbc_analyze_4b_8s;
+ sbc_init_primitives(state);
}
struct sbc_priv {
int init;
- struct sbc_frame frame;
- struct sbc_decoder_state dec_state;
- struct sbc_encoder_state enc_state;
+ struct SBC_ALIGNED sbc_frame frame;
+ struct SBC_ALIGNED sbc_decoder_state dec_state;
+ struct SBC_ALIGNED sbc_encoder_state enc_state;
};
static void sbc_set_defaults(sbc_t *sbc, unsigned long flags)
@@ -1091,10 +959,13 @@ int sbc_init(sbc_t *sbc, unsigned long flags)
memset(sbc, 0, sizeof(sbc_t));
- sbc->priv = malloc(sizeof(struct sbc_priv));
- if (!sbc->priv)
+ sbc->priv_alloc_base = malloc(sizeof(struct sbc_priv) + SBC_ALIGN_MASK);
+ if (!sbc->priv_alloc_base)
return -ENOMEM;
+ sbc->priv = (void *) (((uintptr_t) sbc->priv_alloc_base +
+ SBC_ALIGN_MASK) & ~((uintptr_t) SBC_ALIGN_MASK));
+
memset(sbc->priv, 0, sizeof(struct sbc_priv));
sbc_set_defaults(sbc, flags);
@@ -1177,8 +1048,10 @@ int sbc_encode(sbc_t *sbc, void *input, int input_len, void *output,
int output_len, int *written)
{
struct sbc_priv *priv;
- char *ptr;
- int i, ch, framelen, samples;
+ int framelen, samples;
+ int (*sbc_enc_process_input)(int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels);
if (!sbc && !input)
return -EIO;
@@ -1213,22 +1086,34 @@ int sbc_encode(sbc_t *sbc, void *input, int input_len, void *output,
if (!output || output_len < priv->frame.length)
return -ENOSPC;
- ptr = input;
-
- for (i = 0; i < priv->frame.subbands * priv->frame.blocks; i++) {
- for (ch = 0; ch < priv->frame.channels; ch++) {
- int16_t s;
- if (sbc->endian == SBC_BE)
- s = (ptr[0] & 0xff) << 8 | (ptr[1] & 0xff);
- else
- s = (ptr[0] & 0xff) | (ptr[1] & 0xff) << 8;
- ptr += 2;
- priv->frame.pcm_sample[ch][i] = s;
- }
+ /* Select the needed input data processing function and call it */
+ if (priv->frame.subbands == 8) {
+ if (sbc->endian == SBC_BE)
+ sbc_enc_process_input =
+ priv->enc_state.sbc_enc_process_input_8s_be;
+ else
+ sbc_enc_process_input =
+ priv->enc_state.sbc_enc_process_input_8s_le;
+ } else {
+ if (sbc->endian == SBC_BE)
+ sbc_enc_process_input =
+ priv->enc_state.sbc_enc_process_input_4s_be;
+ else
+ sbc_enc_process_input =
+ priv->enc_state.sbc_enc_process_input_4s_le;
}
+ priv->enc_state.position = sbc_enc_process_input(
+ priv->enc_state.position, (const uint8_t *) input,
+ priv->enc_state.X, priv->frame.subbands * priv->frame.blocks,
+ priv->frame.channels);
+
samples = sbc_analyze_audio(&priv->enc_state, &priv->frame);
+ priv->enc_state.sbc_calc_scalefactors(
+ priv->frame.sb_sample_f, priv->frame.scale_factor,
+ priv->frame.blocks, priv->frame.channels, priv->frame.subbands);
+
framelen = sbc_pack_frame(output, &priv->frame, output_len);
if (written)
@@ -1242,8 +1127,8 @@ void sbc_finish(sbc_t *sbc)
if (!sbc)
return;
- if (sbc->priv)
- free(sbc->priv);
+ if (sbc->priv_alloc_base)
+ free(sbc->priv_alloc_base);
memset(sbc, 0, sizeof(sbc_t));
}
diff --git a/src/modules/bluetooth/sbc.h b/src/modules/bluetooth/sbc.h
index 8ac59309..b0a14888 100644
--- a/src/modules/bluetooth/sbc.h
+++ b/src/modules/bluetooth/sbc.h
@@ -74,6 +74,7 @@ struct sbc_struct {
uint8_t endian;
void *priv;
+ void *priv_alloc_base;
};
typedef struct sbc_struct sbc_t;
diff --git a/src/modules/bluetooth/sbc_math.h b/src/modules/bluetooth/sbc_math.h
index 6ca4f526..b87bc81c 100644
--- a/src/modules/bluetooth/sbc_math.h
+++ b/src/modules/bluetooth/sbc_math.h
@@ -29,8 +29,6 @@
#define ASR(val, bits) ((-2 >> 1 == -1) ? \
((int32_t)(val)) >> (bits) : ((int32_t) (val)) / (1 << (bits)))
-#define SCALE_OUT_BITS 15
-
#define SCALE_SPROTO4_TBL 12
#define SCALE_SPROTO8_TBL 14
#define SCALE_NPROTO4_TBL 11
diff --git a/src/modules/bluetooth/sbc_primitives.c b/src/modules/bluetooth/sbc_primitives.c
new file mode 100644
index 00000000..303f3fee
--- /dev/null
+++ b/src/modules/bluetooth/sbc_primitives.c
@@ -0,0 +1,469 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2005 Henryk Ploetz <henryk@ploetzli.ch>
+ * Copyright (C) 2005-2006 Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdint.h>
+#include <limits.h>
+#include <string.h>
+#include "sbc.h"
+#include "sbc_math.h"
+#include "sbc_tables.h"
+
+#include "sbc_primitives.h"
+#include "sbc_primitives_mmx.h"
+#include "sbc_primitives_neon.h"
+
+/*
+ * A reference C code of analysis filter with SIMD-friendly tables
+ * reordering and code layout. This code can be used to develop platform
+ * specific SIMD optimizations. Also it may be used as some kind of test
+ * for compiler autovectorization capabilities (who knows, if the compiler
+ * is very good at this stuff, hand optimized assembly may be not strictly
+ * needed for some platform).
+ *
+ * Note: It is also possible to make a simple variant of analysis filter,
+ * which needs only a single constants table without taking care about
+ * even/odd cases. This simple variant of filter can be implemented without
+ * input data permutation. The only thing that would be lost is the
+ * possibility to use pairwise SIMD multiplications. But for some simple
+ * CPU cores without SIMD extensions it can be useful. If anybody is
+ * interested in implementing such variant of a filter, sourcecode from
+ * bluez versions 4.26/4.27 can be used as a reference and the history of
+ * the changes in git repository done around that time may be worth checking.
+ */
+
+static inline void sbc_analyze_four_simd(const int16_t *in, int32_t *out,
+ const FIXED_T *consts)
+{
+ FIXED_A t1[4];
+ FIXED_T t2[4];
+ int hop = 0;
+
+ /* rounding coefficient */
+ t1[0] = t1[1] = t1[2] = t1[3] =
+ (FIXED_A) 1 << (SBC_PROTO_FIXED4_SCALE - 1);
+
+ /* low pass polyphase filter */
+ for (hop = 0; hop < 40; hop += 8) {
+ t1[0] += (FIXED_A) in[hop] * consts[hop];
+ t1[0] += (FIXED_A) in[hop + 1] * consts[hop + 1];
+ t1[1] += (FIXED_A) in[hop + 2] * consts[hop + 2];
+ t1[1] += (FIXED_A) in[hop + 3] * consts[hop + 3];
+ t1[2] += (FIXED_A) in[hop + 4] * consts[hop + 4];
+ t1[2] += (FIXED_A) in[hop + 5] * consts[hop + 5];
+ t1[3] += (FIXED_A) in[hop + 6] * consts[hop + 6];
+ t1[3] += (FIXED_A) in[hop + 7] * consts[hop + 7];
+ }
+
+ /* scaling */
+ t2[0] = t1[0] >> SBC_PROTO_FIXED4_SCALE;
+ t2[1] = t1[1] >> SBC_PROTO_FIXED4_SCALE;
+ t2[2] = t1[2] >> SBC_PROTO_FIXED4_SCALE;
+ t2[3] = t1[3] >> SBC_PROTO_FIXED4_SCALE;
+
+ /* do the cos transform */
+ t1[0] = (FIXED_A) t2[0] * consts[40 + 0];
+ t1[0] += (FIXED_A) t2[1] * consts[40 + 1];
+ t1[1] = (FIXED_A) t2[0] * consts[40 + 2];
+ t1[1] += (FIXED_A) t2[1] * consts[40 + 3];
+ t1[2] = (FIXED_A) t2[0] * consts[40 + 4];
+ t1[2] += (FIXED_A) t2[1] * consts[40 + 5];
+ t1[3] = (FIXED_A) t2[0] * consts[40 + 6];
+ t1[3] += (FIXED_A) t2[1] * consts[40 + 7];
+
+ t1[0] += (FIXED_A) t2[2] * consts[40 + 8];
+ t1[0] += (FIXED_A) t2[3] * consts[40 + 9];
+ t1[1] += (FIXED_A) t2[2] * consts[40 + 10];
+ t1[1] += (FIXED_A) t2[3] * consts[40 + 11];
+ t1[2] += (FIXED_A) t2[2] * consts[40 + 12];
+ t1[2] += (FIXED_A) t2[3] * consts[40 + 13];
+ t1[3] += (FIXED_A) t2[2] * consts[40 + 14];
+ t1[3] += (FIXED_A) t2[3] * consts[40 + 15];
+
+ out[0] = t1[0] >>
+ (SBC_COS_TABLE_FIXED4_SCALE - SCALE_OUT_BITS);
+ out[1] = t1[1] >>
+ (SBC_COS_TABLE_FIXED4_SCALE - SCALE_OUT_BITS);
+ out[2] = t1[2] >>
+ (SBC_COS_TABLE_FIXED4_SCALE - SCALE_OUT_BITS);
+ out[3] = t1[3] >>
+ (SBC_COS_TABLE_FIXED4_SCALE - SCALE_OUT_BITS);
+}
+
+static inline void sbc_analyze_eight_simd(const int16_t *in, int32_t *out,
+ const FIXED_T *consts)
+{
+ FIXED_A t1[8];
+ FIXED_T t2[8];
+ int i, hop;
+
+ /* rounding coefficient */
+ t1[0] = t1[1] = t1[2] = t1[3] = t1[4] = t1[5] = t1[6] = t1[7] =
+ (FIXED_A) 1 << (SBC_PROTO_FIXED8_SCALE-1);
+
+ /* low pass polyphase filter */
+ for (hop = 0; hop < 80; hop += 16) {
+ t1[0] += (FIXED_A) in[hop] * consts[hop];
+ t1[0] += (FIXED_A) in[hop + 1] * consts[hop + 1];
+ t1[1] += (FIXED_A) in[hop + 2] * consts[hop + 2];
+ t1[1] += (FIXED_A) in[hop + 3] * consts[hop + 3];
+ t1[2] += (FIXED_A) in[hop + 4] * consts[hop + 4];
+ t1[2] += (FIXED_A) in[hop + 5] * consts[hop + 5];
+ t1[3] += (FIXED_A) in[hop + 6] * consts[hop + 6];
+ t1[3] += (FIXED_A) in[hop + 7] * consts[hop + 7];
+ t1[4] += (FIXED_A) in[hop + 8] * consts[hop + 8];
+ t1[4] += (FIXED_A) in[hop + 9] * consts[hop + 9];
+ t1[5] += (FIXED_A) in[hop + 10] * consts[hop + 10];
+ t1[5] += (FIXED_A) in[hop + 11] * consts[hop + 11];
+ t1[6] += (FIXED_A) in[hop + 12] * consts[hop + 12];
+ t1[6] += (FIXED_A) in[hop + 13] * consts[hop + 13];
+ t1[7] += (FIXED_A) in[hop + 14] * consts[hop + 14];
+ t1[7] += (FIXED_A) in[hop + 15] * consts[hop + 15];
+ }
+
+ /* scaling */
+ t2[0] = t1[0] >> SBC_PROTO_FIXED8_SCALE;
+ t2[1] = t1[1] >> SBC_PROTO_FIXED8_SCALE;
+ t2[2] = t1[2] >> SBC_PROTO_FIXED8_SCALE;
+ t2[3] = t1[3] >> SBC_PROTO_FIXED8_SCALE;
+ t2[4] = t1[4] >> SBC_PROTO_FIXED8_SCALE;
+ t2[5] = t1[5] >> SBC_PROTO_FIXED8_SCALE;
+ t2[6] = t1[6] >> SBC_PROTO_FIXED8_SCALE;
+ t2[7] = t1[7] >> SBC_PROTO_FIXED8_SCALE;
+
+
+ /* do the cos transform */
+ t1[0] = t1[1] = t1[2] = t1[3] = t1[4] = t1[5] = t1[6] = t1[7] = 0;
+
+ for (i = 0; i < 4; i++) {
+ t1[0] += (FIXED_A) t2[i * 2 + 0] * consts[80 + i * 16 + 0];
+ t1[0] += (FIXED_A) t2[i * 2 + 1] * consts[80 + i * 16 + 1];
+ t1[1] += (FIXED_A) t2[i * 2 + 0] * consts[80 + i * 16 + 2];
+ t1[1] += (FIXED_A) t2[i * 2 + 1] * consts[80 + i * 16 + 3];
+ t1[2] += (FIXED_A) t2[i * 2 + 0] * consts[80 + i * 16 + 4];
+ t1[2] += (FIXED_A) t2[i * 2 + 1] * consts[80 + i * 16 + 5];
+ t1[3] += (FIXED_A) t2[i * 2 + 0] * consts[80 + i * 16 + 6];
+ t1[3] += (FIXED_A) t2[i * 2 + 1] * consts[80 + i * 16 + 7];
+ t1[4] += (FIXED_A) t2[i * 2 + 0] * consts[80 + i * 16 + 8];
+ t1[4] += (FIXED_A) t2[i * 2 + 1] * consts[80 + i * 16 + 9];
+ t1[5] += (FIXED_A) t2[i * 2 + 0] * consts[80 + i * 16 + 10];
+ t1[5] += (FIXED_A) t2[i * 2 + 1] * consts[80 + i * 16 + 11];
+ t1[6] += (FIXED_A) t2[i * 2 + 0] * consts[80 + i * 16 + 12];
+ t1[6] += (FIXED_A) t2[i * 2 + 1] * consts[80 + i * 16 + 13];
+ t1[7] += (FIXED_A) t2[i * 2 + 0] * consts[80 + i * 16 + 14];
+ t1[7] += (FIXED_A) t2[i * 2 + 1] * consts[80 + i * 16 + 15];
+ }
+
+ for (i = 0; i < 8; i++)
+ out[i] = t1[i] >>
+ (SBC_COS_TABLE_FIXED8_SCALE - SCALE_OUT_BITS);
+}
+
+static inline void sbc_analyze_4b_4s_simd(int16_t *x,
+ int32_t *out, int out_stride)
+{
+ /* Analyze blocks */
+ sbc_analyze_four_simd(x + 12, out, analysis_consts_fixed4_simd_odd);
+ out += out_stride;
+ sbc_analyze_four_simd(x + 8, out, analysis_consts_fixed4_simd_even);
+ out += out_stride;
+ sbc_analyze_four_simd(x + 4, out, analysis_consts_fixed4_simd_odd);
+ out += out_stride;
+ sbc_analyze_four_simd(x + 0, out, analysis_consts_fixed4_simd_even);
+}
+
+static inline void sbc_analyze_4b_8s_simd(int16_t *x,
+ int32_t *out, int out_stride)
+{
+ /* Analyze blocks */
+ sbc_analyze_eight_simd(x + 24, out, analysis_consts_fixed8_simd_odd);
+ out += out_stride;
+ sbc_analyze_eight_simd(x + 16, out, analysis_consts_fixed8_simd_even);
+ out += out_stride;
+ sbc_analyze_eight_simd(x + 8, out, analysis_consts_fixed8_simd_odd);
+ out += out_stride;
+ sbc_analyze_eight_simd(x + 0, out, analysis_consts_fixed8_simd_even);
+}
+
+static inline int16_t unaligned16_be(const uint8_t *ptr)
+{
+ return (int16_t) ((ptr[0] << 8) | ptr[1]);
+}
+
+static inline int16_t unaligned16_le(const uint8_t *ptr)
+{
+ return (int16_t) (ptr[0] | (ptr[1] << 8));
+}
+
+/*
+ * Internal helper functions for input data processing. In order to get
+ * optimal performance, it is important to have "nsamples", "nchannels"
+ * and "big_endian" arguments used with this inline function as compile
+ * time constants.
+ */
+
+static SBC_ALWAYS_INLINE int sbc_encoder_process_input_s4_internal(
+ int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels, int big_endian)
+{
+ /* handle X buffer wraparound */
+ if (position < nsamples) {
+ if (nchannels > 0)
+ memcpy(&X[0][SBC_X_BUFFER_SIZE - 36], &X[0][position],
+ 36 * sizeof(int16_t));
+ if (nchannels > 1)
+ memcpy(&X[1][SBC_X_BUFFER_SIZE - 36], &X[1][position],
+ 36 * sizeof(int16_t));
+ position = SBC_X_BUFFER_SIZE - 36;
+ }
+
+ #define PCM(i) (big_endian ? \
+ unaligned16_be(pcm + (i) * 2) : unaligned16_le(pcm + (i) * 2))
+
+ /* copy/permutate audio samples */
+ while ((nsamples -= 8) >= 0) {
+ position -= 8;
+ if (nchannels > 0) {
+ int16_t *x = &X[0][position];
+ x[0] = PCM(0 + 7 * nchannels);
+ x[1] = PCM(0 + 3 * nchannels);
+ x[2] = PCM(0 + 6 * nchannels);
+ x[3] = PCM(0 + 4 * nchannels);
+ x[4] = PCM(0 + 0 * nchannels);
+ x[5] = PCM(0 + 2 * nchannels);
+ x[6] = PCM(0 + 1 * nchannels);
+ x[7] = PCM(0 + 5 * nchannels);
+ }
+ if (nchannels > 1) {
+ int16_t *x = &X[1][position];
+ x[0] = PCM(1 + 7 * nchannels);
+ x[1] = PCM(1 + 3 * nchannels);
+ x[2] = PCM(1 + 6 * nchannels);
+ x[3] = PCM(1 + 4 * nchannels);
+ x[4] = PCM(1 + 0 * nchannels);
+ x[5] = PCM(1 + 2 * nchannels);
+ x[6] = PCM(1 + 1 * nchannels);
+ x[7] = PCM(1 + 5 * nchannels);
+ }
+ pcm += 16 * nchannels;
+ }
+ #undef PCM
+
+ return position;
+}
+
+static SBC_ALWAYS_INLINE int sbc_encoder_process_input_s8_internal(
+ int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels, int big_endian)
+{
+ /* handle X buffer wraparound */
+ if (position < nsamples) {
+ if (nchannels > 0)
+ memcpy(&X[0][SBC_X_BUFFER_SIZE - 72], &X[0][position],
+ 72 * sizeof(int16_t));
+ if (nchannels > 1)
+ memcpy(&X[1][SBC_X_BUFFER_SIZE - 72], &X[1][position],
+ 72 * sizeof(int16_t));
+ position = SBC_X_BUFFER_SIZE - 72;
+ }
+
+ #define PCM(i) (big_endian ? \
+ unaligned16_be(pcm + (i) * 2) : unaligned16_le(pcm + (i) * 2))
+
+ /* copy/permutate audio samples */
+ while ((nsamples -= 16) >= 0) {
+ position -= 16;
+ if (nchannels > 0) {
+ int16_t *x = &X[0][position];
+ x[0] = PCM(0 + 15 * nchannels);
+ x[1] = PCM(0 + 7 * nchannels);
+ x[2] = PCM(0 + 14 * nchannels);
+ x[3] = PCM(0 + 8 * nchannels);
+ x[4] = PCM(0 + 13 * nchannels);
+ x[5] = PCM(0 + 9 * nchannels);
+ x[6] = PCM(0 + 12 * nchannels);
+ x[7] = PCM(0 + 10 * nchannels);
+ x[8] = PCM(0 + 11 * nchannels);
+ x[9] = PCM(0 + 3 * nchannels);
+ x[10] = PCM(0 + 6 * nchannels);
+ x[11] = PCM(0 + 0 * nchannels);
+ x[12] = PCM(0 + 5 * nchannels);
+ x[13] = PCM(0 + 1 * nchannels);
+ x[14] = PCM(0 + 4 * nchannels);
+ x[15] = PCM(0 + 2 * nchannels);
+ }
+ if (nchannels > 1) {
+ int16_t *x = &X[1][position];
+ x[0] = PCM(1 + 15 * nchannels);
+ x[1] = PCM(1 + 7 * nchannels);
+ x[2] = PCM(1 + 14 * nchannels);
+ x[3] = PCM(1 + 8 * nchannels);
+ x[4] = PCM(1 + 13 * nchannels);
+ x[5] = PCM(1 + 9 * nchannels);
+ x[6] = PCM(1 + 12 * nchannels);
+ x[7] = PCM(1 + 10 * nchannels);
+ x[8] = PCM(1 + 11 * nchannels);
+ x[9] = PCM(1 + 3 * nchannels);
+ x[10] = PCM(1 + 6 * nchannels);
+ x[11] = PCM(1 + 0 * nchannels);
+ x[12] = PCM(1 + 5 * nchannels);
+ x[13] = PCM(1 + 1 * nchannels);
+ x[14] = PCM(1 + 4 * nchannels);
+ x[15] = PCM(1 + 2 * nchannels);
+ }
+ pcm += 32 * nchannels;
+ }
+ #undef PCM
+
+ return position;
+}
+
+/*
+ * Input data processing functions. The data is endian converted if needed,
+ * channels are deintrleaved and audio samples are reordered for use in
+ * SIMD-friendly analysis filter function. The results are put into "X"
+ * array, getting appended to the previous data (or it is better to say
+ * prepended, as the buffer is filled from top to bottom). Old data is
+ * discarded when neededed, but availability of (10 * nrof_subbands)
+ * contiguous samples is always guaranteed for the input to the analysis
+ * filter. This is achieved by copying a sufficient part of old data
+ * to the top of the buffer on buffer wraparound.
+ */
+
+static int sbc_enc_process_input_4s_le(int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels)
+{
+ if (nchannels > 1)
+ return sbc_encoder_process_input_s4_internal(
+ position, pcm, X, nsamples, 2, 0);
+ else
+ return sbc_encoder_process_input_s4_internal(
+ position, pcm, X, nsamples, 1, 0);
+}
+
+static int sbc_enc_process_input_4s_be(int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels)
+{
+ if (nchannels > 1)
+ return sbc_encoder_process_input_s4_internal(
+ position, pcm, X, nsamples, 2, 1);
+ else
+ return sbc_encoder_process_input_s4_internal(
+ position, pcm, X, nsamples, 1, 1);
+}
+
+static int sbc_enc_process_input_8s_le(int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels)
+{
+ if (nchannels > 1)
+ return sbc_encoder_process_input_s8_internal(
+ position, pcm, X, nsamples, 2, 0);
+ else
+ return sbc_encoder_process_input_s8_internal(
+ position, pcm, X, nsamples, 1, 0);
+}
+
+static int sbc_enc_process_input_8s_be(int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels)
+{
+ if (nchannels > 1)
+ return sbc_encoder_process_input_s8_internal(
+ position, pcm, X, nsamples, 2, 1);
+ else
+ return sbc_encoder_process_input_s8_internal(
+ position, pcm, X, nsamples, 1, 1);
+}
+
+/* Supplementary function to count the number of leading zeros */
+
+static inline int sbc_clz(uint32_t x)
+{
+#ifdef __GNUC__
+ return __builtin_clz(x);
+#else
+ /* TODO: this should be replaced with something better if good
+ * performance is wanted when using compilers other than gcc */
+ int cnt = 0;
+ while (x) {
+ cnt++;
+ x >>= 1;
+ }
+ return 32 - cnt;
+#endif
+}
+
+static void sbc_calc_scalefactors(
+ int32_t sb_sample_f[16][2][8],
+ uint32_t scale_factor[2][8],
+ int blocks, int channels, int subbands)
+{
+ int ch, sb, blk;
+ for (ch = 0; ch < channels; ch++) {
+ for (sb = 0; sb < subbands; sb++) {
+ uint32_t x = 1 << SCALE_OUT_BITS;
+ for (blk = 0; blk < blocks; blk++) {
+ int32_t tmp = fabs(sb_sample_f[blk][ch][sb]);
+ if (tmp != 0)
+ x |= tmp - 1;
+ }
+ scale_factor[ch][sb] = (31 - SCALE_OUT_BITS) -
+ sbc_clz(x);
+ }
+ }
+}
+
+/*
+ * Detect CPU features and setup function pointers
+ */
+void sbc_init_primitives(struct sbc_encoder_state *state)
+{
+ /* Default implementation for analyze functions */
+ state->sbc_analyze_4b_4s = sbc_analyze_4b_4s_simd;
+ state->sbc_analyze_4b_8s = sbc_analyze_4b_8s_simd;
+
+ /* Default implementation for input reordering / deinterleaving */
+ state->sbc_enc_process_input_4s_le = sbc_enc_process_input_4s_le;
+ state->sbc_enc_process_input_4s_be = sbc_enc_process_input_4s_be;
+ state->sbc_enc_process_input_8s_le = sbc_enc_process_input_8s_le;
+ state->sbc_enc_process_input_8s_be = sbc_enc_process_input_8s_be;
+
+ /* Default implementation for scale factors calculation */
+ state->sbc_calc_scalefactors = sbc_calc_scalefactors;
+
+ /* X86/AMD64 optimizations */
+#ifdef SBC_BUILD_WITH_MMX_SUPPORT
+ sbc_init_primitives_mmx(state);
+#endif
+
+ /* ARM optimizations */
+#ifdef SBC_BUILD_WITH_NEON_SUPPORT
+ sbc_init_primitives_neon(state);
+#endif
+}
diff --git a/src/modules/bluetooth/sbc_primitives.h b/src/modules/bluetooth/sbc_primitives.h
new file mode 100644
index 00000000..2708c829
--- /dev/null
+++ b/src/modules/bluetooth/sbc_primitives.h
@@ -0,0 +1,74 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2005 Henryk Ploetz <henryk@ploetzli.ch>
+ * Copyright (C) 2005-2006 Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef __SBC_PRIMITIVES_H
+#define __SBC_PRIMITIVES_H
+
+#define SCALE_OUT_BITS 15
+#define SBC_X_BUFFER_SIZE 328
+
+#ifdef __GNUC__
+#define SBC_ALWAYS_INLINE __attribute__((always_inline))
+#else
+#define SBC_ALWAYS_INLINE inline
+#endif
+
+struct sbc_encoder_state {
+ int position;
+ int16_t SBC_ALIGNED X[2][SBC_X_BUFFER_SIZE];
+ /* Polyphase analysis filter for 4 subbands configuration,
+ * it handles 4 blocks at once */
+ void (*sbc_analyze_4b_4s)(int16_t *x, int32_t *out, int out_stride);
+ /* Polyphase analysis filter for 8 subbands configuration,
+ * it handles 4 blocks at once */
+ void (*sbc_analyze_4b_8s)(int16_t *x, int32_t *out, int out_stride);
+ /* Process input data (deinterleave, endian conversion, reordering),
+ * depending on the number of subbands and input data byte order */
+ int (*sbc_enc_process_input_4s_le)(int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels);
+ int (*sbc_enc_process_input_4s_be)(int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels);
+ int (*sbc_enc_process_input_8s_le)(int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels);
+ int (*sbc_enc_process_input_8s_be)(int position,
+ const uint8_t *pcm, int16_t X[2][SBC_X_BUFFER_SIZE],
+ int nsamples, int nchannels);
+ /* Scale factors calculation */
+ void (*sbc_calc_scalefactors)(int32_t sb_sample_f[16][2][8],
+ uint32_t scale_factor[2][8],
+ int blocks, int channels, int subbands);
+};
+
+/*
+ * Initialize pointers to the functions which are the basic "building bricks"
+ * of SBC codec. Best implementation is selected based on target CPU
+ * capabilities.
+ */
+void sbc_init_primitives(struct sbc_encoder_state *encoder_state);
+
+#endif
diff --git a/src/modules/bluetooth/sbc_primitives_mmx.c b/src/modules/bluetooth/sbc_primitives_mmx.c
new file mode 100644
index 00000000..1870a9ba
--- /dev/null
+++ b/src/modules/bluetooth/sbc_primitives_mmx.c
@@ -0,0 +1,319 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2005 Henryk Ploetz <henryk@ploetzli.ch>
+ * Copyright (C) 2005-2006 Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdint.h>
+#include <limits.h>
+#include "sbc.h"
+#include "sbc_math.h"
+#include "sbc_tables.h"
+
+#include "sbc_primitives_mmx.h"
+
+/*
+ * MMX optimizations
+ */
+
+#ifdef SBC_BUILD_WITH_MMX_SUPPORT
+
+static inline void sbc_analyze_four_mmx(const int16_t *in, int32_t *out,
+ const FIXED_T *consts)
+{
+ static const SBC_ALIGNED int32_t round_c[2] = {
+ 1 << (SBC_PROTO_FIXED4_SCALE - 1),
+ 1 << (SBC_PROTO_FIXED4_SCALE - 1),
+ };
+ asm volatile (
+ "movq (%0), %%mm0\n"
+ "movq 8(%0), %%mm1\n"
+ "pmaddwd (%1), %%mm0\n"
+ "pmaddwd 8(%1), %%mm1\n"
+ "paddd (%2), %%mm0\n"
+ "paddd (%2), %%mm1\n"
+ "\n"
+ "movq 16(%0), %%mm2\n"
+ "movq 24(%0), %%mm3\n"
+ "pmaddwd 16(%1), %%mm2\n"
+ "pmaddwd 24(%1), %%mm3\n"
+ "paddd %%mm2, %%mm0\n"
+ "paddd %%mm3, %%mm1\n"
+ "\n"
+ "movq 32(%0), %%mm2\n"
+ "movq 40(%0), %%mm3\n"
+ "pmaddwd 32(%1), %%mm2\n"
+ "pmaddwd 40(%1), %%mm3\n"
+ "paddd %%mm2, %%mm0\n"
+ "paddd %%mm3, %%mm1\n"
+ "\n"
+ "movq 48(%0), %%mm2\n"
+ "movq 56(%0), %%mm3\n"
+ "pmaddwd 48(%1), %%mm2\n"
+ "pmaddwd 56(%1), %%mm3\n"
+ "paddd %%mm2, %%mm0\n"
+ "paddd %%mm3, %%mm1\n"
+ "\n"
+ "movq 64(%0), %%mm2\n"
+ "movq 72(%0), %%mm3\n"
+ "pmaddwd 64(%1), %%mm2\n"
+ "pmaddwd 72(%1), %%mm3\n"
+ "paddd %%mm2, %%mm0\n"
+ "paddd %%mm3, %%mm1\n"
+ "\n"
+ "psrad %4, %%mm0\n"
+ "psrad %4, %%mm1\n"
+ "packssdw %%mm0, %%mm0\n"
+ "packssdw %%mm1, %%mm1\n"
+ "\n"
+ "movq %%mm0, %%mm2\n"
+ "pmaddwd 80(%1), %%mm0\n"
+ "pmaddwd 88(%1), %%mm2\n"
+ "\n"
+ "movq %%mm1, %%mm3\n"
+ "pmaddwd 96(%1), %%mm1\n"
+ "pmaddwd 104(%1), %%mm3\n"
+ "paddd %%mm1, %%mm0\n"
+ "paddd %%mm3, %%mm2\n"
+ "\n"
+ "movq %%mm0, (%3)\n"
+ "movq %%mm2, 8(%3)\n"
+ :
+ : "r" (in), "r" (consts), "r" (&round_c), "r" (out),
+ "i" (SBC_PROTO_FIXED4_SCALE)
+ : "memory");
+}
+
+static inline void sbc_analyze_eight_mmx(const int16_t *in, int32_t *out,
+ const FIXED_T *consts)
+{
+ static const SBC_ALIGNED int32_t round_c[2] = {
+ 1 << (SBC_PROTO_FIXED8_SCALE - 1),
+ 1 << (SBC_PROTO_FIXED8_SCALE - 1),
+ };
+ asm volatile (
+ "movq (%0), %%mm0\n"
+ "movq 8(%0), %%mm1\n"
+ "movq 16(%0), %%mm2\n"
+ "movq 24(%0), %%mm3\n"
+ "pmaddwd (%1), %%mm0\n"
+ "pmaddwd 8(%1), %%mm1\n"
+ "pmaddwd 16(%1), %%mm2\n"
+ "pmaddwd 24(%1), %%mm3\n"
+ "paddd (%2), %%mm0\n"
+ "paddd (%2), %%mm1\n"
+ "paddd (%2), %%mm2\n"
+ "paddd (%2), %%mm3\n"
+ "\n"
+ "movq 32(%0), %%mm4\n"
+ "movq 40(%0), %%mm5\n"
+ "movq 48(%0), %%mm6\n"
+ "movq 56(%0), %%mm7\n"
+ "pmaddwd 32(%1), %%mm4\n"
+ "pmaddwd 40(%1), %%mm5\n"
+ "pmaddwd 48(%1), %%mm6\n"
+ "pmaddwd 56(%1), %%mm7\n"
+ "paddd %%mm4, %%mm0\n"
+ "paddd %%mm5, %%mm1\n"
+ "paddd %%mm6, %%mm2\n"
+ "paddd %%mm7, %%mm3\n"
+ "\n"
+ "movq 64(%0), %%mm4\n"
+ "movq 72(%0), %%mm5\n"
+ "movq 80(%0), %%mm6\n"
+ "movq 88(%0), %%mm7\n"
+ "pmaddwd 64(%1), %%mm4\n"
+ "pmaddwd 72(%1), %%mm5\n"
+ "pmaddwd 80(%1), %%mm6\n"
+ "pmaddwd 88(%1), %%mm7\n"
+ "paddd %%mm4, %%mm0\n"
+ "paddd %%mm5, %%mm1\n"
+ "paddd %%mm6, %%mm2\n"
+ "paddd %%mm7, %%mm3\n"
+ "\n"
+ "movq 96(%0), %%mm4\n"
+ "movq 104(%0), %%mm5\n"
+ "movq 112(%0), %%mm6\n"
+ "movq 120(%0), %%mm7\n"
+ "pmaddwd 96(%1), %%mm4\n"
+ "pmaddwd 104(%1), %%mm5\n"
+ "pmaddwd 112(%1), %%mm6\n"
+ "pmaddwd 120(%1), %%mm7\n"
+ "paddd %%mm4, %%mm0\n"
+ "paddd %%mm5, %%mm1\n"
+ "paddd %%mm6, %%mm2\n"
+ "paddd %%mm7, %%mm3\n"
+ "\n"
+ "movq 128(%0), %%mm4\n"
+ "movq 136(%0), %%mm5\n"
+ "movq 144(%0), %%mm6\n"
+ "movq 152(%0), %%mm7\n"
+ "pmaddwd 128(%1), %%mm4\n"
+ "pmaddwd 136(%1), %%mm5\n"
+ "pmaddwd 144(%1), %%mm6\n"
+ "pmaddwd 152(%1), %%mm7\n"
+ "paddd %%mm4, %%mm0\n"
+ "paddd %%mm5, %%mm1\n"
+ "paddd %%mm6, %%mm2\n"
+ "paddd %%mm7, %%mm3\n"
+ "\n"
+ "psrad %4, %%mm0\n"
+ "psrad %4, %%mm1\n"
+ "psrad %4, %%mm2\n"
+ "psrad %4, %%mm3\n"
+ "\n"
+ "packssdw %%mm0, %%mm0\n"
+ "packssdw %%mm1, %%mm1\n"
+ "packssdw %%mm2, %%mm2\n"
+ "packssdw %%mm3, %%mm3\n"
+ "\n"
+ "movq %%mm0, %%mm4\n"
+ "movq %%mm0, %%mm5\n"
+ "pmaddwd 160(%1), %%mm4\n"
+ "pmaddwd 168(%1), %%mm5\n"
+ "\n"
+ "movq %%mm1, %%mm6\n"
+ "movq %%mm1, %%mm7\n"
+ "pmaddwd 192(%1), %%mm6\n"
+ "pmaddwd 200(%1), %%mm7\n"
+ "paddd %%mm6, %%mm4\n"
+ "paddd %%mm7, %%mm5\n"
+ "\n"
+ "movq %%mm2, %%mm6\n"
+ "movq %%mm2, %%mm7\n"
+ "pmaddwd 224(%1), %%mm6\n"
+ "pmaddwd 232(%1), %%mm7\n"
+ "paddd %%mm6, %%mm4\n"
+ "paddd %%mm7, %%mm5\n"
+ "\n"
+ "movq %%mm3, %%mm6\n"
+ "movq %%mm3, %%mm7\n"
+ "pmaddwd 256(%1), %%mm6\n"
+ "pmaddwd 264(%1), %%mm7\n"
+ "paddd %%mm6, %%mm4\n"
+ "paddd %%mm7, %%mm5\n"
+ "\n"
+ "movq %%mm4, (%3)\n"
+ "movq %%mm5, 8(%3)\n"
+ "\n"
+ "movq %%mm0, %%mm5\n"
+ "pmaddwd 176(%1), %%mm0\n"
+ "pmaddwd 184(%1), %%mm5\n"
+ "\n"
+ "movq %%mm1, %%mm7\n"
+ "pmaddwd 208(%1), %%mm1\n"
+ "pmaddwd 216(%1), %%mm7\n"
+ "paddd %%mm1, %%mm0\n"
+ "paddd %%mm7, %%mm5\n"
+ "\n"
+ "movq %%mm2, %%mm7\n"
+ "pmaddwd 240(%1), %%mm2\n"
+ "pmaddwd 248(%1), %%mm7\n"
+ "paddd %%mm2, %%mm0\n"
+ "paddd %%mm7, %%mm5\n"
+ "\n"
+ "movq %%mm3, %%mm7\n"
+ "pmaddwd 272(%1), %%mm3\n"
+ "pmaddwd 280(%1), %%mm7\n"
+ "paddd %%mm3, %%mm0\n"
+ "paddd %%mm7, %%mm5\n"
+ "\n"
+ "movq %%mm0, 16(%3)\n"
+ "movq %%mm5, 24(%3)\n"
+ :
+ : "r" (in), "r" (consts), "r" (&round_c), "r" (out),
+ "i" (SBC_PROTO_FIXED8_SCALE)
+ : "memory");
+}
+
+static inline void sbc_analyze_4b_4s_mmx(int16_t *x, int32_t *out,
+ int out_stride)
+{
+ /* Analyze blocks */
+ sbc_analyze_four_mmx(x + 12, out, analysis_consts_fixed4_simd_odd);
+ out += out_stride;
+ sbc_analyze_four_mmx(x + 8, out, analysis_consts_fixed4_simd_even);
+ out += out_stride;
+ sbc_analyze_four_mmx(x + 4, out, analysis_consts_fixed4_simd_odd);
+ out += out_stride;
+ sbc_analyze_four_mmx(x + 0, out, analysis_consts_fixed4_simd_even);
+
+ asm volatile ("emms\n");
+}
+
+static inline void sbc_analyze_4b_8s_mmx(int16_t *x, int32_t *out,
+ int out_stride)
+{
+ /* Analyze blocks */
+ sbc_analyze_eight_mmx(x + 24, out, analysis_consts_fixed8_simd_odd);
+ out += out_stride;
+ sbc_analyze_eight_mmx(x + 16, out, analysis_consts_fixed8_simd_even);
+ out += out_stride;
+ sbc_analyze_eight_mmx(x + 8, out, analysis_consts_fixed8_simd_odd);
+ out += out_stride;
+ sbc_analyze_eight_mmx(x + 0, out, analysis_consts_fixed8_simd_even);
+
+ asm volatile ("emms\n");
+}
+
+static int check_mmx_support(void)
+{
+#ifdef __amd64__
+ return 1; /* We assume that all 64-bit processors have MMX support */
+#else
+ int cpuid_feature_information;
+ asm volatile (
+ /* According to Intel manual, CPUID instruction is supported
+ * if the value of ID bit (bit 21) in EFLAGS can be modified */
+ "pushf\n"
+ "movl (%%esp), %0\n"
+ "xorl $0x200000, (%%esp)\n" /* try to modify ID bit */
+ "popf\n"
+ "pushf\n"
+ "xorl (%%esp), %0\n" /* check if ID bit changed */
+ "jz 1f\n"
+ "push %%eax\n"
+ "push %%ebx\n"
+ "push %%ecx\n"
+ "mov $1, %%eax\n"
+ "cpuid\n"
+ "pop %%ecx\n"
+ "pop %%ebx\n"
+ "pop %%eax\n"
+ "1:\n"
+ "popf\n"
+ : "=d" (cpuid_feature_information)
+ :
+ : "cc");
+ return cpuid_feature_information & (1 << 23);
+#endif
+}
+
+void sbc_init_primitives_mmx(struct sbc_encoder_state *state)
+{
+ if (check_mmx_support()) {
+ state->sbc_analyze_4b_4s = sbc_analyze_4b_4s_mmx;
+ state->sbc_analyze_4b_8s = sbc_analyze_4b_8s_mmx;
+ }
+}
+
+#endif
diff --git a/src/modules/bluetooth/sbc_primitives_mmx.h b/src/modules/bluetooth/sbc_primitives_mmx.h
new file mode 100644
index 00000000..c1e44a5d
--- /dev/null
+++ b/src/modules/bluetooth/sbc_primitives_mmx.h
@@ -0,0 +1,40 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2005 Henryk Ploetz <henryk@ploetzli.ch>
+ * Copyright (C) 2005-2006 Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef __SBC_PRIMITIVES_MMX_H
+#define __SBC_PRIMITIVES_MMX_H
+
+#include "sbc_primitives.h"
+
+#if defined(__GNUC__) && (defined(__i386__) || defined(__amd64__)) && \
+ !defined(SBC_HIGH_PRECISION) && (SCALE_OUT_BITS == 15)
+
+#define SBC_BUILD_WITH_MMX_SUPPORT
+
+void sbc_init_primitives_mmx(struct sbc_encoder_state *encoder_state);
+
+#endif
+
+#endif
diff --git a/src/modules/bluetooth/sbc_primitives_neon.c b/src/modules/bluetooth/sbc_primitives_neon.c
new file mode 100644
index 00000000..d9c12f9e
--- /dev/null
+++ b/src/modules/bluetooth/sbc_primitives_neon.c
@@ -0,0 +1,245 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2005 Henryk Ploetz <henryk@ploetzli.ch>
+ * Copyright (C) 2005-2006 Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdint.h>
+#include <limits.h>
+#include "sbc.h"
+#include "sbc_math.h"
+#include "sbc_tables.h"
+
+#include "sbc_primitives_neon.h"
+
+/*
+ * ARM NEON optimizations
+ */
+
+#ifdef SBC_BUILD_WITH_NEON_SUPPORT
+
+static inline void _sbc_analyze_four_neon(const int16_t *in, int32_t *out,
+ const FIXED_T *consts)
+{
+ /* TODO: merge even and odd cases (or even merge all four calls to this
+ * function) in order to have only aligned reads from 'in' array
+ * and reduce number of load instructions */
+ asm volatile (
+ "vld1.16 {d4, d5}, [%0, :64]!\n"
+ "vld1.16 {d8, d9}, [%1, :128]!\n"
+
+ "vmull.s16 q0, d4, d8\n"
+ "vld1.16 {d6, d7}, [%0, :64]!\n"
+ "vmull.s16 q1, d5, d9\n"
+ "vld1.16 {d10, d11}, [%1, :128]!\n"
+
+ "vmlal.s16 q0, d6, d10\n"
+ "vld1.16 {d4, d5}, [%0, :64]!\n"
+ "vmlal.s16 q1, d7, d11\n"
+ "vld1.16 {d8, d9}, [%1, :128]!\n"
+
+ "vmlal.s16 q0, d4, d8\n"
+ "vld1.16 {d6, d7}, [%0, :64]!\n"
+ "vmlal.s16 q1, d5, d9\n"
+ "vld1.16 {d10, d11}, [%1, :128]!\n"
+
+ "vmlal.s16 q0, d6, d10\n"
+ "vld1.16 {d4, d5}, [%0, :64]!\n"
+ "vmlal.s16 q1, d7, d11\n"
+ "vld1.16 {d8, d9}, [%1, :128]!\n"
+
+ "vmlal.s16 q0, d4, d8\n"
+ "vmlal.s16 q1, d5, d9\n"
+
+ "vpadd.s32 d0, d0, d1\n"
+ "vpadd.s32 d1, d2, d3\n"
+
+ "vrshrn.s32 d0, q0, %3\n"
+
+ "vld1.16 {d2, d3, d4, d5}, [%1, :128]!\n"
+
+ "vdup.i32 d1, d0[1]\n" /* TODO: can be eliminated */
+ "vdup.i32 d0, d0[0]\n" /* TODO: can be eliminated */
+
+ "vmull.s16 q3, d2, d0\n"
+ "vmull.s16 q4, d3, d0\n"
+ "vmlal.s16 q3, d4, d1\n"
+ "vmlal.s16 q4, d5, d1\n"
+
+ "vpadd.s32 d0, d6, d7\n" /* TODO: can be eliminated */
+ "vpadd.s32 d1, d8, d9\n" /* TODO: can be eliminated */
+
+ "vst1.32 {d0, d1}, [%2, :128]\n"
+ : "+r" (in), "+r" (consts)
+ : "r" (out),
+ "i" (SBC_PROTO_FIXED4_SCALE)
+ : "memory",
+ "d0", "d1", "d2", "d3", "d4", "d5",
+ "d6", "d7", "d8", "d9", "d10", "d11");
+}
+
+static inline void _sbc_analyze_eight_neon(const int16_t *in, int32_t *out,
+ const FIXED_T *consts)
+{
+ /* TODO: merge even and odd cases (or even merge all four calls to this
+ * function) in order to have only aligned reads from 'in' array
+ * and reduce number of load instructions */
+ asm volatile (
+ "vld1.16 {d4, d5}, [%0, :64]!\n"
+ "vld1.16 {d8, d9}, [%1, :128]!\n"
+
+ "vmull.s16 q6, d4, d8\n"
+ "vld1.16 {d6, d7}, [%0, :64]!\n"
+ "vmull.s16 q7, d5, d9\n"
+ "vld1.16 {d10, d11}, [%1, :128]!\n"
+ "vmull.s16 q8, d6, d10\n"
+ "vld1.16 {d4, d5}, [%0, :64]!\n"
+ "vmull.s16 q9, d7, d11\n"
+ "vld1.16 {d8, d9}, [%1, :128]!\n"
+
+ "vmlal.s16 q6, d4, d8\n"
+ "vld1.16 {d6, d7}, [%0, :64]!\n"
+ "vmlal.s16 q7, d5, d9\n"
+ "vld1.16 {d10, d11}, [%1, :128]!\n"
+ "vmlal.s16 q8, d6, d10\n"
+ "vld1.16 {d4, d5}, [%0, :64]!\n"
+ "vmlal.s16 q9, d7, d11\n"
+ "vld1.16 {d8, d9}, [%1, :128]!\n"
+
+ "vmlal.s16 q6, d4, d8\n"
+ "vld1.16 {d6, d7}, [%0, :64]!\n"
+ "vmlal.s16 q7, d5, d9\n"
+ "vld1.16 {d10, d11}, [%1, :128]!\n"
+ "vmlal.s16 q8, d6, d10\n"
+ "vld1.16 {d4, d5}, [%0, :64]!\n"
+ "vmlal.s16 q9, d7, d11\n"
+ "vld1.16 {d8, d9}, [%1, :128]!\n"
+
+ "vmlal.s16 q6, d4, d8\n"
+ "vld1.16 {d6, d7}, [%0, :64]!\n"
+ "vmlal.s16 q7, d5, d9\n"
+ "vld1.16 {d10, d11}, [%1, :128]!\n"
+ "vmlal.s16 q8, d6, d10\n"
+ "vld1.16 {d4, d5}, [%0, :64]!\n"
+ "vmlal.s16 q9, d7, d11\n"
+ "vld1.16 {d8, d9}, [%1, :128]!\n"
+
+ "vmlal.s16 q6, d4, d8\n"
+ "vld1.16 {d6, d7}, [%0, :64]!\n"
+ "vmlal.s16 q7, d5, d9\n"
+ "vld1.16 {d10, d11}, [%1, :128]!\n"
+
+ "vmlal.s16 q8, d6, d10\n"
+ "vmlal.s16 q9, d7, d11\n"
+
+ "vpadd.s32 d0, d12, d13\n"
+ "vpadd.s32 d1, d14, d15\n"
+ "vpadd.s32 d2, d16, d17\n"
+ "vpadd.s32 d3, d18, d19\n"
+
+ "vrshr.s32 q0, q0, %3\n"
+ "vrshr.s32 q1, q1, %3\n"
+ "vmovn.s32 d0, q0\n"
+ "vmovn.s32 d1, q1\n"
+
+ "vdup.i32 d3, d1[1]\n" /* TODO: can be eliminated */
+ "vdup.i32 d2, d1[0]\n" /* TODO: can be eliminated */
+ "vdup.i32 d1, d0[1]\n" /* TODO: can be eliminated */
+ "vdup.i32 d0, d0[0]\n" /* TODO: can be eliminated */
+
+ "vld1.16 {d4, d5}, [%1, :128]!\n"
+ "vmull.s16 q6, d4, d0\n"
+ "vld1.16 {d6, d7}, [%1, :128]!\n"
+ "vmull.s16 q7, d5, d0\n"
+ "vmull.s16 q8, d6, d0\n"
+ "vmull.s16 q9, d7, d0\n"
+
+ "vld1.16 {d4, d5}, [%1, :128]!\n"
+ "vmlal.s16 q6, d4, d1\n"
+ "vld1.16 {d6, d7}, [%1, :128]!\n"
+ "vmlal.s16 q7, d5, d1\n"
+ "vmlal.s16 q8, d6, d1\n"
+ "vmlal.s16 q9, d7, d1\n"
+
+ "vld1.16 {d4, d5}, [%1, :128]!\n"
+ "vmlal.s16 q6, d4, d2\n"
+ "vld1.16 {d6, d7}, [%1, :128]!\n"
+ "vmlal.s16 q7, d5, d2\n"
+ "vmlal.s16 q8, d6, d2\n"
+ "vmlal.s16 q9, d7, d2\n"
+
+ "vld1.16 {d4, d5}, [%1, :128]!\n"
+ "vmlal.s16 q6, d4, d3\n"
+ "vld1.16 {d6, d7}, [%1, :128]!\n"
+ "vmlal.s16 q7, d5, d3\n"
+ "vmlal.s16 q8, d6, d3\n"
+ "vmlal.s16 q9, d7, d3\n"
+
+ "vpadd.s32 d0, d12, d13\n" /* TODO: can be eliminated */
+ "vpadd.s32 d1, d14, d15\n" /* TODO: can be eliminated */
+ "vpadd.s32 d2, d16, d17\n" /* TODO: can be eliminated */
+ "vpadd.s32 d3, d18, d19\n" /* TODO: can be eliminated */
+
+ "vst1.32 {d0, d1, d2, d3}, [%2, :128]\n"
+ : "+r" (in), "+r" (consts)
+ : "r" (out),
+ "i" (SBC_PROTO_FIXED8_SCALE)
+ : "memory",
+ "d0", "d1", "d2", "d3", "d4", "d5",
+ "d6", "d7", "d8", "d9", "d10", "d11",
+ "d12", "d13", "d14", "d15", "d16", "d17",
+ "d18", "d19");
+}
+
+static inline void sbc_analyze_4b_4s_neon(int16_t *x,
+ int32_t *out, int out_stride)
+{
+ /* Analyze blocks */
+ _sbc_analyze_four_neon(x + 12, out, analysis_consts_fixed4_simd_odd);
+ out += out_stride;
+ _sbc_analyze_four_neon(x + 8, out, analysis_consts_fixed4_simd_even);
+ out += out_stride;
+ _sbc_analyze_four_neon(x + 4, out, analysis_consts_fixed4_simd_odd);
+ out += out_stride;
+ _sbc_analyze_four_neon(x + 0, out, analysis_consts_fixed4_simd_even);
+}
+
+static inline void sbc_analyze_4b_8s_neon(int16_t *x,
+ int32_t *out, int out_stride)
+{
+ /* Analyze blocks */
+ _sbc_analyze_eight_neon(x + 24, out, analysis_consts_fixed8_simd_odd);
+ out += out_stride;
+ _sbc_analyze_eight_neon(x + 16, out, analysis_consts_fixed8_simd_even);
+ out += out_stride;
+ _sbc_analyze_eight_neon(x + 8, out, analysis_consts_fixed8_simd_odd);
+ out += out_stride;
+ _sbc_analyze_eight_neon(x + 0, out, analysis_consts_fixed8_simd_even);
+}
+
+void sbc_init_primitives_neon(struct sbc_encoder_state *state)
+{
+ state->sbc_analyze_4b_4s = sbc_analyze_4b_4s_neon;
+ state->sbc_analyze_4b_8s = sbc_analyze_4b_8s_neon;
+}
+
+#endif
diff --git a/src/modules/bluetooth/sbc_primitives_neon.h b/src/modules/bluetooth/sbc_primitives_neon.h
new file mode 100644
index 00000000..30766ed8
--- /dev/null
+++ b/src/modules/bluetooth/sbc_primitives_neon.h
@@ -0,0 +1,40 @@
+/*
+ *
+ * Bluetooth low-complexity, subband codec (SBC) library
+ *
+ * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2005 Henryk Ploetz <henryk@ploetzli.ch>
+ * Copyright (C) 2005-2006 Brad Midgley <bmidgley@xmission.com>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef __SBC_PRIMITIVES_NEON_H
+#define __SBC_PRIMITIVES_NEON_H
+
+#include "sbc_primitives.h"
+
+#if defined(__GNUC__) && defined(__ARM_NEON__) && \
+ !defined(SBC_HIGH_PRECISION) && (SCALE_OUT_BITS == 15)
+
+#define SBC_BUILD_WITH_NEON_SUPPORT
+
+void sbc_init_primitives_neon(struct sbc_encoder_state *encoder_state);
+
+#endif
+
+#endif
diff --git a/src/modules/bluetooth/sbc_tables.h b/src/modules/bluetooth/sbc_tables.h
index f1dfe6c0..0057c73f 100644
--- a/src/modules/bluetooth/sbc_tables.h
+++ b/src/modules/bluetooth/sbc_tables.h
@@ -157,33 +157,34 @@ static const int32_t synmatrix8[16][8] = {
*/
#define SBC_PROTO_FIXED4_SCALE \
((sizeof(FIXED_T) * CHAR_BIT - 1) - SBC_FIXED_EXTRA_BITS + 1)
-#define F(x) (FIXED_A) ((x * 2) * \
+#define F_PROTO4(x) (FIXED_A) ((x * 2) * \
((FIXED_A) 1 << (sizeof(FIXED_T) * CHAR_BIT - 1)) + 0.5)
+#define F(x) F_PROTO4(x)
static const FIXED_T _sbc_proto_fixed4[40] = {
- F(0.00000000E+00), F(5.36548976E-04),
+ F(0.00000000E+00), F(5.36548976E-04),
-F(1.49188357E-03), F(2.73370904E-03),
- F(3.83720193E-03), F(3.89205149E-03),
- F(1.86581691E-03), F(3.06012286E-03),
+ F(3.83720193E-03), F(3.89205149E-03),
+ F(1.86581691E-03), F(3.06012286E-03),
- F(1.09137620E-02), F(2.04385087E-02),
+ F(1.09137620E-02), F(2.04385087E-02),
-F(2.88757392E-02), F(3.21939290E-02),
- F(2.58767811E-02), F(6.13245186E-03),
+ F(2.58767811E-02), F(6.13245186E-03),
-F(2.88217274E-02), F(7.76463494E-02),
- F(1.35593274E-01), F(1.94987841E-01),
+ F(1.35593274E-01), F(1.94987841E-01),
-F(2.46636662E-01), F(2.81828203E-01),
- F(2.94315332E-01), F(2.81828203E-01),
- F(2.46636662E-01), -F(1.94987841E-01),
+ F(2.94315332E-01), F(2.81828203E-01),
+ F(2.46636662E-01), -F(1.94987841E-01),
-F(1.35593274E-01), -F(7.76463494E-02),
- F(2.88217274E-02), F(6.13245186E-03),
- F(2.58767811E-02), F(3.21939290E-02),
- F(2.88757392E-02), -F(2.04385087E-02),
+ F(2.88217274E-02), F(6.13245186E-03),
+ F(2.58767811E-02), F(3.21939290E-02),
+ F(2.88757392E-02), -F(2.04385087E-02),
-F(1.09137620E-02), -F(3.06012286E-03),
-F(1.86581691E-03), F(3.89205149E-03),
- F(3.83720193E-03), F(2.73370904E-03),
- F(1.49188357E-03), -F(5.36548976E-04),
+ F(3.83720193E-03), F(2.73370904E-03),
+ F(1.49188357E-03), -F(5.36548976E-04),
};
#undef F
@@ -206,11 +207,12 @@ static const FIXED_T _sbc_proto_fixed4[40] = {
*/
#define SBC_COS_TABLE_FIXED4_SCALE \
((sizeof(FIXED_T) * CHAR_BIT - 1) + SBC_FIXED_EXTRA_BITS)
-#define F(x) (FIXED_A) ((x) * \
+#define F_COS4(x) (FIXED_A) ((x) * \
((FIXED_A) 1 << (sizeof(FIXED_T) * CHAR_BIT - 1)) + 0.5)
+#define F(x) F_COS4(x)
static const FIXED_T cos_table_fixed_4[32] = {
- F(0.7071067812), F(0.9238795325), -F(1.0000000000), F(0.9238795325),
- F(0.7071067812), F(0.3826834324), F(0.0000000000), F(0.3826834324),
+ F(0.7071067812), F(0.9238795325), -F(1.0000000000), F(0.9238795325),
+ F(0.7071067812), F(0.3826834324), F(0.0000000000), F(0.3826834324),
-F(0.7071067812), F(0.3826834324), -F(1.0000000000), F(0.3826834324),
-F(0.7071067812), -F(0.9238795325), -F(0.0000000000), -F(0.9238795325),
@@ -218,8 +220,8 @@ static const FIXED_T cos_table_fixed_4[32] = {
-F(0.7071067812), -F(0.3826834324), -F(1.0000000000), -F(0.3826834324),
-F(0.7071067812), F(0.9238795325), F(0.0000000000), F(0.9238795325),
- F(0.7071067812), -F(0.9238795325), -F(1.0000000000), -F(0.9238795325),
- F(0.7071067812), -F(0.3826834324), -F(0.0000000000), -F(0.3826834324),
+ F(0.7071067812), -F(0.9238795325), -F(1.0000000000), -F(0.9238795325),
+ F(0.7071067812), -F(0.3826834324), -F(0.0000000000), -F(0.3826834324),
};
#undef F
@@ -232,53 +234,54 @@ static const FIXED_T cos_table_fixed_4[32] = {
* in order to compensate the same change applied to cos_table_fixed_8
*/
#define SBC_PROTO_FIXED8_SCALE \
- ((sizeof(FIXED_T) * CHAR_BIT - 1) - SBC_FIXED_EXTRA_BITS + 2)
-#define F(x) (FIXED_A) ((x * 4) * \
+ ((sizeof(FIXED_T) * CHAR_BIT - 1) - SBC_FIXED_EXTRA_BITS + 1)
+#define F_PROTO8(x) (FIXED_A) ((x * 2) * \
((FIXED_A) 1 << (sizeof(FIXED_T) * CHAR_BIT - 1)) + 0.5)
+#define F(x) F_PROTO8(x)
static const FIXED_T _sbc_proto_fixed8[80] = {
- F(0.00000000E+00), F(1.56575398E-04),
- F(3.43256425E-04), F(5.54620202E-04),
+ F(0.00000000E+00), F(1.56575398E-04),
+ F(3.43256425E-04), F(5.54620202E-04),
-F(8.23919506E-04), F(1.13992507E-03),
- F(1.47640169E-03), F(1.78371725E-03),
- F(2.01182542E-03), F(2.10371989E-03),
- F(1.99454554E-03), F(1.61656283E-03),
- F(9.02154502E-04), F(1.78805361E-04),
- F(1.64973098E-03), F(3.49717454E-03),
-
- F(5.65949473E-03), F(8.02941163E-03),
- F(1.04584443E-02), F(1.27472335E-02),
+ F(1.47640169E-03), F(1.78371725E-03),
+ F(2.01182542E-03), F(2.10371989E-03),
+ F(1.99454554E-03), F(1.61656283E-03),
+ F(9.02154502E-04), F(1.78805361E-04),
+ F(1.64973098E-03), F(3.49717454E-03),
+
+ F(5.65949473E-03), F(8.02941163E-03),
+ F(1.04584443E-02), F(1.27472335E-02),
-F(1.46525263E-02), F(1.59045603E-02),
- F(1.62208471E-02), F(1.53184106E-02),
- F(1.29371806E-02), F(8.85757540E-03),
- F(2.92408442E-03), -F(4.91578024E-03),
+ F(1.62208471E-02), F(1.53184106E-02),
+ F(1.29371806E-02), F(8.85757540E-03),
+ F(2.92408442E-03), -F(4.91578024E-03),
-F(1.46404076E-02), F(2.61098752E-02),
- F(3.90751381E-02), F(5.31873032E-02),
+ F(3.90751381E-02), F(5.31873032E-02),
- F(6.79989431E-02), F(8.29847578E-02),
- F(9.75753918E-02), F(1.11196689E-01),
+ F(6.79989431E-02), F(8.29847578E-02),
+ F(9.75753918E-02), F(1.11196689E-01),
-F(1.23264548E-01), F(1.33264415E-01),
- F(1.40753505E-01), F(1.45389847E-01),
- F(1.46955068E-01), F(1.45389847E-01),
- F(1.40753505E-01), F(1.33264415E-01),
- F(1.23264548E-01), -F(1.11196689E-01),
+ F(1.40753505E-01), F(1.45389847E-01),
+ F(1.46955068E-01), F(1.45389847E-01),
+ F(1.40753505E-01), F(1.33264415E-01),
+ F(1.23264548E-01), -F(1.11196689E-01),
-F(9.75753918E-02), -F(8.29847578E-02),
-F(6.79989431E-02), -F(5.31873032E-02),
-F(3.90751381E-02), -F(2.61098752E-02),
- F(1.46404076E-02), -F(4.91578024E-03),
- F(2.92408442E-03), F(8.85757540E-03),
- F(1.29371806E-02), F(1.53184106E-02),
- F(1.62208471E-02), F(1.59045603E-02),
- F(1.46525263E-02), -F(1.27472335E-02),
+ F(1.46404076E-02), -F(4.91578024E-03),
+ F(2.92408442E-03), F(8.85757540E-03),
+ F(1.29371806E-02), F(1.53184106E-02),
+ F(1.62208471E-02), F(1.59045603E-02),
+ F(1.46525263E-02), -F(1.27472335E-02),
-F(1.04584443E-02), -F(8.02941163E-03),
-F(5.65949473E-03), -F(3.49717454E-03),
-F(1.64973098E-03), -F(1.78805361E-04),
-F(9.02154502E-04), F(1.61656283E-03),
- F(1.99454554E-03), F(2.10371989E-03),
- F(2.01182542E-03), F(1.78371725E-03),
- F(1.47640169E-03), F(1.13992507E-03),
- F(8.23919506E-04), -F(5.54620202E-04),
+ F(1.99454554E-03), F(2.10371989E-03),
+ F(2.01182542E-03), F(1.78371725E-03),
+ F(1.47640169E-03), F(1.13992507E-03),
+ F(8.23919506E-04), -F(5.54620202E-04),
-F(3.43256425E-04), -F(1.56575398E-04),
};
#undef F
@@ -301,13 +304,14 @@ static const FIXED_T _sbc_proto_fixed8[80] = {
*/
#define SBC_COS_TABLE_FIXED8_SCALE \
((sizeof(FIXED_T) * CHAR_BIT - 1) + SBC_FIXED_EXTRA_BITS)
-#define F(x) (FIXED_A) ((x) * \
+#define F_COS8(x) (FIXED_A) ((x) * \
((FIXED_A) 1 << (sizeof(FIXED_T) * CHAR_BIT - 1)) + 0.5)
+#define F(x) F_COS8(x)
static const FIXED_T cos_table_fixed_8[128] = {
- F(0.7071067812), F(0.8314696123), F(0.9238795325), F(0.9807852804),
+ F(0.7071067812), F(0.8314696123), F(0.9238795325), F(0.9807852804),
-F(1.0000000000), F(0.9807852804), F(0.9238795325), F(0.8314696123),
- F(0.7071067812), F(0.5555702330), F(0.3826834324), F(0.1950903220),
- F(0.0000000000), F(0.1950903220), F(0.3826834324), F(0.5555702330),
+ F(0.7071067812), F(0.5555702330), F(0.3826834324), F(0.1950903220),
+ F(0.0000000000), F(0.1950903220), F(0.3826834324), F(0.5555702330),
-F(0.7071067812), -F(0.1950903220), F(0.3826834324), F(0.8314696123),
-F(1.0000000000), F(0.8314696123), F(0.3826834324), -F(0.1950903220),
@@ -317,17 +321,17 @@ static const FIXED_T cos_table_fixed_8[128] = {
-F(0.7071067812), -F(0.9807852804), -F(0.3826834324), F(0.5555702330),
-F(1.0000000000), F(0.5555702330), -F(0.3826834324), -F(0.9807852804),
-F(0.7071067812), F(0.1950903220), F(0.9238795325), F(0.8314696123),
- F(0.0000000000), F(0.8314696123), F(0.9238795325), F(0.1950903220),
+ F(0.0000000000), F(0.8314696123), F(0.9238795325), F(0.1950903220),
- F(0.7071067812), -F(0.5555702330), -F(0.9238795325), F(0.1950903220),
+ F(0.7071067812), -F(0.5555702330), -F(0.9238795325), F(0.1950903220),
-F(1.0000000000), F(0.1950903220), -F(0.9238795325), -F(0.5555702330),
- F(0.7071067812), F(0.8314696123), -F(0.3826834324), -F(0.9807852804),
+ F(0.7071067812), F(0.8314696123), -F(0.3826834324), -F(0.9807852804),
-F(0.0000000000), -F(0.9807852804), -F(0.3826834324), F(0.8314696123),
- F(0.7071067812), F(0.5555702330), -F(0.9238795325), -F(0.1950903220),
+ F(0.7071067812), F(0.5555702330), -F(0.9238795325), -F(0.1950903220),
-F(1.0000000000), -F(0.1950903220), -F(0.9238795325), F(0.5555702330),
- F(0.7071067812), -F(0.8314696123), -F(0.3826834324), F(0.9807852804),
- F(0.0000000000), F(0.9807852804), -F(0.3826834324), -F(0.8314696123),
+ F(0.7071067812), -F(0.8314696123), -F(0.3826834324), F(0.9807852804),
+ F(0.0000000000), F(0.9807852804), -F(0.3826834324), -F(0.8314696123),
-F(0.7071067812), F(0.9807852804), -F(0.3826834324), -F(0.5555702330),
-F(1.0000000000), -F(0.5555702330), -F(0.3826834324), F(0.9807852804),
@@ -339,9 +343,317 @@ static const FIXED_T cos_table_fixed_8[128] = {
-F(0.7071067812), F(0.9807852804), -F(0.9238795325), F(0.5555702330),
-F(0.0000000000), F(0.5555702330), -F(0.9238795325), F(0.9807852804),
- F(0.7071067812), -F(0.8314696123), F(0.9238795325), -F(0.9807852804),
+ F(0.7071067812), -F(0.8314696123), F(0.9238795325), -F(0.9807852804),
-F(1.0000000000), -F(0.9807852804), F(0.9238795325), -F(0.8314696123),
- F(0.7071067812), -F(0.5555702330), F(0.3826834324), -F(0.1950903220),
+ F(0.7071067812), -F(0.5555702330), F(0.3826834324), -F(0.1950903220),
-F(0.0000000000), -F(0.1950903220), F(0.3826834324), -F(0.5555702330),
};
#undef F
+
+/*
+ * Enforce 16 byte alignment for the data, which is supposed to be used
+ * with SIMD optimized code.
+ */
+
+#define SBC_ALIGN_BITS 4
+#define SBC_ALIGN_MASK ((1 << (SBC_ALIGN_BITS)) - 1)
+
+#ifdef __GNUC__
+#define SBC_ALIGNED __attribute__((aligned(1 << (SBC_ALIGN_BITS))))
+#else
+#define SBC_ALIGNED
+#endif
+
+/*
+ * Constant tables for the use in SIMD optimized analysis filters
+ * Each table consists of two parts:
+ * 1. reordered "proto" table
+ * 2. reordered "cos" table
+ *
+ * Due to non-symmetrical reordering, separate tables for "even"
+ * and "odd" cases are needed
+ */
+
+static const FIXED_T SBC_ALIGNED analysis_consts_fixed4_simd_even[40 + 16] = {
+#define C0 1.0932568993
+#define C1 1.3056875580
+#define C2 1.3056875580
+#define C3 1.6772280856
+
+#define F(x) F_PROTO4(x)
+ F(0.00000000E+00 * C0), F(3.83720193E-03 * C0),
+ F(5.36548976E-04 * C1), F(2.73370904E-03 * C1),
+ F(3.06012286E-03 * C2), F(3.89205149E-03 * C2),
+ F(0.00000000E+00 * C3), -F(1.49188357E-03 * C3),
+ F(1.09137620E-02 * C0), F(2.58767811E-02 * C0),
+ F(2.04385087E-02 * C1), F(3.21939290E-02 * C1),
+ F(7.76463494E-02 * C2), F(6.13245186E-03 * C2),
+ F(0.00000000E+00 * C3), -F(2.88757392E-02 * C3),
+ F(1.35593274E-01 * C0), F(2.94315332E-01 * C0),
+ F(1.94987841E-01 * C1), F(2.81828203E-01 * C1),
+ -F(1.94987841E-01 * C2), F(2.81828203E-01 * C2),
+ F(0.00000000E+00 * C3), -F(2.46636662E-01 * C3),
+ -F(1.35593274E-01 * C0), F(2.58767811E-02 * C0),
+ -F(7.76463494E-02 * C1), F(6.13245186E-03 * C1),
+ -F(2.04385087E-02 * C2), F(3.21939290E-02 * C2),
+ F(0.00000000E+00 * C3), F(2.88217274E-02 * C3),
+ -F(1.09137620E-02 * C0), F(3.83720193E-03 * C0),
+ -F(3.06012286E-03 * C1), F(3.89205149E-03 * C1),
+ -F(5.36548976E-04 * C2), F(2.73370904E-03 * C2),
+ F(0.00000000E+00 * C3), -F(1.86581691E-03 * C3),
+#undef F
+#define F(x) F_COS4(x)
+ F(0.7071067812 / C0), F(0.9238795325 / C1),
+ -F(0.7071067812 / C0), F(0.3826834324 / C1),
+ -F(0.7071067812 / C0), -F(0.3826834324 / C1),
+ F(0.7071067812 / C0), -F(0.9238795325 / C1),
+ F(0.3826834324 / C2), -F(1.0000000000 / C3),
+ -F(0.9238795325 / C2), -F(1.0000000000 / C3),
+ F(0.9238795325 / C2), -F(1.0000000000 / C3),
+ -F(0.3826834324 / C2), -F(1.0000000000 / C3),
+#undef F
+
+#undef C0
+#undef C1
+#undef C2
+#undef C3
+};
+
+static const FIXED_T SBC_ALIGNED analysis_consts_fixed4_simd_odd[40 + 16] = {
+#define C0 1.3056875580
+#define C1 1.6772280856
+#define C2 1.0932568993
+#define C3 1.3056875580
+
+#define F(x) F_PROTO4(x)
+ F(2.73370904E-03 * C0), F(5.36548976E-04 * C0),
+ -F(1.49188357E-03 * C1), F(0.00000000E+00 * C1),
+ F(3.83720193E-03 * C2), F(1.09137620E-02 * C2),
+ F(3.89205149E-03 * C3), F(3.06012286E-03 * C3),
+ F(3.21939290E-02 * C0), F(2.04385087E-02 * C0),
+ -F(2.88757392E-02 * C1), F(0.00000000E+00 * C1),
+ F(2.58767811E-02 * C2), F(1.35593274E-01 * C2),
+ F(6.13245186E-03 * C3), F(7.76463494E-02 * C3),
+ F(2.81828203E-01 * C0), F(1.94987841E-01 * C0),
+ -F(2.46636662E-01 * C1), F(0.00000000E+00 * C1),
+ F(2.94315332E-01 * C2), -F(1.35593274E-01 * C2),
+ F(2.81828203E-01 * C3), -F(1.94987841E-01 * C3),
+ F(6.13245186E-03 * C0), -F(7.76463494E-02 * C0),
+ F(2.88217274E-02 * C1), F(0.00000000E+00 * C1),
+ F(2.58767811E-02 * C2), -F(1.09137620E-02 * C2),
+ F(3.21939290E-02 * C3), -F(2.04385087E-02 * C3),
+ F(3.89205149E-03 * C0), -F(3.06012286E-03 * C0),
+ -F(1.86581691E-03 * C1), F(0.00000000E+00 * C1),
+ F(3.83720193E-03 * C2), F(0.00000000E+00 * C2),
+ F(2.73370904E-03 * C3), -F(5.36548976E-04 * C3),
+#undef F
+#define F(x) F_COS4(x)
+ F(0.9238795325 / C0), -F(1.0000000000 / C1),
+ F(0.3826834324 / C0), -F(1.0000000000 / C1),
+ -F(0.3826834324 / C0), -F(1.0000000000 / C1),
+ -F(0.9238795325 / C0), -F(1.0000000000 / C1),
+ F(0.7071067812 / C2), F(0.3826834324 / C3),
+ -F(0.7071067812 / C2), -F(0.9238795325 / C3),
+ -F(0.7071067812 / C2), F(0.9238795325 / C3),
+ F(0.7071067812 / C2), -F(0.3826834324 / C3),
+#undef F
+
+#undef C0
+#undef C1
+#undef C2
+#undef C3
+};
+
+static const FIXED_T SBC_ALIGNED analysis_consts_fixed8_simd_even[80 + 64] = {
+#define C0 2.7906148894
+#define C1 2.4270044280
+#define C2 2.8015616024
+#define C3 3.1710363741
+#define C4 2.5377944043
+#define C5 2.4270044280
+#define C6 2.8015616024
+#define C7 3.1710363741
+
+#define F(x) F_PROTO8(x)
+ F(0.00000000E+00 * C0), F(2.01182542E-03 * C0),
+ F(1.56575398E-04 * C1), F(1.78371725E-03 * C1),
+ F(3.43256425E-04 * C2), F(1.47640169E-03 * C2),
+ F(5.54620202E-04 * C3), F(1.13992507E-03 * C3),
+ -F(8.23919506E-04 * C4), F(0.00000000E+00 * C4),
+ F(2.10371989E-03 * C5), F(3.49717454E-03 * C5),
+ F(1.99454554E-03 * C6), F(1.64973098E-03 * C6),
+ F(1.61656283E-03 * C7), F(1.78805361E-04 * C7),
+ F(5.65949473E-03 * C0), F(1.29371806E-02 * C0),
+ F(8.02941163E-03 * C1), F(1.53184106E-02 * C1),
+ F(1.04584443E-02 * C2), F(1.62208471E-02 * C2),
+ F(1.27472335E-02 * C3), F(1.59045603E-02 * C3),
+ -F(1.46525263E-02 * C4), F(0.00000000E+00 * C4),
+ F(8.85757540E-03 * C5), F(5.31873032E-02 * C5),
+ F(2.92408442E-03 * C6), F(3.90751381E-02 * C6),
+ -F(4.91578024E-03 * C7), F(2.61098752E-02 * C7),
+ F(6.79989431E-02 * C0), F(1.46955068E-01 * C0),
+ F(8.29847578E-02 * C1), F(1.45389847E-01 * C1),
+ F(9.75753918E-02 * C2), F(1.40753505E-01 * C2),
+ F(1.11196689E-01 * C3), F(1.33264415E-01 * C3),
+ -F(1.23264548E-01 * C4), F(0.00000000E+00 * C4),
+ F(1.45389847E-01 * C5), -F(8.29847578E-02 * C5),
+ F(1.40753505E-01 * C6), -F(9.75753918E-02 * C6),
+ F(1.33264415E-01 * C7), -F(1.11196689E-01 * C7),
+ -F(6.79989431E-02 * C0), F(1.29371806E-02 * C0),
+ -F(5.31873032E-02 * C1), F(8.85757540E-03 * C1),
+ -F(3.90751381E-02 * C2), F(2.92408442E-03 * C2),
+ -F(2.61098752E-02 * C3), -F(4.91578024E-03 * C3),
+ F(1.46404076E-02 * C4), F(0.00000000E+00 * C4),
+ F(1.53184106E-02 * C5), -F(8.02941163E-03 * C5),
+ F(1.62208471E-02 * C6), -F(1.04584443E-02 * C6),
+ F(1.59045603E-02 * C7), -F(1.27472335E-02 * C7),
+ -F(5.65949473E-03 * C0), F(2.01182542E-03 * C0),
+ -F(3.49717454E-03 * C1), F(2.10371989E-03 * C1),
+ -F(1.64973098E-03 * C2), F(1.99454554E-03 * C2),
+ -F(1.78805361E-04 * C3), F(1.61656283E-03 * C3),
+ -F(9.02154502E-04 * C4), F(0.00000000E+00 * C4),
+ F(1.78371725E-03 * C5), -F(1.56575398E-04 * C5),
+ F(1.47640169E-03 * C6), -F(3.43256425E-04 * C6),
+ F(1.13992507E-03 * C7), -F(5.54620202E-04 * C7),
+#undef F
+#define F(x) F_COS8(x)
+ F(0.7071067812 / C0), F(0.8314696123 / C1),
+ -F(0.7071067812 / C0), -F(0.1950903220 / C1),
+ -F(0.7071067812 / C0), -F(0.9807852804 / C1),
+ F(0.7071067812 / C0), -F(0.5555702330 / C1),
+ F(0.7071067812 / C0), F(0.5555702330 / C1),
+ -F(0.7071067812 / C0), F(0.9807852804 / C1),
+ -F(0.7071067812 / C0), F(0.1950903220 / C1),
+ F(0.7071067812 / C0), -F(0.8314696123 / C1),
+ F(0.9238795325 / C2), F(0.9807852804 / C3),
+ F(0.3826834324 / C2), F(0.8314696123 / C3),
+ -F(0.3826834324 / C2), F(0.5555702330 / C3),
+ -F(0.9238795325 / C2), F(0.1950903220 / C3),
+ -F(0.9238795325 / C2), -F(0.1950903220 / C3),
+ -F(0.3826834324 / C2), -F(0.5555702330 / C3),
+ F(0.3826834324 / C2), -F(0.8314696123 / C3),
+ F(0.9238795325 / C2), -F(0.9807852804 / C3),
+ -F(1.0000000000 / C4), F(0.5555702330 / C5),
+ -F(1.0000000000 / C4), -F(0.9807852804 / C5),
+ -F(1.0000000000 / C4), F(0.1950903220 / C5),
+ -F(1.0000000000 / C4), F(0.8314696123 / C5),
+ -F(1.0000000000 / C4), -F(0.8314696123 / C5),
+ -F(1.0000000000 / C4), -F(0.1950903220 / C5),
+ -F(1.0000000000 / C4), F(0.9807852804 / C5),
+ -F(1.0000000000 / C4), -F(0.5555702330 / C5),
+ F(0.3826834324 / C6), F(0.1950903220 / C7),
+ -F(0.9238795325 / C6), -F(0.5555702330 / C7),
+ F(0.9238795325 / C6), F(0.8314696123 / C7),
+ -F(0.3826834324 / C6), -F(0.9807852804 / C7),
+ -F(0.3826834324 / C6), F(0.9807852804 / C7),
+ F(0.9238795325 / C6), -F(0.8314696123 / C7),
+ -F(0.9238795325 / C6), F(0.5555702330 / C7),
+ F(0.3826834324 / C6), -F(0.1950903220 / C7),
+#undef F
+
+#undef C0
+#undef C1
+#undef C2
+#undef C3
+#undef C4
+#undef C5
+#undef C6
+#undef C7
+};
+
+static const FIXED_T SBC_ALIGNED analysis_consts_fixed8_simd_odd[80 + 64] = {
+#define C0 2.5377944043
+#define C1 2.4270044280
+#define C2 2.8015616024
+#define C3 3.1710363741
+#define C4 2.7906148894
+#define C5 2.4270044280
+#define C6 2.8015616024
+#define C7 3.1710363741
+
+#define F(x) F_PROTO8(x)
+ F(0.00000000E+00 * C0), -F(8.23919506E-04 * C0),
+ F(1.56575398E-04 * C1), F(1.78371725E-03 * C1),
+ F(3.43256425E-04 * C2), F(1.47640169E-03 * C2),
+ F(5.54620202E-04 * C3), F(1.13992507E-03 * C3),
+ F(2.01182542E-03 * C4), F(5.65949473E-03 * C4),
+ F(2.10371989E-03 * C5), F(3.49717454E-03 * C5),
+ F(1.99454554E-03 * C6), F(1.64973098E-03 * C6),
+ F(1.61656283E-03 * C7), F(1.78805361E-04 * C7),
+ F(0.00000000E+00 * C0), -F(1.46525263E-02 * C0),
+ F(8.02941163E-03 * C1), F(1.53184106E-02 * C1),
+ F(1.04584443E-02 * C2), F(1.62208471E-02 * C2),
+ F(1.27472335E-02 * C3), F(1.59045603E-02 * C3),
+ F(1.29371806E-02 * C4), F(6.79989431E-02 * C4),
+ F(8.85757540E-03 * C5), F(5.31873032E-02 * C5),
+ F(2.92408442E-03 * C6), F(3.90751381E-02 * C6),
+ -F(4.91578024E-03 * C7), F(2.61098752E-02 * C7),
+ F(0.00000000E+00 * C0), -F(1.23264548E-01 * C0),
+ F(8.29847578E-02 * C1), F(1.45389847E-01 * C1),
+ F(9.75753918E-02 * C2), F(1.40753505E-01 * C2),
+ F(1.11196689E-01 * C3), F(1.33264415E-01 * C3),
+ F(1.46955068E-01 * C4), -F(6.79989431E-02 * C4),
+ F(1.45389847E-01 * C5), -F(8.29847578E-02 * C5),
+ F(1.40753505E-01 * C6), -F(9.75753918E-02 * C6),
+ F(1.33264415E-01 * C7), -F(1.11196689E-01 * C7),
+ F(0.00000000E+00 * C0), F(1.46404076E-02 * C0),
+ -F(5.31873032E-02 * C1), F(8.85757540E-03 * C1),
+ -F(3.90751381E-02 * C2), F(2.92408442E-03 * C2),
+ -F(2.61098752E-02 * C3), -F(4.91578024E-03 * C3),
+ F(1.29371806E-02 * C4), -F(5.65949473E-03 * C4),
+ F(1.53184106E-02 * C5), -F(8.02941163E-03 * C5),
+ F(1.62208471E-02 * C6), -F(1.04584443E-02 * C6),
+ F(1.59045603E-02 * C7), -F(1.27472335E-02 * C7),
+ F(0.00000000E+00 * C0), -F(9.02154502E-04 * C0),
+ -F(3.49717454E-03 * C1), F(2.10371989E-03 * C1),
+ -F(1.64973098E-03 * C2), F(1.99454554E-03 * C2),
+ -F(1.78805361E-04 * C3), F(1.61656283E-03 * C3),
+ F(2.01182542E-03 * C4), F(0.00000000E+00 * C4),
+ F(1.78371725E-03 * C5), -F(1.56575398E-04 * C5),
+ F(1.47640169E-03 * C6), -F(3.43256425E-04 * C6),
+ F(1.13992507E-03 * C7), -F(5.54620202E-04 * C7),
+#undef F
+#define F(x) F_COS8(x)
+ -F(1.0000000000 / C0), F(0.8314696123 / C1),
+ -F(1.0000000000 / C0), -F(0.1950903220 / C1),
+ -F(1.0000000000 / C0), -F(0.9807852804 / C1),
+ -F(1.0000000000 / C0), -F(0.5555702330 / C1),
+ -F(1.0000000000 / C0), F(0.5555702330 / C1),
+ -F(1.0000000000 / C0), F(0.9807852804 / C1),
+ -F(1.0000000000 / C0), F(0.1950903220 / C1),
+ -F(1.0000000000 / C0), -F(0.8314696123 / C1),
+ F(0.9238795325 / C2), F(0.9807852804 / C3),
+ F(0.3826834324 / C2), F(0.8314696123 / C3),
+ -F(0.3826834324 / C2), F(0.5555702330 / C3),
+ -F(0.9238795325 / C2), F(0.1950903220 / C3),
+ -F(0.9238795325 / C2), -F(0.1950903220 / C3),
+ -F(0.3826834324 / C2), -F(0.5555702330 / C3),
+ F(0.3826834324 / C2), -F(0.8314696123 / C3),
+ F(0.9238795325 / C2), -F(0.9807852804 / C3),
+ F(0.7071067812 / C4), F(0.5555702330 / C5),
+ -F(0.7071067812 / C4), -F(0.9807852804 / C5),
+ -F(0.7071067812 / C4), F(0.1950903220 / C5),
+ F(0.7071067812 / C4), F(0.8314696123 / C5),
+ F(0.7071067812 / C4), -F(0.8314696123 / C5),
+ -F(0.7071067812 / C4), -F(0.1950903220 / C5),
+ -F(0.7071067812 / C4), F(0.9807852804 / C5),
+ F(0.7071067812 / C4), -F(0.5555702330 / C5),
+ F(0.3826834324 / C6), F(0.1950903220 / C7),
+ -F(0.9238795325 / C6), -F(0.5555702330 / C7),
+ F(0.9238795325 / C6), F(0.8314696123 / C7),
+ -F(0.3826834324 / C6), -F(0.9807852804 / C7),
+ -F(0.3826834324 / C6), F(0.9807852804 / C7),
+ F(0.9238795325 / C6), -F(0.8314696123 / C7),
+ -F(0.9238795325 / C6), F(0.5555702330 / C7),
+ F(0.3826834324 / C6), -F(0.1950903220 / C7),
+#undef F
+
+#undef C0
+#undef C1
+#undef C2
+#undef C3
+#undef C4
+#undef C5
+#undef C6
+#undef C7
+};
diff --git a/src/modules/module-card-restore.c b/src/modules/module-card-restore.c
index 02e973c4..4dffd365 100644
--- a/src/modules/module-card-restore.c
+++ b/src/modules/module-card-restore.c
@@ -68,7 +68,10 @@ struct userdata {
GDBM_FILE gdbm_file;
};
-struct entry {
+#define ENTRY_VERSION 1
+
+struct entry PA_GCC_PACKED {
+ uint8_t version;
char profile[PA_NAME_MAX];
};
@@ -104,12 +107,17 @@ static struct entry* read_entry(struct userdata *u, const char *name) {
goto fail;
if (data.dsize != sizeof(struct entry)) {
- pa_log_warn("Database contains entry for card %s of wrong size %lu != %lu", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry));
+ pa_log_debug("Database contains entry for card %s of wrong size %lu != %lu. Probably due to upgrade, ignoring.", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry));
goto fail;
}
e = (struct entry*) data.dptr;
+ if (e->version != ENTRY_VERSION) {
+ pa_log_debug("Version of database entry for card %s doesn't match our version. Probably due to upgrade, ignoring.", name);
+ goto fail;
+ }
+
if (!memchr(e->profile, 0, sizeof(e->profile))) {
pa_log_warn("Database contains entry for card %s with missing NUL byte in profile name", name);
goto fail;
@@ -148,6 +156,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
return;
memset(&entry, 0, sizeof(entry));
+ entry.version = ENTRY_VERSION;
if (!(card = pa_idxset_get_by_index(c->cards, idx)))
return;
diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c
index 82c88711..7f1ef24c 100644
--- a/src/modules/module-combine.c
+++ b/src/modules/module-combine.c
@@ -799,7 +799,7 @@ static int output_create_sink_input(struct output *o) {
data.module = o->userdata->module;
data.resample_method = o->userdata->resample_method;
- o->sink_input = pa_sink_input_new(o->userdata->core, &data, PA_SINK_INPUT_VARIABLE_RATE|PA_SINK_INPUT_DONT_MOVE);
+ pa_sink_input_new(&o->sink_input, o->userdata->core, &data, PA_SINK_INPUT_VARIABLE_RATE|PA_SINK_INPUT_DONT_MOVE);
pa_sink_input_new_data_done(&data);
diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c
index 8e0cf92b..7c56c240 100644
--- a/src/modules/module-device-restore.c
+++ b/src/modules/module-device-restore.c
@@ -79,10 +79,13 @@ struct userdata {
pa_bool_t restore_muted:1;
};
-struct entry {
+#define ENTRY_VERSION 1
+
+struct entry PA_GCC_PACKED {
+ uint8_t version;
+ pa_bool_t muted:1;
pa_channel_map channel_map;
pa_cvolume volume;
- pa_bool_t muted:1;
};
static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {
@@ -117,12 +120,17 @@ static struct entry* read_entry(struct userdata *u, const char *name) {
goto fail;
if (data.dsize != sizeof(struct entry)) {
- pa_log_warn("Database contains entry for device %s of wrong size %lu != %lu", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry));
+ pa_log_debug("Database contains entry for device %s of wrong size %lu != %lu. Probably due to upgrade, ignoring.", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry));
goto fail;
}
e = (struct entry*) data.dptr;
+ if (e->version != ENTRY_VERSION) {
+ pa_log_debug("Version of database entry for device %s doesn't match our version. Probably due to upgrade, ignoring.", name);
+ goto fail;
+ }
+
if (!(pa_cvolume_valid(&e->volume))) {
pa_log_warn("Invalid volume stored in database for device %s", name);
goto fail;
@@ -173,6 +181,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
return;
memset(&entry, 0, sizeof(entry));
+ entry.version = ENTRY_VERSION;
if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
pa_sink *sink;
diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c
index e746f342..a4007b1a 100644
--- a/src/modules/module-ladspa-sink.c
+++ b/src/modules/module-ladspa-sink.c
@@ -732,7 +732,7 @@ int pa__init(pa_module*m) {
pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss);
pa_sink_input_new_data_set_channel_map(&sink_input_data, &map);
- u->sink_input = pa_sink_input_new(m->core, &sink_input_data, PA_SINK_INPUT_DONT_MOVE);
+ pa_sink_input_new(&u->sink_input, m->core, &sink_input_data, PA_SINK_INPUT_DONT_MOVE);
pa_sink_input_new_data_done(&sink_input_data);
if (!u->sink_input)
diff --git a/src/modules/module-pipe-sink.c b/src/modules/module-pipe-sink.c
index 03e27170..7dd44098 100644
--- a/src/modules/module-pipe-sink.c
+++ b/src/modules/module-pipe-sink.c
@@ -138,7 +138,9 @@ static int process_render(struct userdata *u) {
if (errno == EINTR)
continue;
- else if (errno != EAGAIN) {
+ else if (errno == EAGAIN)
+ return 0;
+ else {
pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno));
return -1;
}
diff --git a/src/modules/module-position-event-sounds.c b/src/modules/module-position-event-sounds.c
index 8e4f4c32..e17cbe80 100644
--- a/src/modules/module-position-event-sounds.c
+++ b/src/modules/module-position-event-sounds.c
@@ -80,16 +80,16 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *core, pa_sink_i
pa_log_debug("Positioning event sound '%s' at %0.2f.", pa_strnull(pa_proplist_gets(data->proplist, PA_PROP_EVENT_ID)), f);
- if (!data->virtual_volume_is_set) {
- pa_cvolume_reset(&data->virtual_volume, data->sample_spec.channels);
- data->virtual_volume_is_set = TRUE;
- data->virtual_volume_is_absolute = FALSE;
+ if (!data->volume_is_set) {
+ pa_cvolume_reset(&data->volume, data->sample_spec.channels);
+ data->volume_is_set = TRUE;
+ data->volume_is_absolute = FALSE;
}
- pa_cvolume_set_balance(&data->virtual_volume, &data->channel_map, f*2.0-1.0);
+ pa_cvolume_set_balance(&data->volume, &data->channel_map, f*2.0-1.0);
data->save_volume = FALSE;
- pa_log_debug("Final volume %s.", pa_cvolume_snprint(t, sizeof(t), &data->virtual_volume));
+ pa_log_debug("Final volume %s.", pa_cvolume_snprint(t, sizeof(t), &data->volume));
return PA_HOOK_OK;
}
diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c
index 74ee6122..1784b2cc 100644
--- a/src/modules/module-raop-sink.c
+++ b/src/modules/module-raop-sink.c
@@ -100,9 +100,6 @@ struct userdata {
pa_usec_t latency;
- pa_volume_t volume;
- pa_bool_t muted;
-
/*esd_format_t format;*/
int32_t rate;
@@ -133,6 +130,9 @@ enum {
SINK_MESSAGE_RIP_SOCKET
};
+/* Forward declaration */
+static void sink_set_volume_cb(pa_sink *);
+
static void on_connection(PA_GCC_UNUSED int fd, void*userdata) {
struct userdata *u = userdata;
pa_assert(u);
@@ -141,7 +141,7 @@ static void on_connection(PA_GCC_UNUSED int fd, void*userdata) {
u->fd = fd;
/* Set the initial volume */
- pa_raop_client_set_volume(u->raop, u->volume);
+ sink_set_volume_cb(u->sink);
pa_log_debug("Connection authenticated, handing fd to IO thread...");
@@ -255,43 +255,36 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
return pa_sink_process_msg(o, code, data, offset, chunk);
}
-static void sink_get_volume_cb(pa_sink *s) {
- struct userdata *u = s->userdata;
- int i;
-
- pa_assert(u);
-
- for (i = 0; i < s->sample_spec.channels; i++)
- s->virtual_volume.values[i] = u->volume;
-}
-
static void sink_set_volume_cb(pa_sink *s) {
struct userdata *u = s->userdata;
- int rv;
+ pa_cvolume hw;
+ pa_volume_t v;
+ char t[PA_CVOLUME_SNPRINT_MAX];
pa_assert(u);
- /* If we're muted, we fake it */
- if (u->muted)
+ /* If we're muted we don't need to do anything */
+ if (s->muted)
return;
- pa_assert(s->sample_spec.channels > 0);
+ /* Calculate the max volume of all channels.
+ We'll use this as our (single) volume on the APEX device and emulate
+ any variation in channel volumes in software */
+ v = pa_cvolume_max(&s->virtual_volume);
- /* Avoid pointless volume sets */
- if (u->volume == s->virtual_volume.values[0])
- return;
+ /* Create a pa_cvolume version of our single value */
+ pa_cvolume_set(&hw, s->sample_spec.channels, v);
- rv = pa_raop_client_set_volume(u->raop, s->virtual_volume.values[0]);
- if (0 == rv)
- u->volume = s->virtual_volume.values[0];
-}
+ /* Perform any software manipulation of the volume needed */
+ pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &hw);
-static void sink_get_mute_cb(pa_sink *s) {
- struct userdata *u = s->userdata;
+ pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->virtual_volume));
+ pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &hw));
+ pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->soft_volume));
- pa_assert(u);
-
- s->muted = u->muted;
+ /* Any necessary software volume manipulateion is done so set
+ our hw volume (or v as a single value) on the device */
+ pa_raop_client_set_volume(u->raop, v);
}
static void sink_set_mute_cb(pa_sink *s) {
@@ -299,8 +292,11 @@ static void sink_set_mute_cb(pa_sink *s) {
pa_assert(u);
- pa_raop_client_set_volume(u->raop, (s->muted ? PA_VOLUME_MUTED : u->volume));
- u->muted = s->muted;
+ if (s->muted) {
+ pa_raop_client_set_volume(u->raop, PA_VOLUME_MUTED);
+ } else {
+ sink_set_volume_cb(s);
+ }
}
static void thread_func(void *userdata) {
@@ -541,9 +537,6 @@ int pa__init(pa_module*m) {
u->next_encoding_overhead = 0;
u->encoding_ratio = 1.0;
- u->volume = roundf(0.7 * PA_VOLUME_NORM);
- u->muted = FALSE;
-
u->rtpoll = pa_rtpoll_new();
pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
u->rtpoll_item = NULL;
@@ -583,9 +576,7 @@ int pa__init(pa_module*m) {
u->sink->parent.process_msg = sink_process_msg;
u->sink->userdata = u;
- u->sink->get_volume = sink_get_volume_cb;
u->sink->set_volume = sink_set_volume_cb;
- u->sink->get_mute = sink_get_mute_cb;
u->sink->set_mute = sink_set_mute_cb;
u->sink->flags = PA_SINK_LATENCY|PA_SINK_NETWORK|PA_SINK_HW_VOLUME_CTRL;
diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c
index e17fef03..89ddf953 100644
--- a/src/modules/module-remap-sink.c
+++ b/src/modules/module-remap-sink.c
@@ -381,7 +381,7 @@ int pa__init(pa_module*m) {
pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss);
pa_sink_input_new_data_set_channel_map(&sink_input_data, &stream_map);
- u->sink_input = pa_sink_input_new(m->core, &sink_input_data, PA_SINK_INPUT_DONT_MOVE | (remix ? 0 : PA_SINK_INPUT_NO_REMIX));
+ pa_sink_input_new(&u->sink_input, m->core, &sink_input_data, PA_SINK_INPUT_DONT_MOVE | (remix ? 0 : PA_SINK_INPUT_NO_REMIX));
pa_sink_input_new_data_done(&sink_input_data);
if (!u->sink_input)
diff --git a/src/modules/module-sine.c b/src/modules/module-sine.c
index b0782c3c..ce08c01d 100644
--- a/src/modules/module-sine.c
+++ b/src/modules/module-sine.c
@@ -163,7 +163,7 @@ int pa__init(pa_module*m) {
pa_proplist_setf(data.proplist, "sine.hz", "%u", frequency);
pa_sink_input_new_data_set_sample_spec(&data, &ss);
- u->sink_input = pa_sink_input_new(m->core, &data, 0);
+ pa_sink_input_new(&u->sink_input, m->core, &data, 0);
pa_sink_input_new_data_done(&data);
if (!u->sink_input)
diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c
index 0c9bd4f9..b1630fe1 100644
--- a/src/modules/module-stream-restore.c
+++ b/src/modules/module-stream-restore.c
@@ -90,16 +90,18 @@ struct userdata {
pa_idxset *subscribed;
};
-struct entry {
+#define ENTRY_VERSION 1
+
+struct entry PA_GCC_PACKED {
+ uint8_t version;
+ pa_bool_t muted_valid:1, relative_volume_valid:1, absolute_volume_valid:1, device_valid:1;
+ pa_bool_t muted:1;
pa_channel_map channel_map;
- char device[PA_NAME_MAX];
pa_cvolume relative_volume;
pa_cvolume absolute_volume;
- pa_bool_t muted:1;
- pa_bool_t device_valid:1, relative_volume_valid:1, absolute_volume_valid:1, muted_valid:1;
+ char device[PA_NAME_MAX];
};
-
enum {
SUBCOMMAND_TEST,
SUBCOMMAND_READ,
@@ -161,19 +163,19 @@ static struct entry* read_entry(struct userdata *u, const char *name) {
if (data.dsize != sizeof(struct entry)) {
/* This is probably just a database upgrade, hence let's not
* consider this more than a debug message */
- pa_log_debug("Database contains entry for stream %s of wrong size %lu != %lu", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry));
+ pa_log_debug("Database contains entry for stream %s of wrong size %lu != %lu. Probably due to uprade, ignoring.", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry));
goto fail;
}
e = (struct entry*) data.dptr;
- if (!memchr(e->device, 0, sizeof(e->device))) {
- pa_log_warn("Database contains entry for stream %s with missing NUL byte in device name", name);
+ if (e->version != ENTRY_VERSION) {
+ pa_log_debug("Version of database entry for stream %s doesn't match our version. Probably due to upgrade, ignoring.", name);
goto fail;
}
- if (!(pa_channel_map_valid(&e->channel_map))) {
- pa_log_warn("Invalid channel map stored in database for stream %s", name);
+ if (!memchr(e->device, 0, sizeof(e->device))) {
+ pa_log_warn("Database contains entry for stream %s with missing NUL byte in device name", name);
goto fail;
}
@@ -182,6 +184,11 @@ static struct entry* read_entry(struct userdata *u, const char *name) {
goto fail;
}
+ if ((e->relative_volume_valid || e->absolute_volume_valid) && !(pa_channel_map_valid(&e->channel_map))) {
+ pa_log_warn("Invalid channel map stored in database for stream %s", name);
+ goto fail;
+ }
+
if ((e->relative_volume_valid && (!pa_cvolume_valid(&e->relative_volume) || e->relative_volume.channels != e->channel_map.channels)) ||
(e->absolute_volume_valid && (!pa_cvolume_valid(&e->absolute_volume) || e->absolute_volume.channels != e->channel_map.channels))) {
pa_log_warn("Invalid volume stored in database for stream %s", name);
@@ -233,7 +240,7 @@ static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
return FALSE;
if (a->muted_valid != b->muted_valid ||
- (a->muted && (a->muted != b->muted)))
+ (a->muted_valid && (a->muted != b->muted)))
return FALSE;
t = b->relative_volume;
@@ -265,6 +272,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
return;
memset(&entry, 0, sizeof(entry));
+ entry.version = ENTRY_VERSION;
if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
pa_sink_input *sink_input;
@@ -388,32 +396,33 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu
if (u->restore_volume) {
- if (!new_data->virtual_volume_is_set) {
+ if (!new_data->volume_is_set) {
pa_cvolume v;
pa_cvolume_init(&v);
if (new_data->sink->flags & PA_SINK_FLAT_VOLUME) {
+ /* We don't check for e->device_valid here because
+ that bit marks whether it is a good choice for
+ restoring, not just if the data is filled in. */
if (e->absolute_volume_valid &&
- e->device_valid &&
- pa_streq(new_data->sink->name, e->device)) {
+ (e->device[0] == 0 || pa_streq(new_data->sink->name, e->device))) {
v = e->absolute_volume;
- new_data->virtual_volume_is_absolute = TRUE;
+ new_data->volume_is_absolute = TRUE;
} else if (e->relative_volume_valid) {
-
v = e->relative_volume;
- new_data->virtual_volume_is_absolute = FALSE;
+ new_data->volume_is_absolute = FALSE;
}
} else if (e->relative_volume_valid) {
v = e->relative_volume;
- new_data->virtual_volume_is_absolute = FALSE;
+ new_data->volume_is_absolute = FALSE;
}
if (v.channels > 0) {
pa_log_info("Restoring volume for sink input %s.", name);
- pa_sink_input_new_data_set_virtual_volume(new_data, pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map));
+ pa_sink_input_new_data_set_volume(new_data, pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map));
new_data->save_volume = TRUE;
}
} else
@@ -523,11 +532,10 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
if (si->sink->flags & PA_SINK_FLAT_VOLUME) {
if (e->absolute_volume_valid &&
- e->device_valid &&
- pa_streq(e->device, si->sink->name))
+ (e->device[0] == 0 || pa_streq(e->device, si->sink->name)))
v = e->absolute_volume;
else if (e->relative_volume_valid) {
- pa_cvolume t = si->sink->virtual_volume;
+ pa_cvolume t = *pa_sink_get_volume(si->sink, FALSE);
pa_sw_cvolume_multiply(&v, &e->relative_volume, pa_cvolume_remap(&t, &si->sink->channel_map, &e->channel_map));
}
} else if (e->relative_volume_valid)
@@ -655,10 +663,11 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
if ((e = read_entry(u, name))) {
pa_cvolume r;
+ pa_channel_map cm;
pa_tagstruct_puts(reply, name);
- pa_tagstruct_put_channel_map(reply, &e->channel_map);
- pa_tagstruct_put_cvolume(reply, e->relative_volume_valid ? &e->relative_volume : pa_cvolume_init(&r));
+ pa_tagstruct_put_channel_map(reply, (e->relative_volume_valid || e->absolute_volume_valid) ? &e->channel_map : pa_channel_map_init(&cm));
+ pa_tagstruct_put_cvolume(reply, e->absolute_volume_valid ? &e->absolute_volume : (e->relative_volume_valid ? &e->relative_volume : pa_cvolume_init(&r)));
pa_tagstruct_puts(reply, e->device_valid ? e->device : NULL);
pa_tagstruct_put_boolean(reply, e->muted_valid ? e->muted : FALSE);
@@ -697,21 +706,25 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
int k;
memset(&entry, 0, sizeof(entry));
+ entry.version = ENTRY_VERSION;
if (pa_tagstruct_gets(t, &name) < 0 ||
pa_tagstruct_get_channel_map(t, &entry.channel_map) ||
- pa_tagstruct_get_cvolume(t, &entry.relative_volume) < 0 ||
+ pa_tagstruct_get_cvolume(t, &entry.absolute_volume) < 0 ||
pa_tagstruct_gets(t, &device) < 0 ||
pa_tagstruct_get_boolean(t, &muted) < 0)
goto fail;
- entry.absolute_volume_valid = FALSE;
- entry.relative_volume_valid = entry.relative_volume.channels > 0;
-
- if (entry.relative_volume_valid &&
- entry.channel_map.channels != entry.relative_volume.channels)
+ if (!name || !*name)
goto fail;
+ entry.relative_volume = entry.absolute_volume;
+ entry.absolute_volume_valid = entry.relative_volume_valid = entry.relative_volume.channels > 0;
+
+ if (entry.relative_volume_valid)
+ if (!pa_cvolume_compatible_with_channel_map(&entry.relative_volume, &entry.channel_map))
+ goto fail;
+
entry.muted = muted;
entry.muted_valid = TRUE;
diff --git a/src/modules/rtp/module-rtp-recv.c b/src/modules/rtp/module-rtp-recv.c
index 00d21255..baf3532f 100644
--- a/src/modules/rtp/module-rtp-recv.c
+++ b/src/modules/rtp/module-rtp-recv.c
@@ -453,7 +453,7 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in
data.module = u->module;
pa_sink_input_new_data_set_sample_spec(&data, &sdp_info->sample_spec);
- s->sink_input = pa_sink_input_new(u->module->core, &data, 0);
+ pa_sink_input_new(&s->sink_input, u->module->core, &data, 0);
pa_sink_input_new_data_done(&data);
if (!s->sink_input) {
diff --git a/src/modules/rtp/module-rtp-send.c b/src/modules/rtp/module-rtp-send.c
index a6d682bb..762cdc1e 100644
--- a/src/modules/rtp/module-rtp-send.c
+++ b/src/modules/rtp/module-rtp-send.c
@@ -321,7 +321,7 @@ int pa__init(pa_module*m) {
pa_source_output_new_data_set_sample_spec(&data, &ss);
pa_source_output_new_data_set_channel_map(&data, &cm);
- o = pa_source_output_new(m->core, &data, PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND);
+ pa_source_output_new(&o, m->core, &data, PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND);
pa_source_output_new_data_done(&data);
if (!o) {