[pulseaudio-commits] [SCM] PulseAudio Sound Server branch, lennartsbtfixes, updated. v0.9.13-465-gfea6757

Lennart Poettering gitmailer-noreply at 0pointer.de
Sun Feb 1 17:12: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 lennartsbtfixes branch has been updated
      from  3442d583aaf1b8f17026791f941b506a891b0464 (commit)

- Log -----------------------------------------------------------------
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()
-----------------------------------------------------------------------

Summary of changes:
 configure.ac                                      |    2 +-
 src/Makefile.am                                   |   23 +-
 src/map-file                                      |    1 +
 src/modules/alsa/alsa-util.c                      |   26 +-
 src/modules/alsa/alsa-util.h                      |    2 +
 src/modules/bluetooth/bluetooth-util.c            |   73 +-
 src/modules/bluetooth/bluetooth-util.h            |    4 +
 src/modules/bluetooth/module-bluetooth-device.c   | 1585 +++++++++++++--------
 src/modules/bluetooth/module-bluetooth-discover.c |   12 +-
 src/modules/bluetooth/rtp.h                       |   44 +-
 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/module-pipe-sink.c                    |    4 +-
 src/pulse/channelmap.c                            |   57 +-
 src/pulse/channelmap.h                            |    5 +
 src/pulse/proplist.h                              |    4 +-
 src/pulse/stream.c                                |    4 +-
 src/pulse/stream.h                                |    2 +-
 src/pulse/volume.c                                |    3 +
 src/pulsecore/envelope.c                          |    1 -
 src/pulsecore/log.c                               |    3 +-
 src/pulsecore/ltdl-helper.c                       |    6 +-
 src/pulsecore/macro.h                             |   16 +-
 src/pulsecore/namereg.c                           |    2 +-
 src/pulsecore/proplist-util.c                     |   53 +-
 src/pulsecore/sink.c                              |   70 +-
 src/pulsecore/source.c                            |   29 +-
 src/tests/volume-ui.py                            |   76 +-
 36 files changed, 3046 insertions(+), 1062 deletions(-)
 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

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

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'. */

-- 
hooks/post-receive
PulseAudio Sound Server



More information about the pulseaudio-commits mailing list