[pulseaudio-commits] [SCM] PulseAudio Sound Server branch, master-tx, updated. v0.9.13-475-g2739261

Lennart Poettering gitmailer-noreply at 0pointer.de
Mon Feb 2 18:16:12 PST 2009


This is an automated email from the git hooks/post-receive script. It was
generated because of a push to the "PulseAudio Sound Server" repository.

The master-tx branch has been updated
      from  db2a4255aaf985128b3dccb84dd4e4f1be22a23f (commit)

- Log -----------------------------------------------------------------
2739261... Merge commit 'origin/master-tx'
08800c3... make a couple of functions return proper error codes
162e43b... make a few functions return void where the retval isn't used/never != 0
50bfa77... add new error code PA_ERR_NOTIMPLEMENTED
e47d03d... implement PA_STREAM_FAIL_ON_SUSPEND logic
c61ad2a... make iterating with pa_idxset_next() robust in regards to idxset modifications
f8190be... make update-sbc should also update other BT related sources
59f3001... pull in new SBC/BT files
fea6757... don't use PA_STREAM_NOT_MONOTONOUS anymore
390133f... big module-bluetooth-device.c rework
62f1f3e... make rtp.h ANSI C compliant
e412f1c... whitespace cleanup
121a8b9... handle EAGAIN properly
2854afb... fix soft_mute handling
a41d72b... update sbc stuff
537424a... reset rewind_requested when we enter suspend mode
a9e9ab3... shortcut pa_sink_process_rewind() when no rewind is happenning and none was requested
5fc11a0... Fix a few sink/source calls when they are called in suspended state.
d6fdd71... add new functions pa_bluetooth_cleanup_name() and pa_bluetooth_get_form_factor()
16e3694... set PA_PROP_WINDOW_X11_DISPLAY from :0.0 and initialize PA_PROP_APPLICATION_PROCESS_MACHINE_ID properly
8ccc9aa... try to use glib's g_get_application_name() to set PA_PROP_APPLICATION_NAME
8fbce6e... when determining the minimum volume of all sink inputs make sure to handle the case when there are no sink inputs correctly
55e6331... store the module index shifted by 1 to map PA_INVALID_INDEX to NULL
69a9ed9... drop -pedantic
04acc23... download everything from gitweb twice to make sure we don't get a 'Generating...' message
e0d6b75... add a few new form factors
2c97c15... introduce PA_PROP_APPLICATION_PROCESS_MACHINE_ID
e0fd99b... work around dlsym() return value mistyping as suggested in POSIX
b092f2e... use uintpr_t when casting between pointers and integers
55f643d... check for NULL before accessing the name
e7007fc... allow passing of channel map on command line and hide unused sliders
2c7f2c5... look for libpulse in multiple different places
b979ab3... implement pa_channel_map_can_fade
f725b06... drop -Wpacked
6b80321... Merge branch 'master' into lennartsbtfixes
dbb8951... dump properties when we create a new sink or source
2557017... suppress lines made up only of whitespace
e6f4586... include ALSA driver in properties for cards/sink
4bd6545... add new function pa_alsa_get_driver_name()
3442d58... Merge branch 'master' into lennartsbtfixes
1c94cfe... Add a little Gtk test tool to show how balance/fade/value and the channel volumes play together
1b53f82... implement pa_cvolume_{get|set}_fade
9314db7... fix a bogus assert
634afed... properly deal with the case when l/r is silent when adjust balance
4a75002... add missing files
a71fa02... temporary commit of lennarts new bt changes
47a9b96... add some helpers for dealing with DBusPendingCall based on Mrc-Andre's work in module-bluetooth-discover
746dc2a... get rid of nonsensical late initialization of namereg/scache and things
4a06af6... make use of new functions pa_dbus_add_matches/pa_dbus_remove_matches
509535d... add new functions pa_dbus_add_matches()/pa_dbus_remove_matches()
daf0612... make things compile again
-----------------------------------------------------------------------

Summary of changes:
 configure.ac                                      |    2 +-
 src/Makefile.am                                   |   29 +-
 src/map-file                                      |    3 +
 src/modules/alsa/alsa-util.c                      |   26 +-
 src/modules/alsa/alsa-util.h                      |    2 +
 src/modules/bluetooth/bluetooth-util.c            |  864 ++++++++++
 src/modules/bluetooth/bluetooth-util.h            |   81 +
 src/modules/bluetooth/ipc.c                       |    2 +-
 src/modules/bluetooth/ipc.h                       |    7 +-
 src/modules/bluetooth/module-bluetooth-device.c   | 1827 ++++++++++++++-------
 src/modules/bluetooth/module-bluetooth-discover.c |  839 +---------
 src/modules/bluetooth/rtp.h                       |   46 +-
 src/modules/bluetooth/sbc.c                       |  375 ++---
 src/modules/bluetooth/sbc.h                       |    1 +
 src/modules/bluetooth/sbc_math.h                  |    2 -
 src/modules/bluetooth/sbc_primitives.c            |  469 ++++++
 src/modules/bluetooth/sbc_primitives.h            |   74 +
 src/modules/bluetooth/sbc_primitives_mmx.c        |  319 ++++
 src/modules/bluetooth/sbc_primitives_mmx.h        |   40 +
 src/modules/bluetooth/sbc_primitives_neon.c       |  245 +++
 src/modules/bluetooth/sbc_primitives_neon.h       |   40 +
 src/modules/bluetooth/sbc_tables.h                |  436 +++++-
 src/modules/dbus-util.c                           |  105 ++
 src/modules/dbus-util.h                           |   27 +
 src/modules/module-combine.c                      |    2 +-
 src/modules/module-console-kit.c                  |   16 +-
 src/modules/module-hal-detect.c                   |   27 +-
 src/modules/module-ladspa-sink.c                  |    2 +-
 src/modules/module-pipe-sink.c                    |    4 +-
 src/modules/module-remap-sink.c                   |    2 +-
 src/modules/module-sine.c                         |    2 +-
 src/modules/rtp/module-rtp-recv.c                 |    2 +-
 src/modules/rtp/module-rtp-send.c                 |    2 +-
 src/pulse/channelmap.c                            |   57 +-
 src/pulse/channelmap.h                            |    5 +
 src/pulse/def.h                                   |    9 +-
 src/pulse/error.c                                 |    4 +-
 src/pulse/proplist.h                              |    4 +-
 src/pulse/stream.c                                |    8 +-
 src/pulse/stream.h                                |    2 +-
 src/pulse/volume.c                                |  120 ++-
 src/pulse/volume.h                                |   19 +-
 src/pulsecore/cli-text.c                          |    8 +-
 src/pulsecore/core-scache.c                       |   22 +-
 src/pulsecore/core-scache.h                       |    2 +-
 src/pulsecore/core.c                              |   41 +-
 src/pulsecore/core.h                              |    9 +-
 src/pulsecore/envelope.c                          |    1 -
 src/pulsecore/idxset.c                            |   42 +-
 src/pulsecore/log.c                               |    3 +-
 src/pulsecore/ltdl-helper.c                       |    6 +-
 src/pulsecore/macro.h                             |   16 +-
 src/pulsecore/module.c                            |   20 +-
 src/pulsecore/namereg.c                           |   17 +-
 src/pulsecore/namereg.h                           |    2 -
 src/pulsecore/play-memblockq.c                    |    2 +-
 src/pulsecore/proplist-util.c                     |   53 +-
 src/pulsecore/protocol-esound.c                   |    4 +-
 src/pulsecore/protocol-native.c                   |   45 +-
 src/pulsecore/protocol-simple.c                   |    4 +-
 src/pulsecore/shared.c                            |   26 -
 src/pulsecore/shared.h                            |    6 -
 src/pulsecore/sink-input.c                        |   99 +-
 src/pulsecore/sink-input.h                        |    6 +-
 src/pulsecore/sink.c                              |   88 +-
 src/pulsecore/sound-file-stream.c                 |    2 +-
 src/pulsecore/source-output.c                     |   82 +-
 src/pulsecore/source-output.h                     |    6 +-
 src/pulsecore/source.c                            |   41 +-
 src/tests/volume-ui.py                            |  259 +++
 70 files changed, 5002 insertions(+), 2058 deletions(-)
 create mode 100644 src/modules/bluetooth/bluetooth-util.c
 create mode 100644 src/modules/bluetooth/bluetooth-util.h
 create mode 100644 src/modules/bluetooth/sbc_primitives.c
 create mode 100644 src/modules/bluetooth/sbc_primitives.h
 create mode 100644 src/modules/bluetooth/sbc_primitives_mmx.c
 create mode 100644 src/modules/bluetooth/sbc_primitives_mmx.h
 create mode 100644 src/modules/bluetooth/sbc_primitives_neon.c
 create mode 100644 src/modules/bluetooth/sbc_primitives_neon.h
 create mode 100644 src/tests/volume-ui.py

-----------------------------------------------------------------------

commit daf0612e372f66d70bc40655e637c00675e1b4c1
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jan 28 04:49:07 2009 +0100

    make things compile again

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index e2f6d01..11ed7d3 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -851,15 +851,14 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *msg, void *
 
             if (pa_streq(value, "SpeakerGain")) {
                 pa_log("spk gain: %d", gain);
-                pa_cvolume_set(&u->sink->volume, 1, (pa_volume_t) (gain * PA_VOLUME_NORM / 15));
-                u->sink->virtual_volume = u->sink->volume;
+                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;
 
-                pa_cvolume_set(&u->source->volume, 1, (pa_volume_t) (gain * PA_VOLUME_NORM / 15));
+                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);
             }
         }
@@ -899,7 +898,7 @@ static int sink_set_volume_cb(pa_sink *s) {
 
     dbus_error_init(&e);
 
-    vol = ((float)pa_cvolume_max(&s->volume) / PA_VOLUME_NORM) * 15;
+    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"));
@@ -927,7 +926,7 @@ static int source_set_volume_cb(pa_source *s) {
     struct userdata *u = s->userdata;
     pa_assert(u);
 
-    vol = ((float)pa_cvolume_max(&s->volume) / PA_VOLUME_NORM) * 15;
+    vol = ((float)pa_cvolume_max(&s->virtual_volume) / PA_VOLUME_NORM) * 15;
 
     pa_log_debug("set headset mic volume: %d (not implemented yet)", vol);
 

commit 509535d240506bc35881fe8d254f0bce28aed4c8
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 29 03:30:50 2009 +0100

    add new functions pa_dbus_add_matches()/pa_dbus_remove_matches()

diff --git a/src/modules/dbus-util.c b/src/modules/dbus-util.c
index d2abf08..1fc1e86 100644
--- a/src/modules/dbus-util.c
+++ b/src/modules/dbus-util.c
@@ -24,6 +24,8 @@
 #include <config.h>
 #endif
 
+#include <stdarg.h>
+
 #include <pulse/xmalloc.h>
 #include <pulse/timeval.h>
 #include <pulsecore/log.h>
@@ -325,3 +327,58 @@ pa_dbus_connection* pa_dbus_bus_get(pa_core *c, DBusBusType type, DBusError *err
 
     return pconn;
 }
+
+int pa_dbus_add_matches(DBusConnection *c, DBusError *error, ...) {
+    const char *t;
+    va_list ap;
+    unsigned k = 0;
+
+    pa_assert(c);
+    pa_assert(error);
+
+    va_start(ap, error);
+    while ((t = va_arg(ap, const char*))) {
+        dbus_bus_add_match(c, t, error);
+
+        if (dbus_error_is_set(error))
+            goto fail;
+
+        k++;
+    }
+    va_end(ap);
+    return 0;
+
+fail:
+
+    va_end(ap);
+    va_start(ap, error);
+    for (; k > 0; k--) {
+        DBusError e;
+
+        pa_assert_se(t = va_arg(ap, const char*));
+
+        dbus_error_init(&e);
+        dbus_bus_remove_match(c, t, &e);
+        dbus_error_free(&e);
+    }
+    va_end(ap);
+
+    return -1;
+}
+
+void pa_dbus_remove_matches(DBusConnection *c, ...) {
+    const char *t;
+    va_list ap;
+    DBusError error;
+
+    pa_assert(c);
+
+    dbus_error_init(&error);
+
+    va_start(ap, c);
+    while ((t = va_arg(ap, const char*))) {
+        dbus_bus_remove_match(c, t, &error);
+        dbus_error_free(&error);
+    }
+    va_end(ap);
+}
diff --git a/src/modules/dbus-util.h b/src/modules/dbus-util.h
index c4794da..0ab8780 100644
--- a/src/modules/dbus-util.h
+++ b/src/modules/dbus-util.h
@@ -37,4 +37,7 @@ DBusConnection* pa_dbus_connection_get(pa_dbus_connection *conn);
 pa_dbus_connection* pa_dbus_connection_ref(pa_dbus_connection *conn);
 void pa_dbus_connection_unref(pa_dbus_connection *conn);
 
+int pa_dbus_add_matches(DBusConnection *c, DBusError *error, ...) PA_GCC_SENTINEL;
+void pa_dbus_remove_matches(DBusConnection *c,  ...) PA_GCC_SENTINEL;
+
 #endif

commit 4a06af6081c0fc081ad6ca136880a61ebe1c4e01
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 29 03:31:42 2009 +0100

    make use of new functions pa_dbus_add_matches/pa_dbus_remove_matches

diff --git a/src/modules/module-console-kit.c b/src/modules/module-console-kit.c
index 4f3ed8d..805f5ee 100644
--- a/src/modules/module-console-kit.c
+++ b/src/modules/module-console-kit.c
@@ -313,8 +313,10 @@ int pa__init(pa_module*m) {
         goto fail;
     }
 
-    dbus_bus_add_match(pa_dbus_connection_get(connection), "type='signal',sender='org.freedesktop.ConsoleKit', interface='org.freedesktop.ConsoleKit.Seat'", &error);
-    if (dbus_error_is_set(&error)) {
+    if (pa_dbus_add_matches(
+                pa_dbus_connection_get(connection), &error,
+                "type='signal',sender='org.freedesktop.ConsoleKit',interface='org.freedesktop.ConsoleKit.Seat',member='SessionAdded'",
+                "type='signal',sender='org.freedesktop.ConsoleKit',interface='org.freedesktop.ConsoleKit.Seat',member='SessionRemoved'", NULL) < 0) {
         pa_log_error("Unable to subscribe to ConsoleKit signals: %s: %s", error.name, error.message);
         goto fail;
     }
@@ -354,14 +356,12 @@ void pa__done(pa_module *m) {
     }
 
     if (u->connection) {
-        DBusError error;
-        dbus_error_init(&error);
-
-        dbus_bus_remove_match(pa_dbus_connection_get(u->connection), "type='signal',sender='org.freedesktop.ConsoleKit', interface='org.freedesktop.ConsoleKit.Seat'", &error);
-        dbus_error_free(&error);
+        pa_dbus_remove_matches(
+                pa_dbus_connection_get(u->connection),
+                "type='signal',sender='org.freedesktop.ConsoleKit',interface='org.freedesktop.ConsoleKit.Seat',member='SessionAdded'",
+                "type='signal',sender='org.freedesktop.ConsoleKit',interface='org.freedesktop.ConsoleKit.Seat',member='SessionRemoved'", NULL);
 
         dbus_connection_remove_filter(pa_dbus_connection_get(u->connection), filter_cb, u);
-
         pa_dbus_connection_unref(u->connection);
     }
 
diff --git a/src/modules/module-hal-detect.c b/src/modules/module-hal-detect.c
index d3b351a..8b1f0b2 100644
--- a/src/modules/module-hal-detect.c
+++ b/src/modules/module-hal-detect.c
@@ -773,18 +773,15 @@ int pa__init(pa_module*m) {
         goto fail;
     }
 
-    dbus_bus_add_match(pa_dbus_connection_get(u->connection), "type='signal',sender='org.freedesktop.Hal', interface='org.freedesktop.Hal.Device.AccessControl'", &error);
-    if (dbus_error_is_set(&error)) {
+    if (pa_dbus_add_matches(
+                pa_dbus_connection_get(u->connection), &error,
+                "type='signal',sender='org.freedesktop.Hal',interface='org.freedesktop.Hal.Device.AccessControl',member='ACLAdded'",
+                "type='signal',sender='org.freedesktop.Hal',interface='org.freedesktop.Hal.Device.AccessControl',member='ACLRemoved'",
+                "type='signal',interface='org.pulseaudio.Server',member='DirtyGiveUpMessage'", NULL) < 0) {
         pa_log_error("Unable to subscribe to HAL ACL signals: %s: %s", error.name, error.message);
         goto fail;
     }
 
-    dbus_bus_add_match(pa_dbus_connection_get(u->connection), "type='signal',interface='org.pulseaudio.Server'", &error);
-    if (dbus_error_is_set(&error)) {
-        pa_log_error("Unable to subscribe to PulseAudio signals: %s: %s", error.name, error.message);
-        goto fail;
-    }
-
     pa_log_info("Loaded %i modules.", n);
 
     pa_modargs_free(ma);
@@ -825,17 +822,13 @@ void pa__done(pa_module *m) {
     }
 
     if (u->connection) {
-        DBusError error;
-        dbus_error_init(&error);
-
-        dbus_bus_remove_match(pa_dbus_connection_get(u->connection), "type='signal',sender='org.freedesktop.Hal', interface='org.freedesktop.Hal.Device.AccessControl'", &error);
-        dbus_error_free(&error);
-
-        dbus_bus_remove_match(pa_dbus_connection_get(u->connection), "type='signal',interface='org.pulseaudio.Server'", &error);
-        dbus_error_free(&error);
+        pa_dbus_remove_matches(
+                pa_dbus_connection_get(u->connection),
+                "type='signal',sender='org.freedesktop.Hal',interface='org.freedesktop.Hal.Device.AccessControl',member='ACLAdded'",
+                "type='signal',sender='org.freedesktop.Hal',interface='org.freedesktop.Hal.Device.AccessControl',member='ACLRemoved'",
+                "type='signal',interface='org.pulseaudio.Server',member='DirtyGiveUpMessage'", NULL);
 
         dbus_connection_remove_filter(pa_dbus_connection_get(u->connection), filter_cb, u);
-
         pa_dbus_connection_unref(u->connection);
     }
 

commit 746dc2ac19950d4eecc083929d6ed86443e3a112
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 29 16:25:29 2009 +0100

    get rid of nonsensical late initialization of namereg/scache and things

diff --git a/src/pulsecore/core-scache.c b/src/pulsecore/core-scache.c
index 6d2ae93..e548941 100644
--- a/src/pulsecore/core-scache.c
+++ b/src/pulsecore/core-scache.c
@@ -120,9 +120,6 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {
         e->core = c;
         e->proplist = pa_proplist_new();
 
-        if (!c->scache)
-            c->scache = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
-
         pa_idxset_put(c->scache, e, &e->index);
 
         pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_NEW, e->index);
@@ -287,23 +284,18 @@ int pa_scache_remove_item(pa_core *c, const char *name) {
     return 0;
 }
 
-static void free_cb(void *p, void *userdata) {
-    pa_scache_entry *e = p;
-    pa_assert(e);
-
-    free_entry(e);
-}
+void pa_scache_free_all(pa_core *c) {
+    pa_scache_entry *e;
 
-void pa_scache_free(pa_core *c) {
     pa_assert(c);
 
-    if (c->scache) {
-        pa_idxset_free(c->scache, free_cb, NULL);
-        c->scache = NULL;
-    }
+    while ((e = pa_idxset_steal_first(c->scache, NULL)))
+        free_entry(e);
 
-    if (c->scache_auto_unload_event)
+    if (c->scache_auto_unload_event) {
         c->mainloop->time_free(c->scache_auto_unload_event);
+        c->scache_auto_unload_event = NULL;
+    }
 }
 
 int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx) {
diff --git a/src/pulsecore/core-scache.h b/src/pulsecore/core-scache.h
index a75f8ac..1fe3c30 100644
--- a/src/pulsecore/core-scache.h
+++ b/src/pulsecore/core-scache.h
@@ -58,7 +58,7 @@ int pa_scache_add_directory_lazy(pa_core *c, const char *pathname);
 int pa_scache_remove_item(pa_core *c, const char *name);
 int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx);
 int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx);
-void pa_scache_free(pa_core *c);
+void pa_scache_free_all(pa_core *c);
 
 const char *pa_scache_get_name_by_id(pa_core *c, uint32_t id);
 uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name);
diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c
index 689fc8f..5fd2bdf 100644
--- a/src/pulsecore/core.c
+++ b/src/pulsecore/core.c
@@ -92,21 +92,22 @@ pa_core* pa_core_new(pa_mainloop_api *m, pa_bool_t shared, size_t shm_size) {
 
     c->state = PA_CORE_STARTUP;
     c->mainloop = m;
+
     c->clients = pa_idxset_new(NULL, NULL);
+    c->cards = pa_idxset_new(NULL, NULL);
     c->sinks = pa_idxset_new(NULL, NULL);
     c->sources = pa_idxset_new(NULL, NULL);
-    c->source_outputs = pa_idxset_new(NULL, NULL);
     c->sink_inputs = pa_idxset_new(NULL, NULL);
-    c->cards = pa_idxset_new(NULL, NULL);
+    c->source_outputs = pa_idxset_new(NULL, NULL);
+    c->modules = pa_idxset_new(NULL, NULL);
+    c->scache = pa_idxset_new(NULL, NULL);
+
+    c->namereg = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+    c->shared = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
 
     c->default_source = NULL;
     c->default_sink = NULL;
 
-    c->modules = NULL;
-    c->namereg = NULL;
-    c->scache = NULL;
-    c->running_as_daemon = FALSE;
-
     c->default_sample_spec.format = PA_SAMPLE_S16NE;
     c->default_sample_spec.rate = 44100;
     c->default_sample_spec.channels = 2;
@@ -128,22 +129,20 @@ pa_core* pa_core_new(pa_mainloop_api *m, pa_bool_t shared, size_t shm_size) {
 
     c->exit_idle_time = -1;
     c->scache_idle_time = 20;
-    c->flat_volumes = TRUE;
-
-    c->resample_method = PA_RESAMPLER_SPEEX_FLOAT_BASE + 3;
 
+    c->flat_volumes = TRUE;
     c->disallow_module_loading = FALSE;
     c->disallow_exit = FALSE;
+    c->running_as_daemon = FALSE;
     c->realtime_scheduling = FALSE;
     c->realtime_priority = 5;
     c->disable_remixing = FALSE;
     c->disable_lfe_remixing = FALSE;
+    c->resample_method = PA_RESAMPLER_SPEEX_FLOAT_BASE + 3;
 
     for (j = 0; j < PA_CORE_HOOK_MAX; j++)
         pa_hook_init(&c->hooks[j], c);
 
-    pa_shared_init(c);
-
     pa_random(&c->cookie, sizeof(c->cookie));
 
 #ifdef SIGPIPE
@@ -165,7 +164,13 @@ static void core_free(pa_object *o) {
     c->state = PA_CORE_SHUTDOWN;
 
     pa_module_unload_all(c);
-    pa_assert(!c->modules);
+    pa_scache_free_all(c);
+
+    pa_assert(pa_idxset_isempty(c->scache));
+    pa_idxset_free(c->scache, NULL, NULL);
+
+    pa_assert(pa_idxset_isempty(c->modules));
+    pa_idxset_free(c->modules, NULL, NULL);
 
     pa_assert(pa_idxset_isempty(c->clients));
     pa_idxset_free(c->clients, NULL, NULL);
@@ -185,8 +190,12 @@ static void core_free(pa_object *o) {
     pa_assert(pa_idxset_isempty(c->sink_inputs));
     pa_idxset_free(c->sink_inputs, NULL, NULL);
 
-    pa_scache_free(c);
-    pa_namereg_free(c);
+    pa_assert(pa_hashmap_isempty(c->namereg));
+    pa_hashmap_free(c->namereg, NULL, NULL);
+
+    pa_assert(pa_hashmap_isempty(c->shared));
+    pa_hashmap_free(c->shared, NULL, NULL);
+
     pa_subscription_free_all(c);
 
     if (c->exit_event)
@@ -198,8 +207,6 @@ static void core_free(pa_object *o) {
     pa_silence_cache_done(&c->silence_cache);
     pa_mempool_free(c->mempool);
 
-    pa_shared_cleanup(c);
-
     for (j = 0; j < PA_CORE_HOOK_MAX; j++)
         pa_hook_done(&c->hooks[j]);
 
diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index b349c6f..2b8f819 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -112,7 +112,7 @@ struct pa_core {
     /* Some hashmaps for all sorts of entities */
     pa_hashmap *namereg, *shared;
 
-    /* The name of the default sink/source */
+    /* The default sink/source */
     pa_source *default_source;
     pa_sink *default_sink;
 
@@ -129,13 +129,12 @@ struct pa_core {
     pa_mempool *mempool;
     pa_silence_cache silence_cache;
 
-    int exit_idle_time, scache_idle_time;
-    pa_bool_t flat_volumes;
-
     pa_time_event *exit_event;
-
     pa_time_event *scache_auto_unload_event;
 
+    int exit_idle_time, scache_idle_time;
+
+    pa_bool_t flat_volumes:1;
     pa_bool_t disallow_module_loading:1;
     pa_bool_t disallow_exit:1;
     pa_bool_t running_as_daemon:1;
diff --git a/src/pulsecore/module.c b/src/pulsecore/module.c
index d470bb0..1eb70c8 100644
--- a/src/pulsecore/module.c
+++ b/src/pulsecore/module.c
@@ -75,7 +75,7 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
 
         m->load_once = load_once();
 
-        if (m->load_once && c->modules) {
+        if (m->load_once) {
             pa_module *i;
             uint32_t idx;
             /* OK, the module only wants to be loaded once, let's make sure it is */
@@ -105,9 +105,6 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
         goto fail;
     }
 
-    if (!c->modules)
-        c->modules = pa_idxset_new(NULL, NULL);
-
     pa_assert_se(pa_idxset_put(c->modules, m, &m->index) >= 0);
     pa_assert(m->index != PA_IDXSET_INVALID);
 
@@ -200,17 +197,11 @@ void pa_module_unload_by_index(pa_core *c, uint32_t idx, pa_bool_t force) {
 }
 
 void pa_module_unload_all(pa_core *c) {
+    pa_module *m;
     pa_assert(c);
 
-    if (c->modules) {
-        pa_module *m;
-
-        while ((m = pa_idxset_steal_first(c->modules, NULL)))
-            pa_module_free(m);
-
-        pa_idxset_free(c->modules, NULL, NULL);
-        c->modules = NULL;
-    }
+    while ((m = pa_idxset_steal_first(c->modules, NULL)))
+        pa_module_free(m);
 
     if (c->module_defer_unload_event) {
         c->mainloop->defer_free(c->module_defer_unload_event);
@@ -226,9 +217,6 @@ static void defer_cb(pa_mainloop_api*api, pa_defer_event *e, void *userdata) {
     pa_core_assert_ref(c);
     api->defer_enable(e, 0);
 
-    if (!c->modules)
-        return;
-
     while ((m = pa_idxset_iterate(c->modules, &state, NULL)))
         if (m->unload_requested)
             pa_module_unload(c, m, TRUE);
diff --git a/src/pulsecore/namereg.c b/src/pulsecore/namereg.c
index f3d5a8f..ac456ac 100644
--- a/src/pulsecore/namereg.c
+++ b/src/pulsecore/namereg.c
@@ -87,16 +87,6 @@ char* pa_namereg_make_valid_name(const char *name) {
     return n;
 }
 
-void pa_namereg_free(pa_core *c) {
-    pa_assert(c);
-
-    if (!c->namereg)
-        return;
-
-    pa_assert(pa_hashmap_size(c->namereg) == 0);
-    pa_hashmap_free(c->namereg, NULL, NULL);
-}
-
 const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t type, void *data, pa_bool_t fail) {
     struct namereg_entry *e;
     char *n = NULL;
@@ -118,9 +108,6 @@ const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t
             return NULL;
     }
 
-    if (!c->namereg)
-        c->namereg = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
-
     if ((e = pa_hashmap_get(c->namereg, name)) && fail) {
         pa_xfree(n);
         return NULL;
@@ -210,7 +197,7 @@ void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type) {
     if (*name == '@' || !name || !pa_namereg_is_valid_name(name))
         return NULL;
 
-    if (c->namereg && (e = pa_hashmap_get(c->namereg, name)))
+    if ((e = pa_hashmap_get(c->namereg, name)))
         if (e->type == type)
             return e->data;
 
diff --git a/src/pulsecore/namereg.h b/src/pulsecore/namereg.h
index b91dd52..ff99525 100644
--- a/src/pulsecore/namereg.h
+++ b/src/pulsecore/namereg.h
@@ -34,8 +34,6 @@ typedef enum pa_namereg_type {
     PA_NAMEREG_CARD
 } pa_namereg_type_t;
 
-void pa_namereg_free(pa_core *c);
-
 const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t type, void *data, pa_bool_t fail);
 void pa_namereg_unregister(pa_core *c, const char *name);
 void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type);
diff --git a/src/pulsecore/shared.c b/src/pulsecore/shared.c
index d6e42dd..9485dc3 100644
--- a/src/pulsecore/shared.c
+++ b/src/pulsecore/shared.c
@@ -99,32 +99,6 @@ int pa_shared_remove(pa_core *c, const char *name) {
     return 0;
 }
 
-void pa_shared_init(pa_core *c) {
-    pa_assert(c);
-
-    c->shared = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
-}
-
-void pa_shared_cleanup(pa_core *c) {
-    pa_assert(c);
-
-    if (!c->shared)
-        return;
-
-    if (!pa_hashmap_isempty(c->shared)) {
-        pa_strbuf *s = pa_strbuf_new();
-
-        pa_shared_dump(c, s);
-        pa_log_debug("%s", pa_strbuf_tostring(s));
-        pa_strbuf_free(s);
-        pa_assert(pa_hashmap_isempty(c->shared));
-    }
-
-    pa_hashmap_free(c->shared, NULL, NULL);
-    c->shared = NULL;
-
-}
-
 void pa_shared_dump(pa_core *c, pa_strbuf *s) {
     void *state = NULL;
     pa_shared *p;
diff --git a/src/pulsecore/shared.h b/src/pulsecore/shared.h
index dd3f94e..f6f8d3c 100644
--- a/src/pulsecore/shared.h
+++ b/src/pulsecore/shared.h
@@ -49,12 +49,6 @@ int pa_shared_remove(pa_core *c, const char *name);
 /* A combination of pa_shared_remove() and pa_shared_set() */
 int pa_shared_replace(pa_core *c, const char *name, void *data);
 
-/* Free all memory used by the shared property system */
-void pa_shared_cleanup(pa_core *c);
-
-/* Initialize the shared property system */
-void pa_shared_init(pa_core *c);
-
 /* Dump the current set of shared properties */
 void pa_shared_dump(pa_core *c, pa_strbuf *s);
 

commit 47a9b96b64e9fd949adf4dd1fbd26c5d75a5df30
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 29 16:26:34 2009 +0100

    add some helpers for dealing with DBusPendingCall based on Mrc-Andre's work in module-bluetooth-discover

diff --git a/src/modules/dbus-util.c b/src/modules/dbus-util.c
index 1fc1e86..f6a986a 100644
--- a/src/modules/dbus-util.c
+++ b/src/modules/dbus-util.c
@@ -382,3 +382,51 @@ void pa_dbus_remove_matches(DBusConnection *c, ...) {
     }
     va_end(ap);
 }
+
+pa_dbus_pending *pa_dbus_pending_new(DBusMessage *m, DBusPendingCall *pending, void *context_data, void *call_data) {
+    pa_dbus_pending *p;
+
+    pa_assert(pending);
+
+    p = pa_xnew(pa_dbus_pending, 1);
+    p->message = m;
+    p->pending = pending;
+    p->context_data = context_data;
+    p->call_data = call_data;
+
+    PA_LLIST_INIT(pa_dbus_pending, p);
+
+    return p;
+}
+
+void pa_dbus_pending_free(pa_dbus_pending *p) {
+    pa_assert(p);
+
+    if (p->pending) {
+        dbus_pending_call_cancel(p->pending);
+        dbus_pending_call_unref(p->pending);
+    }
+
+    if (p->message)
+        dbus_message_unref(p->message);
+
+    pa_xfree(p);
+}
+
+void pa_dbus_sync_pending_list(pa_dbus_pending **p) {
+    pa_assert(p);
+
+    while (*p)
+        dbus_pending_call_block((*p)->pending);
+}
+
+void pa_dbus_free_pending_list(pa_dbus_pending **p) {
+    pa_dbus_pending *i;
+
+    pa_assert(p);
+
+    while ((i = *p)) {
+        PA_LLIST_REMOVE(pa_dbus_pending, *p, i);
+        pa_dbus_pending_free(i);
+    }
+}
diff --git a/src/modules/dbus-util.h b/src/modules/dbus-util.h
index 0ab8780..fd97467 100644
--- a/src/modules/dbus-util.h
+++ b/src/modules/dbus-util.h
@@ -25,6 +25,7 @@
 #include <dbus/dbus.h>
 
 #include <pulsecore/core.h>
+#include <pulsecore/llist.h>
 
 typedef struct pa_dbus_connection pa_dbus_connection;
 
@@ -40,4 +41,27 @@ void pa_dbus_connection_unref(pa_dbus_connection *conn);
 int pa_dbus_add_matches(DBusConnection *c, DBusError *error, ...) PA_GCC_SENTINEL;
 void pa_dbus_remove_matches(DBusConnection *c,  ...) PA_GCC_SENTINEL;
 
+typedef struct pa_dbus_pending pa_dbus_pending;
+
+struct userdata; /* We leave the actual definition to the caller */
+
+struct pa_dbus_pending {
+    DBusMessage *message;
+    DBusPendingCall *pending;
+
+    void *context_data;
+    void *call_data;
+
+    PA_LLIST_FIELDS(pa_dbus_pending);
+};
+
+pa_dbus_pending *pa_dbus_pending_new(DBusMessage *m, DBusPendingCall *pending, void *context_data, void *call_data);
+void pa_dbus_pending_free(pa_dbus_pending *p);
+
+/* Sync up a list of pa_dbus_pending_call objects */
+void pa_dbus_sync_pending_list(pa_dbus_pending **p);
+
+/* Free up a list of pa_dbus_pending_call objects */
+void pa_dbus_free_pending_list(pa_dbus_pending **p);
+
 #endif

commit a71fa021a3ca12a6f8eff9b96e073403a0c156c0
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 29 16:27:27 2009 +0100

    temporary commit of lennarts new bt changes

diff --git a/src/Makefile.am b/src/Makefile.am
index 00dd53d..dbb2d0a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1003,9 +1003,11 @@ if HAVE_BLUEZ
 modlibexec_LTLIBRARIES += \
 		module-bluetooth-proximity.la \
 		module-bluetooth-discover.la \
+		libbluetooth-util.la \
 		libbluetooth-ipc.la \
-		libbluetooth-sbc.la \
-		module-bluetooth-device.la
+		libbluetooth-sbc.la
+#\
+#		module-bluetooth-device.la
 
 pulselibexec_PROGRAMS += \
 		proximity-helper
@@ -1445,7 +1447,7 @@ proximity_helper_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
 # Bluetooth sink / source
 module_bluetooth_discover_la_SOURCES = modules/bluetooth/module-bluetooth-discover.c
 module_bluetooth_discover_la_LDFLAGS = $(MODULE_LDFLAGS)
-module_bluetooth_discover_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore- at PA_MAJORMINORMICRO@.la libdbus-util.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
+module_bluetooth_discover_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore- at PA_MAJORMINORMICRO@.la libdbus-util.la libbluetooth-util.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
 module_bluetooth_discover_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
 
 libbluetooth_sbc_la_SOURCES = modules/bluetooth/sbc.c modules/bluetooth/sbc.h modules/bluetooth/sbc_tables.h modules/bluetooth/sbc_math.h
@@ -1459,10 +1461,15 @@ libbluetooth_ipc_la_LDFLAGS = -avoid-version
 libbluetooth_ipc_la_LIBADD = $(AM_LIBADD)libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
 libbluetooth_ipc_la_CFLAGS = $(AM_CFLAGS) -w
 
-module_bluetooth_device_la_SOURCES = modules/bluetooth/module-bluetooth-device.c modules/bluetooth/rtp.h
-module_bluetooth_device_la_LDFLAGS = $(MODULE_LDFLAGS)
-module_bluetooth_device_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore- at PA_MAJORMINORMICRO@.la libdbus-util.la libbluetooth-ipc.la libbluetooth-sbc.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
-module_bluetooth_device_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) -w
+libbluetooth_util_la_SOURCES = modules/bluetooth/bluetooth-util.c modules/bluetooth/bluetooth-util.h
+libbluetooth_util_la_LDFLAGS = -avoid-version
+libbluetooth_util_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la libdbus-util.la
+libbluetooth_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
+
+#module_bluetooth_device_la_SOURCES = modules/bluetooth/module-bluetooth-device.c modules/bluetooth/rtp.h
+#module_bluetooth_device_la_LDFLAGS = $(MODULE_LDFLAGS)
+#module_bluetooth_device_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore- at PA_MAJORMINORMICRO@.la libdbus-util.la libbluetooth-ipc.la libbluetooth-sbc.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
+#module_bluetooth_device_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) -w
 
 # Apple Airtunes/RAOP
 module_raop_sink_la_SOURCES = modules/module-raop-sink.c
diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 11ed7d3..e4b834a 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,13 +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
@@ -63,13 +64,29 @@ 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>");
 
+static const char* const valid_modargs[] = {
+    "name",
+    "card_name",
+    "sink_name",
+    "source_name",
+    "address",
+    "profile",
+    "rate",
+    "channels",
+    "path",
+    NULL
+};
+
 struct bt_a2dp {
     sbc_capabilities_t sbc_capabilities;
     sbc_t sbc;                           /* Codec data */
@@ -84,6 +101,12 @@ struct bt_a2dp {
     unsigned frame_count;                /* Current frames in buffer*/
 };
 
+enum profile {
+    PROFILE_A2DP,
+    PROFILE_SCO,
+    PROFILE_OFF
+};
+
 struct userdata {
     pa_core *core;
     pa_module *module;
@@ -98,33 +121,24 @@ struct userdata {
     uint64_t offset;
     pa_smoother *smoother;
 
-    char *name;
-    char *addr;
-    char *profile;
-    pa_sample_spec ss;
+    char *address;
+    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;
-};
+    pa_dbus_connection *connection;
 
-static const char* const valid_modargs[] = {
-    "sink_name",
-    "address",
-    "profile",
-    "rate",
-    "channels",
-    "path",
-    NULL
+    enum profile profile;
+
+    pa_bluetooth_device *device;
 };
 
 static int bt_audioservice_send(int sk, const bt_audio_msg_header_t *msg) {
@@ -234,7 +248,7 @@ static int bt_getcaps(struct userdata *u) {
     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);
+    strncpy(msg.getcaps_req.device, u->address, 18);
     if (pa_streq(u->profile, "a2dp"))
         msg.getcaps_req.transport = BT_CAPABILITIES_TRANSPORT_A2DP;
     else if (pa_streq(u->profile, "hsp"))
@@ -489,7 +503,6 @@ static int bt_setconf(struct userdata *u) {
     }
 
     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 */
@@ -933,176 +946,398 @@ static int source_set_volume_cb(pa_source *s) {
     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;
+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(m);
-    dbus_error_init(&err);
+    pa_assert(type);
+    pa_assert(ma);
+    pa_assert(device_id);
+    pa_assert(namereg_fail);
 
-    m->userdata = u = pa_xnew0(struct userdata, 1);
-    u->module = m;
-    u->core = m->core;
-    u->audioservice_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->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;
+    t = pa_sprintf_malloc("%s_name", type);
+    n = pa_modargs_get_value(ma, t, NULL);
+    pa_xfree(t);
 
-    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 (n) {
+        *namereg_fail = TRUE;
+        return pa_xstrdup(n);
     }
-    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 ((n = pa_modargs_get_value(ma, "name", NULL)))
+        *namereg_fail = TRUE;
+    else {
+        n = device_id;
+        *namereg_fail = FALSE;
     }
-    if (pa_modargs_get_value_u32(ma, "rate", &u->ss.rate) < 0) {
-        pa_log_error("Failed to get rate from module arguments");
-        goto fail;
+
+    return pa_sprintf_malloc("bluez_%s.%s", type, n);
+}
+
+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 = m;
+    pa_sink_new_data_set_sample_spec(&data, &u->ss);
+    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Bluetooth %s on %s", u->profile == PROFILE_A2DP ? "A2DP" : "HSP/HFP", u->addr);
+    pa_proplist_sets(data.proplist, "bluetooth.protocol", u->profile == PROFILE_A2DP ? "a2dp" : "sco");
+    data.card = u->card;
+    data.name = get_name("sink", ma, u->addr, &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->path = pa_xstrdup(pa_modargs_get_value(ma, "path", NULL));
 
-    channels = u->ss.channels;
-    if (pa_modargs_get_value_u32(ma, "channels", &channels) < 0) {
-        pa_log_error("Failed to get channels from module arguments");
-        goto fail;
+    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 = m;
+    pa_source_new_data_set_sample_spec(&data, &u->ss);
+    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Bluetooth %s on %s", u->profile == PROFILE_A2DP ? "A2DP" : "HSP/HFP", u->addr);
+    pa_proplist_sets(data.proplist, "bluetooth.protocol", u->profile == PROFILE_A2DP ? "a2dp" : "sco");
+    data.card = u->card;
+    data.name = get_name("source", ma, u->addr, &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->ss.channels = (uint8_t) channels;
+
+    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 void init_profile(struct userdata *u) {
+    enum profile *d;
+
+    pa_assert(u);
+
+    if (u->profile == PROFILE_A2DP ||
+        u->profile == PROFILE_SCO)
+        add_sink(u);
+
+    if (u->profile == PROFILE_SCO)
+        add_source(u);
+}
+
+static int init_bt(struct userdata *u) {
+    pa_assert(u);
 
     /* connect to the bluez audio service */
-    u->audioservice_fd = bt_audio_service_open();
-    if (u->audioservice_fd <= 0) {
+    if ((u->audioservice_fd = bt_audio_service_open()) < 0) {
         pa_log_error("Couldn't connect to bluetooth audio service");
-        goto fail;
+        return -1;
     }
     pa_log_debug("Connected to the bluetooth audio service");
 
     /* queries device capabilities */
-    e = bt_getcaps(u);
-    if (e < 0) {
+    if (bt_getcaps(u) < 0) {
         pa_log_error("Failed to get device capabilities");
-        goto fail;
+        return -1;
     }
     pa_log_debug("Got device capabilities");
 
+    return 0;
+}
+
+static setup_bt(struct userdata *u) {
+    pa_assert(u);
+
     /* configures the connection */
-    e = bt_setconf(u);
-    if (e < 0) {
+    if (bt_setconf(u) < 0) {
         pa_log_error("Failed to set config");
-        goto fail;
+        return -1;
     }
     pa_log_debug("Connection to the device configured");
 
     /* gets the device socket */
-    e = bt_getstreamfd(u);
-    if (e < 0) {
+    if (bt_getstreamfd(u) < 0) {
         pa_log_error("Failed to get stream fd (%d)", e);
-        goto fail;
+        return -1;
     }
     pa_log_debug("Got the device socket");
+}
 
-    /* create sink */
-    pa_sink_new_data_init(&data);
+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);
+        pa_sink_unref(u->sink);
+        u->sink = NULL;
+    }
+
+    if (u->source) {
+        outputs = pa_source_move_all_start(u->source);
+
+        pa_source_unlink(u->source);
+        pa_source_unref(u->source);
+        u->source = NULL;
+    }
+
+    u->profile = *d;
+
+    init_profile(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) {
+    pa_card_new_data data;
+    pa_bool_t b;
+    pa_card_profile *p;
+    enum profile *d;
+
+    pa_card_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);
+    data.module = u->module;
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->addr);
     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");
+    data.name = get_name("card", ma, u->addr, &b);
+    data.namereg_fail = b;
+
+    data.profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
+    if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP ||
+        u->transport == BT_CAPABILITIES_TRANSPORT_ANY) {
+
+        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;
+    }
+
+    if (u->transport == BT_CAPABILITIES_TRANSPORT_SCO ||
+        u->transport == BT_CAPABILITIES_TRANSPORT_ANY) {
+
+        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_SCO;
+    }
+
+    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;
+
+    u->card = pa_card_new(m->core, &data);
+    pa_card_new_data_done(&data);
+
+    if (!u->card) {
+        pa_log("Failed to allocate card.");
+        return -1;
+    }
+
+    card->userdata = u;
+    card->set_profile = card_set_profile;
+
+    d = PA_CARD_PROFILE_DATA(u->card->active_profile);
+    u->profile = *d;
+
+    return 0;
+}
+
+int pa__init(pa_module* m) {
+    pa_modargs *ma;
+    uint32_t channels;
+    pa_sink_new_data sink_data;
+    pa_source_new_data source_data;
+    pa_card card_data;
+    struct pollfd *pollfd;
+    struct userdata *u;
+    pa_bool_t b;
+    const char *p;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log_error("Failed to parse module arguments");
         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;
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->module = m;
+    u->core = m->core;
+    u->audioservice_fd = -1;
+    u->stream_fd = -1;
+    u->transport = (uint8_t) -1;
+    u->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->add_source = m->core->default_sample_spec;
 
-    /* start rt thread */
-    if (!(u->thread = pa_thread_new(thread_func, u))) {
-        pa_log_error("Failed to create IO thread");
+    if (!(u->address = pa_xstrdup(pa_modargs_get_value(ma, "address", NULL)))) {
+        pa_log_error("Failed to get device address from module arguments");
+        goto fail;
+    }
+
+    u->path = pa_xstrdup(pa_modargs_get_value(ma, "path", NULL));
+
+    p = pa_modargs_get_value(ma, "profile", NULL);
+    if (p && !pa_streq(p, "hsp") && !pa_streq(p, "a2dp")) {
+        pa_log_error("Failed to get profile from module arguments");
         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);
+
+    if (pa_modargs_get_value_u32(ma, "rate", &u->ss.rate) < 0 ||
+        u->ss.rate <= 0 || u->ss.rate > PA_RATE_MAX) {
+        pa_log_error("Failed to get rate from module arguments");
         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");
+    channels = u->ss.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;
+
+    /* Connect to the BT service and query capabilities */
+    if (init_bt(u) < 0)
+        goto fail;
+
+    /* Add the card structure. This will also initialize the default profile */
+    if (add_card(u) < 0)
+        goto fail;
 
-    if (pa_streq(u->profile, "hsp")) {
+    /* Now configure the the right profile */
+    if (setup_bt(u) < 0)
 
-        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);
+    if (init_profile(u) < 0)
+        goto fail;
+
+    if (u->path) {
+        DBusError err;
+        dbus_error_init(&err);
+        char *t;
+
+        u->conn = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &err);
+        if (dbus_error_is_set(&err) || (!u->conn)) {
+            pa_log("Failed to get D-Bus connection: %s", 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);
+        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;
+            }
+        }
     }
 
-    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;
+    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");
+        goto fail;
     }
 
-end:
+    if (u->sink)
+        pa_sink_put(u->sink);
+
+    if (u->source)
+        pa_sink_put(u->source);
+
     pa_modargs_free(ma);
     return 0;
 
@@ -1114,6 +1349,17 @@ fail:
     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);
@@ -1123,23 +1369,29 @@ void pa__done(pa_module *m) {
 
     if (u->conn) {
         DBusError error;
-        char *tmp;
-        dbus_error_init(&error);
+        char *t;
 
-        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);
+        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_error_init(&error);
+            dbus_bus_remove_match(pa_dbus_connection_get(u->conn), t, &error);
             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);
+            pa_xfree(t);
+        }
+
+        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->conn);
     }
 
@@ -1149,6 +1401,9 @@ void pa__done(pa_module *m) {
     if (u->sink)
         pa_sink_unlink(u->sink);
 
+    if (u->source)
+        pa_source_unlink(u->source);
+
     if (u->thread) {
         pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
         pa_thread_free(u->thread);
@@ -1157,6 +1412,12 @@ void pa__done(pa_module *m) {
     if (u->sink)
         pa_sink_unref(u->sink);
 
+    if (u->source)
+        pa_source_unref(u->source);
+
+    if (u->card)
+        pa_card_unref(u->card);
+
     pa_thread_mq_done(&u->thread_mq);
 
     if (u->rtpoll_item)
@@ -1177,7 +1438,7 @@ void pa__done(pa_module *m) {
         pa_close(u->stream_fd);
 
     if (u->audioservice_fd >= 0)
-        pa_close(u->audioservice_fd);
+        pa_close(u->service_fd);
 
     pa_xfree(u);
 }
diff --git a/src/modules/bluetooth/module-bluetooth-discover.c b/src/modules/bluetooth/module-bluetooth-discover.c
index 1bc05c0..d9d3449 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 (PA_PTR_TO_UINT(d->data) == 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("name=\"%s\" address=\"%s\" path=\"%s\"", d->address, 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(m->index);
+            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 (PA_PTR_TO_UINT(d->data) != 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, PA_PTR_TO_UINT(d->data), 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;
-}

commit 4a75002ebe385302c513b79fb98f59832d73c8ea
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 29 20:30:42 2009 +0100

    add missing files

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
new file mode 100644
index 0000000..019b97e
--- /dev/null
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -0,0 +1,805 @@
+/***
+  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);
+}
diff --git a/src/modules/bluetooth/bluetooth-util.h b/src/modules/bluetooth/bluetooth-util.h
new file mode 100644
index 0000000..ea6687a
--- /dev/null
+++ b/src/modules/bluetooth/bluetooth-util.h
@@ -0,0 +1,77 @@
+#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);
+
+#endif

commit 634afede7dcbf7ce5739310b6398d64a1221d25c
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jan 30 00:14:28 2009 +0100

    properly deal with the case when l/r is silent when adjust balance

diff --git a/src/pulse/volume.c b/src/pulse/volume.c
index 3434cb1..4da133d 100644
--- a/src/pulse/volume.c
+++ b/src/pulse/volume.c
@@ -487,12 +487,12 @@ pa_cvolume* pa_cvolume_set_balance(pa_cvolume *v, const pa_channel_map *map, flo
     for (c = 0; c < map->channels; c++) {
         if (on_left(map->map[c])) {
             if (left == 0)
-                v->values[c] = 0;
+                v->values[c] = nleft;
             else
                 v->values[c] = (pa_volume_t) (((uint64_t) v->values[c] * (uint64_t) nleft) / (uint64_t) left);
         } else if (on_right(map->map[c])) {
             if (right == 0)
-                v->values[c] = 0;
+                v->values[c] = nright;
             else
                 v->values[c] = (pa_volume_t) (((uint64_t) v->values[c] * (uint64_t) nright) / (uint64_t) right);
         }

commit 9314db79db222ac60e0e911e8bfd6455fa433ba9
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jan 30 00:14:50 2009 +0100

    fix a bogus assert

diff --git a/src/pulse/volume.c b/src/pulse/volume.c
index 4da133d..711c3a9 100644
--- a/src/pulse/volume.c
+++ b/src/pulse/volume.c
@@ -505,7 +505,7 @@ pa_cvolume* pa_cvolume_scale(pa_cvolume *v, pa_volume_t max) {
     unsigned c;
     pa_volume_t t = 0;
 
-    pa_assert(c);
+    pa_assert(v);
 
     for (c = 0; c < v->channels; c++)
         if (v->values[c] > t)

commit 1b53f8297f7ee61b6d99dbbf1ae92aae3d7fdc8b
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jan 30 00:15:40 2009 +0100

    implement pa_cvolume_{get|set}_fade

diff --git a/src/map-file b/src/map-file
index d613759..e076a43 100644
--- a/src/map-file
+++ b/src/map-file
@@ -110,12 +110,14 @@ pa_cvolume_channels_equal_to;
 pa_cvolume_compatible;
 pa_cvolume_equal;
 pa_cvolume_get_balance;
+pa_cvolume_get_fade;
 pa_cvolume_init;
 pa_cvolume_max;
 pa_cvolume_remap;
 pa_cvolume_scale;
 pa_cvolume_set;
 pa_cvolume_set_balance;
+pa_cvolume_set_fade;
 pa_cvolume_snprint;
 pa_cvolume_valid;
 pa_ext_stream_restore_delete;
diff --git a/src/pulse/volume.c b/src/pulse/volume.c
index 711c3a9..a6abce2 100644
--- a/src/pulse/volume.c
+++ b/src/pulse/volume.c
@@ -341,6 +341,28 @@ static pa_bool_t on_lfe(pa_channel_position_t p) {
         p == PA_CHANNEL_POSITION_LFE;
 }
 
+static pa_bool_t on_front(pa_channel_position_t p) {
+    return
+        p == PA_CHANNEL_POSITION_FRONT_LEFT ||
+        p == PA_CHANNEL_POSITION_FRONT_RIGHT ||
+        p == PA_CHANNEL_POSITION_FRONT_CENTER ||
+        p == PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER ||
+        p == PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER ||
+        p == PA_CHANNEL_POSITION_TOP_FRONT_LEFT ||
+        p == PA_CHANNEL_POSITION_TOP_FRONT_RIGHT ||
+        p == PA_CHANNEL_POSITION_TOP_FRONT_CENTER;
+}
+
+static pa_bool_t on_rear(pa_channel_position_t p) {
+    return
+        p == PA_CHANNEL_POSITION_REAR_LEFT ||
+        p == PA_CHANNEL_POSITION_REAR_RIGHT ||
+        p == PA_CHANNEL_POSITION_REAR_CENTER ||
+        p == PA_CHANNEL_POSITION_TOP_REAR_LEFT ||
+        p == PA_CHANNEL_POSITION_TOP_REAR_RIGHT ||
+        p == PA_CHANNEL_POSITION_TOP_REAR_CENTER;
+}
+
 pa_cvolume *pa_cvolume_remap(pa_cvolume *v, const pa_channel_map *from, const pa_channel_map *to) {
     int a, b;
     pa_cvolume result;
@@ -519,3 +541,92 @@ pa_cvolume* pa_cvolume_scale(pa_cvolume *v, pa_volume_t max) {
 
     return v;
 }
+
+static void get_avg_fr(const pa_channel_map *map, const pa_cvolume *v, pa_volume_t *f, pa_volume_t *r) {
+    int c;
+    pa_volume_t front = 0, rear = 0;
+    unsigned n_front = 0, n_rear = 0;
+
+    pa_assert(v);
+    pa_assert(map);
+    pa_assert(map->channels == v->channels);
+    pa_assert(f);
+    pa_assert(r);
+
+    for (c = 0; c < map->channels; c++) {
+        if (on_front(map->map[c])) {
+            front += v->values[c];
+            n_front++;
+        } else if (on_rear(map->map[c])) {
+            rear += v->values[c];
+            n_rear++;
+        }
+    }
+
+    if (n_front <= 0)
+        *f = PA_VOLUME_NORM;
+    else
+        *f = front / n_front;
+
+    if (n_rear <= 0)
+        *r = PA_VOLUME_NORM;
+    else
+        *r = rear / n_rear;
+}
+
+float pa_cvolume_get_fade(const pa_cvolume *v, const pa_channel_map *map) {
+    pa_volume_t front, rear;
+
+    pa_assert(v);
+    pa_assert(map);
+    pa_assert(map->channels == v->channels);
+
+    get_avg_fr(map, v, &front, &rear);
+
+    if (front == rear)
+        return 0.0f;
+
+    if (rear > front)
+        return -1.0f + ((float) front / (float) rear);
+    else
+        return 1.0f - ((float) rear / (float) front);
+}
+
+pa_cvolume* pa_cvolume_set_fade(pa_cvolume *v, const pa_channel_map *map, float new_fade) {
+    pa_volume_t front, nfront, rear, nrear, m;
+    unsigned c;
+
+    pa_assert(map->channels == v->channels);
+    pa_assert(map);
+    pa_assert(v);
+    pa_assert(new_fade >= -1.0f);
+    pa_assert(new_fade <= 1.0f);
+
+    get_avg_fr(map, v, &front, &rear);
+
+    m = PA_MAX(front, rear);
+
+    if (new_fade <= 0) {
+        nfront  = (new_fade + 1.0f) * m;
+        nrear = m;
+    } else  {
+        nrear = (1.0f - new_fade) * m;
+        nfront = m;
+    }
+
+    for (c = 0; c < map->channels; c++) {
+        if (on_front(map->map[c])) {
+            if (front == 0)
+                v->values[c] = nfront;
+            else
+                v->values[c] = (pa_volume_t) (((uint64_t) v->values[c] * (uint64_t) nfront) / (uint64_t) front);
+        } else if (on_rear(map->map[c])) {
+            if (rear == 0)
+                v->values[c] = nrear;
+            else
+                v->values[c] = (pa_volume_t) (((uint64_t) v->values[c] * (uint64_t) nrear) / (uint64_t) rear);
+        }
+    }
+
+    return v;
+}
diff --git a/src/pulse/volume.h b/src/pulse/volume.h
index 9a883ca..8eef467 100644
--- a/src/pulse/volume.h
+++ b/src/pulse/volume.h
@@ -242,12 +242,27 @@ float pa_cvolume_get_balance(const pa_cvolume *v, const pa_channel_map *map) PA_
 /** Adjust the 'balance' value for the specified volume with the
  * specified channel map. v will be modified in place and
  * returned. The balance is a value between -1.0f and +1.0f. This
- * operation might not be reversable! Also, after this call
+ * operation might not be reversible! Also, after this call
  * pa_cvolume_get_balance() is not guaranteed to actually return the
- * requested balance (e.g. when the input volume was zero anyway for
+ * requested balance value (e.g. when the input volume was zero anyway for
  * all channels) \since 0.9.15 */
 pa_cvolume* pa_cvolume_set_balance(pa_cvolume *v, const pa_channel_map *map, float new_balance);
 
+/** Calculate a 'fade' value (i.e. 'balance' between front and rear)
+ * for the specified volume with the specified channel map. The return
+ * value will range from -1.0f (rear) to +1.0f (left) \since
+ * 0.9.15 */
+float pa_cvolume_get_fade(const pa_cvolume *v, const pa_channel_map *map) PA_GCC_PURE;
+
+/** Adjust the 'fade' value (i.e. 'balance' between front and rear)
+ * for the specified volume with the specified channel map. v will be
+ * modified in place and returned. The balance is a value between
+ * -1.0f and +1.0f. This operation might not be reversible! Also,
+ * after this call pa_cvolume_get_fade() is not guaranteed to
+ * actually return the requested fade value (e.g. when the input volume
+ * was zero anyway for all channels) \since 0.9.15 */
+pa_cvolume* pa_cvolume_set_fade(pa_cvolume *v, const pa_channel_map *map, float new_fade);
+
 /** Scale the passed pa_cvolume structure so that the maximum volume
  * of all channels equals max. The proportions between the channel
  * volumes are kept. \since 0.9.15 */

commit 1c94cfe29f552c22fb5807ca048c255710a6b487
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jan 30 00:22:07 2009 +0100

    Add a little Gtk test tool to show how balance/fade/value and the channel volumes play together

diff --git a/src/tests/volume-ui.py b/src/tests/volume-ui.py
new file mode 100644
index 0000000..a2b756e
--- /dev/null
+++ b/src/tests/volume-ui.py
@@ -0,0 +1,227 @@
+#!/usr/bin/python
+
+import pygtk, gtk
+from ctypes import *
+
+libpulse = cdll.LoadLibrary("../.libs/libpulse.so")
+
+class ChannelMap(Structure):
+    _fields_ = [("channels", c_ubyte),
+                ("map", c_uint * 32)]
+
+    _to_name = libpulse.pa_channel_map_to_name
+    _to_name.restype = c_char_p
+    _to_name.argtypes = [c_void_p]
+
+    _to_pretty_name = libpulse.pa_channel_map_to_pretty_name
+    _to_pretty_name.restype = c_char_p
+    _to_pretty_name.argtypes = [c_void_p]
+
+    _snprint = libpulse.pa_channel_map_snprint
+    _snprint.restype = c_char_p
+    _snprint.argtypes = [c_char_p, c_ulong, c_void_p]
+
+    _position_to_string = libpulse.pa_channel_position_to_string
+    _position_to_string.restype = c_char_p
+    _position_to_string.argtypes = [c_uint]
+
+    _position_to_pretty_string = libpulse.pa_channel_position_to_pretty_string
+    _position_to_pretty_string.restype = c_char_p
+    _position_to_pretty_string.argtypes = [c_uint]
+
+    def to_name(this):
+        return this._to_name(byref(this))
+
+    def to_pretty_name(this):
+        return this._to_pretty_name(byref(this))
+
+    def snprint(this):
+        s = create_string_buffer(336)
+        r = this._snprint(s, len(s), byref(this))
+
+        if r is None:
+            return None
+        else:
+            return s.raw
+
+    def position_to_string(this, pos):
+        return this._position_to_string(pos)
+
+    def position_to_pretty_string(this, pos):
+        return this._position_to_pretty_string(pos)
+
+class CVolume(Structure):
+    _fields_ = [("channels", c_ubyte),
+                ("values", c_uint32 * 32)]
+
+
+    _snprint = libpulse.pa_cvolume_snprint
+    _snprint.restype = c_char_p
+    _snprint.argtypes = [c_char_p, c_ulong, c_void_p]
+
+    _max = libpulse.pa_cvolume_max
+    _max.restype = c_uint32
+    _max.argtypes = [c_void_p]
+
+    _scale = libpulse.pa_cvolume_scale
+    _scale.restype = c_void_p
+    _scale.argtypes = [c_void_p, c_uint32]
+
+    _get_balance = libpulse.pa_cvolume_get_balance
+    _get_balance.restype = c_float
+    _get_balance.argtypes = [c_void_p, c_void_p]
+
+    _get_fade = libpulse.pa_cvolume_get_fade
+    _get_fade.restype = c_float
+    _get_fade.argtypes = [c_void_p, c_void_p]
+
+    _set_balance = libpulse.pa_cvolume_set_balance
+    _set_balance.restype = c_void_p
+    _set_balance.argtypes = [c_void_p, c_void_p, c_float]
+
+    _set_fade = libpulse.pa_cvolume_set_fade
+    _set_fade.restype = c_void_p
+    _set_fade.argtypes = [c_void_p, c_void_p, c_float]
+
+    def snprint(this):
+        s = create_string_buffer(320)
+        r = this._snprint(s, len(s), byref(this))
+
+        if r is None:
+            return None
+        else:
+            return s.raw
+
+    def max(this):
+        return this._max(byref(this))
+
+    def scale(this, v):
+        return this._scale(byref(this), v)
+
+    def get_balance(this, cm):
+        return this._get_balance(byref(this), byref(cm))
+
+    def get_fade(this, cm):
+        return this._get_fade(byref(this), byref(cm))
+
+    def set_balance(this, cm, f):
+        return this._set_balance(byref(this), byref(cm), f)
+
+    def set_fade(this, cm, f):
+        return this._set_fade(byref(this), byref(cm), f)
+
+
+
+cm = ChannelMap()
+cm.channels = 6
+cm.map[0] = 1
+cm.map[1] = 2
+cm.map[2] = 3
+cm.map[3] = 5
+cm.map[4] = 6
+cm.map[5] = 7
+
+print "Channel map name: %s" % cm.to_name()
+print "Channel map mapping: %s" % cm.snprint()
+
+v = CVolume()
+v.channels = cm.channels
+
+for i in range(cm.channels):
+    v.values[i] = 65536/2
+
+print v.max()
+print v.snprint()
+print v.get_balance(cm)
+print v.get_fade(cm)
+
+window = gtk.Window(gtk.WINDOW_TOPLEVEL)
+window.set_title(cm.to_pretty_name())
+window.set_border_width(12)
+
+vbox = gtk.VBox(spacing=6)
+
+channel_labels = {}
+channel_scales = {}
+
+def update_volume(update_channels = True, update_fade = True, update_balance = True, update_scale = True):
+    if update_channels:
+        for i in range(cm.channels):
+            channel_scales[i].set_value(v.values[i])
+
+    if update_scale:
+        value_scale.set_value(v.max())
+
+    if update_balance:
+        balance_scale.set_value(v.get_balance(cm))
+
+    if update_fade:
+        fade_scale.set_value(v.get_fade(cm))
+
+def fade_value_changed(fs):
+    v.set_fade(cm, fade_scale.get_value())
+    update_volume(update_fade = False)
+
+def balance_value_changed(fs):
+    v.set_balance(cm, balance_scale.get_value())
+    update_volume(update_balance = False)
+
+def value_value_changed(fs):
+    v.scale(int(value_scale.get_value()))
+    update_volume(update_scale = False)
+
+def channel_value_changed(fs, i):
+    v.values[i] = int(channel_scales[i].get_value())
+    update_volume(update_channels = False)
+
+for i in range(cm.channels):
+    channel_labels[i] = gtk.Label(cm.position_to_pretty_string(cm.map[i]))
+    channel_labels[i].set_alignment(0, 1)
+    vbox.pack_start(channel_labels[i], expand=False, fill=True)
+
+    channel_scales[i] = gtk.HScale()
+    channel_scales[i].set_range(0, 65536)
+    channel_scales[i].set_digits(0)
+    channel_scales[i].set_value_pos(gtk.POS_RIGHT)
+    vbox.pack_start(channel_scales[i], expand=False, fill=True)
+
+value_label = gtk.Label("Value")
+value_label.set_alignment(0, .5)
+vbox.pack_start(value_label, expand=False, fill=True)
+value_scale = gtk.HScale()
+value_scale.set_range(0, 65536)
+value_scale.set_value_pos(gtk.POS_RIGHT)
+value_scale.set_digits(0)
+vbox.pack_start(value_scale, expand=False, fill=True)
+
+balance_label = gtk.Label("Balance")
+balance_label.set_alignment(0, .5)
+vbox.pack_start(balance_label, expand=False, fill=True)
+balance_scale = gtk.HScale()
+balance_scale.set_range(-1.0, +1.0)
+balance_scale.set_value_pos(gtk.POS_RIGHT)
+balance_scale.set_digits(2)
+vbox.pack_start(balance_scale, expand=False, fill=True)
+
+fade_label = gtk.Label("Fade")
+fade_label.set_alignment(0, .5)
+vbox.pack_start(fade_label, expand=False, fill=True)
+fade_scale = gtk.HScale()
+fade_scale.set_range(-1.0, +1.0)
+fade_scale.set_value_pos(gtk.POS_RIGHT)
+fade_scale.set_digits(2)
+vbox.pack_start(fade_scale, expand=False, fill=True)
+
+window.add(vbox)
+window.set_default_size(600, 400)
+
+update_volume()
+
+for i in range(cm.channels):
+    channel_scales[i].connect("value_changed", channel_value_changed, i)
+fade_scale.connect("value_changed", fade_value_changed)
+balance_scale.connect("value_changed", balance_value_changed)
+value_scale.connect("value_changed", value_value_changed)
+
+window.show_all()
+gtk.main()

commit 3442d583aaf1b8f17026791f941b506a891b0464
Merge: 4a75002... 1c94cfe...
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jan 30 00:31:03 2009 +0100

    Merge branch 'master' into lennartsbtfixes


commit 4bd654542e43e61bd0422fd1191a6570e9875c4d
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jan 30 02:24:40 2009 +0100

    add new function pa_alsa_get_driver_name()

diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index 7e5a350..f23056a 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -31,6 +31,7 @@
 #include <pulse/sample.h>
 #include <pulse/xmalloc.h>
 #include <pulse/timeval.h>
+#include <pulse/util.h>
 
 #include <pulsecore/log.h>
 #include <pulsecore/macro.h>
@@ -1477,3 +1478,21 @@ int pa_alsa_safe_mmap_begin(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas
 
     return r;
 }
+
+char *pa_alsa_get_driver_name(int card) {
+    char *t, *m, *n;
+
+    pa_assert(card >= 0);
+
+    t = pa_sprintf_malloc("/sys/class/sound/card%i/device/driver/module", card);
+    m = pa_readlink(t);
+    pa_xfree(t);
+
+    if (!m)
+        return NULL;
+
+    n = pa_xstrdup(pa_path_get_filename(m));
+    pa_xfree(m);
+
+    return n;
+}
diff --git a/src/modules/alsa/alsa-util.h b/src/modules/alsa/alsa-util.h
index f2d3278..8a20934 100644
--- a/src/modules/alsa/alsa-util.h
+++ b/src/modules/alsa/alsa-util.h
@@ -129,4 +129,6 @@ pa_rtpoll_item* pa_alsa_build_pollfd(snd_pcm_t *pcm, pa_rtpoll *rtpoll);
 snd_pcm_sframes_t pa_alsa_safe_avail_update(snd_pcm_t *pcm, size_t hwbuf_size, const pa_sample_spec *ss);
 int pa_alsa_safe_mmap_begin(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas, snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames, size_t hwbuf_size, const pa_sample_spec *ss);
 
+char *pa_alsa_get_driver_name(int card);
+
 #endif

commit e6f4586f7b9ab3c64d3ebe80f4da21b3865d056e
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jan 30 02:24:58 2009 +0100

    include ALSA driver in properties for cards/sink

diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index f23056a..5236d02 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -1254,7 +1254,7 @@ void pa_alsa_redirect_errors_dec(void) {
 }
 
 void pa_alsa_init_proplist_card(pa_core *c, pa_proplist *p, int card) {
-    char *cn, *lcn;
+    char *cn, *lcn, *dn;
 
     pa_assert(p);
     pa_assert(card >= 0);
@@ -1271,6 +1271,11 @@ void pa_alsa_init_proplist_card(pa_core *c, pa_proplist *p, int card) {
         free(lcn);
     }
 
+    if ((dn = pa_alsa_get_driver_name(card))) {
+        pa_proplist_sets(p, "alsa.driver_name", dn);
+        pa_xfree(dn);
+    }
+
 #ifdef HAVE_HAL
     pa_hal_get_info(c, p, card);
 #endif

commit 2557017178b2f745d3473f3a9bb2e1782e4d11da
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jan 30 02:25:22 2009 +0100

    suppress lines made up only of whitespace

diff --git a/src/pulsecore/log.c b/src/pulsecore/log.c
index 9a7f7ca..1ae4383 100644
--- a/src/pulsecore/log.c
+++ b/src/pulsecore/log.c
@@ -278,7 +278,8 @@ void pa_log_levelv_meta(
             n++;
         }
 
-        if (!*t)
+        /* We ignore strings only made out of whitespace */
+        if (t[strspn(t, "\t ")] == 0)
             continue;
 
         switch (log_target) {

commit dbb8951a41b184da065b4966f4ae4d7afbf915e6
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jan 30 02:25:49 2009 +0100

    dump properties when we create a new sink or source

diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 3afeadb..61be86a 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -128,6 +128,7 @@ pa_sink* pa_sink_new(
     char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
     pa_source_new_data source_data;
     const char *dn;
+    char *pt;
 
     pa_assert(core);
     pa_assert(data);
@@ -233,11 +234,14 @@ pa_sink* pa_sink_new(
     if (s->card)
         pa_assert_se(pa_idxset_put(s->card->sinks, s, NULL) >= 0);
 
-    pa_log_info("Created sink %u \"%s\" with sample spec %s and channel map %s",
+    pt = pa_proplist_to_string_sep(s->proplist, "\n    ");
+    pa_log_info("Created sink %u \"%s\" with sample spec %s and channel map %s\n    %s",
                 s->index,
                 s->name,
                 pa_sample_spec_snprint(st, sizeof(st), &s->sample_spec),
-                pa_channel_map_snprint(cm, sizeof(cm), &s->channel_map));
+                pa_channel_map_snprint(cm, sizeof(cm), &s->channel_map),
+                pt);
+    pa_xfree(pt);
 
     pa_source_new_data_init(&source_data);
     pa_source_new_data_set_sample_spec(&source_data, &s->sample_spec);
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index 0152b08..38f8e53 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -119,6 +119,7 @@ pa_source* pa_source_new(
     pa_source *s;
     const char *name;
     char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+    char *pt;
 
     pa_assert(core);
     pa_assert(data);
@@ -222,11 +223,14 @@ pa_source* pa_source_new(
     if (s->card)
         pa_assert_se(pa_idxset_put(s->card->sources, s, NULL) >= 0);
 
-    pa_log_info("Created source %u \"%s\" with sample spec %s and channel map %s",
+    pt = pa_proplist_to_string_sep(s->proplist, "\n    ");
+    pa_log_info("Created source %u \"%s\" with sample spec %s and channel map %s\n    %s",
                 s->index,
                 s->name,
                 pa_sample_spec_snprint(st, sizeof(st), &s->sample_spec),
-                pa_channel_map_snprint(cm, sizeof(cm), &s->channel_map));
+                pa_channel_map_snprint(cm, sizeof(cm), &s->channel_map),
+                pt);
+    pa_xfree(pt);
 
     return s;
 }

commit 6b803217ce473aeb24af208ed1b6e63b8ef42caa
Merge: 3442d58... dbb8951...
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jan 30 04:28:05 2009 +0100

    Merge branch 'master' into lennartsbtfixes


commit f725b06f6f76776cfc6749cd97ee783ee0f89ebf
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sat Jan 31 01:16:16 2009 +0100

    drop -Wpacked

diff --git a/configure.ac b/configure.ac
index c02b20e..7aef2f4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -98,7 +98,7 @@ if test "x$M4" = xno ; then
 fi
 
 dnl Compiler flags
-DESIRED_FLAGS="-Wall -W -Wextra -pedantic -pipe -Wno-long-long -Winline -Wvla -Wno-overlength-strings -Wunsafe-loop-optimizations -Wundef -Wformat=2 -Wlogical-op -Wsign-compare -Wpacked -Wformat-security -Wmissing-include-dirs -Wformat-nonliteral -Wold-style-definition -Wpointer-arith -Winit-self -Wdeclaration-after-statement -Wfloat-equal -Wmissing-prototypes -Wstrict-prototypes -Wredundant-decls -Wmissing-declarations -Wmissing-noreturn -Wshadow -Wendif-labels -Wcast-align -Wstrict-aliasing=2 -Wwrite-strings -Wno-unused-parameter -ffast-math -Wp,-D_FORTIFY_SOURCE=2 -fno-common -fdiagnostics-show-option"
+DESIRED_FLAGS="-Wall -W -Wextra -pedantic -pipe -Wno-long-long -Winline -Wvla -Wno-overlength-strings -Wunsafe-loop-optimizations -Wundef -Wformat=2 -Wlogical-op -Wsign-compare -Wformat-security -Wmissing-include-dirs -Wformat-nonliteral -Wold-style-definition -Wpointer-arith -Winit-self -Wdeclaration-after-statement -Wfloat-equal -Wmissing-prototypes -Wstrict-prototypes -Wredundant-decls -Wmissing-declarations -Wmissing-noreturn -Wshadow -Wendif-labels -Wcast-align -Wstrict-aliasing=2 -Wwrite-strings -Wno-unused-parameter -ffast-math -Wp,-D_FORTIFY_SOURCE=2 -fno-common -fdiagnostics-show-option"
 
 for flag in $DESIRED_FLAGS ; do
   CC_CHECK_CFLAGS([$flag], [CFLAGS="$CFLAGS $flag"])

commit b979ab39482a7ebf9eb1725b5a17141d1afce3d8
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sat Jan 31 01:17:09 2009 +0100

    implement pa_channel_map_can_fade

diff --git a/src/map-file b/src/map-file
index e076a43..3aa1d7a 100644
--- a/src/map-file
+++ b/src/map-file
@@ -10,6 +10,7 @@ pa_bytes_per_second;
 pa_bytes_snprint;
 pa_bytes_to_usec;
 pa_channel_map_can_balance;
+pa_channel_map_can_fade;
 pa_channel_map_compatible;
 pa_channel_map_equal;
 pa_channel_map_init;
diff --git a/src/pulse/channelmap.c b/src/pulse/channelmap.c
index 455bda1..6ff30c2 100644
--- a/src/pulse/channelmap.c
+++ b/src/pulse/channelmap.c
@@ -647,30 +647,77 @@ int pa_channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) {
 
 int pa_channel_map_can_balance(const pa_channel_map *map) {
     unsigned c;
+    pa_bool_t left = FALSE, right = FALSE;
 
     pa_assert(map);
 
-    for (c = 0; c < map->channels; c++)
+    for (c = 0; c < map->channels; c++) {
 
         switch (map->map[c]) {
             case PA_CHANNEL_POSITION_LEFT:
-            case PA_CHANNEL_POSITION_RIGHT:
             case PA_CHANNEL_POSITION_REAR_LEFT:
-            case PA_CHANNEL_POSITION_REAR_RIGHT:
             case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
-            case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
             case PA_CHANNEL_POSITION_SIDE_LEFT:
+            case PA_CHANNEL_POSITION_TOP_FRONT_LEFT:
+            case PA_CHANNEL_POSITION_TOP_REAR_LEFT:
+                left = TRUE;
+                break;
+
+            case PA_CHANNEL_POSITION_RIGHT:
+            case PA_CHANNEL_POSITION_REAR_RIGHT:
+            case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
             case PA_CHANNEL_POSITION_SIDE_RIGHT:
+            case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT:
+            case PA_CHANNEL_POSITION_TOP_REAR_RIGHT:
+                right = TRUE;
+                break;
+
+            default:
+                ;
+        }
+
+        if (left && right)
+            return 1;
+    }
+
+    return 0;
+}
+
+int pa_channel_map_can_fade(const pa_channel_map *map) {
+    unsigned c;
+    pa_bool_t front = FALSE, rear = FALSE;
+
+    for (c = 0; c < map->channels; c++) {
+
+        switch (map->map[c]) {
+            case PA_CHANNEL_POSITION_FRONT_LEFT:
+            case PA_CHANNEL_POSITION_FRONT_RIGHT:
+            case PA_CHANNEL_POSITION_FRONT_CENTER:
+            case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
+            case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
             case PA_CHANNEL_POSITION_TOP_FRONT_LEFT:
             case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT:
+            case PA_CHANNEL_POSITION_TOP_FRONT_CENTER:
+                front = TRUE;
+                break;
+
+            case PA_CHANNEL_POSITION_REAR_LEFT:
+            case PA_CHANNEL_POSITION_REAR_RIGHT:
+            case PA_CHANNEL_POSITION_REAR_CENTER:
             case PA_CHANNEL_POSITION_TOP_REAR_LEFT:
             case PA_CHANNEL_POSITION_TOP_REAR_RIGHT:
-                return 1;
+            case PA_CHANNEL_POSITION_TOP_REAR_CENTER:
+                rear = TRUE;
+                break;
 
             default:
                 ;
         }
 
+        if (front && rear)
+            return 1;
+    }
+
     return 0;
 }
 
diff --git a/src/pulse/channelmap.h b/src/pulse/channelmap.h
index de2d712..f9124b2 100644
--- a/src/pulse/channelmap.h
+++ b/src/pulse/channelmap.h
@@ -239,6 +239,11 @@ int pa_channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) PA
  * available. \since 0.9.15 */
 int pa_channel_map_can_balance(const pa_channel_map *map) PA_GCC_PURE;
 
+/** Returns non-zero if it makes sense to apply a volume 'fade'
+ * (i.e. 'balance' between front and rear) with this mapping, i.e. if
+ * there are front/rear channels available. \since 0.9.15 */
+int pa_channel_map_can_fade(const pa_channel_map *map) PA_GCC_PURE;
+
 /** Tries to find a well-known channel mapping name for this channel
  * mapping. I.e. "stereo", "surround-71" and so on. If the channel
  * mapping is unknown NULL will be returned. This name can be parsed

commit 2c7f2c5e79bca877c01eff3f3d7fb4490a4980ef
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Feb 2 00:17:58 2009 +0100

    look for libpulse in multiple different places

diff --git a/src/tests/volume-ui.py b/src/tests/volume-ui.py
index a2b756e..cb4eddb 100644
--- a/src/tests/volume-ui.py
+++ b/src/tests/volume-ui.py
@@ -3,7 +3,13 @@
 import pygtk, gtk
 from ctypes import *
 
-libpulse = cdll.LoadLibrary("../.libs/libpulse.so")
+try:
+    libpulse = cdll.LoadLibrary("../.libs/libpulse.so")
+except OSError:
+    try:
+        libpulse = cdll.LoadLibrary(".libs/libpulse.so")
+    except OSError:
+        libpulse = cdll.LoadLibrary("libpulse.so")
 
 class ChannelMap(Structure):
     _fields_ = [("channels", c_ubyte),

commit e7007fc68031214676caee27279a979d6501d6b1
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Feb 2 00:18:47 2009 +0100

    allow passing of channel map on command line and hide unused sliders

diff --git a/src/tests/volume-ui.py b/src/tests/volume-ui.py
index cb4eddb..6dc1c47 100644
--- a/src/tests/volume-ui.py
+++ b/src/tests/volume-ui.py
@@ -1,6 +1,6 @@
 #!/usr/bin/python
 
-import pygtk, gtk
+import pygtk, gtk, sys
 from ctypes import *
 
 try:
@@ -35,6 +35,18 @@ class ChannelMap(Structure):
     _position_to_pretty_string.restype = c_char_p
     _position_to_pretty_string.argtypes = [c_uint]
 
+    _can_balance = libpulse.pa_channel_map_can_balance
+    _can_balance.restype = c_int
+    _can_balance.argtypes = [c_void_p]
+
+    _can_fade = libpulse.pa_channel_map_can_fade
+    _can_fade.restype = c_int
+    _can_fade.argtypes = [c_void_p]
+
+    _parse = libpulse.pa_channel_map_parse
+    _parse.restype = c_void_p
+    _parse.argtypes = [c_void_p, c_char_p]
+
     def to_name(this):
         return this._to_name(byref(this))
 
@@ -48,7 +60,7 @@ class ChannelMap(Structure):
         if r is None:
             return None
         else:
-            return s.raw
+            return s.value
 
     def position_to_string(this, pos):
         return this._position_to_string(pos)
@@ -56,11 +68,21 @@ class ChannelMap(Structure):
     def position_to_pretty_string(this, pos):
         return this._position_to_pretty_string(pos)
 
+    def can_balance(this):
+        return bool(this._can_balance(byref(this)))
+
+    def can_fade(this):
+        return bool(this._can_fade(byref(this)))
+
+    def parse(this, s):
+        if this._parse(byref(this), s) is None:
+            raise Exception("Parse failure")
+
+
 class CVolume(Structure):
     _fields_ = [("channels", c_ubyte),
                 ("values", c_uint32 * 32)]
 
-
     _snprint = libpulse.pa_cvolume_snprint
     _snprint.restype = c_char_p
     _snprint.argtypes = [c_char_p, c_ulong, c_void_p]
@@ -116,19 +138,12 @@ class CVolume(Structure):
     def set_fade(this, cm, f):
         return this._set_fade(byref(this), byref(cm), f)
 
-
-
 cm = ChannelMap()
-cm.channels = 6
-cm.map[0] = 1
-cm.map[1] = 2
-cm.map[2] = 3
-cm.map[3] = 5
-cm.map[4] = 6
-cm.map[5] = 7
 
-print "Channel map name: %s" % cm.to_name()
-print "Channel map mapping: %s" % cm.snprint()
+if len(sys.argv) > 1:
+    cm.parse(sys.argv[1])
+else:
+    cm.parse("surround-51")
 
 v = CVolume()
 v.channels = cm.channels
@@ -136,13 +151,12 @@ v.channels = cm.channels
 for i in range(cm.channels):
     v.values[i] = 65536/2
 
-print v.max()
-print v.snprint()
-print v.get_balance(cm)
-print v.get_fade(cm)
+title = cm.to_pretty_name()
+if title is None:
+    title = cm.snprint()
 
 window = gtk.Window(gtk.WINDOW_TOPLEVEL)
-window.set_title(cm.to_pretty_name())
+window.set_title(unicode(title))
 window.set_border_width(12)
 
 vbox = gtk.VBox(spacing=6)
@@ -219,7 +233,7 @@ fade_scale.set_digits(2)
 vbox.pack_start(fade_scale, expand=False, fill=True)
 
 window.add(vbox)
-window.set_default_size(600, 400)
+window.set_default_size(600, 50)
 
 update_volume()
 
@@ -229,5 +243,17 @@ fade_scale.connect("value_changed", fade_value_changed)
 balance_scale.connect("value_changed", balance_value_changed)
 value_scale.connect("value_changed", value_value_changed)
 
-window.show_all()
+vbox.show_all()
+
+if not cm.can_balance():
+    balance_label.hide()
+    balance_scale.hide()
+
+if not cm.can_fade():
+    fade_label.hide()
+    fade_scale.hide()
+
+
+window.show()
+
 gtk.main()

commit 55f643d49ea708b2c95a65aedb93dcc38eb9ec4e
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Feb 2 00:19:26 2009 +0100

    check for NULL before accessing the name

diff --git a/src/pulsecore/namereg.c b/src/pulsecore/namereg.c
index ac456ac..86bcef7 100644
--- a/src/pulsecore/namereg.c
+++ b/src/pulsecore/namereg.c
@@ -194,7 +194,7 @@ void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type) {
 
     }
 
-    if (*name == '@' || !name || !pa_namereg_is_valid_name(name))
+    if (!name || *name == '@' || !pa_namereg_is_valid_name(name))
         return NULL;
 
     if ((e = pa_hashmap_get(c->namereg, name)))

commit b092f2e0f8686d78ceccac07bea4b7d2e327fb67
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Feb 2 00:20:05 2009 +0100

    use uintpr_t when casting between pointers and integers

diff --git a/src/pulsecore/macro.h b/src/pulsecore/macro.h
index f9ce949..5946001 100644
--- a/src/pulsecore/macro.h
+++ b/src/pulsecore/macro.h
@@ -187,17 +187,17 @@ typedef int pa_bool_t;
         abort();                                                        \
     } while (FALSE)
 
-#define PA_PTR_TO_UINT(p) ((unsigned int) (unsigned long) (p))
-#define PA_UINT_TO_PTR(u) ((void*) (unsigned long) (u))
+#define PA_PTR_TO_UINT(p) ((unsigned int) ((uintptr_t) (p)))
+#define PA_UINT_TO_PTR(u) ((void*) ((uintptr_t) (u)))
 
-#define PA_PTR_TO_UINT32(p) ((uint32_t) PA_PTR_TO_UINT(p))
-#define PA_UINT32_TO_PTR(u) PA_UINT_TO_PTR((uint32_t) u)
+#define PA_PTR_TO_UINT32(p) ((uint32_t) ((uintptr_t) (p)))
+#define PA_UINT32_TO_PTR(u) ((void*) ((uintptr_t) (u)))
 
-#define PA_PTR_TO_INT(p) ((int) PA_PTR_TO_UINT(p))
-#define PA_INT_TO_PTR(u) PA_UINT_TO_PTR((int) u)
+#define PA_PTR_TO_INT(p) ((int) ((intptr_t) (p)))
+#define PA_INT_TO_PTR(u) ((void*) ((intptr_t) (u)))
 
-#define PA_PTR_TO_INT32(p) ((int32_t) PA_PTR_TO_UINT(p))
-#define PA_INT32_TO_PTR(u) PA_UINT_TO_PTR((int32_t) u)
+#define PA_PTR_TO_INT32(p) ((int32_t) ((intptr_t) (p)))
+#define PA_INT32_TO_PTR(u) ((void*) ((intptr_t) (u)))
 
 #ifdef OS_IS_WIN32
 #define PA_PATH_SEP "\\"

commit e0fd99b911eb3a7ca5590f0d7504f181a6f8c0f4
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Feb 2 00:20:57 2009 +0100

    work around dlsym() return value mistyping as suggested in POSIX

diff --git a/src/pulsecore/ltdl-helper.c b/src/pulsecore/ltdl-helper.c
index 0d4c22f..ed0b63a 100644
--- a/src/pulsecore/ltdl-helper.c
+++ b/src/pulsecore/ltdl-helper.c
@@ -42,7 +42,9 @@ pa_void_func_t pa_load_sym(lt_dlhandle handle, const char *module, const char *s
     pa_assert(handle);
     pa_assert(symbol);
 
-    if ((f = ((pa_void_func_t) (size_t) lt_dlsym(handle, symbol))))
+    *(void**) &f = lt_dlsym(handle, symbol);
+
+    if (f)
         return f;
 
     if (!module)
@@ -57,7 +59,7 @@ pa_void_func_t pa_load_sym(lt_dlhandle handle, const char *module, const char *s
         if (!isalnum(*c))
             *c = '_';
 
-    f = (pa_void_func_t) (size_t) lt_dlsym(handle, sn);
+    *(void**) &f = lt_dlsym(handle, sn);
     pa_xfree(sn);
 
     return f;

commit 2c97c15c55b7c66020367ce1ea30e745a32f1cd8
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Feb 2 00:21:41 2009 +0100

    introduce PA_PROP_APPLICATION_PROCESS_MACHINE_ID

diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h
index 203a28c..5c79dd0 100644
--- a/src/pulse/proplist.h
+++ b/src/pulse/proplist.h
@@ -65,6 +65,7 @@ PA_C_DECL_BEGIN
  *    application.process.binary
  *    application.process.user
  *    application.process.host
+ *    application.process.machine_id D-Bus machine ID
  *    device.string
  *    device.api                    oss, alsa, sunaudio
  *    device.description
@@ -114,6 +115,7 @@ PA_C_DECL_BEGIN
 #define PA_PROP_APPLICATION_PROCESS_BINARY     "application.process.binary"
 #define PA_PROP_APPLICATION_PROCESS_USER       "application.process.user"
 #define PA_PROP_APPLICATION_PROCESS_HOST       "application.process.host"
+#define PA_PROP_APPLICATION_PROCESS_MACHINE_ID "application.process.machine_id"
 #define PA_PROP_DEVICE_STRING                  "device.string"
 #define PA_PROP_DEVICE_API                     "device.api"
 #define PA_PROP_DEVICE_DESCRIPTION             "device.description"

commit e0d6b75119f6b983d3fb6b3d204c1cce1eefc691
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Feb 2 00:22:02 2009 +0100

    add a few new form factors

diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h
index 5c79dd0..990ffd1 100644
--- a/src/pulse/proplist.h
+++ b/src/pulse/proplist.h
@@ -73,7 +73,7 @@ PA_C_DECL_BEGIN
  *    device.serial
  *    device.vendor_product_id
  *    device.class                  sound, modem, monitor, filter, abstract
- *    device.form_factor            laptop-speakers, external-speakers, telephone, tv-capture, webcam-capture, microphone-capture, headset
+ *    device.form_factor            laptop-speakers, external-speakers, telephone, tv-capture, webcam-capture, microphone-capture, headset, headphones, hands-free, car, hifi, computer, portable
  *    device.connector              isa, pci, usb, firewire, bluetooth
  *    device.access_mode            mmap, mmap_rewrite, serial
  *    device.master_device

commit 04acc232ecd6f06e06bb96137a6dd01135762fd6
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Feb 2 00:23:21 2009 +0100

    download everything from gitweb twice to make sure we don't get a 'Generating...' message

diff --git a/src/Makefile.am b/src/Makefile.am
index dbb2d0a..77123c7 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1023,9 +1023,7 @@ modlibexec_LTLIBRARIES += \
 endif
 endif
 
-
-# These are generated by a M4 script
-
+# These are generated by an M4 script
 SYMDEF_FILES = \
 		modules/module-cli-symdef.h \
 		modules/module-cli-protocol-tcp-symdef.h \
@@ -1545,8 +1543,10 @@ massif: pulseaudio
 update-ffmpeg:
 	wget -O pulsecore/ffmpeg/resample2.c http://svn.mplayerhq.hu/ffmpeg/trunk/libavcodec/resample2.c?view=co
 
+# We get things twice here, because sometimes gitweb will us just give a "Generating..." otherwise.
 update-sbc:
 	for i in $(SBC_FILES); do \
+		wget -O /dev/null http://git.kernel.org/\?p=bluetooth/bluez.git\;a=blob_plain\;f=sbc/$$i ; \
 		wget -O modules/bluetooth/$$i http://git.kernel.org/\?p=bluetooth/bluez.git\;a=blob_plain\;f=sbc/$$i ; \
 	done
 

commit 69a9ed9ef79afe30e3781a175a6990ed756a3984
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Feb 2 00:23:49 2009 +0100

    drop -pedantic

diff --git a/configure.ac b/configure.ac
index 7aef2f4..18d7cd9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -98,7 +98,7 @@ if test "x$M4" = xno ; then
 fi
 
 dnl Compiler flags
-DESIRED_FLAGS="-Wall -W -Wextra -pedantic -pipe -Wno-long-long -Winline -Wvla -Wno-overlength-strings -Wunsafe-loop-optimizations -Wundef -Wformat=2 -Wlogical-op -Wsign-compare -Wformat-security -Wmissing-include-dirs -Wformat-nonliteral -Wold-style-definition -Wpointer-arith -Winit-self -Wdeclaration-after-statement -Wfloat-equal -Wmissing-prototypes -Wstrict-prototypes -Wredundant-decls -Wmissing-declarations -Wmissing-noreturn -Wshadow -Wendif-labels -Wcast-align -Wstrict-aliasing=2 -Wwrite-strings -Wno-unused-parameter -ffast-math -Wp,-D_FORTIFY_SOURCE=2 -fno-common -fdiagnostics-show-option"
+DESIRED_FLAGS="-Wall -W -Wextra -pipe -Wno-long-long -Winline -Wvla -Wno-overlength-strings -Wunsafe-loop-optimizations -Wundef -Wformat=2 -Wlogical-op -Wsign-compare -Wformat-security -Wmissing-include-dirs -Wformat-nonliteral -Wold-style-definition -Wpointer-arith -Winit-self -Wdeclaration-after-statement -Wfloat-equal -Wmissing-prototypes -Wstrict-prototypes -Wredundant-decls -Wmissing-declarations -Wmissing-noreturn -Wshadow -Wendif-labels -Wcast-align -Wstrict-aliasing=2 -Wwrite-strings -Wno-unused-parameter -ffast-math -Wp,-D_FORTIFY_SOURCE=2 -fno-common -fdiagnostics-show-option"
 
 for flag in $DESIRED_FLAGS ; do
   CC_CHECK_CFLAGS([$flag], [CFLAGS="$CFLAGS $flag"])

commit 55e6331bed63a304fe465e737c0ea1a57da91e8c
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Feb 2 00:25:02 2009 +0100

    store the module index shifted by 1 to map PA_INVALID_INDEX to NULL

diff --git a/src/modules/bluetooth/module-bluetooth-discover.c b/src/modules/bluetooth/module-bluetooth-discover.c
index d9d3449..f9540b3 100644
--- a/src/modules/bluetooth/module-bluetooth-discover.c
+++ b/src/modules/bluetooth/module-bluetooth-discover.c
@@ -64,31 +64,31 @@ static void load_module_for_device(struct userdata *u, pa_bluetooth_device *d, p
         d->device_connected > 0 &&
         (d->audio_sink_connected > 0 || d->headset_connected > 0)) {
 
-        if (PA_PTR_TO_UINT(d->data) == PA_INVALID_INDEX) {
+        if (((uint32_t) PA_PTR_TO_UINT(d->data))-1 == PA_INVALID_INDEX) {
             pa_module *m = NULL;
             char *args;
 
             /* Oh, awesome, a new device has shown up and been connected! */
 
-            args = pa_sprintf_malloc("name=\"%s\" address=\"%s\" path=\"%s\"", d->address, d->address, d->path);
+            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); */
+            m = pa_module_load(u->module->core, "module-bluetooth-device", args);
             pa_xfree(args);
 
             if (m)
-                d->data = PA_UINT_TO_PTR(m->index);
+                d->data = PA_UINT_TO_PTR((uint32_t) (m->index+1));
             else
                 pa_log_debug("Failed to load module for device %s", d->path);
         }
 
     } else {
 
-        if (PA_PTR_TO_UINT(d->data) != PA_INVALID_INDEX) {
+        if (((uint32_t) PA_PTR_TO_UINT(d->data))-1 != PA_INVALID_INDEX) {
 
             /* Hmm, disconnection? Then let's unload our module */
 
             pa_log_debug("Unloading module for %s", d->path);
-/*             pa_module_unload_request_by_index(u->core, PA_PTR_TO_UINT(d->data), TRUE); */
+            pa_module_unload_request_by_index(u->core, (uint32_t) (PA_PTR_TO_UINT(d->data))-1, TRUE);
             d->data = NULL;
         }
     }

commit 8fbce6eb895ebb0c793d79a617ef54df645a11db
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Feb 2 00:27:47 2009 +0100

    when determining the minimum volume of all sink inputs make sure to handle the case when there are no sink inputs correctly

diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 61be86a..84f3748 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -926,12 +926,24 @@ void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume) {
     pa_sink_input *i;
     uint32_t idx;
 
+    pa_sink_assert_ref(s);
+    pa_assert(new_volume);
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+    pa_assert(s->flags & PA_SINK_FLAT_VOLUME);
+
     /* This is called whenever a sink input volume changes and we
      * might need to fix up the sink volume accordingly. Please note
      * that we don't actually update the sinks volume here, we only
      * return how it needs to be updated. The caller should then call
      * pa_sink_set_flat_volume().*/
 
+    if (pa_idxset_isempty(s->inputs)) {
+        /* In the special case that we have no sink input we leave the
+         * volume unmodified. */
+        *new_volume = s->virtual_volume;
+        return;
+    }
+
     pa_cvolume_mute(new_volume, s->channel_map.channels);
 
     /* First let's determine the new maximum volume of all inputs
@@ -1133,6 +1145,7 @@ pa_bool_t pa_sink_get_mute(pa_sink *s, pa_bool_t force_refresh) {
 pa_bool_t pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p) {
 
     pa_sink_assert_ref(s);
+    pa_assert(p);
 
     pa_proplist_update(s->proplist, mode, p);
 

commit 8ccc9aa6651f3a21be9c2e843d558b75c8fb0616
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Feb 2 00:28:37 2009 +0100

    try to use glib's g_get_application_name() to set PA_PROP_APPLICATION_NAME

diff --git a/src/pulsecore/proplist-util.c b/src/pulsecore/proplist-util.c
index 35c9985..0a2d718 100644
--- a/src/pulsecore/proplist-util.c
+++ b/src/pulsecore/proplist-util.c
@@ -25,6 +25,7 @@
 
 #include <string.h>
 #include <locale.h>
+#include <dlfcn.h>
 
 #include <pulse/proplist.h>
 #include <pulse/utf8.h>
@@ -36,7 +37,6 @@
 #include "proplist-util.h"
 
 void pa_init_proplist(pa_proplist *p) {
-    int a, b;
 #if !HAVE_DECL_ENVIRON
     extern char **environ;
 #endif
@@ -99,22 +99,36 @@ void pa_init_proplist(pa_proplist *p) {
         }
     }
 
-    a = pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_BINARY);
-    b = pa_proplist_contains(p, PA_PROP_APPLICATION_NAME);
-
-    if (!a || !b) {
+    if (!pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_BINARY)) {
         char t[PATH_MAX];
         if (pa_get_binary_name(t, sizeof(t))) {
             char *c = pa_utf8_filter(t);
+            pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_BINARY, c);
+            pa_xfree(c);
+        }
+    }
 
-            if (!a)
-                pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_BINARY, c);
-            if (!b)
-                pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, c);
+#ifdef RTLD_NOLOAD
+    if (!pa_proplist_contains(p, PA_PROP_APPLICATION_NAME)) {
+        void *dl;
 
-            pa_xfree(c);
+        if ((dl = dlopen("libglib-2.0", RTLD_NOLOAD))) {
+            const char *(*_g_get_application_name)(void);
+
+            if ((*(void**) &_g_get_application_name = dlsym(dl, "g_get_application_name")))
+                pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, _g_get_application_name());
+
+            dlclose(dl);
         }
     }
+#endif
+
+    if (!pa_proplist_contains(p, PA_PROP_APPLICATION_NAME)) {
+        const char *t;
+
+        if ((t = pa_proplist_gets(p, PA_PROP_APPLICATION_PROCESS_BINARY)))
+            pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, t);
+    }
 
     if (!pa_proplist_contains(p, PA_PROP_APPLICATION_LANGUAGE)) {
         const char *l;

commit 16e369498cffd28a806f0ba7b173f3c669c0e820
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Feb 2 00:28:55 2009 +0100

    set PA_PROP_WINDOW_X11_DISPLAY from :0.0 and initialize PA_PROP_APPLICATION_PROCESS_MACHINE_ID properly

diff --git a/src/pulsecore/proplist-util.c b/src/pulsecore/proplist-util.c
index 0a2d718..4920c27 100644
--- a/src/pulsecore/proplist-util.c
+++ b/src/pulsecore/proplist-util.c
@@ -136,4 +136,23 @@ void pa_init_proplist(pa_proplist *p) {
         if ((l = setlocale(LC_MESSAGES, NULL)))
             pa_proplist_sets(p, PA_PROP_APPLICATION_LANGUAGE, l);
     }
+
+    if (!pa_proplist_contains(p, PA_PROP_WINDOW_X11_DISPLAY)) {
+        const char *t;
+
+        if ((t = getenv("DISPLAY"))) {
+            char *c = pa_utf8_filter(t);
+            pa_proplist_sets(p, PA_PROP_WINDOW_X11_DISPLAY, c);
+            pa_xfree(c);
+        }
+    }
+
+    if (!pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_MACHINE_ID)) {
+        char *m;
+
+        if ((m = pa_machine_id())) {
+            pa_proplist_sets(p, PA_PROP_APPLICATION_PROCESS_MACHINE_ID, m);
+            pa_xfree(m);
+        }
+    }
 }

commit d6fdd71c02223fdde027d44c36c14c8e17cfb5b9
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Feb 2 00:30:10 2009 +0100

    add new functions pa_bluetooth_cleanup_name() and pa_bluetooth_get_form_factor()

diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index 019b97e..7855c2e 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -155,7 +155,7 @@ static int parse_device_property(pa_bluetooth_discovery *y, pa_bluetooth_device
 
     dbus_message_iter_recurse(i, &variant_i);
 
-    pa_log_debug("Parsing property org.bluez.Device.%s", key);
+/*     pa_log_debug("Parsing property org.bluez.Device.%s", key); */
 
     switch (dbus_message_iter_get_arg_type(&variant_i)) {
 
@@ -175,7 +175,7 @@ static int parse_device_property(pa_bluetooth_discovery *y, pa_bluetooth_device
                 d->address = pa_xstrdup(value);
             }
 
-            pa_log_debug("Value %s", value);
+/*             pa_log_debug("Value %s", value); */
 
             break;
         }
@@ -192,7 +192,7 @@ static int parse_device_property(pa_bluetooth_discovery *y, pa_bluetooth_device
             else if (pa_streq(key, "Trusted"))
                 d->trusted = !!value;
 
-            pa_log_debug("Value %s", pa_yes_no(value));
+/*             pa_log_debug("Value %s", pa_yes_no(value)); */
 
             break;
         }
@@ -205,7 +205,7 @@ static int parse_device_property(pa_bluetooth_discovery *y, pa_bluetooth_device
             if (pa_streq(key, "Class"))
                 d->class = (int) value;
 
-            pa_log_debug("Value %u", (unsigned) value);
+/*             pa_log_debug("Value %u", (unsigned) value); */
 
             break;
         }
@@ -265,7 +265,7 @@ static int parse_audio_property(pa_bluetooth_discovery *u, int *connected, DBusM
 
     dbus_message_iter_recurse(i, &variant_i);
 
-    pa_log_debug("Parsing property org.bluez.{AudioSink|Headset}.%s", key);
+/*     pa_log_debug("Parsing property org.bluez.{AudioSink|Headset}.%s", key); */
 
     switch (dbus_message_iter_get_arg_type(&variant_i)) {
 
@@ -277,7 +277,7 @@ static int parse_audio_property(pa_bluetooth_discovery *u, int *connected, DBusM
             if (pa_streq(key, "Connected"))
                 *connected = !!value;
 
-            pa_log_debug("Value %s", pa_yes_no(value));
+/*             pa_log_debug("Value %s", pa_yes_no(value)); */
 
             break;
         }
@@ -286,7 +286,6 @@ static int parse_audio_property(pa_bluetooth_discovery *u, int *connected, DBusM
     return 0;
 }
 
-
 static void run_callback(pa_bluetooth_discovery *y, pa_bluetooth_device *d, pa_bool_t good) {
     pa_assert(y);
     pa_assert(d);
@@ -803,3 +802,63 @@ void pa_bluetooth_discovery_sync(pa_bluetooth_discovery *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
index ea6687a..2c3ec64 100644
--- a/src/modules/bluetooth/bluetooth-util.h
+++ b/src/modules/bluetooth/bluetooth-util.h
@@ -74,4 +74,8 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_new(DBusConnection *c, pa_bluetoo
 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

commit 5fc11a0724d19ef634c4aaf9147f5f9b82f5c216
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Feb 2 00:45:00 2009 +0100

    Fix a few sink/source calls when they are called in suspended state.

diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 84f3748..4c822b3 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -547,6 +547,9 @@ void pa_sink_process_rewind(pa_sink *s, size_t nbytes) {
     s->thread_info.rewind_nbytes = 0;
     s->thread_info.rewind_requested = FALSE;
 
+    if (s->thread_info.state == PA_SINK_SUSPENDED)
+        return;
+
     if (nbytes > 0)
         pa_log_debug("Processing rewind...");
 
@@ -556,7 +559,7 @@ void pa_sink_process_rewind(pa_sink *s, size_t nbytes) {
     }
 
     if (nbytes > 0)
-        if (s->monitor_source && PA_SOURCE_IS_OPENED(s->monitor_source->thread_info.state))
+        if (s->monitor_source && PA_SOURCE_IS_LINKED(s->monitor_source->thread_info.state))
             pa_source_process_rewind(s->monitor_source, nbytes);
 }
 
@@ -636,7 +639,7 @@ static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned n, pa_memchunk *
         /* Drop read data */
         pa_sink_input_drop(i, result->length);
 
-        if (s->monitor_source && PA_SOURCE_IS_OPENED(pa_source_get_state(s->monitor_source))) {
+        if (s->monitor_source && PA_SOURCE_IS_LINKED(s->monitor_source->thread_info.state)) {
 
             if (pa_hashmap_size(i->thread_info.direct_outputs) > 0) {
                 void *ostate = NULL;
@@ -692,7 +695,7 @@ static void inputs_drop(pa_sink *s, pa_mix_info *info, unsigned n, pa_memchunk *
         }
     }
 
-    if (s->monitor_source && PA_SOURCE_IS_OPENED(pa_source_get_state(s->monitor_source)))
+    if (s->monitor_source && PA_SOURCE_IS_LINKED(s->monitor_source->thread_info.state))
         pa_source_post(s->monitor_source, result);
 }
 
@@ -703,7 +706,7 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
     size_t block_size_max;
 
     pa_sink_assert_ref(s);
-    pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
+    pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
     pa_assert(pa_frame_aligned(length, &s->sample_spec));
     pa_assert(result);
 
@@ -712,6 +715,13 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
     pa_assert(!s->thread_info.rewind_requested);
     pa_assert(s->thread_info.rewind_nbytes == 0);
 
+    if (s->thread_info.state == PA_SINK_SUSPENDED) {
+        result->memblock = pa_memblock_ref(s->silence.memblock);
+        result->index = s->silence.index;
+        result->length = PA_MIN(s->silence.length, length);
+        return;
+    }
+
     if (length <= 0)
         length = pa_frame_align(MIX_BUFFER_LENGTH, &s->sample_spec);
 
@@ -776,7 +786,7 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
     size_t length, block_size_max;
 
     pa_sink_assert_ref(s);
-    pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
+    pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
     pa_assert(target);
     pa_assert(target->memblock);
     pa_assert(target->length > 0);
@@ -787,6 +797,11 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
     pa_assert(!s->thread_info.rewind_requested);
     pa_assert(s->thread_info.rewind_nbytes == 0);
 
+    if (s->thread_info.state == PA_SINK_SUSPENDED) {
+        pa_silence_memchunk(target, &s->sample_spec);
+        return;
+    }
+
     length = target->length;
     block_size_max = pa_mempool_block_size_max(s->core->mempool);
     if (length > block_size_max)
@@ -854,7 +869,7 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
     size_t l, d;
 
     pa_sink_assert_ref(s);
-    pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
+    pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
     pa_assert(target);
     pa_assert(target->memblock);
     pa_assert(target->length > 0);
@@ -884,7 +899,7 @@ void pa_sink_render_into_full(pa_sink *s, pa_memchunk *target) {
 /* Called from IO thread context */
 void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) {
     pa_sink_assert_ref(s);
-    pa_assert(PA_SINK_IS_OPENED(s->thread_info.state));
+    pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
     pa_assert(length > 0);
     pa_assert(pa_frame_aligned(length, &s->sample_spec));
     pa_assert(result);
@@ -910,7 +925,7 @@ pa_usec_t pa_sink_get_latency(pa_sink *s) {
 
     /* The returned value is supposed to be in the time domain of the sound card! */
 
-    if (!PA_SINK_IS_OPENED(s->state))
+    if (s->state == PA_SINK_SUSPENDED)
         return 0;
 
     if (!(s->flags & PA_SINK_LATENCY))
@@ -985,8 +1000,8 @@ void pa_sink_propagate_flat_volume(pa_sink *s, const pa_cvolume *old_volume) {
     uint32_t idx;
 
     pa_sink_assert_ref(s);
-    pa_assert(PA_SINK_IS_LINKED(s->state));
     pa_assert(old_volume);
+    pa_assert(PA_SINK_IS_LINKED(s->state));
     pa_assert(s->flags & PA_SINK_FLAT_VOLUME);
 
     /* This is called whenever the sink volume changes that is not
@@ -1626,6 +1641,9 @@ void pa_sink_request_rewind(pa_sink*s, size_t nbytes) {
     pa_sink_assert_ref(s);
     pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
 
+    if (s->thread_info.state == PA_SINK_SUSPENDED)
+        return;
+
     if (nbytes == (size_t) -1)
         nbytes = s->thread_info.max_rewind;
 
@@ -1687,7 +1705,7 @@ pa_usec_t pa_sink_get_requested_latency(pa_sink *s) {
     pa_sink_assert_ref(s);
     pa_assert(PA_SINK_IS_LINKED(s->state));
 
-    if (!PA_SINK_IS_OPENED(s->state))
+    if (s->state == PA_SINK_SUSPENDED)
         return 0;
 
     pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index 38f8e53..886f8d6 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -479,7 +479,10 @@ void pa_source_process_rewind(pa_source *s, size_t nbytes) {
     void *state = NULL;
 
     pa_source_assert_ref(s);
-    pa_assert(PA_SOURCE_IS_OPENED(s->thread_info.state));
+    pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
+
+    if (s->thread_info.state == PA_SOURCE_SUSPENDED)
+        return;
 
     if (nbytes <= 0)
         return;
@@ -498,9 +501,12 @@ void pa_source_post(pa_source*s, const pa_memchunk *chunk) {
     void *state = NULL;
 
     pa_source_assert_ref(s);
-    pa_assert(PA_SOURCE_IS_OPENED(s->thread_info.state));
+    pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
     pa_assert(chunk);
 
+    if (s->thread_info.state == PA_SOURCE_SUSPENDED)
+        return;
+
     if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&s->thread_info.soft_volume)) {
         pa_memchunk vchunk = *chunk;
 
@@ -534,11 +540,14 @@ void pa_source_post(pa_source*s, const pa_memchunk *chunk) {
 /* Called from IO thread context */
 void pa_source_post_direct(pa_source*s, pa_source_output *o, const pa_memchunk *chunk) {
     pa_source_assert_ref(s);
-    pa_assert(PA_SOURCE_IS_OPENED(s->thread_info.state));
+    pa_assert(PA_SOURCE_IS_LINKED(s->thread_info.state));
     pa_source_output_assert_ref(o);
     pa_assert(o->thread_info.direct_on_input);
     pa_assert(chunk);
 
+    if (s->thread_info.state == PA_SOURCE_SUSPENDED)
+        return;
+
     if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&s->thread_info.soft_volume)) {
         pa_memchunk vchunk = *chunk;
 
@@ -564,7 +573,7 @@ pa_usec_t pa_source_get_latency(pa_source *s) {
     pa_source_assert_ref(s);
     pa_assert(PA_SOURCE_IS_LINKED(s->state));
 
-    if (!PA_SOURCE_IS_OPENED(s->state))
+    if (s->state == PA_SOURCE_SUSPENDED)
         return 0;
 
     if (!(s->flags & PA_SOURCE_LATENCY))
@@ -675,8 +684,8 @@ pa_bool_t pa_source_get_mute(pa_source *s, pa_bool_t force_refresh) {
 
 /* Called from main thread */
 pa_bool_t pa_source_update_proplist(pa_source *s, pa_update_mode_t mode, pa_proplist *p) {
-
     pa_source_assert_ref(s);
+    pa_assert(p);
 
     pa_proplist_update(s->proplist, mode, p);
 
@@ -1000,7 +1009,7 @@ pa_usec_t pa_source_get_requested_latency(pa_source *s) {
     pa_source_assert_ref(s);
     pa_assert(PA_SOURCE_IS_LINKED(s->state));
 
-    if (!PA_SOURCE_IS_OPENED(s->state))
+    if (s->state == PA_SOURCE_SUSPENDED)
         return 0;
 
     pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);

commit a9e9ab32a280956a29a82876f121d7409705818b
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Feb 2 00:45:54 2009 +0100

    shortcut pa_sink_process_rewind() when no rewind is happenning and none was requested

diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 4c822b3..b149bb6 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -544,6 +544,11 @@ void pa_sink_process_rewind(pa_sink *s, size_t nbytes) {
     pa_sink_assert_ref(s);
     pa_assert(PA_SINK_IS_LINKED(s->thread_info.state));
 
+    /* If nobody requested this and this is actually no real rewind
+     * then we can short cut this */
+    if (!s->thread_info.rewind_requested && nbytes <= 0)
+        return;
+
     s->thread_info.rewind_nbytes = 0;
     s->thread_info.rewind_requested = FALSE;
 

commit 537424a9a9fb8c3fbc930a70874c0983b6c5d1a3
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Feb 2 00:46:11 2009 +0100

    reset rewind_requested when we enter suspend mode

diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index b149bb6..aa79b4a 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -1519,6 +1519,10 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
         case PA_SINK_MESSAGE_SET_STATE:
 
             s->thread_info.state = PA_PTR_TO_UINT(userdata);
+
+            if (s->thread_info.state == PA_SINK_SUSPENDED)
+                s->thread_info.rewind_requested = FALSE;
+
             return 0;
 
         case PA_SINK_MESSAGE_DETACH:

commit a41d72bb2ec84a055bb915803dfa75496f09973b
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Feb 2 01:44:37 2009 +0100

    update sbc stuff

diff --git a/src/Makefile.am b/src/Makefile.am
index 77123c7..f85890e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1448,7 +1448,7 @@ module_bluetooth_discover_la_LDFLAGS = $(MODULE_LDFLAGS)
 module_bluetooth_discover_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore- at PA_MAJORMINORMICRO@.la libdbus-util.la libbluetooth-util.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
 module_bluetooth_discover_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
 
-libbluetooth_sbc_la_SOURCES = modules/bluetooth/sbc.c modules/bluetooth/sbc.h modules/bluetooth/sbc_tables.h modules/bluetooth/sbc_math.h
+libbluetooth_sbc_la_SOURCES = modules/bluetooth/sbc.c modules/bluetooth/sbc.h modules/bluetooth/sbc_tables.h modules/bluetooth/sbc_math.h modules/bluetooth/sbc_primitives.h modules/bluetooth/sbc_primitives.c modules/bluetooth/sbc_primitives_mmx.h modules/bluetooth/sbc_primitives_neon.h modules/bluetooth/sbc_primitives_mmx.c modules/bluetooth/sbc_primitives_neon.c
 libbluetooth_sbc_la_LDFLAGS = -avoid-version
 libbluetooth_sbc_la_LIBADD = $(AM_LIBADD) libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
 libbluetooth_sbc_la_CFLAGS = $(AM_CFLAGS)
@@ -1457,7 +1457,7 @@ SBC_FILES = $(subst modules/bluetooth/,,$(libbluetooth_sbc_la_SOURCES))
 libbluetooth_ipc_la_SOURCES = modules/bluetooth/ipc.c modules/bluetooth/ipc.h
 libbluetooth_ipc_la_LDFLAGS = -avoid-version
 libbluetooth_ipc_la_LIBADD = $(AM_LIBADD)libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
-libbluetooth_ipc_la_CFLAGS = $(AM_CFLAGS) -w
+libbluetooth_ipc_la_CFLAGS = $(AM_CFLAGS)
 
 libbluetooth_util_la_SOURCES = modules/bluetooth/bluetooth-util.c modules/bluetooth/bluetooth-util.h
 libbluetooth_util_la_LDFLAGS = -avoid-version
diff --git a/src/modules/bluetooth/sbc.c b/src/modules/bluetooth/sbc.c
index 651981f..29258d0 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 8ac5930..b0a1488 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 6ca4f52..b87bc81 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 0000000..303f3fe
--- /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 at holtmann.org>
+ *  Copyright (C) 2004-2005  Henryk Ploetz <henryk at ploetzli.ch>
+ *  Copyright (C) 2005-2006  Brad Midgley <bmidgley at 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 0000000..2708c82
--- /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 at holtmann.org>
+ *  Copyright (C) 2004-2005  Henryk Ploetz <henryk at ploetzli.ch>
+ *  Copyright (C) 2005-2006  Brad Midgley <bmidgley at 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 0000000..7db4af7
--- /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 at holtmann.org>
+ *  Copyright (C) 2004-2005  Henryk Ploetz <henryk at ploetzli.ch>
+ *  Copyright (C) 2005-2006  Brad Midgley <bmidgley at 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()
+{
+#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 0000000..c1e44a5
--- /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 at holtmann.org>
+ *  Copyright (C) 2004-2005  Henryk Ploetz <henryk at ploetzli.ch>
+ *  Copyright (C) 2005-2006  Brad Midgley <bmidgley at 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 0000000..d9c12f9
--- /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 at holtmann.org>
+ *  Copyright (C) 2004-2005  Henryk Ploetz <henryk at ploetzli.ch>
+ *  Copyright (C) 2005-2006  Brad Midgley <bmidgley at 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 0000000..30766ed
--- /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 at holtmann.org>
+ *  Copyright (C) 2004-2005  Henryk Ploetz <henryk at ploetzli.ch>
+ *  Copyright (C) 2005-2006  Brad Midgley <bmidgley at 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 f1dfe6c..0057c73 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
+};

commit 2854afbb1b6f2d0686c9803eb18d7c99daf4254d
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Feb 2 01:51:27 2009 +0100

    fix soft_mute handling

diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index aa79b4a..b669a71 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -1506,7 +1506,7 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
 
         case PA_SINK_MESSAGE_SET_MUTE:
 
-            if (!s->thread_info.soft_muted != s->muted) {
+            if (s->thread_info.soft_muted != s->muted) {
                 s->thread_info.soft_muted = s->muted;
                 pa_sink_request_rewind(s, (size_t) -1);
             }

commit 121a8b91906119166880e66485745d8d5f92ab9c
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Feb 2 01:53:11 2009 +0100

    handle EAGAIN properly

diff --git a/src/modules/module-pipe-sink.c b/src/modules/module-pipe-sink.c
index 03e2717..7dd4409 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;
             }

commit e412f1cde383b3984e83358d8bfd19110ec8a9da
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Feb 2 01:54:09 2009 +0100

    whitespace cleanup

diff --git a/src/pulse/volume.c b/src/pulse/volume.c
index a6abce2..ba2ee8f 100644
--- a/src/pulse/volume.c
+++ b/src/pulse/volume.c
@@ -337,11 +337,13 @@ static pa_bool_t on_center(pa_channel_position_t p) {
 }
 
 static pa_bool_t on_lfe(pa_channel_position_t p) {
+
     return
         p == PA_CHANNEL_POSITION_LFE;
 }
 
 static pa_bool_t on_front(pa_channel_position_t p) {
+
     return
         p == PA_CHANNEL_POSITION_FRONT_LEFT ||
         p == PA_CHANNEL_POSITION_FRONT_RIGHT ||
@@ -354,6 +356,7 @@ static pa_bool_t on_front(pa_channel_position_t p) {
 }
 
 static pa_bool_t on_rear(pa_channel_position_t p) {
+
     return
         p == PA_CHANNEL_POSITION_REAR_LEFT ||
         p == PA_CHANNEL_POSITION_REAR_RIGHT ||
diff --git a/src/pulsecore/envelope.c b/src/pulsecore/envelope.c
index f872d34..b57b643 100644
--- a/src/pulsecore/envelope.c
+++ b/src/pulsecore/envelope.c
@@ -599,7 +599,6 @@ void pa_envelope_apply(pa_envelope *e, pa_memchunk *chunk) {
 
         switch (e->sample_spec.format) {
 
-
             case PA_SAMPLE_U8: {
                 uint8_t *t;
 

commit 62f1f3e563317ab4876c11e93dc4af8c5096a3a3
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Feb 2 01:54:40 2009 +0100

    make rtp.h ANSI C compliant

diff --git a/src/modules/bluetooth/rtp.h b/src/modules/bluetooth/rtp.h
index 690bd43..e4881a7 100644
--- a/src/modules/bluetooth/rtp.h
+++ b/src/modules/bluetooth/rtp.h
@@ -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

commit 390133f2dcfab26e17a1660b5d6c9f77e10e72a8
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Feb 2 01:58:48 2009 +0100

    big module-bluetooth-device.c rework

diff --git a/src/Makefile.am b/src/Makefile.am
index f85890e..d608791 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1005,9 +1005,8 @@ modlibexec_LTLIBRARIES += \
 		module-bluetooth-discover.la \
 		libbluetooth-util.la \
 		libbluetooth-ipc.la \
-		libbluetooth-sbc.la
-#\
-#		module-bluetooth-device.la
+		libbluetooth-sbc.la \
+		module-bluetooth-device.la
 
 pulselibexec_PROGRAMS += \
 		proximity-helper
@@ -1464,10 +1463,10 @@ libbluetooth_util_la_LDFLAGS = -avoid-version
 libbluetooth_util_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la libdbus-util.la
 libbluetooth_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
 
-#module_bluetooth_device_la_SOURCES = modules/bluetooth/module-bluetooth-device.c modules/bluetooth/rtp.h
-#module_bluetooth_device_la_LDFLAGS = $(MODULE_LDFLAGS)
-#module_bluetooth_device_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore- at PA_MAJORMINORMICRO@.la libdbus-util.la libbluetooth-ipc.la libbluetooth-sbc.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
-#module_bluetooth_device_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) -w
+module_bluetooth_device_la_SOURCES = modules/bluetooth/module-bluetooth-device.c modules/bluetooth/rtp.h
+module_bluetooth_device_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_bluetooth_device_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore- at PA_MAJORMINORMICRO@.la libdbus-util.la libbluetooth-ipc.la libbluetooth-sbc.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
+module_bluetooth_device_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
 
 # Apple Airtunes/RAOP
 module_raop_sink_la_SOURCES = modules/module-raop-sink.c
diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index e4b834a..de6bded 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -52,7 +52,6 @@
 #include "rtp.h"
 #include "bluetooth-util.h"
 
-#define BUFFER_SIZE 2048
 #define MAX_BITPOOL 64
 #define MIN_BITPOOL 2U
 #define SOL_SCO 17
@@ -87,29 +86,29 @@ static const char* const valid_modargs[] = {
     NULL
 };
 
-struct bt_a2dp {
+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_SCO,
+    PROFILE_HSP,
     PROFILE_OFF
 };
 
 struct userdata {
     pa_core *core;
     pa_module *module;
+
+    pa_card *card;
     pa_sink *sink;
     pa_source *source;
 
@@ -118,104 +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 *address;
     pa_sample_spec sample_spec;
 
     int service_fd;
     int stream_fd;
 
-    uint8_t transport;
     size_t link_mtu;
     size_t block_size;
-    pa_usec_t latency;
 
-    struct bt_a2dp a2dp;
-    char *path;
+    struct a2dp_info a2dp;
     pa_dbus_connection *connection;
 
     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;
         }
-    }
-    else {
-        e = -errno;
-        pa_log_error("Error receiving data from audio service: %s(%d)", pa_cstrerror(errno), errno);
+
+        pa_log_debug("Received %s <- %s",
+                     pa_strnull(bt_audio_strtype(msg->type)),
+                     pa_strnull(bt_audio_strname(msg->name)));
+        return 0;
     }
 
-    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 (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 (codec->transport != BT_CAPABILITIES_TRANSPORT_A2DP)
+    if (u->profile != PROFILE_A2DP)
         return 0;
 
     while (bytes_left > 0) {
@@ -223,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->address, 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;
@@ -324,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)
@@ -379,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);
@@ -397,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;
@@ -445,82 +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->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;
@@ -529,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;
         }
 
@@ -608,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;
+}
+
+static int hsp_process_push(struct userdata *u) {
+    int ret = 0;
+    pa_memchunk memchunk;
 
-        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;
+    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);
+
+        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); */
 
-    } while (a2dp->count + (size_t) written <= u->link_mtu);
+        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);
 
@@ -742,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;
         }
     }
@@ -809,142 +1050,142 @@ 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));
+/*     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);
+/*     dbus_error_init(&err); */
 
-    if (dbus_message_is_signal(msg, "org.bluez.Headset", "PropertyChanged") ||
-        dbus_message_is_signal(msg, "org.bluez.AudioSink", "PropertyChanged")) {
+/*     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;
+/*         struct device *d; */
+/*         const char *profile; */
+/*         DBusMessageIter variant_i; */
+/*         dbus_uint16_t gain; */
 
-        if (!dbus_message_iter_init(msg, &arg_i)) {
-            pa_log("dbus: message has no parameters");
-            goto done;
-        }
+/*         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;
-        }
+/*         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);
+/*         dbus_message_iter_get_basic(&arg_i, &value); */
 
-        if (!dbus_message_iter_next(&arg_i)) {
-            pa_log("Property value missing");
-            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;
-        }
+/*         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);
+/*         dbus_message_iter_recurse(&arg_i, &variant_i); */
 
-        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(&variant_i) != DBUS_TYPE_UINT16) { */
+/*             dbus_message_iter_get_basic(&variant_i, &gain); */
 
-            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;
+/*             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; */
 
-                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);
-            }
-        }
-    }
+/*                 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); */
+/*             } */
+/*         } */
+/*     } */
 
-done:
-    dbus_error_free(&err);
-    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-}
+/* done: */
+/*     dbus_error_free(&err); */
+/*     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; */
+/* } */
 
-static int sink_get_volume_cb(pa_sink *s) {
-    struct userdata *u = s->userdata;
-    pa_assert(u);
+/* static int sink_get_volume_cb(pa_sink *s) { */
+/*     struct userdata *u = s->userdata; */
+/*     pa_assert(u); */
 
-    /* refresh? */
+/*     /\* refresh? *\/ */
 
-    return 0;
-}
+/*     return 0; */
+/* } */
 
-static int source_get_volume_cb(pa_source *s) {
-    struct userdata *u = s->userdata;
-    pa_assert(u);
+/* static int source_get_volume_cb(pa_source *s) { */
+/*     struct userdata *u = s->userdata; */
+/*     pa_assert(u); */
 
-    /* refresh? */
+/*     /\* refresh? *\/ */
 
-    return 0;
-}
+/*     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);
+/* 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);
+/*     dbus_error_init(&e); */
 
-    vol = ((float) pa_cvolume_max(&s->virtual_volume) / PA_VOLUME_NORM) * 15;
-    pa_log_debug("set headset volume: %d", vol);
+/*     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);
+/*     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);
+/*     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);
+/* finish: */
+/*     if (m) */
+/*         dbus_message_unref(m); */
+/*     if (r) */
+/*         dbus_message_unref(r); */
 
-    dbus_error_free(&e);
+/*     dbus_error_free(&e); */
 
-    return 0;
-}
+/*     return 0; */
+/* } */
 
-static int source_set_volume_cb(pa_source *s) {
-    dbus_uint16_t vol;
-    struct userdata *u = s->userdata;
-    pa_assert(u);
+/* 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;
+/*     vol = ((float)pa_cvolume_max(&s->virtual_volume) / PA_VOLUME_NORM) * 15; */
 
-    pa_log_debug("set headset mic volume: %d (not implemented yet)", vol);
+/*     pa_log_debug("set headset mic volume: %d (not implemented yet)", vol); */
 
-    return 0;
-}
+/*     return 0; */
+/* } */
 
 static char *get_name(const char *type, pa_modargs *ma, const char *device_id, pa_bool_t *namereg_fail) {
     char *t;
@@ -980,12 +1221,11 @@ static int add_sink(struct userdata *u) {
 
     pa_sink_new_data_init(&data);
     data.driver = __FILE__;
-    data.module = m;
-    pa_sink_new_data_set_sample_spec(&data, &u->ss);
-    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Bluetooth %s on %s", u->profile == PROFILE_A2DP ? "A2DP" : "HSP/HFP", u->addr);
+    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", ma, u->addr, &b);
+    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);
@@ -998,8 +1238,8 @@ static int add_sink(struct userdata *u) {
 
     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;
+/*     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);
@@ -1013,12 +1253,11 @@ static int add_source(struct userdata *u) {
 
     pa_source_new_data_init(&data);
     data.driver = __FILE__;
-    data.module = m;
-    pa_source_new_data_set_sample_spec(&data, &u->ss);
-    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Bluetooth %s on %s", u->profile == PROFILE_A2DP ? "A2DP" : "HSP/HFP", u->addr);
+    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", ma, u->addr, &b);
+    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);
@@ -1031,8 +1270,8 @@ static int add_source(struct userdata *u) {
 
     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;
+/*     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);
@@ -1040,55 +1279,120 @@ static int add_source(struct userdata *u) {
     return 0;
 }
 
-static void init_profile(struct userdata *u) {
-    enum profile *d;
-
-    pa_assert(u);
-
-    if (u->profile == PROFILE_A2DP ||
-        u->profile == PROFILE_SCO)
-        add_sink(u);
-
-    if (u->profile == PROFILE_SCO)
-        add_source(u);
-}
-
 static int init_bt(struct userdata *u) {
     pa_assert(u);
 
     /* connect to the bluez audio service */
-    if ((u->audioservice_fd = bt_audio_service_open()) < 0) {
+    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");
 
-    /* queries device capabilities */
-    if (bt_getcaps(u) < 0) {
-        pa_log_error("Failed to get device capabilities");
-        return -1;
+    return 0;
+}
+
+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);
+
+    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 setup_bt(struct userdata *u) {
+static int init_profile(struct userdata *u) {
+    int r = 0;
     pa_assert(u);
 
-    /* configures the connection */
-    if (bt_setconf(u) < 0) {
-        pa_log_error("Failed to set config");
+    if (setup_bt(u) < 0)
         return -1;
+
+    if (u->profile == PROFILE_A2DP ||
+        u->profile == PROFILE_HSP)
+        if (add_sink(u) < 0)
+            r = -1;
+
+    if (u->profile == PROFILE_HSP)
+        if (add_source(u) < 0)
+            r = -1;
+
+    return 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;
     }
-    pa_log_debug("Connection to the device configured");
 
-    /* gets the device socket */
-    if (bt_getstreamfd(u) < 0) {
-        pa_log_error("Failed to get stream fd (%d)", 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;
     }
-    pa_log_debug("Got the device socket");
+
+    if (u->sink)
+        pa_sink_put(u->sink);
+
+    if (u->source)
+        pa_source_put(u->source);
+
+    return 0;
 }
 
 static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
@@ -1104,24 +1408,28 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
 
     if (u->sink) {
         inputs = pa_sink_move_all_start(u->sink);
-
         pa_sink_unlink(u->sink);
-        pa_sink_unref(u->sink);
-        u->sink = NULL;
     }
 
     if (u->source) {
         outputs = pa_source_move_all_start(u->source);
-
         pa_source_unlink(u->source);
-        pa_source_unref(u->source);
-        u->source = NULL;
     }
 
-    u->profile = *d;
+    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);
@@ -1139,30 +1447,36 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
     return 0;
 }
 
-static int add_card(struct userdata *u) {
+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;
-    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->addr);
-    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*\/ */
-    data.name = get_name("card", ma, u->addr, &b);
+
+    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->transport == BT_CAPABILITIES_TRANSPORT_A2DP ||
-        u->transport == BT_CAPABILITIES_TRANSPORT_ANY) {
-
+    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;
@@ -1172,11 +1486,11 @@ static int add_card(struct userdata *u) {
 
         d = PA_CARD_PROFILE_DATA(p);
         *d = PROFILE_A2DP;
-    }
 
-    if (u->transport == BT_CAPABILITIES_TRANSPORT_SCO ||
-        u->transport == BT_CAPABILITIES_TRANSPORT_ANY) {
+        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;
@@ -1185,7 +1499,9 @@ static int add_card(struct userdata *u) {
         p->max_source_channels = 1;
 
         d = PA_CARD_PROFILE_DATA(p);
-        *d = PROFILE_SCO;
+        *d = PROFILE_HSP;
+
+        pa_hashmap_put(data.profiles, p->name, p);
     }
 
     pa_assert(!pa_hashmap_isempty(data.profiles));
@@ -1193,8 +1509,16 @@ static int add_card(struct userdata *u) {
     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);
 
-    u->card = pa_card_new(m->core, &data);
+    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) {
@@ -1202,8 +1526,8 @@ static int add_card(struct userdata *u) {
         return -1;
     }
 
-    card->userdata = u;
-    card->set_profile = card_set_profile;
+    u->card->userdata = u;
+    u->card->set_profile = card_set_profile;
 
     d = PA_CARD_PROFILE_DATA(u->card->active_profile);
     u->profile = *d;
@@ -1211,16 +1535,54 @@ static int add_card(struct userdata *u) {
     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);
+
+    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;
+        }
+
+        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) {
     pa_modargs *ma;
     uint32_t channels;
-    pa_sink_new_data sink_data;
-    pa_source_new_data source_data;
-    pa_card card_data;
-    struct pollfd *pollfd;
     struct userdata *u;
-    pa_bool_t b;
-    const char *p;
+    const char *address, *path;
 
     pa_assert(m);
 
@@ -1232,119 +1594,92 @@ int pa__init(pa_module* m) {
     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->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->add_source = m->core->default_sample_spec;
+    u->sample_spec = m->core->default_sample_spec;
+    u->modargs = ma;
 
-    if (!(u->address = pa_xstrdup(pa_modargs_get_value(ma, "address", NULL)))) {
-        pa_log_error("Failed to get device address from module arguments");
-        goto fail;
-    }
-
-    u->path = pa_xstrdup(pa_modargs_get_value(ma, "path", NULL));
-
-    p = pa_modargs_get_value(ma, "profile", NULL);
-    if (p && !pa_streq(p, "hsp") && !pa_streq(p, "a2dp")) {
-        pa_log_error("Failed to get profile from module arguments");
-        goto fail;
-    }
-
-    if (pa_modargs_get_value_u32(ma, "rate", &u->ss.rate) < 0 ||
-        u->ss.rate <= 0 || u->ss.rate > PA_RATE_MAX) {
+    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;
     }
 
-    channels = u->ss.channels;
+    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 BT service and query capabilities */
-    if (init_bt(u) < 0)
+    if (setup_dbus(u) < 0)
         goto fail;
 
-    /* Add the card structure. This will also initialize the default profile */
-    if (add_card(u) < 0)
-        goto fail;
+    address = pa_modargs_get_value(ma, "address", NULL);
+    path = pa_modargs_get_value(ma, "path", NULL);
 
-    /* Now configure the the right profile */
-    if (setup_bt(u) < 0)
-
-    if (init_profile(u) < 0)
+    if (find_device(u, address, path) < 0)
         goto fail;
 
-    if (u->path) {
-        DBusError err;
-        dbus_error_init(&err);
-        char *t;
-
-        u->conn = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &err);
-        if (dbus_error_is_set(&err) || (!u->conn)) {
-            pa_log("Failed to get D-Bus connection: %s", err.message);
-            goto fail;
-        }
-
-        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;
-            }
-        }
-    }
+    pa_assert(u->device);
 
-    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;
+    /* 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;
 
-    if (!(u->thread = pa_thread_new(thread_func, u))) {
-        pa_log_error("Failed to create IO thread");
+    /* Connect to the BT service and query capabilities */
+    if (init_bt(u) < 0)
         goto fail;
-    }
 
-    if (u->sink)
-        pa_sink_put(u->sink);
+    if (init_profile(u) < 0)
+        goto fail;
 
-    if (u->source)
-        pa_sink_put(u->source);
+/*     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;
 
-    pa_modargs_free(ma);
     return 0;
 
 fail:
-    if (ma)
-        pa_modargs_free(ma);
-
     pa__done(m);
     return -1;
 }
@@ -1367,78 +1702,72 @@ void pa__done(pa_module *m) {
     if (!(u = m->userdata))
         return;
 
-    if (u->conn) {
-        DBusError error;
-        char *t;
+    if (u->sink)
+        pa_sink_unlink(u->sink);
 
-        if (u->transport == BT_CAPABILITIES_TRANSPORT_SCO ||
-            u->transport == BT_CAPABILITIES_TRANSPORT_ANY) {
+    if (u->source)
+        pa_source_unlink(u->source);
 
-            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);
-        }
+    stop_thread(u);
 
-        if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP ||
-            u->transport == BT_CAPABILITIES_TRANSPORT_ANY) {
+    if (u->connection) {
+/*         DBusError error; */
+/*         char *t; */
 
-            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);
-        }
+/*         if (u->transport == BT_CAPABILITIES_TRANSPORT_SCO || */
+/*             u->transport == BT_CAPABILITIES_TRANSPORT_ANY) { */
 
-        dbus_connection_remove_filter(pa_dbus_connection_get(u->conn), filter_cb, u);
-        pa_dbus_connection_unref(u->conn);
-    }
+/*             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->path)
-        pa_xfree(u->path);
+/*         if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP || */
+/*             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.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); */
+/*         } */
 
-    if (u->source)
-        pa_source_unlink(u->source);
-
-    if (u->thread) {
-        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
-        pa_thread_free(u->thread);
+/*         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->source)
-        pa_source_unref(u->source);
-
     if (u->card)
-        pa_card_unref(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)
+    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);
 }

commit fea675724d209d3ff8a808a111fe26f7c6d331f9
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Feb 2 02:02:31 2009 +0100

    don't use PA_STREAM_NOT_MONOTONOUS anymore

diff --git a/src/pulse/stream.c b/src/pulse/stream.c
index 5a29bd6..4efe63e 100644
--- a/src/pulse/stream.c
+++ b/src/pulse/stream.c
@@ -877,7 +877,7 @@ static int create_stream(
     PA_CHECK_VALIDITY(s->context, s->direct_on_input == PA_INVALID_INDEX || direction == PA_STREAM_RECORD, PA_ERR_BADSTATE);
     PA_CHECK_VALIDITY(s->context, !(flags & ~(PA_STREAM_START_CORKED|
                                               PA_STREAM_INTERPOLATE_TIMING|
-                                              PA_STREAM_NOT_MONOTONOUS|
+                                              PA_STREAM_NOT_MONOTONIC|
                                               PA_STREAM_AUTO_TIMING_UPDATE|
                                               PA_STREAM_NO_REMAP_CHANNELS|
                                               PA_STREAM_NO_REMIX_CHANNELS|
@@ -1902,7 +1902,7 @@ int pa_stream_get_time(pa_stream *s, pa_usec_t *r_usec) {
         usec = calc_time(s, FALSE);
 
     /* Make sure the time runs monotonically */
-    if (!(s->flags & PA_STREAM_NOT_MONOTONOUS)) {
+    if (!(s->flags & PA_STREAM_NOT_MONOTONIC)) {
         if (usec < s->previous_time)
             usec = s->previous_time;
         else
diff --git a/src/pulse/stream.h b/src/pulse/stream.h
index 6cb363c..1bec1eb 100644
--- a/src/pulse/stream.h
+++ b/src/pulse/stream.h
@@ -532,7 +532,7 @@ pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_succe
  * value returned by this function is guaranteed to increase
  * monotonically. (that means: the returned value is always greater or
  * equal to the value returned on the last call) This behaviour can
- * be disabled by using PA_STREAM_NOT_MONOTONOUS. This may be
+ * be disabled by using PA_STREAM_NOT_MONOTONIC. This may be
  * desirable to deal better with bad estimations of transport
  * latencies, but may have strange effects if the application is not
  * able to deal with time going 'backwards'. */

commit 59f3001ca5d48daa2583d1b1546ef6c90232c569
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Feb 3 01:33:48 2009 +0100

    pull in new SBC/BT files

diff --git a/src/modules/bluetooth/ipc.c b/src/modules/bluetooth/ipc.c
index 6778530..f14c92c 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 at holtmann.org>
+ *  Copyright (C) 2004-2009  Marcel Holtmann <marcel at 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 0e985c3..4203150 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 at holtmann.org>
+ *  Copyright (C) 2004-2009  Marcel Holtmann <marcel at 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/rtp.h b/src/modules/bluetooth/rtp.h
index e4881a7..1457362 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 at holtmann.org>
+ *  Copyright (C) 2004-2009  Marcel Holtmann <marcel at holtmann.org>
  *
  *
  *  This library is free software; you can redistribute it and/or
diff --git a/src/modules/bluetooth/sbc_primitives_mmx.c b/src/modules/bluetooth/sbc_primitives_mmx.c
index 7db4af7..1870a9b 100644
--- a/src/modules/bluetooth/sbc_primitives_mmx.c
+++ b/src/modules/bluetooth/sbc_primitives_mmx.c
@@ -275,7 +275,7 @@ static inline void sbc_analyze_4b_8s_mmx(int16_t *x, int32_t *out,
 	asm volatile ("emms\n");
 }
 
-static int check_mmx_support()
+static int check_mmx_support(void)
 {
 #ifdef __amd64__
 	return 1; /* We assume that all 64-bit processors have MMX support */

commit f8190be2e675ebf5ebe5750a74818b02969539bb
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Feb 3 01:34:38 2009 +0100

    make update-sbc should also update other BT related sources

diff --git a/src/Makefile.am b/src/Makefile.am
index d608791..d2917a0 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1451,12 +1451,13 @@ libbluetooth_sbc_la_SOURCES = modules/bluetooth/sbc.c modules/bluetooth/sbc.h mo
 libbluetooth_sbc_la_LDFLAGS = -avoid-version
 libbluetooth_sbc_la_LIBADD = $(AM_LIBADD) libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
 libbluetooth_sbc_la_CFLAGS = $(AM_CFLAGS)
-SBC_FILES = $(subst modules/bluetooth/,,$(libbluetooth_sbc_la_SOURCES))
+BLUETOOTH_SBC_FILES = $(subst modules/bluetooth/,,$(libbluetooth_sbc_la_SOURCES))
 
 libbluetooth_ipc_la_SOURCES = modules/bluetooth/ipc.c modules/bluetooth/ipc.h
 libbluetooth_ipc_la_LDFLAGS = -avoid-version
 libbluetooth_ipc_la_LIBADD = $(AM_LIBADD)libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
 libbluetooth_ipc_la_CFLAGS = $(AM_CFLAGS)
+BLUETOOTH_IPC_FILES = $(subst modules/bluetooth/,,$(libbluetooth_ipc_la_SOURCES)) rtp.h
 
 libbluetooth_util_la_SOURCES = modules/bluetooth/bluetooth-util.c modules/bluetooth/bluetooth-util.h
 libbluetooth_util_la_LDFLAGS = -avoid-version
@@ -1544,10 +1545,14 @@ update-ffmpeg:
 
 # We get things twice here, because sometimes gitweb will us just give a "Generating..." otherwise.
 update-sbc:
-	for i in $(SBC_FILES); do \
+	for i in $(BLUETOOTH_SBC_FILES) ; do \
 		wget -O /dev/null http://git.kernel.org/\?p=bluetooth/bluez.git\;a=blob_plain\;f=sbc/$$i ; \
 		wget -O modules/bluetooth/$$i http://git.kernel.org/\?p=bluetooth/bluez.git\;a=blob_plain\;f=sbc/$$i ; \
 	done
+	for i in $(BLUETOOTH_IPC_FILES); do \
+		wget -O /dev/null http://git.kernel.org/\?p=bluetooth/bluez.git\;a=blob_plain\;f=audio/$$i ; \
+		wget -O modules/bluetooth/$$i http://git.kernel.org/\?p=bluetooth/bluez.git\;a=blob_plain\;f=audio/$$i ; \
+	done
 
 # Automatically generate linker version script. We use the same one for all public .sos
 update-map-file:

commit c61ad2a70619fc2fcec209838b37e047df2d1d2d
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Feb 3 02:15:36 2009 +0100

    make iterating with pa_idxset_next() robust in regards to idxset modifications

diff --git a/src/pulsecore/idxset.c b/src/pulsecore/idxset.c
index 24a28db..352ac97 100644
--- a/src/pulsecore/idxset.c
+++ b/src/pulsecore/idxset.c
@@ -386,8 +386,11 @@ void* pa_idxset_steal_first(pa_idxset *s, uint32_t *idx) {
 void* pa_idxset_first(pa_idxset *s, uint32_t *idx) {
     pa_assert(s);
 
-    if (!s->iterate_list_head)
+    if (!s->iterate_list_head) {
+        if (idx)
+            *idx = PA_IDXSET_INVALID;
         return NULL;
+    }
 
     if (idx)
         *idx = s->iterate_list_head->idx;
@@ -402,20 +405,41 @@ void *pa_idxset_next(pa_idxset *s, uint32_t *idx) {
     pa_assert(s);
     pa_assert(idx);
 
+    if (*idx == PA_IDXSET_INVALID)
+        return NULL;
+
     hash = *idx % NBUCKETS;
 
-    if (!(e = index_scan(s, hash, *idx)))
-        return NULL;
+    if ((e = index_scan(s, hash, *idx))) {
+
+        e = e->iterate_next;
+
+        if (e) {
+            *idx = e->idx;
+            return e->data;
+        } else {
+            *idx = PA_IDXSET_INVALID;
+            return NULL;
+        }
+
+    } else {
+
+        /* If the entry passed doesn't exist anymore we try to find
+         * the next following */
+
+        for ((*idx)++; *idx < s->current_index; (*idx)++) {
+
+            hash = *idx % NBUCKETS;
+
+            if ((e = index_scan(s, hash, *idx))) {
+                *idx = e->idx;
+                return e->data;
+            }
+        }
 
-    if (!e->iterate_next) {
         *idx = PA_IDXSET_INVALID;
         return NULL;
     }
-
-    e = e->iterate_next;
-
-    *idx = e->idx;
-    return e->data;
 }
 
 unsigned pa_idxset_size(pa_idxset*s) {

commit e47d03dea446d0387a7c07a470c9c680537d9c34
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Feb 3 02:23:46 2009 +0100

    implement PA_STREAM_FAIL_ON_SUSPEND logic

diff --git a/src/pulse/def.h b/src/pulse/def.h
index 7517a7a..85ac701 100644
--- a/src/pulse/def.h
+++ b/src/pulse/def.h
@@ -237,12 +237,17 @@ typedef enum pa_stream_flags {
      * checked whether the device this stream is connected to should
      * auto-suspend. \since 0.9.15 */
 
-    PA_STREAM_START_UNMUTED = 0x10000U
+    PA_STREAM_START_UNMUTED = 0x10000U,
     /**< Create in unmuted state. If neither PA_STREAM_START_UNMUTED
      * nor PA_STREAM_START_MUTED it is left to the server to decide
      * whether to create the stream in muted or in unmuted
      * state. \since 0.9.15 */
 
+    PA_STREAM_FAIL_ON_SUSPEND = 0x20000U
+    /**< If the sink/source this stream is connected to is suspended
+     * during the creation of this stream, cause it to fail. If the
+     * sink/source is being suspended during creation of this stream,
+     * make sure this stream is terminated. \since 0.9.15 */
 } pa_stream_flags_t;
 
 /** \cond fulldocs */
@@ -268,6 +273,7 @@ typedef enum pa_stream_flags {
 #define PA_STREAM_EARLY_REQUESTS PA_STREAM_EARLY_REQUESTS
 #define PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND
 #define PA_STREAM_START_UNMUTED PA_STREAM_START_UNMUTED
+#define PA_STREAM_FAIL_ON_SUSPEND PA_STREAM_FAIL_ON_SUSPEND
 
 /** \endcond */
 
diff --git a/src/pulse/stream.c b/src/pulse/stream.c
index 4efe63e..fe2514d 100644
--- a/src/pulse/stream.c
+++ b/src/pulse/stream.c
@@ -891,7 +891,8 @@ static int create_stream(
                                               PA_STREAM_ADJUST_LATENCY|
                                               PA_STREAM_EARLY_REQUESTS|
                                               PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND|
-                                              PA_STREAM_START_UNMUTED)), PA_ERR_INVALID);
+                                              PA_STREAM_START_UNMUTED|
+                                              PA_STREAM_FAIL_ON_SUSPEND)), PA_ERR_INVALID);
 
     PA_CHECK_VALIDITY(s->context, s->context->version >= 12 || !(flags & PA_STREAM_VARIABLE_RATE), PA_ERR_NOTSUPPORTED);
     PA_CHECK_VALIDITY(s->context, s->context->version >= 13 || !(flags & PA_STREAM_PEAK_DETECT), PA_ERR_NOTSUPPORTED);
@@ -1017,6 +1018,7 @@ static int create_stream(
             pa_tagstruct_put_boolean(t, flags & (PA_STREAM_START_MUTED|PA_STREAM_START_UNMUTED));
 
         pa_tagstruct_put_boolean(t, flags & PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND);
+        pa_tagstruct_put_boolean(t, flags & PA_STREAM_FAIL_ON_SUSPEND);
     }
 
     pa_pstream_send_tagstruct(s->context->pstream, t);
diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index 57129d0..8ca8f2d 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -421,7 +421,7 @@ char *pa_source_output_list_to_string(pa_core *c) {
             s,
             "    index: %u\n"
             "\tdriver: <%s>\n"
-            "\tflags: %s%s%s%s%s%s%s%s\n"
+            "\tflags: %s%s%s%s%s%s%s%s%s%s\n"
             "\tstate: %s\n"
             "\tsource: %u <%s>\n"
             "\tcurrent latency: %0.2f ms\n"
@@ -439,6 +439,8 @@ char *pa_source_output_list_to_string(pa_core *c) {
             o->flags & PA_SOURCE_OUTPUT_FIX_FORMAT ? "FIX_FORMAT " : "",
             o->flags & PA_SOURCE_OUTPUT_FIX_RATE ? "FIX_RATE " : "",
             o->flags & PA_SOURCE_OUTPUT_FIX_CHANNELS ? "FIX_CHANNELS " : "",
+            o->flags & PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND ? "DONT_INHIBIT_AUTO_SUSPEND " : "",
+            o->flags & PA_SOURCE_OUTPUT_FAIL_ON_SUSPEND ? "FAIL_ON_SUSPEND " : "",
             state_table[pa_source_output_get_state(o)],
             o->source->index, o->source->name,
             (double) pa_source_output_get_latency(o, NULL) / PA_USEC_PER_MSEC,
@@ -498,7 +500,7 @@ char *pa_sink_input_list_to_string(pa_core *c) {
             s,
             "    index: %u\n"
             "\tdriver: <%s>\n"
-            "\tflags: %s%s%s%s%s%s%s%s\n"
+            "\tflags: %s%s%s%s%s%s%s%s%s%s\n"
             "\tstate: %s\n"
             "\tsink: %u <%s>\n"
             "\tvolume: %s\n"
@@ -520,6 +522,8 @@ char *pa_sink_input_list_to_string(pa_core *c) {
             i->flags & PA_SINK_INPUT_FIX_FORMAT ? "FIX_FORMAT " : "",
             i->flags & PA_SINK_INPUT_FIX_RATE ? "FIX_RATE " : "",
             i->flags & PA_SINK_INPUT_FIX_CHANNELS ? "FIX_CHANNELS " : "",
+            i->flags & PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND ? "DONT_INHIBIT_AUTO_SUSPEND " : "",
+            i->flags & PA_SINK_INPUT_FAIL_ON_SUSPEND ? "FAIL_ON_SUSPEND " : "",
             state_table[pa_sink_input_get_state(i)],
             i->sink->index, i->sink->name,
             pa_cvolume_snprint(cv, sizeof(cv), pa_sink_input_get_volume(i)),
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index c962165..e8080bf 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -1691,8 +1691,8 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
         adjust_latency = FALSE,
         early_requests = FALSE,
         dont_inhibit_auto_suspend = FALSE,
-        muted_set = FALSE;
-
+        muted_set = FALSE,
+        fail_on_suspend = FALSE;
     pa_sink_input_flags_t flags = 0;
     pa_proplist *p;
     pa_bool_t volume_set = TRUE;
@@ -1775,7 +1775,8 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
     if (c->version >= 15) {
 
         if (pa_tagstruct_get_boolean(t, &muted_set) < 0 ||
-            pa_tagstruct_get_boolean(t, &dont_inhibit_auto_suspend) < 0) {
+            pa_tagstruct_get_boolean(t, &dont_inhibit_auto_suspend) < 0 ||
+            pa_tagstruct_get_boolean(t, &fail_on_suspend) < 0) {
             protocol_error(c);
             pa_proplist_free(p);
             return;
@@ -1814,7 +1815,8 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
         (fix_channels ?  PA_SINK_INPUT_FIX_CHANNELS : 0) |
         (no_move ?  PA_SINK_INPUT_DONT_MOVE : 0) |
         (variable_rate ?  PA_SINK_INPUT_VARIABLE_RATE : 0) |
-        (dont_inhibit_auto_suspend ? PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND : 0);
+        (dont_inhibit_auto_suspend ? PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND : 0) |
+        (fail_on_suspend ? PA_SINK_INPUT_FAIL_ON_SUSPEND : 0);
 
     /* Only since protocol version 15 there's a seperate muted_set
      * flag. For older versions we synthesize it here */
@@ -1942,7 +1944,8 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
         adjust_latency = FALSE,
         peak_detect = FALSE,
         early_requests = FALSE,
-        dont_inhibit_auto_suspend = FALSE;
+        dont_inhibit_auto_suspend = FALSE,
+        fail_on_suspend = FALSE;
     pa_source_output_flags_t flags = 0;
     pa_proplist *p;
     uint32_t direct_on_input_idx = PA_INVALID_INDEX;
@@ -2016,7 +2019,8 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
 
     if (c->version >= 15) {
 
-        if (pa_tagstruct_get_boolean(t, &dont_inhibit_auto_suspend) < 0) {
+        if (pa_tagstruct_get_boolean(t, &dont_inhibit_auto_suspend) < 0 ||
+            pa_tagstruct_get_boolean(t, &fail_on_suspend) < 0) {
             protocol_error(c);
             pa_proplist_free(p);
             return;
@@ -2064,7 +2068,8 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
         (fix_channels ?  PA_SOURCE_OUTPUT_FIX_CHANNELS : 0) |
         (no_move ?  PA_SOURCE_OUTPUT_DONT_MOVE : 0) |
         (variable_rate ?  PA_SOURCE_OUTPUT_VARIABLE_RATE : 0) |
-        (dont_inhibit_auto_suspend ? PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND : 0);
+        (dont_inhibit_auto_suspend ? PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND : 0) |
+        (fail_on_suspend ? PA_SOURCE_OUTPUT_FAIL_ON_SUSPEND : 0);
 
     s = record_stream_new(c, source, &ss, &map, peak_detect, &maxlength, &fragment_size, flags, p, adjust_latency, direct_on_input, early_requests);
     pa_proplist_free(p);
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index d4d1119..8bc434e 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -144,9 +144,13 @@ pa_sink_input* pa_sink_input_new(
     }
 
     pa_return_null_if_fail(data->sink);
-    pa_return_null_if_fail(pa_sink_get_state(data->sink) != PA_SINK_UNLINKED);
+    pa_return_null_if_fail(PA_SINK_IS_LINKED(pa_sink_get_state(data->sink)));
     pa_return_null_if_fail(!data->sync_base || (data->sync_base->sink == data->sink && pa_sink_input_get_state(data->sync_base) == PA_SINK_INPUT_CORKED));
 
+    if ((flags & PA_SINK_INPUT_FAIL_ON_SUSPEND) &&
+        pa_sink_get_state(data->sink) == PA_SINK_SUSPENDED)
+        return NULL;
+
     if (!data->sample_spec_is_set)
         data->sample_spec = data->sink->sample_spec;
 
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index 893d869..c27f625 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -58,6 +58,7 @@ typedef enum pa_sink_input_flags {
     PA_SINK_INPUT_FIX_RATE = 64,
     PA_SINK_INPUT_FIX_CHANNELS = 128,
     PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND = 256,
+    PA_SINK_INPUT_FAIL_ON_SUSPEND = 512
 } pa_sink_input_flags_t;
 
 struct pa_sink_input {
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index b669a71..a3b6b14 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -305,6 +305,11 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
 
     s->state = state;
 
+    if (state != PA_SINK_UNLINKED) { /* if we enter UNLINKED state pa_sink_unlink() will fire the apropriate events */
+        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], s);
+        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+    }
+
     if (suspend_change) {
         pa_sink_input *i;
         uint32_t idx;
@@ -312,15 +317,13 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
         /* We're suspending or resuming, tell everyone about it */
 
         for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx)))
-            if (i->suspend)
+            if (s->state == PA_SINK_SUSPENDED &&
+                (i->flags & PA_SINK_INPUT_FAIL_ON_SUSPEND))
+                pa_sink_input_kill(i);
+            else if (i->suspend)
                 i->suspend(i, state == PA_SINK_SUSPENDED);
     }
 
-    if (state != PA_SINK_UNLINKED) { /* if we enter UNLINKED state pa_sink_unlink() will fire the apropriate events */
-        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], s);
-        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
-    }
-
     return 0;
 }
 
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index 1e1ac4e..9ef9b50 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -118,10 +118,13 @@ pa_source_output* pa_source_output_new(
     }
 
     pa_return_null_if_fail(data->source);
-    pa_return_null_if_fail(pa_source_get_state(data->source) != PA_SOURCE_UNLINKED);
-
+    pa_return_null_if_fail(PA_SOURCE_IS_LINKED(pa_source_get_state(data->source)));
     pa_return_null_if_fail(!data->direct_on_input || data->direct_on_input->sink == data->source->monitor_of);
 
+    if ((flags & PA_SOURCE_OUTPUT_FAIL_ON_SUSPEND) &&
+        pa_source_get_state(data->source) == PA_SOURCE_SUSPENDED)
+        return NULL;
+
     if (!data->sample_spec_is_set)
         data->sample_spec = data->source->sample_spec;
 
diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
index 79b4926..f4f9462 100644
--- a/src/pulsecore/source-output.h
+++ b/src/pulsecore/source-output.h
@@ -54,7 +54,8 @@ typedef enum pa_source_output_flags {
     PA_SOURCE_OUTPUT_FIX_FORMAT = 32,
     PA_SOURCE_OUTPUT_FIX_RATE = 64,
     PA_SOURCE_OUTPUT_FIX_CHANNELS = 128,
-    PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND = 256
+    PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND = 256,
+    PA_SOURCE_OUTPUT_FAIL_ON_SUSPEND = 512
 } pa_source_output_flags_t;
 
 struct pa_source_output {
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index 886f8d6..04c7f8b 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -267,6 +267,11 @@ static int source_set_state(pa_source *s, pa_source_state_t state) {
 
     s->state = state;
 
+    if (state != PA_SOURCE_UNLINKED) { /* if we enter UNLINKED state pa_source_unlink() will fire the apropriate events */
+        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED], s);
+        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+    }
+
     if (suspend_change) {
         pa_source_output *o;
         uint32_t idx;
@@ -274,12 +279,13 @@ static int source_set_state(pa_source *s, pa_source_state_t state) {
         /* We're suspending or resuming, tell everyone about it */
 
         for (o = PA_SOURCE_OUTPUT(pa_idxset_first(s->outputs, &idx)); o; o = PA_SOURCE_OUTPUT(pa_idxset_next(s->outputs, &idx)))
-            if (o->suspend)
+            if (s->state == PA_SOURCE_SUSPENDED &&
+                (o->flags & PA_SOURCE_OUTPUT_FAIL_ON_SUSPEND))
+                pa_source_output_kill(o);
+            else if (o->suspend)
                 o->suspend(o, state == PA_SOURCE_SUSPENDED);
     }
 
-    if (state != PA_SOURCE_UNLINKED) /* if we enter UNLINKED state pa_source_unlink() will fire the apropriate events */
-        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED], s);
 
     return 0;
 }

commit 50bfa774f69cd3ee62477f3d21f2b2b25e8d82c6
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Feb 3 03:08:14 2009 +0100

    add new error code PA_ERR_NOTIMPLEMENTED

diff --git a/src/pulse/def.h b/src/pulse/def.h
index 85ac701..1acf19a 100644
--- a/src/pulse/def.h
+++ b/src/pulse/def.h
@@ -364,6 +364,7 @@ enum {
     PA_ERR_UNKNOWN,                /**< The error code was unknown to the client */
     PA_ERR_NOEXTENSION,            /**< Extension does not exist. \since 0.9.12 */
     PA_ERR_OBSOLETE,               /**< Obsolete functionality. \since 0.9.15 */
+    PA_ERR_NOTIMPLEMENTED,         /**< Missing implementation. \since 0.9.15 */
     PA_ERR_MAX                     /**< Not really an error but the first invalid error code */
 };
 
diff --git a/src/pulse/error.c b/src/pulse/error.c
index d9d0a8c..d37084b 100644
--- a/src/pulse/error.c
+++ b/src/pulse/error.c
@@ -61,7 +61,9 @@ const char*pa_strerror(int error) {
         [PA_ERR_TOOLARGE] = N_("Too large"),
         [PA_ERR_NOTSUPPORTED] = N_("Not supported"),
         [PA_ERR_UNKNOWN] = N_("Unknown error code"),
-        [PA_ERR_NOEXTENSION] = N_("No such extension")
+        [PA_ERR_NOEXTENSION] = N_("No such extension"),
+        [PA_ERR_OBSOLETE] = N_("Obsolete functionality"),
+        [PA_ERR_NOTIMPLEMENTED] = N_("Missing implementation")
     };
 
     pa_init_i18n();

commit 162e43b306eb28749f2ae1f9c51818e9ad427996
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Feb 3 03:11:31 2009 +0100

    make a few functions return void where the retval isn't used/never != 0

diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 8bc434e..cc3e108 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -351,7 +351,7 @@ static void update_n_corked(pa_sink_input *i, pa_sink_input_state_t state) {
 }
 
 /* Called from main context */
-static int sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state) {
+static void sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state) {
     pa_sink_input *ssync;
     pa_assert(i);
 
@@ -359,7 +359,7 @@ static int sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state) {
         state = PA_SINK_INPUT_RUNNING;
 
     if (i->state == state)
-        return 0;
+        return;
 
     pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) == 0);
 
@@ -386,8 +386,6 @@ static int sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state) {
     }
 
     pa_sink_update_status(i->sink);
-
-    return 0;
 }
 
 /* Called from main context */
@@ -553,7 +551,7 @@ pa_usec_t pa_sink_input_get_latency(pa_sink_input *i, pa_usec_t *sink_latency) {
 }
 
 /* Called from thread context */
-int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa_memchunk *chunk, pa_cvolume *volume) {
+void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa_memchunk *chunk, pa_cvolume *volume) {
     pa_bool_t do_volume_adj_here;
     pa_bool_t volume_is_norm;
     size_t block_size_max_sink, block_size_max_sink_input;
@@ -567,9 +565,6 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa
 
 /*     pa_log_debug("peek"); */
 
-    if (!i->pop)
-        return -1;
-
     pa_assert(i->thread_info.state == PA_SINK_INPUT_RUNNING ||
               i->thread_info.state == PA_SINK_INPUT_CORKED ||
               i->thread_info.state == PA_SINK_INPUT_DRAINED);
@@ -696,8 +691,6 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa
         pa_cvolume_mute(volume, i->sink->sample_spec.channels);
     else
         *volume = i->thread_info.soft_volume;
-
-    return 0;
 }
 
 /* Called from thread context */
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index a3b6b14..0c297ec 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -584,8 +584,7 @@ static unsigned fill_mix_info(pa_sink *s, size_t *length, pa_mix_info *info, uns
     while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)) && maxinfo > 0) {
         pa_sink_input_assert_ref(i);
 
-        if (pa_sink_input_peek(i, *length, &info->chunk, &info->volume) < 0)
-            continue;
+        pa_sink_input_peek(i, *length, &info->chunk, &info->volume);
 
         if (mixlength == 0 || info->chunk.length < mixlength)
             mixlength = info->chunk.length;
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index 9ef9b50..0204e30 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -265,11 +265,11 @@ static void update_n_corked(pa_source_output *o, pa_source_output_state_t state)
 }
 
 /* Called from main context */
-static int source_output_set_state(pa_source_output *o, pa_source_output_state_t state) {
+static void source_output_set_state(pa_source_output *o, pa_source_output_state_t state) {
     pa_assert(o);
 
     if (o->state == state)
-        return 0;
+        return;
 
     pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) == 0);
 
@@ -280,8 +280,6 @@ static int source_output_set_state(pa_source_output *o, pa_source_output_state_t
         pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], o);
 
     pa_source_update_status(o->source);
-
-    return 0;
 }
 
 /* Called from main context */

commit 08800c35b072820ba485a5a0a211ce91a1656390
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Feb 3 03:14:20 2009 +0100

    make a couple of functions return proper error codes

diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c
index 82c8871..7f1ef24 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-ladspa-sink.c b/src/modules/module-ladspa-sink.c
index e746f34..a4007b1 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-remap-sink.c b/src/modules/module-remap-sink.c
index e17fef0..89ddf95 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 b0782c3..ce08c01 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/rtp/module-rtp-recv.c b/src/modules/rtp/module-rtp-recv.c
index 00d2125..baf3532 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 a6d682b..762cdc1 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) {
diff --git a/src/pulsecore/play-memblockq.c b/src/pulsecore/play-memblockq.c
index eac21de..d163126 100644
--- a/src/pulsecore/play-memblockq.c
+++ b/src/pulsecore/play-memblockq.c
@@ -200,7 +200,7 @@ pa_sink_input* pa_memblockq_sink_input_new(
     pa_sink_input_new_data_set_virtual_volume(&data, volume);
     pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p);
 
-    u->sink_input = pa_sink_input_new(sink->core, &data, 0);
+    pa_sink_input_new(&u->sink_input, sink->core, &data, 0);
     pa_sink_input_new_data_done(&data);
 
     if (!u->sink_input)
diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c
index 1311e67..e1643cb 100644
--- a/src/pulsecore/protocol-esound.c
+++ b/src/pulsecore/protocol-esound.c
@@ -424,7 +424,7 @@ static int esd_proto_stream_play(connection *c, esd_proto_t request, const void
     sdata.sink = sink;
     pa_sink_input_new_data_set_sample_spec(&sdata, &ss);
 
-    c->sink_input = pa_sink_input_new(c->protocol->core, &sdata, 0);
+    pa_sink_input_new(&c->sink_input, c->protocol->core, &sdata, 0);
     pa_sink_input_new_data_done(&sdata);
 
     CHECK_VALIDITY(c->sink_input, "Failed to create sink input.");
@@ -526,7 +526,7 @@ static int esd_proto_stream_record(connection *c, esd_proto_t request, const voi
     sdata.source = source;
     pa_source_output_new_data_set_sample_spec(&sdata, &ss);
 
-    c->source_output = pa_source_output_new(c->protocol->core, &sdata, 0);
+    pa_source_output_new(&c->source_output, c->protocol->core, &sdata, 0);
     pa_source_output_new_data_done(&sdata);
 
     CHECK_VALIDITY(c->source_output, "Failed to create source output.");
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index e8080bf..21a2cfb 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -591,7 +591,8 @@ static record_stream* record_stream_new(
         pa_proplist *p,
         pa_bool_t adjust_latency,
         pa_sink_input *direct_on_input,
-        pa_bool_t early_requests) {
+        pa_bool_t early_requests,
+        int *ret) {
 
     record_stream *s;
     pa_source_output *source_output;
@@ -602,6 +603,7 @@ static record_stream* record_stream_new(
     pa_assert(ss);
     pa_assert(maxlength);
     pa_assert(p);
+    pa_assert(ret);
 
     pa_source_output_new_data_init(&data);
 
@@ -616,7 +618,7 @@ static record_stream* record_stream_new(
     if (peak_detect)
         data.resample_method = PA_RESAMPLER_PEAKS;
 
-    source_output = pa_source_output_new(c->protocol->core, &data, flags);
+    *ret = pa_source_output_new(&source_output, c->protocol->core, &data, flags);
 
     pa_source_output_new_data_done(&data);
 
@@ -965,7 +967,8 @@ static playback_stream* playback_stream_new(
         pa_sink_input_flags_t flags,
         pa_proplist *p,
         pa_bool_t adjust_latency,
-        pa_bool_t early_requests) {
+        pa_bool_t early_requests,
+        int *ret) {
 
     playback_stream *s, *ssync;
     pa_sink_input *sink_input;
@@ -982,6 +985,7 @@ static playback_stream* playback_stream_new(
     pa_assert(minreq);
     pa_assert(missing);
     pa_assert(p);
+    pa_assert(ret);
 
     /* Find syncid group */
     for (ssync = pa_idxset_first(c->output_streams, &idx); ssync; ssync = pa_idxset_next(c->output_streams, &idx)) {
@@ -998,8 +1002,10 @@ static playback_stream* playback_stream_new(
 
         if (!sink)
             sink = ssync->sink_input->sink;
-        else if (sink != ssync->sink_input->sink)
+        else if (sink != ssync->sink_input->sink) {
+            *ret = PA_ERR_INVALID;
             return NULL;
+        }
     }
 
     pa_sink_input_new_data_init(&data);
@@ -1017,7 +1023,7 @@ static playback_stream* playback_stream_new(
         pa_sink_input_new_data_set_muted(&data, muted);
     data.sync_base = ssync ? ssync->sink_input : NULL;
 
-    sink_input = pa_sink_input_new(c->protocol->core, &data, flags);
+    *ret = pa_sink_input_new(&sink_input, c->protocol->core, &data, flags);
 
     pa_sink_input_new_data_done(&data);
 
@@ -1696,6 +1702,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
     pa_sink_input_flags_t flags = 0;
     pa_proplist *p;
     pa_bool_t volume_set = TRUE;
+    int ret = PA_ERR_INVALID;
 
     pa_native_connection_assert_ref(c);
     pa_assert(t);
@@ -1822,10 +1829,10 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
      * flag. For older versions we synthesize it here */
     muted_set = muted_set || muted;
 
-    s = playback_stream_new(c, sink, &ss, &map, &maxlength, &tlength, &prebuf, &minreq, volume_set ? &volume : NULL, muted, muted_set, syncid, &missing, flags, p, adjust_latency, early_requests);
+    s = playback_stream_new(c, sink, &ss, &map, &maxlength, &tlength, &prebuf, &minreq, volume_set ? &volume : NULL, muted, muted_set, syncid, &missing, flags, p, adjust_latency, early_requests, &ret);
     pa_proplist_free(p);
 
-    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, s, tag, ret);
 
     reply = reply_new(tag);
     pa_tagstruct_putu32(reply, s->index);
@@ -1950,6 +1957,7 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
     pa_proplist *p;
     uint32_t direct_on_input_idx = PA_INVALID_INDEX;
     pa_sink_input *direct_on_input = NULL;
+    int ret = PA_ERR_INVALID;
 
     pa_native_connection_assert_ref(c);
     pa_assert(t);
@@ -2071,10 +2079,10 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
         (dont_inhibit_auto_suspend ? PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND : 0) |
         (fail_on_suspend ? PA_SOURCE_OUTPUT_FAIL_ON_SUSPEND : 0);
 
-    s = record_stream_new(c, source, &ss, &map, peak_detect, &maxlength, &fragment_size, flags, p, adjust_latency, direct_on_input, early_requests);
+    s = record_stream_new(c, source, &ss, &map, peak_detect, &maxlength, &fragment_size, flags, p, adjust_latency, direct_on_input, early_requests, &ret);
     pa_proplist_free(p);
 
-    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, s, tag, ret);
 
     reply = reply_new(tag);
     pa_tagstruct_putu32(reply, s->index);
diff --git a/src/pulsecore/protocol-simple.c b/src/pulsecore/protocol-simple.c
index dd8002a..e149c40 100644
--- a/src/pulsecore/protocol-simple.c
+++ b/src/pulsecore/protocol-simple.c
@@ -539,7 +539,7 @@ void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simp
         pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
         pa_sink_input_new_data_set_sample_spec(&data, &o->sample_spec);
 
-        c->sink_input = pa_sink_input_new(p->core, &data, 0);
+        pa_sink_input_new(&c->sink_input, p->core, &data, 0);
         pa_sink_input_new_data_done(&data);
 
         if (!c->sink_input) {
@@ -591,7 +591,7 @@ void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simp
         pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
         pa_source_output_new_data_set_sample_spec(&data, &o->sample_spec);
 
-        c->source_output = pa_source_output_new(p->core, &data, 0);
+        pa_source_output_new(&c->source_output, p->core, &data, 0);
         pa_source_output_new_data_done(&data);
 
         if (!c->source_output) {
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index cc3e108..a3a29e9 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -120,7 +120,8 @@ static void reset_callbacks(pa_sink_input *i) {
 }
 
 /* Called from main context */
-pa_sink_input* pa_sink_input_new(
+int pa_sink_input_new(
+        pa_sink_input **_i,
         pa_core *core,
         pa_sink_input_new_data *data,
         pa_sink_input_flags_t flags) {
@@ -129,32 +130,31 @@ pa_sink_input* pa_sink_input_new(
     pa_resampler *resampler = NULL;
     char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
     pa_channel_map original_cm;
+    int r;
 
+    pa_assert(_i);
     pa_assert(core);
     pa_assert(data);
 
-    if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], data) < 0)
-        return NULL;
+    if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], data)) < 0)
+        return r;
 
-    pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
+    pa_return_val_if_fail(!data->driver || pa_utf8_valid(data->driver), -PA_ERR_INVALID);
 
     if (!data->sink) {
         data->sink = pa_namereg_get(core, NULL, PA_NAMEREG_SINK);
         data->save_sink = FALSE;
     }
 
-    pa_return_null_if_fail(data->sink);
-    pa_return_null_if_fail(PA_SINK_IS_LINKED(pa_sink_get_state(data->sink)));
-    pa_return_null_if_fail(!data->sync_base || (data->sync_base->sink == data->sink && pa_sink_input_get_state(data->sync_base) == PA_SINK_INPUT_CORKED));
-
-    if ((flags & PA_SINK_INPUT_FAIL_ON_SUSPEND) &&
-        pa_sink_get_state(data->sink) == PA_SINK_SUSPENDED)
-        return NULL;
+    pa_return_val_if_fail(data->sink, -PA_ERR_NOENTITY);
+    pa_return_val_if_fail(PA_SINK_IS_LINKED(pa_sink_get_state(data->sink)), -PA_ERR_BADSTATE);
+    pa_return_val_if_fail(!data->sync_base || (data->sync_base->sink == data->sink && pa_sink_input_get_state(data->sync_base) == PA_SINK_INPUT_CORKED), -PA_ERR_INVALID);
+    pa_return_val_if_fail(!(flags & PA_SINK_INPUT_FAIL_ON_SUSPEND) || pa_sink_get_state(data->sink) != PA_SINK_SUSPENDED, -PA_ERR_BADSTATE);
 
     if (!data->sample_spec_is_set)
         data->sample_spec = data->sink->sample_spec;
 
-    pa_return_null_if_fail(pa_sample_spec_valid(&data->sample_spec));
+    pa_return_val_if_fail(pa_sample_spec_valid(&data->sample_spec), -PA_ERR_INVALID);
 
     if (!data->channel_map_is_set) {
         if (pa_channel_map_compatible(&data->sink->channel_map, &data->sample_spec))
@@ -163,8 +163,8 @@ pa_sink_input* pa_sink_input_new(
             pa_channel_map_init_extend(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
     }
 
-    pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map));
-    pa_return_null_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec));
+    pa_return_val_if_fail(pa_channel_map_valid(&data->channel_map), -PA_ERR_INVALID);
+    pa_return_val_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec), -PA_ERR_INVALID);
 
     if (!data->virtual_volume_is_set) {
 
@@ -187,14 +187,14 @@ pa_sink_input* pa_sink_input_new(
         }
     }
 
-    pa_return_null_if_fail(pa_cvolume_valid(&data->virtual_volume));
-    pa_return_null_if_fail(pa_cvolume_compatible(&data->virtual_volume, &data->sample_spec));
+    pa_return_val_if_fail(pa_cvolume_valid(&data->virtual_volume), -PA_ERR_INVALID);
+    pa_return_val_if_fail(pa_cvolume_compatible(&data->virtual_volume, &data->sample_spec), -PA_ERR_INVALID);
 
     if (!data->soft_volume_is_set)
         data->soft_volume = data->virtual_volume;
 
-    pa_return_null_if_fail(pa_cvolume_valid(&data->soft_volume));
-    pa_return_null_if_fail(pa_cvolume_compatible(&data->soft_volume, &data->sample_spec));
+    pa_return_val_if_fail(pa_cvolume_valid(&data->soft_volume), -PA_ERR_INVALID);
+    pa_return_val_if_fail(pa_cvolume_compatible(&data->soft_volume, &data->sample_spec), -PA_ERR_INVALID);
 
     if (!data->muted_is_set)
         data->muted = FALSE;
@@ -222,17 +222,17 @@ pa_sink_input* pa_sink_input_new(
     if (data->resample_method == PA_RESAMPLER_INVALID)
         data->resample_method = core->resample_method;
 
-    pa_return_null_if_fail(data->resample_method < PA_RESAMPLER_MAX);
+    pa_return_val_if_fail(data->resample_method < PA_RESAMPLER_MAX, -PA_ERR_INVALID);
 
     if (data->client)
         pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist);
 
-    if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], data) < 0)
-        return NULL;
+    if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], data)) < 0)
+        return r;
 
     if (pa_idxset_size(data->sink->inputs) >= PA_MAX_INPUTS_PER_SINK) {
         pa_log_warn("Failed to create sink input: too many inputs per sink.");
-        return NULL;
+        return -PA_ERR_TOOLARGE;
     }
 
     if ((flags & PA_SINK_INPUT_VARIABLE_RATE) ||
@@ -249,7 +249,7 @@ pa_sink_input* pa_sink_input_new(
                       (core->disable_remixing || (flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) |
                       (core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)))) {
             pa_log_warn("Unsupported resampling operation.");
-            return NULL;
+            return -PA_ERR_NOTSUPPORTED;
         }
     }
 
@@ -334,7 +334,8 @@ pa_sink_input* pa_sink_input_new(
 
     /* Don't forget to call pa_sink_input_put! */
 
-    return i;
+    *_i = i;
+    return 0;
 }
 
 /* Called from main context */
@@ -957,7 +958,7 @@ void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b) {
 int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate) {
     pa_sink_input_assert_ref(i);
     pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
-    pa_return_val_if_fail(i->thread_info.resampler, -1);
+    pa_return_val_if_fail(i->thread_info.resampler, -PA_ERR_BADSTATE);
 
     if (i->sample_spec.rate == rate)
         return 0;
@@ -1045,16 +1046,17 @@ pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) {
 int pa_sink_input_start_move(pa_sink_input *i) {
     pa_source_output *o, *p = NULL;
     pa_sink *origin;
+    int r;
 
     pa_sink_input_assert_ref(i);
     pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
     pa_assert(i->sink);
 
     if (!pa_sink_input_may_move(i))
-        return -1;
+        return -PA_ERR_NOTSUPPORTED;
 
-    if (pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_START], i) < 0)
-        return -1;
+    if ((r = pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_START], i)) < 0)
+        return r;
 
     origin = i->sink;
 
@@ -1096,14 +1098,7 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
     pa_sink_assert_ref(dest);
 
     if (!pa_sink_input_may_move_to(i, dest))
-        return -1;
-
-    i->sink = dest;
-    i->save_sink = save;
-    pa_idxset_put(dest->inputs, i, NULL);
-
-    if (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED)
-        i->sink->n_corked++;
+        return -PA_ERR_NOTSUPPORTED;
 
     if (i->thread_info.resampler &&
         pa_sample_spec_equal(pa_resampler_output_sample_spec(i->thread_info.resampler), &dest->sample_spec) &&
@@ -1127,11 +1122,18 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
                       ((i->flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
                       (i->core->disable_remixing || (i->flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) {
             pa_log_warn("Unsupported resampling operation.");
-            return -1;
+            return -PA_ERR_NOTSUPPORTED;
         }
     } else
         new_resampler = NULL;
 
+    i->sink = dest;
+    i->save_sink = save;
+    pa_idxset_put(dest->inputs, i, NULL);
+
+    if (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED)
+        i->sink->n_corked++;
+
     /* Replace resampler and render queue */
     if (new_resampler != i->thread_info.resampler) {
 
@@ -1177,6 +1179,8 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
 
 /* Called from main context */
 int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
+    int r;
+
     pa_sink_input_assert_ref(i);
     pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
     pa_assert(i->sink);
@@ -1186,13 +1190,13 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
         return 0;
 
     if (!pa_sink_input_may_move_to(i, dest))
-        return -1;
+        return -PA_ERR_NOTSUPPORTED;
 
-    if (pa_sink_input_start_move(i) < 0)
-        return -1;
+    if ((r = pa_sink_input_start_move(i)) < 0)
+        return r;
 
-    if (pa_sink_input_finish_move(i, dest, save) < 0)
-        return -1;
+    if ((r = pa_sink_input_finish_move(i, dest, save)) < 0)
+        return r;
 
     return 0;
 }
@@ -1307,7 +1311,7 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t
         }
     }
 
-    return -1;
+    return -PA_ERR_NOTIMPLEMENTED;
 }
 
 /* Called from main thread */
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index c27f625..d82a3a6 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -257,7 +257,8 @@ void pa_sink_input_new_data_done(pa_sink_input_new_data *data);
 
 /* To be called by the implementing module only */
 
-pa_sink_input* pa_sink_input_new(
+int pa_sink_input_new(
+        pa_sink_input **i,
         pa_core *core,
         pa_sink_input_new_data *data,
         pa_sink_input_flags_t flags);
@@ -313,7 +314,7 @@ pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i);
 
 /* To be used exclusively by the sink driver IO thread */
 
-int pa_sink_input_peek(pa_sink_input *i, size_t length, pa_memchunk *chunk, pa_cvolume *volume);
+void pa_sink_input_peek(pa_sink_input *i, size_t length, pa_memchunk *chunk, pa_cvolume *volume);
 void pa_sink_input_drop(pa_sink_input *i, size_t length);
 void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in the sink's sample spec */);
 void pa_sink_input_update_max_rewind(pa_sink_input *i, size_t nbytes  /* in the sink's sample spec */);
diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c
index 1be421f..67b072b 100644
--- a/src/pulsecore/sound-file-stream.c
+++ b/src/pulsecore/sound-file-stream.c
@@ -326,7 +326,7 @@ int pa_play_file(
     pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, pa_path_get_filename(fname));
     pa_proplist_sets(data.proplist, PA_PROP_MEDIA_FILENAME, fname);
 
-    u->sink_input = pa_sink_input_new(sink->core, &data, 0);
+    pa_sink_input_new(&u->sink_input, sink->core, &data, 0);
     pa_sink_input_new_data_done(&data);
 
     if (!u->sink_input)
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index 0204e30..c5cb16d 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -95,7 +95,8 @@ static void reset_callbacks(pa_source_output *o) {
 }
 
 /* Called from main context */
-pa_source_output* pa_source_output_new(
+int pa_source_output_new(
+        pa_source_output**_o,
         pa_core *core,
         pa_source_output_new_data *data,
         pa_source_output_flags_t flags) {
@@ -103,32 +104,31 @@ pa_source_output* pa_source_output_new(
     pa_source_output *o;
     pa_resampler *resampler = NULL;
     char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+    int r;
 
+    pa_assert(_o);
     pa_assert(core);
     pa_assert(data);
 
-    if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], data) < 0)
-        return NULL;
+    if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], data)) < 0)
+        return r;
 
-    pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
+    pa_return_val_if_fail(!data->driver || pa_utf8_valid(data->driver), -PA_ERR_INVALID);
 
     if (!data->source) {
         data->source = pa_namereg_get(core, NULL, PA_NAMEREG_SOURCE);
         data->save_source = FALSE;
     }
 
-    pa_return_null_if_fail(data->source);
-    pa_return_null_if_fail(PA_SOURCE_IS_LINKED(pa_source_get_state(data->source)));
-    pa_return_null_if_fail(!data->direct_on_input || data->direct_on_input->sink == data->source->monitor_of);
-
-    if ((flags & PA_SOURCE_OUTPUT_FAIL_ON_SUSPEND) &&
-        pa_source_get_state(data->source) == PA_SOURCE_SUSPENDED)
-        return NULL;
+    pa_return_val_if_fail(data->source, -PA_ERR_NOENTITY);
+    pa_return_val_if_fail(PA_SOURCE_IS_LINKED(pa_source_get_state(data->source)), -PA_ERR_BADSTATE);
+    pa_return_val_if_fail(!data->direct_on_input || data->direct_on_input->sink == data->source->monitor_of, -PA_ERR_INVALID);
+    pa_return_val_if_fail(!(flags & PA_SOURCE_OUTPUT_FAIL_ON_SUSPEND) || pa_source_get_state(data->source) != PA_SOURCE_SUSPENDED, -PA_ERR_BADSTATE);
 
     if (!data->sample_spec_is_set)
         data->sample_spec = data->source->sample_spec;
 
-    pa_return_null_if_fail(pa_sample_spec_valid(&data->sample_spec));
+    pa_return_val_if_fail(pa_sample_spec_valid(&data->sample_spec), -PA_ERR_INVALID);
 
     if (!data->channel_map_is_set) {
         if (pa_channel_map_compatible(&data->source->channel_map, &data->sample_spec))
@@ -137,8 +137,8 @@ pa_source_output* pa_source_output_new(
             pa_channel_map_init_extend(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
     }
 
-    pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map));
-    pa_return_null_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec));
+    pa_return_val_if_fail(pa_channel_map_valid(&data->channel_map), -PA_ERR_INVALID);
+    pa_return_val_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec), -PA_ERR_INVALID);
 
     if (flags & PA_SOURCE_OUTPUT_FIX_FORMAT)
         data->sample_spec.format = data->source->sample_spec.format;
@@ -157,17 +157,17 @@ pa_source_output* pa_source_output_new(
     if (data->resample_method == PA_RESAMPLER_INVALID)
         data->resample_method = core->resample_method;
 
-    pa_return_null_if_fail(data->resample_method < PA_RESAMPLER_MAX);
+    pa_return_val_if_fail(data->resample_method < PA_RESAMPLER_MAX, -PA_ERR_INVALID);
 
     if (data->client)
         pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist);
 
-    if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], data) < 0)
-        return NULL;
+    if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], data)) < 0)
+        return r;
 
     if (pa_idxset_size(data->source->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) {
         pa_log("Failed to create source output: too many outputs per source.");
-        return NULL;
+        return -PA_ERR_TOOLARGE;
     }
 
     if ((flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ||
@@ -184,7 +184,7 @@ pa_source_output* pa_source_output_new(
                       (core->disable_remixing || (flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) |
                       (core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)))) {
             pa_log_warn("Unsupported resampling operation.");
-            return NULL;
+            return -PA_ERR_NOTSUPPORTED;
         }
     }
 
@@ -248,7 +248,8 @@ pa_source_output* pa_source_output_new(
 
     /* Don't forget to call pa_source_output_put! */
 
-    return o;
+    *_o = o;
+    return 0;
 }
 
 /* Called from main context */
@@ -576,7 +577,7 @@ void pa_source_output_cork(pa_source_output *o, pa_bool_t b) {
 int pa_source_output_set_rate(pa_source_output *o, uint32_t rate) {
     pa_source_output_assert_ref(o);
     pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
-    pa_return_val_if_fail(o->thread_info.resampler, -1);
+    pa_return_val_if_fail(o->thread_info.resampler, -PA_ERR_BADSTATE);
 
     if (o->sample_spec.rate == rate)
         return 0;
@@ -676,16 +677,17 @@ pa_bool_t pa_source_output_may_move_to(pa_source_output *o, pa_source *dest) {
 /* Called from main context */
 int pa_source_output_start_move(pa_source_output *o) {
     pa_source *origin;
+    int r;
 
     pa_source_output_assert_ref(o);
     pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
     pa_assert(o->source);
 
     if (!pa_source_output_may_move(o))
-        return -1;
+        return -PA_ERR_NOTSUPPORTED;
 
-    if (pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_START], o) < 0)
-        return -1;
+    if ((r = pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_START], o)) < 0)
+        return r;
 
     origin = o->source;
 
@@ -714,13 +716,6 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t
     if (!pa_source_output_may_move_to(o, dest))
         return -1;
 
-    o->source = dest;
-    o->save_source = save;
-    pa_idxset_put(o->source->outputs, o, NULL);
-
-    if (pa_source_output_get_state(o) == PA_SOURCE_OUTPUT_CORKED)
-        o->source->n_corked++;
-
     if (o->thread_info.resampler &&
         pa_sample_spec_equal(pa_resampler_input_sample_spec(o->thread_info.resampler), &dest->sample_spec) &&
         pa_channel_map_equal(pa_resampler_input_channel_map(o->thread_info.resampler), &dest->channel_map))
@@ -743,11 +738,18 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t
                       ((o->flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
                       (o->core->disable_remixing || (o->flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) {
             pa_log_warn("Unsupported resampling operation.");
-            return -1;
+            return -PA_ERR_NOTSUPPORTED;
         }
     } else
         new_resampler = NULL;
 
+    o->source = dest;
+    o->save_source = save;
+    pa_idxset_put(o->source->outputs, o, NULL);
+
+    if (pa_source_output_get_state(o) == PA_SOURCE_OUTPUT_CORKED)
+        o->source->n_corked++;
+
     /* Replace resampler */
     if (new_resampler != o->thread_info.resampler) {
         if (o->thread_info.resampler)
@@ -784,6 +786,7 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t
 
 /* Called from main context */
 int pa_source_output_move_to(pa_source_output *o, pa_source *dest, pa_bool_t save) {
+    int r;
 
     pa_source_output_assert_ref(o);
     pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
@@ -794,13 +797,13 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest, pa_bool_t sav
         return 0;
 
     if (!pa_source_output_may_move_to(o, dest))
-        return -1;
+        return -PA_ERR_NOTSUPPORTED;
 
-    if (pa_source_output_start_move(o) < 0)
-        return -1;
+    if ((r = pa_source_output_start_move(o)) < 0)
+        return r;
 
-    if (pa_source_output_finish_move(o, dest, save) < 0)
-        return -1;
+    if ((r = pa_source_output_finish_move(o, dest, save)) < 0)
+        return r;
 
     return 0;
 }
@@ -864,5 +867,5 @@ int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int
         }
     }
 
-    return -1;
+    return -PA_ERR_NOTIMPLEMENTED;
 }
diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
index f4f9462..91f28f9 100644
--- a/src/pulsecore/source-output.h
+++ b/src/pulsecore/source-output.h
@@ -205,7 +205,8 @@ void pa_source_output_new_data_done(pa_source_output_new_data *data);
 
 /* To be called by the implementing module only */
 
-pa_source_output* pa_source_output_new(
+int pa_source_output_new(
+        pa_source_output**o,
         pa_core *core,
         pa_source_output_new_data *data,
         pa_source_output_flags_t flags);

commit 273926184709423443831dc53b11c3cf68b0cdad
Merge: 08800c3... db2a425...
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Feb 3 03:15:32 2009 +0100

    Merge commit 'origin/master-tx'


-- 
hooks/post-receive
PulseAudio Sound Server



More information about the pulseaudio-commits mailing list