[pulseaudio-commits] [SCM] PulseAudio Sound Server branch, master, updated. v0.9.19-173-gd0b478e

Lennart Poettering gitmailer-noreply at 0pointer.de
Tue Oct 6 18:46:24 PDT 2009


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

The master branch has been updated
      from  b3592a160f0d2a28605048a81c0261bf7c45acbb (commit)

- Log -----------------------------------------------------------------
d0b478e Merge remote branch 'coling/history'
9f226d2 Merge remote branch 'phish3/master'
692ce73 Merge remote branch 'tanuk/dbus-work'
9d7a27e device-manager: Play nice with module-stream-restore.
cc31d7c device-manager: Make use of PA_IDXSET_FOREACH when applying entries.
fdbb550 device-manager: Keep track as to whether or not the user specifically renamed the device.
6468dcf device-manager: No need to check the version after calling read_entry()
019331d Merge branch 'master' into dbus-work
e895200 module-equalizer-sink: disable active profile name restoration as something in pack/unpack is funky and I don't have time for a proper fix
97056d2 module-equalizer-sink: *added client initiated sync support for filter state *added note of possible unstable behavior with next-power-of-2 sample rate calculation
a434f4c module-equalizer-sink: resyncing with head and fix invalid writes     * pa_log->debug for default equalizer notification     * partially fixed infinite rewind bug     * set max_request to window_size first iteration     * swap order inside ROUND_UP calls     * resync pa_sink_input_new changes     * change pa_sample_clamp parameters to be correct to fix invalid writes     * reenable proper reset logic + proper request size
50db81c device-manager: Fix typo in module loading script.
42e28ce device-manager: Add some scripts that are only run under KDE to load/initialise module-device-manager with routing turned on.
20eedb2 device-manager: Misc fixes to co-exist with other stream management/routing modules.
3a20cf0 device-manager: Misc fixes.
b8a6436 device-manager: Fix the writing of the database when priority doesn't change.
7633bb8 device-manager: Add extra debug messages
f9b2d65 device-manager: Change the prefer/defer options to a single 'reorder' command.
8977abd device-manager: Don't notify clients on every subscription (it happens all the time).
bbf6701 device-manager: Save icon and report current availability over protocol.
8b2cc4d device-manager: Expose the priority lists in the protocol extension.
4dedba7 device-manager: Add a function to dump the database which we do whenever we save it (and on startup)
1d43230 device-manager: Reroute streams when they change allowing the media.role to be updated mid-stream.
d3460e3 device-manager: Refactor the routing method to allow the routing of a single stream
8d0787c device-manager: More sensible names for internal functions
25f7534 device-manager: Reroute the streams on startup and update our cache on enable.
0016b5e device-manager: Keep a cache of the highest priority devices for each role.
ce0b2bd device-manager: Fix the database write mode
1e2d236 device-manager: Update exports
9e44797 device-manager: Some efficiency and safety tweaks
e47f385 device-manager: Allow the routing component to be turned on via a module argument as well as via protocol extn.
4fb9daf device-manager: Remove unused variables
1d04c35 device-manager: Set the most appropriate sink/source when new streams are created
74c1c27 device-manager: Add routing functions that are triggered when sinks/soruces are added/removed.
678d8e9 device-manager: Add a function to get a list of the highest priority device indexes for each role.
ca68105 device-manager: Remove unneeded logic for checking for and (un)loading module-stream-restore. We can co-exist
ed8af7c device-manager: Rough framework (slots etc.) for handling routing.
faae33d device-manager: debug and comments
e589f38 device-manager: Fix the freeing of the datum on prefer/defer.
1802500 device-manager: When a new device is encountered, initialise the priority list to an appropriate value
a64f0f7 device-manager: Let subscribed clients know when something changes.
f8ec8f3 device-manager: Change the write function to a rename function.
aebe478 device-manager: Provide a method for prefering/defering a device.
95f2839 device-manager: Fix copy+paste code that looped over the tagstruct when not necessary
103897a device-manager: Provide a way for clients to enable/disable role-based device-priority routing.
9357bdf device-manager: Update docs version -> 0.9.19 (predicted)
464e1a8 device-manager: Fix copy+paste leftover
aa5d56b device-manager: Only store and save details for non-monitor sources
42b30e1 stream-restore: Preventative initialistion to NULL
6497938 device-restore: Fix the application of an entry to allow changing the name of devices.
40e97eb device-manager: Fix tagstruct description extraction (copy+paste blunder)
70accbb device-manager: Link native protocol library.
0b3b037 device-manager: Export device-manager extension functions
93c3c65 device-manager: Fix indentation
37e82ce device-manager: Add an untested protocol extension.
bc869b5 device-manager: Add a new module to keep track of the names and descriptions of various sinks.
3053bad module-equalizer-sink: resync with ladspa parent sink
263b683 module-equalizer-sink: fix improper usage of pa_modargs_get_value_boolean for u->set_default
f5ceed8 module-equalizer-sink: added server side persistance of profile names
b028e4e module-equalizer-sink: per-channel filtering support + profiles, easier default configuration
2f6fd32 module-equalizer-sink: fixed a bug w/ new zero-latency input scheme (that was an interesting/cool bug!)
735c8ab module-equalizer-sink: added support for preamp
cd54ecd module-equalizer-sink: drop old macros for new library based ones
ab0e20a module-equalizer-sink: fixed timeval initialization
7bd7ce6 module-equalizer-sink.c: swapped order of attach_within_thread and set_max_request within sink_input_attach_cb
07cd6a4 module-equalizer-sink.c     i->sink -> i in pa_get_sink_max_request*
1c1a812 module-equalizer-sink     exchanged improper usage of memblockq_peek'd memchunk for silence block     dropped unneeded function prototypes     changed mround to be slightly more elegant     __restrict__ -> restrict for c99     removed unneeded pa_aupdate_swap calls     first_iteration -> pa_bool_t     cleaned up some usage of pa_malloc0 where pa_new0 was more appropriate     cruft removal, whitespace fixes and reordering of variables
0e6711d module-equalizer-sink:     merging in upstream changes     whitespace fix and fix for first iteration un-windowing
38d608a module-equalizer-sink:     reworked processing so we don't have input->output delay of R samples
8c2f976 module-equalizer-sink:     fix for peek returning a null memblock     pa_log -> pa_log_debug for fft size     updated module description     fixed a comment in dbus error for incorrect x positions
684ad6d module-equalizer-sink:     proper fix for pa_xmalloc(0) given that 0 is illegal     fix coefficients in case there's no resume state     loadprofile now signals filterchanged
1e3c7d3 module-equalizer-sink:     dbus:         eliminated some redundant code in dbus handlers/getall         switched filter back to being a property         signals for changed profiles, added/removed sinks, filter updates and sink reconfigurations     fixed timing routines
4231ac4 module-equalizer-sink: reverted buffering logic back to how the ladspa sink did it
857eea0 module-equalizer-sink:     add lennard's fix for piggy-back sinks in pop_cb     fixed some tsched issues
144f1c4 module-equalizer-sink:     dbus properties and manager so that multiple sinks can be loaded and mixers can be equalizer-sink aware     functionality to seed new filters quickly (rteq guis)     profile support     extra checking in client->server dbus messages
66a6cc6 module-equalizer-sink:     added support for suspend/resume of filter coefficients     unregister the correct dbus interface.     made equalizer state file sink index dependent     expanded dbus properties     whitespace
8934c31 module-equalizer-sink:     added dbus support     removed cruft from inherited from ladspa module and improved clarity     switched dsp processing to reference implementation until project is more mature     tsched=0 seems to help with the micro-dropouts/crackling! oh my!     reformatting/spaces
c7fcc9c module-equalizer-sink:     removed liboil     added sse2 optimized dsp logic implementation     cleaned up a bit
702480a module-equalizer-sink:     first commit of a working state (cpu speed dependant)     added noop processing for filter debugability
09d9096 module-equalizer-sink: simplified sink_input pop callback and introduced new variables that simplify different strategies.
cf8331a module-equalizer-sink: trying new buffering strategies
d4fe5bf module-equalizer-sink: attempt different buffering strategy
182c9c7 module-equalizer-sink: added more assertions to aid in debugging
2e11906 module-equalizer-sink:     added temporary debugging output to track filter output     removed dead code     only a small amount of crackling remains
4315550 module-equalizer-sink added src/Makefile.am: added module-equalizer-sink
5871319 dbus-protocol: Implement argument type checking for normal methods.
8a28e5d dbus: Change IsMuted property names to Mute.
411feae dbusiface-core: Add signals FallbackSinkUnset and FallbackSourceUnset.
0ad2d55 Merge branch 'master' of git://0pointer.de/pulseaudio into dbus-work
0e09663 dbus: Do message argument type checking early, centrally.
7bc8a79 Merge branch 'master' of git://0pointer.de/pulseaudio into dbus-work
219f750 dbus: Finish the Client D-Bus interface.
11fcc8c dbusiface-stream: Only send stream event signals from the right D-Bus objects.
edf8010 dbus: Make sure that subscription callbacks don't try to access removed objects.
3e0e685 dbus: Save one level of identation by returning early.
2f3fc2f Merge branch 'master' of git://0pointer.de/pulseaudio into dbus-work
3025645 dbusiface-module: Implement the Module D-Bus interface.
57886ff dbus-protocol: Print a debug line whenever interfaces are unregistered.
7049b3c modargs: New function: pa_modargs_iterate().
187c4f3 proplist: A couple of documentation fixes.
1e4e26c proplist: Return early from pa_proplist_equal() if the pointers are equal.
179f849 dbusifaca-device: Adapt to the changed pa_sink_get/set_volume() interface.
292d6dc Merge branch 'master' of git://0pointer.de/pulseaudio into dbus-work
b4e0d5d dbusiface-sample: Implement the Sample D-Bus interface.
3de210b dbusiface-core: Assert that _add/remove_interface calls succeed.
636dbc3 dbusiface-core: Use the PA_IDXSET_FOREACH macro.
8e6664f dbusiface-core: Split some overly long lines.
36dc61a dbusiface-stream: Finish the Stream D-Bus interface.
70ff96b dbusiface-device: Save one level of identation by returning early.
b528715 dbusiface-device: Fix argument reading in handle_suspend().
bce6af1 dbusiface-device: Use a single if-else section instead of ternary operator overuse.
150cd16 dbusiface-device: Split some overly long lines.
efec274 dbusiface-core: Two new functions: pa_dbusiface_core_get_sink/source().
a10e836 dbusiface-core: New function: pa_dbusiface_core_get_client_path().
f48684e namereg: Revert default device handling back to the upstream version.
bcaba0b Merge branch 'master' of git://0pointer.de/pulseaudio into dbus-work
2bb3eef dbusiface-stream: Implement about a half of the Stream D-Bus interface.
f0db081 dbusiface-device: Free the copied proplist.
a72bba1 dbusiface-client: Fix indentation.
c7f4ed3 dbusiface-client: Fix the interface name.
9ed25d7 dbusiface-client: Implement the properties of the Client D-Bus interface.
f663d13 dbusiface-core: Two new functions: pa_dbusiface_core_get_playback/record_stream_path().
91f626f dbusiface-device: Implement the Device and DevicePort D-Bus interfaces.
22ab141 dbus-protocol: Use pa_hashmap_remove() instead of _get().
1e65d8d dbusiface-core: New function: pa_dbusiface_core_get_card_path().
90c73db dbusiface-card: Fix the OwnerModule property type in handle_get_all().
31c544d dbusiface-card: Assert that the profiles list is empty if there's no active profile.
18f9f1b dbusiface-card: Use the ++ operator like it's meant to be used.
afb79ee dbusiface-card-profile: Assert the core argument isn't NULL.
8b5550d dbusiface-card: Split some overly long lines.
31117fe dbus-protocol: Fix signal sending for the case when the client doesn't listen for all signals.
7cfda56 dbus-protocol: Add a note for _send_signal that by default the signal isn't actually sent.
8c8df77 dbusiface-card-profile: Implement the CardProfile D-Bus interface.
acad506 dbusiface-card: Implement the Card D-Bus interface.
16dce8d dbus-protocol: Take advantage of the helpers in dbus-util.
7699cfd dbus-protocol: Split some overly long lines.
76bd03b dbus-util: Trivial comment punctuation fix.
3e9de1a dbus-util: Add helpers for proplist handling.
5ece8e8 dbusiface-core: Add functions for getting various object paths.
0b66620 dbusiface-core: Generate more informative error messages.
06232e2 dbus: Take advantage of the PA_HASHMAP_FOREACH macro.
fcf6875 dbus: Three entangled changes:
1457df4 proplist: New function: pa_proplist_equal()
44770c5 dbusiface-memstats: Implement the Memstats D-Bus interface.
0fc0552 dbus-protocol: Remove erroneous protocol object unref.
9eeb8eb dbus-protocol: Make debug logging saner.
b1578e2 dbus-protocol, dbusiface-core: Take a reference when storing the core pointer.
8966c61 dbusiface-core: Make the interface string a public constant.
d9d166a stream-restore: Expose module to D-Bus.
805af5e dbus-util: Fix broken proplist reading logic.
8c84057 dbus-protocol: Add debugging output (temporary change).
a1ba80b dbusiface-core: Don't die if we get a default sink/source change event before the new default device is actually created.
68cb63c dbusiface-core: Send signals whenever extensions are registered and unregistered.
c354a08 dbus-protocol: Implement extension registration.
b061957 dbus/iface-core.c: Make sure D-Bus objects are created only once.
018810e Bug fixing and minor cleanups.
9a77d2f Add the forgotten src/modules/dbus directory to git.
9347e90 Finish the Core dbus interface.
5c7952e dbus: Implement the Name property of the core object.
6e2fec0 server-lookup: Update the D-Bus identifiers to be versioned.
3bff2ee module-cli: Fix compilation by adding libpulsecommon to module_cli_la_LIBADD.
c266595 Merge branch 'master' into dbus-work
0bc538b Merge branch 'master' into dbus-work
b152f3a module-dbus-protocol: Allow anyone to connect the daemon in system mode.
3c6a0ac dbus-protocol: Implement TCP server startup.
123c6a3 dbus-common: Implement infrastructure for registering D-Bus objects on all client connections and for receiving method calls from clients.
c8d819a dbus-protocol: Connection handling for local connections.
5babbaa daemon: Implement the DBus server lookup service.
c94e742 Create module-dbus-protocol with "Hello, world!" functionality.
-----------------------------------------------------------------------

Summary of changes:
 configure.ac                                       |    4 +-
 src/Makefile.am                                    |   67 +-
 src/daemon/daemon-conf.c                           |   49 +
 src/daemon/daemon-conf.h                           |    3 +
 src/daemon/daemon.conf.in                          |    1 +
 src/daemon/default.pa.in                           |    3 +
 src/daemon/main.c                                  |  124 +-
 src/daemon/pulseaudio-kde.desktop.in               |   11 +
 src/daemon/server-lookup.c                         |  522 +++++
 .../auth-cookie.h => daemon/server-lookup.h}       |   23 +-
 .../daemon/start-pulseaudio-kde.in                 |   16 +-
 src/daemon/system.pa.in                            |    4 +
 src/map-file                                       |    9 +
 src/modules/dbus/iface-card-profile.c              |  228 ++
 src/modules/dbus/iface-card-profile.h              |   50 +
 src/modules/dbus/iface-card.c                      |  561 +++++
 .../dllmain.c => modules/dbus/iface-card.h}        |   46 +-
 src/modules/dbus/iface-client.c                    |  459 ++++
 .../dbus-shared.h => modules/dbus/iface-client.h}  |   31 +-
 src/modules/dbus/iface-core.c                      | 2197 ++++++++++++++++++++
 src/modules/dbus/iface-core.h                      |   52 +
 src/modules/dbus/iface-device-port.c               |  190 ++
 src/modules/dbus/iface-device-port.h               |   50 +
 src/modules/dbus/iface-device.c                    | 1316 ++++++++++++
 src/modules/dbus/iface-device.h                    |   53 +
 src/modules/dbus/iface-memstats.c                  |  231 ++
 .../dbus/iface-memstats.h}                         |   29 +-
 src/modules/dbus/iface-module.c                    |  336 +++
 .../cli.h => modules/dbus/iface-module.h}          |   29 +-
 src/modules/dbus/iface-sample.c                    |  519 +++++
 .../dbus-shared.h => modules/dbus/iface-sample.h}  |   31 +-
 src/modules/dbus/iface-stream.c                    |  894 ++++++++
 src/modules/dbus/iface-stream.h                    |   47 +
 src/modules/dbus/module-dbus-protocol.c            |  607 ++++++
 src/modules/module-device-manager.c                | 1540 ++++++++++++++
 src/modules/module-equalizer-sink.c                | 2157 +++++++++++++++++++
 src/modules/module-stream-restore.c                | 1010 +++++++++-
 src/pulse/client-conf.c                            |    3 +
 src/pulse/client-conf.h                            |    3 +-
 src/pulse/client.conf.in                           |    1 +
 src/pulse/context.c                                |    5 +
 .../{ext-stream-restore.c => ext-device-manager.c} |  219 ++-
 src/pulse/ext-device-manager.h                     |  128 ++
 src/pulse/internal.h                               |    6 +
 src/pulse/proplist.c                               |   29 +
 src/pulse/proplist.h                               |    6 +-
 src/pulsecore/core-util.c                          |   22 +
 src/pulsecore/core-util.h                          |    7 +
 src/pulsecore/core.h                               |    9 +
 src/pulsecore/cpu-arm.h                            |    2 +-
 src/pulsecore/cpu-x86.h                            |    2 +-
 src/pulsecore/dbus-util.c                          |  352 ++++
 src/pulsecore/dbus-util.h                          |   43 +
 src/pulsecore/memblock.c                           |    2 +-
 src/pulsecore/modargs.c                            |   10 +
 src/pulsecore/modargs.h                            |    9 +
 src/pulsecore/protocol-dbus.c                      | 1134 ++++++++++
 src/pulsecore/protocol-dbus.h                      |  217 ++
 58 files changed, 15486 insertions(+), 222 deletions(-)
 create mode 100644 src/daemon/pulseaudio-kde.desktop.in
 create mode 100644 src/daemon/server-lookup.c
 copy src/{pulsecore/auth-cookie.h => daemon/server-lookup.h} (58%)
 copy doxygen/Makefile.am => src/daemon/start-pulseaudio-kde.in (80%)
 mode change 100644 => 100755
 create mode 100644 src/modules/dbus/iface-card-profile.c
 create mode 100644 src/modules/dbus/iface-card-profile.h
 create mode 100644 src/modules/dbus/iface-card.c
 copy src/{pulsecore/dllmain.c => modules/dbus/iface-card.h} (53%)
 create mode 100644 src/modules/dbus/iface-client.c
 copy src/{pulsecore/dbus-shared.h => modules/dbus/iface-client.h} (52%)
 create mode 100644 src/modules/dbus/iface-core.c
 create mode 100644 src/modules/dbus/iface-core.h
 create mode 100644 src/modules/dbus/iface-device-port.c
 create mode 100644 src/modules/dbus/iface-device-port.h
 create mode 100644 src/modules/dbus/iface-device.c
 create mode 100644 src/modules/dbus/iface-device.h
 create mode 100644 src/modules/dbus/iface-memstats.c
 copy src/{pulsecore/dbus-shared.h => modules/dbus/iface-memstats.h} (53%)
 create mode 100644 src/modules/dbus/iface-module.c
 copy src/{pulsecore/cli.h => modules/dbus/iface-module.h} (55%)
 create mode 100644 src/modules/dbus/iface-sample.c
 copy src/{pulsecore/dbus-shared.h => modules/dbus/iface-sample.h} (52%)
 create mode 100644 src/modules/dbus/iface-stream.c
 create mode 100644 src/modules/dbus/iface-stream.h
 create mode 100644 src/modules/dbus/module-dbus-protocol.c
 create mode 100644 src/modules/module-device-manager.c
 create mode 100755 src/modules/module-equalizer-sink.c
 copy src/pulse/{ext-stream-restore.c => ext-device-manager.c} (58%)
 create mode 100644 src/pulse/ext-device-manager.h
 create mode 100644 src/pulsecore/protocol-dbus.c
 create mode 100644 src/pulsecore/protocol-dbus.h

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

commit c94e7421aad58c7714f6e26f20642c51af17cc4d
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Wed Jun 3 15:21:50 2009 +0300

    Create module-dbus-protocol with "Hello, world!" functionality.

diff --git a/src/Makefile.am b/src/Makefile.am
index a7ec691..70b16be 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1072,7 +1072,8 @@ endif
 
 if HAVE_DBUS
 modlibexec_LTLIBRARIES += \
-		module-rygel-media-server.la
+		module-rygel-media-server.la \
+		module-dbus-protocol.la
 endif
 
 if HAVE_BLUEZ
@@ -1164,7 +1165,8 @@ SYMDEF_FILES = \
 		modules/module-position-event-sounds-symdef.h \
 		modules/module-augment-properties-symdef.h \
 		modules/module-cork-music-on-phone-symdef.h \
-		modules/module-console-kit-symdef.h
+		modules/module-console-kit-symdef.h \
+		modules/module-dbus-protocol-symdef.h
 
 EXTRA_DIST += $(SYMDEF_FILES)
 BUILT_SOURCES += $(SYMDEF_FILES)
@@ -1213,6 +1215,13 @@ module_http_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_HTTP $(A
 module_http_protocol_unix_la_LDFLAGS = $(MODULE_LDFLAGS)
 module_http_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore- at PA_MAJORMINORMICRO@.la libprotocol-http.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
 
+# D-Bus protocol
+
+module_dbus_protocol_la_SOURCES = modules/module-dbus-protocol.c
+module_dbus_protocol_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
+module_dbus_protocol_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_dbus_protocol_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
+
 # Native protocol
 
 module_native_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c
diff --git a/src/daemon/default.pa.in b/src/daemon/default.pa.in
index fa0683e..ba8e3c8 100755
--- a/src/daemon/default.pa.in
+++ b/src/daemon/default.pa.in
@@ -66,6 +66,9 @@ load-module module-bluetooth-discover
 .ifexists module-esound-protocol-unix at PA_SOEXT@
 load-module module-esound-protocol-unix
 .endif
+.ifexists module-dbus-protocol at PA_SOEXT@
+load-module module-dbus-protocol
+.endif
 load-module module-native-protocol-unix
 
 ### Network access (may be configured with paprefs, so leave this commented
diff --git a/src/daemon/system.pa.in b/src/daemon/system.pa.in
index 27e4281..5541bbe 100755
--- a/src/daemon/system.pa.in
+++ b/src/daemon/system.pa.in
@@ -32,6 +32,9 @@ load-module module-detect
 .ifexists module-esound-protocol-unix at PA_SOEXT@
 load-module module-esound-protocol-unix
 .endif
+.ifexists module-dbus-protocol at PA_SOEXT@
+load-module module-dbus-protocol
+.endif
 load-module module-native-protocol-unix
 
 ### Automatically restore the volume of streams and devices
diff --git a/src/modules/module-dbus-protocol.c b/src/modules/module-dbus-protocol.c
new file mode 100644
index 0000000..077f417
--- /dev/null
+++ b/src/modules/module-dbus-protocol.c
@@ -0,0 +1,66 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/module.h>
+
+#include "module-dbus-protocol-symdef.h"
+
+PA_MODULE_DESCRIPTION("D-Bus interface");
+PA_MODULE_USAGE("<no module arguments>");
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_AUTHOR("Tanu Kaskinen");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+
+struct userdata {
+    pa_module *module;
+};
+
+int pa__init(pa_module *m) {
+    struct userdata *u = NULL;
+
+    pa_assert(m);
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->module = m;
+
+    pa_log_notice("Hello, world!");
+
+    return 0;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    pa_xfree(u);
+    m->userdata = NULL;
+}

commit 5babbaafb26ac4f83db0d8bca53006a843472b8f
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Fri Jun 12 07:16:05 2009 +0300

    daemon: Implement the DBus server lookup service.

diff --git a/src/Makefile.am b/src/Makefile.am
index 70b16be..c56f760 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -135,13 +135,14 @@ BUILT_SOURCES = \
 bin_PROGRAMS = pulseaudio
 
 pulseaudio_SOURCES = \
-		daemon/caps.h daemon/caps.c \
+		daemon/caps.c daemon/caps.h \
 		daemon/cmdline.c daemon/cmdline.h \
 		daemon/cpulimit.c daemon/cpulimit.h \
 		daemon/daemon-conf.c daemon/daemon-conf.h \
 		daemon/dumpmodules.c daemon/dumpmodules.h \
 		daemon/ltdl-bind-now.c daemon/ltdl-bind-now.h \
-		daemon/main.c
+		daemon/main.c \
+		daemon/server-lookup.c daemon/server-lookup.h
 
 pulseaudio_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSPEEX_CFLAGS) $(LIBSNDFILE_CFLAGS) $(CAP_CFLAGS) $(LIBOIL_CFLAGS) $(DBUS_CFLAGS)
 pulseaudio_LDADD = $(AM_LDADD) libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSPEEX_LIBS) $(LIBSNDFILE_LIBS) $(CAP_LIBS) $(LIBOIL_LIBS) $(DBUS_LIBS)
diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c
index ac6cc8a..e6fa8c6 100644
--- a/src/daemon/daemon-conf.c
+++ b/src/daemon/daemon-conf.c
@@ -83,6 +83,9 @@ static const pa_daemon_conf default_conf = {
     .config_file = NULL,
     .use_pid_file = TRUE,
     .system_instance = FALSE,
+#ifdef HAVE_DBUS
+    .local_server_type = PA_SERVER_TYPE_UNSET, /* The actual default is _USER, but we have to detect when the user doesn't specify this option. */
+#endif
     .no_cpu_limit = FALSE,
     .disable_shm = FALSE,
     .default_n_fragments = 4,
@@ -203,6 +206,22 @@ int pa_daemon_conf_set_resample_method(pa_daemon_conf *c, const char *string) {
     return 0;
 }
 
+int pa_daemon_conf_set_local_server_type(pa_daemon_conf *c, const char *string) {
+    pa_assert(c);
+    pa_assert(string);
+
+    if (!strcmp(string, "user"))
+        c->local_server_type = PA_SERVER_TYPE_USER;
+    else if (!strcmp(string, "system")) {
+        c->local_server_type = PA_SERVER_TYPE_SYSTEM;
+    } else if (!strcmp(string, "none")) {
+        c->local_server_type = PA_SERVER_TYPE_NONE;
+    } else
+        return -1;
+
+    return 0;
+}
+
 static int parse_log_target(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) {
     pa_daemon_conf *c = data;
 
@@ -430,6 +449,22 @@ static int parse_rtprio(const char *filename, unsigned line, const char *section
     return 0;
 }
 
+static int parse_server_type(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) {
+    pa_daemon_conf *c = data;
+
+    pa_assert(filename);
+    pa_assert(lvalue);
+    pa_assert(rvalue);
+    pa_assert(data);
+
+    if (pa_daemon_conf_set_local_server_type(c, rvalue) < 0) {
+        pa_log(_("[%s:%u] Invalid server type '%s'."), filename, line, rvalue);
+        return -1;
+    }
+
+    return 0;
+}
+
 int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
     int r = -1;
     FILE *f = NULL;
@@ -443,6 +478,9 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
         { "disallow-exit",              pa_config_parse_bool,     &c->disallow_exit, NULL },
         { "use-pid-file",               pa_config_parse_bool,     &c->use_pid_file, NULL },
         { "system-instance",            pa_config_parse_bool,     &c->system_instance, NULL },
+#ifdef HAVE_DBUS
+        { "local-server-type",          parse_server_type,        c, NULL },
+#endif
         { "no-cpu-limit",               pa_config_parse_bool,     &c->no_cpu_limit, NULL },
         { "disable-shm",                pa_config_parse_bool,     &c->disable_shm, NULL },
         { "flat-volumes",               pa_config_parse_bool,     &c->flat_volumes, NULL },
@@ -604,6 +642,13 @@ static const char* const log_level_to_string[] = {
     [PA_LOG_ERROR] = "error"
 };
 
+static const char* const server_type_to_string[] = {
+    [PA_SERVER_TYPE_UNSET] = "!!UNSET!!",
+    [PA_SERVER_TYPE_USER] = "user",
+    [PA_SERVER_TYPE_SYSTEM] = "system",
+    [PA_SERVER_TYPE_NONE] = "none"
+};
+
 char *pa_daemon_conf_dump(pa_daemon_conf *c) {
     pa_strbuf *s;
     char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
@@ -627,6 +672,9 @@ char *pa_daemon_conf_dump(pa_daemon_conf *c) {
     pa_strbuf_printf(s, "disallow-exit = %s\n", pa_yes_no(c->disallow_exit));
     pa_strbuf_printf(s, "use-pid-file = %s\n", pa_yes_no(c->use_pid_file));
     pa_strbuf_printf(s, "system-instance = %s\n", pa_yes_no(c->system_instance));
+#ifdef HAVE_DBUS
+    pa_strbuf_printf(s, "local-server-type = %s\n", server_type_to_string[c->local_server_type]);
+#endif
     pa_strbuf_printf(s, "no-cpu-limit = %s\n", pa_yes_no(c->no_cpu_limit));
     pa_strbuf_printf(s, "disable-shm = %s\n", pa_yes_no(c->disable_shm));
     pa_strbuf_printf(s, "flat-volumes = %s\n", pa_yes_no(c->flat_volumes));
diff --git a/src/daemon/daemon-conf.h b/src/daemon/daemon-conf.h
index 9cec189..98db864 100644
--- a/src/daemon/daemon-conf.h
+++ b/src/daemon/daemon-conf.h
@@ -48,6 +48,13 @@ typedef enum pa_daemon_conf_cmd {
     PA_CMD_CLEANUP_SHM
 } pa_daemon_conf_cmd_t;
 
+typedef enum pa_daemon_conf_server_type {
+    PA_SERVER_TYPE_UNSET,
+    PA_SERVER_TYPE_USER,
+    PA_SERVER_TYPE_SYSTEM,
+    PA_SERVER_TYPE_NONE
+} pa_daemon_conf_server_type_t;
+
 #ifdef HAVE_SYS_RESOURCE_H
 typedef struct pa_rlimit {
     rlim_t value;
@@ -74,6 +81,7 @@ typedef struct pa_daemon_conf {
         log_meta,
         log_time,
         flat_volumes;
+    pa_daemon_conf_server_type_t local_server_type;
     int exit_idle_time,
         scache_idle_time,
         auto_log_target,
@@ -151,6 +159,7 @@ int pa_daemon_conf_env(pa_daemon_conf *c);
 int pa_daemon_conf_set_log_target(pa_daemon_conf *c, const char *string);
 int pa_daemon_conf_set_log_level(pa_daemon_conf *c, const char *string);
 int pa_daemon_conf_set_resample_method(pa_daemon_conf *c, const char *string);
+int pa_daemon_conf_set_local_server_type(pa_daemon_conf *c, const char *string);
 
 const char *pa_daemon_conf_get_default_script_file(pa_daemon_conf *c);
 FILE *pa_daemon_conf_open_default_script_file(pa_daemon_conf *c);
diff --git a/src/daemon/daemon.conf.in b/src/daemon/daemon.conf.in
index fcd2513..ecdb3a6 100644
--- a/src/daemon/daemon.conf.in
+++ b/src/daemon/daemon.conf.in
@@ -25,6 +25,7 @@
 ; disallow-exit = no
 ; use-pid-file = yes
 ; system-instance = no
+; local-server-type = user
 ; disable-shm = no
 ; shm-size-bytes = 0 # setting this 0 will use the system-default, usually 64 MiB
 
diff --git a/src/daemon/main.c b/src/daemon/main.c
index 3e50baa..1b6ed79 100644
--- a/src/daemon/main.c
+++ b/src/daemon/main.c
@@ -99,6 +99,7 @@
 #include "caps.h"
 #include "ltdl-bind-now.h"
 #include "polkit.h"
+#include "server-lookup.h"
 
 #ifdef HAVE_LIBWRAP
 /* Only one instance of these variables */
@@ -335,33 +336,31 @@ static void set_all_rlimits(const pa_daemon_conf *conf) {
 #endif
 
 #ifdef HAVE_DBUS
-static pa_dbus_connection *register_dbus(pa_core *c) {
+static pa_dbus_connection *register_dbus_name(pa_core *c, DBusBusType bus, const char* name) {
     DBusError error;
     pa_dbus_connection *conn;
 
     dbus_error_init(&error);
 
-    if (!(conn = pa_dbus_bus_get(c, pa_in_system_mode() ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&error)) {
+    if (!(conn = pa_dbus_bus_get(c, bus, &error)) || dbus_error_is_set(&error)) {
         pa_log_warn("Unable to contact D-Bus: %s: %s", error.name, error.message);
         goto fail;
     }
 
-    if (dbus_bus_request_name(pa_dbus_connection_get(conn), "org.pulseaudio.Server", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error) == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
-        pa_log_debug("Got org.pulseaudio.Server!");
+    if (dbus_bus_request_name(pa_dbus_connection_get(conn), name, DBUS_NAME_FLAG_DO_NOT_QUEUE, &error) == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+        pa_log_debug("Got %s!", name);
         return conn;
     }
 
     if (dbus_error_is_set(&error))
-        pa_log_warn("Failed to acquire org.pulseaudio.Server: %s: %s", error.name, error.message);
+        pa_log_error("Failed to acquire %s: %s: %s", name, error.name, error.message);
     else
-        pa_log_warn("D-Bus name org.pulseaudio.Server already taken. Weird shit!");
+        pa_log_error("D-Bus name %s already taken. Weird shit!", name);
 
     /* PA cannot be started twice by the same user and hence we can
-     * ignore mostly the case that org.pulseaudio.Server is already
-     * taken. */
+     * ignore mostly the case that a name is already taken. */
 
 fail:
-
     if (conn)
         pa_dbus_connection_unref(conn);
 
@@ -393,7 +392,10 @@ int main(int argc, char *argv[]) {
     int autospawn_fd = -1;
     pa_bool_t autospawn_locked = FALSE;
 #ifdef HAVE_DBUS
-    pa_dbus_connection *dbus = NULL;
+    pa_dbusobj_server_lookup *server_lookup = NULL; /* /org/pulseaudio/server_lookup */
+    pa_dbus_connection *lookup_service_bus = NULL; /* Always the user bus. */
+    pa_dbus_connection *server_bus = NULL; /* The bus where we reserve org.pulseaudio.Server, either the user or the system bus. */
+    pa_bool_t start_server;
 #endif
 
     pa_log_set_ident("pulseaudio");
@@ -486,8 +488,45 @@ int main(int argc, char *argv[]) {
         pa_log_set_flags(PA_LOG_PRINT_TIME, PA_LOG_SET);
     pa_log_set_show_backtrace(conf->log_backtrace);
 
+#ifdef HAVE_DBUS
+    /* conf->system_instance and conf->local_server_type control almost the
+     * same thing; make them agree about what is requested. */
+    switch (conf->local_server_type) {
+        case PA_SERVER_TYPE_UNSET:
+            conf->local_server_type = conf->system_instance ? PA_SERVER_TYPE_SYSTEM : PA_SERVER_TYPE_USER;
+            break;
+        case PA_SERVER_TYPE_USER:
+        case PA_SERVER_TYPE_NONE:
+            conf->system_instance = FALSE;
+            break;
+        case PA_SERVER_TYPE_SYSTEM:
+            conf->system_instance = TRUE;
+            break;
+        default:
+            pa_assert_not_reached();
+    }
+
+    start_server = conf->local_server_type == PA_SERVER_TYPE_USER || (real_root && conf->local_server_type == PA_SERVER_TYPE_SYSTEM);
+
+    if (!start_server && conf->local_server_type == PA_SERVER_TYPE_SYSTEM) {
+        pa_log_notice(_("System mode refused for non-root user. Only starting the D-Bus server lookup service."));
+        conf->system_instance = FALSE;
+    }
+#endif
+
     pa_log_debug("Started as real root: %s, suid root: %s", pa_yes_no(real_root), pa_yes_no(suid_root));
 
+#ifdef HAVE_DBUS
+    /* XXX: Uhh, goto programming... as if this wasn't hard enough to follow
+     * already. But if we won't start the full server, we want to just skip all
+     * the capability stuff. */
+    if (!start_server) {
+        if (!real_root && pa_have_caps())
+            pa_drop_caps();
+        goto after_caps_setup;
+    }
+#endif
+
     if (!real_root && pa_have_caps()) {
 #ifdef HAVE_SYS_RESOURCE_H
         struct rlimit rl;
@@ -628,6 +667,10 @@ int main(int argc, char *argv[]) {
         conf->realtime_scheduling = FALSE;
     }
 
+#ifdef HAVE_DBUS
+after_caps_setup:
+#endif
+
     pa_log_debug("Can realtime: %s, can high-priority: %s", pa_yes_no(pa_can_realtime()), pa_yes_no(pa_can_high_priority()));
 
     LTDL_SET_PRELOADED_SYMBOLS();
@@ -716,10 +759,12 @@ int main(int argc, char *argv[]) {
 
     if (real_root && !conf->system_instance)
         pa_log_warn(_("This program is not intended to be run as root (unless --system is specified)."));
+#ifndef HAVE_DBUS /* A similar, only a notice worthy check was done earlier, if D-Bus is enabled. */
     else if (!real_root && conf->system_instance) {
         pa_log(_("Root privileges required."));
         goto finish;
     }
+#endif
 
     if (conf->cmd == PA_CMD_START && conf->system_instance) {
         pa_log(_("--start not supported for system instances."));
@@ -1007,34 +1052,47 @@ int main(int argc, char *argv[]) {
         pa_assert_se(pa_cpu_limit_init(pa_mainloop_get_api(mainloop)) == 0);
 
     buf = pa_strbuf_new();
-    if (conf->load_default_script_file) {
-        FILE *f;
 
-        if ((f = pa_daemon_conf_open_default_script_file(conf))) {
-            r = pa_cli_command_execute_file_stream(c, f, buf, &conf->fail);
-            fclose(f);
+#ifdef HAVE_DBUS
+    if (start_server) {
+#endif
+        if (conf->load_default_script_file) {
+            FILE *f;
+
+            if ((f = pa_daemon_conf_open_default_script_file(conf))) {
+                r = pa_cli_command_execute_file_stream(c, f, buf, &conf->fail);
+                fclose(f);
+            }
         }
-    }
 
-    if (r >= 0)
-        r = pa_cli_command_execute(c, conf->script_commands, buf, &conf->fail);
+        if (r >= 0)
+            r = pa_cli_command_execute(c, conf->script_commands, buf, &conf->fail);
 
-    pa_log_error("%s", s = pa_strbuf_tostring_free(buf));
-    pa_xfree(s);
+        pa_log_error("%s", s = pa_strbuf_tostring_free(buf));
+        pa_xfree(s);
 
-    /* We completed the initial module loading, so let's disable it
-     * from now on, if requested */
-    c->disallow_module_loading = !!conf->disallow_module_loading;
+        if (r < 0 && conf->fail) {
+            pa_log(_("Failed to initialize daemon."));
+            goto finish;
+        }
 
-    if (r < 0 && conf->fail) {
-        pa_log(_("Failed to initialize daemon."));
-        goto finish;
+        if (!c->modules || pa_idxset_size(c->modules) == 0) {
+            pa_log(_("Daemon startup without any loaded modules, refusing to work."));
+            goto finish;
+        }
+#ifdef HAVE_DBUS
+    } else {
+        /* When we just provide the D-Bus server lookup service, we don't want
+         * any modules to be loaded. We haven't loaded any so far, so one might
+         * think there's no way to contact the server, but receiving certain
+         * signals could still cause modules to load. */
+        conf->disallow_module_loading = TRUE;
     }
+#endif
 
-    if (!c->modules || pa_idxset_size(c->modules) == 0) {
-        pa_log(_("Daemon startup without any loaded modules, refusing to work."));
-        goto finish;
-    }
+    /* We completed the initial module loading, so let's disable it
+     * from now on, if requested */
+    c->disallow_module_loading = !!conf->disallow_module_loading;
 
 #ifdef HAVE_FORK
     if (daemon_pipe[1] >= 0) {
@@ -1046,7 +1104,15 @@ int main(int argc, char *argv[]) {
 #endif
 
 #ifdef HAVE_DBUS
-    dbus = register_dbus(c);
+    if (!conf->system_instance) {
+        if (!(server_lookup = pa_dbusobj_server_lookup_new(c, conf->local_server_type)))
+            goto finish;
+        if (!(lookup_service_bus = register_dbus_name(c, DBUS_BUS_SESSION, "org.pulseaudio.PulseAudio")))
+            goto finish;
+    }
+
+    if (start_server && !(server_bus = register_dbus_name(c, conf->system_instance ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION, "org.pulseaudio.Server")))
+        goto finish;
 #endif
 
     pa_log_info(_("Daemon startup complete."));
@@ -1059,8 +1125,12 @@ int main(int argc, char *argv[]) {
 
 finish:
 #ifdef HAVE_DBUS
-    if (dbus)
-        pa_dbus_connection_unref(dbus);
+    if (server_bus)
+        pa_dbus_connection_unref(server_bus);
+    if (lookup_service_bus)
+        pa_dbus_connection_unref(lookup_service_bus);
+    if (server_lookup)
+        pa_dbusobj_server_lookup_free(server_lookup);
 #endif
 
     if (autospawn_fd >= 0) {
diff --git a/src/daemon/server-lookup.c b/src/daemon/server-lookup.c
new file mode 100644
index 0000000..867c3a1
--- /dev/null
+++ b/src/daemon/server-lookup.c
@@ -0,0 +1,277 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <dbus/dbus.h>
+
+#include <pulse/client-conf.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-shared.h>
+#include <pulsecore/macro.h>
+
+#include "server-lookup.h"
+
+struct pa_dbusobj_server_lookup {
+    pa_dbus_connection *conn;
+    pa_bool_t path_registered;
+    pa_daemon_conf_server_type_t server_type;
+};
+
+static const char introspection[] =
+    DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
+    "<node>"
+    " <!-- If you are looking for documentation make sure to check out\n"
+    "      http://pulseaudio.org/wiki/DBusInterface -->\n"
+    " <interface name=\"org.pulseaudio.ServerLookup\">"
+    "  <method name=\"GetDBusServers\">"
+    "   <arg name=\"result\" type=\"s\" direction=\"out\"/>"
+    "  </method>"
+    " </interface>"
+    " <interface name=\"org.freedesktop.DBus.Introspectable\">"
+    "  <method name=\"Introspect\">"
+    "   <arg name=\"data\" type=\"s\" direction=\"out\"/>"
+    "  </method>"
+    " </interface>"
+    "</node>";
+
+static void unregister_cb(DBusConnection *conn, void *user_data) {
+    pa_dbusobj_server_lookup *sl = user_data;
+
+    pa_assert(sl);
+    pa_assert(sl->path_registered);
+
+    sl->path_registered = FALSE;
+}
+
+static DBusHandlerResult handle_introspect(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) {
+    const char *i = introspection;
+    DBusMessage *reply = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+
+    if (!(reply = dbus_message_new_method_return(msg)))
+        goto fail;
+
+    if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &i, DBUS_TYPE_INVALID))
+        goto fail;
+
+    if (!dbus_connection_send(conn, reply, NULL))
+        goto oom;
+
+    dbus_message_unref(reply);
+
+    return DBUS_HANDLER_RESULT_HANDLED;
+
+fail:
+    if (reply)
+        dbus_message_unref(reply);
+
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+oom:
+    if (reply)
+        dbus_message_unref(reply);
+
+    return DBUS_HANDLER_RESULT_NEED_MEMORY;
+}
+
+/* Caller frees the string. */
+static char *get_dbus_server_from_type(pa_daemon_conf_server_type_t server_type) {
+    char *server_string = NULL;
+    char *runtime_dir = NULL;
+
+    switch (server_type) {
+        case PA_SERVER_TYPE_USER:
+            runtime_dir = pa_get_runtime_dir();
+
+            if (!runtime_dir)
+                return NULL;
+
+            server_string = pa_sprintf_malloc("unix:path=%s/dbus_socket", runtime_dir);
+            break;
+
+        case PA_SERVER_TYPE_SYSTEM:
+            server_string = pa_xstrdup("unix:path=/var/run/pulse/dbus_socket");
+            break;
+
+        case PA_SERVER_TYPE_NONE:
+            server_string = pa_xnew0(char, 1);
+            break;
+
+        default:
+            pa_assert_not_reached();
+    }
+
+    return server_string;
+}
+
+static DBusHandlerResult handle_get_dbus_servers(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) {
+    DBusMessage *reply = NULL;
+    pa_client_conf *conf = NULL;
+    char *server_string = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(sl);
+
+    conf = pa_client_conf_new();
+
+    if (pa_client_conf_load(conf, NULL) < 0) {
+        if (!(reply = dbus_message_new_error(msg, "org.pulseaudio.ClientConfLoadError", "Failed to load client.conf.")))
+            goto fail;
+        if (!dbus_connection_send(conn, reply, NULL))
+            goto oom;
+        return DBUS_HANDLER_RESULT_HANDLED;
+    }
+
+    server_string = pa_xstrdup(conf->default_dbus_server);
+
+    pa_client_conf_free(conf);
+
+    if (!server_string) {
+        if (!(server_string = get_dbus_server_from_type(sl->server_type))) {
+            if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_FAILED, "get_dbus_server_from_type() failed.")))
+                goto fail;
+            if (!dbus_connection_send(conn, reply, NULL))
+                goto oom;
+            return DBUS_HANDLER_RESULT_HANDLED;
+        }
+    }
+
+    if (!(reply = dbus_message_new_method_return(msg)))
+        goto oom;
+
+    if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &server_string, DBUS_TYPE_INVALID))
+        goto fail;
+
+    if (!dbus_connection_send(conn, reply, NULL))
+        goto oom;
+
+    pa_log("Sent reply with server_string '%s'.", server_string);
+
+    pa_xfree(server_string);
+
+    dbus_message_unref(reply);
+
+    return DBUS_HANDLER_RESULT_HANDLED;
+
+fail:
+    if (conf)
+        pa_client_conf_free(conf);
+
+    pa_xfree(server_string);
+
+    if (reply)
+        dbus_message_unref(reply);
+
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+oom:
+    if (conf)
+        pa_client_conf_free(conf);
+
+    pa_xfree(server_string);
+
+    if (reply)
+        dbus_message_unref(reply);
+
+    return DBUS_HANDLER_RESULT_NEED_MEMORY;
+}
+
+static DBusHandlerResult message_cb(DBusConnection *conn, DBusMessage *msg, void *user_data) {
+    pa_dbusobj_server_lookup *sl = user_data;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(sl);
+
+    /* pa_log("Got message! type = %s   path = %s   iface = %s   member = %s   dest = %s", dbus_message_type_to_string(dbus_message_get_type(msg)), dbus_message_get_path(msg), dbus_message_get_interface(msg), dbus_message_get_member(msg), dbus_message_get_destination(msg)); */
+
+    if (dbus_message_is_method_call(msg, "org.freedesktop.DBus.Introspectable", "Introspect"))
+        return handle_introspect(conn, msg, sl);
+
+    if (dbus_message_is_method_call(msg, "org.pulseaudio.ServerLookup", "GetDBusServers"))
+        return handle_get_dbus_servers(conn, msg, sl);
+
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusObjectPathVTable vtable = {
+    .unregister_function = unregister_cb,
+    .message_function = message_cb,
+    .dbus_internal_pad1 = NULL,
+    .dbus_internal_pad2 = NULL,
+    .dbus_internal_pad3 = NULL,
+    .dbus_internal_pad4 = NULL
+};
+
+pa_dbusobj_server_lookup *pa_dbusobj_server_lookup_new(pa_core *c, pa_daemon_conf_server_type_t server_type) {
+    pa_dbusobj_server_lookup *sl;
+    DBusError error;
+
+    dbus_error_init(&error);
+
+    sl = pa_xnew(pa_dbusobj_server_lookup, 1);
+    sl->path_registered = FALSE;
+    sl->server_type = server_type;
+
+    if (!(sl->conn = pa_dbus_bus_get(c, DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&error)) {
+        pa_log("Unable to contact D-Bus: %s: %s", error.name, error.message);
+        goto fail;
+    }
+
+    if (!dbus_connection_register_object_path(pa_dbus_connection_get(sl->conn), "/org/pulseaudio/server_lookup", &vtable, sl)) {
+        pa_log("dbus_connection_register_object_path() failed for /org/pulseaudio/server_lookup.");
+        goto fail;
+    }
+
+    sl->path_registered = TRUE;
+
+    return sl;
+
+fail:
+    dbus_error_free(&error);
+
+    pa_dbusobj_server_lookup_free(sl);
+
+    return NULL;
+}
+
+void pa_dbusobj_server_lookup_free(pa_dbusobj_server_lookup *sl) {
+    pa_assert(sl);
+
+    if (sl->path_registered) {
+        pa_assert(sl->conn);
+        if (!dbus_connection_unregister_object_path(pa_dbus_connection_get(sl->conn), "/org/pulseaudio/server_lookup"))
+            pa_log_debug("dbus_connection_unregister_object_path() failed for /org/pulseaudio/server_lookup.");
+    }
+
+    if (sl->conn)
+        pa_dbus_connection_unref(sl->conn);
+
+    pa_xfree(sl);
+}
\ No newline at end of file
diff --git a/src/daemon/server-lookup.h b/src/daemon/server-lookup.h
new file mode 100644
index 0000000..69fdacd
--- /dev/null
+++ b/src/daemon/server-lookup.h
@@ -0,0 +1,42 @@
+#ifndef fooserverlookuphfoo
+#define fooserverlookuphfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+/* This object implements the D-Bus object at path
+ * /org/pulseaudio/server_lookup. Implemented interfaces
+ * are org.pulseaudio.ServerLookup and org.freedesktop.DBus.Introspectable.
+ *
+ * See http://pulseaudio.org/wiki/DBusInterface for the ServerLookup interface
+ * documentation.
+ */
+
+#include <pulsecore/core.h>
+
+#include "daemon-conf.h"
+
+typedef struct pa_dbusobj_server_lookup pa_dbusobj_server_lookup;
+
+pa_dbusobj_server_lookup *pa_dbusobj_server_lookup_new(pa_core *c, pa_daemon_conf_server_type_t server_type);
+void pa_dbusobj_server_lookup_free(pa_dbusobj_server_lookup *sl);
+
+#endif
\ No newline at end of file
diff --git a/src/pulse/client-conf.c b/src/pulse/client-conf.c
index 940d0b6..8eab109 100644
--- a/src/pulse/client-conf.c
+++ b/src/pulse/client-conf.c
@@ -57,6 +57,7 @@ static const pa_client_conf default_conf = {
     .default_sink = NULL,
     .default_source = NULL,
     .default_server = NULL,
+    .default_dbus_server = NULL,
     .autospawn = TRUE,
     .disable_shm = FALSE,
     .cookie_file = NULL,
@@ -81,6 +82,7 @@ void pa_client_conf_free(pa_client_conf *c) {
     pa_xfree(c->default_sink);
     pa_xfree(c->default_source);
     pa_xfree(c->default_server);
+    pa_xfree(c->default_dbus_server);
     pa_xfree(c->cookie_file);
     pa_xfree(c);
 }
@@ -97,6 +99,7 @@ int pa_client_conf_load(pa_client_conf *c, const char *filename) {
         { "default-sink",           pa_config_parse_string,  &c->default_sink, NULL },
         { "default-source",         pa_config_parse_string,  &c->default_source, NULL },
         { "default-server",         pa_config_parse_string,  &c->default_server, NULL },
+        { "default-dbus-server",    pa_config_parse_string,  &c->default_dbus_server, NULL },
         { "autospawn",              pa_config_parse_bool,    &c->autospawn, NULL },
         { "cookie-file",            pa_config_parse_string,  &c->cookie_file, NULL },
         { "disable-shm",            pa_config_parse_bool,    &c->disable_shm, NULL },
diff --git a/src/pulse/client-conf.h b/src/pulse/client-conf.h
index ab97dc6..618216f 100644
--- a/src/pulse/client-conf.h
+++ b/src/pulse/client-conf.h
@@ -22,12 +22,13 @@
   USA.
 ***/
 
+#include <pulsecore/macro.h>
 #include <pulsecore/native-common.h>
 
 /* A structure containing configuration data for PulseAudio clients. */
 
 typedef struct pa_client_conf {
-    char *daemon_binary, *extra_arguments, *default_sink, *default_source, *default_server, *cookie_file;
+    char *daemon_binary, *extra_arguments, *default_sink, *default_source, *default_server, *default_dbus_server, *cookie_file;
     pa_bool_t autospawn, disable_shm;
     uint8_t cookie[PA_NATIVE_COOKIE_LENGTH];
     pa_bool_t cookie_valid; /* non-zero, when cookie is valid */
diff --git a/src/pulse/client.conf.in b/src/pulse/client.conf.in
index 579bcc2..3340b0b 100644
--- a/src/pulse/client.conf.in
+++ b/src/pulse/client.conf.in
@@ -22,6 +22,7 @@
 ; default-sink =
 ; default-source =
 ; default-server =
+; default-dbus-server =
 
 ; autospawn = yes
 ; daemon-binary = @PA_BINARY@

commit c8d819a5adbe32e14d7f03a252bca6f7df01d795
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Tue Jun 16 19:03:22 2009 +0300

    dbus-protocol: Connection handling for local connections.

diff --git a/src/Makefile.am b/src/Makefile.am
index c56f760..5302bc2 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -783,6 +783,7 @@ libpulsecore_ at PA_MAJORMINORMICRO@_la_SOURCES = \
 		pulsecore/core-scache.c pulsecore/core-scache.h \
 		pulsecore/core-subscribe.c pulsecore/core-subscribe.h \
 		pulsecore/core.c pulsecore/core.h \
+        pulsecore/dbus-common.c pulsecore/dbus-common.h \
 		pulsecore/envelope.c pulsecore/envelope.h \
 		pulsecore/fdsem.c pulsecore/fdsem.h \
 		pulsecore/g711.c pulsecore/g711.h \
@@ -796,6 +797,7 @@ libpulsecore_ at PA_MAJORMINORMICRO@_la_SOURCES = \
 		pulsecore/object.c pulsecore/object.h \
 		pulsecore/play-memblockq.c pulsecore/play-memblockq.h \
 		pulsecore/play-memchunk.c pulsecore/play-memchunk.h \
+		pulsecore/protocol-dbus.h \
 		pulsecore/resampler.c pulsecore/resampler.h \
 		pulsecore/rtpoll.c pulsecore/rtpoll.h \
 		pulsecore/rtsig.c pulsecore/rtsig.h \
diff --git a/src/daemon/daemon-conf.h b/src/daemon/daemon-conf.h
index 98db864..c4f78cb 100644
--- a/src/daemon/daemon-conf.h
+++ b/src/daemon/daemon-conf.h
@@ -28,6 +28,7 @@
 
 #include <pulsecore/log.h>
 #include <pulsecore/macro.h>
+#include <pulsecore/core.h>
 #include <pulsecore/core-util.h>
 
 #ifdef HAVE_SYS_RESOURCE_H
@@ -48,13 +49,6 @@ typedef enum pa_daemon_conf_cmd {
     PA_CMD_CLEANUP_SHM
 } pa_daemon_conf_cmd_t;
 
-typedef enum pa_daemon_conf_server_type {
-    PA_SERVER_TYPE_UNSET,
-    PA_SERVER_TYPE_USER,
-    PA_SERVER_TYPE_SYSTEM,
-    PA_SERVER_TYPE_NONE
-} pa_daemon_conf_server_type_t;
-
 #ifdef HAVE_SYS_RESOURCE_H
 typedef struct pa_rlimit {
     rlim_t value;
@@ -81,7 +75,7 @@ typedef struct pa_daemon_conf {
         log_meta,
         log_time,
         flat_volumes;
-    pa_daemon_conf_server_type_t local_server_type;
+    pa_server_type_t local_server_type;
     int exit_idle_time,
         scache_idle_time,
         auto_log_target,
diff --git a/src/daemon/main.c b/src/daemon/main.c
index 1b6ed79..62214a5 100644
--- a/src/daemon/main.c
+++ b/src/daemon/main.c
@@ -1028,6 +1028,7 @@ after_caps_setup:
     c->running_as_daemon = !!conf->daemonize;
     c->disallow_exit = conf->disallow_exit;
     c->flat_volumes = conf->flat_volumes;
+    c->server_type = conf->local_server_type;
 
     pa_assert_se(pa_signal_init(pa_mainloop_get_api(mainloop)) == 0);
     pa_signal_new(SIGINT, signal_callback, c);
@@ -1105,7 +1106,7 @@ after_caps_setup:
 
 #ifdef HAVE_DBUS
     if (!conf->system_instance) {
-        if (!(server_lookup = pa_dbusobj_server_lookup_new(c, conf->local_server_type)))
+        if (!(server_lookup = pa_dbusobj_server_lookup_new(c)))
             goto finish;
         if (!(lookup_service_bus = register_dbus_name(c, DBUS_BUS_SESSION, "org.pulseaudio.PulseAudio")))
             goto finish;
diff --git a/src/daemon/server-lookup.c b/src/daemon/server-lookup.c
index 867c3a1..b53fda7 100644
--- a/src/daemon/server-lookup.c
+++ b/src/daemon/server-lookup.c
@@ -28,16 +28,18 @@
 #include <pulse/client-conf.h>
 #include <pulse/xmalloc.h>
 
+#include <pulsecore/core.h>
 #include <pulsecore/core-util.h>
+#include <pulsecore/dbus-common.h>
 #include <pulsecore/dbus-shared.h>
 #include <pulsecore/macro.h>
 
 #include "server-lookup.h"
 
 struct pa_dbusobj_server_lookup {
+    pa_core *core;
     pa_dbus_connection *conn;
     pa_bool_t path_registered;
-    pa_daemon_conf_server_type_t server_type;
 };
 
 static const char introspection[] =
@@ -46,7 +48,7 @@ static const char introspection[] =
     " <!-- If you are looking for documentation make sure to check out\n"
     "      http://pulseaudio.org/wiki/DBusInterface -->\n"
     " <interface name=\"org.pulseaudio.ServerLookup\">"
-    "  <method name=\"GetDBusServers\">"
+    "  <method name=\"GetDBusAddress\">"
     "   <arg name=\"result\" type=\"s\" direction=\"out\"/>"
     "  </method>"
     " </interface>"
@@ -99,40 +101,10 @@ oom:
     return DBUS_HANDLER_RESULT_NEED_MEMORY;
 }
 
-/* Caller frees the string. */
-static char *get_dbus_server_from_type(pa_daemon_conf_server_type_t server_type) {
-    char *server_string = NULL;
-    char *runtime_dir = NULL;
-
-    switch (server_type) {
-        case PA_SERVER_TYPE_USER:
-            runtime_dir = pa_get_runtime_dir();
-
-            if (!runtime_dir)
-                return NULL;
-
-            server_string = pa_sprintf_malloc("unix:path=%s/dbus_socket", runtime_dir);
-            break;
-
-        case PA_SERVER_TYPE_SYSTEM:
-            server_string = pa_xstrdup("unix:path=/var/run/pulse/dbus_socket");
-            break;
-
-        case PA_SERVER_TYPE_NONE:
-            server_string = pa_xnew0(char, 1);
-            break;
-
-        default:
-            pa_assert_not_reached();
-    }
-
-    return server_string;
-}
-
-static DBusHandlerResult handle_get_dbus_servers(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) {
+static DBusHandlerResult handle_get_dbus_address(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) {
     DBusMessage *reply = NULL;
     pa_client_conf *conf = NULL;
-    char *server_string = NULL;
+    char *address = NULL;
 
     pa_assert(conn);
     pa_assert(msg);
@@ -148,12 +120,13 @@ static DBusHandlerResult handle_get_dbus_servers(DBusConnection *conn, DBusMessa
         return DBUS_HANDLER_RESULT_HANDLED;
     }
 
-    server_string = pa_xstrdup(conf->default_dbus_server);
-
     pa_client_conf_free(conf);
 
-    if (!server_string) {
-        if (!(server_string = get_dbus_server_from_type(sl->server_type))) {
+    if (conf->default_dbus_server) {
+        if (!(address = dbus_address_escape_value(conf->default_dbus_server)))
+            goto oom;
+    } else {
+        if (!(address = pa_get_dbus_address_from_server_type(sl->core->server_type))) {
             if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_FAILED, "get_dbus_server_from_type() failed.")))
                 goto fail;
             if (!dbus_connection_send(conn, reply, NULL))
@@ -165,15 +138,15 @@ static DBusHandlerResult handle_get_dbus_servers(DBusConnection *conn, DBusMessa
     if (!(reply = dbus_message_new_method_return(msg)))
         goto oom;
 
-    if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &server_string, DBUS_TYPE_INVALID))
+    if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &address, DBUS_TYPE_INVALID))
         goto fail;
 
     if (!dbus_connection_send(conn, reply, NULL))
         goto oom;
 
-    pa_log("Sent reply with server_string '%s'.", server_string);
+    pa_log_debug("handle_get_dbus_address(): Sent reply with address '%s'.", address);
 
-    pa_xfree(server_string);
+    pa_xfree(address);
 
     dbus_message_unref(reply);
 
@@ -183,7 +156,7 @@ fail:
     if (conf)
         pa_client_conf_free(conf);
 
-    pa_xfree(server_string);
+    pa_xfree(address);
 
     if (reply)
         dbus_message_unref(reply);
@@ -194,7 +167,7 @@ oom:
     if (conf)
         pa_client_conf_free(conf);
 
-    pa_xfree(server_string);
+    pa_xfree(address);
 
     if (reply)
         dbus_message_unref(reply);
@@ -214,8 +187,8 @@ static DBusHandlerResult message_cb(DBusConnection *conn, DBusMessage *msg, void
     if (dbus_message_is_method_call(msg, "org.freedesktop.DBus.Introspectable", "Introspect"))
         return handle_introspect(conn, msg, sl);
 
-    if (dbus_message_is_method_call(msg, "org.pulseaudio.ServerLookup", "GetDBusServers"))
-        return handle_get_dbus_servers(conn, msg, sl);
+    if (dbus_message_is_method_call(msg, "org.pulseaudio.ServerLookup", "GetDBusAddress"))
+        return handle_get_dbus_address(conn, msg, sl);
 
     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 }
@@ -229,15 +202,15 @@ static DBusObjectPathVTable vtable = {
     .dbus_internal_pad4 = NULL
 };
 
-pa_dbusobj_server_lookup *pa_dbusobj_server_lookup_new(pa_core *c, pa_daemon_conf_server_type_t server_type) {
+pa_dbusobj_server_lookup *pa_dbusobj_server_lookup_new(pa_core *c) {
     pa_dbusobj_server_lookup *sl;
     DBusError error;
 
     dbus_error_init(&error);
 
     sl = pa_xnew(pa_dbusobj_server_lookup, 1);
+    sl->core = c;
     sl->path_registered = FALSE;
-    sl->server_type = server_type;
 
     if (!(sl->conn = pa_dbus_bus_get(c, DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&error)) {
         pa_log("Unable to contact D-Bus: %s: %s", error.name, error.message);
@@ -274,4 +247,4 @@ void pa_dbusobj_server_lookup_free(pa_dbusobj_server_lookup *sl) {
         pa_dbus_connection_unref(sl->conn);
 
     pa_xfree(sl);
-}
\ No newline at end of file
+}
diff --git a/src/daemon/server-lookup.h b/src/daemon/server-lookup.h
index 69fdacd..c930d5b 100644
--- a/src/daemon/server-lookup.h
+++ b/src/daemon/server-lookup.h
@@ -32,11 +32,9 @@
 
 #include <pulsecore/core.h>
 
-#include "daemon-conf.h"
-
 typedef struct pa_dbusobj_server_lookup pa_dbusobj_server_lookup;
 
-pa_dbusobj_server_lookup *pa_dbusobj_server_lookup_new(pa_core *c, pa_daemon_conf_server_type_t server_type);
+pa_dbusobj_server_lookup *pa_dbusobj_server_lookup_new(pa_core *c);
 void pa_dbusobj_server_lookup_free(pa_dbusobj_server_lookup *sl);
 
-#endif
\ No newline at end of file
+#endif
diff --git a/src/modules/module-dbus-protocol.c b/src/modules/module-dbus-protocol.c
index 077f417..1455148 100644
--- a/src/modules/module-dbus-protocol.c
+++ b/src/modules/module-dbus-protocol.c
@@ -2,6 +2,8 @@
   This file is part of PulseAudio.
 
   Copyright 2009 Tanu Kaskinen
+  Copyright 2006 Lennart Poettering
+  Copyright 2006 Shams E. King
 
   PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
@@ -23,37 +25,487 @@
 #include <config.h>
 #endif
 
+#include <dbus/dbus.h>
+
+#include <pulse/mainloop-api.h>
+#include <pulse/timeval.h>
 #include <pulse/xmalloc.h>
 
+#include <pulsecore/client.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-common.h>
+#include <pulsecore/dbus-util.h>
+#include <pulsecore/idxset.h>
 #include <pulsecore/macro.h>
+#include <pulsecore/modargs.h>
 #include <pulsecore/module.h>
 
 #include "module-dbus-protocol-symdef.h"
 
 PA_MODULE_DESCRIPTION("D-Bus interface");
-PA_MODULE_USAGE("<no module arguments>");
+PA_MODULE_USAGE(
+        "access=local|remote|local,remote "
+        "tcp_port=<port number>");
 PA_MODULE_LOAD_ONCE(TRUE);
 PA_MODULE_AUTHOR("Tanu Kaskinen");
 PA_MODULE_VERSION(PACKAGE_VERSION);
 
+#define CLEANUP_INTERVAL 10 /* seconds */
+
+struct server;
+struct connection;
+
 struct userdata {
     pa_module *module;
+    pa_bool_t local_access;
+    pa_bool_t remote_access;
+    uint32_t tcp_port;
+
+    struct server *local_server;
+    struct server *tcp_server;
+
+    pa_idxset *connections;
+
+    pa_time_event *cleanup_event;
+};
+
+struct server {
+    struct userdata *userdata;
+    DBusServer *dbus_server;
+};
+
+struct connection {
+    struct server *server;
+    pa_dbus_wrap_connection *wrap_conn;
+    pa_client *client;
+};
+
+static const char* const valid_modargs[] = {
+    "access",
+    "tcp_port",
+    NULL
 };
 
+static void connection_free(struct connection *c) {
+    pa_assert(c);
+
+    pa_client_free(c->client);
+    pa_assert_se(pa_idxset_remove_by_data(c->server->userdata->connections, c, NULL));
+    pa_dbus_wrap_connection_free(c->wrap_conn);
+    pa_xfree(c);
+}
+
+static void client_kill_cb(pa_client *c) {
+    struct connection *conn;
+
+    pa_assert(c);
+    pa_assert(c->userdata);
+
+    conn = c->userdata;
+    connection_free(conn);
+
+    pa_log_info("Connection killed.");
+}
+
+/* Called by D-Bus when a new client connection is received. */
+static void connection_new_cb(DBusServer *dbus_server, DBusConnection *new_connection, void *data) {
+    struct server *s = data;
+    struct connection *c;
+    pa_client_new_data new_data;
+    pa_client *client;
+
+    pa_assert(new_connection);
+    pa_assert(s);
+
+    pa_client_new_data_init(&new_data);
+    new_data.module = s->userdata->module;
+    new_data.driver = __FILE__;
+    pa_proplist_sets(new_data.proplist, PA_PROP_APPLICATION_NAME, "D-Bus client"); /* TODO: Fancier name. */
+    client = pa_client_new(s->userdata->module->core, &new_data);
+    pa_client_new_data_done(&new_data);
+
+    if (!client)
+        return;
+
+    c = pa_xnew(struct connection, 1);
+    c->server = s;
+    c->wrap_conn = pa_dbus_wrap_connection_new_from_existing(s->userdata->module->core->mainloop, new_connection);
+    c->client = client;
+
+    c->client->kill = client_kill_cb;
+    c->client->send_event = NULL;
+    c->client->userdata = c;
+
+    pa_idxset_put(s->userdata->connections, c, NULL);
+}
+
+/* Called by PA mainloop when a D-Bus fd watch event needs handling. */
+static void io_event_cb(pa_mainloop_api *mainloop, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
+    unsigned int flags = 0;
+    DBusWatch *watch = userdata;
+
+#if HAVE_DBUS_WATCH_GET_UNIX_FD
+    pa_assert(fd == dbus_watch_get_unix_fd(watch));
+#else
+    pa_assert(fd == dbus_watch_get_fd(watch));
+#endif
+
+    if (!dbus_watch_get_enabled(watch)) {
+        pa_log_warn("Asked to handle disabled watch: %p %i", (void*) watch, fd);
+        return;
+    }
+
+    if (events & PA_IO_EVENT_INPUT)
+        flags |= DBUS_WATCH_READABLE;
+    if (events & PA_IO_EVENT_OUTPUT)
+        flags |= DBUS_WATCH_WRITABLE;
+    if (events & PA_IO_EVENT_HANGUP)
+        flags |= DBUS_WATCH_HANGUP;
+    if (events & PA_IO_EVENT_ERROR)
+        flags |= DBUS_WATCH_ERROR;
+
+    dbus_watch_handle(watch, flags);
+}
+
+/* Called by PA mainloop when a D-Bus timer event needs handling. */
+static void time_event_cb(pa_mainloop_api *mainloop, pa_time_event* e, const struct timeval *tv, void *userdata) {
+    DBusTimeout *timeout = userdata;
+
+    if (dbus_timeout_get_enabled(timeout)) {
+        struct timeval next = *tv;
+        dbus_timeout_handle(timeout);
+
+        /* restart it for the next scheduled time */
+        pa_timeval_add(&next, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
+        mainloop->time_restart(e, &next);
+    }
+}
+
+/* Translates D-Bus fd watch event flags to PA IO event flags. */
+static pa_io_event_flags_t get_watch_flags(DBusWatch *watch) {
+    unsigned int flags;
+    pa_io_event_flags_t events = 0;
+
+    pa_assert(watch);
+
+    flags = dbus_watch_get_flags(watch);
+
+    /* no watch flags for disabled watches */
+    if (!dbus_watch_get_enabled(watch))
+        return PA_IO_EVENT_NULL;
+
+    if (flags & DBUS_WATCH_READABLE)
+        events |= PA_IO_EVENT_INPUT;
+    if (flags & DBUS_WATCH_WRITABLE)
+        events |= PA_IO_EVENT_OUTPUT;
+
+    return events | PA_IO_EVENT_HANGUP | PA_IO_EVENT_ERROR;
+}
+
+/* Called by D-Bus when a D-Bus fd watch event is added. */
+static dbus_bool_t watch_add_cb(DBusWatch *watch, void *data) {
+    struct server *s = data;
+    pa_mainloop_api *mainloop;
+    pa_io_event *ev;
+
+    pa_assert(watch);
+    pa_assert(s);
+
+    mainloop = s->userdata->module->core->mainloop;
+
+    ev = mainloop->io_new(
+            mainloop,
+#if HAVE_DBUS_WATCH_GET_UNIX_FD
+            dbus_watch_get_unix_fd(watch),
+#else
+            dbus_watch_get_fd(watch),
+#endif
+            get_watch_flags(watch), io_event_cb, watch);
+
+    dbus_watch_set_data(watch, ev, NULL);
+
+    return TRUE;
+}
+
+/* Called by D-Bus when a D-Bus fd watch event is removed. */
+static void watch_remove_cb(DBusWatch *watch, void *data) {
+    struct server *s = data;
+    pa_io_event *ev;
+
+    pa_assert(watch);
+    pa_assert(s);
+
+    if ((ev = dbus_watch_get_data(watch)))
+        s->userdata->module->core->mainloop->io_free(ev);
+}
+
+/* Called by D-Bus when a D-Bus fd watch event is toggled. */
+static void watch_toggled_cb(DBusWatch *watch, void *data) {
+    struct server *s = data;
+    pa_io_event *ev;
+
+    pa_assert(watch);
+    pa_assert(s);
+
+    pa_assert_se(ev = dbus_watch_get_data(watch));
+
+    /* get_watch_flags() checks if the watch is enabled */
+    s->userdata->module->core->mainloop->io_enable(ev, get_watch_flags(watch));
+}
+
+/* Called by D-Bus when a D-Bus timer event is added. */
+static dbus_bool_t timeout_add_cb(DBusTimeout *timeout, void *data) {
+    struct server *s = data;
+    pa_mainloop_api *mainloop;
+    pa_time_event *ev;
+    struct timeval tv;
+
+    pa_assert(timeout);
+    pa_assert(s);
+
+    if (!dbus_timeout_get_enabled(timeout))
+        return FALSE;
+
+    mainloop = s->userdata->module->core->mainloop;
+
+    pa_gettimeofday(&tv);
+    pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
+
+    ev = mainloop->time_new(mainloop, &tv, time_event_cb, timeout);
+
+    dbus_timeout_set_data(timeout, ev, NULL);
+
+    return TRUE;
+}
+
+/* Called by D-Bus when a D-Bus timer event is removed. */
+static void timeout_remove_cb(DBusTimeout *timeout, void *data) {
+    struct server *s = data;
+    pa_time_event *ev;
+
+    pa_assert(timeout);
+    pa_assert(s);
+
+    if ((ev = dbus_timeout_get_data(timeout)))
+        s->userdata->module->core->mainloop->time_free(ev);
+}
+
+/* Called by D-Bus when a D-Bus timer event is toggled. */
+static void timeout_toggled_cb(DBusTimeout *timeout, void *data) {
+    struct server *s = data;
+    pa_mainloop_api *mainloop;
+    pa_time_event *ev;
+
+    pa_assert(timeout);
+    pa_assert(s);
+
+    mainloop = s->userdata->module->core->mainloop;
+
+    pa_assert_se(ev = dbus_timeout_get_data(timeout));
+
+    if (dbus_timeout_get_enabled(timeout)) {
+        struct timeval tv;
+
+        pa_gettimeofday(&tv);
+        pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
+
+        mainloop->time_restart(ev, &tv);
+    } else
+        mainloop->time_restart(ev, NULL);
+}
+
+static void server_free(struct server *s) {
+    pa_assert(s);
+
+    if (s->dbus_server) {
+        dbus_server_disconnect(s->dbus_server);
+        dbus_server_unref(s->dbus_server);
+    }
+
+    pa_xfree(s);
+}
+
+static struct server *start_server(struct userdata *u, const char *address) {
+    /* XXX: We assume that when we unref the DBusServer instance at module
+     * shutdown, nobody else holds any references to it. If we stop assuming
+     * that someday, dbus_server_set_new_connection_function,
+     * dbus_server_set_watch_functions and dbus_server_set_timeout_functions
+     * calls should probably register free callbacks, instead of providing NULL
+     * as they do now. */
+
+    struct server *s = NULL;
+    DBusError error;
+
+    pa_assert(u);
+    pa_assert(address);
+
+    dbus_error_init(&error);
+
+    s = pa_xnew0(struct server, 1);
+    s->userdata = u;
+    s->dbus_server = dbus_server_listen(address, &error);
+
+    if (dbus_error_is_set(&error)) {
+        pa_log("dbus_server_listen() failed: %s: %s", error.name, error.message);
+        goto fail;
+    }
+
+    dbus_server_set_new_connection_function(s->dbus_server, connection_new_cb, s, NULL);
+
+    if (!dbus_server_set_watch_functions(s->dbus_server, watch_add_cb, watch_remove_cb, watch_toggled_cb, s, NULL)) {
+        pa_log("dbus_server_set_watch_functions() ran out of memory.");
+        goto fail;
+    }
+
+    if (!dbus_server_set_timeout_functions(s->dbus_server, timeout_add_cb, timeout_remove_cb, timeout_toggled_cb, s, NULL)) {
+        pa_log("dbus_server_set_timeout_functions() ran out of memory.");
+        goto fail;
+    }
+
+    return s;
+
+fail:
+    if (s)
+        server_free(s);
+
+    dbus_error_free(&error);
+
+    return NULL;
+}
+
+static struct server *start_local_server(struct userdata *u) {
+    struct server *s = NULL;
+    char *address = NULL;
+
+    pa_assert(u);
+
+    address = pa_get_dbus_address_from_server_type(u->module->core->server_type);
+
+    s = start_server(u, address); /* May return NULL */
+
+    pa_xfree(address);
+
+    return s;
+}
+
+static struct server *start_tcp_server(struct userdata *u) {
+    pa_log("start_tcp_server(): Not implemented!");
+    return NULL;
+}
+
+static int get_access_arg(pa_modargs *ma, pa_bool_t *local_access, pa_bool_t *remote_access) {
+    const char *value = NULL;
+
+    pa_assert(ma);
+    pa_assert(local_access);
+    pa_assert(remote_access);
+
+    if (!(value = pa_modargs_get_value(ma, "access", NULL)))
+        return 0;
+
+    if (!strcmp(value, "local")) {
+        *local_access = TRUE;
+        *remote_access = FALSE;
+    } else if (!strcmp(value, "remote")) {
+        *local_access = FALSE;
+        *remote_access = TRUE;
+    } else if (!strcmp(value, "local,remote")) {
+        *local_access = TRUE;
+        *local_access = TRUE;
+    } else
+        return -1;
+
+    return 0;
+}
+
+/* Frees dead client connections. Called every CLEANUP_INTERVAL seconds. */
+static void cleanup_cb(pa_mainloop_api *a, pa_time_event *e, const struct timeval *tv, void *userdata) {
+    struct userdata *u = userdata;
+    struct connection *conn = NULL;
+    uint32_t idx;
+    struct timeval cleanup_timeval;
+    unsigned free_count = 0;
+
+    for (conn = pa_idxset_first(u->connections, &idx); conn; conn = pa_idxset_next(u->connections, &idx)) {
+        if (!dbus_connection_get_is_connected(pa_dbus_wrap_connection_get(conn->wrap_conn))) {
+            connection_free(conn);
+            ++free_count;
+        }
+    }
+
+    if (free_count > 0)
+        pa_log_debug("Freed %u dead D-Bus client connections.", free_count);
+
+    pa_gettimeofday(&cleanup_timeval);
+    cleanup_timeval.tv_sec += CLEANUP_INTERVAL;
+    u->module->core->mainloop->time_restart(e, &cleanup_timeval);
+}
+
 int pa__init(pa_module *m) {
     struct userdata *u = NULL;
+    pa_modargs *ma = NULL;
+    struct timeval cleanup_timeval;
 
     pa_assert(m);
 
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
     m->userdata = u = pa_xnew0(struct userdata, 1);
     u->module = m;
+    u->local_access = TRUE;
+    u->remote_access = FALSE;
+    u->tcp_port = PA_DBUS_DEFAULT_PORT;
+
+    if (get_access_arg(ma, &u->local_access, &u->remote_access) < 0) {
+        pa_log("Invalid access argument: '%s'", pa_modargs_get_value(ma, "access", NULL));
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_u32(ma, "tcp_port", &u->tcp_port) < 0 || u->tcp_port < 1 || u->tcp_port > 49150) {
+        pa_log("Invalid tcp_port argument: '%s'", pa_modargs_get_value(ma, "tcp_port", NULL));
+        goto fail;
+    }
+
+    if (u->local_access && !(u->local_server = start_local_server(u))) {
+        pa_log("Starting the local D-Bus server failed.");
+        goto fail;
+    }
+
+    if (u->remote_access && !(u->tcp_server = start_tcp_server(u))) {
+        pa_log("Starting the D-Bus server for remote connections failed.");
+        goto fail;
+    }
 
-    pa_log_notice("Hello, world!");
+    u->connections = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+    pa_gettimeofday(&cleanup_timeval);
+    cleanup_timeval.tv_sec += CLEANUP_INTERVAL;
+    u->cleanup_event = m->core->mainloop->time_new(m->core->mainloop, &cleanup_timeval, cleanup_cb, u);
 
     return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
 }
 
-void pa__done(pa_module*m) {
+/* Called by idxset when the connection set is freed. */
+static void connection_free_cb(void *p, void *userdata) {
+    struct connection *conn = p;
+
+    pa_assert(conn);
+
+    connection_free(conn);
+}
+
+void pa__done(pa_module *m) {
     struct userdata *u;
 
     pa_assert(m);
@@ -61,6 +513,18 @@ void pa__done(pa_module*m) {
     if (!(u = m->userdata))
         return;
 
+    if (u->cleanup_event)
+        m->core->mainloop->time_free(u->cleanup_event);
+
+    if (u->connections)
+        pa_idxset_free(u->connections, connection_free_cb, NULL);
+
+    if (u->tcp_server)
+        server_free(u->tcp_server);
+
+    if (u->local_server)
+        server_free(u->local_server);
+
     pa_xfree(u);
     m->userdata = NULL;
 }
diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index c679444..f93652e 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -42,6 +42,13 @@ typedef struct pa_core pa_core;
 #include <pulsecore/sink-input.h>
 #include <pulsecore/msgobject.h>
 
+typedef enum pa_server_type {
+    PA_SERVER_TYPE_UNSET,
+    PA_SERVER_TYPE_USER,
+    PA_SERVER_TYPE_SYSTEM,
+    PA_SERVER_TYPE_NONE
+} pa_server_type_t;
+
 typedef enum pa_core_state {
     PA_CORE_STARTUP,
     PA_CORE_RUNNING,
@@ -152,6 +159,8 @@ struct pa_core {
     pa_resample_method_t resample_method;
     int realtime_priority;
 
+    pa_server_type_t server_type;
+
     /* hooks */
     pa_hook hooks[PA_CORE_HOOK_MAX];
 };
diff --git a/src/pulsecore/dbus-common.c b/src/pulsecore/dbus-common.c
new file mode 100644
index 0000000..05931e0
--- /dev/null
+++ b/src/pulsecore/dbus-common.c
@@ -0,0 +1,73 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <dbus/dbus.h>
+
+#include <pulsecore/core-util.h>
+
+#include "dbus-common.h"
+
+char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type) {
+    char *address = NULL;
+    char *runtime_path = NULL;
+    char *escaped_path = NULL;
+
+    switch (server_type) {
+        case PA_SERVER_TYPE_USER:
+            if (!(runtime_path = pa_runtime_path(PA_DBUS_SOCKET_NAME))) {
+                pa_log("pa_runtime_path() failed.");
+                break;
+            }
+
+            if (!(escaped_path = dbus_address_escape_value(runtime_path))) {
+                pa_log("dbus_address_escape_value() failed.");
+                break;
+            }
+
+            address = pa_sprintf_malloc("unix:path=%s", escaped_path);
+            break;
+
+        case PA_SERVER_TYPE_SYSTEM:
+            if (!(escaped_path = dbus_address_escape_value(PA_DBUS_SYSTEM_SOCKET_PATH))) {
+                pa_log("dbus_address_escape_value() failed.");
+                break;
+            }
+
+            address = pa_sprintf_malloc("unix:path=%s", escaped_path);
+            break;
+
+        case PA_SERVER_TYPE_NONE:
+            address = pa_xnew0(char, 1);
+            break;
+
+        default:
+            pa_assert_not_reached();
+    }
+
+    pa_xfree(runtime_path);
+    pa_xfree(escaped_path);
+
+    return address;
+}
diff --git a/src/daemon/server-lookup.h b/src/pulsecore/dbus-common.h
similarity index 57%
copy from src/daemon/server-lookup.h
copy to src/pulsecore/dbus-common.h
index 69fdacd..26bd05d 100644
--- a/src/daemon/server-lookup.h
+++ b/src/pulsecore/dbus-common.h
@@ -1,5 +1,5 @@
-#ifndef fooserverlookuphfoo
-#define fooserverlookuphfoo
+#ifndef foodbuscommonhfoo
+#define foodbuscommonhfoo
 
 /***
   This file is part of PulseAudio.
@@ -22,21 +22,18 @@
   USA.
 ***/
 
-/* This object implements the D-Bus object at path
- * /org/pulseaudio/server_lookup. Implemented interfaces
- * are org.pulseaudio.ServerLookup and org.freedesktop.DBus.Introspectable.
- *
- * See http://pulseaudio.org/wiki/DBusInterface for the ServerLookup interface
- * documentation.
- */
-
 #include <pulsecore/core.h>
+#include <pulsecore/macro.h>
 
-#include "daemon-conf.h"
+#define PA_DBUS_DEFAULT_PORT 24883
+#define PA_DBUS_SOCKET_NAME "dbus_socket"
 
-typedef struct pa_dbusobj_server_lookup pa_dbusobj_server_lookup;
+#define PA_DBUS_SYSTEM_SOCKET_PATH PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_DBUS_SOCKET_NAME
 
-pa_dbusobj_server_lookup *pa_dbusobj_server_lookup_new(pa_core *c, pa_daemon_conf_server_type_t server_type);
-void pa_dbusobj_server_lookup_free(pa_dbusobj_server_lookup *sl);
+/* Returns the default address of the server type in the escaped form. For
+ * PA_SERVER_TYPE_NONE an empty string is returned. The caller frees the
+ * string. This function may fail in some rare cases, in which case NULL is
+ * returned. */
+char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type);
 
-#endif
\ No newline at end of file
+#endif
diff --git a/src/pulsecore/dbus-util.c b/src/pulsecore/dbus-util.c
index ece36de..d8bd0e0 100644
--- a/src/pulsecore/dbus-util.c
+++ b/src/pulsecore/dbus-util.c
@@ -276,6 +276,27 @@ pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *m, DBusBus
     return pconn;
 }
 
+pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing(pa_mainloop_api *m, DBusConnection *conn) {
+    pa_dbus_wrap_connection *pconn;
+
+    pa_assert(m);
+    pa_assert(conn);
+
+    pconn = pa_xnew(pa_dbus_wrap_connection, 1);
+    pconn->mainloop = m;
+    pconn->connection = dbus_connection_ref(conn);
+
+    dbus_connection_set_exit_on_disconnect(conn, FALSE);
+    dbus_connection_set_dispatch_status_function(conn, dispatch_status, pconn, NULL);
+    dbus_connection_set_watch_functions(conn, add_watch, remove_watch, toggle_watch, pconn, NULL);
+    dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout, toggle_timeout, pconn, NULL);
+    dbus_connection_set_wakeup_main_function(conn, wakeup_main, pconn, NULL);
+
+    pconn->dispatch_event = pconn->mainloop->defer_new(pconn->mainloop, dispatch_cb, conn);
+
+    return pconn;
+}
+
 void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* c) {
     pa_assert(c);
 
diff --git a/src/pulsecore/dbus-util.h b/src/pulsecore/dbus-util.h
index 55cda7a..cd08485 100644
--- a/src/pulsecore/dbus-util.h
+++ b/src/pulsecore/dbus-util.h
@@ -31,6 +31,7 @@
 typedef struct pa_dbus_wrap_connection pa_dbus_wrap_connection;
 
 pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *mainloop, DBusBusType type, DBusError *error);
+pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing(pa_mainloop_api *mainloop, DBusConnection *conn);
 void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* conn);
 
 DBusConnection* pa_dbus_wrap_connection_get(pa_dbus_wrap_connection *conn);

commit 123c6a3c6ffc9903c0855e38445fc3b6588311ce
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Fri Jun 19 10:28:08 2009 +0300

    dbus-common: Implement infrastructure for registering D-Bus objects on all
    client connections and for receiving method calls from clients.

diff --git a/src/Makefile.am b/src/Makefile.am
index 5302bc2..d28ffa1 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -783,7 +783,6 @@ libpulsecore_ at PA_MAJORMINORMICRO@_la_SOURCES = \
 		pulsecore/core-scache.c pulsecore/core-scache.h \
 		pulsecore/core-subscribe.c pulsecore/core-subscribe.h \
 		pulsecore/core.c pulsecore/core.h \
-        pulsecore/dbus-common.c pulsecore/dbus-common.h \
 		pulsecore/envelope.c pulsecore/envelope.h \
 		pulsecore/fdsem.c pulsecore/fdsem.h \
 		pulsecore/g711.c pulsecore/g711.h \
@@ -830,7 +829,9 @@ libpulsecore_ at PA_MAJORMINORMICRO@_la_LDFLAGS += $(X11_LIBS)
 endif
 
 if HAVE_DBUS
-libpulsecore_ at PA_MAJORMINORMICRO@_la_SOURCES += pulsecore/dbus-shared.c pulsecore/dbus-shared.h
+libpulsecore_ at PA_MAJORMINORMICRO@_la_SOURCES += \
+		pulsecore/dbus-shared.c pulsecore/dbus-shared.h \
+		pulsecore/dbus-common.c pulsecore/dbus-common.h
 libpulsecore_ at PA_MAJORMINORMICRO@_la_CFLAGS += $(DBUS_CFLAGS)
 libpulsecore_ at PA_MAJORMINORMICRO@_la_LIBADD += $(DBUS_LIBS)
 endif
@@ -1220,7 +1221,9 @@ module_http_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore- at PA_MAJORMINORMI
 
 # D-Bus protocol
 
-module_dbus_protocol_la_SOURCES = modules/module-dbus-protocol.c
+module_dbus_protocol_la_SOURCES = \
+		pulsecore/dbus-objs/core.c pulsecore/dbus-objs/core.h \
+		modules/module-dbus-protocol.c
 module_dbus_protocol_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
 module_dbus_protocol_la_LDFLAGS = $(MODULE_LDFLAGS)
 module_dbus_protocol_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
diff --git a/src/daemon/server-lookup.c b/src/daemon/server-lookup.c
index b53fda7..2d2d8ce 100644
--- a/src/daemon/server-lookup.c
+++ b/src/daemon/server-lookup.c
@@ -48,7 +48,7 @@ static const char introspection[] =
     " <!-- If you are looking for documentation make sure to check out\n"
     "      http://pulseaudio.org/wiki/DBusInterface -->\n"
     " <interface name=\"org.pulseaudio.ServerLookup\">"
-    "  <method name=\"GetDBusAddress\">"
+    "  <method name=\"GetAddress\">"
     "   <arg name=\"result\" type=\"s\" direction=\"out\"/>"
     "  </method>"
     " </interface>"
@@ -101,7 +101,7 @@ oom:
     return DBUS_HANDLER_RESULT_NEED_MEMORY;
 }
 
-static DBusHandlerResult handle_get_dbus_address(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) {
+static DBusHandlerResult handle_get_address(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) {
     DBusMessage *reply = NULL;
     pa_client_conf *conf = NULL;
     char *address = NULL;
@@ -187,8 +187,8 @@ static DBusHandlerResult message_cb(DBusConnection *conn, DBusMessage *msg, void
     if (dbus_message_is_method_call(msg, "org.freedesktop.DBus.Introspectable", "Introspect"))
         return handle_introspect(conn, msg, sl);
 
-    if (dbus_message_is_method_call(msg, "org.pulseaudio.ServerLookup", "GetDBusAddress"))
-        return handle_get_dbus_address(conn, msg, sl);
+    if (dbus_message_is_method_call(msg, "org.pulseaudio.ServerLookup", "GetAddress"))
+        return handle_get_address(conn, msg, sl);
 
     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 }
diff --git a/src/modules/module-dbus-protocol.c b/src/modules/module-dbus-protocol.c
index 1455148..74dfeef 100644
--- a/src/modules/module-dbus-protocol.c
+++ b/src/modules/module-dbus-protocol.c
@@ -40,6 +40,8 @@
 #include <pulsecore/modargs.h>
 #include <pulsecore/module.h>
 
+#include <pulsecore/dbus-objs/core.h>
+
 #include "module-dbus-protocol-symdef.h"
 
 PA_MODULE_DESCRIPTION("D-Bus interface");
@@ -67,6 +69,8 @@ struct userdata {
     pa_idxset *connections;
 
     pa_time_event *cleanup_event;
+
+    pa_dbusobj_core *core_object;
 };
 
 struct server {
@@ -89,12 +93,15 @@ static const char* const valid_modargs[] = {
 static void connection_free(struct connection *c) {
     pa_assert(c);
 
+    pa_assert_se(pa_dbus_unregister_connection(c->server->userdata->module->core, pa_dbus_wrap_connection_get(c->wrap_conn)) >= 0);
+
     pa_client_free(c->client);
     pa_assert_se(pa_idxset_remove_by_data(c->server->userdata->connections, c, NULL));
     pa_dbus_wrap_connection_free(c->wrap_conn);
     pa_xfree(c);
 }
 
+/* Called from pa_client_kill(). */
 static void client_kill_cb(pa_client *c) {
     struct connection *conn;
 
@@ -120,7 +127,7 @@ static void connection_new_cb(DBusServer *dbus_server, DBusConnection *new_conne
     pa_client_new_data_init(&new_data);
     new_data.module = s->userdata->module;
     new_data.driver = __FILE__;
-    pa_proplist_sets(new_data.proplist, PA_PROP_APPLICATION_NAME, "D-Bus client"); /* TODO: Fancier name. */
+    pa_proplist_sets(new_data.proplist, PA_PROP_APPLICATION_NAME, "D-Bus client"); /* TODO: It's probably possible to generate a fancier name. Other props? */
     client = pa_client_new(s->userdata->module->core, &new_data);
     pa_client_new_data_done(&new_data);
 
@@ -133,10 +140,12 @@ static void connection_new_cb(DBusServer *dbus_server, DBusConnection *new_conne
     c->client = client;
 
     c->client->kill = client_kill_cb;
-    c->client->send_event = NULL;
+    c->client->send_event = NULL; /* TODO: Implement this. */
     c->client->userdata = c;
 
     pa_idxset_put(s->userdata->connections, c, NULL);
+
+    pa_assert_se(pa_dbus_register_connection(s->userdata->module->core, new_connection) >= 0);
 }
 
 /* Called by PA mainloop when a D-Bus fd watch event needs handling. */
@@ -485,6 +494,8 @@ int pa__init(pa_module *m) {
     cleanup_timeval.tv_sec += CLEANUP_INTERVAL;
     u->cleanup_event = m->core->mainloop->time_new(m->core->mainloop, &cleanup_timeval, cleanup_cb, u);
 
+    u->core_object = pa_dbusobj_core_new(m->core);
+
     return 0;
 
 fail:
@@ -513,6 +524,9 @@ void pa__done(pa_module *m) {
     if (!(u = m->userdata))
         return;
 
+    if (u->core_object)
+        pa_dbusobj_core_free(u->core_object);
+
     if (u->cleanup_event)
         m->core->mainloop->time_free(u->cleanup_event);
 
diff --git a/src/pulsecore/dbus-common.c b/src/pulsecore/dbus-common.c
index 05931e0..350add8 100644
--- a/src/pulsecore/dbus-common.c
+++ b/src/pulsecore/dbus-common.c
@@ -25,10 +25,35 @@
 
 #include <dbus/dbus.h>
 
+#include <pulse/xmalloc.h>
+
 #include <pulsecore/core-util.h>
+#include <pulsecore/hashmap.h>
+#include <pulsecore/idxset.h>
+#include <pulsecore/strbuf.h>
 
 #include "dbus-common.h"
 
+struct dbus_state {
+    pa_core *core;
+    pa_hashmap *objects; /* Object path -> struct object_entry */
+    pa_idxset *connections; /* DBusConnections */
+};
+
+struct object_entry {
+    char *path;
+    pa_hashmap *interfaces; /* Interface name -> struct interface_entry */
+    char *introspection;
+};
+
+struct interface_entry {
+    char *name;
+    char **methods;
+    char *introspection_snippet;
+    DBusObjectPathMessageFunction receive;
+    void *userdata;
+};
+
 char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type) {
     char *address = NULL;
     char *runtime_path = NULL;
@@ -71,3 +96,408 @@ char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type) {
 
     return address;
 }
+
+static void update_introspection(struct object_entry *oe) {
+    pa_strbuf *buf;
+    void *state = NULL;
+    struct interface_entry *iface_entry = NULL;
+
+    pa_assert(oe);
+
+    buf = pa_strbuf_new();
+    pa_strbuf_puts(buf, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE);
+    pa_strbuf_puts(buf, "<node>");
+
+    while ((iface_entry = pa_hashmap_iterate(oe->interfaces, &state, NULL)))
+        pa_strbuf_puts(buf, iface_entry->introspection_snippet);
+
+    pa_strbuf_puts(buf, " <interface name=\"org.freedesktop.DBus.Introspectable\">"
+                        "  <method name=\"Introspect\">"
+                        "   <arg name=\"data\" type=\"s\" direction=\"out\"/>"
+                        "  </method>"
+                        " </interface>");
+
+    pa_strbuf_puts(buf, "</node>");
+
+    pa_xfree(oe->introspection);
+    oe->introspection = pa_strbuf_tostring_free(buf);
+}
+
+static struct interface_entry *find_interface(struct object_entry *obj_entry, DBusMessage *msg) {
+    const char *interface;
+    struct interface_entry *iface_entry;
+    void *state = NULL;
+
+    pa_assert(obj_entry);
+    pa_assert(msg);
+
+    if ((interface = dbus_message_get_interface(msg)))
+        return pa_hashmap_get(obj_entry->interfaces, interface);
+
+    /* NULL interface, we'll have to search for an interface that contains the
+     * method. */
+
+    while ((iface_entry = pa_hashmap_iterate(obj_entry->interfaces, &state, NULL))) {
+        char *method;
+        char **pos = iface_entry->methods;
+
+        while ((method = *pos++)) {
+            if (!strcmp(dbus_message_get_member(msg), method))
+                return iface_entry;
+        }
+    }
+
+    return NULL;
+}
+
+static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessage *message, void *user_data) {
+    struct dbus_state *dbus_state = user_data;
+    struct object_entry *obj_entry;
+    struct interface_entry *iface_entry;
+    DBusMessage *reply = NULL;
+
+    pa_assert(connection);
+    pa_assert(message);
+    pa_assert(dbus_state);
+    pa_assert(dbus_state->objects);
+
+    if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    pa_assert_se((obj_entry = pa_hashmap_get(dbus_state->objects, dbus_message_get_path(message))));
+
+    if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
+        if (!(reply = dbus_message_new_method_return(message)))
+            goto oom;
+
+        if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &obj_entry->introspection, DBUS_TYPE_INVALID))
+            goto fail;
+
+        if (!dbus_connection_send(connection, reply, NULL))
+            goto oom;
+
+        pa_log_debug("%s.%s handled.", obj_entry->path, "Introspect");
+
+        dbus_message_unref(reply);
+
+        return DBUS_HANDLER_RESULT_HANDLED;
+    }
+
+    if (!(iface_entry = find_interface(obj_entry, message)))
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    return iface_entry->receive(connection, message, iface_entry->userdata);
+
+fail:
+    if (reply)
+        dbus_message_unref(reply);
+
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+oom:
+    if (reply)
+        dbus_message_unref(reply);
+
+    return DBUS_HANDLER_RESULT_NEED_MEMORY;
+}
+
+static DBusObjectPathVTable vtable = {
+    .unregister_function = NULL,
+    .message_function = handle_message_cb,
+    .dbus_internal_pad1 = NULL,
+    .dbus_internal_pad2 = NULL,
+    .dbus_internal_pad3 = NULL,
+    .dbus_internal_pad4 = NULL
+};
+
+static void register_object(struct dbus_state *dbus_state, struct object_entry *obj_entry) {
+    DBusConnection *conn;
+    void *state = NULL;
+
+    pa_assert(dbus_state);
+    pa_assert(obj_entry);
+
+    if (!dbus_state->connections)
+        return;
+
+    while ((conn = pa_idxset_iterate(dbus_state->connections, &state, NULL))) {
+        if (!dbus_connection_register_object_path(conn, obj_entry->path, &vtable, dbus_state))
+            pa_log_debug("dbus_connection_register_object_path() failed.");
+    }
+}
+
+static char **copy_methods(const char * const *methods) {
+    unsigned n = 0;
+    char **copy;
+    unsigned i;
+
+    while (methods[n++])
+        ;
+
+    copy = pa_xnew0(char *, n);
+
+    for (i = 0; i < n - 1; ++i)
+        copy[i] = pa_xstrdup(methods[i]);
+
+    return copy;
+}
+
+int pa_dbus_add_interface(pa_core *c, const char* path, const char* interface, const char * const *methods, const char* introspection_snippet, DBusObjectPathMessageFunction receive_cb, void *userdata) {
+    struct dbus_state *dbus_state;
+    pa_hashmap *objects;
+    struct object_entry *obj_entry;
+    struct interface_entry *iface_entry;
+    pa_bool_t state_created = FALSE;
+    pa_bool_t object_map_created = FALSE;
+    pa_bool_t obj_entry_created = FALSE;
+
+    pa_assert(c);
+    pa_assert(path);
+    pa_assert(introspection_snippet);
+    pa_assert(receive_cb);
+
+    if (!(dbus_state = pa_hashmap_get(c->shared, "dbus-state"))) {
+        dbus_state = pa_xnew0(struct dbus_state, 1);
+        dbus_state->core = c;
+        pa_hashmap_put(c->shared, "dbus-state", dbus_state);
+        state_created = TRUE;
+    }
+
+    if (!(objects = dbus_state->objects)) {
+        objects = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+        dbus_state->objects = objects;
+        object_map_created = TRUE;
+    }
+
+    if (!(obj_entry = pa_hashmap_get(objects, path))) {
+        obj_entry = pa_xnew(struct object_entry, 1);
+        obj_entry->path = pa_xstrdup(path);
+        obj_entry->interfaces = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+        obj_entry->introspection = NULL;
+        pa_hashmap_put(objects, path, obj_entry);
+        obj_entry_created = TRUE;
+    }
+
+    if (pa_hashmap_get(obj_entry->interfaces, interface) != NULL)
+        goto fail; /* The interface was already registered. */
+
+    iface_entry = pa_xnew(struct interface_entry, 1);
+    iface_entry->name = pa_xstrdup(interface);
+    iface_entry->methods = copy_methods(methods);
+    iface_entry->introspection_snippet = pa_xstrdup(introspection_snippet);
+    iface_entry->receive = receive_cb;
+    iface_entry->userdata = userdata;
+    pa_hashmap_put(obj_entry->interfaces, iface_entry->name, iface_entry);
+
+    update_introspection(obj_entry);
+
+    if (obj_entry_created)
+        register_object(dbus_state, obj_entry);
+
+    return 0;
+
+fail:
+    if (obj_entry_created) {
+        pa_hashmap_remove(objects, path);
+        pa_xfree(obj_entry);
+    }
+
+    if (object_map_created) {
+        dbus_state->objects = NULL;
+        pa_hashmap_free(objects, NULL, NULL);
+    }
+
+    if (state_created) {
+        pa_hashmap_remove(c->shared, "dbus-state");
+        pa_xfree(dbus_state);
+    }
+
+    return -1;
+}
+
+static void unregister_object(struct dbus_state *dbus_state, struct object_entry *obj_entry) {
+    DBusConnection *conn;
+    void *state = NULL;
+
+    pa_assert(dbus_state);
+    pa_assert(obj_entry);
+
+    if (!dbus_state->connections)
+        return;
+
+    while ((conn = pa_idxset_iterate(dbus_state->connections, &state, NULL))) {
+        if (!dbus_connection_unregister_object_path(conn, obj_entry->path))
+            pa_log_debug("dbus_connection_unregister_object_path() failed.");
+    }
+}
+
+static void free_methods(char **methods) {
+    char **pos = methods;
+
+    while (*pos++)
+        pa_xfree(*pos);
+
+    pa_xfree(methods);
+}
+
+int pa_dbus_remove_interface(pa_core *c, const char* path, const char* interface) {
+    struct dbus_state *dbus_state;
+    pa_hashmap *objects;
+    struct object_entry *obj_entry;
+    struct interface_entry *iface_entry;
+
+    pa_assert(c);
+    pa_assert(path);
+    pa_assert(interface);
+
+    if (!(dbus_state = pa_hashmap_get(c->shared, "dbus-state")))
+        return -1;
+
+    if (!(objects = dbus_state->objects))
+        return -1;
+
+    if (!(obj_entry = pa_hashmap_get(objects, path)))
+        return -1;
+
+    if (!(iface_entry = pa_hashmap_remove(obj_entry->interfaces, interface)))
+        return -1;
+
+    update_introspection(obj_entry);
+
+    pa_xfree(iface_entry->name);
+    free_methods(iface_entry->methods);
+    pa_xfree(iface_entry->introspection_snippet);
+    pa_xfree(iface_entry);
+
+    if (pa_hashmap_isempty(obj_entry->interfaces)) {
+        unregister_object(dbus_state, obj_entry);
+
+        pa_hashmap_remove(objects, path);
+        pa_xfree(obj_entry->path);
+        pa_hashmap_free(obj_entry->interfaces, NULL, NULL);
+        pa_xfree(obj_entry->introspection);
+        pa_xfree(obj_entry);
+    }
+
+    if (pa_hashmap_isempty(objects)) {
+        dbus_state->objects = NULL;
+        pa_hashmap_free(objects, NULL, NULL);
+    }
+
+    if (!dbus_state->objects && !dbus_state->connections) {
+        pa_hashmap_remove(c->shared, "dbus-state");
+        pa_xfree(dbus_state);
+    }
+
+    return 0;
+}
+
+static void register_all_objects(struct dbus_state *dbus_state, DBusConnection *conn) {
+    struct object_entry *obj_entry;
+    void *state = NULL;
+
+    pa_assert(dbus_state);
+    pa_assert(conn);
+
+    if (!dbus_state->objects)
+        return;
+
+    while ((obj_entry = pa_hashmap_iterate(dbus_state->objects, &state, NULL))) {
+        if (!dbus_connection_register_object_path(conn, obj_entry->path, &vtable, dbus_state))
+            pa_log_debug("dbus_connection_register_object_path() failed.");
+    }
+}
+
+int pa_dbus_register_connection(pa_core *c, DBusConnection *conn) {
+    struct dbus_state *dbus_state;
+    pa_idxset *connections;
+    pa_bool_t state_created = FALSE;
+    pa_bool_t connection_set_created = FALSE;
+
+    pa_assert(c);
+    pa_assert(conn);
+
+    if (!(dbus_state = pa_hashmap_get(c->shared, "dbus-state"))) {
+        dbus_state = pa_xnew0(struct dbus_state, 1);
+        dbus_state->core = c;
+        pa_hashmap_put(c->shared, "dbus-state", dbus_state);
+        state_created = TRUE;
+    }
+
+    if (!(connections = dbus_state->connections)) {
+        connections = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+        dbus_state->connections = connections;
+        connection_set_created = TRUE;
+    }
+
+    if (pa_idxset_get_by_data(connections, conn, NULL))
+        goto fail; /* The connection was already registered. */
+
+    register_all_objects(dbus_state, conn);
+
+    pa_idxset_put(connections, dbus_connection_ref(conn), NULL);
+
+    return 0;
+
+fail:
+    if (connection_set_created) {
+        dbus_state->connections = NULL;
+        pa_idxset_free(connections, NULL, NULL);
+    }
+
+    if (state_created) {
+        pa_hashmap_remove(c->shared, "dbus-state");
+        pa_xfree(dbus_state);
+    }
+
+    return -1;
+}
+
+static void unregister_all_objects(struct dbus_state *dbus_state, DBusConnection *conn) {
+    struct object_entry *obj_entry;
+    void *state = NULL;
+
+    pa_assert(dbus_state);
+    pa_assert(conn);
+
+    if (!dbus_state->objects)
+        return;
+
+    while ((obj_entry = pa_hashmap_iterate(dbus_state->objects, &state, NULL))) {
+        if (!dbus_connection_unregister_object_path(conn, obj_entry->path))
+            pa_log_debug("dus_connection_unregister_object_path() failed.");
+    }
+}
+
+int pa_dbus_unregister_connection(pa_core *c, DBusConnection *conn) {
+    struct dbus_state *dbus_state;
+    pa_idxset *connections;
+
+    pa_assert(c);
+    pa_assert(conn);
+
+    if (!(dbus_state = pa_hashmap_get(c->shared, "dbus-state")))
+        return -1;
+
+    if (!(connections = dbus_state->connections))
+        return -1;
+
+    if (!pa_idxset_remove_by_data(connections, conn, NULL))
+        return -1;
+
+    unregister_all_objects(dbus_state, conn);
+
+    dbus_connection_unref(conn);
+
+    if (pa_idxset_isempty(connections)) {
+        dbus_state->connections = NULL;
+        pa_idxset_free(connections, NULL, NULL);
+    }
+
+    if (!dbus_state->objects && !dbus_state->connections) {
+        pa_hashmap_remove(c->shared, "dbus-state");
+        pa_xfree(dbus_state);
+    }
+
+    return 0;
+}
diff --git a/src/pulsecore/dbus-common.h b/src/pulsecore/dbus-common.h
index 26bd05d..23c7c22 100644
--- a/src/pulsecore/dbus-common.h
+++ b/src/pulsecore/dbus-common.h
@@ -22,6 +22,8 @@
   USA.
 ***/
 
+#include <dbus/dbus.h>
+
 #include <pulsecore/core.h>
 #include <pulsecore/macro.h>
 
@@ -30,10 +32,42 @@
 
 #define PA_DBUS_SYSTEM_SOCKET_PATH PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_DBUS_SOCKET_NAME
 
+/* NOTE: These functions may only be called from the main thread. */
+
 /* Returns the default address of the server type in the escaped form. For
  * PA_SERVER_TYPE_NONE an empty string is returned. The caller frees the
  * string. This function may fail in some rare cases, in which case NULL is
  * returned. */
 char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type);
 
+/* Registers the given interface to the given object path. This is additive: it
+ * doesn't matter whether or not the object has already been registered; if it
+ * is, then its interface set is just extended.
+ *
+ * Introspection requests are handled automatically. For that to work, the
+ * caller gives an XML snippet containing the interface introspection element.
+ * All interface snippets are automatically combined to provide the final
+ * introspection string.
+ *
+ * The introspection snippet contains the interface name and the methods, but
+ * since this function doesn't do XML parsing, the interface name and the set
+ * of method names have to be supplied separately. If the interface doesn't
+ * contain any methods, NULL may be given as the methods parameter, otherwise
+ * the methods parameter must be a NULL-terminated array of strings.
+ *
+ * Fails and returns a negative number if the object already has the interface
+ * registered. */
+int pa_dbus_add_interface(pa_core *c, const char* path, const char* interface, const char * const *methods, const char* introspection_snippet, DBusObjectPathMessageFunction receive_cb, void *userdata);
+
+/* Returns a negative number if the given object doesn't have the given
+ * interface registered. */
+int pa_dbus_remove_interface(pa_core *c, const char* path, const char* interface);
+
+/* Fails and returns a negative number if the connection is already
+ * registered. */
+int pa_dbus_register_connection(pa_core *c, DBusConnection *conn);
+
+/* Returns a negative number if the connection wasn't registered. */
+int pa_dbus_unregister_connection(pa_core *c, DBusConnection *conn);
+
 #endif
diff --git a/src/pulsecore/dbus-objs/core.c b/src/pulsecore/dbus-objs/core.c
new file mode 100644
index 0000000..f59c478
--- /dev/null
+++ b/src/pulsecore/dbus-objs/core.c
@@ -0,0 +1,120 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <dbus/dbus.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/dbus-common.h>
+#include <pulsecore/macro.h>
+
+#include "core.h"
+
+#define OBJECT_NAME "/org/pulseaudio/core"
+#define INTERFACE_NAME "org.pulseaudio.Core"
+
+struct pa_dbusobj_core {
+    pa_core *core;
+};
+
+static const char *introspection_snippet =
+    " <interface name=\""INTERFACE_NAME"\">"
+    "  <method name=\"Test\">"
+    "   <arg name=\"result\" type=\"s\" direction=\"out\"/>"
+    "  </method>"
+    " </interface>";
+
+static const char *methods[] = {
+    "Test",
+    NULL
+};
+
+static DBusHandlerResult handle_test(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) {
+    DBusMessage *reply = NULL;
+    const char *reply_message = "Hello!";
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    if (!(reply = dbus_message_new_method_return(msg)))
+        goto oom;
+
+    if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &reply_message, DBUS_TYPE_INVALID))
+        goto fail;
+
+    if (!dbus_connection_send(conn, reply, NULL))
+        goto oom;
+
+    dbus_message_unref(reply);
+
+    return DBUS_HANDLER_RESULT_HANDLED;
+
+fail:
+    if (reply)
+        dbus_message_unref(reply);
+
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+oom:
+    if (reply)
+        dbus_message_unref(reply);
+
+    return DBUS_HANDLER_RESULT_NEED_MEMORY;
+}
+
+static DBusHandlerResult receive_cb(DBusConnection *connection, DBusMessage *message, void *user_data) {
+    pa_dbusobj_core *c = user_data;
+
+    pa_assert(connection);
+    pa_assert(message);
+    pa_assert(c);
+
+    if (dbus_message_is_method_call(message, INTERFACE_NAME, "Test"))
+        return handle_test(connection, message, c);
+
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+pa_dbusobj_core *pa_dbusobj_core_new(pa_core *core) {
+    pa_dbusobj_core *c;
+
+    pa_assert(core);
+
+    c = pa_xnew(pa_dbusobj_core, 1);
+    c->core = core;
+
+    pa_dbus_add_interface(core, OBJECT_NAME, INTERFACE_NAME, methods, introspection_snippet, receive_cb, c);
+
+    return c;
+}
+
+void pa_dbusobj_core_free(pa_dbusobj_core *c) {
+    pa_assert(c);
+
+    pa_dbus_remove_interface(c->core, OBJECT_NAME, INTERFACE_NAME);
+
+    pa_xfree(c);
+}
diff --git a/src/pulsecore/dbus-common.h b/src/pulsecore/dbus-objs/core.h
similarity index 59%
copy from src/pulsecore/dbus-common.h
copy to src/pulsecore/dbus-objs/core.h
index 26bd05d..8e59cc3 100644
--- a/src/pulsecore/dbus-common.h
+++ b/src/pulsecore/dbus-objs/core.h
@@ -1,5 +1,5 @@
-#ifndef foodbuscommonhfoo
-#define foodbuscommonhfoo
+#ifndef foodbusobjscorehfoo
+#define foodbusobjscorehfoo
 
 /***
   This file is part of PulseAudio.
@@ -22,18 +22,18 @@
   USA.
 ***/
 
-#include <pulsecore/core.h>
-#include <pulsecore/macro.h>
+/* This object implements the D-Bus object at path /org/pulseaudio/core.
+ * The implemented interface is org.pulseaudio.Core.
+ *
+ * See http://pulseaudio.org/wiki/DBusInterface for the Core interface
+ * documentation.
+ */
 
-#define PA_DBUS_DEFAULT_PORT 24883
-#define PA_DBUS_SOCKET_NAME "dbus_socket"
+#include <pulsecore/core.h>
 
-#define PA_DBUS_SYSTEM_SOCKET_PATH PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_DBUS_SOCKET_NAME
+typedef struct pa_dbusobj_core pa_dbusobj_core;
 
-/* Returns the default address of the server type in the escaped form. For
- * PA_SERVER_TYPE_NONE an empty string is returned. The caller frees the
- * string. This function may fail in some rare cases, in which case NULL is
- * returned. */
-char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type);
+pa_dbusobj_core *pa_dbusobj_core_new(pa_core *core);
+void pa_dbusobj_core_free(pa_dbusobj_core *c);
 
 #endif

commit 3c6a0acc98a8326ad7a19c29005bba353396a88b
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Fri Jun 19 15:17:57 2009 +0300

    dbus-protocol: Implement TCP server startup.

diff --git a/src/daemon/server-lookup.c b/src/daemon/server-lookup.c
index 2d2d8ce..33d6b24 100644
--- a/src/daemon/server-lookup.c
+++ b/src/daemon/server-lookup.c
@@ -120,14 +120,11 @@ static DBusHandlerResult handle_get_address(DBusConnection *conn, DBusMessage *m
         return DBUS_HANDLER_RESULT_HANDLED;
     }
 
-    pa_client_conf_free(conf);
-
     if (conf->default_dbus_server) {
-        if (!(address = dbus_address_escape_value(conf->default_dbus_server)))
-            goto oom;
+        address = pa_xstrdup(conf->default_dbus_server);
     } else {
         if (!(address = pa_get_dbus_address_from_server_type(sl->core->server_type))) {
-            if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_FAILED, "get_dbus_server_from_type() failed.")))
+            if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_FAILED, "PulseAudio internal error: get_dbus_server_from_type() failed.")))
                 goto fail;
             if (!dbus_connection_send(conn, reply, NULL))
                 goto oom;
@@ -144,10 +141,8 @@ static DBusHandlerResult handle_get_address(DBusConnection *conn, DBusMessage *m
     if (!dbus_connection_send(conn, reply, NULL))
         goto oom;
 
-    pa_log_debug("handle_get_dbus_address(): Sent reply with address '%s'.", address);
-
+    pa_client_conf_free(conf);
     pa_xfree(address);
-
     dbus_message_unref(reply);
 
     return DBUS_HANDLER_RESULT_HANDLED;
diff --git a/src/daemon/system.pa.in b/src/daemon/system.pa.in
index 5541bbe..0ca32bd 100755
--- a/src/daemon/system.pa.in
+++ b/src/daemon/system.pa.in
@@ -33,7 +33,8 @@ load-module module-detect
 load-module module-esound-protocol-unix
 .endif
 .ifexists module-dbus-protocol at PA_SOEXT@
-load-module module-dbus-protocol
+### If you want to allow TCP connections, set access to "remote" or "local,remote".
+load-module module-dbus-protocol access=local
 .endif
 load-module module-native-protocol-unix
 
diff --git a/src/modules/module-dbus-protocol.c b/src/modules/module-dbus-protocol.c
index 74dfeef..8be9d1a 100644
--- a/src/modules/module-dbus-protocol.c
+++ b/src/modules/module-dbus-protocol.c
@@ -54,6 +54,11 @@ PA_MODULE_VERSION(PACKAGE_VERSION);
 
 #define CLEANUP_INTERVAL 10 /* seconds */
 
+enum server_type {
+    SERVER_TYPE_LOCAL,
+    SERVER_TYPE_TCP
+};
+
 struct server;
 struct connection;
 
@@ -75,6 +80,7 @@ struct userdata {
 
 struct server {
     struct userdata *userdata;
+    enum server_type type;
     DBusServer *dbus_server;
 };
 
@@ -114,6 +120,12 @@ static void client_kill_cb(pa_client *c) {
     pa_log_info("Connection killed.");
 }
 
+static dbus_bool_t user_check_cb(DBusConnection *connection, unsigned long uid, void *data) {
+    pa_log_debug("Allowing connection by user %lu.", uid);
+
+    return TRUE;
+}
+
 /* Called by D-Bus when a new client connection is received. */
 static void connection_new_cb(DBusServer *dbus_server, DBusConnection *new_connection, void *data) {
     struct server *s = data;
@@ -131,8 +143,17 @@ static void connection_new_cb(DBusServer *dbus_server, DBusConnection *new_conne
     client = pa_client_new(s->userdata->module->core, &new_data);
     pa_client_new_data_done(&new_data);
 
-    if (!client)
+    if (!client) {
+        dbus_connection_close(new_connection);
         return;
+    }
+
+    if (s->type == SERVER_TYPE_TCP) {
+        /* FIXME: Here we allow anyone from anywhere to access the server,
+         * anonymously. Access control should be configurable. */
+        dbus_connection_set_unix_user_function(new_connection, user_check_cb, NULL, NULL);
+        dbus_connection_set_allow_anonymous(new_connection, TRUE);
+    }
 
     c = pa_xnew(struct connection, 1);
     c->server = s;
@@ -334,7 +355,7 @@ static void server_free(struct server *s) {
     pa_xfree(s);
 }
 
-static struct server *start_server(struct userdata *u, const char *address) {
+static struct server *start_server(struct userdata *u, const char *address, enum server_type type) {
     /* XXX: We assume that when we unref the DBusServer instance at module
      * shutdown, nobody else holds any references to it. If we stop assuming
      * that someday, dbus_server_set_new_connection_function,
@@ -390,7 +411,7 @@ static struct server *start_local_server(struct userdata *u) {
 
     address = pa_get_dbus_address_from_server_type(u->module->core->server_type);
 
-    s = start_server(u, address); /* May return NULL */
+    s = start_server(u, address, SERVER_TYPE_LOCAL); /* May return NULL */
 
     pa_xfree(address);
 
@@ -398,8 +419,18 @@ static struct server *start_local_server(struct userdata *u) {
 }
 
 static struct server *start_tcp_server(struct userdata *u) {
-    pa_log("start_tcp_server(): Not implemented!");
-    return NULL;
+    struct server *s = NULL;
+    char *address = NULL;
+
+    pa_assert(u);
+
+    address = pa_sprintf_malloc("tcp:host=127.0.0.1,port=%u", u->tcp_port);
+
+    s = start_server(u, address, SERVER_TYPE_TCP); /* May return NULL */
+
+    pa_xfree(address);
+
+    return s;
 }
 
 static int get_access_arg(pa_modargs *ma, pa_bool_t *local_access, pa_bool_t *remote_access) {
@@ -420,7 +451,7 @@ static int get_access_arg(pa_modargs *ma, pa_bool_t *local_access, pa_bool_t *re
         *remote_access = TRUE;
     } else if (!strcmp(value, "local,remote")) {
         *local_access = TRUE;
-        *local_access = TRUE;
+        *remote_access = TRUE;
     } else
         return -1;
 

commit b152f3a052eca7225870a7dc4d8a719bee107f0f
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Sat Jun 20 10:17:46 2009 +0300

    module-dbus-protocol: Allow anyone to connect the daemon in system mode.

diff --git a/src/modules/module-dbus-protocol.c b/src/modules/module-dbus-protocol.c
index 8be9d1a..46f6492 100644
--- a/src/modules/module-dbus-protocol.c
+++ b/src/modules/module-dbus-protocol.c
@@ -148,7 +148,7 @@ static void connection_new_cb(DBusServer *dbus_server, DBusConnection *new_conne
         return;
     }
 
-    if (s->type == SERVER_TYPE_TCP) {
+    if (s->type == SERVER_TYPE_TCP || s->userdata->module->core->server_type == PA_SERVER_TYPE_SYSTEM) {
         /* FIXME: Here we allow anyone from anywhere to access the server,
          * anonymously. Access control should be configurable. */
         dbus_connection_set_unix_user_function(new_connection, user_check_cb, NULL, NULL);

commit 0bc538b08ca5c4efea86700cb6c4685da3f34345
Merge: b152f3a 2654eb7
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Mon Jun 29 18:35:06 2009 +0300

    Merge branch 'master' into dbus-work
    
    Conflicts:
    	src/daemon/daemon-conf.c
    	src/daemon/daemon-conf.h
    	src/daemon/main.c
    	src/pulsecore/dbus-util.h

diff --cc configure.ac
index a3f9f0e,d2d8015..185f13b
--- a/configure.ac
+++ b/configure.ac
@@@ -22,8 -22,8 +22,7 @@@
  
  AC_PREREQ(2.63)
  
--AC_INIT([pulseaudio], m4_esyscmd([./git-version-gen .tarball-version]),
--	[mzchyfrnhqvb (at) 0pointer (dot) net])
++AC_INIT([pulseaudio],[m4_esyscmd(./git-version-gen .tarball-version)],[mzchyfrnhqvb (at) 0pointer (dot) net])
  AC_CONFIG_SRCDIR([src/daemon/main.c])
  AC_CONFIG_MACRO_DIR([m4])
  AC_CONFIG_HEADERS([config.h])
diff --cc src/Makefile.am
index d28ffa1,ae90ae8..b1bc497
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@@ -796,10 -816,8 +817,9 @@@ libpulsecore_ at PA_MAJORMINORMICRO@_la_SO
  		pulsecore/object.c pulsecore/object.h \
  		pulsecore/play-memblockq.c pulsecore/play-memblockq.h \
  		pulsecore/play-memchunk.c pulsecore/play-memchunk.h \
 +		pulsecore/protocol-dbus.h \
  		pulsecore/resampler.c pulsecore/resampler.h \
  		pulsecore/rtpoll.c pulsecore/rtpoll.h \
- 		pulsecore/rtsig.c pulsecore/rtsig.h \
  		pulsecore/sample-util.c pulsecore/sample-util.h \
  		pulsecore/sconv-s16be.c pulsecore/sconv-s16be.h \
  		pulsecore/sconv-s16le.c pulsecore/sconv-s16le.h \
@@@ -1074,10 -1117,14 +1121,15 @@@ modlibexec_LTLIBRARIES += 
  		module-hal-detect.la
  endif
  
+ if HAVE_UDEV
+ modlibexec_LTLIBRARIES += \
+ 		module-udev-detect.la
+ endif
+ 
  if HAVE_DBUS
  modlibexec_LTLIBRARIES += \
 -		module-rygel-media-server.la
 +		module-rygel-media-server.la \
 +		module-dbus-protocol.la
  endif
  
  if HAVE_BLUEZ
diff --cc src/daemon/daemon-conf.c
index e6fa8c6,9010f2f..ace460e
--- a/src/daemon/daemon-conf.c
+++ b/src/daemon/daemon-conf.c
@@@ -83,11 -83,9 +83,12 @@@ static const pa_daemon_conf default_con
      .config_file = NULL,
      .use_pid_file = TRUE,
      .system_instance = FALSE,
 +#ifdef HAVE_DBUS
 +    .local_server_type = PA_SERVER_TYPE_UNSET, /* The actual default is _USER, but we have to detect when the user doesn't specify this option. */
 +#endif
      .no_cpu_limit = FALSE,
      .disable_shm = FALSE,
+     .lock_memory = FALSE,
      .default_n_fragments = 4,
      .default_fragment_size_msec = 25,
      .default_sample_spec = { .format = PA_SAMPLE_S16NE, .rate = 44100, .channels = 2 },
@@@ -633,23 -597,14 +635,22 @@@ FILE *pa_daemon_conf_open_default_scrip
      return f;
  }
  
+ char *pa_daemon_conf_dump(pa_daemon_conf *c) {
+     static const char* const log_level_to_string[] = {
+         [PA_LOG_DEBUG] = "debug",
+         [PA_LOG_INFO] = "info",
+         [PA_LOG_NOTICE] = "notice",
+         [PA_LOG_WARN] = "warning",
+         [PA_LOG_ERROR] = "error"
+     };
 +
- static const char* const log_level_to_string[] = {
-     [PA_LOG_DEBUG] = "debug",
-     [PA_LOG_INFO] = "info",
-     [PA_LOG_NOTICE] = "notice",
-     [PA_LOG_WARN] = "warning",
-     [PA_LOG_ERROR] = "error"
- };
- 
- static const char* const server_type_to_string[] = {
-     [PA_SERVER_TYPE_UNSET] = "!!UNSET!!",
-     [PA_SERVER_TYPE_USER] = "user",
-     [PA_SERVER_TYPE_SYSTEM] = "system",
-     [PA_SERVER_TYPE_NONE] = "none"
- };
++    static const char* const server_type_to_string[] = {
++        [PA_SERVER_TYPE_UNSET] = "!!UNSET!!",
++        [PA_SERVER_TYPE_USER] = "user",
++        [PA_SERVER_TYPE_SYSTEM] = "system",
++        [PA_SERVER_TYPE_NONE] = "none"
++    };
 +
- char *pa_daemon_conf_dump(pa_daemon_conf *c) {
      pa_strbuf *s;
      char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
  
diff --cc src/daemon/daemon-conf.h
index c4f78cb,dd69e04..41c3c4b
--- a/src/daemon/daemon-conf.h
+++ b/src/daemon/daemon-conf.h
@@@ -74,8 -73,8 +74,9 @@@ typedef struct pa_daemon_conf 
          disallow_exit,
          log_meta,
          log_time,
-         flat_volumes;
+         flat_volumes,
+         lock_memory;
 +    pa_server_type_t local_server_type;
      int exit_idle_time,
          scache_idle_time,
          auto_log_target,
diff --cc src/daemon/daemon.conf.in
index ecdb3a6,6931359..9bea614
--- a/src/daemon/daemon.conf.in
+++ b/src/daemon/daemon.conf.in
@@@ -25,9 -25,10 +25,11 @@@
  ; disallow-exit = no
  ; use-pid-file = yes
  ; system-instance = no
 +; local-server-type = user
  ; disable-shm = no
  ; shm-size-bytes = 0 # setting this 0 will use the system-default, usually 64 MiB
+ ; lock-memory = no
+ ; no-cpu-limit = no
  
  ; high-priority = yes
  ; nice-level = -11
diff --cc src/daemon/main.c
index 62214a5,b58bb37..c1730b7
--- a/src/daemon/main.c
+++ b/src/daemon/main.c
@@@ -98,8 -101,6 +101,7 @@@
  #include "dumpmodules.h"
  #include "caps.h"
  #include "ltdl-bind-now.h"
- #include "polkit.h"
 +#include "server-lookup.h"
  
  #ifdef HAVE_LIBWRAP
  /* Only one instance of these variables */
@@@ -488,191 -461,6 +463,32 @@@ int main(int argc, char *argv[]) 
          pa_log_set_flags(PA_LOG_PRINT_TIME, PA_LOG_SET);
      pa_log_set_show_backtrace(conf->log_backtrace);
  
 +#ifdef HAVE_DBUS
 +    /* conf->system_instance and conf->local_server_type control almost the
 +     * same thing; make them agree about what is requested. */
 +    switch (conf->local_server_type) {
 +        case PA_SERVER_TYPE_UNSET:
 +            conf->local_server_type = conf->system_instance ? PA_SERVER_TYPE_SYSTEM : PA_SERVER_TYPE_USER;
 +            break;
 +        case PA_SERVER_TYPE_USER:
 +        case PA_SERVER_TYPE_NONE:
 +            conf->system_instance = FALSE;
 +            break;
 +        case PA_SERVER_TYPE_SYSTEM:
 +            conf->system_instance = TRUE;
 +            break;
 +        default:
 +            pa_assert_not_reached();
 +    }
 +
-     start_server = conf->local_server_type == PA_SERVER_TYPE_USER || (real_root && conf->local_server_type == PA_SERVER_TYPE_SYSTEM);
++    start_server = conf->local_server_type == PA_SERVER_TYPE_USER || (getuid() == 0 && conf->local_server_type == PA_SERVER_TYPE_SYSTEM);
 +
 +    if (!start_server && conf->local_server_type == PA_SERVER_TYPE_SYSTEM) {
 +        pa_log_notice(_("System mode refused for non-root user. Only starting the D-Bus server lookup service."));
 +        conf->system_instance = FALSE;
 +    }
 +#endif
 +
-     pa_log_debug("Started as real root: %s, suid root: %s", pa_yes_no(real_root), pa_yes_no(suid_root));
- 
- #ifdef HAVE_DBUS
-     /* XXX: Uhh, goto programming... as if this wasn't hard enough to follow
-      * already. But if we won't start the full server, we want to just skip all
-      * the capability stuff. */
-     if (!start_server) {
-         if (!real_root && pa_have_caps())
-             pa_drop_caps();
-         goto after_caps_setup;
-     }
- #endif
- 
-     if (!real_root && pa_have_caps()) {
- #ifdef HAVE_SYS_RESOURCE_H
-         struct rlimit rl;
- #endif
-         pa_bool_t allow_high_priority = FALSE, allow_realtime = FALSE;
- 
-         /* Let's better not enable high prio or RT by default */
- 
-         if (conf->high_priority && !allow_high_priority) {
-             if (pa_own_uid_in_group(PA_REALTIME_GROUP, &gid) > 0) {
-                 pa_log_info(_("We're in the group '%s', allowing high-priority scheduling."), PA_REALTIME_GROUP);
-                 allow_high_priority = TRUE;
-             }
-         }
- 
-         if (conf->realtime_scheduling && !allow_realtime) {
-             if (pa_own_uid_in_group(PA_REALTIME_GROUP, &gid) > 0) {
-                 pa_log_info(_("We're in the group '%s', allowing real-time scheduling."), PA_REALTIME_GROUP);
-                 allow_realtime = TRUE;
-             }
-         }
- 
- #ifdef HAVE_POLKIT
-         if (conf->high_priority && !allow_high_priority) {
-             if (pa_polkit_check("org.pulseaudio.acquire-high-priority") > 0) {
-                 pa_log_info(_("PolicyKit grants us acquire-high-priority privilege."));
-                 allow_high_priority = TRUE;
-             } else
-                 pa_log_info(_("PolicyKit refuses acquire-high-priority privilege."));
-         }
- 
-         if (conf->realtime_scheduling && !allow_realtime) {
-             if (pa_polkit_check("org.pulseaudio.acquire-real-time") > 0) {
-                 pa_log_info(_("PolicyKit grants us acquire-real-time privilege."));
-                 allow_realtime = TRUE;
-             } else
-                 pa_log_info(_("PolicyKit refuses acquire-real-time privilege."));
-         }
- #endif
- 
-         if (!allow_high_priority && !allow_realtime) {
- 
-             /* OK, there's no further need to keep CAP_NICE. Hence
-              * let's give it up early */
- 
-             pa_drop_caps();
-         }
- 
- #ifdef RLIMIT_RTPRIO
-         if (getrlimit(RLIMIT_RTPRIO, &rl) >= 0)
-             if (rl.rlim_cur > 0) {
-                 pa_log_info("RLIMIT_RTPRIO is set to %u, allowing real-time scheduling.", (unsigned) rl.rlim_cur);
-                 allow_realtime = TRUE;
-             }
- #endif
- #ifdef RLIMIT_NICE
-         if (getrlimit(RLIMIT_NICE, &rl) >= 0)
-             if (rl.rlim_cur > 20 ) {
-                 pa_log_info("RLIMIT_NICE is set to %u, allowing high-priority scheduling.", (unsigned) rl.rlim_cur);
-                 allow_high_priority = TRUE;
-             }
- #endif
- 
-         if ((conf->high_priority && !allow_high_priority) ||
-             (conf->realtime_scheduling && !allow_realtime))
-             pa_log_info(_("Called SUID root and real-time and/or high-priority scheduling was requested in the configuration. However, we lack the necessary privileges:\n"
-                             "We are not in group '%s', PolicyKit refuse to grant us the requested privileges and we have no increase RLIMIT_NICE/RLIMIT_RTPRIO resource limits.\n"
-                             "For enabling real-time/high-priority scheduling please acquire the appropriate PolicyKit privileges, or become a member of '%s', or increase the RLIMIT_NICE/RLIMIT_RTPRIO resource limits for this user."),
-                           PA_REALTIME_GROUP, PA_REALTIME_GROUP);
- 
- 
-         if (!allow_realtime)
-             conf->realtime_scheduling = FALSE;
- 
-         if (!allow_high_priority)
-             conf->high_priority = FALSE;
-     }
- 
- #ifdef HAVE_SYS_RESOURCE_H
-     /* Reset resource limits. If we are run as root (for system mode)
-      * this might end up increasing the limits, which is intended
-      * behaviour. For all other cases, i.e. started as normal user, or
-      * SUID root at this point we should have no CAP_SYS_RESOURCE and
-      * increasing the limits thus should fail. Which is, too, intended
-      * behaviour */
- 
-     set_all_rlimits(conf);
- #endif
- 
-     if (conf->high_priority && !pa_can_high_priority()) {
-         pa_log_info(_("High-priority scheduling enabled in configuration but not allowed by policy."));
-         conf->high_priority = FALSE;
-     }
- 
-     if (conf->high_priority && (conf->cmd == PA_CMD_DAEMON || conf->cmd == PA_CMD_START))
-         pa_raise_priority(conf->nice_level);
- 
-     pa_log_debug("Can realtime: %s, can high-priority: %s", pa_yes_no(pa_can_realtime()), pa_yes_no(pa_can_high_priority()));
- 
-     if (!real_root && pa_have_caps()) {
-         pa_bool_t drop;
- 
-         drop = (conf->cmd != PA_CMD_DAEMON && conf->cmd != PA_CMD_START) || !conf->realtime_scheduling;
- 
- #ifdef RLIMIT_RTPRIO
-         if (!drop) {
-             struct rlimit rl;
-             /* At this point we still have CAP_NICE if we were loaded
-              * SUID root. If possible let's acquire RLIMIT_RTPRIO
-              * instead and give CAP_NICE up. */
- 
-             if (getrlimit(RLIMIT_RTPRIO, &rl) >= 0) {
- 
-                 if (rl.rlim_cur >= 9)
-                     drop = TRUE;
-                 else {
-                     rl.rlim_max = rl.rlim_cur = 9;
- 
-                     if (setrlimit(RLIMIT_RTPRIO, &rl) >= 0) {
-                         pa_log_info(_("Successfully increased RLIMIT_RTPRIO"));
-                         drop = TRUE;
-                     } else
-                         pa_log_warn(_("RLIMIT_RTPRIO failed: %s"), pa_cstrerror(errno));
-                 }
-             }
-         }
- #endif
- 
-         if (drop)  {
-             pa_log_info(_("Giving up CAP_NICE"));
-             pa_drop_caps();
-             suid_root = FALSE;
-         }
-     }
- 
-     if (conf->realtime_scheduling && !pa_can_realtime()) {
-         pa_log_info(_("Real-time scheduling enabled in configuration but not allowed by policy."));
-         conf->realtime_scheduling = FALSE;
-     }
- 
- #ifdef HAVE_DBUS
- after_caps_setup:
- #endif
- 
-     pa_log_debug("Can realtime: %s, can high-priority: %s", pa_yes_no(pa_can_realtime()), pa_yes_no(pa_can_high_priority()));
- 
      LTDL_SET_PRELOADED_SYMBOLS();
      pa_ltdl_init();
      ltdl_init = TRUE;
@@@ -757,10 -545,9 +573,10 @@@
              pa_assert(conf->cmd == PA_CMD_DAEMON || conf->cmd == PA_CMD_START);
      }
  
-     if (real_root && !conf->system_instance)
+     if (getuid() == 0 && !conf->system_instance)
          pa_log_warn(_("This program is not intended to be run as root (unless --system is specified)."));
 +#ifndef HAVE_DBUS /* A similar, only a notice worthy check was done earlier, if D-Bus is enabled. */
-     else if (!real_root && conf->system_instance) {
+     else if (getuid() != 0 && conf->system_instance) {
          pa_log(_("Root privileges required."));
          goto finish;
      }
@@@ -1028,7 -830,6 +860,9 @@@
      c->running_as_daemon = !!conf->daemonize;
      c->disallow_exit = conf->disallow_exit;
      c->flat_volumes = conf->flat_volumes;
++#ifdef HAVE_DBUS
 +    c->server_type = conf->local_server_type;
++#endif
  
      pa_assert_se(pa_signal_init(pa_mainloop_get_api(mainloop)) == 0);
      pa_signal_new(SIGINT, signal_callback, c);
diff --cc src/modules/module-dbus-protocol.c
index 46f6492,0000000..f7c1f0a
mode 100644,000000..100644
--- a/src/modules/module-dbus-protocol.c
+++ b/src/modules/module-dbus-protocol.c
@@@ -1,575 -1,0 +1,575 @@@
 +/***
 +  This file is part of PulseAudio.
 +
 +  Copyright 2009 Tanu Kaskinen
 +  Copyright 2006 Lennart Poettering
 +  Copyright 2006 Shams E. King
 +
 +  PulseAudio is free software; you can redistribute it and/or modify
 +  it under the terms of the GNU Lesser General Public License as published
 +  by the Free Software Foundation; either version 2.1 of the License,
 +  or (at your option) any later version.
 +
 +  PulseAudio is distributed in the hope that it will be useful, but
 +  WITHOUT ANY WARRANTY; without even the implied warranty of
 +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 +  General Public License for more details.
 +
 +  You should have received a copy of the GNU Lesser General Public License
 +  along with PulseAudio; if not, write to the Free Software
 +  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 +  USA.
 +***/
 +
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include <dbus/dbus.h>
 +
 +#include <pulse/mainloop-api.h>
 +#include <pulse/timeval.h>
 +#include <pulse/xmalloc.h>
 +
 +#include <pulsecore/client.h>
 +#include <pulsecore/core-util.h>
 +#include <pulsecore/dbus-common.h>
 +#include <pulsecore/dbus-util.h>
 +#include <pulsecore/idxset.h>
 +#include <pulsecore/macro.h>
 +#include <pulsecore/modargs.h>
 +#include <pulsecore/module.h>
 +
 +#include <pulsecore/dbus-objs/core.h>
 +
 +#include "module-dbus-protocol-symdef.h"
 +
 +PA_MODULE_DESCRIPTION("D-Bus interface");
 +PA_MODULE_USAGE(
 +        "access=local|remote|local,remote "
 +        "tcp_port=<port number>");
 +PA_MODULE_LOAD_ONCE(TRUE);
 +PA_MODULE_AUTHOR("Tanu Kaskinen");
 +PA_MODULE_VERSION(PACKAGE_VERSION);
 +
 +#define CLEANUP_INTERVAL 10 /* seconds */
 +
 +enum server_type {
 +    SERVER_TYPE_LOCAL,
 +    SERVER_TYPE_TCP
 +};
 +
 +struct server;
 +struct connection;
 +
 +struct userdata {
 +    pa_module *module;
 +    pa_bool_t local_access;
 +    pa_bool_t remote_access;
 +    uint32_t tcp_port;
 +
 +    struct server *local_server;
 +    struct server *tcp_server;
 +
 +    pa_idxset *connections;
 +
 +    pa_time_event *cleanup_event;
 +
 +    pa_dbusobj_core *core_object;
 +};
 +
 +struct server {
 +    struct userdata *userdata;
 +    enum server_type type;
 +    DBusServer *dbus_server;
 +};
 +
 +struct connection {
 +    struct server *server;
 +    pa_dbus_wrap_connection *wrap_conn;
 +    pa_client *client;
 +};
 +
 +static const char* const valid_modargs[] = {
 +    "access",
 +    "tcp_port",
 +    NULL
 +};
 +
 +static void connection_free(struct connection *c) {
 +    pa_assert(c);
 +
 +    pa_assert_se(pa_dbus_unregister_connection(c->server->userdata->module->core, pa_dbus_wrap_connection_get(c->wrap_conn)) >= 0);
 +
 +    pa_client_free(c->client);
 +    pa_assert_se(pa_idxset_remove_by_data(c->server->userdata->connections, c, NULL));
 +    pa_dbus_wrap_connection_free(c->wrap_conn);
 +    pa_xfree(c);
 +}
 +
 +/* Called from pa_client_kill(). */
 +static void client_kill_cb(pa_client *c) {
 +    struct connection *conn;
 +
 +    pa_assert(c);
 +    pa_assert(c->userdata);
 +
 +    conn = c->userdata;
 +    connection_free(conn);
 +
 +    pa_log_info("Connection killed.");
 +}
 +
 +static dbus_bool_t user_check_cb(DBusConnection *connection, unsigned long uid, void *data) {
 +    pa_log_debug("Allowing connection by user %lu.", uid);
 +
 +    return TRUE;
 +}
 +
 +/* Called by D-Bus when a new client connection is received. */
 +static void connection_new_cb(DBusServer *dbus_server, DBusConnection *new_connection, void *data) {
 +    struct server *s = data;
 +    struct connection *c;
 +    pa_client_new_data new_data;
 +    pa_client *client;
 +
 +    pa_assert(new_connection);
 +    pa_assert(s);
 +
 +    pa_client_new_data_init(&new_data);
 +    new_data.module = s->userdata->module;
 +    new_data.driver = __FILE__;
 +    pa_proplist_sets(new_data.proplist, PA_PROP_APPLICATION_NAME, "D-Bus client"); /* TODO: It's probably possible to generate a fancier name. Other props? */
 +    client = pa_client_new(s->userdata->module->core, &new_data);
 +    pa_client_new_data_done(&new_data);
 +
 +    if (!client) {
 +        dbus_connection_close(new_connection);
 +        return;
 +    }
 +
 +    if (s->type == SERVER_TYPE_TCP || s->userdata->module->core->server_type == PA_SERVER_TYPE_SYSTEM) {
 +        /* FIXME: Here we allow anyone from anywhere to access the server,
 +         * anonymously. Access control should be configurable. */
 +        dbus_connection_set_unix_user_function(new_connection, user_check_cb, NULL, NULL);
 +        dbus_connection_set_allow_anonymous(new_connection, TRUE);
 +    }
 +
 +    c = pa_xnew(struct connection, 1);
 +    c->server = s;
-     c->wrap_conn = pa_dbus_wrap_connection_new_from_existing(s->userdata->module->core->mainloop, new_connection);
++    c->wrap_conn = pa_dbus_wrap_connection_new_from_existing(s->userdata->module->core->mainloop, TRUE, new_connection);
 +    c->client = client;
 +
 +    c->client->kill = client_kill_cb;
 +    c->client->send_event = NULL; /* TODO: Implement this. */
 +    c->client->userdata = c;
 +
 +    pa_idxset_put(s->userdata->connections, c, NULL);
 +
 +    pa_assert_se(pa_dbus_register_connection(s->userdata->module->core, new_connection) >= 0);
 +}
 +
 +/* Called by PA mainloop when a D-Bus fd watch event needs handling. */
 +static void io_event_cb(pa_mainloop_api *mainloop, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
 +    unsigned int flags = 0;
 +    DBusWatch *watch = userdata;
 +
 +#if HAVE_DBUS_WATCH_GET_UNIX_FD
 +    pa_assert(fd == dbus_watch_get_unix_fd(watch));
 +#else
 +    pa_assert(fd == dbus_watch_get_fd(watch));
 +#endif
 +
 +    if (!dbus_watch_get_enabled(watch)) {
 +        pa_log_warn("Asked to handle disabled watch: %p %i", (void*) watch, fd);
 +        return;
 +    }
 +
 +    if (events & PA_IO_EVENT_INPUT)
 +        flags |= DBUS_WATCH_READABLE;
 +    if (events & PA_IO_EVENT_OUTPUT)
 +        flags |= DBUS_WATCH_WRITABLE;
 +    if (events & PA_IO_EVENT_HANGUP)
 +        flags |= DBUS_WATCH_HANGUP;
 +    if (events & PA_IO_EVENT_ERROR)
 +        flags |= DBUS_WATCH_ERROR;
 +
 +    dbus_watch_handle(watch, flags);
 +}
 +
 +/* Called by PA mainloop when a D-Bus timer event needs handling. */
 +static void time_event_cb(pa_mainloop_api *mainloop, pa_time_event* e, const struct timeval *tv, void *userdata) {
 +    DBusTimeout *timeout = userdata;
 +
 +    if (dbus_timeout_get_enabled(timeout)) {
 +        struct timeval next = *tv;
 +        dbus_timeout_handle(timeout);
 +
 +        /* restart it for the next scheduled time */
 +        pa_timeval_add(&next, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
 +        mainloop->time_restart(e, &next);
 +    }
 +}
 +
 +/* Translates D-Bus fd watch event flags to PA IO event flags. */
 +static pa_io_event_flags_t get_watch_flags(DBusWatch *watch) {
 +    unsigned int flags;
 +    pa_io_event_flags_t events = 0;
 +
 +    pa_assert(watch);
 +
 +    flags = dbus_watch_get_flags(watch);
 +
 +    /* no watch flags for disabled watches */
 +    if (!dbus_watch_get_enabled(watch))
 +        return PA_IO_EVENT_NULL;
 +
 +    if (flags & DBUS_WATCH_READABLE)
 +        events |= PA_IO_EVENT_INPUT;
 +    if (flags & DBUS_WATCH_WRITABLE)
 +        events |= PA_IO_EVENT_OUTPUT;
 +
 +    return events | PA_IO_EVENT_HANGUP | PA_IO_EVENT_ERROR;
 +}
 +
 +/* Called by D-Bus when a D-Bus fd watch event is added. */
 +static dbus_bool_t watch_add_cb(DBusWatch *watch, void *data) {
 +    struct server *s = data;
 +    pa_mainloop_api *mainloop;
 +    pa_io_event *ev;
 +
 +    pa_assert(watch);
 +    pa_assert(s);
 +
 +    mainloop = s->userdata->module->core->mainloop;
 +
 +    ev = mainloop->io_new(
 +            mainloop,
 +#if HAVE_DBUS_WATCH_GET_UNIX_FD
 +            dbus_watch_get_unix_fd(watch),
 +#else
 +            dbus_watch_get_fd(watch),
 +#endif
 +            get_watch_flags(watch), io_event_cb, watch);
 +
 +    dbus_watch_set_data(watch, ev, NULL);
 +
 +    return TRUE;
 +}
 +
 +/* Called by D-Bus when a D-Bus fd watch event is removed. */
 +static void watch_remove_cb(DBusWatch *watch, void *data) {
 +    struct server *s = data;
 +    pa_io_event *ev;
 +
 +    pa_assert(watch);
 +    pa_assert(s);
 +
 +    if ((ev = dbus_watch_get_data(watch)))
 +        s->userdata->module->core->mainloop->io_free(ev);
 +}
 +
 +/* Called by D-Bus when a D-Bus fd watch event is toggled. */
 +static void watch_toggled_cb(DBusWatch *watch, void *data) {
 +    struct server *s = data;
 +    pa_io_event *ev;
 +
 +    pa_assert(watch);
 +    pa_assert(s);
 +
 +    pa_assert_se(ev = dbus_watch_get_data(watch));
 +
 +    /* get_watch_flags() checks if the watch is enabled */
 +    s->userdata->module->core->mainloop->io_enable(ev, get_watch_flags(watch));
 +}
 +
 +/* Called by D-Bus when a D-Bus timer event is added. */
 +static dbus_bool_t timeout_add_cb(DBusTimeout *timeout, void *data) {
 +    struct server *s = data;
 +    pa_mainloop_api *mainloop;
 +    pa_time_event *ev;
 +    struct timeval tv;
 +
 +    pa_assert(timeout);
 +    pa_assert(s);
 +
 +    if (!dbus_timeout_get_enabled(timeout))
 +        return FALSE;
 +
 +    mainloop = s->userdata->module->core->mainloop;
 +
 +    pa_gettimeofday(&tv);
 +    pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
 +
 +    ev = mainloop->time_new(mainloop, &tv, time_event_cb, timeout);
 +
 +    dbus_timeout_set_data(timeout, ev, NULL);
 +
 +    return TRUE;
 +}
 +
 +/* Called by D-Bus when a D-Bus timer event is removed. */
 +static void timeout_remove_cb(DBusTimeout *timeout, void *data) {
 +    struct server *s = data;
 +    pa_time_event *ev;
 +
 +    pa_assert(timeout);
 +    pa_assert(s);
 +
 +    if ((ev = dbus_timeout_get_data(timeout)))
 +        s->userdata->module->core->mainloop->time_free(ev);
 +}
 +
 +/* Called by D-Bus when a D-Bus timer event is toggled. */
 +static void timeout_toggled_cb(DBusTimeout *timeout, void *data) {
 +    struct server *s = data;
 +    pa_mainloop_api *mainloop;
 +    pa_time_event *ev;
 +
 +    pa_assert(timeout);
 +    pa_assert(s);
 +
 +    mainloop = s->userdata->module->core->mainloop;
 +
 +    pa_assert_se(ev = dbus_timeout_get_data(timeout));
 +
 +    if (dbus_timeout_get_enabled(timeout)) {
 +        struct timeval tv;
 +
 +        pa_gettimeofday(&tv);
 +        pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
 +
 +        mainloop->time_restart(ev, &tv);
 +    } else
 +        mainloop->time_restart(ev, NULL);
 +}
 +
 +static void server_free(struct server *s) {
 +    pa_assert(s);
 +
 +    if (s->dbus_server) {
 +        dbus_server_disconnect(s->dbus_server);
 +        dbus_server_unref(s->dbus_server);
 +    }
 +
 +    pa_xfree(s);
 +}
 +
 +static struct server *start_server(struct userdata *u, const char *address, enum server_type type) {
 +    /* XXX: We assume that when we unref the DBusServer instance at module
 +     * shutdown, nobody else holds any references to it. If we stop assuming
 +     * that someday, dbus_server_set_new_connection_function,
 +     * dbus_server_set_watch_functions and dbus_server_set_timeout_functions
 +     * calls should probably register free callbacks, instead of providing NULL
 +     * as they do now. */
 +
 +    struct server *s = NULL;
 +    DBusError error;
 +
 +    pa_assert(u);
 +    pa_assert(address);
 +
 +    dbus_error_init(&error);
 +
 +    s = pa_xnew0(struct server, 1);
 +    s->userdata = u;
 +    s->dbus_server = dbus_server_listen(address, &error);
 +
 +    if (dbus_error_is_set(&error)) {
 +        pa_log("dbus_server_listen() failed: %s: %s", error.name, error.message);
 +        goto fail;
 +    }
 +
 +    dbus_server_set_new_connection_function(s->dbus_server, connection_new_cb, s, NULL);
 +
 +    if (!dbus_server_set_watch_functions(s->dbus_server, watch_add_cb, watch_remove_cb, watch_toggled_cb, s, NULL)) {
 +        pa_log("dbus_server_set_watch_functions() ran out of memory.");
 +        goto fail;
 +    }
 +
 +    if (!dbus_server_set_timeout_functions(s->dbus_server, timeout_add_cb, timeout_remove_cb, timeout_toggled_cb, s, NULL)) {
 +        pa_log("dbus_server_set_timeout_functions() ran out of memory.");
 +        goto fail;
 +    }
 +
 +    return s;
 +
 +fail:
 +    if (s)
 +        server_free(s);
 +
 +    dbus_error_free(&error);
 +
 +    return NULL;
 +}
 +
 +static struct server *start_local_server(struct userdata *u) {
 +    struct server *s = NULL;
 +    char *address = NULL;
 +
 +    pa_assert(u);
 +
 +    address = pa_get_dbus_address_from_server_type(u->module->core->server_type);
 +
 +    s = start_server(u, address, SERVER_TYPE_LOCAL); /* May return NULL */
 +
 +    pa_xfree(address);
 +
 +    return s;
 +}
 +
 +static struct server *start_tcp_server(struct userdata *u) {
 +    struct server *s = NULL;
 +    char *address = NULL;
 +
 +    pa_assert(u);
 +
 +    address = pa_sprintf_malloc("tcp:host=127.0.0.1,port=%u", u->tcp_port);
 +
 +    s = start_server(u, address, SERVER_TYPE_TCP); /* May return NULL */
 +
 +    pa_xfree(address);
 +
 +    return s;
 +}
 +
 +static int get_access_arg(pa_modargs *ma, pa_bool_t *local_access, pa_bool_t *remote_access) {
 +    const char *value = NULL;
 +
 +    pa_assert(ma);
 +    pa_assert(local_access);
 +    pa_assert(remote_access);
 +
 +    if (!(value = pa_modargs_get_value(ma, "access", NULL)))
 +        return 0;
 +
 +    if (!strcmp(value, "local")) {
 +        *local_access = TRUE;
 +        *remote_access = FALSE;
 +    } else if (!strcmp(value, "remote")) {
 +        *local_access = FALSE;
 +        *remote_access = TRUE;
 +    } else if (!strcmp(value, "local,remote")) {
 +        *local_access = TRUE;
 +        *remote_access = TRUE;
 +    } else
 +        return -1;
 +
 +    return 0;
 +}
 +
 +/* Frees dead client connections. Called every CLEANUP_INTERVAL seconds. */
 +static void cleanup_cb(pa_mainloop_api *a, pa_time_event *e, const struct timeval *tv, void *userdata) {
 +    struct userdata *u = userdata;
 +    struct connection *conn = NULL;
 +    uint32_t idx;
 +    struct timeval cleanup_timeval;
 +    unsigned free_count = 0;
 +
 +    for (conn = pa_idxset_first(u->connections, &idx); conn; conn = pa_idxset_next(u->connections, &idx)) {
 +        if (!dbus_connection_get_is_connected(pa_dbus_wrap_connection_get(conn->wrap_conn))) {
 +            connection_free(conn);
 +            ++free_count;
 +        }
 +    }
 +
 +    if (free_count > 0)
 +        pa_log_debug("Freed %u dead D-Bus client connections.", free_count);
 +
 +    pa_gettimeofday(&cleanup_timeval);
 +    cleanup_timeval.tv_sec += CLEANUP_INTERVAL;
 +    u->module->core->mainloop->time_restart(e, &cleanup_timeval);
 +}
 +
 +int pa__init(pa_module *m) {
 +    struct userdata *u = NULL;
 +    pa_modargs *ma = NULL;
 +    struct timeval cleanup_timeval;
 +
 +    pa_assert(m);
 +
 +    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
 +        pa_log("Failed to parse module arguments.");
 +        goto fail;
 +    }
 +
 +    m->userdata = u = pa_xnew0(struct userdata, 1);
 +    u->module = m;
 +    u->local_access = TRUE;
 +    u->remote_access = FALSE;
 +    u->tcp_port = PA_DBUS_DEFAULT_PORT;
 +
 +    if (get_access_arg(ma, &u->local_access, &u->remote_access) < 0) {
 +        pa_log("Invalid access argument: '%s'", pa_modargs_get_value(ma, "access", NULL));
 +        goto fail;
 +    }
 +
 +    if (pa_modargs_get_value_u32(ma, "tcp_port", &u->tcp_port) < 0 || u->tcp_port < 1 || u->tcp_port > 49150) {
 +        pa_log("Invalid tcp_port argument: '%s'", pa_modargs_get_value(ma, "tcp_port", NULL));
 +        goto fail;
 +    }
 +
 +    if (u->local_access && !(u->local_server = start_local_server(u))) {
 +        pa_log("Starting the local D-Bus server failed.");
 +        goto fail;
 +    }
 +
 +    if (u->remote_access && !(u->tcp_server = start_tcp_server(u))) {
 +        pa_log("Starting the D-Bus server for remote connections failed.");
 +        goto fail;
 +    }
 +
 +    u->connections = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 +
 +    pa_gettimeofday(&cleanup_timeval);
 +    cleanup_timeval.tv_sec += CLEANUP_INTERVAL;
 +    u->cleanup_event = m->core->mainloop->time_new(m->core->mainloop, &cleanup_timeval, cleanup_cb, u);
 +
 +    u->core_object = pa_dbusobj_core_new(m->core);
 +
 +    return 0;
 +
 +fail:
 +    if (ma)
 +        pa_modargs_free(ma);
 +
 +    pa__done(m);
 +
 +    return -1;
 +}
 +
 +/* Called by idxset when the connection set is freed. */
 +static void connection_free_cb(void *p, void *userdata) {
 +    struct connection *conn = p;
 +
 +    pa_assert(conn);
 +
 +    connection_free(conn);
 +}
 +
 +void pa__done(pa_module *m) {
 +    struct userdata *u;
 +
 +    pa_assert(m);
 +
 +    if (!(u = m->userdata))
 +        return;
 +
 +    if (u->core_object)
 +        pa_dbusobj_core_free(u->core_object);
 +
 +    if (u->cleanup_event)
 +        m->core->mainloop->time_free(u->cleanup_event);
 +
 +    if (u->connections)
 +        pa_idxset_free(u->connections, connection_free_cb, NULL);
 +
 +    if (u->tcp_server)
 +        server_free(u->tcp_server);
 +
 +    if (u->local_server)
 +        server_free(u->local_server);
 +
 +    pa_xfree(u);
 +    m->userdata = NULL;
 +}
diff --cc src/pulsecore/dbus-util.c
index d8bd0e0,4e6148f..e047dc3
--- a/src/pulsecore/dbus-util.c
+++ b/src/pulsecore/dbus-util.c
@@@ -276,27 -290,6 +290,28 @@@ pa_dbus_wrap_connection* pa_dbus_wrap_c
      return pconn;
  }
  
- pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing(pa_mainloop_api *m, DBusConnection *conn) {
++pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing(pa_mainloop_api *m, pa_bool_t use_rtclock, DBusConnection *conn) {
 +    pa_dbus_wrap_connection *pconn;
 +
 +    pa_assert(m);
 +    pa_assert(conn);
 +
 +    pconn = pa_xnew(pa_dbus_wrap_connection, 1);
 +    pconn->mainloop = m;
 +    pconn->connection = dbus_connection_ref(conn);
++    pconn->use_rtclock = use_rtclock;
 +
 +    dbus_connection_set_exit_on_disconnect(conn, FALSE);
 +    dbus_connection_set_dispatch_status_function(conn, dispatch_status, pconn, NULL);
 +    dbus_connection_set_watch_functions(conn, add_watch, remove_watch, toggle_watch, pconn, NULL);
 +    dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout, toggle_timeout, pconn, NULL);
 +    dbus_connection_set_wakeup_main_function(conn, wakeup_main, pconn, NULL);
 +
 +    pconn->dispatch_event = pconn->mainloop->defer_new(pconn->mainloop, dispatch_cb, conn);
 +
 +    return pconn;
 +}
 +
  void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* c) {
      pa_assert(c);
  
diff --cc src/pulsecore/dbus-util.h
index cd08485,9ff298d..9732873
--- a/src/pulsecore/dbus-util.h
+++ b/src/pulsecore/dbus-util.h
@@@ -30,8 -30,7 +30,8 @@@
  /* A wrap connection is not shared or refcounted, it is available in client side */
  typedef struct pa_dbus_wrap_connection pa_dbus_wrap_connection;
  
- pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *mainloop, DBusBusType type, DBusError *error);
- pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing(pa_mainloop_api *mainloop, DBusConnection *conn);
+ pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *mainloop, pa_bool_t use_rtclock, DBusBusType type, DBusError *error);
++pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing(pa_mainloop_api *mainloop, pa_bool_t use_rtclock, DBusConnection *conn);
  void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* conn);
  
  DBusConnection* pa_dbus_wrap_connection_get(pa_dbus_wrap_connection *conn);

commit c266595058416b4b776890dadf3ad827787f4e7e
Merge: 0bc538b 1ca7603
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Mon Jun 29 18:55:12 2009 +0300

    Merge branch 'master' into dbus-work


commit 3bff2eee4bf02ff3bc2619079c3c5fe8a07040a9
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Tue Jun 30 15:18:11 2009 +0300

    module-cli: Fix compilation by adding libpulsecommon to module_cli_la_LIBADD.

diff --git a/src/Makefile.am b/src/Makefile.am
index 6cddb83..5866889 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1250,7 +1250,7 @@ module_simple_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore- at PA_MAJORMINOR
 
 module_cli_la_SOURCES = modules/module-cli.c
 module_cli_la_LDFLAGS = $(MODULE_LDFLAGS)
-module_cli_la_LIBADD = $(AM_LIBADD) libpulsecore- at PA_MAJORMINORMICRO@.la libcli.la
+module_cli_la_LIBADD = $(AM_LIBADD) libpulsecore- at PA_MAJORMINORMICRO@.la libcli.la libpulsecommon- at PA_MAJORMINORMICRO@.la
 
 module_cli_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c
 module_cli_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_CLI $(AM_CFLAGS)

commit 6e2fec05ddd38fa460276202ca2f4ecf68761ed0
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Tue Jun 30 15:21:20 2009 +0300

    server-lookup: Update the D-Bus identifiers to be versioned.

diff --git a/src/daemon/main.c b/src/daemon/main.c
index 61b0c84..d320c9e 100644
--- a/src/daemon/main.c
+++ b/src/daemon/main.c
@@ -943,7 +943,7 @@ int main(int argc, char *argv[]) {
     if (!conf->system_instance) {
         if (!(server_lookup = pa_dbusobj_server_lookup_new(c)))
             goto finish;
-        if (!(lookup_service_bus = register_dbus_name(c, DBUS_BUS_SESSION, "org.pulseaudio.PulseAudio")))
+        if (!(lookup_service_bus = register_dbus_name(c, DBUS_BUS_SESSION, "org.pulseaudio.PulseAudio1")))
             goto finish;
     }
 
diff --git a/src/daemon/server-lookup.c b/src/daemon/server-lookup.c
index 33d6b24..7c80d67 100644
--- a/src/daemon/server-lookup.c
+++ b/src/daemon/server-lookup.c
@@ -36,6 +36,9 @@
 
 #include "server-lookup.h"
 
+#define OBJECT_PATH "/org/pulseaudio1/server_lookup"
+#define INTERFACE "org.pulseaudio.ServerLookup1"
+
 struct pa_dbusobj_server_lookup {
     pa_core *core;
     pa_dbus_connection *conn;
@@ -47,7 +50,7 @@ static const char introspection[] =
     "<node>"
     " <!-- If you are looking for documentation make sure to check out\n"
     "      http://pulseaudio.org/wiki/DBusInterface -->\n"
-    " <interface name=\"org.pulseaudio.ServerLookup\">"
+    " <interface name=\"" INTERFACE "\">"
     "  <method name=\"GetAddress\">"
     "   <arg name=\"result\" type=\"s\" direction=\"out\"/>"
     "  </method>"
@@ -182,7 +185,7 @@ static DBusHandlerResult message_cb(DBusConnection *conn, DBusMessage *msg, void
     if (dbus_message_is_method_call(msg, "org.freedesktop.DBus.Introspectable", "Introspect"))
         return handle_introspect(conn, msg, sl);
 
-    if (dbus_message_is_method_call(msg, "org.pulseaudio.ServerLookup", "GetAddress"))
+    if (dbus_message_is_method_call(msg, INTERFACE, "GetAddress"))
         return handle_get_address(conn, msg, sl);
 
     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
@@ -212,8 +215,8 @@ pa_dbusobj_server_lookup *pa_dbusobj_server_lookup_new(pa_core *c) {
         goto fail;
     }
 
-    if (!dbus_connection_register_object_path(pa_dbus_connection_get(sl->conn), "/org/pulseaudio/server_lookup", &vtable, sl)) {
-        pa_log("dbus_connection_register_object_path() failed for /org/pulseaudio/server_lookup.");
+    if (!dbus_connection_register_object_path(pa_dbus_connection_get(sl->conn), OBJECT_PATH, &vtable, sl)) {
+        pa_log("dbus_connection_register_object_path() failed for " OBJECT_PATH ".");
         goto fail;
     }
 
@@ -234,8 +237,8 @@ void pa_dbusobj_server_lookup_free(pa_dbusobj_server_lookup *sl) {
 
     if (sl->path_registered) {
         pa_assert(sl->conn);
-        if (!dbus_connection_unregister_object_path(pa_dbus_connection_get(sl->conn), "/org/pulseaudio/server_lookup"))
-            pa_log_debug("dbus_connection_unregister_object_path() failed for /org/pulseaudio/server_lookup.");
+        if (!dbus_connection_unregister_object_path(pa_dbus_connection_get(sl->conn), OBJECT_PATH))
+            pa_log_debug("dbus_connection_unregister_object_path() failed for " OBJECT_PATH ".");
     }
 
     if (sl->conn)

commit 5c7952e4fa5e560f64255ef173c3e6570bee433a
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Fri Jul 3 02:49:07 2009 +0300

    dbus: Implement the Name property of the core object.

diff --git a/src/daemon/main.c b/src/daemon/main.c
index d320c9e..f420985 100644
--- a/src/daemon/main.c
+++ b/src/daemon/main.c
@@ -943,7 +943,7 @@ int main(int argc, char *argv[]) {
     if (!conf->system_instance) {
         if (!(server_lookup = pa_dbusobj_server_lookup_new(c)))
             goto finish;
-        if (!(lookup_service_bus = register_dbus_name(c, DBUS_BUS_SESSION, "org.pulseaudio.PulseAudio1")))
+        if (!(lookup_service_bus = register_dbus_name(c, DBUS_BUS_SESSION, "org.PulseAudio1")))
             goto finish;
     }
 
diff --git a/src/daemon/server-lookup.c b/src/daemon/server-lookup.c
index 7c80d67..ebacc09 100644
--- a/src/daemon/server-lookup.c
+++ b/src/daemon/server-lookup.c
@@ -37,7 +37,7 @@
 #include "server-lookup.h"
 
 #define OBJECT_PATH "/org/pulseaudio1/server_lookup"
-#define INTERFACE "org.pulseaudio.ServerLookup1"
+#define INTERFACE "org.PulseAudio.ServerLookup1"
 
 struct pa_dbusobj_server_lookup {
     pa_core *core;
@@ -50,17 +50,31 @@ static const char introspection[] =
     "<node>"
     " <!-- If you are looking for documentation make sure to check out\n"
     "      http://pulseaudio.org/wiki/DBusInterface -->\n"
-    " <interface name=\"" INTERFACE "\">"
-    "  <method name=\"GetAddress\">"
-    "   <arg name=\"result\" type=\"s\" direction=\"out\"/>"
-    "  </method>"
-    " </interface>"
-    " <interface name=\"org.freedesktop.DBus.Introspectable\">"
-    "  <method name=\"Introspect\">"
-    "   <arg name=\"data\" type=\"s\" direction=\"out\"/>"
-    "  </method>"
-    " </interface>"
-    "</node>";
+    " <interface name=\"" INTERFACE "\">\n"
+    "  <property name=\"Address\" type=\"s\" access=\"read\"/>\n"
+    " </interface>\n"
+    " <interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">\n"
+    "  <method name=\"Introspect\">\n"
+    "   <arg name=\"data\" type=\"s\" direction=\"out\"/>\n"
+    "  </method>\n"
+    " </interface>\n"
+    " <interface name=\"" DBUS_INTERFACE_PROPERTIES "\">\n"
+    "  <method name=\"Get\">\n"
+    "   <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
+    "   <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
+    "   <arg name=\"value\" type=\"v\" direction=\"out\"/>\n"
+    "  </method>\n"
+    "  <method name=\"Set\">\n"
+    "   <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
+    "   <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
+    "   <arg name=\"value\" type=\"v\" direction=\"in\"/>\n"
+    "  </method>\n"
+    "  <method name=\"GetAll\">\n"
+    "   <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
+    "   <arg name=\"props\" type=\"a{sv}\" direction=\"out\"/>\n"
+    "  </method>\n"
+    " </interface>\n"
+    "</node>\n";
 
 static void unregister_cb(DBusConnection *conn, void *user_data) {
     pa_dbusobj_server_lookup *sl = user_data;
@@ -72,105 +86,352 @@ static void unregister_cb(DBusConnection *conn, void *user_data) {
 }
 
 static DBusHandlerResult handle_introspect(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) {
+    DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED;
     const char *i = introspection;
     DBusMessage *reply = NULL;
 
     pa_assert(conn);
     pa_assert(msg);
 
-    if (!(reply = dbus_message_new_method_return(msg)))
-        goto fail;
+    if (!(reply = dbus_message_new_method_return(msg))) {
+        r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+        goto finish;
+    }
+    if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &i, DBUS_TYPE_INVALID)) {
+        r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+        goto finish;
+    }
+    if (!dbus_connection_send(conn, reply, NULL)) {
+        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+        goto finish;
+    }
 
-    if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &i, DBUS_TYPE_INVALID))
-        goto fail;
+finish:
+    if (reply)
+        dbus_message_unref(reply);
 
-    if (!dbus_connection_send(conn, reply, NULL))
-        goto oom;
+    return r;
+}
 
-    dbus_message_unref(reply);
+enum get_address_result_t {
+    SUCCESS,
+    FAILED_TO_LOAD_CLIENT_CONF,
+    SERVER_FROM_TYPE_FAILED
+};
 
-    return DBUS_HANDLER_RESULT_HANDLED;
+/* Caller frees the returned address. */
+static enum get_address_result_t get_address(pa_server_type_t server_type, char **address) {
+    enum get_address_result_t r = SUCCESS;
+    pa_client_conf *conf = pa_client_conf_new();
 
-fail:
-    if (reply)
-        dbus_message_unref(reply);
+    *address = NULL;
 
-    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+    if (pa_client_conf_load(conf, NULL) < 0) {
+        r = FAILED_TO_LOAD_CLIENT_CONF;
+        goto finish;
+    }
 
-oom:
-    if (reply)
-        dbus_message_unref(reply);
+    if (conf->default_dbus_server)
+        *address = pa_xstrdup(conf->default_dbus_server);
+    else if (!(*address = pa_get_dbus_address_from_server_type(server_type))) {
+        r = SERVER_FROM_TYPE_FAILED;
+        goto finish;
+    }
 
-    return DBUS_HANDLER_RESULT_NEED_MEMORY;
+finish:
+    pa_client_conf_free(conf);
+    return r;
 }
 
 static DBusHandlerResult handle_get_address(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) {
+    DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED;
     DBusMessage *reply = NULL;
-    pa_client_conf *conf = NULL;
     char *address = NULL;
+    DBusMessageIter msg_iter;
+    DBusMessageIter variant_iter;
 
     pa_assert(conn);
     pa_assert(msg);
     pa_assert(sl);
 
-    conf = pa_client_conf_new();
+    switch (get_address(sl->core->server_type, &address)) {
+        case SUCCESS:
+            if (!(reply = dbus_message_new_method_return(msg))) {
+                r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+                goto finish;
+            }
+            dbus_message_iter_init_append(reply, &msg_iter);
+            if (!dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_VARIANT, "s", &variant_iter)) {
+                r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+                goto finish;
+            }
+            if (!dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &address)) {
+                r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+                goto finish;
+            }
+            if (!dbus_message_iter_close_container(&msg_iter, &variant_iter)) {
+                r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+                goto finish;
+            }
+            if (!dbus_connection_send(conn, reply, NULL)) {
+                r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+                goto finish;
+            }
+            r = DBUS_HANDLER_RESULT_HANDLED;
+            goto finish;
+
+        case FAILED_TO_LOAD_CLIENT_CONF:
+            if (!(reply = dbus_message_new_error(msg, "org.pulseaudio.ClientConfLoadError", "Failed to load client.conf."))) {
+                r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+                goto finish;
+            }
+            if (!dbus_connection_send(conn, reply, NULL)) {
+                r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+                goto finish;
+            }
+            r = DBUS_HANDLER_RESULT_HANDLED;
+            goto finish;
+
+        case SERVER_FROM_TYPE_FAILED:
+            if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_FAILED, "PulseAudio internal error: get_dbus_server_from_type() failed."))) {
+                r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+                goto finish;
+            }
+            if (!dbus_connection_send(conn, reply, NULL)) {
+                r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+                goto finish;
+            }
+            r = DBUS_HANDLER_RESULT_HANDLED;
+            goto finish;
+
+        default:
+            pa_assert_not_reached();
+    }
 
-    if (pa_client_conf_load(conf, NULL) < 0) {
-        if (!(reply = dbus_message_new_error(msg, "org.pulseaudio.ClientConfLoadError", "Failed to load client.conf.")))
-            goto fail;
-        if (!dbus_connection_send(conn, reply, NULL))
-            goto oom;
-        return DBUS_HANDLER_RESULT_HANDLED;
-    }
-
-    if (conf->default_dbus_server) {
-        address = pa_xstrdup(conf->default_dbus_server);
-    } else {
-        if (!(address = pa_get_dbus_address_from_server_type(sl->core->server_type))) {
-            if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_FAILED, "PulseAudio internal error: get_dbus_server_from_type() failed.")))
-                goto fail;
-            if (!dbus_connection_send(conn, reply, NULL))
-                goto oom;
-            return DBUS_HANDLER_RESULT_HANDLED;
+finish:
+    pa_xfree(address);
+    if (reply)
+        dbus_message_unref(reply);
+
+    return r;
+}
+
+static DBusHandlerResult handle_get(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) {
+    DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED;
+    const char* interface;
+    const char* property;
+    DBusMessage *reply = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(sl);
+
+    if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) {
+        if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) {
+            r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+            goto finish;
+        }
+        if (!dbus_connection_send(conn, reply, NULL)) {
+            r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+            goto finish;
         }
+        r = DBUS_HANDLER_RESULT_HANDLED;
+        goto finish;
     }
 
-    if (!(reply = dbus_message_new_method_return(msg)))
-        goto oom;
+    if (*interface && !pa_streq(interface, INTERFACE)) {
+        r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+        goto finish;
+    }
 
-    if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &address, DBUS_TYPE_INVALID))
-        goto fail;
+    if (!pa_streq(property, "Address")) {
+        if (!(reply = dbus_message_new_error_printf(msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", property))) {
+            r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+            goto finish;
+        }
+        if (!dbus_connection_send(conn, reply, NULL)) {
+            r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+            goto finish;
+        }
+        r = DBUS_HANDLER_RESULT_HANDLED;
+        goto finish;
+    }
 
-    if (!dbus_connection_send(conn, reply, NULL))
-        goto oom;
+    r = handle_get_address(conn, msg, sl);
 
-    pa_client_conf_free(conf);
-    pa_xfree(address);
-    dbus_message_unref(reply);
+finish:
+    if (reply)
+        dbus_message_unref(reply);
 
-    return DBUS_HANDLER_RESULT_HANDLED;
+    return r;
+}
 
-fail:
-    if (conf)
-        pa_client_conf_free(conf);
+static DBusHandlerResult handle_set(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) {
+    DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED;
+    const char* interface;
+    const char* property;
+    DBusMessage *reply = NULL;
 
-    pa_xfree(address);
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(sl);
+
+    if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) {
+        if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) {
+            r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+            goto finish;
+        }
+        if (!dbus_connection_send(conn, reply, NULL)) {
+            r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+            goto finish;
+        }
+        r = DBUS_HANDLER_RESULT_HANDLED;
+        goto finish;
+    }
+
+    if (*interface && !pa_streq(interface, INTERFACE)) {
+        r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+        goto finish;
+    }
+
+    if (!pa_streq(property, "Address")) {
+        if (!(reply = dbus_message_new_error_printf(msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", property))) {
+            r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+            goto finish;
+        }
+        if (!dbus_connection_send(conn, reply, NULL)) {
+            r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+            goto finish;
+        }
+        r = DBUS_HANDLER_RESULT_HANDLED;
+        goto finish;
+    }
+
+    if (!(reply = dbus_message_new_error_printf(msg, DBUS_ERROR_ACCESS_DENIED, "%s: Property not settable", property))) {
+        r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+        goto finish;
+    }
+    if (!dbus_connection_send(conn, reply, NULL)) {
+        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+        goto finish;
+    }
+    r = DBUS_HANDLER_RESULT_HANDLED;
 
+finish:
     if (reply)
         dbus_message_unref(reply);
 
-    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+    return r;
+}
 
-oom:
-    if (conf)
-        pa_client_conf_free(conf);
+static DBusHandlerResult handle_get_all(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) {
+    DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED;
+    DBusMessage *reply = NULL;
+    const char *property = "Address";
+    char *interface = NULL;
+    char *address = NULL;
+    DBusMessageIter msg_iter;
+    DBusMessageIter dict_iter;
+    DBusMessageIter dict_entry_iter;
+    DBusMessageIter variant_iter;
 
-    pa_xfree(address);
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(sl);
 
+    if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID)) {
+        if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) {
+            r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+            goto finish;
+        }
+        if (!dbus_connection_send(conn, reply, NULL)) {
+            r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+            goto finish;
+        }
+        r = DBUS_HANDLER_RESULT_HANDLED;
+        goto finish;
+    }
+
+    switch (get_address(sl->core->server_type, &address)) {
+        case SUCCESS:
+            if (!(reply = dbus_message_new_method_return(msg))) {
+                r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+                goto finish;
+            }
+            dbus_message_iter_init_append(reply, &msg_iter);
+            if (!dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)) {
+                r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+                goto finish;
+            }
+            if (!dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter)) {
+                r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+                goto finish;
+            }
+            if (!dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &property)) {
+                r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+                goto finish;
+            }
+            if (!dbus_message_iter_open_container(&dict_entry_iter, DBUS_TYPE_VARIANT, "s", &variant_iter)) {
+                r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+                goto finish;
+            }
+            if (!dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &address)) {
+                r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+                goto finish;
+            }
+            if (!dbus_message_iter_close_container(&dict_entry_iter, &variant_iter)) {
+                r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+                goto finish;
+            }
+            if (!dbus_message_iter_close_container(&dict_iter, &dict_entry_iter)) {
+                r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+                goto finish;
+            }
+            if (!dbus_message_iter_close_container(&msg_iter, &dict_iter)) {
+                r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+                goto finish;
+            }
+            if (!dbus_connection_send(conn, reply, NULL)) {
+                r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+                goto finish;
+            }
+            r = DBUS_HANDLER_RESULT_HANDLED;
+            goto finish;
+
+        case FAILED_TO_LOAD_CLIENT_CONF:
+            if (!(reply = dbus_message_new_error(msg, "org.pulseaudio.ClientConfLoadError", "Failed to load client.conf."))) {
+                r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+                goto finish;
+            }
+            if (!dbus_connection_send(conn, reply, NULL)) {
+                r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+                goto finish;
+            }
+            r = DBUS_HANDLER_RESULT_HANDLED;
+            goto finish;
+
+        case SERVER_FROM_TYPE_FAILED:
+            if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_FAILED, "PulseAudio internal error: get_dbus_server_from_type() failed."))) {
+                r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+                goto finish;
+            }
+            if (!dbus_connection_send(conn, reply, NULL)) {
+                r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+                goto finish;
+            }
+            r = DBUS_HANDLER_RESULT_HANDLED;
+            goto finish;
+
+        default:
+            pa_assert_not_reached();
+    }
+
+finish:
+    pa_xfree(address);
     if (reply)
         dbus_message_unref(reply);
 
-    return DBUS_HANDLER_RESULT_NEED_MEMORY;
+    return r;
 }
 
 static DBusHandlerResult message_cb(DBusConnection *conn, DBusMessage *msg, void *user_data) {
@@ -182,11 +443,24 @@ static DBusHandlerResult message_cb(DBusConnection *conn, DBusMessage *msg, void
 
     /* pa_log("Got message! type = %s   path = %s   iface = %s   member = %s   dest = %s", dbus_message_type_to_string(dbus_message_get_type(msg)), dbus_message_get_path(msg), dbus_message_get_interface(msg), dbus_message_get_member(msg), dbus_message_get_destination(msg)); */
 
-    if (dbus_message_is_method_call(msg, "org.freedesktop.DBus.Introspectable", "Introspect"))
+    if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_METHOD_CALL)
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    if (dbus_message_is_method_call(msg, DBUS_INTERFACE_INTROSPECTABLE, "Introspect") ||
+        (!dbus_message_get_interface(msg) && dbus_message_has_member(msg, "Introspect")))
         return handle_introspect(conn, msg, sl);
 
-    if (dbus_message_is_method_call(msg, INTERFACE, "GetAddress"))
-        return handle_get_address(conn, msg, sl);
+    if (dbus_message_is_method_call(msg, DBUS_INTERFACE_PROPERTIES, "Get") ||
+        (!dbus_message_get_interface(msg) && dbus_message_has_member(msg, "Get")))
+        return handle_get(conn, msg, sl);
+
+    if (dbus_message_is_method_call(msg, DBUS_INTERFACE_PROPERTIES, "Set") ||
+        (!dbus_message_get_interface(msg) && dbus_message_has_member(msg, "Set")))
+        return handle_set(conn, msg, sl);
+
+    if (dbus_message_is_method_call(msg, DBUS_INTERFACE_PROPERTIES, "GetAll") ||
+        (!dbus_message_get_interface(msg) && dbus_message_has_member(msg, "GetAll")))
+        return handle_get_all(conn, msg, sl);
 
     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 }
diff --git a/src/pulsecore/dbus-common.c b/src/pulsecore/dbus-common.c
index 350add8..6562cda 100644
--- a/src/pulsecore/dbus-common.c
+++ b/src/pulsecore/dbus-common.c
@@ -48,6 +48,7 @@ struct object_entry {
 
 struct interface_entry {
     char *name;
+    char **properties;
     char **methods;
     char *introspection_snippet;
     DBusObjectPathMessageFunction receive;
@@ -106,48 +107,152 @@ static void update_introspection(struct object_entry *oe) {
 
     buf = pa_strbuf_new();
     pa_strbuf_puts(buf, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE);
-    pa_strbuf_puts(buf, "<node>");
+    pa_strbuf_puts(buf, "<node>\n");
 
     while ((iface_entry = pa_hashmap_iterate(oe->interfaces, &state, NULL)))
         pa_strbuf_puts(buf, iface_entry->introspection_snippet);
 
-    pa_strbuf_puts(buf, " <interface name=\"org.freedesktop.DBus.Introspectable\">"
-                        "  <method name=\"Introspect\">"
-                        "   <arg name=\"data\" type=\"s\" direction=\"out\"/>"
-                        "  </method>"
-                        " </interface>");
-
-    pa_strbuf_puts(buf, "</node>");
+    pa_strbuf_puts(buf, " <interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">\n"
+                        "  <method name=\"Introspect\">\n"
+                        "   <arg name=\"data\" type=\"s\" direction=\"out\"/>\n"
+                        "  </method>\n"
+                        " </interface>\n"
+                        " <interface name=\"" DBUS_INTERFACE_PROPERTIES "\">\n"
+                        "  <method name=\"Get\">\n"
+                        "   <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
+                        "   <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
+                        "   <arg name=\"value\" type=\"v\" direction=\"out\"/>\n"
+                        "  </method>\n"
+                        "  <method name=\"Set\">\n"
+                        "   <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
+                        "   <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
+                        "   <arg name=\"value\" type=\"v\" direction=\"in\"/>\n"
+                        "  </method>\n"
+                        "  <method name=\"GetAll\">\n"
+                        "   <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
+                        "   <arg name=\"props\" type=\"a{sv}\" direction=\"out\"/>\n"
+                        "  </method>\n"
+                        " </interface>\n");
+
+    pa_strbuf_puts(buf, "</node>\n");
 
     pa_xfree(oe->introspection);
     oe->introspection = pa_strbuf_tostring_free(buf);
 }
 
-static struct interface_entry *find_interface(struct object_entry *obj_entry, DBusMessage *msg) {
-    const char *interface;
-    struct interface_entry *iface_entry;
+enum find_result_t {
+    SUCCESS,
+    NO_SUCH_PROPERTY,
+    NO_SUCH_METHOD,
+    INVALID_MESSAGE_ARGUMENTS
+};
+
+static enum find_result_t find_interface_by_property(struct object_entry *obj_entry, const char *property, struct interface_entry **entry) {
     void *state = NULL;
 
     pa_assert(obj_entry);
-    pa_assert(msg);
+    pa_assert(property);
+    pa_assert(entry);
+
+    while ((*entry = pa_hashmap_iterate(obj_entry->interfaces, &state, NULL))) {
+        char *iface_property;
+        char **pos = (*entry)->properties;
+
+        while ((iface_property = *pos++)) {
+            if (pa_streq(iface_property, property))
+                return SUCCESS;
+        }
+    }
+
+    return NO_SUCH_PROPERTY;
+}
 
-    if ((interface = dbus_message_get_interface(msg)))
-        return pa_hashmap_get(obj_entry->interfaces, interface);
+static enum find_result_t find_interface_by_method(struct object_entry *obj_entry, const char *method, struct interface_entry **entry) {
+    void *state = NULL;
 
-    /* NULL interface, we'll have to search for an interface that contains the
-     * method. */
+    pa_assert(obj_entry);
+    pa_assert(method);
+    pa_assert(entry);
 
-    while ((iface_entry = pa_hashmap_iterate(obj_entry->interfaces, &state, NULL))) {
-        char *method;
-        char **pos = iface_entry->methods;
+    while ((*entry = pa_hashmap_iterate(obj_entry->interfaces, &state, NULL))) {
+        char *iface_method;
+        char **pos = (*entry)->methods;
 
-        while ((method = *pos++)) {
-            if (!strcmp(dbus_message_get_member(msg), method))
-                return iface_entry;
+        while ((iface_method = *pos++)) {
+            if (pa_streq(iface_method, method))
+                return SUCCESS;
         }
     }
 
-    return NULL;
+    return NO_SUCH_METHOD;
+}
+
+static enum find_result_t find_interface_from_properties_call(struct object_entry *obj_entry, DBusMessage *msg, struct interface_entry **entry) {
+    const char *interface;
+    const char *property;
+
+    pa_assert(obj_entry);
+    pa_assert(msg);
+    pa_assert(entry);
+
+    if (dbus_message_has_member(msg, "GetAll")) {
+        if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID))
+            return INVALID_MESSAGE_ARGUMENTS;
+
+        if (*interface) {
+            if ((*entry = pa_hashmap_get(obj_entry->interfaces, interface)))
+                return SUCCESS;
+            else
+                return NO_SUCH_METHOD;
+        } else {
+            pa_assert_se((*entry = pa_hashmap_first(obj_entry->interfaces)));
+            return SUCCESS;
+        }
+    } else {
+        pa_assert(dbus_message_has_member(msg, "Get") || dbus_message_has_member(msg, "Set"));
+
+        if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID))
+            return INVALID_MESSAGE_ARGUMENTS;
+
+        if (*interface) {
+            if ((*entry = pa_hashmap_get(obj_entry->interfaces, interface)))
+                return SUCCESS;
+            else
+                return NO_SUCH_METHOD;
+        } else
+            return find_interface_by_property(obj_entry, property, entry);
+    }
+}
+
+static enum find_result_t find_interface(struct object_entry *obj_entry, DBusMessage *msg, struct interface_entry **entry) {
+    const char *interface;
+
+    pa_assert(obj_entry);
+    pa_assert(msg);
+    pa_assert(entry);
+
+    *entry = NULL;
+
+    if (dbus_message_has_interface(msg, DBUS_INTERFACE_PROPERTIES))
+        return find_interface_from_properties_call(obj_entry, msg, entry);
+
+    else if ((interface = dbus_message_get_interface(msg))) {
+        if ((*entry = pa_hashmap_get(obj_entry->interfaces, interface)))
+            return SUCCESS;
+        else
+            return NO_SUCH_METHOD;
+
+    } else { /* The method call doesn't contain an interface. */
+        if (dbus_message_has_member(msg, "Get") || dbus_message_has_member(msg, "Set") || dbus_message_has_member(msg, "GetAll")) {
+            if (find_interface_by_method(obj_entry, dbus_message_get_member(msg), entry) == SUCCESS)
+                return SUCCESS; /* The object has a method named Get, Set or GetAll in some other interface than .Properties. */
+            else
+                /* Assume this is a .Properties call. */
+                return find_interface_from_properties_call(obj_entry, msg, entry);
+
+        } else /* This is not a .Properties call. */
+            return find_interface_by_method(obj_entry, dbus_message_get_member(msg), entry);
+    }
 }
 
 static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessage *message, void *user_data) {
@@ -166,7 +271,8 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa
 
     pa_assert_se((obj_entry = pa_hashmap_get(dbus_state->objects, dbus_message_get_path(message))));
 
-    if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
+    if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect") ||
+        (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Introspect"))) {
         if (!(reply = dbus_message_new_method_return(message)))
             goto oom;
 
@@ -183,10 +289,38 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa
         return DBUS_HANDLER_RESULT_HANDLED;
     }
 
-    if (!(iface_entry = find_interface(obj_entry, message)))
-        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+    switch (find_interface(obj_entry, message, &iface_entry)) {
+        case SUCCESS:
+            return iface_entry->receive(connection, message, iface_entry->userdata);
+
+        case NO_SUCH_PROPERTY:
+            if (!(reply = dbus_message_new_error(message, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "No such property")))
+                goto fail;
 
-    return iface_entry->receive(connection, message, iface_entry->userdata);
+            if (!dbus_connection_send(connection, reply, NULL))
+                goto oom;
+
+            dbus_message_unref(reply);
+
+            return DBUS_HANDLER_RESULT_HANDLED;
+
+        case NO_SUCH_METHOD:
+            return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+        case INVALID_MESSAGE_ARGUMENTS:
+            if (!(reply = dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, "Invalid arguments")))
+                goto fail;
+
+            if (!dbus_connection_send(connection, reply, NULL))
+                goto oom;
+
+            dbus_message_unref(reply);
+
+            return DBUS_HANDLER_RESULT_HANDLED;
+
+        default:
+            pa_assert_not_reached();
+    }
 
 fail:
     if (reply)
@@ -226,23 +360,30 @@ static void register_object(struct dbus_state *dbus_state, struct object_entry *
     }
 }
 
-static char **copy_methods(const char * const *methods) {
+static char **copy_strarray(const char * const *array) {
     unsigned n = 0;
     char **copy;
     unsigned i;
 
-    while (methods[n++])
+    while (array[n++])
         ;
 
     copy = pa_xnew0(char *, n);
 
     for (i = 0; i < n - 1; ++i)
-        copy[i] = pa_xstrdup(methods[i]);
+        copy[i] = pa_xstrdup(array[i]);
 
     return copy;
 }
 
-int pa_dbus_add_interface(pa_core *c, const char* path, const char* interface, const char * const *methods, const char* introspection_snippet, DBusObjectPathMessageFunction receive_cb, void *userdata) {
+int pa_dbus_add_interface(pa_core *c,
+                          const char* path,
+                          const char* interface,
+                          const char * const *properties,
+                          const char * const *methods,
+                          const char* introspection_snippet,
+                          DBusObjectPathMessageFunction receive_cb,
+                          void *userdata) {
     struct dbus_state *dbus_state;
     pa_hashmap *objects;
     struct object_entry *obj_entry;
@@ -283,7 +424,8 @@ int pa_dbus_add_interface(pa_core *c, const char* path, const char* interface, c
 
     iface_entry = pa_xnew(struct interface_entry, 1);
     iface_entry->name = pa_xstrdup(interface);
-    iface_entry->methods = copy_methods(methods);
+    iface_entry->properties = copy_strarray(properties);
+    iface_entry->methods = copy_strarray(methods);
     iface_entry->introspection_snippet = pa_xstrdup(introspection_snippet);
     iface_entry->receive = receive_cb;
     iface_entry->userdata = userdata;
@@ -331,13 +473,13 @@ static void unregister_object(struct dbus_state *dbus_state, struct object_entry
     }
 }
 
-static void free_methods(char **methods) {
-    char **pos = methods;
+static void free_strarray(char **array) {
+    char **pos = array;
 
     while (*pos++)
         pa_xfree(*pos);
 
-    pa_xfree(methods);
+    pa_xfree(array);
 }
 
 int pa_dbus_remove_interface(pa_core *c, const char* path, const char* interface) {
@@ -365,7 +507,8 @@ int pa_dbus_remove_interface(pa_core *c, const char* path, const char* interface
     update_introspection(obj_entry);
 
     pa_xfree(iface_entry->name);
-    free_methods(iface_entry->methods);
+    free_strarray(iface_entry->properties);
+    free_strarray(iface_entry->methods);
     pa_xfree(iface_entry->introspection_snippet);
     pa_xfree(iface_entry);
 
diff --git a/src/pulsecore/dbus-common.h b/src/pulsecore/dbus-common.h
index 23c7c22..4354c4e 100644
--- a/src/pulsecore/dbus-common.h
+++ b/src/pulsecore/dbus-common.h
@@ -28,10 +28,12 @@
 #include <pulsecore/macro.h>
 
 #define PA_DBUS_DEFAULT_PORT 24883
-#define PA_DBUS_SOCKET_NAME "dbus_socket"
+#define PA_DBUS_SOCKET_NAME "dbus-socket"
 
 #define PA_DBUS_SYSTEM_SOCKET_PATH PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_DBUS_SOCKET_NAME
 
+#define PA_DBUS_ERROR_NO_SUCH_PROPERTY "org.PulseAudio.Core1.NoSuchPropertyError"
+
 /* NOTE: These functions may only be called from the main thread. */
 
 /* Returns the default address of the server type in the escaped form. For
@@ -47,17 +49,27 @@ char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type);
  * Introspection requests are handled automatically. For that to work, the
  * caller gives an XML snippet containing the interface introspection element.
  * All interface snippets are automatically combined to provide the final
- * introspection string.
+ * introspection string for the object.
  *
- * The introspection snippet contains the interface name and the methods, but
- * since this function doesn't do XML parsing, the interface name and the set
- * of method names have to be supplied separately. If the interface doesn't
- * contain any methods, NULL may be given as the methods parameter, otherwise
- * the methods parameter must be a NULL-terminated array of strings.
+ * The introspection snippet contains the interface name, the property names
+ * and the method namess, but since this function doesn't do XML parsing, the
+ * information needs to be given separately. Property and method names are
+ * given as a NULL-terminated array of strings. The interface name is used for
+ * message routing, and so are the property and method names too in case the
+ * client doesn't tell which interface he's trying to access; in absence of
+ * interface information from the client, the correct interface is searched
+ * based on the property or method name.
  *
  * Fails and returns a negative number if the object already has the interface
  * registered. */
-int pa_dbus_add_interface(pa_core *c, const char* path, const char* interface, const char * const *methods, const char* introspection_snippet, DBusObjectPathMessageFunction receive_cb, void *userdata);
+int pa_dbus_add_interface(pa_core *c,
+                          const char* path,
+                          const char* interface,
+                          const char * const *properties,
+                          const char * const *methods,
+                          const char* introspection_snippet,
+                          DBusObjectPathMessageFunction receive_cb,
+                          void *userdata);
 
 /* Returns a negative number if the given object doesn't have the given
  * interface registered. */
diff --git a/src/pulsecore/dbus-objs/core.c b/src/pulsecore/dbus-objs/core.c
index f59c478..18bbf78 100644
--- a/src/pulsecore/dbus-objs/core.c
+++ b/src/pulsecore/dbus-objs/core.c
@@ -27,62 +27,414 @@
 
 #include <pulse/xmalloc.h>
 
+#include <pulsecore/core-util.h>
 #include <pulsecore/dbus-common.h>
 #include <pulsecore/macro.h>
 
 #include "core.h"
 
-#define OBJECT_NAME "/org/pulseaudio/core"
-#define INTERFACE_NAME "org.pulseaudio.Core"
+#define OBJECT_PATH "/org/pulseaudio1"
+#define INTERFACE_CORE "org.PulseAudio.Core1"
 
 struct pa_dbusobj_core {
     pa_core *core;
 };
 
 static const char *introspection_snippet =
-    " <interface name=\""INTERFACE_NAME"\">"
-    "  <method name=\"Test\">"
-    "   <arg name=\"result\" type=\"s\" direction=\"out\"/>"
-    "  </method>"
-    " </interface>";
+    " <interface name=\"" INTERFACE_CORE "\">\n"
+    "  <method name=\"GetCardByName\">\n"
+    "   <arg name=\"Name\" type=\"s\" direction=\"in\"/>\n"
+    "   <arg name=\"Card\" type=\"o\" direction=\"out\"/>\n"
+    "  </method>\n"
+    "  <method name=\"GetSinkByName\">\n"
+    "   <arg name=\"Name\" type=\"s\" direction=\"in\"/>\n"
+    "   <arg name=\"Sink\" type=\"o\" direction=\"out\"/>\n"
+    "  </method>\n"
+    "  <method name=\"GetSourceByName\">\n"
+    "   <arg name=\"Name\" type=\"s\" direction=\"in\"/>\n"
+    "   <arg name=\"Source\" type=\"o\" direction=\"out\"/>\n"
+    "  </method>\n"
+    "  <method name=\"GetSampleByName\">\n"
+    "   <arg name=\"Name\" type=\"s\" direction=\"in\"/>\n"
+    "   <arg name=\"Sample\" type=\"o\" direction=\"out\"/>\n"
+    "  </method>\n"
+    "  <method name=\"UploadSample\">\n"
+    "   <arg name=\"Name\" type=\"s\" direction=\"in\"/>\n"
+    "   <arg name=\"SampleFormat\" type=\"y\" direction=\"in\"/>\n"
+    "   <arg name=\"SampleRate\" type=\"u\" direction=\"in\"/>\n"
+    "   <arg name=\"Channels\" type=\"ay\" direction=\"in\"/>\n"
+    "   <arg name=\"DefaultVolume\" type=\"au\" direction=\"in\"/>\n"
+    "   <arg name=\"Proplist\" type=\"a{say}\" direction=\"in\"/>\n"
+    "   <arg name=\"Data\" type=\"ay\" direction=\"in\"/>\n"
+    "   <arg name=\"Sample\" type=\"o\" direction=\"out\"/>\n"
+    "  </method>\n"
+    "  <method name=\"LoadSampleFromFile\">\n"
+    "   <arg name=\"Name\" type=\"s\" direction=\"in\"/>\n"
+    "   <arg name=\"Filepath\" type=\"s\" direction=\"in\"/>\n"
+    "   <arg name=\"Sample\" type=\"o\" direction=\"out\"/>\n"
+    "  </method>\n"
+    "  <method name=\"AddLazySample\">\n"
+    "   <arg name=\"Name\" type=\"s\" direction=\"in\"/>\n"
+    "   <arg name=\"Filepath\" type=\"s\" direction=\"in\"/>\n"
+    "   <arg name=\"Sample\" type=\"o\" direction=\"out\"/>\n"
+    "  </method>\n"
+    "  <method name=\"AddLazySamplesFromDirectory\">\n"
+    "   <arg name=\"Dirpath\" type=\"s\" direction=\"in\"/>\n"
+    "  </method>\n"
+    "  <method name=\"LoadModule\">\n"
+    "   <arg name=\"Name\" type=\"s\" direction=\"in\"/>\n"
+    "   <arg name=\"Arguments\" type=\"a{ss}\" direction=\"in\"/>\n"
+    "   <arg name=\"Module\" type=\"o\" direction=\"out\"/>\n"
+    "  </method>\n"
+    "  <method name=\"Exit\"/>\n"
+    "  <signal name=\"NewCard\">\n"
+    "   <arg name=\"Card\" type=\"o\"/>\n"
+    "  </signal>\n"
+    "  <signal name=\"CardRemoved\">\n"
+    "   <arg name=\"Card\" type=\"o\"/>\n"
+    "  </signal>\n"
+    "  <signal name=\"NewSink\">\n"
+    "   <arg name=\"Sink\" type=\"o\"/>\n"
+    "  </signal>\n"
+    "  <signal name=\"SinkRemoved\">\n"
+    "   <arg name=\"Sink\" type=\"o\"/>\n"
+    "  </signal>\n"
+    "  <signal name=\"FallbackSinkUpdated\">\n"
+    "   <arg name=\"Sink\" type=\"o\"/>\n"
+    "  </signal>\n"
+    "  <signal name=\"NewSource\">\n"
+    "   <arg name=\"Source\" type=\"o\"/>\n"
+    "  </signal>\n"
+    "  <signal name=\"SourceRemoved\">\n"
+    "   <arg name=\"Source\" type=\"o\"/>\n"
+    "  </signal>\n"
+    "  <signal name=\"FallbackSourceUpdated\">\n"
+    "   <arg name=\"Source\" type=\"o\"/>\n"
+    "  </signal>\n"
+    "  <signal name=\"NewPlaybackStream\">\n"
+    "   <arg name=\"PlaybackStream\" type=\"o\"/>\n"
+    "  </signal>\n"
+    "  <signal name=\"PlaybackStreamRemoved\">\n"
+    "   <arg name=\"PlaybackStream\" type=\"o\"/>\n"
+    "  </signal>\n"
+    "  <signal name=\"NewRecordStream\">\n"
+    "   <arg name=\"RecordStream\" type=\"o\"/>\n"
+    "  </signal>\n"
+    "  <signal name=\"RecordStreamRemoved\">\n"
+    "   <arg name=\"RecordStream\" type=\"o\"/>\n"
+    "  </signal>\n"
+    "  <signal name=\"NewSample\">\n"
+    "   <arg name=\"Sample\" type=\"o\"/>\n"
+    "  </signal>\n"
+    "  <signal name=\"SampleRemoved\">\n"
+    "   <arg name=\"Sample\" type=\"o\"/>\n"
+    "  </signal>\n"
+    "  <signal name=\"NewModule\">\n"
+    "   <arg name=\"Module\" type=\"o\"/>\n"
+    "  </signal>\n"
+    "  <signal name=\"ModuleRemoved\">\n"
+    "   <arg name=\"Module\" type=\"o\"/>\n"
+    "  </signal>\n"
+    "  <signal name=\"NewClient\">\n"
+    "   <arg name=\"Client\" type=\"o\"/>\n"
+    "  </signal>\n"
+    "  <signal name=\"ClientRemoved\">\n"
+    "   <arg name=\"Client\" type=\"o\"/>\n"
+    "  </signal>\n"
+    "  <property name=\"InterfaceRevision\" type=\"u\" access=\"read\"/>\n"
+    "  <property name=\"Name\" type=\"s\" access=\"read\"/>\n"
+    "  <property name=\"Version\" type=\"s\" access=\"read\"/>\n"
+    "  <property name=\"Username\" type=\"s\" access=\"read\"/>\n"
+    "  <property name=\"Hostname\" type=\"s\" access=\"read\"/>\n"
+    "  <property name=\"DefaultChannels\" type=\"ay\" access=\"readwrite\"/>\n"
+    "  <property name=\"DefaultSampleFormat\" type=\"y\" access=\"readwrite\"/>\n"
+    "  <property name=\"DefaultSampleRate\" type=\"u\" access=\"readwrite\"/>\n"
+    "  <property name=\"Sinks\" type=\"ao\" access=\"read\"/>\n"
+    "  <property name=\"FallbackSink\" type=\"s\" access=\"readwrite\"/>\n"
+    "  <property name=\"Sources\" type=\"ao\" access=\"read\"/>\n"
+    "  <property name=\"FallbackSource\" type=\"o\" access=\"readwrite\"/>\n"
+    "  <property name=\"PlaybackStreams\" type=\"ao\" access=\"read\"/>\n"
+    "  <property name=\"RecordStreams\" type=\"ao\" access=\"read\"/>\n"
+    "  <property name=\"Samples\" type=\"ao\" access=\"read\"/>\n"
+    "  <property name=\"Modules\" type=\"ao\" access=\"read\"/>\n"
+    "  <property name=\"Clients\" type=\"ao\" access=\"read\"/>\n"
+    "  <property name=\"Extensions\" type=\"as\" access=\"read\"/>\n"
+    " </interface>\n";
+
+/* If you need to modify this list, note that handle_get_all() uses hard-coded
+ * indexes to point to these strings, so make sure the indexes don't go wrong
+ * there. */
+static const char *properties[] = {
+    "InterfaceRevision",
+    "Name",
+    "Version",
+    "Username",
+    "Hostname",
+    "DefaultChannels",
+    "DefaultSampleFormat",
+    "DefaultSampleRate",
+    "Sinks",
+    "FallbackSink",
+    "Sources",
+    "FallbackSource",
+    "PlaybackStreams",
+    "RecordStreams",
+    "Samples",
+    "Modules",
+    "Clients",
+    "Extensions",
+    NULL
+};
 
 static const char *methods[] = {
-    "Test",
+    "GetCardByName",
+    "GetSinkByName",
+    "GetSourceByName",
+    "GetSampleByName",
+    "UploadSample",
+    "LoadSampleFromFile",
+    "AddLazySample",
+    "AddLazySamplesFromDirectory",
+    "LoadModule",
+    "Exit",
     NULL
 };
 
-static DBusHandlerResult handle_test(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) {
+static DBusHandlerResult handle_get_name(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) {
+    DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED;
     DBusMessage *reply = NULL;
-    const char *reply_message = "Hello!";
+    const char *server_name = PACKAGE_NAME;
+    DBusMessageIter msg_iter;
+    DBusMessageIter variant_iter;
 
     pa_assert(conn);
     pa_assert(msg);
     pa_assert(c);
 
-    if (!(reply = dbus_message_new_method_return(msg)))
-        goto oom;
+    if (!(reply = dbus_message_new_method_return(msg))) {
+        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+        goto finish;
+    }
+    dbus_message_iter_init_append(reply, &msg_iter);
+    if (!dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_VARIANT, "s", &variant_iter)) {
+        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+        goto finish;
+    }
+    if (!dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &server_name)) {
+        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+        goto finish;
+    }
+    if (!dbus_message_iter_close_container(&msg_iter, &variant_iter)) {
+        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+        goto finish;
+    }
+    if (!dbus_connection_send(conn, reply, NULL)) {
+        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+        goto finish;
+    }
+    r = DBUS_HANDLER_RESULT_HANDLED;
+
+finish:
+    if (reply)
+        dbus_message_unref(reply);
+
+    return r;
+}
 
-    if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &reply_message, DBUS_TYPE_INVALID))
-        goto fail;
+static DBusHandlerResult handle_get(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) {
+    DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED;
+    const char* interface;
+    const char* property;
+    DBusMessage *reply = NULL;
 
-    if (!dbus_connection_send(conn, reply, NULL))
-        goto oom;
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) {
+        if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) {
+            r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+            goto finish;
+        }
+        if (!dbus_connection_send(conn, reply, NULL)) {
+            r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+            goto finish;
+        }
+        r = DBUS_HANDLER_RESULT_HANDLED;
+        goto finish;
+    }
+
+    if (*interface && !pa_streq(interface, INTERFACE_CORE)) {
+        r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+        goto finish;
+    }
+
+    if (pa_streq(property, "Name")) {
+        r = handle_get_name(conn, msg, c);
+        goto finish;
+    }
+
+    if (!(reply = dbus_message_new_error_printf(msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", property))) {
+        r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+        goto finish;
+    }
+    if (!dbus_connection_send(conn, reply, NULL)) {
+        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+        goto finish;
+    }
+    r = DBUS_HANDLER_RESULT_HANDLED;
+
+finish:
+    if (reply)
+        dbus_message_unref(reply);
+
+    return r;
+}
 
-    dbus_message_unref(reply);
+static DBusHandlerResult handle_set(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) {
+    DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED;
+    const char* interface;
+    const char* property;
+    DBusMessage *reply = NULL;
 
-    return DBUS_HANDLER_RESULT_HANDLED;
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
 
-fail:
+    if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) {
+        if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) {
+            r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+            goto finish;
+        }
+        if (!dbus_connection_send(conn, reply, NULL)) {
+            r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+            goto finish;
+        }
+        r = DBUS_HANDLER_RESULT_HANDLED;
+        goto finish;
+    }
+
+    if (*interface && !pa_streq(interface, INTERFACE_CORE)) {
+        r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+        goto finish;
+    }
+
+    if (pa_streq(property, "Name")) {
+        if (!(reply = dbus_message_new_error_printf(msg, DBUS_ERROR_ACCESS_DENIED, "%s: Property not settable", property))) {
+            r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+            goto finish;
+        }
+        if (!dbus_connection_send(conn, reply, NULL)) {
+            r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+            goto finish;
+        }
+        r = DBUS_HANDLER_RESULT_HANDLED;
+        goto finish;
+    }
+
+    if (!(reply = dbus_message_new_error_printf(msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", property))) {
+        r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+        goto finish;
+    }
+    if (!dbus_connection_send(conn, reply, NULL)) {
+        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+        goto finish;
+    }
+    r = DBUS_HANDLER_RESULT_HANDLED;
+    goto finish;
+
+    if (!(reply = dbus_message_new_error_printf(msg, DBUS_ERROR_ACCESS_DENIED, "%s: Property not settable", property))) {
+        r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+        goto finish;
+    }
+    if (!dbus_connection_send(conn, reply, NULL)) {
+        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+        goto finish;
+    }
+    r = DBUS_HANDLER_RESULT_HANDLED;
+
+finish:
     if (reply)
         dbus_message_unref(reply);
 
-    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+    return r;
+}
+
+static DBusHandlerResult handle_get_all(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) {
+    DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED;
+    DBusMessage *reply = NULL;
+    char *interface = NULL;
+    char const *server_name = PACKAGE_NAME;
+    DBusMessageIter msg_iter;
+    DBusMessageIter dict_iter;
+    DBusMessageIter dict_entry_iter;
+    DBusMessageIter variant_iter;
 
-oom:
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID)) {
+        if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) {
+            r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+            goto finish;
+        }
+        if (!dbus_connection_send(conn, reply, NULL)) {
+            r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+            goto finish;
+        }
+        r = DBUS_HANDLER_RESULT_HANDLED;
+        goto finish;
+    }
+
+    if (!(reply = dbus_message_new_method_return(msg))) {
+        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+        goto finish;
+    }
+    dbus_message_iter_init_append(reply, &msg_iter);
+    if (!dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)) {
+        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+        goto finish;
+    }
+    if (!dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter)) {
+        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+        goto finish;
+    }
+    if (!dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &properties[1])) { /* Name */
+        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+        goto finish;
+    }
+    if (!dbus_message_iter_open_container(&dict_entry_iter, DBUS_TYPE_VARIANT, "s", &variant_iter)) {
+        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+        goto finish;
+    }
+    if (!dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &server_name)) {
+        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+        goto finish;
+    }
+    if (!dbus_message_iter_close_container(&dict_entry_iter, &variant_iter)) {
+        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+        goto finish;
+    }
+    if (!dbus_message_iter_close_container(&dict_iter, &dict_entry_iter)) {
+        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+        goto finish;
+    }
+    if (!dbus_message_iter_close_container(&msg_iter, &dict_iter)) {
+        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+        goto finish;
+    }
+    if (!dbus_connection_send(conn, reply, NULL)) {
+        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
+        goto finish;
+    }
+    r = DBUS_HANDLER_RESULT_HANDLED;
+
+finish:
     if (reply)
         dbus_message_unref(reply);
 
-    return DBUS_HANDLER_RESULT_NEED_MEMORY;
+    return r;
 }
 
 static DBusHandlerResult receive_cb(DBusConnection *connection, DBusMessage *message, void *user_data) {
@@ -92,8 +444,17 @@ static DBusHandlerResult receive_cb(DBusConnection *connection, DBusMessage *mes
     pa_assert(message);
     pa_assert(c);
 
-    if (dbus_message_is_method_call(message, INTERFACE_NAME, "Test"))
-        return handle_test(connection, message, c);
+    if (dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "Get") ||
+        (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Get")))
+        return handle_get(connection, message, c);
+
+    if (dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "Set") ||
+        (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Set")))
+        return handle_set(connection, message, c);
+
+    if (dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "GetAll") ||
+        (!dbus_message_get_interface(message) && dbus_message_has_member(message, "GetAll")))
+        return handle_get_all(connection, message, c);
 
     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 }
@@ -106,7 +467,8 @@ pa_dbusobj_core *pa_dbusobj_core_new(pa_core *core) {
     c = pa_xnew(pa_dbusobj_core, 1);
     c->core = core;
 
-    pa_dbus_add_interface(core, OBJECT_NAME, INTERFACE_NAME, methods, introspection_snippet, receive_cb, c);
+    pa_dbus_add_interface(core, OBJECT_PATH, INTERFACE_CORE, properties, methods, introspection_snippet, receive_cb, c);
+
 
     return c;
 }
@@ -114,7 +476,7 @@ pa_dbusobj_core *pa_dbusobj_core_new(pa_core *core) {
 void pa_dbusobj_core_free(pa_dbusobj_core *c) {
     pa_assert(c);
 
-    pa_dbus_remove_interface(c->core, OBJECT_NAME, INTERFACE_NAME);
+    pa_dbus_remove_interface(c->core, OBJECT_PATH, INTERFACE_CORE);
 
     pa_xfree(c);
 }

commit 9347e90fed732dac619bb88f6518c344e7436447
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Tue Jul 21 00:02:27 2009 +0300

    Finish the Core dbus interface.

diff --git a/src/Makefile.am b/src/Makefile.am
index 5866889..47eb647 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -852,7 +852,7 @@ endif
 if HAVE_DBUS
 libpulsecore_ at PA_MAJORMINORMICRO@_la_SOURCES += \
 		pulsecore/dbus-shared.c pulsecore/dbus-shared.h \
-		pulsecore/dbus-common.c pulsecore/dbus-common.h
+		pulsecore/protocol-dbus.c pulsecore/protocol-dbus.h
 libpulsecore_ at PA_MAJORMINORMICRO@_la_CFLAGS += $(DBUS_CFLAGS)
 libpulsecore_ at PA_MAJORMINORMICRO@_la_LIBADD += $(DBUS_LIBS)
 endif
@@ -1225,7 +1225,7 @@ SYMDEF_FILES = \
 		modules/module-augment-properties-symdef.h \
 		modules/module-cork-music-on-phone-symdef.h \
 		modules/module-console-kit-symdef.h \
-		modules/module-dbus-protocol-symdef.h
+		modules/dbus/module-dbus-protocol-symdef.h
 
 EXTRA_DIST += $(SYMDEF_FILES)
 BUILT_SOURCES += $(SYMDEF_FILES)
@@ -1277,8 +1277,14 @@ module_http_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore- at PA_MAJORMINORMI
 # D-Bus protocol
 
 module_dbus_protocol_la_SOURCES = \
-		pulsecore/dbus-objs/core.c pulsecore/dbus-objs/core.h \
-		modules/module-dbus-protocol.c
+		modules/dbus/iface-card.c modules/dbus/iface-card.h \
+		modules/dbus/iface-client.c modules/dbus/iface-client.h \
+		modules/dbus/iface-core.c modules/dbus/iface-core.h \
+		modules/dbus/iface-device.c modules/dbus/iface-device.h \
+		modules/dbus/iface-module.c modules/dbus/iface-module.h \
+		modules/dbus/iface-sample.c modules/dbus/iface-sample.h \
+		modules/dbus/iface-stream.c modules/dbus/iface-stream.h \
+		modules/dbus/module-dbus-protocol.c
 module_dbus_protocol_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
 module_dbus_protocol_la_LDFLAGS = $(MODULE_LDFLAGS)
 module_dbus_protocol_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
diff --git a/src/daemon/server-lookup.c b/src/daemon/server-lookup.c
index ebacc09..45796e7 100644
--- a/src/daemon/server-lookup.c
+++ b/src/daemon/server-lookup.c
@@ -30,13 +30,13 @@
 
 #include <pulsecore/core.h>
 #include <pulsecore/core-util.h>
-#include <pulsecore/dbus-common.h>
 #include <pulsecore/dbus-shared.h>
 #include <pulsecore/macro.h>
+#include <pulsecore/protocol-dbus.h>
 
 #include "server-lookup.h"
 
-#define OBJECT_PATH "/org/pulseaudio1/server_lookup"
+#define OBJECT_PATH "/org/pulseaudio/server_lookup1"
 #define INTERFACE "org.PulseAudio.ServerLookup1"
 
 struct pa_dbusobj_server_lookup {
diff --git a/src/modules/module-dbus-protocol.c b/src/modules/module-dbus-protocol.c
deleted file mode 100644
index f7c1f0a..0000000
--- a/src/modules/module-dbus-protocol.c
+++ /dev/null
@@ -1,575 +0,0 @@
-/***
-  This file is part of PulseAudio.
-
-  Copyright 2009 Tanu Kaskinen
-  Copyright 2006 Lennart Poettering
-  Copyright 2006 Shams E. King
-
-  PulseAudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2.1 of the License,
-  or (at your option) any later version.
-
-  PulseAudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with PulseAudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <dbus/dbus.h>
-
-#include <pulse/mainloop-api.h>
-#include <pulse/timeval.h>
-#include <pulse/xmalloc.h>
-
-#include <pulsecore/client.h>
-#include <pulsecore/core-util.h>
-#include <pulsecore/dbus-common.h>
-#include <pulsecore/dbus-util.h>
-#include <pulsecore/idxset.h>
-#include <pulsecore/macro.h>
-#include <pulsecore/modargs.h>
-#include <pulsecore/module.h>
-
-#include <pulsecore/dbus-objs/core.h>
-
-#include "module-dbus-protocol-symdef.h"
-
-PA_MODULE_DESCRIPTION("D-Bus interface");
-PA_MODULE_USAGE(
-        "access=local|remote|local,remote "
-        "tcp_port=<port number>");
-PA_MODULE_LOAD_ONCE(TRUE);
-PA_MODULE_AUTHOR("Tanu Kaskinen");
-PA_MODULE_VERSION(PACKAGE_VERSION);
-
-#define CLEANUP_INTERVAL 10 /* seconds */
-
-enum server_type {
-    SERVER_TYPE_LOCAL,
-    SERVER_TYPE_TCP
-};
-
-struct server;
-struct connection;
-
-struct userdata {
-    pa_module *module;
-    pa_bool_t local_access;
-    pa_bool_t remote_access;
-    uint32_t tcp_port;
-
-    struct server *local_server;
-    struct server *tcp_server;
-
-    pa_idxset *connections;
-
-    pa_time_event *cleanup_event;
-
-    pa_dbusobj_core *core_object;
-};
-
-struct server {
-    struct userdata *userdata;
-    enum server_type type;
-    DBusServer *dbus_server;
-};
-
-struct connection {
-    struct server *server;
-    pa_dbus_wrap_connection *wrap_conn;
-    pa_client *client;
-};
-
-static const char* const valid_modargs[] = {
-    "access",
-    "tcp_port",
-    NULL
-};
-
-static void connection_free(struct connection *c) {
-    pa_assert(c);
-
-    pa_assert_se(pa_dbus_unregister_connection(c->server->userdata->module->core, pa_dbus_wrap_connection_get(c->wrap_conn)) >= 0);
-
-    pa_client_free(c->client);
-    pa_assert_se(pa_idxset_remove_by_data(c->server->userdata->connections, c, NULL));
-    pa_dbus_wrap_connection_free(c->wrap_conn);
-    pa_xfree(c);
-}
-
-/* Called from pa_client_kill(). */
-static void client_kill_cb(pa_client *c) {
-    struct connection *conn;
-
-    pa_assert(c);
-    pa_assert(c->userdata);
-
-    conn = c->userdata;
-    connection_free(conn);
-
-    pa_log_info("Connection killed.");
-}
-
-static dbus_bool_t user_check_cb(DBusConnection *connection, unsigned long uid, void *data) {
-    pa_log_debug("Allowing connection by user %lu.", uid);
-
-    return TRUE;
-}
-
-/* Called by D-Bus when a new client connection is received. */
-static void connection_new_cb(DBusServer *dbus_server, DBusConnection *new_connection, void *data) {
-    struct server *s = data;
-    struct connection *c;
-    pa_client_new_data new_data;
-    pa_client *client;
-
-    pa_assert(new_connection);
-    pa_assert(s);
-
-    pa_client_new_data_init(&new_data);
-    new_data.module = s->userdata->module;
-    new_data.driver = __FILE__;
-    pa_proplist_sets(new_data.proplist, PA_PROP_APPLICATION_NAME, "D-Bus client"); /* TODO: It's probably possible to generate a fancier name. Other props? */
-    client = pa_client_new(s->userdata->module->core, &new_data);
-    pa_client_new_data_done(&new_data);
-
-    if (!client) {
-        dbus_connection_close(new_connection);
-        return;
-    }
-
-    if (s->type == SERVER_TYPE_TCP || s->userdata->module->core->server_type == PA_SERVER_TYPE_SYSTEM) {
-        /* FIXME: Here we allow anyone from anywhere to access the server,
-         * anonymously. Access control should be configurable. */
-        dbus_connection_set_unix_user_function(new_connection, user_check_cb, NULL, NULL);
-        dbus_connection_set_allow_anonymous(new_connection, TRUE);
-    }
-
-    c = pa_xnew(struct connection, 1);
-    c->server = s;
-    c->wrap_conn = pa_dbus_wrap_connection_new_from_existing(s->userdata->module->core->mainloop, TRUE, new_connection);
-    c->client = client;
-
-    c->client->kill = client_kill_cb;
-    c->client->send_event = NULL; /* TODO: Implement this. */
-    c->client->userdata = c;
-
-    pa_idxset_put(s->userdata->connections, c, NULL);
-
-    pa_assert_se(pa_dbus_register_connection(s->userdata->module->core, new_connection) >= 0);
-}
-
-/* Called by PA mainloop when a D-Bus fd watch event needs handling. */
-static void io_event_cb(pa_mainloop_api *mainloop, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
-    unsigned int flags = 0;
-    DBusWatch *watch = userdata;
-
-#if HAVE_DBUS_WATCH_GET_UNIX_FD
-    pa_assert(fd == dbus_watch_get_unix_fd(watch));
-#else
-    pa_assert(fd == dbus_watch_get_fd(watch));
-#endif
-
-    if (!dbus_watch_get_enabled(watch)) {
-        pa_log_warn("Asked to handle disabled watch: %p %i", (void*) watch, fd);
-        return;
-    }
-
-    if (events & PA_IO_EVENT_INPUT)
-        flags |= DBUS_WATCH_READABLE;
-    if (events & PA_IO_EVENT_OUTPUT)
-        flags |= DBUS_WATCH_WRITABLE;
-    if (events & PA_IO_EVENT_HANGUP)
-        flags |= DBUS_WATCH_HANGUP;
-    if (events & PA_IO_EVENT_ERROR)
-        flags |= DBUS_WATCH_ERROR;
-
-    dbus_watch_handle(watch, flags);
-}
-
-/* Called by PA mainloop when a D-Bus timer event needs handling. */
-static void time_event_cb(pa_mainloop_api *mainloop, pa_time_event* e, const struct timeval *tv, void *userdata) {
-    DBusTimeout *timeout = userdata;
-
-    if (dbus_timeout_get_enabled(timeout)) {
-        struct timeval next = *tv;
-        dbus_timeout_handle(timeout);
-
-        /* restart it for the next scheduled time */
-        pa_timeval_add(&next, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
-        mainloop->time_restart(e, &next);
-    }
-}
-
-/* Translates D-Bus fd watch event flags to PA IO event flags. */
-static pa_io_event_flags_t get_watch_flags(DBusWatch *watch) {
-    unsigned int flags;
-    pa_io_event_flags_t events = 0;
-
-    pa_assert(watch);
-
-    flags = dbus_watch_get_flags(watch);
-
-    /* no watch flags for disabled watches */
-    if (!dbus_watch_get_enabled(watch))
-        return PA_IO_EVENT_NULL;
-
-    if (flags & DBUS_WATCH_READABLE)
-        events |= PA_IO_EVENT_INPUT;
-    if (flags & DBUS_WATCH_WRITABLE)
-        events |= PA_IO_EVENT_OUTPUT;
-
-    return events | PA_IO_EVENT_HANGUP | PA_IO_EVENT_ERROR;
-}
-
-/* Called by D-Bus when a D-Bus fd watch event is added. */
-static dbus_bool_t watch_add_cb(DBusWatch *watch, void *data) {
-    struct server *s = data;
-    pa_mainloop_api *mainloop;
-    pa_io_event *ev;
-
-    pa_assert(watch);
-    pa_assert(s);
-
-    mainloop = s->userdata->module->core->mainloop;
-
-    ev = mainloop->io_new(
-            mainloop,
-#if HAVE_DBUS_WATCH_GET_UNIX_FD
-            dbus_watch_get_unix_fd(watch),
-#else
-            dbus_watch_get_fd(watch),
-#endif
-            get_watch_flags(watch), io_event_cb, watch);
-
-    dbus_watch_set_data(watch, ev, NULL);
-
-    return TRUE;
-}
-
-/* Called by D-Bus when a D-Bus fd watch event is removed. */
-static void watch_remove_cb(DBusWatch *watch, void *data) {
-    struct server *s = data;
-    pa_io_event *ev;
-
-    pa_assert(watch);
-    pa_assert(s);
-
-    if ((ev = dbus_watch_get_data(watch)))
-        s->userdata->module->core->mainloop->io_free(ev);
-}
-
-/* Called by D-Bus when a D-Bus fd watch event is toggled. */
-static void watch_toggled_cb(DBusWatch *watch, void *data) {
-    struct server *s = data;
-    pa_io_event *ev;
-
-    pa_assert(watch);
-    pa_assert(s);
-
-    pa_assert_se(ev = dbus_watch_get_data(watch));
-
-    /* get_watch_flags() checks if the watch is enabled */
-    s->userdata->module->core->mainloop->io_enable(ev, get_watch_flags(watch));
-}
-
-/* Called by D-Bus when a D-Bus timer event is added. */
-static dbus_bool_t timeout_add_cb(DBusTimeout *timeout, void *data) {
-    struct server *s = data;
-    pa_mainloop_api *mainloop;
-    pa_time_event *ev;
-    struct timeval tv;
-
-    pa_assert(timeout);
-    pa_assert(s);
-
-    if (!dbus_timeout_get_enabled(timeout))
-        return FALSE;
-
-    mainloop = s->userdata->module->core->mainloop;
-
-    pa_gettimeofday(&tv);
-    pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
-
-    ev = mainloop->time_new(mainloop, &tv, time_event_cb, timeout);
-
-    dbus_timeout_set_data(timeout, ev, NULL);
-
-    return TRUE;
-}
-
-/* Called by D-Bus when a D-Bus timer event is removed. */
-static void timeout_remove_cb(DBusTimeout *timeout, void *data) {
-    struct server *s = data;
-    pa_time_event *ev;
-
-    pa_assert(timeout);
-    pa_assert(s);
-
-    if ((ev = dbus_timeout_get_data(timeout)))
-        s->userdata->module->core->mainloop->time_free(ev);
-}
-
-/* Called by D-Bus when a D-Bus timer event is toggled. */
-static void timeout_toggled_cb(DBusTimeout *timeout, void *data) {
-    struct server *s = data;
-    pa_mainloop_api *mainloop;
-    pa_time_event *ev;
-
-    pa_assert(timeout);
-    pa_assert(s);
-
-    mainloop = s->userdata->module->core->mainloop;
-
-    pa_assert_se(ev = dbus_timeout_get_data(timeout));
-
-    if (dbus_timeout_get_enabled(timeout)) {
-        struct timeval tv;
-
-        pa_gettimeofday(&tv);
-        pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
-
-        mainloop->time_restart(ev, &tv);
-    } else
-        mainloop->time_restart(ev, NULL);
-}
-
-static void server_free(struct server *s) {
-    pa_assert(s);
-
-    if (s->dbus_server) {
-        dbus_server_disconnect(s->dbus_server);
-        dbus_server_unref(s->dbus_server);
-    }
-
-    pa_xfree(s);
-}
-
-static struct server *start_server(struct userdata *u, const char *address, enum server_type type) {
-    /* XXX: We assume that when we unref the DBusServer instance at module
-     * shutdown, nobody else holds any references to it. If we stop assuming
-     * that someday, dbus_server_set_new_connection_function,
-     * dbus_server_set_watch_functions and dbus_server_set_timeout_functions
-     * calls should probably register free callbacks, instead of providing NULL
-     * as they do now. */
-
-    struct server *s = NULL;
-    DBusError error;
-
-    pa_assert(u);
-    pa_assert(address);
-
-    dbus_error_init(&error);
-
-    s = pa_xnew0(struct server, 1);
-    s->userdata = u;
-    s->dbus_server = dbus_server_listen(address, &error);
-
-    if (dbus_error_is_set(&error)) {
-        pa_log("dbus_server_listen() failed: %s: %s", error.name, error.message);
-        goto fail;
-    }
-
-    dbus_server_set_new_connection_function(s->dbus_server, connection_new_cb, s, NULL);
-
-    if (!dbus_server_set_watch_functions(s->dbus_server, watch_add_cb, watch_remove_cb, watch_toggled_cb, s, NULL)) {
-        pa_log("dbus_server_set_watch_functions() ran out of memory.");
-        goto fail;
-    }
-
-    if (!dbus_server_set_timeout_functions(s->dbus_server, timeout_add_cb, timeout_remove_cb, timeout_toggled_cb, s, NULL)) {
-        pa_log("dbus_server_set_timeout_functions() ran out of memory.");
-        goto fail;
-    }
-
-    return s;
-
-fail:
-    if (s)
-        server_free(s);
-
-    dbus_error_free(&error);
-
-    return NULL;
-}
-
-static struct server *start_local_server(struct userdata *u) {
-    struct server *s = NULL;
-    char *address = NULL;
-
-    pa_assert(u);
-
-    address = pa_get_dbus_address_from_server_type(u->module->core->server_type);
-
-    s = start_server(u, address, SERVER_TYPE_LOCAL); /* May return NULL */
-
-    pa_xfree(address);
-
-    return s;
-}
-
-static struct server *start_tcp_server(struct userdata *u) {
-    struct server *s = NULL;
-    char *address = NULL;
-
-    pa_assert(u);
-
-    address = pa_sprintf_malloc("tcp:host=127.0.0.1,port=%u", u->tcp_port);
-
-    s = start_server(u, address, SERVER_TYPE_TCP); /* May return NULL */
-
-    pa_xfree(address);
-
-    return s;
-}
-
-static int get_access_arg(pa_modargs *ma, pa_bool_t *local_access, pa_bool_t *remote_access) {
-    const char *value = NULL;
-
-    pa_assert(ma);
-    pa_assert(local_access);
-    pa_assert(remote_access);
-
-    if (!(value = pa_modargs_get_value(ma, "access", NULL)))
-        return 0;
-
-    if (!strcmp(value, "local")) {
-        *local_access = TRUE;
-        *remote_access = FALSE;
-    } else if (!strcmp(value, "remote")) {
-        *local_access = FALSE;
-        *remote_access = TRUE;
-    } else if (!strcmp(value, "local,remote")) {
-        *local_access = TRUE;
-        *remote_access = TRUE;
-    } else
-        return -1;
-
-    return 0;
-}
-
-/* Frees dead client connections. Called every CLEANUP_INTERVAL seconds. */
-static void cleanup_cb(pa_mainloop_api *a, pa_time_event *e, const struct timeval *tv, void *userdata) {
-    struct userdata *u = userdata;
-    struct connection *conn = NULL;
-    uint32_t idx;
-    struct timeval cleanup_timeval;
-    unsigned free_count = 0;
-
-    for (conn = pa_idxset_first(u->connections, &idx); conn; conn = pa_idxset_next(u->connections, &idx)) {
-        if (!dbus_connection_get_is_connected(pa_dbus_wrap_connection_get(conn->wrap_conn))) {
-            connection_free(conn);
-            ++free_count;
-        }
-    }
-
-    if (free_count > 0)
-        pa_log_debug("Freed %u dead D-Bus client connections.", free_count);
-
-    pa_gettimeofday(&cleanup_timeval);
-    cleanup_timeval.tv_sec += CLEANUP_INTERVAL;
-    u->module->core->mainloop->time_restart(e, &cleanup_timeval);
-}
-
-int pa__init(pa_module *m) {
-    struct userdata *u = NULL;
-    pa_modargs *ma = NULL;
-    struct timeval cleanup_timeval;
-
-    pa_assert(m);
-
-    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
-        pa_log("Failed to parse module arguments.");
-        goto fail;
-    }
-
-    m->userdata = u = pa_xnew0(struct userdata, 1);
-    u->module = m;
-    u->local_access = TRUE;
-    u->remote_access = FALSE;
-    u->tcp_port = PA_DBUS_DEFAULT_PORT;
-
-    if (get_access_arg(ma, &u->local_access, &u->remote_access) < 0) {
-        pa_log("Invalid access argument: '%s'", pa_modargs_get_value(ma, "access", NULL));
-        goto fail;
-    }
-
-    if (pa_modargs_get_value_u32(ma, "tcp_port", &u->tcp_port) < 0 || u->tcp_port < 1 || u->tcp_port > 49150) {
-        pa_log("Invalid tcp_port argument: '%s'", pa_modargs_get_value(ma, "tcp_port", NULL));
-        goto fail;
-    }
-
-    if (u->local_access && !(u->local_server = start_local_server(u))) {
-        pa_log("Starting the local D-Bus server failed.");
-        goto fail;
-    }
-
-    if (u->remote_access && !(u->tcp_server = start_tcp_server(u))) {
-        pa_log("Starting the D-Bus server for remote connections failed.");
-        goto fail;
-    }
-
-    u->connections = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
-
-    pa_gettimeofday(&cleanup_timeval);
-    cleanup_timeval.tv_sec += CLEANUP_INTERVAL;
-    u->cleanup_event = m->core->mainloop->time_new(m->core->mainloop, &cleanup_timeval, cleanup_cb, u);
-
-    u->core_object = pa_dbusobj_core_new(m->core);
-
-    return 0;
-
-fail:
-    if (ma)
-        pa_modargs_free(ma);
-
-    pa__done(m);
-
-    return -1;
-}
-
-/* Called by idxset when the connection set is freed. */
-static void connection_free_cb(void *p, void *userdata) {
-    struct connection *conn = p;
-
-    pa_assert(conn);
-
-    connection_free(conn);
-}
-
-void pa__done(pa_module *m) {
-    struct userdata *u;
-
-    pa_assert(m);
-
-    if (!(u = m->userdata))
-        return;
-
-    if (u->core_object)
-        pa_dbusobj_core_free(u->core_object);
-
-    if (u->cleanup_event)
-        m->core->mainloop->time_free(u->cleanup_event);
-
-    if (u->connections)
-        pa_idxset_free(u->connections, connection_free_cb, NULL);
-
-    if (u->tcp_server)
-        server_free(u->tcp_server);
-
-    if (u->local_server)
-        server_free(u->local_server);
-
-    pa_xfree(u);
-    m->userdata = NULL;
-}
diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
index 04e7eb2..ebddf36 100644
--- a/src/pulsecore/core-util.c
+++ b/src/pulsecore/core-util.c
@@ -2656,6 +2656,28 @@ char *pa_replace(const char*s, const char*a, const char *b) {
     return pa_strbuf_tostring_free(sb);
 }
 
+char *pa_escape(const char *p, const char *chars) {
+    const char *s;
+    const char *c;
+    pa_strbuf *buf = pa_strbuf_new();
+
+    for (s = p; *s; ++s) {
+        if (*s == '\\')
+            pa_strbuf_putc(buf, '\\');
+        else if (chars) {
+            for (c = chars; *c; ++c) {
+                if (*s == *c) {
+                    pa_strbuf_putc(buf, '\\');
+                    break;
+                }
+            }
+        }
+        pa_strbuf_putc(buf, *s);
+    }
+
+    return pa_strbuf_tostring_free(buf);
+}
+
 char *pa_unescape(char *p) {
     char *s, *d;
     pa_bool_t escaped = FALSE;
diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h
index 96a0480..aaa51bd 100644
--- a/src/pulsecore/core-util.h
+++ b/src/pulsecore/core-util.h
@@ -220,6 +220,13 @@ unsigned pa_ncpus(void);
 
 char *pa_replace(const char*s, const char*a, const char *b);
 
+/* Escapes p by inserting backslashes in front of backslashes. chars is a
+ * regular (ie. NULL-terminated) string containing additional characters that
+ * should be escaped. chars can be NULL. The caller has to free the returned
+ * string. */
+char *pa_escape(const char *p, const char *chars);
+
+/* Does regular backslash unescaping. Returns the argument p. */
 char *pa_unescape(char *p);
 
 char *pa_realpath(const char *path);
diff --git a/src/pulsecore/dbus-common.c b/src/pulsecore/dbus-common.c
deleted file mode 100644
index 6562cda..0000000
--- a/src/pulsecore/dbus-common.c
+++ /dev/null
@@ -1,646 +0,0 @@
-/***
-  This file is part of PulseAudio.
-
-  Copyright 2009 Tanu Kaskinen
-
-  PulseAudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2.1 of the License,
-  or (at your option) any later version.
-
-  PulseAudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with PulseAudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <dbus/dbus.h>
-
-#include <pulse/xmalloc.h>
-
-#include <pulsecore/core-util.h>
-#include <pulsecore/hashmap.h>
-#include <pulsecore/idxset.h>
-#include <pulsecore/strbuf.h>
-
-#include "dbus-common.h"
-
-struct dbus_state {
-    pa_core *core;
-    pa_hashmap *objects; /* Object path -> struct object_entry */
-    pa_idxset *connections; /* DBusConnections */
-};
-
-struct object_entry {
-    char *path;
-    pa_hashmap *interfaces; /* Interface name -> struct interface_entry */
-    char *introspection;
-};
-
-struct interface_entry {
-    char *name;
-    char **properties;
-    char **methods;
-    char *introspection_snippet;
-    DBusObjectPathMessageFunction receive;
-    void *userdata;
-};
-
-char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type) {
-    char *address = NULL;
-    char *runtime_path = NULL;
-    char *escaped_path = NULL;
-
-    switch (server_type) {
-        case PA_SERVER_TYPE_USER:
-            if (!(runtime_path = pa_runtime_path(PA_DBUS_SOCKET_NAME))) {
-                pa_log("pa_runtime_path() failed.");
-                break;
-            }
-
-            if (!(escaped_path = dbus_address_escape_value(runtime_path))) {
-                pa_log("dbus_address_escape_value() failed.");
-                break;
-            }
-
-            address = pa_sprintf_malloc("unix:path=%s", escaped_path);
-            break;
-
-        case PA_SERVER_TYPE_SYSTEM:
-            if (!(escaped_path = dbus_address_escape_value(PA_DBUS_SYSTEM_SOCKET_PATH))) {
-                pa_log("dbus_address_escape_value() failed.");
-                break;
-            }
-
-            address = pa_sprintf_malloc("unix:path=%s", escaped_path);
-            break;
-
-        case PA_SERVER_TYPE_NONE:
-            address = pa_xnew0(char, 1);
-            break;
-
-        default:
-            pa_assert_not_reached();
-    }
-
-    pa_xfree(runtime_path);
-    pa_xfree(escaped_path);
-
-    return address;
-}
-
-static void update_introspection(struct object_entry *oe) {
-    pa_strbuf *buf;
-    void *state = NULL;
-    struct interface_entry *iface_entry = NULL;
-
-    pa_assert(oe);
-
-    buf = pa_strbuf_new();
-    pa_strbuf_puts(buf, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE);
-    pa_strbuf_puts(buf, "<node>\n");
-
-    while ((iface_entry = pa_hashmap_iterate(oe->interfaces, &state, NULL)))
-        pa_strbuf_puts(buf, iface_entry->introspection_snippet);
-
-    pa_strbuf_puts(buf, " <interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">\n"
-                        "  <method name=\"Introspect\">\n"
-                        "   <arg name=\"data\" type=\"s\" direction=\"out\"/>\n"
-                        "  </method>\n"
-                        " </interface>\n"
-                        " <interface name=\"" DBUS_INTERFACE_PROPERTIES "\">\n"
-                        "  <method name=\"Get\">\n"
-                        "   <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
-                        "   <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
-                        "   <arg name=\"value\" type=\"v\" direction=\"out\"/>\n"
-                        "  </method>\n"
-                        "  <method name=\"Set\">\n"
-                        "   <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
-                        "   <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
-                        "   <arg name=\"value\" type=\"v\" direction=\"in\"/>\n"
-                        "  </method>\n"
-                        "  <method name=\"GetAll\">\n"
-                        "   <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
-                        "   <arg name=\"props\" type=\"a{sv}\" direction=\"out\"/>\n"
-                        "  </method>\n"
-                        " </interface>\n");
-
-    pa_strbuf_puts(buf, "</node>\n");
-
-    pa_xfree(oe->introspection);
-    oe->introspection = pa_strbuf_tostring_free(buf);
-}
-
-enum find_result_t {
-    SUCCESS,
-    NO_SUCH_PROPERTY,
-    NO_SUCH_METHOD,
-    INVALID_MESSAGE_ARGUMENTS
-};
-
-static enum find_result_t find_interface_by_property(struct object_entry *obj_entry, const char *property, struct interface_entry **entry) {
-    void *state = NULL;
-
-    pa_assert(obj_entry);
-    pa_assert(property);
-    pa_assert(entry);
-
-    while ((*entry = pa_hashmap_iterate(obj_entry->interfaces, &state, NULL))) {
-        char *iface_property;
-        char **pos = (*entry)->properties;
-
-        while ((iface_property = *pos++)) {
-            if (pa_streq(iface_property, property))
-                return SUCCESS;
-        }
-    }
-
-    return NO_SUCH_PROPERTY;
-}
-
-static enum find_result_t find_interface_by_method(struct object_entry *obj_entry, const char *method, struct interface_entry **entry) {
-    void *state = NULL;
-
-    pa_assert(obj_entry);
-    pa_assert(method);
-    pa_assert(entry);
-
-    while ((*entry = pa_hashmap_iterate(obj_entry->interfaces, &state, NULL))) {
-        char *iface_method;
-        char **pos = (*entry)->methods;
-
-        while ((iface_method = *pos++)) {
-            if (pa_streq(iface_method, method))
-                return SUCCESS;
-        }
-    }
-
-    return NO_SUCH_METHOD;
-}
-
-static enum find_result_t find_interface_from_properties_call(struct object_entry *obj_entry, DBusMessage *msg, struct interface_entry **entry) {
-    const char *interface;
-    const char *property;
-
-    pa_assert(obj_entry);
-    pa_assert(msg);
-    pa_assert(entry);
-
-    if (dbus_message_has_member(msg, "GetAll")) {
-        if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID))
-            return INVALID_MESSAGE_ARGUMENTS;
-
-        if (*interface) {
-            if ((*entry = pa_hashmap_get(obj_entry->interfaces, interface)))
-                return SUCCESS;
-            else
-                return NO_SUCH_METHOD;
-        } else {
-            pa_assert_se((*entry = pa_hashmap_first(obj_entry->interfaces)));
-            return SUCCESS;
-        }
-    } else {
-        pa_assert(dbus_message_has_member(msg, "Get") || dbus_message_has_member(msg, "Set"));
-
-        if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID))
-            return INVALID_MESSAGE_ARGUMENTS;
-
-        if (*interface) {
-            if ((*entry = pa_hashmap_get(obj_entry->interfaces, interface)))
-                return SUCCESS;
-            else
-                return NO_SUCH_METHOD;
-        } else
-            return find_interface_by_property(obj_entry, property, entry);
-    }
-}
-
-static enum find_result_t find_interface(struct object_entry *obj_entry, DBusMessage *msg, struct interface_entry **entry) {
-    const char *interface;
-
-    pa_assert(obj_entry);
-    pa_assert(msg);
-    pa_assert(entry);
-
-    *entry = NULL;
-
-    if (dbus_message_has_interface(msg, DBUS_INTERFACE_PROPERTIES))
-        return find_interface_from_properties_call(obj_entry, msg, entry);
-
-    else if ((interface = dbus_message_get_interface(msg))) {
-        if ((*entry = pa_hashmap_get(obj_entry->interfaces, interface)))
-            return SUCCESS;
-        else
-            return NO_SUCH_METHOD;
-
-    } else { /* The method call doesn't contain an interface. */
-        if (dbus_message_has_member(msg, "Get") || dbus_message_has_member(msg, "Set") || dbus_message_has_member(msg, "GetAll")) {
-            if (find_interface_by_method(obj_entry, dbus_message_get_member(msg), entry) == SUCCESS)
-                return SUCCESS; /* The object has a method named Get, Set or GetAll in some other interface than .Properties. */
-            else
-                /* Assume this is a .Properties call. */
-                return find_interface_from_properties_call(obj_entry, msg, entry);
-
-        } else /* This is not a .Properties call. */
-            return find_interface_by_method(obj_entry, dbus_message_get_member(msg), entry);
-    }
-}
-
-static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessage *message, void *user_data) {
-    struct dbus_state *dbus_state = user_data;
-    struct object_entry *obj_entry;
-    struct interface_entry *iface_entry;
-    DBusMessage *reply = NULL;
-
-    pa_assert(connection);
-    pa_assert(message);
-    pa_assert(dbus_state);
-    pa_assert(dbus_state->objects);
-
-    if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
-        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-
-    pa_assert_se((obj_entry = pa_hashmap_get(dbus_state->objects, dbus_message_get_path(message))));
-
-    if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect") ||
-        (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Introspect"))) {
-        if (!(reply = dbus_message_new_method_return(message)))
-            goto oom;
-
-        if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &obj_entry->introspection, DBUS_TYPE_INVALID))
-            goto fail;
-
-        if (!dbus_connection_send(connection, reply, NULL))
-            goto oom;
-
-        pa_log_debug("%s.%s handled.", obj_entry->path, "Introspect");
-
-        dbus_message_unref(reply);
-
-        return DBUS_HANDLER_RESULT_HANDLED;
-    }
-
-    switch (find_interface(obj_entry, message, &iface_entry)) {
-        case SUCCESS:
-            return iface_entry->receive(connection, message, iface_entry->userdata);
-
-        case NO_SUCH_PROPERTY:
-            if (!(reply = dbus_message_new_error(message, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "No such property")))
-                goto fail;
-
-            if (!dbus_connection_send(connection, reply, NULL))
-                goto oom;
-
-            dbus_message_unref(reply);
-
-            return DBUS_HANDLER_RESULT_HANDLED;
-
-        case NO_SUCH_METHOD:
-            return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-
-        case INVALID_MESSAGE_ARGUMENTS:
-            if (!(reply = dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, "Invalid arguments")))
-                goto fail;
-
-            if (!dbus_connection_send(connection, reply, NULL))
-                goto oom;
-
-            dbus_message_unref(reply);
-
-            return DBUS_HANDLER_RESULT_HANDLED;
-
-        default:
-            pa_assert_not_reached();
-    }
-
-fail:
-    if (reply)
-        dbus_message_unref(reply);
-
-    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-
-oom:
-    if (reply)
-        dbus_message_unref(reply);
-
-    return DBUS_HANDLER_RESULT_NEED_MEMORY;
-}
-
-static DBusObjectPathVTable vtable = {
-    .unregister_function = NULL,
-    .message_function = handle_message_cb,
-    .dbus_internal_pad1 = NULL,
-    .dbus_internal_pad2 = NULL,
-    .dbus_internal_pad3 = NULL,
-    .dbus_internal_pad4 = NULL
-};
-
-static void register_object(struct dbus_state *dbus_state, struct object_entry *obj_entry) {
-    DBusConnection *conn;
-    void *state = NULL;
-
-    pa_assert(dbus_state);
-    pa_assert(obj_entry);
-
-    if (!dbus_state->connections)
-        return;
-
-    while ((conn = pa_idxset_iterate(dbus_state->connections, &state, NULL))) {
-        if (!dbus_connection_register_object_path(conn, obj_entry->path, &vtable, dbus_state))
-            pa_log_debug("dbus_connection_register_object_path() failed.");
-    }
-}
-
-static char **copy_strarray(const char * const *array) {
-    unsigned n = 0;
-    char **copy;
-    unsigned i;
-
-    while (array[n++])
-        ;
-
-    copy = pa_xnew0(char *, n);
-
-    for (i = 0; i < n - 1; ++i)
-        copy[i] = pa_xstrdup(array[i]);
-
-    return copy;
-}
-
-int pa_dbus_add_interface(pa_core *c,
-                          const char* path,
-                          const char* interface,
-                          const char * const *properties,
-                          const char * const *methods,
-                          const char* introspection_snippet,
-                          DBusObjectPathMessageFunction receive_cb,
-                          void *userdata) {
-    struct dbus_state *dbus_state;
-    pa_hashmap *objects;
-    struct object_entry *obj_entry;
-    struct interface_entry *iface_entry;
-    pa_bool_t state_created = FALSE;
-    pa_bool_t object_map_created = FALSE;
-    pa_bool_t obj_entry_created = FALSE;
-
-    pa_assert(c);
-    pa_assert(path);
-    pa_assert(introspection_snippet);
-    pa_assert(receive_cb);
-
-    if (!(dbus_state = pa_hashmap_get(c->shared, "dbus-state"))) {
-        dbus_state = pa_xnew0(struct dbus_state, 1);
-        dbus_state->core = c;
-        pa_hashmap_put(c->shared, "dbus-state", dbus_state);
-        state_created = TRUE;
-    }
-
-    if (!(objects = dbus_state->objects)) {
-        objects = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
-        dbus_state->objects = objects;
-        object_map_created = TRUE;
-    }
-
-    if (!(obj_entry = pa_hashmap_get(objects, path))) {
-        obj_entry = pa_xnew(struct object_entry, 1);
-        obj_entry->path = pa_xstrdup(path);
-        obj_entry->interfaces = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
-        obj_entry->introspection = NULL;
-        pa_hashmap_put(objects, path, obj_entry);
-        obj_entry_created = TRUE;
-    }
-
-    if (pa_hashmap_get(obj_entry->interfaces, interface) != NULL)
-        goto fail; /* The interface was already registered. */
-
-    iface_entry = pa_xnew(struct interface_entry, 1);
-    iface_entry->name = pa_xstrdup(interface);
-    iface_entry->properties = copy_strarray(properties);
-    iface_entry->methods = copy_strarray(methods);
-    iface_entry->introspection_snippet = pa_xstrdup(introspection_snippet);
-    iface_entry->receive = receive_cb;
-    iface_entry->userdata = userdata;
-    pa_hashmap_put(obj_entry->interfaces, iface_entry->name, iface_entry);
-
-    update_introspection(obj_entry);
-
-    if (obj_entry_created)
-        register_object(dbus_state, obj_entry);
-
-    return 0;
-
-fail:
-    if (obj_entry_created) {
-        pa_hashmap_remove(objects, path);
-        pa_xfree(obj_entry);
-    }
-
-    if (object_map_created) {
-        dbus_state->objects = NULL;
-        pa_hashmap_free(objects, NULL, NULL);
-    }
-
-    if (state_created) {
-        pa_hashmap_remove(c->shared, "dbus-state");
-        pa_xfree(dbus_state);
-    }
-
-    return -1;
-}
-
-static void unregister_object(struct dbus_state *dbus_state, struct object_entry *obj_entry) {
-    DBusConnection *conn;
-    void *state = NULL;
-
-    pa_assert(dbus_state);
-    pa_assert(obj_entry);
-
-    if (!dbus_state->connections)
-        return;
-
-    while ((conn = pa_idxset_iterate(dbus_state->connections, &state, NULL))) {
-        if (!dbus_connection_unregister_object_path(conn, obj_entry->path))
-            pa_log_debug("dbus_connection_unregister_object_path() failed.");
-    }
-}
-
-static void free_strarray(char **array) {
-    char **pos = array;
-
-    while (*pos++)
-        pa_xfree(*pos);
-
-    pa_xfree(array);
-}
-
-int pa_dbus_remove_interface(pa_core *c, const char* path, const char* interface) {
-    struct dbus_state *dbus_state;
-    pa_hashmap *objects;
-    struct object_entry *obj_entry;
-    struct interface_entry *iface_entry;
-
-    pa_assert(c);
-    pa_assert(path);
-    pa_assert(interface);
-
-    if (!(dbus_state = pa_hashmap_get(c->shared, "dbus-state")))
-        return -1;
-
-    if (!(objects = dbus_state->objects))
-        return -1;
-
-    if (!(obj_entry = pa_hashmap_get(objects, path)))
-        return -1;
-
-    if (!(iface_entry = pa_hashmap_remove(obj_entry->interfaces, interface)))
-        return -1;
-
-    update_introspection(obj_entry);
-
-    pa_xfree(iface_entry->name);
-    free_strarray(iface_entry->properties);
-    free_strarray(iface_entry->methods);
-    pa_xfree(iface_entry->introspection_snippet);
-    pa_xfree(iface_entry);
-
-    if (pa_hashmap_isempty(obj_entry->interfaces)) {
-        unregister_object(dbus_state, obj_entry);
-
-        pa_hashmap_remove(objects, path);
-        pa_xfree(obj_entry->path);
-        pa_hashmap_free(obj_entry->interfaces, NULL, NULL);
-        pa_xfree(obj_entry->introspection);
-        pa_xfree(obj_entry);
-    }
-
-    if (pa_hashmap_isempty(objects)) {
-        dbus_state->objects = NULL;
-        pa_hashmap_free(objects, NULL, NULL);
-    }
-
-    if (!dbus_state->objects && !dbus_state->connections) {
-        pa_hashmap_remove(c->shared, "dbus-state");
-        pa_xfree(dbus_state);
-    }
-
-    return 0;
-}
-
-static void register_all_objects(struct dbus_state *dbus_state, DBusConnection *conn) {
-    struct object_entry *obj_entry;
-    void *state = NULL;
-
-    pa_assert(dbus_state);
-    pa_assert(conn);
-
-    if (!dbus_state->objects)
-        return;
-
-    while ((obj_entry = pa_hashmap_iterate(dbus_state->objects, &state, NULL))) {
-        if (!dbus_connection_register_object_path(conn, obj_entry->path, &vtable, dbus_state))
-            pa_log_debug("dbus_connection_register_object_path() failed.");
-    }
-}
-
-int pa_dbus_register_connection(pa_core *c, DBusConnection *conn) {
-    struct dbus_state *dbus_state;
-    pa_idxset *connections;
-    pa_bool_t state_created = FALSE;
-    pa_bool_t connection_set_created = FALSE;
-
-    pa_assert(c);
-    pa_assert(conn);
-
-    if (!(dbus_state = pa_hashmap_get(c->shared, "dbus-state"))) {
-        dbus_state = pa_xnew0(struct dbus_state, 1);
-        dbus_state->core = c;
-        pa_hashmap_put(c->shared, "dbus-state", dbus_state);
-        state_created = TRUE;
-    }
-
-    if (!(connections = dbus_state->connections)) {
-        connections = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
-        dbus_state->connections = connections;
-        connection_set_created = TRUE;
-    }
-
-    if (pa_idxset_get_by_data(connections, conn, NULL))
-        goto fail; /* The connection was already registered. */
-
-    register_all_objects(dbus_state, conn);
-
-    pa_idxset_put(connections, dbus_connection_ref(conn), NULL);
-
-    return 0;
-
-fail:
-    if (connection_set_created) {
-        dbus_state->connections = NULL;
-        pa_idxset_free(connections, NULL, NULL);
-    }
-
-    if (state_created) {
-        pa_hashmap_remove(c->shared, "dbus-state");
-        pa_xfree(dbus_state);
-    }
-
-    return -1;
-}
-
-static void unregister_all_objects(struct dbus_state *dbus_state, DBusConnection *conn) {
-    struct object_entry *obj_entry;
-    void *state = NULL;
-
-    pa_assert(dbus_state);
-    pa_assert(conn);
-
-    if (!dbus_state->objects)
-        return;
-
-    while ((obj_entry = pa_hashmap_iterate(dbus_state->objects, &state, NULL))) {
-        if (!dbus_connection_unregister_object_path(conn, obj_entry->path))
-            pa_log_debug("dus_connection_unregister_object_path() failed.");
-    }
-}
-
-int pa_dbus_unregister_connection(pa_core *c, DBusConnection *conn) {
-    struct dbus_state *dbus_state;
-    pa_idxset *connections;
-
-    pa_assert(c);
-    pa_assert(conn);
-
-    if (!(dbus_state = pa_hashmap_get(c->shared, "dbus-state")))
-        return -1;
-
-    if (!(connections = dbus_state->connections))
-        return -1;
-
-    if (!pa_idxset_remove_by_data(connections, conn, NULL))
-        return -1;
-
-    unregister_all_objects(dbus_state, conn);
-
-    dbus_connection_unref(conn);
-
-    if (pa_idxset_isempty(connections)) {
-        dbus_state->connections = NULL;
-        pa_idxset_free(connections, NULL, NULL);
-    }
-
-    if (!dbus_state->objects && !dbus_state->connections) {
-        pa_hashmap_remove(c->shared, "dbus-state");
-        pa_xfree(dbus_state);
-    }
-
-    return 0;
-}
diff --git a/src/pulsecore/dbus-common.h b/src/pulsecore/dbus-common.h
deleted file mode 100644
index 4354c4e..0000000
--- a/src/pulsecore/dbus-common.h
+++ /dev/null
@@ -1,85 +0,0 @@
-#ifndef foodbuscommonhfoo
-#define foodbuscommonhfoo
-
-/***
-  This file is part of PulseAudio.
-
-  Copyright 2009 Tanu Kaskinen
-
-  PulseAudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2.1 of the License,
-  or (at your option) any later version.
-
-  PulseAudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with PulseAudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#include <dbus/dbus.h>
-
-#include <pulsecore/core.h>
-#include <pulsecore/macro.h>
-
-#define PA_DBUS_DEFAULT_PORT 24883
-#define PA_DBUS_SOCKET_NAME "dbus-socket"
-
-#define PA_DBUS_SYSTEM_SOCKET_PATH PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_DBUS_SOCKET_NAME
-
-#define PA_DBUS_ERROR_NO_SUCH_PROPERTY "org.PulseAudio.Core1.NoSuchPropertyError"
-
-/* NOTE: These functions may only be called from the main thread. */
-
-/* Returns the default address of the server type in the escaped form. For
- * PA_SERVER_TYPE_NONE an empty string is returned. The caller frees the
- * string. This function may fail in some rare cases, in which case NULL is
- * returned. */
-char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type);
-
-/* Registers the given interface to the given object path. This is additive: it
- * doesn't matter whether or not the object has already been registered; if it
- * is, then its interface set is just extended.
- *
- * Introspection requests are handled automatically. For that to work, the
- * caller gives an XML snippet containing the interface introspection element.
- * All interface snippets are automatically combined to provide the final
- * introspection string for the object.
- *
- * The introspection snippet contains the interface name, the property names
- * and the method namess, but since this function doesn't do XML parsing, the
- * information needs to be given separately. Property and method names are
- * given as a NULL-terminated array of strings. The interface name is used for
- * message routing, and so are the property and method names too in case the
- * client doesn't tell which interface he's trying to access; in absence of
- * interface information from the client, the correct interface is searched
- * based on the property or method name.
- *
- * Fails and returns a negative number if the object already has the interface
- * registered. */
-int pa_dbus_add_interface(pa_core *c,
-                          const char* path,
-                          const char* interface,
-                          const char * const *properties,
-                          const char * const *methods,
-                          const char* introspection_snippet,
-                          DBusObjectPathMessageFunction receive_cb,
-                          void *userdata);
-
-/* Returns a negative number if the given object doesn't have the given
- * interface registered. */
-int pa_dbus_remove_interface(pa_core *c, const char* path, const char* interface);
-
-/* Fails and returns a negative number if the connection is already
- * registered. */
-int pa_dbus_register_connection(pa_core *c, DBusConnection *conn);
-
-/* Returns a negative number if the connection wasn't registered. */
-int pa_dbus_unregister_connection(pa_core *c, DBusConnection *conn);
-
-#endif
diff --git a/src/pulsecore/dbus-objs/core.c b/src/pulsecore/dbus-objs/core.c
deleted file mode 100644
index 18bbf78..0000000
--- a/src/pulsecore/dbus-objs/core.c
+++ /dev/null
@@ -1,482 +0,0 @@
-/***
-  This file is part of PulseAudio.
-
-  Copyright 2009 Tanu Kaskinen
-
-  PulseAudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2.1 of the License,
-  or (at your option) any later version.
-
-  PulseAudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with PulseAudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <dbus/dbus.h>
-
-#include <pulse/xmalloc.h>
-
-#include <pulsecore/core-util.h>
-#include <pulsecore/dbus-common.h>
-#include <pulsecore/macro.h>
-
-#include "core.h"
-
-#define OBJECT_PATH "/org/pulseaudio1"
-#define INTERFACE_CORE "org.PulseAudio.Core1"
-
-struct pa_dbusobj_core {
-    pa_core *core;
-};
-
-static const char *introspection_snippet =
-    " <interface name=\"" INTERFACE_CORE "\">\n"
-    "  <method name=\"GetCardByName\">\n"
-    "   <arg name=\"Name\" type=\"s\" direction=\"in\"/>\n"
-    "   <arg name=\"Card\" type=\"o\" direction=\"out\"/>\n"
-    "  </method>\n"
-    "  <method name=\"GetSinkByName\">\n"
-    "   <arg name=\"Name\" type=\"s\" direction=\"in\"/>\n"
-    "   <arg name=\"Sink\" type=\"o\" direction=\"out\"/>\n"
-    "  </method>\n"
-    "  <method name=\"GetSourceByName\">\n"
-    "   <arg name=\"Name\" type=\"s\" direction=\"in\"/>\n"
-    "   <arg name=\"Source\" type=\"o\" direction=\"out\"/>\n"
-    "  </method>\n"
-    "  <method name=\"GetSampleByName\">\n"
-    "   <arg name=\"Name\" type=\"s\" direction=\"in\"/>\n"
-    "   <arg name=\"Sample\" type=\"o\" direction=\"out\"/>\n"
-    "  </method>\n"
-    "  <method name=\"UploadSample\">\n"
-    "   <arg name=\"Name\" type=\"s\" direction=\"in\"/>\n"
-    "   <arg name=\"SampleFormat\" type=\"y\" direction=\"in\"/>\n"
-    "   <arg name=\"SampleRate\" type=\"u\" direction=\"in\"/>\n"
-    "   <arg name=\"Channels\" type=\"ay\" direction=\"in\"/>\n"
-    "   <arg name=\"DefaultVolume\" type=\"au\" direction=\"in\"/>\n"
-    "   <arg name=\"Proplist\" type=\"a{say}\" direction=\"in\"/>\n"
-    "   <arg name=\"Data\" type=\"ay\" direction=\"in\"/>\n"
-    "   <arg name=\"Sample\" type=\"o\" direction=\"out\"/>\n"
-    "  </method>\n"
-    "  <method name=\"LoadSampleFromFile\">\n"
-    "   <arg name=\"Name\" type=\"s\" direction=\"in\"/>\n"
-    "   <arg name=\"Filepath\" type=\"s\" direction=\"in\"/>\n"
-    "   <arg name=\"Sample\" type=\"o\" direction=\"out\"/>\n"
-    "  </method>\n"
-    "  <method name=\"AddLazySample\">\n"
-    "   <arg name=\"Name\" type=\"s\" direction=\"in\"/>\n"
-    "   <arg name=\"Filepath\" type=\"s\" direction=\"in\"/>\n"
-    "   <arg name=\"Sample\" type=\"o\" direction=\"out\"/>\n"
-    "  </method>\n"
-    "  <method name=\"AddLazySamplesFromDirectory\">\n"
-    "   <arg name=\"Dirpath\" type=\"s\" direction=\"in\"/>\n"
-    "  </method>\n"
-    "  <method name=\"LoadModule\">\n"
-    "   <arg name=\"Name\" type=\"s\" direction=\"in\"/>\n"
-    "   <arg name=\"Arguments\" type=\"a{ss}\" direction=\"in\"/>\n"
-    "   <arg name=\"Module\" type=\"o\" direction=\"out\"/>\n"
-    "  </method>\n"
-    "  <method name=\"Exit\"/>\n"
-    "  <signal name=\"NewCard\">\n"
-    "   <arg name=\"Card\" type=\"o\"/>\n"
-    "  </signal>\n"
-    "  <signal name=\"CardRemoved\">\n"
-    "   <arg name=\"Card\" type=\"o\"/>\n"
-    "  </signal>\n"
-    "  <signal name=\"NewSink\">\n"
-    "   <arg name=\"Sink\" type=\"o\"/>\n"
-    "  </signal>\n"
-    "  <signal name=\"SinkRemoved\">\n"
-    "   <arg name=\"Sink\" type=\"o\"/>\n"
-    "  </signal>\n"
-    "  <signal name=\"FallbackSinkUpdated\">\n"
-    "   <arg name=\"Sink\" type=\"o\"/>\n"
-    "  </signal>\n"
-    "  <signal name=\"NewSource\">\n"
-    "   <arg name=\"Source\" type=\"o\"/>\n"
-    "  </signal>\n"
-    "  <signal name=\"SourceRemoved\">\n"
-    "   <arg name=\"Source\" type=\"o\"/>\n"
-    "  </signal>\n"
-    "  <signal name=\"FallbackSourceUpdated\">\n"
-    "   <arg name=\"Source\" type=\"o\"/>\n"
-    "  </signal>\n"
-    "  <signal name=\"NewPlaybackStream\">\n"
-    "   <arg name=\"PlaybackStream\" type=\"o\"/>\n"
-    "  </signal>\n"
-    "  <signal name=\"PlaybackStreamRemoved\">\n"
-    "   <arg name=\"PlaybackStream\" type=\"o\"/>\n"
-    "  </signal>\n"
-    "  <signal name=\"NewRecordStream\">\n"
-    "   <arg name=\"RecordStream\" type=\"o\"/>\n"
-    "  </signal>\n"
-    "  <signal name=\"RecordStreamRemoved\">\n"
-    "   <arg name=\"RecordStream\" type=\"o\"/>\n"
-    "  </signal>\n"
-    "  <signal name=\"NewSample\">\n"
-    "   <arg name=\"Sample\" type=\"o\"/>\n"
-    "  </signal>\n"
-    "  <signal name=\"SampleRemoved\">\n"
-    "   <arg name=\"Sample\" type=\"o\"/>\n"
-    "  </signal>\n"
-    "  <signal name=\"NewModule\">\n"
-    "   <arg name=\"Module\" type=\"o\"/>\n"
-    "  </signal>\n"
-    "  <signal name=\"ModuleRemoved\">\n"
-    "   <arg name=\"Module\" type=\"o\"/>\n"
-    "  </signal>\n"
-    "  <signal name=\"NewClient\">\n"
-    "   <arg name=\"Client\" type=\"o\"/>\n"
-    "  </signal>\n"
-    "  <signal name=\"ClientRemoved\">\n"
-    "   <arg name=\"Client\" type=\"o\"/>\n"
-    "  </signal>\n"
-    "  <property name=\"InterfaceRevision\" type=\"u\" access=\"read\"/>\n"
-    "  <property name=\"Name\" type=\"s\" access=\"read\"/>\n"
-    "  <property name=\"Version\" type=\"s\" access=\"read\"/>\n"
-    "  <property name=\"Username\" type=\"s\" access=\"read\"/>\n"
-    "  <property name=\"Hostname\" type=\"s\" access=\"read\"/>\n"
-    "  <property name=\"DefaultChannels\" type=\"ay\" access=\"readwrite\"/>\n"
-    "  <property name=\"DefaultSampleFormat\" type=\"y\" access=\"readwrite\"/>\n"
-    "  <property name=\"DefaultSampleRate\" type=\"u\" access=\"readwrite\"/>\n"
-    "  <property name=\"Sinks\" type=\"ao\" access=\"read\"/>\n"
-    "  <property name=\"FallbackSink\" type=\"s\" access=\"readwrite\"/>\n"
-    "  <property name=\"Sources\" type=\"ao\" access=\"read\"/>\n"
-    "  <property name=\"FallbackSource\" type=\"o\" access=\"readwrite\"/>\n"
-    "  <property name=\"PlaybackStreams\" type=\"ao\" access=\"read\"/>\n"
-    "  <property name=\"RecordStreams\" type=\"ao\" access=\"read\"/>\n"
-    "  <property name=\"Samples\" type=\"ao\" access=\"read\"/>\n"
-    "  <property name=\"Modules\" type=\"ao\" access=\"read\"/>\n"
-    "  <property name=\"Clients\" type=\"ao\" access=\"read\"/>\n"
-    "  <property name=\"Extensions\" type=\"as\" access=\"read\"/>\n"
-    " </interface>\n";
-
-/* If you need to modify this list, note that handle_get_all() uses hard-coded
- * indexes to point to these strings, so make sure the indexes don't go wrong
- * there. */
-static const char *properties[] = {
-    "InterfaceRevision",
-    "Name",
-    "Version",
-    "Username",
-    "Hostname",
-    "DefaultChannels",
-    "DefaultSampleFormat",
-    "DefaultSampleRate",
-    "Sinks",
-    "FallbackSink",
-    "Sources",
-    "FallbackSource",
-    "PlaybackStreams",
-    "RecordStreams",
-    "Samples",
-    "Modules",
-    "Clients",
-    "Extensions",
-    NULL
-};
-
-static const char *methods[] = {
-    "GetCardByName",
-    "GetSinkByName",
-    "GetSourceByName",
-    "GetSampleByName",
-    "UploadSample",
-    "LoadSampleFromFile",
-    "AddLazySample",
-    "AddLazySamplesFromDirectory",
-    "LoadModule",
-    "Exit",
-    NULL
-};
-
-static DBusHandlerResult handle_get_name(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) {
-    DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED;
-    DBusMessage *reply = NULL;
-    const char *server_name = PACKAGE_NAME;
-    DBusMessageIter msg_iter;
-    DBusMessageIter variant_iter;
-
-    pa_assert(conn);
-    pa_assert(msg);
-    pa_assert(c);
-
-    if (!(reply = dbus_message_new_method_return(msg))) {
-        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
-        goto finish;
-    }
-    dbus_message_iter_init_append(reply, &msg_iter);
-    if (!dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_VARIANT, "s", &variant_iter)) {
-        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
-        goto finish;
-    }
-    if (!dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &server_name)) {
-        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
-        goto finish;
-    }
-    if (!dbus_message_iter_close_container(&msg_iter, &variant_iter)) {
-        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
-        goto finish;
-    }
-    if (!dbus_connection_send(conn, reply, NULL)) {
-        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
-        goto finish;
-    }
-    r = DBUS_HANDLER_RESULT_HANDLED;
-
-finish:
-    if (reply)
-        dbus_message_unref(reply);
-
-    return r;
-}
-
-static DBusHandlerResult handle_get(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) {
-    DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED;
-    const char* interface;
-    const char* property;
-    DBusMessage *reply = NULL;
-
-    pa_assert(conn);
-    pa_assert(msg);
-    pa_assert(c);
-
-    if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) {
-        if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) {
-            r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-            goto finish;
-        }
-        if (!dbus_connection_send(conn, reply, NULL)) {
-            r = DBUS_HANDLER_RESULT_NEED_MEMORY;
-            goto finish;
-        }
-        r = DBUS_HANDLER_RESULT_HANDLED;
-        goto finish;
-    }
-
-    if (*interface && !pa_streq(interface, INTERFACE_CORE)) {
-        r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-        goto finish;
-    }
-
-    if (pa_streq(property, "Name")) {
-        r = handle_get_name(conn, msg, c);
-        goto finish;
-    }
-
-    if (!(reply = dbus_message_new_error_printf(msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", property))) {
-        r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-        goto finish;
-    }
-    if (!dbus_connection_send(conn, reply, NULL)) {
-        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
-        goto finish;
-    }
-    r = DBUS_HANDLER_RESULT_HANDLED;
-
-finish:
-    if (reply)
-        dbus_message_unref(reply);
-
-    return r;
-}
-
-static DBusHandlerResult handle_set(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) {
-    DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED;
-    const char* interface;
-    const char* property;
-    DBusMessage *reply = NULL;
-
-    pa_assert(conn);
-    pa_assert(msg);
-    pa_assert(c);
-
-    if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) {
-        if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) {
-            r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-            goto finish;
-        }
-        if (!dbus_connection_send(conn, reply, NULL)) {
-            r = DBUS_HANDLER_RESULT_NEED_MEMORY;
-            goto finish;
-        }
-        r = DBUS_HANDLER_RESULT_HANDLED;
-        goto finish;
-    }
-
-    if (*interface && !pa_streq(interface, INTERFACE_CORE)) {
-        r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-        goto finish;
-    }
-
-    if (pa_streq(property, "Name")) {
-        if (!(reply = dbus_message_new_error_printf(msg, DBUS_ERROR_ACCESS_DENIED, "%s: Property not settable", property))) {
-            r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-            goto finish;
-        }
-        if (!dbus_connection_send(conn, reply, NULL)) {
-            r = DBUS_HANDLER_RESULT_NEED_MEMORY;
-            goto finish;
-        }
-        r = DBUS_HANDLER_RESULT_HANDLED;
-        goto finish;
-    }
-
-    if (!(reply = dbus_message_new_error_printf(msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", property))) {
-        r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-        goto finish;
-    }
-    if (!dbus_connection_send(conn, reply, NULL)) {
-        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
-        goto finish;
-    }
-    r = DBUS_HANDLER_RESULT_HANDLED;
-    goto finish;
-
-    if (!(reply = dbus_message_new_error_printf(msg, DBUS_ERROR_ACCESS_DENIED, "%s: Property not settable", property))) {
-        r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-        goto finish;
-    }
-    if (!dbus_connection_send(conn, reply, NULL)) {
-        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
-        goto finish;
-    }
-    r = DBUS_HANDLER_RESULT_HANDLED;
-
-finish:
-    if (reply)
-        dbus_message_unref(reply);
-
-    return r;
-}
-
-static DBusHandlerResult handle_get_all(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) {
-    DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED;
-    DBusMessage *reply = NULL;
-    char *interface = NULL;
-    char const *server_name = PACKAGE_NAME;
-    DBusMessageIter msg_iter;
-    DBusMessageIter dict_iter;
-    DBusMessageIter dict_entry_iter;
-    DBusMessageIter variant_iter;
-
-    pa_assert(conn);
-    pa_assert(msg);
-    pa_assert(c);
-
-    if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID)) {
-        if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) {
-            r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-            goto finish;
-        }
-        if (!dbus_connection_send(conn, reply, NULL)) {
-            r = DBUS_HANDLER_RESULT_NEED_MEMORY;
-            goto finish;
-        }
-        r = DBUS_HANDLER_RESULT_HANDLED;
-        goto finish;
-    }
-
-    if (!(reply = dbus_message_new_method_return(msg))) {
-        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
-        goto finish;
-    }
-    dbus_message_iter_init_append(reply, &msg_iter);
-    if (!dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)) {
-        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
-        goto finish;
-    }
-    if (!dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter)) {
-        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
-        goto finish;
-    }
-    if (!dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &properties[1])) { /* Name */
-        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
-        goto finish;
-    }
-    if (!dbus_message_iter_open_container(&dict_entry_iter, DBUS_TYPE_VARIANT, "s", &variant_iter)) {
-        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
-        goto finish;
-    }
-    if (!dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &server_name)) {
-        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
-        goto finish;
-    }
-    if (!dbus_message_iter_close_container(&dict_entry_iter, &variant_iter)) {
-        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
-        goto finish;
-    }
-    if (!dbus_message_iter_close_container(&dict_iter, &dict_entry_iter)) {
-        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
-        goto finish;
-    }
-    if (!dbus_message_iter_close_container(&msg_iter, &dict_iter)) {
-        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
-        goto finish;
-    }
-    if (!dbus_connection_send(conn, reply, NULL)) {
-        r = DBUS_HANDLER_RESULT_NEED_MEMORY;
-        goto finish;
-    }
-    r = DBUS_HANDLER_RESULT_HANDLED;
-
-finish:
-    if (reply)
-        dbus_message_unref(reply);
-
-    return r;
-}
-
-static DBusHandlerResult receive_cb(DBusConnection *connection, DBusMessage *message, void *user_data) {
-    pa_dbusobj_core *c = user_data;
-
-    pa_assert(connection);
-    pa_assert(message);
-    pa_assert(c);
-
-    if (dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "Get") ||
-        (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Get")))
-        return handle_get(connection, message, c);
-
-    if (dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "Set") ||
-        (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Set")))
-        return handle_set(connection, message, c);
-
-    if (dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "GetAll") ||
-        (!dbus_message_get_interface(message) && dbus_message_has_member(message, "GetAll")))
-        return handle_get_all(connection, message, c);
-
-    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-}
-
-pa_dbusobj_core *pa_dbusobj_core_new(pa_core *core) {
-    pa_dbusobj_core *c;
-
-    pa_assert(core);
-
-    c = pa_xnew(pa_dbusobj_core, 1);
-    c->core = core;
-
-    pa_dbus_add_interface(core, OBJECT_PATH, INTERFACE_CORE, properties, methods, introspection_snippet, receive_cb, c);
-
-
-    return c;
-}
-
-void pa_dbusobj_core_free(pa_dbusobj_core *c) {
-    pa_assert(c);
-
-    pa_dbus_remove_interface(c->core, OBJECT_PATH, INTERFACE_CORE);
-
-    pa_xfree(c);
-}
diff --git a/src/pulsecore/dbus-objs/core.h b/src/pulsecore/dbus-objs/core.h
deleted file mode 100644
index 8e59cc3..0000000
--- a/src/pulsecore/dbus-objs/core.h
+++ /dev/null
@@ -1,39 +0,0 @@
-#ifndef foodbusobjscorehfoo
-#define foodbusobjscorehfoo
-
-/***
-  This file is part of PulseAudio.
-
-  Copyright 2009 Tanu Kaskinen
-
-  PulseAudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2.1 of the License,
-  or (at your option) any later version.
-
-  PulseAudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with PulseAudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-/* This object implements the D-Bus object at path /org/pulseaudio/core.
- * The implemented interface is org.pulseaudio.Core.
- *
- * See http://pulseaudio.org/wiki/DBusInterface for the Core interface
- * documentation.
- */
-
-#include <pulsecore/core.h>
-
-typedef struct pa_dbusobj_core pa_dbusobj_core;
-
-pa_dbusobj_core *pa_dbusobj_core_new(pa_core *core);
-void pa_dbusobj_core_free(pa_dbusobj_core *c);
-
-#endif
diff --git a/src/pulsecore/dbus-util.c b/src/pulsecore/dbus-util.c
index e047dc3..cfc3e8c 100644
--- a/src/pulsecore/dbus-util.c
+++ b/src/pulsecore/dbus-util.c
@@ -28,6 +28,7 @@
 
 #include <pulse/rtclock.h>
 #include <pulse/timeval.h>
+#include <pulse/utf8.h>
 #include <pulse/xmalloc.h>
 
 #include <pulsecore/core-rtclock.h>
@@ -444,3 +445,335 @@ void pa_dbus_free_pending_list(pa_dbus_pending **p) {
         pa_dbus_pending_free(i);
     }
 }
+
+void pa_dbus_send_error(DBusConnection *c, DBusMessage *in_reply_to, const char *name, const char *message) {
+    DBusMessage *reply = NULL;
+
+    pa_assert(c);
+    pa_assert(in_reply_to);
+    pa_assert(name);
+    pa_assert(message);
+
+    pa_assert_se((reply = dbus_message_new_error(in_reply_to, name, message)));
+    pa_assert_se(dbus_connection_send(c, reply, NULL));
+
+    dbus_message_unref(reply);
+}
+
+void pa_dbus_send_empty_reply(DBusConnection *c, DBusMessage *in_reply_to) {
+    DBusMessage *reply = NULL;
+
+    pa_assert(c);
+    pa_assert(in_reply_to);
+
+    pa_assert_se((reply = dbus_message_new_method_return(in_reply_to)));
+    pa_assert_se(dbus_connection_send(c, reply, NULL));
+    dbus_message_unref(reply);
+}
+
+void pa_dbus_send_basic_value_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data) {
+    DBusMessage *reply = NULL;
+
+    pa_assert(c);
+    pa_assert(in_reply_to);
+    pa_assert(dbus_type_is_basic(type));
+    pa_assert(data);
+
+    pa_assert_se((reply = dbus_message_new_method_return(in_reply_to)));
+    pa_assert_se(dbus_message_append_args(reply, type, data, DBUS_TYPE_INVALID));
+    pa_assert_se(dbus_connection_send(c, reply, NULL));
+    dbus_message_unref(reply);
+}
+
+static const char *signature_from_basic_type(int type) {
+    switch (type) {
+        case DBUS_TYPE_BOOLEAN: return DBUS_TYPE_BOOLEAN_AS_STRING;
+        case DBUS_TYPE_BYTE: return DBUS_TYPE_BYTE_AS_STRING;
+        case DBUS_TYPE_INT16: return DBUS_TYPE_INT16_AS_STRING;
+        case DBUS_TYPE_UINT16: return DBUS_TYPE_UINT16_AS_STRING;
+        case DBUS_TYPE_INT32: return DBUS_TYPE_INT32_AS_STRING;
+        case DBUS_TYPE_UINT32: return DBUS_TYPE_UINT32_AS_STRING;
+        case DBUS_TYPE_INT64: return DBUS_TYPE_INT64_AS_STRING;
+        case DBUS_TYPE_UINT64: return DBUS_TYPE_UINT64_AS_STRING;
+        case DBUS_TYPE_DOUBLE: return DBUS_TYPE_DOUBLE_AS_STRING;
+        case DBUS_TYPE_STRING: return DBUS_TYPE_STRING_AS_STRING;
+        case DBUS_TYPE_OBJECT_PATH: return DBUS_TYPE_OBJECT_PATH_AS_STRING;
+        case DBUS_TYPE_SIGNATURE: return DBUS_TYPE_SIGNATURE_AS_STRING;
+        default: pa_assert_not_reached();
+    }
+}
+
+void pa_dbus_send_basic_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data) {
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+    DBusMessageIter variant_iter;
+
+    pa_assert(c);
+    pa_assert(in_reply_to);
+    pa_assert(dbus_type_is_basic(type));
+    pa_assert(data);
+
+    pa_assert_se((reply = dbus_message_new_method_return(in_reply_to)));
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_VARIANT, signature_from_basic_type(type), &variant_iter));
+    pa_assert_se(dbus_message_iter_append_basic(&variant_iter, type, data));
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &variant_iter));
+    pa_assert_se(dbus_connection_send(c, reply, NULL));
+    dbus_message_unref(reply);
+}
+
+/* Note: returns sizeof(char*) for strings, object paths and signatures. */
+static unsigned basic_type_size(int type) {
+    switch (type) {
+        case DBUS_TYPE_BOOLEAN: return sizeof(dbus_bool_t);
+        case DBUS_TYPE_BYTE: return 1;
+        case DBUS_TYPE_INT16: return sizeof(dbus_int16_t);
+        case DBUS_TYPE_UINT16: return sizeof(dbus_uint16_t);
+        case DBUS_TYPE_INT32: return sizeof(dbus_int32_t);
+        case DBUS_TYPE_UINT32: return sizeof(dbus_uint32_t);
+        case DBUS_TYPE_INT64: return sizeof(dbus_int64_t);
+        case DBUS_TYPE_UINT64: return sizeof(dbus_uint64_t);
+        case DBUS_TYPE_DOUBLE: return sizeof(double);
+        case DBUS_TYPE_STRING:
+        case DBUS_TYPE_OBJECT_PATH:
+        case DBUS_TYPE_SIGNATURE: return sizeof(char*);
+        default: pa_assert_not_reached();
+    }
+}
+
+void pa_dbus_send_basic_array_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int item_type, void *array, unsigned n) {
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+
+    pa_assert(c);
+    pa_assert(in_reply_to);
+    pa_assert(dbus_type_is_basic(item_type));
+    pa_assert(array || n == 0);
+
+    pa_assert_se((reply = dbus_message_new_method_return(in_reply_to)));
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_dbus_append_basic_array_variant(&msg_iter, item_type, array, n);
+    pa_assert_se(dbus_connection_send(c, reply, NULL));
+    dbus_message_unref(reply);
+}
+
+void pa_dbus_append_basic_array(DBusMessageIter *iter, int item_type, const void *array, unsigned n) {
+    DBusMessageIter array_iter;
+    unsigned i;
+    unsigned item_size;
+
+    pa_assert(iter);
+    pa_assert(dbus_type_is_basic(item_type));
+    pa_assert(array || n == 0);
+
+    item_size = basic_type_size(item_type);
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, signature_from_basic_type(item_type), &array_iter));
+
+    for (i = 0; i < n; ++i)
+        pa_assert_se(dbus_message_iter_append_basic(&array_iter, item_type, &((uint8_t*) array)[i * item_size]));
+
+    pa_assert_se(dbus_message_iter_close_container(iter, &array_iter));
+};
+
+void pa_dbus_append_basic_variant(DBusMessageIter *iter, int type, void *data) {
+    DBusMessageIter variant_iter;
+
+    pa_assert(iter);
+    pa_assert(dbus_type_is_basic(type));
+    pa_assert(data);
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, signature_from_basic_type(type), &variant_iter));
+    pa_assert_se(dbus_message_iter_append_basic(&variant_iter, type, data));
+    pa_assert_se(dbus_message_iter_close_container(iter, &variant_iter));
+}
+
+void pa_dbus_append_basic_array_variant(DBusMessageIter *iter, int item_type, const void *array, unsigned n) {
+    DBusMessageIter variant_iter;
+    char *array_signature;
+
+    pa_assert(iter);
+    pa_assert(dbus_type_is_basic(item_type));
+    pa_assert(array || n == 0);
+
+    array_signature = pa_sprintf_malloc("a%c", *signature_from_basic_type(item_type));
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, array_signature, &variant_iter));
+    pa_dbus_append_basic_array(&variant_iter, item_type, array, n);
+    pa_assert_se(dbus_message_iter_close_container(iter, &variant_iter));
+
+    pa_xfree(array_signature);
+}
+
+void pa_dbus_append_basic_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int type, void *data) {
+    DBusMessageIter dict_entry_iter;
+
+    pa_assert(dict_iter);
+    pa_assert(key);
+    pa_assert(dbus_type_is_basic(type));
+    pa_assert(data);
+
+    pa_assert_se(dbus_message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter));
+    pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key));
+    pa_dbus_append_basic_variant(&dict_entry_iter, type, data);
+    pa_assert_se(dbus_message_iter_close_container(dict_iter, &dict_entry_iter));
+}
+
+void pa_dbus_append_basic_array_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int item_type, const void *array, unsigned n) {
+    DBusMessageIter dict_entry_iter;
+
+    pa_assert(dict_iter);
+    pa_assert(key);
+    pa_assert(dbus_type_is_basic(item_type));
+    pa_assert(array || n == 0);
+
+    pa_assert_se(dbus_message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter));
+    pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key));
+    pa_dbus_append_basic_array_variant(&dict_entry_iter, item_type, array, n);
+    pa_assert_se(dbus_message_iter_close_container(dict_iter, &dict_entry_iter));
+}
+
+int pa_dbus_get_basic_set_property_arg(DBusConnection *c, DBusMessage *msg, int type, void *data) {
+    DBusMessageIter msg_iter;
+    DBusMessageIter variant_iter;
+
+    pa_assert(c);
+    pa_assert(msg);
+    pa_assert(dbus_type_is_basic(type));
+    pa_assert(data);
+
+    /* Skip the interface and property name arguments. */
+    if (!dbus_message_iter_init(msg, &msg_iter) || !dbus_message_iter_next(&msg_iter) || !dbus_message_iter_next(&msg_iter)) {
+        pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments.");
+        return -1;
+    }
+
+    if (dbus_message_iter_get_arg_type(&msg_iter) != DBUS_TYPE_VARIANT) {
+        pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Message argument isn't a variant.");
+        return -1;
+    }
+
+    dbus_message_iter_recurse(&msg_iter, &variant_iter);
+
+    if (dbus_message_iter_get_arg_type(&variant_iter) != type) {
+        pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Variant has wrong contained type.");
+        return -1;
+    }
+
+    dbus_message_iter_get_basic(&variant_iter, data);
+
+    return 0;
+}
+
+int pa_dbus_get_basic_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int type, void *data) {
+    pa_assert(c);
+    pa_assert(msg);
+    pa_assert(iter);
+    pa_assert(dbus_type_is_basic(type));
+    pa_assert(data);
+
+    if (dbus_message_iter_get_arg_type(iter) != type) {
+        pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type or too few arguments.");
+        return -1;
+    }
+
+    dbus_message_iter_get_basic(iter, data);
+
+    return 0;
+}
+
+int pa_dbus_get_fixed_array_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int item_type, void *array, unsigned *n) {
+    DBusMessageIter array_iter;
+    int signed_n;
+
+    pa_assert(c);
+    pa_assert(msg);
+    pa_assert(iter);
+    pa_assert(dbus_type_is_fixed(item_type));
+    pa_assert(array);
+    pa_assert(n);
+
+    if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) {
+        pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type or too few arguments. An array was expected.");
+        return -1;
+    }
+
+    if (dbus_message_iter_get_element_type(iter) != item_type) {
+        pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type.");
+        return -1;
+    }
+
+    dbus_message_iter_recurse(iter, &array_iter);
+
+    dbus_message_iter_get_fixed_array(&array_iter, array, &signed_n);
+
+    pa_assert(signed_n >= 0);
+
+    *n = signed_n;
+
+    return 0;
+}
+
+pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter) {
+    DBusMessageIter dict_iter;
+    pa_proplist *proplist = NULL;
+    const char *key;
+    const uint8_t *value;
+    int value_length;
+
+    pa_assert(c);
+    pa_assert(msg);
+    pa_assert(iter);
+
+    if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) {
+        pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type or too few arguments. An array was expected.");
+        return NULL;
+    }
+
+    if (dbus_message_iter_get_element_type(iter) != DBUS_TYPE_DICT_ENTRY) {
+        pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type. A dict entry was expected.");
+        return NULL;
+    }
+
+    proplist = pa_proplist_new();
+
+    dbus_message_iter_recurse(iter, &dict_iter);
+
+    while (dbus_message_iter_has_next(&dict_iter)) {
+        if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_STRING) {
+            pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict key type. A string was expected.");
+            goto fail;
+        }
+
+        dbus_message_iter_get_basic(&dict_iter, &key);
+
+        if (strlen(key) <= 0 || !pa_ascii_valid(key)) {
+            pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Invalid property list key.");
+            goto fail;
+        }
+
+        if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_ARRAY) {
+            pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value type. An array was expected.");
+            goto fail;
+        }
+
+        if (dbus_message_iter_get_element_type(&dict_iter) != DBUS_TYPE_BYTE) {
+            pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value item type. A byte was expected.");
+            goto fail;
+        }
+
+        dbus_message_iter_get_fixed_array(&dict_iter, &value, &value_length);
+
+        pa_assert(value_length >= 0);
+
+        pa_assert_se(pa_proplist_set(proplist, key, value, value_length) >= 0);
+    }
+
+    return proplist;
+
+fail:
+    if (proplist)
+        pa_proplist_free(proplist);
+
+    return NULL;
+}
diff --git a/src/pulsecore/dbus-util.h b/src/pulsecore/dbus-util.h
index 9732873..1a8aeac 100644
--- a/src/pulsecore/dbus-util.h
+++ b/src/pulsecore/dbus-util.h
@@ -26,6 +26,7 @@
 
 #include <pulsecore/llist.h>
 #include <pulse/mainloop-api.h>
+#include <pulse/proplist.h>
 
 /* A wrap connection is not shared or refcounted, it is available in client side */
 typedef struct pa_dbus_wrap_connection pa_dbus_wrap_connection;
@@ -61,4 +62,32 @@ void pa_dbus_sync_pending_list(pa_dbus_pending **p);
 /* Free up a list of pa_dbus_pending_call objects */
 void pa_dbus_free_pending_list(pa_dbus_pending **p);
 
+/* Sends an error message as the reply to the given message. */
+void pa_dbus_send_error(DBusConnection *c, DBusMessage *in_reply_to, const char *name, const char *message);
+
+void pa_dbus_send_empty_reply(DBusConnection *c, DBusMessage *in_reply_to);
+void pa_dbus_send_basic_value_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data);
+void pa_dbus_send_basic_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data);
+void pa_dbus_send_basic_array_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int item_type, void *array, unsigned n);
+
+void pa_dbus_append_basic_array(DBusMessageIter *iter, int item_type, const void *array, unsigned n);
+void pa_dbus_append_basic_array_variant(DBusMessageIter *iter, int item_type, const void *array, unsigned n);
+void pa_dbus_append_basic_variant(DBusMessageIter *iter, int type, void *data);
+void pa_dbus_append_basic_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int type, void *data);
+void pa_dbus_append_basic_array_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int item_type, const void *array, unsigned n);
+
+/* Helper function for extracting the value argument of a Set call for a
+ * property with a basic type. If the message is invalid, an error reply is
+ * sent and a negative number is returned. */
+int pa_dbus_get_basic_set_property_arg(DBusConnection *c, DBusMessage *msg, int type, void *data);
+
+/* If the arguments can't be read from the iterator, an error reply is sent and
+ * a negative number is returned. */
+int pa_dbus_get_basic_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int type, void *data);
+int pa_dbus_get_fixed_array_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int item_type, void *array, unsigned *n);
+
+/* Returns a new proplist, that the caller has to free. If the proplist can't
+ * be read from the iterator, an error reply is sent and NULL is returned. */
+pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter);
+
 #endif
diff --git a/src/pulsecore/namereg.c b/src/pulsecore/namereg.c
index 9df2f58..046c87b 100644
--- a/src/pulsecore/namereg.c
+++ b/src/pulsecore/namereg.c
@@ -149,21 +149,48 @@ const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t
 
     pa_assert_se(pa_hashmap_put(c->namereg, e->name, e) >= 0);
 
+    if (type == PA_NAMEREG_SINK && !c->default_sink)
+        pa_namereg_set_default_sink(c, data);
+    else if (type == PA_NAMEREG_SOURCE && !c->default_source)
+        pa_namereg_set_default_source(c, data);
+
     return e->name;
 }
 
 void pa_namereg_unregister(pa_core *c, const char *name) {
     struct namereg_entry *e;
+    uint32_t idx;
 
     pa_assert(c);
     pa_assert(name);
 
     pa_assert_se(e = pa_hashmap_remove(c->namereg, name));
 
-    if (c->default_sink == e->data)
-        pa_namereg_set_default_sink(c, NULL);
-    else if (c->default_source == e->data)
-        pa_namereg_set_default_source(c, NULL);
+    if (c->default_sink == e->data) {
+        pa_sink *new_default = pa_idxset_first(c->sinks, &idx);
+
+        if (new_default == e->data)
+            new_default = pa_idxset_next(c->sinks, &idx);
+
+        pa_namereg_set_default_sink(c, new_default);
+
+    } else if (c->default_source == e->data) {
+        pa_source *new_default;
+
+        for (new_default = pa_idxset_first(c->sources, &idx); new_default; new_default = pa_idxset_next(c->sources, &idx)) {
+            if (new_default != e->data && !new_default->monitor_of)
+                break;
+        }
+
+        if (!new_default) {
+            new_default = pa_idxset_first(c->sources, &idx);
+
+            if (new_default == e->data)
+                new_default = pa_idxset_next(c->sources, &idx);
+        }
+
+        pa_namereg_set_default_source(c, new_default);
+    }
 
     pa_xfree(e->name);
     pa_xfree(e);
@@ -191,7 +218,6 @@ void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type) {
 
         if ((s = pa_namereg_get(c, NULL, PA_NAMEREG_SINK)))
             return s->monitor_source;
-
     }
 
     if (!name)
@@ -242,35 +268,16 @@ pa_source* pa_namereg_set_default_source(pa_core*c, pa_source *s) {
     return s;
 }
 
+/* XXX: After removing old functionality, has this function become useless? */
 pa_sink *pa_namereg_get_default_sink(pa_core *c) {
-    pa_sink *s;
-
     pa_assert(c);
 
-    if (c->default_sink)
-        return c->default_sink;
-
-    if ((s = pa_idxset_first(c->sinks, NULL)))
-        return pa_namereg_set_default_sink(c, s);
-
-    return NULL;
+    return c->default_sink;
 }
 
+/* XXX: After removing old functionality, has this function become useless? */
 pa_source *pa_namereg_get_default_source(pa_core *c) {
-    pa_source *s;
-    uint32_t idx;
-
     pa_assert(c);
 
-    if (c->default_source)
-        return c->default_source;
-
-    for (s = PA_SOURCE(pa_idxset_first(c->sources, &idx)); s; s = PA_SOURCE(pa_idxset_next(c->sources, &idx)))
-        if (!s->monitor_of)
-            return pa_namereg_set_default_source(c, s);
-
-    if ((s = pa_idxset_first(c->sources, NULL)))
-        return pa_namereg_set_default_source(c, s);
-
-    return NULL;
+    return c->default_source;
 }
diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c
new file mode 100644
index 0000000..fb7d168
--- /dev/null
+++ b/src/pulsecore/protocol-dbus.c
@@ -0,0 +1,930 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <dbus/dbus.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/hashmap.h>
+#include <pulsecore/idxset.h>
+#include <pulsecore/shared.h>
+#include <pulsecore/strbuf.h>
+
+#include "protocol-dbus.h"
+
+struct pa_dbus_protocol {
+    PA_REFCNT_DECLARE;
+
+    pa_core *core;
+    pa_hashmap *objects; /* Object path -> struct object_entry */
+    pa_hashmap *connections; /* DBusConnection -> struct connection_entry */
+    pa_hashmap *extensions; /* String -> anything */
+};
+
+struct object_entry {
+    char *path;
+    pa_hashmap *interfaces; /* Interface name -> struct interface_entry */
+    char *introspection;
+};
+
+struct connection_entry {
+    DBusConnection *connection;
+    pa_client *client;
+
+    pa_bool_t listening_for_all_signals;
+
+    /* Contains object paths. If this is empty, then signals from all objects
+     * are accepted. Only used when listening_for_all_signals == TRUE. */
+    pa_idxset *all_signals_objects;
+
+    /* Signal name -> idxset. The idxsets contain object paths. If an idxset is
+     * empty, then that signal is accepted from all objects. Only used when
+     * listening_for_all_signals == FALSE. */
+    pa_hashmap *listening_signals;
+};
+
+struct interface_entry {
+    char *name;
+    pa_hashmap *method_handlers;
+    pa_hashmap *property_handlers;
+    pa_dbus_receive_cb_t get_all_properties_cb;
+    pa_dbus_signal_info *signals;
+    unsigned n_signals;
+    void *userdata;
+};
+
+char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type) {
+    char *address = NULL;
+    char *runtime_path = NULL;
+    char *escaped_path = NULL;
+
+    switch (server_type) {
+        case PA_SERVER_TYPE_USER:
+            pa_assert_se((runtime_path = pa_runtime_path(PA_DBUS_SOCKET_NAME)));
+            pa_assert_se((escaped_path = dbus_address_escape_value(runtime_path)));
+            address = pa_sprintf_malloc("unix:path=%s", escaped_path);
+            break;
+
+        case PA_SERVER_TYPE_SYSTEM:
+            pa_assert_se((escaped_path = dbus_address_escape_value(PA_DBUS_SYSTEM_SOCKET_PATH)));
+            address = pa_sprintf_malloc("unix:path=%s", escaped_path);
+            break;
+
+        case PA_SERVER_TYPE_NONE:
+            address = pa_xnew0(char, 1);
+            break;
+
+        default:
+            pa_assert_not_reached();
+    }
+
+    pa_xfree(runtime_path);
+    pa_xfree(escaped_path);
+
+    return address;
+}
+
+static pa_dbus_protocol *dbus_protocol_new(pa_core *c) {
+    pa_dbus_protocol *p;
+
+    pa_assert(c);
+
+    p = pa_xnew(pa_dbus_protocol, 1);
+    PA_REFCNT_INIT(p);
+    p->core = c;
+    p->objects = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+    p->connections = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+    p->extensions = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
+    pa_assert_se(pa_shared_set(c, "dbus-protocol", p) >= 0);
+
+    return p;
+}
+
+pa_dbus_protocol* pa_dbus_protocol_get(pa_core *c) {
+    pa_dbus_protocol *p;
+
+    if ((p = pa_shared_get(c, "dbus-protocol")))
+        return pa_dbus_protocol_ref(p);
+
+    return dbus_protocol_new(c);
+}
+
+pa_dbus_protocol* pa_dbus_protocol_ref(pa_dbus_protocol *p) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) >= 1);
+
+    PA_REFCNT_INC(p);
+
+    return p;
+}
+
+void pa_dbus_protocol_unref(pa_dbus_protocol *p) {
+    pa_assert(p);
+    pa_assert(PA_REFCNT_VALUE(p) >= 1);
+
+    if (PA_REFCNT_DEC(p) > 0)
+        return;
+
+    pa_assert(pa_hashmap_isempty(p->objects));
+    pa_assert(pa_hashmap_isempty(p->connections));
+    pa_assert(pa_hashmap_isempty(p->extensions));
+
+    pa_hashmap_free(p->objects, NULL, NULL);
+    pa_hashmap_free(p->connections, NULL, NULL);
+    pa_hashmap_free(p->extensions, NULL, NULL);
+
+    pa_assert_se(pa_shared_remove(p->core, "dbus-protocol") >= 0);
+
+    pa_xfree(p);
+}
+
+static void update_introspection(struct object_entry *oe) {
+    pa_strbuf *buf;
+    void *interfaces_state = NULL;
+    struct interface_entry *iface_entry = NULL;
+
+    pa_assert(oe);
+
+    buf = pa_strbuf_new();
+    pa_strbuf_puts(buf, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE);
+    pa_strbuf_puts(buf, "<node>\n");
+
+    while ((iface_entry = pa_hashmap_iterate(oe->interfaces, &interfaces_state, NULL))) {
+        pa_dbus_method_handler *method_handler;
+        pa_dbus_property_handler *property_handler;
+        void *handlers_state = NULL;
+        unsigned i;
+        unsigned j;
+
+        pa_strbuf_printf(buf, " <interface name=\"%s\">\n", iface_entry->name);
+
+        while ((method_handler = pa_hashmap_iterate(iface_entry->method_handlers, &handlers_state, NULL))) {
+            pa_strbuf_printf(buf, "  <method name=\"%s\">\n", method_handler->method_name);
+
+            for (i = 0; i < method_handler->n_arguments; ++i)
+                pa_strbuf_printf(buf, "   <arg name=\"%s\" type=\"%s\" direction=\"%s\"/>\n", method_handler->arguments[i].name,
+                                                                                              method_handler->arguments[i].type,
+                                                                                              method_handler->arguments[i].direction);
+
+            pa_strbuf_puts(buf, "  </method>\n");
+        }
+
+        handlers_state = NULL;
+
+        while ((property_handler = pa_hashmap_iterate(iface_entry->property_handlers, &handlers_state, NULL)))
+            pa_strbuf_printf(buf, "  <property name=\"%s\" type=\"%s\" access=\"%s\"/>\n", property_handler->property_name,
+                                                                                           property_handler->type,
+                                                                                           property_handler->get_cb ? (property_handler->set_cb ? "readwrite" : "read") : "write");
+
+        for (i = 0; i < iface_entry->n_signals; ++i) {
+            pa_strbuf_printf(buf, "  <signal name=\"%s\">\n", iface_entry->signals[i].name);
+
+            for (j = 0; j < iface_entry->signals[i].n_arguments; ++j)
+                pa_strbuf_printf(buf, "   <arg name=\"%s\" type=\"%s\"/>\n", iface_entry->signals[i].arguments[j].name,
+                                                                             iface_entry->signals[i].arguments[j].type);
+
+            pa_strbuf_puts(buf, "  </signal>\n");
+        }
+
+        pa_strbuf_puts(buf, " </interface>\n");
+    }
+
+    pa_strbuf_puts(buf, " <interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">\n"
+                        "  <method name=\"Introspect\">\n"
+                        "   <arg name=\"data\" type=\"s\" direction=\"out\"/>\n"
+                        "  </method>\n"
+                        " </interface>\n"
+                        " <interface name=\"" DBUS_INTERFACE_PROPERTIES "\">\n"
+                        "  <method name=\"Get\">\n"
+                        "   <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
+                        "   <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
+                        "   <arg name=\"value\" type=\"v\" direction=\"out\"/>\n"
+                        "  </method>\n"
+                        "  <method name=\"Set\">\n"
+                        "   <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
+                        "   <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
+                        "   <arg name=\"value\" type=\"v\" direction=\"in\"/>\n"
+                        "  </method>\n"
+                        "  <method name=\"GetAll\">\n"
+                        "   <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
+                        "   <arg name=\"props\" type=\"a{sv}\" direction=\"out\"/>\n"
+                        "  </method>\n"
+                        " </interface>\n");
+
+    pa_strbuf_puts(buf, "</node>\n");
+
+    pa_xfree(oe->introspection);
+    oe->introspection = pa_strbuf_tostring_free(buf);
+}
+
+enum find_result_t {
+    FOUND_METHOD,
+    FOUND_GET_PROPERTY,
+    FOUND_SET_PROPERTY,
+    FOUND_GET_ALL,
+    PROPERTY_ACCESS_DENIED,
+    NO_SUCH_METHOD,
+    NO_SUCH_PROPERTY,
+    INVALID_MESSAGE_ARGUMENTS
+};
+
+static enum find_result_t find_handler_by_property(struct object_entry *obj_entry,
+                                                   DBusMessage *msg,
+                                                   const char *property,
+                                                   struct interface_entry **iface_entry,
+                                                   pa_dbus_property_handler **property_handler) {
+    void *state = NULL;
+
+    pa_assert(obj_entry);
+    pa_assert(msg);
+    pa_assert(property);
+    pa_assert(iface_entry);
+    pa_assert(property_handler);
+
+    while ((*iface_entry = pa_hashmap_iterate(obj_entry->interfaces, &state, NULL))) {
+        if ((*property_handler = pa_hashmap_get((*iface_entry)->property_handlers, property))) {
+            if (dbus_message_has_member(msg, "Get"))
+                return (*property_handler)->get_cb ? FOUND_GET_PROPERTY : PROPERTY_ACCESS_DENIED;
+            else if (dbus_message_has_member(msg, "Set"))
+                return (*property_handler)->set_cb ? FOUND_SET_PROPERTY : PROPERTY_ACCESS_DENIED;
+            else
+                pa_assert_not_reached();
+        }
+    }
+
+    return NO_SUCH_PROPERTY;
+}
+
+static enum find_result_t find_handler_by_method(struct object_entry *obj_entry,
+                                                 const char *method,
+                                                 struct interface_entry **iface_entry,
+                                                 pa_dbus_method_handler **method_handler) {
+    void *state = NULL;
+
+    pa_assert(obj_entry);
+    pa_assert(method);
+    pa_assert(iface_entry);
+    pa_assert(method_handler);
+
+    while ((*iface_entry = pa_hashmap_iterate(obj_entry->interfaces, &state, NULL))) {
+        if ((*method_handler = pa_hashmap_get((*iface_entry)->method_handlers, method)))
+            return FOUND_METHOD;
+    }
+
+    return NO_SUCH_METHOD;
+}
+
+static enum find_result_t find_handler_from_properties_call(struct object_entry *obj_entry,
+                                                            DBusMessage *msg,
+                                                            struct interface_entry **iface_entry,
+                                                            pa_dbus_property_handler **property_handler,
+                                                            const char **attempted_property) {
+    const char *interface;
+
+    pa_assert(obj_entry);
+    pa_assert(msg);
+    pa_assert(iface_entry);
+
+    if (dbus_message_has_member(msg, "GetAll")) {
+        if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID))
+            return INVALID_MESSAGE_ARGUMENTS;
+
+        if (*interface) {
+            if ((*iface_entry = pa_hashmap_get(obj_entry->interfaces, interface)))
+                return FOUND_GET_ALL;
+            else
+                return NO_SUCH_METHOD; /* XXX: NO_SUCH_INTERFACE or something like that might be more accurate. */
+        } else {
+            pa_assert_se((*iface_entry = pa_hashmap_first(obj_entry->interfaces)));
+            return FOUND_GET_ALL;
+        }
+    } else {
+        if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, attempted_property, DBUS_TYPE_INVALID))
+            return INVALID_MESSAGE_ARGUMENTS;
+
+        if (*interface) {
+            if ((*iface_entry = pa_hashmap_get(obj_entry->interfaces, interface)) &&
+                (*property_handler = pa_hashmap_get((*iface_entry)->property_handlers, *attempted_property))) {
+                if (dbus_message_has_member(msg, "Get"))
+                    return (*property_handler)->get_cb ? FOUND_GET_PROPERTY : PROPERTY_ACCESS_DENIED;
+                else if (dbus_message_has_member(msg, "Set"))
+                    return (*property_handler)->set_cb ? FOUND_SET_PROPERTY : PROPERTY_ACCESS_DENIED;
+                else
+                    pa_assert_not_reached();
+            } else
+                return NO_SUCH_PROPERTY;
+        } else
+            return find_handler_by_property(obj_entry, msg, *attempted_property, iface_entry, property_handler);
+    }
+}
+
+static enum find_result_t find_handler(struct object_entry *obj_entry,
+                                       DBusMessage *msg,
+                                       struct interface_entry **iface_entry,
+                                       pa_dbus_method_handler **method_handler,
+                                       pa_dbus_property_handler **property_handler,
+                                       const char **attempted_property) {
+    const char *interface;
+
+    pa_assert(obj_entry);
+    pa_assert(msg);
+    pa_assert(iface_entry);
+    pa_assert(method_handler);
+    pa_assert(property_handler);
+    pa_assert(attempted_property);
+
+    *iface_entry = NULL;
+    *method_handler = NULL;
+
+    if (dbus_message_has_interface(msg, DBUS_INTERFACE_PROPERTIES))
+        return find_handler_from_properties_call(obj_entry, msg, iface_entry, property_handler, attempted_property);
+
+    else if ((interface = dbus_message_get_interface(msg))) {
+        if ((*iface_entry = pa_hashmap_get(obj_entry->interfaces, interface)) &&
+            (*method_handler = pa_hashmap_get((*iface_entry)->method_handlers, dbus_message_get_member(msg))))
+            return FOUND_METHOD;
+        else
+            return NO_SUCH_METHOD;
+
+    } else { /* The method call doesn't contain an interface. */
+        if (dbus_message_has_member(msg, "Get") || dbus_message_has_member(msg, "Set") || dbus_message_has_member(msg, "GetAll")) {
+            if (find_handler_by_method(obj_entry, dbus_message_get_member(msg), iface_entry, method_handler) == FOUND_METHOD)
+                return FOUND_METHOD; /* The object has a method named Get, Set or GetAll in some other interface than .Properties. */
+            else
+                /* Assume this is a .Properties call. */
+                return find_handler_from_properties_call(obj_entry, msg, iface_entry, property_handler, attempted_property);
+
+        } else /* This is not a .Properties call. */
+            return find_handler_by_method(obj_entry, dbus_message_get_member(msg), iface_entry, method_handler);
+    }
+}
+
+static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessage *message, void *user_data) {
+    pa_dbus_protocol *p = user_data;
+    struct object_entry *obj_entry = NULL;
+    struct interface_entry *iface_entry = NULL;
+    pa_dbus_method_handler *method_handler = NULL;
+    pa_dbus_property_handler *property_handler = NULL;
+    const char *attempted_property = NULL;
+    DBusMessage *reply = NULL;
+
+    pa_assert(connection);
+    pa_assert(message);
+    pa_assert(p);
+    pa_assert(p->objects);
+
+    if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    pa_assert_se((obj_entry = pa_hashmap_get(p->objects, dbus_message_get_path(message))));
+
+    if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect") ||
+        (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Introspect"))) {
+        pa_assert_se((reply = dbus_message_new_method_return(message)));
+        pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &obj_entry->introspection, DBUS_TYPE_INVALID));
+        pa_assert_se(dbus_connection_send(connection, reply, NULL));
+
+        pa_log_debug("%s.Introspect handled.", obj_entry->path);
+
+        goto finish;
+    }
+
+    switch (find_handler(obj_entry, message, &iface_entry, &method_handler, &property_handler, &attempted_property)) {
+        case FOUND_METHOD:
+            method_handler->receive_cb(connection, message, iface_entry->userdata);
+            break;
+
+        case FOUND_GET_PROPERTY:
+            property_handler->get_cb(connection, message, iface_entry->userdata);
+            break;
+
+        case FOUND_SET_PROPERTY:
+            property_handler->set_cb(connection, message, iface_entry->userdata);
+            break;
+
+        case FOUND_GET_ALL:
+            if (iface_entry->get_all_properties_cb)
+                iface_entry->get_all_properties_cb(connection, message, iface_entry->userdata);
+            break;
+
+        case PROPERTY_ACCESS_DENIED:
+            pa_assert_se((reply = dbus_message_new_error_printf(message, DBUS_ERROR_ACCESS_DENIED, "%s access denied for property %s", dbus_message_get_member(message), attempted_property)));
+            pa_assert_se(dbus_connection_send(connection, reply, NULL));
+            break;
+
+        case NO_SUCH_METHOD:
+            pa_assert_se((reply = dbus_message_new_error_printf(message, DBUS_ERROR_UNKNOWN_METHOD, "%s: No such method", dbus_message_get_member(message))));
+            pa_assert_se(dbus_connection_send(connection, reply, NULL));
+            break;
+
+        case NO_SUCH_PROPERTY:
+            pa_assert_se((reply = dbus_message_new_error_printf(message, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", attempted_property)));
+            pa_assert_se(dbus_connection_send(connection, reply, NULL));
+            break;
+
+        case INVALID_MESSAGE_ARGUMENTS:
+            pa_assert_se((reply = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS, "Invalid arguments for %s", dbus_message_get_member(message))));
+            pa_assert_se(dbus_connection_send(connection, reply, NULL));
+            break;
+
+        default:
+            pa_assert_not_reached();
+    }
+
+finish:
+    if (reply)
+        dbus_message_unref(reply);
+
+    return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusObjectPathVTable vtable = {
+    .unregister_function = NULL,
+    .message_function = handle_message_cb,
+    .dbus_internal_pad1 = NULL,
+    .dbus_internal_pad2 = NULL,
+    .dbus_internal_pad3 = NULL,
+    .dbus_internal_pad4 = NULL
+};
+
+static void register_object(pa_dbus_protocol *p, struct object_entry *obj_entry) {
+    struct connection_entry *conn_entry;
+    void *state = NULL;
+
+    pa_assert(p);
+    pa_assert(obj_entry);
+
+    while ((conn_entry = pa_hashmap_iterate(p->connections, &state, NULL)))
+        pa_assert_se(dbus_connection_register_object_path(conn_entry->connection, obj_entry->path, &vtable, p));
+}
+
+static pa_dbus_arg_info *copy_args(const pa_dbus_arg_info *src, unsigned n) {
+    pa_dbus_arg_info *dst;
+    unsigned i;
+
+    if (n == 0)
+        return NULL;
+
+    pa_assert(src);
+
+    dst = pa_xnew0(pa_dbus_arg_info, n);
+
+    for (i = 0; i < n; ++i) {
+        dst[i].name = pa_xstrdup(src[i].name);
+        dst[i].type = pa_xstrdup(src[i].type);
+        dst[i].direction = pa_xstrdup(src[i].direction);
+    }
+
+    return dst;
+}
+
+static pa_hashmap *create_method_handlers(const pa_dbus_interface_info *info) {
+    pa_hashmap *handlers;
+    unsigned i;
+
+    pa_assert(info);
+    pa_assert(info->method_handlers || info->n_method_handlers == 0);
+
+    handlers = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
+    for (i = 0; i < info->n_method_handlers; ++i) {
+        pa_dbus_method_handler *h = pa_xnew(pa_dbus_method_handler, 1);
+        h->method_name = pa_xstrdup(info->method_handlers[i].method_name);
+        h->arguments = copy_args(info->method_handlers[i].arguments, info->method_handlers[i].n_arguments);
+        h->n_arguments = info->method_handlers[i].n_arguments;
+        h->receive_cb = info->method_handlers[i].receive_cb;
+
+        pa_hashmap_put(handlers, h->method_name, h);
+    }
+
+    return handlers;
+}
+
+static pa_hashmap *create_property_handlers(const pa_dbus_interface_info *info) {
+    pa_hashmap *handlers;
+    unsigned i = 0;
+
+    pa_assert(info);
+    pa_assert(info->property_handlers || info->n_property_handlers == 0);
+
+    handlers = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
+    for (i = 0; i < info->n_property_handlers; ++i) {
+        pa_dbus_property_handler *h = pa_xnew(pa_dbus_property_handler, 1);
+        h->property_name = pa_xstrdup(info->property_handlers[i].property_name);
+        h->type = pa_xstrdup(info->property_handlers[i].type);
+        h->get_cb = info->property_handlers[i].get_cb;
+        h->set_cb = info->property_handlers[i].set_cb;
+
+        pa_hashmap_put(handlers, h->property_name, h);
+    }
+
+    return handlers;
+}
+
+static pa_dbus_signal_info *copy_signals(const pa_dbus_interface_info *info) {
+    pa_dbus_signal_info *dst;
+    unsigned i;
+
+    pa_assert(info);
+
+    if (info->n_signals == 0)
+        return NULL;
+
+    pa_assert(info->signals);
+
+    dst = pa_xnew(pa_dbus_signal_info, info->n_signals);
+
+    for (i = 0; i < info->n_signals; ++i) {
+        dst[i].name = pa_xstrdup(info->signals[i].name);
+        dst[i].arguments = copy_args(info->signals[i].arguments, info->signals[i].n_arguments);
+        dst[i].n_arguments = info->signals[i].n_arguments;
+    }
+
+    return dst;
+}
+
+int pa_dbus_protocol_add_interface(pa_dbus_protocol *p,
+                                   const char *path,
+                                   const pa_dbus_interface_info *info,
+                                   void *userdata) {
+    struct object_entry *obj_entry;
+    struct interface_entry *iface_entry;
+    pa_bool_t obj_entry_created = FALSE;
+
+    pa_assert(p);
+    pa_assert(path);
+    pa_assert(info);
+    pa_assert(info->name);
+    pa_assert(info->method_handlers || info->n_method_handlers == 0);
+    pa_assert(info->property_handlers || info->n_property_handlers == 0);
+    pa_assert(info->get_all_properties_cb || info->n_property_handlers == 0);
+    pa_assert(info->signals || info->n_signals == 0);
+
+    if (!(obj_entry = pa_hashmap_get(p->objects, path))) {
+        obj_entry = pa_xnew(struct object_entry, 1);
+        obj_entry->path = pa_xstrdup(path);
+        obj_entry->interfaces = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+        obj_entry->introspection = NULL;
+
+        pa_hashmap_put(p->objects, path, obj_entry);
+        obj_entry_created = TRUE;
+    }
+
+    if (pa_hashmap_get(obj_entry->interfaces, info->name) != NULL)
+        goto fail; /* The interface was already registered. */
+
+    iface_entry = pa_xnew(struct interface_entry, 1);
+    iface_entry->name = pa_xstrdup(info->name);
+    iface_entry->method_handlers = create_method_handlers(info);
+    iface_entry->property_handlers = create_property_handlers(info);
+    iface_entry->get_all_properties_cb = info->get_all_properties_cb;
+    iface_entry->signals = copy_signals(info);
+    iface_entry->n_signals = info->n_signals;
+    iface_entry->userdata = userdata;
+    pa_hashmap_put(obj_entry->interfaces, iface_entry->name, iface_entry);
+
+    update_introspection(obj_entry);
+
+    if (obj_entry_created)
+        register_object(p, obj_entry);
+
+    return 0;
+
+fail:
+    if (obj_entry_created) {
+        pa_hashmap_remove(p->objects, path);
+        pa_dbus_protocol_unref(p);
+        pa_xfree(obj_entry);
+    }
+
+    return -1;
+}
+
+static void unregister_object(pa_dbus_protocol *p, struct object_entry *obj_entry) {
+    struct connection_entry *conn_entry;
+    void *state = NULL;
+
+    pa_assert(p);
+    pa_assert(obj_entry);
+
+    while ((conn_entry = pa_hashmap_iterate(p->connections, &state, NULL)))
+        pa_assert_se(dbus_connection_unregister_object_path(conn_entry->connection, obj_entry->path));
+}
+
+static void method_handler_free_cb(void *p, void *userdata) {
+    pa_dbus_method_handler *h = p;
+    unsigned i;
+
+    pa_assert(h);
+
+    pa_xfree((char *) h->method_name);
+
+    for (i = 0; i < h->n_arguments; ++i) {
+        pa_xfree((char *) h->arguments[i].name);
+        pa_xfree((char *) h->arguments[i].type);
+        pa_xfree((char *) h->arguments[i].direction);
+    }
+
+    pa_xfree((pa_dbus_arg_info *) h->arguments);
+}
+
+static void property_handler_free_cb(void *p, void *userdata) {
+    pa_dbus_property_handler *h = p;
+
+    pa_assert(h);
+
+    pa_xfree((char *) h->property_name);
+    pa_xfree((char *) h->type);
+}
+
+int pa_dbus_protocol_remove_interface(pa_dbus_protocol *p, const char* path, const char* interface) {
+    struct object_entry *obj_entry;
+    struct interface_entry *iface_entry;
+    unsigned i;
+
+    pa_assert(p);
+    pa_assert(path);
+    pa_assert(interface);
+
+    if (!(obj_entry = pa_hashmap_get(p->objects, path)))
+        return -1;
+
+    if (!(iface_entry = pa_hashmap_remove(obj_entry->interfaces, interface)))
+        return -1;
+
+    update_introspection(obj_entry);
+
+    pa_xfree(iface_entry->name);
+    pa_hashmap_free(iface_entry->method_handlers, method_handler_free_cb, NULL);
+    pa_hashmap_free(iface_entry->property_handlers, property_handler_free_cb, NULL);
+
+    for (i = 0; i < iface_entry->n_signals; ++i) {
+        unsigned j;
+
+        pa_xfree((char *) iface_entry->signals[i].name);
+
+        for (j = 0; j < iface_entry->signals[i].n_arguments; ++j) {
+            pa_xfree((char *) iface_entry->signals[i].arguments[j].name);
+            pa_xfree((char *) iface_entry->signals[i].arguments[j].type);
+            pa_assert(iface_entry->signals[i].arguments[j].direction == NULL);
+        }
+
+        pa_xfree((pa_dbus_arg_info *) iface_entry->signals[i].arguments);
+    }
+
+    pa_xfree(iface_entry->signals);
+    pa_xfree(iface_entry);
+
+    if (pa_hashmap_isempty(obj_entry->interfaces)) {
+        unregister_object(p, obj_entry);
+
+        pa_hashmap_remove(p->objects, path);
+        pa_xfree(obj_entry->path);
+        pa_hashmap_free(obj_entry->interfaces, NULL, NULL);
+        pa_xfree(obj_entry->introspection);
+        pa_xfree(obj_entry);
+    }
+
+    return 0;
+}
+
+static void register_all_objects(pa_dbus_protocol *p, DBusConnection *conn) {
+    struct object_entry *obj_entry;
+    void *state = NULL;
+
+    pa_assert(p);
+    pa_assert(conn);
+
+    while ((obj_entry = pa_hashmap_iterate(p->objects, &state, NULL)))
+        pa_assert_se(dbus_connection_register_object_path(conn, obj_entry->path, &vtable, p));
+}
+
+int pa_dbus_protocol_register_connection(pa_dbus_protocol *p, DBusConnection *conn, pa_client *client) {
+    struct connection_entry *conn_entry;
+
+    pa_assert(p);
+    pa_assert(conn);
+    pa_assert(client);
+
+    if (pa_hashmap_get(p->connections, conn))
+        return -1; /* The connection was already registered. */
+
+    register_all_objects(p, conn);
+
+    conn_entry = pa_xnew(struct connection_entry, 1);
+    conn_entry->connection = dbus_connection_ref(conn);
+    conn_entry->client = client;
+    conn_entry->listening_for_all_signals = FALSE;
+    conn_entry->all_signals_objects = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+    conn_entry->listening_signals = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
+    pa_hashmap_put(p->connections, conn, conn_entry);
+
+    return 0;
+}
+
+static void unregister_all_objects(pa_dbus_protocol *p, DBusConnection *conn) {
+    struct object_entry *obj_entry;
+    void *state = NULL;
+
+    pa_assert(p);
+    pa_assert(conn);
+
+    while ((obj_entry = pa_hashmap_iterate(p->objects, &state, NULL)))
+        pa_assert_se(dbus_connection_unregister_object_path(conn, obj_entry->path));
+}
+
+static void free_listened_object_name_cb(void *p, void *userdata) {
+    pa_assert(p);
+
+    pa_xfree(p);
+}
+
+static void free_listening_signals_idxset_cb(void *p, void *userdata) {
+    pa_idxset *set = p;
+
+    pa_assert(set);
+
+    pa_idxset_free(set, free_listened_object_name_cb, NULL);
+}
+
+int pa_dbus_protocol_unregister_connection(pa_dbus_protocol *p, DBusConnection *conn) {
+    struct connection_entry *conn_entry;
+
+    pa_assert(p);
+    pa_assert(conn);
+
+    if (!(conn_entry = pa_hashmap_remove(p->connections, conn)))
+        return -1;
+
+    unregister_all_objects(p, conn);
+
+    dbus_connection_unref(conn_entry->connection);
+    pa_idxset_free(conn_entry->all_signals_objects, free_listened_object_name_cb, NULL);
+    pa_hashmap_free(conn_entry->listening_signals, free_listening_signals_idxset_cb, NULL);
+    pa_xfree(conn_entry);
+
+    return 0;
+}
+
+void pa_dbus_protocol_add_signal_listener(pa_dbus_protocol *p, DBusConnection *conn, const char *signal, char **objects, unsigned n_objects) {
+    struct connection_entry *conn_entry;
+    pa_idxset *object_set;
+    char *object_path;
+    unsigned i;
+
+    pa_assert(p);
+    pa_assert(conn);
+    pa_assert(objects || n_objects == 0);
+
+    pa_assert_se((conn_entry = pa_hashmap_get(p->connections, conn)));
+
+    /* all_signals_objects will either be emptied or replaced with new objects,
+     * so we empty it here unconditionally. If listening_for_all_signals is
+     * currently FALSE, the idxset is empty already. */
+    while ((object_path = pa_idxset_steal_first(conn_entry->all_signals_objects, NULL)))
+        pa_xfree(object_path);
+
+    if (signal) {
+        conn_entry->listening_for_all_signals = FALSE;
+
+        /* Replace the old object list with a new one. */
+        if ((object_set = pa_hashmap_get(conn_entry->listening_signals, signal)))
+            pa_idxset_free(object_set, free_listened_object_name_cb, NULL);
+        object_set = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
+        for (i = 0; i < n_objects; ++i)
+            pa_idxset_put(object_set, pa_xstrdup(objects[i]), NULL);
+
+    } else {
+        conn_entry->listening_for_all_signals = TRUE;
+
+        /* We're not interested in individual signals anymore, so let's empty
+         * listening_signals. */
+        while ((object_set = pa_hashmap_steal_first(conn_entry->listening_signals)))
+            pa_idxset_free(object_set, free_listened_object_name_cb, NULL);
+
+        for (i = 0; i < n_objects; ++i)
+            pa_idxset_put(conn_entry->all_signals_objects, pa_xstrdup(objects[i]), NULL);
+    }
+}
+
+void pa_dbus_protocol_remove_signal_listener(pa_dbus_protocol *p, DBusConnection *conn, const char *signal) {
+    struct connection_entry *conn_entry;
+    pa_idxset *object_set;
+
+    pa_assert(p);
+    pa_assert(conn);
+
+    pa_assert_se((conn_entry = pa_hashmap_get(p->connections, conn)));
+
+    if (signal) {
+        if ((object_set = pa_hashmap_get(conn_entry->listening_signals, signal)))
+            pa_idxset_free(object_set, free_listened_object_name_cb, NULL);
+
+    } else {
+        char *object_path;
+
+        conn_entry->listening_for_all_signals = FALSE;
+
+        while ((object_path = pa_idxset_steal_first(conn_entry->all_signals_objects, NULL)))
+            pa_xfree(object_path);
+
+        while ((object_set = pa_hashmap_steal_first(conn_entry->listening_signals)))
+            pa_idxset_free(object_set, free_listened_object_name_cb, NULL);
+    }
+}
+
+void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal) {
+    struct connection_entry *conn_entry;
+    void *state = NULL;
+    pa_idxset *object_set;
+    DBusMessage *signal_copy;
+
+    pa_assert(p);
+    pa_assert(signal);
+    pa_assert(dbus_message_get_type(signal) == DBUS_MESSAGE_TYPE_SIGNAL);
+
+    /* XXX: We have to do some linear searching to find connections that want
+     * to receive the signal. This shouldn't be very significant performance
+     * problem, and adding an (object path, signal name) -> connection mapping
+     * would be likely to create substantial complexity. */
+
+    while ((conn_entry = pa_hashmap_iterate(p->connections, &state, NULL))) {
+
+        if ((conn_entry->listening_for_all_signals /* Case 1: listening for all signals */
+             && (pa_idxset_get_by_data(conn_entry->all_signals_objects, dbus_message_get_path(signal), NULL)
+                 || pa_idxset_isempty(conn_entry->all_signals_objects)))
+
+            || (!conn_entry->listening_for_all_signals /* Case 2: not listening for all signals */
+                && (object_set = pa_hashmap_get(conn_entry->listening_signals, signal))
+                && (pa_idxset_get_by_data(object_set, dbus_message_get_path(signal), NULL)
+                    || pa_idxset_isempty(object_set)))) {
+
+            pa_assert_se(signal_copy = dbus_message_copy(signal));
+            pa_assert_se(dbus_connection_send(conn_entry->connection, signal_copy, NULL));
+            dbus_message_unref(signal_copy);
+        }
+    }
+}
+
+pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn) {
+    struct connection_entry *conn_entry;
+
+    pa_assert(p);
+    pa_assert(conn);
+
+    if (!(conn_entry = pa_hashmap_get(p->connections, conn)))
+        return NULL;
+
+    return conn_entry->client;
+}
+
+const char **pa_dbus_protocol_get_extensions(pa_dbus_protocol *p, unsigned *n) {
+    const char **extensions;
+    const char *ext_name;
+    void *state = NULL;
+    unsigned i = 0;
+
+    pa_assert(p);
+    pa_assert(n);
+
+    *n = pa_hashmap_size(p->extensions);
+
+    if (*n <= 0)
+        return NULL;
+
+    extensions = pa_xnew(const char *, *n);
+
+    while (pa_hashmap_iterate(p->extensions, &state, (const void **) &ext_name)) {
+        extensions[i] = ext_name;
+        ++i;
+    }
+
+    return extensions;
+}
diff --git a/src/pulsecore/protocol-dbus.h b/src/pulsecore/protocol-dbus.h
new file mode 100644
index 0000000..27198f4
--- /dev/null
+++ b/src/pulsecore/protocol-dbus.h
@@ -0,0 +1,158 @@
+#ifndef fooprotocoldbushfoo
+#define fooprotocoldbushfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <dbus/dbus.h>
+
+#include <pulsecore/core.h>
+#include <pulsecore/macro.h>
+
+#define PA_DBUS_DEFAULT_PORT 24883
+#define PA_DBUS_SOCKET_NAME "dbus-socket"
+
+#define PA_DBUS_SYSTEM_SOCKET_PATH PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_DBUS_SOCKET_NAME
+
+#define PA_DBUS_ERROR_NO_SUCH_PROPERTY "org.PulseAudio.Core1.NoSuchPropertyError"
+#define PA_DBUS_ERROR_NOT_FOUND "org.PulseAudio.Core1.NotFoundError"
+
+/* Returns the default address of the server type in the escaped form. For
+ * PA_SERVER_TYPE_NONE an empty string is returned. The caller frees the
+ * string. */
+char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type);
+
+typedef struct pa_dbus_protocol pa_dbus_protocol;
+
+/* This function either creates a new pa_dbus_protocol object, or if one
+ * already exists, increases the reference count. */
+pa_dbus_protocol* pa_dbus_protocol_get(pa_core *c);
+
+pa_dbus_protocol* pa_dbus_protocol_ref(pa_dbus_protocol *p);
+void pa_dbus_protocol_unref(pa_dbus_protocol *p);
+
+/* Called when a received message needs handling. Completely ignoring the
+ * message isn't a good idea; if you can't handle the message, reply with an
+ * error.
+ *
+ * All messages are method calls. */
+typedef void (*pa_dbus_receive_cb_t)(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+typedef struct pa_dbus_arg_info {
+    const char *name;
+    const char *type;
+    const char *direction; /* NULL for signal arguments. */
+} pa_dbus_arg_info;
+
+typedef struct pa_dbus_signal_info {
+    const char *name;
+    const pa_dbus_arg_info *arguments; /* NULL, if the signal has no args. */
+    unsigned n_arguments;
+} pa_dbus_signal_info;
+
+typedef struct pa_dbus_method_handler {
+    const char *method_name;
+    const pa_dbus_arg_info *arguments; /* NULL, if the method has no args. */
+    unsigned n_arguments;
+    pa_dbus_receive_cb_t receive_cb;
+} pa_dbus_method_handler;
+
+typedef struct pa_dbus_property_handler {
+    const char *property_name;
+    const char *type;
+
+    /* The access mode for the property is determined by checking whether
+     * get_cb or set_cb is NULL. */
+    pa_dbus_receive_cb_t get_cb;
+    pa_dbus_receive_cb_t set_cb;
+} pa_dbus_property_handler;
+
+typedef struct pa_dbus_interface_info {
+    const char* name;
+    const pa_dbus_method_handler *method_handlers; /* NULL, if the interface has no methods. */
+    unsigned n_method_handlers;
+    const pa_dbus_property_handler *property_handlers; /* NULL, if the interface has no properties. */
+    unsigned n_property_handlers;
+    const pa_dbus_receive_cb_t get_all_properties_cb; /* May be NULL, in which case GetAll returns an error. */
+    const pa_dbus_signal_info *signals; /* NULL, if the interface has no signals. */
+    unsigned n_signals;
+} pa_dbus_interface_info;
+
+
+/* The following functions may only be called from the main thread. */
+
+/* Registers the given interface to the given object path. It doesn't matter
+ * whether or not the object has already been registered; if it is, then its
+ * interface set is extended.
+ *
+ * Introspection requests are handled automatically.
+ *
+ * Userdata is passed to all the callbacks.
+ *
+ * Fails and returns a negative number if the object already has the interface
+ * registered. */
+int pa_dbus_protocol_add_interface(pa_dbus_protocol *p, const char *path, const pa_dbus_interface_info *info, void *userdata);
+
+/* Returns a negative number if the given object doesn't have the given
+ * interface registered. */
+int pa_dbus_protocol_remove_interface(pa_dbus_protocol *p, const char* path, const char* interface);
+
+/* Fails and returns a negative number if the connection is already
+ * registered. */
+int pa_dbus_protocol_register_connection(pa_dbus_protocol *p, DBusConnection *conn, pa_client *client);
+
+/* Returns a negative number if the connection wasn't registered. */
+int pa_dbus_protocol_unregister_connection(pa_dbus_protocol *p, DBusConnection *conn);
+
+/* Enables signal receiving for the given connection. The connection must have
+ * been registered earlier.
+ *
+ * If the signal argument is NULL, all signals will be sent to the connection,
+ * otherwise calling this function only adds the given signal to the list of
+ * signals that will be delivered to the connection.
+ *
+ * The objects argument is a list of object paths. If the list is not empty,
+ * only signals from the given objects are delivered. If this function is
+ * called multiple time for the same connection and signal, the latest call
+ * always replaces the previous object list. */
+void pa_dbus_protocol_add_signal_listener(pa_dbus_protocol *p, DBusConnection *conn, const char *signal, char **objects, unsigned n_objects);
+
+/* Disables the delivery of the signal for the given connection. The connection
+ * must have been registered. If signal is NULL, all signals are disabled. If
+ * signal is non-NULL and _add_signal_listener() was previously called with
+ * NULL signal (causing all signals to be enabled), this function doesn't do
+ * anything. Also, if the signal wasn't enabled before, this function doesn't
+ * do anything in that case either. */
+void pa_dbus_protocol_remove_signal_listener(pa_dbus_protocol *p, DBusConnection *conn, const char *signal);
+
+void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal);
+
+/* Returns NULL if the connection isn't registered. */
+pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn);
+
+/* Returns an array of extension identifier strings. The strings pointers point
+ * to the internal copies, so don't free the strings. The caller must free the
+ * array, however. Also, do not save the returned pointer or any of the string
+ * pointers, because the contained strings may be freed at any time. If you
+ * need to save the array, copy it. */
+const char **pa_dbus_protocol_get_extensions(pa_dbus_protocol *p, unsigned *n);
+
+#endif

commit 9a77d2f81d693d64b63d38be7214626dedf91e11
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Tue Jul 21 00:04:52 2009 +0300

    Add the forgotten src/modules/dbus directory to git.

diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c
new file mode 100644
index 0000000..db6aa26
--- /dev/null
+++ b/src/modules/dbus/iface-card.c
@@ -0,0 +1,61 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/core-util.h>
+
+#include "iface-card.h"
+
+#define OBJECT_NAME "card"
+
+struct pa_dbusiface_card {
+    pa_card *card;
+    char *path;
+};
+
+pa_dbusiface_card *pa_dbusiface_card_new(pa_card *card, const char *path_prefix) {
+    pa_dbusiface_card *c;
+
+    pa_assert(card);
+    pa_assert(path_prefix);
+
+    c = pa_xnew(pa_dbusiface_card, 1);
+    c->card = card;
+    c->path = pa_sprintf_malloc("%s/%s%u", path_prefix, OBJECT_NAME, card->index);
+
+    return c;
+}
+
+void pa_dbusiface_card_free(pa_dbusiface_card *c) {
+    pa_assert(c);
+
+    pa_xfree(c->path);
+    pa_xfree(c);
+}
+
+const char *pa_dbusiface_card_get_path(pa_dbusiface_card *c) {
+    pa_assert(c);
+
+    return c->path;
+}
diff --git a/src/modules/dbus/iface-card.h b/src/modules/dbus/iface-card.h
new file mode 100644
index 0000000..54db610
--- /dev/null
+++ b/src/modules/dbus/iface-card.h
@@ -0,0 +1,40 @@
+#ifndef foodbusifacecardhfoo
+#define foodbusifacecardhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+/* This object implements the D-Bus interface org.PulseAudio.Core1.Card.
+ *
+ * See http://pulseaudio.org/wiki/DBusInterface for the Card interface
+ * documentation.
+ */
+
+#include <pulsecore/card.h>
+
+typedef struct pa_dbusiface_card pa_dbusiface_card;
+
+pa_dbusiface_card *pa_dbusiface_card_new(pa_card *card, const char *path_prefix);
+void pa_dbusiface_card_free(pa_dbusiface_card *c);
+
+const char *pa_dbusiface_card_get_path(pa_dbusiface_card *c);
+
+#endif
diff --git a/src/modules/dbus/iface-client.c b/src/modules/dbus/iface-client.c
new file mode 100644
index 0000000..cfa36d0
--- /dev/null
+++ b/src/modules/dbus/iface-client.c
@@ -0,0 +1,61 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/core-util.h>
+
+#include "iface-client.h"
+
+#define OBJECT_NAME "client"
+
+struct pa_dbusiface_client {
+    pa_client *client;
+    char *path;
+};
+
+pa_dbusiface_client *pa_dbusiface_client_new(pa_client *client, const char *path_prefix) {
+    pa_dbusiface_client *c;
+
+    pa_assert(client);
+    pa_assert(path_prefix);
+
+    c = pa_xnew(pa_dbusiface_client, 1);
+    c->client = client;
+    c->path = pa_sprintf_malloc("%s/%s%u", path_prefix, OBJECT_NAME, client->index);
+
+    return c;
+}
+
+void pa_dbusiface_client_free(pa_dbusiface_client *c) {
+    pa_assert(c);
+
+    pa_xfree(c->path);
+    pa_xfree(c);
+}
+
+const char *pa_dbusiface_client_get_path(pa_dbusiface_client *c) {
+    pa_assert(c);
+
+    return c->path;
+}
diff --git a/src/modules/dbus/iface-client.h b/src/modules/dbus/iface-client.h
new file mode 100644
index 0000000..62cca7f
--- /dev/null
+++ b/src/modules/dbus/iface-client.h
@@ -0,0 +1,40 @@
+#ifndef foodbusifaceclienthfoo
+#define foodbusifaceclienthfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+/* This object implements the D-Bus interface org.PulseAudio.Core1.Card.
+ *
+ * See http://pulseaudio.org/wiki/DBusInterface for the Card interface
+ * documentation.
+ */
+
+#include <pulsecore/client.h>
+
+typedef struct pa_dbusiface_client pa_dbusiface_client;
+
+pa_dbusiface_client *pa_dbusiface_client_new(pa_client *client, const char *path_prefix);
+void pa_dbusiface_client_free(pa_dbusiface_client *c);
+
+const char *pa_dbusiface_client_get_path(pa_dbusiface_client *c);
+
+#endif
diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c
new file mode 100644
index 0000000..31f1260
--- /dev/null
+++ b/src/modules/dbus/iface-core.c
@@ -0,0 +1,1968 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ctype.h>
+
+#include <dbus/dbus.h>
+
+#include <pulse/utf8.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-util.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/protocol-dbus.h>
+#include <pulsecore/socket-util.h>
+#include <pulsecore/strbuf.h>
+
+#include "iface-card.h"
+#include "iface-client.h"
+#include "iface-device.h"
+#include "iface-module.h"
+#include "iface-sample.h"
+#include "iface-stream.h"
+
+#include "iface-core.h"
+
+#define OBJECT_PATH "/org/pulseaudio/core1"
+#define INTERFACE_CORE "org.PulseAudio.Core1"
+
+#define INTERFACE_REVISION 0
+
+
+
+static void handle_get_interface_revision(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_version(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_is_local(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_username(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_hostname(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_default_channels(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_default_channels(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_default_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_default_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_default_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_default_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_cards(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sinks(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_fallback_sink(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sources(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_fallback_source(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_playback_streams(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_record_streams(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_samples(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_modules(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_clients(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_my_client(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_extensions(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_card_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sink_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_source_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sample_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_exit(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_listen_for_signal(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_stop_listening_for_signal(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+struct pa_dbusiface_core {
+    pa_core *core;
+    pa_subscription *subscription;
+
+    pa_dbus_protocol *dbus_protocol;
+
+    pa_hashmap *cards;
+    pa_hashmap *sinks_by_index;
+    pa_hashmap *sinks_by_path;
+    pa_hashmap *sources_by_index;
+    pa_hashmap *sources_by_path;
+    pa_hashmap *playback_streams;
+    pa_hashmap *record_streams;
+    pa_hashmap *samples;
+    pa_hashmap *modules;
+    pa_hashmap *clients;
+
+    pa_sink *fallback_sink;
+    pa_source *fallback_source;
+};
+
+enum property_handler_index {
+    PROPERTY_HANDLER_INTERFACE_REVISION,
+    PROPERTY_HANDLER_NAME,
+    PROPERTY_HANDLER_VERSION,
+    PROPERTY_HANDLER_IS_LOCAL,
+    PROPERTY_HANDLER_USERNAME,
+    PROPERTY_HANDLER_HOSTNAME,
+    PROPERTY_HANDLER_DEFAULT_CHANNELS,
+    PROPERTY_HANDLER_DEFAULT_SAMPLE_FORMAT,
+    PROPERTY_HANDLER_DEFAULT_SAMPLE_RATE,
+    PROPERTY_HANDLER_CARDS,
+    PROPERTY_HANDLER_SINKS,
+    PROPERTY_HANDLER_FALLBACK_SINK,
+    PROPERTY_HANDLER_SOURCES,
+    PROPERTY_HANDLER_FALLBACK_SOURCE,
+    PROPERTY_HANDLER_PLAYBACK_STREAMS,
+    PROPERTY_HANDLER_RECORD_STREAMS,
+    PROPERTY_HANDLER_SAMPLES,
+    PROPERTY_HANDLER_MODULES,
+    PROPERTY_HANDLER_CLIENTS,
+    PROPERTY_HANDLER_MY_CLIENT,
+    PROPERTY_HANDLER_EXTENSIONS,
+    PROPERTY_HANDLER_MAX
+};
+
+static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
+    [PROPERTY_HANDLER_INTERFACE_REVISION]    = { .property_name = "InterfaceRevision",   .type = "u",  .get_cb = handle_get_interface_revision,    .set_cb = NULL },
+    [PROPERTY_HANDLER_NAME]                  = { .property_name = "Name",                .type = "s",  .get_cb = handle_get_name,                  .set_cb = NULL },
+    [PROPERTY_HANDLER_VERSION]               = { .property_name = "Version",             .type = "s",  .get_cb = handle_get_version,               .set_cb = NULL },
+    [PROPERTY_HANDLER_IS_LOCAL]              = { .property_name = "IsLocal",             .type = "b",  .get_cb = handle_get_is_local,              .set_cb = NULL },
+    [PROPERTY_HANDLER_USERNAME]              = { .property_name = "Username",            .type = "s",  .get_cb = handle_get_username,              .set_cb = NULL },
+    [PROPERTY_HANDLER_HOSTNAME]              = { .property_name = "Hostname",            .type = "s",  .get_cb = handle_get_hostname,              .set_cb = NULL },
+    [PROPERTY_HANDLER_DEFAULT_CHANNELS]      = { .property_name = "DefaultChannels",     .type = "au", .get_cb = handle_get_default_channels,      .set_cb = handle_set_default_channels },
+    [PROPERTY_HANDLER_DEFAULT_SAMPLE_FORMAT] = { .property_name = "DefaultSampleFormat", .type = "u",  .get_cb = handle_get_default_sample_format, .set_cb = handle_set_default_sample_format },
+    [PROPERTY_HANDLER_DEFAULT_SAMPLE_RATE]   = { .property_name = "DefaultSampleRate",   .type = "u",  .get_cb = handle_get_default_sample_rate,   .set_cb = handle_set_default_sample_rate },
+    [PROPERTY_HANDLER_CARDS]                 = { .property_name = "Cards",               .type = "ao", .get_cb = handle_get_cards,                 .set_cb = NULL },
+    [PROPERTY_HANDLER_SINKS]                 = { .property_name = "Sinks",               .type = "ao", .get_cb = handle_get_sinks,                 .set_cb = NULL },
+    [PROPERTY_HANDLER_FALLBACK_SINK]         = { .property_name = "FallbackSink",        .type = "o",  .get_cb = handle_get_fallback_sink,         .set_cb = handle_set_fallback_sink },
+    [PROPERTY_HANDLER_SOURCES]               = { .property_name = "Sources",             .type = "ao", .get_cb = handle_get_sources,               .set_cb = NULL },
+    [PROPERTY_HANDLER_FALLBACK_SOURCE]       = { .property_name = "FallbackSource",      .type = "o",  .get_cb = handle_get_fallback_source,       .set_cb = handle_set_fallback_source },
+    [PROPERTY_HANDLER_PLAYBACK_STREAMS]      = { .property_name = "PlaybackStreams",     .type = "ao", .get_cb = handle_get_playback_streams,      .set_cb = NULL },
+    [PROPERTY_HANDLER_RECORD_STREAMS]        = { .property_name = "RecordStreams",       .type = "ao", .get_cb = handle_get_record_streams,        .set_cb = NULL },
+    [PROPERTY_HANDLER_SAMPLES]               = { .property_name = "Samples",             .type = "ao", .get_cb = handle_get_samples,               .set_cb = NULL },
+    [PROPERTY_HANDLER_MODULES]               = { .property_name = "Modules",             .type = "ao", .get_cb = handle_get_modules,               .set_cb = NULL },
+    [PROPERTY_HANDLER_CLIENTS]               = { .property_name = "Clients",             .type = "ao", .get_cb = handle_get_clients,               .set_cb = NULL },
+    [PROPERTY_HANDLER_MY_CLIENT]             = { .property_name = "MyClient",            .type = "o",  .get_cb = handle_get_my_client,             .set_cb = NULL },
+    [PROPERTY_HANDLER_EXTENSIONS]            = { .property_name = "Extensions",          .type = "as", .get_cb = handle_get_extensions,            .set_cb = NULL }
+};
+
+enum method_handler_index {
+    METHOD_HANDLER_GET_CARD_BY_NAME,
+    METHOD_HANDLER_GET_SINK_BY_NAME,
+    METHOD_HANDLER_GET_SOURCE_BY_NAME,
+    METHOD_HANDLER_GET_SAMPLE_BY_NAME,
+    METHOD_HANDLER_UPLOAD_SAMPLE,
+    METHOD_HANDLER_LOAD_MODULE,
+    METHOD_HANDLER_EXIT,
+    METHOD_HANDLER_LISTEN_FOR_SIGNAL,
+    METHOD_HANDLER_STOP_LISTENING_FOR_SIGNAL,
+    METHOD_HANDLER_MAX
+};
+
+static pa_dbus_arg_info get_card_by_name_args[] = { { "name", "s", "in" }, { "card", "o", "out" } };
+static pa_dbus_arg_info get_sink_by_name_args[] = { { "name", "s", "in" }, { "sink", "o", "out" } };
+static pa_dbus_arg_info get_source_by_name_args[] = { { "name", "s", "in" }, { "source", "o", "out" } };
+static pa_dbus_arg_info get_sample_by_name_args[] = { { "name", "s", "in" }, { "sample", "o", "out" } };
+static pa_dbus_arg_info upload_sample_args[] = { { "name",           "s",      "in" },
+                                                 { "sample_format",  "u",      "in" },
+                                                 { "sample_rate",    "u",      "in" },
+                                                 { "channels",       "au",     "in" },
+                                                 { "default_volume", "au",     "in" },
+                                                 { "property_list",  "a{say}", "in" },
+                                                 { "data",           "ay",     "in" },
+                                                 { "sample",         "o",      "out" } };
+static pa_dbus_arg_info load_module_args[] = { { "name", "s", "in" }, { "arguments", "a{ss}", "in" }, { "module", "o", "out" } };
+static pa_dbus_arg_info listen_for_signal_args[] = { { "signal", "s", "in" }, { "objects", "ao", "in" } };
+static pa_dbus_arg_info stop_listening_for_signal_args[] = { { "signal", "s", "in" } };
+
+static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
+    [METHOD_HANDLER_GET_CARD_BY_NAME] = {
+        .method_name = "GetCardByName",
+        .arguments = get_card_by_name_args,
+        .n_arguments = sizeof(get_card_by_name_args) / sizeof(pa_dbus_arg_info),
+        .receive_cb = handle_get_card_by_name },
+    [METHOD_HANDLER_GET_SINK_BY_NAME] = {
+        .method_name = "GetSinkByName",
+        .arguments = get_sink_by_name_args,
+        .n_arguments = sizeof(get_sink_by_name_args) / sizeof(pa_dbus_arg_info),
+        .receive_cb = handle_get_sink_by_name },
+    [METHOD_HANDLER_GET_SOURCE_BY_NAME] = {
+        .method_name = "GetSourceByName",
+        .arguments = get_source_by_name_args,
+        .n_arguments = sizeof(get_source_by_name_args) / sizeof(pa_dbus_arg_info),
+        .receive_cb = handle_get_source_by_name },
+    [METHOD_HANDLER_GET_SAMPLE_BY_NAME] = {
+        .method_name = "GetSampleByName",
+        .arguments = get_sample_by_name_args,
+        .n_arguments = sizeof(get_sample_by_name_args) / sizeof(pa_dbus_arg_info),
+        .receive_cb = handle_get_sample_by_name },
+    [METHOD_HANDLER_UPLOAD_SAMPLE] = {
+        .method_name = "UploadSample",
+        .arguments = upload_sample_args,
+        .n_arguments = sizeof(upload_sample_args) / sizeof(pa_dbus_arg_info),
+        .receive_cb = handle_upload_sample },
+    [METHOD_HANDLER_LOAD_MODULE] = {
+        .method_name = "LoadModule",
+        .arguments = upload_sample_args,
+        .n_arguments = sizeof(load_module_args) / sizeof(pa_dbus_arg_info),
+        .receive_cb = handle_load_module },
+    [METHOD_HANDLER_EXIT] = {
+        .method_name = "Exit",
+        .arguments = NULL,
+        .n_arguments = 0,
+        .receive_cb = handle_exit },
+    [METHOD_HANDLER_LISTEN_FOR_SIGNAL] = {
+        .method_name = "ListenForSignal",
+        .arguments = listen_for_signal_args,
+        .n_arguments = sizeof(listen_for_signal_args) / sizeof(pa_dbus_arg_info),
+        .receive_cb = handle_listen_for_signal },
+    [METHOD_HANDLER_STOP_LISTENING_FOR_SIGNAL] = {
+        .method_name = "StopListeningForSignal",
+        .arguments = stop_listening_for_signal_args,
+        .n_arguments = sizeof(stop_listening_for_signal_args) / sizeof(pa_dbus_arg_info),
+        .receive_cb = handle_stop_listening_for_signal }
+};
+
+enum signal_index {
+    SIGNAL_NEW_CARD,
+    SIGNAL_CARD_REMOVED,
+    SIGNAL_NEW_SINK,
+    SIGNAL_SINK_REMOVED,
+    SIGNAL_FALLBACK_SINK_UPDATED,
+    SIGNAL_NEW_SOURCE,
+    SIGNAL_SOURCE_REMOVED,
+    SIGNAL_FALLBACK_SOURCE_UPDATED,
+    SIGNAL_NEW_PLAYBACK_STREAM,
+    SIGNAL_PLAYBACK_STREAM_REMOVED,
+    SIGNAL_NEW_RECORD_STREAM,
+    SIGNAL_RECORD_STREAM_REMOVED,
+    SIGNAL_NEW_SAMPLE,
+    SIGNAL_SAMPLE_REMOVED,
+    SIGNAL_NEW_MODULE,
+    SIGNAL_MODULE_REMOVED,
+    SIGNAL_NEW_CLIENT,
+    SIGNAL_CLIENT_REMOVED,
+    SIGNAL_MAX
+};
+
+static pa_dbus_arg_info new_card_args[] =                { { "card",            "o", NULL } };
+static pa_dbus_arg_info card_removed_args[] =            { { "card",            "o", NULL } };
+static pa_dbus_arg_info new_sink_args[] =                { { "sink",            "o", NULL } };
+static pa_dbus_arg_info sink_removed_args[] =            { { "sink",            "o", NULL } };
+static pa_dbus_arg_info fallback_sink_updated_args[] =   { { "sink",            "o", NULL } };
+static pa_dbus_arg_info new_source_args[] =              { { "source",          "o", NULL } };
+static pa_dbus_arg_info source_removed_args[] =          { { "source",          "o", NULL } };
+static pa_dbus_arg_info fallback_source_updated_args[] = { { "source",          "o", NULL } };
+static pa_dbus_arg_info new_playback_stream_args[] =     { { "playback_stream", "o", NULL } };
+static pa_dbus_arg_info playback_stream_removed_args[] = { { "playback_stream", "o", NULL } };
+static pa_dbus_arg_info new_record_stream_args[] =       { { "record_stream",   "o", NULL } };
+static pa_dbus_arg_info record_stream_removed_args[] =   { { "record_stream",   "o", NULL } };
+static pa_dbus_arg_info new_sample_args[] =              { { "sample",          "o", NULL } };
+static pa_dbus_arg_info sample_removed_args[] =          { { "sample",          "o", NULL } };
+static pa_dbus_arg_info new_module_args[] =              { { "module",          "o", NULL } };
+static pa_dbus_arg_info module_removed_args[] =          { { "module",          "o", NULL } };
+static pa_dbus_arg_info new_client_args[] =              { { "client",          "o", NULL } };
+static pa_dbus_arg_info client_removed_args[] =          { { "client",          "o", NULL } };
+
+static pa_dbus_signal_info signals[SIGNAL_MAX] = {
+    [SIGNAL_NEW_CARD]                = { .name = "NewCard",               .arguments = new_card_args,                .n_arguments = 1 },
+    [SIGNAL_CARD_REMOVED]            = { .name = "CardRemoved",           .arguments = card_removed_args,            .n_arguments = 1 },
+    [SIGNAL_NEW_SINK]                = { .name = "NewSink",               .arguments = new_sink_args,                .n_arguments = 1 },
+    [SIGNAL_SINK_REMOVED]            = { .name = "SinkRemoved",           .arguments = sink_removed_args,            .n_arguments = 1 },
+    [SIGNAL_FALLBACK_SINK_UPDATED]   = { .name = "FallbackSinkUpdated",   .arguments = fallback_sink_updated_args,   .n_arguments = 1 },
+    [SIGNAL_NEW_SOURCE]              = { .name = "NewSource",             .arguments = new_source_args,              .n_arguments = 1 },
+    [SIGNAL_SOURCE_REMOVED]          = { .name = "SourceRemoved",         .arguments = source_removed_args,          .n_arguments = 1 },
+    [SIGNAL_FALLBACK_SOURCE_UPDATED] = { .name = "FallbackSourceUpdated", .arguments = fallback_source_updated_args, .n_arguments = 1 },
+    [SIGNAL_NEW_PLAYBACK_STREAM]     = { .name = "NewPlaybackStream",     .arguments = new_playback_stream_args,     .n_arguments = 1 },
+    [SIGNAL_PLAYBACK_STREAM_REMOVED] = { .name = "PlaybackStreamRemoved", .arguments = playback_stream_removed_args, .n_arguments = 1 },
+    [SIGNAL_NEW_RECORD_STREAM]       = { .name = "NewRecordStream",       .arguments = new_record_stream_args,       .n_arguments = 1 },
+    [SIGNAL_RECORD_STREAM_REMOVED]   = { .name = "RecordStreamRemoved",   .arguments = record_stream_removed_args,   .n_arguments = 1 },
+    [SIGNAL_NEW_SAMPLE]              = { .name = "NewSample",             .arguments = new_sample_args,              .n_arguments = 1 },
+    [SIGNAL_SAMPLE_REMOVED]          = { .name = "SampleRemoved",         .arguments = sample_removed_args,          .n_arguments = 1 },
+    [SIGNAL_NEW_MODULE]              = { .name = "NewModule",             .arguments = new_module_args,              .n_arguments = 1 },
+    [SIGNAL_MODULE_REMOVED]          = { .name = "ModuleRemoved",         .arguments = module_removed_args,          .n_arguments = 1 },
+    [SIGNAL_NEW_CLIENT]              = { .name = "NewClient",             .arguments = new_client_args,              .n_arguments = 1 },
+    [SIGNAL_CLIENT_REMOVED]          = { .name = "ClientRemoved",         .arguments = client_removed_args,          .n_arguments = 1 },
+};
+
+static pa_dbus_interface_info core_interface_info = {
+    .name = INTERFACE_CORE,
+    .method_handlers = method_handlers,
+    .n_method_handlers = METHOD_HANDLER_MAX,
+    .property_handlers = property_handlers,
+    .n_property_handlers = PROPERTY_HANDLER_MAX,
+    .get_all_properties_cb = handle_get_all,
+    .signals = signals,
+    .n_signals = SIGNAL_MAX
+};
+
+static void handle_get_interface_revision(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    dbus_uint32_t interface_revision = INTERFACE_REVISION;
+
+    pa_assert(conn);
+    pa_assert(msg);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &interface_revision);
+}
+
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    const char *server_name = PACKAGE_NAME;
+
+    pa_assert(conn);
+    pa_assert(msg);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &server_name);
+}
+
+static void handle_get_version(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    const char *version = PACKAGE_VERSION;
+
+    pa_assert(conn);
+    pa_assert(msg);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &version);
+}
+
+static dbus_bool_t get_is_local(DBusConnection *conn) {
+    int conn_fd;
+
+    pa_assert(conn);
+
+    if (!dbus_connection_get_socket(conn, &conn_fd))
+        return FALSE;
+
+    return pa_socket_is_local(conn_fd);
+}
+
+static void handle_get_is_local(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    dbus_bool_t is_local;
+
+    pa_assert(conn);
+    pa_assert(msg);
+
+    is_local = get_is_local(conn);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &is_local);
+}
+
+static void handle_get_username(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    char *username = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+
+    username = pa_get_user_name_malloc();
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &username);
+
+    pa_xfree(username);
+}
+
+static void handle_get_hostname(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    char *hostname = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+
+    hostname = pa_get_host_name_malloc();
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &hostname);
+
+    pa_xfree(hostname);
+}
+
+/* Caller frees the returned array. */
+static dbus_uint32_t *get_default_channels(pa_dbusiface_core *c, unsigned *n) {
+    dbus_uint32_t *default_channels = NULL;
+    unsigned i;
+
+    pa_assert(c);
+    pa_assert(n);
+
+    *n = c->core->default_channel_map.channels;
+    default_channels = pa_xnew(dbus_uint32_t, *n);
+
+    for (i = 0; i < *n; ++i)
+        default_channels[i] = c->core->default_channel_map.map[i];
+
+    return default_channels;
+}
+
+static void handle_get_default_channels(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    dbus_uint32_t *default_channels = NULL;
+    unsigned n;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    default_channels = get_default_channels(c, &n);
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, default_channels, n);
+
+    pa_xfree(default_channels);
+}
+
+static void handle_set_default_channels(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    pa_channel_map new_channel_map;
+    DBusMessageIter msg_iter;
+    DBusMessageIter variant_iter;
+    DBusMessageIter array_iter;
+    dbus_uint32_t *default_channels;
+    int n_channels;
+    unsigned i;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    pa_channel_map_init(&new_channel_map);
+
+    pa_assert_se(dbus_message_iter_init(msg, &msg_iter));
+
+    /* Skip the interface and property name arguments. */
+    if (!dbus_message_iter_next(&msg_iter) || !dbus_message_iter_next(&msg_iter)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments.");
+        return;
+    }
+
+    if (dbus_message_iter_get_arg_type(&msg_iter) != DBUS_TYPE_VARIANT) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Message argument isn't a variant.");
+        return;
+    }
+
+    dbus_message_iter_recurse(&msg_iter, &variant_iter);
+
+    if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_ARRAY) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Variant doesn't contain an array.");
+        return;
+    }
+
+    dbus_message_iter_recurse(&variant_iter, &array_iter);
+
+    if (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_UINT32) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Array type is not uint32.");
+        return;
+    }
+
+    dbus_message_iter_get_fixed_array(&array_iter, &default_channels, &n_channels);
+
+    if (n_channels <= 0) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Empty channel array.");
+        return;
+    }
+
+    new_channel_map.channels = n_channels;
+
+    for (i = 0; i < new_channel_map.channels; ++i) {
+        if (default_channels[i] >= PA_CHANNEL_POSITION_MAX) {
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid channel position.");
+            return;
+        }
+
+        new_channel_map.map[i] = default_channels[i];
+    }
+
+    c->core->default_channel_map = new_channel_map;
+    c->core->default_sample_spec.channels = n_channels;
+
+    pa_dbus_send_empty_reply(conn, msg);
+};
+
+static void handle_get_default_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    dbus_uint32_t default_sample_format;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    default_sample_format = c->core->default_sample_spec.format;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &default_sample_format);
+}
+
+static void handle_set_default_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    dbus_uint32_t default_sample_format;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_UINT32, &default_sample_format) < 0)
+        return;
+
+    if (default_sample_format >= PA_SAMPLE_MAX) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid sample format.");
+        return;
+    }
+
+    c->core->default_sample_spec.format = default_sample_format;
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+static void handle_get_default_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    dbus_uint32_t default_sample_rate;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    default_sample_rate = c->core->default_sample_spec.rate;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &default_sample_rate);
+}
+
+static void handle_set_default_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    dbus_uint32_t default_sample_rate;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_UINT32, &default_sample_rate) < 0)
+        return;
+
+    if (default_sample_rate <= 0 || default_sample_rate > PA_RATE_MAX) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid sample format.");
+        return;
+    }
+
+    c->core->default_sample_spec.rate = default_sample_rate;
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_cards(pa_dbusiface_core *c, unsigned *n) {
+    const char **cards;
+    unsigned i;
+    void *state = NULL;
+    pa_dbusiface_card *card;
+
+    pa_assert(c);
+    pa_assert(n);
+
+    *n = pa_hashmap_size(c->cards);
+
+    if (*n == 0)
+        return NULL;
+
+    cards = pa_xnew(const char *, *n);
+
+    for (i = 0, card = pa_hashmap_iterate(c->cards, &state, NULL); card; ++i, card = pa_hashmap_iterate(c->cards, &state, NULL))
+        cards[i] = pa_dbusiface_card_get_path(card);
+
+    pa_assert(i == *n);
+
+    return cards;
+}
+
+static void handle_get_cards(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    const char **cards;
+    unsigned n;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    cards = get_cards(c, &n);
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, cards, n);
+
+    pa_xfree(cards);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_sinks(pa_dbusiface_core *c, unsigned *n) {
+    const char **sinks;
+    unsigned i;
+    void *state = NULL;
+    pa_dbusiface_device *sink;
+
+    pa_assert(c);
+    pa_assert(n);
+
+    *n = pa_hashmap_size(c->sinks_by_index);
+
+    if (*n == 0)
+        return NULL;
+
+    sinks = pa_xnew(const char *, *n);
+
+    for (i = 0, sink = pa_hashmap_iterate(c->sinks_by_index, &state, NULL); sink; ++i, sink = pa_hashmap_iterate(c->sinks_by_index, &state, NULL))
+        sinks[i] = pa_dbusiface_device_get_path(sink);
+
+    pa_assert(i == *n);
+
+    return sinks;
+}
+
+static void handle_get_sinks(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    const char **sinks;
+    unsigned n;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    sinks = get_sinks(c, &n);
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, sinks, n);
+
+    pa_xfree(sinks);
+}
+
+static void handle_get_fallback_sink(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    pa_dbusiface_device *fallback_sink;
+    const char *object_path;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    if (!c->fallback_sink) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "There are no sinks, and therefore no fallback sink either.");
+        return;
+    }
+
+    pa_assert_se((fallback_sink = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(c->fallback_sink->index))));
+    object_path = pa_dbusiface_device_get_path(fallback_sink);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
+}
+
+static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    pa_dbusiface_device *fallback_sink;
+    const char *object_path;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    if (!c->fallback_sink) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "There are no sinks, and therefore no fallback sink either.");
+        return;
+    }
+
+    if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path) < 0)
+        return;
+
+    if (!(fallback_sink = pa_hashmap_get(c->sinks_by_path, object_path))) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such sink.");
+        return;
+    }
+
+    pa_namereg_set_default_sink(c->core, pa_dbusiface_device_get_sink(fallback_sink));
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_sources(pa_dbusiface_core *c, unsigned *n) {
+    const char **sources;
+    unsigned i;
+    void *state = NULL;
+    pa_dbusiface_device *source;
+
+    pa_assert(c);
+    pa_assert(n);
+
+    *n = pa_hashmap_size(c->sources_by_index);
+
+    if (*n == 0)
+        return NULL;
+
+    sources = pa_xnew(const char *, *n);
+
+    for (i = 0, source = pa_hashmap_iterate(c->sources_by_index, &state, NULL); source; ++i, source = pa_hashmap_iterate(c->sources_by_index, &state, NULL))
+        sources[i] = pa_dbusiface_device_get_path(source);
+
+    pa_assert(i == *n);
+
+    return sources;
+}
+
+static void handle_get_sources(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    const char **sources;
+    unsigned n;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    sources = get_sources(c, &n);
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, sources, n);
+
+    pa_xfree(sources);
+}
+
+static void handle_get_fallback_source(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    pa_dbusiface_device *fallback_source;
+    const char *object_path;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    if (!c->fallback_source) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "There are no sources, and therefore no fallback source either.");
+        return;
+    }
+
+    pa_assert_se((fallback_source = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(c->fallback_source->index))));
+    object_path = pa_dbusiface_device_get_path(fallback_source);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
+}
+
+static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    pa_dbusiface_device *fallback_source;
+    const char *object_path;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    if (!c->fallback_source) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "There are no sources, and therefore no fallback source either.");
+        return;
+    }
+
+    if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path) < 0)
+        return;
+
+    if (!(fallback_source = pa_hashmap_get(c->sources_by_path, object_path))) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such source.");
+        return;
+    }
+
+    pa_namereg_set_default_source(c->core, pa_dbusiface_device_get_source(fallback_source));
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_playback_streams(pa_dbusiface_core *c, unsigned *n) {
+    const char **streams;
+    unsigned i;
+    void *state = NULL;
+    pa_dbusiface_stream *stream;
+
+    pa_assert(c);
+    pa_assert(n);
+
+    *n = pa_hashmap_size(c->playback_streams);
+
+    if (*n == 0)
+        return NULL;
+
+    streams = pa_xnew(const char *, *n);
+
+    for (i = 0, stream = pa_hashmap_iterate(c->playback_streams, &state, NULL); stream; ++i, stream = pa_hashmap_iterate(c->playback_streams, &state, NULL))
+        streams[i] = pa_dbusiface_stream_get_path(stream);
+
+    pa_assert(i == *n);
+
+    return streams;
+}
+
+static void handle_get_playback_streams(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    const char **playback_streams;
+    unsigned n;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    playback_streams = get_playback_streams(c, &n);
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, playback_streams, n);
+
+    pa_xfree(playback_streams);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_record_streams(pa_dbusiface_core *c, unsigned *n) {
+    const char **streams;
+    unsigned i;
+    void *state = NULL;
+    pa_dbusiface_stream *stream;
+
+    pa_assert(c);
+    pa_assert(n);
+
+    *n = pa_hashmap_size(c->record_streams);
+
+    if (*n == 0)
+        return NULL;
+
+    streams = pa_xnew(const char *, *n);
+
+    for (i = 0, stream = pa_hashmap_iterate(c->record_streams, &state, NULL); stream; ++i, stream = pa_hashmap_iterate(c->record_streams, &state, NULL))
+        streams[i] = pa_dbusiface_stream_get_path(stream);
+
+    pa_assert(i == *n);
+
+    return streams;
+}
+
+static void handle_get_record_streams(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    const char **record_streams;
+    unsigned n;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    record_streams = get_record_streams(c, &n);
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, record_streams, n);
+
+    pa_xfree(record_streams);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_samples(pa_dbusiface_core *c, unsigned *n) {
+    const char **samples;
+    unsigned i;
+    void *state = NULL;
+    pa_dbusiface_sample *sample;
+
+    pa_assert(c);
+    pa_assert(n);
+
+    *n = pa_hashmap_size(c->samples);
+
+    if (*n == 0)
+        return NULL;
+
+    samples = pa_xnew(const char *, *n);
+
+    for (i = 0, sample = pa_hashmap_iterate(c->samples, &state, NULL); sample; ++i, sample = pa_hashmap_iterate(c->samples, &state, NULL))
+        samples[i] = pa_dbusiface_sample_get_path(sample);
+
+    pa_assert(i == *n);
+
+    return samples;
+}
+
+static void handle_get_samples(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    const char **samples;
+    unsigned n;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    samples = get_samples(c, &n);
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, samples, n);
+
+    pa_xfree(samples);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_modules(pa_dbusiface_core *c, unsigned *n) {
+    const char **modules;
+    unsigned i;
+    void *state = NULL;
+    pa_dbusiface_module *module;
+
+    pa_assert(c);
+    pa_assert(n);
+
+    *n = pa_hashmap_size(c->modules);
+
+    if (*n == 0)
+        return NULL;
+
+    modules = pa_xnew(const char *, *n);
+
+    for (i = 0, module = pa_hashmap_iterate(c->modules, &state, NULL); module; ++i, module = pa_hashmap_iterate(c->modules, &state, NULL))
+        modules[i] = pa_dbusiface_module_get_path(module);
+
+    pa_assert(i == *n);
+
+    return modules;
+}
+
+static void handle_get_modules(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    const char **modules;
+    unsigned n;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    modules = get_modules(c, &n);
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, modules, n);
+
+    pa_xfree(modules);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_clients(pa_dbusiface_core *c, unsigned *n) {
+    const char **clients;
+    unsigned i;
+    void *state = NULL;
+    pa_dbusiface_client *client;
+
+    pa_assert(c);
+    pa_assert(n);
+
+    *n = pa_hashmap_size(c->clients);
+
+    if (*n == 0)
+        return NULL;
+
+    clients = pa_xnew(const char *, *n);
+
+    for (i = 0, client = pa_hashmap_iterate(c->clients, &state, NULL); client; ++i, client = pa_hashmap_iterate(c->clients, &state, NULL))
+        clients[i] = pa_dbusiface_client_get_path(client);
+
+    pa_assert(i == *n);
+
+    return clients;
+}
+
+static void handle_get_clients(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    const char **clients;
+    unsigned n;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    clients = get_clients(c, &n);
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, clients, n);
+
+    pa_xfree(clients);
+}
+
+static const char *get_my_client(pa_dbusiface_core *c, DBusConnection *conn) {
+    pa_client *my_client;
+
+    pa_assert(c);
+    pa_assert(conn);
+
+    pa_assert_se((my_client = pa_dbus_protocol_get_client(c->dbus_protocol, conn)));
+
+    return pa_dbusiface_client_get_path(pa_hashmap_get(c->clients, PA_UINT32_TO_PTR(my_client->index)));
+}
+
+static void handle_get_my_client(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    const char *my_client;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    my_client = get_my_client(c, conn);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &my_client);
+}
+
+static void handle_get_extensions(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    const char **extensions;
+    unsigned n;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    extensions = pa_dbus_protocol_get_extensions(c->dbus_protocol, &n);
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_STRING, extensions, n);
+
+    pa_xfree(extensions);
+}
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+    DBusMessageIter dict_iter;
+    dbus_uint32_t interface_revision;
+    const char *server_name;
+    const char *version;
+    dbus_bool_t is_local;
+    char *username;
+    char *hostname;
+    dbus_uint32_t *default_channels;
+    unsigned n_default_channels;
+    dbus_uint32_t default_sample_format;
+    dbus_uint32_t default_sample_rate;
+    const char **cards;
+    unsigned n_cards;
+    const char **sinks;
+    unsigned n_sinks;
+    const char *fallback_sink;
+    const char **sources;
+    unsigned n_sources;
+    const char *fallback_source;
+    const char **playback_streams;
+    unsigned n_playback_streams;
+    const char **record_streams;
+    unsigned n_record_streams;
+    const char **samples;
+    unsigned n_samples;
+    const char **modules;
+    unsigned n_modules;
+    const char **clients;
+    unsigned n_clients;
+    const char *my_client;
+    const char **extensions;
+    unsigned n_extensions;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    interface_revision = INTERFACE_REVISION;
+    server_name = PACKAGE_NAME;
+    version = PACKAGE_VERSION;
+    is_local = get_is_local(conn);
+    username = pa_get_user_name_malloc();
+    hostname = pa_get_host_name_malloc();
+    default_channels = get_default_channels(c, &n_default_channels);
+    default_sample_format = c->core->default_sample_spec.format;
+    default_sample_rate = c->core->default_sample_spec.rate;
+    cards = get_cards(c, &n_cards);
+    sinks = get_sinks(c, &n_sinks);
+    fallback_sink = c->fallback_sink ? pa_dbusiface_device_get_path(pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(c->fallback_sink->index))) : NULL;
+    sources = get_sources(c, &n_sources);
+    fallback_source = c->fallback_source ? pa_dbusiface_device_get_path(pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(c->fallback_source->index))) : NULL;
+    playback_streams = get_playback_streams(c, &n_playback_streams);
+    record_streams = get_record_streams(c, &n_record_streams);
+    samples = get_samples(c, &n_samples);
+    modules = get_modules(c, &n_modules);
+    clients = get_clients(c, &n_clients);
+    my_client = get_my_client(c, conn);
+    extensions = pa_dbus_protocol_get_extensions(c->dbus_protocol, &n_extensions);
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INTERFACE_REVISION].property_name, DBUS_TYPE_UINT32, &interface_revision);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &server_name);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_VERSION].property_name, DBUS_TYPE_STRING, &version);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_IS_LOCAL].property_name, DBUS_TYPE_BOOLEAN, &is_local);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_USERNAME].property_name, DBUS_TYPE_STRING, &username);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HOSTNAME].property_name, DBUS_TYPE_STRING, &hostname);
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DEFAULT_CHANNELS].property_name, DBUS_TYPE_UINT32, default_channels, n_default_channels);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DEFAULT_SAMPLE_FORMAT].property_name, DBUS_TYPE_UINT32, &default_sample_format);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DEFAULT_SAMPLE_RATE].property_name, DBUS_TYPE_UINT32, &default_sample_rate);
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CARDS].property_name, DBUS_TYPE_OBJECT_PATH, cards, n_cards);
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SINKS].property_name, DBUS_TYPE_OBJECT_PATH, sinks, n_sinks);
+
+    if (fallback_sink)
+        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_FALLBACK_SINK].property_name, DBUS_TYPE_OBJECT_PATH, &fallback_sink);
+
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SOURCES].property_name, DBUS_TYPE_OBJECT_PATH, sources, n_sources);
+
+    if (fallback_source)
+        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_FALLBACK_SOURCE].property_name, DBUS_TYPE_OBJECT_PATH, &fallback_source);
+
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PLAYBACK_STREAMS].property_name, DBUS_TYPE_OBJECT_PATH, playback_streams, n_playback_streams);
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_RECORD_STREAMS].property_name, DBUS_TYPE_OBJECT_PATH, record_streams, n_record_streams);
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLES].property_name, DBUS_TYPE_OBJECT_PATH, samples, n_samples);
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_MODULES].property_name, DBUS_TYPE_OBJECT_PATH, modules, n_modules);
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CLIENTS].property_name, DBUS_TYPE_OBJECT_PATH, clients, n_clients);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_MY_CLIENT].property_name, DBUS_TYPE_OBJECT_PATH, &my_client);
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_EXTENSIONS].property_name, DBUS_TYPE_STRING, extensions, n_extensions);
+
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+
+    dbus_message_unref(reply);
+
+    pa_xfree(username);
+    pa_xfree(hostname);
+    pa_xfree(default_channels);
+    pa_xfree(cards);
+    pa_xfree(sinks);
+    pa_xfree(sources);
+    pa_xfree(playback_streams);
+    pa_xfree(record_streams);
+    pa_xfree(samples);
+    pa_xfree(modules);
+    pa_xfree(clients);
+    pa_xfree(extensions);
+}
+
+static void handle_get_card_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    char *card_name;
+    pa_card *card;
+    pa_dbusiface_card *dbus_card;
+    const char *object_path;
+    DBusError error;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    dbus_error_init(&error);
+
+    if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &card_name, DBUS_TYPE_INVALID)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, error.message);
+        dbus_error_free(&error);
+        return;
+    }
+
+    if (!(card = pa_namereg_get(c->core, card_name, PA_NAMEREG_CARD))) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such card.");
+        return;
+    }
+
+    pa_assert_se((dbus_card = pa_hashmap_get(c->cards, PA_UINT32_TO_PTR(card->index))));
+
+    object_path = pa_dbusiface_card_get_path(dbus_card);
+
+    pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
+}
+
+static void handle_get_sink_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    char *sink_name;
+    pa_sink *sink;
+    pa_dbusiface_device *dbus_sink;
+    const char *object_path;
+    DBusError error;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    dbus_error_init(&error);
+
+    if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &sink_name, DBUS_TYPE_INVALID)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, error.message);
+        dbus_error_free(&error);
+        return;
+    }
+
+    if (!(sink = pa_namereg_get(c->core, sink_name, PA_NAMEREG_SINK))) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such sink.");
+        return;
+    }
+
+    pa_assert_se((dbus_sink = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(sink->index))));
+
+    object_path = pa_dbusiface_device_get_path(dbus_sink);
+
+    pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
+}
+
+static void handle_get_source_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    char *source_name;
+    pa_source *source;
+    pa_dbusiface_device *dbus_source;
+    const char *object_path;
+    DBusError error;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    dbus_error_init(&error);
+
+    if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &source_name, DBUS_TYPE_INVALID)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, error.message);
+        dbus_error_free(&error);
+        return;
+    }
+
+    if (!(source = pa_namereg_get(c->core, source_name, PA_NAMEREG_SOURCE))) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such source.");
+        return;
+    }
+
+    pa_assert_se((dbus_source = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(source->index))));
+
+    object_path = pa_dbusiface_device_get_path(dbus_source);
+
+    pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
+}
+
+static void handle_get_sample_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    char *sample_name;
+    pa_scache_entry *sample;
+    pa_dbusiface_sample *dbus_sample;
+    const char *object_path;
+    DBusError error;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    dbus_error_init(&error);
+
+    if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &sample_name, DBUS_TYPE_INVALID)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, error.message);
+        dbus_error_free(&error);
+        return;
+    }
+
+    if (!(sample = pa_namereg_get(c->core, sample_name, PA_NAMEREG_SAMPLE))) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such sample.");
+        return;
+    }
+
+    pa_assert_se((dbus_sample = pa_hashmap_get(c->samples, PA_UINT32_TO_PTR(sample->index))));
+
+    object_path = pa_dbusiface_sample_get_path(dbus_sample);
+
+    pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
+}
+
+static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    DBusMessageIter msg_iter;
+    const char *name;
+    dbus_uint32_t sample_format;
+    dbus_uint32_t sample_rate;
+    const dbus_uint32_t *channels;
+    unsigned n_channels;
+    const dbus_uint32_t *default_volume;
+    unsigned n_volume_entries;
+    pa_proplist *property_list;
+    const uint8_t *data;
+    unsigned data_length;
+    unsigned i;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    pa_memchunk chunk;
+    uint32_t idx;
+    pa_dbusiface_sample *dbus_sample = NULL;
+    pa_scache_entry *sample = NULL;
+    const char *object_path;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    chunk.memblock = NULL;
+
+    if (!dbus_message_iter_init(msg, &msg_iter)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments.");
+        return;
+    }
+
+    if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_STRING, &name) < 0)
+        return;
+
+    if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &sample_format) < 0)
+        return;
+
+    if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &sample_rate) < 0)
+        return;
+
+    if (pa_dbus_get_fixed_array_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &channels, &n_channels) < 0)
+        return;
+
+    if (pa_dbus_get_fixed_array_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &default_volume, &n_volume_entries) < 0)
+        return;
+
+    if (!(property_list = pa_dbus_get_proplist_arg(conn, msg, &msg_iter)))
+        return;
+
+    if (pa_dbus_get_fixed_array_arg(conn, msg, &msg_iter, DBUS_TYPE_BYTE, &data, &data_length) < 0)
+        goto finish;
+
+    if (sample_format >= PA_SAMPLE_MAX) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid sample format.");
+        goto finish;
+    }
+
+    if (sample_rate <= 0 || sample_rate > PA_RATE_MAX) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid sample rate.");
+        goto finish;
+    }
+
+    if (n_channels == 0) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Empty channel map.");
+        goto finish;
+    }
+
+    if (n_channels > PA_CHANNELS_MAX) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too many channels.");
+        goto finish;
+    }
+
+    for (i = 0; i < n_channels; ++i) {
+        if (channels[i] >= PA_CHANNEL_POSITION_MAX) {
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid channel position.");
+            goto finish;
+        }
+    }
+
+    if (n_volume_entries != 0 && n_volume_entries != n_channels) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "The channels and default_volume arguments have different number of elements.");
+        goto finish;
+    }
+
+    for (i = 0; i < n_volume_entries; ++i) {
+        if (default_volume[i] > PA_VOLUME_MAX) {
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid volume.");
+            goto finish;
+        }
+    }
+
+    if (data_length == 0) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Empty data.");
+        goto finish;
+    }
+
+    if (data_length > PA_SCACHE_ENTRY_SIZE_MAX) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too big sample.");
+        goto finish;
+    }
+
+    ss.format = sample_format;
+    ss.rate = sample_rate;
+    ss.channels = n_channels;
+
+    if (!pa_frame_aligned(data_length, &ss)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "The sample length in bytes doesn't align with the sample format and channels.");
+        goto finish;
+    }
+
+    map.channels = n_channels;
+    for (i = 0; i < n_channels; ++i)
+        map.map[i] = channels[i];
+
+    chunk.memblock = pa_memblock_new(c->core->mempool, data_length);
+    chunk.index = 0;
+    chunk.length = data_length;
+
+    memcpy(pa_memblock_acquire(chunk.memblock), data, data_length);
+    pa_memblock_release(chunk.memblock);
+
+    if (pa_scache_add_item(c->core, name, &ss, &map, &chunk, property_list, &idx) < 0) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Adding the sample failed.");
+        goto finish;
+    }
+
+    sample = pa_idxset_get_by_index(c->core->scache, idx);
+
+    if (n_volume_entries > 0) {
+        sample->volume.channels = n_channels;
+        for (i = 0; i < n_volume_entries; ++i)
+            sample->volume.values[i] = default_volume[i];
+        sample->volume_is_set = TRUE;
+    } else {
+        sample->volume_is_set = FALSE;
+    }
+
+    dbus_sample = pa_dbusiface_sample_new(sample, OBJECT_PATH);
+    pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), dbus_sample);
+
+    object_path = pa_dbusiface_sample_get_path(dbus_sample);
+
+    pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
+
+finish:
+    if (property_list)
+        pa_proplist_free(property_list);
+
+    if (chunk.memblock)
+        pa_memblock_unref(chunk.memblock);
+}
+
+static pa_bool_t contains_space(const char *string) {
+    const char *p;
+
+    pa_assert(string);
+
+    for (p = string; *p; ++p) {
+        if (isspace(*p))
+            return TRUE;
+    }
+
+    return FALSE;
+}
+
+static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    DBusMessageIter msg_iter;
+    DBusMessageIter dict_iter;
+    char *name;
+    const char *key;
+    const char *value;
+    char *escaped_value;
+    pa_strbuf *arg_buffer = NULL;
+    pa_module *module;
+    pa_dbusiface_module *dbus_module;
+    const char *object_path;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    if (c->core->disallow_module_loading) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "The server is configured to disallow module loading.");
+        return;
+    }
+
+    if (!dbus_message_iter_init(msg, &msg_iter)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments.");
+        return;
+    }
+
+    if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_STRING, &name) < 0)
+        return;
+
+    if (dbus_message_iter_get_arg_type(&msg_iter) != DBUS_TYPE_ARRAY) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type or too few arguments. An array was expected.");
+        return;
+    }
+
+    if (dbus_message_iter_get_element_type(&msg_iter) != DBUS_TYPE_DICT_ENTRY) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type. A dict entry was expected.");
+        return;
+    }
+
+    arg_buffer = pa_strbuf_new();
+
+    dbus_message_iter_recurse(&msg_iter, &dict_iter);
+
+    while (dbus_message_iter_has_next(&dict_iter)) {
+        if (!pa_strbuf_isempty(arg_buffer))
+            pa_strbuf_putc(arg_buffer, ' ');
+
+        if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_STRING) {
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict key type. A string was expected.");
+            goto finish;
+        }
+
+        dbus_message_iter_get_basic(&dict_iter, &key);
+
+        if (strlen(key) <= 0 || !pa_ascii_valid(key) || contains_space(key)) {
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid module argument name.");
+            goto finish;
+        }
+
+        if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_STRING) {
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value type. A string was expected.");
+            goto finish;
+        }
+
+        dbus_message_iter_get_basic(&dict_iter, &value);
+
+        escaped_value = pa_escape(value, "\"");
+        pa_strbuf_printf(arg_buffer, "%s=\"%s\"", key, escaped_value);
+        pa_xfree(escaped_value);
+    }
+
+    if (!(module = pa_module_load(c->core, name, pa_strbuf_tostring(arg_buffer)))) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Failed to load module.");
+        goto finish;
+    }
+
+    dbus_module = pa_dbusiface_module_new(module, OBJECT_PATH);
+    pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(module->index), dbus_module);
+
+    object_path = pa_dbusiface_module_get_path(dbus_module);
+
+    pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
+
+finish:
+    if (arg_buffer)
+        pa_strbuf_free(arg_buffer);
+}
+
+static void handle_exit(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    if (c->core->disallow_exit) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "The server is configured to disallow exiting.");
+        return;
+    }
+
+    pa_dbus_send_empty_reply(conn, msg);
+
+    pa_core_exit(c->core, FALSE, 0);
+}
+
+static void handle_listen_for_signal(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    const char *signal;
+    char **objects = NULL;
+    int n_objects;
+    DBusError error;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    dbus_error_init(&error);
+
+    if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &signal, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &objects, &n_objects, DBUS_TYPE_INVALID)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, error.message);
+        dbus_error_free(&error);
+        goto finish;
+    }
+
+    pa_dbus_protocol_add_signal_listener(c->dbus_protocol, conn, *signal ? signal : NULL, objects, n_objects);
+
+    pa_dbus_send_empty_reply(conn, msg);
+
+finish:
+    dbus_free_string_array(objects);
+}
+
+static void handle_stop_listening_for_signal(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    const char *signal;
+    DBusError error;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    dbus_error_init(&error);
+
+    if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &signal, DBUS_TYPE_INVALID)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, error.message);
+        dbus_error_free(&error);
+        return;
+    }
+
+    pa_dbus_protocol_remove_signal_listener(c->dbus_protocol, conn, *signal ? signal : NULL);
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
+    pa_dbusiface_core *c = userdata;
+    pa_dbusiface_card *card = NULL;
+    pa_dbusiface_device *device = NULL;
+    pa_dbusiface_stream *stream = NULL;
+    pa_dbusiface_sample *sample = NULL;
+    pa_dbusiface_module *module = NULL;
+    pa_dbusiface_client *client = NULL;
+    DBusMessage *signal = NULL;
+    const char *object_path = NULL;
+    pa_sink *new_fallback_sink = NULL;
+    pa_source *new_fallback_source = NULL;
+
+    pa_assert(c);
+
+    switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
+        case PA_SUBSCRIPTION_EVENT_SERVER:
+            new_fallback_sink = pa_namereg_get_default_sink(core);
+            new_fallback_source = pa_namereg_get_default_source(core);
+
+            if (c->fallback_sink != new_fallback_sink) {
+                c->fallback_sink = new_fallback_sink;
+
+                if (new_fallback_sink) {
+                    pa_assert_se((device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(new_fallback_sink->index))));
+
+                    object_path = pa_dbusiface_device_get_path(device);
+
+                    pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name)));
+                    pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+                    pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
+                    dbus_message_unref(signal);
+                    signal = NULL;
+                }
+            }
+
+            if (c->fallback_source != new_fallback_source) {
+                c->fallback_source = new_fallback_source;
+
+                if (new_fallback_source) {
+                    pa_assert_se((device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(new_fallback_source->index))));
+
+                    object_path = pa_dbusiface_device_get_path(device);
+
+                    pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name)));
+                    pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+                    pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
+                    dbus_message_unref(signal);
+                    signal = NULL;
+                }
+            }
+            break;
+
+        case PA_SUBSCRIPTION_EVENT_CARD:
+            if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
+                card = pa_dbusiface_card_new(pa_idxset_get_by_index(core->cards, idx), OBJECT_PATH);
+                pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), card);
+
+                object_path = pa_dbusiface_card_get_path(card);
+
+                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_CARD].name)));
+                pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+            } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
+                pa_assert_se((card = pa_hashmap_remove(c->cards, PA_UINT32_TO_PTR(idx))));
+
+                object_path = pa_dbusiface_card_get_path(card);
+
+                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_CARD_REMOVED].name)));
+                pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+                pa_dbusiface_card_free(card);
+            }
+            break;
+
+        case PA_SUBSCRIPTION_EVENT_SINK:
+            if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
+                device = pa_dbusiface_device_new_sink(pa_idxset_get_by_index(core->sinks, idx), OBJECT_PATH);
+                object_path = pa_dbusiface_device_get_path(device);
+
+                pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(idx), device);
+                pa_hashmap_put(c->sinks_by_path, object_path, device);
+
+                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_SINK].name)));
+                pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+            } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
+                pa_assert_se((device = pa_hashmap_remove(c->sinks_by_index, PA_UINT32_TO_PTR(idx))));
+                object_path = pa_dbusiface_device_get_path(device);
+                pa_assert_se(pa_hashmap_remove(c->sinks_by_path, object_path));
+
+                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_SINK_REMOVED].name)));
+                pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+                pa_dbusiface_device_free(device);
+            }
+            break;
+
+        case PA_SUBSCRIPTION_EVENT_SOURCE:
+            if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
+                device = pa_dbusiface_device_new_source(pa_idxset_get_by_index(core->sources, idx), OBJECT_PATH);
+                object_path = pa_dbusiface_device_get_path(device);
+
+                pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(idx), device);
+                pa_hashmap_put(c->sources_by_path, object_path, device);
+
+                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_SOURCE].name)));
+                pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+            } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
+                pa_assert_se((device = pa_hashmap_remove(c->sources_by_index, PA_UINT32_TO_PTR(idx))));
+                object_path = pa_dbusiface_device_get_path(device);
+                pa_assert_se(pa_hashmap_remove(c->sources_by_path, object_path));
+
+                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_SOURCE_REMOVED].name)));
+                pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+                pa_dbusiface_device_free(device);
+            }
+            break;
+
+        case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
+            if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
+                stream = pa_dbusiface_stream_new_playback(pa_idxset_get_by_index(core->sink_inputs, idx), OBJECT_PATH);
+                pa_hashmap_put(c->playback_streams, PA_UINT32_TO_PTR(idx), stream);
+
+                object_path = pa_dbusiface_stream_get_path(stream);
+
+                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_PLAYBACK_STREAM].name)));
+                pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+            } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
+                pa_assert_se((stream = pa_hashmap_remove(c->playback_streams, PA_UINT32_TO_PTR(idx))));
+
+                object_path = pa_dbusiface_stream_get_path(stream);
+
+                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_PLAYBACK_STREAM_REMOVED].name)));
+                pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+                pa_dbusiface_stream_free(stream);
+            }
+            break;
+
+        case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT:
+            if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
+                stream = pa_dbusiface_stream_new_record(pa_idxset_get_by_index(core->source_outputs, idx), OBJECT_PATH);
+                pa_hashmap_put(c->record_streams, PA_UINT32_TO_PTR(idx), stream);
+
+                object_path = pa_dbusiface_stream_get_path(stream);
+
+                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_RECORD_STREAM].name)));
+                pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+            } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
+                pa_assert_se((stream = pa_hashmap_remove(c->record_streams, PA_UINT32_TO_PTR(idx))));
+
+                object_path = pa_dbusiface_stream_get_path(stream);
+
+                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_RECORD_STREAM_REMOVED].name)));
+                pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+                pa_dbusiface_stream_free(stream);
+            }
+            break;
+
+        case PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE:
+            if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
+                /* We may have created the pa_dbusiface_sample object already
+                 * in handle_upload_sample. */
+                if (!(sample = pa_hashmap_get(c->samples, PA_UINT32_TO_PTR(idx)))) {
+                    sample = pa_dbusiface_sample_new(pa_idxset_get_by_index(core->scache, idx), OBJECT_PATH);
+                    pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), sample);
+                }
+
+                object_path = pa_dbusiface_sample_get_path(sample);
+
+                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_SAMPLE].name)));
+                pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+            } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
+                pa_assert_se((sample = pa_hashmap_remove(c->samples, PA_UINT32_TO_PTR(idx))));
+
+                object_path = pa_dbusiface_sample_get_path(sample);
+
+                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_SAMPLE_REMOVED].name)));
+                pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+                pa_dbusiface_sample_free(sample);
+            }
+            break;
+
+        case PA_SUBSCRIPTION_EVENT_MODULE:
+            if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
+                /* We may have created the pa_dbusiface_module object already
+                 * in handle_load_module. */
+                if (!(module = pa_hashmap_get(c->modules, PA_UINT32_TO_PTR(idx)))) {
+                    module = pa_dbusiface_module_new(pa_idxset_get_by_index(core->modules, idx), OBJECT_PATH);
+                    pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), module);
+                }
+
+                object_path = pa_dbusiface_module_get_path(module);
+
+                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_MODULE].name)));
+                pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+            } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
+                pa_assert_se((module = pa_hashmap_remove(c->modules, PA_UINT32_TO_PTR(idx))));
+
+                object_path = pa_dbusiface_module_get_path(module);
+
+                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_MODULE_REMOVED].name)));
+                pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+                pa_dbusiface_module_free(module);
+            }
+            break;
+
+        case PA_SUBSCRIPTION_EVENT_CLIENT:
+            if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
+                client = pa_dbusiface_client_new(pa_idxset_get_by_index(core->clients, idx), OBJECT_PATH);
+                pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), client);
+
+                object_path = pa_dbusiface_client_get_path(client);
+
+                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_CLIENT].name)));
+                pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+            } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
+                pa_assert_se((client = pa_hashmap_remove(c->clients, PA_UINT32_TO_PTR(idx))));
+
+                object_path = pa_dbusiface_client_get_path(client);
+
+                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_CLIENT_REMOVED].name)));
+                pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+                pa_dbusiface_client_free(client);
+            }
+            break;
+    }
+
+    if (signal) {
+        pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
+        dbus_message_unref(signal);
+    }
+}
+
+pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) {
+    pa_dbusiface_core *c;
+    pa_card *card;
+    pa_sink *sink;
+    pa_source *source;
+    pa_dbusiface_device *device;
+    pa_sink_input *sink_input;
+    pa_source_output *source_output;
+    pa_scache_entry *sample;
+    pa_module *module;
+    pa_client *client;
+    uint32_t idx;
+
+    pa_assert(core);
+
+    c = pa_xnew(pa_dbusiface_core, 1);
+    c->core = core;
+    c->subscription = pa_subscription_new(core, PA_SUBSCRIPTION_MASK_ALL, subscription_cb, c);
+    c->dbus_protocol = pa_dbus_protocol_get(core);
+    c->cards = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+    c->sinks_by_index = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+    c->sinks_by_path = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+    c->sources_by_index = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+    c->sources_by_path = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+    c->playback_streams = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+    c->record_streams = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+    c->samples = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+    c->modules = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+    c->clients = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+    c->fallback_sink = pa_namereg_get_default_sink(core);
+    c->fallback_source = pa_namereg_get_default_source(core);
+
+    for (card = pa_idxset_first(core->cards, &idx); card; card = pa_idxset_next(core->cards, &idx))
+        pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), pa_dbusiface_card_new(card, OBJECT_PATH));
+
+    for (sink = pa_idxset_first(core->sinks, &idx); sink; sink = pa_idxset_next(core->sinks, &idx)) {
+        device = pa_dbusiface_device_new_sink(sink, OBJECT_PATH);
+        pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(idx), device);
+        pa_hashmap_put(c->sinks_by_path, pa_dbusiface_device_get_path(device), device);
+    }
+
+    for (source = pa_idxset_first(core->sources, &idx); source; source = pa_idxset_next(core->sources, &idx)) {
+        device = pa_dbusiface_device_new_source(source, OBJECT_PATH);
+        pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(idx), device);
+        pa_hashmap_put(c->sources_by_path, pa_dbusiface_device_get_path(device), device);
+    }
+
+    for (sink_input = pa_idxset_first(core->sink_inputs, &idx); sink_input; sink_input = pa_idxset_next(core->sink_inputs, &idx))
+        pa_hashmap_put(c->playback_streams, PA_UINT32_TO_PTR(idx), pa_dbusiface_stream_new_playback(sink_input, OBJECT_PATH));
+
+    for (source_output = pa_idxset_first(core->source_outputs, &idx); source_output; source_output = pa_idxset_next(core->source_outputs, &idx))
+        pa_hashmap_put(c->record_streams, PA_UINT32_TO_PTR(idx), pa_dbusiface_stream_new_record(source_output, OBJECT_PATH));
+
+    for (sample = pa_idxset_first(core->scache, &idx); sample; sample = pa_idxset_next(core->scache, &idx))
+        pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), pa_dbusiface_sample_new(sample, OBJECT_PATH));
+
+    for (module = pa_idxset_first(core->modules, &idx); module; module = pa_idxset_next(core->modules, &idx))
+        pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), pa_dbusiface_module_new(module, OBJECT_PATH));
+
+    for (client = pa_idxset_first(core->clients, &idx); client; client = pa_idxset_next(core->clients, &idx))
+        pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), pa_dbusiface_client_new(client, OBJECT_PATH));
+
+    pa_dbus_protocol_add_interface(c->dbus_protocol, OBJECT_PATH, &core_interface_info, c);
+
+    return c;
+}
+
+static void free_card_cb(void *p, void *userdata) {
+    pa_dbusiface_card *c = p;
+
+    pa_assert(c);
+
+    pa_dbusiface_card_free(c);
+}
+
+static void free_device_cb(void *p, void *userdata) {
+    pa_dbusiface_device *d = p;
+
+    pa_assert(d);
+
+    pa_dbusiface_device_free(d);
+}
+
+static void free_stream_cb(void *p, void *userdata) {
+    pa_dbusiface_stream *s = p;
+
+    pa_assert(s);
+
+    pa_dbusiface_stream_free(s);
+}
+
+static void free_sample_cb(void *p, void *userdata) {
+    pa_dbusiface_sample *s = p;
+
+    pa_assert(s);
+
+    pa_dbusiface_sample_free(s);
+}
+
+static void free_module_cb(void *p, void *userdata) {
+    pa_dbusiface_module *m = p;
+
+    pa_assert(m);
+
+    pa_dbusiface_module_free(m);
+}
+
+static void free_client_cb(void *p, void *userdata) {
+    pa_dbusiface_client *c = p;
+
+    pa_assert(c);
+
+    pa_dbusiface_client_free(c);
+}
+
+void pa_dbusiface_core_free(pa_dbusiface_core *c) {
+    pa_assert(c);
+
+    pa_dbus_protocol_remove_interface(c->dbus_protocol, OBJECT_PATH, INTERFACE_CORE);
+
+    pa_subscription_free(c->subscription);
+    pa_hashmap_free(c->cards, free_card_cb, NULL);
+    pa_hashmap_free(c->sinks_by_index, free_device_cb, NULL);
+    pa_hashmap_free(c->sinks_by_path, NULL, NULL);
+    pa_hashmap_free(c->sources_by_index, free_device_cb, NULL);
+    pa_hashmap_free(c->sources_by_path, NULL, NULL);
+    pa_hashmap_free(c->playback_streams, free_stream_cb, NULL);
+    pa_hashmap_free(c->record_streams, free_stream_cb, NULL);
+    pa_hashmap_free(c->samples, free_sample_cb, NULL);
+    pa_hashmap_free(c->modules, free_module_cb, NULL);
+    pa_hashmap_free(c->clients, free_client_cb, NULL);
+
+    pa_dbus_protocol_unref(c->dbus_protocol);
+
+    pa_xfree(c);
+}
diff --git a/src/modules/dbus/iface-core.h b/src/modules/dbus/iface-core.h
new file mode 100644
index 0000000..964a37b
--- /dev/null
+++ b/src/modules/dbus/iface-core.h
@@ -0,0 +1,38 @@
+#ifndef foodbusifacecorehfoo
+#define foodbusifacecorehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+/* This object implements the D-Bus interface org.PulseAudio.Core1.
+ *
+ * See http://pulseaudio.org/wiki/DBusInterface for the Core interface
+ * documentation.
+ */
+
+#include <pulsecore/core.h>
+
+typedef struct pa_dbusiface_core pa_dbusiface_core;
+
+pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core);
+void pa_dbusiface_core_free(pa_dbusiface_core *c);
+
+#endif
diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c
new file mode 100644
index 0000000..3b3795e
--- /dev/null
+++ b/src/modules/dbus/iface-device.c
@@ -0,0 +1,105 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/core-util.h>
+
+#include "iface-device.h"
+
+#define SINK_OBJECT_NAME "sink"
+#define SOURCE_OBJECT_NAME "source"
+
+enum device_type {
+    DEVICE_TYPE_SINK,
+    DEVICE_TYPE_SOURCE
+};
+
+struct pa_dbusiface_device {
+    union {
+        pa_sink *sink;
+        pa_source *source;
+    };
+    enum device_type type;
+    char *path;
+};
+
+pa_dbusiface_device *pa_dbusiface_device_new_sink(pa_sink *sink, const char *path_prefix) {
+    pa_dbusiface_device *d;
+
+    pa_assert(sink);
+    pa_assert(path_prefix);
+
+    d = pa_xnew(pa_dbusiface_device, 1);
+    d->sink = pa_sink_ref(sink);
+    d->type = DEVICE_TYPE_SINK;
+    d->path = pa_sprintf_malloc("%s/%s%u", path_prefix, SINK_OBJECT_NAME, sink->index);
+
+    return d;
+}
+
+pa_dbusiface_device *pa_dbusiface_device_new_source(pa_source *source, const char *path_prefix) {
+    pa_dbusiface_device *d;
+
+    pa_assert(source);
+    pa_assert(path_prefix);
+
+    d = pa_xnew(pa_dbusiface_device, 1);
+    d->source = pa_source_ref(source);
+    d->type = DEVICE_TYPE_SOURCE;
+    d->path = pa_sprintf_malloc("%s/%s%u", path_prefix, SOURCE_OBJECT_NAME, source->index);
+
+    return d;
+}
+
+void pa_dbusiface_device_free(pa_dbusiface_device *d) {
+    pa_assert(d);
+
+    if (d->type == DEVICE_TYPE_SINK)
+        pa_sink_unref(d->sink);
+    else
+        pa_source_unref(d->source);
+
+    pa_xfree(d->path);
+    pa_xfree(d);
+}
+
+const char *pa_dbusiface_device_get_path(pa_dbusiface_device *d) {
+    pa_assert(d);
+
+    return d->path;
+}
+
+pa_sink *pa_dbusiface_device_get_sink(pa_dbusiface_device *d) {
+    pa_assert(d);
+    pa_assert(d->type == DEVICE_TYPE_SINK);
+
+    return d->sink;
+}
+
+pa_source *pa_dbusiface_device_get_source(pa_dbusiface_device *d) {
+    pa_assert(d);
+    pa_assert(d->type == DEVICE_TYPE_SOURCE);
+
+    return d->source;
+}
diff --git a/src/modules/dbus/iface-device.h b/src/modules/dbus/iface-device.h
new file mode 100644
index 0000000..81ad1d8
--- /dev/null
+++ b/src/modules/dbus/iface-device.h
@@ -0,0 +1,46 @@
+#ifndef foodbusifacedevicehfoo
+#define foodbusifacedevicehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+/* This object implements the D-Bus interfaces org.PulseAudio.Core1.Device,
+ * org.PulseAudio.Core1.Sink and org.PulseAudio.Core1.Source.
+ *
+ * See http://pulseaudio.org/wiki/DBusInterface for the interface
+ * documentation.
+ */
+
+#include <pulsecore/sink.h>
+#include <pulsecore/source.h>
+
+typedef struct pa_dbusiface_device pa_dbusiface_device;
+
+pa_dbusiface_device *pa_dbusiface_device_new_sink(pa_sink *sink, const char *path_prefix);
+pa_dbusiface_device *pa_dbusiface_device_new_source(pa_source *source, const char *path_prefix);
+void pa_dbusiface_device_free(pa_dbusiface_device *d);
+
+const char *pa_dbusiface_device_get_path(pa_dbusiface_device *d);
+
+pa_sink *pa_dbusiface_device_get_sink(pa_dbusiface_device *d);
+pa_source *pa_dbusiface_device_get_source(pa_dbusiface_device *d);
+
+#endif
diff --git a/src/modules/dbus/iface-module.c b/src/modules/dbus/iface-module.c
new file mode 100644
index 0000000..1c95f9e
--- /dev/null
+++ b/src/modules/dbus/iface-module.c
@@ -0,0 +1,61 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/core-util.h>
+
+#include "iface-module.h"
+
+#define OBJECT_NAME "module"
+
+struct pa_dbusiface_module {
+    pa_module *module;
+    char *path;
+};
+
+pa_dbusiface_module *pa_dbusiface_module_new(pa_module *module, const char *path_prefix) {
+    pa_dbusiface_module *m;
+
+    pa_assert(module);
+    pa_assert(path_prefix);
+
+    m = pa_xnew(pa_dbusiface_module, 1);
+    m->module = module;
+    m->path = pa_sprintf_malloc("%s/%s%u", path_prefix, OBJECT_NAME, module->index);
+
+    return m;
+}
+
+void pa_dbusiface_module_free(pa_dbusiface_module *m) {
+    pa_assert(m);
+
+    pa_xfree(m->path);
+    pa_xfree(m);
+}
+
+const char *pa_dbusiface_module_get_path(pa_dbusiface_module *m) {
+    pa_assert(m);
+
+    return m->path;
+}
diff --git a/src/modules/dbus/iface-module.h b/src/modules/dbus/iface-module.h
new file mode 100644
index 0000000..7f683e6
--- /dev/null
+++ b/src/modules/dbus/iface-module.h
@@ -0,0 +1,40 @@
+#ifndef foodbusifacemodulehfoo
+#define foodbusifacemodulehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+/* This object implements the D-Bus interface org.PulseAudio.Core1.Module.
+ *
+ * See http://pulseaudio.org/wiki/DBusInterface for the Module interface
+ * documentation.
+ */
+
+#include <pulsecore/module.h>
+
+typedef struct pa_dbusiface_module pa_dbusiface_module;
+
+pa_dbusiface_module *pa_dbusiface_module_new(pa_module *module, const char *path_prefix);
+void pa_dbusiface_module_free(pa_dbusiface_module *m);
+
+const char *pa_dbusiface_module_get_path(pa_dbusiface_module *m);
+
+#endif
diff --git a/src/modules/dbus/iface-sample.c b/src/modules/dbus/iface-sample.c
new file mode 100644
index 0000000..b4a308a
--- /dev/null
+++ b/src/modules/dbus/iface-sample.c
@@ -0,0 +1,61 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/core-util.h>
+
+#include "iface-sample.h"
+
+#define OBJECT_NAME "sample"
+
+struct pa_dbusiface_sample {
+    pa_scache_entry *sample;
+    char *path;
+};
+
+pa_dbusiface_sample *pa_dbusiface_sample_new(pa_scache_entry *sample, const char *path_prefix) {
+    pa_dbusiface_sample *s;
+
+    pa_assert(sample);
+    pa_assert(path_prefix);
+
+    s = pa_xnew(pa_dbusiface_sample, 1);
+    s->sample = sample;
+    s->path = pa_sprintf_malloc("%s/%s%u", path_prefix, OBJECT_NAME, sample->index);
+
+    return s;
+}
+
+void pa_dbusiface_sample_free(pa_dbusiface_sample *s) {
+    pa_assert(s);
+
+    pa_xfree(s->path);
+    pa_xfree(s);
+}
+
+const char *pa_dbusiface_sample_get_path(pa_dbusiface_sample *s) {
+    pa_assert(s);
+
+    return s->path;
+}
diff --git a/src/modules/dbus/iface-sample.h b/src/modules/dbus/iface-sample.h
new file mode 100644
index 0000000..1b85404
--- /dev/null
+++ b/src/modules/dbus/iface-sample.h
@@ -0,0 +1,40 @@
+#ifndef foodbusifacesamplehfoo
+#define foodbusifacesamplehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+/* This object implements the D-Bus interface org.PulseAudio.Core1.Sample.
+ *
+ * See http://pulseaudio.org/wiki/DBusInterface for the Sample interface
+ * documentation.
+ */
+
+#include <pulsecore/core-scache.h>
+
+typedef struct pa_dbusiface_sample pa_dbusiface_sample;
+
+pa_dbusiface_sample *pa_dbusiface_sample_new(pa_scache_entry *sample, const char *path_prefix);
+void pa_dbusiface_sample_free(pa_dbusiface_sample *c);
+
+const char *pa_dbusiface_sample_get_path(pa_dbusiface_sample *c);
+
+#endif
diff --git a/src/modules/dbus/iface-stream.c b/src/modules/dbus/iface-stream.c
new file mode 100644
index 0000000..1d9ffee
--- /dev/null
+++ b/src/modules/dbus/iface-stream.c
@@ -0,0 +1,91 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/core-util.h>
+
+#include "iface-stream.h"
+
+#define PLAYBACK_OBJECT_NAME "playback_stream"
+#define RECORD_OBJECT_NAME "record_stream"
+
+enum stream_type {
+    STREAM_TYPE_PLAYBACK,
+    STREAM_TYPE_RECORD
+};
+
+struct pa_dbusiface_stream {
+    union {
+        pa_sink_input *sink_input;
+        pa_source_output *source_output;
+    };
+    enum stream_type type;
+    char *path;
+};
+
+pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_sink_input *sink_input, const char *path_prefix) {
+    pa_dbusiface_stream *s;
+
+    pa_assert(sink_input);
+    pa_assert(path_prefix);
+
+    s = pa_xnew(pa_dbusiface_stream, 1);
+    s->sink_input = pa_sink_input_ref(sink_input);
+    s->type = STREAM_TYPE_PLAYBACK;
+    s->path = pa_sprintf_malloc("%s/%s%u", path_prefix, PLAYBACK_OBJECT_NAME, sink_input->index);
+
+    return s;
+}
+
+pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_source_output *source_output, const char *path_prefix) {
+    pa_dbusiface_stream *s;
+
+    pa_assert(source_output);
+    pa_assert(path_prefix);
+
+    s = pa_xnew(pa_dbusiface_stream, 1);
+    s->source_output = pa_source_output_ref(source_output);
+    s->type = STREAM_TYPE_RECORD;
+    s->path = pa_sprintf_malloc("%s/%s%u", path_prefix, RECORD_OBJECT_NAME, source_output->index);
+
+    return s;
+}
+
+void pa_dbusiface_stream_free(pa_dbusiface_stream *s) {
+    pa_assert(s);
+
+    if (s->type == STREAM_TYPE_PLAYBACK)
+        pa_sink_input_unref(s->sink_input);
+    else
+        pa_source_output_unref(s->source_output);
+
+    pa_xfree(s->path);
+    pa_xfree(s);
+}
+
+const char *pa_dbusiface_stream_get_path(pa_dbusiface_stream *s) {
+    pa_assert(s);
+
+    return s->path;
+}
diff --git a/src/modules/dbus/iface-stream.h b/src/modules/dbus/iface-stream.h
new file mode 100644
index 0000000..cc2f3d6
--- /dev/null
+++ b/src/modules/dbus/iface-stream.h
@@ -0,0 +1,42 @@
+#ifndef foodbusifacestreamhfoo
+#define foodbusifacestreamhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+/* This object implements the D-Bus interface org.PulseAudio.Core1.Stream.
+ *
+ * See http://pulseaudio.org/wiki/DBusInterface for the Stream interface
+ * documentation.
+ */
+
+#include <pulsecore/sink-input.h>
+#include <pulsecore/source-output.h>
+
+typedef struct pa_dbusiface_stream pa_dbusiface_stream;
+
+pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_sink_input *sink_input, const char *path_prefix);
+pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_source_output *source_output, const char *path_prefix);
+void pa_dbusiface_stream_free(pa_dbusiface_stream *s);
+
+const char *pa_dbusiface_stream_get_path(pa_dbusiface_stream *s);
+
+#endif
diff --git a/src/modules/dbus/module-dbus-protocol.c b/src/modules/dbus/module-dbus-protocol.c
new file mode 100644
index 0000000..807d32d
--- /dev/null
+++ b/src/modules/dbus/module-dbus-protocol.c
@@ -0,0 +1,580 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+  Copyright 2006 Lennart Poettering
+  Copyright 2006 Shams E. King
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <dbus/dbus.h>
+
+#include <pulse/mainloop-api.h>
+#include <pulse/timeval.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/client.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-util.h>
+#include <pulsecore/idxset.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/module.h>
+#include <pulsecore/protocol-dbus.h>
+
+#include "iface-core.h"
+
+#include "module-dbus-protocol-symdef.h"
+
+PA_MODULE_DESCRIPTION("D-Bus interface");
+PA_MODULE_USAGE(
+        "access=local|remote|local,remote "
+        "tcp_port=<port number>");
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_AUTHOR("Tanu Kaskinen");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+
+#define CLEANUP_INTERVAL 10 /* seconds */
+
+enum server_type {
+    SERVER_TYPE_LOCAL,
+    SERVER_TYPE_TCP
+};
+
+struct server;
+struct connection;
+
+struct userdata {
+    pa_module *module;
+    pa_bool_t local_access;
+    pa_bool_t remote_access;
+    uint32_t tcp_port;
+
+    struct server *local_server;
+    struct server *tcp_server;
+
+    pa_idxset *connections;
+
+    pa_time_event *cleanup_event;
+
+    pa_dbus_protocol *dbus_protocol;
+    pa_dbusiface_core *core_iface;
+};
+
+struct server {
+    struct userdata *userdata;
+    enum server_type type;
+    DBusServer *dbus_server;
+};
+
+struct connection {
+    struct server *server;
+    pa_dbus_wrap_connection *wrap_conn;
+    pa_client *client;
+};
+
+static const char* const valid_modargs[] = {
+    "access",
+    "tcp_port",
+    NULL
+};
+
+static void connection_free(struct connection *c) {
+    pa_assert(c);
+
+    pa_assert_se(pa_dbus_protocol_unregister_connection(c->server->userdata->dbus_protocol, pa_dbus_wrap_connection_get(c->wrap_conn)) >= 0);
+
+    pa_client_free(c->client);
+    pa_assert_se(pa_idxset_remove_by_data(c->server->userdata->connections, c, NULL));
+    pa_dbus_wrap_connection_free(c->wrap_conn);
+    pa_xfree(c);
+}
+
+/* Called from pa_client_kill(). */
+static void client_kill_cb(pa_client *c) {
+    struct connection *conn;
+
+    pa_assert(c);
+    pa_assert(c->userdata);
+
+    conn = c->userdata;
+    connection_free(conn);
+
+    pa_log_info("Connection killed.");
+}
+
+static dbus_bool_t user_check_cb(DBusConnection *connection, unsigned long uid, void *data) {
+    pa_log_debug("Allowing connection by user %lu.", uid);
+
+    return TRUE;
+}
+
+/* Called by D-Bus when a new client connection is received. */
+static void connection_new_cb(DBusServer *dbus_server, DBusConnection *new_connection, void *data) {
+    struct server *s = data;
+    struct connection *c;
+    pa_client_new_data new_data;
+    pa_client *client;
+
+    pa_assert(new_connection);
+    pa_assert(s);
+
+    pa_client_new_data_init(&new_data);
+    new_data.module = s->userdata->module;
+    new_data.driver = __FILE__;
+    pa_proplist_sets(new_data.proplist, PA_PROP_APPLICATION_NAME, "D-Bus client"); /* TODO: It's probably possible to generate a fancier name. Other props? */
+    client = pa_client_new(s->userdata->module->core, &new_data);
+    pa_client_new_data_done(&new_data);
+
+    if (!client) {
+        dbus_connection_close(new_connection);
+        return;
+    }
+
+    if (s->type == SERVER_TYPE_TCP || s->userdata->module->core->server_type == PA_SERVER_TYPE_SYSTEM) {
+        /* FIXME: Here we allow anyone from anywhere to access the server,
+         * anonymously. Access control should be configurable. */
+        dbus_connection_set_unix_user_function(new_connection, user_check_cb, NULL, NULL);
+        dbus_connection_set_allow_anonymous(new_connection, TRUE);
+    }
+
+    c = pa_xnew(struct connection, 1);
+    c->server = s;
+    c->wrap_conn = pa_dbus_wrap_connection_new_from_existing(s->userdata->module->core->mainloop, TRUE, new_connection);
+    c->client = client;
+
+    c->client->kill = client_kill_cb;
+    c->client->send_event = NULL; /* TODO: Implement this. */
+    c->client->userdata = c;
+
+    pa_idxset_put(s->userdata->connections, c, NULL);
+
+    pa_assert_se(pa_dbus_protocol_register_connection(s->userdata->dbus_protocol, new_connection, c->client) >= 0);
+}
+
+/* Called by PA mainloop when a D-Bus fd watch event needs handling. */
+static void io_event_cb(pa_mainloop_api *mainloop, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
+    unsigned int flags = 0;
+    DBusWatch *watch = userdata;
+
+#if HAVE_DBUS_WATCH_GET_UNIX_FD
+    pa_assert(fd == dbus_watch_get_unix_fd(watch));
+#else
+    pa_assert(fd == dbus_watch_get_fd(watch));
+#endif
+
+    if (!dbus_watch_get_enabled(watch)) {
+        pa_log_warn("Asked to handle disabled watch: %p %i", (void*) watch, fd);
+        return;
+    }
+
+    if (events & PA_IO_EVENT_INPUT)
+        flags |= DBUS_WATCH_READABLE;
+    if (events & PA_IO_EVENT_OUTPUT)
+        flags |= DBUS_WATCH_WRITABLE;
+    if (events & PA_IO_EVENT_HANGUP)
+        flags |= DBUS_WATCH_HANGUP;
+    if (events & PA_IO_EVENT_ERROR)
+        flags |= DBUS_WATCH_ERROR;
+
+    dbus_watch_handle(watch, flags);
+}
+
+/* Called by PA mainloop when a D-Bus timer event needs handling. */
+static void time_event_cb(pa_mainloop_api *mainloop, pa_time_event* e, const struct timeval *tv, void *userdata) {
+    DBusTimeout *timeout = userdata;
+
+    if (dbus_timeout_get_enabled(timeout)) {
+        struct timeval next = *tv;
+        dbus_timeout_handle(timeout);
+
+        /* restart it for the next scheduled time */
+        pa_timeval_add(&next, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
+        mainloop->time_restart(e, &next);
+    }
+}
+
+/* Translates D-Bus fd watch event flags to PA IO event flags. */
+static pa_io_event_flags_t get_watch_flags(DBusWatch *watch) {
+    unsigned int flags;
+    pa_io_event_flags_t events = 0;
+
+    pa_assert(watch);
+
+    flags = dbus_watch_get_flags(watch);
+
+    /* no watch flags for disabled watches */
+    if (!dbus_watch_get_enabled(watch))
+        return PA_IO_EVENT_NULL;
+
+    if (flags & DBUS_WATCH_READABLE)
+        events |= PA_IO_EVENT_INPUT;
+    if (flags & DBUS_WATCH_WRITABLE)
+        events |= PA_IO_EVENT_OUTPUT;
+
+    return events | PA_IO_EVENT_HANGUP | PA_IO_EVENT_ERROR;
+}
+
+/* Called by D-Bus when a D-Bus fd watch event is added. */
+static dbus_bool_t watch_add_cb(DBusWatch *watch, void *data) {
+    struct server *s = data;
+    pa_mainloop_api *mainloop;
+    pa_io_event *ev;
+
+    pa_assert(watch);
+    pa_assert(s);
+
+    mainloop = s->userdata->module->core->mainloop;
+
+    ev = mainloop->io_new(
+            mainloop,
+#if HAVE_DBUS_WATCH_GET_UNIX_FD
+            dbus_watch_get_unix_fd(watch),
+#else
+            dbus_watch_get_fd(watch),
+#endif
+            get_watch_flags(watch), io_event_cb, watch);
+
+    dbus_watch_set_data(watch, ev, NULL);
+
+    return TRUE;
+}
+
+/* Called by D-Bus when a D-Bus fd watch event is removed. */
+static void watch_remove_cb(DBusWatch *watch, void *data) {
+    struct server *s = data;
+    pa_io_event *ev;
+
+    pa_assert(watch);
+    pa_assert(s);
+
+    if ((ev = dbus_watch_get_data(watch)))
+        s->userdata->module->core->mainloop->io_free(ev);
+}
+
+/* Called by D-Bus when a D-Bus fd watch event is toggled. */
+static void watch_toggled_cb(DBusWatch *watch, void *data) {
+    struct server *s = data;
+    pa_io_event *ev;
+
+    pa_assert(watch);
+    pa_assert(s);
+
+    pa_assert_se(ev = dbus_watch_get_data(watch));
+
+    /* get_watch_flags() checks if the watch is enabled */
+    s->userdata->module->core->mainloop->io_enable(ev, get_watch_flags(watch));
+}
+
+/* Called by D-Bus when a D-Bus timer event is added. */
+static dbus_bool_t timeout_add_cb(DBusTimeout *timeout, void *data) {
+    struct server *s = data;
+    pa_mainloop_api *mainloop;
+    pa_time_event *ev;
+    struct timeval tv;
+
+    pa_assert(timeout);
+    pa_assert(s);
+
+    if (!dbus_timeout_get_enabled(timeout))
+        return FALSE;
+
+    mainloop = s->userdata->module->core->mainloop;
+
+    pa_gettimeofday(&tv);
+    pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
+
+    ev = mainloop->time_new(mainloop, &tv, time_event_cb, timeout);
+
+    dbus_timeout_set_data(timeout, ev, NULL);
+
+    return TRUE;
+}
+
+/* Called by D-Bus when a D-Bus timer event is removed. */
+static void timeout_remove_cb(DBusTimeout *timeout, void *data) {
+    struct server *s = data;
+    pa_time_event *ev;
+
+    pa_assert(timeout);
+    pa_assert(s);
+
+    if ((ev = dbus_timeout_get_data(timeout)))
+        s->userdata->module->core->mainloop->time_free(ev);
+}
+
+/* Called by D-Bus when a D-Bus timer event is toggled. */
+static void timeout_toggled_cb(DBusTimeout *timeout, void *data) {
+    struct server *s = data;
+    pa_mainloop_api *mainloop;
+    pa_time_event *ev;
+
+    pa_assert(timeout);
+    pa_assert(s);
+
+    mainloop = s->userdata->module->core->mainloop;
+
+    pa_assert_se(ev = dbus_timeout_get_data(timeout));
+
+    if (dbus_timeout_get_enabled(timeout)) {
+        struct timeval tv;
+
+        pa_gettimeofday(&tv);
+        pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
+
+        mainloop->time_restart(ev, &tv);
+    } else
+        mainloop->time_restart(ev, NULL);
+}
+
+static void server_free(struct server *s) {
+    pa_assert(s);
+
+    if (s->dbus_server) {
+        dbus_server_disconnect(s->dbus_server);
+        dbus_server_unref(s->dbus_server);
+    }
+
+    pa_xfree(s);
+}
+
+static struct server *start_server(struct userdata *u, const char *address, enum server_type type) {
+    /* XXX: We assume that when we unref the DBusServer instance at module
+     * shutdown, nobody else holds any references to it. If we stop assuming
+     * that someday, dbus_server_set_new_connection_function,
+     * dbus_server_set_watch_functions and dbus_server_set_timeout_functions
+     * calls should probably register free callbacks, instead of providing NULL
+     * as they do now. */
+
+    struct server *s = NULL;
+    DBusError error;
+
+    pa_assert(u);
+    pa_assert(address);
+
+    dbus_error_init(&error);
+
+    s = pa_xnew0(struct server, 1);
+    s->userdata = u;
+    s->dbus_server = dbus_server_listen(address, &error);
+
+    if (dbus_error_is_set(&error)) {
+        pa_log("dbus_server_listen() failed: %s: %s", error.name, error.message);
+        goto fail;
+    }
+
+    dbus_server_set_new_connection_function(s->dbus_server, connection_new_cb, s, NULL);
+
+    if (!dbus_server_set_watch_functions(s->dbus_server, watch_add_cb, watch_remove_cb, watch_toggled_cb, s, NULL)) {
+        pa_log("dbus_server_set_watch_functions() ran out of memory.");
+        goto fail;
+    }
+
+    if (!dbus_server_set_timeout_functions(s->dbus_server, timeout_add_cb, timeout_remove_cb, timeout_toggled_cb, s, NULL)) {
+        pa_log("dbus_server_set_timeout_functions() ran out of memory.");
+        goto fail;
+    }
+
+    return s;
+
+fail:
+    if (s)
+        server_free(s);
+
+    dbus_error_free(&error);
+
+    return NULL;
+}
+
+static struct server *start_local_server(struct userdata *u) {
+    struct server *s = NULL;
+    char *address = NULL;
+
+    pa_assert(u);
+
+    address = pa_get_dbus_address_from_server_type(u->module->core->server_type);
+
+    s = start_server(u, address, SERVER_TYPE_LOCAL); /* May return NULL */
+
+    pa_xfree(address);
+
+    return s;
+}
+
+static struct server *start_tcp_server(struct userdata *u) {
+    struct server *s = NULL;
+    char *address = NULL;
+
+    pa_assert(u);
+
+    address = pa_sprintf_malloc("tcp:host=127.0.0.1,port=%u", u->tcp_port);
+
+    s = start_server(u, address, SERVER_TYPE_TCP); /* May return NULL */
+
+    pa_xfree(address);
+
+    return s;
+}
+
+static int get_access_arg(pa_modargs *ma, pa_bool_t *local_access, pa_bool_t *remote_access) {
+    const char *value = NULL;
+
+    pa_assert(ma);
+    pa_assert(local_access);
+    pa_assert(remote_access);
+
+    if (!(value = pa_modargs_get_value(ma, "access", NULL)))
+        return 0;
+
+    if (!strcmp(value, "local")) {
+        *local_access = TRUE;
+        *remote_access = FALSE;
+    } else if (!strcmp(value, "remote")) {
+        *local_access = FALSE;
+        *remote_access = TRUE;
+    } else if (!strcmp(value, "local,remote")) {
+        *local_access = TRUE;
+        *remote_access = TRUE;
+    } else
+        return -1;
+
+    return 0;
+}
+
+/* Frees dead client connections. Called every CLEANUP_INTERVAL seconds. */
+static void cleanup_cb(pa_mainloop_api *a, pa_time_event *e, const struct timeval *tv, void *userdata) {
+    struct userdata *u = userdata;
+    struct connection *conn = NULL;
+    uint32_t idx;
+    struct timeval cleanup_timeval;
+    unsigned free_count = 0;
+
+    for (conn = pa_idxset_first(u->connections, &idx); conn; conn = pa_idxset_next(u->connections, &idx)) {
+        if (!dbus_connection_get_is_connected(pa_dbus_wrap_connection_get(conn->wrap_conn))) {
+            connection_free(conn);
+            ++free_count;
+        }
+    }
+
+    if (free_count > 0)
+        pa_log_debug("Freed %u dead D-Bus client connections.", free_count);
+
+    pa_gettimeofday(&cleanup_timeval);
+    cleanup_timeval.tv_sec += CLEANUP_INTERVAL;
+    u->module->core->mainloop->time_restart(e, &cleanup_timeval);
+}
+
+int pa__init(pa_module *m) {
+    struct userdata *u = NULL;
+    pa_modargs *ma = NULL;
+    struct timeval cleanup_timeval;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->module = m;
+    u->local_access = TRUE;
+    u->remote_access = FALSE;
+    u->tcp_port = PA_DBUS_DEFAULT_PORT;
+
+    if (get_access_arg(ma, &u->local_access, &u->remote_access) < 0) {
+        pa_log("Invalid access argument: '%s'", pa_modargs_get_value(ma, "access", NULL));
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_u32(ma, "tcp_port", &u->tcp_port) < 0 || u->tcp_port < 1 || u->tcp_port > 49150) {
+        pa_log("Invalid tcp_port argument: '%s'", pa_modargs_get_value(ma, "tcp_port", NULL));
+        goto fail;
+    }
+
+    if (u->local_access && !(u->local_server = start_local_server(u))) {
+        pa_log("Starting the local D-Bus server failed.");
+        goto fail;
+    }
+
+    if (u->remote_access && !(u->tcp_server = start_tcp_server(u))) {
+        pa_log("Starting the D-Bus server for remote connections failed.");
+        goto fail;
+    }
+
+    u->connections = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+    pa_gettimeofday(&cleanup_timeval);
+    cleanup_timeval.tv_sec += CLEANUP_INTERVAL;
+    u->cleanup_event = m->core->mainloop->time_new(m->core->mainloop, &cleanup_timeval, cleanup_cb, u);
+
+    u->dbus_protocol = pa_dbus_protocol_get(m->core);
+    u->core_iface = pa_dbusiface_core_new(m->core);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+
+    return -1;
+}
+
+/* Called by idxset when the connection set is freed. */
+static void connection_free_cb(void *p, void *userdata) {
+    struct connection *conn = p;
+
+    pa_assert(conn);
+
+    connection_free(conn);
+}
+
+void pa__done(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->core_iface)
+        pa_dbusiface_core_free(u->core_iface);
+
+    if (u->cleanup_event)
+        m->core->mainloop->time_free(u->cleanup_event);
+
+    if (u->connections)
+        pa_idxset_free(u->connections, connection_free_cb, NULL);
+
+    if (u->tcp_server)
+        server_free(u->tcp_server);
+
+    if (u->local_server)
+        server_free(u->local_server);
+
+    if (u->dbus_protocol)
+        pa_dbus_protocol_unref(u->dbus_protocol);
+
+    pa_xfree(u);
+    m->userdata = NULL;
+}

commit 018810ec9a96d63446bdc9a82d6c931779f7a97d
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Mon Jul 27 20:01:39 2009 +0300

    Bug fixing and minor cleanups.

diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c
index 31f1260..d17499c 100644
--- a/src/modules/dbus/iface-core.c
+++ b/src/modules/dbus/iface-core.c
@@ -220,7 +220,7 @@ static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
         .receive_cb = handle_upload_sample },
     [METHOD_HANDLER_LOAD_MODULE] = {
         .method_name = "LoadModule",
-        .arguments = upload_sample_args,
+        .arguments = load_module_args,
         .n_arguments = sizeof(load_module_args) / sizeof(pa_dbus_arg_info),
         .receive_cb = handle_load_module },
     [METHOD_HANDLER_EXIT] = {
@@ -424,11 +424,8 @@ static void handle_get_default_channels(DBusConnection *conn, DBusMessage *msg,
 static void handle_set_default_channels(DBusConnection *conn, DBusMessage *msg, void *userdata) {
     pa_dbusiface_core *c = userdata;
     pa_channel_map new_channel_map;
-    DBusMessageIter msg_iter;
-    DBusMessageIter variant_iter;
-    DBusMessageIter array_iter;
     dbus_uint32_t *default_channels;
-    int n_channels;
+    unsigned n_channels;
     unsigned i;
 
     pa_assert(conn);
@@ -437,37 +434,16 @@ static void handle_set_default_channels(DBusConnection *conn, DBusMessage *msg,
 
     pa_channel_map_init(&new_channel_map);
 
-    pa_assert_se(dbus_message_iter_init(msg, &msg_iter));
-
-    /* Skip the interface and property name arguments. */
-    if (!dbus_message_iter_next(&msg_iter) || !dbus_message_iter_next(&msg_iter)) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments.");
+    if (pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_UINT32, &default_channels, &n_channels) < 0)
         return;
-    }
-
-    if (dbus_message_iter_get_arg_type(&msg_iter) != DBUS_TYPE_VARIANT) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Message argument isn't a variant.");
-        return;
-    }
-
-    dbus_message_iter_recurse(&msg_iter, &variant_iter);
-
-    if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_ARRAY) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Variant doesn't contain an array.");
-        return;
-    }
 
-    dbus_message_iter_recurse(&variant_iter, &array_iter);
-
-    if (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_UINT32) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Array type is not uint32.");
+    if (n_channels <= 0) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Empty channel array.");
         return;
     }
 
-    dbus_message_iter_get_fixed_array(&array_iter, &default_channels, &n_channels);
-
-    if (n_channels <= 0) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Empty channel array.");
+    if (n_channels > PA_CHANNELS_MAX) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too many channels: %u. The maximum number of channels is %u.", n_channels, PA_CHANNELS_MAX);
         return;
     }
 
@@ -475,7 +451,7 @@ static void handle_set_default_channels(DBusConnection *conn, DBusMessage *msg,
 
     for (i = 0; i < new_channel_map.channels; ++i) {
         if (default_channels[i] >= PA_CHANNEL_POSITION_MAX) {
-            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid channel position.");
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid channel position: %u.", default_channels[i]);
             return;
         }
 
@@ -547,7 +523,7 @@ static void handle_set_default_sample_rate(DBusConnection *conn, DBusMessage *ms
         return;
 
     if (default_sample_rate <= 0 || default_sample_rate > PA_RATE_MAX) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid sample format.");
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid sample rate.");
         return;
     }
 
@@ -1149,7 +1125,7 @@ static void handle_get_card_by_name(DBusConnection *conn, DBusMessage *msg, void
     dbus_error_init(&error);
 
     if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &card_name, DBUS_TYPE_INVALID)) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, error.message);
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
         dbus_error_free(&error);
         return;
     }
@@ -1181,7 +1157,7 @@ static void handle_get_sink_by_name(DBusConnection *conn, DBusMessage *msg, void
     dbus_error_init(&error);
 
     if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &sink_name, DBUS_TYPE_INVALID)) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, error.message);
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
         dbus_error_free(&error);
         return;
     }
@@ -1213,7 +1189,7 @@ static void handle_get_source_by_name(DBusConnection *conn, DBusMessage *msg, vo
     dbus_error_init(&error);
 
     if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &source_name, DBUS_TYPE_INVALID)) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, error.message);
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
         dbus_error_free(&error);
         return;
     }
@@ -1245,7 +1221,7 @@ static void handle_get_sample_by_name(DBusConnection *conn, DBusMessage *msg, vo
     dbus_error_init(&error);
 
     if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &sample_name, DBUS_TYPE_INVALID)) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, error.message);
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
         dbus_error_free(&error);
         return;
     }
@@ -1433,6 +1409,7 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use
     pa_dbusiface_core *c = userdata;
     DBusMessageIter msg_iter;
     DBusMessageIter dict_iter;
+    DBusMessageIter dict_entry_iter;
     char *name;
     const char *key;
     const char *value;
@@ -1441,6 +1418,7 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use
     pa_module *module;
     pa_dbusiface_module *dbus_module;
     const char *object_path;
+    int arg_type;
 
     pa_assert(conn);
     pa_assert(msg);
@@ -1459,13 +1437,18 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use
     if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_STRING, &name) < 0)
         return;
 
-    if (dbus_message_iter_get_arg_type(&msg_iter) != DBUS_TYPE_ARRAY) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type or too few arguments. An array was expected.");
+    arg_type = dbus_message_iter_get_arg_type(&msg_iter);
+    if (arg_type != DBUS_TYPE_ARRAY) {
+        if (arg_type == DBUS_TYPE_INVALID)
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. A dictionary from strings to strings was expected.");
+        else
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. An dictionary from strings to strings was expected.", (char) arg_type);
         return;
     }
 
-    if (dbus_message_iter_get_element_type(&msg_iter) != DBUS_TYPE_DICT_ENTRY) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type. A dict entry was expected.");
+    arg_type = dbus_message_iter_get_element_type(&msg_iter);
+    if (arg_type != DBUS_TYPE_DICT_ENTRY) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type: '%c'. A dict entry (string to string) was expected.", (char) arg_type);
         return;
     }
 
@@ -1473,28 +1456,38 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use
 
     dbus_message_iter_recurse(&msg_iter, &dict_iter);
 
-    while (dbus_message_iter_has_next(&dict_iter)) {
+    while (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_INVALID) {
         if (!pa_strbuf_isempty(arg_buffer))
             pa_strbuf_putc(arg_buffer, ' ');
 
-        if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_STRING) {
-            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict key type. A string was expected.");
+        dbus_message_iter_recurse(&dict_iter, &dict_entry_iter);
+
+        arg_type = dbus_message_iter_get_arg_type(&dict_entry_iter);
+        if (arg_type != DBUS_TYPE_STRING) {
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict key type: '%c'. A string was expected.", (char) arg_type);
             goto finish;
         }
 
-        dbus_message_iter_get_basic(&dict_iter, &key);
+        dbus_message_iter_get_basic(&dict_entry_iter, &key);
+        dbus_message_iter_next(&dict_entry_iter);
 
         if (strlen(key) <= 0 || !pa_ascii_valid(key) || contains_space(key)) {
-            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid module argument name.");
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid module argument name: %s", key);
             goto finish;
         }
 
-        if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_STRING) {
-            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value type. A string was expected.");
+        arg_type = dbus_message_iter_get_arg_type(&dict_entry_iter);
+        if (arg_type != DBUS_TYPE_STRING) {
+            if (arg_type == DBUS_TYPE_INVALID)
+                pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Dict value missing.");
+            else
+                pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value type: '%c'. A string was expected.", (char) arg_type);
             goto finish;
         }
 
-        dbus_message_iter_get_basic(&dict_iter, &value);
+        dbus_message_iter_get_basic(&dict_entry_iter, &value);
+
+        dbus_message_iter_next(&dict_iter);
 
         escaped_value = pa_escape(value, "\"");
         pa_strbuf_printf(arg_buffer, "%s=\"%s\"", key, escaped_value);
@@ -1549,7 +1542,7 @@ static void handle_listen_for_signal(DBusConnection *conn, DBusMessage *msg, voi
     dbus_error_init(&error);
 
     if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &signal, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &objects, &n_objects, DBUS_TYPE_INVALID)) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, error.message);
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
         dbus_error_free(&error);
         goto finish;
     }
@@ -1574,7 +1567,7 @@ static void handle_stop_listening_for_signal(DBusConnection *conn, DBusMessage *
     dbus_error_init(&error);
 
     if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &signal, DBUS_TYPE_INVALID)) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, error.message);
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
         dbus_error_free(&error);
         return;
     }
diff --git a/src/pulsecore/dbus-util.c b/src/pulsecore/dbus-util.c
index cfc3e8c..43dbd6b 100644
--- a/src/pulsecore/dbus-util.c
+++ b/src/pulsecore/dbus-util.c
@@ -446,18 +446,25 @@ void pa_dbus_free_pending_list(pa_dbus_pending **p) {
     }
 }
 
-void pa_dbus_send_error(DBusConnection *c, DBusMessage *in_reply_to, const char *name, const char *message) {
+void pa_dbus_send_error(DBusConnection *c, DBusMessage *in_reply_to, const char *name, const char *format, ...) {
+    va_list ap;
+    char *message;
     DBusMessage *reply = NULL;
 
     pa_assert(c);
     pa_assert(in_reply_to);
     pa_assert(name);
-    pa_assert(message);
+    pa_assert(format);
 
+    va_start(ap, format);
+    message = pa_vsprintf_malloc(format, ap);
+    va_end(ap);
     pa_assert_se((reply = dbus_message_new_error(in_reply_to, name, message)));
     pa_assert_se(dbus_connection_send(c, reply, NULL));
 
     dbus_message_unref(reply);
+
+    pa_xfree(message);
 }
 
 void pa_dbus_send_empty_reply(DBusConnection *c, DBusMessage *in_reply_to) {
@@ -655,36 +662,71 @@ int pa_dbus_get_basic_set_property_arg(DBusConnection *c, DBusMessage *msg, int
 
     dbus_message_iter_recurse(&msg_iter, &variant_iter);
 
-    if (dbus_message_iter_get_arg_type(&variant_iter) != type) {
-        pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Variant has wrong contained type.");
+    if (pa_dbus_get_basic_arg(c, msg, &variant_iter, type, data) < 0)
+        return -1;
+
+    return 0;
+}
+
+int pa_dbus_get_fixed_array_set_property_arg(DBusConnection *c, DBusMessage *msg, int item_type, void *data, unsigned *n) {
+    DBusMessageIter msg_iter;
+    DBusMessageIter variant_iter;
+
+    pa_assert(c);
+    pa_assert(msg);
+    pa_assert(dbus_type_is_fixed(item_type));
+    pa_assert(data);
+    pa_assert(n);
+
+    /* Skip the interface and property name arguments. */
+    if (!dbus_message_iter_init(msg, &msg_iter) || !dbus_message_iter_next(&msg_iter) || !dbus_message_iter_next(&msg_iter)) {
+        pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments.");
         return -1;
     }
 
-    dbus_message_iter_get_basic(&variant_iter, data);
+    if (dbus_message_iter_get_arg_type(&msg_iter) != DBUS_TYPE_VARIANT) {
+        pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Message argument isn't a variant.");
+        return -1;
+    }
+
+    dbus_message_iter_recurse(&msg_iter, &variant_iter);
+
+    if (pa_dbus_get_fixed_array_arg(c, msg, &variant_iter, item_type, data, n) < 0)
+        return -1;
 
     return 0;
 }
 
 int pa_dbus_get_basic_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int type, void *data) {
+    int arg_type;
+
     pa_assert(c);
     pa_assert(msg);
     pa_assert(iter);
     pa_assert(dbus_type_is_basic(type));
     pa_assert(data);
 
-    if (dbus_message_iter_get_arg_type(iter) != type) {
-        pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type or too few arguments.");
+    arg_type = dbus_message_iter_get_arg_type(iter);
+    if (arg_type != type) {
+        if (arg_type == DBUS_TYPE_INVALID)
+            pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. D-Bus type '%c' expected.", (char) type);
+        else
+            pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. Expected type '%c'.", (char) arg_type, (char) type);
         return -1;
     }
 
     dbus_message_iter_get_basic(iter, data);
 
+    dbus_message_iter_next(iter);
+
     return 0;
 }
 
 int pa_dbus_get_fixed_array_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int item_type, void *array, unsigned *n) {
     DBusMessageIter array_iter;
     int signed_n;
+    int arg_type;
+    int element_type;
 
     pa_assert(c);
     pa_assert(msg);
@@ -693,13 +735,18 @@ int pa_dbus_get_fixed_array_arg(DBusConnection *c, DBusMessage *msg, DBusMessage
     pa_assert(array);
     pa_assert(n);
 
-    if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) {
-        pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type or too few arguments. An array was expected.");
+    arg_type = dbus_message_iter_get_arg_type(iter);
+    if (arg_type != DBUS_TYPE_ARRAY) {
+        if (arg_type == DBUS_TYPE_INVALID)
+            pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. An array of type '%c' was expected.", (char) item_type);
+        else
+            pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. An array of type '%c' was expected.", (char) arg_type, (char) item_type);
         return -1;
     }
 
-    if (dbus_message_iter_get_element_type(iter) != item_type) {
-        pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type.");
+    element_type = dbus_message_iter_get_element_type(iter);
+    if (element_type != item_type) {
+        pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type: '%c'. Element type '%c' was expected.", (char) element_type, (char) item_type);
         return -1;
     }
 
@@ -707,6 +754,8 @@ int pa_dbus_get_fixed_array_arg(DBusConnection *c, DBusMessage *msg, DBusMessage
 
     dbus_message_iter_get_fixed_array(&array_iter, array, &signed_n);
 
+    dbus_message_iter_next(iter);
+
     pa_assert(signed_n >= 0);
 
     *n = signed_n;
@@ -769,6 +818,8 @@ pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusM
         pa_assert_se(pa_proplist_set(proplist, key, value, value_length) >= 0);
     }
 
+    dbus_message_iter_next(iter);
+
     return proplist;
 
 fail:
diff --git a/src/pulsecore/dbus-util.h b/src/pulsecore/dbus-util.h
index 1a8aeac..97aae37 100644
--- a/src/pulsecore/dbus-util.h
+++ b/src/pulsecore/dbus-util.h
@@ -63,7 +63,7 @@ void pa_dbus_sync_pending_list(pa_dbus_pending **p);
 void pa_dbus_free_pending_list(pa_dbus_pending **p);
 
 /* Sends an error message as the reply to the given message. */
-void pa_dbus_send_error(DBusConnection *c, DBusMessage *in_reply_to, const char *name, const char *message);
+void pa_dbus_send_error(DBusConnection *c, DBusMessage *in_reply_to, const char *name, const char *format, ...) PA_GCC_PRINTF_ATTR(4, 5);
 
 void pa_dbus_send_empty_reply(DBusConnection *c, DBusMessage *in_reply_to);
 void pa_dbus_send_basic_value_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data);
@@ -76,10 +76,11 @@ void pa_dbus_append_basic_variant(DBusMessageIter *iter, int type, void *data);
 void pa_dbus_append_basic_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int type, void *data);
 void pa_dbus_append_basic_array_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int item_type, const void *array, unsigned n);
 
-/* Helper function for extracting the value argument of a Set call for a
- * property with a basic type. If the message is invalid, an error reply is
- * sent and a negative number is returned. */
+/* Helper functions for extracting the value argument of a Set call. If the
+ * message is invalid, an error reply is sent and a negative number is
+ * returned. */
 int pa_dbus_get_basic_set_property_arg(DBusConnection *c, DBusMessage *msg, int type, void *data);
+int pa_dbus_get_fixed_array_set_property_arg(DBusConnection *c, DBusMessage *msg, int item_type, void *data, unsigned *n);
 
 /* If the arguments can't be read from the iterator, an error reply is sent and
  * a negative number is returned. */

commit b061957e57f74d7aa51bde9d24dd5e5c75af9497
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Thu Jul 30 13:11:32 2009 +0300

    dbus/iface-core.c: Make sure D-Bus objects are created only once.

diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c
index d17499c..cdfd2a3 100644
--- a/src/modules/dbus/iface-core.c
+++ b/src/modules/dbus/iface-core.c
@@ -1632,8 +1632,10 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
         case PA_SUBSCRIPTION_EVENT_CARD:
             if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
-                card = pa_dbusiface_card_new(pa_idxset_get_by_index(core->cards, idx), OBJECT_PATH);
-                pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), card);
+                if (!(card = pa_hashmap_get(c->cards, PA_UINT32_TO_PTR(idx)))) {
+                    card = pa_dbusiface_card_new(pa_idxset_get_by_index(core->cards, idx), OBJECT_PATH);
+                    pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), card);
+                }
 
                 object_path = pa_dbusiface_card_get_path(card);
 
@@ -1654,11 +1656,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
         case PA_SUBSCRIPTION_EVENT_SINK:
             if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
-                device = pa_dbusiface_device_new_sink(pa_idxset_get_by_index(core->sinks, idx), OBJECT_PATH);
-                object_path = pa_dbusiface_device_get_path(device);
+                if (!(device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(idx)))) {
+                    device = pa_dbusiface_device_new_sink(pa_idxset_get_by_index(core->sinks, idx), OBJECT_PATH);
+                    pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(idx), device);
+                    pa_hashmap_put(c->sinks_by_path, pa_dbusiface_device_get_path(device), device);
+                }
 
-                pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(idx), device);
-                pa_hashmap_put(c->sinks_by_path, object_path, device);
+                object_path = pa_dbusiface_device_get_path(device);
 
                 pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_SINK].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
@@ -1677,11 +1681,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
         case PA_SUBSCRIPTION_EVENT_SOURCE:
             if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
-                device = pa_dbusiface_device_new_source(pa_idxset_get_by_index(core->sources, idx), OBJECT_PATH);
-                object_path = pa_dbusiface_device_get_path(device);
+                if (!(device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(idx)))) {
+                    device = pa_dbusiface_device_new_source(pa_idxset_get_by_index(core->sources, idx), OBJECT_PATH);
+                    pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(idx), device);
+                    pa_hashmap_put(c->sources_by_path, pa_dbusiface_device_get_path(device), device);
+                }
 
-                pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(idx), device);
-                pa_hashmap_put(c->sources_by_path, object_path, device);
+                object_path = pa_dbusiface_device_get_path(device);
 
                 pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_SOURCE].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
@@ -1700,8 +1706,10 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
         case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
             if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
-                stream = pa_dbusiface_stream_new_playback(pa_idxset_get_by_index(core->sink_inputs, idx), OBJECT_PATH);
-                pa_hashmap_put(c->playback_streams, PA_UINT32_TO_PTR(idx), stream);
+                if (!(stream = pa_hashmap_get(c->playback_streams, PA_UINT32_TO_PTR(idx)))) {
+                    stream = pa_dbusiface_stream_new_playback(pa_idxset_get_by_index(core->sink_inputs, idx), OBJECT_PATH);
+                    pa_hashmap_put(c->playback_streams, PA_UINT32_TO_PTR(idx), stream);
+                }
 
                 object_path = pa_dbusiface_stream_get_path(stream);
 
@@ -1722,8 +1730,10 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
         case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT:
             if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
-                stream = pa_dbusiface_stream_new_record(pa_idxset_get_by_index(core->source_outputs, idx), OBJECT_PATH);
-                pa_hashmap_put(c->record_streams, PA_UINT32_TO_PTR(idx), stream);
+                if (!(stream = pa_hashmap_get(c->record_streams, PA_UINT32_TO_PTR(idx)))) {
+                    stream = pa_dbusiface_stream_new_record(pa_idxset_get_by_index(core->source_outputs, idx), OBJECT_PATH);
+                    pa_hashmap_put(c->record_streams, PA_UINT32_TO_PTR(idx), stream);
+                }
 
                 object_path = pa_dbusiface_stream_get_path(stream);
 
@@ -1744,8 +1754,6 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
         case PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE:
             if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
-                /* We may have created the pa_dbusiface_sample object already
-                 * in handle_upload_sample. */
                 if (!(sample = pa_hashmap_get(c->samples, PA_UINT32_TO_PTR(idx)))) {
                     sample = pa_dbusiface_sample_new(pa_idxset_get_by_index(core->scache, idx), OBJECT_PATH);
                     pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), sample);
@@ -1770,8 +1778,6 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
         case PA_SUBSCRIPTION_EVENT_MODULE:
             if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
-                /* We may have created the pa_dbusiface_module object already
-                 * in handle_load_module. */
                 if (!(module = pa_hashmap_get(c->modules, PA_UINT32_TO_PTR(idx)))) {
                     module = pa_dbusiface_module_new(pa_idxset_get_by_index(core->modules, idx), OBJECT_PATH);
                     pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), module);
@@ -1796,8 +1802,10 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
         case PA_SUBSCRIPTION_EVENT_CLIENT:
             if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
-                client = pa_dbusiface_client_new(pa_idxset_get_by_index(core->clients, idx), OBJECT_PATH);
-                pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), client);
+                if (!(client = pa_hashmap_get(c->clients, PA_UINT32_TO_PTR(idx)))) {
+                    client = pa_dbusiface_client_new(pa_idxset_get_by_index(core->clients, idx), OBJECT_PATH);
+                    pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), client);
+                }
 
                 object_path = pa_dbusiface_client_get_path(client);
 

commit c354a08fe3cf2468688264124b763efb1bf1df85
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Fri Jul 31 12:05:49 2009 +0300

    dbus-protocol: Implement extension registration.

diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c
index fb7d168..81a6c02 100644
--- a/src/pulsecore/protocol-dbus.c
+++ b/src/pulsecore/protocol-dbus.c
@@ -41,7 +41,9 @@ struct pa_dbus_protocol {
     pa_core *core;
     pa_hashmap *objects; /* Object path -> struct object_entry */
     pa_hashmap *connections; /* DBusConnection -> struct connection_entry */
-    pa_hashmap *extensions; /* String -> anything */
+    pa_idxset *extensions; /* Strings */
+
+    pa_hook hooks[PA_DBUS_PROTOCOL_HOOK_MAX];
 };
 
 struct object_entry {
@@ -109,6 +111,7 @@ char *pa_get_dbus_address_from_server_type(pa_server_type_t server_type) {
 
 static pa_dbus_protocol *dbus_protocol_new(pa_core *c) {
     pa_dbus_protocol *p;
+    unsigned i;
 
     pa_assert(c);
 
@@ -117,7 +120,10 @@ static pa_dbus_protocol *dbus_protocol_new(pa_core *c) {
     p->core = c;
     p->objects = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
     p->connections = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
-    p->extensions = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+    p->extensions = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
+    for (i = 0; i < PA_DBUS_PROTOCOL_HOOK_MAX; ++i)
+        pa_hook_init(&p->hooks[i], p);
 
     pa_assert_se(pa_shared_set(c, "dbus-protocol", p) >= 0);
 
@@ -143,6 +149,8 @@ pa_dbus_protocol* pa_dbus_protocol_ref(pa_dbus_protocol *p) {
 }
 
 void pa_dbus_protocol_unref(pa_dbus_protocol *p) {
+    unsigned i;
+
     pa_assert(p);
     pa_assert(PA_REFCNT_VALUE(p) >= 1);
 
@@ -151,11 +159,14 @@ void pa_dbus_protocol_unref(pa_dbus_protocol *p) {
 
     pa_assert(pa_hashmap_isempty(p->objects));
     pa_assert(pa_hashmap_isempty(p->connections));
-    pa_assert(pa_hashmap_isempty(p->extensions));
+    pa_assert(pa_idxset_isempty(p->extensions));
 
     pa_hashmap_free(p->objects, NULL, NULL);
     pa_hashmap_free(p->connections, NULL, NULL);
-    pa_hashmap_free(p->extensions, NULL, NULL);
+    pa_idxset_free(p->extensions, NULL, NULL);
+
+    for (i = 0; i < PA_DBUS_PROTOCOL_HOOK_MAX; ++i)
+        pa_hook_done(&p->hooks[i]);
 
     pa_assert_se(pa_shared_remove(p->core, "dbus-protocol") >= 0);
 
@@ -660,6 +671,8 @@ static void property_handler_free_cb(void *p, void *userdata) {
 
     pa_xfree((char *) h->property_name);
     pa_xfree((char *) h->type);
+
+    pa_xfree(h);
 }
 
 int pa_dbus_protocol_remove_interface(pa_dbus_protocol *p, const char* path, const char* interface) {
@@ -792,6 +805,18 @@ int pa_dbus_protocol_unregister_connection(pa_dbus_protocol *p, DBusConnection *
     return 0;
 }
 
+pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn) {
+    struct connection_entry *conn_entry;
+
+    pa_assert(p);
+    pa_assert(conn);
+
+    if (!(conn_entry = pa_hashmap_get(p->connections, conn)))
+        return NULL;
+
+    return conn_entry->client;
+}
+
 void pa_dbus_protocol_add_signal_listener(pa_dbus_protocol *p, DBusConnection *conn, const char *signal, char **objects, unsigned n_objects) {
     struct connection_entry *conn_entry;
     pa_idxset *object_set;
@@ -893,18 +918,6 @@ void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal) {
     }
 }
 
-pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn) {
-    struct connection_entry *conn_entry;
-
-    pa_assert(p);
-    pa_assert(conn);
-
-    if (!(conn_entry = pa_hashmap_get(p->connections, conn)))
-        return NULL;
-
-    return conn_entry->client;
-}
-
 const char **pa_dbus_protocol_get_extensions(pa_dbus_protocol *p, unsigned *n) {
     const char **extensions;
     const char *ext_name;
@@ -914,17 +927,59 @@ const char **pa_dbus_protocol_get_extensions(pa_dbus_protocol *p, unsigned *n) {
     pa_assert(p);
     pa_assert(n);
 
-    *n = pa_hashmap_size(p->extensions);
+    *n = pa_idxset_size(p->extensions);
 
     if (*n <= 0)
         return NULL;
 
     extensions = pa_xnew(const char *, *n);
 
-    while (pa_hashmap_iterate(p->extensions, &state, (const void **) &ext_name)) {
+    while ((ext_name = pa_idxset_iterate(p->extensions, &state, NULL))) {
         extensions[i] = ext_name;
         ++i;
     }
 
     return extensions;
 }
+
+int pa_dbus_protocol_register_extension(pa_dbus_protocol *p, const char *name) {
+    char *internal_name;
+
+    pa_assert(p);
+    pa_assert(name);
+
+    internal_name = pa_xstrdup(name);
+
+    if (pa_idxset_put(p->extensions, internal_name, NULL) < 0) {
+        pa_xfree(internal_name);
+        return -1;
+    }
+
+    pa_hook_fire(&p->hooks[PA_DBUS_PROTOCOL_HOOK_EXTENSION_REGISTERED], internal_name);
+
+    return 0;
+}
+
+int pa_dbus_protocol_unregister_extension(pa_dbus_protocol *p, const char *name) {
+    char *internal_name;
+
+    pa_assert(p);
+    pa_assert(name);
+
+    if (!(internal_name = pa_idxset_remove_by_data(p->extensions, name, NULL)))
+        return -1;
+
+    pa_hook_fire(&p->hooks[PA_DBUS_PROTOCOL_HOOK_EXTENSION_UNREGISTERED], internal_name);
+
+    pa_xfree(internal_name);
+
+    return 0;
+}
+
+pa_hook_slot *pa_dbus_protocol_hook_connect(pa_dbus_protocol *p, pa_dbus_protocol_hook_t hook, pa_hook_priority_t prio, pa_hook_cb_t cb, void *data) {
+    pa_assert(p);
+    pa_assert(hook < PA_DBUS_PROTOCOL_HOOK_MAX);
+    pa_assert(cb);
+
+    return pa_hook_connect(&p->hooks[hook], prio, cb, data);
+}
diff --git a/src/pulsecore/protocol-dbus.h b/src/pulsecore/protocol-dbus.h
index 27198f4..f2b1b50 100644
--- a/src/pulsecore/protocol-dbus.h
+++ b/src/pulsecore/protocol-dbus.h
@@ -119,9 +119,12 @@ int pa_dbus_protocol_remove_interface(pa_dbus_protocol *p, const char* path, con
  * registered. */
 int pa_dbus_protocol_register_connection(pa_dbus_protocol *p, DBusConnection *conn, pa_client *client);
 
-/* Returns a negative number if the connection wasn't registered. */
+/* Returns a negative number if the connection isn't registered. */
 int pa_dbus_protocol_unregister_connection(pa_dbus_protocol *p, DBusConnection *conn);
 
+/* Returns NULL if the connection isn't registered. */
+pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn);
+
 /* Enables signal receiving for the given connection. The connection must have
  * been registered earlier.
  *
@@ -145,9 +148,6 @@ void pa_dbus_protocol_remove_signal_listener(pa_dbus_protocol *p, DBusConnection
 
 void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal);
 
-/* Returns NULL if the connection isn't registered. */
-pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn);
-
 /* Returns an array of extension identifier strings. The strings pointers point
  * to the internal copies, so don't free the strings. The caller must free the
  * array, however. Also, do not save the returned pointer or any of the string
@@ -155,4 +155,35 @@ pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn
  * need to save the array, copy it. */
 const char **pa_dbus_protocol_get_extensions(pa_dbus_protocol *p, unsigned *n);
 
+/* Modules that want to provide a D-Bus interface for clients should register
+ * an identifier that the clients can use to check whether the additional
+ * functionality is available.
+ *
+ * This function registers the extension with the given name. It is recommended
+ * that the name follows the D-Bus interface naming convention, so that the
+ * names remain unique in case there will be at some point in the future
+ * extensions that aren't included with the main PulseAudio source tree. For
+ * in-tree extensions the convention is to use the org.PulseAudio.Ext
+ * namespace.
+ *
+ * It is suggested that the name contains a version number, and whenever the
+ * extension interface is modified in non-backwards compatible way, the version
+ * number is incremented.
+ *
+ * Fails and returns a negative number if the extension is already registered.
+ */
+int pa_dbus_protocol_register_extension(pa_dbus_protocol *p, const char *name);
+
+/* Returns a negative number if the extension isn't registered. */
+int pa_dbus_protocol_unregister_extension(pa_dbus_protocol *p, const char *name);
+
+/* All hooks have the pa_dbus_protocol object as hook data. */
+typedef enum pa_dbus_protocol_hook {
+    PA_DBUS_PROTOCOL_HOOK_EXTENSION_REGISTERED, /* Extension name as call data. */
+    PA_DBUS_PROTOCOL_HOOK_EXTENSION_UNREGISTERED, /* Extension name as call data. */
+    PA_DBUS_PROTOCOL_HOOK_MAX
+} pa_dbus_protocol_hook_t;
+
+pa_hook_slot *pa_dbus_protocol_hook_connect(pa_dbus_protocol *p, pa_dbus_protocol_hook_t hook, pa_hook_priority_t prio, pa_hook_cb_t cb, void *data);
+
 #endif

commit 68cb63c0d9b1493ebc4274f9ea1fb05a8c273574
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Fri Jul 31 12:06:53 2009 +0300

    dbusiface-core: Send signals whenever extensions are registered and unregistered.

diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c
index cdfd2a3..e2e3be2 100644
--- a/src/modules/dbus/iface-core.c
+++ b/src/modules/dbus/iface-core.c
@@ -112,6 +112,9 @@ struct pa_dbusiface_core {
 
     pa_sink *fallback_sink;
     pa_source *fallback_source;
+
+    pa_hook_slot *extension_registered_slot;
+    pa_hook_slot *extension_unregistered_slot;
 };
 
 enum property_handler_index {
@@ -259,6 +262,8 @@ enum signal_index {
     SIGNAL_MODULE_REMOVED,
     SIGNAL_NEW_CLIENT,
     SIGNAL_CLIENT_REMOVED,
+    SIGNAL_NEW_EXTENSION,
+    SIGNAL_EXTENSION_REMOVED,
     SIGNAL_MAX
 };
 
@@ -280,6 +285,8 @@ static pa_dbus_arg_info new_module_args[] =              { { "module",
 static pa_dbus_arg_info module_removed_args[] =          { { "module",          "o", NULL } };
 static pa_dbus_arg_info new_client_args[] =              { { "client",          "o", NULL } };
 static pa_dbus_arg_info client_removed_args[] =          { { "client",          "o", NULL } };
+static pa_dbus_arg_info new_extension_args[] =           { { "extension",       "s", NULL } };
+static pa_dbus_arg_info extension_removed_args[] =       { { "extension",       "s", NULL } };
 
 static pa_dbus_signal_info signals[SIGNAL_MAX] = {
     [SIGNAL_NEW_CARD]                = { .name = "NewCard",               .arguments = new_card_args,                .n_arguments = 1 },
@@ -300,6 +307,8 @@ static pa_dbus_signal_info signals[SIGNAL_MAX] = {
     [SIGNAL_MODULE_REMOVED]          = { .name = "ModuleRemoved",         .arguments = module_removed_args,          .n_arguments = 1 },
     [SIGNAL_NEW_CLIENT]              = { .name = "NewClient",             .arguments = new_client_args,              .n_arguments = 1 },
     [SIGNAL_CLIENT_REMOVED]          = { .name = "ClientRemoved",         .arguments = client_removed_args,          .n_arguments = 1 },
+    [SIGNAL_NEW_EXTENSION]           = { .name = "NewExtension",          .arguments = new_extension_args,           .n_arguments = 1 },
+    [SIGNAL_EXTENSION_REMOVED]       = { .name = "ExtensionRemoved",      .arguments = extension_removed_args,       .n_arguments = 1 }
 };
 
 static pa_dbus_interface_info core_interface_info = {
@@ -1831,6 +1840,40 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
     }
 }
 
+static pa_hook_result_t extension_registered_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_core *c = slot_data;
+    const char *ext_name = call_data;
+    DBusMessage *signal = NULL;
+
+    pa_assert(c);
+    pa_assert(ext_name);
+
+    pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_EXTENSION].name)));
+    pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_STRING, &ext_name, DBUS_TYPE_INVALID));
+
+    pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
+    dbus_message_unref(signal);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t extension_unregistered_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_core *c = slot_data;
+    const char *ext_name = call_data;
+    DBusMessage *signal = NULL;
+
+    pa_assert(c);
+    pa_assert(ext_name);
+
+    pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_EXTENSION_REMOVED].name)));
+    pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_STRING, &ext_name, DBUS_TYPE_INVALID));
+
+    pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
+    dbus_message_unref(signal);
+
+    return PA_HOOK_OK;
+}
+
 pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) {
     pa_dbusiface_core *c;
     pa_card *card;
@@ -1862,6 +1905,8 @@ pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) {
     c->clients = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
     c->fallback_sink = pa_namereg_get_default_sink(core);
     c->fallback_source = pa_namereg_get_default_source(core);
+    c->extension_registered_slot = pa_dbus_protocol_hook_connect(c->dbus_protocol, PA_DBUS_PROTOCOL_HOOK_EXTENSION_REGISTERED, PA_HOOK_NORMAL, extension_registered_cb, c);
+    c->extension_unregistered_slot = pa_dbus_protocol_hook_connect(c->dbus_protocol, PA_DBUS_PROTOCOL_HOOK_EXTENSION_UNREGISTERED, PA_HOOK_NORMAL, extension_unregistered_cb, c);
 
     for (card = pa_idxset_first(core->cards, &idx); card; card = pa_idxset_next(core->cards, &idx))
         pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), pa_dbusiface_card_new(card, OBJECT_PATH));
@@ -1962,6 +2007,8 @@ void pa_dbusiface_core_free(pa_dbusiface_core *c) {
     pa_hashmap_free(c->samples, free_sample_cb, NULL);
     pa_hashmap_free(c->modules, free_module_cb, NULL);
     pa_hashmap_free(c->clients, free_client_cb, NULL);
+    pa_hook_slot_free(c->extension_registered_slot);
+    pa_hook_slot_free(c->extension_unregistered_slot);
 
     pa_dbus_protocol_unref(c->dbus_protocol);
 

commit a1ba80bc4e715b8ce77f55676bc63b02c1a82d3c
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Sat Aug 1 08:26:51 2009 +0300

    dbusiface-core: Don't die if we get a default sink/source change event before the new default device is actually created.

diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c
index e2e3be2..69c1bd2 100644
--- a/src/modules/dbus/iface-core.c
+++ b/src/modules/dbus/iface-core.c
@@ -1609,9 +1609,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
             if (c->fallback_sink != new_fallback_sink) {
                 c->fallback_sink = new_fallback_sink;
 
-                if (new_fallback_sink) {
-                    pa_assert_se((device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(new_fallback_sink->index))));
-
+                if (new_fallback_sink && (device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(new_fallback_sink->index)))) {
                     object_path = pa_dbusiface_device_get_path(device);
 
                     pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name)));
@@ -1625,9 +1623,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
             if (c->fallback_source != new_fallback_source) {
                 c->fallback_source = new_fallback_source;
 
-                if (new_fallback_source) {
-                    pa_assert_se((device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(new_fallback_source->index))));
-
+                if (new_fallback_source && (device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(new_fallback_source->index)))) {
                     object_path = pa_dbusiface_device_get_path(device);
 
                     pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name)));
@@ -1665,8 +1661,10 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
         case PA_SUBSCRIPTION_EVENT_SINK:
             if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
+                pa_sink *sink = pa_idxset_get_by_index(core->sinks, idx);
+
                 if (!(device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(idx)))) {
-                    device = pa_dbusiface_device_new_sink(pa_idxset_get_by_index(core->sinks, idx), OBJECT_PATH);
+                    device = pa_dbusiface_device_new_sink(sink, OBJECT_PATH);
                     pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(idx), device);
                     pa_hashmap_put(c->sinks_by_path, pa_dbusiface_device_get_path(device), device);
                 }
@@ -1676,6 +1674,23 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
                 pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_SINK].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
+                pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
+                dbus_message_unref(signal);
+                signal = NULL;
+
+                if (c->fallback_sink && pa_streq(c->fallback_sink->name, sink->name)) {
+                    /* We have got default sink change event, but at that point
+                     * the D-Bus sink object wasn't created yet. Now that the
+                     * object is created, let's send the fallback sink change
+                     * signal. */
+                    pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name)));
+                    pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+                    pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
+                    dbus_message_unref(signal);
+                    signal = NULL;
+                }
+
             } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
                 pa_assert_se((device = pa_hashmap_remove(c->sinks_by_index, PA_UINT32_TO_PTR(idx))));
                 object_path = pa_dbusiface_device_get_path(device);
@@ -1690,8 +1705,10 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
         case PA_SUBSCRIPTION_EVENT_SOURCE:
             if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
+                pa_source *source = pa_idxset_get_by_index(core->sources, idx);
+
                 if (!(device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(idx)))) {
-                    device = pa_dbusiface_device_new_source(pa_idxset_get_by_index(core->sources, idx), OBJECT_PATH);
+                    device = pa_dbusiface_device_new_source(source, OBJECT_PATH);
                     pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(idx), device);
                     pa_hashmap_put(c->sources_by_path, pa_dbusiface_device_get_path(device), device);
                 }
@@ -1701,6 +1718,23 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
                 pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_SOURCE].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
+                pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
+                dbus_message_unref(signal);
+                signal = NULL;
+
+                if (c->fallback_source && pa_streq(c->fallback_source->name, source->name)) {
+                    /* We have got default source change event, but at that
+                     * point the D-Bus source object wasn't created yet. Now
+                     * that the object is created, let's send the fallback
+                     * source change signal. */
+                    pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name)));
+                    pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+                    pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
+                    dbus_message_unref(signal);
+                    signal = NULL;
+                }
+
             } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
                 pa_assert_se((device = pa_hashmap_remove(c->sources_by_index, PA_UINT32_TO_PTR(idx))));
                 object_path = pa_dbusiface_device_get_path(device);

commit 8c840572c7e2e560efcefe66707527a1dc4d16a4
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Sun Aug 2 11:12:21 2009 +0300

    dbus-protocol: Add debugging output (temporary change).

diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c
index 81a6c02..475b952 100644
--- a/src/pulsecore/protocol-dbus.c
+++ b/src/pulsecore/protocol-dbus.c
@@ -306,6 +306,7 @@ static enum find_result_t find_handler_by_method(struct object_entry *obj_entry,
             return FOUND_METHOD;
     }
 
+    pa_log("find_handler_by_method() failed.");
     return NO_SUCH_METHOD;
 }
 
@@ -327,8 +328,10 @@ static enum find_result_t find_handler_from_properties_call(struct object_entry
         if (*interface) {
             if ((*iface_entry = pa_hashmap_get(obj_entry->interfaces, interface)))
                 return FOUND_GET_ALL;
-            else
+            else {
+                pa_log("GetAll message has unknown interface: %s", interface);
                 return NO_SUCH_METHOD; /* XXX: NO_SUCH_INTERFACE or something like that might be more accurate. */
+            }
         } else {
             pa_assert_se((*iface_entry = pa_hashmap_first(obj_entry->interfaces)));
             return FOUND_GET_ALL;
@@ -378,8 +381,10 @@ static enum find_result_t find_handler(struct object_entry *obj_entry,
         if ((*iface_entry = pa_hashmap_get(obj_entry->interfaces, interface)) &&
             (*method_handler = pa_hashmap_get((*iface_entry)->method_handlers, dbus_message_get_member(msg))))
             return FOUND_METHOD;
-        else
+        else {
+            pa_log("Message has unknown interface or there's no method handler.");
             return NO_SUCH_METHOD;
+        }
 
     } else { /* The method call doesn't contain an interface. */
         if (dbus_message_has_member(msg, "Get") || dbus_message_has_member(msg, "Set") || dbus_message_has_member(msg, "GetAll")) {
@@ -411,6 +416,8 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa
     if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 
+    pa_log("Received method call: destination = %s, name = %s, iface = %s", dbus_message_get_path(message), dbus_message_get_member(message), dbus_message_get_interface(message));
+
     pa_assert_se((obj_entry = pa_hashmap_get(p->objects, dbus_message_get_path(message))));
 
     if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect") ||
@@ -624,6 +631,8 @@ int pa_dbus_protocol_add_interface(pa_dbus_protocol *p,
     if (obj_entry_created)
         register_object(p, obj_entry);
 
+    pa_log("Interface %s added for object %s. GetAll callback? %s", iface_entry->name, obj_entry->path, iface_entry->get_all_properties_cb ? "yes" : "no");
+
     return 0;
 
 fail:

commit 805af5e8010228bee7521fbd1148e167cfb21e77
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Mon Aug 3 19:36:19 2009 +0300

    dbus-util: Fix broken proplist reading logic.

diff --git a/src/pulsecore/dbus-util.c b/src/pulsecore/dbus-util.c
index 43dbd6b..5db7f21 100644
--- a/src/pulsecore/dbus-util.c
+++ b/src/pulsecore/dbus-util.c
@@ -765,6 +765,8 @@ int pa_dbus_get_fixed_array_arg(DBusConnection *c, DBusMessage *msg, DBusMessage
 
 pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter) {
     DBusMessageIter dict_iter;
+    DBusMessageIter dict_entry_iter;
+    int arg_type;
     pa_proplist *proplist = NULL;
     const char *key;
     const uint8_t *value;
@@ -774,13 +776,18 @@ pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusM
     pa_assert(msg);
     pa_assert(iter);
 
-    if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) {
-        pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type or too few arguments. An array was expected.");
+    arg_type = dbus_message_iter_get_arg_type(iter);
+    if (arg_type != DBUS_TYPE_ARRAY) {
+        if (arg_type == DBUS_TYPE_INVALID)
+            pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. An array was expected.");
+        else
+            pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. An array was expected.", (char) arg_type);
         return NULL;
     }
 
-    if (dbus_message_iter_get_element_type(iter) != DBUS_TYPE_DICT_ENTRY) {
-        pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type. A dict entry was expected.");
+    arg_type = dbus_message_iter_get_element_type(iter);
+    if (arg_type != DBUS_TYPE_DICT_ENTRY) {
+        pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type: '%c'. A dictionary entry was expected.", (char) arg_type);
         return NULL;
     }
 
@@ -788,34 +795,45 @@ pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusM
 
     dbus_message_iter_recurse(iter, &dict_iter);
 
-    while (dbus_message_iter_has_next(&dict_iter)) {
-        if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_STRING) {
-            pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict key type. A string was expected.");
+    while (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_INVALID) {
+        dbus_message_iter_recurse(&dict_iter, &dict_entry_iter);
+
+        arg_type = dbus_message_iter_get_arg_type(&dict_entry_iter);
+        if (arg_type != DBUS_TYPE_STRING) {
+            pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict key type: '%c'. A string was expected.", (char) arg_type);
             goto fail;
         }
 
-        dbus_message_iter_get_basic(&dict_iter, &key);
+        dbus_message_iter_get_basic(&dict_entry_iter, &key);
+        dbus_message_iter_next(&dict_entry_iter);
 
         if (strlen(key) <= 0 || !pa_ascii_valid(key)) {
             pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Invalid property list key.");
             goto fail;
         }
 
-        if (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_ARRAY) {
-            pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value type. An array was expected.");
+        arg_type = dbus_message_iter_get_arg_type(&dict_entry_iter);
+        if (arg_type != DBUS_TYPE_ARRAY) {
+            if (arg_type == DBUS_TYPE_INVALID)
+                pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Dict value missing.");
+            else
+                pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value type: '%c'. An array was expected.", (char) arg_type);
             goto fail;
         }
 
-        if (dbus_message_iter_get_element_type(&dict_iter) != DBUS_TYPE_BYTE) {
-            pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value item type. A byte was expected.");
+        arg_type = dbus_message_iter_get_element_type(&dict_entry_iter);
+        if (arg_type != DBUS_TYPE_BYTE) {
+            pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value item type: '%c'. A byte was expected.", (char) arg_type);
             goto fail;
         }
 
-        dbus_message_iter_get_fixed_array(&dict_iter, &value, &value_length);
+        dbus_message_iter_get_fixed_array(&dict_entry_iter, &value, &value_length);
 
         pa_assert(value_length >= 0);
 
         pa_assert_se(pa_proplist_set(proplist, key, value, value_length) >= 0);
+
+        dbus_message_iter_next(&dict_iter);
     }
 
     dbus_message_iter_next(iter);

commit d9d166a691cd5ecc786b73f1377244b8d51b4327
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Mon Aug 3 19:38:02 2009 +0300

    stream-restore: Expose module to D-Bus.

diff --git a/src/Makefile.am b/src/Makefile.am
index 47eb647..07f81a6 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1536,6 +1536,11 @@ module_stream_restore_la_LDFLAGS = $(MODULE_LDFLAGS)
 module_stream_restore_la_LIBADD = $(AM_LIBADD) libprotocol-native.la libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
 module_stream_restore_la_CFLAGS = $(AM_CFLAGS)
 
+if HAVE_DBUS
+module_stream_restore_la_LIBADD += $(DBUS_LIBS)
+module_stream_restore_la_CFLAGS += $(DBUS_CFLAGS)
+endif
+
 # Card profile restore module
 module_card_restore_la_SOURCES = modules/module-card-restore.c
 module_card_restore_la_LDFLAGS = $(MODULE_LDFLAGS)
diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c
index e60cc73..bccdf93 100644
--- a/src/modules/module-stream-restore.c
+++ b/src/modules/module-stream-restore.c
@@ -2,6 +2,7 @@
   This file is part of PulseAudio.
 
   Copyright 2008 Lennart Poettering
+  Copyright 2009 Tanu Kaskinen
 
   PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
@@ -51,6 +52,11 @@
 #include <pulsecore/pstream-util.h>
 #include <pulsecore/database.h>
 
+#ifdef HAVE_DBUS
+#include <pulsecore/dbus-util.h>
+#include <pulsecore/protocol-dbus.h>
+#endif
+
 #include "module-stream-restore-symdef.h"
 
 PA_MODULE_AUTHOR("Lennart Poettering");
@@ -100,6 +106,12 @@ struct userdata {
 
     pa_native_protocol *protocol;
     pa_idxset *subscribed;
+
+#ifdef HAVE_DBUS
+    pa_dbus_protocol *dbus_protocol;
+    pa_hashmap *dbus_entries;
+    uint32_t next_index; /* For generating object paths for entries. */
+#endif
 };
 
 #define ENTRY_VERSION 2
@@ -122,6 +134,883 @@ enum {
     SUBCOMMAND_EVENT
 };
 
+static struct entry *read_entry(struct userdata *u, const char *name);
+static void apply_entry(struct userdata *u, const char *name, struct entry *e);
+static void trigger_save(struct userdata *u);
+
+#ifdef HAVE_DBUS
+
+#define OBJECT_PATH "/org/pulseaudio/stream_restore1"
+#define ENTRY_OBJECT_NAME "entry"
+#define INTERFACE_STREAM_RESTORE "org.PulseAudio.Ext.StreamRestore1"
+#define INTERFACE_ENTRY INTERFACE_STREAM_RESTORE ".RestoreEntry"
+
+#define DBUS_INTERFACE_REVISION 0
+
+struct dbus_entry {
+    struct userdata *userdata;
+
+    char *entry_name;
+    uint32_t index;
+    char *object_path;
+};
+
+static void handle_get_interface_revision(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_entries(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_add_entry(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_entry_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_entry_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_entry_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_entry_get_device(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_entry_set_device(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_entry_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_entry_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_entry_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_entry_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_entry_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_entry_remove(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+enum property_handler_index {
+    PROPERTY_HANDLER_INTERFACE_REVISION,
+    PROPERTY_HANDLER_ENTRIES,
+    PROPERTY_HANDLER_MAX
+};
+
+enum entry_property_handler_index {
+    ENTRY_PROPERTY_HANDLER_INDEX,
+    ENTRY_PROPERTY_HANDLER_NAME,
+    ENTRY_PROPERTY_HANDLER_DEVICE,
+    ENTRY_PROPERTY_HANDLER_VOLUME,
+    ENTRY_PROPERTY_HANDLER_IS_MUTED,
+    ENTRY_PROPERTY_HANDLER_MAX
+};
+
+static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
+    [PROPERTY_HANDLER_INTERFACE_REVISION] = { .property_name = "InterfaceRevision", .type = "u",  .get_cb = handle_get_interface_revision, .set_cb = NULL },
+    [PROPERTY_HANDLER_ENTRIES]            = { .property_name = "Entries",           .type = "ao", .get_cb = handle_get_entries,            .set_cb = NULL }
+};
+
+static pa_dbus_property_handler entry_property_handlers[ENTRY_PROPERTY_HANDLER_MAX] = {
+    [ENTRY_PROPERTY_HANDLER_INDEX]    = { .property_name = "Index",   .type = "u",     .get_cb = handle_entry_get_index,    .set_cb = NULL },
+    [ENTRY_PROPERTY_HANDLER_NAME]     = { .property_name = "Name",    .type = "s",     .get_cb = handle_entry_get_name,     .set_cb = NULL },
+    [ENTRY_PROPERTY_HANDLER_DEVICE]   = { .property_name = "Device",  .type = "s",     .get_cb = handle_entry_get_device,   .set_cb = handle_entry_set_device },
+    [ENTRY_PROPERTY_HANDLER_VOLUME]   = { .property_name = "Volume",  .type = "a(uu)", .get_cb = handle_entry_get_volume,   .set_cb = handle_entry_set_volume },
+    [ENTRY_PROPERTY_HANDLER_IS_MUTED] = { .property_name = "IsMuted", .type = "b",     .get_cb = handle_entry_get_is_muted, .set_cb = handle_entry_set_is_muted }
+};
+
+enum method_handler_index {
+    METHOD_HANDLER_ADD_ENTRY,
+    METHOD_HANDLER_GET_ENTRY_BY_NAME,
+    METHOD_HANDLER_MAX
+};
+
+enum entry_method_handler_index {
+    ENTRY_METHOD_HANDLER_REMOVE,
+    ENTRY_METHOD_HANDLER_MAX
+};
+
+static pa_dbus_arg_info add_entry_args[] = { { "name",     "s",     "in" },
+                                             { "device",   "s",     "in" },
+                                             { "volume",   "a(uu)", "in" },
+                                             { "is_muted", "b",     "in" },
+                                             { "entry",    "o",     "out" } };
+static pa_dbus_arg_info get_entry_by_name_args[] = { { "name", "s", "in" }, { "entry", "o", "out" } };
+
+static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
+    [METHOD_HANDLER_ADD_ENTRY] = {
+        .method_name = "AddEntry",
+        .arguments = add_entry_args,
+        .n_arguments = sizeof(add_entry_args) / sizeof(pa_dbus_arg_info),
+        .receive_cb = handle_add_entry },
+    [METHOD_HANDLER_GET_ENTRY_BY_NAME] = {
+        .method_name = "GetEntryByName",
+        .arguments = get_entry_by_name_args,
+        .n_arguments = sizeof(get_entry_by_name_args) / sizeof(pa_dbus_arg_info),
+        .receive_cb = handle_get_entry_by_name }
+};
+
+static pa_dbus_method_handler entry_method_handlers[ENTRY_METHOD_HANDLER_MAX] = {
+    [ENTRY_METHOD_HANDLER_REMOVE] = {
+        .method_name = "Remove",
+        .arguments = NULL,
+        .n_arguments = 0,
+        .receive_cb = handle_entry_remove }
+};
+
+enum signal_index {
+    SIGNAL_NEW_ENTRY,
+    SIGNAL_ENTRY_REMOVED,
+    SIGNAL_MAX
+};
+
+enum entry_signal_index {
+    ENTRY_SIGNAL_DEVICE_UPDATED,
+    ENTRY_SIGNAL_VOLUME_UPDATED,
+    ENTRY_SIGNAL_MUTE_UPDATED,
+    ENTRY_SIGNAL_MAX
+};
+
+static pa_dbus_arg_info new_entry_args[]     = { { "entry", "o", NULL } };
+static pa_dbus_arg_info entry_removed_args[] = { { "entry", "o", NULL } };
+
+static pa_dbus_arg_info entry_device_updated_args[] = { { "device", "s",     NULL } };
+static pa_dbus_arg_info entry_volume_updated_args[] = { { "volume", "a(uu)", NULL } };
+static pa_dbus_arg_info entry_mute_updated_args[]   = { { "muted",  "b",     NULL } };
+
+static pa_dbus_signal_info signals[SIGNAL_MAX] = {
+    [SIGNAL_NEW_ENTRY]     = { .name = "NewEntry",     .arguments = new_entry_args,     .n_arguments = 1 },
+    [SIGNAL_ENTRY_REMOVED] = { .name = "EntryRemoved", .arguments = entry_removed_args, .n_arguments = 1 }
+};
+
+static pa_dbus_signal_info entry_signals[ENTRY_SIGNAL_MAX] = {
+    [ENTRY_SIGNAL_DEVICE_UPDATED] = { .name = "DeviceUpdated", .arguments = entry_device_updated_args, .n_arguments = 1 },
+    [ENTRY_SIGNAL_VOLUME_UPDATED] = { .name = "VolumeUpdated", .arguments = entry_volume_updated_args, .n_arguments = 1 },
+    [ENTRY_SIGNAL_MUTE_UPDATED]   = { .name = "MuteUpdated",   .arguments = entry_mute_updated_args,   .n_arguments = 1 }
+};
+
+static pa_dbus_interface_info stream_restore_interface_info = {
+    .name = INTERFACE_STREAM_RESTORE,
+    .method_handlers = method_handlers,
+    .n_method_handlers = METHOD_HANDLER_MAX,
+    .property_handlers = property_handlers,
+    .n_property_handlers = PROPERTY_HANDLER_MAX,
+    .get_all_properties_cb = handle_get_all,
+    .signals = signals,
+    .n_signals = SIGNAL_MAX
+};
+
+static pa_dbus_interface_info entry_interface_info = {
+    .name = INTERFACE_ENTRY,
+    .method_handlers = entry_method_handlers,
+    .n_method_handlers = ENTRY_METHOD_HANDLER_MAX,
+    .property_handlers = entry_property_handlers,
+    .n_property_handlers = ENTRY_PROPERTY_HANDLER_MAX,
+    .get_all_properties_cb = handle_entry_get_all,
+    .signals = entry_signals,
+    .n_signals = ENTRY_SIGNAL_MAX
+};
+
+static struct dbus_entry *dbus_entry_new(struct userdata *u, const char *entry_name) {
+    struct dbus_entry *de;
+
+    pa_assert(u);
+    pa_assert(entry_name);
+    pa_assert(*entry_name);
+
+    de = pa_xnew(struct dbus_entry, 1);
+    de->userdata = u;
+    de->entry_name = pa_xstrdup(entry_name);
+    de->index = u->next_index++;
+    de->object_path = pa_sprintf_malloc("%s/%s%u", OBJECT_PATH, ENTRY_OBJECT_NAME, de->index);
+
+    pa_assert_se(pa_dbus_protocol_add_interface(u->dbus_protocol, de->object_path, &entry_interface_info, u) >= 0);
+
+    return de;
+}
+
+static void dbus_entry_free(struct dbus_entry *de) {
+    pa_assert(de);
+
+    pa_assert_se(pa_dbus_protocol_remove_interface(de->userdata->dbus_protocol, de->object_path, entry_interface_info.name) >= 0);
+
+    pa_xfree(de->entry_name);
+    pa_xfree(de->object_path);
+}
+
+/* Reads an array [(UInt32, UInt32)] from the iterator. The struct items are
+ * are a channel position and a volume value, respectively. The result is
+ * stored in the map and vol arguments. If the volume can't be read from the
+ * iterator, an error reply is sent and a negative number is returned. In case
+ * of a failure we make no guarantees about the state of map and vol. In case
+ * of an empty array the channels field of both map and vol are set to 0. */
+static int get_volume_arg(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, pa_channel_map *map, pa_cvolume *vol) {
+    DBusMessageIter array_iter;
+    DBusMessageIter struct_iter;
+    int arg_type;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(iter);
+    pa_assert(map);
+    pa_assert(vol);
+
+    pa_channel_map_init(map);
+    pa_cvolume_init(vol);
+
+    map->channels = 0;
+    vol->channels = 0;
+
+    arg_type = dbus_message_iter_get_arg_type(iter);
+    if (arg_type != DBUS_TYPE_ARRAY) {
+        if (arg_type == DBUS_TYPE_INVALID)
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. An array was expected.");
+        else
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. An array was expected.", (char) arg_type);
+        return -1;
+    }
+
+    arg_type = dbus_message_iter_get_element_type(iter);
+    if (arg_type != DBUS_TYPE_STRUCT) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type: '%c'. A struct was expected.", (char) arg_type);
+        return -1;
+    }
+
+    dbus_message_iter_recurse(iter, &array_iter);
+
+    while (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_INVALID) {
+        dbus_uint32_t chan_pos;
+        dbus_uint32_t chan_vol;
+
+        dbus_message_iter_recurse(&array_iter, &struct_iter);
+
+        arg_type = dbus_message_iter_get_arg_type(&struct_iter);
+        if (arg_type != DBUS_TYPE_UINT32) {
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong channel position type: '%c'. An unsigned 32-bit integer was expected.", (char) arg_type);
+            return -1;
+        }
+
+        dbus_message_iter_get_basic(&struct_iter, &chan_pos);
+        dbus_message_iter_next(&struct_iter);
+
+        if (chan_pos >= PA_CHANNEL_POSITION_MAX) {
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid channel position: %u", chan_pos);
+            return -1;
+        }
+
+        arg_type = dbus_message_iter_get_arg_type(&struct_iter);
+        if (arg_type != DBUS_TYPE_UINT32) {
+            if (arg_type == DBUS_TYPE_INVALID)
+                pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Channel volume missing.");
+            else
+                pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong volume value type: '%c'. An unsigned 32-bit integer was expected.", (char) arg_type);
+            return -1;
+        }
+
+        dbus_message_iter_get_basic(&struct_iter, &chan_vol);
+
+        if (chan_vol > PA_VOLUME_MAX) {
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid volume: %u", chan_vol);
+            return -1;
+        }
+
+        if (map->channels < PA_CHANNELS_MAX) {
+            map->map[map->channels] = chan_pos;
+            vol->values[map->channels] = chan_vol;
+        }
+        ++map->channels;
+        ++vol->channels;
+
+        dbus_message_iter_next(&array_iter);
+    }
+
+    if (map->channels > PA_CHANNELS_MAX) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too many channels: %u. The maximum is %u.", map->channels, PA_CHANNELS_MAX);
+        return -1;
+    }
+
+    dbus_message_iter_next(iter);
+
+    return 0;
+}
+
+static void append_volume(DBusMessageIter *iter, struct entry *e) {
+    DBusMessageIter array_iter;
+    DBusMessageIter struct_iter;
+    unsigned i;
+
+    pa_assert(iter);
+    pa_assert(e);
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "(uu)", &array_iter));
+
+    if (!e->volume_valid) {
+        pa_assert_se(dbus_message_iter_close_container(iter, &array_iter));
+        return;
+    }
+
+    for (i = 0; i < e->channel_map.channels; ++i) {
+        pa_assert_se(dbus_message_iter_open_container(&array_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter));
+
+        pa_assert_se(dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT32, &e->channel_map.map[i]));
+        pa_assert_se(dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT32, &e->volume.values[i]));
+
+        pa_assert_se(dbus_message_iter_close_container(&array_iter, &struct_iter));
+    }
+
+    pa_assert_se(dbus_message_iter_close_container(iter, &array_iter));
+}
+
+static void append_volume_variant(DBusMessageIter *iter, struct entry *e) {
+    DBusMessageIter variant_iter;
+
+    pa_assert(iter);
+    pa_assert(e);
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a(uu)", &variant_iter));
+
+    append_volume(&variant_iter, e);
+
+    pa_assert_se(dbus_message_iter_close_container(iter, &variant_iter));
+}
+
+static void send_new_entry_signal(struct dbus_entry *entry) {
+    DBusMessage *signal;
+
+    pa_assert(entry);
+
+    pa_assert_se(signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_STREAM_RESTORE, signals[SIGNAL_NEW_ENTRY].name));
+    pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &entry->object_path, DBUS_TYPE_INVALID));
+    pa_dbus_protocol_send_signal(entry->userdata->dbus_protocol, signal);
+    dbus_message_unref(signal);
+}
+
+static void send_entry_removed_signal(struct dbus_entry *entry) {
+    DBusMessage *signal;
+
+    pa_assert(entry);
+
+    pa_assert_se(signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_STREAM_RESTORE, signals[SIGNAL_ENTRY_REMOVED].name));
+    pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &entry->object_path, DBUS_TYPE_INVALID));
+    pa_dbus_protocol_send_signal(entry->userdata->dbus_protocol, signal);
+    dbus_message_unref(signal);
+}
+
+static void send_device_updated_signal(struct dbus_entry *de, struct entry *e) {
+    DBusMessage *signal;
+    const char *device;
+
+    pa_assert(de);
+    pa_assert(e);
+
+    device = e->device_valid ? e->device : "";
+
+    pa_assert_se(signal = dbus_message_new_signal(de->object_path, INTERFACE_ENTRY, entry_signals[ENTRY_SIGNAL_DEVICE_UPDATED].name));
+    pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_STRING, &device, DBUS_TYPE_INVALID));
+    pa_dbus_protocol_send_signal(de->userdata->dbus_protocol, signal);
+    dbus_message_unref(signal);
+}
+
+static void send_volume_updated_signal(struct dbus_entry *de, struct entry *e) {
+    DBusMessage *signal;
+    DBusMessageIter msg_iter;
+
+    pa_assert(de);
+    pa_assert(e);
+
+    pa_assert_se(signal = dbus_message_new_signal(de->object_path, INTERFACE_ENTRY, entry_signals[ENTRY_SIGNAL_VOLUME_UPDATED].name));
+    dbus_message_iter_init_append(signal, &msg_iter);
+    append_volume(&msg_iter, e);
+    pa_dbus_protocol_send_signal(de->userdata->dbus_protocol, signal);
+    dbus_message_unref(signal);
+}
+
+static void send_mute_updated_signal(struct dbus_entry *de, struct entry *e) {
+    DBusMessage *signal;
+    dbus_bool_t muted;
+
+    pa_assert(de);
+    pa_assert(e);
+
+    pa_assert(e->muted_valid);
+
+    muted = e->muted;
+
+    pa_assert_se(signal = dbus_message_new_signal(de->object_path, INTERFACE_ENTRY, entry_signals[ENTRY_SIGNAL_MUTE_UPDATED].name));
+    pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_BOOLEAN, &muted, DBUS_TYPE_INVALID));
+    pa_dbus_protocol_send_signal(de->userdata->dbus_protocol, signal);
+    dbus_message_unref(signal);
+}
+
+static void handle_get_interface_revision(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    dbus_uint32_t interface_revision = DBUS_INTERFACE_REVISION;
+
+    pa_assert(conn);
+    pa_assert(msg);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &interface_revision);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_entries(struct userdata *u, unsigned *n) {
+    const char **entries;
+    unsigned i = 0;
+    void *state = NULL;
+    struct dbus_entry *de;
+
+    pa_assert(u);
+    pa_assert(n);
+
+    *n = pa_hashmap_size(u->dbus_entries);
+
+    if (*n == 0)
+        return NULL;
+
+    entries = pa_xnew(const char *, *n);
+
+    while ((de = pa_hashmap_iterate(u->dbus_entries, &state, NULL))) {
+        entries[i] = de->object_path;
+        ++i;
+    }
+
+    return entries;
+}
+
+static void handle_get_entries(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    struct userdata *u = userdata;
+    const char **entries;
+    unsigned n;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(u);
+
+    entries = get_entries(u, &n);
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, entries, n);
+
+    pa_xfree(entries);
+}
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    struct userdata *u = userdata;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+    DBusMessageIter dict_iter;
+    dbus_uint32_t interface_revision;
+    const char **entries;
+    unsigned n_entries;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(u);
+
+    interface_revision = DBUS_INTERFACE_REVISION;
+    entries = get_entries(u, &n_entries);
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INTERFACE_REVISION].property_name, DBUS_TYPE_UINT32, &interface_revision);
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_ENTRIES].property_name, DBUS_TYPE_OBJECT_PATH, entries, n_entries);
+
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+
+    dbus_message_unref(reply);
+
+    pa_xfree(entries);
+}
+
+static void handle_add_entry(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    struct userdata *u = userdata;
+    DBusMessageIter msg_iter;
+    const char *name;
+    const char *device;
+    pa_channel_map map;
+    pa_cvolume vol;
+    dbus_bool_t muted;
+    dbus_bool_t apply_immediately;
+    pa_datum key;
+    pa_datum value;
+    struct dbus_entry *dbus_entry;
+    struct entry *e;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(u);
+
+    if (!dbus_message_iter_init(msg, &msg_iter)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments.");
+        return;
+    }
+
+    if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_STRING, &name) < 0)
+        return;
+
+    if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_STRING, &device) < 0)
+        return;
+
+    if (get_volume_arg(conn, msg, &msg_iter, &map, &vol) < 0)
+        return;
+
+    if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_BOOLEAN, &muted) < 0)
+        return;
+
+    if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_BOOLEAN, &apply_immediately) < 0)
+        return;
+
+    if (!*name) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "An empty string was given as the entry name.");
+        return;
+    }
+
+    if ((dbus_entry = pa_hashmap_get(u->dbus_entries, name))) {
+        pa_bool_t mute_updated = FALSE;
+        pa_bool_t volume_updated = FALSE;
+        pa_bool_t device_updated = FALSE;
+
+        pa_assert_se(e = read_entry(u, name));
+        mute_updated = e->muted != muted;
+        e->muted = muted;
+        e->muted_valid = TRUE;
+
+        volume_updated = (e->volume_valid != !!map.channels) || !pa_cvolume_equal(&e->volume, &vol);
+        e->volume = vol;
+        e->channel_map = map;
+        e->volume_valid = !!map.channels;
+
+        device_updated = (e->device_valid != !!device[0]) || !pa_streq(e->device, device);
+        pa_strlcpy(e->device, device, sizeof(e->device));
+        e->device_valid = !!device[0];
+
+        if (mute_updated)
+            send_mute_updated_signal(dbus_entry, e);
+        if (volume_updated)
+            send_volume_updated_signal(dbus_entry, e);
+        if (device_updated)
+            send_device_updated_signal(dbus_entry, e);
+
+    } else {
+        dbus_entry = dbus_entry_new(u, name);
+        pa_assert(pa_hashmap_put(u->dbus_entries, dbus_entry->entry_name, dbus_entry) >= 0);
+
+        e->muted_valid = TRUE;
+        e->volume_valid = !!map.channels;
+        e->device_valid = !!device[0];
+        e->muted = muted;
+        e->volume = vol;
+        e->channel_map = map;
+        pa_strlcpy(e->device, device, sizeof(e->device));
+
+        send_new_entry_signal(dbus_entry);
+    }
+
+    key.data = (char *) name;
+    key.size = strlen(name);
+
+    value.data = e;
+    value.size = sizeof(struct entry);
+
+    pa_assert_se(pa_database_set(u->database, &key, &value, TRUE) == 0);
+    if (apply_immediately)
+        apply_entry(u, name, e);
+
+    trigger_save(u);
+
+    pa_dbus_send_empty_reply(conn, msg);
+
+    pa_xfree(e);
+}
+
+static void handle_get_entry_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    struct userdata *u = userdata;
+    const char *name;
+    struct dbus_entry *de;
+    DBusError error;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(u);
+
+    dbus_error_init(&error);
+
+    if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
+        dbus_error_free(&error);
+        return;
+    }
+
+    if (!(de = pa_hashmap_get(u->dbus_entries, name))) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such stream restore entry.");
+        return;
+    }
+
+    pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &de->object_path);
+}
+
+static void handle_entry_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    struct dbus_entry *de = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(de);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &de->index);
+}
+
+static void handle_entry_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    struct dbus_entry *de = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(de);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &de->entry_name);
+}
+
+static void handle_entry_get_device(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    struct dbus_entry *de = userdata;
+    struct entry *e;
+    const char *device;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(de);
+
+    pa_assert_se(e = read_entry(de->userdata, de->entry_name));
+
+    device = e->device_valid ? e->device : "";
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &device);
+
+    pa_xfree(e);
+}
+
+static void handle_entry_set_device(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    struct dbus_entry *de = userdata;
+    const char *device;
+    struct entry *e;
+    pa_bool_t updated;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(de);
+
+    if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_STRING, &device) < 0)
+        return;
+
+    pa_assert_se(e = read_entry(de->userdata, de->entry_name));
+
+    updated = (e->device_valid != !!device[0]) || !pa_streq(e->device, device);
+
+    if (updated) {
+        pa_datum key;
+        pa_datum value;
+
+        pa_strlcpy(e->device, device, sizeof(e->device));
+        e->device_valid = !!device[0];
+
+        key.data = de->entry_name;
+        key.size = strlen(de->entry_name);
+        value.data = e;
+        value.size = sizeof(struct entry);
+        pa_assert_se(pa_database_set(de->userdata->database, &key, &value, TRUE) == 0);
+
+        send_device_updated_signal(de, e);
+        trigger_save(de->userdata);
+    }
+
+    pa_dbus_send_empty_reply(conn, msg);
+
+    pa_xfree(e);
+}
+
+static void handle_entry_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    struct dbus_entry *de = userdata;
+    DBusMessage *reply;
+    DBusMessageIter msg_iter;
+    struct entry *e;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(de);
+
+    pa_assert_se(e = read_entry(de->userdata, de->entry_name));
+
+    pa_assert_se(reply = dbus_message_new_method_return(msg));
+
+    dbus_message_iter_init_append(reply, &msg_iter);
+    append_volume_variant(&msg_iter, e);
+
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+
+    pa_xfree(e);
+}
+
+static void handle_entry_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    struct dbus_entry *de = userdata;
+    DBusMessageIter msg_iter;
+    pa_channel_map map;
+    pa_cvolume vol;
+    struct entry *e;
+    pa_bool_t updated;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(de);
+
+    /* Skip the interface and property name arguments. */
+    if (!dbus_message_iter_init(msg, &msg_iter) || !dbus_message_iter_next(&msg_iter) || !dbus_message_iter_next(&msg_iter)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments.");
+        return;
+    }
+
+    if (get_volume_arg(conn, msg, &msg_iter, &map, &vol) < 0)
+        return;
+
+    pa_assert_se(e = read_entry(de->userdata, de->entry_name));
+
+    updated = (e->volume_valid != !!map.channels) || !pa_cvolume_equal(&e->volume, &vol);
+
+    if (updated) {
+        pa_datum key;
+        pa_datum value;
+
+        e->volume = vol;
+        e->channel_map = map;
+        e->volume_valid = !!map.channels;
+
+        key.data = de->entry_name;
+        key.size = strlen(de->entry_name);
+        value.data = e;
+        value.size = sizeof(struct entry);
+        pa_assert_se(pa_database_set(de->userdata->database, &key, &value, TRUE) == 0);
+
+        send_volume_updated_signal(de, e);
+        trigger_save(de->userdata);
+    }
+
+    pa_dbus_send_empty_reply(conn, msg);
+
+    pa_xfree(e);
+}
+
+static void handle_entry_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    struct dbus_entry *de = userdata;
+    struct entry *e;
+    dbus_bool_t muted;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(de);
+
+    pa_assert_se(e = read_entry(de->userdata, de->entry_name));
+
+    muted = e->muted_valid ? e->muted : FALSE;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &muted);
+
+    pa_xfree(e);
+}
+
+static void handle_entry_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    struct dbus_entry *de = userdata;
+    pa_bool_t muted;
+    struct entry *e;
+    pa_bool_t updated;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(de);
+
+    if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_BOOLEAN, &muted) < 0)
+        return;
+
+    pa_assert_se(e = read_entry(de->userdata, de->entry_name));
+
+    updated = !e->muted_valid || e->muted != muted;
+
+    if (updated) {
+        pa_datum key;
+        pa_datum value;
+
+        e->muted = muted;
+        e->muted_valid = TRUE;
+
+        key.data = de->entry_name;
+        key.size = strlen(de->entry_name);
+        value.data = e;
+        value.size = sizeof(struct entry);
+        pa_assert_se(pa_database_set(de->userdata->database, &key, &value, TRUE) == 0);
+
+        send_mute_updated_signal(de, e);
+        trigger_save(de->userdata);
+    }
+
+    pa_dbus_send_empty_reply(conn, msg);
+
+    pa_xfree(e);
+}
+
+static void handle_entry_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    struct dbus_entry *de = userdata;
+    struct entry *e;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+    DBusMessageIter dict_iter;
+    DBusMessageIter dict_entry_iter;
+    const char *device;
+    dbus_bool_t muted;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(de);
+
+    pa_assert_se(e = read_entry(de->userdata, de->entry_name));
+
+    device = e->device_valid ? e->device : "";
+    muted = e->muted_valid ? e->muted : FALSE;
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, entry_property_handlers[ENTRY_PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &de->index);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, entry_property_handlers[ENTRY_PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &de->entry_name);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, entry_property_handlers[ENTRY_PROPERTY_HANDLER_DEVICE].property_name, DBUS_TYPE_STRING, &device);
+
+    pa_assert_se(dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter));
+
+    pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &entry_property_handlers[ENTRY_PROPERTY_HANDLER_VOLUME].property_name));
+    append_volume_variant(&dict_entry_iter, e);
+
+    pa_assert_se(dbus_message_iter_close_container(&dict_iter, &dict_entry_iter));
+
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, entry_property_handlers[ENTRY_PROPERTY_HANDLER_IS_MUTED].property_name, DBUS_TYPE_BOOLEAN, &muted);
+
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+
+    dbus_message_unref(reply);
+
+    pa_xfree(e);
+}
+
+static void handle_entry_remove(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    struct dbus_entry *de = userdata;
+    pa_datum key;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(de);
+
+    key.data = de->entry_name;
+    key.size = strlen(de->entry_name);
+
+    pa_assert_se(pa_database_unset(de->userdata->database, &key) == 0);
+
+    send_entry_removed_signal(de);
+    trigger_save(de->userdata);
+
+    pa_assert_se(pa_hashmap_remove(de->userdata->dbus_entries, de->entry_name));
+    dbus_entry_free(de);
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+#endif /* HAVE_DBUS */
+
 static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
     struct userdata *u = userdata;
 
@@ -162,7 +1051,7 @@ static char *get_name(pa_proplist *p, const char *prefix) {
     return t;
 }
 
-static struct entry* read_entry(struct userdata *u, const char *name) {
+static struct entry *read_entry(struct userdata *u, const char *name) {
     pa_datum key, data;
     struct entry *e;
 
@@ -270,6 +1159,17 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
     char *name;
     pa_datum key, data;
 
+    /* These are only used when D-Bus is enabled, but in order to reduce ifdef
+     * clutter these are defined here unconditionally. */
+    pa_bool_t created_new_entry = TRUE;
+    pa_bool_t device_updated = FALSE;
+    pa_bool_t volume_updated = FALSE;
+    pa_bool_t mute_updated = FALSE;
+
+#ifdef HAVE_DBUS
+    struct dbus_entry *de = NULL;
+#endif
+
     pa_assert(c);
     pa_assert(u);
 
@@ -291,23 +1191,34 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
         if (!(name = get_name(sink_input->proplist, "sink-input")))
             return;
 
-        if ((old = read_entry(u, name)))
+        if ((old = read_entry(u, name))) {
             entry = *old;
+            created_new_entry = FALSE;
+        }
 
         if (sink_input->save_volume) {
             entry.channel_map = sink_input->channel_map;
             pa_sink_input_get_volume(sink_input, &entry.volume, FALSE);
             entry.volume_valid = TRUE;
+
+            volume_updated = !created_new_entry
+                             && (!old->volume_valid
+                                 || !pa_channel_map_equal(&entry.channel_map, &old->channel_map)
+                                 || !pa_cvolume_equal(&entry.volume, &old->volume));
         }
 
         if (sink_input->save_muted) {
             entry.muted = pa_sink_input_get_mute(sink_input);
             entry.muted_valid = TRUE;
+
+            mute_updated = !created_new_entry && (!old->muted_valid || entry.muted != old->muted);
         }
 
         if (sink_input->save_sink) {
             pa_strlcpy(entry.device, sink_input->sink->name, sizeof(entry.device));
             entry.device_valid = TRUE;
+
+            device_updated = !created_new_entry && (!old->device_valid || !pa_streq(entry.device, old->device));
         }
 
     } else {
@@ -321,12 +1232,16 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
         if (!(name = get_name(source_output->proplist, "source-output")))
             return;
 
-        if ((old = read_entry(u, name)))
+        if ((old = read_entry(u, name))) {
             entry = *old;
+            created_new_entry = FALSE;
+        }
 
         if (source_output->save_source) {
             pa_strlcpy(entry.device, source_output->source->name, sizeof(entry.device));
             entry.device_valid = source_output->save_source;
+
+            device_updated = !created_new_entry && (!old->device_valid || !pa_streq(entry.device, old->device));
         }
     }
 
@@ -351,6 +1266,23 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
 
     pa_database_set(u->database, &key, &data, TRUE);
 
+#ifdef HAVE_DBUS
+    if (created_new_entry) {
+        de = dbus_entry_new(u, name);
+        pa_hashmap_put(u->dbus_entries, de->entry_name, de);
+        send_new_entry_signal(de);
+    } else {
+        pa_assert((de = pa_hashmap_get(u->dbus_entries, name)));
+
+        if (device_updated)
+            send_device_updated_signal(de, &entry);
+        if (volume_updated)
+            send_volume_updated_signal(de, &entry);
+        if (mute_updated)
+            send_mute_updated_signal(de, &entry);
+    }
+#endif
+
     pa_xfree(name);
 
     trigger_save(u);
@@ -810,14 +1742,27 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
                 mode != PA_UPDATE_SET)
                 goto fail;
 
-            if (mode == PA_UPDATE_SET)
+            if (mode == PA_UPDATE_SET) {
+#ifdef HAVE_DBUS
+                struct dbus_entry *de;
+                void *state = NULL;
+
+                while ((de = pa_hashmap_iterate(u->dbus_entries, &state, NULL))) {
+                    send_entry_removed_signal(de);
+                    dbus_entry_free(pa_hashmap_remove(u->dbus_entries, de->entry_name));
+                }
+#endif
                 pa_database_clear(u->database);
+            }
 
             while (!pa_tagstruct_eof(t)) {
                 const char *name, *device;
                 pa_bool_t muted;
                 struct entry entry;
                 pa_datum key, data;
+#ifdef HAVE_DBUS
+                struct entry *old;
+#endif
 
                 pa_zero(entry);
                 entry.version = ENTRY_VERSION;
@@ -849,15 +1794,50 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
                     !pa_namereg_is_valid_name(entry.device))
                     goto fail;
 
+#ifdef HAVE_DBUS
+                old = read_entry(u, name);
+#endif
+
                 key.data = (char*) name;
                 key.size = strlen(name);
 
                 data.data = &entry;
                 data.size = sizeof(entry);
 
-                if (pa_database_set(u->database, &key, &data, mode == PA_UPDATE_REPLACE) == 0)
+                if (pa_database_set(u->database, &key, &data, mode == PA_UPDATE_REPLACE) == 0) {
+#ifdef HAVE_DBUS
+                    struct dbus_entry *de;
+
+                    if (old) {
+                        pa_assert_se((de = pa_hashmap_get(u->dbus_entries, name)));
+
+                        if ((old->device_valid != entry.device_valid)
+                            || (entry.device_valid && !pa_streq(entry.device, old->device)))
+                            send_device_updated_signal(de, &entry);
+
+                        if ((old->volume_valid != entry.volume_valid)
+                            || (entry.volume_valid
+                                && (!pa_cvolume_equal(&entry.volume, &old->volume) || !pa_channel_map_equal(&entry.channel_map, &old->channel_map))))
+                            send_volume_updated_signal(de, &entry);
+
+                        if (!old->muted_valid || (entry.muted != old->muted))
+                            send_mute_updated_signal(de, &entry);
+
+                    } else {
+                        de = dbus_entry_new(u, name);
+                        pa_assert_se(pa_hashmap_put(u->dbus_entries, de->entry_name, de));
+                        send_new_entry_signal(de);
+                    }
+#endif
+
                     if (apply_immediately)
                         apply_entry(u, name, &entry);
+                }
+
+#ifdef HAVE_DBUS
+                if (old)
+                    pa_xfree(old);
+#endif
             }
 
             trigger_save(u);
@@ -870,10 +1850,20 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
             while (!pa_tagstruct_eof(t)) {
                 const char *name;
                 pa_datum key;
+#ifdef HAVE_DBUS
+                struct dbus_entry *de;
+#endif
 
                 if (pa_tagstruct_gets(t, &name) < 0)
                     goto fail;
 
+#ifdef HAVE_DBUS
+                if ((de = pa_hashmap_get(u->dbus_entries, name))) {
+                    send_entry_removed_signal(de);
+                    dbus_entry_free(pa_hashmap_remove(u->dbus_entries, name));
+                }
+#endif
+
                 key.data = (char*) name;
                 key.size = strlen(name);
 
@@ -932,6 +1922,10 @@ int pa__init(pa_module*m) {
     pa_source_output *so;
     uint32_t idx;
     pa_bool_t restore_device = TRUE, restore_volume = TRUE, restore_muted = TRUE, on_hotplug = TRUE, on_rescue = TRUE;
+#ifdef HAVE_DBUS
+    pa_datum key;
+    pa_bool_t done;
+#endif
 
     pa_assert(m);
 
@@ -1002,6 +1996,34 @@ int pa__init(pa_module*m) {
     pa_log_info("Sucessfully opened database file '%s'.", fname);
     pa_xfree(fname);
 
+#ifdef HAVE_DBUS
+    u->dbus_protocol = pa_dbus_protocol_get(u->core);
+    u->dbus_entries = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
+    pa_assert_se(pa_dbus_protocol_add_interface(u->dbus_protocol, OBJECT_PATH, &stream_restore_interface_info, u) >= 0);
+    pa_assert_se(pa_dbus_protocol_register_extension(u->dbus_protocol, INTERFACE_STREAM_RESTORE) >= 0);
+
+    /* Create the initial dbus entries. */
+    done = !pa_database_first(u->database, &key, NULL);
+    while (!done) {
+        pa_datum next_key;
+        char *name;
+        struct dbus_entry *de;
+
+        done = !pa_database_next(u->database, &key, &next_key, NULL);
+
+        name = pa_xstrndup(key.data, key.size);
+        pa_datum_free(&key);
+
+        de = dbus_entry_new(u, name);
+        pa_assert_se(pa_hashmap_put(u->dbus_entries, de->entry_name, de) >= 0);
+
+        pa_xfree(name);
+
+        key = next_key;
+    }
+#endif
+
     PA_IDXSET_FOREACH(si, m->core->sink_inputs, idx)
         subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, si->index, u);
 
@@ -1020,6 +2042,16 @@ fail:
     return  -1;
 }
 
+#ifdef HAVE_DBUS
+static void free_dbus_entry_cb(void *p, void *userdata) {
+    struct dbus_entry *de = p;
+
+    pa_assert(de);
+
+    dbus_entry_free(de);
+}
+#endif
+
 void pa__done(pa_module*m) {
     struct userdata* u;
 
@@ -1028,6 +2060,19 @@ void pa__done(pa_module*m) {
     if (!(u = m->userdata))
         return;
 
+#ifdef HAVE_DBUS
+    if (u->dbus_protocol) {
+        pa_assert(u->dbus_entries);
+
+        pa_assert_se(pa_dbus_protocol_unregister_extension(u->dbus_protocol, INTERFACE_STREAM_RESTORE) >= 0);
+        pa_assert_se(pa_dbus_protocol_remove_interface(u->dbus_protocol, OBJECT_PATH, stream_restore_interface_info.name) >= 0);
+
+        pa_hashmap_free(u->dbus_entries, free_dbus_entry_cb, NULL);
+
+        pa_dbus_protocol_unref(u->dbus_protocol);
+    }
+#endif
+
     if (u->subscription)
         pa_subscription_free(u->subscription);
 

commit 8966c61d3343e13502cfa210bb7123b7d7e7b27e
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Tue Aug 4 17:50:18 2009 +0300

    dbusiface-core: Make the interface string a public constant.

diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c
index 69c1bd2..695e4a3 100644
--- a/src/modules/dbus/iface-core.c
+++ b/src/modules/dbus/iface-core.c
@@ -48,7 +48,6 @@
 #include "iface-core.h"
 
 #define OBJECT_PATH "/org/pulseaudio/core1"
-#define INTERFACE_CORE "org.PulseAudio.Core1"
 
 #define INTERFACE_REVISION 0
 
@@ -312,7 +311,7 @@ static pa_dbus_signal_info signals[SIGNAL_MAX] = {
 };
 
 static pa_dbus_interface_info core_interface_info = {
-    .name = INTERFACE_CORE,
+    .name = PA_DBUSIFACE_CORE_INTERFACE,
     .method_handlers = method_handlers,
     .n_method_handlers = METHOD_HANDLER_MAX,
     .property_handlers = property_handlers,
@@ -1612,7 +1611,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
                 if (new_fallback_sink && (device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(new_fallback_sink->index)))) {
                     object_path = pa_dbusiface_device_get_path(device);
 
-                    pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name)));
+                    pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name)));
                     pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
                     pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
                     dbus_message_unref(signal);
@@ -1626,7 +1625,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
                 if (new_fallback_source && (device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(new_fallback_source->index)))) {
                     object_path = pa_dbusiface_device_get_path(device);
 
-                    pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name)));
+                    pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name)));
                     pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
                     pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
                     dbus_message_unref(signal);
@@ -1644,7 +1643,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_card_get_path(card);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_CARD].name)));
+                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_CARD].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
             } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
@@ -1652,7 +1651,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_card_get_path(card);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_CARD_REMOVED].name)));
+                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_CARD_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbusiface_card_free(card);
@@ -1671,7 +1670,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_device_get_path(device);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_SINK].name)));
+                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_SINK].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
@@ -1683,7 +1682,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
                      * the D-Bus sink object wasn't created yet. Now that the
                      * object is created, let's send the fallback sink change
                      * signal. */
-                    pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name)));
+                    pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name)));
                     pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                     pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
@@ -1696,7 +1695,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
                 object_path = pa_dbusiface_device_get_path(device);
                 pa_assert_se(pa_hashmap_remove(c->sinks_by_path, object_path));
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_SINK_REMOVED].name)));
+                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_SINK_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbusiface_device_free(device);
@@ -1715,7 +1714,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_device_get_path(device);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_SOURCE].name)));
+                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_SOURCE].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
@@ -1727,7 +1726,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
                      * point the D-Bus source object wasn't created yet. Now
                      * that the object is created, let's send the fallback
                      * source change signal. */
-                    pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name)));
+                    pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name)));
                     pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                     pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
@@ -1740,7 +1739,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
                 object_path = pa_dbusiface_device_get_path(device);
                 pa_assert_se(pa_hashmap_remove(c->sources_by_path, object_path));
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_SOURCE_REMOVED].name)));
+                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_SOURCE_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbusiface_device_free(device);
@@ -1756,7 +1755,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_stream_get_path(stream);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_PLAYBACK_STREAM].name)));
+                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_PLAYBACK_STREAM].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
             } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
@@ -1764,7 +1763,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_stream_get_path(stream);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_PLAYBACK_STREAM_REMOVED].name)));
+                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_PLAYBACK_STREAM_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbusiface_stream_free(stream);
@@ -1780,7 +1779,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_stream_get_path(stream);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_RECORD_STREAM].name)));
+                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_RECORD_STREAM].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
             } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
@@ -1788,7 +1787,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_stream_get_path(stream);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_RECORD_STREAM_REMOVED].name)));
+                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_RECORD_STREAM_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbusiface_stream_free(stream);
@@ -1804,7 +1803,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_sample_get_path(sample);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_SAMPLE].name)));
+                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_SAMPLE].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
             } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
@@ -1812,7 +1811,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_sample_get_path(sample);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_SAMPLE_REMOVED].name)));
+                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_SAMPLE_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbusiface_sample_free(sample);
@@ -1828,7 +1827,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_module_get_path(module);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_MODULE].name)));
+                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_MODULE].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
             } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
@@ -1836,7 +1835,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_module_get_path(module);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_MODULE_REMOVED].name)));
+                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_MODULE_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbusiface_module_free(module);
@@ -1852,7 +1851,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_client_get_path(client);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_CLIENT].name)));
+                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_CLIENT].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
             } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
@@ -1860,7 +1859,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_client_get_path(client);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_CLIENT_REMOVED].name)));
+                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_CLIENT_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbusiface_client_free(client);
@@ -1882,7 +1881,7 @@ static pa_hook_result_t extension_registered_cb(void *hook_data, void *call_data
     pa_assert(c);
     pa_assert(ext_name);
 
-    pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_NEW_EXTENSION].name)));
+    pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_EXTENSION].name)));
     pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_STRING, &ext_name, DBUS_TYPE_INVALID));
 
     pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
@@ -1899,7 +1898,7 @@ static pa_hook_result_t extension_unregistered_cb(void *hook_data, void *call_da
     pa_assert(c);
     pa_assert(ext_name);
 
-    pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, INTERFACE_CORE, signals[SIGNAL_EXTENSION_REMOVED].name)));
+    pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_EXTENSION_REMOVED].name)));
     pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_STRING, &ext_name, DBUS_TYPE_INVALID));
 
     pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
@@ -2028,7 +2027,7 @@ static void free_client_cb(void *p, void *userdata) {
 void pa_dbusiface_core_free(pa_dbusiface_core *c) {
     pa_assert(c);
 
-    pa_dbus_protocol_remove_interface(c->dbus_protocol, OBJECT_PATH, INTERFACE_CORE);
+    pa_dbus_protocol_remove_interface(c->dbus_protocol, OBJECT_PATH, core_interface_info.name);
 
     pa_subscription_free(c->subscription);
     pa_hashmap_free(c->cards, free_card_cb, NULL);
diff --git a/src/modules/dbus/iface-core.h b/src/modules/dbus/iface-core.h
index 964a37b..6c5191b 100644
--- a/src/modules/dbus/iface-core.h
+++ b/src/modules/dbus/iface-core.h
@@ -30,6 +30,8 @@
 
 #include <pulsecore/core.h>
 
+#define PA_DBUSIFACE_CORE_INTERFACE "org.PulseAudio.Core1"
+
 typedef struct pa_dbusiface_core pa_dbusiface_core;
 
 pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core);

commit b1578e27b62e7332111bb706f79858b0866029e3
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Tue Aug 4 17:55:10 2009 +0300

    dbus-protocol, dbusiface-core: Take a reference when storing the core pointer.

diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c
index 695e4a3..ca9ba58 100644
--- a/src/modules/dbus/iface-core.c
+++ b/src/modules/dbus/iface-core.c
@@ -1923,7 +1923,7 @@ pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) {
     pa_assert(core);
 
     c = pa_xnew(pa_dbusiface_core, 1);
-    c->core = core;
+    c->core = pa_core_ref(core);
     c->subscription = pa_subscription_new(core, PA_SUBSCRIPTION_MASK_ALL, subscription_cb, c);
     c->dbus_protocol = pa_dbus_protocol_get(core);
     c->cards = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
@@ -2044,6 +2044,7 @@ void pa_dbusiface_core_free(pa_dbusiface_core *c) {
     pa_hook_slot_free(c->extension_unregistered_slot);
 
     pa_dbus_protocol_unref(c->dbus_protocol);
+    pa_core_unref(c->core);
 
     pa_xfree(c);
 }
diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c
index 475b952..8fc0803 100644
--- a/src/pulsecore/protocol-dbus.c
+++ b/src/pulsecore/protocol-dbus.c
@@ -117,7 +117,7 @@ static pa_dbus_protocol *dbus_protocol_new(pa_core *c) {
 
     p = pa_xnew(pa_dbus_protocol, 1);
     PA_REFCNT_INIT(p);
-    p->core = c;
+    p->core = pa_core_ref(c);
     p->objects = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
     p->connections = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
     p->extensions = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
@@ -170,6 +170,8 @@ void pa_dbus_protocol_unref(pa_dbus_protocol *p) {
 
     pa_assert_se(pa_shared_remove(p->core, "dbus-protocol") >= 0);
 
+    pa_core_unref(p->core);
+
     pa_xfree(p);
 }
 

commit 9eeb8eb2729cdc77fa98c704eb8fc7fcca15336b
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Tue Aug 4 17:57:44 2009 +0300

    dbus-protocol: Make debug logging saner.

diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c
index 8fc0803..c82bb5f 100644
--- a/src/pulsecore/protocol-dbus.c
+++ b/src/pulsecore/protocol-dbus.c
@@ -308,7 +308,6 @@ static enum find_result_t find_handler_by_method(struct object_entry *obj_entry,
             return FOUND_METHOD;
     }
 
-    pa_log("find_handler_by_method() failed.");
     return NO_SUCH_METHOD;
 }
 
@@ -331,7 +330,6 @@ static enum find_result_t find_handler_from_properties_call(struct object_entry
             if ((*iface_entry = pa_hashmap_get(obj_entry->interfaces, interface)))
                 return FOUND_GET_ALL;
             else {
-                pa_log("GetAll message has unknown interface: %s", interface);
                 return NO_SUCH_METHOD; /* XXX: NO_SUCH_INTERFACE or something like that might be more accurate. */
             }
         } else {
@@ -418,7 +416,10 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa
     if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 
-    pa_log("Received method call: destination = %s, name = %s, iface = %s", dbus_message_get_path(message), dbus_message_get_member(message), dbus_message_get_interface(message));
+    pa_log_debug("Received message: destination = %s, interface = %s, member = %s",
+                 dbus_message_get_path(message),
+                 dbus_message_get_interface(message),
+                 dbus_message_get_member(message));
 
     pa_assert_se((obj_entry = pa_hashmap_get(p->objects, dbus_message_get_path(message))));
 
@@ -428,8 +429,6 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa
         pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &obj_entry->introspection, DBUS_TYPE_INVALID));
         pa_assert_se(dbus_connection_send(connection, reply, NULL));
 
-        pa_log_debug("%s.Introspect handled.", obj_entry->path);
-
         goto finish;
     }
 
@@ -633,7 +632,7 @@ int pa_dbus_protocol_add_interface(pa_dbus_protocol *p,
     if (obj_entry_created)
         register_object(p, obj_entry);
 
-    pa_log("Interface %s added for object %s. GetAll callback? %s", iface_entry->name, obj_entry->path, iface_entry->get_all_properties_cb ? "yes" : "no");
+    pa_log_debug("Interface %s added for object %s", iface_entry->name, obj_entry->path);
 
     return 0;
 

commit 0fc055226c60fa7429abf80e38f40a565f9e7922
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Tue Aug 4 18:00:08 2009 +0300

    dbus-protocol: Remove erroneous protocol object unref.

diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c
index c82bb5f..0609867 100644
--- a/src/pulsecore/protocol-dbus.c
+++ b/src/pulsecore/protocol-dbus.c
@@ -639,7 +639,6 @@ int pa_dbus_protocol_add_interface(pa_dbus_protocol *p,
 fail:
     if (obj_entry_created) {
         pa_hashmap_remove(p->objects, path);
-        pa_dbus_protocol_unref(p);
         pa_xfree(obj_entry);
     }
 

commit 44770c59e92f49288341afe8646d8bc39eb9f589
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Tue Aug 4 18:01:26 2009 +0300

    dbusiface-memstats: Implement the Memstats D-Bus interface.

diff --git a/src/Makefile.am b/src/Makefile.am
index 07f81a6..e10b4ab 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1281,6 +1281,7 @@ module_dbus_protocol_la_SOURCES = \
 		modules/dbus/iface-client.c modules/dbus/iface-client.h \
 		modules/dbus/iface-core.c modules/dbus/iface-core.h \
 		modules/dbus/iface-device.c modules/dbus/iface-device.h \
+		modules/dbus/iface-memstats.c modules/dbus/iface-memstats.h \
 		modules/dbus/iface-module.c modules/dbus/iface-module.h \
 		modules/dbus/iface-sample.c modules/dbus/iface-sample.h \
 		modules/dbus/iface-stream.c modules/dbus/iface-stream.h \
diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c
index ca9ba58..ad729f9 100644
--- a/src/modules/dbus/iface-core.c
+++ b/src/modules/dbus/iface-core.c
@@ -41,6 +41,7 @@
 #include "iface-card.h"
 #include "iface-client.h"
 #include "iface-device.h"
+#include "iface-memstats.h"
 #include "iface-module.h"
 #include "iface-sample.h"
 #include "iface-stream.h"
@@ -114,6 +115,8 @@ struct pa_dbusiface_core {
 
     pa_hook_slot *extension_registered_slot;
     pa_hook_slot *extension_unregistered_slot;
+
+    pa_dbusiface_memstats *memstats;
 };
 
 enum property_handler_index {
@@ -1940,6 +1943,7 @@ pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) {
     c->fallback_source = pa_namereg_get_default_source(core);
     c->extension_registered_slot = pa_dbus_protocol_hook_connect(c->dbus_protocol, PA_DBUS_PROTOCOL_HOOK_EXTENSION_REGISTERED, PA_HOOK_NORMAL, extension_registered_cb, c);
     c->extension_unregistered_slot = pa_dbus_protocol_hook_connect(c->dbus_protocol, PA_DBUS_PROTOCOL_HOOK_EXTENSION_UNREGISTERED, PA_HOOK_NORMAL, extension_unregistered_cb, c);
+    c->memstats = pa_dbusiface_memstats_new(core, OBJECT_PATH);
 
     for (card = pa_idxset_first(core->cards, &idx); card; card = pa_idxset_next(core->cards, &idx))
         pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), pa_dbusiface_card_new(card, OBJECT_PATH));
@@ -2042,6 +2046,7 @@ void pa_dbusiface_core_free(pa_dbusiface_core *c) {
     pa_hashmap_free(c->clients, free_client_cb, NULL);
     pa_hook_slot_free(c->extension_registered_slot);
     pa_hook_slot_free(c->extension_unregistered_slot);
+    pa_dbusiface_memstats_free(c->memstats);
 
     pa_dbus_protocol_unref(c->dbus_protocol);
     pa_core_unref(c->core);
diff --git a/src/modules/dbus/iface-memstats.c b/src/modules/dbus/iface-memstats.c
new file mode 100644
index 0000000..d3412a2
--- /dev/null
+++ b/src/modules/dbus/iface-memstats.c
@@ -0,0 +1,231 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <dbus/dbus.h>
+
+#include <pulsecore/core-scache.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-util.h>
+#include <pulsecore/protocol-dbus.h>
+
+#include "iface-memstats.h"
+
+#define OBJECT_NAME "memstats"
+
+static void handle_get_current_memblocks(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_current_memblocks_size(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_accumulated_memblocks(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_accumulated_memblocks_size(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sample_cache_size(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+struct pa_dbusiface_memstats {
+    pa_core *core;
+    char *path;
+    pa_dbus_protocol *dbus_protocol;
+};
+
+enum property_handler_index {
+    PROPERTY_HANDLER_CURRENT_MEMBLOCKS,
+    PROPERTY_HANDLER_CURRENT_MEMBLOCKS_SIZE,
+    PROPERTY_HANDLER_ACCUMULATED_MEMBLOCKS,
+    PROPERTY_HANDLER_ACCUMULATED_MEMBLOCKS_SIZE,
+    PROPERTY_HANDLER_SAMPLE_CACHE_SIZE,
+    PROPERTY_HANDLER_MAX
+};
+
+static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
+    [PROPERTY_HANDLER_CURRENT_MEMBLOCKS]          = { .property_name = "CurrentMemblocks",         .type = "u", .get_cb = handle_get_current_memblocks,          .set_cb = NULL },
+    [PROPERTY_HANDLER_CURRENT_MEMBLOCKS_SIZE]     = { .property_name = "CurrentMemblocksSize",     .type = "u", .get_cb = handle_get_current_memblocks_size,     .set_cb = NULL },
+    [PROPERTY_HANDLER_ACCUMULATED_MEMBLOCKS]      = { .property_name = "AccumulatedMemblocks",     .type = "u", .get_cb = handle_get_accumulated_memblocks,      .set_cb = NULL },
+    [PROPERTY_HANDLER_ACCUMULATED_MEMBLOCKS_SIZE] = { .property_name = "AccumulatedMemblocksSize", .type = "u", .get_cb = handle_get_accumulated_memblocks_size, .set_cb = NULL },
+    [PROPERTY_HANDLER_SAMPLE_CACHE_SIZE]          = { .property_name = "SampleCacheSize",          .type = "u", .get_cb = handle_get_sample_cache_size,          .set_cb = NULL }
+};
+
+static pa_dbus_interface_info memstats_interface_info = {
+    .name = PA_DBUSIFACE_MEMSTATS_INTERFACE,
+    .method_handlers = NULL,
+    .n_method_handlers = 0,
+    .property_handlers = property_handlers,
+    .n_property_handlers = PROPERTY_HANDLER_MAX,
+    .get_all_properties_cb = handle_get_all,
+    .signals = NULL,
+    .n_signals = 0
+};
+
+static void handle_get_current_memblocks(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_memstats *m = userdata;
+    const pa_mempool_stat *stat;
+    dbus_uint32_t current_memblocks;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(m);
+
+    stat = pa_mempool_get_stat(m->core->mempool);
+
+    current_memblocks = pa_atomic_load(&stat->n_allocated);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &current_memblocks);
+}
+
+static void handle_get_current_memblocks_size(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_memstats *m = userdata;
+    const pa_mempool_stat *stat;
+    dbus_uint32_t current_memblocks_size;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(m);
+
+    stat = pa_mempool_get_stat(m->core->mempool);
+
+    current_memblocks_size = pa_atomic_load(&stat->allocated_size);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &current_memblocks_size);
+}
+
+static void handle_get_accumulated_memblocks(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_memstats *m = userdata;
+    const pa_mempool_stat *stat;
+    dbus_uint32_t accumulated_memblocks;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(m);
+
+    stat = pa_mempool_get_stat(m->core->mempool);
+
+    accumulated_memblocks = pa_atomic_load(&stat->n_accumulated);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &accumulated_memblocks);
+}
+
+static void handle_get_accumulated_memblocks_size(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_memstats *m = userdata;
+    const pa_mempool_stat *stat;
+    dbus_uint32_t accumulated_memblocks_size;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(m);
+
+    stat = pa_mempool_get_stat(m->core->mempool);
+
+    accumulated_memblocks_size = pa_atomic_load(&stat->accumulated_size);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &accumulated_memblocks_size);
+}
+
+static void handle_get_sample_cache_size(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_memstats *m = userdata;
+    dbus_uint32_t sample_cache_size;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(m);
+
+    sample_cache_size = pa_scache_total_size(m->core);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sample_cache_size);
+}
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_memstats *m = userdata;
+    const pa_mempool_stat *stat;
+    dbus_uint32_t current_memblocks;
+    dbus_uint32_t current_memblocks_size;
+    dbus_uint32_t accumulated_memblocks;
+    dbus_uint32_t accumulated_memblocks_size;
+    dbus_uint32_t sample_cache_size;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+    DBusMessageIter dict_iter;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(m);
+
+    stat = pa_mempool_get_stat(m->core->mempool);
+
+    current_memblocks = pa_atomic_load(&stat->n_allocated);
+    current_memblocks_size = pa_atomic_load(&stat->allocated_size);
+    accumulated_memblocks = pa_atomic_load(&stat->n_accumulated);
+    accumulated_memblocks_size = pa_atomic_load(&stat->accumulated_size);
+    sample_cache_size = pa_scache_total_size(m->core);
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CURRENT_MEMBLOCKS].property_name, DBUS_TYPE_UINT32, &current_memblocks);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CURRENT_MEMBLOCKS_SIZE].property_name, DBUS_TYPE_UINT32, &current_memblocks_size);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_ACCUMULATED_MEMBLOCKS].property_name, DBUS_TYPE_UINT32, &accumulated_memblocks);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_ACCUMULATED_MEMBLOCKS_SIZE].property_name, DBUS_TYPE_UINT32, &accumulated_memblocks_size);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_CACHE_SIZE].property_name, DBUS_TYPE_UINT32, &sample_cache_size);
+
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+
+    dbus_message_unref(reply);
+}
+
+pa_dbusiface_memstats *pa_dbusiface_memstats_new(pa_core *core, const char *path_prefix) {
+    pa_dbusiface_memstats *m;
+
+    pa_assert(core);
+    pa_assert(path_prefix);
+
+    m = pa_xnew(pa_dbusiface_memstats, 1);
+    m->core = pa_core_ref(core);
+    m->path = pa_sprintf_malloc("%s/%s", path_prefix, OBJECT_NAME);
+    m->dbus_protocol = pa_dbus_protocol_get(core);
+
+    pa_assert_se(pa_dbus_protocol_add_interface(m->dbus_protocol, m->path, &memstats_interface_info, m) >= 0);
+
+    return m;
+}
+
+void pa_dbusiface_memstats_free(pa_dbusiface_memstats *m) {
+    pa_assert(m);
+
+    pa_assert_se(pa_dbus_protocol_remove_interface(m->dbus_protocol, m->path, memstats_interface_info.name) >= 0);
+
+    pa_xfree(m->path);
+
+    pa_dbus_protocol_unref(m->dbus_protocol);
+    pa_core_unref(m->core);
+
+    pa_xfree(m);
+}
+
+const char *pa_dbusiface_memstats_get_path(pa_dbusiface_memstats *m) {
+    pa_assert(m);
+
+    return m->path;
+}
diff --git a/src/modules/dbus/iface-memstats.h b/src/modules/dbus/iface-memstats.h
new file mode 100644
index 0000000..d7773ee
--- /dev/null
+++ b/src/modules/dbus/iface-memstats.h
@@ -0,0 +1,44 @@
+#ifndef foodbusifacememstatshfoo
+#define foodbusifacememstatshfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+/* This object implements the D-Bus interface org.PulseAudio.Core1.Memstats.
+ *
+ * See http://pulseaudio.org/wiki/DBusInterface for the Memstats interface
+ * documentation.
+ */
+
+#include <pulsecore/core.h>
+
+#include "iface-core.h"
+
+#define PA_DBUSIFACE_MEMSTATS_INTERFACE PA_DBUSIFACE_CORE_INTERFACE ".Memstats"
+
+typedef struct pa_dbusiface_memstats pa_dbusiface_memstats;
+
+pa_dbusiface_memstats *pa_dbusiface_memstats_new(pa_core *core, const char *path_prefix);
+void pa_dbusiface_memstats_free(pa_dbusiface_memstats *m);
+
+const char *pa_dbusiface_memstats_get_path(pa_dbusiface_memstats *m);
+
+#endif

commit 1457df40eee692834d1c5faf95ca0057d74f86d1
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Sun Aug 9 07:59:06 2009 +0300

    proplist: New function: pa_proplist_equal()

diff --git a/src/map-file b/src/map-file
index a1d0a06..3db3a2d 100644
--- a/src/map-file
+++ b/src/map-file
@@ -180,6 +180,7 @@ pa_path_get_filename;
 pa_proplist_clear;
 pa_proplist_contains;
 pa_proplist_copy;
+pa_proplist_equal;
 pa_proplist_free;
 pa_proplist_from_string;
 pa_proplist_get;
diff --git a/src/pulse/proplist.c b/src/pulse/proplist.c
index c904f53..4f0d6a6 100644
--- a/src/pulse/proplist.c
+++ b/src/pulse/proplist.c
@@ -680,3 +680,29 @@ int pa_proplist_isempty(pa_proplist *p) {
 
     return pa_hashmap_isempty(MAKE_HASHMAP(p));
 }
+
+int pa_proplist_equal(pa_proplist *a, pa_proplist *b) {
+    const void *key = NULL;
+    struct property *a_prop = NULL;
+    struct property *b_prop = NULL;
+    void *state = NULL;
+
+    pa_assert(a);
+    pa_assert(b);
+
+    if (pa_proplist_size(a) != pa_proplist_size(b))
+        return 0;
+
+    while ((a_prop = pa_hashmap_iterate(MAKE_HASHMAP(a), &state, &key))) {
+        if (!(b_prop = pa_hashmap_get(MAKE_HASHMAP(b), key)))
+            return 0;
+
+        if (a_prop->nbytes != b_prop->nbytes)
+            return 0;
+
+        if (memcmp(a_prop->value, b_prop->value, a_prop->nbytes) != 0)
+            return 0;
+    }
+
+    return 1;
+}
diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h
index bc4dbd8..a585944 100644
--- a/src/pulse/proplist.h
+++ b/src/pulse/proplist.h
@@ -354,6 +354,9 @@ unsigned pa_proplist_size(pa_proplist *t);
 /** Returns 0 when the proplist is empty, positive otherwise \since 0.9.15 */
 int pa_proplist_isempty(pa_proplist *t);
 
+/** Return non-zero when a and b have the same keys and values. */
+int pa_proplist_equal(pa_proplist *a, pa_proplist *b);
+
 PA_C_DECL_END
 
 #endif

commit fcf68752e687123a171b1144f78f8eba1625a204
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Sun Aug 9 08:37:33 2009 +0300

    dbus: Three entangled changes:
    
     * Make the dbus object constructors take a pa_dbusiface_core pointer
       as an argument. Remove the path_prefix argument.
    
     * Expose the core object path as a constant in protocol-dbus.h.
    
     * Move the core interface name constant from iface-core.h to
       protocol-dbus.h.

diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c
index db6aa26..e203c39 100644
--- a/src/modules/dbus/iface-card.c
+++ b/src/modules/dbus/iface-card.c
@@ -24,25 +24,30 @@
 #endif
 
 #include <pulsecore/core-util.h>
+#include <pulsecore/protocol-dbus.h>
 
 #include "iface-card.h"
 
 #define OBJECT_NAME "card"
 
 struct pa_dbusiface_card {
+    pa_dbusiface_core *core;
+
     pa_card *card;
     char *path;
 };
 
-pa_dbusiface_card *pa_dbusiface_card_new(pa_card *card, const char *path_prefix) {
-    pa_dbusiface_card *c;
 
+pa_dbusiface_card *pa_dbusiface_card_new(pa_dbusiface_core *core, pa_card *card) {
+    pa_dbusiface_card *c = NULL;
+
+    pa_assert(core);
     pa_assert(card);
-    pa_assert(path_prefix);
 
-    c = pa_xnew(pa_dbusiface_card, 1);
+    c = pa_xnew0(pa_dbusiface_card, 1);
+    c->core = core;
     c->card = card;
-    c->path = pa_sprintf_malloc("%s/%s%u", path_prefix, OBJECT_NAME, card->index);
+    c->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, card->index);
 
     return c;
 }
diff --git a/src/modules/dbus/iface-card.h b/src/modules/dbus/iface-card.h
index 54db610..57ad4e3 100644
--- a/src/modules/dbus/iface-card.h
+++ b/src/modules/dbus/iface-card.h
@@ -30,9 +30,13 @@
 
 #include <pulsecore/card.h>
 
+#include "iface-core.h"
+
+#define PA_DBUSIFACE_CARD_INTERFACE PA_DBUS_CORE_INTERFACE ".Card"
+
 typedef struct pa_dbusiface_card pa_dbusiface_card;
 
-pa_dbusiface_card *pa_dbusiface_card_new(pa_card *card, const char *path_prefix);
+pa_dbusiface_card *pa_dbusiface_card_new(pa_dbusiface_core *core, pa_card *card);
 void pa_dbusiface_card_free(pa_dbusiface_card *c);
 
 const char *pa_dbusiface_card_get_path(pa_dbusiface_card *c);
diff --git a/src/modules/dbus/iface-client.c b/src/modules/dbus/iface-client.c
index cfa36d0..d9c8653 100644
--- a/src/modules/dbus/iface-client.c
+++ b/src/modules/dbus/iface-client.c
@@ -24,6 +24,7 @@
 #endif
 
 #include <pulsecore/core-util.h>
+#include <pulsecore/protocol-dbus.h>
 
 #include "iface-client.h"
 
@@ -34,15 +35,15 @@ struct pa_dbusiface_client {
     char *path;
 };
 
-pa_dbusiface_client *pa_dbusiface_client_new(pa_client *client, const char *path_prefix) {
+pa_dbusiface_client *pa_dbusiface_client_new(pa_dbusiface_core *core, pa_client *client) {
     pa_dbusiface_client *c;
 
+    pa_assert(core);
     pa_assert(client);
-    pa_assert(path_prefix);
 
     c = pa_xnew(pa_dbusiface_client, 1);
     c->client = client;
-    c->path = pa_sprintf_malloc("%s/%s%u", path_prefix, OBJECT_NAME, client->index);
+    c->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, client->index);
 
     return c;
 }
diff --git a/src/modules/dbus/iface-client.h b/src/modules/dbus/iface-client.h
index 62cca7f..ff90625 100644
--- a/src/modules/dbus/iface-client.h
+++ b/src/modules/dbus/iface-client.h
@@ -30,9 +30,11 @@
 
 #include <pulsecore/client.h>
 
+#include "iface-core.h"
+
 typedef struct pa_dbusiface_client pa_dbusiface_client;
 
-pa_dbusiface_client *pa_dbusiface_client_new(pa_client *client, const char *path_prefix);
+pa_dbusiface_client *pa_dbusiface_client_new(pa_dbusiface_core *core, pa_client *client);
 void pa_dbusiface_client_free(pa_dbusiface_client *c);
 
 const char *pa_dbusiface_client_get_path(pa_dbusiface_client *c);
diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c
index ad729f9..e40cb97 100644
--- a/src/modules/dbus/iface-core.c
+++ b/src/modules/dbus/iface-core.c
@@ -48,12 +48,8 @@
 
 #include "iface-core.h"
 
-#define OBJECT_PATH "/org/pulseaudio/core1"
-
 #define INTERFACE_REVISION 0
 
-
-
 static void handle_get_interface_revision(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_version(DBusConnection *conn, DBusMessage *msg, void *userdata);
@@ -314,7 +310,7 @@ static pa_dbus_signal_info signals[SIGNAL_MAX] = {
 };
 
 static pa_dbus_interface_info core_interface_info = {
-    .name = PA_DBUSIFACE_CORE_INTERFACE,
+    .name = PA_DBUS_CORE_INTERFACE,
     .method_handlers = method_handlers,
     .n_method_handlers = METHOD_HANDLER_MAX,
     .property_handlers = property_handlers,
@@ -1388,7 +1384,7 @@ static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *u
         sample->volume_is_set = FALSE;
     }
 
-    dbus_sample = pa_dbusiface_sample_new(sample, OBJECT_PATH);
+    dbus_sample = pa_dbusiface_sample_new(c, sample);
     pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), dbus_sample);
 
     object_path = pa_dbusiface_sample_get_path(dbus_sample);
@@ -1510,7 +1506,7 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use
         goto finish;
     }
 
-    dbus_module = pa_dbusiface_module_new(module, OBJECT_PATH);
+    dbus_module = pa_dbusiface_module_new(c, module);
     pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(module->index), dbus_module);
 
     object_path = pa_dbusiface_module_get_path(dbus_module);
@@ -1614,7 +1610,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
                 if (new_fallback_sink && (device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(new_fallback_sink->index)))) {
                     object_path = pa_dbusiface_device_get_path(device);
 
-                    pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name)));
+                    pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name)));
                     pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
                     pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
                     dbus_message_unref(signal);
@@ -1628,7 +1624,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
                 if (new_fallback_source && (device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(new_fallback_source->index)))) {
                     object_path = pa_dbusiface_device_get_path(device);
 
-                    pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name)));
+                    pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name)));
                     pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
                     pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
                     dbus_message_unref(signal);
@@ -1640,13 +1636,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
         case PA_SUBSCRIPTION_EVENT_CARD:
             if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
                 if (!(card = pa_hashmap_get(c->cards, PA_UINT32_TO_PTR(idx)))) {
-                    card = pa_dbusiface_card_new(pa_idxset_get_by_index(core->cards, idx), OBJECT_PATH);
+                    card = pa_dbusiface_card_new(c, pa_idxset_get_by_index(core->cards, idx));
                     pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), card);
                 }
 
                 object_path = pa_dbusiface_card_get_path(card);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_CARD].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_CARD].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
             } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
@@ -1654,7 +1650,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_card_get_path(card);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_CARD_REMOVED].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_CARD_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbusiface_card_free(card);
@@ -1666,14 +1662,14 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
                 pa_sink *sink = pa_idxset_get_by_index(core->sinks, idx);
 
                 if (!(device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(idx)))) {
-                    device = pa_dbusiface_device_new_sink(sink, OBJECT_PATH);
+                    device = pa_dbusiface_device_new_sink(c, sink);
                     pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(idx), device);
                     pa_hashmap_put(c->sinks_by_path, pa_dbusiface_device_get_path(device), device);
                 }
 
                 object_path = pa_dbusiface_device_get_path(device);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_SINK].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_SINK].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
@@ -1685,7 +1681,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
                      * the D-Bus sink object wasn't created yet. Now that the
                      * object is created, let's send the fallback sink change
                      * signal. */
-                    pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name)));
+                    pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name)));
                     pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                     pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
@@ -1698,7 +1694,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
                 object_path = pa_dbusiface_device_get_path(device);
                 pa_assert_se(pa_hashmap_remove(c->sinks_by_path, object_path));
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_SINK_REMOVED].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_SINK_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbusiface_device_free(device);
@@ -1710,14 +1706,14 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
                 pa_source *source = pa_idxset_get_by_index(core->sources, idx);
 
                 if (!(device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(idx)))) {
-                    device = pa_dbusiface_device_new_source(source, OBJECT_PATH);
+                    device = pa_dbusiface_device_new_source(c, source);
                     pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(idx), device);
                     pa_hashmap_put(c->sources_by_path, pa_dbusiface_device_get_path(device), device);
                 }
 
                 object_path = pa_dbusiface_device_get_path(device);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_SOURCE].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_SOURCE].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
@@ -1729,7 +1725,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
                      * point the D-Bus source object wasn't created yet. Now
                      * that the object is created, let's send the fallback
                      * source change signal. */
-                    pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name)));
+                    pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name)));
                     pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                     pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
@@ -1742,7 +1738,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
                 object_path = pa_dbusiface_device_get_path(device);
                 pa_assert_se(pa_hashmap_remove(c->sources_by_path, object_path));
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_SOURCE_REMOVED].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_SOURCE_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbusiface_device_free(device);
@@ -1752,13 +1748,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
         case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
             if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
                 if (!(stream = pa_hashmap_get(c->playback_streams, PA_UINT32_TO_PTR(idx)))) {
-                    stream = pa_dbusiface_stream_new_playback(pa_idxset_get_by_index(core->sink_inputs, idx), OBJECT_PATH);
+                    stream = pa_dbusiface_stream_new_playback(c, pa_idxset_get_by_index(core->sink_inputs, idx));
                     pa_hashmap_put(c->playback_streams, PA_UINT32_TO_PTR(idx), stream);
                 }
 
                 object_path = pa_dbusiface_stream_get_path(stream);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_PLAYBACK_STREAM].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_PLAYBACK_STREAM].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
             } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
@@ -1766,7 +1762,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_stream_get_path(stream);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_PLAYBACK_STREAM_REMOVED].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_PLAYBACK_STREAM_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbusiface_stream_free(stream);
@@ -1776,13 +1772,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
         case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT:
             if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
                 if (!(stream = pa_hashmap_get(c->record_streams, PA_UINT32_TO_PTR(idx)))) {
-                    stream = pa_dbusiface_stream_new_record(pa_idxset_get_by_index(core->source_outputs, idx), OBJECT_PATH);
+                    stream = pa_dbusiface_stream_new_record(c, pa_idxset_get_by_index(core->source_outputs, idx));
                     pa_hashmap_put(c->record_streams, PA_UINT32_TO_PTR(idx), stream);
                 }
 
                 object_path = pa_dbusiface_stream_get_path(stream);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_RECORD_STREAM].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_RECORD_STREAM].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
             } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
@@ -1790,7 +1786,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_stream_get_path(stream);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_RECORD_STREAM_REMOVED].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_RECORD_STREAM_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbusiface_stream_free(stream);
@@ -1800,13 +1796,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
         case PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE:
             if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
                 if (!(sample = pa_hashmap_get(c->samples, PA_UINT32_TO_PTR(idx)))) {
-                    sample = pa_dbusiface_sample_new(pa_idxset_get_by_index(core->scache, idx), OBJECT_PATH);
+                    sample = pa_dbusiface_sample_new(c, pa_idxset_get_by_index(core->scache, idx));
                     pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), sample);
                 }
 
                 object_path = pa_dbusiface_sample_get_path(sample);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_SAMPLE].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_SAMPLE].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
             } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
@@ -1814,7 +1810,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_sample_get_path(sample);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_SAMPLE_REMOVED].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_SAMPLE_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbusiface_sample_free(sample);
@@ -1824,13 +1820,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
         case PA_SUBSCRIPTION_EVENT_MODULE:
             if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
                 if (!(module = pa_hashmap_get(c->modules, PA_UINT32_TO_PTR(idx)))) {
-                    module = pa_dbusiface_module_new(pa_idxset_get_by_index(core->modules, idx), OBJECT_PATH);
+                    module = pa_dbusiface_module_new(c, pa_idxset_get_by_index(core->modules, idx));
                     pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), module);
                 }
 
                 object_path = pa_dbusiface_module_get_path(module);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_MODULE].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_MODULE].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
             } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
@@ -1838,7 +1834,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_module_get_path(module);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_MODULE_REMOVED].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_MODULE_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbusiface_module_free(module);
@@ -1848,13 +1844,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
         case PA_SUBSCRIPTION_EVENT_CLIENT:
             if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
                 if (!(client = pa_hashmap_get(c->clients, PA_UINT32_TO_PTR(idx)))) {
-                    client = pa_dbusiface_client_new(pa_idxset_get_by_index(core->clients, idx), OBJECT_PATH);
+                    client = pa_dbusiface_client_new(c, pa_idxset_get_by_index(core->clients, idx));
                     pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), client);
                 }
 
                 object_path = pa_dbusiface_client_get_path(client);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_CLIENT].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_CLIENT].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
             } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
@@ -1862,7 +1858,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_client_get_path(client);
 
-                pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_CLIENT_REMOVED].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_CLIENT_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbusiface_client_free(client);
@@ -1884,7 +1880,7 @@ static pa_hook_result_t extension_registered_cb(void *hook_data, void *call_data
     pa_assert(c);
     pa_assert(ext_name);
 
-    pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_NEW_EXTENSION].name)));
+    pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_EXTENSION].name)));
     pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_STRING, &ext_name, DBUS_TYPE_INVALID));
 
     pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
@@ -1901,7 +1897,7 @@ static pa_hook_result_t extension_unregistered_cb(void *hook_data, void *call_da
     pa_assert(c);
     pa_assert(ext_name);
 
-    pa_assert_se((signal = dbus_message_new_signal(OBJECT_PATH, PA_DBUSIFACE_CORE_INTERFACE, signals[SIGNAL_EXTENSION_REMOVED].name)));
+    pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_EXTENSION_REMOVED].name)));
     pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_STRING, &ext_name, DBUS_TYPE_INVALID));
 
     pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
@@ -1943,39 +1939,39 @@ pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) {
     c->fallback_source = pa_namereg_get_default_source(core);
     c->extension_registered_slot = pa_dbus_protocol_hook_connect(c->dbus_protocol, PA_DBUS_PROTOCOL_HOOK_EXTENSION_REGISTERED, PA_HOOK_NORMAL, extension_registered_cb, c);
     c->extension_unregistered_slot = pa_dbus_protocol_hook_connect(c->dbus_protocol, PA_DBUS_PROTOCOL_HOOK_EXTENSION_UNREGISTERED, PA_HOOK_NORMAL, extension_unregistered_cb, c);
-    c->memstats = pa_dbusiface_memstats_new(core, OBJECT_PATH);
+    c->memstats = pa_dbusiface_memstats_new(c, core);
 
     for (card = pa_idxset_first(core->cards, &idx); card; card = pa_idxset_next(core->cards, &idx))
-        pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), pa_dbusiface_card_new(card, OBJECT_PATH));
+        pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), pa_dbusiface_card_new(c, card));
 
     for (sink = pa_idxset_first(core->sinks, &idx); sink; sink = pa_idxset_next(core->sinks, &idx)) {
-        device = pa_dbusiface_device_new_sink(sink, OBJECT_PATH);
+        device = pa_dbusiface_device_new_sink(c, sink);
         pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(idx), device);
         pa_hashmap_put(c->sinks_by_path, pa_dbusiface_device_get_path(device), device);
     }
 
     for (source = pa_idxset_first(core->sources, &idx); source; source = pa_idxset_next(core->sources, &idx)) {
-        device = pa_dbusiface_device_new_source(source, OBJECT_PATH);
+        device = pa_dbusiface_device_new_source(c, source);
         pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(idx), device);
         pa_hashmap_put(c->sources_by_path, pa_dbusiface_device_get_path(device), device);
     }
 
     for (sink_input = pa_idxset_first(core->sink_inputs, &idx); sink_input; sink_input = pa_idxset_next(core->sink_inputs, &idx))
-        pa_hashmap_put(c->playback_streams, PA_UINT32_TO_PTR(idx), pa_dbusiface_stream_new_playback(sink_input, OBJECT_PATH));
+        pa_hashmap_put(c->playback_streams, PA_UINT32_TO_PTR(idx), pa_dbusiface_stream_new_playback(c, sink_input));
 
     for (source_output = pa_idxset_first(core->source_outputs, &idx); source_output; source_output = pa_idxset_next(core->source_outputs, &idx))
-        pa_hashmap_put(c->record_streams, PA_UINT32_TO_PTR(idx), pa_dbusiface_stream_new_record(source_output, OBJECT_PATH));
+        pa_hashmap_put(c->record_streams, PA_UINT32_TO_PTR(idx), pa_dbusiface_stream_new_record(c, source_output));
 
     for (sample = pa_idxset_first(core->scache, &idx); sample; sample = pa_idxset_next(core->scache, &idx))
-        pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), pa_dbusiface_sample_new(sample, OBJECT_PATH));
+        pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), pa_dbusiface_sample_new(c, sample));
 
     for (module = pa_idxset_first(core->modules, &idx); module; module = pa_idxset_next(core->modules, &idx))
-        pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), pa_dbusiface_module_new(module, OBJECT_PATH));
+        pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), pa_dbusiface_module_new(c, module));
 
     for (client = pa_idxset_first(core->clients, &idx); client; client = pa_idxset_next(core->clients, &idx))
-        pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), pa_dbusiface_client_new(client, OBJECT_PATH));
+        pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), pa_dbusiface_client_new(c, client));
 
-    pa_dbus_protocol_add_interface(c->dbus_protocol, OBJECT_PATH, &core_interface_info, c);
+    pa_dbus_protocol_add_interface(c->dbus_protocol, PA_DBUS_CORE_OBJECT_PATH, &core_interface_info, c);
 
     return c;
 }
@@ -2031,7 +2027,7 @@ static void free_client_cb(void *p, void *userdata) {
 void pa_dbusiface_core_free(pa_dbusiface_core *c) {
     pa_assert(c);
 
-    pa_dbus_protocol_remove_interface(c->dbus_protocol, OBJECT_PATH, core_interface_info.name);
+    pa_dbus_protocol_remove_interface(c->dbus_protocol, PA_DBUS_CORE_OBJECT_PATH, core_interface_info.name);
 
     pa_subscription_free(c->subscription);
     pa_hashmap_free(c->cards, free_card_cb, NULL);
diff --git a/src/modules/dbus/iface-core.h b/src/modules/dbus/iface-core.h
index 6c5191b..964a37b 100644
--- a/src/modules/dbus/iface-core.h
+++ b/src/modules/dbus/iface-core.h
@@ -30,8 +30,6 @@
 
 #include <pulsecore/core.h>
 
-#define PA_DBUSIFACE_CORE_INTERFACE "org.PulseAudio.Core1"
-
 typedef struct pa_dbusiface_core pa_dbusiface_core;
 
 pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core);
diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c
index 3b3795e..1a64f43 100644
--- a/src/modules/dbus/iface-device.c
+++ b/src/modules/dbus/iface-device.c
@@ -24,6 +24,7 @@
 #endif
 
 #include <pulsecore/core-util.h>
+#include <pulsecore/protocol-dbus.h>
 
 #include "iface-device.h"
 
@@ -44,30 +45,30 @@ struct pa_dbusiface_device {
     char *path;
 };
 
-pa_dbusiface_device *pa_dbusiface_device_new_sink(pa_sink *sink, const char *path_prefix) {
+pa_dbusiface_device *pa_dbusiface_device_new_sink(pa_dbusiface_core *core, pa_sink *sink) {
     pa_dbusiface_device *d;
 
+    pa_assert(core);
     pa_assert(sink);
-    pa_assert(path_prefix);
 
     d = pa_xnew(pa_dbusiface_device, 1);
     d->sink = pa_sink_ref(sink);
     d->type = DEVICE_TYPE_SINK;
-    d->path = pa_sprintf_malloc("%s/%s%u", path_prefix, SINK_OBJECT_NAME, sink->index);
+    d->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, SINK_OBJECT_NAME, sink->index);
 
     return d;
 }
 
-pa_dbusiface_device *pa_dbusiface_device_new_source(pa_source *source, const char *path_prefix) {
+pa_dbusiface_device *pa_dbusiface_device_new_source(pa_dbusiface_core *core, pa_source *source) {
     pa_dbusiface_device *d;
 
+    pa_assert(core);
     pa_assert(source);
-    pa_assert(path_prefix);
 
     d = pa_xnew(pa_dbusiface_device, 1);
     d->source = pa_source_ref(source);
     d->type = DEVICE_TYPE_SOURCE;
-    d->path = pa_sprintf_malloc("%s/%s%u", path_prefix, SOURCE_OBJECT_NAME, source->index);
+    d->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, SOURCE_OBJECT_NAME, source->index);
 
     return d;
 }
diff --git a/src/modules/dbus/iface-device.h b/src/modules/dbus/iface-device.h
index 81ad1d8..1e9af83 100644
--- a/src/modules/dbus/iface-device.h
+++ b/src/modules/dbus/iface-device.h
@@ -32,10 +32,12 @@
 #include <pulsecore/sink.h>
 #include <pulsecore/source.h>
 
+#include "iface-core.h"
+
 typedef struct pa_dbusiface_device pa_dbusiface_device;
 
-pa_dbusiface_device *pa_dbusiface_device_new_sink(pa_sink *sink, const char *path_prefix);
-pa_dbusiface_device *pa_dbusiface_device_new_source(pa_source *source, const char *path_prefix);
+pa_dbusiface_device *pa_dbusiface_device_new_sink(pa_dbusiface_core *core, pa_sink *sink);
+pa_dbusiface_device *pa_dbusiface_device_new_source(pa_dbusiface_core *core, pa_source *source);
 void pa_dbusiface_device_free(pa_dbusiface_device *d);
 
 const char *pa_dbusiface_device_get_path(pa_dbusiface_device *d);
diff --git a/src/modules/dbus/iface-memstats.c b/src/modules/dbus/iface-memstats.c
index d3412a2..73a84be 100644
--- a/src/modules/dbus/iface-memstats.c
+++ b/src/modules/dbus/iface-memstats.c
@@ -195,15 +195,15 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat
     dbus_message_unref(reply);
 }
 
-pa_dbusiface_memstats *pa_dbusiface_memstats_new(pa_core *core, const char *path_prefix) {
+pa_dbusiface_memstats *pa_dbusiface_memstats_new(pa_dbusiface_core *dbus_core, pa_core *core) {
     pa_dbusiface_memstats *m;
 
+    pa_assert(dbus_core);
     pa_assert(core);
-    pa_assert(path_prefix);
 
     m = pa_xnew(pa_dbusiface_memstats, 1);
     m->core = pa_core_ref(core);
-    m->path = pa_sprintf_malloc("%s/%s", path_prefix, OBJECT_NAME);
+    m->path = pa_sprintf_malloc("%s/%s", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME);
     m->dbus_protocol = pa_dbus_protocol_get(core);
 
     pa_assert_se(pa_dbus_protocol_add_interface(m->dbus_protocol, m->path, &memstats_interface_info, m) >= 0);
diff --git a/src/modules/dbus/iface-memstats.h b/src/modules/dbus/iface-memstats.h
index d7773ee..0820e8f 100644
--- a/src/modules/dbus/iface-memstats.h
+++ b/src/modules/dbus/iface-memstats.h
@@ -29,14 +29,15 @@
  */
 
 #include <pulsecore/core.h>
+#include <pulsecore/protocol-dbus.h>
 
 #include "iface-core.h"
 
-#define PA_DBUSIFACE_MEMSTATS_INTERFACE PA_DBUSIFACE_CORE_INTERFACE ".Memstats"
+#define PA_DBUSIFACE_MEMSTATS_INTERFACE PA_DBUS_CORE_INTERFACE ".Memstats"
 
 typedef struct pa_dbusiface_memstats pa_dbusiface_memstats;
 
-pa_dbusiface_memstats *pa_dbusiface_memstats_new(pa_core *core, const char *path_prefix);
+pa_dbusiface_memstats *pa_dbusiface_memstats_new(pa_dbusiface_core *dbus_core, pa_core *core);
 void pa_dbusiface_memstats_free(pa_dbusiface_memstats *m);
 
 const char *pa_dbusiface_memstats_get_path(pa_dbusiface_memstats *m);
diff --git a/src/modules/dbus/iface-module.c b/src/modules/dbus/iface-module.c
index 1c95f9e..788d104 100644
--- a/src/modules/dbus/iface-module.c
+++ b/src/modules/dbus/iface-module.c
@@ -24,6 +24,7 @@
 #endif
 
 #include <pulsecore/core-util.h>
+#include <pulsecore/protocol-dbus.h>
 
 #include "iface-module.h"
 
@@ -34,15 +35,15 @@ struct pa_dbusiface_module {
     char *path;
 };
 
-pa_dbusiface_module *pa_dbusiface_module_new(pa_module *module, const char *path_prefix) {
+pa_dbusiface_module *pa_dbusiface_module_new(pa_dbusiface_core *core, pa_module *module) {
     pa_dbusiface_module *m;
 
+    pa_assert(core);
     pa_assert(module);
-    pa_assert(path_prefix);
 
     m = pa_xnew(pa_dbusiface_module, 1);
     m->module = module;
-    m->path = pa_sprintf_malloc("%s/%s%u", path_prefix, OBJECT_NAME, module->index);
+    m->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, module->index);
 
     return m;
 }
diff --git a/src/modules/dbus/iface-module.h b/src/modules/dbus/iface-module.h
index 7f683e6..c4f2921 100644
--- a/src/modules/dbus/iface-module.h
+++ b/src/modules/dbus/iface-module.h
@@ -30,9 +30,11 @@
 
 #include <pulsecore/module.h>
 
+#include "iface-core.h"
+
 typedef struct pa_dbusiface_module pa_dbusiface_module;
 
-pa_dbusiface_module *pa_dbusiface_module_new(pa_module *module, const char *path_prefix);
+pa_dbusiface_module *pa_dbusiface_module_new(pa_dbusiface_core *core, pa_module *module);
 void pa_dbusiface_module_free(pa_dbusiface_module *m);
 
 const char *pa_dbusiface_module_get_path(pa_dbusiface_module *m);
diff --git a/src/modules/dbus/iface-sample.c b/src/modules/dbus/iface-sample.c
index b4a308a..44cfb03 100644
--- a/src/modules/dbus/iface-sample.c
+++ b/src/modules/dbus/iface-sample.c
@@ -24,6 +24,7 @@
 #endif
 
 #include <pulsecore/core-util.h>
+#include <pulsecore/protocol-dbus.h>
 
 #include "iface-sample.h"
 
@@ -34,15 +35,15 @@ struct pa_dbusiface_sample {
     char *path;
 };
 
-pa_dbusiface_sample *pa_dbusiface_sample_new(pa_scache_entry *sample, const char *path_prefix) {
+pa_dbusiface_sample *pa_dbusiface_sample_new(pa_dbusiface_core *core, pa_scache_entry *sample) {
     pa_dbusiface_sample *s;
 
+    pa_assert(core);
     pa_assert(sample);
-    pa_assert(path_prefix);
 
     s = pa_xnew(pa_dbusiface_sample, 1);
     s->sample = sample;
-    s->path = pa_sprintf_malloc("%s/%s%u", path_prefix, OBJECT_NAME, sample->index);
+    s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, sample->index);
 
     return s;
 }
diff --git a/src/modules/dbus/iface-sample.h b/src/modules/dbus/iface-sample.h
index 1b85404..1b82648 100644
--- a/src/modules/dbus/iface-sample.h
+++ b/src/modules/dbus/iface-sample.h
@@ -30,9 +30,11 @@
 
 #include <pulsecore/core-scache.h>
 
+#include "iface-core.h"
+
 typedef struct pa_dbusiface_sample pa_dbusiface_sample;
 
-pa_dbusiface_sample *pa_dbusiface_sample_new(pa_scache_entry *sample, const char *path_prefix);
+pa_dbusiface_sample *pa_dbusiface_sample_new(pa_dbusiface_core *core, pa_scache_entry *sample);
 void pa_dbusiface_sample_free(pa_dbusiface_sample *c);
 
 const char *pa_dbusiface_sample_get_path(pa_dbusiface_sample *c);
diff --git a/src/modules/dbus/iface-stream.c b/src/modules/dbus/iface-stream.c
index 1d9ffee..b5a1789 100644
--- a/src/modules/dbus/iface-stream.c
+++ b/src/modules/dbus/iface-stream.c
@@ -24,6 +24,7 @@
 #endif
 
 #include <pulsecore/core-util.h>
+#include <pulsecore/protocol-dbus.h>
 
 #include "iface-stream.h"
 
@@ -44,30 +45,30 @@ struct pa_dbusiface_stream {
     char *path;
 };
 
-pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_sink_input *sink_input, const char *path_prefix) {
+pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, pa_sink_input *sink_input) {
     pa_dbusiface_stream *s;
 
+    pa_assert(core);
     pa_assert(sink_input);
-    pa_assert(path_prefix);
 
     s = pa_xnew(pa_dbusiface_stream, 1);
     s->sink_input = pa_sink_input_ref(sink_input);
     s->type = STREAM_TYPE_PLAYBACK;
-    s->path = pa_sprintf_malloc("%s/%s%u", path_prefix, PLAYBACK_OBJECT_NAME, sink_input->index);
+    s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, PLAYBACK_OBJECT_NAME, sink_input->index);
 
     return s;
 }
 
-pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_source_output *source_output, const char *path_prefix) {
+pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_dbusiface_core *core, pa_source_output *source_output) {
     pa_dbusiface_stream *s;
 
+    pa_assert(core);
     pa_assert(source_output);
-    pa_assert(path_prefix);
 
     s = pa_xnew(pa_dbusiface_stream, 1);
     s->source_output = pa_source_output_ref(source_output);
     s->type = STREAM_TYPE_RECORD;
-    s->path = pa_sprintf_malloc("%s/%s%u", path_prefix, RECORD_OBJECT_NAME, source_output->index);
+    s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, RECORD_OBJECT_NAME, source_output->index);
 
     return s;
 }
diff --git a/src/modules/dbus/iface-stream.h b/src/modules/dbus/iface-stream.h
index cc2f3d6..b1b1854 100644
--- a/src/modules/dbus/iface-stream.h
+++ b/src/modules/dbus/iface-stream.h
@@ -31,10 +31,12 @@
 #include <pulsecore/sink-input.h>
 #include <pulsecore/source-output.h>
 
+#include "iface-core.h"
+
 typedef struct pa_dbusiface_stream pa_dbusiface_stream;
 
-pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_sink_input *sink_input, const char *path_prefix);
-pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_source_output *source_output, const char *path_prefix);
+pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, pa_sink_input *sink_input);
+pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_dbusiface_core *core, pa_source_output *source_output);
 void pa_dbusiface_stream_free(pa_dbusiface_stream *s);
 
 const char *pa_dbusiface_stream_get_path(pa_dbusiface_stream *s);
diff --git a/src/pulsecore/protocol-dbus.h b/src/pulsecore/protocol-dbus.h
index f2b1b50..c6b630a 100644
--- a/src/pulsecore/protocol-dbus.h
+++ b/src/pulsecore/protocol-dbus.h
@@ -32,8 +32,11 @@
 
 #define PA_DBUS_SYSTEM_SOCKET_PATH PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_DBUS_SOCKET_NAME
 
-#define PA_DBUS_ERROR_NO_SUCH_PROPERTY "org.PulseAudio.Core1.NoSuchPropertyError"
-#define PA_DBUS_ERROR_NOT_FOUND "org.PulseAudio.Core1.NotFoundError"
+#define PA_DBUS_CORE_INTERFACE "org.PulseAudio.Core1"
+#define PA_DBUS_CORE_OBJECT_PATH "/org/pulseaudio/core1"
+
+#define PA_DBUS_ERROR_NO_SUCH_PROPERTY PA_DBUS_CORE_INTERFACE ".NoSuchPropertyError"
+#define PA_DBUS_ERROR_NOT_FOUND PA_DBUS_CORE_INTERFACE ".NotFoundError"
 
 /* Returns the default address of the server type in the escaped form. For
  * PA_SERVER_TYPE_NONE an empty string is returned. The caller frees the

commit 06232e2965ee02d62ca566fcbf5e805c571b574a
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Sun Aug 9 09:04:15 2009 +0300

    dbus: Take advantage of the PA_HASHMAP_FOREACH macro.

diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c
index e40cb97..685ba63 100644
--- a/src/modules/dbus/iface-core.c
+++ b/src/modules/dbus/iface-core.c
@@ -542,7 +542,7 @@ static void handle_set_default_sample_rate(DBusConnection *conn, DBusMessage *ms
 /* The caller frees the array, but not the strings. */
 static const char **get_cards(pa_dbusiface_core *c, unsigned *n) {
     const char **cards;
-    unsigned i;
+    unsigned i = 0;
     void *state = NULL;
     pa_dbusiface_card *card;
 
@@ -556,10 +556,8 @@ static const char **get_cards(pa_dbusiface_core *c, unsigned *n) {
 
     cards = pa_xnew(const char *, *n);
 
-    for (i = 0, card = pa_hashmap_iterate(c->cards, &state, NULL); card; ++i, card = pa_hashmap_iterate(c->cards, &state, NULL))
-        cards[i] = pa_dbusiface_card_get_path(card);
-
-    pa_assert(i == *n);
+    PA_HASHMAP_FOREACH(card, c->cards, state)
+        cards[i++] = pa_dbusiface_card_get_path(card);
 
     return cards;
 }
@@ -583,7 +581,7 @@ static void handle_get_cards(DBusConnection *conn, DBusMessage *msg, void *userd
 /* The caller frees the array, but not the strings. */
 static const char **get_sinks(pa_dbusiface_core *c, unsigned *n) {
     const char **sinks;
-    unsigned i;
+    unsigned i = 0;
     void *state = NULL;
     pa_dbusiface_device *sink;
 
@@ -597,10 +595,8 @@ static const char **get_sinks(pa_dbusiface_core *c, unsigned *n) {
 
     sinks = pa_xnew(const char *, *n);
 
-    for (i = 0, sink = pa_hashmap_iterate(c->sinks_by_index, &state, NULL); sink; ++i, sink = pa_hashmap_iterate(c->sinks_by_index, &state, NULL))
-        sinks[i] = pa_dbusiface_device_get_path(sink);
-
-    pa_assert(i == *n);
+    PA_HASHMAP_FOREACH(sink, c->sinks_by_index, state)
+        sinks[i++] = pa_dbusiface_device_get_path(sink);
 
     return sinks;
 }
@@ -671,7 +667,7 @@ static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, voi
 /* The caller frees the array, but not the strings. */
 static const char **get_sources(pa_dbusiface_core *c, unsigned *n) {
     const char **sources;
-    unsigned i;
+    unsigned i = 0;
     void *state = NULL;
     pa_dbusiface_device *source;
 
@@ -685,10 +681,8 @@ static const char **get_sources(pa_dbusiface_core *c, unsigned *n) {
 
     sources = pa_xnew(const char *, *n);
 
-    for (i = 0, source = pa_hashmap_iterate(c->sources_by_index, &state, NULL); source; ++i, source = pa_hashmap_iterate(c->sources_by_index, &state, NULL))
-        sources[i] = pa_dbusiface_device_get_path(source);
-
-    pa_assert(i == *n);
+    PA_HASHMAP_FOREACH(source, c->sources_by_index, state)
+        sources[i++] = pa_dbusiface_device_get_path(source);
 
     return sources;
 }
@@ -759,7 +753,7 @@ static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, v
 /* The caller frees the array, but not the strings. */
 static const char **get_playback_streams(pa_dbusiface_core *c, unsigned *n) {
     const char **streams;
-    unsigned i;
+    unsigned i = 0;
     void *state = NULL;
     pa_dbusiface_stream *stream;
 
@@ -773,10 +767,8 @@ static const char **get_playback_streams(pa_dbusiface_core *c, unsigned *n) {
 
     streams = pa_xnew(const char *, *n);
 
-    for (i = 0, stream = pa_hashmap_iterate(c->playback_streams, &state, NULL); stream; ++i, stream = pa_hashmap_iterate(c->playback_streams, &state, NULL))
-        streams[i] = pa_dbusiface_stream_get_path(stream);
-
-    pa_assert(i == *n);
+    PA_HASHMAP_FOREACH(stream, c->playback_streams, state)
+        streams[i++] = pa_dbusiface_stream_get_path(stream);
 
     return streams;
 }
@@ -800,7 +792,7 @@ static void handle_get_playback_streams(DBusConnection *conn, DBusMessage *msg,
 /* The caller frees the array, but not the strings. */
 static const char **get_record_streams(pa_dbusiface_core *c, unsigned *n) {
     const char **streams;
-    unsigned i;
+    unsigned i = 0;
     void *state = NULL;
     pa_dbusiface_stream *stream;
 
@@ -814,10 +806,8 @@ static const char **get_record_streams(pa_dbusiface_core *c, unsigned *n) {
 
     streams = pa_xnew(const char *, *n);
 
-    for (i = 0, stream = pa_hashmap_iterate(c->record_streams, &state, NULL); stream; ++i, stream = pa_hashmap_iterate(c->record_streams, &state, NULL))
-        streams[i] = pa_dbusiface_stream_get_path(stream);
-
-    pa_assert(i == *n);
+    PA_HASHMAP_FOREACH(stream, c->record_streams, state)
+        streams[i++] = pa_dbusiface_stream_get_path(stream);
 
     return streams;
 }
@@ -841,7 +831,7 @@ static void handle_get_record_streams(DBusConnection *conn, DBusMessage *msg, vo
 /* The caller frees the array, but not the strings. */
 static const char **get_samples(pa_dbusiface_core *c, unsigned *n) {
     const char **samples;
-    unsigned i;
+    unsigned i = 0;
     void *state = NULL;
     pa_dbusiface_sample *sample;
 
@@ -855,10 +845,8 @@ static const char **get_samples(pa_dbusiface_core *c, unsigned *n) {
 
     samples = pa_xnew(const char *, *n);
 
-    for (i = 0, sample = pa_hashmap_iterate(c->samples, &state, NULL); sample; ++i, sample = pa_hashmap_iterate(c->samples, &state, NULL))
-        samples[i] = pa_dbusiface_sample_get_path(sample);
-
-    pa_assert(i == *n);
+    PA_HASHMAP_FOREACH(sample, c->samples, state)
+        samples[i++] = pa_dbusiface_sample_get_path(sample);
 
     return samples;
 }
@@ -882,7 +870,7 @@ static void handle_get_samples(DBusConnection *conn, DBusMessage *msg, void *use
 /* The caller frees the array, but not the strings. */
 static const char **get_modules(pa_dbusiface_core *c, unsigned *n) {
     const char **modules;
-    unsigned i;
+    unsigned i = 0;
     void *state = NULL;
     pa_dbusiface_module *module;
 
@@ -896,10 +884,8 @@ static const char **get_modules(pa_dbusiface_core *c, unsigned *n) {
 
     modules = pa_xnew(const char *, *n);
 
-    for (i = 0, module = pa_hashmap_iterate(c->modules, &state, NULL); module; ++i, module = pa_hashmap_iterate(c->modules, &state, NULL))
-        modules[i] = pa_dbusiface_module_get_path(module);
-
-    pa_assert(i == *n);
+    PA_HASHMAP_FOREACH(module, c->modules, state)
+        modules[i++] = pa_dbusiface_module_get_path(module);
 
     return modules;
 }
@@ -923,7 +909,7 @@ static void handle_get_modules(DBusConnection *conn, DBusMessage *msg, void *use
 /* The caller frees the array, but not the strings. */
 static const char **get_clients(pa_dbusiface_core *c, unsigned *n) {
     const char **clients;
-    unsigned i;
+    unsigned i = 0;
     void *state = NULL;
     pa_dbusiface_client *client;
 
@@ -937,10 +923,8 @@ static const char **get_clients(pa_dbusiface_core *c, unsigned *n) {
 
     clients = pa_xnew(const char *, *n);
 
-    for (i = 0, client = pa_hashmap_iterate(c->clients, &state, NULL); client; ++i, client = pa_hashmap_iterate(c->clients, &state, NULL))
-        clients[i] = pa_dbusiface_client_get_path(client);
-
-    pa_assert(i == *n);
+    PA_HASHMAP_FOREACH(client, c->clients, state)
+        clients[i++] = pa_dbusiface_client_get_path(client);
 
     return clients;
 }
diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c
index bccdf93..e906303 100644
--- a/src/modules/module-stream-restore.c
+++ b/src/modules/module-stream-restore.c
@@ -553,10 +553,8 @@ static const char **get_entries(struct userdata *u, unsigned *n) {
 
     entries = pa_xnew(const char *, *n);
 
-    while ((de = pa_hashmap_iterate(u->dbus_entries, &state, NULL))) {
-        entries[i] = de->object_path;
-        ++i;
-    }
+    PA_HASHMAP_FOREACH(de, u->dbus_entries, state)
+        entries[i++] = de->object_path;
 
     return entries;
 }
@@ -1747,7 +1745,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
                 struct dbus_entry *de;
                 void *state = NULL;
 
-                while ((de = pa_hashmap_iterate(u->dbus_entries, &state, NULL))) {
+                PA_HASHMAP_FOREACH(de, u->dbus_entries, state) {
                     send_entry_removed_signal(de);
                     dbus_entry_free(pa_hashmap_remove(u->dbus_entries, de->entry_name));
                 }
diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c
index 0609867..5cbd0d9 100644
--- a/src/pulsecore/protocol-dbus.c
+++ b/src/pulsecore/protocol-dbus.c
@@ -186,7 +186,7 @@ static void update_introspection(struct object_entry *oe) {
     pa_strbuf_puts(buf, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE);
     pa_strbuf_puts(buf, "<node>\n");
 
-    while ((iface_entry = pa_hashmap_iterate(oe->interfaces, &interfaces_state, NULL))) {
+    PA_HASHMAP_FOREACH(iface_entry, oe->interfaces, interfaces_state) {
         pa_dbus_method_handler *method_handler;
         pa_dbus_property_handler *property_handler;
         void *handlers_state = NULL;
@@ -195,7 +195,7 @@ static void update_introspection(struct object_entry *oe) {
 
         pa_strbuf_printf(buf, " <interface name=\"%s\">\n", iface_entry->name);
 
-        while ((method_handler = pa_hashmap_iterate(iface_entry->method_handlers, &handlers_state, NULL))) {
+        PA_HASHMAP_FOREACH(method_handler, iface_entry->method_handlers, handlers_state) {
             pa_strbuf_printf(buf, "  <method name=\"%s\">\n", method_handler->method_name);
 
             for (i = 0; i < method_handler->n_arguments; ++i)
@@ -278,7 +278,7 @@ static enum find_result_t find_handler_by_property(struct object_entry *obj_entr
     pa_assert(iface_entry);
     pa_assert(property_handler);
 
-    while ((*iface_entry = pa_hashmap_iterate(obj_entry->interfaces, &state, NULL))) {
+    PA_HASHMAP_FOREACH(*iface_entry, obj_entry->interfaces, state) {
         if ((*property_handler = pa_hashmap_get((*iface_entry)->property_handlers, property))) {
             if (dbus_message_has_member(msg, "Get"))
                 return (*property_handler)->get_cb ? FOUND_GET_PROPERTY : PROPERTY_ACCESS_DENIED;
@@ -303,7 +303,7 @@ static enum find_result_t find_handler_by_method(struct object_entry *obj_entry,
     pa_assert(iface_entry);
     pa_assert(method_handler);
 
-    while ((*iface_entry = pa_hashmap_iterate(obj_entry->interfaces, &state, NULL))) {
+    PA_HASHMAP_FOREACH(*iface_entry, obj_entry->interfaces, state) {
         if ((*method_handler = pa_hashmap_get((*iface_entry)->method_handlers, method)))
             return FOUND_METHOD;
     }
@@ -497,7 +497,7 @@ static void register_object(pa_dbus_protocol *p, struct object_entry *obj_entry)
     pa_assert(p);
     pa_assert(obj_entry);
 
-    while ((conn_entry = pa_hashmap_iterate(p->connections, &state, NULL)))
+    PA_HASHMAP_FOREACH(conn_entry, p->connections, state)
         pa_assert_se(dbus_connection_register_object_path(conn_entry->connection, obj_entry->path, &vtable, p));
 }
 
@@ -652,7 +652,7 @@ static void unregister_object(pa_dbus_protocol *p, struct object_entry *obj_entr
     pa_assert(p);
     pa_assert(obj_entry);
 
-    while ((conn_entry = pa_hashmap_iterate(p->connections, &state, NULL)))
+    PA_HASHMAP_FOREACH(conn_entry, p->connections, state)
         pa_assert_se(dbus_connection_unregister_object_path(conn_entry->connection, obj_entry->path));
 }
 
@@ -742,7 +742,7 @@ static void register_all_objects(pa_dbus_protocol *p, DBusConnection *conn) {
     pa_assert(p);
     pa_assert(conn);
 
-    while ((obj_entry = pa_hashmap_iterate(p->objects, &state, NULL)))
+    PA_HASHMAP_FOREACH(obj_entry, p->objects, state)
         pa_assert_se(dbus_connection_register_object_path(conn, obj_entry->path, &vtable, p));
 }
 
@@ -777,7 +777,7 @@ static void unregister_all_objects(pa_dbus_protocol *p, DBusConnection *conn) {
     pa_assert(p);
     pa_assert(conn);
 
-    while ((obj_entry = pa_hashmap_iterate(p->objects, &state, NULL)))
+    PA_HASHMAP_FOREACH(obj_entry, p->objects, state)
         pa_assert_se(dbus_connection_unregister_object_path(conn, obj_entry->path));
 }
 
@@ -904,13 +904,7 @@ void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal) {
     pa_assert(signal);
     pa_assert(dbus_message_get_type(signal) == DBUS_MESSAGE_TYPE_SIGNAL);
 
-    /* XXX: We have to do some linear searching to find connections that want
-     * to receive the signal. This shouldn't be very significant performance
-     * problem, and adding an (object path, signal name) -> connection mapping
-     * would be likely to create substantial complexity. */
-
-    while ((conn_entry = pa_hashmap_iterate(p->connections, &state, NULL))) {
-
+    PA_HASHMAP_FOREACH(conn_entry, p->connections, state) {
         if ((conn_entry->listening_for_all_signals /* Case 1: listening for all signals */
              && (pa_idxset_get_by_data(conn_entry->all_signals_objects, dbus_message_get_path(signal), NULL)
                  || pa_idxset_isempty(conn_entry->all_signals_objects)))
@@ -943,10 +937,8 @@ const char **pa_dbus_protocol_get_extensions(pa_dbus_protocol *p, unsigned *n) {
 
     extensions = pa_xnew(const char *, *n);
 
-    while ((ext_name = pa_idxset_iterate(p->extensions, &state, NULL))) {
-        extensions[i] = ext_name;
-        ++i;
-    }
+    while ((ext_name = pa_idxset_iterate(p->extensions, &state, NULL)))
+        extensions[i++] = ext_name;
 
     return extensions;
 }

commit 0b6662023bfb121b0e553ce00b4229705c8e7aef
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Sun Aug 9 09:06:21 2009 +0300

    dbusiface-core: Generate more informative error messages.

diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c
index 685ba63..be07648 100644
--- a/src/modules/dbus/iface-core.c
+++ b/src/modules/dbus/iface-core.c
@@ -655,7 +655,7 @@ static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, voi
         return;
 
     if (!(fallback_sink = pa_hashmap_get(c->sinks_by_path, object_path))) {
-        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such sink.");
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such sink.", object_path);
         return;
     }
 
@@ -741,7 +741,7 @@ static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, v
         return;
 
     if (!(fallback_source = pa_hashmap_get(c->sources_by_path, object_path))) {
-        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such source.");
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such source.", object_path);
         return;
     }
 
@@ -1154,7 +1154,7 @@ static void handle_get_sink_by_name(DBusConnection *conn, DBusMessage *msg, void
     }
 
     if (!(sink = pa_namereg_get(c->core, sink_name, PA_NAMEREG_SINK))) {
-        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such sink.");
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such sink.", sink_name);
         return;
     }
 
@@ -1186,7 +1186,7 @@ static void handle_get_source_by_name(DBusConnection *conn, DBusMessage *msg, vo
     }
 
     if (!(source = pa_namereg_get(c->core, source_name, PA_NAMEREG_SOURCE))) {
-        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such source.");
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such source.", source_name);
         return;
     }
 

commit 5ece8e8833ff40a4c535fc7d6705377fcf7fd0fd
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Sun Aug 9 09:10:05 2009 +0300

    dbusiface-core: Add functions for getting various object paths.

diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c
index be07648..e8ea50b 100644
--- a/src/modules/dbus/iface-core.c
+++ b/src/modules/dbus/iface-core.c
@@ -2033,3 +2033,24 @@ void pa_dbusiface_core_free(pa_dbusiface_core *c) {
 
     pa_xfree(c);
 }
+
+const char *pa_dbusiface_core_get_sink_path(pa_dbusiface_core *c, const pa_sink *sink) {
+    pa_assert(c);
+    pa_assert(sink);
+
+    return pa_dbusiface_device_get_path(pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(sink->index)));
+}
+
+const char *pa_dbusiface_core_get_source_path(pa_dbusiface_core *c, const pa_source *source) {
+    pa_assert(c);
+    pa_assert(source);
+
+    return pa_dbusiface_device_get_path(pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(source->index)));
+}
+
+const char *pa_dbusiface_core_get_module_path(pa_dbusiface_core *c, const pa_module *module) {
+    pa_assert(c);
+    pa_assert(module);
+
+    return pa_dbusiface_module_get_path(pa_hashmap_get(c->modules, PA_UINT32_TO_PTR(module->index)));
+}
diff --git a/src/modules/dbus/iface-core.h b/src/modules/dbus/iface-core.h
index 964a37b..1b73782 100644
--- a/src/modules/dbus/iface-core.h
+++ b/src/modules/dbus/iface-core.h
@@ -35,4 +35,8 @@ typedef struct pa_dbusiface_core pa_dbusiface_core;
 pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core);
 void pa_dbusiface_core_free(pa_dbusiface_core *c);
 
+const char *pa_dbusiface_core_get_sink_path(pa_dbusiface_core *c, const pa_sink *sink);
+const char *pa_dbusiface_core_get_source_path(pa_dbusiface_core *c, const pa_source *source);
+const char *pa_dbusiface_core_get_module_path(pa_dbusiface_core *c, const pa_module *module);
+
 #endif

commit 3e9de1a36c3f85f558f08167cd82163b0cbf3484
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Sun Aug 9 09:12:31 2009 +0300

    dbus-util: Add helpers for proplist handling.

diff --git a/src/pulsecore/dbus-util.c b/src/pulsecore/dbus-util.c
index 5db7f21..903acad 100644
--- a/src/pulsecore/dbus-util.c
+++ b/src/pulsecore/dbus-util.c
@@ -564,6 +564,21 @@ void pa_dbus_send_basic_array_variant_reply(DBusConnection *c, DBusMessage *in_r
     dbus_message_unref(reply);
 }
 
+void pa_dbus_send_proplist_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, pa_proplist *proplist) {
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+
+    pa_assert(c);
+    pa_assert(in_reply_to);
+    pa_assert(proplist);
+
+    pa_assert_se((reply = dbus_message_new_method_return(in_reply_to)));
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_dbus_append_proplist_variant(&msg_iter, proplist);
+    pa_assert_se(dbus_connection_send(c, reply, NULL));
+    dbus_message_unref(reply);
+}
+
 void pa_dbus_append_basic_array(DBusMessageIter *iter, int item_type, const void *array, unsigned n) {
     DBusMessageIter array_iter;
     unsigned i;
@@ -640,6 +655,62 @@ void pa_dbus_append_basic_array_variant_dict_entry(DBusMessageIter *dict_iter, c
     pa_assert_se(dbus_message_iter_close_container(dict_iter, &dict_entry_iter));
 }
 
+void pa_dbus_append_proplist(DBusMessageIter *iter, pa_proplist *proplist) {
+    DBusMessageIter dict_iter;
+    DBusMessageIter dict_entry_iter;
+    DBusMessageIter array_iter;
+    void *state = NULL;
+    const char *key;
+
+    pa_assert(iter);
+    pa_assert(proplist);
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{say}", &dict_iter));
+
+    while ((key = pa_proplist_iterate(proplist, state))) {
+        const void *value = NULL;
+        size_t nbytes;
+
+        pa_assert_se(pa_proplist_get(proplist, key, &value, &nbytes) >= 0);
+
+        pa_assert_se(dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter));
+
+        pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key));
+
+        pa_assert_se(dbus_message_iter_open_container(&dict_entry_iter, DBUS_TYPE_ARRAY, "y", &array_iter));
+        pa_assert_se(dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_BYTE, &value, nbytes));
+        pa_assert_se(dbus_message_iter_close_container(&dict_entry_iter, &array_iter));
+
+        pa_assert_se(dbus_message_iter_close_container(&dict_iter, &dict_entry_iter));
+    }
+
+    pa_assert_se(dbus_message_iter_close_container(iter, &dict_iter));
+}
+
+void pa_dbus_append_proplist_variant(DBusMessageIter *iter, pa_proplist *proplist) {
+    DBusMessageIter variant_iter;
+
+    pa_assert(iter);
+    pa_assert(proplist);
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a{say}", &variant_iter));
+    pa_dbus_append_proplist(&variant_iter, proplist);
+    pa_assert_se(dbus_message_iter_close_container(iter, &variant_iter));
+}
+
+void pa_dbus_append_proplist_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, pa_proplist *proplist) {
+    DBusMessageIter dict_entry_iter;
+
+    pa_assert(dict_iter);
+    pa_assert(key);
+    pa_assert(proplist);
+
+    pa_assert_se(dbus_message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter));
+    pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key));
+    pa_dbus_append_proplist_variant(&dict_entry_iter, proplist);
+    pa_assert_se(dbus_message_iter_close_container(dict_iter, &dict_entry_iter));
+}
+
 int pa_dbus_get_basic_set_property_arg(DBusConnection *c, DBusMessage *msg, int type, void *data) {
     DBusMessageIter msg_iter;
     DBusMessageIter variant_iter;
diff --git a/src/pulsecore/dbus-util.h b/src/pulsecore/dbus-util.h
index 97aae37..5443a4c 100644
--- a/src/pulsecore/dbus-util.h
+++ b/src/pulsecore/dbus-util.h
@@ -69,12 +69,16 @@ void pa_dbus_send_empty_reply(DBusConnection *c, DBusMessage *in_reply_to);
 void pa_dbus_send_basic_value_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data);
 void pa_dbus_send_basic_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data);
 void pa_dbus_send_basic_array_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int item_type, void *array, unsigned n);
+void pa_dbus_send_proplist_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, pa_proplist *proplist);
 
 void pa_dbus_append_basic_array(DBusMessageIter *iter, int item_type, const void *array, unsigned n);
 void pa_dbus_append_basic_array_variant(DBusMessageIter *iter, int item_type, const void *array, unsigned n);
 void pa_dbus_append_basic_variant(DBusMessageIter *iter, int type, void *data);
 void pa_dbus_append_basic_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int type, void *data);
 void pa_dbus_append_basic_array_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int item_type, const void *array, unsigned n);
+void pa_dbus_append_proplist(DBusMessageIter *iter, pa_proplist *proplist);
+void pa_dbus_append_proplist_variant(DBusMessageIter *iter, pa_proplist *proplist);
+void pa_dbus_append_proplist_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, pa_proplist *proplist);
 
 /* Helper functions for extracting the value argument of a Set call. If the
  * message is invalid, an error reply is sent and a negative number is

commit 76bd03bddb7967c56d68ead22e5d4eae5a82625a
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Sun Aug 9 09:14:27 2009 +0300

    dbus-util: Trivial comment punctuation fix.

diff --git a/src/pulsecore/dbus-util.h b/src/pulsecore/dbus-util.h
index 5443a4c..9cee293 100644
--- a/src/pulsecore/dbus-util.h
+++ b/src/pulsecore/dbus-util.h
@@ -91,8 +91,8 @@ int pa_dbus_get_fixed_array_set_property_arg(DBusConnection *c, DBusMessage *msg
 int pa_dbus_get_basic_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int type, void *data);
 int pa_dbus_get_fixed_array_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int item_type, void *array, unsigned *n);
 
-/* Returns a new proplist, that the caller has to free. If the proplist can't
- * be read from the iterator, an error reply is sent and NULL is returned. */
+/* Returns a new proplist that the caller has to free. If the proplist can't be
+ * read from the iterator, an error reply is sent and NULL is returned. */
 pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter);
 
 #endif

commit 7699cfd4c041eedd2dae124cac75da0879b87f1e
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Sun Aug 9 09:18:03 2009 +0300

    dbus-protocol: Split some overly long lines.

diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c
index 5cbd0d9..2461e4b 100644
--- a/src/pulsecore/protocol-dbus.c
+++ b/src/pulsecore/protocol-dbus.c
@@ -199,19 +199,21 @@ static void update_introspection(struct object_entry *oe) {
             pa_strbuf_printf(buf, "  <method name=\"%s\">\n", method_handler->method_name);
 
             for (i = 0; i < method_handler->n_arguments; ++i)
-                pa_strbuf_printf(buf, "   <arg name=\"%s\" type=\"%s\" direction=\"%s\"/>\n", method_handler->arguments[i].name,
-                                                                                              method_handler->arguments[i].type,
-                                                                                              method_handler->arguments[i].direction);
+                pa_strbuf_printf(buf, "   <arg name=\"%s\" type=\"%s\" direction=\"%s\"/>\n",
+                                 method_handler->arguments[i].name,
+                                 method_handler->arguments[i].type,
+                                 method_handler->arguments[i].direction);
 
             pa_strbuf_puts(buf, "  </method>\n");
         }
 
         handlers_state = NULL;
 
-        while ((property_handler = pa_hashmap_iterate(iface_entry->property_handlers, &handlers_state, NULL)))
-            pa_strbuf_printf(buf, "  <property name=\"%s\" type=\"%s\" access=\"%s\"/>\n", property_handler->property_name,
-                                                                                           property_handler->type,
-                                                                                           property_handler->get_cb ? (property_handler->set_cb ? "readwrite" : "read") : "write");
+        PA_HASHMAP_FOREACH(property_handler, iface_entry->property_handlers, handlers_state)
+            pa_strbuf_printf(buf, "  <property name=\"%s\" type=\"%s\" access=\"%s\"/>\n",
+                             property_handler->property_name,
+                             property_handler->type,
+                             property_handler->get_cb ? (property_handler->set_cb ? "readwrite" : "read") : "write");
 
         for (i = 0; i < iface_entry->n_signals; ++i) {
             pa_strbuf_printf(buf, "  <signal name=\"%s\">\n", iface_entry->signals[i].name);

commit 16dce8d7cbe4dcad56c3fbd1f47af6b50d2581a7
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Sun Aug 9 09:19:33 2009 +0300

    dbus-protocol: Take advantage of the helpers in dbus-util.

diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c
index 2461e4b..62f74a5 100644
--- a/src/pulsecore/protocol-dbus.c
+++ b/src/pulsecore/protocol-dbus.c
@@ -28,6 +28,7 @@
 #include <pulse/xmalloc.h>
 
 #include <pulsecore/core-util.h>
+#include <pulsecore/dbus-util.h>
 #include <pulsecore/hashmap.h>
 #include <pulsecore/idxset.h>
 #include <pulsecore/shared.h>
@@ -408,7 +409,6 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa
     pa_dbus_method_handler *method_handler = NULL;
     pa_dbus_property_handler *property_handler = NULL;
     const char *attempted_property = NULL;
-    DBusMessage *reply = NULL;
 
     pa_assert(connection);
     pa_assert(message);
@@ -427,10 +427,7 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa
 
     if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect") ||
         (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Introspect"))) {
-        pa_assert_se((reply = dbus_message_new_method_return(message)));
-        pa_assert_se(dbus_message_append_args(reply, DBUS_TYPE_STRING, &obj_entry->introspection, DBUS_TYPE_INVALID));
-        pa_assert_se(dbus_connection_send(connection, reply, NULL));
-
+        pa_dbus_send_basic_value_reply(connection, message, DBUS_TYPE_STRING, &obj_entry->introspection);
         goto finish;
     }
 
@@ -450,26 +447,23 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa
         case FOUND_GET_ALL:
             if (iface_entry->get_all_properties_cb)
                 iface_entry->get_all_properties_cb(connection, message, iface_entry->userdata);
+            /* TODO: Write an else branch where a dummy response is sent. */
             break;
 
         case PROPERTY_ACCESS_DENIED:
-            pa_assert_se((reply = dbus_message_new_error_printf(message, DBUS_ERROR_ACCESS_DENIED, "%s access denied for property %s", dbus_message_get_member(message), attempted_property)));
-            pa_assert_se(dbus_connection_send(connection, reply, NULL));
+            pa_dbus_send_error(connection, message, DBUS_ERROR_ACCESS_DENIED, "%s access denied for property %s", dbus_message_get_member(message), attempted_property);
             break;
 
         case NO_SUCH_METHOD:
-            pa_assert_se((reply = dbus_message_new_error_printf(message, DBUS_ERROR_UNKNOWN_METHOD, "%s: No such method", dbus_message_get_member(message))));
-            pa_assert_se(dbus_connection_send(connection, reply, NULL));
+            pa_dbus_send_error(connection, message, DBUS_ERROR_UNKNOWN_METHOD, "%s: No such method", dbus_message_get_member(message));
             break;
 
         case NO_SUCH_PROPERTY:
-            pa_assert_se((reply = dbus_message_new_error_printf(message, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", attempted_property)));
-            pa_assert_se(dbus_connection_send(connection, reply, NULL));
+            pa_dbus_send_error(connection, message, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", attempted_property);
             break;
 
         case INVALID_MESSAGE_ARGUMENTS:
-            pa_assert_se((reply = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS, "Invalid arguments for %s", dbus_message_get_member(message))));
-            pa_assert_se(dbus_connection_send(connection, reply, NULL));
+            pa_dbus_send_error(connection, message, DBUS_ERROR_INVALID_ARGS, "Invalid arguments for %s", dbus_message_get_member(message));
             break;
 
         default:
@@ -477,9 +471,6 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa
     }
 
 finish:
-    if (reply)
-        dbus_message_unref(reply);
-
     return DBUS_HANDLER_RESULT_HANDLED;
 }
 

commit acad5063284f37315c8208916f1ac151791d7220
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Sun Aug 9 09:20:22 2009 +0300

    dbusiface-card: Implement the Card D-Bus interface.

diff --git a/src/Makefile.am b/src/Makefile.am
index e10b4ab..e605ad7 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1278,6 +1278,7 @@ module_http_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore- at PA_MAJORMINORMI
 
 module_dbus_protocol_la_SOURCES = \
 		modules/dbus/iface-card.c modules/dbus/iface-card.h \
+		modules/dbus/iface-card-profile.c modules/dbus/iface-card-profile.h \
 		modules/dbus/iface-client.c modules/dbus/iface-client.h \
 		modules/dbus/iface-core.c modules/dbus/iface-core.h \
 		modules/dbus/iface-device.c modules/dbus/iface-device.h \
diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card-profile.c
similarity index 51%
copy from src/modules/dbus/iface-card.c
copy to src/modules/dbus/iface-card-profile.c
index e203c39..7952494 100644
--- a/src/modules/dbus/iface-card.c
+++ b/src/modules/dbus/iface-card-profile.c
@@ -24,43 +24,44 @@
 #endif
 
 #include <pulsecore/core-util.h>
-#include <pulsecore/protocol-dbus.h>
 
-#include "iface-card.h"
+#include "iface-card-profile.h"
 
-#define OBJECT_NAME "card"
+#define OBJECT_NAME "profile"
 
-struct pa_dbusiface_card {
-    pa_dbusiface_core *core;
-
-    pa_card *card;
+struct pa_dbusiface_card_profile {
+    pa_card_profile *profile;
     char *path;
 };
 
+pa_dbusiface_card_profile *pa_dbusiface_card_profile_new(pa_dbusiface_card *card, pa_card_profile *profile, uint32_t idx) {
+    pa_dbusiface_card_profile *p = NULL;
 
-pa_dbusiface_card *pa_dbusiface_card_new(pa_dbusiface_core *core, pa_card *card) {
-    pa_dbusiface_card *c = NULL;
-
-    pa_assert(core);
     pa_assert(card);
+    pa_assert(profile);
+
+    p = pa_xnew(pa_dbusiface_card_profile, 1);
+    p->profile = profile;
+    p->path = pa_sprintf_malloc("%s/%s%u", pa_dbusiface_card_get_path(card), OBJECT_NAME, idx);
+
+    return p;
+}
 
-    c = pa_xnew0(pa_dbusiface_card, 1);
-    c->core = core;
-    c->card = card;
-    c->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, card->index);
+void pa_dbusiface_card_profile_free(pa_dbusiface_card_profile *p) {
+    pa_assert(p);
 
-    return c;
+    pa_xfree(p->path);
+    pa_xfree(p);
 }
 
-void pa_dbusiface_card_free(pa_dbusiface_card *c) {
-    pa_assert(c);
+const char *pa_dbusiface_card_profile_get_path(pa_dbusiface_card_profile *p) {
+    pa_assert(p);
 
-    pa_xfree(c->path);
-    pa_xfree(c);
+    return p->path;
 }
 
-const char *pa_dbusiface_card_get_path(pa_dbusiface_card *c) {
-    pa_assert(c);
+const char *pa_dbusiface_card_profile_get_name(pa_dbusiface_card_profile *p) {
+    pa_assert(p);
 
-    return c->path;
+    return p->profile->name;
 }
diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card-profile.h
similarity index 50%
copy from src/modules/dbus/iface-card.c
copy to src/modules/dbus/iface-card-profile.h
index e203c39..e90313c 100644
--- a/src/modules/dbus/iface-card.c
+++ b/src/modules/dbus/iface-card-profile.h
@@ -1,3 +1,6 @@
+#ifndef foodbusifacecardprofilehfoo
+#define foodbusifacecardprofilehfoo
+
 /***
   This file is part of PulseAudio.
 
@@ -19,48 +22,22 @@
   USA.
 ***/
 
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
+/* This object implements the D-Bus interface org.PulseAudio.Core1.CardProfile.
+ *
+ * See http://pulseaudio.org/wiki/DBusInterface for the CardProfile interface
+ * documentation.
+ */
 
-#include <pulsecore/core-util.h>
-#include <pulsecore/protocol-dbus.h>
+#include <pulsecore/core-scache.h>
 
 #include "iface-card.h"
 
-#define OBJECT_NAME "card"
-
-struct pa_dbusiface_card {
-    pa_dbusiface_core *core;
-
-    pa_card *card;
-    char *path;
-};
-
-
-pa_dbusiface_card *pa_dbusiface_card_new(pa_dbusiface_core *core, pa_card *card) {
-    pa_dbusiface_card *c = NULL;
+typedef struct pa_dbusiface_card_profile pa_dbusiface_card_profile;
 
-    pa_assert(core);
-    pa_assert(card);
+pa_dbusiface_card_profile *pa_dbusiface_card_profile_new(pa_dbusiface_card *card, pa_card_profile *profile, uint32_t idx);
+void pa_dbusiface_card_profile_free(pa_dbusiface_card_profile *p);
 
-    c = pa_xnew0(pa_dbusiface_card, 1);
-    c->core = core;
-    c->card = card;
-    c->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, card->index);
+const char *pa_dbusiface_card_profile_get_path(pa_dbusiface_card_profile *p);
+const char *pa_dbusiface_card_profile_get_name(pa_dbusiface_card_profile *p);
 
-    return c;
-}
-
-void pa_dbusiface_card_free(pa_dbusiface_card *c) {
-    pa_assert(c);
-
-    pa_xfree(c->path);
-    pa_xfree(c);
-}
-
-const char *pa_dbusiface_card_get_path(pa_dbusiface_card *c) {
-    pa_assert(c);
-
-    return c->path;
-}
+#endif
diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c
index e203c39..4ede718 100644
--- a/src/modules/dbus/iface-card.c
+++ b/src/modules/dbus/iface-card.c
@@ -23,20 +23,476 @@
 #include <config.h>
 #endif
 
+#include <dbus/dbus.h>
+
 #include <pulsecore/core-util.h>
+#include <pulsecore/dbus-util.h>
 #include <pulsecore/protocol-dbus.h>
 
+#include "iface-card-profile.h"
+
 #include "iface-card.h"
 
 #define OBJECT_NAME "card"
 
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sinks(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sources(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_profiles(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_active_profile(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_active_profile(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_profile_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
 struct pa_dbusiface_card {
     pa_dbusiface_core *core;
 
     pa_card *card;
     char *path;
+    pa_hashmap *profiles;
+    uint32_t next_profile_index;
+    pa_card_profile *active_profile;
+    pa_proplist *proplist;
+
+    pa_dbus_protocol *dbus_protocol;
+    pa_subscription *subscription;
 };
 
+enum property_handler_index {
+    PROPERTY_HANDLER_INDEX,
+    PROPERTY_HANDLER_NAME,
+    PROPERTY_HANDLER_DRIVER,
+    PROPERTY_HANDLER_OWNER_MODULE,
+    PROPERTY_HANDLER_SINKS,
+    PROPERTY_HANDLER_SOURCES,
+    PROPERTY_HANDLER_PROFILES,
+    PROPERTY_HANDLER_ACTIVE_PROFILE,
+    PROPERTY_HANDLER_PROPERTY_LIST,
+    PROPERTY_HANDLER_MAX
+};
+
+static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
+    [PROPERTY_HANDLER_INDEX]          = { .property_name = "Index",         .type = "u",      .get_cb = handle_get_index,          .set_cb = NULL },
+    [PROPERTY_HANDLER_NAME]           = { .property_name = "Name",          .type = "s",      .get_cb = handle_get_name,           .set_cb = NULL },
+    [PROPERTY_HANDLER_DRIVER]         = { .property_name = "Driver",        .type = "s",      .get_cb = handle_get_driver,         .set_cb = NULL },
+    [PROPERTY_HANDLER_OWNER_MODULE]   = { .property_name = "OwnerModule",   .type = "o",      .get_cb = handle_get_owner_module,   .set_cb = NULL },
+    [PROPERTY_HANDLER_SINKS]          = { .property_name = "Sinks",         .type = "ao",     .get_cb = handle_get_sinks,          .set_cb = NULL },
+    [PROPERTY_HANDLER_SOURCES]        = { .property_name = "Sources",       .type = "ao",     .get_cb = handle_get_sources,        .set_cb = NULL },
+    [PROPERTY_HANDLER_PROFILES]       = { .property_name = "Profiles",      .type = "ao",     .get_cb = handle_get_profiles,       .set_cb = NULL },
+    [PROPERTY_HANDLER_ACTIVE_PROFILE] = { .property_name = "ActiveProfile", .type = "o",      .get_cb = handle_get_active_profile, .set_cb = handle_set_active_profile },
+    [PROPERTY_HANDLER_PROPERTY_LIST]  = { .property_name = "PropertyList",  .type = "a{say}", .get_cb = handle_get_property_list,  .set_cb = NULL }
+};
+
+enum method_handler_index {
+    METHOD_HANDLER_GET_PROFILE_BY_NAME,
+    METHOD_HANDLER_MAX
+};
+
+static pa_dbus_arg_info get_profile_by_name_args[] = { { "name", "s", "in" }, { "profile", "o", "out" } };
+
+static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
+    [METHOD_HANDLER_GET_PROFILE_BY_NAME] = {
+        .method_name = "GetProfileByName",
+        .arguments = get_profile_by_name_args,
+        .n_arguments = sizeof(get_profile_by_name_args) / sizeof(pa_dbus_arg_info),
+        .receive_cb = handle_get_profile_by_name }
+};
+
+enum signal_index {
+    SIGNAL_ACTIVE_PROFILE_UPDATED,
+    SIGNAL_PROPERTY_LIST_UPDATED,
+    SIGNAL_MAX
+};
+
+static pa_dbus_arg_info active_profile_updated_args[] = { { "profile",       "o",      NULL } };
+static pa_dbus_arg_info property_list_updated_args[] =  { { "property_list", "a{say}", NULL } };
+
+static pa_dbus_signal_info signals[SIGNAL_MAX] = {
+    [SIGNAL_ACTIVE_PROFILE_UPDATED] = { .name = "ActiveProfileUpdated", .arguments = active_profile_updated_args, .n_arguments = 1 },
+    [SIGNAL_PROPERTY_LIST_UPDATED]  = { .name = "PropertyListUpdated",  .arguments = property_list_updated_args,  .n_arguments = 1 }
+};
+
+static pa_dbus_interface_info card_interface_info = {
+    .name = PA_DBUSIFACE_CARD_INTERFACE,
+    .method_handlers = method_handlers,
+    .n_method_handlers = METHOD_HANDLER_MAX,
+    .property_handlers = property_handlers,
+    .n_property_handlers = PROPERTY_HANDLER_MAX,
+    .get_all_properties_cb = handle_get_all,
+    .signals = signals,
+    .n_signals = SIGNAL_MAX
+};
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card *c = userdata;
+    dbus_uint32_t idx;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    idx = c->card->index;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx);
+}
+
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card *c = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &c->card->name);
+}
+
+static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card *c = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &c->card->driver);
+}
+
+static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card *c = userdata;
+    const char *owner_module;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    if (!c->card->module) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Card %s doesn't have an owner module.", c->card->name);
+        return;
+    }
+
+    owner_module = pa_dbusiface_core_get_module_path(c->core, c->card->module);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &owner_module);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_sinks(pa_dbusiface_card *c, unsigned *n) {
+    const char **sinks = NULL;
+    unsigned i = 0;
+    uint32_t idx = 0;
+    pa_sink *sink = NULL;
+
+    pa_assert(c);
+    pa_assert(n);
+
+    *n = pa_idxset_size(c->card->sinks);
+
+    if (*n == 0)
+        return NULL;
+
+    sinks = pa_xnew(const char *, *n);
+
+    PA_IDXSET_FOREACH(sink, c->card->sinks, idx) {
+        sinks[i] = pa_dbusiface_core_get_sink_path(c->core, sink);
+        ++i;
+    }
+
+    return sinks;
+}
+
+static void handle_get_sinks(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card *c = userdata;
+    const char **sinks;
+    unsigned n_sinks;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    sinks = get_sinks(c, &n_sinks);
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, sinks, n_sinks);
+
+    pa_xfree(sinks);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_sources(pa_dbusiface_card *c, unsigned *n) {
+    const char **sources = NULL;
+    unsigned i = 0;
+    uint32_t idx = 0;
+    pa_source *source = NULL;
+
+    pa_assert(c);
+    pa_assert(n);
+
+    *n = pa_idxset_size(c->card->sources);
+
+    if (*n == 0)
+        return NULL;
+
+    sources = pa_xnew(const char *, *n);
+
+    PA_IDXSET_FOREACH(source, c->card->sinks, idx) {
+        sources[i] = pa_dbusiface_core_get_source_path(c->core, source);
+        ++i;
+    }
+
+    return sources;
+}
+
+static void handle_get_sources(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card *c = userdata;
+    const char **sources;
+    unsigned n_sources;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    sources = get_sources(c, &n_sources);
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, sources, n_sources);
+
+    pa_xfree(sources);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_profiles(pa_dbusiface_card *c, unsigned *n) {
+    const char **profiles;
+    unsigned i = 0;
+    void *state = NULL;
+    pa_dbusiface_card_profile *profile;
+
+    pa_assert(c);
+    pa_assert(n);
+
+    *n = pa_hashmap_size(c->profiles);
+
+    if (*n == 0)
+        return NULL;
+
+    profiles = pa_xnew(const char *, *n);
+
+    PA_HASHMAP_FOREACH(profile, c->profiles, state) {
+        profiles[i] = pa_dbusiface_card_profile_get_path(profile);
+        ++i;
+    }
+
+    return profiles;
+}
+
+static void handle_get_profiles(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card *c = userdata;
+    const char **profiles;
+    unsigned n_profiles;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    profiles = get_profiles(c, &n_profiles);
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, profiles, n_profiles);
+
+    pa_xfree(profiles);
+}
+
+static void handle_get_active_profile(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card *c = userdata;
+    const char *active_profile;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    if (!c->active_profile) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "The card %s has no profiles, and therefore there's no active profile either.", c->card->name);
+        return;
+    }
+
+    active_profile = pa_dbusiface_card_profile_get_path(pa_hashmap_get(c->profiles, c->active_profile->name));
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &active_profile);
+}
+
+static void handle_set_active_profile(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card *c = userdata;
+    const char *new_active_path;
+    pa_dbusiface_card_profile *new_active;
+    int r;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_OBJECT_PATH, &new_active_path) < 0)
+        return;
+
+    if (!c->active_profile) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                           "The card %s has no profiles, and therefore there's no active profile either.",
+                           c->card->name);
+        return;
+    }
+
+    if (!(new_active = pa_hashmap_get(c->profiles, new_active_path))) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such profile.", new_active_path);
+        return;
+    }
+
+    if ((r = pa_card_set_profile(c->card, pa_dbusiface_card_profile_get_name(new_active), TRUE)) < 0) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED,
+                           "Internal error in PulseAudio: pa_card_set_profile() failed with error code %i.", r);
+        return;
+    }
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card *c = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    pa_dbus_send_proplist_variant_reply(conn, msg, c->proplist);
+}
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card *c = userdata;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+    DBusMessageIter dict_iter;
+    dbus_uint32_t idx;
+    const char *owner_module = NULL;
+    const char **sinks = NULL;
+    unsigned n_sinks = 0;
+    const char **sources = NULL;
+    unsigned n_sources = 0;
+    const char **profiles = NULL;
+    unsigned n_profiles = 0;
+    const char *active_profile = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    idx = c->card->index;
+    if (c->card->module)
+        owner_module = pa_dbusiface_core_get_module_path(c->core, c->card->module);
+    sinks = get_sinks(c, &n_sinks);
+    sources = get_sources(c, &n_sources);
+    profiles = get_profiles(c, &n_profiles);
+    if (c->active_profile)
+        active_profile = pa_dbusiface_card_profile_get_path(pa_hashmap_get(c->profiles, c->active_profile->name));
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &c->card->name);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DRIVER].property_name, DBUS_TYPE_STRING, &c->card->driver);
+
+    if (owner_module)
+        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_OWNER_MODULE].property_name, DBUS_TYPE_STRING, &owner_module);
+
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SINKS].property_name, DBUS_TYPE_OBJECT_PATH, sinks, n_sinks);
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SOURCES].property_name, DBUS_TYPE_OBJECT_PATH, sources, n_sources);
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROFILES].property_name, DBUS_TYPE_OBJECT_PATH, profiles, n_profiles);
+
+    if (active_profile)
+        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_ACTIVE_PROFILE].property_name, DBUS_TYPE_OBJECT_PATH, &active_profile);
+
+    pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, c->proplist);
+
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+
+    dbus_message_unref(reply);
+
+    pa_xfree(sinks);
+    pa_xfree(sources);
+    pa_xfree(profiles);
+}
+
+static void handle_get_profile_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card *c = userdata;
+    DBusError error;
+    const char *profile_name = NULL;
+    pa_dbusiface_card_profile *profile = NULL;
+    const char *profile_path = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    dbus_error_init(&error);
+
+    if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &profile_name, DBUS_TYPE_INVALID)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
+        dbus_error_free(&error);
+        return;
+    }
+
+    if (!(profile = pa_hashmap_get(c->profiles, profile_name))) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such profile on card %s.", profile_name, c->card->name);
+        return;
+    }
+
+    profile_path = pa_dbusiface_card_profile_get_path(profile);
+
+    pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &profile_path);
+}
+
+static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
+    pa_dbusiface_card *c = userdata;
+
+    pa_assert(core);
+    pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_CARD);
+    pa_assert(c);
+
+    if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) {
+        DBusMessage *signal = NULL;
+
+        if (c->active_profile != c->card->active_profile) {
+            const char *object_path;
+
+            c->active_profile = c->card->active_profile;
+            object_path = pa_dbusiface_card_profile_get_path(pa_hashmap_get(c->profiles, c->active_profile->name));
+
+            pa_assert_se(signal = dbus_message_new_signal(c->path, PA_DBUSIFACE_CARD_INTERFACE, signals[SIGNAL_ACTIVE_PROFILE_UPDATED].name));
+            pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+            pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
+            dbus_message_unref(signal);
+            signal = NULL;
+        }
+
+        if (!pa_proplist_equal(c->proplist, c->card->proplist)) {
+            DBusMessageIter msg_iter;
+
+            pa_proplist_update(c->proplist, PA_UPDATE_SET, c->card->proplist);
+
+            pa_assert_se(signal = dbus_message_new_signal(c->path, PA_DBUSIFACE_CARD_INTERFACE, signals[SIGNAL_PROPERTY_LIST_UPDATED].name));
+            dbus_message_iter_init_append(signal, &msg_iter);
+            pa_dbus_append_proplist(&msg_iter, c->proplist);
+
+            pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
+            dbus_message_unref(signal);
+            signal = NULL;
+        }
+    }
+}
 
 pa_dbusiface_card *pa_dbusiface_card_new(pa_dbusiface_core *core, pa_card *card) {
     pa_dbusiface_card *c = NULL;
@@ -48,13 +504,47 @@ pa_dbusiface_card *pa_dbusiface_card_new(pa_dbusiface_core *core, pa_card *card)
     c->core = core;
     c->card = card;
     c->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, card->index);
+    c->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+    c->next_profile_index = 0;
+    c->active_profile = NULL;
+    c->proplist = pa_proplist_copy(card->proplist);
+    c->dbus_protocol = pa_dbus_protocol_get(card->core);
+    c->subscription = pa_subscription_new(card->core, PA_SUBSCRIPTION_MASK_CARD, subscription_cb, c);
+
+    if (card->profiles) {
+        pa_card_profile *profile;
+        void *state = NULL;
+
+        PA_HASHMAP_FOREACH(profile, card->profiles, state) {
+            pa_dbusiface_card_profile *p = pa_dbusiface_card_profile_new(c, profile, c->next_profile_index++);
+            pa_hashmap_put(c->profiles, pa_dbusiface_card_profile_get_name(p), p);
+        }
+        pa_assert_se(c->active_profile = card->active_profile);
+    }
+
+    pa_assert_se(pa_dbus_protocol_add_interface(c->dbus_protocol, c->path, &card_interface_info, c) >= 0);
 
     return c;
 }
 
+static void profile_free_cb(void *p, void *userdata) {
+    pa_dbusiface_card_profile *profile = p;
+
+    pa_assert(profile);
+
+    pa_dbusiface_card_profile_free(profile);
+}
+
 void pa_dbusiface_card_free(pa_dbusiface_card *c) {
     pa_assert(c);
 
+    pa_assert_se(pa_dbus_protocol_remove_interface(c->dbus_protocol, c->path, card_interface_info.name) >= 0);
+
+    pa_hashmap_free(c->profiles, profile_free_cb, NULL);
+    pa_proplist_free(c->proplist);
+    pa_dbus_protocol_unref(c->dbus_protocol);
+    pa_subscription_free(c->subscription);
+
     pa_xfree(c->path);
     pa_xfree(c);
 }

commit 8c8df77d2a774a65741539381496d02f04747974
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Sun Aug 9 10:36:20 2009 +0300

    dbusiface-card-profile: Implement the CardProfile D-Bus interface.

diff --git a/src/modules/dbus/iface-card-profile.c b/src/modules/dbus/iface-card-profile.c
index 7952494..2b85a5f 100644
--- a/src/modules/dbus/iface-card-profile.c
+++ b/src/modules/dbus/iface-card-profile.c
@@ -23,26 +23,178 @@
 #include <config.h>
 #endif
 
+#include <dbus/dbus.h>
+
 #include <pulsecore/core-util.h>
+#include <pulsecore/dbus-util.h>
 
 #include "iface-card-profile.h"
 
 #define OBJECT_NAME "profile"
 
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_description(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sinks(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sources(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_priority(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
 struct pa_dbusiface_card_profile {
+    uint32_t index;
     pa_card_profile *profile;
     char *path;
+    pa_dbus_protocol *dbus_protocol;
+};
+
+enum property_handler_index {
+    PROPERTY_HANDLER_INDEX,
+    PROPERTY_HANDLER_NAME,
+    PROPERTY_HANDLER_DESCRIPTION,
+    PROPERTY_HANDLER_SINKS,
+    PROPERTY_HANDLER_SOURCES,
+    PROPERTY_HANDLER_PRIORITY,
+    PROPERTY_HANDLER_MAX
+};
+
+static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
+    [PROPERTY_HANDLER_INDEX]       = { .property_name = "Index",       .type = "u", .get_cb = handle_get_index,       .set_cb = NULL },
+    [PROPERTY_HANDLER_NAME]        = { .property_name = "Name",        .type = "s", .get_cb = handle_get_name,        .set_cb = NULL },
+    [PROPERTY_HANDLER_DESCRIPTION] = { .property_name = "Description", .type = "s", .get_cb = handle_get_description, .set_cb = NULL },
+    [PROPERTY_HANDLER_SINKS]       = { .property_name = "Sinks",       .type = "u", .get_cb = handle_get_sinks,       .set_cb = NULL },
+    [PROPERTY_HANDLER_SOURCES]     = { .property_name = "Sources",     .type = "u", .get_cb = handle_get_sources,     .set_cb = NULL },
+    [PROPERTY_HANDLER_PRIORITY]    = { .property_name = "Priority",    .type = "u", .get_cb = handle_get_priority,    .set_cb = NULL },
 };
 
-pa_dbusiface_card_profile *pa_dbusiface_card_profile_new(pa_dbusiface_card *card, pa_card_profile *profile, uint32_t idx) {
+static pa_dbus_interface_info profile_interface_info = {
+    .name = PA_DBUSIFACE_CARD_PROFILE_INTERFACE,
+    .method_handlers = NULL,
+    .n_method_handlers = 0,
+    .property_handlers = property_handlers,
+    .n_property_handlers = PROPERTY_HANDLER_MAX,
+    .get_all_properties_cb = handle_get_all,
+    .signals = NULL,
+    .n_signals = 0
+};
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card_profile *p = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(p);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &p->index);
+}
+
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card_profile *p = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(p);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &p->profile->name);
+}
+
+static void handle_get_description(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card_profile *p = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(p);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &p->profile->description);
+}
+
+static void handle_get_sinks(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card_profile *p = userdata;
+    dbus_uint32_t sinks = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(p);
+
+    sinks = p->profile->n_sinks;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sinks);
+}
+
+static void handle_get_sources(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card_profile *p = userdata;
+    dbus_uint32_t sources = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(p);
+
+    sources = p->profile->n_sources;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sources);
+}
+
+static void handle_get_priority(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card_profile *p = userdata;
+    dbus_uint32_t priority = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(p);
+
+    priority = p->profile->priority;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &priority);
+}
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_card_profile *p = userdata;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+    DBusMessageIter dict_iter;
+    dbus_uint32_t sinks = 0;
+    dbus_uint32_t sources = 0;
+    dbus_uint32_t priority = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(p);
+
+    sinks = p->profile->n_sinks;
+    sources = p->profile->n_sources;
+    priority = p->profile->priority;
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &p->index);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &p->profile->name);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DESCRIPTION].property_name, DBUS_TYPE_STRING, &p->profile->description);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SINKS].property_name, DBUS_TYPE_UINT32, &sinks);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SOURCES].property_name, DBUS_TYPE_UINT32, &sources);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PRIORITY].property_name, DBUS_TYPE_UINT32, &priority);
+
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+    dbus_message_unref(reply);
+}
+
+pa_dbusiface_card_profile *pa_dbusiface_card_profile_new(pa_dbusiface_card *card, pa_core *core, pa_card_profile *profile, uint32_t idx) {
     pa_dbusiface_card_profile *p = NULL;
 
     pa_assert(card);
     pa_assert(profile);
 
     p = pa_xnew(pa_dbusiface_card_profile, 1);
+    p->index = idx;
     p->profile = profile;
     p->path = pa_sprintf_malloc("%s/%s%u", pa_dbusiface_card_get_path(card), OBJECT_NAME, idx);
+    p->dbus_protocol = pa_dbus_protocol_get(core);
+
+    pa_assert_se(pa_dbus_protocol_add_interface(p->dbus_protocol, p->path, &profile_interface_info, p) >= 0);
 
     return p;
 }
@@ -50,6 +202,10 @@ pa_dbusiface_card_profile *pa_dbusiface_card_profile_new(pa_dbusiface_card *card
 void pa_dbusiface_card_profile_free(pa_dbusiface_card_profile *p) {
     pa_assert(p);
 
+    pa_assert_se(pa_dbus_protocol_remove_interface(p->dbus_protocol, p->path, profile_interface_info.name) >= 0);
+
+    pa_dbus_protocol_unref(p->dbus_protocol);
+
     pa_xfree(p->path);
     pa_xfree(p);
 }
diff --git a/src/modules/dbus/iface-card-profile.h b/src/modules/dbus/iface-card-profile.h
index e90313c..9edcde7 100644
--- a/src/modules/dbus/iface-card-profile.h
+++ b/src/modules/dbus/iface-card-profile.h
@@ -29,12 +29,15 @@
  */
 
 #include <pulsecore/core-scache.h>
+#include <pulsecore/protocol-dbus.h>
 
 #include "iface-card.h"
 
+#define PA_DBUSIFACE_CARD_PROFILE_INTERFACE PA_DBUS_CORE_INTERFACE ".CardProfile"
+
 typedef struct pa_dbusiface_card_profile pa_dbusiface_card_profile;
 
-pa_dbusiface_card_profile *pa_dbusiface_card_profile_new(pa_dbusiface_card *card, pa_card_profile *profile, uint32_t idx);
+pa_dbusiface_card_profile *pa_dbusiface_card_profile_new(pa_dbusiface_card *card, pa_core *core, pa_card_profile *profile, uint32_t idx);
 void pa_dbusiface_card_profile_free(pa_dbusiface_card_profile *p);
 
 const char *pa_dbusiface_card_profile_get_path(pa_dbusiface_card_profile *p);
diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c
index 4ede718..64ec12a 100644
--- a/src/modules/dbus/iface-card.c
+++ b/src/modules/dbus/iface-card.c
@@ -516,7 +516,7 @@ pa_dbusiface_card *pa_dbusiface_card_new(pa_dbusiface_core *core, pa_card *card)
         void *state = NULL;
 
         PA_HASHMAP_FOREACH(profile, card->profiles, state) {
-            pa_dbusiface_card_profile *p = pa_dbusiface_card_profile_new(c, profile, c->next_profile_index++);
+            pa_dbusiface_card_profile *p = pa_dbusiface_card_profile_new(c, card->core, profile, c->next_profile_index++);
             pa_hashmap_put(c->profiles, pa_dbusiface_card_profile_get_name(p), p);
         }
         pa_assert_se(c->active_profile = card->active_profile);
diff --git a/src/modules/dbus/iface-card.h b/src/modules/dbus/iface-card.h
index 57ad4e3..e2c08a3 100644
--- a/src/modules/dbus/iface-card.h
+++ b/src/modules/dbus/iface-card.h
@@ -29,6 +29,7 @@
  */
 
 #include <pulsecore/card.h>
+#include <pulsecore/protocol-dbus.h>
 
 #include "iface-core.h"
 

commit 7cfda56af99b6d6bbb92fd6de208edd4c6991240
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Mon Aug 10 10:38:01 2009 +0300

    dbus-protocol: Add a note for _send_signal that by default the signal isn't
    actually sent.

diff --git a/src/pulsecore/protocol-dbus.h b/src/pulsecore/protocol-dbus.h
index c6b630a..38ba8a1 100644
--- a/src/pulsecore/protocol-dbus.h
+++ b/src/pulsecore/protocol-dbus.h
@@ -149,6 +149,10 @@ void pa_dbus_protocol_add_signal_listener(pa_dbus_protocol *p, DBusConnection *c
  * do anything in that case either. */
 void pa_dbus_protocol_remove_signal_listener(pa_dbus_protocol *p, DBusConnection *conn, const char *signal);
 
+/* Sends the given signal to all interested clients. By default no signals are
+ * sent - clients have to explicitly to request signals by calling
+ * .Core1.ListenForSignal. That method's handler then calls
+ * pa_dbus_protocol_add_signal_listener(). */
 void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal);
 
 /* Returns an array of extension identifier strings. The strings pointers point

commit 31117fe99e07b11b55564a6df455211db712c2f3
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Mon Aug 10 10:40:40 2009 +0300

    dbus-protocol: Fix signal sending for the case when the client doesn't listen
    for all signals.

diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c
index 62f74a5..0128570 100644
--- a/src/pulsecore/protocol-dbus.c
+++ b/src/pulsecore/protocol-dbus.c
@@ -848,6 +848,8 @@ void pa_dbus_protocol_add_signal_listener(pa_dbus_protocol *p, DBusConnection *c
         for (i = 0; i < n_objects; ++i)
             pa_idxset_put(object_set, pa_xstrdup(objects[i]), NULL);
 
+        pa_hashmap_put(conn_entry->listening_signals, signal, object_set);
+
     } else {
         conn_entry->listening_for_all_signals = TRUE;
 
@@ -892,10 +894,15 @@ void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal) {
     void *state = NULL;
     pa_idxset *object_set;
     DBusMessage *signal_copy;
+    char *signal_string;
 
     pa_assert(p);
     pa_assert(signal);
     pa_assert(dbus_message_get_type(signal) == DBUS_MESSAGE_TYPE_SIGNAL);
+    pa_assert_se(dbus_message_get_interface(signal));
+    pa_assert_se(dbus_message_get_member(signal));
+
+    signal_string = pa_sprintf_malloc("%s.%s", dbus_message_get_interface(signal), dbus_message_get_member(signal));
 
     PA_HASHMAP_FOREACH(conn_entry, p->connections, state) {
         if ((conn_entry->listening_for_all_signals /* Case 1: listening for all signals */
@@ -903,7 +910,7 @@ void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal) {
                  || pa_idxset_isempty(conn_entry->all_signals_objects)))
 
             || (!conn_entry->listening_for_all_signals /* Case 2: not listening for all signals */
-                && (object_set = pa_hashmap_get(conn_entry->listening_signals, signal))
+                && (object_set = pa_hashmap_get(conn_entry->listening_signals, signal_string))
                 && (pa_idxset_get_by_data(object_set, dbus_message_get_path(signal), NULL)
                     || pa_idxset_isempty(object_set)))) {
 
@@ -912,6 +919,8 @@ void pa_dbus_protocol_send_signal(pa_dbus_protocol *p, DBusMessage *signal) {
             dbus_message_unref(signal_copy);
         }
     }
+
+    pa_xfree(signal_string);
 }
 
 const char **pa_dbus_protocol_get_extensions(pa_dbus_protocol *p, unsigned *n) {
diff --git a/src/pulsecore/protocol-dbus.h b/src/pulsecore/protocol-dbus.h
index 38ba8a1..d771b4f 100644
--- a/src/pulsecore/protocol-dbus.h
+++ b/src/pulsecore/protocol-dbus.h
@@ -129,7 +129,8 @@ int pa_dbus_protocol_unregister_connection(pa_dbus_protocol *p, DBusConnection *
 pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn);
 
 /* Enables signal receiving for the given connection. The connection must have
- * been registered earlier.
+ * been registered earlier. The signal string must contain both the signal
+ * interface and the signal name, concatenated using a period as the separator.
  *
  * If the signal argument is NULL, all signals will be sent to the connection,
  * otherwise calling this function only adds the given signal to the list of

commit 8b5550dba32857918fd8e70b93be9094c11cf0c8
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Sat Aug 15 05:51:55 2009 +0300

    dbusiface-card: Split some overly long lines.

diff --git a/src/modules/dbus/iface-card-profile.c b/src/modules/dbus/iface-card-profile.c
index 2b85a5f..4a1696c 100644
--- a/src/modules/dbus/iface-card-profile.c
+++ b/src/modules/dbus/iface-card-profile.c
@@ -182,7 +182,11 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat
     dbus_message_unref(reply);
 }
 
-pa_dbusiface_card_profile *pa_dbusiface_card_profile_new(pa_dbusiface_card *card, pa_core *core, pa_card_profile *profile, uint32_t idx) {
+pa_dbusiface_card_profile *pa_dbusiface_card_profile_new(
+        pa_dbusiface_card *card,
+        pa_core *core,
+        pa_card_profile *profile,
+        uint32_t idx) {
     pa_dbusiface_card_profile *p = NULL;
 
     pa_assert(card);
diff --git a/src/modules/dbus/iface-card-profile.h b/src/modules/dbus/iface-card-profile.h
index 9edcde7..a09767f 100644
--- a/src/modules/dbus/iface-card-profile.h
+++ b/src/modules/dbus/iface-card-profile.h
@@ -37,7 +37,11 @@
 
 typedef struct pa_dbusiface_card_profile pa_dbusiface_card_profile;
 
-pa_dbusiface_card_profile *pa_dbusiface_card_profile_new(pa_dbusiface_card *card, pa_core *core, pa_card_profile *profile, uint32_t idx);
+pa_dbusiface_card_profile *pa_dbusiface_card_profile_new(
+        pa_dbusiface_card *card,
+        pa_core *core,
+        pa_card_profile *profile,
+        uint32_t idx);
 void pa_dbusiface_card_profile_free(pa_dbusiface_card_profile *p);
 
 const char *pa_dbusiface_card_profile_get_path(pa_dbusiface_card_profile *p);
diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c
index 64ec12a..40031d2 100644
--- a/src/modules/dbus/iface-card.c
+++ b/src/modules/dbus/iface-card.c
@@ -312,7 +312,10 @@ static void handle_get_active_profile(DBusConnection *conn, DBusMessage *msg, vo
     pa_assert(c);
 
     if (!c->active_profile) {
-        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "The card %s has no profiles, and therefore there's no active profile either.", c->card->name);
+        pa_assert(pa_hashmap_isempty(c->profiles));
+
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                           "The card %s has no profiles, and therefore there's no active profile either.", c->card->name);
         return;
     }
 
@@ -470,7 +473,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
             c->active_profile = c->card->active_profile;
             object_path = pa_dbusiface_card_profile_get_path(pa_hashmap_get(c->profiles, c->active_profile->name));
 
-            pa_assert_se(signal = dbus_message_new_signal(c->path, PA_DBUSIFACE_CARD_INTERFACE, signals[SIGNAL_ACTIVE_PROFILE_UPDATED].name));
+            pa_assert_se(signal = dbus_message_new_signal(c->path,
+                                                          PA_DBUSIFACE_CARD_INTERFACE,
+                                                          signals[SIGNAL_ACTIVE_PROFILE_UPDATED].name));
             pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
             pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
@@ -483,7 +488,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
             pa_proplist_update(c->proplist, PA_UPDATE_SET, c->card->proplist);
 
-            pa_assert_se(signal = dbus_message_new_signal(c->path, PA_DBUSIFACE_CARD_INTERFACE, signals[SIGNAL_PROPERTY_LIST_UPDATED].name));
+            pa_assert_se(signal = dbus_message_new_signal(c->path,
+                                                          PA_DBUSIFACE_CARD_INTERFACE,
+                                                          signals[SIGNAL_PROPERTY_LIST_UPDATED].name));
             dbus_message_iter_init_append(signal, &msg_iter);
             pa_dbus_append_proplist(&msg_iter, c->proplist);
 

commit afb79ee83e95b3c492abe55ea0c26a9fe902b075
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Sat Aug 15 05:53:36 2009 +0300

    dbusiface-card-profile: Assert the core argument isn't NULL.

diff --git a/src/modules/dbus/iface-card-profile.c b/src/modules/dbus/iface-card-profile.c
index 4a1696c..004e2e8 100644
--- a/src/modules/dbus/iface-card-profile.c
+++ b/src/modules/dbus/iface-card-profile.c
@@ -190,6 +190,7 @@ pa_dbusiface_card_profile *pa_dbusiface_card_profile_new(
     pa_dbusiface_card_profile *p = NULL;
 
     pa_assert(card);
+    pa_assert(core);
     pa_assert(profile);
 
     p = pa_xnew(pa_dbusiface_card_profile, 1);

commit 18f9f1b5d17c83280205083760edd4dccb37ed6a
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Sat Aug 15 05:58:24 2009 +0300

    dbusiface-card: Use the ++ operator like it's meant to be used.

diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c
index 40031d2..e3606c7 100644
--- a/src/modules/dbus/iface-card.c
+++ b/src/modules/dbus/iface-card.c
@@ -279,10 +279,8 @@ static const char **get_profiles(pa_dbusiface_card *c, unsigned *n) {
 
     profiles = pa_xnew(const char *, *n);
 
-    PA_HASHMAP_FOREACH(profile, c->profiles, state) {
-        profiles[i] = pa_dbusiface_card_profile_get_path(profile);
-        ++i;
-    }
+    PA_HASHMAP_FOREACH(profile, c->profiles, state)
+        profiles[i++] = pa_dbusiface_card_profile_get_path(profile);
 
     return profiles;
 }

commit 31c544d8439edee56ecc641c07012042ab38bb80
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Sat Aug 15 06:00:46 2009 +0300

    dbusiface-card: Assert that the profiles list is empty if there's no active
    profile.

diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c
index e3606c7..924bea9 100644
--- a/src/modules/dbus/iface-card.c
+++ b/src/modules/dbus/iface-card.c
@@ -336,6 +336,8 @@ static void handle_set_active_profile(DBusConnection *conn, DBusMessage *msg, vo
         return;
 
     if (!c->active_profile) {
+        pa_assert(pa_hashmap_isempty(c->profiles));
+
         pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
                            "The card %s has no profiles, and therefore there's no active profile either.",
                            c->card->name);

commit 90c73db449e67cf999a354c7e3ccad3edd44a829
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Sat Aug 15 06:09:35 2009 +0300

    dbusiface-card: Fix the OwnerModule property type in handle_get_all().

diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c
index 924bea9..5a79794 100644
--- a/src/modules/dbus/iface-card.c
+++ b/src/modules/dbus/iface-card.c
@@ -406,7 +406,7 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat
     pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DRIVER].property_name, DBUS_TYPE_STRING, &c->card->driver);
 
     if (owner_module)
-        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_OWNER_MODULE].property_name, DBUS_TYPE_STRING, &owner_module);
+        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_OWNER_MODULE].property_name, DBUS_TYPE_OBJECT_PATH, &owner_module);
 
     pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SINKS].property_name, DBUS_TYPE_OBJECT_PATH, sinks, n_sinks);
     pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SOURCES].property_name, DBUS_TYPE_OBJECT_PATH, sources, n_sources);

commit 1e65d8d35b23d3c76a30155c42bec282257579e5
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Sat Aug 15 06:11:38 2009 +0300

    dbusiface-core: New function: pa_dbusiface_core_get_card_path().

diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c
index e8ea50b..81e709f 100644
--- a/src/modules/dbus/iface-core.c
+++ b/src/modules/dbus/iface-core.c
@@ -2034,6 +2034,13 @@ void pa_dbusiface_core_free(pa_dbusiface_core *c) {
     pa_xfree(c);
 }
 
+const char *pa_dbusiface_core_get_card_path(pa_dbusiface_core *c, const pa_card *card) {
+    pa_assert(c);
+    pa_assert(card);
+
+    return pa_dbusiface_card_get_path(pa_hashmap_get(c->cards, PA_UINT32_TO_PTR(card->index)));
+}
+
 const char *pa_dbusiface_core_get_sink_path(pa_dbusiface_core *c, const pa_sink *sink) {
     pa_assert(c);
     pa_assert(sink);
diff --git a/src/modules/dbus/iface-core.h b/src/modules/dbus/iface-core.h
index 1b73782..7010205 100644
--- a/src/modules/dbus/iface-core.h
+++ b/src/modules/dbus/iface-core.h
@@ -35,6 +35,7 @@ typedef struct pa_dbusiface_core pa_dbusiface_core;
 pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core);
 void pa_dbusiface_core_free(pa_dbusiface_core *c);
 
+const char *pa_dbusiface_core_get_card_path(pa_dbusiface_core *c, const pa_card *card);
 const char *pa_dbusiface_core_get_sink_path(pa_dbusiface_core *c, const pa_sink *sink);
 const char *pa_dbusiface_core_get_source_path(pa_dbusiface_core *c, const pa_source *source);
 const char *pa_dbusiface_core_get_module_path(pa_dbusiface_core *c, const pa_module *module);

commit 22ab1414507f84d6a42c5255d66ba15d8097d35f
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Sat Aug 15 06:13:17 2009 +0300

    dbus-protocol: Use pa_hashmap_remove() instead of _get().

diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c
index 0128570..cf561c8 100644
--- a/src/pulsecore/protocol-dbus.c
+++ b/src/pulsecore/protocol-dbus.c
@@ -841,7 +841,7 @@ void pa_dbus_protocol_add_signal_listener(pa_dbus_protocol *p, DBusConnection *c
         conn_entry->listening_for_all_signals = FALSE;
 
         /* Replace the old object list with a new one. */
-        if ((object_set = pa_hashmap_get(conn_entry->listening_signals, signal)))
+        if ((object_set = pa_hashmap_remove(conn_entry->listening_signals, signal)))
             pa_idxset_free(object_set, free_listened_object_name_cb, NULL);
         object_set = pa_idxset_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
 

commit 91f626f777402fa6485658b04cbcdf2a7aac3f01
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Sat Aug 15 06:18:41 2009 +0300

    dbusiface-device: Implement the Device and DevicePort D-Bus interfaces.

diff --git a/src/Makefile.am b/src/Makefile.am
index e605ad7..8722f97 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1282,6 +1282,7 @@ module_dbus_protocol_la_SOURCES = \
 		modules/dbus/iface-client.c modules/dbus/iface-client.h \
 		modules/dbus/iface-core.c modules/dbus/iface-core.h \
 		modules/dbus/iface-device.c modules/dbus/iface-device.h \
+		modules/dbus/iface-device-port.c modules/dbus/iface-device-port.h \
 		modules/dbus/iface-memstats.c modules/dbus/iface-memstats.h \
 		modules/dbus/iface-module.c modules/dbus/iface-module.h \
 		modules/dbus/iface-sample.c modules/dbus/iface-sample.h \
diff --git a/src/modules/dbus/iface-device-port.c b/src/modules/dbus/iface-device-port.c
new file mode 100644
index 0000000..d403b6a
--- /dev/null
+++ b/src/modules/dbus/iface-device-port.c
@@ -0,0 +1,190 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Tanu Kaskinen
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <dbus/dbus.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/dbus-util.h>
+
+#include "iface-device-port.h"
+
+#define OBJECT_NAME "port"
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_description(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_priority(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+struct pa_dbusiface_device_port {
+    uint32_t index;
+    pa_device_port *port;
+    char *path;
+    pa_dbus_protocol *dbus_protocol;
+};
+
+enum property_handler_index {
+    PROPERTY_HANDLER_INDEX,
+    PROPERTY_HANDLER_NAME,
+    PROPERTY_HANDLER_DESCRIPTION,
+    PROPERTY_HANDLER_PRIORITY,
+    PROPERTY_HANDLER_MAX
+};
+
+static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
+    [PROPERTY_HANDLER_INDEX]       = { .property_name = "Index",       .type = "u", .get_cb = handle_get_index,       .set_cb = NULL },
+    [PROPERTY_HANDLER_NAME]        = { .property_name = "Name",        .type = "s", .get_cb = handle_get_name,        .set_cb = NULL },
+    [PROPERTY_HANDLER_DESCRIPTION] = { .property_name = "Description", .type = "s", .get_cb = handle_get_description, .set_cb = NULL },
+    [PROPERTY_HANDLER_PRIORITY]    = { .property_name = "Priority",    .type = "u", .get_cb = handle_get_priority,    .set_cb = NULL },
+};
+
+static pa_dbus_interface_info port_interface_info = {
+    .name = PA_DBUSIFACE_DEVICE_PORT_INTERFACE,
+    .method_handlers = NULL,
+    .n_method_handlers = 0,
+    .property_handlers = property_handlers,
+    .n_property_handlers = PROPERTY_HANDLER_MAX,
+    .get_all_properties_cb = handle_get_all,
+    .signals = NULL,
+    .n_signals = 0
+};
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device_port *p = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(p);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &p->index);
+}
+
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device_port *p = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(p);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &p->port->name);
+}
+
+static void handle_get_description(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device_port *p = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(p);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &p->port->description);
+}
+
+static void handle_get_priority(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device_port *p = userdata;
+    dbus_uint32_t priority = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(p);
+
+    priority = p->port->priority;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &priority);
+}
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device_port *p = userdata;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+    DBusMessageIter dict_iter;
+    dbus_uint32_t priority = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(p);
+
+    priority = p->port->priority;
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &p->index);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &p->port->name);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DESCRIPTION].property_name, DBUS_TYPE_STRING, &p->port->description);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PRIORITY].property_name, DBUS_TYPE_UINT32, &priority);
+
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+    dbus_message_unref(reply);
+}
+
+pa_dbusiface_device_port *pa_dbusiface_device_port_new(
+        pa_dbusiface_device *device,
+        pa_core *core,
+        pa_device_port *port,
+        uint32_t idx) {
+    pa_dbusiface_device_port *p = NULL;
+
+    pa_assert(device);
+    pa_assert(core);
+    pa_assert(port);
+
+    p = pa_xnew(pa_dbusiface_device_port, 1);
+    p->index = idx;
+    p->port = port;
+    p->path = pa_sprintf_malloc("%s/%s%u", pa_dbusiface_device_get_path(device), OBJECT_NAME, idx);
+    p->dbus_protocol = pa_dbus_protocol_get(core);
+
+    pa_assert_se(pa_dbus_protocol_add_interface(p->dbus_protocol, p->path, &port_interface_info, p) >= 0);
+
+    return p;
+}
+
+void pa_dbusiface_device_port_free(pa_dbusiface_device_port *p) {
+    pa_assert(p);
+
+    pa_assert_se(pa_dbus_protocol_remove_interface(p->dbus_protocol, p->path, port_interface_info.name) >= 0);
+
+    pa_dbus_protocol_unref(p->dbus_protocol);
+
+    pa_xfree(p->path);
+    pa_xfree(p);
+}
+
+const char *pa_dbusiface_device_port_get_path(pa_dbusiface_device_port *p) {
+    pa_assert(p);
+
+    return p->path;
+}
+
+const char *pa_dbusiface_device_port_get_name(pa_dbusiface_device_port *p) {
+    pa_assert(p);
+
+    return p->port->name;
+}
diff --git a/src/modules/dbus/iface-device.h b/src/modules/dbus/iface-device-port.h
similarity index 51%
copy from src/modules/dbus/iface-device.h
copy to src/modules/dbus/iface-device-port.h
index 1e9af83..0461e2f 100644
--- a/src/modules/dbus/iface-device.h
+++ b/src/modules/dbus/iface-device-port.h
@@ -1,5 +1,5 @@
-#ifndef foodbusifacedevicehfoo
-#define foodbusifacedevicehfoo
+#ifndef foodbusifacedeviceporthfoo
+#define foodbusifacedeviceporthfoo
 
 /***
   This file is part of PulseAudio.
@@ -22,27 +22,29 @@
   USA.
 ***/
 
-/* This object implements the D-Bus interfaces org.PulseAudio.Core1.Device,
- * org.PulseAudio.Core1.Sink and org.PulseAudio.Core1.Source.
+/* This object implements the D-Bus interface org.PulseAudio.Core1.DevicePort.
  *
- * See http://pulseaudio.org/wiki/DBusInterface for the interface
+ * See http://pulseaudio.org/wiki/DBusInterface for the DevicePort interface
  * documentation.
  */
 
+#include <pulsecore/protocol-dbus.h>
 #include <pulsecore/sink.h>
-#include <pulsecore/source.h>
 
-#include "iface-core.h"
+#include "iface-device.h"
 
-typedef struct pa_dbusiface_device pa_dbusiface_device;
+#define PA_DBUSIFACE_DEVICE_PORT_INTERFACE PA_DBUS_CORE_INTERFACE ".DevicePort"
 
-pa_dbusiface_device *pa_dbusiface_device_new_sink(pa_dbusiface_core *core, pa_sink *sink);
-pa_dbusiface_device *pa_dbusiface_device_new_source(pa_dbusiface_core *core, pa_source *source);
-void pa_dbusiface_device_free(pa_dbusiface_device *d);
+typedef struct pa_dbusiface_device_port pa_dbusiface_device_port;
 
-const char *pa_dbusiface_device_get_path(pa_dbusiface_device *d);
+pa_dbusiface_device_port *pa_dbusiface_device_port_new(
+        pa_dbusiface_device *device,
+        pa_core *core,
+        pa_device_port *port,
+        uint32_t idx);
+void pa_dbusiface_device_port_free(pa_dbusiface_device_port *p);
 
-pa_sink *pa_dbusiface_device_get_sink(pa_dbusiface_device *d);
-pa_source *pa_dbusiface_device_get_source(pa_dbusiface_device *d);
+const char *pa_dbusiface_device_port_get_path(pa_dbusiface_device_port *p);
+const char *pa_dbusiface_device_port_get_name(pa_dbusiface_device_port *p);
 
 #endif
diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c
index 1a64f43..3cf9d19 100644
--- a/src/modules/dbus/iface-device.c
+++ b/src/modules/dbus/iface-device.c
@@ -24,62 +24,1258 @@
 #endif
 
 #include <pulsecore/core-util.h>
+#include <pulsecore/dbus-util.h>
 #include <pulsecore/protocol-dbus.h>
 
+#include "iface-device-port.h"
+
 #include "iface-device.h"
 
 #define SINK_OBJECT_NAME "sink"
 #define SOURCE_OBJECT_NAME "source"
 
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_card(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_has_flat_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_has_convertible_to_decibel_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_base_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_volume_steps(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_has_hardware_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_has_hardware_mute(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_configured_latency(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_has_dynamic_latency(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_latency(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_is_hardware_device(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_is_network_device(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_state(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_ports(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_active_port(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_active_port(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_suspend(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_port_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_sink_get_monitor_source(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_sink_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_source_get_monitor_of_sink(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_source_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
 enum device_type {
     DEVICE_TYPE_SINK,
     DEVICE_TYPE_SOURCE
 };
 
 struct pa_dbusiface_device {
+    pa_dbusiface_core *core;
+
     union {
         pa_sink *sink;
         pa_source *source;
     };
     enum device_type type;
     char *path;
+    pa_cvolume volume;
+    pa_bool_t is_muted;
+    union {
+        pa_sink_state_t sink_state;
+        pa_source_state_t source_state;
+    };
+    pa_hashmap *ports;
+    uint32_t next_port_index;
+    pa_device_port *active_port;
+    pa_proplist *proplist;
+
+    pa_dbus_protocol *dbus_protocol;
+    pa_subscription *subscription;
 };
 
+enum property_handler_index {
+    PROPERTY_HANDLER_INDEX,
+    PROPERTY_HANDLER_NAME,
+    PROPERTY_HANDLER_DRIVER,
+    PROPERTY_HANDLER_OWNER_MODULE,
+    PROPERTY_HANDLER_CARD,
+    PROPERTY_HANDLER_SAMPLE_FORMAT,
+    PROPERTY_HANDLER_SAMPLE_RATE,
+    PROPERTY_HANDLER_CHANNELS,
+    PROPERTY_HANDLER_VOLUME,
+    PROPERTY_HANDLER_HAS_FLAT_VOLUME,
+    PROPERTY_HANDLER_HAS_CONVERTIBLE_TO_DECIBEL_VOLUME,
+    PROPERTY_HANDLER_BASE_VOLUME,
+    PROPERTY_HANDLER_VOLUME_STEPS,
+    PROPERTY_HANDLER_IS_MUTED,
+    PROPERTY_HANDLER_HAS_HARDWARE_VOLUME,
+    PROPERTY_HANDLER_HAS_HARDWARE_MUTE,
+    PROPERTY_HANDLER_CONFIGURED_LATENCY,
+    PROPERTY_HANDLER_HAS_DYNAMIC_LATENCY,
+    PROPERTY_HANDLER_LATENCY,
+    PROPERTY_HANDLER_IS_HARDWARE_DEVICE,
+    PROPERTY_HANDLER_IS_NETWORK_DEVICE,
+    PROPERTY_HANDLER_STATE,
+    PROPERTY_HANDLER_PORTS,
+    PROPERTY_HANDLER_ACTIVE_PORT,
+    PROPERTY_HANDLER_PROPERTY_LIST,
+    PROPERTY_HANDLER_MAX
+};
+
+enum sink_property_handler_index {
+    SINK_PROPERTY_HANDLER_MONITOR_SOURCE,
+    SINK_PROPERTY_HANDLER_MAX
+};
+
+enum source_property_handler_index {
+    SOURCE_PROPERTY_HANDLER_MONITOR_OF_SINK,
+    SOURCE_PROPERTY_HANDLER_MAX
+};
+
+static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
+    [PROPERTY_HANDLER_INDEX]                             = { .property_name = "Index",                         .type = "u",      .get_cb = handle_get_index,                             .set_cb = NULL },
+    [PROPERTY_HANDLER_NAME]                              = { .property_name = "Name",                          .type = "s",      .get_cb = handle_get_name,                              .set_cb = NULL },
+    [PROPERTY_HANDLER_DRIVER]                            = { .property_name = "Driver",                        .type = "s",      .get_cb = handle_get_driver,                            .set_cb = NULL },
+    [PROPERTY_HANDLER_OWNER_MODULE]                      = { .property_name = "OwnerModule",                   .type = "o",      .get_cb = handle_get_owner_module,                      .set_cb = NULL },
+    [PROPERTY_HANDLER_CARD]                              = { .property_name = "Card",                          .type = "o",      .get_cb = handle_get_card,                              .set_cb = NULL },
+    [PROPERTY_HANDLER_SAMPLE_FORMAT]                     = { .property_name = "SampleFormat",                  .type = "u",      .get_cb = handle_get_sample_format,                     .set_cb = NULL },
+    [PROPERTY_HANDLER_SAMPLE_RATE]                       = { .property_name = "SampleRate",                    .type = "u",      .get_cb = handle_get_sample_rate,                       .set_cb = NULL },
+    [PROPERTY_HANDLER_CHANNELS]                          = { .property_name = "Channels",                      .type = "au",     .get_cb = handle_get_channels,                          .set_cb = NULL },
+    [PROPERTY_HANDLER_VOLUME]                            = { .property_name = "Volume",                        .type = "au",     .get_cb = handle_get_volume,                            .set_cb = handle_set_volume },
+    [PROPERTY_HANDLER_HAS_FLAT_VOLUME]                   = { .property_name = "HasFlatVolume",                 .type = "b",      .get_cb = handle_get_has_flat_volume,                   .set_cb = NULL },
+    [PROPERTY_HANDLER_HAS_CONVERTIBLE_TO_DECIBEL_VOLUME] = { .property_name = "HasConvertibleToDecibelVolume", .type = "b",      .get_cb = handle_get_has_convertible_to_decibel_volume, .set_cb = NULL },
+    [PROPERTY_HANDLER_BASE_VOLUME]                       = { .property_name = "BaseVolume",                    .type = "u",      .get_cb = handle_get_base_volume,                       .set_cb = NULL },
+    [PROPERTY_HANDLER_VOLUME_STEPS]                      = { .property_name = "VolumeSteps",                   .type = "u",      .get_cb = handle_get_volume_steps,                      .set_cb = NULL },
+    [PROPERTY_HANDLER_IS_MUTED]                          = { .property_name = "IsMuted",                       .type = "b",      .get_cb = handle_get_is_muted,                          .set_cb = handle_set_is_muted },
+    [PROPERTY_HANDLER_HAS_HARDWARE_VOLUME]               = { .property_name = "HasHardwareVolume",             .type = "b",      .get_cb = handle_get_has_hardware_volume,               .set_cb = NULL },
+    [PROPERTY_HANDLER_HAS_HARDWARE_MUTE]                 = { .property_name = "HasHardwareMute",               .type = "b",      .get_cb = handle_get_has_hardware_mute,                 .set_cb = NULL },
+    [PROPERTY_HANDLER_CONFIGURED_LATENCY]                = { .property_name = "ConfiguredLatency",             .type = "t",      .get_cb = handle_get_configured_latency,                .set_cb = NULL },
+    [PROPERTY_HANDLER_HAS_DYNAMIC_LATENCY]               = { .property_name = "HasDynamicLatency",             .type = "b",      .get_cb = handle_get_has_dynamic_latency,               .set_cb = NULL },
+    [PROPERTY_HANDLER_LATENCY]                           = { .property_name = "Latency",                       .type = "t",      .get_cb = handle_get_latency,                           .set_cb = NULL },
+    [PROPERTY_HANDLER_IS_HARDWARE_DEVICE]                = { .property_name = "IsHardwareDevice",              .type = "b",      .get_cb = handle_get_is_hardware_device,                .set_cb = NULL },
+    [PROPERTY_HANDLER_IS_NETWORK_DEVICE]                 = { .property_name = "IsNetworkDevice",               .type = "b",      .get_cb = handle_get_is_network_device,                 .set_cb = NULL },
+    [PROPERTY_HANDLER_STATE]                             = { .property_name = "State",                         .type = "u",      .get_cb = handle_get_state,                             .set_cb = NULL },
+    [PROPERTY_HANDLER_PORTS]                             = { .property_name = "Ports",                         .type = "ao",     .get_cb = handle_get_ports,                             .set_cb = NULL },
+    [PROPERTY_HANDLER_ACTIVE_PORT]                       = { .property_name = "ActivePort",                    .type = "o",      .get_cb = handle_get_active_port,                       .set_cb = handle_set_active_port },
+    [PROPERTY_HANDLER_PROPERTY_LIST]                     = { .property_name = "PropertyList",                  .type = "a{say}", .get_cb = handle_get_property_list,                     .set_cb = NULL }
+};
+
+static pa_dbus_property_handler sink_property_handlers[SINK_PROPERTY_HANDLER_MAX] = {
+    [SINK_PROPERTY_HANDLER_MONITOR_SOURCE] = { .property_name = "MonitorSource", .type = "o", .get_cb = handle_sink_get_monitor_source, .set_cb = NULL }
+};
+
+static pa_dbus_property_handler source_property_handlers[SOURCE_PROPERTY_HANDLER_MAX] = {
+    [SOURCE_PROPERTY_HANDLER_MONITOR_OF_SINK] = { .property_name = "MonitorOfSink", .type = "o", .get_cb = handle_source_get_monitor_of_sink, .set_cb = NULL }
+};
+
+enum method_handler_index {
+    METHOD_HANDLER_SUSPEND,
+    METHOD_HANDLER_GET_PORT_BY_NAME,
+    METHOD_HANDLER_MAX
+};
+
+static pa_dbus_arg_info suspend_args[] = { { "suspend", "b", "in" } };
+static pa_dbus_arg_info get_port_by_name_args[] = { { "name", "s", "in" }, { "port", "o", "out" } };
+
+static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
+    [METHOD_HANDLER_SUSPEND] = {
+        .method_name = "Suspend",
+        .arguments = suspend_args,
+        .n_arguments = sizeof(suspend_args) / sizeof(pa_dbus_arg_info),
+        .receive_cb = handle_suspend },
+    [METHOD_HANDLER_GET_PORT_BY_NAME] = {
+        .method_name = "GetPortByName",
+        .arguments = get_port_by_name_args,
+        .n_arguments = sizeof(get_port_by_name_args) / sizeof(pa_dbus_arg_info),
+        .receive_cb = handle_get_port_by_name }
+};
+
+enum signal_index {
+    SIGNAL_VOLUME_UPDATED,
+    SIGNAL_MUTE_UPDATED,
+    SIGNAL_STATE_UPDATED,
+    SIGNAL_ACTIVE_PORT_UPDATED,
+    SIGNAL_PROPERTY_LIST_UPDATED,
+    SIGNAL_MAX
+};
+
+static pa_dbus_arg_info volume_updated_args[]        = { { "volume",        "au",     NULL } };
+static pa_dbus_arg_info mute_updated_args[]          = { { "muted",         "b",      NULL } };
+static pa_dbus_arg_info state_updated_args[]         = { { "state",         "u",      NULL } };
+static pa_dbus_arg_info active_port_updated_args[]   = { { "port",          "o",      NULL } };
+static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } };
+
+static pa_dbus_signal_info signals[SIGNAL_MAX] = {
+    [SIGNAL_VOLUME_UPDATED]        = { .name = "VolumeUpdated",       .arguments = volume_updated_args,        .n_arguments = 1 },
+    [SIGNAL_MUTE_UPDATED]          = { .name = "MuteUpdated",         .arguments = mute_updated_args,          .n_arguments = 1 },
+    [SIGNAL_STATE_UPDATED]         = { .name = "StateUpdated",        .arguments = state_updated_args,         .n_arguments = 1 },
+    [SIGNAL_ACTIVE_PORT_UPDATED]   = { .name = "ActivePortUpdated",   .arguments = active_port_updated_args,   .n_arguments = 1 },
+    [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 }
+};
+
+static pa_dbus_interface_info device_interface_info = {
+    .name = PA_DBUSIFACE_DEVICE_INTERFACE,
+    .method_handlers = method_handlers,
+    .n_method_handlers = METHOD_HANDLER_MAX,
+    .property_handlers = property_handlers,
+    .n_property_handlers = PROPERTY_HANDLER_MAX,
+    .get_all_properties_cb = handle_get_all,
+    .signals = signals,
+    .n_signals = SIGNAL_MAX
+};
+
+static pa_dbus_interface_info sink_interface_info = {
+    .name = PA_DBUSIFACE_SINK_INTERFACE,
+    .method_handlers = NULL,
+    .n_method_handlers = 0,
+    .property_handlers = sink_property_handlers,
+    .n_property_handlers = SINK_PROPERTY_HANDLER_MAX,
+    .get_all_properties_cb = handle_sink_get_all,
+    .signals = NULL,
+    .n_signals = 0
+};
+
+static pa_dbus_interface_info source_interface_info = {
+    .name = PA_DBUSIFACE_SOURCE_INTERFACE,
+    .method_handlers = NULL,
+    .n_method_handlers = 0,
+    .property_handlers = source_property_handlers,
+    .n_property_handlers = SOURCE_PROPERTY_HANDLER_MAX,
+    .get_all_properties_cb = handle_source_get_all,
+    .signals = NULL,
+    .n_signals = 0
+};
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    dbus_uint32_t idx = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    idx = (d->type == DEVICE_TYPE_SINK) ? d->sink->index : d->source->index;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx);
+}
+
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    const char *name = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    name = (d->type == DEVICE_TYPE_SINK) ? d->sink->name : d->source->name;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &name);
+}
+
+static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    const char *driver = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    driver = (d->type == DEVICE_TYPE_SINK) ? d->sink->driver : d->source->driver;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &driver);
+}
+
+static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    pa_module *owner_module = NULL;
+    const char *object_path = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    owner_module = (d->type == DEVICE_TYPE_SINK) ? d->sink->module : d->source->module;
+
+    if (!owner_module) {
+        if (d->type == DEVICE_TYPE_SINK)
+            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Sink %s doesn't have an owner module.", d->sink->name);
+        else
+            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Source %s doesn't have an owner module.", d->source->name);
+        return;
+    }
+
+    object_path = pa_dbusiface_core_get_module_path(d->core, owner_module);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
+}
+
+static void handle_get_card(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    pa_card *card = NULL;
+    const char *object_path = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    card = (d->type == DEVICE_TYPE_SINK) ? d->sink->card : d->source->card;
+
+    if (!card) {
+        if (d->type == DEVICE_TYPE_SINK)
+            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Sink %s doesn't belong to any card.", d->sink->name);
+        else
+            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Source %s doesn't belong to any card.", d->source->name);
+        return;
+    }
+
+    object_path = pa_dbusiface_core_get_card_path(d->core, card);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
+}
+
+static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    dbus_uint32_t sample_format = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    sample_format = (d->type == DEVICE_TYPE_SINK) ? d->sink->sample_spec.format : d->source->sample_spec.format;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sample_format);
+}
+
+static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    dbus_uint32_t sample_rate = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    sample_rate = (d->type == DEVICE_TYPE_SINK) ? d->sink->sample_spec.rate : d->source->sample_spec.rate;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sample_rate);
+}
+
+static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    pa_channel_map *channel_map = NULL;
+    dbus_uint32_t channels[PA_CHANNELS_MAX];
+    unsigned i = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    channel_map = (d->type == DEVICE_TYPE_SINK) ? &d->sink->channel_map : &d->source->channel_map;
+
+    for (i = 0; i < channel_map->channels; ++i)
+        channels[i] = channel_map->map[i];
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, channels, channel_map->channels);
+}
+
+static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    dbus_uint32_t volume[PA_CHANNELS_MAX];
+    unsigned i = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    for (i = 0; i < d->volume.channels; ++i)
+        volume[i] = d->volume.values[i];
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, volume, d->volume.channels);
+}
+
+static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    unsigned device_channels = 0;
+    dbus_uint32_t *volume = NULL;
+    unsigned n_volume_entries = 0;
+    pa_cvolume new_vol;
+    unsigned i = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    pa_cvolume_init(&new_vol);
+
+    device_channels = (d->type == DEVICE_TYPE_SINK) ? d->sink->channel_map.channels : d->source->channel_map.channels;
+
+    new_vol.channels = device_channels;
+
+    if (pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_UINT32, &volume, &n_volume_entries) < 0)
+        return;
+
+    if (n_volume_entries != device_channels) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Expected %u volume entries, got %u.", device_channels, n_volume_entries);
+        return;
+    }
+
+    for (i = 0; i < n_volume_entries; ++i) {
+        if (volume[i] > PA_VOLUME_MAX) {
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too large volume value: %u", volume[i]);
+            return;
+        }
+        new_vol.values[i] = volume[i];
+    }
+
+    if (d->type == DEVICE_TYPE_SINK)
+        pa_sink_set_volume(d->sink, &new_vol, TRUE, TRUE, TRUE, TRUE);
+    else
+        pa_source_set_volume(d->source, &new_vol, TRUE);
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+static void handle_get_has_flat_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    dbus_bool_t has_flat_volume = FALSE;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    has_flat_volume = (d->type == DEVICE_TYPE_SINK) ? (d->sink->flags & PA_SINK_FLAT_VOLUME) : FALSE;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &has_flat_volume);
+}
+
+static void handle_get_has_convertible_to_decibel_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    dbus_bool_t has_convertible_to_decibel_volume = FALSE;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    has_convertible_to_decibel_volume = (d->type == DEVICE_TYPE_SINK)
+                                        ? (d->sink->flags & PA_SINK_DECIBEL_VOLUME)
+                                        : (d->source->flags & PA_SOURCE_DECIBEL_VOLUME);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &has_convertible_to_decibel_volume);
+}
+
+static void handle_get_base_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    dbus_uint32_t base_volume;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    base_volume = (d->type == DEVICE_TYPE_SINK) ? d->sink->base_volume : d->source->base_volume;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &base_volume);
+}
+
+static void handle_get_volume_steps(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    dbus_uint32_t volume_steps;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    volume_steps = (d->type == DEVICE_TYPE_SINK) ? d->sink->n_volume_steps : d->source->n_volume_steps;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &volume_steps);
+}
+
+static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &d->is_muted);
+}
+
+static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    dbus_bool_t is_muted = FALSE;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_BOOLEAN, &is_muted) < 0)
+        return;
+
+    if (d->type == DEVICE_TYPE_SINK)
+        pa_sink_set_mute(d->sink, is_muted, TRUE);
+    else
+        pa_source_set_mute(d->source, is_muted, TRUE);
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+static void handle_get_has_hardware_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    dbus_bool_t has_hardware_volume = FALSE;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    has_hardware_volume = (d->type == DEVICE_TYPE_SINK)
+                          ? (d->sink->flags & PA_SINK_HW_VOLUME_CTRL)
+                          : (d->source->flags & PA_SOURCE_HW_VOLUME_CTRL);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &has_hardware_volume);
+}
+
+static void handle_get_has_hardware_mute(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    dbus_bool_t has_hardware_mute = FALSE;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    has_hardware_mute = (d->type == DEVICE_TYPE_SINK)
+                        ? (d->sink->flags & PA_SINK_HW_MUTE_CTRL)
+                        : (d->source->flags & PA_SOURCE_HW_MUTE_CTRL);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &has_hardware_mute);
+}
+
+static void handle_get_configured_latency(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    dbus_uint64_t configured_latency = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    configured_latency = (d->type == DEVICE_TYPE_SINK)
+                         ? pa_sink_get_requested_latency(d->sink)
+                         : pa_source_get_requested_latency(d->source);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT64, &configured_latency);
+}
+
+static void handle_get_has_dynamic_latency(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    dbus_bool_t has_dynamic_latency = FALSE;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    has_dynamic_latency = (d->type == DEVICE_TYPE_SINK)
+                          ? (d->sink->flags & PA_SINK_DYNAMIC_LATENCY)
+                          : (d->source->flags & PA_SOURCE_DYNAMIC_LATENCY);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &has_dynamic_latency);
+}
+
+static void handle_get_latency(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    dbus_uint64_t latency = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    if (d->type == DEVICE_TYPE_SINK && !(d->sink->flags & PA_SINK_LATENCY))
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Sink %s doesn't support latency querying.", d->sink->name);
+    else if (d->type == DEVICE_TYPE_SOURCE && !(d->source->flags & PA_SOURCE_LATENCY))
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Source %s doesn't support latency querying.", d->source->name);
+    return;
+
+    latency = (d->type == DEVICE_TYPE_SINK) ? pa_sink_get_latency(d->sink) : pa_source_get_latency(d->source);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT64, &latency);
+}
+
+static void handle_get_is_hardware_device(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    dbus_bool_t is_hardware_device = FALSE;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    is_hardware_device = (d->type == DEVICE_TYPE_SINK)
+                         ? (d->sink->flags & PA_SINK_HARDWARE)
+                         : (d->source->flags & PA_SOURCE_HARDWARE);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &is_hardware_device);
+}
+
+static void handle_get_is_network_device(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    dbus_bool_t is_network_device = FALSE;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    is_network_device = (d->type == DEVICE_TYPE_SINK)
+                        ? (d->sink->flags & PA_SINK_NETWORK)
+                        : (d->source->flags & PA_SOURCE_NETWORK);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &is_network_device);
+}
+
+static void handle_get_state(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    dbus_uint32_t state;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    state = (d->type == DEVICE_TYPE_SINK) ? d->sink_state : d->source_state;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &state);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_ports(pa_dbusiface_device *d, unsigned *n) {
+    const char **ports;
+    unsigned i = 0;
+    void *state = NULL;
+    pa_dbusiface_device_port *port = NULL;
+
+    pa_assert(d);
+    pa_assert(n);
+
+    *n = pa_hashmap_size(d->ports);
+
+    if (*n == 0)
+        return NULL;
+
+    ports = pa_xnew(const char *, *n);
+
+    PA_HASHMAP_FOREACH(port, d->ports, state)
+        ports[i++] = pa_dbusiface_device_port_get_path(port);
+
+    return ports;
+}
+
+static void handle_get_ports(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    const char **ports = NULL;
+    unsigned n_ports = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    ports = get_ports(d, &n_ports);
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, ports, n_ports);
+
+    pa_xfree(ports);
+}
+
+static void handle_get_active_port(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    const char *active_port;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    if (!d->active_port) {
+        pa_assert(pa_hashmap_isempty(d->ports));
+
+        if (d->type == DEVICE_TYPE_SINK)
+            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                               "The sink %s has no ports, and therefore there's no active port either.", d->sink->name);
+        else
+            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                               "The source %s has no ports, and therefore there's no active port either.", d->source->name);
+        return;
+    }
+
+    active_port = pa_dbusiface_device_port_get_path(pa_hashmap_get(d->ports, d->active_port->name));
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &active_port);
+}
+
+static void handle_set_active_port(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    const char *new_active_path;
+    pa_dbusiface_device_port *new_active;
+    int r;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_OBJECT_PATH, &new_active_path) < 0)
+        return;
+
+    if (!d->active_port) {
+        pa_assert(pa_hashmap_isempty(d->ports));
+
+        if (d->type == DEVICE_TYPE_SINK)
+            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                               "The sink %s has no ports, and therefore there's no active port either.", d->sink->name);
+        else
+            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                               "The source %s has no ports, and therefore there's no active port either.", d->source->name);
+        return;
+    }
+
+    if (!(new_active = pa_hashmap_get(d->ports, new_active_path))) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such port.", new_active_path);
+        return;
+    }
+
+    if (d->type == DEVICE_TYPE_SINK) {
+        if ((r = pa_sink_set_port(d->sink, pa_dbusiface_device_port_get_name(new_active), TRUE)) < 0) {
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED,
+                               "Internal error in PulseAudio: pa_sink_set_port() failed with error code %i.", r);
+            return;
+        }
+    } else {
+        if ((r = pa_source_set_port(d->source, pa_dbusiface_device_port_get_name(new_active), TRUE)) < 0) {
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED,
+                               "Internal error in PulseAudio: pa_source_set_port() failed with error code %i.", r);
+            return;
+        }
+    }
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    pa_dbus_send_proplist_variant_reply(conn, msg, d->proplist);
+}
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+    DBusMessageIter dict_iter;
+    dbus_uint32_t idx = 0;
+    const char *name = NULL;
+    const char *driver = NULL;
+    pa_module *owner_module = NULL;
+    const char *owner_module_path = NULL;
+    pa_card *card = NULL;
+    const char *card_path = NULL;
+    dbus_uint32_t sample_format = 0;
+    dbus_uint32_t sample_rate = 0;
+    pa_channel_map *channel_map = NULL;
+    dbus_uint32_t channels[PA_CHANNELS_MAX];
+    dbus_uint32_t volume[PA_CHANNELS_MAX];
+    dbus_bool_t has_flat_volume = FALSE;
+    dbus_bool_t has_convertible_to_decibel_volume = FALSE;
+    dbus_uint32_t base_volume = 0;
+    dbus_uint32_t volume_steps = 0;
+    dbus_bool_t has_hardware_volume = FALSE;
+    dbus_bool_t has_hardware_mute = FALSE;
+    dbus_uint64_t configured_latency = 0;
+    dbus_bool_t has_dynamic_latency = FALSE;
+    dbus_uint64_t latency = 0;
+    dbus_bool_t is_hardware_device = FALSE;
+    dbus_bool_t is_network_device = FALSE;
+    dbus_uint32_t state = 0;
+    const char **ports = NULL;
+    unsigned n_ports = 0;
+    const char *active_port = NULL;
+    unsigned i = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    idx = (d->type == DEVICE_TYPE_SINK) ? d->sink->index : d->source->index;
+    name = (d->type == DEVICE_TYPE_SINK) ? d->sink->name : d->source->name;
+    driver = (d->type == DEVICE_TYPE_SINK) ? d->sink->driver : d->source->driver;
+    owner_module = (d->type == DEVICE_TYPE_SINK) ? d->sink->module : d->source->module;
+    if (owner_module)
+        owner_module_path = pa_dbusiface_core_get_module_path(d->core, owner_module);
+    card = (d->type == DEVICE_TYPE_SINK) ? d->sink->card : d->source->card;
+    if (card)
+        card_path = pa_dbusiface_core_get_card_path(d->core, card);
+    sample_format = (d->type == DEVICE_TYPE_SINK) ? d->sink->sample_spec.format : d->source->sample_spec.format;
+    sample_rate = (d->type == DEVICE_TYPE_SINK) ? d->sink->sample_spec.rate : d->source->sample_spec.rate;
+    channel_map = (d->type == DEVICE_TYPE_SINK) ? &d->sink->channel_map : &d->source->channel_map;
+    for (i = 0; i < channel_map->channels; ++i)
+        channels[i] = channel_map->map[i];
+    for (i = 0; i < d->volume.channels; ++i)
+        volume[i] = d->volume.values[i];
+    has_flat_volume = (d->type == DEVICE_TYPE_SINK) ? (d->sink->flags & PA_SINK_FLAT_VOLUME) : FALSE;
+    has_convertible_to_decibel_volume = (d->type == DEVICE_TYPE_SINK)
+                                        ? (d->sink->flags & PA_SINK_DECIBEL_VOLUME)
+                                        : (d->source->flags & PA_SOURCE_DECIBEL_VOLUME);
+    base_volume = (d->type == DEVICE_TYPE_SINK) ? d->sink->base_volume : d->source->base_volume;
+    volume_steps = (d->type == DEVICE_TYPE_SINK) ? d->sink->n_volume_steps : d->source->n_volume_steps;
+    has_hardware_volume = (d->type == DEVICE_TYPE_SINK)
+                          ? (d->sink->flags & PA_SINK_HW_VOLUME_CTRL)
+                          : (d->source->flags & PA_SOURCE_HW_VOLUME_CTRL);
+    has_hardware_mute = (d->type == DEVICE_TYPE_SINK)
+                        ? (d->sink->flags & PA_SINK_HW_MUTE_CTRL)
+                        : (d->source->flags & PA_SOURCE_HW_MUTE_CTRL);
+    configured_latency = (d->type == DEVICE_TYPE_SINK)
+                         ? pa_sink_get_requested_latency(d->sink)
+                         : pa_source_get_requested_latency(d->source);
+    has_dynamic_latency = (d->type == DEVICE_TYPE_SINK)
+                          ? (d->sink->flags & PA_SINK_DYNAMIC_LATENCY)
+                          : (d->source->flags & PA_SOURCE_DYNAMIC_LATENCY);
+    latency = (d->type == DEVICE_TYPE_SINK) ? pa_sink_get_latency(d->sink) : pa_source_get_latency(d->source);
+    is_hardware_device = (d->type == DEVICE_TYPE_SINK)
+                         ? (d->sink->flags & PA_SINK_HARDWARE)
+                         : (d->source->flags & PA_SOURCE_HARDWARE);
+    is_network_device = (d->type == DEVICE_TYPE_SINK)
+                        ? (d->sink->flags & PA_SINK_NETWORK)
+                        : (d->source->flags & PA_SOURCE_NETWORK);
+    state = (d->type == DEVICE_TYPE_SINK) ? pa_sink_get_state(d->sink) : pa_source_get_state(d->source);
+    ports = get_ports(d, &n_ports);
+    if (d->active_port)
+        active_port = pa_dbusiface_device_port_get_path(pa_hashmap_get(d->ports, d->active_port->name));
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &name);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DRIVER].property_name, DBUS_TYPE_STRING, &driver);
+
+    if (owner_module)
+        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_OWNER_MODULE].property_name, DBUS_TYPE_OBJECT_PATH, &owner_module_path);
+
+    if (card)
+        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CARD].property_name, DBUS_TYPE_OBJECT_PATH, &card_path);
+
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_FORMAT].property_name, DBUS_TYPE_UINT32, &sample_format);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_RATE].property_name, DBUS_TYPE_UINT32, &sample_rate);
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CHANNELS].property_name, DBUS_TYPE_UINT32, channels, channel_map->channels);
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_VOLUME].property_name, DBUS_TYPE_UINT32, volume, d->volume.channels);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HAS_FLAT_VOLUME].property_name, DBUS_TYPE_BOOLEAN, &has_flat_volume);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HAS_CONVERTIBLE_TO_DECIBEL_VOLUME].property_name, DBUS_TYPE_BOOLEAN, &has_convertible_to_decibel_volume);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_BASE_VOLUME].property_name, DBUS_TYPE_UINT32, &base_volume);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_VOLUME_STEPS].property_name, DBUS_TYPE_UINT32, &volume_steps);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_IS_MUTED].property_name, DBUS_TYPE_BOOLEAN, &d->is_muted);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HAS_HARDWARE_VOLUME].property_name, DBUS_TYPE_BOOLEAN, &has_hardware_volume);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HAS_HARDWARE_MUTE].property_name, DBUS_TYPE_BOOLEAN, &has_hardware_mute);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CONFIGURED_LATENCY].property_name, DBUS_TYPE_UINT64, &configured_latency);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HAS_DYNAMIC_LATENCY].property_name, DBUS_TYPE_BOOLEAN, &has_dynamic_latency);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_LATENCY].property_name, DBUS_TYPE_UINT64, &latency);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_IS_HARDWARE_DEVICE].property_name, DBUS_TYPE_BOOLEAN, &is_hardware_device);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_IS_NETWORK_DEVICE].property_name, DBUS_TYPE_BOOLEAN, &is_network_device);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_STATE].property_name, DBUS_TYPE_UINT32, &state);
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PORTS].property_name, DBUS_TYPE_OBJECT_PATH, ports, n_ports);
+
+    if (active_port)
+        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_ACTIVE_PORT].property_name, DBUS_TYPE_OBJECT_PATH, &active_port);
+
+    pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, d->proplist);
+
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+
+    dbus_message_unref(reply);
+
+    pa_xfree(ports);
+}
+
+static void handle_suspend(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    dbus_bool_t suspend = FALSE;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_BOOLEAN, &suspend) < 0)
+        return;
+
+    if ((d->type == DEVICE_TYPE_SINK) && (pa_sink_suspend(d->sink, suspend, PA_SUSPEND_USER) < 0)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Internal error in PulseAudio: pa_sink_suspend() failed.");
+        return;
+    } else if ((d->type == DEVICE_TYPE_SOURCE) && (pa_source_suspend(d->source, suspend, PA_SUSPEND_USER) < 0)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Internal error in PulseAudio: pa_source_suspend() failed.");
+        return;
+    }
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+static void handle_get_port_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    DBusError error;
+    const char *port_name = NULL;
+    pa_dbusiface_device_port *port = NULL;
+    const char *port_path = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+
+    dbus_error_init(&error);
+
+    if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &port_name, DBUS_TYPE_INVALID)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
+        dbus_error_free(&error);
+        return;
+    }
+
+    if (!(port = pa_hashmap_get(d->ports, port_name))) {
+        if (d->type == DEVICE_TYPE_SINK)
+            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND,
+                               "%s: No such port on sink %s.", port_name, d->sink->name);
+        else
+            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND,
+                               "%s: No such port on source %s.", port_name, d->source->name);
+        return;
+    }
+
+    port_path = pa_dbusiface_device_port_get_path(port);
+
+    pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &port_path);
+}
+
+static void handle_sink_get_monitor_source(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    const char *monitor_source = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+    pa_assert(d->type == DEVICE_TYPE_SINK);
+
+    monitor_source = pa_dbusiface_core_get_source_path(d->core, d->sink->monitor_source);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &monitor_source);
+}
+
+static void handle_sink_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+    DBusMessageIter dict_iter;
+    const char *monitor_source = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+    pa_assert(d->type == DEVICE_TYPE_SINK);
+
+    monitor_source = pa_dbusiface_core_get_source_path(d->core, d->sink->monitor_source);
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[SINK_PROPERTY_HANDLER_MONITOR_SOURCE].property_name, DBUS_TYPE_OBJECT_PATH, &monitor_source);
+
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+
+    dbus_message_unref(reply);
+}
+
+static void handle_source_get_monitor_of_sink(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    const char *monitor_of_sink = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+    pa_assert(d->type == DEVICE_TYPE_SOURCE);
+
+    if (!d->source->monitor_of) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Source %s is not a monitor source.", d->source->name);
+        return;
+    }
+
+    monitor_of_sink = pa_dbusiface_core_get_sink_path(d->core, d->source->monitor_of);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &monitor_of_sink);
+}
+
+static void handle_source_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+    DBusMessageIter dict_iter;
+    const char *monitor_of_sink = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(d);
+    pa_assert(d->type == DEVICE_TYPE_SOURCE);
+
+    if (d->source->monitor_of)
+        monitor_of_sink = pa_dbusiface_core_get_sink_path(d->core, d->source->monitor_of);
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+    if (monitor_of_sink)
+        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[SOURCE_PROPERTY_HANDLER_MONITOR_OF_SINK].property_name, DBUS_TYPE_OBJECT_PATH, &monitor_of_sink);
+
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+
+    dbus_message_unref(reply);
+}
+
+static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
+    pa_dbusiface_device *d = userdata;
+
+    pa_assert(c);
+    pa_assert(d);
+
+    if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) {
+        DBusMessage *signal = NULL;
+        const pa_cvolume *new_volume = NULL;
+        pa_bool_t new_muted = FALSE;
+        pa_sink_state_t new_sink_state = 0;
+        pa_source_state_t new_source_state = 0;
+        pa_device_port *new_active_port = NULL;
+        pa_proplist *new_proplist = NULL;
+        unsigned i = 0;
+
+        pa_assert(((d->type == DEVICE_TYPE_SINK)
+                    && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK))
+                  || ((d->type == DEVICE_TYPE_SOURCE)
+                       && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE)));
+
+        new_volume = (d->type == DEVICE_TYPE_SINK)
+                     ? pa_sink_get_volume(d->sink, FALSE, FALSE)
+                     : pa_source_get_volume(d->source, FALSE);
+
+        if (!pa_cvolume_equal(&d->volume, new_volume)) {
+            dbus_uint32_t volume[PA_CHANNELS_MAX];
+            dbus_uint32_t *volume_ptr = volume;
+
+            d->volume = *new_volume;
+
+            for (i = 0; i < d->volume.channels; ++i)
+                volume[i] = d->volume.values[i];
+
+            pa_assert_se(signal = dbus_message_new_signal(d->path,
+                                                          PA_DBUSIFACE_DEVICE_INTERFACE,
+                                                          signals[SIGNAL_VOLUME_UPDATED].name));
+            pa_assert_se(dbus_message_append_args(signal,
+                                                  DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &volume_ptr, d->volume.channels,
+                                                  DBUS_TYPE_INVALID));
+
+            pa_dbus_protocol_send_signal(d->dbus_protocol, signal);
+            dbus_message_unref(signal);
+            signal = NULL;
+        }
+
+        new_muted = (d->type == DEVICE_TYPE_SINK) ? pa_sink_get_mute(d->sink, FALSE) : pa_source_get_mute(d->source, FALSE);
+
+        if (d->is_muted != new_muted) {
+            d->is_muted = new_muted;
+
+            pa_assert_se(signal = dbus_message_new_signal(d->path,
+                                                          PA_DBUSIFACE_DEVICE_INTERFACE,
+                                                          signals[SIGNAL_MUTE_UPDATED].name));
+            pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_BOOLEAN, &d->is_muted, DBUS_TYPE_INVALID));
+
+            pa_dbus_protocol_send_signal(d->dbus_protocol, signal);
+            dbus_message_unref(signal);
+            signal = NULL;
+        }
+
+        if (d->type == DEVICE_TYPE_SINK)
+            new_sink_state = pa_sink_get_state(d->sink);
+        else
+            new_source_state = pa_source_get_state(d->source);
+
+        if ((d->type == DEVICE_TYPE_SINK && d->sink_state != new_sink_state)
+            || (d->type == DEVICE_TYPE_SOURCE && d->source_state != new_source_state)) {
+            dbus_uint32_t state = 0;
+
+            if (d->type == DEVICE_TYPE_SINK)
+                d->sink_state = new_sink_state;
+            else
+                d->source_state = new_source_state;
+
+            state = (d->type == DEVICE_TYPE_SINK) ? d->sink_state : d->source_state;
+
+            pa_assert_se(signal = dbus_message_new_signal(d->path,
+                                                          PA_DBUSIFACE_DEVICE_INTERFACE,
+                                                          signals[SIGNAL_STATE_UPDATED].name));
+            pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_UINT32, &state, DBUS_TYPE_INVALID));
+
+            pa_dbus_protocol_send_signal(d->dbus_protocol, signal);
+            dbus_message_unref(signal);
+            signal = NULL;
+        }
+
+        new_active_port = (d->type == DEVICE_TYPE_SINK) ? d->sink->active_port : d->source->active_port;
+
+        if (d->active_port != new_active_port) {
+            const char *object_path = NULL;
+
+            d->active_port = new_active_port;
+            object_path = pa_dbusiface_device_port_get_path(pa_hashmap_get(d->ports, d->active_port->name));
+
+            pa_assert_se(signal = dbus_message_new_signal(d->path,
+                                                          PA_DBUSIFACE_DEVICE_INTERFACE,
+                                                          signals[SIGNAL_ACTIVE_PORT_UPDATED].name));
+            pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+            pa_dbus_protocol_send_signal(d->dbus_protocol, signal);
+            dbus_message_unref(signal);
+            signal = NULL;
+        }
+
+        new_proplist = (d->type == DEVICE_TYPE_SINK) ? d->sink->proplist : d->source->proplist;
+
+        if (!pa_proplist_equal(d->proplist, new_proplist)) {
+            DBusMessageIter msg_iter;
+
+            pa_proplist_update(d->proplist, PA_UPDATE_SET, new_proplist);
+
+            pa_assert_se(signal = dbus_message_new_signal(d->path,
+                                                          PA_DBUSIFACE_DEVICE_INTERFACE,
+                                                          signals[SIGNAL_PROPERTY_LIST_UPDATED].name));
+            dbus_message_iter_init_append(signal, &msg_iter);
+            pa_dbus_append_proplist(&msg_iter, d->proplist);
+
+            pa_dbus_protocol_send_signal(d->dbus_protocol, signal);
+            dbus_message_unref(signal);
+            signal = NULL;
+        }
+    }
+}
+
 pa_dbusiface_device *pa_dbusiface_device_new_sink(pa_dbusiface_core *core, pa_sink *sink) {
-    pa_dbusiface_device *d;
+    pa_dbusiface_device *d = NULL;
 
     pa_assert(core);
     pa_assert(sink);
 
-    d = pa_xnew(pa_dbusiface_device, 1);
+    d = pa_xnew0(pa_dbusiface_device, 1);
+    d->core = core;
     d->sink = pa_sink_ref(sink);
     d->type = DEVICE_TYPE_SINK;
     d->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, SINK_OBJECT_NAME, sink->index);
+    d->volume = *pa_sink_get_volume(sink, FALSE, FALSE);
+    d->is_muted = pa_sink_get_mute(sink, FALSE);
+    d->sink_state = pa_sink_get_state(sink);
+    d->ports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+    d->next_port_index = 0;
+    d->active_port = NULL;
+    d->proplist = pa_proplist_copy(sink->proplist);
+    d->dbus_protocol = pa_dbus_protocol_get(sink->core);
+    d->subscription = pa_subscription_new(sink->core, PA_SUBSCRIPTION_MASK_SINK, subscription_cb, d);
+
+    if (sink->ports) {
+        pa_device_port *port;
+        void *state = NULL;
+
+        PA_HASHMAP_FOREACH(port, sink->ports, state) {
+            pa_dbusiface_device_port *p = pa_dbusiface_device_port_new(d, sink->core, port, d->next_port_index++);
+            pa_hashmap_put(d->ports, pa_dbusiface_device_port_get_name(p), p);
+        }
+        pa_assert_se(d->active_port = sink->active_port);
+    }
+
+    pa_assert_se(pa_dbus_protocol_add_interface(d->dbus_protocol, d->path, &device_interface_info, d) >= 0);
+    pa_assert_se(pa_dbus_protocol_add_interface(d->dbus_protocol, d->path, &sink_interface_info, d) >= 0);
 
     return d;
 }
 
 pa_dbusiface_device *pa_dbusiface_device_new_source(pa_dbusiface_core *core, pa_source *source) {
-    pa_dbusiface_device *d;
+    pa_dbusiface_device *d = NULL;
 
     pa_assert(core);
     pa_assert(source);
 
-    d = pa_xnew(pa_dbusiface_device, 1);
+    d = pa_xnew0(pa_dbusiface_device, 1);
+    d->core = core;
     d->source = pa_source_ref(source);
     d->type = DEVICE_TYPE_SOURCE;
     d->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, SOURCE_OBJECT_NAME, source->index);
+    d->volume = *pa_source_get_volume(source, FALSE);
+    d->is_muted = pa_source_get_mute(source, FALSE);
+    d->source_state = pa_source_get_state(source);
+    d->ports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+    d->next_port_index = 0;
+    d->active_port = NULL;
+    d->proplist = pa_proplist_copy(source->proplist);
+    d->dbus_protocol = pa_dbus_protocol_get(source->core);
+    d->subscription = pa_subscription_new(source->core, PA_SUBSCRIPTION_MASK_SOURCE, subscription_cb, d);
+
+    if (source->ports) {
+        pa_device_port *port;
+        void *state = NULL;
+
+        PA_HASHMAP_FOREACH(port, source->ports, state) {
+            pa_dbusiface_device_port *p = pa_dbusiface_device_port_new(d, source->core, port, d->next_port_index++);
+            pa_hashmap_put(d->ports, pa_dbusiface_device_port_get_name(p), p);
+        }
+        pa_assert_se(d->active_port = source->active_port);
+    }
+
+    pa_assert_se(pa_dbus_protocol_add_interface(d->dbus_protocol, d->path, &device_interface_info, d) >= 0);
+    pa_assert_se(pa_dbus_protocol_add_interface(d->dbus_protocol, d->path, &source_interface_info, d) >= 0);
 
     return d;
 }
 
+static void port_free_cb(void *p, void *userdata) {
+    pa_dbusiface_device_port *port = p;
+
+    pa_assert(port);
+
+    pa_dbusiface_device_port_free(port);
+}
+
 void pa_dbusiface_device_free(pa_dbusiface_device *d) {
     pa_assert(d);
 
-    if (d->type == DEVICE_TYPE_SINK)
+    pa_assert_se(pa_dbus_protocol_remove_interface(d->dbus_protocol, d->path, device_interface_info.name) >= 0);
+
+    if (d->type == DEVICE_TYPE_SINK) {
+        pa_assert_se(pa_dbus_protocol_remove_interface(d->dbus_protocol, d->path, sink_interface_info.name) >= 0);
         pa_sink_unref(d->sink);
-    else
+
+    } else {
+        pa_assert_se(pa_dbus_protocol_remove_interface(d->dbus_protocol, d->path, source_interface_info.name) >= 0);
         pa_source_unref(d->source);
+    }
+    pa_hashmap_free(d->ports, port_free_cb, NULL);
+    pa_dbus_protocol_unref(d->dbus_protocol);
+    pa_subscription_free(d->subscription);
 
     pa_xfree(d->path);
     pa_xfree(d);
diff --git a/src/modules/dbus/iface-device.h b/src/modules/dbus/iface-device.h
index 1e9af83..62e05e9 100644
--- a/src/modules/dbus/iface-device.h
+++ b/src/modules/dbus/iface-device.h
@@ -29,11 +29,16 @@
  * documentation.
  */
 
+#include <pulsecore/protocol-dbus.h>
 #include <pulsecore/sink.h>
 #include <pulsecore/source.h>
 
 #include "iface-core.h"
 
+#define PA_DBUSIFACE_DEVICE_INTERFACE PA_DBUS_CORE_INTERFACE ".Device"
+#define PA_DBUSIFACE_SINK_INTERFACE PA_DBUS_CORE_INTERFACE ".Sink"
+#define PA_DBUSIFACE_SOURCE_INTERFACE PA_DBUS_CORE_INTERFACE ".Source"
+
 typedef struct pa_dbusiface_device pa_dbusiface_device;
 
 pa_dbusiface_device *pa_dbusiface_device_new_sink(pa_dbusiface_core *core, pa_sink *sink);

commit f663d13acd306feafe2526c6a9258f14fd5d2ffc
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Sat Aug 15 16:54:11 2009 +0300

    dbusiface-core: Two new functions: pa_dbusiface_core_get_playback/record_stream_path().

diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c
index 81e709f..2b5cf0b 100644
--- a/src/modules/dbus/iface-core.c
+++ b/src/modules/dbus/iface-core.c
@@ -2055,6 +2055,20 @@ const char *pa_dbusiface_core_get_source_path(pa_dbusiface_core *c, const pa_sou
     return pa_dbusiface_device_get_path(pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(source->index)));
 }
 
+const char *pa_dbusiface_core_get_playback_stream_path(pa_dbusiface_core *c, const pa_sink_input *sink_input) {
+    pa_assert(c);
+    pa_assert(sink_input);
+
+    return pa_dbusiface_stream_get_path(pa_hashmap_get(c->playback_streams, PA_UINT32_TO_PTR(sink_input->index)));
+}
+
+const char *pa_dbusiface_core_get_record_stream_path(pa_dbusiface_core *c, const pa_source_output *source_output) {
+    pa_assert(c);
+    pa_assert(source_output);
+
+    return pa_dbusiface_stream_get_path(pa_hashmap_get(c->record_streams, PA_UINT32_TO_PTR(source_output->index)));
+}
+
 const char *pa_dbusiface_core_get_module_path(pa_dbusiface_core *c, const pa_module *module) {
     pa_assert(c);
     pa_assert(module);
diff --git a/src/modules/dbus/iface-core.h b/src/modules/dbus/iface-core.h
index 7010205..14dff7e 100644
--- a/src/modules/dbus/iface-core.h
+++ b/src/modules/dbus/iface-core.h
@@ -38,6 +38,8 @@ void pa_dbusiface_core_free(pa_dbusiface_core *c);
 const char *pa_dbusiface_core_get_card_path(pa_dbusiface_core *c, const pa_card *card);
 const char *pa_dbusiface_core_get_sink_path(pa_dbusiface_core *c, const pa_sink *sink);
 const char *pa_dbusiface_core_get_source_path(pa_dbusiface_core *c, const pa_source *source);
+const char *pa_dbusiface_core_get_playback_stream_path(pa_dbusiface_core *c, const pa_sink_input *sink_input);
+const char *pa_dbusiface_core_get_record_stream_path(pa_dbusiface_core *c, const pa_source_output *source_output);
 const char *pa_dbusiface_core_get_module_path(pa_dbusiface_core *c, const pa_module *module);
 
 #endif

commit 9ed25d7388bacfb0948d21d9aefa4fe92dbea0b5
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Sat Aug 15 16:55:29 2009 +0300

    dbusiface-client: Implement the properties of the Client D-Bus interface.
    
    Based on a patch from Vincent Filali-Ansary.

diff --git a/src/modules/dbus/iface-client.c b/src/modules/dbus/iface-client.c
index d9c8653..dd4e57a 100644
--- a/src/modules/dbus/iface-client.c
+++ b/src/modules/dbus/iface-client.c
@@ -2,6 +2,7 @@
   This file is part of PulseAudio.
 
   Copyright 2009 Tanu Kaskinen
+  Copyright 2009 Vincent Filali-Ansary <filali.v at azurdigitalnetworks.net>
 
   PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
@@ -23,7 +24,10 @@
 #include <config.h>
 #endif
 
+#include <dbus/dbus.h>
+
 #include <pulsecore/core-util.h>
+#include <pulsecore/dbus-util.h>
 #include <pulsecore/protocol-dbus.h>
 
 #include "iface-client.h"
@@ -31,19 +35,288 @@
 #define OBJECT_NAME "client"
 
 struct pa_dbusiface_client {
+    pa_dbusiface_core *core;
+
     pa_client *client;
     char *path;
+
+    pa_dbus_protocol *dbus_protocol;
+};
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_playback_streams(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_record_streams(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+/*static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_update_properties(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_remove_properties(DBusConnection *conn, DBusMessage *msg, void *userdata);*/
+
+enum property_handler_index {
+    PROPERTY_HANDLER_INDEX,
+    PROPERTY_HANDLER_DRIVER,
+    PROPERTY_HANDLER_OWNER_MODULE,
+    PROPERTY_HANDLER_PLAYBACK_STREAMS,
+    PROPERTY_HANDLER_RECORD_STREAMS,
+    PROPERTY_HANDLER_PROPERTY_LIST,
+    PROPERTY_HANDLER_MAX
+};
+
+static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
+    [PROPERTY_HANDLER_INDEX]            = { .property_name = "Index",           .type = "u",      .get_cb = handle_get_index,            .set_cb = NULL },
+    [PROPERTY_HANDLER_DRIVER]           = { .property_name = "Driver",          .type = "s",      .get_cb = handle_get_driver,           .set_cb = NULL },
+    [PROPERTY_HANDLER_OWNER_MODULE]     = { .property_name = "OwnerModule",     .type = "o",      .get_cb = handle_get_owner_module,     .set_cb = NULL },
+    [PROPERTY_HANDLER_PLAYBACK_STREAMS] = { .property_name = "PlaybackStreams", .type = "ao",     .get_cb = handle_get_playback_streams, .set_cb = NULL },
+    [PROPERTY_HANDLER_RECORD_STREAMS]   = { .property_name = "RecordStreams",   .type = "ao",     .get_cb = handle_get_record_streams,   .set_cb = NULL },
+    [PROPERTY_HANDLER_PROPERTY_LIST]    = { .property_name = "PropertyList",    .type = "a{say}", .get_cb = handle_get_property_list,    .set_cb = NULL }
+};
+
+/*enum method_handler_index {
+    METHOD_HANDLER_KILL,
+    METHOD_HANDLER_UPDATE_PROPERTIES,
+    METHOD_HANDLER_REMOVE_PROPERTIES,
+    METHOD_HANDLER_MAX
 };
 
+static pa_dbus_arg_info update_properties_args[] = { { "property_list", "a{say}", "in" }, { "update_mode", "u", "in" } };
+static pa_dbus_arg_info update_properties_args[] = { { "keys", "as", "in" } };
+
+static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
+    [METHOD_HANDLER_KILL] = {
+        .method_name = "Kill",
+        .arguments = NULL,
+        .n_arguments = 0,
+        .receive_cb = handle_kill },
+    [METHOD_HANDLER_UPDATE_PROPERTIES] = {
+        .method_name = "UpdateProperties",
+        .arguments = update_propertes_args,
+        .n_arguments = sizeof(update_properties_args) / sizeof(pa_dbus_arg_info),
+        .receive_cb = handle_update_properties },
+    [METHOD_HANDLER_REMOVE_PROPERTIES] = {
+        .method_name = "RemoveProperties",
+        .arguments = remove_propertes_args,
+        .n_arguments = sizeof(update_properties_args) / sizeof(pa_dbus_arg_info),
+        .receive_cb = handle_update_properties }
+};
+
+enum signal_index {
+    SIGNAL_PROPERTY_LIST_UPDATED,
+    SIGNAL_CLIENT_EVENT,
+    SIGNAL_MAX
+};
+
+static pa_dbus_arg_info active_profile_updated_args[] = { { "profile",       "o",      NULL } };
+static pa_dbus_arg_info property_list_updated_args[]  = { { "property_list", "a{say}", NULL } };
+
+static pa_dbus_signal_info signals[SIGNAL_MAX] = {
+    [SIGNAL_ACTIVE_PROFILE_UPDATED] = { .name = "ActiveProfileUpdated", .arguments = active_profile_updated_args, .n_arguments = 1 },
+    [SIGNAL_PROPERTY_LIST_UPDATED]  = { .name = "PropertyListUpdated",  .arguments = property_list_updated_args,  .n_arguments = 1 }
+};*/
+
+static pa_dbus_interface_info client_interface_info = {
+    .name = OBJECT_NAME,
+    .method_handlers = /*method_handlers*/ NULL,
+    .n_method_handlers = /*METHOD_HANDLER_MAX*/ 0,
+    .property_handlers = property_handlers,
+    .n_property_handlers = PROPERTY_HANDLER_MAX,
+    .get_all_properties_cb = handle_get_all,
+    .signals = /*signals*/ NULL,
+    .n_signals = /*SIGNAL_MAX*/ 0
+};
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+  pa_dbusiface_client *c = userdata;
+  dbus_uint32_t idx = 0;
+
+  pa_assert(conn);
+  pa_assert(msg);
+  pa_assert(c);
+
+  idx = c->client->index;
+
+  pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx);
+}
+
+static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_client *c = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &c->client->driver);
+}
+
+static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_client *c = userdata;
+    const char *owner_module = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    if (!c->client->module) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Client %d doesn't have an owner module.", c->client->index);
+        return;
+    }
+
+    owner_module = pa_dbusiface_core_get_module_path(c->core, c->client->module);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &owner_module);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_playback_streams(pa_dbusiface_client *c, unsigned *n) {
+    const char **playback_streams = NULL;
+    unsigned i = 0;
+    uint32_t idx = 0;
+    pa_sink_input *sink_input = NULL;
+
+    pa_assert(c);
+    pa_assert(n);
+
+    *n = pa_idxset_size(c->client->sink_inputs);
+
+    if (*n == 0)
+        return NULL;
+
+    playback_streams = pa_xnew(const char *, *n);
+
+    PA_IDXSET_FOREACH(sink_input, c->client->sink_inputs, idx)
+        playback_streams[i++] = pa_dbusiface_core_get_playback_stream_path(c->core, sink_input);
+
+    return playback_streams;
+}
+
+static void handle_get_playback_streams(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_client *c = userdata;
+    const char **playback_streams = NULL;
+    unsigned n_playback_streams = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    playback_streams = get_playback_streams(c, &n_playback_streams);
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, playback_streams, n_playback_streams);
+
+    pa_xfree(playback_streams);
+}
+
+/* The caller frees the array, but not the strings. */
+static const char **get_record_streams(pa_dbusiface_client *c, unsigned *n) {
+    const char **record_streams = NULL;
+    unsigned i = 0;
+    uint32_t idx = 0;
+    pa_source_output *source_output = NULL;
+
+    pa_assert(c);
+    pa_assert(n);
+
+    *n = pa_idxset_size(c->client->source_outputs);
+
+    if (*n == 0)
+        return NULL;
+
+    record_streams = pa_xnew(const char *, *n);
+
+    PA_IDXSET_FOREACH(source_output, c->client->source_outputs, idx)
+        record_streams[i++] = pa_dbusiface_core_get_record_stream_path(c->core, source_output);
+
+    return record_streams;
+}
+
+static void handle_get_record_streams(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_client *c = userdata;
+    const char **record_streams = NULL;
+    unsigned n_record_streams = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    record_streams = get_record_streams(c, &n_record_streams);
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, record_streams, n_record_streams);
+
+    pa_xfree(record_streams);
+}
+
+static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_client *c = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    pa_dbus_send_proplist_variant_reply(conn, msg, c->client->proplist);
+}
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_client *c = userdata;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+    DBusMessageIter dict_iter;
+    dbus_uint32_t idx = 0;
+    const char *owner_module = NULL;
+    const char **playback_streams = NULL;
+    unsigned n_playback_streams = 0;
+    const char **record_streams = NULL;
+    unsigned n_record_streams = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    idx = c->client->index;
+    if (c->client->module)
+        owner_module = pa_dbusiface_core_get_module_path(c->core, c->client->module);
+    playback_streams = get_playback_streams(c, &n_playback_streams);
+    record_streams = get_record_streams(c, &n_record_streams);
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DRIVER].property_name, DBUS_TYPE_STRING, &c->client->driver);
+
+    if (owner_module)
+        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_OWNER_MODULE].property_name, DBUS_TYPE_OBJECT_PATH, &owner_module);
+
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PLAYBACK_STREAMS].property_name, DBUS_TYPE_OBJECT_PATH, playback_streams, n_playback_streams);
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_RECORD_STREAMS].property_name, DBUS_TYPE_OBJECT_PATH, record_streams, n_record_streams);
+    pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, c->client->proplist);
+
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+
+    dbus_message_unref(reply);
+
+    pa_xfree(playback_streams);
+    pa_xfree(record_streams);
+}
+
 pa_dbusiface_client *pa_dbusiface_client_new(pa_dbusiface_core *core, pa_client *client) {
-    pa_dbusiface_client *c;
+    pa_dbusiface_client *c = NULL;
 
     pa_assert(core);
     pa_assert(client);
 
     c = pa_xnew(pa_dbusiface_client, 1);
+    c->core = core;
     c->client = client;
     c->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, client->index);
+    c->dbus_protocol = pa_dbus_protocol_get(client->core);
+
+    pa_assert_se(pa_dbus_protocol_add_interface(c->dbus_protocol, c->path, &client_interface_info, c) >= 0);
 
     return c;
 }
@@ -51,6 +324,10 @@ pa_dbusiface_client *pa_dbusiface_client_new(pa_dbusiface_core *core, pa_client
 void pa_dbusiface_client_free(pa_dbusiface_client *c) {
     pa_assert(c);
 
+    pa_assert_se(pa_dbus_protocol_remove_interface(c->dbus_protocol, c->path, client_interface_info.name) >= 0);
+
+    pa_dbus_protocol_unref(c->dbus_protocol);
+
     pa_xfree(c->path);
     pa_xfree(c);
 }

commit c7f4ed3c7a0453ff29181fe03666e0c7d3822317
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Sat Aug 15 17:08:21 2009 +0300

    dbusiface-client: Fix the interface name.

diff --git a/src/modules/dbus/iface-client.c b/src/modules/dbus/iface-client.c
index dd4e57a..186e2d3 100644
--- a/src/modules/dbus/iface-client.c
+++ b/src/modules/dbus/iface-client.c
@@ -118,7 +118,7 @@ static pa_dbus_signal_info signals[SIGNAL_MAX] = {
 };*/
 
 static pa_dbus_interface_info client_interface_info = {
-    .name = OBJECT_NAME,
+    .name = PA_DBUSIFACE_CLIENT_INTERFACE,
     .method_handlers = /*method_handlers*/ NULL,
     .n_method_handlers = /*METHOD_HANDLER_MAX*/ 0,
     .property_handlers = property_handlers,
diff --git a/src/modules/dbus/iface-client.h b/src/modules/dbus/iface-client.h
index ff90625..e8f151c 100644
--- a/src/modules/dbus/iface-client.h
+++ b/src/modules/dbus/iface-client.h
@@ -22,16 +22,19 @@
   USA.
 ***/
 
-/* This object implements the D-Bus interface org.PulseAudio.Core1.Card.
+/* This object implements the D-Bus interface org.PulseAudio.Core1.Client.
  *
- * See http://pulseaudio.org/wiki/DBusInterface for the Card interface
+ * See http://pulseaudio.org/wiki/DBusInterface for the Client interface
  * documentation.
  */
 
 #include <pulsecore/client.h>
+#include <pulsecore/protocol-dbus.h>
 
 #include "iface-core.h"
 
+#define PA_DBUSIFACE_CLIENT_INTERFACE PA_DBUS_CORE_INTERFACE ".Client"
+
 typedef struct pa_dbusiface_client pa_dbusiface_client;
 
 pa_dbusiface_client *pa_dbusiface_client_new(pa_dbusiface_core *core, pa_client *client);

commit a72bba18ea4dd273371179844a0a964e706fd489
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Sun Aug 16 19:39:39 2009 +0300

    dbusiface-client: Fix indentation.

diff --git a/src/modules/dbus/iface-client.c b/src/modules/dbus/iface-client.c
index 186e2d3..587d5c4 100644
--- a/src/modules/dbus/iface-client.c
+++ b/src/modules/dbus/iface-client.c
@@ -129,16 +129,16 @@ static pa_dbus_interface_info client_interface_info = {
 };
 
 static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) {
-  pa_dbusiface_client *c = userdata;
-  dbus_uint32_t idx = 0;
+    pa_dbusiface_client *c = userdata;
+    dbus_uint32_t idx = 0;
 
-  pa_assert(conn);
-  pa_assert(msg);
-  pa_assert(c);
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
 
-  idx = c->client->index;
+    idx = c->client->index;
 
-  pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx);
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx);
 }
 
 static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata) {

commit f0db081223dd056d6ddbc2a97c557254198a0eaf
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Sun Aug 16 19:41:43 2009 +0300

    dbusiface-device: Free the copied proplist.

diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c
index 3cf9d19..15ece83 100644
--- a/src/modules/dbus/iface-device.c
+++ b/src/modules/dbus/iface-device.c
@@ -1274,6 +1274,7 @@ void pa_dbusiface_device_free(pa_dbusiface_device *d) {
         pa_source_unref(d->source);
     }
     pa_hashmap_free(d->ports, port_free_cb, NULL);
+    pa_proplist_free(d->proplist);
     pa_dbus_protocol_unref(d->dbus_protocol);
     pa_subscription_free(d->subscription);
 

commit 2bb3eef414f80189cf6af6cd66c519630e4c0a43
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Sun Aug 16 19:42:56 2009 +0300

    dbusiface-stream: Implement about a half of the Stream D-Bus interface.

diff --git a/src/modules/dbus/iface-stream.c b/src/modules/dbus/iface-stream.c
index b5a1789..4333c16 100644
--- a/src/modules/dbus/iface-stream.c
+++ b/src/modules/dbus/iface-stream.c
@@ -2,6 +2,7 @@
   This file is part of PulseAudio.
 
   Copyright 2009 Tanu Kaskinen
+  Copyright 2009 Vincent Filali-Ansary <filali.v at azurdigitalnetworks.net>
 
   PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
@@ -24,6 +25,7 @@
 #endif
 
 #include <pulsecore/core-util.h>
+#include <pulsecore/dbus-util.h>
 #include <pulsecore/protocol-dbus.h>
 
 #include "iface-stream.h"
@@ -43,8 +45,369 @@ struct pa_dbusiface_stream {
     };
     enum stream_type type;
     char *path;
+    pa_cvolume volume;
+    pa_bool_t is_muted;
+    pa_proplist *proplist;
+
+    pa_dbus_protocol *dbus_protocol;
+    pa_subscription *subscription;
+};
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata);
+/*static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_client(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_device(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata);*/
+static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata);
+/*static void handle_get_buffer_latency(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_device_latency(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_resample_method(DBusConnection *conn, DBusMessage *msg, void *userdata);*/
+static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+/*static void handle_move(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata);*/
+
+enum property_handler_index {
+    PROPERTY_HANDLER_INDEX,
+/*    PROPERTY_HANDLER_DRIVER,
+    PROPERTY_HANDLER_OWNER_MODULE,
+    PROPERTY_HANDLER_CLIENT,
+    PROPERTY_HANDLER_DEVICE,
+    PROPERTY_HANDLER_SAMPLE_FORMAT,
+    PROPERTY_HANDLER_SAMPLE_RATE,
+    PROPERTY_HANDLER_CHANNELS,*/
+    PROPERTY_HANDLER_VOLUME,
+    PROPERTY_HANDLER_IS_MUTED,
+/*    PROPERTY_HANDLER_BUFFER_LATENCY,
+    PROPERTY_HANDLER_DEVICE_LATENCY,
+    PROPERTY_HANDLER_RESAMPLE_METHOD,*/
+    PROPERTY_HANDLER_PROPERTY_LIST,
+    PROPERTY_HANDLER_MAX
+};
+
+static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
+    [PROPERTY_HANDLER_INDEX]           = { .property_name = "Index",          .type = "u",      .get_cb = handle_get_index,           .set_cb = NULL },
+/*    [PROPERTY_HANDLER_DRIVER]          = { .property_name = "Driver",         .type = "s",      .get_cb = handle_get_driver,          .set_cb = NULL },
+    [PROPERTY_HANDLER_OWNER_MODULE]    = { .property_name = "OwnerModule",    .type = "o",      .get_cb = handle_get_owner_module,    .set_cb = NULL },
+    [PROPERTY_HANDLER_CLIENT]          = { .property_name = "Client",         .type = "o",      .get_cb = handle_get_client,          .set_cb = NULL },
+    [PROPERTY_HANDLER_DEVICE]          = { .property_name = "Device",         .type = "o",      .get_cb = handle_get_device,          .set_cb = NULL },
+    [PROPERTY_HANDLER_SAMPLE_FORMAT]   = { .property_name = "SampleFormat",   .type = "u",      .get_cb = handle_get_sample_format,   .set_cb = NULL },
+    [PROPERTY_HANDLER_SAMPLE_RATE]     = { .property_name = "SampleRate",     .type = "u",      .get_cb = handle_get_sample_rate,     .set_cb = NULL },
+    [PROPERTY_HANDLER_CHANNELS]        = { .property_name = "Channels",       .type = "au",     .get_cb = handle_get_channels,        .set_cb = NULL },*/
+    [PROPERTY_HANDLER_VOLUME]          = { .property_name = "Volume",         .type = "au",     .get_cb = handle_get_volume,          .set_cb = handle_set_volume },
+    [PROPERTY_HANDLER_IS_MUTED]        = { .property_name = "IsMuted",        .type = "b",      .get_cb = handle_get_is_muted,        .set_cb = handle_set_is_muted },
+/*    [PROPERTY_HANDLER_BUFFER_LATENCY]  = { .property_name = "BufferLatency",  .type = "t",      .get_cb = handle_get_buffer_latency,  .set_cb = NULL },
+    [PROPERTY_HANDLER_DEVICE_LATENCY]  = { .property_name = "DeviceLatency",  .type = "t",      .get_cb = handle_get_device_latency,  .set_cb = NULL },
+    [PROPERTY_HANDLER_RESAMPLE_METHOD] = { .property_name = "ResampleMethod", .type = "s",      .get_cb = handle_get_resample_method, .set_cb = NULL },*/
+    [PROPERTY_HANDLER_PROPERTY_LIST]   = { .property_name = "PropertyList",   .type = "a{say}", .get_cb = handle_get_property_list,   .set_cb = NULL }
+};
+
+/*enum method_handler_index {
+    METHOD_HANDLER_MOVE,
+    METHOD_HANDLER_KILL,
+    METHOD_HANDLER_MAX
+};
+
+static pa_dbus_arg_info move_args[] = { { "device", "o", "in" } };
+
+static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
+    [METHOD_HANDLER_MOVE] = {
+        .method_name = "Move",
+        .arguments = move_args,
+        .n_arguments = sizeof(move_args) / sizeof(pa_dbus_arg_info),
+        .receive_cb = handle_move },
+    [METHOD_HANDLER_KILL] = {
+        .method_name = "Kill",
+        .arguments = NULL,
+        .n_arguments = 0,
+        .receive_cb = handle_kill }
+};*/
+
+enum signal_index {
+/*    SIGNAL_DEVICE_UPDATED,
+    SIGNAL_SAMPLE_RATE_UPDATED,*/
+    SIGNAL_VOLUME_UPDATED,
+    SIGNAL_MUTE_UPDATED,
+    SIGNAL_PROPERTY_LIST_UPDATED,
+/*    SIGNAL_STREAM_EVENT,*/
+    SIGNAL_MAX
+};
+
+/*static pa_dbus_arg_info device_updated_args[]        = { { "device",        "o",      NULL } };
+static pa_dbus_arg_info sample_rate_updated_args[]   = { { "sample_rate",   "u",      NULL } };*/
+static pa_dbus_arg_info volume_updated_args[]        = { { "volume",        "au",     NULL } };
+static pa_dbus_arg_info mute_updated_args[]          = { { "muted",         "b",      NULL } };
+static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } };
+/*static pa_dbus_arg_info stream_event_args[]          = { { "name",          "s",      NULL }, { "property_list", "a{say}", NULL } };*/
+
+static pa_dbus_signal_info signals[SIGNAL_MAX] = {
+/*    [SIGNAL_DEVICE_UPDATED]        = { .name = "DeviceUpdated",       .arguments = device_updated_args,        .n_arguments = 1 },
+    [SIGNAL_SAMPLE_RATE_UPDATED]   = { .name = "SampleRateUpdated",   .arguments = sample_rate_updated_args,   .n_arguments = 1 },*/
+    [SIGNAL_VOLUME_UPDATED]        = { .name = "VolumeUpdated",       .arguments = volume_updated_args,        .n_arguments = 1 },
+    [SIGNAL_MUTE_UPDATED]          = { .name = "MuteUpdated",         .arguments = mute_updated_args,          .n_arguments = 1 },
+    [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 }/*,
+    [SIGNAL_STREAM_EVENT]          = { .name = "StreamEvent",         .arguments = stream_event_args,          .n_arguments = sizeof(stream_event_args) / sizeof(pa_dbus_arg_info) }*/
 };
 
+static pa_dbus_interface_info stream_interface_info = {
+    .name = PA_DBUSIFACE_STREAM_INTERFACE,
+    .method_handlers = /*method_handlers*/ NULL,
+    .n_method_handlers = /*METHOD_HANDLER_MAX*/ 0,
+    .property_handlers = property_handlers,
+    .n_property_handlers = PROPERTY_HANDLER_MAX,
+    .get_all_properties_cb = handle_get_all,
+    .signals = signals,
+    .n_signals = SIGNAL_MAX
+};
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+    dbus_uint32_t idx;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    idx = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->index : s->source_output->index;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx);
+}
+
+static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+    dbus_uint32_t volume[PA_CHANNELS_MAX];
+    unsigned i = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    if (s->type == STREAM_TYPE_RECORD) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have volume.");
+        return;
+    }
+
+    for (i = 0; i < s->volume.channels; ++i)
+        volume[i] = s->volume.values[i];
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, volume, s->volume.channels);
+}
+
+static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+    unsigned stream_channels = 0;
+    dbus_uint32_t *volume = NULL;
+    unsigned n_volume_entries = 0;
+    pa_cvolume new_vol;
+    unsigned i = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    if (s->type == STREAM_TYPE_RECORD) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have volume.");
+        return;
+    }
+
+    pa_cvolume_init(&new_vol);
+
+    stream_channels = s->sink_input->channel_map.channels;
+
+    new_vol.channels = stream_channels;
+
+    if (pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_UINT32, &volume, &n_volume_entries) < 0)
+        return;
+
+    if (n_volume_entries != stream_channels) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Expected %u volume entries, got %u.", stream_channels, n_volume_entries);
+        return;
+    }
+
+    for (i = 0; i < n_volume_entries; ++i) {
+        if (volume[i] > PA_VOLUME_MAX) {
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too large volume value: %u", volume[i]);
+            return;
+        }
+        new_vol.values[i] = volume[i];
+    }
+
+    pa_sink_input_set_volume(s->sink_input, &new_vol, TRUE, TRUE);
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    if (s->type == STREAM_TYPE_RECORD) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have mute.");
+        return;
+    }
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &s->is_muted);
+}
+
+static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+    dbus_bool_t is_muted = FALSE;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_BOOLEAN, &is_muted) < 0)
+        return;
+
+    if (s->type == STREAM_TYPE_RECORD) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have mute.");
+        return;
+    }
+
+    pa_sink_input_set_mute(s->sink_input, is_muted, TRUE);
+
+    pa_dbus_send_empty_reply(conn, msg);
+};
+
+static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    pa_dbus_send_proplist_variant_reply(conn, msg, s->proplist);
+}
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+    DBusMessageIter dict_iter;
+    dbus_uint32_t idx;
+    dbus_uint32_t volume[PA_CHANNELS_MAX];
+    unsigned i = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    idx = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->index : s->source_output->index;
+    if (s->type == STREAM_TYPE_PLAYBACK) {
+        for (i = 0; i < s->volume.channels; ++i)
+            volume[i] = s->volume.values[i];
+    }
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx);
+
+    if (s->type == STREAM_TYPE_PLAYBACK) {
+        pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_VOLUME].property_name, DBUS_TYPE_UINT32, volume, s->volume.channels);
+        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_IS_MUTED].property_name, DBUS_TYPE_BOOLEAN, &s->is_muted);
+    }
+
+    pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, s->proplist);
+
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+    dbus_message_unref(reply);
+}
+
+static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+
+    pa_assert(c);
+    pa_assert(s);
+
+    if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) {
+        DBusMessage *signal = NULL;
+        pa_proplist *new_proplist = NULL;
+        unsigned i = 0;
+
+        pa_assert(((s->type == STREAM_TYPE_PLAYBACK)
+                    && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT))
+                  || ((s->type == STREAM_TYPE_RECORD)
+                       && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT)));
+
+        if (s->type == STREAM_TYPE_PLAYBACK) {
+            pa_cvolume new_volume;
+            pa_bool_t new_muted = FALSE;
+
+            pa_sink_input_get_volume(s->sink_input, &new_volume, TRUE);
+
+            if (!pa_cvolume_equal(&s->volume, &new_volume)) {
+                dbus_uint32_t volume[PA_CHANNELS_MAX];
+                dbus_uint32_t *volume_ptr = volume;
+
+                s->volume = new_volume;
+
+                for (i = 0; i < s->volume.channels; ++i)
+                    volume[i] = s->volume.values[i];
+
+                pa_assert_se(signal = dbus_message_new_signal(s->path,
+                                                              PA_DBUSIFACE_STREAM_INTERFACE,
+                                                              signals[SIGNAL_VOLUME_UPDATED].name));
+                pa_assert_se(dbus_message_append_args(signal,
+                                                      DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &volume_ptr, s->volume.channels,
+                                                      DBUS_TYPE_INVALID));
+
+                pa_dbus_protocol_send_signal(s->dbus_protocol, signal);
+                dbus_message_unref(signal);
+                signal = NULL;
+            }
+
+            new_muted = pa_sink_input_get_mute(s->sink_input);
+
+            if (s->is_muted != new_muted) {
+                s->is_muted = new_muted;
+
+                pa_assert_se(signal = dbus_message_new_signal(s->path,
+                                                              PA_DBUSIFACE_STREAM_INTERFACE,
+                                                              signals[SIGNAL_MUTE_UPDATED].name));
+                pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_BOOLEAN, &s->is_muted, DBUS_TYPE_INVALID));
+
+                pa_dbus_protocol_send_signal(s->dbus_protocol, signal);
+                dbus_message_unref(signal);
+                signal = NULL;
+            }
+        }
+
+        new_proplist = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->proplist : s->source_output->proplist;
+
+        if (!pa_proplist_equal(s->proplist, new_proplist)) {
+            DBusMessageIter msg_iter;
+
+            pa_proplist_update(s->proplist, PA_UPDATE_SET, new_proplist);
+
+            pa_assert_se(signal = dbus_message_new_signal(s->path,
+                                                          PA_DBUSIFACE_STREAM_INTERFACE,
+                                                          signals[SIGNAL_PROPERTY_LIST_UPDATED].name));
+            dbus_message_iter_init_append(signal, &msg_iter);
+            pa_dbus_append_proplist(&msg_iter, s->proplist);
+
+            pa_dbus_protocol_send_signal(s->dbus_protocol, signal);
+            dbus_message_unref(signal);
+            signal = NULL;
+        }
+    }
+}
+
 pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, pa_sink_input *sink_input) {
     pa_dbusiface_stream *s;
 
@@ -55,6 +418,13 @@ pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, p
     s->sink_input = pa_sink_input_ref(sink_input);
     s->type = STREAM_TYPE_PLAYBACK;
     s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, PLAYBACK_OBJECT_NAME, sink_input->index);
+    pa_sink_input_get_volume(sink_input, &s->volume, TRUE);
+    s->is_muted = pa_sink_input_get_mute(sink_input);
+    s->proplist = pa_proplist_copy(sink_input->proplist);
+    s->dbus_protocol = pa_dbus_protocol_get(sink_input->core);
+    s->subscription = pa_subscription_new(sink_input->core, PA_SUBSCRIPTION_MASK_SINK_INPUT, subscription_cb, s);
+
+    pa_assert_se(pa_dbus_protocol_add_interface(s->dbus_protocol, s->path, &stream_interface_info, s) >= 0);
 
     return s;
 }
@@ -69,6 +439,13 @@ pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_dbusiface_core *core, pa_
     s->source_output = pa_source_output_ref(source_output);
     s->type = STREAM_TYPE_RECORD;
     s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, RECORD_OBJECT_NAME, source_output->index);
+    pa_cvolume_init(&s->volume);
+    s->is_muted = FALSE;
+    s->proplist = pa_proplist_copy(source_output->proplist);
+    s->dbus_protocol = pa_dbus_protocol_get(source_output->core);
+    s->subscription = pa_subscription_new(source_output->core, PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscription_cb, s);
+
+    pa_assert_se(pa_dbus_protocol_add_interface(s->dbus_protocol, s->path, &stream_interface_info, s) >= 0);
 
     return s;
 }
@@ -76,11 +453,17 @@ pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_dbusiface_core *core, pa_
 void pa_dbusiface_stream_free(pa_dbusiface_stream *s) {
     pa_assert(s);
 
+    pa_assert_se(pa_dbus_protocol_remove_interface(s->dbus_protocol, s->path, stream_interface_info.name) >= 0);
+
     if (s->type == STREAM_TYPE_PLAYBACK)
         pa_sink_input_unref(s->sink_input);
     else
         pa_source_output_unref(s->source_output);
 
+    pa_proplist_free(s->proplist);
+    pa_dbus_protocol_unref(s->dbus_protocol);
+    pa_subscription_free(s->subscription);
+
     pa_xfree(s->path);
     pa_xfree(s);
 }
diff --git a/src/modules/dbus/iface-stream.h b/src/modules/dbus/iface-stream.h
index b1b1854..036b4e7 100644
--- a/src/modules/dbus/iface-stream.h
+++ b/src/modules/dbus/iface-stream.h
@@ -28,11 +28,14 @@
  * documentation.
  */
 
+#include <pulsecore/protocol-dbus.h>
 #include <pulsecore/sink-input.h>
 #include <pulsecore/source-output.h>
 
 #include "iface-core.h"
 
+#define PA_DBUSIFACE_STREAM_INTERFACE PA_DBUS_CORE_INTERFACE ".Stream"
+
 typedef struct pa_dbusiface_stream pa_dbusiface_stream;
 
 pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, pa_sink_input *sink_input);

commit bcaba0b1b43d6a1b32aadfa98860f40b2c93e136
Merge: 2bb3eef 01e4b61
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Sun Aug 16 21:25:48 2009 +0300

    Merge branch 'master' of git://0pointer.de/pulseaudio into dbus-work
    
    Conflicts:
    	src/Makefile.am
    	src/daemon/daemon-conf.c
    	src/daemon/daemon.conf.in
    	src/modules/module-stream-restore.c
    	src/pulse/client-conf.c
    	src/pulsecore/namereg.c

diff --cc po/es.po
index 8af1c73,b36d693..6995716
--- a/po/es.po
+++ b/po/es.po
@@@ -2600,9 -2408,8 +2408,7 @@@ msgstr "Servidor de Sonido PulseAudio
  #~ "Tipo: %s\n"
  #~ "Módulo: %s\n"
  #~ "Argumento: %s\n"
- 
  #~ msgid "sink"
  #~ msgstr "destino"
- 
  #~ msgid "source"
  #~ msgstr "fuente"
 -
diff --cc po/pt_BR.po
index fd4f539,20365ee..323e5b9
--- a/po/pt_BR.po
+++ b/po/pt_BR.po
@@@ -2497,6 -2370,6 +2370,5 @@@ msgstr "Servidor de som PulseAudio
  #~ msgstr ""
  #~ "', ou eleve o RLIMIT_NICE/RLIMIT_RTPRIO dos limites do recurso para este "
  #~ "usuário."
- 
  #~ msgid "socketpair(): %s"
  #~ msgstr "socketpair(): %s"
 -
diff --cc src/daemon/daemon-conf.c
index ace460e,9a87b55..31f2900
--- a/src/daemon/daemon-conf.c
+++ b/src/daemon/daemon-conf.c
@@@ -476,14 -441,15 +476,18 @@@ int pa_daemon_conf_load(pa_daemon_conf 
          { "high-priority",              pa_config_parse_bool,     &c->high_priority, NULL },
          { "realtime-scheduling",        pa_config_parse_bool,     &c->realtime_scheduling, NULL },
          { "disallow-module-loading",    pa_config_parse_bool,     &c->disallow_module_loading, NULL },
+         { "allow-module-loading",       pa_config_parse_not_bool, &c->disallow_module_loading, NULL },
          { "disallow-exit",              pa_config_parse_bool,     &c->disallow_exit, NULL },
+         { "allow-exit",                 pa_config_parse_not_bool, &c->disallow_exit, NULL },
          { "use-pid-file",               pa_config_parse_bool,     &c->use_pid_file, NULL },
          { "system-instance",            pa_config_parse_bool,     &c->system_instance, NULL },
 +#ifdef HAVE_DBUS
 +        { "local-server-type",          parse_server_type,        c, NULL },
 +#endif
          { "no-cpu-limit",               pa_config_parse_bool,     &c->no_cpu_limit, NULL },
+         { "cpu-limit",                  pa_config_parse_not_bool, &c->no_cpu_limit, NULL },
          { "disable-shm",                pa_config_parse_bool,     &c->disable_shm, NULL },
+         { "enable-shm",                 pa_config_parse_not_bool, &c->disable_shm, NULL },
          { "flat-volumes",               pa_config_parse_bool,     &c->flat_volumes, NULL },
          { "lock-memory",                pa_config_parse_bool,     &c->lock_memory, NULL },
          { "exit-idle-time",             pa_config_parse_int,      &c->exit_idle_time, NULL },
@@@ -669,15 -629,12 +675,15 @@@ char *pa_daemon_conf_dump(pa_daemon_con
      pa_strbuf_printf(s, "nice-level = %i\n", c->nice_level);
      pa_strbuf_printf(s, "realtime-scheduling = %s\n", pa_yes_no(c->realtime_scheduling));
      pa_strbuf_printf(s, "realtime-priority = %i\n", c->realtime_priority);
-     pa_strbuf_printf(s, "disallow-module-loading = %s\n", pa_yes_no(c->disallow_module_loading));
-     pa_strbuf_printf(s, "disallow-exit = %s\n", pa_yes_no(c->disallow_exit));
+     pa_strbuf_printf(s, "allow-module-loading = %s\n", pa_yes_no(!c->disallow_module_loading));
+     pa_strbuf_printf(s, "allow-exit = %s\n", pa_yes_no(!c->disallow_exit));
      pa_strbuf_printf(s, "use-pid-file = %s\n", pa_yes_no(c->use_pid_file));
      pa_strbuf_printf(s, "system-instance = %s\n", pa_yes_no(c->system_instance));
 +#ifdef HAVE_DBUS
 +    pa_strbuf_printf(s, "local-server-type = %s\n", server_type_to_string[c->local_server_type]);
 +#endif
-     pa_strbuf_printf(s, "no-cpu-limit = %s\n", pa_yes_no(c->no_cpu_limit));
-     pa_strbuf_printf(s, "disable-shm = %s\n", pa_yes_no(c->disable_shm));
+     pa_strbuf_printf(s, "cpu-limit = %s\n", pa_yes_no(!c->no_cpu_limit));
+     pa_strbuf_printf(s, "enable-shm = %s\n", pa_yes_no(!c->disable_shm));
      pa_strbuf_printf(s, "flat-volumes = %s\n", pa_yes_no(c->flat_volumes));
      pa_strbuf_printf(s, "lock-memory = %s\n", pa_yes_no(c->lock_memory));
      pa_strbuf_printf(s, "exit-idle-time = %i\n", c->exit_idle_time);
diff --cc src/daemon/daemon.conf.in
index 9bea614,d8b58d8..a11fd06
--- a/src/daemon/daemon.conf.in
+++ b/src/daemon/daemon.conf.in
@@@ -21,15 -21,14 +21,15 @@@
  
  ; daemonize = no
  ; fail = yes
- ; disallow-module-loading = no
- ; disallow-exit = no
+ ; allow-module-loading = yes
+ ; allow-exit = yes
  ; use-pid-file = yes
  ; system-instance = no
 +; local-server-type = user
- ; disable-shm = no
+ ; enable-shm = yes
  ; shm-size-bytes = 0 # setting this 0 will use the system-default, usually 64 MiB
  ; lock-memory = no
- ; no-cpu-limit = no
+ ; cpu-limit = yes
  
  ; high-priority = yes
  ; nice-level = -11
diff --cc src/modules/module-stream-restore.c
index e906303,e560bd2..076b391
--- a/src/modules/module-stream-restore.c
+++ b/src/modules/module-stream-restore.c
@@@ -106,15 -100,9 +106,15 @@@ struct userdata 
  
      pa_native_protocol *protocol;
      pa_idxset *subscribed;
 +
 +#ifdef HAVE_DBUS
 +    pa_dbus_protocol *dbus_protocol;
 +    pa_hashmap *dbus_entries;
 +    uint32_t next_index; /* For generating object paths for entries. */
 +#endif
  };
  
- #define ENTRY_VERSION 2
+ #define ENTRY_VERSION 3
  
  struct entry {
      uint8_t version;
@@@ -1216,7 -324,10 +1231,11 @@@ static void subscribe_callback(pa_core 
              pa_strlcpy(entry.device, sink_input->sink->name, sizeof(entry.device));
              entry.device_valid = TRUE;
  
 +            device_updated = !created_new_entry && (!old->device_valid || !pa_streq(entry.device, old->device));
+             if (sink_input->sink->card) {
+                 pa_strlcpy(entry.card, sink_input->sink->card->name, sizeof(entry.card));
+                 entry.card_valid = TRUE;
+             }
          }
  
      } else {
@@@ -1239,7 -348,10 +1258,12 @@@
              pa_strlcpy(entry.device, source_output->source->name, sizeof(entry.device));
              entry.device_valid = source_output->save_source;
  
 +            device_updated = !created_new_entry && (!old->device_valid || !pa_streq(entry.device, old->device));
++
+             if (source_output->source->card) {
+                 pa_strlcpy(entry.card, source_output->source->card->name, sizeof(entry.card));
+                 entry.card_valid = TRUE;
+             }
          }
      }
  
diff --cc src/pulse/client-conf.c
index 8eab109,4aa4ba1..62c06f6
--- a/src/pulse/client-conf.c
+++ b/src/pulse/client-conf.c
@@@ -94,17 -92,17 +94,18 @@@ int pa_client_conf_load(pa_client_conf 
  
      /* Prepare the configuration parse table */
      pa_config_item table[] = {
-         { "daemon-binary",          pa_config_parse_string,  &c->daemon_binary, NULL },
-         { "extra-arguments",        pa_config_parse_string,  &c->extra_arguments, NULL },
-         { "default-sink",           pa_config_parse_string,  &c->default_sink, NULL },
-         { "default-source",         pa_config_parse_string,  &c->default_source, NULL },
-         { "default-server",         pa_config_parse_string,  &c->default_server, NULL },
-         { "default-dbus-server",    pa_config_parse_string,  &c->default_dbus_server, NULL },
-         { "autospawn",              pa_config_parse_bool,    &c->autospawn, NULL },
-         { "cookie-file",            pa_config_parse_string,  &c->cookie_file, NULL },
-         { "disable-shm",            pa_config_parse_bool,    &c->disable_shm, NULL },
-         { "shm-size-bytes",         pa_config_parse_size,    &c->shm_size, NULL },
-         { NULL,                     NULL,                    NULL, NULL },
+         { "daemon-binary",          pa_config_parse_string,   &c->daemon_binary, NULL },
+         { "extra-arguments",        pa_config_parse_string,   &c->extra_arguments, NULL },
+         { "default-sink",           pa_config_parse_string,   &c->default_sink, NULL },
+         { "default-source",         pa_config_parse_string,   &c->default_source, NULL },
+         { "default-server",         pa_config_parse_string,   &c->default_server, NULL },
++        { "default-dbus-server",    pa_config_parse_string,   &c->default_dbus_server, NULL },
+         { "autospawn",              pa_config_parse_bool,     &c->autospawn, NULL },
+         { "cookie-file",            pa_config_parse_string,   &c->cookie_file, NULL },
+         { "disable-shm",            pa_config_parse_bool,     &c->disable_shm, NULL },
+         { "enable-shm",             pa_config_parse_not_bool, &c->disable_shm, NULL },
+         { "shm-size-bytes",         pa_config_parse_size,     &c->shm_size, NULL },
+         { NULL,                     NULL,                     NULL, NULL },
      };
  
      if (filename) {
diff --cc src/pulsecore/dbus-util.c
index 903acad,4e6148f..b45e6a6
--- a/src/pulsecore/dbus-util.c
+++ b/src/pulsecore/dbus-util.c
@@@ -445,475 -422,3 +445,475 @@@ void pa_dbus_free_pending_list(pa_dbus_
          pa_dbus_pending_free(i);
      }
  }
 +
 +void pa_dbus_send_error(DBusConnection *c, DBusMessage *in_reply_to, const char *name, const char *format, ...) {
 +    va_list ap;
 +    char *message;
 +    DBusMessage *reply = NULL;
 +
 +    pa_assert(c);
 +    pa_assert(in_reply_to);
 +    pa_assert(name);
 +    pa_assert(format);
 +
 +    va_start(ap, format);
 +    message = pa_vsprintf_malloc(format, ap);
 +    va_end(ap);
 +    pa_assert_se((reply = dbus_message_new_error(in_reply_to, name, message)));
 +    pa_assert_se(dbus_connection_send(c, reply, NULL));
 +
 +    dbus_message_unref(reply);
 +
 +    pa_xfree(message);
 +}
 +
 +void pa_dbus_send_empty_reply(DBusConnection *c, DBusMessage *in_reply_to) {
 +    DBusMessage *reply = NULL;
 +
 +    pa_assert(c);
 +    pa_assert(in_reply_to);
 +
 +    pa_assert_se((reply = dbus_message_new_method_return(in_reply_to)));
 +    pa_assert_se(dbus_connection_send(c, reply, NULL));
 +    dbus_message_unref(reply);
 +}
 +
 +void pa_dbus_send_basic_value_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data) {
 +    DBusMessage *reply = NULL;
 +
 +    pa_assert(c);
 +    pa_assert(in_reply_to);
 +    pa_assert(dbus_type_is_basic(type));
 +    pa_assert(data);
 +
 +    pa_assert_se((reply = dbus_message_new_method_return(in_reply_to)));
 +    pa_assert_se(dbus_message_append_args(reply, type, data, DBUS_TYPE_INVALID));
 +    pa_assert_se(dbus_connection_send(c, reply, NULL));
 +    dbus_message_unref(reply);
 +}
 +
 +static const char *signature_from_basic_type(int type) {
 +    switch (type) {
 +        case DBUS_TYPE_BOOLEAN: return DBUS_TYPE_BOOLEAN_AS_STRING;
 +        case DBUS_TYPE_BYTE: return DBUS_TYPE_BYTE_AS_STRING;
 +        case DBUS_TYPE_INT16: return DBUS_TYPE_INT16_AS_STRING;
 +        case DBUS_TYPE_UINT16: return DBUS_TYPE_UINT16_AS_STRING;
 +        case DBUS_TYPE_INT32: return DBUS_TYPE_INT32_AS_STRING;
 +        case DBUS_TYPE_UINT32: return DBUS_TYPE_UINT32_AS_STRING;
 +        case DBUS_TYPE_INT64: return DBUS_TYPE_INT64_AS_STRING;
 +        case DBUS_TYPE_UINT64: return DBUS_TYPE_UINT64_AS_STRING;
 +        case DBUS_TYPE_DOUBLE: return DBUS_TYPE_DOUBLE_AS_STRING;
 +        case DBUS_TYPE_STRING: return DBUS_TYPE_STRING_AS_STRING;
 +        case DBUS_TYPE_OBJECT_PATH: return DBUS_TYPE_OBJECT_PATH_AS_STRING;
 +        case DBUS_TYPE_SIGNATURE: return DBUS_TYPE_SIGNATURE_AS_STRING;
 +        default: pa_assert_not_reached();
 +    }
 +}
 +
 +void pa_dbus_send_basic_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data) {
 +    DBusMessage *reply = NULL;
 +    DBusMessageIter msg_iter;
 +    DBusMessageIter variant_iter;
 +
 +    pa_assert(c);
 +    pa_assert(in_reply_to);
 +    pa_assert(dbus_type_is_basic(type));
 +    pa_assert(data);
 +
 +    pa_assert_se((reply = dbus_message_new_method_return(in_reply_to)));
 +    dbus_message_iter_init_append(reply, &msg_iter);
 +    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_VARIANT, signature_from_basic_type(type), &variant_iter));
 +    pa_assert_se(dbus_message_iter_append_basic(&variant_iter, type, data));
 +    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &variant_iter));
 +    pa_assert_se(dbus_connection_send(c, reply, NULL));
 +    dbus_message_unref(reply);
 +}
 +
 +/* Note: returns sizeof(char*) for strings, object paths and signatures. */
 +static unsigned basic_type_size(int type) {
 +    switch (type) {
 +        case DBUS_TYPE_BOOLEAN: return sizeof(dbus_bool_t);
 +        case DBUS_TYPE_BYTE: return 1;
 +        case DBUS_TYPE_INT16: return sizeof(dbus_int16_t);
 +        case DBUS_TYPE_UINT16: return sizeof(dbus_uint16_t);
 +        case DBUS_TYPE_INT32: return sizeof(dbus_int32_t);
 +        case DBUS_TYPE_UINT32: return sizeof(dbus_uint32_t);
 +        case DBUS_TYPE_INT64: return sizeof(dbus_int64_t);
 +        case DBUS_TYPE_UINT64: return sizeof(dbus_uint64_t);
 +        case DBUS_TYPE_DOUBLE: return sizeof(double);
 +        case DBUS_TYPE_STRING:
 +        case DBUS_TYPE_OBJECT_PATH:
 +        case DBUS_TYPE_SIGNATURE: return sizeof(char*);
 +        default: pa_assert_not_reached();
 +    }
 +}
 +
 +void pa_dbus_send_basic_array_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int item_type, void *array, unsigned n) {
 +    DBusMessage *reply = NULL;
 +    DBusMessageIter msg_iter;
 +
 +    pa_assert(c);
 +    pa_assert(in_reply_to);
 +    pa_assert(dbus_type_is_basic(item_type));
 +    pa_assert(array || n == 0);
 +
 +    pa_assert_se((reply = dbus_message_new_method_return(in_reply_to)));
 +    dbus_message_iter_init_append(reply, &msg_iter);
 +    pa_dbus_append_basic_array_variant(&msg_iter, item_type, array, n);
 +    pa_assert_se(dbus_connection_send(c, reply, NULL));
 +    dbus_message_unref(reply);
 +}
 +
 +void pa_dbus_send_proplist_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, pa_proplist *proplist) {
 +    DBusMessage *reply = NULL;
 +    DBusMessageIter msg_iter;
 +
 +    pa_assert(c);
 +    pa_assert(in_reply_to);
 +    pa_assert(proplist);
 +
 +    pa_assert_se((reply = dbus_message_new_method_return(in_reply_to)));
 +    dbus_message_iter_init_append(reply, &msg_iter);
 +    pa_dbus_append_proplist_variant(&msg_iter, proplist);
 +    pa_assert_se(dbus_connection_send(c, reply, NULL));
 +    dbus_message_unref(reply);
 +}
 +
 +void pa_dbus_append_basic_array(DBusMessageIter *iter, int item_type, const void *array, unsigned n) {
 +    DBusMessageIter array_iter;
 +    unsigned i;
 +    unsigned item_size;
 +
 +    pa_assert(iter);
 +    pa_assert(dbus_type_is_basic(item_type));
 +    pa_assert(array || n == 0);
 +
 +    item_size = basic_type_size(item_type);
 +
 +    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, signature_from_basic_type(item_type), &array_iter));
 +
 +    for (i = 0; i < n; ++i)
 +        pa_assert_se(dbus_message_iter_append_basic(&array_iter, item_type, &((uint8_t*) array)[i * item_size]));
 +
 +    pa_assert_se(dbus_message_iter_close_container(iter, &array_iter));
 +};
 +
 +void pa_dbus_append_basic_variant(DBusMessageIter *iter, int type, void *data) {
 +    DBusMessageIter variant_iter;
 +
 +    pa_assert(iter);
 +    pa_assert(dbus_type_is_basic(type));
 +    pa_assert(data);
 +
 +    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, signature_from_basic_type(type), &variant_iter));
 +    pa_assert_se(dbus_message_iter_append_basic(&variant_iter, type, data));
 +    pa_assert_se(dbus_message_iter_close_container(iter, &variant_iter));
 +}
 +
 +void pa_dbus_append_basic_array_variant(DBusMessageIter *iter, int item_type, const void *array, unsigned n) {
 +    DBusMessageIter variant_iter;
 +    char *array_signature;
 +
 +    pa_assert(iter);
 +    pa_assert(dbus_type_is_basic(item_type));
 +    pa_assert(array || n == 0);
 +
 +    array_signature = pa_sprintf_malloc("a%c", *signature_from_basic_type(item_type));
 +
 +    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, array_signature, &variant_iter));
 +    pa_dbus_append_basic_array(&variant_iter, item_type, array, n);
 +    pa_assert_se(dbus_message_iter_close_container(iter, &variant_iter));
 +
 +    pa_xfree(array_signature);
 +}
 +
 +void pa_dbus_append_basic_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int type, void *data) {
 +    DBusMessageIter dict_entry_iter;
 +
 +    pa_assert(dict_iter);
 +    pa_assert(key);
 +    pa_assert(dbus_type_is_basic(type));
 +    pa_assert(data);
 +
 +    pa_assert_se(dbus_message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter));
 +    pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key));
 +    pa_dbus_append_basic_variant(&dict_entry_iter, type, data);
 +    pa_assert_se(dbus_message_iter_close_container(dict_iter, &dict_entry_iter));
 +}
 +
 +void pa_dbus_append_basic_array_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int item_type, const void *array, unsigned n) {
 +    DBusMessageIter dict_entry_iter;
 +
 +    pa_assert(dict_iter);
 +    pa_assert(key);
 +    pa_assert(dbus_type_is_basic(item_type));
 +    pa_assert(array || n == 0);
 +
 +    pa_assert_se(dbus_message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter));
 +    pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key));
 +    pa_dbus_append_basic_array_variant(&dict_entry_iter, item_type, array, n);
 +    pa_assert_se(dbus_message_iter_close_container(dict_iter, &dict_entry_iter));
 +}
 +
 +void pa_dbus_append_proplist(DBusMessageIter *iter, pa_proplist *proplist) {
 +    DBusMessageIter dict_iter;
 +    DBusMessageIter dict_entry_iter;
 +    DBusMessageIter array_iter;
 +    void *state = NULL;
 +    const char *key;
 +
 +    pa_assert(iter);
 +    pa_assert(proplist);
 +
 +    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{say}", &dict_iter));
 +
-     while ((key = pa_proplist_iterate(proplist, state))) {
++    while ((key = pa_proplist_iterate(proplist, &state))) {
 +        const void *value = NULL;
 +        size_t nbytes;
 +
 +        pa_assert_se(pa_proplist_get(proplist, key, &value, &nbytes) >= 0);
 +
 +        pa_assert_se(dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter));
 +
 +        pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key));
 +
 +        pa_assert_se(dbus_message_iter_open_container(&dict_entry_iter, DBUS_TYPE_ARRAY, "y", &array_iter));
 +        pa_assert_se(dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_BYTE, &value, nbytes));
 +        pa_assert_se(dbus_message_iter_close_container(&dict_entry_iter, &array_iter));
 +
 +        pa_assert_se(dbus_message_iter_close_container(&dict_iter, &dict_entry_iter));
 +    }
 +
 +    pa_assert_se(dbus_message_iter_close_container(iter, &dict_iter));
 +}
 +
 +void pa_dbus_append_proplist_variant(DBusMessageIter *iter, pa_proplist *proplist) {
 +    DBusMessageIter variant_iter;
 +
 +    pa_assert(iter);
 +    pa_assert(proplist);
 +
 +    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a{say}", &variant_iter));
 +    pa_dbus_append_proplist(&variant_iter, proplist);
 +    pa_assert_se(dbus_message_iter_close_container(iter, &variant_iter));
 +}
 +
 +void pa_dbus_append_proplist_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, pa_proplist *proplist) {
 +    DBusMessageIter dict_entry_iter;
 +
 +    pa_assert(dict_iter);
 +    pa_assert(key);
 +    pa_assert(proplist);
 +
 +    pa_assert_se(dbus_message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter));
 +    pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key));
 +    pa_dbus_append_proplist_variant(&dict_entry_iter, proplist);
 +    pa_assert_se(dbus_message_iter_close_container(dict_iter, &dict_entry_iter));
 +}
 +
 +int pa_dbus_get_basic_set_property_arg(DBusConnection *c, DBusMessage *msg, int type, void *data) {
 +    DBusMessageIter msg_iter;
 +    DBusMessageIter variant_iter;
 +
 +    pa_assert(c);
 +    pa_assert(msg);
 +    pa_assert(dbus_type_is_basic(type));
 +    pa_assert(data);
 +
 +    /* Skip the interface and property name arguments. */
 +    if (!dbus_message_iter_init(msg, &msg_iter) || !dbus_message_iter_next(&msg_iter) || !dbus_message_iter_next(&msg_iter)) {
 +        pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments.");
 +        return -1;
 +    }
 +
 +    if (dbus_message_iter_get_arg_type(&msg_iter) != DBUS_TYPE_VARIANT) {
 +        pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Message argument isn't a variant.");
 +        return -1;
 +    }
 +
 +    dbus_message_iter_recurse(&msg_iter, &variant_iter);
 +
 +    if (pa_dbus_get_basic_arg(c, msg, &variant_iter, type, data) < 0)
 +        return -1;
 +
 +    return 0;
 +}
 +
 +int pa_dbus_get_fixed_array_set_property_arg(DBusConnection *c, DBusMessage *msg, int item_type, void *data, unsigned *n) {
 +    DBusMessageIter msg_iter;
 +    DBusMessageIter variant_iter;
 +
 +    pa_assert(c);
 +    pa_assert(msg);
 +    pa_assert(dbus_type_is_fixed(item_type));
 +    pa_assert(data);
 +    pa_assert(n);
 +
 +    /* Skip the interface and property name arguments. */
 +    if (!dbus_message_iter_init(msg, &msg_iter) || !dbus_message_iter_next(&msg_iter) || !dbus_message_iter_next(&msg_iter)) {
 +        pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments.");
 +        return -1;
 +    }
 +
 +    if (dbus_message_iter_get_arg_type(&msg_iter) != DBUS_TYPE_VARIANT) {
 +        pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Message argument isn't a variant.");
 +        return -1;
 +    }
 +
 +    dbus_message_iter_recurse(&msg_iter, &variant_iter);
 +
 +    if (pa_dbus_get_fixed_array_arg(c, msg, &variant_iter, item_type, data, n) < 0)
 +        return -1;
 +
 +    return 0;
 +}
 +
 +int pa_dbus_get_basic_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int type, void *data) {
 +    int arg_type;
 +
 +    pa_assert(c);
 +    pa_assert(msg);
 +    pa_assert(iter);
 +    pa_assert(dbus_type_is_basic(type));
 +    pa_assert(data);
 +
 +    arg_type = dbus_message_iter_get_arg_type(iter);
 +    if (arg_type != type) {
 +        if (arg_type == DBUS_TYPE_INVALID)
 +            pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. D-Bus type '%c' expected.", (char) type);
 +        else
 +            pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. Expected type '%c'.", (char) arg_type, (char) type);
 +        return -1;
 +    }
 +
 +    dbus_message_iter_get_basic(iter, data);
 +
 +    dbus_message_iter_next(iter);
 +
 +    return 0;
 +}
 +
 +int pa_dbus_get_fixed_array_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int item_type, void *array, unsigned *n) {
 +    DBusMessageIter array_iter;
 +    int signed_n;
 +    int arg_type;
 +    int element_type;
 +
 +    pa_assert(c);
 +    pa_assert(msg);
 +    pa_assert(iter);
 +    pa_assert(dbus_type_is_fixed(item_type));
 +    pa_assert(array);
 +    pa_assert(n);
 +
 +    arg_type = dbus_message_iter_get_arg_type(iter);
 +    if (arg_type != DBUS_TYPE_ARRAY) {
 +        if (arg_type == DBUS_TYPE_INVALID)
 +            pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. An array of type '%c' was expected.", (char) item_type);
 +        else
 +            pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. An array of type '%c' was expected.", (char) arg_type, (char) item_type);
 +        return -1;
 +    }
 +
 +    element_type = dbus_message_iter_get_element_type(iter);
 +    if (element_type != item_type) {
 +        pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type: '%c'. Element type '%c' was expected.", (char) element_type, (char) item_type);
 +        return -1;
 +    }
 +
 +    dbus_message_iter_recurse(iter, &array_iter);
 +
 +    dbus_message_iter_get_fixed_array(&array_iter, array, &signed_n);
 +
 +    dbus_message_iter_next(iter);
 +
 +    pa_assert(signed_n >= 0);
 +
 +    *n = signed_n;
 +
 +    return 0;
 +}
 +
 +pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter) {
 +    DBusMessageIter dict_iter;
 +    DBusMessageIter dict_entry_iter;
 +    int arg_type;
 +    pa_proplist *proplist = NULL;
 +    const char *key;
 +    const uint8_t *value;
 +    int value_length;
 +
 +    pa_assert(c);
 +    pa_assert(msg);
 +    pa_assert(iter);
 +
 +    arg_type = dbus_message_iter_get_arg_type(iter);
 +    if (arg_type != DBUS_TYPE_ARRAY) {
 +        if (arg_type == DBUS_TYPE_INVALID)
 +            pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. An array was expected.");
 +        else
 +            pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. An array was expected.", (char) arg_type);
 +        return NULL;
 +    }
 +
 +    arg_type = dbus_message_iter_get_element_type(iter);
 +    if (arg_type != DBUS_TYPE_DICT_ENTRY) {
 +        pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type: '%c'. A dictionary entry was expected.", (char) arg_type);
 +        return NULL;
 +    }
 +
 +    proplist = pa_proplist_new();
 +
 +    dbus_message_iter_recurse(iter, &dict_iter);
 +
 +    while (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_INVALID) {
 +        dbus_message_iter_recurse(&dict_iter, &dict_entry_iter);
 +
 +        arg_type = dbus_message_iter_get_arg_type(&dict_entry_iter);
 +        if (arg_type != DBUS_TYPE_STRING) {
 +            pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict key type: '%c'. A string was expected.", (char) arg_type);
 +            goto fail;
 +        }
 +
 +        dbus_message_iter_get_basic(&dict_entry_iter, &key);
 +        dbus_message_iter_next(&dict_entry_iter);
 +
 +        if (strlen(key) <= 0 || !pa_ascii_valid(key)) {
 +            pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Invalid property list key.");
 +            goto fail;
 +        }
 +
 +        arg_type = dbus_message_iter_get_arg_type(&dict_entry_iter);
 +        if (arg_type != DBUS_TYPE_ARRAY) {
 +            if (arg_type == DBUS_TYPE_INVALID)
 +                pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Dict value missing.");
 +            else
 +                pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value type: '%c'. An array was expected.", (char) arg_type);
 +            goto fail;
 +        }
 +
 +        arg_type = dbus_message_iter_get_element_type(&dict_entry_iter);
 +        if (arg_type != DBUS_TYPE_BYTE) {
 +            pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value item type: '%c'. A byte was expected.", (char) arg_type);
 +            goto fail;
 +        }
 +
 +        dbus_message_iter_get_fixed_array(&dict_entry_iter, &value, &value_length);
 +
 +        pa_assert(value_length >= 0);
 +
 +        pa_assert_se(pa_proplist_set(proplist, key, value, value_length) >= 0);
 +
 +        dbus_message_iter_next(&dict_iter);
 +    }
 +
 +    dbus_message_iter_next(iter);
 +
 +    return proplist;
 +
 +fail:
 +    if (proplist)
 +        pa_proplist_free(proplist);
 +
 +    return NULL;
 +}
diff --cc src/pulsecore/namereg.c
index 046c87b,e26923d..d7d83c5
--- a/src/pulsecore/namereg.c
+++ b/src/pulsecore/namereg.c
@@@ -166,31 -160,10 +166,38 @@@ void pa_namereg_unregister(pa_core *c, 
  
      pa_assert_se(e = pa_hashmap_remove(c->namereg, name));
  
 -    if (c->default_sink == e->data)
 -        pa_namereg_set_default_sink(c, NULL);
 -    else if (c->default_source == e->data)
 -        pa_namereg_set_default_source(c, NULL);
 +    if (c->default_sink == e->data) {
-         pa_sink *new_default = pa_idxset_first(c->sinks, &idx);
++        pa_sink *new_default = NULL;
 +
-         if (new_default == e->data)
-             new_default = pa_idxset_next(c->sinks, &idx);
++        /* FIXME: the selection here should be based priority values on
++         * the sinks */
++
++        PA_IDXSET_FOREACH(new_default, c->sinks, idx) {
++            if (new_default != e->data && PA_SINK_IS_LINKED(pa_sink_get_state(new_default)))
++                break;
++        }
 +
 +        pa_namereg_set_default_sink(c, new_default);
 +
 +    } else if (c->default_source == e->data) {
-         pa_source *new_default;
++        pa_source *new_default = NULL;
 +
-         for (new_default = pa_idxset_first(c->sources, &idx); new_default; new_default = pa_idxset_next(c->sources, &idx)) {
-             if (new_default != e->data && !new_default->monitor_of)
++        /* First, try to find one that isn't a monitor */
++        PA_IDXSET_FOREACH(new_default, c->sources, idx) {
++            if (new_default != e->data && !new_default->monitor_of && PA_SOURCE_IS_LINKED(pa_source_get_state(new_default)))
 +                break;
 +        }
 +
 +        if (!new_default) {
-             new_default = pa_idxset_first(c->sources, &idx);
- 
-             if (new_default == e->data)
-                 new_default = pa_idxset_next(c->sources, &idx);
++            /* Then, fallback to a monitor */
++            PA_IDXSET_FOREACH(new_default, c->sources, idx) {
++                if (new_default != e->data && PA_SOURCE_IS_LINKED(pa_source_get_state(new_default)))
++                    break;
++            }
 +        }
 +
 +        pa_namereg_set_default_source(c, new_default);
 +    }
  
      pa_xfree(e->name);
      pa_xfree(e);
@@@ -268,16 -248,43 +281,49 @@@ pa_source* pa_namereg_set_default_sourc
      return s;
  }
  
 +/* XXX: After removing old functionality, has this function become useless? */
  pa_sink *pa_namereg_get_default_sink(pa_core *c) {
+     pa_sink *s;
+     uint32_t idx;
+ 
      pa_assert(c);
  
-     return c->default_sink;
 -    if (c->default_sink && PA_SINK_IS_LINKED(pa_sink_get_state(c->default_sink)))
++    if (!c->default_sink || PA_SINK_IS_LINKED(pa_sink_get_state(c->default_sink)))
+         return c->default_sink;
+ 
++    /* The old default sink has become unlinked, set a new one. */
++
+     /* FIXME: the selection here should be based priority values on
+      * the sinks */
+ 
+     PA_IDXSET_FOREACH(s, c->sinks, idx)
+         if (PA_SINK_IS_LINKED(pa_sink_get_state(s)))
+             return pa_namereg_set_default_sink(c, s);
+ 
 -    return NULL;
++    return pa_namereg_set_default_sink(c, NULL);
  }
  
 +/* XXX: After removing old functionality, has this function become useless? */
  pa_source *pa_namereg_get_default_source(pa_core *c) {
+     pa_source *s;
+     uint32_t idx;
+ 
      pa_assert(c);
  
-     return c->default_source;
 -    if (c->default_source && PA_SOURCE_IS_LINKED(pa_source_get_state(c->default_source)))
++    if (!c->default_source || PA_SOURCE_IS_LINKED(pa_source_get_state(c->default_source)))
+         return c->default_source;
+ 
++    /* The old default source has become unlinked, set a new one. */
++
+     /* First, try to find one that isn't a monitor */
+     PA_IDXSET_FOREACH(s, c->sources, idx)
+         if (!s->monitor_of && PA_SOURCE_IS_LINKED(pa_source_get_state(s)))
+             return pa_namereg_set_default_source(c, s);
+ 
+     /* Then, fallback to a monitor */
+     PA_IDXSET_FOREACH(s, c->sources, idx)
+         if (PA_SOURCE_IS_LINKED(pa_source_get_state(s)))
+             return pa_namereg_set_default_source(c, s);
+ 
 -    return NULL;
++    return pa_namereg_set_default_source(c, NULL);
  }

commit f48684e4dbc00d102ed17700fb693726a2676566
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Mon Aug 17 08:26:06 2009 +0300

    namereg: Revert default device handling back to the upstream version.

diff --git a/src/pulsecore/namereg.c b/src/pulsecore/namereg.c
index d7d83c5..e26923d 100644
--- a/src/pulsecore/namereg.c
+++ b/src/pulsecore/namereg.c
@@ -149,55 +149,21 @@ const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t
 
     pa_assert_se(pa_hashmap_put(c->namereg, e->name, e) >= 0);
 
-    if (type == PA_NAMEREG_SINK && !c->default_sink)
-        pa_namereg_set_default_sink(c, data);
-    else if (type == PA_NAMEREG_SOURCE && !c->default_source)
-        pa_namereg_set_default_source(c, data);
-
     return e->name;
 }
 
 void pa_namereg_unregister(pa_core *c, const char *name) {
     struct namereg_entry *e;
-    uint32_t idx;
 
     pa_assert(c);
     pa_assert(name);
 
     pa_assert_se(e = pa_hashmap_remove(c->namereg, name));
 
-    if (c->default_sink == e->data) {
-        pa_sink *new_default = NULL;
-
-        /* FIXME: the selection here should be based priority values on
-         * the sinks */
-
-        PA_IDXSET_FOREACH(new_default, c->sinks, idx) {
-            if (new_default != e->data && PA_SINK_IS_LINKED(pa_sink_get_state(new_default)))
-                break;
-        }
-
-        pa_namereg_set_default_sink(c, new_default);
-
-    } else if (c->default_source == e->data) {
-        pa_source *new_default = NULL;
-
-        /* First, try to find one that isn't a monitor */
-        PA_IDXSET_FOREACH(new_default, c->sources, idx) {
-            if (new_default != e->data && !new_default->monitor_of && PA_SOURCE_IS_LINKED(pa_source_get_state(new_default)))
-                break;
-        }
-
-        if (!new_default) {
-            /* Then, fallback to a monitor */
-            PA_IDXSET_FOREACH(new_default, c->sources, idx) {
-                if (new_default != e->data && PA_SOURCE_IS_LINKED(pa_source_get_state(new_default)))
-                    break;
-            }
-        }
-
-        pa_namereg_set_default_source(c, new_default);
-    }
+    if (c->default_sink == e->data)
+        pa_namereg_set_default_sink(c, NULL);
+    else if (c->default_source == e->data)
+        pa_namereg_set_default_source(c, NULL);
 
     pa_xfree(e->name);
     pa_xfree(e);
@@ -225,6 +191,7 @@ void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type) {
 
         if ((s = pa_namereg_get(c, NULL, PA_NAMEREG_SINK)))
             return s->monitor_source;
+
     }
 
     if (!name)
@@ -281,18 +248,15 @@ pa_source* pa_namereg_set_default_source(pa_core*c, pa_source *s) {
     return s;
 }
 
-/* XXX: After removing old functionality, has this function become useless? */
 pa_sink *pa_namereg_get_default_sink(pa_core *c) {
     pa_sink *s;
     uint32_t idx;
 
     pa_assert(c);
 
-    if (!c->default_sink || PA_SINK_IS_LINKED(pa_sink_get_state(c->default_sink)))
+    if (c->default_sink && PA_SINK_IS_LINKED(pa_sink_get_state(c->default_sink)))
         return c->default_sink;
 
-    /* The old default sink has become unlinked, set a new one. */
-
     /* FIXME: the selection here should be based priority values on
      * the sinks */
 
@@ -300,21 +264,18 @@ pa_sink *pa_namereg_get_default_sink(pa_core *c) {
         if (PA_SINK_IS_LINKED(pa_sink_get_state(s)))
             return pa_namereg_set_default_sink(c, s);
 
-    return pa_namereg_set_default_sink(c, NULL);
+    return NULL;
 }
 
-/* XXX: After removing old functionality, has this function become useless? */
 pa_source *pa_namereg_get_default_source(pa_core *c) {
     pa_source *s;
     uint32_t idx;
 
     pa_assert(c);
 
-    if (!c->default_source || PA_SOURCE_IS_LINKED(pa_source_get_state(c->default_source)))
+    if (c->default_source && PA_SOURCE_IS_LINKED(pa_source_get_state(c->default_source)))
         return c->default_source;
 
-    /* The old default source has become unlinked, set a new one. */
-
     /* First, try to find one that isn't a monitor */
     PA_IDXSET_FOREACH(s, c->sources, idx)
         if (!s->monitor_of && PA_SOURCE_IS_LINKED(pa_source_get_state(s)))
@@ -325,5 +286,5 @@ pa_source *pa_namereg_get_default_source(pa_core *c) {
         if (PA_SOURCE_IS_LINKED(pa_source_get_state(s)))
             return pa_namereg_set_default_source(c, s);
 
-    return pa_namereg_set_default_source(c, NULL);
+    return NULL;
 }

commit a10e8360d72626635de1242cfc2c77207f13d56f
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Mon Aug 17 16:42:06 2009 +0300

    dbusiface-core: New function: pa_dbusiface_core_get_client_path().

diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c
index 2b5cf0b..ec87158 100644
--- a/src/modules/dbus/iface-core.c
+++ b/src/modules/dbus/iface-core.c
@@ -2075,3 +2075,10 @@ const char *pa_dbusiface_core_get_module_path(pa_dbusiface_core *c, const pa_mod
 
     return pa_dbusiface_module_get_path(pa_hashmap_get(c->modules, PA_UINT32_TO_PTR(module->index)));
 }
+
+const char *pa_dbusiface_core_get_client_path(pa_dbusiface_core *c, const pa_client *client) {
+    pa_assert(c);
+    pa_assert(client);
+
+    return pa_dbusiface_client_get_path(pa_hashmap_get(c->clients, PA_UINT32_TO_PTR(client->index)));
+}
diff --git a/src/modules/dbus/iface-core.h b/src/modules/dbus/iface-core.h
index 14dff7e..cf2a3b2 100644
--- a/src/modules/dbus/iface-core.h
+++ b/src/modules/dbus/iface-core.h
@@ -41,5 +41,6 @@ const char *pa_dbusiface_core_get_source_path(pa_dbusiface_core *c, const pa_sou
 const char *pa_dbusiface_core_get_playback_stream_path(pa_dbusiface_core *c, const pa_sink_input *sink_input);
 const char *pa_dbusiface_core_get_record_stream_path(pa_dbusiface_core *c, const pa_source_output *source_output);
 const char *pa_dbusiface_core_get_module_path(pa_dbusiface_core *c, const pa_module *module);
+const char *pa_dbusiface_core_get_client_path(pa_dbusiface_core *c, const pa_client *client);
 
 #endif

commit efec274b6dcf239f580713f889957c370ac7ffc7
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Mon Aug 17 16:42:58 2009 +0300

    dbusiface-core: Two new functions: pa_dbusiface_core_get_sink/source().

diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c
index ec87158..86a8fc7 100644
--- a/src/modules/dbus/iface-core.c
+++ b/src/modules/dbus/iface-core.c
@@ -2082,3 +2082,31 @@ const char *pa_dbusiface_core_get_client_path(pa_dbusiface_core *c, const pa_cli
 
     return pa_dbusiface_client_get_path(pa_hashmap_get(c->clients, PA_UINT32_TO_PTR(client->index)));
 }
+
+pa_sink *pa_dbusiface_core_get_sink(pa_dbusiface_core *c, const char *object_path) {
+    pa_dbusiface_device *device = NULL;
+
+    pa_assert(c);
+    pa_assert(object_path);
+
+    device = pa_hashmap_get(c->sinks_by_path, object_path);
+
+    if (device)
+        return pa_dbusiface_device_get_sink(device);
+    else
+        return NULL;
+}
+
+pa_source *pa_dbusiface_core_get_source(pa_dbusiface_core *c, const char *object_path) {
+    pa_dbusiface_device *device = NULL;
+
+    pa_assert(c);
+    pa_assert(object_path);
+
+    device = pa_hashmap_get(c->sources_by_path, object_path);
+
+    if (device)
+        return pa_dbusiface_device_get_source(device);
+    else
+        return NULL;
+}
diff --git a/src/modules/dbus/iface-core.h b/src/modules/dbus/iface-core.h
index cf2a3b2..900b6d1 100644
--- a/src/modules/dbus/iface-core.h
+++ b/src/modules/dbus/iface-core.h
@@ -43,4 +43,10 @@ const char *pa_dbusiface_core_get_record_stream_path(pa_dbusiface_core *c, const
 const char *pa_dbusiface_core_get_module_path(pa_dbusiface_core *c, const pa_module *module);
 const char *pa_dbusiface_core_get_client_path(pa_dbusiface_core *c, const pa_client *client);
 
+/* Returns NULL if there's no sink with the given path. */
+pa_sink *pa_dbusiface_core_get_sink(pa_dbusiface_core *c, const char *object_path);
+
+/* Returns NULL if there's no source with the given path. */
+pa_source *pa_dbusiface_core_get_source(pa_dbusiface_core *c, const char *object_path);
+
 #endif

commit 150cd1684a1e67d3a3797b34b45256afb0fa7f53
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Mon Aug 17 16:50:29 2009 +0300

    dbusiface-device: Split some overly long lines.

diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c
index 15ece83..486a094 100644
--- a/src/modules/dbus/iface-device.c
+++ b/src/modules/dbus/iface-device.c
@@ -310,9 +310,11 @@ static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void
 
     if (!owner_module) {
         if (d->type == DEVICE_TYPE_SINK)
-            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Sink %s doesn't have an owner module.", d->sink->name);
+            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                               "Sink %s doesn't have an owner module.", d->sink->name);
         else
-            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Source %s doesn't have an owner module.", d->source->name);
+            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                               "Source %s doesn't have an owner module.", d->source->name);
         return;
     }
 
@@ -334,9 +336,11 @@ static void handle_get_card(DBusConnection *conn, DBusMessage *msg, void *userda
 
     if (!card) {
         if (d->type == DEVICE_TYPE_SINK)
-            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Sink %s doesn't belong to any card.", d->sink->name);
+            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                               "Sink %s doesn't belong to any card.", d->sink->name);
         else
-            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Source %s doesn't belong to any card.", d->source->name);
+            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                               "Source %s doesn't belong to any card.", d->source->name);
         return;
     }
 
@@ -426,7 +430,8 @@ static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *user
         return;
 
     if (n_volume_entries != device_channels) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Expected %u volume entries, got %u.", device_channels, n_volume_entries);
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
+                           "Expected %u volume entries, got %u.", device_channels, n_volume_entries);
         return;
     }
 
@@ -598,9 +603,11 @@ static void handle_get_latency(DBusConnection *conn, DBusMessage *msg, void *use
     pa_assert(d);
 
     if (d->type == DEVICE_TYPE_SINK && !(d->sink->flags & PA_SINK_LATENCY))
-        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Sink %s doesn't support latency querying.", d->sink->name);
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                           "Sink %s doesn't support latency querying.", d->sink->name);
     else if (d->type == DEVICE_TYPE_SOURCE && !(d->source->flags & PA_SOURCE_LATENCY))
-        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Source %s doesn't support latency querying.", d->source->name);
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                           "Source %s doesn't support latency querying.", d->source->name);
     return;
 
     latency = (d->type == DEVICE_TYPE_SINK) ? pa_sink_get_latency(d->sink) : pa_source_get_latency(d->source);

commit bce6af18a3cc761375acebe589c821fc2abf9d1e
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Mon Aug 17 16:52:10 2009 +0300

    dbusiface-device: Use a single if-else section instead of ternary operator overuse.

diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c
index 486a094..078ae74 100644
--- a/src/modules/dbus/iface-device.c
+++ b/src/modules/dbus/iface-device.c
@@ -817,48 +817,57 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat
     pa_assert(msg);
     pa_assert(d);
 
-    idx = (d->type == DEVICE_TYPE_SINK) ? d->sink->index : d->source->index;
-    name = (d->type == DEVICE_TYPE_SINK) ? d->sink->name : d->source->name;
-    driver = (d->type == DEVICE_TYPE_SINK) ? d->sink->driver : d->source->driver;
-    owner_module = (d->type == DEVICE_TYPE_SINK) ? d->sink->module : d->source->module;
+    if (d->type == DEVICE_TYPE_SINK) {
+        idx = d->sink->index;
+        name = d->sink->name;
+        driver = d->sink->driver;
+        owner_module = d->sink->module;
+        card = d->sink->card;
+        sample_format = d->sink->sample_spec.format;
+        sample_rate = d->sink->sample_spec.rate;
+        channel_map = &d->sink->channel_map;
+        has_flat_volume = d->sink->flags & PA_SINK_FLAT_VOLUME;
+        has_convertible_to_decibel_volume = d->sink->flags & PA_SINK_DECIBEL_VOLUME;
+        base_volume = d->sink->base_volume;
+        volume_steps = d->sink->n_volume_steps;
+        has_hardware_volume = d->sink->flags & PA_SINK_HW_VOLUME_CTRL;
+        has_hardware_mute = d->sink->flags & PA_SINK_HW_MUTE_CTRL;
+        configured_latency = pa_sink_get_requested_latency(d->sink);
+        has_dynamic_latency = d->sink->flags & PA_SINK_DYNAMIC_LATENCY;
+        latency = pa_sink_get_latency(d->sink);
+        is_hardware_device = d->sink->flags & PA_SINK_HARDWARE;
+        is_network_device = d->sink->flags & PA_SINK_NETWORK;
+        state = pa_sink_get_state(d->sink);
+    } else {
+        idx = d->source->index;
+        name = d->source->name;
+        driver = d->source->driver;
+        owner_module = d->source->module;
+        card = d->source->card;
+        sample_format = d->source->sample_spec.format;
+        sample_rate = d->source->sample_spec.rate;
+        channel_map = &d->source->channel_map;
+        has_flat_volume = FALSE;
+        has_convertible_to_decibel_volume = d->source->flags & PA_SOURCE_DECIBEL_VOLUME;
+        base_volume = d->source->base_volume;
+        volume_steps = d->source->n_volume_steps;
+        has_hardware_volume = d->source->flags & PA_SOURCE_HW_VOLUME_CTRL;
+        has_hardware_mute = d->source->flags & PA_SOURCE_HW_MUTE_CTRL;
+        configured_latency = pa_source_get_requested_latency(d->source);
+        has_dynamic_latency = d->source->flags & PA_SOURCE_DYNAMIC_LATENCY;
+        latency = pa_source_get_latency(d->source);
+        is_hardware_device = d->source->flags & PA_SOURCE_HARDWARE;
+        is_network_device = d->source->flags & PA_SOURCE_NETWORK;
+        state = pa_source_get_state(d->source);
+    }
     if (owner_module)
         owner_module_path = pa_dbusiface_core_get_module_path(d->core, owner_module);
-    card = (d->type == DEVICE_TYPE_SINK) ? d->sink->card : d->source->card;
     if (card)
         card_path = pa_dbusiface_core_get_card_path(d->core, card);
-    sample_format = (d->type == DEVICE_TYPE_SINK) ? d->sink->sample_spec.format : d->source->sample_spec.format;
-    sample_rate = (d->type == DEVICE_TYPE_SINK) ? d->sink->sample_spec.rate : d->source->sample_spec.rate;
-    channel_map = (d->type == DEVICE_TYPE_SINK) ? &d->sink->channel_map : &d->source->channel_map;
     for (i = 0; i < channel_map->channels; ++i)
         channels[i] = channel_map->map[i];
     for (i = 0; i < d->volume.channels; ++i)
         volume[i] = d->volume.values[i];
-    has_flat_volume = (d->type == DEVICE_TYPE_SINK) ? (d->sink->flags & PA_SINK_FLAT_VOLUME) : FALSE;
-    has_convertible_to_decibel_volume = (d->type == DEVICE_TYPE_SINK)
-                                        ? (d->sink->flags & PA_SINK_DECIBEL_VOLUME)
-                                        : (d->source->flags & PA_SOURCE_DECIBEL_VOLUME);
-    base_volume = (d->type == DEVICE_TYPE_SINK) ? d->sink->base_volume : d->source->base_volume;
-    volume_steps = (d->type == DEVICE_TYPE_SINK) ? d->sink->n_volume_steps : d->source->n_volume_steps;
-    has_hardware_volume = (d->type == DEVICE_TYPE_SINK)
-                          ? (d->sink->flags & PA_SINK_HW_VOLUME_CTRL)
-                          : (d->source->flags & PA_SOURCE_HW_VOLUME_CTRL);
-    has_hardware_mute = (d->type == DEVICE_TYPE_SINK)
-                        ? (d->sink->flags & PA_SINK_HW_MUTE_CTRL)
-                        : (d->source->flags & PA_SOURCE_HW_MUTE_CTRL);
-    configured_latency = (d->type == DEVICE_TYPE_SINK)
-                         ? pa_sink_get_requested_latency(d->sink)
-                         : pa_source_get_requested_latency(d->source);
-    has_dynamic_latency = (d->type == DEVICE_TYPE_SINK)
-                          ? (d->sink->flags & PA_SINK_DYNAMIC_LATENCY)
-                          : (d->source->flags & PA_SOURCE_DYNAMIC_LATENCY);
-    latency = (d->type == DEVICE_TYPE_SINK) ? pa_sink_get_latency(d->sink) : pa_source_get_latency(d->source);
-    is_hardware_device = (d->type == DEVICE_TYPE_SINK)
-                         ? (d->sink->flags & PA_SINK_HARDWARE)
-                         : (d->source->flags & PA_SOURCE_HARDWARE);
-    is_network_device = (d->type == DEVICE_TYPE_SINK)
-                        ? (d->sink->flags & PA_SINK_NETWORK)
-                        : (d->source->flags & PA_SOURCE_NETWORK);
-    state = (d->type == DEVICE_TYPE_SINK) ? pa_sink_get_state(d->sink) : pa_source_get_state(d->source);
     ports = get_ports(d, &n_ports);
     if (d->active_port)
         active_port = pa_dbusiface_device_port_get_path(pa_hashmap_get(d->ports, d->active_port->name));

commit b52871517944d55cec549af362398a5012f0b8b8
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Mon Aug 17 16:53:58 2009 +0300

    dbusiface-device: Fix argument reading in handle_suspend().

diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c
index 078ae74..2e5940c 100644
--- a/src/modules/dbus/iface-device.c
+++ b/src/modules/dbus/iface-device.c
@@ -923,13 +923,19 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat
 static void handle_suspend(DBusConnection *conn, DBusMessage *msg, void *userdata) {
     pa_dbusiface_device *d = userdata;
     dbus_bool_t suspend = FALSE;
+    DBusError error;
 
     pa_assert(conn);
     pa_assert(msg);
     pa_assert(d);
 
-    if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_BOOLEAN, &suspend) < 0)
+    dbus_error_init(&error);
+
+    if (!dbus_message_get_args(msg, &error, DBUS_TYPE_BOOLEAN, &suspend, DBUS_TYPE_INVALID)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
+        dbus_error_free(&error);
         return;
+    }
 
     if ((d->type == DEVICE_TYPE_SINK) && (pa_sink_suspend(d->sink, suspend, PA_SUSPEND_USER) < 0)) {
         pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Internal error in PulseAudio: pa_sink_suspend() failed.");

commit 70ff96b8ab100abb4969b882f66518ce739ae655
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Mon Aug 17 16:55:08 2009 +0300

    dbusiface-device: Save one level of identation by returning early.

diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c
index 2e5940c..8dc0b2c 100644
--- a/src/modules/dbus/iface-device.c
+++ b/src/modules/dbus/iface-device.c
@@ -1075,126 +1075,129 @@ static void handle_source_get_all(DBusConnection *conn, DBusMessage *msg, void *
 
 static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
     pa_dbusiface_device *d = userdata;
+    DBusMessage *signal = NULL;
+    const pa_cvolume *new_volume = NULL;
+    pa_bool_t new_muted = FALSE;
+    pa_sink_state_t new_sink_state = 0;
+    pa_source_state_t new_source_state = 0;
+    pa_device_port *new_active_port = NULL;
+    pa_proplist *new_proplist = NULL;
+    unsigned i = 0;
 
     pa_assert(c);
     pa_assert(d);
 
-    if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) {
-        DBusMessage *signal = NULL;
-        const pa_cvolume *new_volume = NULL;
-        pa_bool_t new_muted = FALSE;
-        pa_sink_state_t new_sink_state = 0;
-        pa_source_state_t new_source_state = 0;
-        pa_device_port *new_active_port = NULL;
-        pa_proplist *new_proplist = NULL;
-        unsigned i = 0;
-
-        pa_assert(((d->type == DEVICE_TYPE_SINK)
-                    && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK))
-                  || ((d->type == DEVICE_TYPE_SOURCE)
-                       && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE)));
-
-        new_volume = (d->type == DEVICE_TYPE_SINK)
-                     ? pa_sink_get_volume(d->sink, FALSE, FALSE)
-                     : pa_source_get_volume(d->source, FALSE);
-
-        if (!pa_cvolume_equal(&d->volume, new_volume)) {
-            dbus_uint32_t volume[PA_CHANNELS_MAX];
-            dbus_uint32_t *volume_ptr = volume;
-
-            d->volume = *new_volume;
-
-            for (i = 0; i < d->volume.channels; ++i)
-                volume[i] = d->volume.values[i];
-
-            pa_assert_se(signal = dbus_message_new_signal(d->path,
-                                                          PA_DBUSIFACE_DEVICE_INTERFACE,
-                                                          signals[SIGNAL_VOLUME_UPDATED].name));
-            pa_assert_se(dbus_message_append_args(signal,
-                                                  DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &volume_ptr, d->volume.channels,
-                                                  DBUS_TYPE_INVALID));
-
-            pa_dbus_protocol_send_signal(d->dbus_protocol, signal);
-            dbus_message_unref(signal);
-            signal = NULL;
-        }
+    if ((d->type == DEVICE_TYPE_SINK && idx != d->sink->index) || (d->type == DEVICE_TYPE_SOURCE && idx != d->source->index))
+        return;
 
-        new_muted = (d->type == DEVICE_TYPE_SINK) ? pa_sink_get_mute(d->sink, FALSE) : pa_source_get_mute(d->source, FALSE);
+    if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE)
+        return;
 
-        if (d->is_muted != new_muted) {
-            d->is_muted = new_muted;
+    pa_assert(((d->type == DEVICE_TYPE_SINK)
+                && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK))
+              || ((d->type == DEVICE_TYPE_SOURCE)
+                   && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE)));
 
-            pa_assert_se(signal = dbus_message_new_signal(d->path,
-                                                          PA_DBUSIFACE_DEVICE_INTERFACE,
-                                                          signals[SIGNAL_MUTE_UPDATED].name));
-            pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_BOOLEAN, &d->is_muted, DBUS_TYPE_INVALID));
+    new_volume = (d->type == DEVICE_TYPE_SINK)
+                 ? pa_sink_get_volume(d->sink, FALSE, FALSE)
+                 : pa_source_get_volume(d->source, FALSE);
 
-            pa_dbus_protocol_send_signal(d->dbus_protocol, signal);
-            dbus_message_unref(signal);
-            signal = NULL;
-        }
+    if (!pa_cvolume_equal(&d->volume, new_volume)) {
+        dbus_uint32_t volume[PA_CHANNELS_MAX];
+        dbus_uint32_t *volume_ptr = volume;
 
-        if (d->type == DEVICE_TYPE_SINK)
-            new_sink_state = pa_sink_get_state(d->sink);
-        else
-            new_source_state = pa_source_get_state(d->source);
+        d->volume = *new_volume;
 
-        if ((d->type == DEVICE_TYPE_SINK && d->sink_state != new_sink_state)
-            || (d->type == DEVICE_TYPE_SOURCE && d->source_state != new_source_state)) {
-            dbus_uint32_t state = 0;
+        for (i = 0; i < d->volume.channels; ++i)
+            volume[i] = d->volume.values[i];
 
-            if (d->type == DEVICE_TYPE_SINK)
-                d->sink_state = new_sink_state;
-            else
-                d->source_state = new_source_state;
+        pa_assert_se(signal = dbus_message_new_signal(d->path,
+                                                      PA_DBUSIFACE_DEVICE_INTERFACE,
+                                                      signals[SIGNAL_VOLUME_UPDATED].name));
+        pa_assert_se(dbus_message_append_args(signal,
+                                              DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &volume_ptr, d->volume.channels,
+                                              DBUS_TYPE_INVALID));
 
-            state = (d->type == DEVICE_TYPE_SINK) ? d->sink_state : d->source_state;
+        pa_dbus_protocol_send_signal(d->dbus_protocol, signal);
+        dbus_message_unref(signal);
+        signal = NULL;
+    }
 
-            pa_assert_se(signal = dbus_message_new_signal(d->path,
-                                                          PA_DBUSIFACE_DEVICE_INTERFACE,
-                                                          signals[SIGNAL_STATE_UPDATED].name));
-            pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_UINT32, &state, DBUS_TYPE_INVALID));
+    new_muted = (d->type == DEVICE_TYPE_SINK) ? pa_sink_get_mute(d->sink, FALSE) : pa_source_get_mute(d->source, FALSE);
 
-            pa_dbus_protocol_send_signal(d->dbus_protocol, signal);
-            dbus_message_unref(signal);
-            signal = NULL;
-        }
+    if (d->is_muted != new_muted) {
+        d->is_muted = new_muted;
 
-        new_active_port = (d->type == DEVICE_TYPE_SINK) ? d->sink->active_port : d->source->active_port;
+        pa_assert_se(signal = dbus_message_new_signal(d->path,
+                                                      PA_DBUSIFACE_DEVICE_INTERFACE,
+                                                      signals[SIGNAL_MUTE_UPDATED].name));
+        pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_BOOLEAN, &d->is_muted, DBUS_TYPE_INVALID));
 
-        if (d->active_port != new_active_port) {
-            const char *object_path = NULL;
+        pa_dbus_protocol_send_signal(d->dbus_protocol, signal);
+        dbus_message_unref(signal);
+        signal = NULL;
+    }
 
-            d->active_port = new_active_port;
-            object_path = pa_dbusiface_device_port_get_path(pa_hashmap_get(d->ports, d->active_port->name));
+    if (d->type == DEVICE_TYPE_SINK)
+        new_sink_state = pa_sink_get_state(d->sink);
+    else
+        new_source_state = pa_source_get_state(d->source);
 
-            pa_assert_se(signal = dbus_message_new_signal(d->path,
-                                                          PA_DBUSIFACE_DEVICE_INTERFACE,
-                                                          signals[SIGNAL_ACTIVE_PORT_UPDATED].name));
-            pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+    if ((d->type == DEVICE_TYPE_SINK && d->sink_state != new_sink_state)
+        || (d->type == DEVICE_TYPE_SOURCE && d->source_state != new_source_state)) {
+        dbus_uint32_t state = 0;
 
-            pa_dbus_protocol_send_signal(d->dbus_protocol, signal);
-            dbus_message_unref(signal);
-            signal = NULL;
-        }
+        if (d->type == DEVICE_TYPE_SINK)
+            d->sink_state = new_sink_state;
+        else
+            d->source_state = new_source_state;
 
-        new_proplist = (d->type == DEVICE_TYPE_SINK) ? d->sink->proplist : d->source->proplist;
+        state = (d->type == DEVICE_TYPE_SINK) ? d->sink_state : d->source_state;
 
-        if (!pa_proplist_equal(d->proplist, new_proplist)) {
-            DBusMessageIter msg_iter;
+        pa_assert_se(signal = dbus_message_new_signal(d->path,
+                                                      PA_DBUSIFACE_DEVICE_INTERFACE,
+                                                      signals[SIGNAL_STATE_UPDATED].name));
+        pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_UINT32, &state, DBUS_TYPE_INVALID));
 
-            pa_proplist_update(d->proplist, PA_UPDATE_SET, new_proplist);
+        pa_dbus_protocol_send_signal(d->dbus_protocol, signal);
+        dbus_message_unref(signal);
+        signal = NULL;
+    }
 
-            pa_assert_se(signal = dbus_message_new_signal(d->path,
-                                                          PA_DBUSIFACE_DEVICE_INTERFACE,
-                                                          signals[SIGNAL_PROPERTY_LIST_UPDATED].name));
-            dbus_message_iter_init_append(signal, &msg_iter);
-            pa_dbus_append_proplist(&msg_iter, d->proplist);
+    new_active_port = (d->type == DEVICE_TYPE_SINK) ? d->sink->active_port : d->source->active_port;
 
-            pa_dbus_protocol_send_signal(d->dbus_protocol, signal);
-            dbus_message_unref(signal);
-            signal = NULL;
-        }
+    if (d->active_port != new_active_port) {
+        const char *object_path = NULL;
+
+        d->active_port = new_active_port;
+        object_path = pa_dbusiface_device_port_get_path(pa_hashmap_get(d->ports, d->active_port->name));
+
+        pa_assert_se(signal = dbus_message_new_signal(d->path,
+                                                      PA_DBUSIFACE_DEVICE_INTERFACE,
+                                                      signals[SIGNAL_ACTIVE_PORT_UPDATED].name));
+        pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+
+        pa_dbus_protocol_send_signal(d->dbus_protocol, signal);
+        dbus_message_unref(signal);
+        signal = NULL;
+    }
+
+    new_proplist = (d->type == DEVICE_TYPE_SINK) ? d->sink->proplist : d->source->proplist;
+
+    if (!pa_proplist_equal(d->proplist, new_proplist)) {
+        DBusMessageIter msg_iter;
+
+        pa_proplist_update(d->proplist, PA_UPDATE_SET, new_proplist);
+
+        pa_assert_se(signal = dbus_message_new_signal(d->path,
+                                                      PA_DBUSIFACE_DEVICE_INTERFACE,
+                                                      signals[SIGNAL_PROPERTY_LIST_UPDATED].name));
+        dbus_message_iter_init_append(signal, &msg_iter);
+        pa_dbus_append_proplist(&msg_iter, d->proplist);
+
+        pa_dbus_protocol_send_signal(d->dbus_protocol, signal);
+        dbus_message_unref(signal);
+        signal = NULL;
     }
 }
 

commit 36dc61a2bff7ee93ca289c33c24b76cb82068cac
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Mon Aug 17 16:56:12 2009 +0300

    dbusiface-stream: Finish the Stream D-Bus interface.

diff --git a/src/modules/dbus/iface-stream.c b/src/modules/dbus/iface-stream.c
index 4333c16..354ca6e 100644
--- a/src/modules/dbus/iface-stream.c
+++ b/src/modules/dbus/iface-stream.c
@@ -39,78 +39,86 @@ enum stream_type {
 };
 
 struct pa_dbusiface_stream {
+    pa_dbusiface_core *core;
+
     union {
         pa_sink_input *sink_input;
         pa_source_output *source_output;
     };
     enum stream_type type;
     char *path;
+    union {
+        pa_sink *sink;
+        pa_source *source;
+    };
+    uint32_t sample_rate;
     pa_cvolume volume;
     pa_bool_t is_muted;
     pa_proplist *proplist;
 
     pa_dbus_protocol *dbus_protocol;
     pa_subscription *subscription;
+    pa_hook_slot *send_event_slot;
 };
 
 static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata);
-/*static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_client(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_device(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata);
-static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata);*/
+static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata);
-/*static void handle_get_buffer_latency(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_buffer_latency(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_device_latency(DBusConnection *conn, DBusMessage *msg, void *userdata);
-static void handle_get_resample_method(DBusConnection *conn, DBusMessage *msg, void *userdata);*/
+static void handle_get_resample_method(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata);
 
 static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
 
-/*static void handle_move(DBusConnection *conn, DBusMessage *msg, void *userdata);
-static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata);*/
+static void handle_move(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata);
 
 enum property_handler_index {
     PROPERTY_HANDLER_INDEX,
-/*    PROPERTY_HANDLER_DRIVER,
+    PROPERTY_HANDLER_DRIVER,
     PROPERTY_HANDLER_OWNER_MODULE,
     PROPERTY_HANDLER_CLIENT,
     PROPERTY_HANDLER_DEVICE,
     PROPERTY_HANDLER_SAMPLE_FORMAT,
     PROPERTY_HANDLER_SAMPLE_RATE,
-    PROPERTY_HANDLER_CHANNELS,*/
+    PROPERTY_HANDLER_CHANNELS,
     PROPERTY_HANDLER_VOLUME,
     PROPERTY_HANDLER_IS_MUTED,
-/*    PROPERTY_HANDLER_BUFFER_LATENCY,
+    PROPERTY_HANDLER_BUFFER_LATENCY,
     PROPERTY_HANDLER_DEVICE_LATENCY,
-    PROPERTY_HANDLER_RESAMPLE_METHOD,*/
+    PROPERTY_HANDLER_RESAMPLE_METHOD,
     PROPERTY_HANDLER_PROPERTY_LIST,
     PROPERTY_HANDLER_MAX
 };
 
 static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
     [PROPERTY_HANDLER_INDEX]           = { .property_name = "Index",          .type = "u",      .get_cb = handle_get_index,           .set_cb = NULL },
-/*    [PROPERTY_HANDLER_DRIVER]          = { .property_name = "Driver",         .type = "s",      .get_cb = handle_get_driver,          .set_cb = NULL },
+    [PROPERTY_HANDLER_DRIVER]          = { .property_name = "Driver",         .type = "s",      .get_cb = handle_get_driver,          .set_cb = NULL },
     [PROPERTY_HANDLER_OWNER_MODULE]    = { .property_name = "OwnerModule",    .type = "o",      .get_cb = handle_get_owner_module,    .set_cb = NULL },
     [PROPERTY_HANDLER_CLIENT]          = { .property_name = "Client",         .type = "o",      .get_cb = handle_get_client,          .set_cb = NULL },
     [PROPERTY_HANDLER_DEVICE]          = { .property_name = "Device",         .type = "o",      .get_cb = handle_get_device,          .set_cb = NULL },
     [PROPERTY_HANDLER_SAMPLE_FORMAT]   = { .property_name = "SampleFormat",   .type = "u",      .get_cb = handle_get_sample_format,   .set_cb = NULL },
     [PROPERTY_HANDLER_SAMPLE_RATE]     = { .property_name = "SampleRate",     .type = "u",      .get_cb = handle_get_sample_rate,     .set_cb = NULL },
-    [PROPERTY_HANDLER_CHANNELS]        = { .property_name = "Channels",       .type = "au",     .get_cb = handle_get_channels,        .set_cb = NULL },*/
+    [PROPERTY_HANDLER_CHANNELS]        = { .property_name = "Channels",       .type = "au",     .get_cb = handle_get_channels,        .set_cb = NULL },
     [PROPERTY_HANDLER_VOLUME]          = { .property_name = "Volume",         .type = "au",     .get_cb = handle_get_volume,          .set_cb = handle_set_volume },
     [PROPERTY_HANDLER_IS_MUTED]        = { .property_name = "IsMuted",        .type = "b",      .get_cb = handle_get_is_muted,        .set_cb = handle_set_is_muted },
-/*    [PROPERTY_HANDLER_BUFFER_LATENCY]  = { .property_name = "BufferLatency",  .type = "t",      .get_cb = handle_get_buffer_latency,  .set_cb = NULL },
+    [PROPERTY_HANDLER_BUFFER_LATENCY]  = { .property_name = "BufferLatency",  .type = "t",      .get_cb = handle_get_buffer_latency,  .set_cb = NULL },
     [PROPERTY_HANDLER_DEVICE_LATENCY]  = { .property_name = "DeviceLatency",  .type = "t",      .get_cb = handle_get_device_latency,  .set_cb = NULL },
-    [PROPERTY_HANDLER_RESAMPLE_METHOD] = { .property_name = "ResampleMethod", .type = "s",      .get_cb = handle_get_resample_method, .set_cb = NULL },*/
+    [PROPERTY_HANDLER_RESAMPLE_METHOD] = { .property_name = "ResampleMethod", .type = "s",      .get_cb = handle_get_resample_method, .set_cb = NULL },
     [PROPERTY_HANDLER_PROPERTY_LIST]   = { .property_name = "PropertyList",   .type = "a{say}", .get_cb = handle_get_property_list,   .set_cb = NULL }
 };
 
-/*enum method_handler_index {
+enum method_handler_index {
     METHOD_HANDLER_MOVE,
     METHOD_HANDLER_KILL,
     METHOD_HANDLER_MAX
@@ -129,38 +137,38 @@ static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
         .arguments = NULL,
         .n_arguments = 0,
         .receive_cb = handle_kill }
-};*/
+};
 
 enum signal_index {
-/*    SIGNAL_DEVICE_UPDATED,
-    SIGNAL_SAMPLE_RATE_UPDATED,*/
+    SIGNAL_DEVICE_UPDATED,
+    SIGNAL_SAMPLE_RATE_UPDATED,
     SIGNAL_VOLUME_UPDATED,
     SIGNAL_MUTE_UPDATED,
     SIGNAL_PROPERTY_LIST_UPDATED,
-/*    SIGNAL_STREAM_EVENT,*/
+    SIGNAL_STREAM_EVENT,
     SIGNAL_MAX
 };
 
-/*static pa_dbus_arg_info device_updated_args[]        = { { "device",        "o",      NULL } };
-static pa_dbus_arg_info sample_rate_updated_args[]   = { { "sample_rate",   "u",      NULL } };*/
+static pa_dbus_arg_info device_updated_args[]        = { { "device",        "o",      NULL } };
+static pa_dbus_arg_info sample_rate_updated_args[]   = { { "sample_rate",   "u",      NULL } };
 static pa_dbus_arg_info volume_updated_args[]        = { { "volume",        "au",     NULL } };
 static pa_dbus_arg_info mute_updated_args[]          = { { "muted",         "b",      NULL } };
 static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } };
-/*static pa_dbus_arg_info stream_event_args[]          = { { "name",          "s",      NULL }, { "property_list", "a{say}", NULL } };*/
+static pa_dbus_arg_info stream_event_args[]          = { { "name",          "s",      NULL }, { "property_list", "a{say}", NULL } };
 
 static pa_dbus_signal_info signals[SIGNAL_MAX] = {
-/*    [SIGNAL_DEVICE_UPDATED]        = { .name = "DeviceUpdated",       .arguments = device_updated_args,        .n_arguments = 1 },
-    [SIGNAL_SAMPLE_RATE_UPDATED]   = { .name = "SampleRateUpdated",   .arguments = sample_rate_updated_args,   .n_arguments = 1 },*/
+    [SIGNAL_DEVICE_UPDATED]        = { .name = "DeviceUpdated",       .arguments = device_updated_args,        .n_arguments = 1 },
+    [SIGNAL_SAMPLE_RATE_UPDATED]   = { .name = "SampleRateUpdated",   .arguments = sample_rate_updated_args,   .n_arguments = 1 },
     [SIGNAL_VOLUME_UPDATED]        = { .name = "VolumeUpdated",       .arguments = volume_updated_args,        .n_arguments = 1 },
     [SIGNAL_MUTE_UPDATED]          = { .name = "MuteUpdated",         .arguments = mute_updated_args,          .n_arguments = 1 },
-    [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 }/*,
-    [SIGNAL_STREAM_EVENT]          = { .name = "StreamEvent",         .arguments = stream_event_args,          .n_arguments = sizeof(stream_event_args) / sizeof(pa_dbus_arg_info) }*/
+    [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 },
+    [SIGNAL_STREAM_EVENT]          = { .name = "StreamEvent",         .arguments = stream_event_args,          .n_arguments = sizeof(stream_event_args) / sizeof(pa_dbus_arg_info) }
 };
 
 static pa_dbus_interface_info stream_interface_info = {
     .name = PA_DBUSIFACE_STREAM_INTERFACE,
-    .method_handlers = /*method_handlers*/ NULL,
-    .n_method_handlers = /*METHOD_HANDLER_MAX*/ 0,
+    .method_handlers = method_handlers,
+    .n_method_handlers = METHOD_HANDLER_MAX,
     .property_handlers = property_handlers,
     .n_property_handlers = PROPERTY_HANDLER_MAX,
     .get_all_properties_cb = handle_get_all,
@@ -181,6 +189,140 @@ static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userd
     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx);
 }
 
+static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+    const char *driver = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    driver = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->driver : s->source_output->driver;
+
+    if (!driver) {
+        if (s->type == STREAM_TYPE_PLAYBACK)
+            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                               "Playback stream %u doesn't have a driver.", s->sink_input->index);
+        else
+            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                               "Record stream %u doesn't have a driver.", s->source_output->index);
+        return;
+    }
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &driver);
+}
+
+static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+    pa_module *owner_module = NULL;
+    const char *object_path = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    owner_module = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->module : s->source_output->module;
+
+    if (!owner_module) {
+        if (s->type == STREAM_TYPE_PLAYBACK)
+            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                               "Playback stream %u doesn't have an owner module.", s->sink_input->index);
+        else
+            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                               "Record stream %u doesn't have an owner module.", s->source_output->index);
+        return;
+    }
+
+    object_path = pa_dbusiface_core_get_module_path(s->core, owner_module);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
+}
+
+static void handle_get_client(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+    pa_client *client = NULL;
+    const char *object_path = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    client = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->client : s->source_output->client;
+
+    if (!client) {
+        if (s->type == STREAM_TYPE_PLAYBACK)
+            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                               "Playback stream %u isn't associated to any client.", s->sink_input->index);
+        else
+            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                               "Record stream %u isn't associated to any client.", s->source_output->index);
+        return;
+    }
+
+    object_path = pa_dbusiface_core_get_client_path(s->core, client);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
+}
+
+static void handle_get_device(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+    const char *device = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    if (s->type == STREAM_TYPE_PLAYBACK)
+        device = pa_dbusiface_core_get_sink_path(s->core, s->sink);
+    else
+        device = pa_dbusiface_core_get_source_path(s->core, s->source);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &device);
+}
+
+static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+    dbus_uint32_t sample_format = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    sample_format = (s->type == STREAM_TYPE_PLAYBACK)
+                    ? s->sink_input->sample_spec.format
+                    : s->source_output->sample_spec.format;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sample_format);
+}
+
+static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &s->sample_rate);
+}
+
+static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+    pa_channel_map *channel_map = NULL;
+    dbus_uint32_t channels[PA_CHANNELS_MAX];
+    unsigned i = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    channel_map = (s->type == STREAM_TYPE_PLAYBACK) ? &s->sink_input->channel_map : &s->source_output->channel_map;
+
+    for (i = 0; i < channel_map->channels; ++i)
+        channels[i] = channel_map->map[i];
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, channels, channel_map->channels);
+}
+
 static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) {
     pa_dbusiface_stream *s = userdata;
     dbus_uint32_t volume[PA_CHANNELS_MAX];
@@ -228,7 +370,8 @@ static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *user
         return;
 
     if (n_volume_entries != stream_channels) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Expected %u volume entries, got %u.", stream_channels, n_volume_entries);
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
+                           "Expected %u volume entries, got %u.", stream_channels, n_volume_entries);
         return;
     }
 
@@ -281,6 +424,54 @@ static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *us
     pa_dbus_send_empty_reply(conn, msg);
 };
 
+static void handle_get_buffer_latency(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+    dbus_uint64_t buffer_latency = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    if (s->type == STREAM_TYPE_PLAYBACK)
+        buffer_latency = pa_sink_input_get_latency(s->sink_input, NULL);
+    else
+        buffer_latency = pa_source_output_get_latency(s->source_output, NULL);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT64, &buffer_latency);
+}
+
+static void handle_get_device_latency(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+    dbus_uint64_t device_latency = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    if (s->type == STREAM_TYPE_PLAYBACK)
+        pa_sink_input_get_latency(s->sink_input, &device_latency);
+    else
+        pa_source_output_get_latency(s->source_output, &device_latency);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT64, &device_latency);
+}
+
+static void handle_get_resample_method(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+    const char *resample_method = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    if (s->type == STREAM_TYPE_PLAYBACK)
+        resample_method = pa_resample_method_to_string(s->sink_input->actual_resample_method);
+    else
+        resample_method = pa_resample_method_to_string(s->source_output->actual_resample_method);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &resample_method);
+}
+
 static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) {
     pa_dbusiface_stream *s = userdata;
 
@@ -296,19 +487,55 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat
     DBusMessage *reply = NULL;
     DBusMessageIter msg_iter;
     DBusMessageIter dict_iter;
-    dbus_uint32_t idx;
+    dbus_uint32_t idx = 0;
+    const char *driver = NULL;
+    pa_module *owner_module = NULL;
+    const char *owner_module_path = NULL;
+    pa_client *client = NULL;
+    const char *client_path = NULL;
+    const char *device = NULL;
+    dbus_uint32_t sample_format = 0;
+    pa_channel_map *channel_map = NULL;
+    dbus_uint32_t channels[PA_CHANNELS_MAX];
     dbus_uint32_t volume[PA_CHANNELS_MAX];
+    dbus_uint64_t buffer_latency = 0;
+    dbus_uint64_t device_latency = 0;
+    const char *resample_method = NULL;
     unsigned i = 0;
 
     pa_assert(conn);
     pa_assert(msg);
     pa_assert(s);
 
-    idx = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->index : s->source_output->index;
     if (s->type == STREAM_TYPE_PLAYBACK) {
+        idx = s->sink_input->index;
+        driver = s->sink_input->driver;
+        owner_module = s->sink_input->module;
+        client = s->sink_input->client;
+        device = pa_dbusiface_core_get_sink_path(s->core, s->sink);
+        sample_format = s->sink_input->sample_spec.format;
+        channel_map = &s->sink_input->channel_map;
         for (i = 0; i < s->volume.channels; ++i)
             volume[i] = s->volume.values[i];
+        buffer_latency = pa_sink_input_get_latency(s->sink_input, &device_latency);
+        resample_method = pa_resample_method_to_string(s->sink_input->actual_resample_method);
+    } else {
+        idx = s->source_output->index;
+        driver = s->source_output->driver;
+        owner_module = s->source_output->module;
+        client = s->source_output->client;
+        device = pa_dbusiface_core_get_source_path(s->core, s->source);
+        sample_format = s->source_output->sample_spec.format;
+        channel_map = &s->source_output->channel_map;
+        buffer_latency = pa_source_output_get_latency(s->source_output, &device_latency);
+        resample_method = pa_resample_method_to_string(s->source_output->actual_resample_method);
     }
+    if (owner_module)
+        owner_module_path = pa_dbusiface_core_get_module_path(s->core, owner_module);
+    if (client)
+        client_path = pa_dbusiface_core_get_client_path(s->core, client);
+    for (i = 0; i < channel_map->channels; ++i)
+        channels[i] = channel_map->map[i];
 
     pa_assert_se((reply = dbus_message_new_method_return(msg)));
 
@@ -317,11 +544,27 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat
 
     pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx);
 
+    if (driver)
+        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DRIVER].property_name, DBUS_TYPE_STRING, &driver);
+
+    if (owner_module)
+        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_OWNER_MODULE].property_name, DBUS_TYPE_OBJECT_PATH, &owner_module_path);
+
+    if (client)
+        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CLIENT].property_name, DBUS_TYPE_OBJECT_PATH, &client_path);
+
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_FORMAT].property_name, DBUS_TYPE_UINT32, &sample_format);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_RATE].property_name, DBUS_TYPE_UINT32, &s->sample_rate);
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CHANNELS].property_name, DBUS_TYPE_UINT32, channels, channel_map->channels);
+
     if (s->type == STREAM_TYPE_PLAYBACK) {
         pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_VOLUME].property_name, DBUS_TYPE_UINT32, volume, s->volume.channels);
         pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_IS_MUTED].property_name, DBUS_TYPE_BOOLEAN, &s->is_muted);
     }
 
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_BUFFER_LATENCY].property_name, DBUS_TYPE_UINT64, &buffer_latency);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DEVICE_LATENCY].property_name, DBUS_TYPE_UINT64, &device_latency);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_RESAMPLE_METHOD].property_name, DBUS_TYPE_STRING, &resample_method);
     pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, s->proplist);
 
     pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
@@ -329,83 +572,240 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat
     dbus_message_unref(reply);
 }
 
+static void handle_move(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+    const char *device = NULL;
+    DBusError error;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    dbus_error_init(&error);
+
+    if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &device, DBUS_TYPE_INVALID)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
+        dbus_error_free(&error);
+        return;
+    }
+
+    if (s->type == STREAM_TYPE_PLAYBACK) {
+        pa_sink *sink = pa_dbusiface_core_get_sink(s->core, device);
+
+        if (!sink) {
+            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such sink.", device);
+            return;
+        }
+
+        if (pa_sink_input_move_to(s->sink_input, sink, TRUE) < 0) {
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED,
+                               "Moving playback stream %u to sink %s failed.", s->sink_input->index, sink->name);
+            return;
+        }
+    } else {
+        pa_source *source = pa_dbusiface_core_get_source(s->core, device);
+
+        if (!source) {
+            pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such source.", device);
+            return;
+        }
+
+        if (pa_source_output_move_to(s->source_output, source, TRUE) < 0) {
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED,
+                               "Moving record stream %u to source %s failed.", s->source_output->index, source->name);
+            return;
+        }
+    }
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_stream *s = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    if (s->type == STREAM_TYPE_PLAYBACK)
+        pa_sink_input_kill(s->sink_input);
+    else
+        pa_source_output_kill(s->source_output);
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
 static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
     pa_dbusiface_stream *s = userdata;
+    DBusMessage *signal = NULL;
+    const char *new_device_path = NULL;
+    uint32_t new_sample_rate = 0;
+    pa_proplist *new_proplist = NULL;
+    unsigned i = 0;
 
     pa_assert(c);
     pa_assert(s);
 
-    if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) {
-        DBusMessage *signal = NULL;
-        pa_proplist *new_proplist = NULL;
-        unsigned i = 0;
-
-        pa_assert(((s->type == STREAM_TYPE_PLAYBACK)
-                    && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT))
-                  || ((s->type == STREAM_TYPE_RECORD)
-                       && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT)));
+    if ((s->type == STREAM_TYPE_PLAYBACK && idx != s->sink_input->index)
+        || (s->type == STREAM_TYPE_RECORD && idx != s->source_output->index))
+        return;
 
-        if (s->type == STREAM_TYPE_PLAYBACK) {
-            pa_cvolume new_volume;
-            pa_bool_t new_muted = FALSE;
+    if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE)
+        return;
 
-            pa_sink_input_get_volume(s->sink_input, &new_volume, TRUE);
+    pa_assert(((s->type == STREAM_TYPE_PLAYBACK)
+                && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT))
+              || ((s->type == STREAM_TYPE_RECORD)
+                   && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT)));
 
-            if (!pa_cvolume_equal(&s->volume, &new_volume)) {
-                dbus_uint32_t volume[PA_CHANNELS_MAX];
-                dbus_uint32_t *volume_ptr = volume;
+    if (s->type == STREAM_TYPE_PLAYBACK) {
+        pa_sink *new_sink = s->sink_input->sink;
 
-                s->volume = new_volume;
+        if (s->sink != new_sink) {
+            pa_sink_unref(s->sink);
+            s->sink = pa_sink_ref(new_sink);
 
-                for (i = 0; i < s->volume.channels; ++i)
-                    volume[i] = s->volume.values[i];
+            new_device_path = pa_dbusiface_core_get_sink_path(s->core, new_sink);
 
-                pa_assert_se(signal = dbus_message_new_signal(s->path,
-                                                              PA_DBUSIFACE_STREAM_INTERFACE,
-                                                              signals[SIGNAL_VOLUME_UPDATED].name));
-                pa_assert_se(dbus_message_append_args(signal,
-                                                      DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &volume_ptr, s->volume.channels,
-                                                      DBUS_TYPE_INVALID));
+            pa_assert_se(signal = dbus_message_new_signal(s->path,
+                                                          PA_DBUSIFACE_STREAM_INTERFACE,
+                                                          signals[SIGNAL_DEVICE_UPDATED].name));
+            pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &new_device_path, DBUS_TYPE_INVALID));
 
-                pa_dbus_protocol_send_signal(s->dbus_protocol, signal);
-                dbus_message_unref(signal);
-                signal = NULL;
-            }
+            pa_dbus_protocol_send_signal(s->dbus_protocol, signal);
+            dbus_message_unref(signal);
+            signal = NULL;
+        }
+    } else {
+        pa_source *new_source = s->source_output->source;
 
-            new_muted = pa_sink_input_get_mute(s->sink_input);
+        if (s->source != new_source) {
+            pa_source_unref(s->source);
+            s->source = pa_source_ref(new_source);
 
-            if (s->is_muted != new_muted) {
-                s->is_muted = new_muted;
+            new_device_path = pa_dbusiface_core_get_source_path(s->core, new_source);
 
-                pa_assert_se(signal = dbus_message_new_signal(s->path,
-                                                              PA_DBUSIFACE_STREAM_INTERFACE,
-                                                              signals[SIGNAL_MUTE_UPDATED].name));
-                pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_BOOLEAN, &s->is_muted, DBUS_TYPE_INVALID));
+            pa_assert_se(signal = dbus_message_new_signal(s->path,
+                                                          PA_DBUSIFACE_STREAM_INTERFACE,
+                                                          signals[SIGNAL_DEVICE_UPDATED].name));
+            pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &new_device_path, DBUS_TYPE_INVALID));
 
-                pa_dbus_protocol_send_signal(s->dbus_protocol, signal);
-                dbus_message_unref(signal);
-                signal = NULL;
-            }
+            pa_dbus_protocol_send_signal(s->dbus_protocol, signal);
+            dbus_message_unref(signal);
+            signal = NULL;
         }
+    }
+
+    new_sample_rate = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->sample_spec.rate : s->source_output->sample_spec.rate;
+
+    if (s->sample_rate != new_sample_rate) {
+        s->sample_rate = new_sample_rate;
+
+        pa_assert_se(signal = dbus_message_new_signal(s->path,
+                                                      PA_DBUSIFACE_STREAM_INTERFACE,
+                                                      signals[SIGNAL_SAMPLE_RATE_UPDATED].name));
+        pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_UINT32, &s->sample_rate, DBUS_TYPE_INVALID));
+
+        pa_dbus_protocol_send_signal(s->dbus_protocol, signal);
+        dbus_message_unref(signal);
+        signal = NULL;
+    }
+
+    if (s->type == STREAM_TYPE_PLAYBACK) {
+        pa_cvolume new_volume;
+        pa_bool_t new_muted = FALSE;
+
+        pa_sink_input_get_volume(s->sink_input, &new_volume, TRUE);
+
+        if (!pa_cvolume_equal(&s->volume, &new_volume)) {
+            dbus_uint32_t volume[PA_CHANNELS_MAX];
+            dbus_uint32_t *volume_ptr = volume;
+
+            s->volume = new_volume;
+
+            for (i = 0; i < s->volume.channels; ++i)
+                volume[i] = s->volume.values[i];
+
+            pa_assert_se(signal = dbus_message_new_signal(s->path,
+                                                          PA_DBUSIFACE_STREAM_INTERFACE,
+                                                          signals[SIGNAL_VOLUME_UPDATED].name));
+            pa_assert_se(dbus_message_append_args(signal,
+                                                  DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &volume_ptr, s->volume.channels,
+                                                  DBUS_TYPE_INVALID));
 
-        new_proplist = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->proplist : s->source_output->proplist;
+            pa_dbus_protocol_send_signal(s->dbus_protocol, signal);
+            dbus_message_unref(signal);
+            signal = NULL;
+        }
 
-        if (!pa_proplist_equal(s->proplist, new_proplist)) {
-            DBusMessageIter msg_iter;
+        new_muted = pa_sink_input_get_mute(s->sink_input);
 
-            pa_proplist_update(s->proplist, PA_UPDATE_SET, new_proplist);
+        if (s->is_muted != new_muted) {
+            s->is_muted = new_muted;
 
             pa_assert_se(signal = dbus_message_new_signal(s->path,
                                                           PA_DBUSIFACE_STREAM_INTERFACE,
-                                                          signals[SIGNAL_PROPERTY_LIST_UPDATED].name));
-            dbus_message_iter_init_append(signal, &msg_iter);
-            pa_dbus_append_proplist(&msg_iter, s->proplist);
+                                                          signals[SIGNAL_MUTE_UPDATED].name));
+            pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_BOOLEAN, &s->is_muted, DBUS_TYPE_INVALID));
 
             pa_dbus_protocol_send_signal(s->dbus_protocol, signal);
             dbus_message_unref(signal);
             signal = NULL;
         }
     }
+
+    new_proplist = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->proplist : s->source_output->proplist;
+
+    if (!pa_proplist_equal(s->proplist, new_proplist)) {
+        DBusMessageIter msg_iter;
+
+        pa_proplist_update(s->proplist, PA_UPDATE_SET, new_proplist);
+
+        pa_assert_se(signal = dbus_message_new_signal(s->path,
+                                                      PA_DBUSIFACE_STREAM_INTERFACE,
+                                                      signals[SIGNAL_PROPERTY_LIST_UPDATED].name));
+        dbus_message_iter_init_append(signal, &msg_iter);
+        pa_dbus_append_proplist(&msg_iter, s->proplist);
+
+        pa_dbus_protocol_send_signal(s->dbus_protocol, signal);
+        dbus_message_unref(signal);
+        signal = NULL;
+    }
+}
+
+static pa_hook_result_t send_event_cb(void *hook_data, void *call_data, void *slot_data) {
+    pa_dbusiface_stream *s = slot_data;
+    DBusMessage *signal = NULL;
+    DBusMessageIter msg_iter;
+    const char *name = NULL;
+    pa_proplist *property_list = NULL;
+
+    pa_assert(call_data);
+    pa_assert(s);
+
+    if (s->type == STREAM_TYPE_PLAYBACK) {
+        pa_sink_input_send_event_hook_data *data = call_data;
+
+        name = data->event;
+        property_list = data->data;
+    } else {
+        pa_source_output_send_event_hook_data *data = call_data;
+
+        name = data->event;
+        property_list = data->data;
+    }
+
+    pa_assert_se(signal = dbus_message_new_signal(s->path,
+                                                  PA_DBUSIFACE_STREAM_INTERFACE,
+                                                  signals[SIGNAL_STREAM_EVENT].name));
+    dbus_message_iter_init_append(signal, &msg_iter);
+    pa_assert_se(dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_STRING, &name));
+    pa_dbus_append_proplist(&msg_iter, property_list);
+
+    pa_dbus_protocol_send_signal(s->dbus_protocol, signal);
+    dbus_message_unref(signal);
+
+    return PA_HOOK_OK;
 }
 
 pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, pa_sink_input *sink_input) {
@@ -415,14 +815,21 @@ pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, p
     pa_assert(sink_input);
 
     s = pa_xnew(pa_dbusiface_stream, 1);
+    s->core = core;
     s->sink_input = pa_sink_input_ref(sink_input);
     s->type = STREAM_TYPE_PLAYBACK;
     s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, PLAYBACK_OBJECT_NAME, sink_input->index);
+    s->sink = pa_sink_ref(sink_input->sink);
+    s->sample_rate = sink_input->sample_spec.rate;
     pa_sink_input_get_volume(sink_input, &s->volume, TRUE);
     s->is_muted = pa_sink_input_get_mute(sink_input);
     s->proplist = pa_proplist_copy(sink_input->proplist);
     s->dbus_protocol = pa_dbus_protocol_get(sink_input->core);
     s->subscription = pa_subscription_new(sink_input->core, PA_SUBSCRIPTION_MASK_SINK_INPUT, subscription_cb, s);
+    s->send_event_slot = pa_hook_connect(&sink_input->core->hooks[PA_CORE_HOOK_SINK_INPUT_SEND_EVENT],
+                                         PA_HOOK_NORMAL,
+                                         send_event_cb,
+                                         s);
 
     pa_assert_se(pa_dbus_protocol_add_interface(s->dbus_protocol, s->path, &stream_interface_info, s) >= 0);
 
@@ -436,14 +843,21 @@ pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_dbusiface_core *core, pa_
     pa_assert(source_output);
 
     s = pa_xnew(pa_dbusiface_stream, 1);
+    s->core = core;
     s->source_output = pa_source_output_ref(source_output);
     s->type = STREAM_TYPE_RECORD;
     s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, RECORD_OBJECT_NAME, source_output->index);
+    s->source = pa_source_ref(source_output->source);
+    s->sample_rate = source_output->sample_spec.rate;
     pa_cvolume_init(&s->volume);
     s->is_muted = FALSE;
     s->proplist = pa_proplist_copy(source_output->proplist);
     s->dbus_protocol = pa_dbus_protocol_get(source_output->core);
     s->subscription = pa_subscription_new(source_output->core, PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscription_cb, s);
+    s->send_event_slot = pa_hook_connect(&source_output->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_SEND_EVENT],
+                                         PA_HOOK_NORMAL,
+                                         send_event_cb,
+                                         s);
 
     pa_assert_se(pa_dbus_protocol_add_interface(s->dbus_protocol, s->path, &stream_interface_info, s) >= 0);
 
@@ -455,14 +869,18 @@ void pa_dbusiface_stream_free(pa_dbusiface_stream *s) {
 
     pa_assert_se(pa_dbus_protocol_remove_interface(s->dbus_protocol, s->path, stream_interface_info.name) >= 0);
 
-    if (s->type == STREAM_TYPE_PLAYBACK)
+    if (s->type == STREAM_TYPE_PLAYBACK) {
         pa_sink_input_unref(s->sink_input);
-    else
+        pa_sink_unref(s->sink);
+    } else {
         pa_source_output_unref(s->source_output);
+        pa_source_unref(s->source);
+    }
 
     pa_proplist_free(s->proplist);
     pa_dbus_protocol_unref(s->dbus_protocol);
     pa_subscription_free(s->subscription);
+    pa_hook_slot_free(s->send_event_slot);
 
     pa_xfree(s->path);
     pa_xfree(s);

commit 8e6664f499ff3431ea51c99baf366ef11c5301a5
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Wed Aug 19 09:09:40 2009 +0300

    dbusiface-core: Split some overly long lines.

diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c
index 86a8fc7..946fdcc 100644
--- a/src/modules/dbus/iface-core.c
+++ b/src/modules/dbus/iface-core.c
@@ -450,7 +450,8 @@ static void handle_set_default_channels(DBusConnection *conn, DBusMessage *msg,
     }
 
     if (n_channels > PA_CHANNELS_MAX) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too many channels: %u. The maximum number of channels is %u.", n_channels, PA_CHANNELS_MAX);
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
+                           "Too many channels: %u. The maximum number of channels is %u.", n_channels, PA_CHANNELS_MAX);
         return;
     }
 
@@ -627,7 +628,8 @@ static void handle_get_fallback_sink(DBusConnection *conn, DBusMessage *msg, voi
     pa_assert(c);
 
     if (!c->fallback_sink) {
-        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "There are no sinks, and therefore no fallback sink either.");
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                           "There are no sinks, and therefore no fallback sink either.");
         return;
     }
 
@@ -647,7 +649,8 @@ static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, voi
     pa_assert(c);
 
     if (!c->fallback_sink) {
-        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "There are no sinks, and therefore no fallback sink either.");
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                           "There are no sinks, and therefore no fallback sink either.");
         return;
     }
 
@@ -713,7 +716,8 @@ static void handle_get_fallback_source(DBusConnection *conn, DBusMessage *msg, v
     pa_assert(c);
 
     if (!c->fallback_source) {
-        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "There are no sources, and therefore no fallback source either.");
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                           "There are no sources, and therefore no fallback source either.");
         return;
     }
 
@@ -733,7 +737,8 @@ static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, v
     pa_assert(c);
 
     if (!c->fallback_source) {
-        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "There are no sources, and therefore no fallback source either.");
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                           "There are no sources, and therefore no fallback source either.");
         return;
     }
 
@@ -1037,9 +1042,14 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat
     default_sample_rate = c->core->default_sample_spec.rate;
     cards = get_cards(c, &n_cards);
     sinks = get_sinks(c, &n_sinks);
-    fallback_sink = c->fallback_sink ? pa_dbusiface_device_get_path(pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(c->fallback_sink->index))) : NULL;
+    fallback_sink = c->fallback_sink
+                    ? pa_dbusiface_device_get_path(pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(c->fallback_sink->index)))
+                    : NULL;
     sources = get_sources(c, &n_sources);
-    fallback_source = c->fallback_source ? pa_dbusiface_device_get_path(pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(c->fallback_source->index))) : NULL;
+    fallback_source = c->fallback_source
+                      ? pa_dbusiface_device_get_path(pa_hashmap_get(c->sources_by_index,
+                                                                    PA_UINT32_TO_PTR(c->fallback_source->index)))
+                      : NULL;
     playback_streams = get_playback_streams(c, &n_playback_streams);
     record_streams = get_record_streams(c, &n_record_streams);
     samples = get_samples(c, &n_samples);
@@ -1299,7 +1309,8 @@ static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *u
     }
 
     if (n_channels > PA_CHANNELS_MAX) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too many channels.");
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
+                           "Too many channels: %u. The maximum is %u.", n_channels, PA_CHANNELS_MAX);
         goto finish;
     }
 
@@ -1311,7 +1322,8 @@ static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *u
     }
 
     if (n_volume_entries != 0 && n_volume_entries != n_channels) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "The channels and default_volume arguments have different number of elements.");
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
+                           "The channels and default_volume arguments have different number of elements.");
         goto finish;
     }
 
@@ -1337,7 +1349,8 @@ static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *u
     ss.channels = n_channels;
 
     if (!pa_frame_aligned(data_length, &ss)) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "The sample length in bytes doesn't align with the sample format and channels.");
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
+                           "The sample length in bytes doesn't align with the sample format and channels.");
         goto finish;
     }
 
@@ -1431,15 +1444,19 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use
     arg_type = dbus_message_iter_get_arg_type(&msg_iter);
     if (arg_type != DBUS_TYPE_ARRAY) {
         if (arg_type == DBUS_TYPE_INVALID)
-            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. A dictionary from strings to strings was expected.");
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
+                               "Too few arguments. A dictionary from strings to strings was expected.");
         else
-            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. An dictionary from strings to strings was expected.", (char) arg_type);
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
+                               "Wrong argument type: '%c'. An dictionary from strings to strings was expected.",
+                               (char) arg_type);
         return;
     }
 
     arg_type = dbus_message_iter_get_element_type(&msg_iter);
     if (arg_type != DBUS_TYPE_DICT_ENTRY) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type: '%c'. A dict entry (string to string) was expected.", (char) arg_type);
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
+                           "Wrong array element type: '%c'. A dict entry (string to string) was expected.", (char) arg_type);
         return;
     }
 
@@ -1455,7 +1472,8 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use
 
         arg_type = dbus_message_iter_get_arg_type(&dict_entry_iter);
         if (arg_type != DBUS_TYPE_STRING) {
-            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict key type: '%c'. A string was expected.", (char) arg_type);
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
+                               "Wrong dict key type: '%c'. A string was expected.", (char) arg_type);
             goto finish;
         }
 
@@ -1472,7 +1490,8 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use
             if (arg_type == DBUS_TYPE_INVALID)
                 pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Dict value missing.");
             else
-                pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value type: '%c'. A string was expected.", (char) arg_type);
+                pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
+                                   "Wrong dict value type: '%c'. A string was expected.", (char) arg_type);
             goto finish;
         }
 
@@ -1532,7 +1551,10 @@ static void handle_listen_for_signal(DBusConnection *conn, DBusMessage *msg, voi
 
     dbus_error_init(&error);
 
-    if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &signal, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &objects, &n_objects, DBUS_TYPE_INVALID)) {
+    if (!dbus_message_get_args(msg, &error,
+                               DBUS_TYPE_STRING, &signal,
+                               DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &objects, &n_objects,
+                               DBUS_TYPE_INVALID)) {
         pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
         dbus_error_free(&error);
         goto finish;
@@ -1591,10 +1613,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
             if (c->fallback_sink != new_fallback_sink) {
                 c->fallback_sink = new_fallback_sink;
 
-                if (new_fallback_sink && (device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(new_fallback_sink->index)))) {
+                if (new_fallback_sink
+                    && (device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(new_fallback_sink->index)))) {
                     object_path = pa_dbusiface_device_get_path(device);
 
-                    pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name)));
+                    pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                                   PA_DBUS_CORE_INTERFACE,
+                                                                   signals[SIGNAL_FALLBACK_SINK_UPDATED].name)));
                     pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
                     pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
                     dbus_message_unref(signal);
@@ -1605,10 +1630,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
             if (c->fallback_source != new_fallback_source) {
                 c->fallback_source = new_fallback_source;
 
-                if (new_fallback_source && (device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(new_fallback_source->index)))) {
+                if (new_fallback_source
+                    && (device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(new_fallback_source->index)))) {
                     object_path = pa_dbusiface_device_get_path(device);
 
-                    pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name)));
+                    pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                                   PA_DBUS_CORE_INTERFACE,
+                                                                   signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name)));
                     pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
                     pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
                     dbus_message_unref(signal);
@@ -1626,7 +1654,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_card_get_path(card);
 
-                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_CARD].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                               PA_DBUS_CORE_INTERFACE,
+                                                               signals[SIGNAL_NEW_CARD].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
             } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
@@ -1634,7 +1664,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_card_get_path(card);
 
-                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_CARD_REMOVED].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                               PA_DBUS_CORE_INTERFACE,
+                                                               signals[SIGNAL_CARD_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbusiface_card_free(card);
@@ -1653,7 +1685,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_device_get_path(device);
 
-                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_SINK].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                               PA_DBUS_CORE_INTERFACE,
+                                                               signals[SIGNAL_NEW_SINK].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
@@ -1665,7 +1699,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
                      * the D-Bus sink object wasn't created yet. Now that the
                      * object is created, let's send the fallback sink change
                      * signal. */
-                    pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SINK_UPDATED].name)));
+                    pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                                   PA_DBUS_CORE_INTERFACE,
+                                                                   signals[SIGNAL_FALLBACK_SINK_UPDATED].name)));
                     pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                     pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
@@ -1678,7 +1714,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
                 object_path = pa_dbusiface_device_get_path(device);
                 pa_assert_se(pa_hashmap_remove(c->sinks_by_path, object_path));
 
-                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_SINK_REMOVED].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                               PA_DBUS_CORE_INTERFACE,
+                                                               signals[SIGNAL_SINK_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbusiface_device_free(device);
@@ -1697,7 +1735,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_device_get_path(device);
 
-                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_SOURCE].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                               PA_DBUS_CORE_INTERFACE,
+                                                               signals[SIGNAL_NEW_SOURCE].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
@@ -1709,7 +1749,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
                      * point the D-Bus source object wasn't created yet. Now
                      * that the object is created, let's send the fallback
                      * source change signal. */
-                    pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name)));
+                    pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                                   PA_DBUS_CORE_INTERFACE,
+                                                                   signals[SIGNAL_FALLBACK_SOURCE_UPDATED].name)));
                     pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                     pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
@@ -1722,7 +1764,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
                 object_path = pa_dbusiface_device_get_path(device);
                 pa_assert_se(pa_hashmap_remove(c->sources_by_path, object_path));
 
-                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_SOURCE_REMOVED].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                               PA_DBUS_CORE_INTERFACE,
+                                                               signals[SIGNAL_SOURCE_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbusiface_device_free(device);
@@ -1738,7 +1782,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_stream_get_path(stream);
 
-                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_PLAYBACK_STREAM].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                               PA_DBUS_CORE_INTERFACE,
+                                                               signals[SIGNAL_NEW_PLAYBACK_STREAM].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
             } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
@@ -1746,7 +1792,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_stream_get_path(stream);
 
-                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_PLAYBACK_STREAM_REMOVED].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                               PA_DBUS_CORE_INTERFACE,
+                                                               signals[SIGNAL_PLAYBACK_STREAM_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbusiface_stream_free(stream);
@@ -1762,7 +1810,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_stream_get_path(stream);
 
-                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_RECORD_STREAM].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                               PA_DBUS_CORE_INTERFACE,
+                                                               signals[SIGNAL_NEW_RECORD_STREAM].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
             } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
@@ -1770,7 +1820,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_stream_get_path(stream);
 
-                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_RECORD_STREAM_REMOVED].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                               PA_DBUS_CORE_INTERFACE,
+                                                               signals[SIGNAL_RECORD_STREAM_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbusiface_stream_free(stream);
@@ -1786,7 +1838,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_sample_get_path(sample);
 
-                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_SAMPLE].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                               PA_DBUS_CORE_INTERFACE,
+                                                               signals[SIGNAL_NEW_SAMPLE].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
             } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
@@ -1794,7 +1848,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_sample_get_path(sample);
 
-                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_SAMPLE_REMOVED].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                               PA_DBUS_CORE_INTERFACE,
+                                                               signals[SIGNAL_SAMPLE_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbusiface_sample_free(sample);
@@ -1810,7 +1866,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_module_get_path(module);
 
-                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_MODULE].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                               PA_DBUS_CORE_INTERFACE,
+                                                               signals[SIGNAL_NEW_MODULE].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
             } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
@@ -1818,7 +1876,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_module_get_path(module);
 
-                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_MODULE_REMOVED].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                               PA_DBUS_CORE_INTERFACE,
+                                                               signals[SIGNAL_MODULE_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbusiface_module_free(module);
@@ -1834,7 +1894,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_client_get_path(client);
 
-                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_CLIENT].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                               PA_DBUS_CORE_INTERFACE,
+                                                               signals[SIGNAL_NEW_CLIENT].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
             } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
@@ -1842,7 +1904,9 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
                 object_path = pa_dbusiface_client_get_path(client);
 
-                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_CLIENT_REMOVED].name)));
+                pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                               PA_DBUS_CORE_INTERFACE,
+                                                               signals[SIGNAL_CLIENT_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
                 pa_dbusiface_client_free(client);
@@ -1864,7 +1928,9 @@ static pa_hook_result_t extension_registered_cb(void *hook_data, void *call_data
     pa_assert(c);
     pa_assert(ext_name);
 
-    pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_NEW_EXTENSION].name)));
+    pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                   PA_DBUS_CORE_INTERFACE,
+                                                   signals[SIGNAL_NEW_EXTENSION].name)));
     pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_STRING, &ext_name, DBUS_TYPE_INVALID));
 
     pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
@@ -1881,7 +1947,9 @@ static pa_hook_result_t extension_unregistered_cb(void *hook_data, void *call_da
     pa_assert(c);
     pa_assert(ext_name);
 
-    pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH, PA_DBUS_CORE_INTERFACE, signals[SIGNAL_EXTENSION_REMOVED].name)));
+    pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                   PA_DBUS_CORE_INTERFACE,
+                                                   signals[SIGNAL_EXTENSION_REMOVED].name)));
     pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_STRING, &ext_name, DBUS_TYPE_INVALID));
 
     pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
@@ -1921,8 +1989,16 @@ pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) {
     c->clients = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
     c->fallback_sink = pa_namereg_get_default_sink(core);
     c->fallback_source = pa_namereg_get_default_source(core);
-    c->extension_registered_slot = pa_dbus_protocol_hook_connect(c->dbus_protocol, PA_DBUS_PROTOCOL_HOOK_EXTENSION_REGISTERED, PA_HOOK_NORMAL, extension_registered_cb, c);
-    c->extension_unregistered_slot = pa_dbus_protocol_hook_connect(c->dbus_protocol, PA_DBUS_PROTOCOL_HOOK_EXTENSION_UNREGISTERED, PA_HOOK_NORMAL, extension_unregistered_cb, c);
+    c->extension_registered_slot = pa_dbus_protocol_hook_connect(c->dbus_protocol,
+                                                                 PA_DBUS_PROTOCOL_HOOK_EXTENSION_REGISTERED,
+                                                                 PA_HOOK_NORMAL,
+                                                                 extension_registered_cb,
+                                                                 c);
+    c->extension_unregistered_slot = pa_dbus_protocol_hook_connect(c->dbus_protocol,
+                                                                   PA_DBUS_PROTOCOL_HOOK_EXTENSION_UNREGISTERED,
+                                                                   PA_HOOK_NORMAL,
+                                                                   extension_unregistered_cb,
+                                                                   c);
     c->memstats = pa_dbusiface_memstats_new(c, core);
 
     for (card = pa_idxset_first(core->cards, &idx); card; card = pa_idxset_next(core->cards, &idx))

commit 636dbc31f9f7acd76402ea01121f327d21315177
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Wed Aug 19 09:10:38 2009 +0300

    dbusiface-core: Use the PA_IDXSET_FOREACH macro.

diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c
index 946fdcc..e0aedbe 100644
--- a/src/modules/dbus/iface-core.c
+++ b/src/modules/dbus/iface-core.c
@@ -2001,34 +2001,34 @@ pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) {
                                                                    c);
     c->memstats = pa_dbusiface_memstats_new(c, core);
 
-    for (card = pa_idxset_first(core->cards, &idx); card; card = pa_idxset_next(core->cards, &idx))
+    PA_IDXSET_FOREACH(card, core->cards, idx)
         pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), pa_dbusiface_card_new(c, card));
 
-    for (sink = pa_idxset_first(core->sinks, &idx); sink; sink = pa_idxset_next(core->sinks, &idx)) {
+    PA_IDXSET_FOREACH(sink, core->sinks, idx) {
         device = pa_dbusiface_device_new_sink(c, sink);
         pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(idx), device);
         pa_hashmap_put(c->sinks_by_path, pa_dbusiface_device_get_path(device), device);
     }
 
-    for (source = pa_idxset_first(core->sources, &idx); source; source = pa_idxset_next(core->sources, &idx)) {
+    PA_IDXSET_FOREACH(source, core->sources, idx) {
         device = pa_dbusiface_device_new_source(c, source);
         pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(idx), device);
         pa_hashmap_put(c->sources_by_path, pa_dbusiface_device_get_path(device), device);
     }
 
-    for (sink_input = pa_idxset_first(core->sink_inputs, &idx); sink_input; sink_input = pa_idxset_next(core->sink_inputs, &idx))
+    PA_IDXSET_FOREACH(sink_input, core->sink_inputs, idx)
         pa_hashmap_put(c->playback_streams, PA_UINT32_TO_PTR(idx), pa_dbusiface_stream_new_playback(c, sink_input));
 
-    for (source_output = pa_idxset_first(core->source_outputs, &idx); source_output; source_output = pa_idxset_next(core->source_outputs, &idx))
+    PA_IDXSET_FOREACH(source_output, core->source_outputs, idx)
         pa_hashmap_put(c->record_streams, PA_UINT32_TO_PTR(idx), pa_dbusiface_stream_new_record(c, source_output));
 
-    for (sample = pa_idxset_first(core->scache, &idx); sample; sample = pa_idxset_next(core->scache, &idx))
+    PA_IDXSET_FOREACH(sample, core->scache, idx)
         pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), pa_dbusiface_sample_new(c, sample));
 
-    for (module = pa_idxset_first(core->modules, &idx); module; module = pa_idxset_next(core->modules, &idx))
+    PA_IDXSET_FOREACH(module, core->modules, idx)
         pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), pa_dbusiface_module_new(c, module));
 
-    for (client = pa_idxset_first(core->clients, &idx); client; client = pa_idxset_next(core->clients, &idx))
+    PA_IDXSET_FOREACH(client, core->clients, idx)
         pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), pa_dbusiface_client_new(c, client));
 
     pa_dbus_protocol_add_interface(c->dbus_protocol, PA_DBUS_CORE_OBJECT_PATH, &core_interface_info, c);

commit 3de210b67120debc680d74e93118a80d360fe1e1
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Wed Aug 19 09:13:59 2009 +0300

    dbusiface-core: Assert that _add/remove_interface calls succeed.

diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c
index e0aedbe..a0694d6 100644
--- a/src/modules/dbus/iface-core.c
+++ b/src/modules/dbus/iface-core.c
@@ -2031,7 +2031,7 @@ pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) {
     PA_IDXSET_FOREACH(client, core->clients, idx)
         pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), pa_dbusiface_client_new(c, client));
 
-    pa_dbus_protocol_add_interface(c->dbus_protocol, PA_DBUS_CORE_OBJECT_PATH, &core_interface_info, c);
+    pa_assert_se(pa_dbus_protocol_add_interface(c->dbus_protocol, PA_DBUS_CORE_OBJECT_PATH, &core_interface_info, c) >= 0);
 
     return c;
 }
@@ -2087,7 +2087,7 @@ static void free_client_cb(void *p, void *userdata) {
 void pa_dbusiface_core_free(pa_dbusiface_core *c) {
     pa_assert(c);
 
-    pa_dbus_protocol_remove_interface(c->dbus_protocol, PA_DBUS_CORE_OBJECT_PATH, core_interface_info.name);
+    pa_assert_se(pa_dbus_protocol_remove_interface(c->dbus_protocol, PA_DBUS_CORE_OBJECT_PATH, core_interface_info.name) >= 0);
 
     pa_subscription_free(c->subscription);
     pa_hashmap_free(c->cards, free_card_cb, NULL);

commit b4e0d5d1e17409557d21dae9a770d1429e17cb15
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Wed Aug 19 09:18:50 2009 +0300

    dbusiface-sample: Implement the Sample D-Bus interface.

diff --git a/src/modules/dbus/iface-sample.c b/src/modules/dbus/iface-sample.c
index 44cfb03..4cffd59 100644
--- a/src/modules/dbus/iface-sample.c
+++ b/src/modules/dbus/iface-sample.c
@@ -24,6 +24,8 @@
 #endif
 
 #include <pulsecore/core-util.h>
+#include <pulsecore/dbus-util.h>
+#include <pulsecore/namereg.h>
 #include <pulsecore/protocol-dbus.h>
 
 #include "iface-sample.h"
@@ -31,19 +33,474 @@
 #define OBJECT_NAME "sample"
 
 struct pa_dbusiface_sample {
+    pa_dbusiface_core *core;
+
     pa_scache_entry *sample;
     char *path;
+    pa_proplist *proplist;
+
+    pa_dbus_protocol *dbus_protocol;
+    pa_subscription *subscription;
+};
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_default_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_duration(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_bytes(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_play(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_play_to_sink(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_remove(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+enum property_handler_index {
+    PROPERTY_HANDLER_INDEX,
+    PROPERTY_HANDLER_NAME,
+    PROPERTY_HANDLER_SAMPLE_FORMAT,
+    PROPERTY_HANDLER_SAMPLE_RATE,
+    PROPERTY_HANDLER_CHANNELS,
+    PROPERTY_HANDLER_DEFAULT_VOLUME,
+    PROPERTY_HANDLER_DURATION,
+    PROPERTY_HANDLER_BYTES,
+    PROPERTY_HANDLER_PROPERTY_LIST,
+    PROPERTY_HANDLER_MAX
+};
+
+static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
+    [PROPERTY_HANDLER_INDEX]          = { .property_name = "Index",         .type = "u",      .get_cb = handle_get_index,          .set_cb = NULL },
+    [PROPERTY_HANDLER_NAME]           = { .property_name = "Name",          .type = "s",      .get_cb = handle_get_name,           .set_cb = NULL },
+    [PROPERTY_HANDLER_SAMPLE_FORMAT]  = { .property_name = "SampleFormat",  .type = "u",      .get_cb = handle_get_sample_format,  .set_cb = NULL },
+    [PROPERTY_HANDLER_SAMPLE_RATE]    = { .property_name = "SampleRate",    .type = "u",      .get_cb = handle_get_sample_rate,    .set_cb = NULL },
+    [PROPERTY_HANDLER_CHANNELS]       = { .property_name = "Channels",      .type = "au",     .get_cb = handle_get_channels,       .set_cb = NULL },
+    [PROPERTY_HANDLER_DEFAULT_VOLUME] = { .property_name = "DefaultVolume", .type = "au",     .get_cb = handle_get_default_volume, .set_cb = NULL },
+    [PROPERTY_HANDLER_DURATION]       = { .property_name = "Duration",      .type = "t",      .get_cb = handle_get_duration,       .set_cb = NULL },
+    [PROPERTY_HANDLER_BYTES]          = { .property_name = "Bytes",         .type = "u",      .get_cb = handle_get_bytes,          .set_cb = NULL },
+    [PROPERTY_HANDLER_PROPERTY_LIST]  = { .property_name = "PropertyList",  .type = "a{say}", .get_cb = handle_get_property_list,  .set_cb = NULL }
+};
+
+enum method_handler_index {
+    METHOD_HANDLER_PLAY,
+    METHOD_HANDLER_PLAY_TO_SINK,
+    METHOD_HANDLER_REMOVE,
+    METHOD_HANDLER_MAX
+};
+
+static pa_dbus_arg_info play_args[] = { { "volume", "u", "in" }, { "property_list", "a{say}", "in" } };
+static pa_dbus_arg_info play_to_sink_args[] = { { "sink",          "o",      "in" },
+                                                { "volume",        "u",      "in" },
+                                                { "property_list", "a{say}", "in" } };
+
+static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
+    [METHOD_HANDLER_PLAY] = {
+        .method_name = "Play",
+        .arguments = play_args,
+        .n_arguments = sizeof(play_args) / sizeof(pa_dbus_arg_info),
+        .receive_cb = handle_play },
+    [METHOD_HANDLER_PLAY_TO_SINK] = {
+        .method_name = "PlayToSink",
+        .arguments = play_to_sink_args,
+        .n_arguments = sizeof(play_to_sink_args) / sizeof(pa_dbus_arg_info),
+        .receive_cb = handle_play_to_sink },
+    [METHOD_HANDLER_REMOVE] = {
+        .method_name = "Remove",
+        .arguments = NULL,
+        .n_arguments = 0,
+        .receive_cb = handle_remove }
+};
+
+enum signal_index {
+    SIGNAL_PROPERTY_LIST_UPDATED,
+    SIGNAL_MAX
+};
+
+static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } };
+
+static pa_dbus_signal_info signals[SIGNAL_MAX] = {
+    [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 }
 };
 
+static pa_dbus_interface_info sample_interface_info = {
+    .name = PA_DBUSIFACE_SAMPLE_INTERFACE,
+    .method_handlers = method_handlers,
+    .n_method_handlers = METHOD_HANDLER_MAX,
+    .property_handlers = property_handlers,
+    .n_property_handlers = PROPERTY_HANDLER_MAX,
+    .get_all_properties_cb = handle_get_all,
+    .signals = signals,
+    .n_signals = SIGNAL_MAX
+};
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_sample *s = userdata;
+    dbus_uint32_t idx = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    idx = s->sample->index;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx);
+}
+
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_sample *s = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &s->sample->name);
+}
+
+static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_sample *s = userdata;
+    dbus_uint32_t sample_format = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    if (!s->sample->memchunk.memblock) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                           "Sample %s isn't loaded into memory yet, so its sample format is unknown.", s->sample->name);
+        return;
+    }
+
+    sample_format = s->sample->sample_spec.format;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sample_format);
+}
+
+static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_sample *s = userdata;
+    dbus_uint32_t sample_rate = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    if (!s->sample->memchunk.memblock) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                           "Sample %s isn't loaded into memory yet, so its sample rate is unknown.", s->sample->name);
+        return;
+    }
+
+    sample_rate = s->sample->sample_spec.rate;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &sample_rate);
+}
+
+static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_sample *s = userdata;
+    dbus_uint32_t channels[PA_CHANNELS_MAX];
+    unsigned i = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    if (!s->sample->memchunk.memblock) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                           "Sample %s isn't loaded into memory yet, so its channel map is unknown.", s->sample->name);
+        return;
+    }
+
+    for (i = 0; i < s->sample->channel_map.channels; ++i)
+        channels[i] = s->sample->channel_map.map[i];
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, channels, s->sample->channel_map.channels);
+}
+
+static void handle_get_default_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_sample *s = userdata;
+    dbus_uint32_t default_volume[PA_CHANNELS_MAX];
+    unsigned i = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    if (!s->sample->volume_is_set) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                           "Sample %s doesn't have default volume stored.", s->sample->name);
+        return;
+    }
+
+    for (i = 0; i < s->sample->volume.channels; ++i)
+        default_volume[i] = s->sample->volume.values[i];
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, default_volume, s->sample->volume.channels);
+}
+
+static void handle_get_duration(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_sample *s = userdata;
+    dbus_uint64_t duration = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    if (!s->sample->memchunk.memblock) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                           "Sample %s isn't loaded into memory yet, so its duration is unknown.", s->sample->name);
+        return;
+    }
+
+    duration = pa_bytes_to_usec(s->sample->memchunk.length, &s->sample->sample_spec);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT64, &duration);
+}
+
+static void handle_get_bytes(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_sample *s = userdata;
+    dbus_uint32_t bytes = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    if (!s->sample->memchunk.memblock) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                           "Sample %s isn't loaded into memory yet, so its size is unknown.", s->sample->name);
+        return;
+    }
+
+    bytes = s->sample->memchunk.length;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &bytes);
+}
+
+static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_sample *s = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    pa_dbus_send_proplist_variant_reply(conn, msg, s->proplist);
+}
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_sample *s = userdata;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+    DBusMessageIter dict_iter;
+    dbus_uint32_t idx = 0;
+    dbus_uint32_t sample_format = 0;
+    dbus_uint32_t sample_rate = 0;
+    dbus_uint32_t channels[PA_CHANNELS_MAX];
+    dbus_uint32_t default_volume[PA_CHANNELS_MAX];
+    dbus_uint64_t duration = 0;
+    dbus_uint32_t bytes = 0;
+    unsigned i = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    idx = s->sample->index;
+    if (s->sample->memchunk.memblock) {
+        sample_format = s->sample->sample_spec.format;
+        sample_rate = s->sample->sample_spec.rate;
+        for (i = 0; i < s->sample->channel_map.channels; ++i)
+            channels[i] = s->sample->channel_map.map[i];
+        duration = pa_bytes_to_usec(s->sample->memchunk.length, &s->sample->sample_spec);
+        bytes = s->sample->memchunk.length;
+    }
+    if (s->sample->volume_is_set) {
+        for (i = 0; i < s->sample->volume.channels; ++i)
+            default_volume[i] = s->sample->volume.values[i];
+    }
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &s->sample->name);
+
+    if (s->sample->memchunk.memblock) {
+        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_FORMAT].property_name, DBUS_TYPE_UINT32, &sample_format);
+        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_RATE].property_name, DBUS_TYPE_UINT32, &sample_rate);
+        pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CHANNELS].property_name, DBUS_TYPE_UINT32, channels, s->sample->channel_map.channels);
+    }
+
+    if (s->sample->volume_is_set)
+        pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DEFAULT_VOLUME].property_name, DBUS_TYPE_UINT32, default_volume, s->sample->volume.channels);
+
+    if (s->sample->memchunk.memblock) {
+        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DURATION].property_name, DBUS_TYPE_UINT64, &duration);
+        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_BYTES].property_name, DBUS_TYPE_UINT32, &bytes);
+    }
+
+    pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, s->proplist);
+
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+    dbus_message_unref(reply);
+}
+
+static void handle_play(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_sample *s = userdata;
+    DBusMessageIter msg_iter;
+    dbus_uint32_t volume = 0;
+    pa_proplist *property_list = NULL;
+    pa_sink *sink = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    if (!dbus_message_iter_init(msg, &msg_iter)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments.");
+        return;
+    }
+
+    if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &volume) < 0)
+        return;
+
+    if (!(property_list = pa_dbus_get_proplist_arg(conn, msg, &msg_iter)))
+        return;
+
+    if (volume > PA_VOLUME_MAX) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid volume.");
+        goto finish;
+    }
+
+    if (!(sink = pa_namereg_get_default_sink(s->sample->core))) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED,
+                           "Can't play sample %s, because there are no sinks available.", s->sample->name);
+        goto finish;
+    }
+
+    if (pa_scache_play_item(s->sample->core, s->sample->name, sink, volume, property_list, NULL) < 0) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Playing sample %s failed.", s->sample->name);
+        goto finish;
+    }
+
+    pa_dbus_send_empty_reply(conn, msg);
+
+finish:
+    if (property_list)
+        pa_proplist_free(property_list);
+}
+
+static void handle_play_to_sink(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_sample *s = userdata;
+    DBusMessageIter msg_iter;
+    const char *sink_path = NULL;
+    dbus_uint32_t volume = 0;
+    pa_proplist *property_list = NULL;
+    pa_sink *sink = NULL;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    if (!dbus_message_iter_init(msg, &msg_iter)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments.");
+        return;
+    }
+
+    if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_OBJECT_PATH, &sink_path) < 0)
+        return;
+
+    if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &volume) < 0)
+        return;
+
+    if (!(property_list = pa_dbus_get_proplist_arg(conn, msg, &msg_iter)))
+        return;
+
+    if (!(sink = pa_dbusiface_core_get_sink(s->core, sink_path))) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such sink.", sink_path);
+        goto finish;
+    }
+
+    if (volume > PA_VOLUME_MAX) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid volume.");
+        goto finish;
+    }
+
+    if (pa_scache_play_item(s->sample->core, s->sample->name, sink, volume, property_list, NULL) < 0) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Playing sample %s failed.", s->sample->name);
+        goto finish;
+    }
+
+    pa_dbus_send_empty_reply(conn, msg);
+
+finish:
+    if (property_list)
+        pa_proplist_free(property_list);
+}
+
+static void handle_remove(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_sample *s = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(s);
+
+    if (pa_scache_remove_item(s->sample->core, s->sample->name) < 0) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Removing sample %s failed.", s->sample->name);
+        return;
+    }
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
+    pa_dbusiface_sample *s = userdata;
+    DBusMessage *signal = NULL;
+
+    pa_assert(c);
+    pa_assert(s);
+
+    if (idx != s->sample->index)
+        return;
+
+    if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE)
+        return;
+
+    if (!pa_proplist_equal(s->proplist, s->sample->proplist)) {
+        DBusMessageIter msg_iter;
+
+        pa_proplist_update(s->proplist, PA_UPDATE_SET, s->sample->proplist);
+
+        pa_assert_se(signal = dbus_message_new_signal(s->path,
+                                                      PA_DBUSIFACE_SAMPLE_INTERFACE,
+                                                      signals[SIGNAL_PROPERTY_LIST_UPDATED].name));
+        dbus_message_iter_init_append(signal, &msg_iter);
+        pa_dbus_append_proplist(&msg_iter, s->proplist);
+
+        pa_dbus_protocol_send_signal(s->dbus_protocol, signal);
+        dbus_message_unref(signal);
+        signal = NULL;
+    }
+}
+
 pa_dbusiface_sample *pa_dbusiface_sample_new(pa_dbusiface_core *core, pa_scache_entry *sample) {
-    pa_dbusiface_sample *s;
+    pa_dbusiface_sample *s = NULL;
 
     pa_assert(core);
     pa_assert(sample);
 
-    s = pa_xnew(pa_dbusiface_sample, 1);
+    s = pa_xnew0(pa_dbusiface_sample, 1);
+    s->core = core;
     s->sample = sample;
     s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, sample->index);
+    s->proplist = pa_proplist_copy(sample->proplist);
+    s->dbus_protocol = pa_dbus_protocol_get(sample->core);
+    s->subscription = pa_subscription_new(sample->core, PA_SUBSCRIPTION_MASK_SAMPLE_CACHE, subscription_cb, s);
+
+    pa_assert_se(pa_dbus_protocol_add_interface(s->dbus_protocol, s->path, &sample_interface_info, s) >= 0);
 
     return s;
 }
@@ -51,6 +508,12 @@ pa_dbusiface_sample *pa_dbusiface_sample_new(pa_dbusiface_core *core, pa_scache_
 void pa_dbusiface_sample_free(pa_dbusiface_sample *s) {
     pa_assert(s);
 
+    pa_assert_se(pa_dbus_protocol_remove_interface(s->dbus_protocol, s->path, sample_interface_info.name) >= 0);
+
+    pa_proplist_free(s->proplist);
+    pa_dbus_protocol_unref(s->dbus_protocol);
+    pa_subscription_free(s->subscription);
+
     pa_xfree(s->path);
     pa_xfree(s);
 }
diff --git a/src/modules/dbus/iface-sample.h b/src/modules/dbus/iface-sample.h
index 1b82648..f1947ce 100644
--- a/src/modules/dbus/iface-sample.h
+++ b/src/modules/dbus/iface-sample.h
@@ -29,9 +29,12 @@
  */
 
 #include <pulsecore/core-scache.h>
+#include <pulsecore/protocol-dbus.h>
 
 #include "iface-core.h"
 
+#define PA_DBUSIFACE_SAMPLE_INTERFACE PA_DBUS_CORE_INTERFACE ".Sample"
+
 typedef struct pa_dbusiface_sample pa_dbusiface_sample;
 
 pa_dbusiface_sample *pa_dbusiface_sample_new(pa_dbusiface_core *core, pa_scache_entry *sample);

commit 292d6dcb5f1568fdc66a813cc464c66571092991
Merge: b4e0d5d f4f16ab
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Wed Aug 19 09:20:02 2009 +0300

    Merge branch 'master' of git://0pointer.de/pulseaudio into dbus-work


commit 179f849c0869982c6fc97d6bbf9083203c586b17
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Wed Aug 19 09:54:09 2009 +0300

    dbusifaca-device: Adapt to the changed pa_sink_get/set_volume() interface.

diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c
index 8dc0b2c..8f719bc 100644
--- a/src/modules/dbus/iface-device.c
+++ b/src/modules/dbus/iface-device.c
@@ -444,7 +444,7 @@ static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *user
     }
 
     if (d->type == DEVICE_TYPE_SINK)
-        pa_sink_set_volume(d->sink, &new_vol, TRUE, TRUE, TRUE, TRUE);
+        pa_sink_set_volume(d->sink, &new_vol, TRUE, TRUE);
     else
         pa_source_set_volume(d->source, &new_vol, TRUE);
 
@@ -1099,7 +1099,7 @@ static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t
                    && ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE)));
 
     new_volume = (d->type == DEVICE_TYPE_SINK)
-                 ? pa_sink_get_volume(d->sink, FALSE, FALSE)
+                 ? pa_sink_get_volume(d->sink, FALSE)
                  : pa_source_get_volume(d->source, FALSE);
 
     if (!pa_cvolume_equal(&d->volume, new_volume)) {
@@ -1212,7 +1212,7 @@ pa_dbusiface_device *pa_dbusiface_device_new_sink(pa_dbusiface_core *core, pa_si
     d->sink = pa_sink_ref(sink);
     d->type = DEVICE_TYPE_SINK;
     d->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, SINK_OBJECT_NAME, sink->index);
-    d->volume = *pa_sink_get_volume(sink, FALSE, FALSE);
+    d->volume = *pa_sink_get_volume(sink, FALSE);
     d->is_muted = pa_sink_get_mute(sink, FALSE);
     d->sink_state = pa_sink_get_state(sink);
     d->ports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);

commit 1e4e26c87fc4f74c9805cc084c88783558acb418
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Mon Aug 24 14:22:32 2009 +0300

    proplist: Return early from pa_proplist_equal() if the pointers are equal.

diff --git a/src/pulse/proplist.c b/src/pulse/proplist.c
index 4f0d6a6..8b5b653 100644
--- a/src/pulse/proplist.c
+++ b/src/pulse/proplist.c
@@ -690,6 +690,9 @@ int pa_proplist_equal(pa_proplist *a, pa_proplist *b) {
     pa_assert(a);
     pa_assert(b);
 
+    if (a == b)
+        return 1;
+
     if (pa_proplist_size(a) != pa_proplist_size(b))
         return 0;
 

commit 187c4f32cffb75787213e8692b27d0a3c736b95e
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Mon Aug 24 14:23:49 2009 +0300

    proplist: A couple of documentation fixes.

diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h
index a585944..9effc86 100644
--- a/src/pulse/proplist.h
+++ b/src/pulse/proplist.h
@@ -337,7 +337,7 @@ char *pa_proplist_to_string_sep(pa_proplist *p, const char *sep);
  * readable string. \since 0.9.15 */
 pa_proplist *pa_proplist_from_string(const char *str);
 
-  /** Returns 1 if an entry for the specified key is existant in the
+/** Returns 1 if an entry for the specified key is existant in the
  * property list. \since 0.9.11 */
 int pa_proplist_contains(pa_proplist *p, const char *key);
 
@@ -354,7 +354,8 @@ unsigned pa_proplist_size(pa_proplist *t);
 /** Returns 0 when the proplist is empty, positive otherwise \since 0.9.15 */
 int pa_proplist_isempty(pa_proplist *t);
 
-/** Return non-zero when a and b have the same keys and values. */
+/** Return non-zero when a and b have the same keys and values.
+ * \since 0.9.16 */
 int pa_proplist_equal(pa_proplist *a, pa_proplist *b);
 
 PA_C_DECL_END

commit 7049b3c5bc351b9ea7ed932baa5bf7ccc1b67347
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Mon Aug 24 14:24:59 2009 +0300

    modargs: New function: pa_modargs_iterate().

diff --git a/src/pulsecore/modargs.c b/src/pulsecore/modargs.c
index c7d734d..e78cdb9 100644
--- a/src/pulsecore/modargs.c
+++ b/src/pulsecore/modargs.c
@@ -415,3 +415,13 @@ int pa_modargs_get_proplist(pa_modargs *ma, const char *name, pa_proplist *p, pa
 
     return 0;
 }
+
+const char *pa_modargs_iterate(pa_modargs *ma, void **state) {
+    pa_hashmap *map = (pa_hashmap*) ma;
+    struct entry *e;
+
+    if (!(e = pa_hashmap_iterate(map, state, NULL)))
+        return NULL;
+
+    return e->key;
+}
diff --git a/src/pulsecore/modargs.h b/src/pulsecore/modargs.h
index b3125b1..1ed66e9 100644
--- a/src/pulsecore/modargs.h
+++ b/src/pulsecore/modargs.h
@@ -60,4 +60,13 @@ int pa_modargs_get_sample_spec_and_channel_map(pa_modargs *ma, pa_sample_spec *s
 
 int pa_modargs_get_proplist(pa_modargs *ma, const char *name, pa_proplist *p, pa_update_mode_t m);
 
+/* Iterate through the module argument list. The user should allocate a
+ * state variable of type void* and initialize it with NULL. A pointer
+ * to this variable should then be passed to pa_modargs_iterate()
+ * which should be called in a loop until it returns NULL which
+ * signifies EOL. On each invication this function will return the
+ * key string for the next entry. The keys in the argument list do not
+ * have any particular order. */
+const char *pa_modargs_iterate(pa_modargs *ma, void **state);
+
 #endif

commit 57886ff34ac39dae709cb50cbc7fbbf817df534b
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Mon Aug 24 14:26:13 2009 +0300

    dbus-protocol: Print a debug line whenever interfaces are unregistered.

diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c
index cf561c8..d1e19ff 100644
--- a/src/pulsecore/protocol-dbus.c
+++ b/src/pulsecore/protocol-dbus.c
@@ -694,6 +694,8 @@ int pa_dbus_protocol_remove_interface(pa_dbus_protocol *p, const char* path, con
 
     update_introspection(obj_entry);
 
+    pa_log_debug("Interface %s removed from object %s", iface_entry->name, obj_entry->path);
+
     pa_xfree(iface_entry->name);
     pa_hashmap_free(iface_entry->method_handlers, method_handler_free_cb, NULL);
     pa_hashmap_free(iface_entry->property_handlers, property_handler_free_cb, NULL);

commit 3025645b0b58f476f6404f2aed3b61a633794d10
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Mon Aug 24 14:27:14 2009 +0300

    dbusiface-module: Implement the Module D-Bus interface.

diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c
index a0694d6..9e8f775 100644
--- a/src/modules/dbus/iface-core.c
+++ b/src/modules/dbus/iface-core.c
@@ -1509,7 +1509,7 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use
         goto finish;
     }
 
-    dbus_module = pa_dbusiface_module_new(c, module);
+    dbus_module = pa_dbusiface_module_new(module);
     pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(module->index), dbus_module);
 
     object_path = pa_dbusiface_module_get_path(dbus_module);
@@ -1860,7 +1860,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
         case PA_SUBSCRIPTION_EVENT_MODULE:
             if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
                 if (!(module = pa_hashmap_get(c->modules, PA_UINT32_TO_PTR(idx)))) {
-                    module = pa_dbusiface_module_new(c, pa_idxset_get_by_index(core->modules, idx));
+                    module = pa_dbusiface_module_new(pa_idxset_get_by_index(core->modules, idx));
                     pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), module);
                 }
 
@@ -2026,7 +2026,7 @@ pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) {
         pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), pa_dbusiface_sample_new(c, sample));
 
     PA_IDXSET_FOREACH(module, core->modules, idx)
-        pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), pa_dbusiface_module_new(c, module));
+        pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), pa_dbusiface_module_new(module));
 
     PA_IDXSET_FOREACH(client, core->clients, idx)
         pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), pa_dbusiface_client_new(c, client));
diff --git a/src/modules/dbus/iface-module.c b/src/modules/dbus/iface-module.c
index 788d104..63786da 100644
--- a/src/modules/dbus/iface-module.c
+++ b/src/modules/dbus/iface-module.c
@@ -24,6 +24,8 @@
 #endif
 
 #include <pulsecore/core-util.h>
+#include <pulsecore/dbus-util.h>
+#include <pulsecore/modargs.h>
 #include <pulsecore/protocol-dbus.h>
 
 #include "iface-module.h"
@@ -33,17 +35,278 @@
 struct pa_dbusiface_module {
     pa_module *module;
     char *path;
+    pa_proplist *proplist;
+
+    pa_dbus_protocol *dbus_protocol;
+    pa_subscription *subscription;
 };
 
-pa_dbusiface_module *pa_dbusiface_module_new(pa_dbusiface_core *core, pa_module *module) {
-    pa_dbusiface_module *m;
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_arguments(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_usage_counter(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+static void handle_unload(DBusConnection *conn, DBusMessage *msg, void *userdata);
+
+enum property_handler_index {
+    PROPERTY_HANDLER_INDEX,
+    PROPERTY_HANDLER_NAME,
+    PROPERTY_HANDLER_ARGUMENTS,
+    PROPERTY_HANDLER_USAGE_COUNTER,
+    PROPERTY_HANDLER_PROPERTY_LIST,
+    PROPERTY_HANDLER_MAX
+};
+
+static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
+    [PROPERTY_HANDLER_INDEX]         = { .property_name = "Index",        .type = "u",      .get_cb = handle_get_index,         .set_cb = NULL },
+    [PROPERTY_HANDLER_NAME]          = { .property_name = "Name",         .type = "s",      .get_cb = handle_get_name,          .set_cb = NULL },
+    [PROPERTY_HANDLER_ARGUMENTS]     = { .property_name = "Arguments",    .type = "a{ss}",  .get_cb = handle_get_arguments,     .set_cb = NULL },
+    [PROPERTY_HANDLER_USAGE_COUNTER] = { .property_name = "UsageCounter", .type = "u",      .get_cb = handle_get_usage_counter, .set_cb = NULL },
+    [PROPERTY_HANDLER_PROPERTY_LIST] = { .property_name = "PropertyList", .type = "a{say}", .get_cb = handle_get_property_list, .set_cb = NULL }
+};
+
+enum method_handler_index {
+    METHOD_HANDLER_UNLOAD,
+    METHOD_HANDLER_MAX
+};
+
+static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
+    [METHOD_HANDLER_UNLOAD] = {
+        .method_name = "Unload",
+        .arguments = NULL,
+        .n_arguments = 0,
+        .receive_cb = handle_unload }
+};
+
+enum signal_index {
+    SIGNAL_PROPERTY_LIST_UPDATED,
+    SIGNAL_MAX
+};
+
+static pa_dbus_arg_info property_list_updated_args[] =  { { "property_list", "a{say}", NULL } };
+
+static pa_dbus_signal_info signals[SIGNAL_MAX] = {
+    [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 }
+};
+
+static pa_dbus_interface_info module_interface_info = {
+    .name = PA_DBUSIFACE_MODULE_INTERFACE,
+    .method_handlers = method_handlers,
+    .n_method_handlers = METHOD_HANDLER_MAX,
+    .property_handlers = property_handlers,
+    .n_property_handlers = PROPERTY_HANDLER_MAX,
+    .get_all_properties_cb = handle_get_all,
+    .signals = signals,
+    .n_signals = SIGNAL_MAX
+};
+
+static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_module *m = userdata;
+    dbus_uint32_t idx = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(m);
+
+    idx = m->module->index;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx);
+}
+
+static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_module *m = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(m);
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &m->module->name);
+}
+
+static void append_modargs_variant(DBusMessageIter *iter, pa_dbusiface_module *m) {
+    pa_modargs *ma = NULL;
+    DBusMessageIter variant_iter;
+    DBusMessageIter dict_iter;
+    DBusMessageIter dict_entry_iter;
+    void *state = NULL;
+    const char *key = NULL;
+    const char *value = NULL;
+
+    pa_assert(iter);
+    pa_assert(m);
+
+    pa_assert_se(ma = pa_modargs_new(m->module->argument, NULL));
+
+    pa_assert_se(dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a{ss}", &variant_iter));
+    pa_assert_se(dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY, "{ss}", &dict_iter));
+
+    for (state = NULL, key = pa_modargs_iterate(ma, &state); key; key = pa_modargs_iterate(ma, &state)) {
+        pa_assert_se(value = pa_modargs_get_value(ma, key, NULL));
+
+        pa_assert_se(dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter));
+
+        pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &key));
+        pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &value));
+
+        pa_assert_se(dbus_message_iter_close_container(&dict_iter, &dict_entry_iter));
+    }
+
+    pa_assert_se(dbus_message_iter_close_container(&variant_iter, &dict_iter));
+    pa_assert_se(dbus_message_iter_close_container(iter, &variant_iter));
+
+    pa_modargs_free(ma);
+}
+
+static void handle_get_arguments(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_module *m = userdata;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(m);
+
+    pa_assert_se(reply = dbus_message_new_method_return(msg));
+    dbus_message_iter_init_append(reply, &msg_iter);
+    append_modargs_variant(&msg_iter, m);
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+    dbus_message_unref(reply);
+}
+
+static void handle_get_usage_counter(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_module *m = userdata;
+    int real_counter_value = -1;
+    dbus_uint32_t usage_counter = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(m);
+
+    if (!m->module->get_n_used || (real_counter_value = m->module->get_n_used(m->module)) < 0) {
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY,
+                           "Module %u (%s) doesn't have a usage counter.", m->module->index, m->module->name);
+        return;
+    }
+
+    usage_counter = real_counter_value;
+
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &usage_counter);
+}
+
+static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_module *m = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(m);
+
+    pa_dbus_send_proplist_variant_reply(conn, msg, m->proplist);
+}
+
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_module *m = userdata;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+    DBusMessageIter dict_iter;
+    DBusMessageIter dict_entry_iter;
+    dbus_uint32_t idx = 0;
+    int real_counter_value = -1;
+    dbus_uint32_t usage_counter = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(m);
+
+    idx = m->module->index;
+    if (m->module->get_n_used && (real_counter_value = m->module->get_n_used(m->module)) >= 0)
+        usage_counter = real_counter_value;
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &idx);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &m->module->name);
+
+    pa_assert_se(dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter));
+    pa_assert_se(dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &property_handlers[PROPERTY_HANDLER_ARGUMENTS].property_name));
+    append_modargs_variant(&dict_entry_iter, m);
+    pa_assert_se(dbus_message_iter_close_container(&dict_iter, &dict_entry_iter));
+
+    if (real_counter_value >= 0)
+        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_ARGUMENTS].property_name, DBUS_TYPE_UINT32, &usage_counter);
+
+    pa_dbus_append_proplist_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PROPERTY_LIST].property_name, m->proplist);
+
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+
+    dbus_message_unref(reply);
+}
+
+static void handle_unload(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_module *m = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(m);
+
+    if (m->module->core->disallow_module_loading) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "The server is configured to disallow module unloading.");
+        return;
+    }
+
+    pa_module_unload_request(m->module, FALSE);
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
+    pa_dbusiface_module *m = userdata;
 
     pa_assert(core);
+    pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_MODULE);
+    pa_assert(m);
+
+    if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) {
+        DBusMessage *signal = NULL;
+
+        if (!pa_proplist_equal(m->proplist, m->module->proplist)) {
+            DBusMessageIter msg_iter;
+
+            pa_proplist_update(m->proplist, PA_UPDATE_SET, m->module->proplist);
+
+            pa_assert_se(signal = dbus_message_new_signal(m->path,
+                                                          PA_DBUSIFACE_MODULE_INTERFACE,
+                                                          signals[SIGNAL_PROPERTY_LIST_UPDATED].name));
+            dbus_message_iter_init_append(signal, &msg_iter);
+            pa_dbus_append_proplist(&msg_iter, m->proplist);
+
+            pa_dbus_protocol_send_signal(m->dbus_protocol, signal);
+            dbus_message_unref(signal);
+            signal = NULL;
+        }
+    }
+}
+
+pa_dbusiface_module *pa_dbusiface_module_new(pa_module *module) {
+    pa_dbusiface_module *m;
+
     pa_assert(module);
 
-    m = pa_xnew(pa_dbusiface_module, 1);
+    m = pa_xnew0(pa_dbusiface_module, 1);
     m->module = module;
     m->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, module->index);
+    m->proplist = pa_proplist_copy(module->proplist);
+    m->dbus_protocol = pa_dbus_protocol_get(module->core);
+    m->subscription = pa_subscription_new(module->core, PA_SUBSCRIPTION_MASK_MODULE, subscription_cb, m);
+
+    pa_assert_se(pa_dbus_protocol_add_interface(m->dbus_protocol, m->path, &module_interface_info, m) >= 0);
 
     return m;
 }
@@ -51,6 +314,12 @@ pa_dbusiface_module *pa_dbusiface_module_new(pa_dbusiface_core *core, pa_module
 void pa_dbusiface_module_free(pa_dbusiface_module *m) {
     pa_assert(m);
 
+    pa_assert_se(pa_dbus_protocol_remove_interface(m->dbus_protocol, m->path, module_interface_info.name) >= 0);
+
+    pa_proplist_free(m->proplist);
+    pa_dbus_protocol_unref(m->dbus_protocol);
+    pa_subscription_free(m->subscription);
+
     pa_xfree(m->path);
     pa_xfree(m);
 }
diff --git a/src/modules/dbus/iface-module.h b/src/modules/dbus/iface-module.h
index c4f2921..68ca1de 100644
--- a/src/modules/dbus/iface-module.h
+++ b/src/modules/dbus/iface-module.h
@@ -29,12 +29,15 @@
  */
 
 #include <pulsecore/module.h>
+#include <pulsecore/protocol-dbus.h>
 
 #include "iface-core.h"
 
+#define PA_DBUSIFACE_MODULE_INTERFACE PA_DBUS_CORE_INTERFACE ".Module"
+
 typedef struct pa_dbusiface_module pa_dbusiface_module;
 
-pa_dbusiface_module *pa_dbusiface_module_new(pa_dbusiface_core *core, pa_module *module);
+pa_dbusiface_module *pa_dbusiface_module_new(pa_module *module);
 void pa_dbusiface_module_free(pa_dbusiface_module *m);
 
 const char *pa_dbusiface_module_get_path(pa_dbusiface_module *m);

commit 2f3fc2f1d6ba418303a4bab6f8fd3caed7d291b4
Merge: 3025645 be46eaa
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Mon Aug 24 14:43:11 2009 +0300

    Merge branch 'master' of git://0pointer.de/pulseaudio into dbus-work
    
    Conflicts:
    	src/Makefile.am

diff --cc src/pulsecore/cpu-arm.h
index b00a16a,3ccd070..a87cb63
--- a/src/pulsecore/cpu-arm.h
+++ b/src/pulsecore/cpu-arm.h
@@@ -5,7 -5,7 +5,7 @@@
    This file is part of PulseAudio.
  
    Copyright 2004-2006 Lennart Poettering
-   Copyright 2006 Pierre Ossman <ossman at cendio.se> for Cendio AB
 -  Copyright 2009 Wim Taymans <wim.taymans at collabora.co.uk> 
++  Copyright 2009 Wim Taymans <wim.taymans at collabora.co.uk>
  
    PulseAudio is free software; you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published
diff --cc src/pulsecore/cpu-x86.h
index 0000000,b11ef6e..f3f9e56
mode 000000,100644..100644
--- a/src/pulsecore/cpu-x86.h
+++ b/src/pulsecore/cpu-x86.h
@@@ -1,0 -1,68 +1,68 @@@
+ #ifndef foocpux86hfoo
+ #define foocpux86hfoo
+ 
+ /***
+   This file is part of PulseAudio.
+ 
+   Copyright 2004-2006 Lennart Poettering
 -  Copyright 2009 Wim Taymans <wim.taymans at collabora.co.uk> 
++  Copyright 2009 Wim Taymans <wim.taymans at collabora.co.uk>
+ 
+   PulseAudio is free software; you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as published
+   by the Free Software Foundation; either version 2.1 of the License,
+   or (at your option) any later version.
+ 
+   PulseAudio is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+   General Public License for more details.
+ 
+   You should have received a copy of the GNU Lesser General Public License
+   along with PulseAudio; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+   USA.
+ ***/
+ 
+ #include <stdint.h>
+ 
+ typedef enum pa_cpu_x86_flag {
+     PA_CPU_X86_MMX       = (1 << 0),
+     PA_CPU_X86_MMXEXT    = (1 << 1),
+     PA_CPU_X86_SSE       = (1 << 2),
+     PA_CPU_X86_SSE2      = (1 << 3),
+     PA_CPU_X86_SSE3      = (1 << 4),
+     PA_CPU_X86_SSSE3     = (1 << 5),
+     PA_CPU_X86_SSE4_1    = (1 << 6),
+     PA_CPU_X86_SSE4_2    = (1 << 7),
+     PA_CPU_X86_3DNOW     = (1 << 8),
+     PA_CPU_X86_3DNOWEXT  = (1 << 9)
+ } pa_cpu_x86_flag_t;
+ 
+ void pa_cpu_init_x86 (void);
+ 
+ 
+ #if defined (__i386__)
+ typedef int32_t pa_reg_x86;
+ #define PA_REG_a "eax"
+ #define PA_REG_b "ebx"
+ #define PA_REG_c "ecx"
+ #define PA_REG_d "edx"
+ #define PA_REG_D "edi"
+ #define PA_REG_S "esi"
+ #elif defined (__amd64__)
+ typedef int64_t pa_reg_x86;
+ #define PA_REG_a "rax"
+ #define PA_REG_b "rbx"
+ #define PA_REG_c "rcx"
+ #define PA_REG_d "rdx"
+ #define PA_REG_D "rdi"
+ #define PA_REG_S "rsi"
+ #endif
+ 
+ /* some optimized functions */
+ void pa_volume_func_init_mmx(pa_cpu_x86_flag_t flags);
+ void pa_volume_func_init_sse(pa_cpu_x86_flag_t flags);
+ 
+ void pa_remap_func_init_mmx(pa_cpu_x86_flag_t flags);
+ 
+ #endif /* foocpux86hfoo */

commit 3e0e685a8c6372fb67efad5ffa54583dc757203e
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Wed Aug 26 14:12:42 2009 +0300

    dbus: Save one level of identation by returning early.

diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c
index 5a79794..fce44ff 100644
--- a/src/modules/dbus/iface-card.c
+++ b/src/modules/dbus/iface-card.c
@@ -459,45 +459,46 @@ static void handle_get_profile_by_name(DBusConnection *conn, DBusMessage *msg, v
 
 static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
     pa_dbusiface_card *c = userdata;
+    DBusMessage *signal = NULL;
 
     pa_assert(core);
     pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_CARD);
     pa_assert(c);
 
-    if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) {
-        DBusMessage *signal = NULL;
 
-        if (c->active_profile != c->card->active_profile) {
-            const char *object_path;
+    if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE)
+        return;
 
-            c->active_profile = c->card->active_profile;
-            object_path = pa_dbusiface_card_profile_get_path(pa_hashmap_get(c->profiles, c->active_profile->name));
+    if (c->active_profile != c->card->active_profile) {
+        const char *object_path;
 
-            pa_assert_se(signal = dbus_message_new_signal(c->path,
-                                                          PA_DBUSIFACE_CARD_INTERFACE,
-                                                          signals[SIGNAL_ACTIVE_PROFILE_UPDATED].name));
-            pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
+        c->active_profile = c->card->active_profile;
+        object_path = pa_dbusiface_card_profile_get_path(pa_hashmap_get(c->profiles, c->active_profile->name));
 
-            pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
-            dbus_message_unref(signal);
-            signal = NULL;
-        }
+        pa_assert_se(signal = dbus_message_new_signal(c->path,
+                                                      PA_DBUSIFACE_CARD_INTERFACE,
+                                                      signals[SIGNAL_ACTIVE_PROFILE_UPDATED].name));
+        pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
-        if (!pa_proplist_equal(c->proplist, c->card->proplist)) {
-            DBusMessageIter msg_iter;
+        pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
+        dbus_message_unref(signal);
+        signal = NULL;
+    }
 
-            pa_proplist_update(c->proplist, PA_UPDATE_SET, c->card->proplist);
+    if (!pa_proplist_equal(c->proplist, c->card->proplist)) {
+        DBusMessageIter msg_iter;
 
-            pa_assert_se(signal = dbus_message_new_signal(c->path,
-                                                          PA_DBUSIFACE_CARD_INTERFACE,
-                                                          signals[SIGNAL_PROPERTY_LIST_UPDATED].name));
-            dbus_message_iter_init_append(signal, &msg_iter);
-            pa_dbus_append_proplist(&msg_iter, c->proplist);
+        pa_proplist_update(c->proplist, PA_UPDATE_SET, c->card->proplist);
 
-            pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
-            dbus_message_unref(signal);
-            signal = NULL;
-        }
+        pa_assert_se(signal = dbus_message_new_signal(c->path,
+                                                      PA_DBUSIFACE_CARD_INTERFACE,
+                                                      signals[SIGNAL_PROPERTY_LIST_UPDATED].name));
+        dbus_message_iter_init_append(signal, &msg_iter);
+        pa_dbus_append_proplist(&msg_iter, c->proplist);
+
+        pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
+        dbus_message_unref(signal);
+        signal = NULL;
     }
 }
 
diff --git a/src/modules/dbus/iface-module.c b/src/modules/dbus/iface-module.c
index 63786da..e3882fc 100644
--- a/src/modules/dbus/iface-module.c
+++ b/src/modules/dbus/iface-module.c
@@ -268,29 +268,29 @@ static void handle_unload(DBusConnection *conn, DBusMessage *msg, void *userdata
 
 static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
     pa_dbusiface_module *m = userdata;
+    DBusMessage *signal = NULL;
 
     pa_assert(core);
     pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_MODULE);
     pa_assert(m);
 
-    if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) {
-        DBusMessage *signal = NULL;
+    if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE)
+        return;
 
-        if (!pa_proplist_equal(m->proplist, m->module->proplist)) {
-            DBusMessageIter msg_iter;
+    if (!pa_proplist_equal(m->proplist, m->module->proplist)) {
+        DBusMessageIter msg_iter;
 
-            pa_proplist_update(m->proplist, PA_UPDATE_SET, m->module->proplist);
+        pa_proplist_update(m->proplist, PA_UPDATE_SET, m->module->proplist);
 
-            pa_assert_se(signal = dbus_message_new_signal(m->path,
-                                                          PA_DBUSIFACE_MODULE_INTERFACE,
-                                                          signals[SIGNAL_PROPERTY_LIST_UPDATED].name));
-            dbus_message_iter_init_append(signal, &msg_iter);
-            pa_dbus_append_proplist(&msg_iter, m->proplist);
+        pa_assert_se(signal = dbus_message_new_signal(m->path,
+                                                      PA_DBUSIFACE_MODULE_INTERFACE,
+                                                      signals[SIGNAL_PROPERTY_LIST_UPDATED].name));
+        dbus_message_iter_init_append(signal, &msg_iter);
+        pa_dbus_append_proplist(&msg_iter, m->proplist);
 
-            pa_dbus_protocol_send_signal(m->dbus_protocol, signal);
-            dbus_message_unref(signal);
-            signal = NULL;
-        }
+        pa_dbus_protocol_send_signal(m->dbus_protocol, signal);
+        dbus_message_unref(signal);
+        signal = NULL;
     }
 }
 

commit edf80104e32830a3d4ec58f88f9a092c32203d8a
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Wed Aug 26 14:17:35 2009 +0300

    dbus: Make sure that subscription callbacks don't try to access removed objects.

diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c
index fce44ff..7e37f8b 100644
--- a/src/modules/dbus/iface-card.c
+++ b/src/modules/dbus/iface-card.c
@@ -465,6 +465,10 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
     pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_CARD);
     pa_assert(c);
 
+    /* We can't use idx != c->card->index, because the c->card pointer may
+     * be stale at this point. */
+    if (pa_idxset_get_by_index(core->cards, idx) != c->card)
+        return;
 
     if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE)
         return;
diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c
index 9e8f775..c54a3b7 100644
--- a/src/modules/dbus/iface-core.c
+++ b/src/modules/dbus/iface-core.c
@@ -1592,12 +1592,12 @@ static void handle_stop_listening_for_signal(DBusConnection *conn, DBusMessage *
 
 static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
     pa_dbusiface_core *c = userdata;
-    pa_dbusiface_card *card = NULL;
-    pa_dbusiface_device *device = NULL;
-    pa_dbusiface_stream *stream = NULL;
-    pa_dbusiface_sample *sample = NULL;
-    pa_dbusiface_module *module = NULL;
-    pa_dbusiface_client *client = NULL;
+    pa_dbusiface_card *card_iface = NULL;
+    pa_dbusiface_device *device_iface = NULL;
+    pa_dbusiface_stream *stream_iface = NULL;
+    pa_dbusiface_sample *sample_iface = NULL;
+    pa_dbusiface_module *module_iface = NULL;
+    pa_dbusiface_client *client_iface = NULL;
     DBusMessage *signal = NULL;
     const char *object_path = NULL;
     pa_sink *new_fallback_sink = NULL;
@@ -1611,11 +1611,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
             new_fallback_source = pa_namereg_get_default_source(core);
 
             if (c->fallback_sink != new_fallback_sink) {
-                c->fallback_sink = new_fallback_sink;
+                if (c->fallback_sink)
+                    pa_sink_unref(c->fallback_sink);
+                c->fallback_sink = new_fallback_sink ? pa_sink_ref(new_fallback_sink) : NULL;
 
                 if (new_fallback_sink
-                    && (device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(new_fallback_sink->index)))) {
-                    object_path = pa_dbusiface_device_get_path(device);
+                    && (device_iface = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(new_fallback_sink->index)))) {
+                    object_path = pa_dbusiface_device_get_path(device_iface);
 
                     pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
                                                                    PA_DBUS_CORE_INTERFACE,
@@ -1628,11 +1630,13 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
             }
 
             if (c->fallback_source != new_fallback_source) {
-                c->fallback_source = new_fallback_source;
+                if (c->fallback_source)
+                    pa_source_unref(c->fallback_source);
+                c->fallback_source = new_fallback_source ? pa_source_ref(new_fallback_source) : NULL;
 
                 if (new_fallback_source
-                    && (device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(new_fallback_source->index)))) {
-                    object_path = pa_dbusiface_device_get_path(device);
+                    && (device_iface = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(new_fallback_source->index)))) {
+                    object_path = pa_dbusiface_device_get_path(device_iface);
 
                     pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
                                                                    PA_DBUS_CORE_INTERFACE,
@@ -1647,12 +1651,17 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
 
         case PA_SUBSCRIPTION_EVENT_CARD:
             if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
-                if (!(card = pa_hashmap_get(c->cards, PA_UINT32_TO_PTR(idx)))) {
-                    card = pa_dbusiface_card_new(c, pa_idxset_get_by_index(core->cards, idx));
-                    pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), card);
+                if (!(card_iface = pa_hashmap_get(c->cards, PA_UINT32_TO_PTR(idx)))) {
+                    pa_card *card = NULL;
+
+                    if (!(card = pa_idxset_get_by_index(core->cards, idx)))
+                        return; /* The card was removed immediately after creation. */
+
+                    card_iface = pa_dbusiface_card_new(c, card);
+                    pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), card_iface);
                 }
 
-                object_path = pa_dbusiface_card_get_path(card);
+                object_path = pa_dbusiface_card_get_path(card_iface);
 
                 pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
                                                                PA_DBUS_CORE_INTERFACE,
@@ -1660,30 +1669,34 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
             } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
-                pa_assert_se((card = pa_hashmap_remove(c->cards, PA_UINT32_TO_PTR(idx))));
+                if (!(card_iface = pa_hashmap_remove(c->cards, PA_UINT32_TO_PTR(idx))))
+                    return;
 
-                object_path = pa_dbusiface_card_get_path(card);
+                object_path = pa_dbusiface_card_get_path(card_iface);
 
                 pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
                                                                PA_DBUS_CORE_INTERFACE,
                                                                signals[SIGNAL_CARD_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
-                pa_dbusiface_card_free(card);
+                pa_dbusiface_card_free(card_iface);
             }
             break;
 
         case PA_SUBSCRIPTION_EVENT_SINK:
             if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
-                pa_sink *sink = pa_idxset_get_by_index(core->sinks, idx);
+                pa_sink *sink = NULL;
 
-                if (!(device = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(idx)))) {
-                    device = pa_dbusiface_device_new_sink(c, sink);
-                    pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(idx), device);
-                    pa_hashmap_put(c->sinks_by_path, pa_dbusiface_device_get_path(device), device);
+                if (!(sink = pa_idxset_get_by_index(core->sinks, idx)))
+                    return; /* The sink was removed immediately after creation. */
+
+                if (!(device_iface = pa_hashmap_get(c->sinks_by_index, PA_UINT32_TO_PTR(idx)))) {
+                    device_iface = pa_dbusiface_device_new_sink(c, sink);
+                    pa_hashmap_put(c->sinks_by_index, PA_UINT32_TO_PTR(idx), device_iface);
+                    pa_hashmap_put(c->sinks_by_path, pa_dbusiface_device_get_path(device_iface), device_iface);
                 }
 
-                object_path = pa_dbusiface_device_get_path(device);
+                object_path = pa_dbusiface_device_get_path(device_iface);
 
                 pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
                                                                PA_DBUS_CORE_INTERFACE,
@@ -1710,8 +1723,10 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
                 }
 
             } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
-                pa_assert_se((device = pa_hashmap_remove(c->sinks_by_index, PA_UINT32_TO_PTR(idx))));
-                object_path = pa_dbusiface_device_get_path(device);
+                if (!(device_iface = pa_hashmap_remove(c->sinks_by_index, PA_UINT32_TO_PTR(idx))))
+                    return;
+
+                object_path = pa_dbusiface_device_get_path(device_iface);
                 pa_assert_se(pa_hashmap_remove(c->sinks_by_path, object_path));
 
                 pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
@@ -1719,7 +1734,7 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
                                                                signals[SIGNAL_SINK_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
-                pa_dbusiface_device_free(device);
+                pa_dbusiface_device_free(device_iface);
             }
             break;
 
@@ -1727,13 +1742,16 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
             if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
                 pa_source *source = pa_idxset_get_by_index(core->sources, idx);
 
-                if (!(device = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(idx)))) {
-                    device = pa_dbusiface_device_new_source(c, source);
-                    pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(idx), device);
-                    pa_hashmap_put(c->sources_by_path, pa_dbusiface_device_get_path(device), device);
+                if (!(source = pa_idxset_get_by_index(core->sources, idx)))
+                    return; /* The source was removed immediately after creation. */
+
+                if (!(device_iface = pa_hashmap_get(c->sources_by_index, PA_UINT32_TO_PTR(idx)))) {
+                    device_iface = pa_dbusiface_device_new_source(c, source);
+                    pa_hashmap_put(c->sources_by_index, PA_UINT32_TO_PTR(idx), device_iface);
+                    pa_hashmap_put(c->sources_by_path, pa_dbusiface_device_get_path(device_iface), device_iface);
                 }
 
-                object_path = pa_dbusiface_device_get_path(device);
+                object_path = pa_dbusiface_device_get_path(device_iface);
 
                 pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
                                                                PA_DBUS_CORE_INTERFACE,
@@ -1760,8 +1778,10 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
                 }
 
             } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
-                pa_assert_se((device = pa_hashmap_remove(c->sources_by_index, PA_UINT32_TO_PTR(idx))));
-                object_path = pa_dbusiface_device_get_path(device);
+                if (!(device_iface = pa_hashmap_remove(c->sources_by_index, PA_UINT32_TO_PTR(idx))))
+                    return;
+
+                object_path = pa_dbusiface_device_get_path(device_iface);
                 pa_assert_se(pa_hashmap_remove(c->sources_by_path, object_path));
 
                 pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
@@ -1769,18 +1789,23 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
                                                                signals[SIGNAL_SOURCE_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
-                pa_dbusiface_device_free(device);
+                pa_dbusiface_device_free(device_iface);
             }
             break;
 
         case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
             if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
-                if (!(stream = pa_hashmap_get(c->playback_streams, PA_UINT32_TO_PTR(idx)))) {
-                    stream = pa_dbusiface_stream_new_playback(c, pa_idxset_get_by_index(core->sink_inputs, idx));
-                    pa_hashmap_put(c->playback_streams, PA_UINT32_TO_PTR(idx), stream);
+                pa_sink_input *sink_input = NULL;
+
+                if (!(sink_input = pa_idxset_get_by_index(core->sink_inputs, idx)))
+                    return; /* The sink input was removed immediately after creation. */
+
+                if (!(stream_iface = pa_hashmap_get(c->playback_streams, PA_UINT32_TO_PTR(idx)))) {
+                    stream_iface = pa_dbusiface_stream_new_playback(c, sink_input);
+                    pa_hashmap_put(c->playback_streams, PA_UINT32_TO_PTR(idx), stream_iface);
                 }
 
-                object_path = pa_dbusiface_stream_get_path(stream);
+                object_path = pa_dbusiface_stream_get_path(stream_iface);
 
                 pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
                                                                PA_DBUS_CORE_INTERFACE,
@@ -1788,27 +1813,33 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
             } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
-                pa_assert_se((stream = pa_hashmap_remove(c->playback_streams, PA_UINT32_TO_PTR(idx))));
+                if (!(stream_iface = pa_hashmap_remove(c->playback_streams, PA_UINT32_TO_PTR(idx))))
+                    return;
 
-                object_path = pa_dbusiface_stream_get_path(stream);
+                object_path = pa_dbusiface_stream_get_path(stream_iface);
 
                 pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
                                                                PA_DBUS_CORE_INTERFACE,
                                                                signals[SIGNAL_PLAYBACK_STREAM_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
-                pa_dbusiface_stream_free(stream);
+                pa_dbusiface_stream_free(stream_iface);
             }
             break;
 
         case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT:
             if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
-                if (!(stream = pa_hashmap_get(c->record_streams, PA_UINT32_TO_PTR(idx)))) {
-                    stream = pa_dbusiface_stream_new_record(c, pa_idxset_get_by_index(core->source_outputs, idx));
-                    pa_hashmap_put(c->record_streams, PA_UINT32_TO_PTR(idx), stream);
+                pa_source_output *source_output = NULL;
+
+                if (!(source_output = pa_idxset_get_by_index(core->source_outputs, idx)))
+                    return; /* The source output was removed immediately after creation. */
+
+                if (!(stream_iface = pa_hashmap_get(c->record_streams, PA_UINT32_TO_PTR(idx)))) {
+                    stream_iface = pa_dbusiface_stream_new_record(c, source_output);
+                    pa_hashmap_put(c->record_streams, PA_UINT32_TO_PTR(idx), stream_iface);
                 }
 
-                object_path = pa_dbusiface_stream_get_path(stream);
+                object_path = pa_dbusiface_stream_get_path(stream_iface);
 
                 pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
                                                                PA_DBUS_CORE_INTERFACE,
@@ -1816,27 +1847,33 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
             } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
-                pa_assert_se((stream = pa_hashmap_remove(c->record_streams, PA_UINT32_TO_PTR(idx))));
+                if (!(stream_iface = pa_hashmap_remove(c->record_streams, PA_UINT32_TO_PTR(idx))))
+                    return;
 
-                object_path = pa_dbusiface_stream_get_path(stream);
+                object_path = pa_dbusiface_stream_get_path(stream_iface);
 
                 pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
                                                                PA_DBUS_CORE_INTERFACE,
                                                                signals[SIGNAL_RECORD_STREAM_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
-                pa_dbusiface_stream_free(stream);
+                pa_dbusiface_stream_free(stream_iface);
             }
             break;
 
         case PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE:
             if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
-                if (!(sample = pa_hashmap_get(c->samples, PA_UINT32_TO_PTR(idx)))) {
-                    sample = pa_dbusiface_sample_new(c, pa_idxset_get_by_index(core->scache, idx));
-                    pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), sample);
+                pa_scache_entry *sample = NULL;
+
+                if (!(sample = pa_idxset_get_by_index(core->scache, idx)))
+                    return; /* The sample was removed immediately after creation. */
+
+                if (!(sample_iface = pa_hashmap_get(c->samples, PA_UINT32_TO_PTR(idx)))) {
+                    sample_iface = pa_dbusiface_sample_new(c, sample);
+                    pa_hashmap_put(c->samples, PA_UINT32_TO_PTR(idx), sample_iface);
                 }
 
-                object_path = pa_dbusiface_sample_get_path(sample);
+                object_path = pa_dbusiface_sample_get_path(sample_iface);
 
                 pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
                                                                PA_DBUS_CORE_INTERFACE,
@@ -1844,27 +1881,33 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
             } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
-                pa_assert_se((sample = pa_hashmap_remove(c->samples, PA_UINT32_TO_PTR(idx))));
+                if (!(sample_iface = pa_hashmap_remove(c->samples, PA_UINT32_TO_PTR(idx))))
+                    return;
 
-                object_path = pa_dbusiface_sample_get_path(sample);
+                object_path = pa_dbusiface_sample_get_path(sample_iface);
 
                 pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
                                                                PA_DBUS_CORE_INTERFACE,
                                                                signals[SIGNAL_SAMPLE_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
-                pa_dbusiface_sample_free(sample);
+                pa_dbusiface_sample_free(sample_iface);
             }
             break;
 
         case PA_SUBSCRIPTION_EVENT_MODULE:
             if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
-                if (!(module = pa_hashmap_get(c->modules, PA_UINT32_TO_PTR(idx)))) {
-                    module = pa_dbusiface_module_new(pa_idxset_get_by_index(core->modules, idx));
-                    pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), module);
+                pa_module *module = NULL;
+
+                if (!(module = pa_idxset_get_by_index(core->modules, idx)))
+                    return; /* The module was removed immediately after creation. */
+
+                if (!(module_iface = pa_hashmap_get(c->modules, PA_UINT32_TO_PTR(idx)))) {
+                    module_iface = pa_dbusiface_module_new(module);
+                    pa_hashmap_put(c->modules, PA_UINT32_TO_PTR(idx), module_iface);
                 }
 
-                object_path = pa_dbusiface_module_get_path(module);
+                object_path = pa_dbusiface_module_get_path(module_iface);
 
                 pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
                                                                PA_DBUS_CORE_INTERFACE,
@@ -1872,27 +1915,33 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
             } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
-                pa_assert_se((module = pa_hashmap_remove(c->modules, PA_UINT32_TO_PTR(idx))));
+                if (!(module_iface = pa_hashmap_remove(c->modules, PA_UINT32_TO_PTR(idx))))
+                    return;
 
-                object_path = pa_dbusiface_module_get_path(module);
+                object_path = pa_dbusiface_module_get_path(module_iface);
 
                 pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
                                                                PA_DBUS_CORE_INTERFACE,
                                                                signals[SIGNAL_MODULE_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
-                pa_dbusiface_module_free(module);
+                pa_dbusiface_module_free(module_iface);
             }
             break;
 
         case PA_SUBSCRIPTION_EVENT_CLIENT:
             if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
-                if (!(client = pa_hashmap_get(c->clients, PA_UINT32_TO_PTR(idx)))) {
-                    client = pa_dbusiface_client_new(c, pa_idxset_get_by_index(core->clients, idx));
-                    pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), client);
+                pa_client *client = NULL;
+
+                if (!(client = pa_idxset_get_by_index(core->clients, idx)))
+                    return; /* The client was removed immediately after creation. */
+
+                if (!(client_iface = pa_hashmap_get(c->clients, PA_UINT32_TO_PTR(idx)))) {
+                    client_iface = pa_dbusiface_client_new(c, client);
+                    pa_hashmap_put(c->clients, PA_UINT32_TO_PTR(idx), client_iface);
                 }
 
-                object_path = pa_dbusiface_client_get_path(client);
+                object_path = pa_dbusiface_client_get_path(client_iface);
 
                 pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
                                                                PA_DBUS_CORE_INTERFACE,
@@ -1900,16 +1949,17 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
             } else if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
-                pa_assert_se((client = pa_hashmap_remove(c->clients, PA_UINT32_TO_PTR(idx))));
+                if (!(client_iface = pa_hashmap_remove(c->clients, PA_UINT32_TO_PTR(idx))))
+                    return;
 
-                object_path = pa_dbusiface_client_get_path(client);
+                object_path = pa_dbusiface_client_get_path(client_iface);
 
                 pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
                                                                PA_DBUS_CORE_INTERFACE,
                                                                signals[SIGNAL_CLIENT_REMOVED].name)));
                 pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID));
 
-                pa_dbusiface_client_free(client);
+                pa_dbusiface_client_free(client_iface);
             }
             break;
     }
@@ -2001,6 +2051,11 @@ pa_dbusiface_core *pa_dbusiface_core_new(pa_core *core) {
                                                                    c);
     c->memstats = pa_dbusiface_memstats_new(c, core);
 
+    if (c->fallback_sink)
+        pa_sink_ref(c->fallback_sink);
+    if (c->fallback_source)
+        pa_source_ref(c->fallback_source);
+
     PA_IDXSET_FOREACH(card, core->cards, idx)
         pa_hashmap_put(c->cards, PA_UINT32_TO_PTR(idx), pa_dbusiface_card_new(c, card));
 
@@ -2104,6 +2159,11 @@ void pa_dbusiface_core_free(pa_dbusiface_core *c) {
     pa_hook_slot_free(c->extension_unregistered_slot);
     pa_dbusiface_memstats_free(c->memstats);
 
+    if (c->fallback_sink)
+        pa_sink_unref(c->fallback_sink);
+    if (c->fallback_source)
+        pa_source_unref(c->fallback_source);
+
     pa_dbus_protocol_unref(c->dbus_protocol);
     pa_core_unref(c->core);
 
diff --git a/src/modules/dbus/iface-module.c b/src/modules/dbus/iface-module.c
index e3882fc..e8aea50 100644
--- a/src/modules/dbus/iface-module.c
+++ b/src/modules/dbus/iface-module.c
@@ -274,6 +274,11 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
     pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_MODULE);
     pa_assert(m);
 
+    /* We can't use idx != m->module->index, because the m->module pointer may
+     * be stale at this point. */
+    if (pa_idxset_get_by_index(core->modules, idx) != m->module)
+        return;
+
     if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE)
         return;
 
diff --git a/src/modules/dbus/iface-sample.c b/src/modules/dbus/iface-sample.c
index 4cffd59..7147be1 100644
--- a/src/modules/dbus/iface-sample.c
+++ b/src/modules/dbus/iface-sample.c
@@ -463,7 +463,9 @@ static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t
     pa_assert(c);
     pa_assert(s);
 
-    if (idx != s->sample->index)
+    /* We can't use idx != s->sample->index, because the s->sample pointer may
+     * be stale at this point. */
+    if (pa_idxset_get_by_index(c->scache, idx) != s->sample)
         return;
 
     if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE)

commit 11fcc8c85f15dba8e78dffb88b3d0d04ebc329e1
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Wed Aug 26 14:19:11 2009 +0300

    dbusiface-stream: Only send stream event signals from the right D-Bus objects.

diff --git a/src/modules/dbus/iface-stream.c b/src/modules/dbus/iface-stream.c
index 354ca6e..183625b 100644
--- a/src/modules/dbus/iface-stream.c
+++ b/src/modules/dbus/iface-stream.c
@@ -786,11 +786,17 @@ static pa_hook_result_t send_event_cb(void *hook_data, void *call_data, void *sl
     if (s->type == STREAM_TYPE_PLAYBACK) {
         pa_sink_input_send_event_hook_data *data = call_data;
 
+        if (data->sink_input != s->sink_input)
+            return PA_HOOK_OK;
+
         name = data->event;
         property_list = data->data;
     } else {
         pa_source_output_send_event_hook_data *data = call_data;
 
+        if (data->source_output != s->source_output)
+            return PA_HOOK_OK;
+
         name = data->event;
         property_list = data->data;
     }

commit 219f7508f6420f94ad8c426c6aa3dc79df246f36
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Wed Aug 26 14:20:26 2009 +0300

    dbus: Finish the Client D-Bus interface.

diff --git a/src/modules/dbus/iface-client.c b/src/modules/dbus/iface-client.c
index 587d5c4..a68259a 100644
--- a/src/modules/dbus/iface-client.c
+++ b/src/modules/dbus/iface-client.c
@@ -39,8 +39,10 @@ struct pa_dbusiface_client {
 
     pa_client *client;
     char *path;
+    pa_proplist *proplist;
 
     pa_dbus_protocol *dbus_protocol;
+    pa_subscription *subscription;
 };
 
 static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata);
@@ -52,9 +54,9 @@ static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, voi
 
 static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
 
-/*static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_update_properties(DBusConnection *conn, DBusMessage *msg, void *userdata);
-static void handle_remove_properties(DBusConnection *conn, DBusMessage *msg, void *userdata);*/
+static void handle_remove_properties(DBusConnection *conn, DBusMessage *msg, void *userdata);
 
 enum property_handler_index {
     PROPERTY_HANDLER_INDEX,
@@ -75,7 +77,7 @@ static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
     [PROPERTY_HANDLER_PROPERTY_LIST]    = { .property_name = "PropertyList",    .type = "a{say}", .get_cb = handle_get_property_list,    .set_cb = NULL }
 };
 
-/*enum method_handler_index {
+enum method_handler_index {
     METHOD_HANDLER_KILL,
     METHOD_HANDLER_UPDATE_PROPERTIES,
     METHOD_HANDLER_REMOVE_PROPERTIES,
@@ -83,7 +85,7 @@ static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
 };
 
 static pa_dbus_arg_info update_properties_args[] = { { "property_list", "a{say}", "in" }, { "update_mode", "u", "in" } };
-static pa_dbus_arg_info update_properties_args[] = { { "keys", "as", "in" } };
+static pa_dbus_arg_info remove_properties_args[] = { { "keys", "as", "in" } };
 
 static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
     [METHOD_HANDLER_KILL] = {
@@ -93,14 +95,14 @@ static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
         .receive_cb = handle_kill },
     [METHOD_HANDLER_UPDATE_PROPERTIES] = {
         .method_name = "UpdateProperties",
-        .arguments = update_propertes_args,
+        .arguments = update_properties_args,
         .n_arguments = sizeof(update_properties_args) / sizeof(pa_dbus_arg_info),
         .receive_cb = handle_update_properties },
     [METHOD_HANDLER_REMOVE_PROPERTIES] = {
         .method_name = "RemoveProperties",
-        .arguments = remove_propertes_args,
+        .arguments = remove_properties_args,
         .n_arguments = sizeof(update_properties_args) / sizeof(pa_dbus_arg_info),
-        .receive_cb = handle_update_properties }
+        .receive_cb = handle_remove_properties }
 };
 
 enum signal_index {
@@ -109,23 +111,25 @@ enum signal_index {
     SIGNAL_MAX
 };
 
-static pa_dbus_arg_info active_profile_updated_args[] = { { "profile",       "o",      NULL } };
-static pa_dbus_arg_info property_list_updated_args[]  = { { "property_list", "a{say}", NULL } };
+static pa_dbus_arg_info property_list_updated_args[] = { { "property_list", "a{say}", NULL } };
+static pa_dbus_arg_info client_event_args[]          = { { "name",          "s",      NULL },
+                                                         { "property_list", "a{say}", NULL } };
 
 static pa_dbus_signal_info signals[SIGNAL_MAX] = {
-    [SIGNAL_ACTIVE_PROFILE_UPDATED] = { .name = "ActiveProfileUpdated", .arguments = active_profile_updated_args, .n_arguments = 1 },
-    [SIGNAL_PROPERTY_LIST_UPDATED]  = { .name = "PropertyListUpdated",  .arguments = property_list_updated_args,  .n_arguments = 1 }
-};*/
+    [SIGNAL_PROPERTY_LIST_UPDATED] = { .name = "PropertyListUpdated", .arguments = property_list_updated_args, .n_arguments = 1 },
+    /* ClientEvent is sent from module-dbus-protocol.c. */
+    [SIGNAL_CLIENT_EVENT]          = { .name = "ClientEvent",         .arguments = client_event_args,          .n_arguments = 1 }
+};
 
 static pa_dbus_interface_info client_interface_info = {
     .name = PA_DBUSIFACE_CLIENT_INTERFACE,
-    .method_handlers = /*method_handlers*/ NULL,
-    .n_method_handlers = /*METHOD_HANDLER_MAX*/ 0,
+    .method_handlers = method_handlers,
+    .n_method_handlers = METHOD_HANDLER_MAX,
     .property_handlers = property_handlers,
     .n_property_handlers = PROPERTY_HANDLER_MAX,
     .get_all_properties_cb = handle_get_all,
-    .signals = /*signals*/ NULL,
-    .n_signals = /*SIGNAL_MAX*/ 0
+    .signals = signals,
+    .n_signals = SIGNAL_MAX
 };
 
 static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) {
@@ -304,6 +308,131 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat
     pa_xfree(record_streams);
 }
 
+static void handle_kill(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_client *c = userdata;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    dbus_connection_ref(conn);
+
+    pa_client_kill(c->client);
+
+    pa_dbus_send_empty_reply(conn, msg);
+
+    dbus_connection_unref(conn);
+}
+
+static void handle_update_properties(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_client *c = userdata;
+    DBusMessageIter msg_iter;
+    pa_proplist *property_list = NULL;
+    dbus_uint32_t update_mode = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    if (pa_dbus_protocol_get_client(c->dbus_protocol, conn) != c->client) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "Client tried to modify the property list of another client.");
+        return;
+    }
+
+    if (!dbus_message_iter_init(msg, &msg_iter)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments.");
+        return;
+    }
+
+    if (!(property_list = pa_dbus_get_proplist_arg(conn, msg, &msg_iter)))
+        return;
+
+    if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &update_mode) < 0)
+        goto finish;
+
+    if (!(update_mode == PA_UPDATE_SET || update_mode == PA_UPDATE_MERGE || update_mode == PA_UPDATE_REPLACE)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid update mode: %u", update_mode);
+        goto finish;
+    }
+
+    pa_client_update_proplist(c->client, update_mode, property_list);
+
+    pa_dbus_send_empty_reply(conn, msg);
+
+finish:
+    if (property_list)
+        pa_proplist_free(property_list);
+}
+
+static void handle_remove_properties(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+    pa_dbusiface_client *c = userdata;
+    char **keys = NULL;
+    int n_keys = 0;
+    DBusError error;
+    pa_bool_t changed = FALSE;
+    int i = 0;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+
+    dbus_error_init(&error);
+
+    if (pa_dbus_protocol_get_client(c->dbus_protocol, conn) != c->client) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "Client tried to modify the property list of another client.");
+        return;
+    }
+
+    if (!dbus_message_get_args(msg, &error, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &keys, &n_keys, DBUS_TYPE_INVALID)) {
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
+        dbus_error_free(&error);
+        return;
+    }
+
+    for (i = 0; i < n_keys; ++i)
+        changed |= pa_proplist_unset(c->client->proplist, keys[i]) >= 0;
+
+    pa_dbus_send_empty_reply(conn, msg);
+
+    if (changed)
+        pa_subscription_post(c->client->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->client->index);
+
+    dbus_free_string_array(keys);
+}
+
+static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
+    pa_dbusiface_client *c = userdata;
+    DBusMessage *signal = NULL;
+
+    pa_assert(core);
+    pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_CLIENT);
+    pa_assert(c);
+
+    /* We can't use idx != c->client->index, because the c->client pointer may
+     * be stale at this point. */
+    if (pa_idxset_get_by_index(core->clients, idx) != c->client)
+        return;
+
+    if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE)
+        return;
+
+    if (!pa_proplist_equal(c->proplist, c->client->proplist)) {
+        DBusMessageIter msg_iter;
+
+        pa_proplist_update(c->proplist, PA_UPDATE_SET, c->client->proplist);
+
+        pa_assert_se(signal = dbus_message_new_signal(c->path,
+                                                      PA_DBUSIFACE_CLIENT_INTERFACE,
+                                                      signals[SIGNAL_PROPERTY_LIST_UPDATED].name));
+        dbus_message_iter_init_append(signal, &msg_iter);
+        pa_dbus_append_proplist(&msg_iter, c->proplist);
+
+        pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
+        dbus_message_unref(signal);
+        signal = NULL;
+    }
+}
+
 pa_dbusiface_client *pa_dbusiface_client_new(pa_dbusiface_core *core, pa_client *client) {
     pa_dbusiface_client *c = NULL;
 
@@ -314,7 +443,9 @@ pa_dbusiface_client *pa_dbusiface_client_new(pa_dbusiface_core *core, pa_client
     c->core = core;
     c->client = client;
     c->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, OBJECT_NAME, client->index);
+    c->proplist = pa_proplist_copy(client->proplist);
     c->dbus_protocol = pa_dbus_protocol_get(client->core);
+    c->subscription = pa_subscription_new(client->core, PA_SUBSCRIPTION_MASK_CLIENT, subscription_cb, c);
 
     pa_assert_se(pa_dbus_protocol_add_interface(c->dbus_protocol, c->path, &client_interface_info, c) >= 0);
 
diff --git a/src/modules/dbus/module-dbus-protocol.c b/src/modules/dbus/module-dbus-protocol.c
index 807d32d..11064c3 100644
--- a/src/modules/dbus/module-dbus-protocol.c
+++ b/src/modules/dbus/module-dbus-protocol.c
@@ -40,6 +40,7 @@
 #include <pulsecore/module.h>
 #include <pulsecore/protocol-dbus.h>
 
+#include "iface-client.h"
 #include "iface-core.h"
 
 #include "module-dbus-protocol-symdef.h"
@@ -117,10 +118,36 @@ static void client_kill_cb(pa_client *c) {
 
     conn = c->userdata;
     connection_free(conn);
+    c->userdata = NULL;
 
     pa_log_info("Connection killed.");
 }
 
+/* Called from pa_client_send_event(). */
+static void client_send_event_cb(pa_client *c, const char *name, pa_proplist *data) {
+    struct connection *conn = NULL;
+    DBusMessage *signal = NULL;
+    DBusMessageIter msg_iter;
+
+    pa_assert(c);
+    pa_assert(name);
+    pa_assert(data);
+    pa_assert(c->userdata);
+
+    conn = c->userdata;
+
+    pa_assert_se(signal = dbus_message_new_signal(pa_dbusiface_core_get_client_path(conn->server->userdata->core_iface, c),
+                                                  PA_DBUSIFACE_CLIENT_INTERFACE,
+                                                  "ClientEvent"));
+    dbus_message_iter_init_append(signal, &msg_iter);
+    pa_assert_se(dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_STRING, &name));
+    pa_dbus_append_proplist(&msg_iter, data);
+
+    pa_assert_se(dbus_connection_send(pa_dbus_wrap_connection_get(conn->wrap_conn), signal, NULL));
+    dbus_message_unref(signal);
+}
+
+/* Called by D-Bus at the authentication phase. */
 static dbus_bool_t user_check_cb(DBusConnection *connection, unsigned long uid, void *data) {
     pa_log_debug("Allowing connection by user %lu.", uid);
 
@@ -140,7 +167,7 @@ static void connection_new_cb(DBusServer *dbus_server, DBusConnection *new_conne
     pa_client_new_data_init(&new_data);
     new_data.module = s->userdata->module;
     new_data.driver = __FILE__;
-    pa_proplist_sets(new_data.proplist, PA_PROP_APPLICATION_NAME, "D-Bus client"); /* TODO: It's probably possible to generate a fancier name. Other props? */
+    pa_proplist_sets(new_data.proplist, PA_PROP_APPLICATION_NAME, "D-Bus client");
     client = pa_client_new(s->userdata->module->core, &new_data);
     pa_client_new_data_done(&new_data);
 
@@ -162,7 +189,7 @@ static void connection_new_cb(DBusServer *dbus_server, DBusConnection *new_conne
     c->client = client;
 
     c->client->kill = client_kill_cb;
-    c->client->send_event = NULL; /* TODO: Implement this. */
+    c->client->send_event = client_send_event_cb;
     c->client->userdata = c;
 
     pa_idxset_put(s->userdata->connections, c, NULL);

commit 7bc8a793b8ae4c46e59a444313e4d06186de1680
Merge: 219f750 4614412
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Wed Aug 26 14:29:55 2009 +0300

    Merge branch 'master' of git://0pointer.de/pulseaudio into dbus-work
    
    Conflicts:
    	src/Makefile.am

diff --cc src/Makefile.am
index c3ca1eb,e65662c..16834ee
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@@ -1253,7 -1250,7 +1254,8 @@@ SYMDEF_FILES = 
  		modules/module-augment-properties-symdef.h \
  		modules/module-cork-music-on-phone-symdef.h \
  		modules/module-console-kit-symdef.h \
- 		modules/dbus/module-dbus-protocol-symdef.h
++		modules/dbus/module-dbus-protocol-symdef.h \
+ 		modules/module-loopback-symdef.h
  
  EXTRA_DIST += $(SYMDEF_FILES)
  BUILT_SOURCES += $(SYMDEF_FILES)

commit 0e096632c53b746b9f4b4c0249d9e5a18c1c543d
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Sun Aug 30 19:52:22 2009 +0300

    dbus: Do message argument type checking early, centrally.

diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c
index 7e37f8b..1714df3 100644
--- a/src/modules/dbus/iface-card.c
+++ b/src/modules/dbus/iface-card.c
@@ -43,7 +43,7 @@ static void handle_get_sinks(DBusConnection *conn, DBusMessage *msg, void *userd
 static void handle_get_sources(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_profiles(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_active_profile(DBusConnection *conn, DBusMessage *msg, void *userdata);
-static void handle_set_active_profile(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_active_profile(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
 static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata);
 
 static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
@@ -322,7 +322,7 @@ static void handle_get_active_profile(DBusConnection *conn, DBusMessage *msg, vo
     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &active_profile);
 }
 
-static void handle_set_active_profile(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+static void handle_set_active_profile(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
     pa_dbusiface_card *c = userdata;
     const char *new_active_path;
     pa_dbusiface_card_profile *new_active;
@@ -330,11 +330,9 @@ static void handle_set_active_profile(DBusConnection *conn, DBusMessage *msg, vo
 
     pa_assert(conn);
     pa_assert(msg);
+    pa_assert(iter);
     pa_assert(c);
 
-    if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_OBJECT_PATH, &new_active_path) < 0)
-        return;
-
     if (!c->active_profile) {
         pa_assert(pa_hashmap_isempty(c->profiles));
 
@@ -344,6 +342,8 @@ static void handle_set_active_profile(DBusConnection *conn, DBusMessage *msg, vo
         return;
     }
 
+    dbus_message_iter_get_basic(iter, &new_active_path);
+
     if (!(new_active = pa_hashmap_get(c->profiles, new_active_path))) {
         pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such profile.", new_active_path);
         return;
@@ -430,7 +430,6 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat
 
 static void handle_get_profile_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
     pa_dbusiface_card *c = userdata;
-    DBusError error;
     const char *profile_name = NULL;
     pa_dbusiface_card_profile *profile = NULL;
     const char *profile_path = NULL;
@@ -439,13 +438,7 @@ static void handle_get_profile_by_name(DBusConnection *conn, DBusMessage *msg, v
     pa_assert(msg);
     pa_assert(c);
 
-    dbus_error_init(&error);
-
-    if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &profile_name, DBUS_TYPE_INVALID)) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
-        dbus_error_free(&error);
-        return;
-    }
+    pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &profile_name, DBUS_TYPE_INVALID));
 
     if (!(profile = pa_hashmap_get(c->profiles, profile_name))) {
         pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such profile on card %s.", profile_name, c->card->name);
diff --git a/src/modules/dbus/iface-client.c b/src/modules/dbus/iface-client.c
index a68259a..54550d2 100644
--- a/src/modules/dbus/iface-client.c
+++ b/src/modules/dbus/iface-client.c
@@ -339,16 +339,12 @@ static void handle_update_properties(DBusConnection *conn, DBusMessage *msg, voi
         return;
     }
 
-    if (!dbus_message_iter_init(msg, &msg_iter)) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments.");
-        return;
-    }
+    pa_assert_se(dbus_message_iter_init(msg, &msg_iter));
 
     if (!(property_list = pa_dbus_get_proplist_arg(conn, msg, &msg_iter)))
         return;
 
-    if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &update_mode) < 0)
-        goto finish;
+    dbus_message_iter_get_basic(&msg_iter, &update_mode);
 
     if (!(update_mode == PA_UPDATE_SET || update_mode == PA_UPDATE_MERGE || update_mode == PA_UPDATE_REPLACE)) {
         pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid update mode: %u", update_mode);
@@ -368,7 +364,6 @@ static void handle_remove_properties(DBusConnection *conn, DBusMessage *msg, voi
     pa_dbusiface_client *c = userdata;
     char **keys = NULL;
     int n_keys = 0;
-    DBusError error;
     pa_bool_t changed = FALSE;
     int i = 0;
 
@@ -376,18 +371,12 @@ static void handle_remove_properties(DBusConnection *conn, DBusMessage *msg, voi
     pa_assert(msg);
     pa_assert(c);
 
-    dbus_error_init(&error);
-
     if (pa_dbus_protocol_get_client(c->dbus_protocol, conn) != c->client) {
         pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "Client tried to modify the property list of another client.");
         return;
     }
 
-    if (!dbus_message_get_args(msg, &error, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &keys, &n_keys, DBUS_TYPE_INVALID)) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
-        dbus_error_free(&error);
-        return;
-    }
+    pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &keys, &n_keys, DBUS_TYPE_INVALID));
 
     for (i = 0; i < n_keys; ++i)
         changed |= pa_proplist_unset(c->client->proplist, keys[i]) >= 0;
diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c
index c54a3b7..0507ac9 100644
--- a/src/modules/dbus/iface-core.c
+++ b/src/modules/dbus/iface-core.c
@@ -57,18 +57,18 @@ static void handle_get_is_local(DBusConnection *conn, DBusMessage *msg, void *us
 static void handle_get_username(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_hostname(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_default_channels(DBusConnection *conn, DBusMessage *msg, void *userdata);
-static void handle_set_default_channels(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_default_channels(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
 static void handle_get_default_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata);
-static void handle_set_default_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_default_sample_format(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
 static void handle_get_default_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata);
-static void handle_set_default_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_default_sample_rate(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
 static void handle_get_cards(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_sinks(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_fallback_sink(DBusConnection *conn, DBusMessage *msg, void *userdata);
-static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
 static void handle_get_sources(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_fallback_source(DBusConnection *conn, DBusMessage *msg, void *userdata);
-static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
 static void handle_get_playback_streams(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_record_streams(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_samples(DBusConnection *conn, DBusMessage *msg, void *userdata);
@@ -428,30 +428,32 @@ static void handle_get_default_channels(DBusConnection *conn, DBusMessage *msg,
     pa_xfree(default_channels);
 }
 
-static void handle_set_default_channels(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+static void handle_set_default_channels(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
     pa_dbusiface_core *c = userdata;
+    DBusMessageIter array_iter;
     pa_channel_map new_channel_map;
-    dbus_uint32_t *default_channels;
-    unsigned n_channels;
+    const dbus_uint32_t *default_channels;
+    int n_channels;
     unsigned i;
 
     pa_assert(conn);
     pa_assert(msg);
+    pa_assert(iter);
     pa_assert(c);
 
     pa_channel_map_init(&new_channel_map);
 
-    if (pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_UINT32, &default_channels, &n_channels) < 0)
-        return;
+    dbus_message_iter_recurse(iter, &array_iter);
+    dbus_message_iter_get_fixed_array(&array_iter, &default_channels, &n_channels);
 
     if (n_channels <= 0) {
         pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Empty channel array.");
         return;
     }
 
-    if (n_channels > PA_CHANNELS_MAX) {
+    if (n_channels > (int) PA_CHANNELS_MAX) {
         pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
-                           "Too many channels: %u. The maximum number of channels is %u.", n_channels, PA_CHANNELS_MAX);
+                           "Too many channels: %i. The maximum number of channels is %u.", n_channels, PA_CHANNELS_MAX);
         return;
     }
 
@@ -485,16 +487,16 @@ static void handle_get_default_sample_format(DBusConnection *conn, DBusMessage *
     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &default_sample_format);
 }
 
-static void handle_set_default_sample_format(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+static void handle_set_default_sample_format(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
     pa_dbusiface_core *c = userdata;
     dbus_uint32_t default_sample_format;
 
     pa_assert(conn);
     pa_assert(msg);
+    pa_assert(iter);
     pa_assert(c);
 
-    if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_UINT32, &default_sample_format) < 0)
-        return;
+    dbus_message_iter_get_basic(iter, &default_sample_format);
 
     if (default_sample_format >= PA_SAMPLE_MAX) {
         pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid sample format.");
@@ -519,16 +521,16 @@ static void handle_get_default_sample_rate(DBusConnection *conn, DBusMessage *ms
     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &default_sample_rate);
 }
 
-static void handle_set_default_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+static void handle_set_default_sample_rate(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
     pa_dbusiface_core *c = userdata;
     dbus_uint32_t default_sample_rate;
 
     pa_assert(conn);
     pa_assert(msg);
+    pa_assert(iter);
     pa_assert(c);
 
-    if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_UINT32, &default_sample_rate) < 0)
-        return;
+    dbus_message_iter_get_basic(iter, &default_sample_rate);
 
     if (default_sample_rate <= 0 || default_sample_rate > PA_RATE_MAX) {
         pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid sample rate.");
@@ -639,13 +641,14 @@ static void handle_get_fallback_sink(DBusConnection *conn, DBusMessage *msg, voi
     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
 }
 
-static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
     pa_dbusiface_core *c = userdata;
     pa_dbusiface_device *fallback_sink;
     const char *object_path;
 
     pa_assert(conn);
     pa_assert(msg);
+    pa_assert(iter);
     pa_assert(c);
 
     if (!c->fallback_sink) {
@@ -654,8 +657,7 @@ static void handle_set_fallback_sink(DBusConnection *conn, DBusMessage *msg, voi
         return;
     }
 
-    if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path) < 0)
-        return;
+    dbus_message_iter_get_basic(iter, &object_path);
 
     if (!(fallback_sink = pa_hashmap_get(c->sinks_by_path, object_path))) {
         pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such sink.", object_path);
@@ -727,13 +729,14 @@ static void handle_get_fallback_source(DBusConnection *conn, DBusMessage *msg, v
     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path);
 }
 
-static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
     pa_dbusiface_core *c = userdata;
     pa_dbusiface_device *fallback_source;
     const char *object_path;
 
     pa_assert(conn);
     pa_assert(msg);
+    pa_assert(iter);
     pa_assert(c);
 
     if (!c->fallback_source) {
@@ -742,8 +745,7 @@ static void handle_set_fallback_source(DBusConnection *conn, DBusMessage *msg, v
         return;
     }
 
-    if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_OBJECT_PATH, &object_path) < 0)
-        return;
+    dbus_message_iter_get_basic(iter, &object_path);
 
     if (!(fallback_source = pa_hashmap_get(c->sources_by_path, object_path))) {
         pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such source.", object_path);
@@ -1117,19 +1119,12 @@ static void handle_get_card_by_name(DBusConnection *conn, DBusMessage *msg, void
     pa_card *card;
     pa_dbusiface_card *dbus_card;
     const char *object_path;
-    DBusError error;
 
     pa_assert(conn);
     pa_assert(msg);
     pa_assert(c);
 
-    dbus_error_init(&error);
-
-    if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &card_name, DBUS_TYPE_INVALID)) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
-        dbus_error_free(&error);
-        return;
-    }
+    pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &card_name, DBUS_TYPE_INVALID));
 
     if (!(card = pa_namereg_get(c->core, card_name, PA_NAMEREG_CARD))) {
         pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such card.");
@@ -1149,19 +1144,12 @@ static void handle_get_sink_by_name(DBusConnection *conn, DBusMessage *msg, void
     pa_sink *sink;
     pa_dbusiface_device *dbus_sink;
     const char *object_path;
-    DBusError error;
 
     pa_assert(conn);
     pa_assert(msg);
     pa_assert(c);
 
-    dbus_error_init(&error);
-
-    if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &sink_name, DBUS_TYPE_INVALID)) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
-        dbus_error_free(&error);
-        return;
-    }
+    pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &sink_name, DBUS_TYPE_INVALID));
 
     if (!(sink = pa_namereg_get(c->core, sink_name, PA_NAMEREG_SINK))) {
         pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such sink.", sink_name);
@@ -1181,19 +1169,12 @@ static void handle_get_source_by_name(DBusConnection *conn, DBusMessage *msg, vo
     pa_source *source;
     pa_dbusiface_device *dbus_source;
     const char *object_path;
-    DBusError error;
 
     pa_assert(conn);
     pa_assert(msg);
     pa_assert(c);
 
-    dbus_error_init(&error);
-
-    if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &source_name, DBUS_TYPE_INVALID)) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
-        dbus_error_free(&error);
-        return;
-    }
+    pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &source_name, DBUS_TYPE_INVALID));
 
     if (!(source = pa_namereg_get(c->core, source_name, PA_NAMEREG_SOURCE))) {
         pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such source.", source_name);
@@ -1213,19 +1194,12 @@ static void handle_get_sample_by_name(DBusConnection *conn, DBusMessage *msg, vo
     pa_scache_entry *sample;
     pa_dbusiface_sample *dbus_sample;
     const char *object_path;
-    DBusError error;
 
     pa_assert(conn);
     pa_assert(msg);
     pa_assert(c);
 
-    dbus_error_init(&error);
-
-    if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &sample_name, DBUS_TYPE_INVALID)) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
-        dbus_error_free(&error);
-        return;
-    }
+    pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &sample_name, DBUS_TYPE_INVALID));
 
     if (!(sample = pa_namereg_get(c->core, sample_name, PA_NAMEREG_SAMPLE))) {
         pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such sample.");
@@ -1242,17 +1216,18 @@ static void handle_get_sample_by_name(DBusConnection *conn, DBusMessage *msg, vo
 static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *userdata) {
     pa_dbusiface_core *c = userdata;
     DBusMessageIter msg_iter;
+    DBusMessageIter array_iter;
     const char *name;
     dbus_uint32_t sample_format;
     dbus_uint32_t sample_rate;
     const dbus_uint32_t *channels;
-    unsigned n_channels;
+    int n_channels;
     const dbus_uint32_t *default_volume;
-    unsigned n_volume_entries;
+    int n_volume_entries;
     pa_proplist *property_list;
     const uint8_t *data;
-    unsigned data_length;
-    unsigned i;
+    int data_length;
+    int i;
     pa_sample_spec ss;
     pa_channel_map map;
     pa_memchunk chunk;
@@ -1267,31 +1242,29 @@ static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *u
 
     chunk.memblock = NULL;
 
-    if (!dbus_message_iter_init(msg, &msg_iter)) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments.");
-        return;
-    }
-
-    if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_STRING, &name) < 0)
-        return;
+    pa_assert_se(dbus_message_iter_init(msg, &msg_iter));
+    dbus_message_iter_get_basic(&msg_iter, &name);
 
-    if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &sample_format) < 0)
-        return;
+    pa_assert_se(dbus_message_iter_next(&msg_iter));
+    dbus_message_iter_get_basic(&msg_iter, &sample_format);
 
-    if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &sample_rate) < 0)
-        return;
+    pa_assert_se(dbus_message_iter_next(&msg_iter));
+    dbus_message_iter_get_basic(&msg_iter, &sample_rate);
 
-    if (pa_dbus_get_fixed_array_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &channels, &n_channels) < 0)
-        return;
+    pa_assert_se(dbus_message_iter_next(&msg_iter));
+    dbus_message_iter_recurse(&msg_iter, &array_iter);
+    dbus_message_iter_get_fixed_array(&array_iter, &channels, &n_channels);
 
-    if (pa_dbus_get_fixed_array_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &default_volume, &n_volume_entries) < 0)
-        return;
+    pa_assert_se(dbus_message_iter_next(&msg_iter));
+    dbus_message_iter_recurse(&msg_iter, &array_iter);
+    dbus_message_iter_get_fixed_array(&array_iter, &default_volume, &n_volume_entries);
 
+    pa_assert_se(dbus_message_iter_next(&msg_iter));
     if (!(property_list = pa_dbus_get_proplist_arg(conn, msg, &msg_iter)))
         return;
 
-    if (pa_dbus_get_fixed_array_arg(conn, msg, &msg_iter, DBUS_TYPE_BYTE, &data, &data_length) < 0)
-        goto finish;
+    dbus_message_iter_recurse(&msg_iter, &array_iter);
+    dbus_message_iter_get_fixed_array(&array_iter, &data, &data_length);
 
     if (sample_format >= PA_SAMPLE_MAX) {
         pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid sample format.");
@@ -1303,14 +1276,14 @@ static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *u
         goto finish;
     }
 
-    if (n_channels == 0) {
+    if (n_channels <= 0) {
         pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Empty channel map.");
         goto finish;
     }
 
-    if (n_channels > PA_CHANNELS_MAX) {
+    if (n_channels > (int) PA_CHANNELS_MAX) {
         pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
-                           "Too many channels: %u. The maximum is %u.", n_channels, PA_CHANNELS_MAX);
+                           "Too many channels: %i. The maximum is %u.", n_channels, PA_CHANNELS_MAX);
         goto finish;
     }
 
@@ -1323,13 +1296,14 @@ static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *u
 
     if (n_volume_entries != 0 && n_volume_entries != n_channels) {
         pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
-                           "The channels and default_volume arguments have different number of elements.");
+                           "The channels and default_volume arguments have different number of elements (%i and %i, resp).",
+                           n_channels, n_volume_entries);
         goto finish;
     }
 
     for (i = 0; i < n_volume_entries; ++i) {
         if (default_volume[i] > PA_VOLUME_MAX) {
-            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid volume.");
+            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid volume: %u.", default_volume[i]);
             goto finish;
         }
     }
@@ -1340,7 +1314,9 @@ static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *u
     }
 
     if (data_length > PA_SCACHE_ENTRY_SIZE_MAX) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too big sample.");
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
+                           "Too big sample: %i bytes. The maximum sample length is %u bytes.",
+                           data_length, PA_SCACHE_ENTRY_SIZE_MAX);
         goto finish;
     }
 
@@ -1348,9 +1324,13 @@ static void handle_upload_sample(DBusConnection *conn, DBusMessage *msg, void *u
     ss.rate = sample_rate;
     ss.channels = n_channels;
 
+    pa_assert(pa_sample_spec_valid(&ss));
+
     if (!pa_frame_aligned(data_length, &ss)) {
+        char buf[PA_SAMPLE_SPEC_SNPRINT_MAX];
         pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
-                           "The sample length in bytes doesn't align with the sample format and channels.");
+                           "The sample length (%i bytes) doesn't align with the sample format and channels (%s).",
+                           data_length, pa_sample_spec_snprint(buf, sizeof(buf), &ss));
         goto finish;
     }
 
@@ -1414,15 +1394,15 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use
     DBusMessageIter msg_iter;
     DBusMessageIter dict_iter;
     DBusMessageIter dict_entry_iter;
-    char *name;
-    const char *key;
-    const char *value;
-    char *escaped_value;
+    char *name = NULL;
+    const char *key = NULL;
+    const char *value = NULL;
+    char *escaped_value = NULL;
     pa_strbuf *arg_buffer = NULL;
-    pa_module *module;
-    pa_dbusiface_module *dbus_module;
-    const char *object_path;
-    int arg_type;
+    char *arg_string = NULL;
+    pa_module *module = NULL;
+    pa_dbusiface_module *dbus_module = NULL;
+    const char *object_path = NULL;
 
     pa_assert(conn);
     pa_assert(msg);
@@ -1433,35 +1413,12 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use
         return;
     }
 
-    if (!dbus_message_iter_init(msg, &msg_iter)) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments.");
-        return;
-    }
-
-    if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_STRING, &name) < 0)
-        return;
-
-    arg_type = dbus_message_iter_get_arg_type(&msg_iter);
-    if (arg_type != DBUS_TYPE_ARRAY) {
-        if (arg_type == DBUS_TYPE_INVALID)
-            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
-                               "Too few arguments. A dictionary from strings to strings was expected.");
-        else
-            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
-                               "Wrong argument type: '%c'. An dictionary from strings to strings was expected.",
-                               (char) arg_type);
-        return;
-    }
-
-    arg_type = dbus_message_iter_get_element_type(&msg_iter);
-    if (arg_type != DBUS_TYPE_DICT_ENTRY) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
-                           "Wrong array element type: '%c'. A dict entry (string to string) was expected.", (char) arg_type);
-        return;
-    }
+    pa_assert_se(dbus_message_iter_init(msg, &msg_iter));
+    dbus_message_iter_get_basic(&msg_iter, &name);
 
     arg_buffer = pa_strbuf_new();
 
+    pa_assert_se(dbus_message_iter_next(&msg_iter));
     dbus_message_iter_recurse(&msg_iter, &dict_iter);
 
     while (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_INVALID) {
@@ -1470,41 +1427,26 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use
 
         dbus_message_iter_recurse(&dict_iter, &dict_entry_iter);
 
-        arg_type = dbus_message_iter_get_arg_type(&dict_entry_iter);
-        if (arg_type != DBUS_TYPE_STRING) {
-            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
-                               "Wrong dict key type: '%c'. A string was expected.", (char) arg_type);
-            goto finish;
-        }
-
         dbus_message_iter_get_basic(&dict_entry_iter, &key);
-        dbus_message_iter_next(&dict_entry_iter);
 
         if (strlen(key) <= 0 || !pa_ascii_valid(key) || contains_space(key)) {
             pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid module argument name: %s", key);
             goto finish;
         }
 
-        arg_type = dbus_message_iter_get_arg_type(&dict_entry_iter);
-        if (arg_type != DBUS_TYPE_STRING) {
-            if (arg_type == DBUS_TYPE_INVALID)
-                pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Dict value missing.");
-            else
-                pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
-                                   "Wrong dict value type: '%c'. A string was expected.", (char) arg_type);
-            goto finish;
-        }
-
+        pa_assert_se(dbus_message_iter_next(&dict_entry_iter));
         dbus_message_iter_get_basic(&dict_entry_iter, &value);
 
-        dbus_message_iter_next(&dict_iter);
-
         escaped_value = pa_escape(value, "\"");
         pa_strbuf_printf(arg_buffer, "%s=\"%s\"", key, escaped_value);
         pa_xfree(escaped_value);
+
+        dbus_message_iter_next(&dict_iter);
     }
 
-    if (!(module = pa_module_load(c->core, name, pa_strbuf_tostring(arg_buffer)))) {
+    arg_string = pa_strbuf_tostring(arg_buffer);
+
+    if (!(module = pa_module_load(c->core, name, arg_string))) {
         pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Failed to load module.");
         goto finish;
     }
@@ -1519,6 +1461,8 @@ static void handle_load_module(DBusConnection *conn, DBusMessage *msg, void *use
 finish:
     if (arg_buffer)
         pa_strbuf_free(arg_buffer);
+
+    pa_xfree(arg_string);
 }
 
 static void handle_exit(DBusConnection *conn, DBusMessage *msg, void *userdata) {
@@ -1543,47 +1487,32 @@ static void handle_listen_for_signal(DBusConnection *conn, DBusMessage *msg, voi
     const char *signal;
     char **objects = NULL;
     int n_objects;
-    DBusError error;
 
     pa_assert(conn);
     pa_assert(msg);
     pa_assert(c);
 
-    dbus_error_init(&error);
-
-    if (!dbus_message_get_args(msg, &error,
-                               DBUS_TYPE_STRING, &signal,
-                               DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &objects, &n_objects,
-                               DBUS_TYPE_INVALID)) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
-        dbus_error_free(&error);
-        goto finish;
-    }
+    pa_assert_se(dbus_message_get_args(msg, NULL,
+                                       DBUS_TYPE_STRING, &signal,
+                                       DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &objects, &n_objects,
+                                       DBUS_TYPE_INVALID));
 
     pa_dbus_protocol_add_signal_listener(c->dbus_protocol, conn, *signal ? signal : NULL, objects, n_objects);
 
     pa_dbus_send_empty_reply(conn, msg);
 
-finish:
     dbus_free_string_array(objects);
 }
 
 static void handle_stop_listening_for_signal(DBusConnection *conn, DBusMessage *msg, void *userdata) {
     pa_dbusiface_core *c = userdata;
     const char *signal;
-    DBusError error;
 
     pa_assert(conn);
     pa_assert(msg);
     pa_assert(c);
 
-    dbus_error_init(&error);
-
-    if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &signal, DBUS_TYPE_INVALID)) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
-        dbus_error_free(&error);
-        return;
-    }
+    pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &signal, DBUS_TYPE_INVALID));
 
     pa_dbus_protocol_remove_signal_listener(c->dbus_protocol, conn, *signal ? signal : NULL);
 
diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c
index 8f719bc..2752511 100644
--- a/src/modules/dbus/iface-device.c
+++ b/src/modules/dbus/iface-device.c
@@ -43,13 +43,13 @@ static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, voi
 static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
-static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
 static void handle_get_has_flat_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_has_convertible_to_decibel_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_base_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_volume_steps(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata);
-static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
 static void handle_get_has_hardware_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_has_hardware_mute(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_configured_latency(DBusConnection *conn, DBusMessage *msg, void *userdata);
@@ -60,7 +60,7 @@ static void handle_get_is_network_device(DBusConnection *conn, DBusMessage *msg,
 static void handle_get_state(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_ports(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_active_port(DBusConnection *conn, DBusMessage *msg, void *userdata);
-static void handle_set_active_port(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_active_port(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
 static void handle_get_property_list(DBusConnection *conn, DBusMessage *msg, void *userdata);
 
 static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
@@ -408,16 +408,18 @@ static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *user
     pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, volume, d->volume.channels);
 }
 
-static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
     pa_dbusiface_device *d = userdata;
-    unsigned device_channels = 0;
+    DBusMessageIter array_iter;
+    int device_channels = 0;
     dbus_uint32_t *volume = NULL;
-    unsigned n_volume_entries = 0;
+    int n_volume_entries = 0;
     pa_cvolume new_vol;
-    unsigned i = 0;
+    int i = 0;
 
     pa_assert(conn);
     pa_assert(msg);
+    pa_assert(iter);
     pa_assert(d);
 
     pa_cvolume_init(&new_vol);
@@ -426,12 +428,12 @@ static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *user
 
     new_vol.channels = device_channels;
 
-    if (pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_UINT32, &volume, &n_volume_entries) < 0)
-        return;
+    dbus_message_iter_recurse(iter, &array_iter);
+    dbus_message_iter_get_fixed_array(&array_iter, &volume, &n_volume_entries);
 
     if (n_volume_entries != device_channels) {
         pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
-                           "Expected %u volume entries, got %u.", device_channels, n_volume_entries);
+                           "Expected %u volume entries, got %i.", device_channels, n_volume_entries);
         return;
     }
 
@@ -515,16 +517,16 @@ static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *us
     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &d->is_muted);
 }
 
-static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
     pa_dbusiface_device *d = userdata;
     dbus_bool_t is_muted = FALSE;
 
     pa_assert(conn);
     pa_assert(msg);
+    pa_assert(iter);
     pa_assert(d);
 
-    if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_BOOLEAN, &is_muted) < 0)
-        return;
+    dbus_message_iter_get_basic(iter, &is_muted);
 
     if (d->type == DEVICE_TYPE_SINK)
         pa_sink_set_mute(d->sink, is_muted, TRUE);
@@ -722,7 +724,7 @@ static void handle_get_active_port(DBusConnection *conn, DBusMessage *msg, void
     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, &active_port);
 }
 
-static void handle_set_active_port(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+static void handle_set_active_port(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
     pa_dbusiface_device *d = userdata;
     const char *new_active_path;
     pa_dbusiface_device_port *new_active;
@@ -730,11 +732,9 @@ static void handle_set_active_port(DBusConnection *conn, DBusMessage *msg, void
 
     pa_assert(conn);
     pa_assert(msg);
+    pa_assert(iter);
     pa_assert(d);
 
-    if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_OBJECT_PATH, &new_active_path) < 0)
-        return;
-
     if (!d->active_port) {
         pa_assert(pa_hashmap_isempty(d->ports));
 
@@ -747,8 +747,10 @@ static void handle_set_active_port(DBusConnection *conn, DBusMessage *msg, void
         return;
     }
 
+    dbus_message_iter_get_basic(iter, &new_active_path);
+
     if (!(new_active = pa_hashmap_get(d->ports, new_active_path))) {
-        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "%s: No such port.", new_active_path);
+        pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such port: %s", new_active_path);
         return;
     }
 
@@ -923,19 +925,12 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat
 static void handle_suspend(DBusConnection *conn, DBusMessage *msg, void *userdata) {
     pa_dbusiface_device *d = userdata;
     dbus_bool_t suspend = FALSE;
-    DBusError error;
 
     pa_assert(conn);
     pa_assert(msg);
     pa_assert(d);
 
-    dbus_error_init(&error);
-
-    if (!dbus_message_get_args(msg, &error, DBUS_TYPE_BOOLEAN, &suspend, DBUS_TYPE_INVALID)) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
-        dbus_error_free(&error);
-        return;
-    }
+    pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_BOOLEAN, &suspend, DBUS_TYPE_INVALID));
 
     if ((d->type == DEVICE_TYPE_SINK) && (pa_sink_suspend(d->sink, suspend, PA_SUSPEND_USER) < 0)) {
         pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "Internal error in PulseAudio: pa_sink_suspend() failed.");
@@ -950,7 +945,6 @@ static void handle_suspend(DBusConnection *conn, DBusMessage *msg, void *userdat
 
 static void handle_get_port_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
     pa_dbusiface_device *d = userdata;
-    DBusError error;
     const char *port_name = NULL;
     pa_dbusiface_device_port *port = NULL;
     const char *port_path = NULL;
@@ -959,13 +953,7 @@ static void handle_get_port_by_name(DBusConnection *conn, DBusMessage *msg, void
     pa_assert(msg);
     pa_assert(d);
 
-    dbus_error_init(&error);
-
-    if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &port_name, DBUS_TYPE_INVALID)) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
-        dbus_error_free(&error);
-        return;
-    }
+    pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &port_name, DBUS_TYPE_INVALID));
 
     if (!(port = pa_hashmap_get(d->ports, port_name))) {
         if (d->type == DEVICE_TYPE_SINK)
diff --git a/src/modules/dbus/iface-sample.c b/src/modules/dbus/iface-sample.c
index 7147be1..b0542a6 100644
--- a/src/modules/dbus/iface-sample.c
+++ b/src/modules/dbus/iface-sample.c
@@ -359,14 +359,10 @@ static void handle_play(DBusConnection *conn, DBusMessage *msg, void *userdata)
     pa_assert(msg);
     pa_assert(s);
 
-    if (!dbus_message_iter_init(msg, &msg_iter)) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments.");
-        return;
-    }
-
-    if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &volume) < 0)
-        return;
+    pa_assert_se(dbus_message_iter_init(msg, &msg_iter));
+    dbus_message_iter_get_basic(&msg_iter, &volume);
 
+    pa_assert_se(dbus_message_iter_next(&msg_iter));
     if (!(property_list = pa_dbus_get_proplist_arg(conn, msg, &msg_iter)))
         return;
 
@@ -405,17 +401,13 @@ static void handle_play_to_sink(DBusConnection *conn, DBusMessage *msg, void *us
     pa_assert(msg);
     pa_assert(s);
 
-    if (!dbus_message_iter_init(msg, &msg_iter)) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments.");
-        return;
-    }
-
-    if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_OBJECT_PATH, &sink_path) < 0)
-        return;
+    pa_assert_se(dbus_message_iter_init(msg, &msg_iter));
+    dbus_message_iter_get_basic(&msg_iter, &sink_path);
 
-    if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_UINT32, &volume) < 0)
-        return;
+    pa_assert_se(dbus_message_iter_next(&msg_iter));
+    dbus_message_iter_get_basic(&msg_iter, &volume);
 
+    pa_assert_se(dbus_message_iter_next(&msg_iter));
     if (!(property_list = pa_dbus_get_proplist_arg(conn, msg, &msg_iter)))
         return;
 
diff --git a/src/modules/dbus/iface-stream.c b/src/modules/dbus/iface-stream.c
index 183625b..a5f9bb5 100644
--- a/src/modules/dbus/iface-stream.c
+++ b/src/modules/dbus/iface-stream.c
@@ -70,9 +70,9 @@ static void handle_get_sample_format(DBusConnection *conn, DBusMessage *msg, voi
 static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
-static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
 static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata);
-static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
 static void handle_get_buffer_latency(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_device_latency(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_resample_method(DBusConnection *conn, DBusMessage *msg, void *userdata);
@@ -343,16 +343,18 @@ static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *user
     pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_UINT32, volume, s->volume.channels);
 }
 
-static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
     pa_dbusiface_stream *s = userdata;
-    unsigned stream_channels = 0;
+    DBusMessageIter array_iter;
+    int stream_channels = 0;
     dbus_uint32_t *volume = NULL;
-    unsigned n_volume_entries = 0;
+    int n_volume_entries = 0;
     pa_cvolume new_vol;
-    unsigned i = 0;
+    int i = 0;
 
     pa_assert(conn);
     pa_assert(msg);
+    pa_assert(iter);
     pa_assert(s);
 
     if (s->type == STREAM_TYPE_RECORD) {
@@ -366,8 +368,8 @@ static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, void *user
 
     new_vol.channels = stream_channels;
 
-    if (pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_UINT32, &volume, &n_volume_entries) < 0)
-        return;
+    dbus_message_iter_recurse(iter, &array_iter);
+    dbus_message_iter_get_fixed_array(&array_iter, &volume, &n_volume_entries);
 
     if (n_volume_entries != stream_channels) {
         pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS,
@@ -403,16 +405,16 @@ static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *us
     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &s->is_muted);
 }
 
-static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
     pa_dbusiface_stream *s = userdata;
     dbus_bool_t is_muted = FALSE;
 
     pa_assert(conn);
     pa_assert(msg);
+    pa_assert(iter);
     pa_assert(s);
 
-    if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_BOOLEAN, &is_muted) < 0)
-        return;
+    dbus_message_iter_get_basic(iter, &is_muted);
 
     if (s->type == STREAM_TYPE_RECORD) {
         pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have mute.");
@@ -575,19 +577,12 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat
 static void handle_move(DBusConnection *conn, DBusMessage *msg, void *userdata) {
     pa_dbusiface_stream *s = userdata;
     const char *device = NULL;
-    DBusError error;
 
     pa_assert(conn);
     pa_assert(msg);
     pa_assert(s);
 
-    dbus_error_init(&error);
-
-    if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &device, DBUS_TYPE_INVALID)) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
-        dbus_error_free(&error);
-        return;
-    }
+    pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &device, DBUS_TYPE_INVALID));
 
     if (s->type == STREAM_TYPE_PLAYBACK) {
         pa_sink *sink = pa_dbusiface_core_get_sink(s->core, device);
diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c
index 076b391..d45cf79 100644
--- a/src/modules/module-stream-restore.c
+++ b/src/modules/module-stream-restore.c
@@ -167,11 +167,11 @@ static void handle_get_entry_by_name(DBusConnection *conn, DBusMessage *msg, voi
 static void handle_entry_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_entry_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_entry_get_device(DBusConnection *conn, DBusMessage *msg, void *userdata);
-static void handle_entry_set_device(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_entry_set_device(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
 static void handle_entry_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
-static void handle_entry_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_entry_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
 static void handle_entry_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata);
-static void handle_entry_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_entry_set_is_muted(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
 
 static void handle_entry_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
 
@@ -326,18 +326,20 @@ static void dbus_entry_free(struct dbus_entry *de) {
 
 /* Reads an array [(UInt32, UInt32)] from the iterator. The struct items are
  * are a channel position and a volume value, respectively. The result is
- * stored in the map and vol arguments. If the volume can't be read from the
- * iterator, an error reply is sent and a negative number is returned. In case
- * of a failure we make no guarantees about the state of map and vol. In case
- * of an empty array the channels field of both map and vol are set to 0. */
+ * stored in the map and vol arguments. The iterator must point to a "a(uu)"
+ * element. If the data is invalid, an error reply is sent and a negative
+ * number is returned. In case of a failure we make no guarantees about the
+ * state of map and vol. In case of an empty array the channels field of both
+ * map and vol are set to 0. This function calls dbus_message_iter_next(iter)
+ * before returning. */
 static int get_volume_arg(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, pa_channel_map *map, pa_cvolume *vol) {
     DBusMessageIter array_iter;
     DBusMessageIter struct_iter;
-    int arg_type;
 
     pa_assert(conn);
     pa_assert(msg);
     pa_assert(iter);
+    pa_assert(pa_streq(dbus_message_iter_get_signature(iter), "a(uu)"));
     pa_assert(map);
     pa_assert(vol);
 
@@ -347,21 +349,6 @@ static int get_volume_arg(DBusConnection *conn, DBusMessage *msg, DBusMessageIte
     map->channels = 0;
     vol->channels = 0;
 
-    arg_type = dbus_message_iter_get_arg_type(iter);
-    if (arg_type != DBUS_TYPE_ARRAY) {
-        if (arg_type == DBUS_TYPE_INVALID)
-            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. An array was expected.");
-        else
-            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. An array was expected.", (char) arg_type);
-        return -1;
-    }
-
-    arg_type = dbus_message_iter_get_element_type(iter);
-    if (arg_type != DBUS_TYPE_STRUCT) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type: '%c'. A struct was expected.", (char) arg_type);
-        return -1;
-    }
-
     dbus_message_iter_recurse(iter, &array_iter);
 
     while (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_INVALID) {
@@ -370,29 +357,14 @@ static int get_volume_arg(DBusConnection *conn, DBusMessage *msg, DBusMessageIte
 
         dbus_message_iter_recurse(&array_iter, &struct_iter);
 
-        arg_type = dbus_message_iter_get_arg_type(&struct_iter);
-        if (arg_type != DBUS_TYPE_UINT32) {
-            pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong channel position type: '%c'. An unsigned 32-bit integer was expected.", (char) arg_type);
-            return -1;
-        }
-
         dbus_message_iter_get_basic(&struct_iter, &chan_pos);
-        dbus_message_iter_next(&struct_iter);
 
         if (chan_pos >= PA_CHANNEL_POSITION_MAX) {
             pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Invalid channel position: %u", chan_pos);
             return -1;
         }
 
-        arg_type = dbus_message_iter_get_arg_type(&struct_iter);
-        if (arg_type != DBUS_TYPE_UINT32) {
-            if (arg_type == DBUS_TYPE_INVALID)
-                pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Channel volume missing.");
-            else
-                pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Wrong volume value type: '%c'. An unsigned 32-bit integer was expected.", (char) arg_type);
-            return -1;
-        }
-
+        pa_assert_se(dbus_message_iter_next(&struct_iter));
         dbus_message_iter_get_basic(&struct_iter, &chan_vol);
 
         if (chan_vol > PA_VOLUME_MAX) {
@@ -612,40 +584,35 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat
 static void handle_add_entry(DBusConnection *conn, DBusMessage *msg, void *userdata) {
     struct userdata *u = userdata;
     DBusMessageIter msg_iter;
-    const char *name;
-    const char *device;
+    const char *name = NULL;
+    const char *device = NULL;
     pa_channel_map map;
     pa_cvolume vol;
-    dbus_bool_t muted;
-    dbus_bool_t apply_immediately;
+    dbus_bool_t muted = FALSE;
+    dbus_bool_t apply_immediately = FALSE;
     pa_datum key;
     pa_datum value;
-    struct dbus_entry *dbus_entry;
-    struct entry *e;
+    struct dbus_entry *dbus_entry = NULL;
+    struct entry *e = NULL;
 
     pa_assert(conn);
     pa_assert(msg);
     pa_assert(u);
 
-    if (!dbus_message_iter_init(msg, &msg_iter)) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments.");
-        return;
-    }
+    pa_assert_se(dbus_message_iter_init(msg, &msg_iter));
+    dbus_message_iter_get_basic(&msg_iter, &name);
 
-    if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_STRING, &name) < 0)
-        return;
-
-    if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_STRING, &device) < 0)
-        return;
+    pa_assert_se(dbus_message_iter_next(&msg_iter));
+    dbus_message_iter_get_basic(&msg_iter, &device);
 
+    pa_assert_se(dbus_message_iter_next(&msg_iter));
     if (get_volume_arg(conn, msg, &msg_iter, &map, &vol) < 0)
         return;
 
-    if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_BOOLEAN, &muted) < 0)
-        return;
+    dbus_message_iter_get_basic(&msg_iter, &muted);
 
-    if (pa_dbus_get_basic_arg(conn, msg, &msg_iter, DBUS_TYPE_BOOLEAN, &apply_immediately) < 0)
-        return;
+    pa_assert_se(dbus_message_iter_next(&msg_iter));
+    dbus_message_iter_get_basic(&msg_iter, &apply_immediately);
 
     if (!*name) {
         pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "An empty string was given as the entry name.");
@@ -714,19 +681,12 @@ static void handle_get_entry_by_name(DBusConnection *conn, DBusMessage *msg, voi
     struct userdata *u = userdata;
     const char *name;
     struct dbus_entry *de;
-    DBusError error;
 
     pa_assert(conn);
     pa_assert(msg);
     pa_assert(u);
 
-    dbus_error_init(&error);
-
-    if (!dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
-        dbus_error_free(&error);
-        return;
-    }
+    pa_assert_se(dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID));
 
     if (!(de = pa_hashmap_get(u->dbus_entries, name))) {
         pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NOT_FOUND, "No such stream restore entry.");
@@ -774,7 +734,7 @@ static void handle_entry_get_device(DBusConnection *conn, DBusMessage *msg, void
     pa_xfree(e);
 }
 
-static void handle_entry_set_device(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+static void handle_entry_set_device(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
     struct dbus_entry *de = userdata;
     const char *device;
     struct entry *e;
@@ -782,10 +742,10 @@ static void handle_entry_set_device(DBusConnection *conn, DBusMessage *msg, void
 
     pa_assert(conn);
     pa_assert(msg);
+    pa_assert(iter);
     pa_assert(de);
 
-    if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_STRING, &device) < 0)
-        return;
+    dbus_message_iter_get_basic(iter, &device);
 
     pa_assert_se(e = read_entry(de->userdata, de->entry_name));
 
@@ -835,25 +795,19 @@ static void handle_entry_get_volume(DBusConnection *conn, DBusMessage *msg, void
     pa_xfree(e);
 }
 
-static void handle_entry_set_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+static void handle_entry_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
     struct dbus_entry *de = userdata;
-    DBusMessageIter msg_iter;
     pa_channel_map map;
     pa_cvolume vol;
-    struct entry *e;
-    pa_bool_t updated;
+    struct entry *e = NULL;
+    pa_bool_t updated = FALSE;
 
     pa_assert(conn);
     pa_assert(msg);
+    pa_assert(iter);
     pa_assert(de);
 
-    /* Skip the interface and property name arguments. */
-    if (!dbus_message_iter_init(msg, &msg_iter) || !dbus_message_iter_next(&msg_iter) || !dbus_message_iter_next(&msg_iter)) {
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments.");
-        return;
-    }
-
-    if (get_volume_arg(conn, msg, &msg_iter, &map, &vol) < 0)
+    if (get_volume_arg(conn, msg, iter, &map, &vol) < 0)
         return;
 
     pa_assert_se(e = read_entry(de->userdata, de->entry_name));
@@ -901,7 +855,7 @@ static void handle_entry_get_is_muted(DBusConnection *conn, DBusMessage *msg, vo
     pa_xfree(e);
 }
 
-static void handle_entry_set_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+static void handle_entry_set_is_muted(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
     struct dbus_entry *de = userdata;
     pa_bool_t muted;
     struct entry *e;
@@ -909,10 +863,10 @@ static void handle_entry_set_is_muted(DBusConnection *conn, DBusMessage *msg, vo
 
     pa_assert(conn);
     pa_assert(msg);
+    pa_assert(iter);
     pa_assert(de);
 
-    if (pa_dbus_get_basic_set_property_arg(conn, msg, DBUS_TYPE_BOOLEAN, &muted) < 0)
-        return;
+    dbus_message_iter_get_basic(iter, &muted);
 
     pa_assert_se(e = read_entry(de->userdata, de->entry_name));
 
diff --git a/src/pulsecore/dbus-util.c b/src/pulsecore/dbus-util.c
index b45e6a6..e3700ea 100644
--- a/src/pulsecore/dbus-util.c
+++ b/src/pulsecore/dbus-util.c
@@ -291,7 +291,10 @@ pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *m, pa_bool
     return pconn;
 }
 
-pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing(pa_mainloop_api *m, pa_bool_t use_rtclock, DBusConnection *conn) {
+pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing(
+        pa_mainloop_api *m,
+        pa_bool_t use_rtclock,
+        DBusConnection *conn) {
     pa_dbus_wrap_connection *pconn;
 
     pa_assert(m);
@@ -522,7 +525,10 @@ void pa_dbus_send_basic_variant_reply(DBusConnection *c, DBusMessage *in_reply_t
 
     pa_assert_se((reply = dbus_message_new_method_return(in_reply_to)));
     dbus_message_iter_init_append(reply, &msg_iter);
-    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_VARIANT, signature_from_basic_type(type), &variant_iter));
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter,
+                                                  DBUS_TYPE_VARIANT,
+                                                  signature_from_basic_type(type),
+                                                  &variant_iter));
     pa_assert_se(dbus_message_iter_append_basic(&variant_iter, type, data));
     pa_assert_se(dbus_message_iter_close_container(&msg_iter, &variant_iter));
     pa_assert_se(dbus_connection_send(c, reply, NULL));
@@ -548,7 +554,12 @@ static unsigned basic_type_size(int type) {
     }
 }
 
-void pa_dbus_send_basic_array_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int item_type, void *array, unsigned n) {
+void pa_dbus_send_basic_array_variant_reply(
+        DBusConnection *c,
+        DBusMessage *in_reply_to,
+        int item_type,
+        void *array,
+        unsigned n) {
     DBusMessage *reply = NULL;
     DBusMessageIter msg_iter;
 
@@ -641,7 +652,12 @@ void pa_dbus_append_basic_variant_dict_entry(DBusMessageIter *dict_iter, const c
     pa_assert_se(dbus_message_iter_close_container(dict_iter, &dict_entry_iter));
 }
 
-void pa_dbus_append_basic_array_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int item_type, const void *array, unsigned n) {
+void pa_dbus_append_basic_array_variant_dict_entry(
+        DBusMessageIter *dict_iter,
+        const char *key,
+        int item_type,
+        const void *array,
+        unsigned n) {
     DBusMessageIter dict_entry_iter;
 
     pa_assert(dict_iter);
@@ -711,156 +727,18 @@ void pa_dbus_append_proplist_variant_dict_entry(DBusMessageIter *dict_iter, cons
     pa_assert_se(dbus_message_iter_close_container(dict_iter, &dict_entry_iter));
 }
 
-int pa_dbus_get_basic_set_property_arg(DBusConnection *c, DBusMessage *msg, int type, void *data) {
-    DBusMessageIter msg_iter;
-    DBusMessageIter variant_iter;
-
-    pa_assert(c);
-    pa_assert(msg);
-    pa_assert(dbus_type_is_basic(type));
-    pa_assert(data);
-
-    /* Skip the interface and property name arguments. */
-    if (!dbus_message_iter_init(msg, &msg_iter) || !dbus_message_iter_next(&msg_iter) || !dbus_message_iter_next(&msg_iter)) {
-        pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments.");
-        return -1;
-    }
-
-    if (dbus_message_iter_get_arg_type(&msg_iter) != DBUS_TYPE_VARIANT) {
-        pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Message argument isn't a variant.");
-        return -1;
-    }
-
-    dbus_message_iter_recurse(&msg_iter, &variant_iter);
-
-    if (pa_dbus_get_basic_arg(c, msg, &variant_iter, type, data) < 0)
-        return -1;
-
-    return 0;
-}
-
-int pa_dbus_get_fixed_array_set_property_arg(DBusConnection *c, DBusMessage *msg, int item_type, void *data, unsigned *n) {
-    DBusMessageIter msg_iter;
-    DBusMessageIter variant_iter;
-
-    pa_assert(c);
-    pa_assert(msg);
-    pa_assert(dbus_type_is_fixed(item_type));
-    pa_assert(data);
-    pa_assert(n);
-
-    /* Skip the interface and property name arguments. */
-    if (!dbus_message_iter_init(msg, &msg_iter) || !dbus_message_iter_next(&msg_iter) || !dbus_message_iter_next(&msg_iter)) {
-        pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments.");
-        return -1;
-    }
-
-    if (dbus_message_iter_get_arg_type(&msg_iter) != DBUS_TYPE_VARIANT) {
-        pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Message argument isn't a variant.");
-        return -1;
-    }
-
-    dbus_message_iter_recurse(&msg_iter, &variant_iter);
-
-    if (pa_dbus_get_fixed_array_arg(c, msg, &variant_iter, item_type, data, n) < 0)
-        return -1;
-
-    return 0;
-}
-
-int pa_dbus_get_basic_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int type, void *data) {
-    int arg_type;
-
-    pa_assert(c);
-    pa_assert(msg);
-    pa_assert(iter);
-    pa_assert(dbus_type_is_basic(type));
-    pa_assert(data);
-
-    arg_type = dbus_message_iter_get_arg_type(iter);
-    if (arg_type != type) {
-        if (arg_type == DBUS_TYPE_INVALID)
-            pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. D-Bus type '%c' expected.", (char) type);
-        else
-            pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. Expected type '%c'.", (char) arg_type, (char) type);
-        return -1;
-    }
-
-    dbus_message_iter_get_basic(iter, data);
-
-    dbus_message_iter_next(iter);
-
-    return 0;
-}
-
-int pa_dbus_get_fixed_array_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int item_type, void *array, unsigned *n) {
-    DBusMessageIter array_iter;
-    int signed_n;
-    int arg_type;
-    int element_type;
-
-    pa_assert(c);
-    pa_assert(msg);
-    pa_assert(iter);
-    pa_assert(dbus_type_is_fixed(item_type));
-    pa_assert(array);
-    pa_assert(n);
-
-    arg_type = dbus_message_iter_get_arg_type(iter);
-    if (arg_type != DBUS_TYPE_ARRAY) {
-        if (arg_type == DBUS_TYPE_INVALID)
-            pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. An array of type '%c' was expected.", (char) item_type);
-        else
-            pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. An array of type '%c' was expected.", (char) arg_type, (char) item_type);
-        return -1;
-    }
-
-    element_type = dbus_message_iter_get_element_type(iter);
-    if (element_type != item_type) {
-        pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type: '%c'. Element type '%c' was expected.", (char) element_type, (char) item_type);
-        return -1;
-    }
-
-    dbus_message_iter_recurse(iter, &array_iter);
-
-    dbus_message_iter_get_fixed_array(&array_iter, array, &signed_n);
-
-    dbus_message_iter_next(iter);
-
-    pa_assert(signed_n >= 0);
-
-    *n = signed_n;
-
-    return 0;
-}
-
 pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter) {
     DBusMessageIter dict_iter;
     DBusMessageIter dict_entry_iter;
-    int arg_type;
     pa_proplist *proplist = NULL;
-    const char *key;
-    const uint8_t *value;
-    int value_length;
+    const char *key = NULL;
+    const uint8_t *value = NULL;
+    int value_length = 0;
 
     pa_assert(c);
     pa_assert(msg);
     pa_assert(iter);
-
-    arg_type = dbus_message_iter_get_arg_type(iter);
-    if (arg_type != DBUS_TYPE_ARRAY) {
-        if (arg_type == DBUS_TYPE_INVALID)
-            pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Too few arguments. An array was expected.");
-        else
-            pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong argument type: '%c'. An array was expected.", (char) arg_type);
-        return NULL;
-    }
-
-    arg_type = dbus_message_iter_get_element_type(iter);
-    if (arg_type != DBUS_TYPE_DICT_ENTRY) {
-        pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong array element type: '%c'. A dictionary entry was expected.", (char) arg_type);
-        return NULL;
-    }
+    pa_assert(pa_streq(dbus_message_iter_get_signature(iter), "a{say}"));
 
     proplist = pa_proplist_new();
 
@@ -869,32 +747,11 @@ pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusM
     while (dbus_message_iter_get_arg_type(&dict_iter) != DBUS_TYPE_INVALID) {
         dbus_message_iter_recurse(&dict_iter, &dict_entry_iter);
 
-        arg_type = dbus_message_iter_get_arg_type(&dict_entry_iter);
-        if (arg_type != DBUS_TYPE_STRING) {
-            pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict key type: '%c'. A string was expected.", (char) arg_type);
-            goto fail;
-        }
-
         dbus_message_iter_get_basic(&dict_entry_iter, &key);
         dbus_message_iter_next(&dict_entry_iter);
 
         if (strlen(key) <= 0 || !pa_ascii_valid(key)) {
-            pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Invalid property list key.");
-            goto fail;
-        }
-
-        arg_type = dbus_message_iter_get_arg_type(&dict_entry_iter);
-        if (arg_type != DBUS_TYPE_ARRAY) {
-            if (arg_type == DBUS_TYPE_INVALID)
-                pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Dict value missing.");
-            else
-                pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value type: '%c'. An array was expected.", (char) arg_type);
-            goto fail;
-        }
-
-        arg_type = dbus_message_iter_get_element_type(&dict_entry_iter);
-        if (arg_type != DBUS_TYPE_BYTE) {
-            pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Wrong dict value item type: '%c'. A byte was expected.", (char) arg_type);
+            pa_dbus_send_error(c, msg, DBUS_ERROR_INVALID_ARGS, "Invalid property list key: '%s'.", key);
             goto fail;
         }
 
diff --git a/src/pulsecore/dbus-util.h b/src/pulsecore/dbus-util.h
index 9cee293..f35e66c 100644
--- a/src/pulsecore/dbus-util.h
+++ b/src/pulsecore/dbus-util.h
@@ -32,7 +32,10 @@
 typedef struct pa_dbus_wrap_connection pa_dbus_wrap_connection;
 
 pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *mainloop, pa_bool_t use_rtclock, DBusBusType type, DBusError *error);
-pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing(pa_mainloop_api *mainloop, pa_bool_t use_rtclock, DBusConnection *conn);
+pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing(
+        pa_mainloop_api *mainloop,
+        pa_bool_t use_rtclock,
+        DBusConnection *conn);
 void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* conn);
 
 DBusConnection* pa_dbus_wrap_connection_get(pa_dbus_wrap_connection *conn);
@@ -63,36 +66,41 @@ void pa_dbus_sync_pending_list(pa_dbus_pending **p);
 void pa_dbus_free_pending_list(pa_dbus_pending **p);
 
 /* Sends an error message as the reply to the given message. */
-void pa_dbus_send_error(DBusConnection *c, DBusMessage *in_reply_to, const char *name, const char *format, ...) PA_GCC_PRINTF_ATTR(4, 5);
+void pa_dbus_send_error(
+        DBusConnection *c,
+        DBusMessage *in_reply_to,
+        const char *name,
+        const char *format, ...) PA_GCC_PRINTF_ATTR(4, 5);
 
 void pa_dbus_send_empty_reply(DBusConnection *c, DBusMessage *in_reply_to);
 void pa_dbus_send_basic_value_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data);
 void pa_dbus_send_basic_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int type, void *data);
-void pa_dbus_send_basic_array_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, int item_type, void *array, unsigned n);
+void pa_dbus_send_basic_array_variant_reply(
+        DBusConnection *c,
+        DBusMessage *in_reply_to,
+        int item_type,
+        void *array,
+        unsigned n);
 void pa_dbus_send_proplist_variant_reply(DBusConnection *c, DBusMessage *in_reply_to, pa_proplist *proplist);
 
 void pa_dbus_append_basic_array(DBusMessageIter *iter, int item_type, const void *array, unsigned n);
 void pa_dbus_append_basic_array_variant(DBusMessageIter *iter, int item_type, const void *array, unsigned n);
 void pa_dbus_append_basic_variant(DBusMessageIter *iter, int type, void *data);
 void pa_dbus_append_basic_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int type, void *data);
-void pa_dbus_append_basic_array_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, int item_type, const void *array, unsigned n);
+void pa_dbus_append_basic_array_variant_dict_entry(
+        DBusMessageIter *dict_iter,
+        const char *key,
+        int item_type,
+        const void *array,
+        unsigned n);
 void pa_dbus_append_proplist(DBusMessageIter *iter, pa_proplist *proplist);
 void pa_dbus_append_proplist_variant(DBusMessageIter *iter, pa_proplist *proplist);
 void pa_dbus_append_proplist_variant_dict_entry(DBusMessageIter *dict_iter, const char *key, pa_proplist *proplist);
 
-/* Helper functions for extracting the value argument of a Set call. If the
- * message is invalid, an error reply is sent and a negative number is
- * returned. */
-int pa_dbus_get_basic_set_property_arg(DBusConnection *c, DBusMessage *msg, int type, void *data);
-int pa_dbus_get_fixed_array_set_property_arg(DBusConnection *c, DBusMessage *msg, int item_type, void *data, unsigned *n);
-
-/* If the arguments can't be read from the iterator, an error reply is sent and
- * a negative number is returned. */
-int pa_dbus_get_basic_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int type, void *data);
-int pa_dbus_get_fixed_array_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter, int item_type, void *array, unsigned *n);
-
-/* Returns a new proplist that the caller has to free. If the proplist can't be
- * read from the iterator, an error reply is sent and NULL is returned. */
+/* Returns a new proplist that the caller has to free. If the proplist contains
+ * invalid keys, an error reply is sent and NULL is returned. The iterator must
+ * point to "a{say}" element. This function calls dbus_message_iter_next(iter)
+ * before returning. */
 pa_proplist *pa_dbus_get_proplist_arg(DBusConnection *c, DBusMessage *msg, DBusMessageIter *iter);
 
 #endif
diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c
index d1e19ff..9102251 100644
--- a/src/pulsecore/protocol-dbus.c
+++ b/src/pulsecore/protocol-dbus.c
@@ -257,37 +257,90 @@ static void update_introspection(struct object_entry *oe) {
     oe->introspection = pa_strbuf_tostring_free(buf);
 }
 
+/* Return value of find_handler() and its subfunctions. */
 enum find_result_t {
-    FOUND_METHOD,
+    /* The received message is a valid .Get call. */
     FOUND_GET_PROPERTY,
+
+    /* The received message is a valid .Set call. */
     FOUND_SET_PROPERTY,
+
+    /* The received message is a valid .GetAll call. */
     FOUND_GET_ALL,
+
+    /* The received message is a valid method call. */
+    FOUND_METHOD,
+
+    /* The interface of the received message hasn't been registered for the
+     * destination object. */
+    NO_SUCH_INTERFACE,
+
+    /* No property handler was found for the received .Get or .Set call. */
+    NO_SUCH_PROPERTY,
+
+    /* The interface argument of a property call didn't match any registered
+     * interface. */
+    NO_SUCH_PROPERTY_INTERFACE,
+
+    /* The received message called .Get or .Set for a property whose access
+     * mode doesn't match the call. */
     PROPERTY_ACCESS_DENIED,
+
+    /* The new value signature of a .Set call didn't match the expexted
+     * signature. */
+    INVALID_PROPERTY_SIG,
+
+    /* No method handler was found for the received message. */
     NO_SUCH_METHOD,
-    NO_SUCH_PROPERTY,
-    INVALID_MESSAGE_ARGUMENTS
+
+    /* The signature of the received message didn't match the expected
+     * signature. Despite the name, this can also be returned for a property
+     * call if its message signature is invalid. */
+    INVALID_METHOD_SIG
 };
 
-static enum find_result_t find_handler_by_property(struct object_entry *obj_entry,
-                                                   DBusMessage *msg,
-                                                   const char *property,
-                                                   struct interface_entry **iface_entry,
-                                                   pa_dbus_property_handler **property_handler) {
+/* Data for resolving the correct reaction to a received message. */
+struct call_info {
+    DBusMessage *message; /* The received message. */
+    struct object_entry *obj_entry;
+    const char *interface; /* Destination interface name (extracted from the message). */
+    struct interface_entry *iface_entry;
+
+    const char *property; /* Property name (extracted from the message). */
+    const char *property_interface; /* The interface argument of a property call is stored here. */
+    pa_dbus_property_handler *property_handler;
+    const char *expected_property_sig; /* Property signature from the introspection data. */
+    const char *property_sig; /* The signature of the new value in the received .Set message. */
+    DBusMessageIter variant_iter; /* Iterator pointing to the beginning of the new value variant of a .Set call. */
+
+    const char *method; /* Method name (extracted from the message). */
+    pa_dbus_method_handler *method_handler;
+    const char *expected_method_sig; /* Method signature from the introspection data. */
+    const char *method_sig; /* The signature of the received message. */
+};
+
+/* Called when call_info->property has been set and the property interface has
+ * not been given. In case of a Set call, call_info->property_sig is also set,
+ * which is checked against the expected value in this function. */
+static enum find_result_t find_handler_by_property(struct call_info *call_info) {
     void *state = NULL;
 
-    pa_assert(obj_entry);
-    pa_assert(msg);
-    pa_assert(property);
-    pa_assert(iface_entry);
-    pa_assert(property_handler);
-
-    PA_HASHMAP_FOREACH(*iface_entry, obj_entry->interfaces, state) {
-        if ((*property_handler = pa_hashmap_get((*iface_entry)->property_handlers, property))) {
-            if (dbus_message_has_member(msg, "Get"))
-                return (*property_handler)->get_cb ? FOUND_GET_PROPERTY : PROPERTY_ACCESS_DENIED;
-            else if (dbus_message_has_member(msg, "Set"))
-                return (*property_handler)->set_cb ? FOUND_SET_PROPERTY : PROPERTY_ACCESS_DENIED;
-            else
+    pa_assert(call_info);
+
+    PA_HASHMAP_FOREACH(call_info->iface_entry, call_info->obj_entry->interfaces, state) {
+        if ((call_info->property_handler = pa_hashmap_get(call_info->iface_entry->property_handlers, call_info->property))) {
+            if (pa_streq(call_info->method, "Get"))
+                return call_info->property_handler->get_cb ? FOUND_GET_PROPERTY : PROPERTY_ACCESS_DENIED;
+
+            else if (pa_streq(call_info->method, "Set")) {
+                call_info->expected_property_sig = call_info->property_handler->type;
+
+                if (pa_streq(call_info->property_sig, call_info->expected_property_sig))
+                    return call_info->property_handler->set_cb ? FOUND_SET_PROPERTY : PROPERTY_ACCESS_DENIED;
+                else
+                    return INVALID_PROPERTY_SIG;
+
+            } else
                 pa_assert_not_reached();
         }
     }
@@ -295,120 +348,138 @@ static enum find_result_t find_handler_by_property(struct object_entry *obj_entr
     return NO_SUCH_PROPERTY;
 }
 
-static enum find_result_t find_handler_by_method(struct object_entry *obj_entry,
-                                                 const char *method,
-                                                 struct interface_entry **iface_entry,
-                                                 pa_dbus_method_handler **method_handler) {
+static enum find_result_t find_handler_by_method(struct call_info *call_info) {
     void *state = NULL;
 
-    pa_assert(obj_entry);
-    pa_assert(method);
-    pa_assert(iface_entry);
-    pa_assert(method_handler);
+    pa_assert(call_info);
 
-    PA_HASHMAP_FOREACH(*iface_entry, obj_entry->interfaces, state) {
-        if ((*method_handler = pa_hashmap_get((*iface_entry)->method_handlers, method)))
+    PA_HASHMAP_FOREACH(call_info->iface_entry, call_info->obj_entry->interfaces, state) {
+        if ((call_info->method_handler = pa_hashmap_get(call_info->iface_entry->method_handlers, call_info->method)))
             return FOUND_METHOD;
     }
 
     return NO_SUCH_METHOD;
 }
 
-static enum find_result_t find_handler_from_properties_call(struct object_entry *obj_entry,
-                                                            DBusMessage *msg,
-                                                            struct interface_entry **iface_entry,
-                                                            pa_dbus_property_handler **property_handler,
-                                                            const char **attempted_property) {
-    const char *interface;
+static enum find_result_t find_handler_from_properties_call(struct call_info *call_info) {
+    pa_assert(call_info);
 
-    pa_assert(obj_entry);
-    pa_assert(msg);
-    pa_assert(iface_entry);
+    if (pa_streq(call_info->method, "GetAll")) {
+        call_info->expected_method_sig = "s";
+        if (!pa_streq(call_info->method_sig, call_info->expected_method_sig))
+            return INVALID_METHOD_SIG;
 
-    if (dbus_message_has_member(msg, "GetAll")) {
-        if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID))
-            return INVALID_MESSAGE_ARGUMENTS;
+        pa_assert_se(dbus_message_get_args(call_info->message, NULL,
+                                           DBUS_TYPE_STRING, &call_info->property_interface,
+                                           DBUS_TYPE_INVALID));
 
-        if (*interface) {
-            if ((*iface_entry = pa_hashmap_get(obj_entry->interfaces, interface)))
+        if (*call_info->property_interface) {
+            if ((call_info->iface_entry = pa_hashmap_get(call_info->obj_entry->interfaces, call_info->property_interface)))
                 return FOUND_GET_ALL;
-            else {
-                return NO_SUCH_METHOD; /* XXX: NO_SUCH_INTERFACE or something like that might be more accurate. */
-            }
+            else
+                return NO_SUCH_PROPERTY_INTERFACE;
+
         } else {
-            pa_assert_se((*iface_entry = pa_hashmap_first(obj_entry->interfaces)));
+            pa_assert_se(call_info->iface_entry = pa_hashmap_first(call_info->obj_entry->interfaces));
             return FOUND_GET_ALL;
         }
-    } else {
-        if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, attempted_property, DBUS_TYPE_INVALID))
-            return INVALID_MESSAGE_ARGUMENTS;
-
-        if (*interface) {
-            if ((*iface_entry = pa_hashmap_get(obj_entry->interfaces, interface)) &&
-                (*property_handler = pa_hashmap_get((*iface_entry)->property_handlers, *attempted_property))) {
-                if (dbus_message_has_member(msg, "Get"))
-                    return (*property_handler)->get_cb ? FOUND_GET_PROPERTY : PROPERTY_ACCESS_DENIED;
-                else if (dbus_message_has_member(msg, "Set"))
-                    return (*property_handler)->set_cb ? FOUND_SET_PROPERTY : PROPERTY_ACCESS_DENIED;
+
+    } else if (pa_streq(call_info->method, "Get")) {
+        call_info->expected_method_sig = "ss";
+        if (!pa_streq(call_info->method_sig, call_info->expected_method_sig))
+            return INVALID_METHOD_SIG;
+
+        pa_assert_se(dbus_message_get_args(call_info->message, NULL,
+                                           DBUS_TYPE_STRING, &call_info->property_interface,
+                                           DBUS_TYPE_STRING, &call_info->property,
+                                           DBUS_TYPE_INVALID));
+
+        if (*call_info->property_interface) {
+            if (!(call_info->iface_entry = pa_hashmap_get(call_info->obj_entry->interfaces, call_info->property_interface)))
+                return NO_SUCH_PROPERTY_INTERFACE;
+            else if ((call_info->property_handler =
+                        pa_hashmap_get(call_info->iface_entry->property_handlers, call_info->property)))
+                return FOUND_GET_PROPERTY;
+            else
+                return NO_SUCH_PROPERTY;
+
+        } else
+            return find_handler_by_property(call_info);
+
+    } else if (pa_streq(call_info->method, "Set")) {
+        DBusMessageIter msg_iter;
+
+        call_info->expected_method_sig = "ssv";
+        if (!pa_streq(call_info->method_sig, call_info->expected_method_sig))
+            return INVALID_METHOD_SIG;
+
+        pa_assert_se(dbus_message_iter_init(call_info->message, &msg_iter));
+
+        dbus_message_iter_get_basic(&msg_iter, &call_info->property_interface);
+        pa_assert_se(dbus_message_iter_next(&msg_iter));
+        dbus_message_iter_get_basic(&msg_iter, &call_info->property);
+        pa_assert_se(dbus_message_iter_next(&msg_iter));
+
+        dbus_message_iter_recurse(&msg_iter, &call_info->variant_iter);
+
+        call_info->property_sig = dbus_message_iter_get_signature(&call_info->variant_iter);
+
+        if (*call_info->property_interface) {
+            if (!(call_info->iface_entry = pa_hashmap_get(call_info->obj_entry->interfaces, call_info->property_interface)))
+                return NO_SUCH_PROPERTY_INTERFACE;
+
+            else if ((call_info->property_handler =
+                        pa_hashmap_get(call_info->iface_entry->property_handlers, call_info->property))) {
+                call_info->expected_property_sig = call_info->property_handler->type;
+
+                if (pa_streq(call_info->property_sig, call_info->expected_property_sig))
+                    return FOUND_SET_PROPERTY;
                 else
-                    pa_assert_not_reached();
+                    return INVALID_PROPERTY_SIG;
+
             } else
                 return NO_SUCH_PROPERTY;
+
         } else
-            return find_handler_by_property(obj_entry, msg, *attempted_property, iface_entry, property_handler);
-    }
-}
+            return find_handler_by_property(call_info);
 
-static enum find_result_t find_handler(struct object_entry *obj_entry,
-                                       DBusMessage *msg,
-                                       struct interface_entry **iface_entry,
-                                       pa_dbus_method_handler **method_handler,
-                                       pa_dbus_property_handler **property_handler,
-                                       const char **attempted_property) {
-    const char *interface;
+    } else
+        pa_assert_not_reached();
+}
 
-    pa_assert(obj_entry);
-    pa_assert(msg);
-    pa_assert(iface_entry);
-    pa_assert(method_handler);
-    pa_assert(property_handler);
-    pa_assert(attempted_property);
+static enum find_result_t find_handler(struct call_info *call_info) {
+    pa_assert(call_info);
 
-    *iface_entry = NULL;
-    *method_handler = NULL;
+    if (call_info->interface) {
+        if (pa_streq(call_info->interface, DBUS_INTERFACE_PROPERTIES))
+            return find_handler_from_properties_call(call_info);
 
-    if (dbus_message_has_interface(msg, DBUS_INTERFACE_PROPERTIES))
-        return find_handler_from_properties_call(obj_entry, msg, iface_entry, property_handler, attempted_property);
+        else if (!(call_info->iface_entry = pa_hashmap_get(call_info->obj_entry->interfaces, call_info->interface)))
+            return NO_SUCH_INTERFACE;
 
-    else if ((interface = dbus_message_get_interface(msg))) {
-        if ((*iface_entry = pa_hashmap_get(obj_entry->interfaces, interface)) &&
-            (*method_handler = pa_hashmap_get((*iface_entry)->method_handlers, dbus_message_get_member(msg))))
+        else if ((call_info->method_handler = pa_hashmap_get(call_info->iface_entry->method_handlers, call_info->method)))
             return FOUND_METHOD;
-        else {
-            pa_log("Message has unknown interface or there's no method handler.");
+
+        else
             return NO_SUCH_METHOD;
-        }
 
     } else { /* The method call doesn't contain an interface. */
-        if (dbus_message_has_member(msg, "Get") || dbus_message_has_member(msg, "Set") || dbus_message_has_member(msg, "GetAll")) {
-            if (find_handler_by_method(obj_entry, dbus_message_get_member(msg), iface_entry, method_handler) == FOUND_METHOD)
-                return FOUND_METHOD; /* The object has a method named Get, Set or GetAll in some other interface than .Properties. */
+        if (pa_streq(call_info->method, "Get") || pa_streq(call_info->method, "Set") || pa_streq(call_info->method, "GetAll")) {
+            if (find_handler_by_method(call_info) == FOUND_METHOD)
+                /* The object has a method named Get, Set or GetAll in some other interface than .Properties. */
+                return FOUND_METHOD;
             else
                 /* Assume this is a .Properties call. */
-                return find_handler_from_properties_call(obj_entry, msg, iface_entry, property_handler, attempted_property);
+                return find_handler_from_properties_call(call_info);
 
         } else /* This is not a .Properties call. */
-            return find_handler_by_method(obj_entry, dbus_message_get_member(msg), iface_entry, method_handler);
+            return find_handler_by_method(call_info);
     }
 }
 
 static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessage *message, void *user_data) {
     pa_dbus_protocol *p = user_data;
-    struct object_entry *obj_entry = NULL;
-    struct interface_entry *iface_entry = NULL;
-    pa_dbus_method_handler *method_handler = NULL;
-    pa_dbus_property_handler *property_handler = NULL;
-    const char *attempted_property = NULL;
+    struct call_info call_info;
 
     pa_assert(connection);
     pa_assert(message);
@@ -423,49 +494,72 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa
                  dbus_message_get_interface(message),
                  dbus_message_get_member(message));
 
-    pa_assert_se((obj_entry = pa_hashmap_get(p->objects, dbus_message_get_path(message))));
+    call_info.message = message;
+    pa_assert_se(call_info.obj_entry = pa_hashmap_get(p->objects, dbus_message_get_path(message)));
+    call_info.interface = dbus_message_get_interface(message);
+    pa_assert_se(call_info.method = dbus_message_get_member(message));
+    pa_assert_se(call_info.method_sig = dbus_message_get_signature(message));
 
     if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect") ||
         (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Introspect"))) {
-        pa_dbus_send_basic_value_reply(connection, message, DBUS_TYPE_STRING, &obj_entry->introspection);
+        pa_dbus_send_basic_value_reply(connection, message, DBUS_TYPE_STRING, &call_info.obj_entry->introspection);
         goto finish;
     }
 
-    switch (find_handler(obj_entry, message, &iface_entry, &method_handler, &property_handler, &attempted_property)) {
-        case FOUND_METHOD:
-            method_handler->receive_cb(connection, message, iface_entry->userdata);
-            break;
-
+    switch (find_handler(&call_info)) {
         case FOUND_GET_PROPERTY:
-            property_handler->get_cb(connection, message, iface_entry->userdata);
+            call_info.property_handler->get_cb(connection, message, call_info.iface_entry->userdata);
             break;
 
         case FOUND_SET_PROPERTY:
-            property_handler->set_cb(connection, message, iface_entry->userdata);
+            call_info.property_handler->set_cb(connection, message, &call_info.variant_iter, call_info.iface_entry->userdata);
+            break;
+
+        case FOUND_METHOD:
+            call_info.method_handler->receive_cb(connection, message, call_info.iface_entry->userdata);
             break;
 
         case FOUND_GET_ALL:
-            if (iface_entry->get_all_properties_cb)
-                iface_entry->get_all_properties_cb(connection, message, iface_entry->userdata);
-            /* TODO: Write an else branch where a dummy response is sent. */
+            if (call_info.iface_entry->get_all_properties_cb)
+                call_info.iface_entry->get_all_properties_cb(connection, message, call_info.iface_entry->userdata);
+            else {
+                DBusMessage *dummy_reply = NULL;
+                DBusMessageIter msg_iter;
+                DBusMessageIter dict_iter;
+
+                pa_assert_se(dummy_reply = dbus_message_new_method_return(message));
+                dbus_message_iter_init_append(dummy_reply, &msg_iter);
+                pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+                pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+                pa_assert_se(dbus_connection_send(connection, dummy_reply, NULL));
+                dbus_message_unref(dummy_reply);
+            }
             break;
 
         case PROPERTY_ACCESS_DENIED:
-            pa_dbus_send_error(connection, message, DBUS_ERROR_ACCESS_DENIED, "%s access denied for property %s", dbus_message_get_member(message), attempted_property);
+            pa_dbus_send_error(connection, message, DBUS_ERROR_ACCESS_DENIED,
+                               "%s access denied for property %s", call_info.method, call_info.property);
             break;
 
         case NO_SUCH_METHOD:
-            pa_dbus_send_error(connection, message, DBUS_ERROR_UNKNOWN_METHOD, "%s: No such method", dbus_message_get_member(message));
+            pa_dbus_send_error(connection, message, DBUS_ERROR_UNKNOWN_METHOD, "No such method: %s", call_info.method);
             break;
 
         case NO_SUCH_PROPERTY:
-            pa_dbus_send_error(connection, message, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", attempted_property);
+            pa_dbus_send_error(connection, message, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "No such property: %s", call_info.property);
             break;
 
-        case INVALID_MESSAGE_ARGUMENTS:
-            pa_dbus_send_error(connection, message, DBUS_ERROR_INVALID_ARGS, "Invalid arguments for %s", dbus_message_get_member(message));
+        case INVALID_METHOD_SIG:
+            pa_dbus_send_error(connection, message, DBUS_ERROR_INVALID_ARGS,
+                               "Invalid signature for method %s: '%s'. Expected '%s'.",
+                               call_info.method, call_info.method_sig, call_info.expected_method_sig);
             break;
 
+        case INVALID_PROPERTY_SIG:
+            pa_dbus_send_error(connection, message, DBUS_ERROR_INVALID_ARGS,
+                               "Invalid signature for property %s: '%s'. Expected '%s'.",
+                               call_info.property, call_info.property_sig, call_info.expected_property_sig);
+
         default:
             pa_assert_not_reached();
     }
@@ -821,7 +915,12 @@ pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn
     return conn_entry->client;
 }
 
-void pa_dbus_protocol_add_signal_listener(pa_dbus_protocol *p, DBusConnection *conn, const char *signal, char **objects, unsigned n_objects) {
+void pa_dbus_protocol_add_signal_listener(
+        pa_dbus_protocol *p,
+        DBusConnection *conn,
+        const char *signal,
+        char **objects,
+        unsigned n_objects) {
     struct connection_entry *conn_entry;
     pa_idxset *object_set;
     char *object_path;
@@ -981,7 +1080,12 @@ int pa_dbus_protocol_unregister_extension(pa_dbus_protocol *p, const char *name)
     return 0;
 }
 
-pa_hook_slot *pa_dbus_protocol_hook_connect(pa_dbus_protocol *p, pa_dbus_protocol_hook_t hook, pa_hook_priority_t prio, pa_hook_cb_t cb, void *data) {
+pa_hook_slot *pa_dbus_protocol_hook_connect(
+        pa_dbus_protocol *p,
+        pa_dbus_protocol_hook_t hook,
+        pa_hook_priority_t prio,
+        pa_hook_cb_t cb,
+        void *data) {
     pa_assert(p);
     pa_assert(hook < PA_DBUS_PROTOCOL_HOOK_MAX);
     pa_assert(cb);
diff --git a/src/pulsecore/protocol-dbus.h b/src/pulsecore/protocol-dbus.h
index d771b4f..6d100f7 100644
--- a/src/pulsecore/protocol-dbus.h
+++ b/src/pulsecore/protocol-dbus.h
@@ -56,9 +56,19 @@ void pa_dbus_protocol_unref(pa_dbus_protocol *p);
  * message isn't a good idea; if you can't handle the message, reply with an
  * error.
  *
+ * The message signature is already checked against the introspection data, so
+ * you don't have to do that yourself.
+ *
  * All messages are method calls. */
 typedef void (*pa_dbus_receive_cb_t)(DBusConnection *conn, DBusMessage *msg, void *userdata);
 
+/* A specialized version of pa_dbus_receive_cb_t: the additional iterator
+ * argument points to the element inside the new value variant.
+ *
+ * The new value signature is checked against the introspection data, so you
+ * don't have to do that yourself. */
+typedef void (*pa_dbus_set_property_cb_t)(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
+
 typedef struct pa_dbus_arg_info {
     const char *name;
     const char *type;
@@ -85,7 +95,7 @@ typedef struct pa_dbus_property_handler {
     /* The access mode for the property is determined by checking whether
      * get_cb or set_cb is NULL. */
     pa_dbus_receive_cb_t get_cb;
-    pa_dbus_receive_cb_t set_cb;
+    pa_dbus_set_property_cb_t set_cb;
 } pa_dbus_property_handler;
 
 typedef struct pa_dbus_interface_info {
@@ -140,7 +150,12 @@ pa_client *pa_dbus_protocol_get_client(pa_dbus_protocol *p, DBusConnection *conn
  * only signals from the given objects are delivered. If this function is
  * called multiple time for the same connection and signal, the latest call
  * always replaces the previous object list. */
-void pa_dbus_protocol_add_signal_listener(pa_dbus_protocol *p, DBusConnection *conn, const char *signal, char **objects, unsigned n_objects);
+void pa_dbus_protocol_add_signal_listener(
+        pa_dbus_protocol *p,
+        DBusConnection *conn,
+        const char *signal,
+        char **objects,
+        unsigned n_objects);
 
 /* Disables the delivery of the signal for the given connection. The connection
  * must have been registered. If signal is NULL, all signals are disabled. If
@@ -192,6 +207,11 @@ typedef enum pa_dbus_protocol_hook {
     PA_DBUS_PROTOCOL_HOOK_MAX
 } pa_dbus_protocol_hook_t;
 
-pa_hook_slot *pa_dbus_protocol_hook_connect(pa_dbus_protocol *p, pa_dbus_protocol_hook_t hook, pa_hook_priority_t prio, pa_hook_cb_t cb, void *data);
+pa_hook_slot *pa_dbus_protocol_hook_connect(
+        pa_dbus_protocol *p,
+        pa_dbus_protocol_hook_t hook,
+        pa_hook_priority_t prio,
+        pa_hook_cb_t cb,
+        void *data);
 
 #endif

commit 0ad2d55cbe86d13d5cd355f2b5d59360d681ccc2
Merge: 0e09663 8bf2e3f
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Sun Aug 30 20:07:31 2009 +0300

    Merge branch 'master' of git://0pointer.de/pulseaudio into dbus-work
    
    Conflicts:
    	src/modules/module-stream-restore.c

diff --cc src/modules/module-stream-restore.c
index d45cf79,d6e3c15..8389be6
--- a/src/modules/module-stream-restore.c
+++ b/src/modules/module-stream-restore.c
@@@ -1834,40 -934,13 +1834,44 @@@ static int extension_cb(pa_native_proto
                  data.data = &entry;
                  data.size = sizeof(entry);
  
+                 pa_log_debug("Client %s changes entry %s.",
+                              pa_strnull(pa_proplist_gets(pa_native_connection_get_client(c)->proplist, PA_PROP_APPLICATION_PROCESS_BINARY)),
+                              name);
+ 
 -                if (pa_database_set(u->database, &key, &data, mode == PA_UPDATE_REPLACE) == 0)
 +                if (pa_database_set(u->database, &key, &data, mode == PA_UPDATE_REPLACE) == 0) {
 +#ifdef HAVE_DBUS
 +                    struct dbus_entry *de;
 +
 +                    if (old) {
 +                        pa_assert_se((de = pa_hashmap_get(u->dbus_entries, name)));
 +
 +                        if ((old->device_valid != entry.device_valid)
 +                            || (entry.device_valid && !pa_streq(entry.device, old->device)))
 +                            send_device_updated_signal(de, &entry);
 +
 +                        if ((old->volume_valid != entry.volume_valid)
-                             || (entry.volume_valid
-                                 && (!pa_cvolume_equal(&entry.volume, &old->volume) || !pa_channel_map_equal(&entry.channel_map, &old->channel_map))))
++                            || (entry.volume_valid && (!pa_cvolume_equal(&entry.volume, &old->volume)
++                                                       || !pa_channel_map_equal(&entry.channel_map, &old->channel_map))))
 +                            send_volume_updated_signal(de, &entry);
 +
 +                        if (!old->muted_valid || (entry.muted != old->muted))
 +                            send_mute_updated_signal(de, &entry);
 +
 +                    } else {
 +                        de = dbus_entry_new(u, name);
 +                        pa_assert_se(pa_hashmap_put(u->dbus_entries, de->entry_name, de));
 +                        send_new_entry_signal(de);
 +                    }
 +#endif
 +
                      if (apply_immediately)
                          apply_entry(u, name, &entry);
 +                }
 +
 +#ifdef HAVE_DBUS
 +                if (old)
 +                    pa_xfree(old);
 +#endif
              }
  
              trigger_save(u);

commit 411feaed15edcef685e88582d76eddc5acfd965e
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Mon Aug 31 09:14:50 2009 +0300

    dbusiface-core: Add signals FallbackSinkUnset and FallbackSourceUnset.

diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c
index 0507ac9..169e8e5 100644
--- a/src/modules/dbus/iface-core.c
+++ b/src/modules/dbus/iface-core.c
@@ -247,9 +247,11 @@ enum signal_index {
     SIGNAL_NEW_SINK,
     SIGNAL_SINK_REMOVED,
     SIGNAL_FALLBACK_SINK_UPDATED,
+    SIGNAL_FALLBACK_SINK_UNSET,
     SIGNAL_NEW_SOURCE,
     SIGNAL_SOURCE_REMOVED,
     SIGNAL_FALLBACK_SOURCE_UPDATED,
+    SIGNAL_FALLBACK_SOURCE_UNSET,
     SIGNAL_NEW_PLAYBACK_STREAM,
     SIGNAL_PLAYBACK_STREAM_REMOVED,
     SIGNAL_NEW_RECORD_STREAM,
@@ -292,9 +294,11 @@ static pa_dbus_signal_info signals[SIGNAL_MAX] = {
     [SIGNAL_NEW_SINK]                = { .name = "NewSink",               .arguments = new_sink_args,                .n_arguments = 1 },
     [SIGNAL_SINK_REMOVED]            = { .name = "SinkRemoved",           .arguments = sink_removed_args,            .n_arguments = 1 },
     [SIGNAL_FALLBACK_SINK_UPDATED]   = { .name = "FallbackSinkUpdated",   .arguments = fallback_sink_updated_args,   .n_arguments = 1 },
+    [SIGNAL_FALLBACK_SINK_UNSET]     = { .name = "FallbackSinkUnset",     .arguments = NULL,                         .n_arguments = 0 },
     [SIGNAL_NEW_SOURCE]              = { .name = "NewSource",             .arguments = new_source_args,              .n_arguments = 1 },
     [SIGNAL_SOURCE_REMOVED]          = { .name = "SourceRemoved",         .arguments = source_removed_args,          .n_arguments = 1 },
     [SIGNAL_FALLBACK_SOURCE_UPDATED] = { .name = "FallbackSourceUpdated", .arguments = fallback_source_updated_args, .n_arguments = 1 },
+    [SIGNAL_FALLBACK_SOURCE_UNSET]   = { .name = "FallbackSourceUnset",   .arguments = NULL,                         .n_arguments = 0 },
     [SIGNAL_NEW_PLAYBACK_STREAM]     = { .name = "NewPlaybackStream",     .arguments = new_playback_stream_args,     .n_arguments = 1 },
     [SIGNAL_PLAYBACK_STREAM_REMOVED] = { .name = "PlaybackStreamRemoved", .arguments = playback_stream_removed_args, .n_arguments = 1 },
     [SIGNAL_NEW_RECORD_STREAM]       = { .name = "NewRecordStream",       .arguments = new_record_stream_args,       .n_arguments = 1 },
@@ -1555,6 +1559,14 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
                     pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
                     dbus_message_unref(signal);
                     signal = NULL;
+
+                } else if (!new_fallback_sink) {
+                    pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                                   PA_DBUS_CORE_INTERFACE,
+                                                                   signals[SIGNAL_FALLBACK_SINK_UNSET].name)));
+                    pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
+                    dbus_message_unref(signal);
+                    signal = NULL;
                 }
             }
 
@@ -1574,6 +1586,14 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t t, uint3
                     pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
                     dbus_message_unref(signal);
                     signal = NULL;
+
+                } else if (!new_fallback_source) {
+                    pa_assert_se((signal = dbus_message_new_signal(PA_DBUS_CORE_OBJECT_PATH,
+                                                                   PA_DBUS_CORE_INTERFACE,
+                                                                   signals[SIGNAL_FALLBACK_SOURCE_UNSET].name)));
+                    pa_dbus_protocol_send_signal(c->dbus_protocol, signal);
+                    dbus_message_unref(signal);
+                    signal = NULL;
                 }
             }
             break;

commit 8a28e5de941cece0fccaac1c2babc502e2b2d46f
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Mon Aug 31 17:17:09 2009 +0300

    dbus: Change IsMuted property names to Mute.

diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c
index 2752511..3a747a4 100644
--- a/src/modules/dbus/iface-device.c
+++ b/src/modules/dbus/iface-device.c
@@ -48,8 +48,8 @@ static void handle_get_has_flat_volume(DBusConnection *conn, DBusMessage *msg, v
 static void handle_get_has_convertible_to_decibel_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_base_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_volume_steps(DBusConnection *conn, DBusMessage *msg, void *userdata);
-static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata);
-static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
+static void handle_get_mute(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_mute(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
 static void handle_get_has_hardware_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_has_hardware_mute(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_configured_latency(DBusConnection *conn, DBusMessage *msg, void *userdata);
@@ -91,7 +91,7 @@ struct pa_dbusiface_device {
     enum device_type type;
     char *path;
     pa_cvolume volume;
-    pa_bool_t is_muted;
+    dbus_bool_t mute;
     union {
         pa_sink_state_t sink_state;
         pa_source_state_t source_state;
@@ -119,7 +119,7 @@ enum property_handler_index {
     PROPERTY_HANDLER_HAS_CONVERTIBLE_TO_DECIBEL_VOLUME,
     PROPERTY_HANDLER_BASE_VOLUME,
     PROPERTY_HANDLER_VOLUME_STEPS,
-    PROPERTY_HANDLER_IS_MUTED,
+    PROPERTY_HANDLER_MUTE,
     PROPERTY_HANDLER_HAS_HARDWARE_VOLUME,
     PROPERTY_HANDLER_HAS_HARDWARE_MUTE,
     PROPERTY_HANDLER_CONFIGURED_LATENCY,
@@ -158,7 +158,7 @@ static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
     [PROPERTY_HANDLER_HAS_CONVERTIBLE_TO_DECIBEL_VOLUME] = { .property_name = "HasConvertibleToDecibelVolume", .type = "b",      .get_cb = handle_get_has_convertible_to_decibel_volume, .set_cb = NULL },
     [PROPERTY_HANDLER_BASE_VOLUME]                       = { .property_name = "BaseVolume",                    .type = "u",      .get_cb = handle_get_base_volume,                       .set_cb = NULL },
     [PROPERTY_HANDLER_VOLUME_STEPS]                      = { .property_name = "VolumeSteps",                   .type = "u",      .get_cb = handle_get_volume_steps,                      .set_cb = NULL },
-    [PROPERTY_HANDLER_IS_MUTED]                          = { .property_name = "IsMuted",                       .type = "b",      .get_cb = handle_get_is_muted,                          .set_cb = handle_set_is_muted },
+    [PROPERTY_HANDLER_MUTE]                              = { .property_name = "Mute",                          .type = "b",      .get_cb = handle_get_mute,                              .set_cb = handle_set_mute },
     [PROPERTY_HANDLER_HAS_HARDWARE_VOLUME]               = { .property_name = "HasHardwareVolume",             .type = "b",      .get_cb = handle_get_has_hardware_volume,               .set_cb = NULL },
     [PROPERTY_HANDLER_HAS_HARDWARE_MUTE]                 = { .property_name = "HasHardwareMute",               .type = "b",      .get_cb = handle_get_has_hardware_mute,                 .set_cb = NULL },
     [PROPERTY_HANDLER_CONFIGURED_LATENCY]                = { .property_name = "ConfiguredLatency",             .type = "t",      .get_cb = handle_get_configured_latency,                .set_cb = NULL },
@@ -507,31 +507,31 @@ static void handle_get_volume_steps(DBusConnection *conn, DBusMessage *msg, void
     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &volume_steps);
 }
 
-static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+static void handle_get_mute(DBusConnection *conn, DBusMessage *msg, void *userdata) {
     pa_dbusiface_device *d = userdata;
 
     pa_assert(conn);
     pa_assert(msg);
     pa_assert(d);
 
-    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &d->is_muted);
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &d->mute);
 }
 
-static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
+static void handle_set_mute(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
     pa_dbusiface_device *d = userdata;
-    dbus_bool_t is_muted = FALSE;
+    dbus_bool_t mute = FALSE;
 
     pa_assert(conn);
     pa_assert(msg);
     pa_assert(iter);
     pa_assert(d);
 
-    dbus_message_iter_get_basic(iter, &is_muted);
+    dbus_message_iter_get_basic(iter, &mute);
 
     if (d->type == DEVICE_TYPE_SINK)
-        pa_sink_set_mute(d->sink, is_muted, TRUE);
+        pa_sink_set_mute(d->sink, mute, TRUE);
     else
-        pa_source_set_mute(d->source, is_muted, TRUE);
+        pa_source_set_mute(d->source, mute, TRUE);
 
     pa_dbus_send_empty_reply(conn, msg);
 }
@@ -897,7 +897,7 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat
     pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HAS_CONVERTIBLE_TO_DECIBEL_VOLUME].property_name, DBUS_TYPE_BOOLEAN, &has_convertible_to_decibel_volume);
     pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_BASE_VOLUME].property_name, DBUS_TYPE_UINT32, &base_volume);
     pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_VOLUME_STEPS].property_name, DBUS_TYPE_UINT32, &volume_steps);
-    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_IS_MUTED].property_name, DBUS_TYPE_BOOLEAN, &d->is_muted);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_MUTE].property_name, DBUS_TYPE_BOOLEAN, &d->mute);
     pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HAS_HARDWARE_VOLUME].property_name, DBUS_TYPE_BOOLEAN, &has_hardware_volume);
     pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_HAS_HARDWARE_MUTE].property_name, DBUS_TYPE_BOOLEAN, &has_hardware_mute);
     pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CONFIGURED_LATENCY].property_name, DBUS_TYPE_UINT64, &configured_latency);
@@ -1065,7 +1065,7 @@ static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t
     pa_dbusiface_device *d = userdata;
     DBusMessage *signal = NULL;
     const pa_cvolume *new_volume = NULL;
-    pa_bool_t new_muted = FALSE;
+    pa_bool_t new_mute = FALSE;
     pa_sink_state_t new_sink_state = 0;
     pa_source_state_t new_source_state = 0;
     pa_device_port *new_active_port = NULL;
@@ -1111,15 +1111,15 @@ static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t
         signal = NULL;
     }
 
-    new_muted = (d->type == DEVICE_TYPE_SINK) ? pa_sink_get_mute(d->sink, FALSE) : pa_source_get_mute(d->source, FALSE);
+    new_mute = (d->type == DEVICE_TYPE_SINK) ? pa_sink_get_mute(d->sink, FALSE) : pa_source_get_mute(d->source, FALSE);
 
-    if (d->is_muted != new_muted) {
-        d->is_muted = new_muted;
+    if (d->mute != new_mute) {
+        d->mute = new_mute;
 
         pa_assert_se(signal = dbus_message_new_signal(d->path,
                                                       PA_DBUSIFACE_DEVICE_INTERFACE,
                                                       signals[SIGNAL_MUTE_UPDATED].name));
-        pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_BOOLEAN, &d->is_muted, DBUS_TYPE_INVALID));
+        pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_BOOLEAN, &d->mute, DBUS_TYPE_INVALID));
 
         pa_dbus_protocol_send_signal(d->dbus_protocol, signal);
         dbus_message_unref(signal);
@@ -1201,7 +1201,7 @@ pa_dbusiface_device *pa_dbusiface_device_new_sink(pa_dbusiface_core *core, pa_si
     d->type = DEVICE_TYPE_SINK;
     d->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, SINK_OBJECT_NAME, sink->index);
     d->volume = *pa_sink_get_volume(sink, FALSE);
-    d->is_muted = pa_sink_get_mute(sink, FALSE);
+    d->mute = pa_sink_get_mute(sink, FALSE);
     d->sink_state = pa_sink_get_state(sink);
     d->ports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
     d->next_port_index = 0;
@@ -1239,7 +1239,7 @@ pa_dbusiface_device *pa_dbusiface_device_new_source(pa_dbusiface_core *core, pa_
     d->type = DEVICE_TYPE_SOURCE;
     d->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, SOURCE_OBJECT_NAME, source->index);
     d->volume = *pa_source_get_volume(source, FALSE);
-    d->is_muted = pa_source_get_mute(source, FALSE);
+    d->mute = pa_source_get_mute(source, FALSE);
     d->source_state = pa_source_get_state(source);
     d->ports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
     d->next_port_index = 0;
diff --git a/src/modules/dbus/iface-stream.c b/src/modules/dbus/iface-stream.c
index a5f9bb5..04a45e6 100644
--- a/src/modules/dbus/iface-stream.c
+++ b/src/modules/dbus/iface-stream.c
@@ -53,7 +53,7 @@ struct pa_dbusiface_stream {
     };
     uint32_t sample_rate;
     pa_cvolume volume;
-    pa_bool_t is_muted;
+    dbus_bool_t mute;
     pa_proplist *proplist;
 
     pa_dbus_protocol *dbus_protocol;
@@ -71,8 +71,8 @@ static void handle_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void
 static void handle_get_channels(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
-static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata);
-static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
+static void handle_get_mute(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_set_mute(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
 static void handle_get_buffer_latency(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_device_latency(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_get_resample_method(DBusConnection *conn, DBusMessage *msg, void *userdata);
@@ -93,7 +93,7 @@ enum property_handler_index {
     PROPERTY_HANDLER_SAMPLE_RATE,
     PROPERTY_HANDLER_CHANNELS,
     PROPERTY_HANDLER_VOLUME,
-    PROPERTY_HANDLER_IS_MUTED,
+    PROPERTY_HANDLER_MUTE,
     PROPERTY_HANDLER_BUFFER_LATENCY,
     PROPERTY_HANDLER_DEVICE_LATENCY,
     PROPERTY_HANDLER_RESAMPLE_METHOD,
@@ -111,7 +111,7 @@ static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
     [PROPERTY_HANDLER_SAMPLE_RATE]     = { .property_name = "SampleRate",     .type = "u",      .get_cb = handle_get_sample_rate,     .set_cb = NULL },
     [PROPERTY_HANDLER_CHANNELS]        = { .property_name = "Channels",       .type = "au",     .get_cb = handle_get_channels,        .set_cb = NULL },
     [PROPERTY_HANDLER_VOLUME]          = { .property_name = "Volume",         .type = "au",     .get_cb = handle_get_volume,          .set_cb = handle_set_volume },
-    [PROPERTY_HANDLER_IS_MUTED]        = { .property_name = "IsMuted",        .type = "b",      .get_cb = handle_get_is_muted,        .set_cb = handle_set_is_muted },
+    [PROPERTY_HANDLER_MUTE]            = { .property_name = "Mute",           .type = "b",      .get_cb = handle_get_mute,            .set_cb = handle_set_mute },
     [PROPERTY_HANDLER_BUFFER_LATENCY]  = { .property_name = "BufferLatency",  .type = "t",      .get_cb = handle_get_buffer_latency,  .set_cb = NULL },
     [PROPERTY_HANDLER_DEVICE_LATENCY]  = { .property_name = "DeviceLatency",  .type = "t",      .get_cb = handle_get_device_latency,  .set_cb = NULL },
     [PROPERTY_HANDLER_RESAMPLE_METHOD] = { .property_name = "ResampleMethod", .type = "s",      .get_cb = handle_get_resample_method, .set_cb = NULL },
@@ -390,7 +390,7 @@ static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessag
     pa_dbus_send_empty_reply(conn, msg);
 }
 
-static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+static void handle_get_mute(DBusConnection *conn, DBusMessage *msg, void *userdata) {
     pa_dbusiface_stream *s = userdata;
 
     pa_assert(conn);
@@ -402,26 +402,26 @@ static void handle_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *us
         return;
     }
 
-    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &s->is_muted);
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &s->mute);
 }
 
-static void handle_set_is_muted(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
+static void handle_set_mute(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
     pa_dbusiface_stream *s = userdata;
-    dbus_bool_t is_muted = FALSE;
+    dbus_bool_t mute = FALSE;
 
     pa_assert(conn);
     pa_assert(msg);
     pa_assert(iter);
     pa_assert(s);
 
-    dbus_message_iter_get_basic(iter, &is_muted);
+    dbus_message_iter_get_basic(iter, &mute);
 
     if (s->type == STREAM_TYPE_RECORD) {
         pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have mute.");
         return;
     }
 
-    pa_sink_input_set_mute(s->sink_input, is_muted, TRUE);
+    pa_sink_input_set_mute(s->sink_input, mute, TRUE);
 
     pa_dbus_send_empty_reply(conn, msg);
 };
@@ -561,7 +561,7 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat
 
     if (s->type == STREAM_TYPE_PLAYBACK) {
         pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_VOLUME].property_name, DBUS_TYPE_UINT32, volume, s->volume.channels);
-        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_IS_MUTED].property_name, DBUS_TYPE_BOOLEAN, &s->is_muted);
+        pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_MUTE].property_name, DBUS_TYPE_BOOLEAN, &s->mute);
     }
 
     pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_BUFFER_LATENCY].property_name, DBUS_TYPE_UINT64, &buffer_latency);
@@ -708,7 +708,7 @@ static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t
 
     if (s->type == STREAM_TYPE_PLAYBACK) {
         pa_cvolume new_volume;
-        pa_bool_t new_muted = FALSE;
+        pa_bool_t new_mute = FALSE;
 
         pa_sink_input_get_volume(s->sink_input, &new_volume, TRUE);
 
@@ -733,15 +733,15 @@ static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t
             signal = NULL;
         }
 
-        new_muted = pa_sink_input_get_mute(s->sink_input);
+        new_mute = pa_sink_input_get_mute(s->sink_input);
 
-        if (s->is_muted != new_muted) {
-            s->is_muted = new_muted;
+        if (s->mute != new_mute) {
+            s->mute = new_mute;
 
             pa_assert_se(signal = dbus_message_new_signal(s->path,
                                                           PA_DBUSIFACE_STREAM_INTERFACE,
                                                           signals[SIGNAL_MUTE_UPDATED].name));
-            pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_BOOLEAN, &s->is_muted, DBUS_TYPE_INVALID));
+            pa_assert_se(dbus_message_append_args(signal, DBUS_TYPE_BOOLEAN, &s->mute, DBUS_TYPE_INVALID));
 
             pa_dbus_protocol_send_signal(s->dbus_protocol, signal);
             dbus_message_unref(signal);
@@ -823,7 +823,7 @@ pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, p
     s->sink = pa_sink_ref(sink_input->sink);
     s->sample_rate = sink_input->sample_spec.rate;
     pa_sink_input_get_volume(sink_input, &s->volume, TRUE);
-    s->is_muted = pa_sink_input_get_mute(sink_input);
+    s->mute = pa_sink_input_get_mute(sink_input);
     s->proplist = pa_proplist_copy(sink_input->proplist);
     s->dbus_protocol = pa_dbus_protocol_get(sink_input->core);
     s->subscription = pa_subscription_new(sink_input->core, PA_SUBSCRIPTION_MASK_SINK_INPUT, subscription_cb, s);
@@ -851,7 +851,7 @@ pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_dbusiface_core *core, pa_
     s->source = pa_source_ref(source_output->source);
     s->sample_rate = source_output->sample_spec.rate;
     pa_cvolume_init(&s->volume);
-    s->is_muted = FALSE;
+    s->mute = FALSE;
     s->proplist = pa_proplist_copy(source_output->proplist);
     s->dbus_protocol = pa_dbus_protocol_get(source_output->core);
     s->subscription = pa_subscription_new(source_output->core, PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscription_cb, s);
diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c
index 8389be6..5a6c8a3 100644
--- a/src/modules/module-stream-restore.c
+++ b/src/modules/module-stream-restore.c
@@ -170,8 +170,8 @@ static void handle_entry_get_device(DBusConnection *conn, DBusMessage *msg, void
 static void handle_entry_set_device(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
 static void handle_entry_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata);
 static void handle_entry_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
-static void handle_entry_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata);
-static void handle_entry_set_is_muted(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
+static void handle_entry_get_mute(DBusConnection *conn, DBusMessage *msg, void *userdata);
+static void handle_entry_set_mute(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata);
 
 static void handle_entry_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
 
@@ -188,7 +188,7 @@ enum entry_property_handler_index {
     ENTRY_PROPERTY_HANDLER_NAME,
     ENTRY_PROPERTY_HANDLER_DEVICE,
     ENTRY_PROPERTY_HANDLER_VOLUME,
-    ENTRY_PROPERTY_HANDLER_IS_MUTED,
+    ENTRY_PROPERTY_HANDLER_MUTE,
     ENTRY_PROPERTY_HANDLER_MAX
 };
 
@@ -202,7 +202,7 @@ static pa_dbus_property_handler entry_property_handlers[ENTRY_PROPERTY_HANDLER_M
     [ENTRY_PROPERTY_HANDLER_NAME]     = { .property_name = "Name",    .type = "s",     .get_cb = handle_entry_get_name,     .set_cb = NULL },
     [ENTRY_PROPERTY_HANDLER_DEVICE]   = { .property_name = "Device",  .type = "s",     .get_cb = handle_entry_get_device,   .set_cb = handle_entry_set_device },
     [ENTRY_PROPERTY_HANDLER_VOLUME]   = { .property_name = "Volume",  .type = "a(uu)", .get_cb = handle_entry_get_volume,   .set_cb = handle_entry_set_volume },
-    [ENTRY_PROPERTY_HANDLER_IS_MUTED] = { .property_name = "IsMuted", .type = "b",     .get_cb = handle_entry_get_is_muted, .set_cb = handle_entry_set_is_muted }
+    [ENTRY_PROPERTY_HANDLER_MUTE]     = { .property_name = "Mute",    .type = "b",     .get_cb = handle_entry_get_mute,     .set_cb = handle_entry_set_mute }
 };
 
 enum method_handler_index {
@@ -216,11 +216,11 @@ enum entry_method_handler_index {
     ENTRY_METHOD_HANDLER_MAX
 };
 
-static pa_dbus_arg_info add_entry_args[] = { { "name",     "s",     "in" },
-                                             { "device",   "s",     "in" },
-                                             { "volume",   "a(uu)", "in" },
-                                             { "is_muted", "b",     "in" },
-                                             { "entry",    "o",     "out" } };
+static pa_dbus_arg_info add_entry_args[] = { { "name",   "s",     "in" },
+                                             { "device", "s",     "in" },
+                                             { "volume", "a(uu)", "in" },
+                                             { "mute",   "b",     "in" },
+                                             { "entry",  "o",     "out" } };
 static pa_dbus_arg_info get_entry_by_name_args[] = { { "name", "s", "in" }, { "entry", "o", "out" } };
 
 static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
@@ -837,10 +837,10 @@ static void handle_entry_set_volume(DBusConnection *conn, DBusMessage *msg, DBus
     pa_xfree(e);
 }
 
-static void handle_entry_get_is_muted(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+static void handle_entry_get_mute(DBusConnection *conn, DBusMessage *msg, void *userdata) {
     struct dbus_entry *de = userdata;
     struct entry *e;
-    dbus_bool_t muted;
+    dbus_bool_t mute;
 
     pa_assert(conn);
     pa_assert(msg);
@@ -848,16 +848,16 @@ static void handle_entry_get_is_muted(DBusConnection *conn, DBusMessage *msg, vo
 
     pa_assert_se(e = read_entry(de->userdata, de->entry_name));
 
-    muted = e->muted_valid ? e->muted : FALSE;
+    mute = e->muted_valid ? e->muted : FALSE;
 
-    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &muted);
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &mute);
 
     pa_xfree(e);
 }
 
-static void handle_entry_set_is_muted(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
+static void handle_entry_set_mute(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
     struct dbus_entry *de = userdata;
-    pa_bool_t muted;
+    dbus_bool_t mute;
     struct entry *e;
     pa_bool_t updated;
 
@@ -866,17 +866,17 @@ static void handle_entry_set_is_muted(DBusConnection *conn, DBusMessage *msg, DB
     pa_assert(iter);
     pa_assert(de);
 
-    dbus_message_iter_get_basic(iter, &muted);
+    dbus_message_iter_get_basic(iter, &mute);
 
     pa_assert_se(e = read_entry(de->userdata, de->entry_name));
 
-    updated = !e->muted_valid || e->muted != muted;
+    updated = !e->muted_valid || e->muted != mute;
 
     if (updated) {
         pa_datum key;
         pa_datum value;
 
-        e->muted = muted;
+        e->muted = mute;
         e->muted_valid = TRUE;
 
         key.data = de->entry_name;
@@ -902,7 +902,7 @@ static void handle_entry_get_all(DBusConnection *conn, DBusMessage *msg, void *u
     DBusMessageIter dict_iter;
     DBusMessageIter dict_entry_iter;
     const char *device;
-    dbus_bool_t muted;
+    dbus_bool_t mute;
 
     pa_assert(conn);
     pa_assert(msg);
@@ -911,7 +911,7 @@ static void handle_entry_get_all(DBusConnection *conn, DBusMessage *msg, void *u
     pa_assert_se(e = read_entry(de->userdata, de->entry_name));
 
     device = e->device_valid ? e->device : "";
-    muted = e->muted_valid ? e->muted : FALSE;
+    mute = e->muted_valid ? e->muted : FALSE;
 
     pa_assert_se((reply = dbus_message_new_method_return(msg)));
 
@@ -929,7 +929,7 @@ static void handle_entry_get_all(DBusConnection *conn, DBusMessage *msg, void *u
 
     pa_assert_se(dbus_message_iter_close_container(&dict_iter, &dict_entry_iter));
 
-    pa_dbus_append_basic_variant_dict_entry(&dict_iter, entry_property_handlers[ENTRY_PROPERTY_HANDLER_IS_MUTED].property_name, DBUS_TYPE_BOOLEAN, &muted);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, entry_property_handlers[ENTRY_PROPERTY_HANDLER_MUTE].property_name, DBUS_TYPE_BOOLEAN, &mute);
 
     pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
 

commit 587131917f9129c8347c789febb7e755dfb091de
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Mon Aug 31 18:12:55 2009 +0300

    dbus-protocol: Implement argument type checking for normal methods.

diff --git a/src/modules/dbus/iface-client.c b/src/modules/dbus/iface-client.c
index 54550d2..546370f 100644
--- a/src/modules/dbus/iface-client.c
+++ b/src/modules/dbus/iface-client.c
@@ -101,7 +101,7 @@ static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
     [METHOD_HANDLER_REMOVE_PROPERTIES] = {
         .method_name = "RemoveProperties",
         .arguments = remove_properties_args,
-        .n_arguments = sizeof(update_properties_args) / sizeof(pa_dbus_arg_info),
+        .n_arguments = sizeof(remove_properties_args) / sizeof(pa_dbus_arg_info),
         .receive_cb = handle_remove_properties }
 };
 
diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c
index 9102251..5c1127b 100644
--- a/src/pulsecore/protocol-dbus.c
+++ b/src/pulsecore/protocol-dbus.c
@@ -72,6 +72,7 @@ struct connection_entry {
 struct interface_entry {
     char *name;
     pa_hashmap *method_handlers;
+    pa_hashmap *method_signatures; /* Derived from method_handlers. Contains only "in" arguments. */
     pa_hashmap *property_handlers;
     pa_dbus_receive_cb_t get_all_properties_cb;
     pa_dbus_signal_info *signals;
@@ -354,8 +355,14 @@ static enum find_result_t find_handler_by_method(struct call_info *call_info) {
     pa_assert(call_info);
 
     PA_HASHMAP_FOREACH(call_info->iface_entry, call_info->obj_entry->interfaces, state) {
-        if ((call_info->method_handler = pa_hashmap_get(call_info->iface_entry->method_handlers, call_info->method)))
-            return FOUND_METHOD;
+        if ((call_info->method_handler = pa_hashmap_get(call_info->iface_entry->method_handlers, call_info->method))) {
+            call_info->expected_method_sig = pa_hashmap_get(call_info->iface_entry->method_signatures, call_info->method);
+
+            if (pa_streq(call_info->method_sig, call_info->expected_method_sig))
+                return FOUND_METHOD;
+            else
+                return INVALID_METHOD_SIG;
+        }
     }
 
     return NO_SUCH_METHOD;
@@ -630,6 +637,31 @@ static pa_hashmap *create_method_handlers(const pa_dbus_interface_info *info) {
     return handlers;
 }
 
+static pa_hashmap *extract_method_signatures(pa_hashmap *method_handlers) {
+    pa_hashmap *signatures = NULL;
+    pa_dbus_method_handler *handler = NULL;
+    void *state = NULL;
+    pa_strbuf *sig_buf = NULL;
+    unsigned i = 0;
+
+    pa_assert(method_handlers);
+
+    signatures = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+
+    PA_HASHMAP_FOREACH(handler, method_handlers, state) {
+        sig_buf = pa_strbuf_new();
+
+        for (i = 0; i < handler->n_arguments; ++i) {
+            if (pa_streq(handler->arguments[i].direction, "in"))
+                pa_strbuf_puts(sig_buf, handler->arguments[i].type);
+        }
+
+        pa_hashmap_put(signatures, handler->method_name, pa_strbuf_tostring_free(sig_buf));
+    }
+
+    return signatures;
+}
+
 static pa_hashmap *create_property_handlers(const pa_dbus_interface_info *info) {
     pa_hashmap *handlers;
     unsigned i = 0;
@@ -707,6 +739,7 @@ int pa_dbus_protocol_add_interface(pa_dbus_protocol *p,
     iface_entry = pa_xnew(struct interface_entry, 1);
     iface_entry->name = pa_xstrdup(info->name);
     iface_entry->method_handlers = create_method_handlers(info);
+    iface_entry->method_signatures = extract_method_signatures(iface_entry->method_handlers);
     iface_entry->property_handlers = create_property_handlers(info);
     iface_entry->get_all_properties_cb = info->get_all_properties_cb;
     iface_entry->signals = copy_signals(info);
@@ -760,6 +793,12 @@ static void method_handler_free_cb(void *p, void *userdata) {
     pa_xfree((pa_dbus_arg_info *) h->arguments);
 }
 
+static void method_signature_free_cb(void *p, void *userdata) {
+    pa_assert(p);
+
+    pa_xfree(p);
+}
+
 static void property_handler_free_cb(void *p, void *userdata) {
     pa_dbus_property_handler *h = p;
 
@@ -792,6 +831,7 @@ int pa_dbus_protocol_remove_interface(pa_dbus_protocol *p, const char* path, con
 
     pa_xfree(iface_entry->name);
     pa_hashmap_free(iface_entry->method_handlers, method_handler_free_cb, NULL);
+    pa_hashmap_free(iface_entry->method_signatures, method_signature_free_cb, NULL);
     pa_hashmap_free(iface_entry->property_handlers, property_handler_free_cb, NULL);
 
     for (i = 0; i < iface_entry->n_signals; ++i) {

commit 431555030ead3aabad83028f359849314e95065e
Author: Jason Newton <jason at arcuid.wyred.org>
Date:   Mon Jun 22 00:36:14 2009 -0700

    module-equalizer-sink added
    src/Makefile.am: added module-equalizer-sink

diff --git a/src/Makefile.am b/src/Makefile.am
index 6544e2a..10f9a79 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1006,6 +1006,7 @@ modlibexec_LTLIBRARIES += \
 		module-combine.la \
 		module-remap-sink.la \
 		module-ladspa-sink.la \
+		module-equalizer-sink.la \
 		module-esound-sink.la \
 		module-tunnel-sink.la \
 		module-tunnel-source.la \
@@ -1200,6 +1201,7 @@ SYMDEF_FILES = \
 		modules/module-combine-symdef.h \
 		modules/module-remap-sink-symdef.h \
 		modules/module-ladspa-sink-symdef.h \
+		modules/module-equalizer-sink-symdef.h \
 		modules/module-esound-compat-spawnfd-symdef.h \
 		modules/module-esound-compat-spawnpid-symdef.h \
 		modules/module-match-symdef.h \
@@ -1380,6 +1382,11 @@ module_ladspa_sink_la_CFLAGS = -DLADSPA_PATH=\"$(libdir)/ladspa:/usr/local/lib/l
 module_ladspa_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
 module_ladspa_sink_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
 
+module_equalizer_sink_la_SOURCES = modules/module-equalizer-sink.c
+module_equalizer_sink_la_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
+module_equalizer_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_equalizer_sink_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) $(LIBOIL_LIBS) -lfftw3f libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
+
 module_match_la_SOURCES = modules/module-match.c
 module_match_la_LDFLAGS = $(MODULE_LDFLAGS)
 module_match_la_LIBADD = $(AM_LIBADD) libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
new file mode 100755
index 0000000..8b34fa0
--- /dev/null
+++ b/src/modules/module-equalizer-sink.c
@@ -0,0 +1,850 @@
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <fftw3.h>
+#include <float.h>
+
+
+#include <pulse/xmalloc.h>
+#include <pulse/i18n.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/ltdl-helper.h>
+#include <liboil/liboilfuncs.h>
+#include <liboil/liboil.h>
+
+
+#include <stdint.h>
+#include <time.h>
+
+
+#include "module-equalizer-sink-symdef.h"
+
+PA_MODULE_AUTHOR("Jason Newton");
+PA_MODULE_DESCRIPTION(_("General Purpose Equalizer"));
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE(_("sink=<sink to connect to> "));
+
+#define MEMBLOCKQ_MAXLENGTH (16*1024*1024)
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    pa_sink *sink, *master;
+    pa_sink_input *sink_input;
+
+    size_t channels;
+    size_t fft_size; //length (res) of fft
+    size_t window_size;//even!
+    size_t overlap_size;
+    size_t samples_gathered;
+    size_t n_buffered_output;
+    size_t max_output;
+    float *H;//frequency response filter (magnitude based)
+    float *W;//windowing function (time domain)
+    float *work_buffer,**input,**overlap_accum,**output_buffer;
+    fftwf_complex *output_window;
+    fftwf_plan forward_plan,inverse_plan;
+
+    pa_memblockq *memblockq;
+};
+
+static const char* const valid_modargs[] = {
+    "sink_name",
+    "sink_properties",
+    "master",
+    "format",
+    "rate",
+    "channels",
+    "channel_map",
+    NULL
+};
+
+uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p)
+{
+    return ((timeA_p->tv_sec * 1000000000) + timeA_p->tv_nsec) -
+    ((timeB_p->tv_sec * 1000000000) + timeB_p->tv_nsec);
+}
+
+void hanning_normalized_window(float *W,size_t window_size){
+    //h = sqrt(2)/2 * (1+cos(t*pi)) ./ sqrt( 1+cos(t*pi).^2 )
+    float c;
+    for(size_t i=0;i<window_size;++i){
+        c=cos(M_PI*i/(window_size-1));
+        W[i]=sqrt(2.0)/2.0*(1.0+c) / sqrt(1.0+c*c);
+    }
+}
+void hanning_window(float *W,size_t window_size){
+    //h=.5*(1-cos(2*pi*j/(window_size+1)), COLA for R=(M+1)/2
+    for(size_t i=0;i<window_size;++i){
+        W[i]=.5*(1-cos(2*M_PI*i/(window_size+1)));
+    }
+}
+void hamming_window(float *W,size_t window_size){
+    //h=.54-.46*cos(2*pi*j/(window_size-1))
+    //COLA for R=(M-1)/2,(M-1)/4 etc when endpoints are divided by 2
+    //or one endpoint is zeroed
+    float m;
+    for(size_t i=0;i<window_size;++i){
+        m=i;
+        m/=(window_size-1);
+        W[i]=.54-.46*cos(2*M_PI*m);
+    }
+    W[0]/=2;
+    W[window_size-1]/=2;
+}
+void blackman_window(float *W,size_t window_size){
+    //h=.42-.5*cos(2*pi*m)+.08*cos(4*pi*m), m=(0:W-1)/(W-1)
+    //COLA for R=(M-1)/3 when M is odd and R is an integer
+    //R=M/3 when M is even and R is an integer
+    float m;
+    for(size_t i=0;i<window_size;++i){
+        m=i;
+        m/=(window_size-1);
+        W[i]=.42-.5*cos(2*M_PI*m)+.08*cos(4*M_PI*m);
+    }
+}
+
+
+void sin_window(float *W,size_t window_size){
+    //h = (cos(t*pi)+1)/2 .* float(abs(t)<1);
+    for(size_t i=0;i<window_size;++i){
+        W[i]=sin(M_PI*i/(window_size-1));
+    }
+}
+
+
+void array_out(const char *name,float *a,size_t length){
+    FILE *p=fopen(name,"w");
+    for(size_t i=0;i<length;++i){
+        fprintf(p,"%e,",a[i]);
+        //if(i%1000==0){
+        //    fprintf(p,"\n");
+        //}
+    }
+    fprintf(p,"\n");
+    fclose(p);
+}
+
+
+/* Called from I/O thread context */
+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;
+
+    switch (code) {
+
+        case PA_SINK_MESSAGE_GET_LATENCY: {
+            pa_usec_t usec = 0;
+            pa_sample_spec *ss=&u->sink->sample_spec;
+
+            /* Get the latency of the master sink */
+            if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
+                usec = 0;
+
+            usec+=pa_bytes_to_usec(u->n_buffered_output*pa_frame_size(ss),ss);
+            /* Add the latency internal to our sink input on top */
+            usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec);
+            *((pa_usec_t*) data) = usec;
+            return 0;
+        }
+    }
+
+    return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+
+/* Called from main context */
+static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (PA_SINK_IS_LINKED(state) &&
+        u->sink_input &&
+        PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input)))
+
+        pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED);
+
+    return 0;
+}
+
+/* Called from I/O thread context */
+static void sink_request_rewind(pa_sink *s) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    /* Just hand this one over to the master sink */
+    pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes + pa_memblockq_get_length(u->memblockq), TRUE, FALSE, FALSE);
+}
+
+/* Called from I/O thread context */
+static void sink_update_requested_latency(pa_sink *s) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    /* Just hand this one over to the master sink */
+    pa_sink_input_set_requested_latency_within_thread(
+            u->sink_input,
+            pa_sink_get_requested_latency_within_thread(s));
+}
+
+/* Called from I/O thread context */
+static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
+    struct userdata *u;
+    float *src, *dst;
+    size_t c;
+    pa_memchunk tchunk;
+    pa_sink_input_assert_ref(i);
+    pa_assert(chunk);
+    pa_assert_se(u = i->userdata);
+    size_t fs = pa_frame_size(&u->sink->sample_spec);
+    size_t ss=pa_sample_size(&u->sink->sample_spec);
+    size_t fe = fs/ss;
+
+    if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
+        return -1;
+
+    //output any buffered outputs first
+    if(u->n_buffered_output>0){
+        //pa_log("outputing %ld buffered samples",u->n_buffered_output);
+        chunk->index = 0;
+        size_t n_outputable=PA_MIN(u->n_buffered_output,nbytes/fs);
+        chunk->length = n_outputable*fs;
+        chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length);
+        pa_memblockq_drop(u->memblockq, chunk->length);
+        dst = (float*) pa_memblock_acquire(chunk->memblock);
+        for(size_t j=0;j<u->channels;++j){
+            pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+j, fs, u->output_buffer[j], sizeof(float),n_outputable);
+            memmove(u->output_buffer[j],u->output_buffer[j]+n_outputable,(u->n_buffered_output-n_outputable)*sizeof(float));
+        }
+        u->n_buffered_output-=n_outputable;
+        pa_memblock_release(chunk->memblock);
+        return 0;
+    }
+    pa_assert_se(u->n_buffered_output==0);
+
+    //collect the minimum number of samples
+    while(u->samples_gathered < (u->window_size-u->overlap_size)){
+        //render some new fragments to our memblockq
+        //size_t desired_samples=PA_MIN(u->min_input-samples_gathered,u->max_output);
+        size_t desired_samples=PA_MIN((u->window_size-u->overlap_size)-u->samples_gathered,u->max_output);
+        while (pa_memblockq_peek(u->memblockq, &tchunk) < 0) {
+            pa_memchunk nchunk;
+
+            pa_sink_render(u->sink, desired_samples*fs, &nchunk);
+            pa_memblockq_push(u->memblockq, &nchunk);
+            pa_memblock_unref(nchunk.memblock);
+        }
+        if(tchunk.length/fs!=desired_samples){
+            pa_log("got %ld samples, asked for %ld",tchunk.length/fs,desired_samples);
+        }
+        size_t n_samples=PA_MIN(tchunk.length/fs,u->window_size-u->overlap_size-u->samples_gathered);
+        //TODO: figure out what to do with rest of the samples when there's too many (rare?)
+        src = (float*) ((uint8_t*) pa_memblock_acquire(tchunk.memblock) + tchunk.index);
+        for (size_t c=0;c<u->channels;c++) {
+            pa_sample_clamp(PA_SAMPLE_FLOAT32NE,u->input[c]+u->overlap_size+u->samples_gathered,sizeof(float), src+c, fs, n_samples);
+        }
+
+        u->samples_gathered+=n_samples;
+        pa_memblock_release(tchunk.memblock);
+        pa_memblock_unref(tchunk.memblock);
+    }
+    //IT should be this guy if we're buffering like how its supposed to
+    //size_t n_outputable=PA_MIN(u->window_size-u->overlap_size,nbytes/fs);
+    //This one takes into account the actual data gathered but then the dsp
+    //stuff is wrong when the buffer "underruns"
+    size_t n_outputable=PA_MIN(u->samples_gathered,nbytes/fs);
+    /*
+    //debugging: tests if immediate release of freshly buffered data
+    //plays ok and prevents any other processing
+    chunk->index=0;
+    chunk->length=n_outputable*fs;
+    chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length);
+    pa_memblockq_drop(u->memblockq, chunk->length);
+    dst = (float*) pa_memblock_acquire(chunk->memblock);;
+    for (size_t c=0;c<u->channels;c++) {
+        pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c, fs, u->input[c]+u->overlap_size, sizeof(float),n_outputable);
+    }
+    u->samples_gathered=0;
+    pa_memblock_release(chunk->memblock);
+    return 0;
+    */
+
+    //pa_log("%ld dequed samples",u->samples_gathered);
+
+    chunk->index=0;
+    chunk->length=n_outputable*fs;
+    chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length);
+    pa_memblockq_drop(u->memblockq, chunk->length);
+    dst = (float*) pa_memblock_acquire(chunk->memblock);
+    //pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input, sizeof(float), src+c, fs, samples);
+    //pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c,fs, u->input, sizeof(float), samples);
+
+    /*
+    struct timespec start, end;
+    uint64_t elapsed;
+    clock_gettime(CLOCK_MONOTONIC, &start);
+    */
+    //use a zero-phase sliding dft and overlap-add method
+
+    pa_assert_se(u->fft_size>=u->window_size);
+    //pa_assert_se(u->window_size%2==0);
+    pa_assert_se(u->overlap_size<u->window_size);
+    pa_assert_se(u->samples_gathered>=u->window_size-u->overlap_size);
+    size_t sample_rem=u->window_size-u->overlap_size-n_outputable;
+    //size_t w_mid=u->window_size/2;
+    //pa_log("hello world a");
+    for (c=0;c<u->channels;c++) {
+        //center the data for zero phase
+        //zero-pad TODO: optimization if sure these zeros aren't overwritten
+        //memset(u->work_buffer+w_mid,0,(u->fft_size-u->window_size)*sizeof(float));
+        //memset(u->work_buffer,0,u->fft_size*sizeof(float));
+        /*
+        for(size_t j=0;j<u->window_size;++j){
+            u->work_buffer[j]=u->W[j]*u->input[c][j];
+            u->work_buffer[j]=u->input[c][j];
+        }
+        */
+        //zero padd the data, don't worry about zerophase, shouldn't really matter
+        memset(u->work_buffer+u->overlap_size,0,(u->fft_size-u->overlap_size)*sizeof(float));
+        //window the data
+        for(size_t j=0;j<u->window_size;++j){
+            u->work_buffer[j]=u->W[j]*u->input[c][j];
+        }
+        /*
+        //recenter for zero phase
+        for(size_t j=0;j<w_mid;++j){
+            float tmp=u->work_buffer[j];
+            u->work_buffer[j]=u->input[c][j+w_mid];
+            u->work_buffer[j+u->fft_size-w_mid]=tmp;
+        }
+        */
+        //pa_log("hello world b");
+
+        /*
+        //window and zero phase shift
+        for(size_t j=0;j<w_mid;++j){
+            //u->work_buffer[j]=u->input[c][j+w_mid];
+            //u->work_buffer[j+u->fft_size-w_mid]=u->input[c][j];
+            u->work_buffer[j]=u->W[j+w_mid]*u->input[c][j+w_mid];
+            u->work_buffer[j+u->fft_size-w_mid]=u->W[j]*u->input[c][j];
+        }*/
+        //Processing is done here!
+        //do fft
+        fftwf_execute_dft_r2c(u->forward_plan,u->work_buffer,u->output_window);
+        //perform filtering
+        for(size_t j=0;j<u->fft_size/2+1;++j){
+            ////identity transform (fft size)
+            //u->output_window[j][0]/=u->fft_size;
+            //u->output_window[j][1]/=u->fft_size;
+            ////identity transform (window size)
+            //u->output_window[j][0]/=u->window_size;
+            //u->output_window[j][1]/=u->window_size;
+            //filtered
+            u->output_window[j][0]*=u->H[j];
+            u->output_window[j][1]*=u->H[j];
+        }
+        //inverse fft
+        fftwf_execute_dft_c2r(u->inverse_plan,u->output_window,u->work_buffer);
+
+        /*
+        //uncenter the data
+        for(size_t j=0;j<w_mid;++j){
+            const float tmp=u->work_buffer[j];
+            u->work_buffer[j]=u->work_buffer[j+u->fft_size-w_mid];
+            u->work_buffer[j+w_mid]=tmp;
+        }
+        */
+        /*
+        //divide out fft gain (more stable here?)
+        for(size_t j=0;j<u->window_size;++j){
+            u->work_buffer[j]/=u->fft_size;
+        }
+        */
+        /*
+        //debug: tests overlaping add
+        //and negates ALL PREVIOUS processing
+        //yields a perfect reconstruction if COLA is held
+        for(size_t j=0;j<u->window_size;++j){
+            u->work_buffer[j]=u->W[j]*u->input[c][j];
+        }
+        */
+        /*
+        //debug: tests if basic buffering works
+        //shouldn't modify the signal AT ALL
+        for(size_t j=0;j<u->window_size;++j){
+            u->work_buffer[j]=u->input[c][j];
+        }
+        */
+
+        /*
+        //overlap add and preserve overlap component from this window (zero phase)
+        for(size_t j=0;j<u->overlap_size;++j){
+            u->work_buffer[j]+=u->overlap_accum[c][j];
+            u->overlap_accum[c][j]=u->work_buffer[u->window_size-u->overlap_size+j];
+        }
+        */
+        //overlap add and preserve overlap component from this window (linear phase)
+        for(size_t j=0;j<u->overlap_size;++j){
+            u->work_buffer[j]+=u->overlap_accum[c][j];
+            u->overlap_accum[c][j]=u->work_buffer[u->window_size-u->overlap_size+j];
+        }
+
+        //preseve the needed input for the next windows overlap
+        memmove(u->input[c],u->input[c]+u->overlap_size,(u->window_size-u->overlap_size)*sizeof(float));
+        //output the samples that are outputable now
+        pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c, fs, u->work_buffer, sizeof(float),n_outputable);
+        //buffer the rest of them
+        memcpy(u->output_buffer[c]+u->n_buffered_output,u->work_buffer+n_outputable,sample_rem*sizeof(float));
+    }
+    /*
+    clock_gettime(CLOCK_MONOTONIC, &end);
+    elapsed=time_diff(&end, &start);
+    pa_log("processed: %ld, time: %ld",u->samples_gathered,elapsed);
+    */
+    u->n_buffered_output+=sample_rem;
+    u->samples_gathered=0;
+
+
+    //pa_log("%ld samples queued",u->n_buffered_output);
+
+    pa_memblock_release(chunk->memblock);
+
+
+    return 0;
+}
+
+/* Called from I/O thread context */
+static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    struct userdata *u;
+    size_t amount = 0;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
+        return;
+
+    if (u->sink->thread_info.rewind_nbytes > 0) {
+        size_t max_rewrite;
+
+        max_rewrite = nbytes + pa_memblockq_get_length(u->memblockq);
+        amount = PA_MIN(u->sink->thread_info.rewind_nbytes, max_rewrite);
+        u->sink->thread_info.rewind_nbytes = 0;
+
+        if (amount > 0) {
+            pa_memblockq_seek(u->memblockq, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE);
+            pa_log_debug("Resetting equalizer");
+        }
+    }
+
+    pa_sink_process_rewind(u->sink, amount);
+    pa_memblockq_rewind(u->memblockq, nbytes);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        return;
+
+    pa_memblockq_set_maxrewind(u->memblockq, nbytes);
+    pa_sink_set_max_rewind_within_thread(u->sink, nbytes);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        return;
+
+    pa_sink_set_max_request_within_thread(u->sink, nbytes);
+}
+
+/* Called from I/O thread context */
+static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        return;
+
+    pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
+}
+
+/* Called from I/O thread context */
+static void sink_input_detach_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        return;
+
+    pa_sink_detach_within_thread(u->sink);
+    pa_sink_set_asyncmsgq(u->sink, NULL);
+    pa_sink_set_rtpoll(u->sink, NULL);
+}
+
+/* Called from I/O thread context */
+static void sink_input_attach_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
+        return;
+
+    pa_sink_set_asyncmsgq(u->sink, i->sink->asyncmsgq);
+    pa_sink_set_rtpoll(u->sink, i->sink->rtpoll);
+    pa_sink_attach_within_thread(u->sink);
+
+    pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency);
+}
+
+/* Called from main context */
+static void sink_input_kill_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_sink_unlink(u->sink);
+    pa_sink_input_unlink(u->sink_input);
+
+    pa_sink_unref(u->sink);
+    u->sink = NULL;
+    pa_sink_input_unref(u->sink_input);
+    u->sink_input = NULL;
+
+    pa_module_unload_request(u->module, TRUE);
+}
+
+/* Called from IO thread context */
+static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    /* If we are added for the first time, ask for a rewinding so that
+     * we are heard right-away. */
+    if (PA_SINK_INPUT_IS_LINKED(state) &&
+        i->thread_info.state == PA_SINK_INPUT_INIT) {
+        pa_log_debug("Requesting rewind due to state change.");
+        pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE);
+    }
+}
+
+/* Called from main context */
+static pa_bool_t sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    return u->sink != dest;
+}
+
+int pa__init(pa_module*m) {
+    struct userdata *u;
+    pa_sample_spec ss;
+    pa_channel_map map;
+    pa_modargs *ma;
+    const char *z;
+    pa_sink *master;
+    pa_sink_input_new_data sink_input_data;
+    pa_sink_new_data sink_data;
+    pa_bool_t *use_default = NULL;
+    size_t fs;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    if (!(master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "master", NULL), PA_NAMEREG_SINK))) {
+        pa_log("Master sink not found");
+        goto fail;
+    }
+
+    ss = master->sample_spec;
+    ss.format = PA_SAMPLE_FLOAT32;
+    map = master->channel_map;
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
+        pa_log("Invalid sample format specification or channel map");
+        goto fail;
+    }
+    fs=pa_frame_size(&ss);
+
+    u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    m->userdata = u;
+    u->master = master;
+    u->sink = NULL;
+    u->sink_input = NULL;
+    u->memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, fs, 1, 1, 0, NULL);
+
+    //u->fft_size=44100;
+    //u->fft_size=48000;
+    //u->fft_size=1024;
+    u->channels=ss.channels;
+    u->fft_size=pow(2,ceil(log(ss.rate)/log(2)));
+    //u->fft_size=ss.rate;
+    //u->fft_size=65536;
+    pa_log("fft size: %ld",u->fft_size);
+    u->window_size=8001;
+    u->overlap_size=(u->window_size+1)/2;
+    //u->overlap_size=u->window_size/2;
+    //u->overlap_size=0;
+    u->samples_gathered=0;
+    u->n_buffered_output=0;
+    u->max_output=pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss)/pa_frame_size(&ss);
+    u->H=(float*) fftwf_malloc((u->fft_size/2+1)*sizeof(float));
+    u->W=(float*) fftwf_malloc((u->window_size)*sizeof(float));
+    u->work_buffer=(float*) fftwf_malloc(u->fft_size*sizeof(float));
+    u->input=(float **)malloc(sizeof(float *)*u->channels);
+    u->overlap_accum=(float **)malloc(sizeof(float *)*u->channels);
+    u->output_buffer=(float **)malloc(sizeof(float *)*u->channels);
+    for(size_t c=0;c<u->channels;++c){
+        u->input[c]=(float*) fftwf_malloc(u->window_size*sizeof(float));
+        memset(u->input[c],0,u->window_size*sizeof(float));
+        u->overlap_accum[c]=(float*) fftwf_malloc(u->overlap_size*sizeof(float));
+        memset(u->overlap_accum[c],0,u->overlap_size*sizeof(float));
+        u->output_buffer[c]=(float*) fftwf_malloc(u->window_size*sizeof(float));
+    }
+    u->output_window = (fftwf_complex *) fftwf_malloc(sizeof(fftwf_complex) * (u->fft_size/2+1));
+    u->forward_plan=fftwf_plan_dft_r2c_1d(u->fft_size, u->work_buffer, u->output_window, FFTW_ESTIMATE);
+    u->inverse_plan=fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_ESTIMATE);
+
+    /*
+    //rectangular window
+    for(size_t j=0;j<u->window_size;++j){
+        u->W[j]=1.0;
+    }
+    */
+    //hanning_normalized_window(u->W,u->window_size);
+    hanning_window(u->W,u->window_size);
+    //sin_window(u->W,u->window_size);
+    array_out("/home/jason/window.txt",u->W,u->window_size);
+    //u->forward_plan=fftwf_plan_dft_r2c_1d(u->fft_size, u->input, u->output_window, FFTW_ESTIMATE);
+    //u->inverse_plan=fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_ESTIMATE);
+    //u->forward_plan=fftwf_plan_dft_r2c_1d(u->fft_size, u->input, u->output, FFTW_MEASURE);
+    //u->inverse_plan=fftwf_plan_dft_c2r_1d(u->fft_size, u->output, u->input, FFTW_MEASURE);
+    const int freqs[]={0,25,50,100,200,300,400,800,1500,
+        2000,3000,4000,5000,6000,7000,8000,9000,10000,11000,12000,
+        13000,14000,15000,16000,17000,18000,19000,20000,21000,22000,23000,24000,INT_MAX};
+    const float coefficients[]={1,1,1,1,1,1,1,1,1,1,
+        1,1,1,1,1,1,1,1,
+        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
+    const size_t ncoefficients=sizeof(coefficients)/sizeof(float);
+    pa_assert_se(sizeof(freqs)/sizeof(int)==sizeof(coefficients)/sizeof(float));
+    float *freq_translated=(float *) malloc(sizeof(float)*(ncoefficients));
+    freq_translated[0]=1;
+    //Translate the frequencies in their natural sampling rate to the new sampling rate frequencies
+    for(size_t i=1;i<ncoefficients-1;++i){
+        freq_translated[i]=((float)freqs[i]*u->fft_size)/ss.rate;
+        //pa_log("i: %ld: %d , %g",i,freqs[i],freq_translated[i]);
+        pa_assert_se(freq_translated[i]>=freq_translated[i-1]);
+    }
+    freq_translated[ncoefficients-1]=DBL_MAX;
+    //Interpolate the specified frequency band values
+    u->H[0]=1;
+    for(size_t i=1,j=0;i<(u->fft_size/2+1);++i){
+        pa_assert_se(j<ncoefficients);
+        //max frequency range passed, consider the rest as one band
+        if(freq_translated[j+1]>=DBL_MAX){
+            for(;i<(u->fft_size/2+1);++i){
+                u->H[i]=coefficients[j];
+            }
+            break;
+        }
+        //pa_log("i: %d, j: %d, freq: %f",i,j,freq_translated[j]);
+        //pa_log("interp: %0.4f %0.4f",freq_translated[j],freq_translated[j+1]);
+        pa_assert_se(freq_translated[j]<freq_translated[j+1]);
+        pa_assert_se(i>=freq_translated[j]);
+        pa_assert_se(i<=freq_translated[j+1]);
+        //bilinear-inerpolation of coefficients specified
+        float c0=(i-freq_translated[j])/(freq_translated[j+1]-freq_translated[j]);
+        pa_assert_se(c0>=0&&c0<=1.0);
+        u->H[i]=((1.0f-c0)*coefficients[j]+c0*coefficients[j+1]);
+        pa_assert_se(u->H[i]>0);
+        while(i>=floor(freq_translated[j+1])){
+            j++;
+        }
+    }
+    array_out("/home/jason/coffs.txt",u->H,u->fft_size/2+1);
+    //divide out the fft gain
+    for(int i=0;i<(u->fft_size/2+1);++i){
+        u->H[i]/=u->fft_size;
+    }
+    free(freq_translated);
+
+    /* Create sink */
+    pa_sink_new_data_init(&sink_data);
+    sink_data.driver = __FILE__;
+    sink_data.module = m;
+    if (!(sink_data.name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL))))
+        sink_data.name = pa_sprintf_malloc("%s.equalizer", master->name);
+    sink_data.namereg_fail = FALSE;
+    pa_sink_new_data_set_sample_spec(&sink_data, &ss);
+    pa_sink_new_data_set_channel_map(&sink_data, &map);
+    z = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION);
+    pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "FFT based equalizer");
+    pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name);
+    pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter");
+
+    if (pa_modargs_get_proplist(ma, "sink_properties", sink_data.proplist, PA_UPDATE_REPLACE) < 0) {
+        pa_log("Invalid properties");
+        pa_sink_new_data_done(&sink_data);
+        goto fail;
+    }
+
+    u->sink = pa_sink_new(m->core, &sink_data, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY);
+    pa_sink_new_data_done(&sink_data);
+
+    if (!u->sink) {
+        pa_log("Failed to create sink.");
+        goto fail;
+    }
+
+    u->sink->parent.process_msg = sink_process_msg;
+    u->sink->set_state = sink_set_state;
+    u->sink->update_requested_latency = sink_update_requested_latency;
+    u->sink->request_rewind = sink_request_rewind;
+    u->sink->userdata = u;
+
+    pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq);
+    pa_sink_set_rtpoll(u->sink, master->rtpoll);
+
+    /* Create sink input */
+    pa_sink_input_new_data_init(&sink_input_data);
+    sink_input_data.driver = __FILE__;
+    sink_input_data.module = m;
+    sink_input_data.sink = u->master;
+    pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Equalized Stream");
+    pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
+    pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss);
+    pa_sink_input_new_data_set_channel_map(&sink_input_data, &map);
+
+    pa_sink_input_new(&u->sink_input, m->core, &sink_input_data, PA_SINK_INPUT_DONT_MOVE);
+    pa_sink_input_new_data_done(&sink_input_data);
+
+    if (!u->sink_input)
+        goto fail;
+
+    u->sink_input->pop = sink_input_pop_cb;
+    u->sink_input->process_rewind = sink_input_process_rewind_cb;
+    u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
+    u->sink_input->update_max_request = sink_input_update_max_request_cb;
+    u->sink_input->update_sink_latency_range = sink_input_update_sink_latency_range_cb;
+    u->sink_input->kill = sink_input_kill_cb;
+    u->sink_input->attach = sink_input_attach_cb;
+    u->sink_input->detach = sink_input_detach_cb;
+    u->sink_input->state_change = sink_input_state_change_cb;
+    u->sink_input->may_move_to = sink_input_may_move_to_cb;
+    u->sink_input->userdata = u;
+
+    pa_sink_put(u->sink);
+    pa_sink_input_put(u->sink_input);
+
+    pa_modargs_free(ma);
+
+    pa_xfree(use_default);
+
+    return 0;
+
+fail:
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa_xfree(use_default);
+
+    pa__done(m);
+
+    return -1;
+}
+
+int pa__get_n_used(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+    pa_assert_se(u = m->userdata);
+
+    return pa_sink_linked_by(u->sink);
+}
+
+void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->sink) {
+        pa_sink_unlink(u->sink);
+        pa_sink_unref(u->sink);
+    }
+
+    if (u->sink_input) {
+        pa_sink_input_unlink(u->sink_input);
+        pa_sink_input_unref(u->sink_input);
+    }
+
+    if (u->memblockq)
+        pa_memblockq_free(u->memblockq);
+
+    fftwf_destroy_plan(u->inverse_plan);
+    fftwf_destroy_plan(u->forward_plan);
+    fftwf_free(u->output_window);
+    for(size_t c=0;c<u->channels;++c){
+        fftwf_free(u->output_buffer[c]);
+        fftwf_free(u->overlap_accum[c]);
+        fftwf_free(u->input[c]);
+    }
+    free(u->output_buffer);
+    free(u->overlap_accum);
+    free(u->input);
+    fftwf_free(u->work_buffer);
+    fftwf_free(u->W);
+    fftwf_free(u->H);
+
+    pa_xfree(u);
+}

commit 2e119060cb6b61fa59976b636300eea913c20827
Author: Jason Newton <jason at arcuid.wyred.org>
Date:   Wed Jul 15 06:57:33 2009 -0700

    module-equalizer-sink:
        added temporary debugging output to track filter output
        removed dead code
        only a small amount of crackling remains

diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
index 8b34fa0..cabb0dc 100755
--- a/src/modules/module-equalizer-sink.c
+++ b/src/modules/module-equalizer-sink.c
@@ -1,4 +1,29 @@
+/***
+This file is part of PulseAudio.
 
+This module is based off Lennart Poettering's LADSPA sink and swaps out
+LADSPA functionality for a STFT OLA based digital equalizer.  All new work
+is published under Pulseaudio's original license.
+Copyright 2009 Jason Newton <nevion at gmail.com>
+
+Original Author:
+Copyright 2004-2008 Lennart Poettering
+
+PulseAudio is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published
+by the Free Software Foundation; either version 2.1 of the License,
+or (at your option) any later version.
+
+PulseAudio is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with PulseAudio; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+USA.
+***/
 
 #ifdef HAVE_CONFIG_H
 #include <config.h>
@@ -25,9 +50,6 @@
 #include <pulsecore/rtpoll.h>
 #include <pulsecore/sample-util.h>
 #include <pulsecore/ltdl-helper.h>
-#include <liboil/liboilfuncs.h>
-#include <liboil/liboil.h>
-
 
 #include <stdint.h>
 #include <time.h>
@@ -50,9 +72,15 @@ struct userdata {
     pa_sink_input *sink_input;
 
     size_t channels;
-    size_t fft_size; //length (res) of fft
-    size_t window_size;//even!
-    size_t overlap_size;
+    size_t fft_size;//length (res) of fft
+    size_t window_size;/*
+                        *sliding window size
+                        *effectively chooses R
+                        */
+    size_t R;/* the hop size between overlapping windows
+              * the latency of the filter, calculated from window_size
+              * based on constraints of COLA and window function
+              */
     size_t samples_gathered;
     size_t n_buffered_output;
     size_t max_output;
@@ -61,6 +89,7 @@ struct userdata {
     float *work_buffer,**input,**overlap_accum,**output_buffer;
     fftwf_complex *output_window;
     fftwf_plan forward_plan,inverse_plan;
+    //size_t samplings;
 
     pa_memblockq *memblockq;
 };
@@ -106,8 +135,9 @@ void hamming_window(float *W,size_t window_size){
         m/=(window_size-1);
         W[i]=.54-.46*cos(2*M_PI*m);
     }
-    W[0]/=2;
-    W[window_size-1]/=2;
+    W[window_size-1]=0;
+    //W[0]/=2;
+    //W[window_size-1]/=2;
 }
 void blackman_window(float *W,size_t window_size){
     //h=.42-.5*cos(2*pi*m)+.08*cos(4*pi*m), m=(0:W-1)/(W-1)
@@ -132,6 +162,10 @@ void sin_window(float *W,size_t window_size){
 
 void array_out(const char *name,float *a,size_t length){
     FILE *p=fopen(name,"w");
+    if(!p){
+        pa_log("opening %s failed!",name);
+        return;
+    }
     for(size_t i=0;i<length;++i){
         fprintf(p,"%e,",a[i]);
         //if(i%1000==0){
@@ -213,7 +247,6 @@ static void sink_update_requested_latency(pa_sink *s) {
 static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
     struct userdata *u;
     float *src, *dst;
-    size_t c;
     pa_memchunk tchunk;
     pa_sink_input_assert_ref(i);
     pa_assert(chunk);
@@ -229,7 +262,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
     if(u->n_buffered_output>0){
         //pa_log("outputing %ld buffered samples",u->n_buffered_output);
         chunk->index = 0;
-        size_t n_outputable=PA_MIN(u->n_buffered_output,nbytes/fs);
+        size_t n_outputable=PA_MIN(u->n_buffered_output,u->max_output);
         chunk->length = n_outputable*fs;
         chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length);
         pa_memblockq_drop(u->memblockq, chunk->length);
@@ -245,10 +278,11 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
     pa_assert_se(u->n_buffered_output==0);
 
     //collect the minimum number of samples
-    while(u->samples_gathered < (u->window_size-u->overlap_size)){
+    //TODO figure out a better way of buffering the needed
+    //number of samples, this doesn't seem to work correctly
+    while(u->samples_gathered < u->R){
         //render some new fragments to our memblockq
-        //size_t desired_samples=PA_MIN(u->min_input-samples_gathered,u->max_output);
-        size_t desired_samples=PA_MIN((u->window_size-u->overlap_size)-u->samples_gathered,u->max_output);
+        size_t desired_samples=PA_MIN(u->R-u->samples_gathered,u->max_output);
         while (pa_memblockq_peek(u->memblockq, &tchunk) < 0) {
             pa_memchunk nchunk;
 
@@ -259,137 +293,80 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
         if(tchunk.length/fs!=desired_samples){
             pa_log("got %ld samples, asked for %ld",tchunk.length/fs,desired_samples);
         }
-        size_t n_samples=PA_MIN(tchunk.length/fs,u->window_size-u->overlap_size-u->samples_gathered);
+        size_t n_samples=PA_MIN(tchunk.length/fs,u->R-u->samples_gathered);
         //TODO: figure out what to do with rest of the samples when there's too many (rare?)
         src = (float*) ((uint8_t*) pa_memblock_acquire(tchunk.memblock) + tchunk.index);
         for (size_t c=0;c<u->channels;c++) {
-            pa_sample_clamp(PA_SAMPLE_FLOAT32NE,u->input[c]+u->overlap_size+u->samples_gathered,sizeof(float), src+c, fs, n_samples);
+            pa_sample_clamp(PA_SAMPLE_FLOAT32NE,u->input[c]+(u->window_size-u->R)+u->samples_gathered,sizeof(float), src+c, fs, n_samples);
         }
-
         u->samples_gathered+=n_samples;
         pa_memblock_release(tchunk.memblock);
         pa_memblock_unref(tchunk.memblock);
     }
     //IT should be this guy if we're buffering like how its supposed to
-    //size_t n_outputable=PA_MIN(u->window_size-u->overlap_size,nbytes/fs);
+    //size_t n_outputable=PA_MIN(u->window_size-u->R,u->max_output);
     //This one takes into account the actual data gathered but then the dsp
     //stuff is wrong when the buffer "underruns"
-    size_t n_outputable=PA_MIN(u->samples_gathered,nbytes/fs);
-    /*
-    //debugging: tests if immediate release of freshly buffered data
-    //plays ok and prevents any other processing
-    chunk->index=0;
-    chunk->length=n_outputable*fs;
-    chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length);
-    pa_memblockq_drop(u->memblockq, chunk->length);
-    dst = (float*) pa_memblock_acquire(chunk->memblock);;
-    for (size_t c=0;c<u->channels;c++) {
-        pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c, fs, u->input[c]+u->overlap_size, sizeof(float),n_outputable);
-    }
-    u->samples_gathered=0;
-    pa_memblock_release(chunk->memblock);
-    return 0;
-    */
+    size_t n_outputable=PA_MIN(u->R,u->max_output);
 
-    //pa_log("%ld dequed samples",u->samples_gathered);
 
     chunk->index=0;
     chunk->length=n_outputable*fs;
     chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length);
     pa_memblockq_drop(u->memblockq, chunk->length);
     dst = (float*) pa_memblock_acquire(chunk->memblock);
-    //pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input, sizeof(float), src+c, fs, samples);
-    //pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c,fs, u->input, sizeof(float), samples);
-
-    /*
-    struct timespec start, end;
-    uint64_t elapsed;
-    clock_gettime(CLOCK_MONOTONIC, &start);
-    */
-    //use a zero-phase sliding dft and overlap-add method
 
     pa_assert_se(u->fft_size>=u->window_size);
-    //pa_assert_se(u->window_size%2==0);
-    pa_assert_se(u->overlap_size<u->window_size);
-    pa_assert_se(u->samples_gathered>=u->window_size-u->overlap_size);
-    size_t sample_rem=u->window_size-u->overlap_size-n_outputable;
-    //size_t w_mid=u->window_size/2;
-    //pa_log("hello world a");
-    for (c=0;c<u->channels;c++) {
-        //center the data for zero phase
-        //zero-pad TODO: optimization if sure these zeros aren't overwritten
-        //memset(u->work_buffer+w_mid,0,(u->fft_size-u->window_size)*sizeof(float));
+    pa_assert_se(u->R<u->window_size);
+    pa_assert_se(u->samples_gathered>=u->R);
+    size_t sample_rem=u->R-n_outputable;
+    //use a linear-phase sliding STFT and overlap-add method (for each channel)
+    for (size_t c=0;c<u->channels;c++) {
+        ////zero padd the data
         //memset(u->work_buffer,0,u->fft_size*sizeof(float));
-        /*
-        for(size_t j=0;j<u->window_size;++j){
-            u->work_buffer[j]=u->W[j]*u->input[c][j];
-            u->work_buffer[j]=u->input[c][j];
-        }
-        */
-        //zero padd the data, don't worry about zerophase, shouldn't really matter
-        memset(u->work_buffer+u->overlap_size,0,(u->fft_size-u->overlap_size)*sizeof(float));
-        //window the data
+        memset(u->work_buffer+u->window_size,0,(u->fft_size-u->window_size)*sizeof(float));
+        ////window the data
         for(size_t j=0;j<u->window_size;++j){
             u->work_buffer[j]=u->W[j]*u->input[c][j];
         }
-        /*
-        //recenter for zero phase
-        for(size_t j=0;j<w_mid;++j){
-            float tmp=u->work_buffer[j];
-            u->work_buffer[j]=u->input[c][j+w_mid];
-            u->work_buffer[j+u->fft_size-w_mid]=tmp;
-        }
-        */
-        //pa_log("hello world b");
-
-        /*
-        //window and zero phase shift
-        for(size_t j=0;j<w_mid;++j){
-            //u->work_buffer[j]=u->input[c][j+w_mid];
-            //u->work_buffer[j+u->fft_size-w_mid]=u->input[c][j];
-            u->work_buffer[j]=u->W[j+w_mid]*u->input[c][j+w_mid];
-            u->work_buffer[j+u->fft_size-w_mid]=u->W[j]*u->input[c][j];
-        }*/
         //Processing is done here!
         //do fft
+        //char fname[1024];
+        //if(u->samplings==200){
+        //    pa_assert_se(0);
+        //}
+
+        //this iterations input
+        //sprintf(fname,"/home/jason/input%ld-%ld.txt",u->samplings+1,c);
+        //array_out(fname,u->input[c]+(u->window_size-u->R),u->R);
+
         fftwf_execute_dft_r2c(u->forward_plan,u->work_buffer,u->output_window);
         //perform filtering
         for(size_t j=0;j<u->fft_size/2+1;++j){
-            ////identity transform (fft size)
-            //u->output_window[j][0]/=u->fft_size;
-            //u->output_window[j][1]/=u->fft_size;
-            ////identity transform (window size)
-            //u->output_window[j][0]/=u->window_size;
-            //u->output_window[j][1]/=u->window_size;
-            //filtered
             u->output_window[j][0]*=u->H[j];
             u->output_window[j][1]*=u->H[j];
         }
-        //inverse fft
+        ////inverse fft
         fftwf_execute_dft_c2r(u->inverse_plan,u->output_window,u->work_buffer);
+        //the output for the previous iteration's input
+        //sprintf(fname,"/home/jason/output%ld-%ld.txt",u->samplings,c);
+        //array_out(fname,u->work_buffer,u->window_size);
 
-        /*
-        //uncenter the data
-        for(size_t j=0;j<w_mid;++j){
-            const float tmp=u->work_buffer[j];
-            u->work_buffer[j]=u->work_buffer[j+u->fft_size-w_mid];
-            u->work_buffer[j+w_mid]=tmp;
-        }
-        */
-        /*
-        //divide out fft gain (more stable here?)
-        for(size_t j=0;j<u->window_size;++j){
-            u->work_buffer[j]/=u->fft_size;
-        }
-        */
-        /*
-        //debug: tests overlaping add
-        //and negates ALL PREVIOUS processing
-        //yields a perfect reconstruction if COLA is held
-        for(size_t j=0;j<u->window_size;++j){
-            u->work_buffer[j]=u->W[j]*u->input[c][j];
+
+        ////debug: tests overlaping add
+        ////and negates ALL PREVIOUS processing
+        ////yields a perfect reconstruction if COLA is held
+        //for(size_t j=0;j<u->window_size;++j){
+        //    u->work_buffer[j]=u->W[j]*u->input[c][j];
+        //}
+
+        //overlap add and preserve overlap component from this window (linear phase)
+        for(size_t j=0;j<u->R;++j){
+            u->work_buffer[j]+=u->overlap_accum[c][j];
+            u->overlap_accum[c][j]=u->work_buffer[u->window_size-u->R+j];
         }
-        */
+
+
         /*
         //debug: tests if basic buffering works
         //shouldn't modify the signal AT ALL
@@ -398,40 +375,20 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
         }
         */
 
-        /*
-        //overlap add and preserve overlap component from this window (zero phase)
-        for(size_t j=0;j<u->overlap_size;++j){
-            u->work_buffer[j]+=u->overlap_accum[c][j];
-            u->overlap_accum[c][j]=u->work_buffer[u->window_size-u->overlap_size+j];
-        }
-        */
-        //overlap add and preserve overlap component from this window (linear phase)
-        for(size_t j=0;j<u->overlap_size;++j){
-            u->work_buffer[j]+=u->overlap_accum[c][j];
-            u->overlap_accum[c][j]=u->work_buffer[u->window_size-u->overlap_size+j];
-        }
-
         //preseve the needed input for the next windows overlap
-        memmove(u->input[c],u->input[c]+u->overlap_size,(u->window_size-u->overlap_size)*sizeof(float));
+        memmove(u->input[c],u->input[c]+u->R,
+            (u->window_size-u->R)*sizeof(float)
+        );
         //output the samples that are outputable now
         pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c, fs, u->work_buffer, sizeof(float),n_outputable);
         //buffer the rest of them
         memcpy(u->output_buffer[c]+u->n_buffered_output,u->work_buffer+n_outputable,sample_rem*sizeof(float));
+
     }
-    /*
-    clock_gettime(CLOCK_MONOTONIC, &end);
-    elapsed=time_diff(&end, &start);
-    pa_log("processed: %ld, time: %ld",u->samples_gathered,elapsed);
-    */
+    //u->samplings++;
     u->n_buffered_output+=sample_rem;
     u->samples_gathered=0;
-
-
-    //pa_log("%ld samples queued",u->n_buffered_output);
-
     pa_memblock_release(chunk->memblock);
-
-
     return 0;
 }
 
@@ -456,6 +413,8 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
         if (amount > 0) {
             pa_memblockq_seek(u->memblockq, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE);
             pa_log_debug("Resetting equalizer");
+            u->n_buffered_output=0;
+            u->samples_gathered=0;
         }
     }
 
@@ -621,18 +580,12 @@ int pa__init(pa_module*m) {
     u->sink_input = NULL;
     u->memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, fs, 1, 1, 0, NULL);
 
-    //u->fft_size=44100;
-    //u->fft_size=48000;
-    //u->fft_size=1024;
+    //u->samplings=0;
     u->channels=ss.channels;
     u->fft_size=pow(2,ceil(log(ss.rate)/log(2)));
-    //u->fft_size=ss.rate;
-    //u->fft_size=65536;
     pa_log("fft size: %ld",u->fft_size);
-    u->window_size=8001;
-    u->overlap_size=(u->window_size+1)/2;
-    //u->overlap_size=u->window_size/2;
-    //u->overlap_size=0;
+    u->window_size=7999;
+    u->R=(u->window_size+1)/2;
     u->samples_gathered=0;
     u->n_buffered_output=0;
     u->max_output=pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss)/pa_frame_size(&ss);
@@ -645,34 +598,26 @@ int pa__init(pa_module*m) {
     for(size_t c=0;c<u->channels;++c){
         u->input[c]=(float*) fftwf_malloc(u->window_size*sizeof(float));
         memset(u->input[c],0,u->window_size*sizeof(float));
-        u->overlap_accum[c]=(float*) fftwf_malloc(u->overlap_size*sizeof(float));
-        memset(u->overlap_accum[c],0,u->overlap_size*sizeof(float));
+        u->overlap_accum[c]=(float*) fftwf_malloc(u->R*sizeof(float));
+        memset(u->overlap_accum[c],0,u->R*sizeof(float));
         u->output_buffer[c]=(float*) fftwf_malloc(u->window_size*sizeof(float));
     }
     u->output_window = (fftwf_complex *) fftwf_malloc(sizeof(fftwf_complex) * (u->fft_size/2+1));
     u->forward_plan=fftwf_plan_dft_r2c_1d(u->fft_size, u->work_buffer, u->output_window, FFTW_ESTIMATE);
     u->inverse_plan=fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_ESTIMATE);
-
     /*
-    //rectangular window
     for(size_t j=0;j<u->window_size;++j){
-        u->W[j]=1.0;
+        u->W[j]=.5;
     }
     */
-    //hanning_normalized_window(u->W,u->window_size);
     hanning_window(u->W,u->window_size);
-    //sin_window(u->W,u->window_size);
-    array_out("/home/jason/window.txt",u->W,u->window_size);
-    //u->forward_plan=fftwf_plan_dft_r2c_1d(u->fft_size, u->input, u->output_window, FFTW_ESTIMATE);
-    //u->inverse_plan=fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_ESTIMATE);
-    //u->forward_plan=fftwf_plan_dft_r2c_1d(u->fft_size, u->input, u->output, FFTW_MEASURE);
-    //u->inverse_plan=fftwf_plan_dft_c2r_1d(u->fft_size, u->output, u->input, FFTW_MEASURE);
+
     const int freqs[]={0,25,50,100,200,300,400,800,1500,
         2000,3000,4000,5000,6000,7000,8000,9000,10000,11000,12000,
         13000,14000,15000,16000,17000,18000,19000,20000,21000,22000,23000,24000,INT_MAX};
-    const float coefficients[]={1,1,1,1,1,1,1,1,1,1,
-        1,1,1,1,1,1,1,1,
-        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
+    const float coefficients[]={.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,
+        .1,.1,.1,.1,.1,.1,.1,.1,
+        .1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1};
     const size_t ncoefficients=sizeof(coefficients)/sizeof(float);
     pa_assert_se(sizeof(freqs)/sizeof(int)==sizeof(coefficients)/sizeof(float));
     float *freq_translated=(float *) malloc(sizeof(float)*(ncoefficients));
@@ -683,13 +628,13 @@ int pa__init(pa_module*m) {
         //pa_log("i: %ld: %d , %g",i,freqs[i],freq_translated[i]);
         pa_assert_se(freq_translated[i]>=freq_translated[i-1]);
     }
-    freq_translated[ncoefficients-1]=DBL_MAX;
+    freq_translated[ncoefficients-1]=FLT_MAX;
     //Interpolate the specified frequency band values
     u->H[0]=1;
     for(size_t i=1,j=0;i<(u->fft_size/2+1);++i){
         pa_assert_se(j<ncoefficients);
         //max frequency range passed, consider the rest as one band
-        if(freq_translated[j+1]>=DBL_MAX){
+        if(freq_translated[j+1]>=FLT_MAX){
             for(;i<(u->fft_size/2+1);++i){
                 u->H[i]=coefficients[j];
             }
@@ -709,9 +654,8 @@ int pa__init(pa_module*m) {
             j++;
         }
     }
-    array_out("/home/jason/coffs.txt",u->H,u->fft_size/2+1);
     //divide out the fft gain
-    for(int i=0;i<(u->fft_size/2+1);++i){
+    for(size_t i=0;i<(u->fft_size/2+1);++i){
         u->H[i]/=u->fft_size;
     }
     free(freq_translated);

commit 182c9c7dcb3c7e6b8d273ab85e6f3e67070a4694
Author: Jason Newton <jason at arcuid.wyred.org>
Date:   Thu Jul 16 18:18:14 2009 -0700

    module-equalizer-sink: added more assertions to aid in debugging

diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
index cabb0dc..d4cec3c 100755
--- a/src/modules/module-equalizer-sink.c
+++ b/src/modules/module-equalizer-sink.c
@@ -251,8 +251,9 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
     pa_sink_input_assert_ref(i);
     pa_assert(chunk);
     pa_assert_se(u = i->userdata);
-    size_t fs = pa_frame_size(&u->sink->sample_spec);
-    size_t ss=pa_sample_size(&u->sink->sample_spec);
+    pa_assert_se(u->sink);
+    size_t fs = pa_frame_size(&(u->sink->sample_spec));
+    size_t ss=pa_sample_size(&(u->sink->sample_spec));
     size_t fe = fs/ss;
 
     if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
@@ -597,10 +598,14 @@ int pa__init(pa_module*m) {
     u->output_buffer=(float **)malloc(sizeof(float *)*u->channels);
     for(size_t c=0;c<u->channels;++c){
         u->input[c]=(float*) fftwf_malloc(u->window_size*sizeof(float));
+        pa_assert_se(u->input[c]);
         memset(u->input[c],0,u->window_size*sizeof(float));
+        pa_assert_se(u->input[c]);
         u->overlap_accum[c]=(float*) fftwf_malloc(u->R*sizeof(float));
+        pa_assert_se(u->overlap_accum[c]);
         memset(u->overlap_accum[c],0,u->R*sizeof(float));
         u->output_buffer[c]=(float*) fftwf_malloc(u->window_size*sizeof(float));
+        pa_assert_se(u->output_buffer[c]);
     }
     u->output_window = (fftwf_complex *) fftwf_malloc(sizeof(fftwf_complex) * (u->fft_size/2+1));
     u->forward_plan=fftwf_plan_dft_r2c_1d(u->fft_size, u->work_buffer, u->output_window, FFTW_ESTIMATE);
@@ -615,9 +620,9 @@ int pa__init(pa_module*m) {
     const int freqs[]={0,25,50,100,200,300,400,800,1500,
         2000,3000,4000,5000,6000,7000,8000,9000,10000,11000,12000,
         13000,14000,15000,16000,17000,18000,19000,20000,21000,22000,23000,24000,INT_MAX};
-    const float coefficients[]={.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,
-        .1,.1,.1,.1,.1,.1,.1,.1,
-        .1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1,.1};
+    const float coefficients[]={1,1,1,1,1,1,1,1,1,1,
+        1,1,1,1,1,1,1,1,
+        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
     const size_t ncoefficients=sizeof(coefficients)/sizeof(float);
     pa_assert_se(sizeof(freqs)/sizeof(int)==sizeof(coefficients)/sizeof(float));
     float *freq_translated=(float *) malloc(sizeof(float)*(ncoefficients));

commit d4fe5bfce988765fd51d291c61217ffef9df7698
Author: Jason Newton <jason at arcuid.wyred.org>
Date:   Thu Jul 16 22:00:38 2009 -0700

    module-equalizer-sink: attempt different buffering strategy

diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
index d4cec3c..d6e28f3 100755
--- a/src/modules/module-equalizer-sink.c
+++ b/src/modules/module-equalizer-sink.c
@@ -259,29 +259,11 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
     if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
         return -1;
 
-    //output any buffered outputs first
-    if(u->n_buffered_output>0){
-        //pa_log("outputing %ld buffered samples",u->n_buffered_output);
-        chunk->index = 0;
-        size_t n_outputable=PA_MIN(u->n_buffered_output,u->max_output);
-        chunk->length = n_outputable*fs;
-        chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length);
-        pa_memblockq_drop(u->memblockq, chunk->length);
-        dst = (float*) pa_memblock_acquire(chunk->memblock);
-        for(size_t j=0;j<u->channels;++j){
-            pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+j, fs, u->output_buffer[j], sizeof(float),n_outputable);
-            memmove(u->output_buffer[j],u->output_buffer[j]+n_outputable,(u->n_buffered_output-n_outputable)*sizeof(float));
-        }
-        u->n_buffered_output-=n_outputable;
-        pa_memblock_release(chunk->memblock);
-        return 0;
-    }
-    pa_assert_se(u->n_buffered_output==0);
-
     //collect the minimum number of samples
     //TODO figure out a better way of buffering the needed
     //number of samples, this doesn't seem to work correctly
-    while(u->samples_gathered < u->R){
+    //most of the itme
+    if(u->samples_gathered < u->R){
         //render some new fragments to our memblockq
         size_t desired_samples=PA_MIN(u->R-u->samples_gathered,u->max_output);
         while (pa_memblockq_peek(u->memblockq, &tchunk) < 0) {
@@ -295,7 +277,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
             pa_log("got %ld samples, asked for %ld",tchunk.length/fs,desired_samples);
         }
         size_t n_samples=PA_MIN(tchunk.length/fs,u->R-u->samples_gathered);
-        //TODO: figure out what to do with rest of the samples when there's too many (rare?)
+        pa_assert_se(n_samples<=u->R-u->samples_gathered);
         src = (float*) ((uint8_t*) pa_memblock_acquire(tchunk.memblock) + tchunk.index);
         for (size_t c=0;c<u->channels;c++) {
             pa_sample_clamp(PA_SAMPLE_FLOAT32NE,u->input[c]+(u->window_size-u->R)+u->samples_gathered,sizeof(float), src+c, fs, n_samples);
@@ -304,12 +286,33 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
         pa_memblock_release(tchunk.memblock);
         pa_memblock_unref(tchunk.memblock);
     }
+    //output any buffered outputs first
+    if(u->n_buffered_output>0){
+        //pa_log("outputing %ld buffered samples",u->n_buffered_output);
+        chunk->index = 0;
+        size_t n_outputable=PA_MIN(u->n_buffered_output,u->max_output);
+        chunk->length = n_outputable*fs;
+        chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length);
+        pa_memblockq_drop(u->memblockq, chunk->length);
+        dst = (float*) pa_memblock_acquire(chunk->memblock);
+        for(size_t j=0;j<u->channels;++j){
+            pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+j, fs, u->output_buffer[j], sizeof(float),n_outputable);
+            memmove(u->output_buffer[j],u->output_buffer[j]+n_outputable,(u->n_buffered_output-n_outputable)*sizeof(float));
+        }
+        u->n_buffered_output-=n_outputable;
+        pa_memblock_release(chunk->memblock);
+        return 0;
+    }
+    pa_assert_se(u->n_buffered_output==0);
+
+    if(u->samples_gathered<u->R){
+        return -1;
+    }
     //IT should be this guy if we're buffering like how its supposed to
     //size_t n_outputable=PA_MIN(u->window_size-u->R,u->max_output);
     //This one takes into account the actual data gathered but then the dsp
     //stuff is wrong when the buffer "underruns"
-    size_t n_outputable=PA_MIN(u->R,u->max_output);
-
+    size_t n_outputable=PA_MIN(u->R,u->max_output)*(u->R==u->samples_gathered);
 
     chunk->index=0;
     chunk->length=n_outputable*fs;
@@ -319,7 +322,6 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
 
     pa_assert_se(u->fft_size>=u->window_size);
     pa_assert_se(u->R<u->window_size);
-    pa_assert_se(u->samples_gathered>=u->R);
     size_t sample_rem=u->R-n_outputable;
     //use a linear-phase sliding STFT and overlap-add method (for each channel)
     for (size_t c=0;c<u->channels;c++) {
@@ -389,6 +391,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
     //u->samplings++;
     u->n_buffered_output+=sample_rem;
     u->samples_gathered=0;
+end:
     pa_memblock_release(chunk->memblock);
     return 0;
 }

commit cf8331a0da9df5e4eff6105a002f4912be673d0a
Author: Jason Newton <jason at arcuid.wyred.org>
Date:   Sat Jul 18 01:00:35 2009 -0700

    module-equalizer-sink: trying new buffering strategies

diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
index d6e28f3..1d4a423 100755
--- a/src/modules/module-equalizer-sink.c
+++ b/src/modules/module-equalizer-sink.c
@@ -81,9 +81,10 @@ struct userdata {
               * the latency of the filter, calculated from window_size
               * based on constraints of COLA and window function
               */
+    size_t overlap_size;//window_size-R
     size_t samples_gathered;
-    size_t n_buffered_output;
     size_t max_output;
+    size_t target_samples;
     float *H;//frequency response filter (magnitude based)
     float *W;//windowing function (time domain)
     float *work_buffer,**input,**overlap_accum,**output_buffer;
@@ -91,7 +92,8 @@ struct userdata {
     fftwf_plan forward_plan,inverse_plan;
     //size_t samplings;
 
-    pa_memblockq *memblockq;
+    pa_memchunk conv_buffer;
+    pa_memblockq *rendered_q;
 };
 
 static const char* const valid_modargs[] = {
@@ -186,12 +188,14 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
         case PA_SINK_MESSAGE_GET_LATENCY: {
             pa_usec_t usec = 0;
             pa_sample_spec *ss=&u->sink->sample_spec;
+            size_t fs=pa_frame_size(ss);
 
             /* Get the latency of the master sink */
             if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
                 usec = 0;
 
-            usec+=pa_bytes_to_usec(u->n_buffered_output*pa_frame_size(ss),ss);
+            usec+=pa_bytes_to_usec(u->samples_gathered*fs,ss);
+            usec += pa_bytes_to_usec(pa_memblockq_get_length(u->rendered_q), ss);
             /* Add the latency internal to our sink input on top */
             usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec);
             *((pa_usec_t*) data) = usec;
@@ -227,7 +231,7 @@ static void sink_request_rewind(pa_sink *s) {
     pa_assert_se(u = s->userdata);
 
     /* Just hand this one over to the master sink */
-    pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes + pa_memblockq_get_length(u->memblockq), TRUE, FALSE, FALSE);
+    pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes + pa_memblockq_get_length(u->rendered_q), TRUE, FALSE, FALSE);
 }
 
 /* Called from I/O thread context */
@@ -246,153 +250,159 @@ static void sink_update_requested_latency(pa_sink *s) {
 /* Called from I/O thread context */
 static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
     struct userdata *u;
-    float *src, *dst;
-    pa_memchunk tchunk;
     pa_sink_input_assert_ref(i);
     pa_assert(chunk);
     pa_assert_se(u = i->userdata);
     pa_assert_se(u->sink);
-    size_t fs = pa_frame_size(&(u->sink->sample_spec));
+    size_t fs=pa_frame_size(&(u->sink->sample_spec));
     size_t ss=pa_sample_size(&(u->sink->sample_spec));
     size_t fe = fs/ss;
+    size_t samples_requested=nbytes/fs;
+    pa_memchunk tchunk;
+    chunk->memblock=NULL;
+    size_t buffered_samples=pa_memblockq_get_length(u->rendered_q)/fs;
 
     if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
         return -1;
 
-    //collect the minimum number of samples
-    //TODO figure out a better way of buffering the needed
-    //number of samples, this doesn't seem to work correctly
-    //most of the itme
-    if(u->samples_gathered < u->R){
-        //render some new fragments to our memblockq
-        size_t desired_samples=PA_MIN(u->R-u->samples_gathered,u->max_output);
-        while (pa_memblockq_peek(u->memblockq, &tchunk) < 0) {
-            pa_memchunk nchunk;
-
-            pa_sink_render(u->sink, desired_samples*fs, &nchunk);
-            pa_memblockq_push(u->memblockq, &nchunk);
-            pa_memblock_unref(nchunk.memblock);
-        }
-        if(tchunk.length/fs!=desired_samples){
-            pa_log("got %ld samples, asked for %ld",tchunk.length/fs,desired_samples);
+    pa_log("start output-buffered %ld, input-buffered %ld",buffered_samples,u->samples_gathered);
+    //collect samples
+    size_t buffered_remaining=pa_memblockq_get_length(u->rendered_q)/fs;
+    size_t buffer_missing=pa_memblockq_missing(u->rendered_q)/fs;
+    size_t desired_samples=(buffer_missing>=u->R)*PA_MIN(u->target_samples-u->samples_gathered,buffer_missing);
+    if(desired_samples>0){
+        u->conv_buffer.index=0;
+        //if we still had buffered output, 
+        //or can gather any more in the buffer
+        //politely request (optimistic)
+        if(buffered_samples>=samples_requested ||
+            (u->samples_gathered/u->R)*u->R>=samples_requested){
+            u->conv_buffer.length=desired_samples*fs;
+            pa_log("trying to buffer %ld samples",desired_samples);
+            pa_sink_render_into(u->sink, &u->conv_buffer);
+        }else{//we need it now! force it
+            //TODO: minimum amount or the whole buffer better?
+            desired_samples=u->R-u->samples_gathered%u->R;
+            u->conv_buffer.length=desired_samples*fs;
+            pa_log("force-buffer %ld samples",desired_samples);
+            pa_sink_render_into_full(u->sink, &u->conv_buffer);
+            pa_assert_se(u->conv_buffer.length==desired_samples*fs);
         }
-        size_t n_samples=PA_MIN(tchunk.length/fs,u->R-u->samples_gathered);
-        pa_assert_se(n_samples<=u->R-u->samples_gathered);
-        src = (float*) ((uint8_t*) pa_memblock_acquire(tchunk.memblock) + tchunk.index);
+        size_t n_samples=u->conv_buffer.length/fs;
+        float *src;
+        pa_log("received %ld samples",n_samples);
+
+        pa_assert_se(n_samples<=u->target_samples-u->samples_gathered);
+        src = (float*) ((uint8_t*) pa_memblock_acquire(u->conv_buffer.memblock) + u->conv_buffer.index);
         for (size_t c=0;c<u->channels;c++) {
-            pa_sample_clamp(PA_SAMPLE_FLOAT32NE,u->input[c]+(u->window_size-u->R)+u->samples_gathered,sizeof(float), src+c, fs, n_samples);
+            //buffer with an offset after the overlap from previous
+            //iterations
+            pa_assert_se(
+                u->input[c]+u->overlap_size+u->samples_gathered+n_samples<=u->input[c]+u->target_samples+u->overlap_size
+            );
+            pa_sample_clamp(PA_SAMPLE_FLOAT32NE,u->input[c]+u->overlap_size+u->samples_gathered,sizeof(float), src+c, fs, n_samples);
         }
         u->samples_gathered+=n_samples;
-        pa_memblock_release(tchunk.memblock);
-        pa_memblock_unref(tchunk.memblock);
-    }
-    //output any buffered outputs first
-    if(u->n_buffered_output>0){
-        //pa_log("outputing %ld buffered samples",u->n_buffered_output);
-        chunk->index = 0;
-        size_t n_outputable=PA_MIN(u->n_buffered_output,u->max_output);
-        chunk->length = n_outputable*fs;
-        chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length);
-        pa_memblockq_drop(u->memblockq, chunk->length);
-        dst = (float*) pa_memblock_acquire(chunk->memblock);
-        for(size_t j=0;j<u->channels;++j){
-            pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+j, fs, u->output_buffer[j], sizeof(float),n_outputable);
-            memmove(u->output_buffer[j],u->output_buffer[j]+n_outputable,(u->n_buffered_output-n_outputable)*sizeof(float));
-        }
-        u->n_buffered_output-=n_outputable;
-        pa_memblock_release(chunk->memblock);
-        return 0;
-    }
-    pa_assert_se(u->n_buffered_output==0);
-
-    if(u->samples_gathered<u->R){
-        return -1;
+        pa_memblock_release(u->conv_buffer.memblock);
     }
-    //IT should be this guy if we're buffering like how its supposed to
-    //size_t n_outputable=PA_MIN(u->window_size-u->R,u->max_output);
-    //This one takes into account the actual data gathered but then the dsp
-    //stuff is wrong when the buffer "underruns"
-    size_t n_outputable=PA_MIN(u->R,u->max_output)*(u->R==u->samples_gathered);
-
-    chunk->index=0;
-    chunk->length=n_outputable*fs;
-    chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length);
-    pa_memblockq_drop(u->memblockq, chunk->length);
-    dst = (float*) pa_memblock_acquire(chunk->memblock);
-
+    //pa_assert_se(u->samples_gathered>=u->R);
     pa_assert_se(u->fft_size>=u->window_size);
     pa_assert_se(u->R<u->window_size);
-    size_t sample_rem=u->R-n_outputable;
-    //use a linear-phase sliding STFT and overlap-add method (for each channel)
-    for (size_t c=0;c<u->channels;c++) {
-        ////zero padd the data
-        //memset(u->work_buffer,0,u->fft_size*sizeof(float));
-        memset(u->work_buffer+u->window_size,0,(u->fft_size-u->window_size)*sizeof(float));
-        ////window the data
-        for(size_t j=0;j<u->window_size;++j){
-            u->work_buffer[j]=u->W[j]*u->input[c][j];
-        }
-        //Processing is done here!
-        //do fft
-        //char fname[1024];
-        //if(u->samplings==200){
-        //    pa_assert_se(0);
-        //}
-
-        //this iterations input
-        //sprintf(fname,"/home/jason/input%ld-%ld.txt",u->samplings+1,c);
-        //array_out(fname,u->input[c]+(u->window_size-u->R),u->R);
-
-        fftwf_execute_dft_r2c(u->forward_plan,u->work_buffer,u->output_window);
-        //perform filtering
-        for(size_t j=0;j<u->fft_size/2+1;++j){
-            u->output_window[j][0]*=u->H[j];
-            u->output_window[j][1]*=u->H[j];
-        }
-        ////inverse fft
-        fftwf_execute_dft_c2r(u->inverse_plan,u->output_window,u->work_buffer);
-        //the output for the previous iteration's input
-        //sprintf(fname,"/home/jason/output%ld-%ld.txt",u->samplings,c);
-        //array_out(fname,u->work_buffer,u->window_size);
-
-
-        ////debug: tests overlaping add
-        ////and negates ALL PREVIOUS processing
-        ////yields a perfect reconstruction if COLA is held
-        //for(size_t j=0;j<u->window_size;++j){
-        //    u->work_buffer[j]=u->W[j]*u->input[c][j];
-        //}
-
-        //overlap add and preserve overlap component from this window (linear phase)
-        for(size_t j=0;j<u->R;++j){
-            u->work_buffer[j]+=u->overlap_accum[c][j];
-            u->overlap_accum[c][j]=u->work_buffer[u->window_size-u->R+j];
-        }
-
-
-        /*
-        //debug: tests if basic buffering works
-        //shouldn't modify the signal AT ALL
-        for(size_t j=0;j<u->window_size;++j){
-            u->work_buffer[j]=u->input[c][j];
+    //process every complete block on hand
+    while(u->samples_gathered>=u->R&&buffer_missing>=u->R){
+        float *dst;
+        //pa_log("iter gathered: %ld",u->samples_gathered);
+        tchunk.index=0;
+        tchunk.length=u->R*fs;
+        tchunk.memblock=pa_memblock_new(u->core->mempool,tchunk.length);
+        //pa_memblockq_drop(u->rendered_q, tchunk.length);
+        pa_assert_se(tchunk.length==u->R*fs);
+        dst=(float*)pa_memblock_acquire(tchunk.memblock);
+        //use a linear-phase sliding STFT and overlap-add method (for each channel)
+        for (size_t c=0;c<u->channels;c++) {
+            //zero padd the data
+            memset(u->work_buffer+u->window_size,0,(u->fft_size-u->window_size)*sizeof(float));
+            //window the data
+            for(size_t j=0;j<u->window_size;++j){
+                u->work_buffer[j]=u->W[j]*u->input[c][j];
+            }
+            //Processing is done here!
+            //do fft
+            fftwf_execute_dft_r2c(u->forward_plan,u->work_buffer,u->output_window);
+            //perform filtering
+            for(size_t j=0;j<u->fft_size/2+1;++j){
+                u->output_window[j][0]*=u->H[j];
+                u->output_window[j][1]*=u->H[j];
+            }
+            //inverse fft
+            fftwf_execute_dft_c2r(u->inverse_plan,u->output_window,u->work_buffer);
+            ////debug: tests overlaping add
+            ////and negates ALL PREVIOUS processing
+            ////yields a perfect reconstruction if COLA is held
+            //for(size_t j=0;j<u->window_size;++j){
+            //    u->work_buffer[j]=u->W[j]*u->input[c][j];
+            //}
+
+            //overlap add and preserve overlap component from this window (linear phase)
+            for(size_t j=0;j<u->R;++j){
+                u->work_buffer[j]+=u->overlap_accum[c][j];
+                u->overlap_accum[c][j]=u->work_buffer[u->overlap_size+j];
+            }
+            
+            //debug: tests if basic buffering works
+            //shouldn't modify the signal AT ALL (beyond roundoff)
+            for(size_t j=0;j<u->window_size;++j){
+                u->work_buffer[j]=u->input[c][j];
+            }
+            
+            //preseve the needed input for the next window's overlap
+            memmove(u->input[c],u->input[c]+u->R,
+                (u->samples_gathered+u->overlap_size-u->R)*sizeof(float)
+            );
+            //output the samples that are outputable now
+            pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c, fs, u->work_buffer, sizeof(float),u->R);
         }
-        */
-
-        //preseve the needed input for the next windows overlap
-        memmove(u->input[c],u->input[c]+u->R,
-            (u->window_size-u->R)*sizeof(float)
-        );
-        //output the samples that are outputable now
-        pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c, fs, u->work_buffer, sizeof(float),n_outputable);
-        //buffer the rest of them
-        memcpy(u->output_buffer[c]+u->n_buffered_output,u->work_buffer+n_outputable,sample_rem*sizeof(float));
-
+        pa_memblock_release(tchunk.memblock);
+        pa_memblockq_push(u->rendered_q, &tchunk);
+        pa_memblock_unref(tchunk.memblock);
+        u->samples_gathered-=u->R;
+        buffer_missing-=u->R;
     }
-    //u->samplings++;
-    u->n_buffered_output+=sample_rem;
-    u->samples_gathered=0;
-end:
-    pa_memblock_release(chunk->memblock);
+    //deque from renderq and output
+    //pa_memblockq_set_prebuf(u->rendered_q,samples_requested*fs);
+    pa_assert_se(pa_memblockq_peek(u->rendered_q,&tchunk)>=0);
+    if(tchunk.length>=nbytes){
+        *chunk=tchunk;
+        chunk->length=samples_requested*fs;
+        pa_memblock_ref(chunk->memblock);
+        pa_memblock_unref(tchunk.memblock);
+        pa_memblockq_drop(u->rendered_q, chunk->length);
+    }else{
+        size_t copied=0;
+        chunk->length=nbytes;
+        chunk->memblock=pa_memblock_new(u->core->mempool,chunk->length);
+        uint8_t *dst=(uint8_t*)pa_memblock_acquire(chunk->memblock);
+        do{
+            size_t l=PA_MIN(tchunk.length-tchunk.index,nbytes-copied);
+            uint8_t *src=(((uint8_t*)pa_memblock_acquire(tchunk.memblock))+tchunk.index);
+            memmove(dst+copied,src,l);
+            copied+=l;
+            pa_memblock_release(tchunk.memblock);
+            pa_memblock_unref(tchunk.memblock);
+            pa_memblockq_drop(u->rendered_q,l);
+            if(copied<nbytes){
+                if(pa_memblockq_get_length(u->rendered_q)==0){
+                    chunk->length=copied;
+                    break;
+                }
+                pa_memblockq_peek(u->rendered_q,&tchunk);
+            }
+        }while(copied<nbytes);
+        pa_memblock_release(chunk->memblock);
+    }
+    pa_assert_se(chunk->memblock);
+    pa_log("output requested %ld, gave %ld",nbytes/fs,chunk->length/fs);
+    //pa_log("end pop");
     return 0;
 }
 
@@ -410,20 +420,19 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
     if (u->sink->thread_info.rewind_nbytes > 0) {
         size_t max_rewrite;
 
-        max_rewrite = nbytes + pa_memblockq_get_length(u->memblockq);
+        max_rewrite = nbytes + pa_memblockq_get_length(u->rendered_q);
         amount = PA_MIN(u->sink->thread_info.rewind_nbytes, max_rewrite);
         u->sink->thread_info.rewind_nbytes = 0;
 
         if (amount > 0) {
-            pa_memblockq_seek(u->memblockq, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE);
+            pa_memblockq_seek(u->rendered_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE);
             pa_log_debug("Resetting equalizer");
-            u->n_buffered_output=0;
             u->samples_gathered=0;
         }
     }
 
     pa_sink_process_rewind(u->sink, amount);
-    pa_memblockq_rewind(u->memblockq, nbytes);
+    pa_memblockq_rewind(u->rendered_q, nbytes);
 }
 
 /* Called from I/O thread context */
@@ -436,7 +445,7 @@ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
     if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
         return;
 
-    pa_memblockq_set_maxrewind(u->memblockq, nbytes);
+    pa_memblockq_set_maxrewind(u->rendered_q, nbytes);
     pa_sink_set_max_rewind_within_thread(u->sink, nbytes);
 }
 
@@ -582,17 +591,20 @@ int pa__init(pa_module*m) {
     u->master = master;
     u->sink = NULL;
     u->sink_input = NULL;
-    u->memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, fs, 1, 1, 0, NULL);
 
-    //u->samplings=0;
     u->channels=ss.channels;
     u->fft_size=pow(2,ceil(log(ss.rate)/log(2)));
     pa_log("fft size: %ld",u->fft_size);
     u->window_size=7999;
     u->R=(u->window_size+1)/2;
+    u->overlap_size=u->window_size-u->R;
+    u->target_samples=5*u->R;
     u->samples_gathered=0;
-    u->n_buffered_output=0;
     u->max_output=pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss)/pa_frame_size(&ss);
+    u->rendered_q = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH,u->target_samples*fs, fs, fs, 0, 0, NULL);
+    u->conv_buffer.memblock=pa_memblock_new(u->core->mempool,u->target_samples*fs);
+
+
     u->H=(float*) fftwf_malloc((u->fft_size/2+1)*sizeof(float));
     u->W=(float*) fftwf_malloc((u->window_size)*sizeof(float));
     u->work_buffer=(float*) fftwf_malloc(u->fft_size*sizeof(float));
@@ -600,9 +612,9 @@ int pa__init(pa_module*m) {
     u->overlap_accum=(float **)malloc(sizeof(float *)*u->channels);
     u->output_buffer=(float **)malloc(sizeof(float *)*u->channels);
     for(size_t c=0;c<u->channels;++c){
-        u->input[c]=(float*) fftwf_malloc(u->window_size*sizeof(float));
+        u->input[c]=(float*) fftwf_malloc((u->target_samples+u->overlap_size)*sizeof(float));
         pa_assert_se(u->input[c]);
-        memset(u->input[c],0,u->window_size*sizeof(float));
+        memset(u->input[c],0,(u->target_samples+u->overlap_size)*sizeof(float));
         pa_assert_se(u->input[c]);
         u->overlap_accum[c]=(float*) fftwf_malloc(u->R*sizeof(float));
         pa_assert_se(u->overlap_accum[c]);
@@ -780,8 +792,11 @@ void pa__done(pa_module*m) {
         pa_sink_input_unref(u->sink_input);
     }
 
-    if (u->memblockq)
-        pa_memblockq_free(u->memblockq);
+    if(u->conv_buffer.memblock)
+        pa_memblock_unref(u->conv_buffer.memblock);
+
+    if (u->rendered_q)
+        pa_memblockq_free(u->rendered_q);
 
     fftwf_destroy_plan(u->inverse_plan);
     fftwf_destroy_plan(u->forward_plan);
diff --git a/src/pulsecore/memblock.c b/src/pulsecore/memblock.c
index f38b17c..eac4a59 100644
--- a/src/pulsecore/memblock.c
+++ b/src/pulsecore/memblock.c
@@ -54,7 +54,7 @@
  * stored in SHM and our OS does not commit the memory before we use
  * it for the first time. */
 #define PA_MEMPOOL_SLOTS_MAX 1024
-#define PA_MEMPOOL_SLOT_SIZE (64*1024)
+#define PA_MEMPOOL_SLOT_SIZE (128*1024)
 
 #define PA_MEMEXPORT_SLOTS_MAX 128
 

commit 09d9096069360d1eecd30b11df7b4c7d2c39ac35
Author: Jason Newton <jason at arcuid.wyred.org>
Date:   Tue Jul 21 03:24:57 2009 -0700

    module-equalizer-sink: simplified sink_input pop callback and introduced new variables that simplify different strategies.

diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
index 1d4a423..970b20d 100755
--- a/src/modules/module-equalizer-sink.c
+++ b/src/modules/module-equalizer-sink.c
@@ -65,6 +65,7 @@ PA_MODULE_USAGE(_("sink=<sink to connect to> "));
 
 #define MEMBLOCKQ_MAXLENGTH (16*1024*1024)
 
+
 struct userdata {
     pa_core *core;
     pa_module *module;
@@ -107,6 +108,21 @@ static const char* const valid_modargs[] = {
     NULL
 };
 
+uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p);
+void hanning_normalized_window(float *W,size_t window_size);
+void hanning_window(float *W,size_t window_size);
+void hamming_window(float *W,size_t window_size);
+void blackman_window(float *W,size_t window_size);
+void sin_window(float *W,size_t window_size);
+void array_out(const char *name,float *a,size_t length);
+
+static void dsp_logic(float *dst,struct userdata *u);
+static void process_samples(struct userdata *u);
+void input_buffer(struct userdata *u,pa_memchunk *in);
+
+#define gettime(x) clock_gettime(CLOCK_MONOTONIC,&x)
+#define tdiff(x,y) time_diff(&x,&y)
+
 uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p)
 {
     return ((timeA_p->tv_sec * 1000000000) + timeA_p->tv_nsec) -
@@ -188,13 +204,14 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
         case PA_SINK_MESSAGE_GET_LATENCY: {
             pa_usec_t usec = 0;
             pa_sample_spec *ss=&u->sink->sample_spec;
-            size_t fs=pa_frame_size(ss);
+            size_t fs=pa_frame_size(&(u->sink->sample_spec));
 
             /* Get the latency of the master sink */
             if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
                 usec = 0;
 
-            usec+=pa_bytes_to_usec(u->samples_gathered*fs,ss);
+            usec+=pa_bytes_to_usec(u->R*fs,ss);
+            //usec+=pa_bytes_to_usec(u->samples_gathered*fs,ss);
             usec += pa_bytes_to_usec(pa_memblockq_get_length(u->rendered_q), ss);
             /* Add the latency internal to our sink input on top */
             usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec);
@@ -247,6 +264,90 @@ static void sink_update_requested_latency(pa_sink *s) {
             pa_sink_get_requested_latency_within_thread(s));
 }
 
+static void process_samples(struct userdata *u){
+    pa_memchunk tchunk;
+    size_t fs=pa_frame_size(&(u->sink->sample_spec));
+    while(u->samples_gathered>=u->R){
+        float *dst;
+        //pa_log("iter gathered: %ld",u->samples_gathered);
+        //pa_memblockq_drop(u->rendered_q, tchunk.length);
+        tchunk.index=0;
+        tchunk.length=u->R*fs;
+        tchunk.memblock=pa_memblock_new(u->core->mempool,tchunk.length);
+        dst=((float*)pa_memblock_acquire(tchunk.memblock));
+        dsp_logic(dst,u);
+        pa_memblock_release(tchunk.memblock);
+        pa_memblockq_push(u->rendered_q, &tchunk);
+        pa_memblock_unref(tchunk.memblock);
+        u->samples_gathered-=u->R;
+    }
+}
+
+static void dsp_logic(float *dst,struct userdata *u){
+    size_t fs=pa_frame_size(&(u->sink->sample_spec));
+    //use a linear-phase sliding STFT and overlap-add method (for each channel)
+    for (size_t c=0;c<u->channels;c++) {
+        //zero padd the data
+        memset(u->work_buffer+u->window_size,0,(u->fft_size-u->window_size)*sizeof(float));
+        //window the data
+        for(size_t j=0;j<u->window_size;++j){
+            u->work_buffer[j]=u->W[j]*u->input[c][j];
+        }
+        //Processing is done here!
+        //do fft
+        fftwf_execute_dft_r2c(u->forward_plan,u->work_buffer,u->output_window);
+        //perform filtering
+        for(size_t j=0;j<u->fft_size/2+1;++j){
+            u->output_window[j][0]*=u->H[j];
+            u->output_window[j][1]*=u->H[j];
+        }
+        //inverse fft
+        fftwf_execute_dft_c2r(u->inverse_plan,u->output_window,u->work_buffer);
+        ////debug: tests overlaping add
+        ////and negates ALL PREVIOUS processing
+        ////yields a perfect reconstruction if COLA is held
+        //for(size_t j=0;j<u->window_size;++j){
+        //    u->work_buffer[j]=u->W[j]*u->input[c][j];
+        //}
+
+        //overlap add and preserve overlap component from this window (linear phase)
+        for(size_t j=0;j<u->R;++j){
+            u->work_buffer[j]+=u->overlap_accum[c][j];
+            u->overlap_accum[c][j]=u->work_buffer[u->overlap_size+j];
+        }
+
+        //debug: tests if basic buffering works
+        //shouldn't modify the signal AT ALL (beyond roundoff)
+        for(size_t j=0;j<u->window_size;++j){
+            u->work_buffer[j]=u->input[c][j];
+        }
+
+        //preseve the needed input for the next window's overlap
+        memmove(u->input[c],u->input[c]+u->R,
+            (u->samples_gathered+u->overlap_size-u->R)*sizeof(float)
+        );
+        //output the samples that are outputable now
+        pa_sample_clamp(PA_SAMPLE_FLOAT32NE,dst+c,fs,u->work_buffer,sizeof(float),u->R);
+    }
+}
+
+void input_buffer(struct userdata *u,pa_memchunk *in){
+    size_t fs=pa_frame_size(&(u->sink->sample_spec));
+    size_t samples=in->length/fs;
+    pa_assert_se(samples<=u->target_samples-u->samples_gathered);
+    float *src = (float*) ((uint8_t*) pa_memblock_acquire(in->memblock) + in->index);
+    for (size_t c=0;c<u->channels;c++) {
+        //buffer with an offset after the overlap from previous
+        //iterations
+        pa_assert_se(
+            u->input[c]+u->overlap_size+u->samples_gathered+samples<=u->input[c]+u->target_samples+u->overlap_size
+        );
+        pa_sample_clamp(PA_SAMPLE_FLOAT32NE,u->input[c]+u->overlap_size+u->samples_gathered,sizeof(float),src+c,fs,samples);
+    }
+    u->samples_gathered+=samples;
+    pa_memblock_release(in->memblock);
+}
+
 /* Called from I/O thread context */
 static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
     struct userdata *u;
@@ -255,153 +356,98 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
     pa_assert_se(u = i->userdata);
     pa_assert_se(u->sink);
     size_t fs=pa_frame_size(&(u->sink->sample_spec));
-    size_t ss=pa_sample_size(&(u->sink->sample_spec));
-    size_t fe = fs/ss;
     size_t samples_requested=nbytes/fs;
+    size_t buffered_samples=pa_memblockq_get_length(u->rendered_q)/fs;
     pa_memchunk tchunk;
     chunk->memblock=NULL;
-    size_t buffered_samples=pa_memblockq_get_length(u->rendered_q)/fs;
-
     if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
         return -1;
 
-    pa_log("start output-buffered %ld, input-buffered %ld",buffered_samples,u->samples_gathered);
-    //collect samples
-    size_t buffered_remaining=pa_memblockq_get_length(u->rendered_q)/fs;
-    size_t buffer_missing=pa_memblockq_missing(u->rendered_q)/fs;
-    size_t desired_samples=(buffer_missing>=u->R)*PA_MIN(u->target_samples-u->samples_gathered,buffer_missing);
-    if(desired_samples>0){
-        u->conv_buffer.index=0;
-        //if we still had buffered output, 
-        //or can gather any more in the buffer
-        //politely request (optimistic)
-        if(buffered_samples>=samples_requested ||
-            (u->samples_gathered/u->R)*u->R>=samples_requested){
-            u->conv_buffer.length=desired_samples*fs;
-            pa_log("trying to buffer %ld samples",desired_samples);
-            pa_sink_render_into(u->sink, &u->conv_buffer);
-        }else{//we need it now! force it
-            //TODO: minimum amount or the whole buffer better?
-            desired_samples=u->R-u->samples_gathered%u->R;
-            u->conv_buffer.length=desired_samples*fs;
-            pa_log("force-buffer %ld samples",desired_samples);
-            pa_sink_render_into_full(u->sink, &u->conv_buffer);
-            pa_assert_se(u->conv_buffer.length==desired_samples*fs);
-        }
-        size_t n_samples=u->conv_buffer.length/fs;
-        float *src;
-        pa_log("received %ld samples",n_samples);
-
-        pa_assert_se(n_samples<=u->target_samples-u->samples_gathered);
-        src = (float*) ((uint8_t*) pa_memblock_acquire(u->conv_buffer.memblock) + u->conv_buffer.index);
-        for (size_t c=0;c<u->channels;c++) {
-            //buffer with an offset after the overlap from previous
-            //iterations
-            pa_assert_se(
-                u->input[c]+u->overlap_size+u->samples_gathered+n_samples<=u->input[c]+u->target_samples+u->overlap_size
-            );
-            pa_sample_clamp(PA_SAMPLE_FLOAT32NE,u->input[c]+u->overlap_size+u->samples_gathered,sizeof(float), src+c, fs, n_samples);
-        }
-        u->samples_gathered+=n_samples;
-        pa_memblock_release(u->conv_buffer.memblock);
-    }
-    //pa_assert_se(u->samples_gathered>=u->R);
-    pa_assert_se(u->fft_size>=u->window_size);
-    pa_assert_se(u->R<u->window_size);
-    //process every complete block on hand
-    while(u->samples_gathered>=u->R&&buffer_missing>=u->R){
-        float *dst;
-        //pa_log("iter gathered: %ld",u->samples_gathered);
-        tchunk.index=0;
-        tchunk.length=u->R*fs;
-        tchunk.memblock=pa_memblock_new(u->core->mempool,tchunk.length);
-        //pa_memblockq_drop(u->rendered_q, tchunk.length);
-        pa_assert_se(tchunk.length==u->R*fs);
-        dst=(float*)pa_memblock_acquire(tchunk.memblock);
-        //use a linear-phase sliding STFT and overlap-add method (for each channel)
-        for (size_t c=0;c<u->channels;c++) {
-            //zero padd the data
-            memset(u->work_buffer+u->window_size,0,(u->fft_size-u->window_size)*sizeof(float));
-            //window the data
-            for(size_t j=0;j<u->window_size;++j){
-                u->work_buffer[j]=u->W[j]*u->input[c][j];
-            }
-            //Processing is done here!
-            //do fft
-            fftwf_execute_dft_r2c(u->forward_plan,u->work_buffer,u->output_window);
-            //perform filtering
-            for(size_t j=0;j<u->fft_size/2+1;++j){
-                u->output_window[j][0]*=u->H[j];
-                u->output_window[j][1]*=u->H[j];
-            }
-            //inverse fft
-            fftwf_execute_dft_c2r(u->inverse_plan,u->output_window,u->work_buffer);
-            ////debug: tests overlaping add
-            ////and negates ALL PREVIOUS processing
-            ////yields a perfect reconstruction if COLA is held
-            //for(size_t j=0;j<u->window_size;++j){
-            //    u->work_buffer[j]=u->W[j]*u->input[c][j];
-            //}
-
-            //overlap add and preserve overlap component from this window (linear phase)
-            for(size_t j=0;j<u->R;++j){
-                u->work_buffer[j]+=u->overlap_accum[c][j];
-                u->overlap_accum[c][j]=u->work_buffer[u->overlap_size+j];
-            }
-            
-            //debug: tests if basic buffering works
-            //shouldn't modify the signal AT ALL (beyond roundoff)
-            for(size_t j=0;j<u->window_size;++j){
-                u->work_buffer[j]=u->input[c][j];
-            }
-            
-            //preseve the needed input for the next window's overlap
-            memmove(u->input[c],u->input[c]+u->R,
-                (u->samples_gathered+u->overlap_size-u->R)*sizeof(float)
-            );
-            //output the samples that are outputable now
-            pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c, fs, u->work_buffer, sizeof(float),u->R);
-        }
-        pa_memblock_release(tchunk.memblock);
-        pa_memblockq_push(u->rendered_q, &tchunk);
-        pa_memblock_unref(tchunk.memblock);
-        u->samples_gathered-=u->R;
-        buffer_missing-=u->R;
-    }
-    //deque from renderq and output
-    //pa_memblockq_set_prebuf(u->rendered_q,samples_requested*fs);
-    pa_assert_se(pa_memblockq_peek(u->rendered_q,&tchunk)>=0);
-    if(tchunk.length>=nbytes){
+    pa_log("start output-buffered %ld, input-buffered %ld, requested %ld",buffered_samples,u->samples_gathered,samples_requested);
+    struct timespec start,end;
+
+    if(pa_memblockq_peek(u->rendered_q,&tchunk)==0){
         *chunk=tchunk;
-        chunk->length=samples_requested*fs;
-        pa_memblock_ref(chunk->memblock);
-        pa_memblock_unref(tchunk.memblock);
         pa_memblockq_drop(u->rendered_q, chunk->length);
-    }else{
-        size_t copied=0;
-        chunk->length=nbytes;
-        chunk->memblock=pa_memblock_new(u->core->mempool,chunk->length);
-        uint8_t *dst=(uint8_t*)pa_memblock_acquire(chunk->memblock);
-        do{
-            size_t l=PA_MIN(tchunk.length-tchunk.index,nbytes-copied);
-            uint8_t *src=(((uint8_t*)pa_memblock_acquire(tchunk.memblock))+tchunk.index);
-            memmove(dst+copied,src,l);
-            copied+=l;
-            pa_memblock_release(tchunk.memblock);
-            pa_memblock_unref(tchunk.memblock);
-            pa_memblockq_drop(u->rendered_q,l);
-            if(copied<nbytes){
-                if(pa_memblockq_get_length(u->rendered_q)==0){
-                    chunk->length=copied;
-                    break;
-                }
-                pa_memblockq_peek(u->rendered_q,&tchunk);
-            }
-        }while(copied<nbytes);
-        pa_memblock_release(chunk->memblock);
+        return 0;
     }
+    do{
+        pa_memchunk *buffer;
+        size_t input_remaining=u->target_samples-u->samples_gathered;
+        pa_assert(input_remaining>0);
+        //collect samples
+
+        //buffer=&u->conv_buffer;
+        //buffer->length=input_remaining*fs;
+        //buffer->index=0;
+        //pa_memblock_ref(buffer);
+        //pa_sink_render_into(u->sink,buffer);
+
+        if(u->sink->thread_info.rewind_requested)
+            sink_request_rewind(u->sink);
+
+        pa_memchunk p;
+        buffer=&p;
+        pa_sink_render(u->sink,u->R*fs,buffer);
+        buffer->length=PA_MIN(input_remaining*fs,buffer->length);
+
+        //debug block
+        //pa_memblockq_push(u->rendered_q,buffer);
+        //pa_memblock_unref(buffer->memblock);
+        //goto END;
+
+        pa_log("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs);
+        //copy new input
+        gettime(start);
+        input_buffer(u,buffer);
+        gettime(end);
+        pa_log("Took %0.5f seconds to setup",tdiff(end,start)*1e-9);
+
+        pa_memblock_unref(buffer->memblock);
+
+        pa_assert_se(u->fft_size>=u->window_size);
+        pa_assert_se(u->R<u->window_size);
+        //process every complete block on hand
+
+        gettime(start);
+        process_samples(u);
+        gettime(end);
+        pa_log("Took %0.5f seconds to process",tdiff(end,start)*1e-9);
+
+        buffered_samples=pa_memblockq_get_length(u->rendered_q)/fs;
+    }while(buffered_samples<u->R);
+
+    //deque from rendered_q and output
+    pa_assert_se(pa_memblockq_peek(u->rendered_q,&tchunk)==0);
+    *chunk=tchunk;
+    pa_memblockq_drop(u->rendered_q, chunk->length);
+    //if(tchunk.length>=nbytes){
+        //chunk->length=PA_MIN(tchunk.length,nbytes);
+    //}else{
+    //    size_t copied=0;
+    //    chunk->index=0;
+    //    chunk->length=PA_MIN(nbytes,pa_memblockq_get_length(u->rendered_q));
+    //    chunk->memblock=pa_memblock_new(u->core->mempool,chunk->length);
+    //    uint8_t *dst=(uint8_t*)pa_memblock_acquire(chunk->memblock);
+    //    for(;;){
+    //        size_t l=PA_MIN(tchunk.length,nbytes-copied);
+    //        pa_assert_se(l>0);
+    //        uint8_t *src=(((uint8_t*)pa_memblock_acquire(tchunk.memblock))+tchunk.index);
+    //        memmove(dst+copied,src,l);
+    //        copied+=l;
+    //        pa_memblock_release(tchunk.memblock);
+    //        pa_memblock_unref(tchunk.memblock);
+    //        pa_memblockq_drop(u->rendered_q,l);
+    //        if(copied<chunk->length){
+    //            pa_assert_se(pa_memblockq_peek(u->rendered_q,&tchunk)==0);
+    //        }else{
+    //            break;
+    //        }
+    //    }
+    //    pa_memblock_release(chunk->memblock);
+    //}
     pa_assert_se(chunk->memblock);
-    pa_log("output requested %ld, gave %ld",nbytes/fs,chunk->length/fs);
+    pa_log("gave %ld",chunk->length/fs);
     //pa_log("end pop");
     return 0;
 }
@@ -411,6 +457,7 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
     struct userdata *u;
     size_t amount = 0;
 
+    pa_log_debug("Rewind callback!");
     pa_sink_input_assert_ref(i);
     pa_assert_se(u = i->userdata);
 
@@ -425,6 +472,7 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
         u->sink->thread_info.rewind_nbytes = 0;
 
         if (amount > 0) {
+            //pa_sample_spec *ss=&u->sink->sample_spec;
             pa_memblockq_seek(u->rendered_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE);
             pa_log_debug("Resetting equalizer");
             u->samples_gathered=0;
@@ -459,7 +507,8 @@ static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
     if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
         return;
 
-    pa_sink_set_max_request_within_thread(u->sink, nbytes);
+    size_t fs=pa_frame_size(&(u->sink->sample_spec));
+    pa_sink_set_max_request_within_thread(u->sink, u->R*fs);
 }
 
 /* Called from I/O thread context */
@@ -472,7 +521,9 @@ static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) {
     if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
         return;
 
-    pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
+    size_t fs=pa_frame_size(&(u->sink->sample_spec));
+    pa_sink_set_latency_range_within_thread(u->sink,u->R*fs ,u->R*fs );
+    //pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
 }
 
 /* Called from I/O thread context */
@@ -504,7 +555,9 @@ static void sink_input_attach_cb(pa_sink_input *i) {
     pa_sink_set_rtpoll(u->sink, i->sink->rtpoll);
     pa_sink_attach_within_thread(u->sink);
 
-    pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency);
+    size_t fs=pa_frame_size(&(u->sink->sample_spec));
+    pa_sink_set_latency_range_within_thread(u->sink, u->R*fs, u->R*fs);
+    //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency);
 }
 
 /* Called from main context */
@@ -598,7 +651,7 @@ int pa__init(pa_module*m) {
     u->window_size=7999;
     u->R=(u->window_size+1)/2;
     u->overlap_size=u->window_size-u->R;
-    u->target_samples=5*u->R;
+    u->target_samples=1*u->R;
     u->samples_gathered=0;
     u->max_output=pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss)/pa_frame_size(&ss);
     u->rendered_q = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH,u->target_samples*fs, fs, fs, 0, 0, NULL);
@@ -716,6 +769,8 @@ int pa__init(pa_module*m) {
 
     pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq);
     pa_sink_set_rtpoll(u->sink, master->rtpoll);
+    pa_sink_set_max_request(u->sink,u->R*fs);
+    //pa_sink_set_fixed_latency(u->sink,pa_bytes_to_usec(u->R*fs,&ss));
 
     /* Create sink input */
     pa_sink_input_new_data_init(&sink_input_data);

commit 702480a8836eefb95cb41d7d4a05d4065d6560dc
Author: Jason Newton <nevion at gmail.com>
Date:   Thu Jul 23 03:26:35 2009 -0700

    module-equalizer-sink:
        first commit of a working state (cpu speed dependant)
        added noop processing for filter debugability

diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
index 970b20d..4d595e1 100755
--- a/src/modules/module-equalizer-sink.c
+++ b/src/modules/module-equalizer-sink.c
@@ -82,6 +82,7 @@ struct userdata {
               * the latency of the filter, calculated from window_size
               * based on constraints of COLA and window function
               */
+    size_t latency;
     size_t overlap_size;//window_size-R
     size_t samples_gathered;
     size_t max_output;
@@ -210,9 +211,9 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
             if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
                 usec = 0;
 
-            usec+=pa_bytes_to_usec(u->R*fs,ss);
+            usec+=pa_bytes_to_usec(u->latency*fs,ss);
             //usec+=pa_bytes_to_usec(u->samples_gathered*fs,ss);
-            usec += pa_bytes_to_usec(pa_memblockq_get_length(u->rendered_q), ss);
+            //usec += pa_bytes_to_usec(pa_memblockq_get_length(u->rendered_q), ss);
             /* Add the latency internal to our sink input on top */
             usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec);
             *((pa_usec_t*) data) = usec;
@@ -316,11 +317,11 @@ static void dsp_logic(float *dst,struct userdata *u){
             u->overlap_accum[c][j]=u->work_buffer[u->overlap_size+j];
         }
 
-        //debug: tests if basic buffering works
-        //shouldn't modify the signal AT ALL (beyond roundoff)
-        for(size_t j=0;j<u->window_size;++j){
-            u->work_buffer[j]=u->input[c][j];
-        }
+        ////debug: tests if basic buffering works
+        ////shouldn't modify the signal AT ALL (beyond roundoff)
+        //for(size_t j=0;j<u->window_size;++j){
+        //    u->work_buffer[j]=u->input[c][j];
+        //}
 
         //preseve the needed input for the next window's overlap
         memmove(u->input[c],u->input[c]+u->R,
@@ -363,7 +364,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
     if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
         return -1;
 
-    pa_log("start output-buffered %ld, input-buffered %ld, requested %ld",buffered_samples,u->samples_gathered,samples_requested);
+    //pa_log("start output-buffered %ld, input-buffered %ld, requested %ld",buffered_samples,u->samples_gathered,samples_requested);
     struct timespec start,end;
 
     if(pa_memblockq_peek(u->rendered_q,&tchunk)==0){
@@ -377,31 +378,31 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
         pa_assert(input_remaining>0);
         //collect samples
 
-        //buffer=&u->conv_buffer;
-        //buffer->length=input_remaining*fs;
-        //buffer->index=0;
-        //pa_memblock_ref(buffer);
-        //pa_sink_render_into(u->sink,buffer);
+        buffer=&u->conv_buffer;
+        buffer->length=input_remaining*fs;
+        buffer->index=0;
+        pa_memblock_ref(buffer->memblock);
+        pa_sink_render_into(u->sink,buffer);
 
-        if(u->sink->thread_info.rewind_requested)
-            sink_request_rewind(u->sink);
+        //if(u->sink->thread_info.rewind_requested)
+        //    sink_request_rewind(u->sink);
 
-        pa_memchunk p;
-        buffer=&p;
-        pa_sink_render(u->sink,u->R*fs,buffer);
-        buffer->length=PA_MIN(input_remaining*fs,buffer->length);
+        //pa_memchunk p;
+        //buffer=&p;
+        //pa_sink_render(u->sink,u->R*fs,buffer);
+        //buffer->length=PA_MIN(input_remaining*fs,buffer->length);
 
         //debug block
         //pa_memblockq_push(u->rendered_q,buffer);
         //pa_memblock_unref(buffer->memblock);
         //goto END;
 
-        pa_log("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs);
+        //pa_log("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs);
         //copy new input
         gettime(start);
         input_buffer(u,buffer);
         gettime(end);
-        pa_log("Took %0.5f seconds to setup",tdiff(end,start)*1e-9);
+        //pa_log("Took %0.5f seconds to setup",tdiff(end,start)*1e-9);
 
         pa_memblock_unref(buffer->memblock);
 
@@ -412,7 +413,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
         gettime(start);
         process_samples(u);
         gettime(end);
-        pa_log("Took %0.5f seconds to process",tdiff(end,start)*1e-9);
+        //pa_log("Took %0.5f seconds to process",tdiff(end,start)*1e-9);
 
         buffered_samples=pa_memblockq_get_length(u->rendered_q)/fs;
     }while(buffered_samples<u->R);
@@ -447,7 +448,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
     //    pa_memblock_release(chunk->memblock);
     //}
     pa_assert_se(chunk->memblock);
-    pa_log("gave %ld",chunk->length/fs);
+    //pa_log("gave %ld",chunk->length/fs);
     //pa_log("end pop");
     return 0;
 }
@@ -522,7 +523,7 @@ static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) {
         return;
 
     size_t fs=pa_frame_size(&(u->sink->sample_spec));
-    pa_sink_set_latency_range_within_thread(u->sink,u->R*fs ,u->R*fs );
+    pa_sink_set_latency_range_within_thread(u->sink,u->latency*fs ,u->latency*fs );
     //pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
 }
 
@@ -556,7 +557,7 @@ static void sink_input_attach_cb(pa_sink_input *i) {
     pa_sink_attach_within_thread(u->sink);
 
     size_t fs=pa_frame_size(&(u->sink->sample_spec));
-    pa_sink_set_latency_range_within_thread(u->sink, u->R*fs, u->R*fs);
+    pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs);
     //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency);
 }
 
@@ -656,6 +657,7 @@ int pa__init(pa_module*m) {
     u->max_output=pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss)/pa_frame_size(&ss);
     u->rendered_q = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH,u->target_samples*fs, fs, fs, 0, 0, NULL);
     u->conv_buffer.memblock=pa_memblock_new(u->core->mempool,u->target_samples*fs);
+    u->latency=u->R;
 
 
     u->H=(float*) fftwf_malloc((u->fft_size/2+1)*sizeof(float));
@@ -676,8 +678,8 @@ int pa__init(pa_module*m) {
         pa_assert_se(u->output_buffer[c]);
     }
     u->output_window = (fftwf_complex *) fftwf_malloc(sizeof(fftwf_complex) * (u->fft_size/2+1));
-    u->forward_plan=fftwf_plan_dft_r2c_1d(u->fft_size, u->work_buffer, u->output_window, FFTW_ESTIMATE);
-    u->inverse_plan=fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_ESTIMATE);
+    u->forward_plan=fftwf_plan_dft_r2c_1d(u->fft_size, u->work_buffer, u->output_window, FFTW_MEASURE);
+    u->inverse_plan=fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_MEASURE);
     /*
     for(size_t j=0;j<u->window_size;++j){
         u->W[j]=.5;

commit c7fcc9cc01c807c30b6c96f9995ef2c596c74146
Author: Jason Newton <nevion at gmail.com>
Date:   Mon Jul 27 01:22:26 2009 -0700

    module-equalizer-sink:
        removed liboil
        added sse2 optimized dsp logic implementation
        cleaned up a bit

diff --git a/src/Makefile.am b/src/Makefile.am
index 10f9a79..281bdf1 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1383,9 +1383,9 @@ module_ladspa_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
 module_ladspa_sink_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
 
 module_equalizer_sink_la_SOURCES = modules/module-equalizer-sink.c
-module_equalizer_sink_la_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
+module_equalizer_sink_la_CFLAGS = $(AM_CFLAGS)
 module_equalizer_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
-module_equalizer_sink_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) $(LIBOIL_LIBS) -lfftw3f libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
+module_equalizer_sink_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) -lfftw3f libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
 
 module_match_la_SOURCES = modules/module-match.c
 module_match_la_LDFLAGS = $(MODULE_LDFLAGS)
diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
index 4d595e1..e20e07f 100755
--- a/src/modules/module-equalizer-sink.c
+++ b/src/modules/module-equalizer-sink.c
@@ -29,11 +29,13 @@ USA.
 #include <config.h>
 #endif
 
+#include <stdlib.h>
 #include <stdio.h>
+#include <float.h>
 #include <math.h>
 #include <fftw3.h>
-#include <float.h>
-
+#include <string.h>
+#include <malloc.h>
 
 #include <pulse/xmalloc.h>
 #include <pulse/i18n.h>
@@ -55,6 +57,14 @@ USA.
 #include <time.h>
 
 
+//#undef __SSE2__
+#ifdef __SSE2__
+#include <xmmintrin.h>
+#include <emmintrin.h>
+#endif
+
+
+
 #include "module-equalizer-sink-symdef.h"
 
 PA_MODULE_AUTHOR("Jason Newton");
@@ -82,10 +92,12 @@ struct userdata {
               * the latency of the filter, calculated from window_size
               * based on constraints of COLA and window function
               */
-    size_t latency;
+    size_t latency;//Really just R but made into it's own variable
+    //for twiddling with pulseaudio
     size_t overlap_size;//window_size-R
     size_t samples_gathered;
-    size_t max_output;
+    size_t max_output;//max amount of samples outputable in a single
+    //message
     size_t target_samples;
     float *H;//frequency response filter (magnitude based)
     float *W;//windowing function (time domain)
@@ -109,76 +121,39 @@ static const char* const valid_modargs[] = {
     NULL
 };
 
-uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p);
-void hanning_normalized_window(float *W,size_t window_size);
-void hanning_window(float *W,size_t window_size);
-void hamming_window(float *W,size_t window_size);
-void blackman_window(float *W,size_t window_size);
-void sin_window(float *W,size_t window_size);
-void array_out(const char *name,float *a,size_t length);
-
-static void dsp_logic(float *dst,struct userdata *u);
+static uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p);
+static void hanning_window(float *W,size_t window_size);
+static void array_out(const char *name,float *a,size_t length);
 static void process_samples(struct userdata *u);
-void input_buffer(struct userdata *u,pa_memchunk *in);
-
+static void input_buffer(struct userdata *u,pa_memchunk *in);
+
+void dsp_logic(
+    float * __restrict__ dst,
+    float * __restrict__ src,
+    float * __restrict__ overlap,
+    const float * __restrict__ H,
+    const float * __restrict__ W,
+    fftwf_complex * __restrict__ output_window,
+    struct userdata *u);
+
+#define v_size 4
 #define gettime(x) clock_gettime(CLOCK_MONOTONIC,&x)
 #define tdiff(x,y) time_diff(&x,&y)
+#define mround(x,y) (x%y==0?x:(x/y+1)*y)
 
 uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p)
 {
-    return ((timeA_p->tv_sec * 1000000000) + timeA_p->tv_nsec) -
-    ((timeB_p->tv_sec * 1000000000) + timeB_p->tv_nsec);
+    return ((timeA_p->tv_sec * 1000000000ULL) + timeA_p->tv_nsec) -
+    ((timeB_p->tv_sec * 1000000000ULL) + timeB_p->tv_nsec);
 }
 
-void hanning_normalized_window(float *W,size_t window_size){
-    //h = sqrt(2)/2 * (1+cos(t*pi)) ./ sqrt( 1+cos(t*pi).^2 )
-    float c;
-    for(size_t i=0;i<window_size;++i){
-        c=cos(M_PI*i/(window_size-1));
-        W[i]=sqrt(2.0)/2.0*(1.0+c) / sqrt(1.0+c*c);
-    }
-}
 void hanning_window(float *W,size_t window_size){
     //h=.5*(1-cos(2*pi*j/(window_size+1)), COLA for R=(M+1)/2
     for(size_t i=0;i<window_size;++i){
-        W[i]=.5*(1-cos(2*M_PI*i/(window_size+1)));
-    }
-}
-void hamming_window(float *W,size_t window_size){
-    //h=.54-.46*cos(2*pi*j/(window_size-1))
-    //COLA for R=(M-1)/2,(M-1)/4 etc when endpoints are divided by 2
-    //or one endpoint is zeroed
-    float m;
-    for(size_t i=0;i<window_size;++i){
-        m=i;
-        m/=(window_size-1);
-        W[i]=.54-.46*cos(2*M_PI*m);
-    }
-    W[window_size-1]=0;
-    //W[0]/=2;
-    //W[window_size-1]/=2;
-}
-void blackman_window(float *W,size_t window_size){
-    //h=.42-.5*cos(2*pi*m)+.08*cos(4*pi*m), m=(0:W-1)/(W-1)
-    //COLA for R=(M-1)/3 when M is odd and R is an integer
-    //R=M/3 when M is even and R is an integer
-    float m;
-    for(size_t i=0;i<window_size;++i){
-        m=i;
-        m/=(window_size-1);
-        W[i]=.42-.5*cos(2*M_PI*m)+.08*cos(4*M_PI*m);
-    }
-}
-
-
-void sin_window(float *W,size_t window_size){
-    //h = (cos(t*pi)+1)/2 .* float(abs(t)<1);
-    for(size_t i=0;i<window_size;++i){
-        W[i]=sin(M_PI*i/(window_size-1));
+        W[i]=(float).5*(1-cos(2*M_PI*i/(window_size+1)));
     }
 }
 
-
 void array_out(const char *name,float *a,size_t length){
     FILE *p=fopen(name,"w");
     if(!p){
@@ -211,9 +186,9 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
             if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
                 usec = 0;
 
-            usec+=pa_bytes_to_usec(u->latency*fs,ss);
+            //usec+=pa_bytes_to_usec(u->latency*fs,ss);
             //usec+=pa_bytes_to_usec(u->samples_gathered*fs,ss);
-            //usec += pa_bytes_to_usec(pa_memblockq_get_length(u->rendered_q), ss);
+            usec += pa_bytes_to_usec(pa_memblockq_get_length(u->rendered_q), ss);
             /* Add the latency internal to our sink input on top */
             usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec);
             *((pa_usec_t*) data) = usec;
@@ -276,7 +251,18 @@ static void process_samples(struct userdata *u){
         tchunk.length=u->R*fs;
         tchunk.memblock=pa_memblock_new(u->core->mempool,tchunk.length);
         dst=((float*)pa_memblock_acquire(tchunk.memblock));
-        dsp_logic(dst,u);
+        for (size_t c=0;c<u->channels;c++) {
+            dsp_logic(
+                u->work_buffer,
+                u->input[c],
+                u->overlap_accum[c],
+                u->H,
+                u->W,
+                u->output_window,
+                u
+            );
+            pa_sample_clamp(PA_SAMPLE_FLOAT32NE,dst+c,fs,u->work_buffer,sizeof(float),u->R);
+        }
         pa_memblock_release(tchunk.memblock);
         pa_memblockq_push(u->rendered_q, &tchunk);
         pa_memblock_unref(tchunk.memblock);
@@ -284,54 +270,166 @@ static void process_samples(struct userdata *u){
     }
 }
 
-static void dsp_logic(float *dst,struct userdata *u){
-    size_t fs=pa_frame_size(&(u->sink->sample_spec));
-    //use a linear-phase sliding STFT and overlap-add method (for each channel)
-    for (size_t c=0;c<u->channels;c++) {
-        //zero padd the data
-        memset(u->work_buffer+u->window_size,0,(u->fft_size-u->window_size)*sizeof(float));
-        //window the data
-        for(size_t j=0;j<u->window_size;++j){
-            u->work_buffer[j]=u->W[j]*u->input[c][j];
-        }
-        //Processing is done here!
-        //do fft
-        fftwf_execute_dft_r2c(u->forward_plan,u->work_buffer,u->output_window);
-        //perform filtering
-        for(size_t j=0;j<u->fft_size/2+1;++j){
-            u->output_window[j][0]*=u->H[j];
-            u->output_window[j][1]*=u->H[j];
-        }
-        //inverse fft
-        fftwf_execute_dft_c2r(u->inverse_plan,u->output_window,u->work_buffer);
-        ////debug: tests overlaping add
-        ////and negates ALL PREVIOUS processing
-        ////yields a perfect reconstruction if COLA is held
-        //for(size_t j=0;j<u->window_size;++j){
-        //    u->work_buffer[j]=u->W[j]*u->input[c][j];
-        //}
+typedef float v4sf __attribute__ ((__aligned__(v_size*sizeof(float))));
+typedef union float_vector {
+    float f[v_size];
+    v4sf v;
+#ifdef __SSE2__
+    __m128 m;
+#endif
+} float_vector_t;
+
+////reference implementation
+//void dsp_logic(
+//    float * __restrict__ dst,//used as a temp array too, needs to be fft_length!
+//    float * __restrict__ src,/*input data w/ overlap at start,
+//                               *automatically cycled in routine
+//                               */
+//    float * __restrict__ overlap,//The size of the overlap
+//    const float * __restrict__ H,//The freq. magnitude scalers filter
+//    const float * __restrict__ W,//The windowing function
+//    fftwf_complex * __restrict__ output_window,//The transformed window'd src
+//    struct userdata *u){
+//    //use a linear-phase sliding STFT and overlap-add method (for each channel)
+//    //zero padd the data
+//    memset(dst+u->window_size,0,(u->fft_size-u->window_size)*sizeof(float));
+//    //window the data
+//    for(size_t j=0;j<u->window_size;++j){
+//        dst[j]=W[j]*src[j];
+//    }
+//    //Processing is done here!
+//    //do fft
+//    fftwf_execute_dft_r2c(u->forward_plan,dst,output_window);
+//    //perform filtering
+//    for(size_t j=0;j<u->fft_size/2+1;++j){
+//        u->output_window[j][0]*=u->H[j];
+//        u->output_window[j][1]*=u->H[j];
+//    }
+//    //inverse fft
+//    fftwf_execute_dft_c2r(u->inverse_plan,output_window,dst);
+//    ////debug: tests overlaping add
+//    ////and negates ALL PREVIOUS processing
+//    ////yields a perfect reconstruction if COLA is held
+//    //for(size_t j=0;j<u->window_size;++j){
+//    //    u->work_buffer[j]=u->W[j]*u->input[c][j];
+//    //}
+//
+//    //overlap add and preserve overlap component from this window (linear phase)
+//    for(size_t j=0;j<u->overlap_size;++j){
+//        u->work_buffer[j]+=overlap[j];
+//        overlap[j]=dst[u->R+j];
+//    }
+//    ////debug: tests if basic buffering works
+//    ////shouldn't modify the signal AT ALL (beyond roundoff)
+//    //for(size_t j=0;j<u->window_size;++j){
+//    //    u->work_buffer[j]=u->input[c][j];
+//    //}
+//
+//    //preseve the needed input for the next window's overlap
+//    memmove(src,src+u->R,
+//        (u->samples_gathered+u->overlap_size-u->R)*sizeof(float)
+//    );
+//}
+
+//regardless of sse enabled, the loops in here assume
+//16 byte aligned addresses and memory allocations divisible by v_size
+void dsp_logic(
+    float * __restrict__ dst,//used as a temp array too, needs to be fft_length!
+    float * __restrict__ src,/*input data w/ overlap at start,
+                               *automatically cycled in routine
+                               */
+    float * __restrict__ overlap,//The size of the overlap
+    const float * __restrict__ H,//The freq. magnitude scalers filter
+    const float * __restrict__ W,//The windowing function
+    fftwf_complex * __restrict__ output_window,//The transformed window'd src
+    struct userdata *u){//Collection of constants
+
+    const size_t window_size=mround(u->window_size,v_size);
+    const size_t fft_h=mround(u->fft_size/2+1,v_size/2);
+    const size_t R=mround(u->R,v_size);
+    const size_t overlap_size=mround(u->overlap_size,v_size);
+
+    //assert(u->samples_gathered>=u->R);
+    //zero out the bit beyond the real overlap so we don't add garbage
+    for(size_t j=overlap_size;j>u->overlap_size;--j){
+       overlap[j-1]=0;
+    }
+    //use a linear-phase sliding STFT and overlap-add method
+    //zero padd the data
+    memset(dst+u->window_size,0,(u->fft_size-u->window_size)*sizeof(float));
+    //window the data
+    for(size_t j=0;j<window_size;j+=v_size){
+        //dst[j]=W[j]*src[j];
+        float_vector_t *d=(float_vector_t*)(dst+j);
+        float_vector_t *w=(float_vector_t*)(W+j);
+        float_vector_t *s=(float_vector_t*)(src+j);
+#if __SSE2__
+        d->m=_mm_mul_ps(w->m,s->m);
+#else
+        d->v=w->v*s->v;
+#endif
+    }
+    //Processing is done here!
+    //do fft
+    fftwf_execute_dft_r2c(u->forward_plan,dst,output_window);
+
+
+    //perform filtering - purely magnitude based
+    for(size_t j=0;j<fft_h;j+=v_size/2){
+        //output_window[j][0]*=H[j];
+        //output_window[j][1]*=H[j];
+        float_vector_t *d=(float_vector_t*)(output_window+j);
+        float_vector_t h;
+        h.f[0]=h.f[1]=H[j];
+        h.f[2]=h.f[3]=H[j+1];
+#if __SSE2__
+        d->m=_mm_mul_ps(d->m,h.m);
+#else
+        d->v=d->v*h->v;
+#endif
+    }
 
-        //overlap add and preserve overlap component from this window (linear phase)
-        for(size_t j=0;j<u->R;++j){
-            u->work_buffer[j]+=u->overlap_accum[c][j];
-            u->overlap_accum[c][j]=u->work_buffer[u->overlap_size+j];
-        }
 
-        ////debug: tests if basic buffering works
-        ////shouldn't modify the signal AT ALL (beyond roundoff)
-        //for(size_t j=0;j<u->window_size;++j){
-        //    u->work_buffer[j]=u->input[c][j];
-        //}
+    //inverse fft
+    fftwf_execute_dft_c2r(u->inverse_plan,output_window,dst);
 
-        //preseve the needed input for the next window's overlap
-        memmove(u->input[c],u->input[c]+u->R,
-            (u->samples_gathered+u->overlap_size-u->R)*sizeof(float)
-        );
-        //output the samples that are outputable now
-        pa_sample_clamp(PA_SAMPLE_FLOAT32NE,dst+c,fs,u->work_buffer,sizeof(float),u->R);
+    ////debug: tests overlaping add
+    ////and negates ALL PREVIOUS processing
+    ////yields a perfect reconstruction if COLA is held
+    //for(size_t j=0;j<u->window_size;++j){
+    //    dst[j]=W[j]*src[j];
+    //}
+
+    //overlap add and preserve overlap component from this window (linear phase)
+    for(size_t j=0;j<overlap_size;j+=v_size){
+        //dst[j]+=overlap[j];
+        //overlap[j]+=dst[j+R];
+        float_vector_t *d=(float_vector_t*)(dst+j);
+        float_vector_t *o=(float_vector_t*)(overlap+j);
+#if __SSE2__
+        d->m=_mm_add_ps(d->m,o->m);
+        o->m=((float_vector_t*)(dst+u->R+j))->m;
+#else
+        d->v=d->v+o->v;
+        o->v=((float_vector_t*)(dst+u->R+j))->v;
+#endif
     }
+    //memcpy(overlap,dst+u->R,u->overlap_size*sizeof(float));
+
+    //////debug: tests if basic buffering works
+    //////shouldn't modify the signal AT ALL (beyond roundoff)
+    //for(size_t j=0;j<u->window_size;++j){
+    //    dst[j]=src[j];
+    //}
+
+    //preseve the needed input for the next window's overlap
+    memmove(src,src+u->R,
+        (u->overlap_size+u->samples_gathered-u->R)*sizeof(float)
+    );
 }
 
+
+
 void input_buffer(struct userdata *u,pa_memchunk *in){
     size_t fs=pa_frame_size(&(u->sink->sample_spec));
     size_t samples=in->length/fs;
@@ -422,31 +520,6 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
     pa_assert_se(pa_memblockq_peek(u->rendered_q,&tchunk)==0);
     *chunk=tchunk;
     pa_memblockq_drop(u->rendered_q, chunk->length);
-    //if(tchunk.length>=nbytes){
-        //chunk->length=PA_MIN(tchunk.length,nbytes);
-    //}else{
-    //    size_t copied=0;
-    //    chunk->index=0;
-    //    chunk->length=PA_MIN(nbytes,pa_memblockq_get_length(u->rendered_q));
-    //    chunk->memblock=pa_memblock_new(u->core->mempool,chunk->length);
-    //    uint8_t *dst=(uint8_t*)pa_memblock_acquire(chunk->memblock);
-    //    for(;;){
-    //        size_t l=PA_MIN(tchunk.length,nbytes-copied);
-    //        pa_assert_se(l>0);
-    //        uint8_t *src=(((uint8_t*)pa_memblock_acquire(tchunk.memblock))+tchunk.index);
-    //        memmove(dst+copied,src,l);
-    //        copied+=l;
-    //        pa_memblock_release(tchunk.memblock);
-    //        pa_memblock_unref(tchunk.memblock);
-    //        pa_memblockq_drop(u->rendered_q,l);
-    //        if(copied<chunk->length){
-    //            pa_assert_se(pa_memblockq_peek(u->rendered_q,&tchunk)==0);
-    //        }else{
-    //            break;
-    //        }
-    //    }
-    //    pa_memblock_release(chunk->memblock);
-    //}
     pa_assert_se(chunk->memblock);
     //pa_log("gave %ld",chunk->length/fs);
     //pa_log("end pop");
@@ -509,7 +582,8 @@ static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
         return;
 
     size_t fs=pa_frame_size(&(u->sink->sample_spec));
-    pa_sink_set_max_request_within_thread(u->sink, u->R*fs);
+    pa_sink_set_max_request_within_thread(u->sink, nbytes);
+    //pa_sink_set_max_request_within_thread(u->sink, u->R*fs);
 }
 
 /* Called from I/O thread context */
@@ -523,7 +597,8 @@ static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) {
         return;
 
     size_t fs=pa_frame_size(&(u->sink->sample_spec));
-    pa_sink_set_latency_range_within_thread(u->sink,u->latency*fs ,u->latency*fs );
+    pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs);
+    //pa_sink_set_latency_range_within_thread(u->sink,u->latency*fs ,u->latency*fs );
     //pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
 }
 
@@ -557,7 +632,12 @@ static void sink_input_attach_cb(pa_sink_input *i) {
     pa_sink_attach_within_thread(u->sink);
 
     size_t fs=pa_frame_size(&(u->sink->sample_spec));
-    pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs);
+    //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs);
+    //pa_sink_set_latency_range_within_thread(u->sink,u->latency*fs, u->master->thread_info.max_latency);
+    //TODO: setting this guy minimizes drop outs but doesn't get rid
+    //of them completely, figure out why
+    pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs);
+    //TODO: this guy causes dropouts constantly+rewinds, it's unusable
     //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency);
 }
 
@@ -605,6 +685,16 @@ static pa_bool_t sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) {
     return u->sink != dest;
 }
 
+
+//ensure's memory allocated is a multiple of v_size
+//and aligned
+static void * alloc(size_t x,size_t s){
+    size_t f=mround(x*s,sizeof(float)*v_size);
+    //printf("requested %ld floats=%ld bytes, rem=%ld\n",x,x*sizeof(float),x*sizeof(float)%16);
+    //printf("giving %ld floats=%ld bytes, rem=%ld\n",f,f*sizeof(float),f*sizeof(float)%16);
+    return fftwf_malloc(f*s);
+}
+
 int pa__init(pa_module*m) {
     struct userdata *u;
     pa_sample_spec ss;
@@ -649,7 +739,7 @@ int pa__init(pa_module*m) {
     u->channels=ss.channels;
     u->fft_size=pow(2,ceil(log(ss.rate)/log(2)));
     pa_log("fft size: %ld",u->fft_size);
-    u->window_size=7999;
+    u->window_size=15999;
     u->R=(u->window_size+1)/2;
     u->overlap_size=u->window_size-u->R;
     u->target_samples=1*u->R;
@@ -659,32 +749,28 @@ int pa__init(pa_module*m) {
     u->conv_buffer.memblock=pa_memblock_new(u->core->mempool,u->target_samples*fs);
     u->latency=u->R;
 
-
-    u->H=(float*) fftwf_malloc((u->fft_size/2+1)*sizeof(float));
-    u->W=(float*) fftwf_malloc((u->window_size)*sizeof(float));
-    u->work_buffer=(float*) fftwf_malloc(u->fft_size*sizeof(float));
+    u->H=alloc((u->fft_size/2+1),sizeof(fftwf_complex));
+    u->W=alloc(u->window_size,sizeof(float));
+    u->work_buffer=alloc(u->fft_size,sizeof(float));
+    memset(u->work_buffer,0,u->fft_size*sizeof(float));
     u->input=(float **)malloc(sizeof(float *)*u->channels);
     u->overlap_accum=(float **)malloc(sizeof(float *)*u->channels);
     u->output_buffer=(float **)malloc(sizeof(float *)*u->channels);
     for(size_t c=0;c<u->channels;++c){
-        u->input[c]=(float*) fftwf_malloc((u->target_samples+u->overlap_size)*sizeof(float));
+        u->input[c]=alloc(u->target_samples+u->overlap_size,sizeof(float));
         pa_assert_se(u->input[c]);
         memset(u->input[c],0,(u->target_samples+u->overlap_size)*sizeof(float));
         pa_assert_se(u->input[c]);
-        u->overlap_accum[c]=(float*) fftwf_malloc(u->R*sizeof(float));
+        u->overlap_accum[c]=alloc(u->overlap_size,sizeof(float));
         pa_assert_se(u->overlap_accum[c]);
-        memset(u->overlap_accum[c],0,u->R*sizeof(float));
-        u->output_buffer[c]=(float*) fftwf_malloc(u->window_size*sizeof(float));
+        memset(u->overlap_accum[c],0,u->overlap_size*sizeof(float));
+        u->output_buffer[c]=alloc(u->window_size,sizeof(float));
         pa_assert_se(u->output_buffer[c]);
     }
-    u->output_window = (fftwf_complex *) fftwf_malloc(sizeof(fftwf_complex) * (u->fft_size/2+1));
+    u->output_window=alloc((u->fft_size/2+1),sizeof(fftwf_complex));
     u->forward_plan=fftwf_plan_dft_r2c_1d(u->fft_size, u->work_buffer, u->output_window, FFTW_MEASURE);
     u->inverse_plan=fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_MEASURE);
-    /*
-    for(size_t j=0;j<u->window_size;++j){
-        u->W[j]=.5;
-    }
-    */
+
     hanning_window(u->W,u->window_size);
 
     const int freqs[]={0,25,50,100,200,300,400,800,1500,
@@ -735,6 +821,7 @@ int pa__init(pa_module*m) {
     }
     free(freq_translated);
 
+
     /* Create sink */
     pa_sink_new_data_init(&sink_data);
     sink_data.driver = __FILE__;
@@ -857,18 +944,18 @@ void pa__done(pa_module*m) {
 
     fftwf_destroy_plan(u->inverse_plan);
     fftwf_destroy_plan(u->forward_plan);
-    fftwf_free(u->output_window);
+    free(u->output_window);
     for(size_t c=0;c<u->channels;++c){
-        fftwf_free(u->output_buffer[c]);
-        fftwf_free(u->overlap_accum[c]);
-        fftwf_free(u->input[c]);
+        free(u->output_buffer[c]);
+        free(u->overlap_accum[c]);
+        free(u->input[c]);
     }
     free(u->output_buffer);
     free(u->overlap_accum);
     free(u->input);
-    fftwf_free(u->work_buffer);
-    fftwf_free(u->W);
-    fftwf_free(u->H);
+    free(u->work_buffer);
+    free(u->W);
+    free(u->H);
 
     pa_xfree(u);
 }

commit 8934c314f6401b953b871bbf5b6810b5fe05a9ac
Author: Jason Newton <nevion at gmail.com>
Date:   Fri Jul 31 18:10:11 2009 -0700

    module-equalizer-sink:
        added dbus support
        removed cruft from inherited from ladspa module and improved clarity
        switched dsp processing to reference implementation until project is more mature
        tsched=0 seems to help with the micro-dropouts/crackling! oh my!
        reformatting/spaces

diff --git a/configure.ac b/configure.ac
index 78234fc..ebfdc2b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1199,6 +1199,7 @@ if test "x${dbus}" != xno || test "x${bluez}" != xno || test "x${hal}" != xno ;
             HAVE_DBUS=1
             saved_LIBS="$LIBS"
             LIBS="$LIBS $DBUS_LIBS"
+            CFLAGS="$CFLAGS $DBUS_CFLAGS"
             AC_CHECK_FUNCS(dbus_watch_get_unix_fd)
             LIBS="$saved_LIBS"
             AC_DEFINE([HAVE_DBUS], 1, [Have D-Bus.])
diff --git a/src/Makefile.am b/src/Makefile.am
index 281bdf1..82bc2f9 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1385,7 +1385,7 @@ module_ladspa_sink_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) libpulsecore- at PA_MAJORMIN
 module_equalizer_sink_la_SOURCES = modules/module-equalizer-sink.c
 module_equalizer_sink_la_CFLAGS = $(AM_CFLAGS)
 module_equalizer_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
-module_equalizer_sink_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) -lfftw3f libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
+module_equalizer_sink_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) -lfftw3f libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
 
 module_match_la_SOURCES = modules/module-match.c
 module_match_la_LDFLAGS = $(MODULE_LDFLAGS)
diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
index e20e07f..d8eb5f3 100755
--- a/src/modules/module-equalizer-sink.c
+++ b/src/modules/module-equalizer-sink.c
@@ -35,7 +35,6 @@ USA.
 #include <math.h>
 #include <fftw3.h>
 #include <string.h>
-#include <malloc.h>
 
 #include <pulse/xmalloc.h>
 #include <pulse/i18n.h>
@@ -52,6 +51,8 @@ USA.
 #include <pulsecore/rtpoll.h>
 #include <pulsecore/sample-util.h>
 #include <pulsecore/ltdl-helper.h>
+#include <pulsecore/protocol-dbus.h>
+#include <pulsecore/dbus-util.h>
 
 #include <stdint.h>
 #include <time.h>
@@ -101,13 +102,18 @@ struct userdata {
     size_t target_samples;
     float *H;//frequency response filter (magnitude based)
     float *W;//windowing function (time domain)
-    float *work_buffer,**input,**overlap_accum,**output_buffer;
+    float *work_buffer, **input, **overlap_accum;
     fftwf_complex *output_window;
-    fftwf_plan forward_plan,inverse_plan;
+    fftwf_plan forward_plan, inverse_plan;
     //size_t samplings;
 
+    float *Hs[2];//thread updatable copies
+    pa_aupdate *a_H;
     pa_memchunk conv_buffer;
     pa_memblockq *rendered_q;
+
+    pa_dbus_protocol *dbus_protocol;
+    char *dbus_path;
 };
 
 static const char* const valid_modargs[] = {
@@ -122,10 +128,10 @@ static const char* const valid_modargs[] = {
 };
 
 static uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p);
-static void hanning_window(float *W,size_t window_size);
-static void array_out(const char *name,float *a,size_t length);
+static void hanning_window(float *W, size_t window_size);
+static void array_out(const char *name, float *a, size_t length);
 static void process_samples(struct userdata *u);
-static void input_buffer(struct userdata *u,pa_memchunk *in);
+static void input_buffer(struct userdata *u, pa_memchunk *in);
 
 void dsp_logic(
     float * __restrict__ dst,
@@ -136,10 +142,17 @@ void dsp_logic(
     fftwf_complex * __restrict__ output_window,
     struct userdata *u);
 
+static void dbus_init(struct userdata *u);
+static void dbus_done(struct userdata *u);
+static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void get_filter(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void set_filter(DBusConnection *conn, DBusMessage *msg, void *_u);
+
 #define v_size 4
-#define gettime(x) clock_gettime(CLOCK_MONOTONIC,&x)
-#define tdiff(x,y) time_diff(&x,&y)
-#define mround(x,y) (x%y==0?x:(x/y+1)*y)
+#define gettime(x) clock_gettime(CLOCK_MONOTONIC, &x)
+#define tdiff(x, y) time_diff(&x, &y)
+#define mround(x, y) (x % y == 0 ? x : ( x / y + 1) * y)
 
 uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p)
 {
@@ -147,26 +160,33 @@ uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p)
     ((timeB_p->tv_sec * 1000000000ULL) + timeB_p->tv_nsec);
 }
 
-void hanning_window(float *W,size_t window_size){
+static void hanning_window(float *W, size_t window_size){
     //h=.5*(1-cos(2*pi*j/(window_size+1)), COLA for R=(M+1)/2
-    for(size_t i=0;i<window_size;++i){
-        W[i]=(float).5*(1-cos(2*M_PI*i/(window_size+1)));
+    for(size_t i=0; i < window_size;++i){
+        W[i] = (float).5*(1-cos(2*M_PI*i/(window_size+1)));
     }
 }
 
-void array_out(const char *name,float *a,size_t length){
-    FILE *p=fopen(name,"w");
+static void fix_filter(float *H, size_t fft_size){
+    //divide out the fft gain
+    for(size_t i = 0; i < (fft_size / 2 + 1); ++i){
+        H[i] /= fft_size;
+    }
+}
+
+void array_out(const char *name, float *a, size_t length){
+    FILE *p=fopen(name, "w");
     if(!p){
-        pa_log("opening %s failed!",name);
+        pa_log("opening %s failed!", name);
         return;
     }
-    for(size_t i=0;i<length;++i){
-        fprintf(p,"%e,",a[i]);
+    for(size_t i = 0; i < length; ++i){
+        fprintf(p, "%e,", a[i]);
         //if(i%1000==0){
-        //    fprintf(p,"\n");
+        //    fprintf(p, "\n");
         //}
     }
-    fprintf(p,"\n");
+    fprintf(p, "\n");
     fclose(p);
 }
 
@@ -180,14 +200,14 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
         case PA_SINK_MESSAGE_GET_LATENCY: {
             pa_usec_t usec = 0;
             pa_sample_spec *ss=&u->sink->sample_spec;
-            size_t fs=pa_frame_size(&(u->sink->sample_spec));
+            //size_t fs=pa_frame_size(&(u->sink->sample_spec));
 
             /* Get the latency of the master sink */
             if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
                 usec = 0;
 
-            //usec+=pa_bytes_to_usec(u->latency*fs,ss);
-            //usec+=pa_bytes_to_usec(u->samples_gathered*fs,ss);
+            //usec+=pa_bytes_to_usec(u->latency * fs, ss);
+            //usec+=pa_bytes_to_usec(u->samples_gathered * fs, ss);
             usec += pa_bytes_to_usec(pa_memblockq_get_length(u->rendered_q), ss);
             /* Add the latency internal to our sink input on top */
             usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec);
@@ -243,15 +263,15 @@ static void sink_update_requested_latency(pa_sink *s) {
 static void process_samples(struct userdata *u){
     pa_memchunk tchunk;
     size_t fs=pa_frame_size(&(u->sink->sample_spec));
-    while(u->samples_gathered>=u->R){
+    while(u->samples_gathered >= u->R){
         float *dst;
-        //pa_log("iter gathered: %ld",u->samples_gathered);
+        //pa_log("iter gathered: %ld", u->samples_gathered);
         //pa_memblockq_drop(u->rendered_q, tchunk.length);
         tchunk.index=0;
         tchunk.length=u->R*fs;
-        tchunk.memblock=pa_memblock_new(u->core->mempool,tchunk.length);
+        tchunk.memblock=pa_memblock_new(u->core->mempool, tchunk.length);
         dst=((float*)pa_memblock_acquire(tchunk.memblock));
-        for (size_t c=0;c<u->channels;c++) {
+        for(size_t c=0;c < u->channels; c++) {
             dsp_logic(
                 u->work_buffer,
                 u->input[c],
@@ -261,7 +281,7 @@ static void process_samples(struct userdata *u){
                 u->output_window,
                 u
             );
-            pa_sample_clamp(PA_SAMPLE_FLOAT32NE,dst+c,fs,u->work_buffer,sizeof(float),u->R);
+            pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst + c, fs, u->work_buffer, sizeof(float), u->R);
         }
         pa_memblock_release(tchunk.memblock);
         pa_memblockq_push(u->rendered_q, &tchunk);
@@ -279,60 +299,7 @@ typedef union float_vector {
 #endif
 } float_vector_t;
 
-////reference implementation
-//void dsp_logic(
-//    float * __restrict__ dst,//used as a temp array too, needs to be fft_length!
-//    float * __restrict__ src,/*input data w/ overlap at start,
-//                               *automatically cycled in routine
-//                               */
-//    float * __restrict__ overlap,//The size of the overlap
-//    const float * __restrict__ H,//The freq. magnitude scalers filter
-//    const float * __restrict__ W,//The windowing function
-//    fftwf_complex * __restrict__ output_window,//The transformed window'd src
-//    struct userdata *u){
-//    //use a linear-phase sliding STFT and overlap-add method (for each channel)
-//    //zero padd the data
-//    memset(dst+u->window_size,0,(u->fft_size-u->window_size)*sizeof(float));
-//    //window the data
-//    for(size_t j=0;j<u->window_size;++j){
-//        dst[j]=W[j]*src[j];
-//    }
-//    //Processing is done here!
-//    //do fft
-//    fftwf_execute_dft_r2c(u->forward_plan,dst,output_window);
-//    //perform filtering
-//    for(size_t j=0;j<u->fft_size/2+1;++j){
-//        u->output_window[j][0]*=u->H[j];
-//        u->output_window[j][1]*=u->H[j];
-//    }
-//    //inverse fft
-//    fftwf_execute_dft_c2r(u->inverse_plan,output_window,dst);
-//    ////debug: tests overlaping add
-//    ////and negates ALL PREVIOUS processing
-//    ////yields a perfect reconstruction if COLA is held
-//    //for(size_t j=0;j<u->window_size;++j){
-//    //    u->work_buffer[j]=u->W[j]*u->input[c][j];
-//    //}
-//
-//    //overlap add and preserve overlap component from this window (linear phase)
-//    for(size_t j=0;j<u->overlap_size;++j){
-//        u->work_buffer[j]+=overlap[j];
-//        overlap[j]=dst[u->R+j];
-//    }
-//    ////debug: tests if basic buffering works
-//    ////shouldn't modify the signal AT ALL (beyond roundoff)
-//    //for(size_t j=0;j<u->window_size;++j){
-//    //    u->work_buffer[j]=u->input[c][j];
-//    //}
-//
-//    //preseve the needed input for the next window's overlap
-//    memmove(src,src+u->R,
-//        (u->samples_gathered+u->overlap_size-u->R)*sizeof(float)
-//    );
-//}
-
-//regardless of sse enabled, the loops in here assume
-//16 byte aligned addresses and memory allocations divisible by v_size
+//reference implementation
 void dsp_logic(
     float * __restrict__ dst,//used as a temp array too, needs to be fft_length!
     float * __restrict__ src,/*input data w/ overlap at start,
@@ -342,106 +309,159 @@ void dsp_logic(
     const float * __restrict__ H,//The freq. magnitude scalers filter
     const float * __restrict__ W,//The windowing function
     fftwf_complex * __restrict__ output_window,//The transformed window'd src
-    struct userdata *u){//Collection of constants
-
-    const size_t window_size=mround(u->window_size,v_size);
-    const size_t fft_h=mround(u->fft_size/2+1,v_size/2);
-    const size_t R=mround(u->R,v_size);
-    const size_t overlap_size=mround(u->overlap_size,v_size);
-
-    //assert(u->samples_gathered>=u->R);
-    //zero out the bit beyond the real overlap so we don't add garbage
-    for(size_t j=overlap_size;j>u->overlap_size;--j){
-       overlap[j-1]=0;
-    }
-    //use a linear-phase sliding STFT and overlap-add method
+    struct userdata *u){
+    //use a linear-phase sliding STFT and overlap-add method (for each channel)
     //zero padd the data
-    memset(dst+u->window_size,0,(u->fft_size-u->window_size)*sizeof(float));
+    memset(dst + u->window_size, 0, (u->fft_size - u->window_size) * sizeof(float));
     //window the data
-    for(size_t j=0;j<window_size;j+=v_size){
-        //dst[j]=W[j]*src[j];
-        float_vector_t *d=(float_vector_t*)(dst+j);
-        float_vector_t *w=(float_vector_t*)(W+j);
-        float_vector_t *s=(float_vector_t*)(src+j);
-#if __SSE2__
-        d->m=_mm_mul_ps(w->m,s->m);
-#else
-        d->v=w->v*s->v;
-#endif
+    for(size_t j = 0;j < u->window_size; ++j){
+        dst[j] = W[j] * src[j];
     }
     //Processing is done here!
     //do fft
-    fftwf_execute_dft_r2c(u->forward_plan,dst,output_window);
-
-
-    //perform filtering - purely magnitude based
-    for(size_t j=0;j<fft_h;j+=v_size/2){
-        //output_window[j][0]*=H[j];
-        //output_window[j][1]*=H[j];
-        float_vector_t *d=(float_vector_t*)(output_window+j);
-        float_vector_t h;
-        h.f[0]=h.f[1]=H[j];
-        h.f[2]=h.f[3]=H[j+1];
-#if __SSE2__
-        d->m=_mm_mul_ps(d->m,h.m);
-#else
-        d->v=d->v*h->v;
-#endif
+    fftwf_execute_dft_r2c(u->forward_plan, dst, output_window);
+    //perform filtering
+    for(size_t j = 0;j < u->fft_size / 2 + 1; ++j){
+        u->output_window[j][0] *= u->H[j];
+        u->output_window[j][1] *= u->H[j];
     }
-
-
     //inverse fft
-    fftwf_execute_dft_c2r(u->inverse_plan,output_window,dst);
-
+    fftwf_execute_dft_c2r(u->inverse_plan, output_window, dst);
     ////debug: tests overlaping add
     ////and negates ALL PREVIOUS processing
     ////yields a perfect reconstruction if COLA is held
-    //for(size_t j=0;j<u->window_size;++j){
-    //    dst[j]=W[j]*src[j];
+    //for(size_t j = 0; j < u->window_size; ++j){
+    //    u->work_buffer[j] = u->W[j] * u->input[c][j];
     //}
 
     //overlap add and preserve overlap component from this window (linear phase)
-    for(size_t j=0;j<overlap_size;j+=v_size){
-        //dst[j]+=overlap[j];
-        //overlap[j]+=dst[j+R];
-        float_vector_t *d=(float_vector_t*)(dst+j);
-        float_vector_t *o=(float_vector_t*)(overlap+j);
-#if __SSE2__
-        d->m=_mm_add_ps(d->m,o->m);
-        o->m=((float_vector_t*)(dst+u->R+j))->m;
-#else
-        d->v=d->v+o->v;
-        o->v=((float_vector_t*)(dst+u->R+j))->v;
-#endif
+    for(size_t j = 0;j < u->overlap_size; ++j){
+        u->work_buffer[j] += overlap[j];
+        overlap[j] = dst[u->R+j];
     }
-    //memcpy(overlap,dst+u->R,u->overlap_size*sizeof(float));
-
-    //////debug: tests if basic buffering works
-    //////shouldn't modify the signal AT ALL (beyond roundoff)
-    //for(size_t j=0;j<u->window_size;++j){
-    //    dst[j]=src[j];
+    ////debug: tests if basic buffering works
+    ////shouldn't modify the signal AT ALL (beyond roundoff)
+    //for(size_t j = 0; j < u->window_size;++j){
+    //    u->work_buffer[j] = u->input[c][j];
     //}
 
     //preseve the needed input for the next window's overlap
-    memmove(src,src+u->R,
-        (u->overlap_size+u->samples_gathered-u->R)*sizeof(float)
+    memmove(src, src+u->R,
+        ((u->overlap_size + u->samples_gathered) - u->R)*sizeof(float)
     );
 }
 
+////regardless of sse enabled, the loops in here assume
+////16 byte aligned addresses and memory allocations divisible by v_size
+//void dsp_logic(
+//    float * __restrict__ dst,//used as a temp array too, needs to be fft_length!
+//    float * __restrict__ src,/*input data w/ overlap at start,
+//                               *automatically cycled in routine
+//                               */
+//    float * __restrict__ overlap,//The size of the overlap
+//    const float * __restrict__ H,//The freq. magnitude scalers filter
+//    const float * __restrict__ W,//The windowing function
+//    fftwf_complex * __restrict__ output_window,//The transformed window'd src
+//    struct userdata *u){//Collection of constants
+//
+//    const size_t window_size = mround(u->window_size,v_size);
+//    const size_t fft_h = mround(u->fft_size / 2 + 1, v_size / 2);
+//    //const size_t R = mround(u->R, v_size);
+//    const size_t overlap_size = mround(u->overlap_size, v_size);
+//
+//    //assert(u->samples_gathered >= u->R);
+//    //zero out the bit beyond the real overlap so we don't add garbage
+//    for(size_t j = overlap_size; j > u->overlap_size; --j){
+//       overlap[j-1] = 0;
+//    }
+//    //use a linear-phase sliding STFT and overlap-add method
+//    //zero padd the data
+//    memset(dst + u->window_size, 0, (u->fft_size - u->window_size)*sizeof(float));
+//    //window the data
+//    for(size_t j = 0; j < window_size; j += v_size){
+//        //dst[j] = W[j]*src[j];
+//        float_vector_t *d = (float_vector_t*) (dst+j);
+//        float_vector_t *w = (float_vector_t*) (W+j);
+//        float_vector_t *s = (float_vector_t*) (src+j);
+//#if __SSE2__
+//        d->m = _mm_mul_ps(w->m, s->m);
+//#else
+//        d->v = w->v * s->v;
+//#endif
+//    }
+//    //Processing is done here!
+//    //do fft
+//    fftwf_execute_dft_r2c(u->forward_plan, dst, output_window);
+//
+//
+//    //perform filtering - purely magnitude based
+//    for(size_t j = 0;j < fft_h; j+=v_size/2){
+//        //output_window[j][0]*=H[j];
+//        //output_window[j][1]*=H[j];
+//        float_vector_t *d = (float_vector_t*)(output_window+j);
+//        float_vector_t h;
+//        h.f[0] = h.f[1] = H[j];
+//        h.f[2] = h.f[3] = H[j+1];
+//#if __SSE2__
+//        d->m = _mm_mul_ps(d->m, h.m);
+//#else
+//        d->v = d->v*h->v;
+//#endif
+//    }
+//
+//
+//    //inverse fft
+//    fftwf_execute_dft_c2r(u->inverse_plan, output_window, dst);
+//
+//    ////debug: tests overlaping add
+//    ////and negates ALL PREVIOUS processing
+//    ////yields a perfect reconstruction if COLA is held
+//    //for(size_t j = 0; j < u->window_size; ++j){
+//    //    dst[j] = W[j]*src[j];
+//    //}
+//
+//    //overlap add and preserve overlap component from this window (linear phase)
+//    for(size_t j = 0; j < overlap_size; j+=v_size){
+//        //dst[j]+=overlap[j];
+//        //overlap[j]+=dst[j+R];
+//        float_vector_t *d = (float_vector_t*)(dst+j);
+//        float_vector_t *o = (float_vector_t*)(overlap+j);
+//#if __SSE2__
+//        d->m = _mm_add_ps(d->m, o->m);
+//        o->m = ((float_vector_t*)(dst+u->R+j))->m;
+//#else
+//        d->v = d->v+o->v;
+//        o->v = ((float_vector_t*)(dst+u->R+j))->v;
+//#endif
+//    }
+//    //memcpy(overlap, dst+u->R, u->overlap_size*sizeof(float));
+//
+//    //////debug: tests if basic buffering works
+//    //////shouldn't modify the signal AT ALL (beyond roundoff)
+//    //for(size_t j = 0; j < u->window_size; ++j){
+//    //    dst[j] = src[j];
+//    //}
+//
+//    //preseve the needed input for the next window's overlap
+//    memmove(src, src+u->R,
+//        ((u->overlap_size+u->samples_gathered)+-u->R)*sizeof(float)
+//    );
+//}
 
 
-void input_buffer(struct userdata *u,pa_memchunk *in){
-    size_t fs=pa_frame_size(&(u->sink->sample_spec));
-    size_t samples=in->length/fs;
-    pa_assert_se(samples<=u->target_samples-u->samples_gathered);
+
+void input_buffer(struct userdata *u, pa_memchunk *in){
+    size_t fs = pa_frame_size(&(u->sink->sample_spec));
+    size_t samples = in->length/fs;
+    pa_assert_se(samples <= u->target_samples-u->samples_gathered);
     float *src = (float*) ((uint8_t*) pa_memblock_acquire(in->memblock) + in->index);
-    for (size_t c=0;c<u->channels;c++) {
+    for(size_t c = 0; c < u->channels; c++) {
         //buffer with an offset after the overlap from previous
         //iterations
         pa_assert_se(
-            u->input[c]+u->overlap_size+u->samples_gathered+samples<=u->input[c]+u->target_samples+u->overlap_size
+            u->input[c]+u->overlap_size+u->samples_gathered+samples <= u->input[c]+u->overlap_size+u->target_samples
         );
-        pa_sample_clamp(PA_SAMPLE_FLOAT32NE,u->input[c]+u->overlap_size+u->samples_gathered,sizeof(float),src+c,fs,samples);
+        pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c]+u->overlap_size+u->samples_gathered, sizeof(float), src + c, fs, samples);
     }
     u->samples_gathered+=samples;
     pa_memblock_release(in->memblock);
@@ -454,74 +474,81 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
     pa_assert(chunk);
     pa_assert_se(u = i->userdata);
     pa_assert_se(u->sink);
-    size_t fs=pa_frame_size(&(u->sink->sample_spec));
-    size_t samples_requested=nbytes/fs;
-    size_t buffered_samples=pa_memblockq_get_length(u->rendered_q)/fs;
+    size_t fs = pa_frame_size(&(u->sink->sample_spec));
+    //size_t samples_requested = nbytes/fs;
+    size_t buffered_samples = pa_memblockq_get_length(u->rendered_q)/fs;
     pa_memchunk tchunk;
-    chunk->memblock=NULL;
+    chunk->memblock = NULL;
     if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
         return -1;
 
     //pa_log("start output-buffered %ld, input-buffered %ld, requested %ld",buffered_samples,u->samples_gathered,samples_requested);
-    struct timespec start,end;
+    struct timespec start, end;
 
-    if(pa_memblockq_peek(u->rendered_q,&tchunk)==0){
-        *chunk=tchunk;
+    if(pa_memblockq_peek(u->rendered_q, &tchunk)==0){
+        *chunk = tchunk;
         pa_memblockq_drop(u->rendered_q, chunk->length);
         return 0;
     }
+
+    /*
+        Set the H filter
+    */
+    unsigned H_i = pa_aupdate_read_begin(u->a_H);
+    u->H = u->Hs[H_i];
+    
     do{
         pa_memchunk *buffer;
-        size_t input_remaining=u->target_samples-u->samples_gathered;
+        size_t input_remaining = u->target_samples-u->samples_gathered;
         pa_assert(input_remaining>0);
         //collect samples
 
-        buffer=&u->conv_buffer;
-        buffer->length=input_remaining*fs;
-        buffer->index=0;
+        buffer = &u->conv_buffer;
+        buffer->length = input_remaining*fs;
+        buffer->index = 0;
         pa_memblock_ref(buffer->memblock);
-        pa_sink_render_into(u->sink,buffer);
+        pa_sink_render_into(u->sink, buffer);
 
         //if(u->sink->thread_info.rewind_requested)
         //    sink_request_rewind(u->sink);
 
         //pa_memchunk p;
-        //buffer=&p;
-        //pa_sink_render(u->sink,u->R*fs,buffer);
-        //buffer->length=PA_MIN(input_remaining*fs,buffer->length);
+        //buffer = &p;
+        //pa_sink_render(u->sink, u->R*fs, buffer);
+        //buffer->length = PA_MIN(input_remaining*fs, buffer->length);
 
         //debug block
-        //pa_memblockq_push(u->rendered_q,buffer);
+        //pa_memblockq_push(u->rendered_q, buffer);
         //pa_memblock_unref(buffer->memblock);
         //goto END;
 
         //pa_log("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs);
         //copy new input
         gettime(start);
-        input_buffer(u,buffer);
+        input_buffer(u, buffer);
         gettime(end);
-        //pa_log("Took %0.5f seconds to setup",tdiff(end,start)*1e-9);
+        //pa_log("Took %0.5f seconds to setup", tdiff(end, start)*1e-9);
 
         pa_memblock_unref(buffer->memblock);
 
-        pa_assert_se(u->fft_size>=u->window_size);
-        pa_assert_se(u->R<u->window_size);
+        pa_assert_se(u->fft_size >= u->window_size);
+        pa_assert_se(u->R < u->window_size);
         //process every complete block on hand
 
         gettime(start);
         process_samples(u);
         gettime(end);
-        //pa_log("Took %0.5f seconds to process",tdiff(end,start)*1e-9);
+        //pa_log("Took %0.5f seconds to process", tdiff(end, start)*1e-9);
 
-        buffered_samples=pa_memblockq_get_length(u->rendered_q)/fs;
-    }while(buffered_samples<u->R);
+        buffered_samples = pa_memblockq_get_length(u->rendered_q)/fs;
+    }while(buffered_samples < u->R);
 
     //deque from rendered_q and output
-    pa_assert_se(pa_memblockq_peek(u->rendered_q,&tchunk)==0);
-    *chunk=tchunk;
+    pa_assert_se(pa_memblockq_peek(u->rendered_q, &tchunk)==0);
+    *chunk = tchunk;
     pa_memblockq_drop(u->rendered_q, chunk->length);
     pa_assert_se(chunk->memblock);
-    //pa_log("gave %ld",chunk->length/fs);
+    //pa_log("gave %ld", chunk->length/fs);
     //pa_log("end pop");
     return 0;
 }
@@ -546,10 +573,10 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
         u->sink->thread_info.rewind_nbytes = 0;
 
         if (amount > 0) {
-            //pa_sample_spec *ss=&u->sink->sample_spec;
+            //pa_sample_spec *ss = &u->sink->sample_spec;
             pa_memblockq_seek(u->rendered_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE);
             pa_log_debug("Resetting equalizer");
-            u->samples_gathered=0;
+            u->samples_gathered = 0;
         }
     }
 
@@ -581,9 +608,9 @@ static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
     if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
         return;
 
-    size_t fs=pa_frame_size(&(u->sink->sample_spec));
-    pa_sink_set_max_request_within_thread(u->sink, nbytes);
-    //pa_sink_set_max_request_within_thread(u->sink, u->R*fs);
+    size_t fs = pa_frame_size(&(u->sink->sample_spec));
+    //pa_sink_set_max_request_within_thread(u->sink, nbytes);
+    pa_sink_set_max_request_within_thread(u->sink, u->R*fs);
 }
 
 /* Called from I/O thread context */
@@ -596,9 +623,9 @@ static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) {
     if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
         return;
 
-    size_t fs=pa_frame_size(&(u->sink->sample_spec));
-    pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs);
-    //pa_sink_set_latency_range_within_thread(u->sink,u->latency*fs ,u->latency*fs );
+    size_t fs = pa_frame_size(&(u->sink->sample_spec));
+    //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs);
+    pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs );
     //pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
 }
 
@@ -631,9 +658,9 @@ static void sink_input_attach_cb(pa_sink_input *i) {
     pa_sink_set_rtpoll(u->sink, i->sink->rtpoll);
     pa_sink_attach_within_thread(u->sink);
 
-    size_t fs=pa_frame_size(&(u->sink->sample_spec));
+    size_t fs = pa_frame_size(&(u->sink->sample_spec));
     //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs);
-    //pa_sink_set_latency_range_within_thread(u->sink,u->latency*fs, u->master->thread_info.max_latency);
+    //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->master->thread_info.max_latency);
     //TODO: setting this guy minimizes drop outs but doesn't get rid
     //of them completely, figure out why
     pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs);
@@ -689,10 +716,13 @@ static pa_bool_t sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) {
 //ensure's memory allocated is a multiple of v_size
 //and aligned
 static void * alloc(size_t x,size_t s){
-    size_t f=mround(x*s,sizeof(float)*v_size);
-    //printf("requested %ld floats=%ld bytes, rem=%ld\n",x,x*sizeof(float),x*sizeof(float)%16);
-    //printf("giving %ld floats=%ld bytes, rem=%ld\n",f,f*sizeof(float),f*sizeof(float)%16);
-    return fftwf_malloc(f*s);
+    size_t f = mround(x*s, sizeof(float)*v_size);
+    pa_assert_se(f >= x*s);
+    //printf("requested %ld floats=%ld bytes, rem=%ld\n", x, x*sizeof(float), x*sizeof(float)%16);
+    //printf("giving %ld floats=%ld bytes, rem=%ld\n", f, f*sizeof(float), f*sizeof(float)%16);
+    float *t = fftwf_malloc(f);
+    memset(t, 0, f);
+    return t;
 }
 
 int pa__init(pa_module*m) {
@@ -726,7 +756,7 @@ int pa__init(pa_module*m) {
         pa_log("Invalid sample format specification or channel map");
         goto fail;
     }
-    fs=pa_frame_size(&ss);
+    fs = pa_frame_size(&ss);
 
     u = pa_xnew0(struct userdata, 1);
     u->core = m->core;
@@ -736,90 +766,96 @@ int pa__init(pa_module*m) {
     u->sink = NULL;
     u->sink_input = NULL;
 
-    u->channels=ss.channels;
-    u->fft_size=pow(2,ceil(log(ss.rate)/log(2)));
-    pa_log("fft size: %ld",u->fft_size);
-    u->window_size=15999;
-    u->R=(u->window_size+1)/2;
-    u->overlap_size=u->window_size-u->R;
-    u->target_samples=1*u->R;
-    u->samples_gathered=0;
-    u->max_output=pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss)/pa_frame_size(&ss);
-    u->rendered_q = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH,u->target_samples*fs, fs, fs, 0, 0, NULL);
-    u->conv_buffer.memblock=pa_memblock_new(u->core->mempool,u->target_samples*fs);
-    u->latency=u->R;
-
-    u->H=alloc((u->fft_size/2+1),sizeof(fftwf_complex));
-    u->W=alloc(u->window_size,sizeof(float));
-    u->work_buffer=alloc(u->fft_size,sizeof(float));
-    memset(u->work_buffer,0,u->fft_size*sizeof(float));
-    u->input=(float **)malloc(sizeof(float *)*u->channels);
-    u->overlap_accum=(float **)malloc(sizeof(float *)*u->channels);
-    u->output_buffer=(float **)malloc(sizeof(float *)*u->channels);
-    for(size_t c=0;c<u->channels;++c){
-        u->input[c]=alloc(u->target_samples+u->overlap_size,sizeof(float));
+    u->channels = ss.channels;
+    u->fft_size = pow(2, ceil(log(ss.rate)/log(2)));
+    pa_log("fft size: %ld", u->fft_size);
+    u->window_size = 7999;
+    u->R = (u->window_size+1)/2;
+    u->overlap_size = u->window_size-u->R;
+    u->target_samples = 1*u->R;
+    u->samples_gathered = 0;
+    u->max_output = pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss)/pa_frame_size(&ss);
+    u->rendered_q = pa_memblockq_new(0,  MEMBLOCKQ_MAXLENGTH, u->target_samples*fs, fs, fs, 0, 0, NULL);
+    u->a_H = pa_aupdate_new();
+    u->conv_buffer.memblock = pa_memblock_new(u->core->mempool, u->target_samples*fs);
+    u->latency = u->R;
+    for(size_t i = 0; i < 2; ++i){
+        u->Hs[i] = alloc((u->fft_size / 2 + 1), sizeof(float));
+    }
+    u->W = alloc(u->window_size, sizeof(float));
+    u->work_buffer = alloc(u->fft_size, sizeof(float));
+    memset(u->work_buffer, 0, u->fft_size*sizeof(float));
+    u->input = (float **)pa_xmalloc0(sizeof(float *)*u->channels);
+    u->overlap_accum = (float **)pa_xmalloc0(sizeof(float *)*u->channels);
+    for(size_t c = 0; c < u->channels; ++c){
+        u->input[c] = alloc(u->overlap_size+u->target_samples, sizeof(float));
         pa_assert_se(u->input[c]);
-        memset(u->input[c],0,(u->target_samples+u->overlap_size)*sizeof(float));
+        memset(u->input[c], 0, (u->overlap_size+u->target_samples)*sizeof(float));
         pa_assert_se(u->input[c]);
-        u->overlap_accum[c]=alloc(u->overlap_size,sizeof(float));
+        u->overlap_accum[c] = alloc(u->overlap_size, sizeof(float));
         pa_assert_se(u->overlap_accum[c]);
-        memset(u->overlap_accum[c],0,u->overlap_size*sizeof(float));
-        u->output_buffer[c]=alloc(u->window_size,sizeof(float));
-        pa_assert_se(u->output_buffer[c]);
-    }
-    u->output_window=alloc((u->fft_size/2+1),sizeof(fftwf_complex));
-    u->forward_plan=fftwf_plan_dft_r2c_1d(u->fft_size, u->work_buffer, u->output_window, FFTW_MEASURE);
-    u->inverse_plan=fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_MEASURE);
-
-    hanning_window(u->W,u->window_size);
-
-    const int freqs[]={0,25,50,100,200,300,400,800,1500,
-        2000,3000,4000,5000,6000,7000,8000,9000,10000,11000,12000,
-        13000,14000,15000,16000,17000,18000,19000,20000,21000,22000,23000,24000,INT_MAX};
-    const float coefficients[]={1,1,1,1,1,1,1,1,1,1,
-        1,1,1,1,1,1,1,1,
-        1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
-    const size_t ncoefficients=sizeof(coefficients)/sizeof(float);
-    pa_assert_se(sizeof(freqs)/sizeof(int)==sizeof(coefficients)/sizeof(float));
-    float *freq_translated=(float *) malloc(sizeof(float)*(ncoefficients));
-    freq_translated[0]=1;
-    //Translate the frequencies in their natural sampling rate to the new sampling rate frequencies
-    for(size_t i=1;i<ncoefficients-1;++i){
-        freq_translated[i]=((float)freqs[i]*u->fft_size)/ss.rate;
-        //pa_log("i: %ld: %d , %g",i,freqs[i],freq_translated[i]);
-        pa_assert_se(freq_translated[i]>=freq_translated[i-1]);
+        memset(u->overlap_accum[c], 0, u->overlap_size*sizeof(float));
     }
-    freq_translated[ncoefficients-1]=FLT_MAX;
-    //Interpolate the specified frequency band values
-    u->H[0]=1;
-    for(size_t i=1,j=0;i<(u->fft_size/2+1);++i){
-        pa_assert_se(j<ncoefficients);
-        //max frequency range passed, consider the rest as one band
-        if(freq_translated[j+1]>=FLT_MAX){
-            for(;i<(u->fft_size/2+1);++i){
-                u->H[i]=coefficients[j];
-            }
-            break;
-        }
-        //pa_log("i: %d, j: %d, freq: %f",i,j,freq_translated[j]);
-        //pa_log("interp: %0.4f %0.4f",freq_translated[j],freq_translated[j+1]);
-        pa_assert_se(freq_translated[j]<freq_translated[j+1]);
-        pa_assert_se(i>=freq_translated[j]);
-        pa_assert_se(i<=freq_translated[j+1]);
-        //bilinear-inerpolation of coefficients specified
-        float c0=(i-freq_translated[j])/(freq_translated[j+1]-freq_translated[j]);
-        pa_assert_se(c0>=0&&c0<=1.0);
-        u->H[i]=((1.0f-c0)*coefficients[j]+c0*coefficients[j+1]);
-        pa_assert_se(u->H[i]>0);
-        while(i>=floor(freq_translated[j+1])){
-            j++;
-        }
-    }
-    //divide out the fft gain
-    for(size_t i=0;i<(u->fft_size/2+1);++i){
-        u->H[i]/=u->fft_size;
+    u->output_window = alloc((u->fft_size / 2 + 1), sizeof(fftwf_complex));
+    u->forward_plan = fftwf_plan_dft_r2c_1d(u->fft_size, u->work_buffer, u->output_window, FFTW_MEASURE);
+    u->inverse_plan = fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_MEASURE);
+
+    hanning_window(u->W, u->window_size);
+
+    unsigned H_i = pa_aupdate_write_begin(u->a_H);
+    u->H = u->Hs[H_i];
+    for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){
+        u->H[i] = 1.0;
     }
-    free(freq_translated);
+
+    //TODO cut this out and leave it for the client side
+    //const int freqs[] = {0,25,50,100,200,300,400,800,1500,
+    //    2000,3000,4000,5000,6000,7000,8000,9000,10000,11000,12000,
+    //    13000,14000,15000,16000,17000,18000,19000,20000,21000,22000,23000,24000,INT_MAX};
+    //const float coefficients[] = {1,1,1,1,1,1,1,1,1,1,
+    //    1,1,1,1,1,1,1,1,
+    //    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
+    //const size_t ncoefficients = sizeof(coefficients)/sizeof(float);
+    //pa_assert_se(sizeof(freqs)/sizeof(int)==sizeof(coefficients)/sizeof(float));
+    //float *freq_translated = (float *) pa_xmalloc0(sizeof(float)*(ncoefficients));
+    //freq_translated[0] = 1;
+    ////Translate the frequencies in their natural sampling rate to the new sampling rate frequencies
+    //for(size_t i = 1; i < ncoefficients-1; ++i){
+    //    freq_translated[i] = ((float)freqs[i]*u->fft_size)/ss.rate;
+    //    //pa_log("i: %ld: %d , %g",i, freqs[i], freq_translated[i]);
+    //    pa_assert_se(freq_translated[i] >= freq_translated[i-1]);
+    //}
+    //freq_translated[ncoefficients-1] = FLT_MAX;
+    //
+    ////Interpolate the specified frequency band values
+    //u->H[0] = 1;
+    //for(size_t i = 1, j = 0; i < (u->fft_size / 2 + 1); ++i){
+    //    pa_assert_se(j < ncoefficients);
+    //    //max frequency range passed, consider the rest as one band
+    //    if(freq_translated[j+1] >= FLT_MAX){
+    //        for(; i < (u->fft_size / 2 + 1); ++i){
+    //            u->H[i] = coefficients[j];
+    //        }
+    //        break;
+    //    }
+    //    //pa_log("i: %d, j: %d, freq: %f", i, j, freq_translated[j]);
+    //    //pa_log("interp: %0.4f %0.4f", freq_translated[j], freq_translated[j+1]);
+    //    pa_assert_se(freq_translated[j] < freq_translated[j+1]);
+    //    pa_assert_se(i >= freq_translated[j]);
+    //    pa_assert_se(i <= freq_translated[j+1]);
+    //    //bilinear-inerpolation of coefficients specified
+    //    float c0 = (i-freq_translated[j])/(freq_translated[j+1]-freq_translated[j]);
+    //    pa_assert_se(c0 >= 0&&c0 <= 1.0);
+    //    u->H[i] = ((1.0f-c0)*coefficients[j]+c0*coefficients[j+1]);
+    //    pa_assert_se(u->H[i]>0);
+    //    while(i >= floor(freq_translated[j+1])){
+    //        j++;
+    //    }
+    //}
+    //pa_xfree(freq_translated);
+    fix_filter(u->H, u->fft_size);
+    pa_aupdate_write_swap(u->a_H);
+    pa_aupdate_write_end(u->a_H);
 
 
     /* Create sink */
@@ -858,8 +894,8 @@ int pa__init(pa_module*m) {
 
     pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq);
     pa_sink_set_rtpoll(u->sink, master->rtpoll);
-    pa_sink_set_max_request(u->sink,u->R*fs);
-    //pa_sink_set_fixed_latency(u->sink,pa_bytes_to_usec(u->R*fs,&ss));
+    pa_sink_set_max_request(u->sink, u->R*fs);
+    //pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(u->R*fs, &ss));
 
     /* Create sink input */
     pa_sink_input_new_data_init(&sink_input_data);
@@ -896,6 +932,8 @@ int pa__init(pa_module*m) {
 
     pa_xfree(use_default);
 
+    dbus_init(u);
+
     return 0;
 
 fail:
@@ -925,6 +963,7 @@ void pa__done(pa_module*m) {
 
     if (!(u = m->userdata))
         return;
+    dbus_done(u);
 
     if (u->sink) {
         pa_sink_unlink(u->sink);
@@ -944,18 +983,152 @@ void pa__done(pa_module*m) {
 
     fftwf_destroy_plan(u->inverse_plan);
     fftwf_destroy_plan(u->forward_plan);
-    free(u->output_window);
-    for(size_t c=0;c<u->channels;++c){
-        free(u->output_buffer[c]);
-        free(u->overlap_accum[c]);
-        free(u->input[c]);
+    pa_xfree(u->output_window);
+    for(size_t c=0; c < u->channels; ++c){
+        pa_xfree(u->overlap_accum[c]);
+        pa_xfree(u->input[c]);
+    }
+    pa_xfree(u->overlap_accum);
+    pa_xfree(u->input);
+    pa_xfree(u->work_buffer);
+    pa_xfree(u->W);
+    for(size_t i = 0; i < 2; ++i){
+        pa_xfree(u->Hs[i]);
     }
-    free(u->output_buffer);
-    free(u->overlap_accum);
-    free(u->input);
-    free(u->work_buffer);
-    free(u->W);
-    free(u->H);
 
     pa_xfree(u);
 }
+
+enum property_handler_index {
+    PROPERTY_HANDLER_N_COEFS,
+    PROPERTY_HANDLER_COEFS,
+    PROPERTY_HANDLER_MAX
+};
+
+static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX]={
+    [PROPERTY_HANDLER_N_COEFS]{.property_name="n_filter_coefficients",.type="u",.get_cb=get_n_coefs,.set_cb=NULL},
+    [PROPERTY_HANDLER_COEFS]{.property_name="filter_coefficients",.type="ai",.get_cb=get_filter,.set_cb=set_filter}
+};
+
+//static pa_dbus_arg_info new_equalizer_args[] = { { "path","o",NULL} };
+//static pa_dbus_signal_info signals[SIGNAL_MAX] = {
+//    [SIGNAL_NEW_EQUALIZER]={.name="NewEqualizer",.arguments=new_equalizer_args,.n_arguments=1}
+//};
+
+#define EXTNAME "org.PulseAudio.Ext.Equalizing1"
+
+static pa_dbus_interface_info interface_info={
+    .name=EXTNAME ".Equalizer",
+    .method_handlers=NULL,
+    .n_method_handlers=0,
+    .property_handlers=property_handlers,
+    .n_property_handlers=PROPERTY_HANDLER_MAX,
+    .get_all_properties_cb=handle_get_all,
+    .signals=NULL,
+    .n_signals=0
+};
+
+
+void dbus_init(struct userdata *u){
+    u->dbus_protocol=pa_dbus_protocol_get(u->core);
+    u->dbus_path=pa_sprintf_malloc("/org/pulseaudio/core1/sink%d", u->sink->index);
+
+    pa_dbus_protocol_add_interface(u->dbus_protocol, u->dbus_path, &interface_info, u);
+    pa_dbus_protocol_register_extension(u->dbus_protocol, EXTNAME);
+}
+
+void dbus_done(struct userdata *u){
+    pa_dbus_protocol_unregister_extension(u->dbus_protocol, EXTNAME);
+    pa_dbus_protocol_remove_interface(u->dbus_protocol, u->dbus_path, EXTNAME);
+    
+    pa_xfree(u->dbus_path);
+    pa_dbus_protocol_unref(u->dbus_protocol);
+}
+
+void get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u){
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(_u);
+
+    struct userdata *u=(struct userdata *)_u;
+
+    uint32_t n_coefs=(uint32_t)(u->fft_size / 2 + 1);
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &n_coefs);
+}
+
+void get_filter(DBusConnection *conn, DBusMessage *msg, void *_u){
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(_u);
+
+    struct userdata *u=(struct userdata *)_u;
+    
+    unsigned n_coefs=(unsigned)(u->fft_size / 2 + 1);
+    double *H_=(double *)pa_xmalloc0(n_coefs*sizeof(double));
+    
+    unsigned H_i=pa_aupdate_read_begin(u->a_H);
+    float *H=u->Hs[H_i];
+    for(size_t i = 0;i < u->fft_size / 2 + 1; ++i){
+        H_[i]=H[i];
+    }
+    pa_aupdate_read_end(u->a_H);
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_DOUBLE, &H_, n_coefs);
+    pa_xfree(H_);
+}
+
+void set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(_u);
+
+    struct userdata *u=(struct userdata *)_u;
+    double *H_;
+    unsigned _n_coefs;
+    pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_DOUBLE, &H_, &_n_coefs);
+    if(_n_coefs!=u->fft_size / 2 + 1){
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "This filter takes exactly %ld coefficients, you gave %d", u->fft_size / 2 + 1, _n_coefs);
+        return;
+    }
+    unsigned H_i = pa_aupdate_write_begin(u->a_H);
+    float *H = u->Hs[H_i];
+    for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){
+        H[i] = (float)H_[i];
+    }
+    pa_aupdate_write_swap(u->a_H);
+    pa_aupdate_write_end(u->a_H);
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(_u);
+
+    struct userdata *u = (struct userdata *)_u;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter, dict_iter;
+
+    int n_coefs=(unsigned)(u->fft_size / 2 + 1);
+    double *H_=(double *)pa_xmalloc0(n_coefs*sizeof(double));
+    
+    unsigned H_i=pa_aupdate_read_begin(u->a_H);
+    float *H=u->Hs[H_i];
+    for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){
+        H_[i] = H[i];
+    }
+    pa_aupdate_read_end(u->a_H);
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_N_COEFS].property_name, DBUS_TYPE_UINT32, &n_coefs);
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_COEFS].property_name, DBUS_TYPE_DOUBLE, H_, n_coefs);
+
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+    dbus_message_unref(reply);
+
+    pa_xfree(H_);
+}

commit 66a6cc693bcf441d86ed56a3b15be948008e9de7
Author: Jason Newton <nevion at gmail.com>
Date:   Sat Aug 1 20:23:49 2009 -0700

    module-equalizer-sink:
        added support for suspend/resume of filter coefficients
        unregister the correct dbus interface.
        made equalizer state file sink index dependent
        expanded dbus properties
        whitespace

diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
index d8eb5f3..6ea5951 100755
--- a/src/modules/module-equalizer-sink.c
+++ b/src/modules/module-equalizer-sink.c
@@ -50,7 +50,7 @@ USA.
 #include <pulsecore/thread-mq.h>
 #include <pulsecore/rtpoll.h>
 #include <pulsecore/sample-util.h>
-#include <pulsecore/ltdl-helper.h>
+#include <pulsecore/database.h>
 #include <pulsecore/protocol-dbus.h>
 #include <pulsecore/dbus-util.h>
 
@@ -114,6 +114,8 @@ struct userdata {
 
     pa_dbus_protocol *dbus_protocol;
     char *dbus_path;
+
+    pa_database *database;
 };
 
 static const char* const valid_modargs[] = {
@@ -145,14 +147,18 @@ void dsp_logic(
 static void dbus_init(struct userdata *u);
 static void dbus_done(struct userdata *u);
 static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *_u);
+void get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u);
+void get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u);
 static void get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u);
 static void get_filter(DBusConnection *conn, DBusMessage *msg, void *_u);
 static void set_filter(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void save_state(struct userdata *u);
 
 #define v_size 4
 #define gettime(x) clock_gettime(CLOCK_MONOTONIC, &x)
 #define tdiff(x, y) time_diff(&x, &y)
 #define mround(x, y) (x % y == 0 ? x : ( x / y + 1) * y)
+#define COEFKEY "coefficients"
 
 uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p)
 {
@@ -321,7 +327,7 @@ void dsp_logic(
     //do fft
     fftwf_execute_dft_r2c(u->forward_plan, dst, output_window);
     //perform filtering
-    for(size_t j = 0;j < u->fft_size / 2 + 1; ++j){
+    for(size_t j = 0; j < u->fft_size / 2 + 1; ++j){
         u->output_window[j][0] *= u->H[j];
         u->output_window[j][1] *= u->H[j];
     }
@@ -702,6 +708,22 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s
     }
 }
 
+void save_state(struct userdata *u){
+    const float *H = u->Hs[pa_aupdate_read_begin(u->a_H)];
+    float *H_n = pa_xmalloc((u->fft_size / 2 + 1) * sizeof(float));
+    for(size_t i = 0 ; i <= u->fft_size / 2 + 1; ++i){
+        H_n[i] = H[i] * u->fft_size;
+    }
+    pa_aupdate_read_end(u->a_H);
+    pa_datum key, data;
+    key.data = (char *) COEFKEY;
+    key.size = strlen(key.data);
+    data.data = H_n;
+    data.size = (u->fft_size / 2 + 1) * sizeof(float);
+    pa_database_set(u->database, &key, &data, TRUE);
+    pa_database_sync(u->database);
+}
+
 /* Called from main context */
 static pa_bool_t sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) {
     struct userdata *u;
@@ -802,62 +824,6 @@ int pa__init(pa_module*m) {
 
     hanning_window(u->W, u->window_size);
 
-    unsigned H_i = pa_aupdate_write_begin(u->a_H);
-    u->H = u->Hs[H_i];
-    for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){
-        u->H[i] = 1.0;
-    }
-
-    //TODO cut this out and leave it for the client side
-    //const int freqs[] = {0,25,50,100,200,300,400,800,1500,
-    //    2000,3000,4000,5000,6000,7000,8000,9000,10000,11000,12000,
-    //    13000,14000,15000,16000,17000,18000,19000,20000,21000,22000,23000,24000,INT_MAX};
-    //const float coefficients[] = {1,1,1,1,1,1,1,1,1,1,
-    //    1,1,1,1,1,1,1,1,
-    //    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
-    //const size_t ncoefficients = sizeof(coefficients)/sizeof(float);
-    //pa_assert_se(sizeof(freqs)/sizeof(int)==sizeof(coefficients)/sizeof(float));
-    //float *freq_translated = (float *) pa_xmalloc0(sizeof(float)*(ncoefficients));
-    //freq_translated[0] = 1;
-    ////Translate the frequencies in their natural sampling rate to the new sampling rate frequencies
-    //for(size_t i = 1; i < ncoefficients-1; ++i){
-    //    freq_translated[i] = ((float)freqs[i]*u->fft_size)/ss.rate;
-    //    //pa_log("i: %ld: %d , %g",i, freqs[i], freq_translated[i]);
-    //    pa_assert_se(freq_translated[i] >= freq_translated[i-1]);
-    //}
-    //freq_translated[ncoefficients-1] = FLT_MAX;
-    //
-    ////Interpolate the specified frequency band values
-    //u->H[0] = 1;
-    //for(size_t i = 1, j = 0; i < (u->fft_size / 2 + 1); ++i){
-    //    pa_assert_se(j < ncoefficients);
-    //    //max frequency range passed, consider the rest as one band
-    //    if(freq_translated[j+1] >= FLT_MAX){
-    //        for(; i < (u->fft_size / 2 + 1); ++i){
-    //            u->H[i] = coefficients[j];
-    //        }
-    //        break;
-    //    }
-    //    //pa_log("i: %d, j: %d, freq: %f", i, j, freq_translated[j]);
-    //    //pa_log("interp: %0.4f %0.4f", freq_translated[j], freq_translated[j+1]);
-    //    pa_assert_se(freq_translated[j] < freq_translated[j+1]);
-    //    pa_assert_se(i >= freq_translated[j]);
-    //    pa_assert_se(i <= freq_translated[j+1]);
-    //    //bilinear-inerpolation of coefficients specified
-    //    float c0 = (i-freq_translated[j])/(freq_translated[j+1]-freq_translated[j]);
-    //    pa_assert_se(c0 >= 0&&c0 <= 1.0);
-    //    u->H[i] = ((1.0f-c0)*coefficients[j]+c0*coefficients[j+1]);
-    //    pa_assert_se(u->H[i]>0);
-    //    while(i >= floor(freq_translated[j+1])){
-    //        j++;
-    //    }
-    //}
-    //pa_xfree(freq_translated);
-    fix_filter(u->H, u->fft_size);
-    pa_aupdate_write_swap(u->a_H);
-    pa_aupdate_write_end(u->a_H);
-
-
     /* Create sink */
     pa_sink_new_data_init(&sink_data);
     sink_data.driver = __FILE__;
@@ -932,6 +898,82 @@ int pa__init(pa_module*m) {
 
     pa_xfree(use_default);
 
+    char *dbname;
+    char *pref = pa_sprintf_malloc("equalizer-%s-state", u->sink->name);
+    pa_assert_se(dbname = pa_state_path(pref, TRUE));
+    pa_xfree(pref);
+    pa_assert_se(u->database = pa_database_open(dbname, TRUE));
+    pa_xfree(dbname);
+
+    unsigned H_i = pa_aupdate_write_begin(u->a_H);
+    u->H = u->Hs[H_i];
+    for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){
+        u->H[i] = 1.0;
+    }
+
+    //TODO cut this out and leave it for the client side
+    //const int freqs[] = {0,25,50,100,200,300,400,800,1500,
+    //    2000,3000,4000,5000,6000,7000,8000,9000,10000,11000,12000,
+    //    13000,14000,15000,16000,17000,18000,19000,20000,21000,22000,23000,24000,INT_MAX};
+    //const float coefficients[] = {1,1,1,1,1,1,1,1,1,1,
+    //    1,1,1,1,1,1,1,1,
+    //    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
+    //const size_t ncoefficients = sizeof(coefficients)/sizeof(float);
+    //pa_assert_se(sizeof(freqs)/sizeof(int)==sizeof(coefficients)/sizeof(float));
+    //float *freq_translated = (float *) pa_xmalloc0(sizeof(float)*(ncoefficients));
+    //freq_translated[0] = 1;
+    ////Translate the frequencies in their natural sampling rate to the new sampling rate frequencies
+    //for(size_t i = 1; i < ncoefficients-1; ++i){
+    //    freq_translated[i] = ((float)freqs[i]*u->fft_size)/ss.rate;
+    //    //pa_log("i: %ld: %d , %g",i, freqs[i], freq_translated[i]);
+    //    pa_assert_se(freq_translated[i] >= freq_translated[i-1]);
+    //}
+    //freq_translated[ncoefficients-1] = FLT_MAX;
+    //
+    ////Interpolate the specified frequency band values
+    //u->H[0] = 1;
+    //for(size_t i = 1, j = 0; i < (u->fft_size / 2 + 1); ++i){
+    //    pa_assert_se(j < ncoefficients);
+    //    //max frequency range passed, consider the rest as one band
+    //    if(freq_translated[j+1] >= FLT_MAX){
+    //        for(; i < (u->fft_size / 2 + 1); ++i){
+    //            u->H[i] = coefficients[j];
+    //        }
+    //        break;
+    //    }
+    //    //pa_log("i: %d, j: %d, freq: %f", i, j, freq_translated[j]);
+    //    //pa_log("interp: %0.4f %0.4f", freq_translated[j], freq_translated[j+1]);
+    //    pa_assert_se(freq_translated[j] < freq_translated[j+1]);
+    //    pa_assert_se(i >= freq_translated[j]);
+    //    pa_assert_se(i <= freq_translated[j+1]);
+    //    //bilinear-inerpolation of coefficients specified
+    //    float c0 = (i-freq_translated[j])/(freq_translated[j+1]-freq_translated[j]);
+    //    pa_assert_se(c0 >= 0&&c0 <= 1.0);
+    //    u->H[i] = ((1.0f-c0)*coefficients[j]+c0*coefficients[j+1]);
+    //    pa_assert_se(u->H[i]>0);
+    //    while(i >= floor(freq_translated[j+1])){
+    //        j++;
+    //    }
+    //}
+    //pa_xfree(freq_translated);
+
+    //load old parameters
+    pa_datum key,value;
+    key.data = (char *) COEFKEY;
+    key.size = strlen(key.data);
+    if (pa_database_get(u->database, &key, &value) != NULL){
+        if(value.size == (u->fft_size / 2 + 1) * sizeof(float)){
+            memcpy(u->H, value.data, (u->fft_size / 2 + 1) * sizeof(float));
+        }
+        pa_datum_free(&value);
+    }
+
+    fix_filter(u->H, u->fft_size);
+    pa_aupdate_write_swap(u->a_H);
+    pa_aupdate_write_end(u->a_H);
+
+
+
     dbus_init(u);
 
     return 0;
@@ -963,6 +1005,10 @@ void pa__done(pa_module*m) {
 
     if (!(u = m->userdata))
         return;
+
+    save_state(u);
+    pa_database_close(u->database);
+
     dbus_done(u);
 
     if (u->sink) {
@@ -1000,14 +1046,18 @@ void pa__done(pa_module*m) {
 }
 
 enum property_handler_index {
+    PROPERTY_HANDLER_SAMPLERATE,
+    PROPERTY_HANDLER_FILTERSAMPLERATE,
     PROPERTY_HANDLER_N_COEFS,
     PROPERTY_HANDLER_COEFS,
     PROPERTY_HANDLER_MAX
 };
 
 static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX]={
-    [PROPERTY_HANDLER_N_COEFS]{.property_name="n_filter_coefficients",.type="u",.get_cb=get_n_coefs,.set_cb=NULL},
-    [PROPERTY_HANDLER_COEFS]{.property_name="filter_coefficients",.type="ai",.get_cb=get_filter,.set_cb=set_filter}
+    [PROPERTY_HANDLER_SAMPLERATE]{.property_name="SampleRate",.type="u",.get_cb=get_sample_rate,.set_cb=NULL},
+    [PROPERTY_HANDLER_FILTERSAMPLERATE]{.property_name="FilterSampleRate",.type="u",.get_cb=get_filter_rate,.set_cb=NULL},
+    [PROPERTY_HANDLER_N_COEFS]{.property_name="NFilterCoefficients",.type="u",.get_cb=get_n_coefs,.set_cb=NULL},
+    [PROPERTY_HANDLER_COEFS]{.property_name="FilterCoefficients",.type="ai",.get_cb=get_filter,.set_cb=set_filter}
 };
 
 //static pa_dbus_arg_info new_equalizer_args[] = { { "path","o",NULL} };
@@ -1039,7 +1089,7 @@ void dbus_init(struct userdata *u){
 
 void dbus_done(struct userdata *u){
     pa_dbus_protocol_unregister_extension(u->dbus_protocol, EXTNAME);
-    pa_dbus_protocol_remove_interface(u->dbus_protocol, u->dbus_path, EXTNAME);
+    pa_dbus_protocol_remove_interface(u->dbus_protocol, u->dbus_path, interface_info.name);
     
     pa_xfree(u->dbus_path);
     pa_dbus_protocol_unref(u->dbus_protocol);
@@ -1056,23 +1106,41 @@ void get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u){
     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &n_coefs);
 }
 
+void get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u){
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(_u);
+
+    struct userdata *u=(struct userdata *) _u;
+    uint32_t rate=(uint32_t) u->sink->sample_spec.rate;
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &rate);
+}
+void get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u){
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(_u);
+
+    struct userdata *u=(struct userdata *) _u;
+    uint32_t fft_size=(uint32_t) u->fft_size;
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &fft_size);
+}
+
 void get_filter(DBusConnection *conn, DBusMessage *msg, void *_u){
     pa_assert(conn);
     pa_assert(msg);
     pa_assert(_u);
 
-    struct userdata *u=(struct userdata *)_u;
+    struct userdata *u = (struct userdata *)_u;
     
-    unsigned n_coefs=(unsigned)(u->fft_size / 2 + 1);
-    double *H_=(double *)pa_xmalloc0(n_coefs*sizeof(double));
+    unsigned n_coefs = (unsigned) (u->fft_size / 2 + 1);
+    double *H_ = (double *) pa_xmalloc0(n_coefs * sizeof(double));
     
-    unsigned H_i=pa_aupdate_read_begin(u->a_H);
-    float *H=u->Hs[H_i];
+    float *H=u->Hs[pa_aupdate_read_begin(u->a_H)];
     for(size_t i = 0;i < u->fft_size / 2 + 1; ++i){
-        H_[i]=H[i];
+        H_[i] = H[i] * u->fft_size;
     }
     pa_aupdate_read_end(u->a_H);
-    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_DOUBLE, &H_, n_coefs);
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_DOUBLE, H_, n_coefs);
     pa_xfree(H_);
 }
 
@@ -1089,14 +1157,17 @@ void set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){
         pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "This filter takes exactly %ld coefficients, you gave %d", u->fft_size / 2 + 1, _n_coefs);
         return;
     }
-    unsigned H_i = pa_aupdate_write_begin(u->a_H);
-    float *H = u->Hs[H_i];
+    float *H = u->Hs[pa_aupdate_write_begin(u->a_H)];
     for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){
-        H[i] = (float)H_[i];
+        H[i] = (float) H_[i];
     }
+    fix_filter(H, u->fft_size);
     pa_aupdate_write_swap(u->a_H);
     pa_aupdate_write_end(u->a_H);
 
+    //Stupid for IO reasons?  Add a save signal to dbus instead
+    save_state(u);
+
     pa_dbus_send_empty_reply(conn, msg);
 }
 
@@ -1109,7 +1180,9 @@ void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){
     DBusMessage *reply = NULL;
     DBusMessageIter msg_iter, dict_iter;
 
-    int n_coefs=(unsigned)(u->fft_size / 2 + 1);
+    uint32_t rate=(uint32_t) u->sink->sample_spec.rate;
+    uint32_t fft_size=(uint32_t) u->fft_size;
+    uint32_t n_coefs=(uint32_t)(u->fft_size / 2 + 1);
     double *H_=(double *)pa_xmalloc0(n_coefs*sizeof(double));
     
     unsigned H_i=pa_aupdate_read_begin(u->a_H);
@@ -1123,6 +1196,8 @@ void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){
     dbus_message_iter_init_append(reply, &msg_iter);
     pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
 
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLERATE].property_name, DBUS_TYPE_UINT32, &rate);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_FILTERSAMPLERATE].property_name, DBUS_TYPE_UINT32, &fft_size);
     pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_N_COEFS].property_name, DBUS_TYPE_UINT32, &n_coefs);
     pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_COEFS].property_name, DBUS_TYPE_DOUBLE, H_, n_coefs);
 

commit 144f1c4f31fe3cde73a0d9ea08d2ae34cc24bdf6
Author: Jason Newton <nevion at gmail.com>
Date:   Wed Aug 5 00:52:16 2009 -0700

    module-equalizer-sink:
        dbus properties and manager so that multiple sinks can be loaded and mixers can be equalizer-sink aware
        functionality to seed new filters quickly (rteq guis)
        profile support
        extra checking in client->server dbus messages

diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
index 6ea5951..285fddb 100755
--- a/src/modules/module-equalizer-sink.c
+++ b/src/modules/module-equalizer-sink.c
@@ -50,6 +50,8 @@ USA.
 #include <pulsecore/thread-mq.h>
 #include <pulsecore/rtpoll.h>
 #include <pulsecore/sample-util.h>
+#include <pulsecore/shared.h>
+#include <pulsecore/idxset.h>
 #include <pulsecore/database.h>
 #include <pulsecore/protocol-dbus.h>
 #include <pulsecore/dbus-util.h>
@@ -82,6 +84,7 @@ struct userdata {
     pa_module *module;
     pa_sink *sink, *master;
     pa_sink_input *sink_input;
+    char *name;
 
     size_t channels;
     size_t fft_size;//length (res) of fft
@@ -131,9 +134,17 @@ static const char* const valid_modargs[] = {
 
 static uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p);
 static void hanning_window(float *W, size_t window_size);
+void fix_filter(float *H, size_t fft_size);
+void interpolate(float *signal, size_t length, uint32_t *xs, float *ys, size_t n_points);
 static void array_out(const char *name, float *a, size_t length);
+static int is_monotonic(uint32_t *xs, size_t length);
 static void process_samples(struct userdata *u);
 static void input_buffer(struct userdata *u, pa_memchunk *in);
+static void save_profile(struct userdata *u,char *name);
+static void save_state(struct userdata *u);
+static void remove_profile(pa_core *u,char *name);
+static const char * load_profile(struct userdata *u,char *name);
+static void load_state(struct userdata *u);
 
 void dsp_logic(
     float * __restrict__ dst,
@@ -144,21 +155,37 @@ void dsp_logic(
     fftwf_complex * __restrict__ output_window,
     struct userdata *u);
 
+
+/*
+ * DBus Routines and Callbacks
+ */
+#define EXTNAME "org.PulseAudio.Ext.Equalizing1"
+#define MANAGER_PATH "/org/pulseaudio/equalizing1"
 static void dbus_init(struct userdata *u);
 static void dbus_done(struct userdata *u);
-static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *_u);
-void get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u);
-void get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u);
-static void get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u);
-static void get_filter(DBusConnection *conn, DBusMessage *msg, void *_u);
-static void set_filter(DBusConnection *conn, DBusMessage *msg, void *_u);
-static void save_state(struct userdata *u);
+static void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u);
 
 #define v_size 4
 #define gettime(x) clock_gettime(CLOCK_MONOTONIC, &x)
 #define tdiff(x, y) time_diff(&x, &y)
 #define mround(x, y) (x % y == 0 ? x : ( x / y + 1) * y)
-#define COEFKEY "coefficients"
+#define SINKLIST "equalized_sinklist"
+#define EQDB "equalizer_db"
 
 uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p)
 {
@@ -166,20 +193,43 @@ uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p)
     ((timeB_p->tv_sec * 1000000000ULL) + timeB_p->tv_nsec);
 }
 
-static void hanning_window(float *W, size_t window_size){
+void hanning_window(float *W, size_t window_size){
     //h=.5*(1-cos(2*pi*j/(window_size+1)), COLA for R=(M+1)/2
     for(size_t i=0; i < window_size;++i){
         W[i] = (float).5*(1-cos(2*M_PI*i/(window_size+1)));
     }
 }
 
-static void fix_filter(float *H, size_t fft_size){
+void fix_filter(float *H, size_t fft_size){
     //divide out the fft gain
-    for(size_t i = 0; i < (fft_size / 2 + 1); ++i){
+    for(size_t i = 0; i < fft_size / 2 + 1; ++i){
         H[i] /= fft_size;
     }
 }
 
+void interpolate(float *signal, size_t length, uint32_t *xs, float *ys, size_t n_points){
+    //Note that xs must be monotonically increasing!
+    pa_assert_se(n_points>=2);
+    pa_assert_se(xs[0] == 0);
+    pa_assert_se(xs[n_points - 1] == length - 1);
+    for(size_t x = 0, x_range_lower_i = 0; x < length-1; ++x){
+        pa_assert(x_range_lower_i < n_points-1);
+        float x_range_lower = (float) (xs[x_range_lower_i]);
+        float x_range_upper = (float) (xs[x_range_lower_i+1]);
+        pa_assert_se(x_range_lower < x_range_upper);
+        pa_assert_se(x >= x_range_lower);
+        pa_assert_se(x <= x_range_upper);
+        //bilinear-interpolation of coefficients specified
+        float c0 = (x-x_range_lower)/(x_range_upper-x_range_lower);
+        pa_assert_se(c0 >= 0&&c0 <= 1.0);
+        signal[x] = ((1.0f - c0) * ys[x_range_lower_i] + c0 * ys[x_range_lower_i + 1]);
+        while(x >= xs[x_range_lower_i + 1]){
+            x_range_lower_i++;
+        }
+    }
+    signal[length-1]=ys[n_points-1];
+}
+
 void array_out(const char *name, float *a, size_t length){
     FILE *p=fopen(name, "w");
     if(!p){
@@ -195,6 +245,17 @@ void array_out(const char *name, float *a, size_t length){
     fprintf(p, "\n");
     fclose(p);
 }
+static int is_monotonic(uint32_t *xs,size_t length){
+    if(length<2){
+        return 1;
+    }
+    for(size_t i = 1; i < length; ++i){
+        if(xs[i]<=xs[i-1]){
+            return 0;
+        }
+    }
+    return 1;
+}
 
 
 /* Called from I/O thread context */
@@ -708,15 +769,16 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s
     }
 }
 
-void save_state(struct userdata *u){
-    const float *H = u->Hs[pa_aupdate_read_begin(u->a_H)];
+void save_profile(struct userdata *u, char *name){
     float *H_n = pa_xmalloc((u->fft_size / 2 + 1) * sizeof(float));
+    const float *H = u->Hs[pa_aupdate_read_begin(u->a_H)];
     for(size_t i = 0 ; i <= u->fft_size / 2 + 1; ++i){
-        H_n[i] = H[i] * u->fft_size;
+        //H_n[i] = H[i] * u->fft_size;
+        H_n[i] = H[i];
     }
     pa_aupdate_read_end(u->a_H);
     pa_datum key, data;
-    key.data = (char *) COEFKEY;
+    key.data=name;
     key.size = strlen(key.data);
     data.data = H_n;
     data.size = (u->fft_size / 2 + 1) * sizeof(float);
@@ -724,6 +786,49 @@ void save_state(struct userdata *u){
     pa_database_sync(u->database);
 }
 
+void save_state(struct userdata *u){
+    char *state_name = pa_sprintf_malloc("%s-previous-state", u->name);
+    save_profile(u, state_name);
+    pa_xfree(state_name);
+}
+
+void remove_profile(pa_core *c,char *name){
+    pa_datum key;
+    key.data = name;
+    key.size = strlen(key.data);
+    pa_database *database;
+    pa_assert_se(database = pa_shared_get(c,EQDB));
+    pa_database_unset(database,&key);
+    pa_database_sync(database);
+}
+
+const char* load_profile(struct userdata *u,char *name){
+    pa_datum key,value;
+    key.data = name;
+    key.size = strlen(key.data);
+    if(pa_database_get(u->database, &key, &value) != NULL){
+        if(value.size == (u->fft_size / 2 + 1) * sizeof(float)){
+            float *H=u->Hs[pa_aupdate_write_begin(u->a_H)];
+            memcpy(H, value.data, value.size);
+            pa_aupdate_write_swap(u->a_H);
+            pa_aupdate_write_end(u->a_H);
+        }else{
+            return "incompatible size";
+        }
+        pa_datum_free(&value);
+    }else{
+        return "profile doesn't exist";
+    }
+    return NULL;
+    //fix_filter(u->H, u->fft_size);
+}
+void load_state(struct userdata *u){
+    char *state_name=pa_sprintf_malloc("%s-previous-state", u->name);
+    load_profile(u,state_name);
+    pa_xfree(state_name);
+}
+
+
 /* Called from main context */
 static pa_bool_t sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) {
     struct userdata *u;
@@ -791,7 +896,7 @@ int pa__init(pa_module*m) {
     u->channels = ss.channels;
     u->fft_size = pow(2, ceil(log(ss.rate)/log(2)));
     pa_log("fft size: %ld", u->fft_size);
-    u->window_size = 7999;
+    u->window_size = 15999;
     u->R = (u->window_size+1)/2;
     u->overlap_size = u->window_size-u->R;
     u->target_samples = 1*u->R;
@@ -851,7 +956,7 @@ int pa__init(pa_module*m) {
         pa_log("Failed to create sink.");
         goto fail;
     }
-
+    u->name=pa_xstrdup(u->sink->name);
     u->sink->parent.process_msg = sink_process_msg;
     u->sink->set_state = sink_set_state;
     u->sink->update_requested_latency = sink_update_requested_latency;
@@ -898,83 +1003,14 @@ int pa__init(pa_module*m) {
 
     pa_xfree(use_default);
 
-    char *dbname;
-    char *pref = pa_sprintf_malloc("equalizer-%s-state", u->sink->name);
-    pa_assert_se(dbname = pa_state_path(pref, TRUE));
-    pa_xfree(pref);
-    pa_assert_se(u->database = pa_database_open(dbname, TRUE));
-    pa_xfree(dbname);
+    dbus_init(u);
 
-    unsigned H_i = pa_aupdate_write_begin(u->a_H);
-    u->H = u->Hs[H_i];
+    //default filter to these
     for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){
-        u->H[i] = 1.0;
+        u->Hs[1][i]=u->Hs[0][i] = 1.0;
     }
-
-    //TODO cut this out and leave it for the client side
-    //const int freqs[] = {0,25,50,100,200,300,400,800,1500,
-    //    2000,3000,4000,5000,6000,7000,8000,9000,10000,11000,12000,
-    //    13000,14000,15000,16000,17000,18000,19000,20000,21000,22000,23000,24000,INT_MAX};
-    //const float coefficients[] = {1,1,1,1,1,1,1,1,1,1,
-    //    1,1,1,1,1,1,1,1,
-    //    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
-    //const size_t ncoefficients = sizeof(coefficients)/sizeof(float);
-    //pa_assert_se(sizeof(freqs)/sizeof(int)==sizeof(coefficients)/sizeof(float));
-    //float *freq_translated = (float *) pa_xmalloc0(sizeof(float)*(ncoefficients));
-    //freq_translated[0] = 1;
-    ////Translate the frequencies in their natural sampling rate to the new sampling rate frequencies
-    //for(size_t i = 1; i < ncoefficients-1; ++i){
-    //    freq_translated[i] = ((float)freqs[i]*u->fft_size)/ss.rate;
-    //    //pa_log("i: %ld: %d , %g",i, freqs[i], freq_translated[i]);
-    //    pa_assert_se(freq_translated[i] >= freq_translated[i-1]);
-    //}
-    //freq_translated[ncoefficients-1] = FLT_MAX;
-    //
-    ////Interpolate the specified frequency band values
-    //u->H[0] = 1;
-    //for(size_t i = 1, j = 0; i < (u->fft_size / 2 + 1); ++i){
-    //    pa_assert_se(j < ncoefficients);
-    //    //max frequency range passed, consider the rest as one band
-    //    if(freq_translated[j+1] >= FLT_MAX){
-    //        for(; i < (u->fft_size / 2 + 1); ++i){
-    //            u->H[i] = coefficients[j];
-    //        }
-    //        break;
-    //    }
-    //    //pa_log("i: %d, j: %d, freq: %f", i, j, freq_translated[j]);
-    //    //pa_log("interp: %0.4f %0.4f", freq_translated[j], freq_translated[j+1]);
-    //    pa_assert_se(freq_translated[j] < freq_translated[j+1]);
-    //    pa_assert_se(i >= freq_translated[j]);
-    //    pa_assert_se(i <= freq_translated[j+1]);
-    //    //bilinear-inerpolation of coefficients specified
-    //    float c0 = (i-freq_translated[j])/(freq_translated[j+1]-freq_translated[j]);
-    //    pa_assert_se(c0 >= 0&&c0 <= 1.0);
-    //    u->H[i] = ((1.0f-c0)*coefficients[j]+c0*coefficients[j+1]);
-    //    pa_assert_se(u->H[i]>0);
-    //    while(i >= floor(freq_translated[j+1])){
-    //        j++;
-    //    }
-    //}
-    //pa_xfree(freq_translated);
-
     //load old parameters
-    pa_datum key,value;
-    key.data = (char *) COEFKEY;
-    key.size = strlen(key.data);
-    if (pa_database_get(u->database, &key, &value) != NULL){
-        if(value.size == (u->fft_size / 2 + 1) * sizeof(float)){
-            memcpy(u->H, value.data, (u->fft_size / 2 + 1) * sizeof(float));
-        }
-        pa_datum_free(&value);
-    }
-
-    fix_filter(u->H, u->fft_size);
-    pa_aupdate_write_swap(u->a_H);
-    pa_aupdate_write_end(u->a_H);
-
-
-
-    dbus_init(u);
+    load_state(u);
 
     return 0;
 
@@ -1007,7 +1043,6 @@ void pa__done(pa_module*m) {
         return;
 
     save_state(u);
-    pa_database_close(u->database);
 
     dbus_done(u);
 
@@ -1042,22 +1077,130 @@ void pa__done(pa_module*m) {
         pa_xfree(u->Hs[i]);
     }
 
+    pa_xfree(u->name);
+
     pa_xfree(u);
 }
 
-enum property_handler_index {
-    PROPERTY_HANDLER_SAMPLERATE,
-    PROPERTY_HANDLER_FILTERSAMPLERATE,
-    PROPERTY_HANDLER_N_COEFS,
-    PROPERTY_HANDLER_COEFS,
-    PROPERTY_HANDLER_MAX
+enum manager_method_index {
+    MANAGER_METHOD_REMOVE_PROFILE,
+    MANAGER_METHOD_MAX
+};
+
+pa_dbus_arg_info remove_profile_args[]={
+    {"name", "s","in"},
+};
+
+static pa_dbus_method_handler manager_methods[MANAGER_METHOD_MAX]={
+    [MANAGER_METHOD_REMOVE_PROFILE]{
+        .method_name="RemoveProfile",
+        .arguments=remove_profile_args,
+        .n_arguments=sizeof(remove_profile_args)/sizeof(pa_dbus_arg_info),
+        .receive_cb=manager_handle_remove_profile}
+};
+
+enum manager_handler_index {
+    MANAGER_HANDLER_REVISION,
+    MANAGER_HANDLER_EQUALIZED_SINKS,
+    MANAGER_HANDLER_PROFILES,
+    MANAGER_HANDLER_MAX
+};
+
+static pa_dbus_property_handler manager_handlers[MANAGER_HANDLER_MAX]={
+    [MANAGER_HANDLER_REVISION]={.property_name="InterfaceRevision",.type="u",.get_cb=manager_get_revision,.set_cb=NULL},
+    [MANAGER_HANDLER_EQUALIZED_SINKS]={.property_name="EqualizedSinks",.type="ao",.get_cb=manager_get_sinks,.set_cb=NULL},
+    [MANAGER_HANDLER_PROFILES]={.property_name="Profiles",.type="as",.get_cb=manager_get_profiles,.set_cb=NULL}
+};
+
+static pa_dbus_interface_info manager_info={
+    .name=EXTNAME ".Manager",
+    .method_handlers=manager_methods,
+    .n_method_handlers=MANAGER_METHOD_MAX,
+    .property_handlers=manager_handlers,
+    .n_property_handlers=MANAGER_HANDLER_MAX,
+    .get_all_properties_cb=manager_get_all,
+    .signals=NULL,
+    .n_signals=0
+};
+
+
+enum equalizer_method_index {
+    EQUALIZER_METHOD_FILTER_POINTS,
+    EQUALIZER_METHOD_SEED_FILTER,
+    EQUALIZER_METHOD_SAVE_PROFILE,
+    EQUALIZER_METHOD_LOAD_PROFILE,
+    EQUALIZER_METHOD_SET_FILTER,
+    EQUALIZER_METHOD_GET_FILTER,
+    EQUALIZER_METHOD_MAX
+};
+
+enum equalizer_handler_index {
+    EQUALIZER_HANDLER_REVISION,
+    EQUALIZER_HANDLER_SAMPLERATE,
+    EQUALIZER_HANDLER_FILTERSAMPLERATE,
+    EQUALIZER_HANDLER_N_COEFS,
+    EQUALIZER_HANDLER_MAX
+};
+
+pa_dbus_arg_info filter_points_args[]={
+    {"xs", "au","in"},
+    {"ys", "ad","out"},
+};
+pa_dbus_arg_info seed_filter_args[]={
+    {"xs", "au","in"},
+    {"ys", "ad","in"},
+};
+pa_dbus_arg_info save_profile_args[]={
+    {"name", "s","in"},
+};
+pa_dbus_arg_info load_profile_args[]={
+    {"name", "s","in"},
+};
+pa_dbus_arg_info set_filter_args[]={
+    {"coefficients", "ad","in"},
+};
+pa_dbus_arg_info get_filter_args[]={
+    {"coefficients", "ad","out"},
+};
+
+static pa_dbus_method_handler equalizer_methods[EQUALIZER_METHOD_MAX]={
+    [EQUALIZER_METHOD_SEED_FILTER]{
+        .method_name="SeedFilter",
+        .arguments=seed_filter_args,
+        .n_arguments=sizeof(seed_filter_args)/sizeof(pa_dbus_arg_info),
+        .receive_cb=equalizer_handle_seed_filter},
+    [EQUALIZER_METHOD_FILTER_POINTS]{
+        .method_name="FilterAtPoints",
+        .arguments=filter_points_args,
+        .n_arguments=sizeof(filter_points_args)/sizeof(pa_dbus_arg_info),
+        .receive_cb=equalizer_handle_get_filter_points},
+    [EQUALIZER_METHOD_SAVE_PROFILE]{
+        .method_name="SaveProfile",
+        .arguments=save_profile_args,
+        .n_arguments=sizeof(save_profile_args)/sizeof(pa_dbus_arg_info),
+        .receive_cb=equalizer_handle_save_profile},
+    [EQUALIZER_METHOD_LOAD_PROFILE]{
+        .method_name="LoadProfile",
+        .arguments=load_profile_args,
+        .n_arguments=sizeof(load_profile_args)/sizeof(pa_dbus_arg_info),
+        .receive_cb=equalizer_handle_load_profile},
+    [EQUALIZER_METHOD_SET_FILTER]{
+        .method_name="SetFilterCoefficients",
+        .arguments=set_filter_args,
+        .n_arguments=sizeof(set_filter_args)/sizeof(pa_dbus_arg_info),
+        .receive_cb=equalizer_set_filter},
+    [EQUALIZER_METHOD_GET_FILTER]{
+        .method_name="GetFilterCoefficients",
+        .arguments=get_filter_args,
+        .n_arguments=sizeof(get_filter_args)/sizeof(pa_dbus_arg_info),
+        .receive_cb=equalizer_get_filter}
 };
 
-static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX]={
-    [PROPERTY_HANDLER_SAMPLERATE]{.property_name="SampleRate",.type="u",.get_cb=get_sample_rate,.set_cb=NULL},
-    [PROPERTY_HANDLER_FILTERSAMPLERATE]{.property_name="FilterSampleRate",.type="u",.get_cb=get_filter_rate,.set_cb=NULL},
-    [PROPERTY_HANDLER_N_COEFS]{.property_name="NFilterCoefficients",.type="u",.get_cb=get_n_coefs,.set_cb=NULL},
-    [PROPERTY_HANDLER_COEFS]{.property_name="FilterCoefficients",.type="ai",.get_cb=get_filter,.set_cb=set_filter}
+static pa_dbus_property_handler equalizer_handlers[EQUALIZER_HANDLER_MAX]={
+    [EQUALIZER_HANDLER_REVISION]={.property_name="InterfaceRevision",.type="u",.get_cb=equalizer_get_revision,.set_cb=NULL},
+    [EQUALIZER_HANDLER_SAMPLERATE]{.property_name="SampleRate",.type="u",.get_cb=equalizer_get_sample_rate,.set_cb=NULL},
+    [EQUALIZER_HANDLER_FILTERSAMPLERATE]{.property_name="FilterSampleRate",.type="u",.get_cb=equalizer_get_filter_rate,.set_cb=NULL},
+    [EQUALIZER_HANDLER_N_COEFS]{.property_name="NFilterCoefficients",.type="u",.get_cb=equalizer_get_n_coefs,.set_cb=NULL},
 };
 
 //static pa_dbus_arg_info new_equalizer_args[] = { { "path","o",NULL} };
@@ -1065,37 +1208,354 @@ static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX]={
 //    [SIGNAL_NEW_EQUALIZER]={.name="NewEqualizer",.arguments=new_equalizer_args,.n_arguments=1}
 //};
 
-#define EXTNAME "org.PulseAudio.Ext.Equalizing1"
-
-static pa_dbus_interface_info interface_info={
+static pa_dbus_interface_info equalizer_info={
     .name=EXTNAME ".Equalizer",
-    .method_handlers=NULL,
-    .n_method_handlers=0,
-    .property_handlers=property_handlers,
-    .n_property_handlers=PROPERTY_HANDLER_MAX,
-    .get_all_properties_cb=handle_get_all,
+    .method_handlers=equalizer_methods,
+    .n_method_handlers=EQUALIZER_METHOD_MAX,
+    .property_handlers=equalizer_handlers,
+    .n_property_handlers=EQUALIZER_HANDLER_MAX,
+    .get_all_properties_cb=equalizer_get_all,
     .signals=NULL,
     .n_signals=0
 };
 
-
 void dbus_init(struct userdata *u){
     u->dbus_protocol=pa_dbus_protocol_get(u->core);
     u->dbus_path=pa_sprintf_malloc("/org/pulseaudio/core1/sink%d", u->sink->index);
 
-    pa_dbus_protocol_add_interface(u->dbus_protocol, u->dbus_path, &interface_info, u);
-    pa_dbus_protocol_register_extension(u->dbus_protocol, EXTNAME);
+    pa_dbus_protocol_add_interface(u->dbus_protocol, u->dbus_path, &equalizer_info, u);
+    pa_idxset *sink_list=pa_shared_get(u->core,SINKLIST);
+    u->database=pa_shared_get(u->core,EQDB);
+    if(sink_list==NULL){
+        sink_list=pa_idxset_new(&pa_idxset_trivial_hash_func, &pa_idxset_trivial_compare_func);
+        pa_shared_set(u->core, SINKLIST, sink_list);
+        char *dbname;
+        pa_assert_se(dbname = pa_state_path("equalizers", TRUE));
+        pa_assert_se(u->database = pa_database_open(dbname, TRUE));
+        pa_xfree(dbname);
+        pa_shared_set(u->core,EQDB,u->database);
+        pa_dbus_protocol_add_interface(u->dbus_protocol, MANAGER_PATH, &manager_info, u->core);
+        pa_dbus_protocol_register_extension(u->dbus_protocol, EXTNAME);
+    }
+    uint32_t dummy;
+    pa_idxset_put(sink_list,u,&dummy);
 }
 
 void dbus_done(struct userdata *u){
-    pa_dbus_protocol_unregister_extension(u->dbus_protocol, EXTNAME);
-    pa_dbus_protocol_remove_interface(u->dbus_protocol, u->dbus_path, interface_info.name);
-    
+    pa_idxset *sink_list;
+    uint32_t dummy;
+
+    pa_assert_se(sink_list=pa_shared_get(u->core,SINKLIST));
+    pa_idxset_remove_by_data(sink_list,u,&dummy);
+    if(pa_idxset_size(sink_list)==0){
+        pa_dbus_protocol_unregister_extension(u->dbus_protocol, EXTNAME);
+        pa_dbus_protocol_remove_interface(u->dbus_protocol, MANAGER_PATH, manager_info.name);
+        pa_shared_remove(u->core, EQDB);
+        pa_database_close(u->database);
+        pa_shared_remove(u->core, SINKLIST);
+        pa_xfree(sink_list);
+    }
+    pa_dbus_protocol_remove_interface(u->dbus_protocol, u->dbus_path, equalizer_info.name);
     pa_xfree(u->dbus_path);
     pa_dbus_protocol_unref(u->dbus_protocol);
 }
 
-void get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u){
+void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void *_u) {
+    DBusError error;
+    pa_core *c = (pa_core *)_u;
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(c);
+    dbus_error_init(&error);
+    char *name;
+    if(!dbus_message_get_args(msg, &error,
+                 DBUS_TYPE_STRING, &name,
+                DBUS_TYPE_INVALID)){
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
+        dbus_error_free(&error);
+        return;
+    }
+    remove_profile(c,name);
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){
+    uint32_t rev=1;
+    pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_UINT32, &rev);
+}
+
+void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u){
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(_u);
+
+    pa_core *c = (pa_core *)_u;
+    pa_idxset *sink_list;
+    uint32_t dummy;
+
+    pa_assert_se(sink_list = pa_shared_get(c, SINKLIST));
+    unsigned n_sinks = (unsigned) pa_idxset_size(sink_list);
+    char **names = NULL;
+    void *iter = NULL;
+    struct userdata *sink_u = NULL;
+    pa_assert_se(names = pa_xnew0(char *,n_sinks));
+    for(uint32_t i = 0; i < n_sinks; ++i){
+        sink_u=(struct userdata *) pa_idxset_iterate(sink_list, &iter, &dummy);
+        names[i] = sink_u->dbus_path;
+    }
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, names, n_sinks);
+    pa_xfree(names);
+}
+
+void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u){
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(_u);
+
+    pa_core *core=_u;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter, array_iter;
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "s", &array_iter));
+
+    char *name;
+    pa_datum key,next_key;
+    int done;
+    pa_database *database;
+    pa_assert_se(database=pa_shared_get(core, EQDB));
+    done = !pa_database_first(database, &key, NULL);
+
+    while(!done){
+        done = !pa_database_next(database, &key, &next_key, NULL);
+        name=pa_xmalloc(key.size + 1);
+        memcpy(name, key.data, key.size);
+        name[key.size] = '\0';
+        pa_datum_free(&key);
+        dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING, &name);
+        pa_xfree(name);
+        key = next_key;
+    }
+    dbus_message_iter_close_container(&msg_iter, &array_iter);
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+    dbus_message_unref(reply);
+}
+
+void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(_u);
+
+    pa_core *u=(pa_core *) _u;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter, dict_iter, sub_iter, array_iter;
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+    uint32_t rev=1;
+    pa_idxset *sink_list;
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, manager_handlers[MANAGER_HANDLER_REVISION].property_name, DBUS_TYPE_UINT32, &rev);
+
+    pa_assert_se(sink_list = pa_shared_get(u, SINKLIST));
+    unsigned n_sinks = (unsigned) pa_idxset_size(sink_list);
+    char **names = NULL;
+    void *iter = NULL;
+    struct userdata *sink_u = NULL;
+    pa_assert_se(names = pa_xnew0(char *,n_sinks));
+    for(uint32_t i = 0; i < n_sinks; ++i){
+        unsigned dummy;
+        sink_u=(struct userdata *) pa_idxset_iterate(sink_list, &iter, &dummy);
+        names[i] = sink_u->dbus_path;
+    }
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, manager_handlers[MANAGER_HANDLER_EQUALIZED_SINKS].property_name, DBUS_TYPE_OBJECT_PATH, names, n_sinks);
+    pa_xfree(names);
+
+    pa_database *database;
+    pa_assert_se(database=pa_shared_get(u, EQDB));
+
+    pa_datum key,next_key;
+    char *profile_name;
+    int done;
+    done = !pa_database_first(database, &key, NULL);
+    dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &sub_iter);
+    dbus_message_iter_append_basic(&sub_iter, DBUS_TYPE_STRING, &manager_handlers[MANAGER_HANDLER_PROFILES].property_name);
+
+    dbus_message_iter_open_container(&sub_iter, DBUS_TYPE_ARRAY, "s", &array_iter);
+    while(!done){
+        done = !pa_database_next(database, &key, &next_key, NULL);
+        profile_name=pa_xmalloc(key.size + 1);
+        memcpy(profile_name, key.data, key.size);
+        profile_name[key.size] = '\0';
+        pa_datum_free(&key);
+        dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING, &profile_name);
+        pa_xfree(profile_name);
+        key = next_key;
+    }
+    pa_assert_se(dbus_message_iter_close_container(&sub_iter, &array_iter));
+    pa_assert_se(dbus_message_iter_close_container(&dict_iter, &sub_iter));
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+    dbus_message_unref(reply);
+}
+
+void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *_u) {
+    struct userdata *u=(struct userdata *) _u;
+    DBusError error;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(u);
+    float *ys;
+    uint32_t *xs;
+    double *_ys;
+    unsigned x_npoints,y_npoints;
+
+    dbus_error_init(&error);
+
+    if(!dbus_message_get_args(msg, &error,
+                DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &xs, &x_npoints,
+                DBUS_TYPE_ARRAY, DBUS_TYPE_DOUBLE, &_ys, &y_npoints,
+                DBUS_TYPE_INVALID)){
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
+        dbus_error_free(&error);
+        return;
+    }
+    int points_good=1;
+    for(size_t i = 0; i < x_npoints; ++i){
+        if(xs[i] >= u->fft_size / 2 + 1){
+            points_good=0;
+            break;
+        }
+    }
+    if(!is_monotonic(xs,x_npoints) || !points_good){
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs must be monotonic and 0<x<%ld", u->fft_size / 2);
+        dbus_error_free(&error);
+        return;
+
+    }
+    else if(x_npoints != y_npoints || x_npoints < 2 || x_npoints > u->fft_size / 2 +1  ){
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs and ys must be the same length and 2<=l<=%ld!", u->fft_size / 2 + 1);
+        dbus_error_free(&error);
+        return;
+    }
+
+    ys = pa_xmalloc(x_npoints * sizeof(float));
+    for(uint32_t i = 0; i < x_npoints; ++i){
+        ys[i] = (float) _ys[i];
+    }
+
+    float *H = u->Hs[pa_aupdate_write_begin(u->a_H)];
+    interpolate(H, u->fft_size / 2 + 1, xs, ys, x_npoints);
+    fix_filter(H, u->fft_size);
+    pa_aupdate_write_swap(u->a_H);
+    pa_aupdate_write_end(u->a_H);
+    pa_xfree(ys);
+
+    //Stupid for IO reasons?  Add a save signal to dbus instead
+    //save_state(u);
+
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u) {
+    struct userdata *u=(struct userdata *) _u;
+    DBusError error;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(u);
+    uint32_t *xs;
+    double *ys;
+    unsigned x_npoints;
+
+    dbus_error_init(&error);
+
+    if(!dbus_message_get_args(msg, &error,
+                DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &xs, &x_npoints,
+                DBUS_TYPE_INVALID)){
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
+        dbus_error_free(&error);
+        return;
+    }
+    int points_good=1;
+    for(size_t i = 0; i < x_npoints; ++i){
+        if(xs[i] >= u->fft_size / 2 + 1){
+            points_good=0;
+            break;
+        }
+    }
+
+    if(x_npoints > u->fft_size / 2 +1 || !points_good){
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs indices/length must be <= %ld!", u->fft_size / 2 + 1);
+        dbus_error_free(&error);
+        return;
+    }
+
+    ys = pa_xmalloc(x_npoints * sizeof(double));
+    float *H = u->Hs[pa_aupdate_read_begin(u->a_H)];
+    for(uint32_t i = 0; i < x_npoints; ++i){
+        ys[i] = H[xs[i]] * u->fft_size;
+    }
+    pa_aupdate_read_end(u->a_H);
+
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_DOUBLE, ys, x_npoints);
+    pa_xfree(ys);
+}
+
+void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u) {
+    struct userdata *u=(struct userdata *) _u;
+    DBusError error;
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(u);
+    dbus_error_init(&error);
+    char *name;
+
+    if(!dbus_message_get_args(msg, &error,
+                 DBUS_TYPE_STRING, &name,
+                DBUS_TYPE_INVALID)){
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
+        dbus_error_free(&error);
+        return;
+    }
+    save_profile(u,name);
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u) {
+    struct userdata *u=(struct userdata *) _u;
+    DBusError error;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(u);
+    dbus_error_init(&error);
+    char *name;
+
+    if(!dbus_message_get_args(msg, &error,
+                 DBUS_TYPE_STRING, &name,
+                DBUS_TYPE_INVALID)){
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
+        dbus_error_free(&error);
+        return;
+    }
+    const char *err_msg=load_profile(u,name);
+    if(err_msg!=NULL){
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "error loading profile %s: %s", name,err_msg);
+        dbus_error_free(&error);
+        return;
+    }
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
+void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){
+    uint32_t rev=1;
+    pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_UINT32, &rev);
+}
+
+void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u){
     pa_assert(conn);
     pa_assert(msg);
     pa_assert(_u);
@@ -1106,7 +1566,7 @@ void get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u){
     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &n_coefs);
 }
 
-void get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u){
+void equalizer_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u){
     pa_assert(conn);
     pa_assert(msg);
     pa_assert(_u);
@@ -1115,7 +1575,8 @@ void get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u){
     uint32_t rate=(uint32_t) u->sink->sample_spec.rate;
     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &rate);
 }
-void get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u){
+
+void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u){
     pa_assert(conn);
     pa_assert(msg);
     pa_assert(_u);
@@ -1125,7 +1586,34 @@ void get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u){
     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &fft_size);
 }
 
-void get_filter(DBusConnection *conn, DBusMessage *msg, void *_u){
+void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(_u);
+
+    struct userdata *u=(struct userdata *) _u;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter, dict_iter;
+    uint32_t rev=1;
+    uint32_t n_coefs=(uint32_t)(u->fft_size / 2 + 1);
+    uint32_t rate=(uint32_t) u->sink->sample_spec.rate;
+    uint32_t fft_size=(uint32_t) u->fft_size;
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_REVISION].property_name, DBUS_TYPE_UINT32, &rev);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_SAMPLERATE].property_name, DBUS_TYPE_UINT32, &rate);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_FILTERSAMPLERATE].property_name, DBUS_TYPE_UINT32, &fft_size);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_N_COEFS].property_name, DBUS_TYPE_UINT32, &n_coefs);
+
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+    dbus_message_unref(reply);
+}
+
+void equalizer_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u){
     pa_assert(conn);
     pa_assert(msg);
     pa_assert(_u);
@@ -1144,7 +1632,7 @@ void get_filter(DBusConnection *conn, DBusMessage *msg, void *_u){
     pa_xfree(H_);
 }
 
-void set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){
+void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){
     pa_assert(conn);
     pa_assert(msg);
     pa_assert(_u);
@@ -1152,7 +1640,9 @@ void set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){
     struct userdata *u=(struct userdata *)_u;
     double *H_;
     unsigned _n_coefs;
-    pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_DOUBLE, &H_, &_n_coefs);
+    if(pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_DOUBLE, &H_, &_n_coefs)){
+        return;
+    }
     if(_n_coefs!=u->fft_size / 2 + 1){
         pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "This filter takes exactly %ld coefficients, you gave %d", u->fft_size / 2 + 1, _n_coefs);
         return;
@@ -1166,44 +1656,7 @@ void set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){
     pa_aupdate_write_end(u->a_H);
 
     //Stupid for IO reasons?  Add a save signal to dbus instead
-    save_state(u);
+    //save_state(u);
 
     pa_dbus_send_empty_reply(conn, msg);
 }
-
-void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){
-    pa_assert(conn);
-    pa_assert(msg);
-    pa_assert(_u);
-
-    struct userdata *u = (struct userdata *)_u;
-    DBusMessage *reply = NULL;
-    DBusMessageIter msg_iter, dict_iter;
-
-    uint32_t rate=(uint32_t) u->sink->sample_spec.rate;
-    uint32_t fft_size=(uint32_t) u->fft_size;
-    uint32_t n_coefs=(uint32_t)(u->fft_size / 2 + 1);
-    double *H_=(double *)pa_xmalloc0(n_coefs*sizeof(double));
-    
-    unsigned H_i=pa_aupdate_read_begin(u->a_H);
-    float *H=u->Hs[H_i];
-    for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){
-        H_[i] = H[i];
-    }
-    pa_aupdate_read_end(u->a_H);
-
-    pa_assert_se((reply = dbus_message_new_method_return(msg)));
-    dbus_message_iter_init_append(reply, &msg_iter);
-    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
-
-    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLERATE].property_name, DBUS_TYPE_UINT32, &rate);
-    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_FILTERSAMPLERATE].property_name, DBUS_TYPE_UINT32, &fft_size);
-    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_N_COEFS].property_name, DBUS_TYPE_UINT32, &n_coefs);
-    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_COEFS].property_name, DBUS_TYPE_DOUBLE, H_, n_coefs);
-
-    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
-    pa_assert_se(dbus_connection_send(conn, reply, NULL));
-    dbus_message_unref(reply);
-
-    pa_xfree(H_);
-}

commit 857eea062129b4b9e04950a9187ebc47fc84899f
Author: Jason Newton <nevion at gmail.com>
Date:   Fri Aug 7 15:08:57 2009 -0700

    module-equalizer-sink:
        add lennard's fix for piggy-back sinks in pop_cb
        fixed some tsched issues

diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
index 285fddb..eb9d6ef 100755
--- a/src/modules/module-equalizer-sink.c
+++ b/src/modules/module-equalizer-sink.c
@@ -549,6 +549,9 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
     if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
         return -1;
 
+    /* Hmm, process any reweind request that might be queued up */
+    pa_sink_process_rewind(u->sink, 0);
+
     //pa_log("start output-buffered %ld, input-buffered %ld, requested %ld",buffered_samples,u->samples_gathered,samples_requested);
     struct timespec start, end;
 
@@ -559,11 +562,11 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
     }
 
     /*
-        Set the H filter
-    */
+     *   Set the H filter
+     */
     unsigned H_i = pa_aupdate_read_begin(u->a_H);
     u->H = u->Hs[H_i];
-    
+
     do{
         pa_memchunk *buffer;
         size_t input_remaining = u->target_samples-u->samples_gathered;
@@ -641,14 +644,14 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
 
         if (amount > 0) {
             //pa_sample_spec *ss = &u->sink->sample_spec;
-            pa_memblockq_seek(u->rendered_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE);
+            //pa_memblockq_seek(u->rendered_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE);
             pa_log_debug("Resetting equalizer");
             u->samples_gathered = 0;
         }
     }
 
     pa_sink_process_rewind(u->sink, amount);
-    pa_memblockq_rewind(u->rendered_q, nbytes);
+    //pa_memblockq_rewind(u->rendered_q, nbytes);
 }
 
 /* Called from I/O thread context */
@@ -677,7 +680,8 @@ static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
 
     size_t fs = pa_frame_size(&(u->sink->sample_spec));
     //pa_sink_set_max_request_within_thread(u->sink, nbytes);
-    pa_sink_set_max_request_within_thread(u->sink, u->R*fs);
+    //pa_sink_set_max_request_within_thread(u->sink, u->R*fs);
+    pa_sink_set_max_request_within_thread(u->sink, ((nbytes+u->R*fs-1)/(u->R*fs))*(u->R*fs));
 }
 
 /* Called from I/O thread context */
@@ -692,8 +696,8 @@ static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) {
 
     size_t fs = pa_frame_size(&(u->sink->sample_spec));
     //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs);
-    pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs );
-    //pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
+    //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs );
+    pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
 }
 
 /* Called from I/O thread context */
@@ -924,8 +928,8 @@ int pa__init(pa_module*m) {
         memset(u->overlap_accum[c], 0, u->overlap_size*sizeof(float));
     }
     u->output_window = alloc((u->fft_size / 2 + 1), sizeof(fftwf_complex));
-    u->forward_plan = fftwf_plan_dft_r2c_1d(u->fft_size, u->work_buffer, u->output_window, FFTW_MEASURE);
-    u->inverse_plan = fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_MEASURE);
+    u->forward_plan = fftwf_plan_dft_r2c_1d(u->fft_size, u->work_buffer, u->output_window, FFTW_ESTIMATE);
+    u->inverse_plan = fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_ESTIMATE);
 
     hanning_window(u->W, u->window_size);
 
@@ -965,7 +969,9 @@ int pa__init(pa_module*m) {
 
     pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq);
     pa_sink_set_rtpoll(u->sink, master->rtpoll);
-    pa_sink_set_max_request(u->sink, u->R*fs);
+    pa_sink_set_max_request(u->sink,
+        ((pa_sink_get_max_request(u->sink)+u->R*fs-1)/(u->R*fs))*(u->R*fs)
+    );
     //pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(u->R*fs, &ss));
 
     /* Create sink input */

commit 4231ac444fc1492bab58fe12d439c4479ef1f648
Author: Jason Newton <nevion at gmail.com>
Date:   Sat Aug 8 00:53:40 2009 -0700

    module-equalizer-sink: reverted buffering logic back to how the ladspa sink did it

diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
index eb9d6ef..6311181 100755
--- a/src/modules/module-equalizer-sink.c
+++ b/src/modules/module-equalizer-sink.c
@@ -113,7 +113,7 @@ struct userdata {
     float *Hs[2];//thread updatable copies
     pa_aupdate *a_H;
     pa_memchunk conv_buffer;
-    pa_memblockq *rendered_q;
+    pa_memblockq *input_q;
 
     pa_dbus_protocol *dbus_protocol;
     char *dbus_path;
@@ -138,7 +138,7 @@ void fix_filter(float *H, size_t fft_size);
 void interpolate(float *signal, size_t length, uint32_t *xs, float *ys, size_t n_points);
 static void array_out(const char *name, float *a, size_t length);
 static int is_monotonic(uint32_t *xs, size_t length);
-static void process_samples(struct userdata *u);
+static void process_samples(struct userdata *u, pa_memchunk *tchunk);
 static void input_buffer(struct userdata *u, pa_memchunk *in);
 static void save_profile(struct userdata *u,char *name);
 static void save_state(struct userdata *u);
@@ -273,9 +273,9 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
             if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
                 usec = 0;
 
-            //usec+=pa_bytes_to_usec(u->latency * fs, ss);
-            //usec+=pa_bytes_to_usec(u->samples_gathered * fs, ss);
-            usec += pa_bytes_to_usec(pa_memblockq_get_length(u->rendered_q), ss);
+            //usec += pa_bytes_to_usec(u->latency * fs, ss);
+            //usec += pa_bytes_to_usec(u->samples_gathered * fs, ss);
+            usec += pa_bytes_to_usec(pa_memblockq_get_length(u->input_q), ss);
             /* Add the latency internal to our sink input on top */
             usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec);
             *((pa_usec_t*) data) = usec;
@@ -310,8 +310,10 @@ static void sink_request_rewind(pa_sink *s) {
     pa_sink_assert_ref(s);
     pa_assert_se(u = s->userdata);
 
+    //pa_memblockq_drop(u->input_q,pa_memblockq_get_length(u->input_q));
+
     /* Just hand this one over to the master sink */
-    pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes + pa_memblockq_get_length(u->rendered_q), TRUE, FALSE, FALSE);
+    pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes+pa_memblockq_get_length(u->input_q), TRUE, FALSE, FALSE);
 }
 
 /* Called from I/O thread context */
@@ -327,34 +329,28 @@ static void sink_update_requested_latency(pa_sink *s) {
             pa_sink_get_requested_latency_within_thread(s));
 }
 
-static void process_samples(struct userdata *u){
-    pa_memchunk tchunk;
+static void process_samples(struct userdata *u, pa_memchunk *tchunk){
     size_t fs=pa_frame_size(&(u->sink->sample_spec));
-    while(u->samples_gathered >= u->R){
-        float *dst;
-        //pa_log("iter gathered: %ld", u->samples_gathered);
-        //pa_memblockq_drop(u->rendered_q, tchunk.length);
-        tchunk.index=0;
-        tchunk.length=u->R*fs;
-        tchunk.memblock=pa_memblock_new(u->core->mempool, tchunk.length);
-        dst=((float*)pa_memblock_acquire(tchunk.memblock));
-        for(size_t c=0;c < u->channels; c++) {
-            dsp_logic(
-                u->work_buffer,
-                u->input[c],
-                u->overlap_accum[c],
-                u->H,
-                u->W,
-                u->output_window,
-                u
-            );
-            pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst + c, fs, u->work_buffer, sizeof(float), u->R);
-        }
-        pa_memblock_release(tchunk.memblock);
-        pa_memblockq_push(u->rendered_q, &tchunk);
-        pa_memblock_unref(tchunk.memblock);
-        u->samples_gathered-=u->R;
+    pa_assert(u->samples_gathered >= u->R);
+    float *dst;
+    tchunk->index=0;
+    tchunk->length=u->R*fs;
+    tchunk->memblock=pa_memblock_new(u->core->mempool, tchunk->length);
+    dst=((float*)pa_memblock_acquire(tchunk->memblock));
+    for(size_t c=0;c < u->channels; c++) {
+        dsp_logic(
+            u->work_buffer,
+            u->input[c],
+            u->overlap_accum[c],
+            u->H,
+            u->W,
+            u->output_window,
+            u
+        );
+        pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst + c, fs, u->work_buffer, sizeof(float), u->R);
     }
+    pa_memblock_release(tchunk->memblock);
+    u->samples_gathered-=u->R;
 }
 
 typedef float v4sf __attribute__ ((__aligned__(v_size*sizeof(float))));
@@ -539,85 +535,59 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
     struct userdata *u;
     pa_sink_input_assert_ref(i);
     pa_assert(chunk);
-    pa_assert_se(u = i->userdata);
-    pa_assert_se(u->sink);
+    pa_assert(u = i->userdata);
+    pa_assert(u->sink);
     size_t fs = pa_frame_size(&(u->sink->sample_spec));
-    //size_t samples_requested = nbytes/fs;
-    size_t buffered_samples = pa_memblockq_get_length(u->rendered_q)/fs;
     pa_memchunk tchunk;
     chunk->memblock = NULL;
     if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
         return -1;
 
-    /* Hmm, process any reweind request that might be queued up */
+    /* Hmm, process any rewind request that might be queued up */
     pa_sink_process_rewind(u->sink, 0);
 
     //pa_log("start output-buffered %ld, input-buffered %ld, requested %ld",buffered_samples,u->samples_gathered,samples_requested);
     struct timespec start, end;
 
-    if(pa_memblockq_peek(u->rendered_q, &tchunk)==0){
-        *chunk = tchunk;
-        pa_memblockq_drop(u->rendered_q, chunk->length);
-        return 0;
-    }
-
-    /*
-     *   Set the H filter
-     */
-    unsigned H_i = pa_aupdate_read_begin(u->a_H);
-    u->H = u->Hs[H_i];
-
     do{
-        pa_memchunk *buffer;
         size_t input_remaining = u->target_samples-u->samples_gathered;
         pa_assert(input_remaining>0);
         //collect samples
 
-        buffer = &u->conv_buffer;
-        buffer->length = input_remaining*fs;
-        buffer->index = 0;
-        pa_memblock_ref(buffer->memblock);
-        pa_sink_render_into(u->sink, buffer);
-
-        //if(u->sink->thread_info.rewind_requested)
-        //    sink_request_rewind(u->sink);
-
-        //pa_memchunk p;
-        //buffer = &p;
-        //pa_sink_render(u->sink, u->R*fs, buffer);
-        //buffer->length = PA_MIN(input_remaining*fs, buffer->length);
-
-        //debug block
-        //pa_memblockq_push(u->rendered_q, buffer);
-        //pa_memblock_unref(buffer->memblock);
-        //goto END;
-
+        //buffer = &u->conv_buffer;
+        //buffer->length = input_remaining*fs;
+        //buffer->index = 0;
+        //pa_memblock_ref(buffer->memblock);
+        //pa_sink_render_into(u->sink, buffer);
+        while(pa_memblockq_peek(u->input_q, &tchunk) < 0){
+            pa_sink_render(u->sink, input_remaining*fs, &tchunk);
+            pa_memblockq_push(u->input_q, &tchunk);
+            pa_memblock_unref(tchunk.memblock);
+        }
+        tchunk.length = PA_MIN(input_remaining*fs, tchunk.length);
+        pa_memblockq_drop(u->input_q,tchunk.length);
         //pa_log("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs);
-        //copy new input
+        /* copy new input */
         gettime(start);
-        input_buffer(u, buffer);
+        input_buffer(u, &tchunk);
         gettime(end);
         //pa_log("Took %0.5f seconds to setup", tdiff(end, start)*1e-9);
+        pa_memblock_unref(tchunk.memblock);
+    }while(u->samples_gathered < u->R);
 
-        pa_memblock_unref(buffer->memblock);
-
-        pa_assert_se(u->fft_size >= u->window_size);
-        pa_assert_se(u->R < u->window_size);
-        //process every complete block on hand
-
-        gettime(start);
-        process_samples(u);
-        gettime(end);
-        //pa_log("Took %0.5f seconds to process", tdiff(end, start)*1e-9);
-
-        buffered_samples = pa_memblockq_get_length(u->rendered_q)/fs;
-    }while(buffered_samples < u->R);
+    pa_assert(u->fft_size >= u->window_size);
+    pa_assert(u->R < u->window_size);
+    /* set the H filter */
+    unsigned H_i = pa_aupdate_read_begin(u->a_H);
+    u->H = u->Hs[H_i];
+    gettime(start);
+    /* process a block */
+    process_samples(u,chunk);
+    gettime(end);
+    //pa_log("Took %0.5f seconds to process", tdiff(end, start)*1e-9);
+    pa_aupdate_read_end(u->a_H);
 
-    //deque from rendered_q and output
-    pa_assert_se(pa_memblockq_peek(u->rendered_q, &tchunk)==0);
-    *chunk = tchunk;
-    pa_memblockq_drop(u->rendered_q, chunk->length);
-    pa_assert_se(chunk->memblock);
+    pa_assert(chunk->memblock);
     //pa_log("gave %ld", chunk->length/fs);
     //pa_log("end pop");
     return 0;
@@ -638,20 +608,25 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
     if (u->sink->thread_info.rewind_nbytes > 0) {
         size_t max_rewrite;
 
-        max_rewrite = nbytes + pa_memblockq_get_length(u->rendered_q);
+        //max_rewrite = nbytes;
+        max_rewrite = nbytes + pa_memblockq_get_length(u->input_q);
+        //PA_MIN(pa_memblockq_get_length(u->input_q), nbytes);
         amount = PA_MIN(u->sink->thread_info.rewind_nbytes, max_rewrite);
         u->sink->thread_info.rewind_nbytes = 0;
 
         if (amount > 0) {
             //pa_sample_spec *ss = &u->sink->sample_spec;
-            //pa_memblockq_seek(u->rendered_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE);
+            //invalidate the output q
+            pa_memblockq_seek(u->input_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE);
+            //pa_memblockq_drop(u->input_q, pa_memblockq_get_length(u->input_q));
+            //pa_memblockq_seek(u->input_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE);
             pa_log_debug("Resetting equalizer");
             u->samples_gathered = 0;
         }
     }
 
     pa_sink_process_rewind(u->sink, amount);
-    //pa_memblockq_rewind(u->rendered_q, nbytes);
+    pa_memblockq_rewind(u->input_q, nbytes);
 }
 
 /* Called from I/O thread context */
@@ -664,7 +639,7 @@ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
     if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
         return;
 
-    pa_memblockq_set_maxrewind(u->rendered_q, nbytes);
+    pa_memblockq_set_maxrewind(u->input_q, nbytes);
     pa_sink_set_max_rewind_within_thread(u->sink, nbytes);
 }
 
@@ -694,7 +669,6 @@ static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) {
     if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
         return;
 
-    size_t fs = pa_frame_size(&(u->sink->sample_spec));
     //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs);
     //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs );
     pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
@@ -729,14 +703,14 @@ static void sink_input_attach_cb(pa_sink_input *i) {
     pa_sink_set_rtpoll(u->sink, i->sink->rtpoll);
     pa_sink_attach_within_thread(u->sink);
 
-    size_t fs = pa_frame_size(&(u->sink->sample_spec));
+    //size_t fs = pa_frame_size(&(u->sink->sample_spec));
     //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs);
     //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->master->thread_info.max_latency);
     //TODO: setting this guy minimizes drop outs but doesn't get rid
     //of them completely, figure out why
-    pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs);
+    //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs);
     //TODO: this guy causes dropouts constantly+rewinds, it's unusable
-    //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency);
+    pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency);
 }
 
 /* Called from main context */
@@ -906,7 +880,7 @@ int pa__init(pa_module*m) {
     u->target_samples = 1*u->R;
     u->samples_gathered = 0;
     u->max_output = pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss)/pa_frame_size(&ss);
-    u->rendered_q = pa_memblockq_new(0,  MEMBLOCKQ_MAXLENGTH, u->target_samples*fs, fs, fs, 0, 0, NULL);
+    u->input_q = pa_memblockq_new(0,  MEMBLOCKQ_MAXLENGTH, 0, fs, 1, 1, 0, NULL);
     u->a_H = pa_aupdate_new();
     u->conv_buffer.memblock = pa_memblock_new(u->core->mempool, u->target_samples*fs);
     u->latency = u->R;
@@ -1062,11 +1036,9 @@ void pa__done(pa_module*m) {
         pa_sink_input_unref(u->sink_input);
     }
 
-    if(u->conv_buffer.memblock)
-        pa_memblock_unref(u->conv_buffer.memblock);
-
-    if (u->rendered_q)
-        pa_memblockq_free(u->rendered_q);
+    pa_aupdate_free(u->a_H);
+    pa_memblock_unref(u->conv_buffer.memblock);
+    pa_memblockq_free(u->input_q);
 
     fftwf_destroy_plan(u->inverse_plan);
     fftwf_destroy_plan(u->forward_plan);

commit 1e3c7d326f4feb2e8dca8626afa58e88bca40fcd
Author: Jason Newton <nevion at gmail.com>
Date:   Sat Aug 8 22:20:05 2009 -0700

    module-equalizer-sink:
        dbus:
            eliminated some redundant code in dbus handlers/getall
            switched filter back to being a property
            signals for changed profiles, added/removed sinks, filter updates and sink reconfigurations
        fixed timing routines

diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
index 6311181..88c1074 100755
--- a/src/modules/module-equalizer-sink.c
+++ b/src/modules/module-equalizer-sink.c
@@ -52,6 +52,7 @@ USA.
 #include <pulsecore/sample-util.h>
 #include <pulsecore/shared.h>
 #include <pulsecore/idxset.h>
+#include <pulsecore/strlist.h>
 #include <pulsecore/database.h>
 #include <pulsecore/protocol-dbus.h>
 #include <pulsecore/dbus-util.h>
@@ -161,10 +162,14 @@ void dsp_logic(
  */
 #define EXTNAME "org.PulseAudio.Ext.Equalizing1"
 #define MANAGER_PATH "/org/pulseaudio/equalizing1"
+#define MANAGER_IFACE EXTNAME ".Manager"
+#define EQUALIZER_IFACE EXTNAME ".Equalizer"
 static void dbus_init(struct userdata *u);
 static void dbus_done(struct userdata *u);
 static void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void get_sinks(pa_core *u, char ***names, unsigned *n_sinks);
 static void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void get_profiles(pa_core *u, char ***names, unsigned *n_sinks);
 static void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u);
 static void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u);
 static void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void *_u);
@@ -179,6 +184,8 @@ static void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg,
 static void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u);
 static void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u);
 static void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void get_filter(struct userdata *u, double **H_);
+static void set_filter(struct userdata *u, double **H_);
 
 #define v_size 4
 #define gettime(x) clock_gettime(CLOCK_MONOTONIC, &x)
@@ -471,8 +478,6 @@ void dsp_logic(
 //        d->v = d->v*h->v;
 //#endif
 //    }
-//
-//
 //    //inverse fft
 //    fftwf_execute_dft_c2r(u->inverse_plan, output_window, dst);
 //
@@ -511,8 +516,6 @@ void dsp_logic(
 //    );
 //}
 
-
-
 void input_buffer(struct userdata *u, pa_memchunk *in){
     size_t fs = pa_frame_size(&(u->sink->sample_spec));
     size_t samples = in->length/fs;
@@ -546,9 +549,9 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
     /* Hmm, process any rewind request that might be queued up */
     pa_sink_process_rewind(u->sink, 0);
 
-    //pa_log("start output-buffered %ld, input-buffered %ld, requested %ld",buffered_samples,u->samples_gathered,samples_requested);
+    //pa_log_debug("start output-buffered %ld, input-buffered %ld, requested %ld",buffered_samples,u->samples_gathered,samples_requested);
     struct timespec start, end;
-
+    gettime(start);
     do{
         size_t input_remaining = u->target_samples-u->samples_gathered;
         pa_assert(input_remaining>0);
@@ -565,15 +568,17 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
             pa_memblock_unref(tchunk.memblock);
         }
         tchunk.length = PA_MIN(input_remaining*fs, tchunk.length);
-        pa_memblockq_drop(u->input_q,tchunk.length);
-        //pa_log("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs);
+        pa_memblockq_drop(u->input_q, tchunk.length);
+        //pa_log_debug("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs);
         /* copy new input */
-        gettime(start);
+        //gettime(start);
         input_buffer(u, &tchunk);
-        gettime(end);
-        //pa_log("Took %0.5f seconds to setup", tdiff(end, start)*1e-9);
+        //gettime(end);
+        //pa_log_debug("Took %0.5f seconds to setup", tdiff(end, start)*1e-9);
         pa_memblock_unref(tchunk.memblock);
     }while(u->samples_gathered < u->R);
+    gettime(end);
+    pa_log_debug("Took %0.6f seconds to get data", tdiff(end, start)*1e-9);
 
     pa_assert(u->fft_size >= u->window_size);
     pa_assert(u->R < u->window_size);
@@ -584,12 +589,12 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
     /* process a block */
     process_samples(u,chunk);
     gettime(end);
-    //pa_log("Took %0.5f seconds to process", tdiff(end, start)*1e-9);
+    pa_log_debug("Took %0.6f seconds to process", tdiff(end, start)*1e-9);
     pa_aupdate_read_end(u->a_H);
 
     pa_assert(chunk->memblock);
-    //pa_log("gave %ld", chunk->length/fs);
-    //pa_log("end pop");
+    //pa_log_debug("gave %ld", chunk->length/fs);
+    //pa_log_debug("end pop");
     return 0;
 }
 
@@ -1090,25 +1095,39 @@ static pa_dbus_property_handler manager_handlers[MANAGER_HANDLER_MAX]={
     [MANAGER_HANDLER_PROFILES]={.property_name="Profiles",.type="as",.get_cb=manager_get_profiles,.set_cb=NULL}
 };
 
+pa_dbus_arg_info sink_args[]={
+    {"sink", "o", NULL}
+};
+
+enum manager_signal_index{
+    MANAGER_SIGNAL_SINK_ADDED,
+    MANAGER_SIGNAL_SINK_REMOVED,
+    MANAGER_SIGNAL_PROFILES_CHANGED,
+    MANAGER_SIGNAL_MAX
+};
+
+static pa_dbus_signal_info manager_signals[MANAGER_SIGNAL_MAX]={
+    [MANAGER_SIGNAL_SINK_ADDED]={.name="SinkAdded", .arguments=sink_args, .n_arguments=sizeof(sink_args)/sizeof(pa_dbus_arg_info)},
+    [MANAGER_SIGNAL_SINK_REMOVED]={.name="SinkRemoved", .arguments=sink_args, .n_arguments=sizeof(sink_args)/sizeof(pa_dbus_arg_info)},
+    [MANAGER_SIGNAL_PROFILES_CHANGED]={.name="ProfilesChanged", .arguments=NULL, .n_arguments=0}
+};
+
 static pa_dbus_interface_info manager_info={
-    .name=EXTNAME ".Manager",
+    .name=MANAGER_IFACE,
     .method_handlers=manager_methods,
     .n_method_handlers=MANAGER_METHOD_MAX,
     .property_handlers=manager_handlers,
     .n_property_handlers=MANAGER_HANDLER_MAX,
     .get_all_properties_cb=manager_get_all,
-    .signals=NULL,
-    .n_signals=0
+    .signals=manager_signals,
+    .n_signals=MANAGER_SIGNAL_MAX
 };
 
-
 enum equalizer_method_index {
     EQUALIZER_METHOD_FILTER_POINTS,
     EQUALIZER_METHOD_SEED_FILTER,
     EQUALIZER_METHOD_SAVE_PROFILE,
     EQUALIZER_METHOD_LOAD_PROFILE,
-    EQUALIZER_METHOD_SET_FILTER,
-    EQUALIZER_METHOD_GET_FILTER,
     EQUALIZER_METHOD_MAX
 };
 
@@ -1117,6 +1136,7 @@ enum equalizer_handler_index {
     EQUALIZER_HANDLER_SAMPLERATE,
     EQUALIZER_HANDLER_FILTERSAMPLERATE,
     EQUALIZER_HANDLER_N_COEFS,
+    EQUALIZER_HANDLER_FILTER,
     EQUALIZER_HANDLER_MAX
 };
 
@@ -1134,12 +1154,6 @@ pa_dbus_arg_info save_profile_args[]={
 pa_dbus_arg_info load_profile_args[]={
     {"name", "s","in"},
 };
-pa_dbus_arg_info set_filter_args[]={
-    {"coefficients", "ad","in"},
-};
-pa_dbus_arg_info get_filter_args[]={
-    {"coefficients", "ad","out"},
-};
 
 static pa_dbus_method_handler equalizer_methods[EQUALIZER_METHOD_MAX]={
     [EQUALIZER_METHOD_SEED_FILTER]{
@@ -1162,16 +1176,6 @@ static pa_dbus_method_handler equalizer_methods[EQUALIZER_METHOD_MAX]={
         .arguments=load_profile_args,
         .n_arguments=sizeof(load_profile_args)/sizeof(pa_dbus_arg_info),
         .receive_cb=equalizer_handle_load_profile},
-    [EQUALIZER_METHOD_SET_FILTER]{
-        .method_name="SetFilterCoefficients",
-        .arguments=set_filter_args,
-        .n_arguments=sizeof(set_filter_args)/sizeof(pa_dbus_arg_info),
-        .receive_cb=equalizer_set_filter},
-    [EQUALIZER_METHOD_GET_FILTER]{
-        .method_name="GetFilterCoefficients",
-        .arguments=get_filter_args,
-        .n_arguments=sizeof(get_filter_args)/sizeof(pa_dbus_arg_info),
-        .receive_cb=equalizer_get_filter}
 };
 
 static pa_dbus_property_handler equalizer_handlers[EQUALIZER_HANDLER_MAX]={
@@ -1179,22 +1183,29 @@ static pa_dbus_property_handler equalizer_handlers[EQUALIZER_HANDLER_MAX]={
     [EQUALIZER_HANDLER_SAMPLERATE]{.property_name="SampleRate",.type="u",.get_cb=equalizer_get_sample_rate,.set_cb=NULL},
     [EQUALIZER_HANDLER_FILTERSAMPLERATE]{.property_name="FilterSampleRate",.type="u",.get_cb=equalizer_get_filter_rate,.set_cb=NULL},
     [EQUALIZER_HANDLER_N_COEFS]{.property_name="NFilterCoefficients",.type="u",.get_cb=equalizer_get_n_coefs,.set_cb=NULL},
+    [EQUALIZER_HANDLER_FILTER]{.property_name="Filter",.type="ad",.get_cb=equalizer_get_filter,.set_cb=equalizer_set_filter},
+};
+
+enum equalizer_signal_index{
+    EQUALIZER_SIGNAL_FILTER_CHANGED,
+    EQUALIZER_SIGNAL_SINK_RECONFIGURED,
+    EQUALIZER_SIGNAL_MAX
 };
 
-//static pa_dbus_arg_info new_equalizer_args[] = { { "path","o",NULL} };
-//static pa_dbus_signal_info signals[SIGNAL_MAX] = {
-//    [SIGNAL_NEW_EQUALIZER]={.name="NewEqualizer",.arguments=new_equalizer_args,.n_arguments=1}
-//};
+static pa_dbus_signal_info equalizer_signals[EQUALIZER_SIGNAL_MAX]={
+    [EQUALIZER_SIGNAL_FILTER_CHANGED]={.name="FilterChanged", .arguments=NULL, .n_arguments=0},
+    [EQUALIZER_SIGNAL_SINK_RECONFIGURED]={.name="SinkReconfigured", .arguments=NULL, .n_arguments=0},
+};
 
 static pa_dbus_interface_info equalizer_info={
-    .name=EXTNAME ".Equalizer",
+    .name=EQUALIZER_IFACE,
     .method_handlers=equalizer_methods,
     .n_method_handlers=EQUALIZER_METHOD_MAX,
     .property_handlers=equalizer_handlers,
     .n_property_handlers=EQUALIZER_HANDLER_MAX,
     .get_all_properties_cb=equalizer_get_all,
-    .signals=NULL,
-    .n_signals=0
+    .signals=equalizer_signals,
+    .n_signals=EQUALIZER_SIGNAL_MAX
 };
 
 void dbus_init(struct userdata *u){
@@ -1202,8 +1213,8 @@ void dbus_init(struct userdata *u){
     u->dbus_path=pa_sprintf_malloc("/org/pulseaudio/core1/sink%d", u->sink->index);
 
     pa_dbus_protocol_add_interface(u->dbus_protocol, u->dbus_path, &equalizer_info, u);
-    pa_idxset *sink_list=pa_shared_get(u->core,SINKLIST);
-    u->database=pa_shared_get(u->core,EQDB);
+    pa_idxset *sink_list=pa_shared_get(u->core, SINKLIST);
+    u->database=pa_shared_get(u->core, EQDB);
     if(sink_list==NULL){
         sink_list=pa_idxset_new(&pa_idxset_trivial_hash_func, &pa_idxset_trivial_compare_func);
         pa_shared_set(u->core, SINKLIST, sink_list);
@@ -1216,13 +1227,25 @@ void dbus_init(struct userdata *u){
         pa_dbus_protocol_register_extension(u->dbus_protocol, EXTNAME);
     }
     uint32_t dummy;
-    pa_idxset_put(sink_list,u,&dummy);
+    pa_idxset_put(sink_list, u, &dummy);
+
+    DBusMessage *signal = NULL;
+    pa_assert_se((signal = dbus_message_new_signal(MANAGER_PATH, MANAGER_IFACE, manager_signals[MANAGER_SIGNAL_SINK_ADDED].name)));
+    dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &u->dbus_path, DBUS_TYPE_INVALID);
+    pa_dbus_protocol_send_signal(u->dbus_protocol, signal);
+    dbus_message_unref(signal);
 }
 
 void dbus_done(struct userdata *u){
     pa_idxset *sink_list;
     uint32_t dummy;
 
+    DBusMessage *signal = NULL;
+    pa_assert_se((signal = dbus_message_new_signal(MANAGER_PATH, MANAGER_IFACE, manager_signals[MANAGER_SIGNAL_SINK_REMOVED].name)));
+    dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &u->dbus_path, DBUS_TYPE_INVALID);
+    pa_dbus_protocol_send_signal(u->dbus_protocol, signal);
+    dbus_message_unref(signal);
+
     pa_assert_se(sink_list=pa_shared_get(u->core,SINKLIST));
     pa_idxset_remove_by_data(sink_list,u,&dummy);
     if(pa_idxset_size(sink_list)==0){
@@ -1255,6 +1278,13 @@ void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void
     }
     remove_profile(c,name);
     pa_dbus_send_empty_reply(conn, msg);
+
+    DBusMessage *signal = NULL;
+    pa_assert_se((signal = dbus_message_new_signal(MANAGER_PATH, MANAGER_IFACE, manager_signals[MANAGER_SIGNAL_PROFILES_CHANGED].name)));
+    pa_dbus_protocol *dbus_protocol = pa_dbus_protocol_get(c);
+    pa_dbus_protocol_send_signal(dbus_protocol, signal);
+    pa_dbus_protocol_unref(dbus_protocol);
+    dbus_message_unref(signal);
 }
 
 void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){
@@ -1262,62 +1292,85 @@ void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){
     pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_UINT32, &rev);
 }
 
-void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u){
-    pa_assert(conn);
-    pa_assert(msg);
-    pa_assert(_u);
-
-    pa_core *c = (pa_core *)_u;
-    pa_idxset *sink_list;
-    uint32_t dummy;
+void get_sinks(pa_core *u, char ***names, unsigned *n_sinks){
+    pa_assert(u);
+    pa_assert(names);
+    pa_assert(n_sinks);
 
-    pa_assert_se(sink_list = pa_shared_get(c, SINKLIST));
-    unsigned n_sinks = (unsigned) pa_idxset_size(sink_list);
-    char **names = NULL;
     void *iter = NULL;
     struct userdata *sink_u = NULL;
-    pa_assert_se(names = pa_xnew0(char *,n_sinks));
-    for(uint32_t i = 0; i < n_sinks; ++i){
-        sink_u=(struct userdata *) pa_idxset_iterate(sink_list, &iter, &dummy);
-        names[i] = sink_u->dbus_path;
+    uint32_t dummy;
+    pa_idxset *sink_list;
+    pa_assert_se(sink_list = pa_shared_get(u, SINKLIST));
+    *n_sinks = (unsigned) pa_idxset_size(sink_list);
+    pa_assert_se(*names = pa_xnew0(char *,*n_sinks));
+    for(uint32_t i = 0; i < *n_sinks; ++i){
+        sink_u = (struct userdata *) pa_idxset_iterate(sink_list, &iter, &dummy);
+        (*names)[i] = pa_xstrdup(sink_u->dbus_path);
     }
-    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, names, n_sinks);
-    pa_xfree(names);
 }
 
-void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u){
+void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u){
     pa_assert(conn);
     pa_assert(msg);
     pa_assert(_u);
 
-    pa_core *core=_u;
-    DBusMessage *reply = NULL;
-    DBusMessageIter msg_iter, array_iter;
-
-    pa_assert_se((reply = dbus_message_new_method_return(msg)));
-    dbus_message_iter_init_append(reply, &msg_iter);
-    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "s", &array_iter));
+    unsigned n;
+    char **names = NULL;
+    get_sinks((pa_core *) _u, &names, &n);
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, names, n);
+    for(unsigned i = 0; i < n; ++i){
+        pa_xfree(names[i]);
+    }
+    pa_xfree(names);
+}
 
+void get_profiles(pa_core *c, char ***names, unsigned *n){
+    pa_assert(c);
+    pa_assert(names);
+    pa_assert(n);
     char *name;
-    pa_datum key,next_key;
-    int done;
     pa_database *database;
-    pa_assert_se(database=pa_shared_get(core, EQDB));
-    done = !pa_database_first(database, &key, NULL);
+    pa_assert_se(database = pa_shared_get(c, EQDB));
+    pa_datum key, next_key;
+    pa_strlist *head=NULL, *iter;
 
+    int done;
+    done = !pa_database_first(database, &key, NULL);
+    *n = 0;
     while(!done){
         done = !pa_database_next(database, &key, &next_key, NULL);
         name=pa_xmalloc(key.size + 1);
         memcpy(name, key.data, key.size);
         name[key.size] = '\0';
         pa_datum_free(&key);
-        dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING, &name);
+        head = pa_strlist_prepend(head, name);
         pa_xfree(name);
         key = next_key;
+        (*n)++;
     }
-    dbus_message_iter_close_container(&msg_iter, &array_iter);
-    pa_assert_se(dbus_connection_send(conn, reply, NULL));
-    dbus_message_unref(reply);
+    (*names) = pa_xnew0(char *, *n);
+    iter=head;
+    for(unsigned i = 0; i < *n; ++i){
+        (*names)[*n-1-i]=pa_xstrdup(pa_strlist_data(iter));
+        iter=pa_strlist_next(iter);
+    }
+    pa_strlist_free(head);
+}
+
+void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u){
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(_u);
+
+    char **names;
+    unsigned n;
+    get_profiles((pa_core *)_u, &names, &n);
+    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_STRING, names, n);
+    for(unsigned i = 0; i < n; ++i){
+        pa_xfree(names[i]);
+    }
+    pa_xfree(names);
 }
 
 void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){
@@ -1325,54 +1378,31 @@ void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){
     pa_assert(msg);
     pa_assert(_u);
 
-    pa_core *u=(pa_core *) _u;
+    pa_core *c = (pa_core *)_u;
+    char **names = NULL;
+    unsigned n;
     DBusMessage *reply = NULL;
-    DBusMessageIter msg_iter, dict_iter, sub_iter, array_iter;
+    DBusMessageIter msg_iter, dict_iter;
     pa_assert_se((reply = dbus_message_new_method_return(msg)));
     dbus_message_iter_init_append(reply, &msg_iter);
     pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
 
     uint32_t rev=1;
-    pa_idxset *sink_list;
     pa_dbus_append_basic_variant_dict_entry(&dict_iter, manager_handlers[MANAGER_HANDLER_REVISION].property_name, DBUS_TYPE_UINT32, &rev);
 
-    pa_assert_se(sink_list = pa_shared_get(u, SINKLIST));
-    unsigned n_sinks = (unsigned) pa_idxset_size(sink_list);
-    char **names = NULL;
-    void *iter = NULL;
-    struct userdata *sink_u = NULL;
-    pa_assert_se(names = pa_xnew0(char *,n_sinks));
-    for(uint32_t i = 0; i < n_sinks; ++i){
-        unsigned dummy;
-        sink_u=(struct userdata *) pa_idxset_iterate(sink_list, &iter, &dummy);
-        names[i] = sink_u->dbus_path;
+    get_sinks(c, &names, &n);
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter,manager_handlers[MANAGER_HANDLER_EQUALIZED_SINKS].property_name, DBUS_TYPE_OBJECT_PATH, names, n);
+    for(unsigned i = 0; i < n; ++i){
+        pa_xfree(names[i]);
     }
-    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, manager_handlers[MANAGER_HANDLER_EQUALIZED_SINKS].property_name, DBUS_TYPE_OBJECT_PATH, names, n_sinks);
     pa_xfree(names);
 
-    pa_database *database;
-    pa_assert_se(database=pa_shared_get(u, EQDB));
-
-    pa_datum key,next_key;
-    char *profile_name;
-    int done;
-    done = !pa_database_first(database, &key, NULL);
-    dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &sub_iter);
-    dbus_message_iter_append_basic(&sub_iter, DBUS_TYPE_STRING, &manager_handlers[MANAGER_HANDLER_PROFILES].property_name);
-
-    dbus_message_iter_open_container(&sub_iter, DBUS_TYPE_ARRAY, "s", &array_iter);
-    while(!done){
-        done = !pa_database_next(database, &key, &next_key, NULL);
-        profile_name=pa_xmalloc(key.size + 1);
-        memcpy(profile_name, key.data, key.size);
-        profile_name[key.size] = '\0';
-        pa_datum_free(&key);
-        dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING, &profile_name);
-        pa_xfree(profile_name);
-        key = next_key;
+    get_profiles(c, &names, &n);
+    pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, manager_handlers[MANAGER_HANDLER_PROFILES].property_name, DBUS_TYPE_STRING, names, n);
+    for(unsigned i = 0; i < n; ++i){
+        pa_xfree(names[i]);
     }
-    pa_assert_se(dbus_message_iter_close_container(&sub_iter, &array_iter));
-    pa_assert_se(dbus_message_iter_close_container(&dict_iter, &sub_iter));
+    pa_xfree(names);
     pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
     pa_assert_se(dbus_connection_send(conn, reply, NULL));
     dbus_message_unref(reply);
@@ -1412,11 +1442,14 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *
         dbus_error_free(&error);
         return;
 
-    }
-    else if(x_npoints != y_npoints || x_npoints < 2 || x_npoints > u->fft_size / 2 +1  ){
+    }else if(x_npoints != y_npoints || x_npoints < 2 || x_npoints > u->fft_size / 2 +1  ){
         pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs and ys must be the same length and 2<=l<=%ld!", u->fft_size / 2 + 1);
         dbus_error_free(&error);
         return;
+    }else if(xs[0] != 0 || xs[x_npoints-1] != u->fft_size / 2){
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs[0] must be 0 and xs[-1]=fft_size/2");
+        dbus_error_free(&error);
+        return;
     }
 
     ys = pa_xmalloc(x_npoints * sizeof(float));
@@ -1435,6 +1468,11 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *
     //save_state(u);
 
     pa_dbus_send_empty_reply(conn, msg);
+
+    DBusMessage *signal = NULL;
+    pa_assert_se((signal = dbus_message_new_signal(u->dbus_path, EQUALIZER_IFACE, equalizer_signals[EQUALIZER_SIGNAL_FILTER_CHANGED].name)));
+    pa_dbus_protocol_send_signal(u->dbus_protocol, signal);
+    dbus_message_unref(signal);
 }
 
 void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u) {
@@ -1500,6 +1538,11 @@ void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void
     }
     save_profile(u,name);
     pa_dbus_send_empty_reply(conn, msg);
+
+    DBusMessage *signal = NULL;
+    pa_assert_se((signal = dbus_message_new_signal(MANAGER_PATH, MANAGER_IFACE, manager_signals[MANAGER_SIGNAL_PROFILES_CHANGED].name)));
+    pa_dbus_protocol_send_signal(u->dbus_protocol, signal);
+    dbus_message_unref(signal);
 }
 
 void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u) {
@@ -1564,6 +1607,15 @@ void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u)
     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &fft_size);
 }
 
+void get_filter(struct userdata *u, double **H_){
+    *H_ = pa_xnew0(double, u->fft_size / 2 + 1);
+    float *H=u->Hs[pa_aupdate_read_begin(u->a_H)];
+    for(size_t i = 0;i < u->fft_size / 2 + 1; ++i){
+        (*H_)[i] = H[i] * u->fft_size;
+    }
+    pa_aupdate_read_end(u->a_H);
+}
+
 void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){
     pa_assert(conn);
     pa_assert(msg);
@@ -1585,6 +1637,10 @@ void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){
     pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_SAMPLERATE].property_name, DBUS_TYPE_UINT32, &rate);
     pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_FILTERSAMPLERATE].property_name, DBUS_TYPE_UINT32, &fft_size);
     pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_N_COEFS].property_name, DBUS_TYPE_UINT32, &n_coefs);
+    double *H;
+    get_filter(u, &H);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_FILTER].property_name, DBUS_TYPE_UINT32, &H);
+    pa_xfree(H);
 
     pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
     pa_assert_se(dbus_connection_send(conn, reply, NULL));
@@ -1597,44 +1653,46 @@ void equalizer_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u){
     pa_assert(_u);
 
     struct userdata *u = (struct userdata *)_u;
-    
-    unsigned n_coefs = (unsigned) (u->fft_size / 2 + 1);
-    double *H_ = (double *) pa_xmalloc0(n_coefs * sizeof(double));
-    
-    float *H=u->Hs[pa_aupdate_read_begin(u->a_H)];
-    for(size_t i = 0;i < u->fft_size / 2 + 1; ++i){
-        H_[i] = H[i] * u->fft_size;
-    }
-    pa_aupdate_read_end(u->a_H);
+    unsigned n_coefs = u->fft_size / 2 + 1;
+    double *H_;
+    get_filter(u, &H_);
     pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_DOUBLE, H_, n_coefs);
     pa_xfree(H_);
 }
 
+void set_filter(struct userdata *u, double **H_){
+    float *H = u->Hs[pa_aupdate_write_begin(u->a_H)];
+    for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){
+        H[i] = (float) (*H_)[i];
+    }
+    fix_filter(H, u->fft_size);
+    pa_aupdate_write_swap(u->a_H);
+    pa_aupdate_write_end(u->a_H);
+}
+
 void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){
     pa_assert(conn);
     pa_assert(msg);
     pa_assert(_u);
 
     struct userdata *u=(struct userdata *)_u;
-    double *H_;
+    double *H;
     unsigned _n_coefs;
-    if(pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_DOUBLE, &H_, &_n_coefs)){
+    if(pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_DOUBLE, &H, &_n_coefs)){
         return;
     }
     if(_n_coefs!=u->fft_size / 2 + 1){
         pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "This filter takes exactly %ld coefficients, you gave %d", u->fft_size / 2 + 1, _n_coefs);
         return;
     }
-    float *H = u->Hs[pa_aupdate_write_begin(u->a_H)];
-    for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){
-        H[i] = (float) H_[i];
-    }
-    fix_filter(H, u->fft_size);
-    pa_aupdate_write_swap(u->a_H);
-    pa_aupdate_write_end(u->a_H);
-
+    set_filter(u, &H);
     //Stupid for IO reasons?  Add a save signal to dbus instead
     //save_state(u);
 
     pa_dbus_send_empty_reply(conn, msg);
+
+    DBusMessage *signal = NULL;
+    pa_assert_se((signal = dbus_message_new_signal(u->dbus_path, EQUALIZER_IFACE, equalizer_signals[EQUALIZER_SIGNAL_FILTER_CHANGED].name)));
+    pa_dbus_protocol_send_signal(u->dbus_protocol, signal);
+    dbus_message_unref(signal);
 }

commit 684ad6ddb7ab0e8ba11bf6df620b3cf9c1facf8a
Author: Jason Newton <nevion at gmail.com>
Date:   Mon Aug 10 15:33:55 2009 -0700

    module-equalizer-sink:
        proper fix for pa_xmalloc(0) given that 0 is illegal
        fix coefficients in case there's no resume state
        loadprofile now signals filterchanged

diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
index 88c1074..8049a99 100755
--- a/src/modules/module-equalizer-sink.c
+++ b/src/modules/module-equalizer-sink.c
@@ -991,9 +991,13 @@ int pa__init(pa_module*m) {
     dbus_init(u);
 
     //default filter to these
+    float *H=u->Hs[pa_aupdate_write_begin(u->a_H)];
     for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){
-        u->Hs[1][i]=u->Hs[0][i] = 1.0;
+        H[i] = 1.0 / sqrtf(2.0f);
     }
+    fix_filter(H, u->fft_size);
+    pa_aupdate_write_swap(u->a_H);
+    pa_aupdate_write_end(u->a_H);
     //load old parameters
     load_state(u);
 
@@ -1303,7 +1307,7 @@ void get_sinks(pa_core *u, char ***names, unsigned *n_sinks){
     pa_idxset *sink_list;
     pa_assert_se(sink_list = pa_shared_get(u, SINKLIST));
     *n_sinks = (unsigned) pa_idxset_size(sink_list);
-    pa_assert_se(*names = pa_xnew0(char *,*n_sinks));
+    *names = *n_sinks > 0 ? pa_xnew0(char *,*n_sinks) : NULL;
     for(uint32_t i = 0; i < *n_sinks; ++i){
         sink_u = (struct userdata *) pa_idxset_iterate(sink_list, &iter, &dummy);
         (*names)[i] = pa_xstrdup(sink_u->dbus_path);
@@ -1349,7 +1353,7 @@ void get_profiles(pa_core *c, char ***names, unsigned *n){
         key = next_key;
         (*n)++;
     }
-    (*names) = pa_xnew0(char *, *n);
+    (*names) = *n > 0 ? pa_xnew0(char *, *n) : NULL;
     iter=head;
     for(unsigned i = 0; i < *n; ++i){
         (*names)[*n-1-i]=pa_xstrdup(pa_strlist_data(iter));
@@ -1569,6 +1573,11 @@ void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void
         return;
     }
     pa_dbus_send_empty_reply(conn, msg);
+
+    DBusMessage *signal = NULL;
+    pa_assert_se((signal = dbus_message_new_signal(u->dbus_path, EQUALIZER_IFACE, equalizer_signals[EQUALIZER_SIGNAL_FILTER_CHANGED].name)));
+    pa_dbus_protocol_send_signal(u->dbus_protocol, signal);
+    dbus_message_unref(signal);
 }
 
 void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){

commit 8c2f9763df96550e06e94d8e985b24a9f6675677
Author: Jason Newton <nevion at gmail.com>
Date:   Tue Aug 11 03:00:28 2009 -0700

    module-equalizer-sink:
        fix for peek returning a null memblock
        pa_log -> pa_log_debug for fft size
        updated module description
        fixed a comment in dbus error for incorrect x positions

diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
index 8049a99..270e73e 100755
--- a/src/modules/module-equalizer-sink.c
+++ b/src/modules/module-equalizer-sink.c
@@ -2,8 +2,8 @@
 This file is part of PulseAudio.
 
 This module is based off Lennart Poettering's LADSPA sink and swaps out
-LADSPA functionality for a STFT OLA based digital equalizer.  All new work
-is published under Pulseaudio's original license.
+LADSPA functionality for a dbus-aware STFT OLA based digital equalizer.
+All new work is published under Pulseaudio's original license.
 Copyright 2009 Jason Newton <nevion at gmail.com>
 
 Original Author:
@@ -562,11 +562,13 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
         //buffer->index = 0;
         //pa_memblock_ref(buffer->memblock);
         //pa_sink_render_into(u->sink, buffer);
-        while(pa_memblockq_peek(u->input_q, &tchunk) < 0){
+        while(pa_memblockq_peek(u->input_q, &tchunk) < 0 || tchunk.memblock == NULL){
             pa_sink_render(u->sink, input_remaining*fs, &tchunk);
+            pa_assert(tchunk.memblock);
             pa_memblockq_push(u->input_q, &tchunk);
             pa_memblock_unref(tchunk.memblock);
         }
+        pa_assert(tchunk.memblock);
         tchunk.length = PA_MIN(input_remaining*fs, tchunk.length);
         pa_memblockq_drop(u->input_q, tchunk.length);
         //pa_log_debug("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs);
@@ -878,7 +880,7 @@ int pa__init(pa_module*m) {
 
     u->channels = ss.channels;
     u->fft_size = pow(2, ceil(log(ss.rate)/log(2)));
-    pa_log("fft size: %ld", u->fft_size);
+    pa_log_debug("fft size: %ld", u->fft_size);
     u->window_size = 15999;
     u->R = (u->window_size+1)/2;
     u->overlap_size = u->window_size-u->R;
@@ -1442,7 +1444,7 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *
         }
     }
     if(!is_monotonic(xs,x_npoints) || !points_good){
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs must be monotonic and 0<x<%ld", u->fft_size / 2);
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs must be monotonic and 0<=x<=%ld", u->fft_size / 2);
         dbus_error_free(&error);
         return;
 

commit 38d608ad5a0a3fa0ff7f93007dde46d8661348c3
Author: Jason Newton <nevion at gmail.com>
Date:   Sat Aug 15 05:22:29 2009 -0700

    module-equalizer-sink:
        reworked processing so we don't have input->output delay of R samples

diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
index 270e73e..5f16cd6 100755
--- a/src/modules/module-equalizer-sink.c
+++ b/src/modules/module-equalizer-sink.c
@@ -103,7 +103,6 @@ struct userdata {
     size_t samples_gathered;
     size_t max_output;//max amount of samples outputable in a single
     //message
-    size_t target_samples;
     float *H;//frequency response filter (magnitude based)
     float *W;//windowing function (time domain)
     float *work_buffer, **input, **overlap_accum;
@@ -115,6 +114,7 @@ struct userdata {
     pa_aupdate *a_H;
     pa_memchunk conv_buffer;
     pa_memblockq *input_q;
+    int first_iteration;
 
     pa_dbus_protocol *dbus_protocol;
     char *dbus_path;
@@ -135,11 +135,13 @@ static const char* const valid_modargs[] = {
 
 static uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p);
 static void hanning_window(float *W, size_t window_size);
-void fix_filter(float *H, size_t fft_size);
-void interpolate(float *signal, size_t length, uint32_t *xs, float *ys, size_t n_points);
+static void fix_filter(float *H, size_t fft_size);
+static void interpolate(float *signal, size_t length, uint32_t *xs, float *ys, size_t n_points);
 static void array_out(const char *name, float *a, size_t length);
 static int is_monotonic(uint32_t *xs, size_t length);
+static void reset_filter(struct userdata *u);
 static void process_samples(struct userdata *u, pa_memchunk *tchunk);
+static void initialize_buffer(struct userdata *u, pa_memchunk *in);
 static void input_buffer(struct userdata *u, pa_memchunk *in);
 static void save_profile(struct userdata *u,char *name);
 static void save_state(struct userdata *u);
@@ -274,13 +276,13 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
         case PA_SINK_MESSAGE_GET_LATENCY: {
             pa_usec_t usec = 0;
             pa_sample_spec *ss=&u->sink->sample_spec;
-            //size_t fs=pa_frame_size(&(u->sink->sample_spec));
+            size_t fs=pa_frame_size(&(u->sink->sample_spec));
 
             /* Get the latency of the master sink */
             if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
                 usec = 0;
 
-            //usec += pa_bytes_to_usec(u->latency * fs, ss);
+            usec += pa_bytes_to_usec(u->latency * fs, ss);
             //usec += pa_bytes_to_usec(u->samples_gathered * fs, ss);
             usec += pa_bytes_to_usec(pa_memblockq_get_length(u->input_q), ss);
             /* Add the latency internal to our sink input on top */
@@ -340,10 +342,10 @@ static void process_samples(struct userdata *u, pa_memchunk *tchunk){
     size_t fs=pa_frame_size(&(u->sink->sample_spec));
     pa_assert(u->samples_gathered >= u->R);
     float *dst;
-    tchunk->index=0;
-    tchunk->length=u->R*fs;
-    tchunk->memblock=pa_memblock_new(u->core->mempool, tchunk->length);
-    dst=((float*)pa_memblock_acquire(tchunk->memblock));
+    tchunk->index = 0;
+    tchunk->length = u->R * fs;
+    tchunk->memblock = pa_memblock_new(u->core->mempool, tchunk->length);
+    dst = ((float*)pa_memblock_acquire(tchunk->memblock));
     for(size_t c=0;c < u->channels; c++) {
         dsp_logic(
             u->work_buffer,
@@ -354,13 +356,21 @@ static void process_samples(struct userdata *u, pa_memchunk *tchunk){
             u->output_window,
             u
         );
+        if(u->first_iteration){
+            /* The windowing function will make the audio ramped in, as a cheap fix we can
+             * undo the windowing (for non-zero window values)
+             */
+            for(size_t i = 0;i < u->overlap_size; ++i){
+                u->work_buffer[i] = u->W[i] <= FLT_EPSILON ? u->W[i] : u->work_buffer[i] / u->W[i];
+            }
+        }
         pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst + c, fs, u->work_buffer, sizeof(float), u->R);
     }
     pa_memblock_release(tchunk->memblock);
-    u->samples_gathered-=u->R;
+    u->samples_gathered-= u->R;
 }
 
-typedef float v4sf __attribute__ ((__aligned__(v_size*sizeof(float))));
+typedef float v4sf __attribute__ ((__aligned__(v_size * sizeof(float))));
 typedef union float_vector {
     float f[v_size];
     v4sf v;
@@ -516,20 +526,34 @@ void dsp_logic(
 //    );
 //}
 
+void initialize_buffer(struct userdata *u, pa_memchunk *in){
+    size_t fs = pa_frame_size(&(u->sink->sample_spec));
+    size_t samples = in->length/fs;
+    pa_assert_se(u->samples_gathered + samples == u->window_size);
+    float *src = (float*) ((uint8_t*) pa_memblock_acquire(in->memblock) + in->index);
+    for(size_t c = 0; c < u->channels; c++) {
+        //buffer with an offset after the overlap from previous
+        //iterations
+        pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c]+u->samples_gathered, sizeof(float), src + c, fs, samples);
+    }
+    u->samples_gathered+=samples;
+    pa_memblock_release(in->memblock);
+}
+
 void input_buffer(struct userdata *u, pa_memchunk *in){
     size_t fs = pa_frame_size(&(u->sink->sample_spec));
     size_t samples = in->length/fs;
-    pa_assert_se(samples <= u->target_samples-u->samples_gathered);
+    pa_assert_se(samples <= u->window_size - u->samples_gathered);
     float *src = (float*) ((uint8_t*) pa_memblock_acquire(in->memblock) + in->index);
     for(size_t c = 0; c < u->channels; c++) {
         //buffer with an offset after the overlap from previous
         //iterations
         pa_assert_se(
-            u->input[c]+u->overlap_size+u->samples_gathered+samples <= u->input[c]+u->overlap_size+u->target_samples
+            u->input[c]+u->overlap_size+u->samples_gathered+samples <= u->input[c]+u->window_size
         );
         pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c]+u->overlap_size+u->samples_gathered, sizeof(float), src + c, fs, samples);
     }
-    u->samples_gathered+=samples;
+    u->samples_gathered += samples;
     pa_memblock_release(in->memblock);
 }
 
@@ -553,8 +577,8 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
     struct timespec start, end;
     gettime(start);
     do{
-        size_t input_remaining = u->target_samples-u->samples_gathered;
-        pa_assert(input_remaining>0);
+        size_t input_remaining = u->window_size - u->samples_gathered;
+        pa_assert(input_remaining > 0);
         //collect samples
 
         //buffer = &u->conv_buffer;
@@ -563,7 +587,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
         //pa_memblock_ref(buffer->memblock);
         //pa_sink_render_into(u->sink, buffer);
         while(pa_memblockq_peek(u->input_q, &tchunk) < 0 || tchunk.memblock == NULL){
-            pa_sink_render(u->sink, input_remaining*fs, &tchunk);
+            pa_sink_render_full(u->sink, input_remaining*fs, &tchunk);
             pa_assert(tchunk.memblock);
             pa_memblockq_push(u->input_q, &tchunk);
             pa_memblock_unref(tchunk.memblock);
@@ -574,11 +598,15 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
         //pa_log_debug("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs);
         /* copy new input */
         //gettime(start);
-        input_buffer(u, &tchunk);
+        if(u->first_iteration){
+            initialize_buffer(u, &tchunk);
+        }else{
+            input_buffer(u, &tchunk);
+        }
         //gettime(end);
         //pa_log_debug("Took %0.5f seconds to setup", tdiff(end, start)*1e-9);
         pa_memblock_unref(tchunk.memblock);
-    }while(u->samples_gathered < u->R);
+    }while(u->samples_gathered <= u->window_size);
     gettime(end);
     pa_log_debug("Took %0.6f seconds to get data", tdiff(end, start)*1e-9);
 
@@ -597,6 +625,9 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
     pa_assert(chunk->memblock);
     //pa_log_debug("gave %ld", chunk->length/fs);
     //pa_log_debug("end pop");
+    if(u->first_iteration){
+        u->first_iteration = 0;
+    }
     return 0;
 }
 
@@ -627,8 +658,8 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
             pa_memblockq_seek(u->input_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE);
             //pa_memblockq_drop(u->input_q, pa_memblockq_get_length(u->input_q));
             //pa_memblockq_seek(u->input_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE);
-            pa_log_debug("Resetting equalizer");
-            u->samples_gathered = 0;
+            pa_log("Resetting filter");
+            reset_filter(u);
         }
     }
 
@@ -636,6 +667,14 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
     pa_memblockq_rewind(u->input_q, nbytes);
 }
 
+void reset_filter(struct userdata *u){
+    u->samples_gathered = 0;
+    for(size_t i = 0;i < u->channels; ++i){
+        memset(u->overlap_accum[i], 0, u->overlap_size * sizeof(float));
+    }
+    u->first_iteration = 1;
+}
+
 /* Called from I/O thread context */
 static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
     struct userdata *u;
@@ -882,15 +921,13 @@ int pa__init(pa_module*m) {
     u->fft_size = pow(2, ceil(log(ss.rate)/log(2)));
     pa_log_debug("fft size: %ld", u->fft_size);
     u->window_size = 15999;
-    u->R = (u->window_size+1)/2;
-    u->overlap_size = u->window_size-u->R;
-    u->target_samples = 1*u->R;
+    u->R = (u->window_size + 1) / 2;
+    u->overlap_size = u->window_size - u->R;
     u->samples_gathered = 0;
-    u->max_output = pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss)/pa_frame_size(&ss);
+    u->max_output = pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss) / pa_frame_size(&ss);
     u->input_q = pa_memblockq_new(0,  MEMBLOCKQ_MAXLENGTH, 0, fs, 1, 1, 0, NULL);
     u->a_H = pa_aupdate_new();
-    u->conv_buffer.memblock = pa_memblock_new(u->core->mempool, u->target_samples*fs);
-    u->latency = u->R;
+    u->latency = u->window_size - u->R;
     for(size_t i = 0; i < 2; ++i){
         u->Hs[i] = alloc((u->fft_size / 2 + 1), sizeof(float));
     }
@@ -900,9 +937,9 @@ int pa__init(pa_module*m) {
     u->input = (float **)pa_xmalloc0(sizeof(float *)*u->channels);
     u->overlap_accum = (float **)pa_xmalloc0(sizeof(float *)*u->channels);
     for(size_t c = 0; c < u->channels; ++c){
-        u->input[c] = alloc(u->overlap_size+u->target_samples, sizeof(float));
+        u->input[c] = alloc(u->window_size, sizeof(float));
         pa_assert_se(u->input[c]);
-        memset(u->input[c], 0, (u->overlap_size+u->target_samples)*sizeof(float));
+        memset(u->input[c], 0, (u->window_size)*sizeof(float));
         pa_assert_se(u->input[c]);
         u->overlap_accum[c] = alloc(u->overlap_size, sizeof(float));
         pa_assert_se(u->overlap_accum[c]);
@@ -913,6 +950,7 @@ int pa__init(pa_module*m) {
     u->inverse_plan = fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_ESTIMATE);
 
     hanning_window(u->W, u->window_size);
+    u->first_iteration = 1;
 
     /* Create sink */
     pa_sink_new_data_init(&sink_data);
@@ -1048,7 +1086,6 @@ void pa__done(pa_module*m) {
     }
 
     pa_aupdate_free(u->a_H);
-    pa_memblock_unref(u->conv_buffer.memblock);
     pa_memblockq_free(u->input_q);
 
     fftwf_destroy_plan(u->inverse_plan);

commit 0e6711ddd0659d62613e8812b33e36d581875ba0
Author: Jason Newton <nevion at gmail.com>
Date:   Sat Aug 15 06:17:40 2009 -0700

    module-equalizer-sink:
        merging in upstream changes
        whitespace fix and fix for first iteration un-windowing

diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
index 5f16cd6..944aa7a 100755
--- a/src/modules/module-equalizer-sink.c
+++ b/src/modules/module-equalizer-sink.c
@@ -81,9 +81,8 @@ PA_MODULE_USAGE(_("sink=<sink to connect to> "));
 
 
 struct userdata {
-    pa_core *core;
     pa_module *module;
-    pa_sink *sink, *master;
+    pa_sink *sink;
     pa_sink_input *sink_input;
     char *name;
 
@@ -101,7 +100,6 @@ struct userdata {
     //for twiddling with pulseaudio
     size_t overlap_size;//window_size-R
     size_t samples_gathered;
-    size_t max_output;//max amount of samples outputable in a single
     //message
     float *H;//frequency response filter (magnitude based)
     float *W;//windowing function (time domain)
@@ -274,20 +272,26 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
     switch (code) {
 
         case PA_SINK_MESSAGE_GET_LATENCY: {
-            pa_usec_t usec = 0;
-            pa_sample_spec *ss=&u->sink->sample_spec;
-            size_t fs=pa_frame_size(&(u->sink->sample_spec));
-
-            /* Get the latency of the master sink */
-            if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
-                usec = 0;
-
-            usec += pa_bytes_to_usec(u->latency * fs, ss);
-            //usec += pa_bytes_to_usec(u->samples_gathered * fs, ss);
-            usec += pa_bytes_to_usec(pa_memblockq_get_length(u->input_q), ss);
-            /* Add the latency internal to our sink input on top */
-            usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec);
-            *((pa_usec_t*) data) = usec;
+            size_t fs=pa_frame_size(&u->sink->sample_spec);
+
+            /* The sink is _put() before the sink input is, so let's
+             * make sure we don't access it in that time. Also, the
+             * sink input is first shut down, the sink second. */
+            if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) ||
+                !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) {
+                *((pa_usec_t*) data) = 0;
+                return 0;
+            }
+
+            *((pa_usec_t*) data) =
+                /* Get the latency of the master sink */
+                pa_sink_get_latency_within_thread(u->sink_input->sink) +
+
+                /* Add the latency internal to our sink input on top */
+                pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->sink_input->sink->sample_spec) +
+                pa_bytes_to_usec(u->samples_gathered * fs, &u->sink->sample_spec);
+            //+ pa_bytes_to_usec(u->latency * fs, ss)
+            //+ pa_bytes_to_usec(pa_memblockq_get_length(u->input_q), ss);
             return 0;
         }
     }
@@ -303,12 +307,11 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
     pa_sink_assert_ref(s);
     pa_assert_se(u = s->userdata);
 
-    if (PA_SINK_IS_LINKED(state) &&
-        u->sink_input &&
-        PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input)))
-
-        pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED);
+    if (!PA_SINK_IS_LINKED(state) ||
+        !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input)))
+        return 0;
 
+    pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED);
     return 0;
 }
 
@@ -319,7 +322,9 @@ static void sink_request_rewind(pa_sink *s) {
     pa_sink_assert_ref(s);
     pa_assert_se(u = s->userdata);
 
-    //pa_memblockq_drop(u->input_q,pa_memblockq_get_length(u->input_q));
+    if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) ||
+        !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state))
+        return;
 
     /* Just hand this one over to the master sink */
     pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes+pa_memblockq_get_length(u->input_q), TRUE, FALSE, FALSE);
@@ -332,6 +337,10 @@ static void sink_update_requested_latency(pa_sink *s) {
     pa_sink_assert_ref(s);
     pa_assert_se(u = s->userdata);
 
+    if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) ||
+        !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state))
+        return;
+
     /* Just hand this one over to the master sink */
     pa_sink_input_set_requested_latency_within_thread(
             u->sink_input,
@@ -344,7 +353,7 @@ static void process_samples(struct userdata *u, pa_memchunk *tchunk){
     float *dst;
     tchunk->index = 0;
     tchunk->length = u->R * fs;
-    tchunk->memblock = pa_memblock_new(u->core->mempool, tchunk->length);
+    tchunk->memblock = pa_memblock_new(u->sink->core->mempool, tchunk->length);
     dst = ((float*)pa_memblock_acquire(tchunk->memblock));
     for(size_t c=0;c < u->channels; c++) {
         dsp_logic(
@@ -361,7 +370,7 @@ static void process_samples(struct userdata *u, pa_memchunk *tchunk){
              * undo the windowing (for non-zero window values)
              */
             for(size_t i = 0;i < u->overlap_size; ++i){
-                u->work_buffer[i] = u->W[i] <= FLT_EPSILON ? u->W[i] : u->work_buffer[i] / u->W[i];
+                u->work_buffer[i] = u->W[i] <= FLT_EPSILON ? u->work_buffer[i] : u->work_buffer[i] / u->W[i];
             }
         }
         pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst + c, fs, u->work_buffer, sizeof(float), u->R);
@@ -529,7 +538,7 @@ void dsp_logic(
 void initialize_buffer(struct userdata *u, pa_memchunk *in){
     size_t fs = pa_frame_size(&(u->sink->sample_spec));
     size_t samples = in->length/fs;
-    pa_assert_se(u->samples_gathered + samples == u->window_size);
+    pa_assert_se(u->samples_gathered + samples <= u->window_size);
     float *src = (float*) ((uint8_t*) pa_memblock_acquire(in->memblock) + in->index);
     for(size_t c = 0; c < u->channels; c++) {
         //buffer with an offset after the overlap from previous
@@ -549,7 +558,7 @@ void input_buffer(struct userdata *u, pa_memchunk *in){
         //buffer with an offset after the overlap from previous
         //iterations
         pa_assert_se(
-            u->input[c]+u->overlap_size+u->samples_gathered+samples <= u->input[c]+u->window_size
+            u->input[c]+u->samples_gathered+samples <= u->input[c]+u->window_size
         );
         pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c]+u->overlap_size+u->samples_gathered, sizeof(float), src + c, fs, samples);
     }
@@ -561,14 +570,12 @@ void input_buffer(struct userdata *u, pa_memchunk *in){
 static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
     struct userdata *u;
     pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
     pa_assert(chunk);
-    pa_assert(u = i->userdata);
     pa_assert(u->sink);
     size_t fs = pa_frame_size(&(u->sink->sample_spec));
     pa_memchunk tchunk;
     chunk->memblock = NULL;
-    if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
-        return -1;
 
     /* Hmm, process any rewind request that might be queued up */
     pa_sink_process_rewind(u->sink, 0);
@@ -587,13 +594,13 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
         //pa_memblock_ref(buffer->memblock);
         //pa_sink_render_into(u->sink, buffer);
         while(pa_memblockq_peek(u->input_q, &tchunk) < 0 || tchunk.memblock == NULL){
-            pa_sink_render_full(u->sink, input_remaining*fs, &tchunk);
+            pa_sink_render(u->sink, input_remaining*fs, &tchunk);
             pa_assert(tchunk.memblock);
             pa_memblockq_push(u->input_q, &tchunk);
             pa_memblock_unref(tchunk.memblock);
         }
         pa_assert(tchunk.memblock);
-        tchunk.length = PA_MIN(input_remaining*fs, tchunk.length);
+        tchunk.length = PA_MIN(input_remaining * fs, tchunk.length);
         pa_memblockq_drop(u->input_q, tchunk.length);
         //pa_log_debug("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs);
         /* copy new input */
@@ -606,7 +613,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
         //gettime(end);
         //pa_log_debug("Took %0.5f seconds to setup", tdiff(end, start)*1e-9);
         pa_memblock_unref(tchunk.memblock);
-    }while(u->samples_gathered <= u->window_size);
+    }while(u->samples_gathered < u->window_size);
     gettime(end);
     pa_log_debug("Took %0.6f seconds to get data", tdiff(end, start)*1e-9);
 
@@ -617,7 +624,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
     u->H = u->Hs[H_i];
     gettime(start);
     /* process a block */
-    process_samples(u,chunk);
+    process_samples(u, chunk);
     gettime(end);
     pa_log_debug("Took %0.6f seconds to process", tdiff(end, start)*1e-9);
     pa_aupdate_read_end(u->a_H);
@@ -640,9 +647,6 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
     pa_sink_input_assert_ref(i);
     pa_assert_se(u = i->userdata);
 
-    if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
-        return;
-
     if (u->sink->thread_info.rewind_nbytes > 0) {
         size_t max_rewrite;
 
@@ -682,9 +686,6 @@ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
     pa_sink_input_assert_ref(i);
     pa_assert_se(u = i->userdata);
 
-    if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
-        return;
-
     pa_memblockq_set_maxrewind(u->input_q, nbytes);
     pa_sink_set_max_rewind_within_thread(u->sink, nbytes);
 }
@@ -696,9 +697,6 @@ static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
     pa_sink_input_assert_ref(i);
     pa_assert_se(u = i->userdata);
 
-    if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
-        return;
-
     size_t fs = pa_frame_size(&(u->sink->sample_spec));
     //pa_sink_set_max_request_within_thread(u->sink, nbytes);
     //pa_sink_set_max_request_within_thread(u->sink, u->R*fs);
@@ -712,26 +710,30 @@ static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) {
     pa_sink_input_assert_ref(i);
     pa_assert_se(u = i->userdata);
 
-    if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
-        return;
-
     //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs);
     //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs );
     pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
 }
 
 /* Called from I/O thread context */
-static void sink_input_detach_cb(pa_sink_input *i) {
+static void sink_input_update_sink_fixed_latency_cb(pa_sink_input *i) {
     struct userdata *u;
 
     pa_sink_input_assert_ref(i);
     pa_assert_se(u = i->userdata);
 
-    if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
-        return;
+    pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency);
+}
+
+/* Called from I/O thread context */
+static void sink_input_detach_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
 
     pa_sink_detach_within_thread(u->sink);
-    pa_sink_set_asyncmsgq(u->sink, NULL);
+
     pa_sink_set_rtpoll(u->sink, NULL);
 }
 
@@ -742,11 +744,10 @@ static void sink_input_attach_cb(pa_sink_input *i) {
     pa_sink_input_assert_ref(i);
     pa_assert_se(u = i->userdata);
 
-    if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
-        return;
+    pa_sink_set_rtpoll(u->sink, i->sink->thread_info.rtpoll);
+    pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
 
-    pa_sink_set_asyncmsgq(u->sink, i->sink->asyncmsgq);
-    pa_sink_set_rtpoll(u->sink, i->sink->rtpoll);
+    pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency);
     pa_sink_attach_within_thread(u->sink);
 
     //size_t fs = pa_frame_size(&(u->sink->sample_spec));
@@ -756,7 +757,8 @@ static void sink_input_attach_cb(pa_sink_input *i) {
     //of them completely, figure out why
     //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs);
     //TODO: this guy causes dropouts constantly+rewinds, it's unusable
-    pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency);
+    //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency);
+    pa_sink_attach_within_thread(u->sink);
 }
 
 /* Called from main context */
@@ -766,14 +768,18 @@ static void sink_input_kill_cb(pa_sink_input *i) {
     pa_sink_input_assert_ref(i);
     pa_assert_se(u = i->userdata);
 
-    pa_sink_unlink(u->sink);
+    /* The order here matters! We first kill the sink input, followed
+     * by the sink. That means the sink callbacks must be protected
+     * against an unconnected sink input! */
     pa_sink_input_unlink(u->sink_input);
+    pa_sink_unlink(u->sink);
 
-    pa_sink_unref(u->sink);
-    u->sink = NULL;
     pa_sink_input_unref(u->sink_input);
     u->sink_input = NULL;
 
+    pa_sink_unref(u->sink);
+    u->sink = NULL;
+
     pa_module_unload_request(u->module, TRUE);
 }
 
@@ -863,6 +869,16 @@ static pa_bool_t sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) {
     return u->sink != dest;
 }
 
+/* Called from main context */
+static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_sink_set_asyncmsgq(u->sink, dest->asyncmsgq);
+    pa_sink_update_flags(u->sink, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY, dest->flags);
+}
 
 //ensure's memory allocated is a multiple of v_size
 //and aligned
@@ -910,12 +926,8 @@ int pa__init(pa_module*m) {
     fs = pa_frame_size(&ss);
 
     u = pa_xnew0(struct userdata, 1);
-    u->core = m->core;
     u->module = m;
     m->userdata = u;
-    u->master = master;
-    u->sink = NULL;
-    u->sink_input = NULL;
 
     u->channels = ss.channels;
     u->fft_size = pow(2, ceil(log(ss.rate)/log(2)));
@@ -924,7 +936,6 @@ int pa__init(pa_module*m) {
     u->R = (u->window_size + 1) / 2;
     u->overlap_size = u->window_size - u->R;
     u->samples_gathered = 0;
-    u->max_output = pa_frame_align(pa_mempool_block_size_max(m->core->mempool), &ss) / pa_frame_size(&ss);
     u->input_q = pa_memblockq_new(0,  MEMBLOCKQ_MAXLENGTH, 0, fs, 1, 1, 0, NULL);
     u->a_H = pa_aupdate_new();
     u->latency = u->window_size - u->R;
@@ -958,11 +969,10 @@ int pa__init(pa_module*m) {
     sink_data.module = m;
     if (!(sink_data.name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL))))
         sink_data.name = pa_sprintf_malloc("%s.equalizer", master->name);
-    sink_data.namereg_fail = FALSE;
     pa_sink_new_data_set_sample_spec(&sink_data, &ss);
     pa_sink_new_data_set_channel_map(&sink_data, &map);
     z = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION);
-    pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "FFT based equalizer");
+    pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "FFT based equalizer on %s",z? z: master->name);
     pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name);
     pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter");
 
@@ -972,7 +982,7 @@ int pa__init(pa_module*m) {
         goto fail;
     }
 
-    u->sink = pa_sink_new(m->core, &sink_data, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY);
+    u->sink = pa_sink_new(m->core, &sink_data, master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY));
     pa_sink_new_data_done(&sink_data);
 
     if (!u->sink) {
@@ -987,7 +997,6 @@ int pa__init(pa_module*m) {
     u->sink->userdata = u;
 
     pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq);
-    pa_sink_set_rtpoll(u->sink, master->rtpoll);
     pa_sink_set_max_request(u->sink,
         ((pa_sink_get_max_request(u->sink)+u->R*fs-1)/(u->R*fs))*(u->R*fs)
     );
@@ -997,13 +1006,13 @@ int pa__init(pa_module*m) {
     pa_sink_input_new_data_init(&sink_input_data);
     sink_input_data.driver = __FILE__;
     sink_input_data.module = m;
-    sink_input_data.sink = u->master;
+    sink_input_data.sink = master;
     pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Equalized Stream");
     pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
     pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss);
     pa_sink_input_new_data_set_channel_map(&sink_input_data, &map);
 
-    pa_sink_input_new(&u->sink_input, m->core, &sink_input_data, PA_SINK_INPUT_DONT_MOVE);
+    pa_sink_input_new(&u->sink_input, m->core, &sink_input_data, 0);
     pa_sink_input_new_data_done(&sink_input_data);
 
     if (!u->sink_input)
@@ -1014,11 +1023,13 @@ int pa__init(pa_module*m) {
     u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
     u->sink_input->update_max_request = sink_input_update_max_request_cb;
     u->sink_input->update_sink_latency_range = sink_input_update_sink_latency_range_cb;
+    u->sink_input->update_sink_fixed_latency = sink_input_update_sink_fixed_latency_cb;
     u->sink_input->kill = sink_input_kill_cb;
     u->sink_input->attach = sink_input_attach_cb;
     u->sink_input->detach = sink_input_detach_cb;
     u->sink_input->state_change = sink_input_state_change_cb;
     u->sink_input->may_move_to = sink_input_may_move_to_cb;
+    u->sink_input->moving = sink_input_moving_cb;
     u->sink_input->userdata = u;
 
     pa_sink_put(u->sink);
@@ -1075,15 +1086,20 @@ void pa__done(pa_module*m) {
 
     dbus_done(u);
 
-    if (u->sink) {
-        pa_sink_unlink(u->sink);
-        pa_sink_unref(u->sink);
-    }
+    /* See comments in sink_input_kill_cb() above regarding
+     * destruction order! */
 
-    if (u->sink_input) {
+    if (u->sink_input)
         pa_sink_input_unlink(u->sink_input);
+
+    if (u->sink)
+        pa_sink_unlink(u->sink);
+
+    if (u->sink_input)
         pa_sink_input_unref(u->sink_input);
-    }
+
+    if (u->sink)
+        pa_sink_unref(u->sink);
 
     pa_aupdate_free(u->a_H);
     pa_memblockq_free(u->input_q);
@@ -1252,21 +1268,21 @@ static pa_dbus_interface_info equalizer_info={
 };
 
 void dbus_init(struct userdata *u){
-    u->dbus_protocol=pa_dbus_protocol_get(u->core);
+    u->dbus_protocol=pa_dbus_protocol_get(u->sink->core);
     u->dbus_path=pa_sprintf_malloc("/org/pulseaudio/core1/sink%d", u->sink->index);
 
     pa_dbus_protocol_add_interface(u->dbus_protocol, u->dbus_path, &equalizer_info, u);
-    pa_idxset *sink_list=pa_shared_get(u->core, SINKLIST);
-    u->database=pa_shared_get(u->core, EQDB);
+    pa_idxset *sink_list=pa_shared_get(u->sink->core, SINKLIST);
+    u->database=pa_shared_get(u->sink->core, EQDB);
     if(sink_list==NULL){
         sink_list=pa_idxset_new(&pa_idxset_trivial_hash_func, &pa_idxset_trivial_compare_func);
-        pa_shared_set(u->core, SINKLIST, sink_list);
+        pa_shared_set(u->sink->core, SINKLIST, sink_list);
         char *dbname;
         pa_assert_se(dbname = pa_state_path("equalizers", TRUE));
         pa_assert_se(u->database = pa_database_open(dbname, TRUE));
         pa_xfree(dbname);
-        pa_shared_set(u->core,EQDB,u->database);
-        pa_dbus_protocol_add_interface(u->dbus_protocol, MANAGER_PATH, &manager_info, u->core);
+        pa_shared_set(u->sink->core,EQDB,u->database);
+        pa_dbus_protocol_add_interface(u->dbus_protocol, MANAGER_PATH, &manager_info, u->sink->core);
         pa_dbus_protocol_register_extension(u->dbus_protocol, EXTNAME);
     }
     uint32_t dummy;
@@ -1289,14 +1305,14 @@ void dbus_done(struct userdata *u){
     pa_dbus_protocol_send_signal(u->dbus_protocol, signal);
     dbus_message_unref(signal);
 
-    pa_assert_se(sink_list=pa_shared_get(u->core,SINKLIST));
+    pa_assert_se(sink_list=pa_shared_get(u->sink->core,SINKLIST));
     pa_idxset_remove_by_data(sink_list,u,&dummy);
     if(pa_idxset_size(sink_list)==0){
         pa_dbus_protocol_unregister_extension(u->dbus_protocol, EXTNAME);
         pa_dbus_protocol_remove_interface(u->dbus_protocol, MANAGER_PATH, manager_info.name);
-        pa_shared_remove(u->core, EQDB);
+        pa_shared_remove(u->sink->core, EQDB);
         pa_database_close(u->database);
-        pa_shared_remove(u->core, SINKLIST);
+        pa_shared_remove(u->sink->core, SINKLIST);
         pa_xfree(sink_list);
     }
     pa_dbus_protocol_remove_interface(u->dbus_protocol, u->dbus_path, equalizer_info.name);

commit 1c1a812b32897a6aa217bbe381e9e82f3b2728fc
Author: Jason Newton <nevion at gmail.com>
Date:   Sun Aug 16 11:38:12 2009 -0700

    module-equalizer-sink
        exchanged improper usage of memblockq_peek'd memchunk for silence block
        dropped unneeded function prototypes
        changed mround to be slightly more elegant
        __restrict__ -> restrict for c99
        removed unneeded pa_aupdate_swap calls
        first_iteration -> pa_bool_t
        cleaned up some usage of pa_malloc0 where pa_new0 was more appropriate
        cruft removal, whitespace fixes and reordering of variables

diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
index 944aa7a..b7f61c6 100755
--- a/src/modules/module-equalizer-sink.c
+++ b/src/modules/module-equalizer-sink.c
@@ -38,7 +38,9 @@ USA.
 
 #include <pulse/xmalloc.h>
 #include <pulse/i18n.h>
+#include <pulse/timeval.h>
 
+#include <pulsecore/aupdate.h>
 #include <pulsecore/core-error.h>
 #include <pulsecore/namereg.h>
 #include <pulsecore/sink.h>
@@ -112,7 +114,7 @@ struct userdata {
     pa_aupdate *a_H;
     pa_memchunk conv_buffer;
     pa_memblockq *input_q;
-    int first_iteration;
+    pa_bool_t first_iteration;
 
     pa_dbus_protocol *dbus_protocol;
     char *dbus_path;
@@ -131,103 +133,43 @@ static const char* const valid_modargs[] = {
     NULL
 };
 
-static uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p);
-static void hanning_window(float *W, size_t window_size);
-static void fix_filter(float *H, size_t fft_size);
-static void interpolate(float *signal, size_t length, uint32_t *xs, float *ys, size_t n_points);
-static void array_out(const char *name, float *a, size_t length);
-static int is_monotonic(uint32_t *xs, size_t length);
-static void reset_filter(struct userdata *u);
-static void process_samples(struct userdata *u, pa_memchunk *tchunk);
-static void initialize_buffer(struct userdata *u, pa_memchunk *in);
-static void input_buffer(struct userdata *u, pa_memchunk *in);
-static void save_profile(struct userdata *u,char *name);
-static void save_state(struct userdata *u);
-static void remove_profile(pa_core *u,char *name);
-static const char * load_profile(struct userdata *u,char *name);
-static void load_state(struct userdata *u);
-
-void dsp_logic(
-    float * __restrict__ dst,
-    float * __restrict__ src,
-    float * __restrict__ overlap,
-    const float * __restrict__ H,
-    const float * __restrict__ W,
-    fftwf_complex * __restrict__ output_window,
-    struct userdata *u);
-
-
-/*
- * DBus Routines and Callbacks
- */
-#define EXTNAME "org.PulseAudio.Ext.Equalizing1"
-#define MANAGER_PATH "/org/pulseaudio/equalizing1"
-#define MANAGER_IFACE EXTNAME ".Manager"
-#define EQUALIZER_IFACE EXTNAME ".Equalizer"
-static void dbus_init(struct userdata *u);
-static void dbus_done(struct userdata *u);
-static void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u);
-static void get_sinks(pa_core *u, char ***names, unsigned *n_sinks);
-static void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u);
-static void get_profiles(pa_core *u, char ***names, unsigned *n_sinks);
-static void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u);
-static void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u);
-static void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void *_u);
-static void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u);
-static void equalizer_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u);
-static void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u);
-static void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u);
-static void equalizer_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u);
-static void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u);
-static void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u);
-static void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *_u);
-static void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u);
-static void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u);
-static void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u);
-static void get_filter(struct userdata *u, double **H_);
-static void set_filter(struct userdata *u, double **H_);
 
 #define v_size 4
-#define gettime(x) clock_gettime(CLOCK_MONOTONIC, &x)
-#define tdiff(x, y) time_diff(&x, &y)
-#define mround(x, y) (x % y == 0 ? x : ( x / y + 1) * y)
+#define mround(x, y) ((x + y - 1) / y) * y
 #define SINKLIST "equalized_sinklist"
 #define EQDB "equalizer_db"
+static void dbus_init(struct userdata *u);
+static void dbus_done(struct userdata *u);
 
-uint64_t time_diff(struct timespec *timeA_p, struct timespec *timeB_p)
-{
-    return ((timeA_p->tv_sec * 1000000000ULL) + timeA_p->tv_nsec) -
-    ((timeB_p->tv_sec * 1000000000ULL) + timeB_p->tv_nsec);
-}
-
-void hanning_window(float *W, size_t window_size){
+static void hanning_window(float *W, size_t window_size){
     //h=.5*(1-cos(2*pi*j/(window_size+1)), COLA for R=(M+1)/2
     for(size_t i=0; i < window_size;++i){
         W[i] = (float).5*(1-cos(2*M_PI*i/(window_size+1)));
     }
 }
 
-void fix_filter(float *H, size_t fft_size){
+static void fix_filter(float *H, size_t fft_size){
     //divide out the fft gain
     for(size_t i = 0; i < fft_size / 2 + 1; ++i){
         H[i] /= fft_size;
     }
 }
 
-void interpolate(float *signal, size_t length, uint32_t *xs, float *ys, size_t n_points){
+static void interpolate(float *signal, size_t length, uint32_t *xs, float *ys, size_t n_points){
     //Note that xs must be monotonically increasing!
+    float x_range_lower, x_range_upper, c0;
     pa_assert_se(n_points>=2);
     pa_assert_se(xs[0] == 0);
     pa_assert_se(xs[n_points - 1] == length - 1);
     for(size_t x = 0, x_range_lower_i = 0; x < length-1; ++x){
         pa_assert(x_range_lower_i < n_points-1);
-        float x_range_lower = (float) (xs[x_range_lower_i]);
-        float x_range_upper = (float) (xs[x_range_lower_i+1]);
+        x_range_lower = (float) (xs[x_range_lower_i]);
+        x_range_upper = (float) (xs[x_range_lower_i+1]);
         pa_assert_se(x_range_lower < x_range_upper);
         pa_assert_se(x >= x_range_lower);
         pa_assert_se(x <= x_range_upper);
         //bilinear-interpolation of coefficients specified
-        float c0 = (x-x_range_lower)/(x_range_upper-x_range_lower);
+        c0 = (x-x_range_lower)/(x_range_upper-x_range_lower);
         pa_assert_se(c0 >= 0&&c0 <= 1.0);
         signal[x] = ((1.0f - c0) * ys[x_range_lower_i] + c0 * ys[x_range_lower_i + 1]);
         while(x >= xs[x_range_lower_i + 1]){
@@ -237,22 +179,7 @@ void interpolate(float *signal, size_t length, uint32_t *xs, float *ys, size_t n
     signal[length-1]=ys[n_points-1];
 }
 
-void array_out(const char *name, float *a, size_t length){
-    FILE *p=fopen(name, "w");
-    if(!p){
-        pa_log("opening %s failed!", name);
-        return;
-    }
-    for(size_t i = 0; i < length; ++i){
-        fprintf(p, "%e,", a[i]);
-        //if(i%1000==0){
-        //    fprintf(p, "\n");
-        //}
-    }
-    fprintf(p, "\n");
-    fclose(p);
-}
-static int is_monotonic(uint32_t *xs,size_t length){
+static int is_monotonic(const uint32_t *xs,size_t length){
     if(length<2){
         return 1;
     }
@@ -347,38 +274,6 @@ static void sink_update_requested_latency(pa_sink *s) {
             pa_sink_get_requested_latency_within_thread(s));
 }
 
-static void process_samples(struct userdata *u, pa_memchunk *tchunk){
-    size_t fs=pa_frame_size(&(u->sink->sample_spec));
-    pa_assert(u->samples_gathered >= u->R);
-    float *dst;
-    tchunk->index = 0;
-    tchunk->length = u->R * fs;
-    tchunk->memblock = pa_memblock_new(u->sink->core->mempool, tchunk->length);
-    dst = ((float*)pa_memblock_acquire(tchunk->memblock));
-    for(size_t c=0;c < u->channels; c++) {
-        dsp_logic(
-            u->work_buffer,
-            u->input[c],
-            u->overlap_accum[c],
-            u->H,
-            u->W,
-            u->output_window,
-            u
-        );
-        if(u->first_iteration){
-            /* The windowing function will make the audio ramped in, as a cheap fix we can
-             * undo the windowing (for non-zero window values)
-             */
-            for(size_t i = 0;i < u->overlap_size; ++i){
-                u->work_buffer[i] = u->W[i] <= FLT_EPSILON ? u->work_buffer[i] : u->work_buffer[i] / u->W[i];
-            }
-        }
-        pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst + c, fs, u->work_buffer, sizeof(float), u->R);
-    }
-    pa_memblock_release(tchunk->memblock);
-    u->samples_gathered-= u->R;
-}
-
 typedef float v4sf __attribute__ ((__aligned__(v_size * sizeof(float))));
 typedef union float_vector {
     float f[v_size];
@@ -389,15 +284,15 @@ typedef union float_vector {
 } float_vector_t;
 
 //reference implementation
-void dsp_logic(
-    float * __restrict__ dst,//used as a temp array too, needs to be fft_length!
-    float * __restrict__ src,/*input data w/ overlap at start,
+static void dsp_logic(
+    float * restrict dst,//used as a temp array too, needs to be fft_length!
+    float * restrict src,/*input data w/ overlap at start,
                                *automatically cycled in routine
                                */
-    float * __restrict__ overlap,//The size of the overlap
-    const float * __restrict__ H,//The freq. magnitude scalers filter
-    const float * __restrict__ W,//The windowing function
-    fftwf_complex * __restrict__ output_window,//The transformed window'd src
+    float * restrict overlap,//The size of the overlap
+    const float * restrict H,//The freq. magnitude scalers filter
+    const float * restrict W,//The windowing function
+    fftwf_complex * restrict output_window,//The transformed window'd src
     struct userdata *u){
     //use a linear-phase sliding STFT and overlap-add method (for each channel)
     //zero padd the data
@@ -443,14 +338,14 @@ void dsp_logic(
 ////regardless of sse enabled, the loops in here assume
 ////16 byte aligned addresses and memory allocations divisible by v_size
 //void dsp_logic(
-//    float * __restrict__ dst,//used as a temp array too, needs to be fft_length!
-//    float * __restrict__ src,/*input data w/ overlap at start,
+//    float * restrict dst,//used as a temp array too, needs to be fft_length!
+//    float * restrict src,/*input data w/ overlap at start,
 //                               *automatically cycled in routine
 //                               */
-//    float * __restrict__ overlap,//The size of the overlap
-//    const float * __restrict__ H,//The freq. magnitude scalers filter
-//    const float * __restrict__ W,//The windowing function
-//    fftwf_complex * __restrict__ output_window,//The transformed window'd src
+//    float * restrict overlap,//The size of the overlap
+//    const float * restrict H,//The freq. magnitude scalers filter
+//    const float * restrict W,//The windowing function
+//    fftwf_complex * restrict output_window,//The transformed window'd src
 //    struct userdata *u){//Collection of constants
 //
 //    const size_t window_size = mround(u->window_size,v_size);
@@ -535,25 +430,57 @@ void dsp_logic(
 //    );
 //}
 
-void initialize_buffer(struct userdata *u, pa_memchunk *in){
-    size_t fs = pa_frame_size(&(u->sink->sample_spec));
-    size_t samples = in->length/fs;
-    pa_assert_se(u->samples_gathered + samples <= u->window_size);
+static void process_samples(struct userdata *u, pa_memchunk *tchunk){
+    size_t fs=pa_frame_size(&(u->sink->sample_spec));
+    float *dst;
+    pa_assert(u->samples_gathered >= u->R);
+    tchunk->index = 0;
+    tchunk->length = u->R * fs;
+    tchunk->memblock = pa_memblock_new(u->sink->core->mempool, tchunk->length);
+    dst = ((float*)pa_memblock_acquire(tchunk->memblock));
+    for(size_t c=0;c < u->channels; c++) {
+        dsp_logic(
+            u->work_buffer,
+            u->input[c],
+            u->overlap_accum[c],
+            u->H,
+            u->W,
+            u->output_window,
+            u
+        );
+        if(u->first_iteration){
+            /* The windowing function will make the audio ramped in, as a cheap fix we can
+             * undo the windowing (for non-zero window values)
+             */
+            for(size_t i = 0;i < u->overlap_size; ++i){
+                u->work_buffer[i] = u->W[i] <= FLT_EPSILON ? u->work_buffer[i] : u->work_buffer[i] / u->W[i];
+            }
+        }
+        pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst + c, fs, u->work_buffer, sizeof(float), u->R);
+    }
+    pa_memblock_release(tchunk->memblock);
+    u->samples_gathered -= u->R;
+}
+
+static void initialize_buffer(struct userdata *u, pa_memchunk *in){
+    size_t fs = pa_frame_size(&u->sink->sample_spec);
+    size_t samples = in->length / fs;
     float *src = (float*) ((uint8_t*) pa_memblock_acquire(in->memblock) + in->index);
+    pa_assert_se(u->samples_gathered + samples <= u->window_size);
     for(size_t c = 0; c < u->channels; c++) {
         //buffer with an offset after the overlap from previous
         //iterations
-        pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c]+u->samples_gathered, sizeof(float), src + c, fs, samples);
+        pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c] + u->samples_gathered, sizeof(float), src + c, fs, samples);
     }
-    u->samples_gathered+=samples;
+    u->samples_gathered += samples;
     pa_memblock_release(in->memblock);
 }
 
-void input_buffer(struct userdata *u, pa_memchunk *in){
+static void input_buffer(struct userdata *u, pa_memchunk *in){
     size_t fs = pa_frame_size(&(u->sink->sample_spec));
     size_t samples = in->length/fs;
-    pa_assert_se(samples <= u->window_size - u->samples_gathered);
     float *src = (float*) ((uint8_t*) pa_memblock_acquire(in->memblock) + in->index);
+    pa_assert_se(samples <= u->window_size - u->samples_gathered);
     for(size_t c = 0; c < u->channels; c++) {
         //buffer with an offset after the overlap from previous
         //iterations
@@ -569,20 +496,21 @@ void input_buffer(struct userdata *u, pa_memchunk *in){
 /* Called from I/O thread context */
 static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
     struct userdata *u;
+    size_t fs;
+    struct timeval start, end;
+    pa_memchunk tchunk;
     pa_sink_input_assert_ref(i);
     pa_assert_se(u = i->userdata);
     pa_assert(chunk);
     pa_assert(u->sink);
-    size_t fs = pa_frame_size(&(u->sink->sample_spec));
-    pa_memchunk tchunk;
+    fs = pa_frame_size(&(u->sink->sample_spec));
     chunk->memblock = NULL;
 
     /* Hmm, process any rewind request that might be queued up */
     pa_sink_process_rewind(u->sink, 0);
 
     //pa_log_debug("start output-buffered %ld, input-buffered %ld, requested %ld",buffered_samples,u->samples_gathered,samples_requested);
-    struct timespec start, end;
-    gettime(start);
+    pa_timeval_load(&start);
     do{
         size_t input_remaining = u->window_size - u->samples_gathered;
         pa_assert(input_remaining > 0);
@@ -593,7 +521,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
         //buffer->index = 0;
         //pa_memblock_ref(buffer->memblock);
         //pa_sink_render_into(u->sink, buffer);
-        while(pa_memblockq_peek(u->input_q, &tchunk) < 0 || tchunk.memblock == NULL){
+        while(pa_memblockq_peek(u->input_q, &tchunk) < 0){
             pa_sink_render(u->sink, input_remaining*fs, &tchunk);
             pa_assert(tchunk.memblock);
             pa_memblockq_push(u->input_q, &tchunk);
@@ -604,40 +532,47 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
         pa_memblockq_drop(u->input_q, tchunk.length);
         //pa_log_debug("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs);
         /* copy new input */
-        //gettime(start);
+        //pa_timeval_load(start);
         if(u->first_iteration){
             initialize_buffer(u, &tchunk);
         }else{
             input_buffer(u, &tchunk);
         }
-        //gettime(end);
-        //pa_log_debug("Took %0.5f seconds to setup", tdiff(end, start)*1e-9);
+        //pa_timeval_load(&end);
+        //pa_log_debug("Took %0.5f seconds to setup", pa_timeval_diff(end, start) / (double) PA_USEC_PER_SEC);
         pa_memblock_unref(tchunk.memblock);
     }while(u->samples_gathered < u->window_size);
-    gettime(end);
-    pa_log_debug("Took %0.6f seconds to get data", tdiff(end, start)*1e-9);
+    pa_timeval_load(&end);
+    pa_log_debug("Took %0.6f seconds to get data", pa_timeval_diff(&end, &start) / (double) PA_USEC_PER_SEC);
 
     pa_assert(u->fft_size >= u->window_size);
     pa_assert(u->R < u->window_size);
     /* set the H filter */
-    unsigned H_i = pa_aupdate_read_begin(u->a_H);
-    u->H = u->Hs[H_i];
-    gettime(start);
+    u->H = u->Hs[pa_aupdate_read_begin(u->a_H)];
+    pa_timeval_load(&start);
     /* process a block */
     process_samples(u, chunk);
-    gettime(end);
-    pa_log_debug("Took %0.6f seconds to process", tdiff(end, start)*1e-9);
+    pa_timeval_load(&end);
+    pa_log_debug("Took %0.6f seconds to process", pa_timeval_diff(&end, &start) / (double) PA_USEC_PER_SEC);
     pa_aupdate_read_end(u->a_H);
 
     pa_assert(chunk->memblock);
     //pa_log_debug("gave %ld", chunk->length/fs);
     //pa_log_debug("end pop");
     if(u->first_iteration){
-        u->first_iteration = 0;
+        u->first_iteration = FALSE;
     }
     return 0;
 }
 
+static void reset_filter(struct userdata *u){
+    u->samples_gathered = 0;
+    for(size_t i = 0;i < u->channels; ++i){
+        memset(u->overlap_accum[i], 0, u->overlap_size * sizeof(float));
+    }
+    u->first_iteration = TRUE;
+}
+
 /* Called from I/O thread context */
 static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
     struct userdata *u;
@@ -671,14 +606,6 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
     pa_memblockq_rewind(u->input_q, nbytes);
 }
 
-void reset_filter(struct userdata *u){
-    u->samples_gathered = 0;
-    for(size_t i = 0;i < u->channels; ++i){
-        memset(u->overlap_accum[i], 0, u->overlap_size * sizeof(float));
-    }
-    u->first_iteration = 1;
-}
-
 /* Called from I/O thread context */
 static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
     struct userdata *u;
@@ -693,11 +620,11 @@ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
 /* Called from I/O thread context */
 static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
     struct userdata *u;
-
+    size_t fs;
     pa_sink_input_assert_ref(i);
     pa_assert_se(u = i->userdata);
 
-    size_t fs = pa_frame_size(&(u->sink->sample_spec));
+    fs = pa_frame_size(&(u->sink->sample_spec));
     //pa_sink_set_max_request_within_thread(u->sink, nbytes);
     //pa_sink_set_max_request_within_thread(u->sink, u->R*fs);
     pa_sink_set_max_request_within_thread(u->sink, ((nbytes+u->R*fs-1)/(u->R*fs))*(u->R*fs));
@@ -740,7 +667,7 @@ static void sink_input_detach_cb(pa_sink_input *i) {
 /* Called from I/O thread context */
 static void sink_input_attach_cb(pa_sink_input *i) {
     struct userdata *u;
-
+    size_t fs;
     pa_sink_input_assert_ref(i);
     pa_assert_se(u = i->userdata);
 
@@ -748,9 +675,10 @@ static void sink_input_attach_cb(pa_sink_input *i) {
     pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
 
     pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency);
+    fs = pa_frame_size(&(u->sink->sample_spec));
     pa_sink_attach_within_thread(u->sink);
+    pa_sink_set_max_request_within_thread(u->sink, mround(pa_sink_get_max_request(i->sink), u->R*fs));
 
-    //size_t fs = pa_frame_size(&(u->sink->sample_spec));
     //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs);
     //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->master->thread_info.max_latency);
     //TODO: setting this guy minimizes drop outs but doesn't get rid
@@ -758,7 +686,6 @@ static void sink_input_attach_cb(pa_sink_input *i) {
     //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs);
     //TODO: this guy causes dropouts constantly+rewinds, it's unusable
     //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency);
-    pa_sink_attach_within_thread(u->sink);
 }
 
 /* Called from main context */
@@ -799,15 +726,15 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s
     }
 }
 
-void save_profile(struct userdata *u, char *name){
+static void save_profile(struct userdata *u, char *name){
     float *H_n = pa_xmalloc((u->fft_size / 2 + 1) * sizeof(float));
     const float *H = u->Hs[pa_aupdate_read_begin(u->a_H)];
+    pa_datum key, data;
     for(size_t i = 0 ; i <= u->fft_size / 2 + 1; ++i){
         //H_n[i] = H[i] * u->fft_size;
         H_n[i] = H[i];
     }
     pa_aupdate_read_end(u->a_H);
-    pa_datum key, data;
     key.data=name;
     key.size = strlen(key.data);
     data.data = H_n;
@@ -816,23 +743,23 @@ void save_profile(struct userdata *u, char *name){
     pa_database_sync(u->database);
 }
 
-void save_state(struct userdata *u){
+static void save_state(struct userdata *u){
     char *state_name = pa_sprintf_malloc("%s-previous-state", u->name);
     save_profile(u, state_name);
     pa_xfree(state_name);
 }
 
-void remove_profile(pa_core *c,char *name){
+static void remove_profile(pa_core *c, char *name){
     pa_datum key;
+    pa_database *database;
     key.data = name;
     key.size = strlen(key.data);
-    pa_database *database;
-    pa_assert_se(database = pa_shared_get(c,EQDB));
-    pa_database_unset(database,&key);
+    pa_assert_se(database = pa_shared_get(c, EQDB));
+    pa_database_unset(database, &key);
     pa_database_sync(database);
 }
 
-const char* load_profile(struct userdata *u,char *name){
+static const char* load_profile(struct userdata *u, char *name){
     pa_datum key,value;
     key.data = name;
     key.size = strlen(key.data);
@@ -840,7 +767,6 @@ const char* load_profile(struct userdata *u,char *name){
         if(value.size == (u->fft_size / 2 + 1) * sizeof(float)){
             float *H=u->Hs[pa_aupdate_write_begin(u->a_H)];
             memcpy(H, value.data, value.size);
-            pa_aupdate_write_swap(u->a_H);
             pa_aupdate_write_end(u->a_H);
         }else{
             return "incompatible size";
@@ -852,13 +778,13 @@ const char* load_profile(struct userdata *u,char *name){
     return NULL;
     //fix_filter(u->H, u->fft_size);
 }
-void load_state(struct userdata *u){
+
+static void load_state(struct userdata *u){
     char *state_name=pa_sprintf_malloc("%s-previous-state", u->name);
     load_profile(u,state_name);
     pa_xfree(state_name);
 }
 
-
 /* Called from main context */
 static pa_bool_t sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) {
     struct userdata *u;
@@ -884,10 +810,11 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
 //and aligned
 static void * alloc(size_t x,size_t s){
     size_t f = mround(x*s, sizeof(float)*v_size);
+    float *t;
     pa_assert_se(f >= x*s);
     //printf("requested %ld floats=%ld bytes, rem=%ld\n", x, x*sizeof(float), x*sizeof(float)%16);
     //printf("giving %ld floats=%ld bytes, rem=%ld\n", f, f*sizeof(float), f*sizeof(float)%16);
-    float *t = fftwf_malloc(f);
+    t = fftwf_malloc(f);
     memset(t, 0, f);
     return t;
 }
@@ -903,6 +830,7 @@ int pa__init(pa_module*m) {
     pa_sink_new_data sink_data;
     pa_bool_t *use_default = NULL;
     size_t fs;
+    float *H;
 
     pa_assert(m);
 
@@ -936,7 +864,6 @@ int pa__init(pa_module*m) {
     u->R = (u->window_size + 1) / 2;
     u->overlap_size = u->window_size - u->R;
     u->samples_gathered = 0;
-    u->input_q = pa_memblockq_new(0,  MEMBLOCKQ_MAXLENGTH, 0, fs, 1, 1, 0, NULL);
     u->a_H = pa_aupdate_new();
     u->latency = u->window_size - u->R;
     for(size_t i = 0; i < 2; ++i){
@@ -945,8 +872,8 @@ int pa__init(pa_module*m) {
     u->W = alloc(u->window_size, sizeof(float));
     u->work_buffer = alloc(u->fft_size, sizeof(float));
     memset(u->work_buffer, 0, u->fft_size*sizeof(float));
-    u->input = (float **)pa_xmalloc0(sizeof(float *)*u->channels);
-    u->overlap_accum = (float **)pa_xmalloc0(sizeof(float *)*u->channels);
+    u->input = pa_xnew0(float *, u->channels);
+    u->overlap_accum = pa_xnew0(float *, u->channels);
     for(size_t c = 0; c < u->channels; ++c){
         u->input[c] = alloc(u->window_size, sizeof(float));
         pa_assert_se(u->input[c]);
@@ -961,7 +888,7 @@ int pa__init(pa_module*m) {
     u->inverse_plan = fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_ESTIMATE);
 
     hanning_window(u->W, u->window_size);
-    u->first_iteration = 1;
+    u->first_iteration = TRUE;
 
     /* Create sink */
     pa_sink_new_data_init(&sink_data);
@@ -995,11 +922,9 @@ int pa__init(pa_module*m) {
     u->sink->update_requested_latency = sink_update_requested_latency;
     u->sink->request_rewind = sink_request_rewind;
     u->sink->userdata = u;
+    u->input_q = pa_memblockq_new(0,  MEMBLOCKQ_MAXLENGTH, 0, fs, 1, 1, 0, &u->sink->silence);
 
     pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq);
-    pa_sink_set_max_request(u->sink,
-        ((pa_sink_get_max_request(u->sink)+u->R*fs-1)/(u->R*fs))*(u->R*fs)
-    );
     //pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(u->R*fs, &ss));
 
     /* Create sink input */
@@ -1042,12 +967,11 @@ int pa__init(pa_module*m) {
     dbus_init(u);
 
     //default filter to these
-    float *H=u->Hs[pa_aupdate_write_begin(u->a_H)];
+    H=u->Hs[pa_aupdate_write_begin(u->a_H)];
     for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){
         H[i] = 1.0 / sqrtf(2.0f);
     }
     fix_filter(H, u->fft_size);
-    pa_aupdate_write_swap(u->a_H);
     pa_aupdate_write_end(u->a_H);
     //load old parameters
     load_state(u);
@@ -1124,6 +1048,33 @@ void pa__done(pa_module*m) {
     pa_xfree(u);
 }
 
+/*
+ * DBus Routines and Callbacks
+ */
+#define EXTNAME "org.PulseAudio.Ext.Equalizing1"
+#define MANAGER_PATH "/org/pulseaudio/equalizing1"
+#define MANAGER_IFACE EXTNAME ".Manager"
+#define EQUALIZER_IFACE EXTNAME ".Equalizer"
+static void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void get_sinks(pa_core *u, char ***names, unsigned *n_sinks);
+static void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void get_profiles(pa_core *u, char ***names, unsigned *n_sinks);
+static void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void get_filter(struct userdata *u, double **H_);
+static void set_filter(struct userdata *u, double **H_);
 enum manager_method_index {
     MANAGER_METHOD_REMOVE_PROFILE,
     MANAGER_METHOD_MAX
@@ -1267,17 +1218,20 @@ static pa_dbus_interface_info equalizer_info={
     .n_signals=EQUALIZER_SIGNAL_MAX
 };
 
-void dbus_init(struct userdata *u){
+static void dbus_init(struct userdata *u){
+    uint32_t dummy;
+    DBusMessage *signal = NULL;
+    pa_idxset *sink_list = NULL;
     u->dbus_protocol=pa_dbus_protocol_get(u->sink->core);
     u->dbus_path=pa_sprintf_malloc("/org/pulseaudio/core1/sink%d", u->sink->index);
 
     pa_dbus_protocol_add_interface(u->dbus_protocol, u->dbus_path, &equalizer_info, u);
-    pa_idxset *sink_list=pa_shared_get(u->sink->core, SINKLIST);
+    sink_list = pa_shared_get(u->sink->core, SINKLIST);
     u->database=pa_shared_get(u->sink->core, EQDB);
     if(sink_list==NULL){
+        char *dbname;
         sink_list=pa_idxset_new(&pa_idxset_trivial_hash_func, &pa_idxset_trivial_compare_func);
         pa_shared_set(u->sink->core, SINKLIST, sink_list);
-        char *dbname;
         pa_assert_se(dbname = pa_state_path("equalizers", TRUE));
         pa_assert_se(u->database = pa_database_open(dbname, TRUE));
         pa_xfree(dbname);
@@ -1285,17 +1239,15 @@ void dbus_init(struct userdata *u){
         pa_dbus_protocol_add_interface(u->dbus_protocol, MANAGER_PATH, &manager_info, u->sink->core);
         pa_dbus_protocol_register_extension(u->dbus_protocol, EXTNAME);
     }
-    uint32_t dummy;
     pa_idxset_put(sink_list, u, &dummy);
 
-    DBusMessage *signal = NULL;
     pa_assert_se((signal = dbus_message_new_signal(MANAGER_PATH, MANAGER_IFACE, manager_signals[MANAGER_SIGNAL_SINK_ADDED].name)));
     dbus_message_append_args(signal, DBUS_TYPE_OBJECT_PATH, &u->dbus_path, DBUS_TYPE_INVALID);
     pa_dbus_protocol_send_signal(u->dbus_protocol, signal);
     dbus_message_unref(signal);
 }
 
-void dbus_done(struct userdata *u){
+static void dbus_done(struct userdata *u){
     pa_idxset *sink_list;
     uint32_t dummy;
 
@@ -1320,14 +1272,16 @@ void dbus_done(struct userdata *u){
     pa_dbus_protocol_unref(u->dbus_protocol);
 }
 
-void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void *_u) {
+static void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void *_u) {
     DBusError error;
     pa_core *c = (pa_core *)_u;
+    DBusMessage *signal = NULL;
+    pa_dbus_protocol *dbus_protocol;
+    char *name;
     pa_assert(conn);
     pa_assert(msg);
     pa_assert(c);
     dbus_error_init(&error);
-    char *name;
     if(!dbus_message_get_args(msg, &error,
                  DBUS_TYPE_STRING, &name,
                 DBUS_TYPE_INVALID)){
@@ -1338,28 +1292,27 @@ void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void
     remove_profile(c,name);
     pa_dbus_send_empty_reply(conn, msg);
 
-    DBusMessage *signal = NULL;
     pa_assert_se((signal = dbus_message_new_signal(MANAGER_PATH, MANAGER_IFACE, manager_signals[MANAGER_SIGNAL_PROFILES_CHANGED].name)));
-    pa_dbus_protocol *dbus_protocol = pa_dbus_protocol_get(c);
+    dbus_protocol = pa_dbus_protocol_get(c);
     pa_dbus_protocol_send_signal(dbus_protocol, signal);
     pa_dbus_protocol_unref(dbus_protocol);
     dbus_message_unref(signal);
 }
 
-void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){
+static void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){
     uint32_t rev=1;
     pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_UINT32, &rev);
 }
 
-void get_sinks(pa_core *u, char ***names, unsigned *n_sinks){
-    pa_assert(u);
-    pa_assert(names);
-    pa_assert(n_sinks);
-
+static void get_sinks(pa_core *u, char ***names, unsigned *n_sinks){
     void *iter = NULL;
     struct userdata *sink_u = NULL;
     uint32_t dummy;
     pa_idxset *sink_list;
+    pa_assert(u);
+    pa_assert(names);
+    pa_assert(n_sinks);
+
     pa_assert_se(sink_list = pa_shared_get(u, SINKLIST));
     *n_sinks = (unsigned) pa_idxset_size(sink_list);
     *names = *n_sinks > 0 ? pa_xnew0(char *,*n_sinks) : NULL;
@@ -1369,13 +1322,13 @@ void get_sinks(pa_core *u, char ***names, unsigned *n_sinks){
     }
 }
 
-void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u){
+static void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u){
+    unsigned n;
+    char **names = NULL;
     pa_assert(conn);
     pa_assert(msg);
     pa_assert(_u);
 
-    unsigned n;
-    char **names = NULL;
     get_sinks((pa_core *) _u, &names, &n);
     pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_OBJECT_PATH, names, n);
     for(unsigned i = 0; i < n; ++i){
@@ -1384,17 +1337,17 @@ void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u){
     pa_xfree(names);
 }
 
-void get_profiles(pa_core *c, char ***names, unsigned *n){
-    pa_assert(c);
-    pa_assert(names);
-    pa_assert(n);
+static void get_profiles(pa_core *c, char ***names, unsigned *n){
     char *name;
     pa_database *database;
-    pa_assert_se(database = pa_shared_get(c, EQDB));
     pa_datum key, next_key;
     pa_strlist *head=NULL, *iter;
+    pa_bool_t done;
+    pa_assert_se(database = pa_shared_get(c, EQDB));
 
-    int done;
+    pa_assert(c);
+    pa_assert(names);
+    pa_assert(n);
     done = !pa_database_first(database, &key, NULL);
     *n = 0;
     while(!done){
@@ -1411,19 +1364,19 @@ void get_profiles(pa_core *c, char ***names, unsigned *n){
     (*names) = *n > 0 ? pa_xnew0(char *, *n) : NULL;
     iter=head;
     for(unsigned i = 0; i < *n; ++i){
-        (*names)[*n-1-i]=pa_xstrdup(pa_strlist_data(iter));
-        iter=pa_strlist_next(iter);
+        (*names)[*n - 1 - i] = pa_xstrdup(pa_strlist_data(iter));
+        iter = pa_strlist_next(iter);
     }
     pa_strlist_free(head);
 }
 
-void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u){
+static void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u){
+    char **names;
+    unsigned n;
     pa_assert(conn);
     pa_assert(msg);
     pa_assert(_u);
 
-    char **names;
-    unsigned n;
     get_profiles((pa_core *)_u, &names, &n);
     pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_STRING, names, n);
     for(unsigned i = 0; i < n; ++i){
@@ -1432,21 +1385,22 @@ void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u){
     pa_xfree(names);
 }
 
-void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){
-    pa_assert(conn);
-    pa_assert(msg);
-    pa_assert(_u);
-
-    pa_core *c = (pa_core *)_u;
+static void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){
+    pa_core *c;
     char **names = NULL;
     unsigned n;
     DBusMessage *reply = NULL;
     DBusMessageIter msg_iter, dict_iter;
+    uint32_t rev;
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert_se(c = _u);
+
     pa_assert_se((reply = dbus_message_new_method_return(msg)));
     dbus_message_iter_init_append(reply, &msg_iter);
     pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
 
-    uint32_t rev=1;
+    rev = 1;
     pa_dbus_append_basic_variant_dict_entry(&dict_iter, manager_handlers[MANAGER_HANDLER_REVISION].property_name, DBUS_TYPE_UINT32, &rev);
 
     get_sinks(c, &names, &n);
@@ -1467,17 +1421,19 @@ void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){
     dbus_message_unref(reply);
 }
 
-void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *_u) {
+static void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *_u) {
     struct userdata *u=(struct userdata *) _u;
     DBusError error;
-
-    pa_assert(conn);
-    pa_assert(msg);
-    pa_assert(u);
+    DBusMessage *signal = NULL;
     float *ys;
     uint32_t *xs;
     double *_ys;
-    unsigned x_npoints,y_npoints;
+    unsigned x_npoints, y_npoints;
+    float *H;
+    pa_bool_t points_good = TRUE;
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(u);
 
     dbus_error_init(&error);
 
@@ -1489,14 +1445,13 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *
         dbus_error_free(&error);
         return;
     }
-    int points_good=1;
     for(size_t i = 0; i < x_npoints; ++i){
         if(xs[i] >= u->fft_size / 2 + 1){
-            points_good=0;
+            points_good = FALSE;
             break;
         }
     }
-    if(!is_monotonic(xs,x_npoints) || !points_good){
+    if(!is_monotonic(xs, x_npoints) || !points_good){
         pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs must be monotonic and 0<=x<=%ld", u->fft_size / 2);
         dbus_error_free(&error);
         return;
@@ -1505,7 +1460,7 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *
         pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs and ys must be the same length and 2<=l<=%ld!", u->fft_size / 2 + 1);
         dbus_error_free(&error);
         return;
-    }else if(xs[0] != 0 || xs[x_npoints-1] != u->fft_size / 2){
+    }else if(xs[0] != 0 || xs[x_npoints - 1] != u->fft_size / 2){
         pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs[0] must be 0 and xs[-1]=fft_size/2");
         dbus_error_free(&error);
         return;
@@ -1516,10 +1471,9 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *
         ys[i] = (float) _ys[i];
     }
 
-    float *H = u->Hs[pa_aupdate_write_begin(u->a_H)];
+    H = u->Hs[pa_aupdate_write_begin(u->a_H)];
     interpolate(H, u->fft_size / 2 + 1, xs, ys, x_npoints);
     fix_filter(H, u->fft_size);
-    pa_aupdate_write_swap(u->a_H);
     pa_aupdate_write_end(u->a_H);
     pa_xfree(ys);
 
@@ -1528,22 +1482,23 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *
 
     pa_dbus_send_empty_reply(conn, msg);
 
-    DBusMessage *signal = NULL;
     pa_assert_se((signal = dbus_message_new_signal(u->dbus_path, EQUALIZER_IFACE, equalizer_signals[EQUALIZER_SIGNAL_FILTER_CHANGED].name)));
     pa_dbus_protocol_send_signal(u->dbus_protocol, signal);
     dbus_message_unref(signal);
 }
 
-void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u) {
-    struct userdata *u=(struct userdata *) _u;
+static void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u) {
+    struct userdata *u = (struct userdata *) _u;
     DBusError error;
+    uint32_t *xs;
+    double *ys;
+    unsigned x_npoints;
+    float *H;
+    pa_bool_t points_good=TRUE;
 
     pa_assert(conn);
     pa_assert(msg);
     pa_assert(u);
-    uint32_t *xs;
-    double *ys;
-    unsigned x_npoints;
 
     dbus_error_init(&error);
 
@@ -1554,10 +1509,9 @@ void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg,
         dbus_error_free(&error);
         return;
     }
-    int points_good=1;
     for(size_t i = 0; i < x_npoints; ++i){
         if(xs[i] >= u->fft_size / 2 + 1){
-            points_good=0;
+            points_good=FALSE;
             break;
         }
     }
@@ -1569,7 +1523,7 @@ void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg,
     }
 
     ys = pa_xmalloc(x_npoints * sizeof(double));
-    float *H = u->Hs[pa_aupdate_read_begin(u->a_H)];
+    H = u->Hs[pa_aupdate_read_begin(u->a_H)];
     for(uint32_t i = 0; i < x_npoints; ++i){
         ys[i] = H[xs[i]] * u->fft_size;
     }
@@ -1579,14 +1533,15 @@ void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg,
     pa_xfree(ys);
 }
 
-void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u) {
-    struct userdata *u=(struct userdata *) _u;
+static void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u) {
+    struct userdata *u = (struct userdata *) _u;
+    char *name;
+    DBusMessage *signal = NULL;
     DBusError error;
     pa_assert(conn);
     pa_assert(msg);
     pa_assert(u);
     dbus_error_init(&error);
-    char *name;
 
     if(!dbus_message_get_args(msg, &error,
                  DBUS_TYPE_STRING, &name,
@@ -1598,21 +1553,22 @@ void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void
     save_profile(u,name);
     pa_dbus_send_empty_reply(conn, msg);
 
-    DBusMessage *signal = NULL;
     pa_assert_se((signal = dbus_message_new_signal(MANAGER_PATH, MANAGER_IFACE, manager_signals[MANAGER_SIGNAL_PROFILES_CHANGED].name)));
     pa_dbus_protocol_send_signal(u->dbus_protocol, signal);
     dbus_message_unref(signal);
 }
 
-void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u) {
+static void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u) {
     struct userdata *u=(struct userdata *) _u;
+    char *name;
     DBusError error;
+    const char *err_msg = NULL;
+    DBusMessage *signal = NULL;
 
     pa_assert(conn);
     pa_assert(msg);
     pa_assert(u);
     dbus_error_init(&error);
-    char *name;
 
     if(!dbus_message_get_args(msg, &error,
                  DBUS_TYPE_STRING, &name,
@@ -1621,77 +1577,80 @@ void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void
         dbus_error_free(&error);
         return;
     }
-    const char *err_msg=load_profile(u,name);
-    if(err_msg!=NULL){
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "error loading profile %s: %s", name,err_msg);
+    err_msg = load_profile(u,name);
+    if(err_msg != NULL){
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "error loading profile %s: %s", name, err_msg);
         dbus_error_free(&error);
         return;
     }
     pa_dbus_send_empty_reply(conn, msg);
 
-    DBusMessage *signal = NULL;
     pa_assert_se((signal = dbus_message_new_signal(u->dbus_path, EQUALIZER_IFACE, equalizer_signals[EQUALIZER_SIGNAL_FILTER_CHANGED].name)));
     pa_dbus_protocol_send_signal(u->dbus_protocol, signal);
     dbus_message_unref(signal);
 }
 
-void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){
+static void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){
     uint32_t rev=1;
     pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_UINT32, &rev);
 }
 
-void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u){
+static void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u){
+    struct userdata *u;
+    uint32_t n_coefs;
+    pa_assert_se(u = (struct userdata *) _u);
     pa_assert(conn);
     pa_assert(msg);
-    pa_assert(_u);
-
-    struct userdata *u=(struct userdata *)_u;
 
-    uint32_t n_coefs=(uint32_t)(u->fft_size / 2 + 1);
+    n_coefs = (uint32_t) (u->fft_size / 2 + 1);
     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &n_coefs);
 }
 
-void equalizer_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u){
+static void equalizer_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u){
+    struct userdata *u;
+    uint32_t rate;
+    pa_assert_se(u = (struct userdata *) _u);
     pa_assert(conn);
     pa_assert(msg);
-    pa_assert(_u);
 
-    struct userdata *u=(struct userdata *) _u;
-    uint32_t rate=(uint32_t) u->sink->sample_spec.rate;
+    rate = (uint32_t) u->sink->sample_spec.rate;
     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &rate);
 }
 
-void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u){
+static void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u){
+    struct userdata *u;
+    uint32_t fft_size;
+    pa_assert_se(u = (struct userdata *) _u);
     pa_assert(conn);
     pa_assert(msg);
-    pa_assert(_u);
 
-    struct userdata *u=(struct userdata *) _u;
-    uint32_t fft_size=(uint32_t) u->fft_size;
+    fft_size = (uint32_t) u->fft_size;
     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &fft_size);
 }
 
-void get_filter(struct userdata *u, double **H_){
+static void get_filter(struct userdata *u, double **H_){
+    float *H;
     *H_ = pa_xnew0(double, u->fft_size / 2 + 1);
-    float *H=u->Hs[pa_aupdate_read_begin(u->a_H)];
+    H = u->Hs[pa_aupdate_read_begin(u->a_H)];
     for(size_t i = 0;i < u->fft_size / 2 + 1; ++i){
         (*H_)[i] = H[i] * u->fft_size;
     }
     pa_aupdate_read_end(u->a_H);
 }
 
-void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){
-    pa_assert(conn);
-    pa_assert(msg);
-    pa_assert(_u);
-
-    struct userdata *u=(struct userdata *) _u;
+static void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){
+    struct userdata *u;
     DBusMessage *reply = NULL;
     DBusMessageIter msg_iter, dict_iter;
-    uint32_t rev=1;
-    uint32_t n_coefs=(uint32_t)(u->fft_size / 2 + 1);
-    uint32_t rate=(uint32_t) u->sink->sample_spec.rate;
-    uint32_t fft_size=(uint32_t) u->fft_size;
+    uint32_t rev, n_coefs, rate, fft_size;
+    double *H;
+    pa_assert_se(u = (struct userdata *) _u);
+    pa_assert(msg);
+
+    rev = 1;
+    n_coefs = (uint32_t) (u->fft_size / 2 + 1);
+    rate = (uint32_t) u->sink->sample_spec.rate;
+    fft_size = (uint32_t) u->fft_size;
 
     pa_assert_se((reply = dbus_message_new_method_return(msg)));
     dbus_message_iter_init_append(reply, &msg_iter);
@@ -1701,7 +1660,6 @@ void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){
     pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_SAMPLERATE].property_name, DBUS_TYPE_UINT32, &rate);
     pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_FILTERSAMPLERATE].property_name, DBUS_TYPE_UINT32, &fft_size);
     pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_N_COEFS].property_name, DBUS_TYPE_UINT32, &n_coefs);
-    double *H;
     get_filter(u, &H);
     pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_FILTER].property_name, DBUS_TYPE_UINT32, &H);
     pa_xfree(H);
@@ -1711,37 +1669,38 @@ void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){
     dbus_message_unref(reply);
 }
 
-void equalizer_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u){
+static void equalizer_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u){
+    struct userdata *u;
+    unsigned n_coefs;
+    double *H_;
+    pa_assert_se(u = (struct userdata *) _u);
+
+    n_coefs = u->fft_size / 2 + 1;
     pa_assert(conn);
     pa_assert(msg);
-    pa_assert(_u);
-
-    struct userdata *u = (struct userdata *)_u;
-    unsigned n_coefs = u->fft_size / 2 + 1;
-    double *H_;
     get_filter(u, &H_);
     pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_DOUBLE, H_, n_coefs);
     pa_xfree(H_);
 }
 
-void set_filter(struct userdata *u, double **H_){
+static void set_filter(struct userdata *u, double **H_){
     float *H = u->Hs[pa_aupdate_write_begin(u->a_H)];
     for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){
         H[i] = (float) (*H_)[i];
     }
     fix_filter(H, u->fft_size);
-    pa_aupdate_write_swap(u->a_H);
     pa_aupdate_write_end(u->a_H);
 }
 
-void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){
+static void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){
+    struct userdata *u;
+    double *H;
+    unsigned _n_coefs;
+    DBusMessage *signal = NULL;
+    pa_assert_se(u = (struct userdata *) _u);
     pa_assert(conn);
     pa_assert(msg);
-    pa_assert(_u);
 
-    struct userdata *u=(struct userdata *)_u;
-    double *H;
-    unsigned _n_coefs;
     if(pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_DOUBLE, &H, &_n_coefs)){
         return;
     }
@@ -1755,7 +1714,6 @@ void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){
 
     pa_dbus_send_empty_reply(conn, msg);
 
-    DBusMessage *signal = NULL;
     pa_assert_se((signal = dbus_message_new_signal(u->dbus_path, EQUALIZER_IFACE, equalizer_signals[EQUALIZER_SIGNAL_FILTER_CHANGED].name)));
     pa_dbus_protocol_send_signal(u->dbus_protocol, signal);
     dbus_message_unref(signal);

commit 07cd6a4c3d357e9406bbb0444995c8c21f3a3cab
Author: Jason Newton <nevion at gmail.com>
Date:   Sun Aug 16 14:29:21 2009 -0700

    module-equalizer-sink.c
        i->sink -> i in pa_get_sink_max_request*

diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
index b7f61c6..d470774 100755
--- a/src/modules/module-equalizer-sink.c
+++ b/src/modules/module-equalizer-sink.c
@@ -677,7 +677,7 @@ static void sink_input_attach_cb(pa_sink_input *i) {
     pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency);
     fs = pa_frame_size(&(u->sink->sample_spec));
     pa_sink_attach_within_thread(u->sink);
-    pa_sink_set_max_request_within_thread(u->sink, mround(pa_sink_get_max_request(i->sink), u->R*fs));
+    pa_sink_set_max_request_within_thread(u->sink, mround(pa_sink_input_get_max_request(i), u->R*fs));
 
     //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs);
     //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->master->thread_info.max_latency);
@@ -811,7 +811,7 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
 static void * alloc(size_t x,size_t s){
     size_t f = mround(x*s, sizeof(float)*v_size);
     float *t;
-    pa_assert_se(f >= x*s);
+    pa_assert(f >= x*s);
     //printf("requested %ld floats=%ld bytes, rem=%ld\n", x, x*sizeof(float), x*sizeof(float)%16);
     //printf("giving %ld floats=%ld bytes, rem=%ld\n", f, f*sizeof(float), f*sizeof(float)%16);
     t = fftwf_malloc(f);
@@ -876,11 +876,8 @@ int pa__init(pa_module*m) {
     u->overlap_accum = pa_xnew0(float *, u->channels);
     for(size_t c = 0; c < u->channels; ++c){
         u->input[c] = alloc(u->window_size, sizeof(float));
-        pa_assert_se(u->input[c]);
         memset(u->input[c], 0, (u->window_size)*sizeof(float));
-        pa_assert_se(u->input[c]);
         u->overlap_accum[c] = alloc(u->overlap_size, sizeof(float));
-        pa_assert_se(u->overlap_accum[c]);
         memset(u->overlap_accum[c], 0, u->overlap_size*sizeof(float));
     }
     u->output_window = alloc((u->fft_size / 2 + 1), sizeof(fftwf_complex));

commit 7bd7ce6deca125d9061ea8d29f6a17e65ba36116
Author: Jason Newton <nevion at gmail.com>
Date:   Sun Aug 16 15:41:40 2009 -0700

    module-equalizer-sink.c: swapped order of attach_within_thread and set_max_request within sink_input_attach_cb

diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
index d470774..9a4740f 100755
--- a/src/modules/module-equalizer-sink.c
+++ b/src/modules/module-equalizer-sink.c
@@ -543,7 +543,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
         pa_memblock_unref(tchunk.memblock);
     }while(u->samples_gathered < u->window_size);
     pa_timeval_load(&end);
-    pa_log_debug("Took %0.6f seconds to get data", pa_timeval_diff(&end, &start) / (double) PA_USEC_PER_SEC);
+    pa_log_debug("Took %0.6f seconds to get data", (double) pa_timeval_diff(&end, &start) / PA_USEC_PER_SEC);
 
     pa_assert(u->fft_size >= u->window_size);
     pa_assert(u->R < u->window_size);
@@ -553,7 +553,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
     /* process a block */
     process_samples(u, chunk);
     pa_timeval_load(&end);
-    pa_log_debug("Took %0.6f seconds to process", pa_timeval_diff(&end, &start) / (double) PA_USEC_PER_SEC);
+    pa_log_debug("Took %0.6f seconds to process", (double) pa_timeval_diff(&end, &start) / PA_USEC_PER_SEC);
     pa_aupdate_read_end(u->a_H);
 
     pa_assert(chunk->memblock);
@@ -676,7 +676,6 @@ static void sink_input_attach_cb(pa_sink_input *i) {
 
     pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency);
     fs = pa_frame_size(&(u->sink->sample_spec));
-    pa_sink_attach_within_thread(u->sink);
     pa_sink_set_max_request_within_thread(u->sink, mround(pa_sink_input_get_max_request(i), u->R*fs));
 
     //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs);
@@ -686,6 +685,7 @@ static void sink_input_attach_cb(pa_sink_input *i) {
     //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs);
     //TODO: this guy causes dropouts constantly+rewinds, it's unusable
     //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency);
+    pa_sink_attach_within_thread(u->sink);
 }
 
 /* Called from main context */

commit ab0e20ab2c0014e495cadb9c4f8e188df125fa7f
Author: Jason Newton <nevion at gmail.com>
Date:   Sun Aug 16 16:14:30 2009 -0700

    module-equalizer-sink: fixed timeval initialization

diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
index 9a4740f..a8ccff8 100755
--- a/src/modules/module-equalizer-sink.c
+++ b/src/modules/module-equalizer-sink.c
@@ -40,6 +40,7 @@ USA.
 #include <pulse/i18n.h>
 #include <pulse/timeval.h>
 
+#include <pulsecore/core-rtclock.h>
 #include <pulsecore/aupdate.h>
 #include <pulsecore/core-error.h>
 #include <pulsecore/namereg.h>
@@ -510,7 +511,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
     pa_sink_process_rewind(u->sink, 0);
 
     //pa_log_debug("start output-buffered %ld, input-buffered %ld, requested %ld",buffered_samples,u->samples_gathered,samples_requested);
-    pa_timeval_load(&start);
+    pa_rtclock_get(&start);
     do{
         size_t input_remaining = u->window_size - u->samples_gathered;
         pa_assert(input_remaining > 0);
@@ -532,27 +533,27 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
         pa_memblockq_drop(u->input_q, tchunk.length);
         //pa_log_debug("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs);
         /* copy new input */
-        //pa_timeval_load(start);
+        //pa_rtclock_get(start);
         if(u->first_iteration){
             initialize_buffer(u, &tchunk);
         }else{
             input_buffer(u, &tchunk);
         }
-        //pa_timeval_load(&end);
+        //pa_rtclock_get(&end);
         //pa_log_debug("Took %0.5f seconds to setup", pa_timeval_diff(end, start) / (double) PA_USEC_PER_SEC);
         pa_memblock_unref(tchunk.memblock);
     }while(u->samples_gathered < u->window_size);
-    pa_timeval_load(&end);
+    pa_rtclock_get(&end);
     pa_log_debug("Took %0.6f seconds to get data", (double) pa_timeval_diff(&end, &start) / PA_USEC_PER_SEC);
 
     pa_assert(u->fft_size >= u->window_size);
     pa_assert(u->R < u->window_size);
     /* set the H filter */
     u->H = u->Hs[pa_aupdate_read_begin(u->a_H)];
-    pa_timeval_load(&start);
+    pa_rtclock_get(&start);
     /* process a block */
     process_samples(u, chunk);
-    pa_timeval_load(&end);
+    pa_rtclock_get(&end);
     pa_log_debug("Took %0.6f seconds to process", (double) pa_timeval_diff(&end, &start) / PA_USEC_PER_SEC);
     pa_aupdate_read_end(u->a_H);
 

commit cd54ecdc8c6b7947bd06a7db69055cfba078f1bf
Author: Jason Newton <nevion at gmail.com>
Date:   Mon Aug 17 10:52:43 2009 -0700

    module-equalizer-sink: drop old macros for new library based ones

diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
index a8ccff8..5152b64 100755
--- a/src/modules/module-equalizer-sink.c
+++ b/src/modules/module-equalizer-sink.c
@@ -136,7 +136,6 @@ static const char* const valid_modargs[] = {
 
 
 #define v_size 4
-#define mround(x, y) ((x + y - 1) / y) * y
 #define SINKLIST "equalized_sinklist"
 #define EQDB "equalizer_db"
 static void dbus_init(struct userdata *u);
@@ -349,10 +348,10 @@ static void dsp_logic(
 //    fftwf_complex * restrict output_window,//The transformed window'd src
 //    struct userdata *u){//Collection of constants
 //
-//    const size_t window_size = mround(u->window_size,v_size);
-//    const size_t fft_h = mround(u->fft_size / 2 + 1, v_size / 2);
-//    //const size_t R = mround(u->R, v_size);
-//    const size_t overlap_size = mround(u->overlap_size, v_size);
+//    const size_t window_size = PA_ROUND_UP(u->window_size,v_size);
+//    const size_t fft_h = PA_ROUND_UP(u->fft_size / 2 + 1, v_size / 2);
+//    //const size_t R = PA_ROUND_UP(u->R, v_size);
+//    const size_t overlap_size = PA_ROUND_UP(u->overlap_size, v_size);
 //
 //    //assert(u->samples_gathered >= u->R);
 //    //zero out the bit beyond the real overlap so we don't add garbage
@@ -677,7 +676,7 @@ static void sink_input_attach_cb(pa_sink_input *i) {
 
     pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency);
     fs = pa_frame_size(&(u->sink->sample_spec));
-    pa_sink_set_max_request_within_thread(u->sink, mround(pa_sink_input_get_max_request(i), u->R*fs));
+    pa_sink_set_max_request_within_thread(u->sink, PA_ROUND_UP(pa_sink_input_get_max_request(i), u->R*fs));
 
     //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs);
     //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->master->thread_info.max_latency);
@@ -810,7 +809,7 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
 //ensure's memory allocated is a multiple of v_size
 //and aligned
 static void * alloc(size_t x,size_t s){
-    size_t f = mround(x*s, sizeof(float)*v_size);
+    size_t f = PA_ROUND_UP(x*s, sizeof(float)*v_size);
     float *t;
     pa_assert(f >= x*s);
     //printf("requested %ld floats=%ld bytes, rem=%ld\n", x, x*sizeof(float), x*sizeof(float)%16);

commit 735c8ab6fbd9b673c05a2506f3f3c1d64d4d3970
Author: Jason Newton <nevion at gmail.com>
Date:   Tue Aug 18 22:11:37 2009 -0700

    module-equalizer-sink: added support for preamp

diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
index 5152b64..0d82e01 100755
--- a/src/modules/module-equalizer-sink.c
+++ b/src/modules/module-equalizer-sink.c
@@ -104,6 +104,7 @@ struct userdata {
     size_t overlap_size;//window_size-R
     size_t samples_gathered;
     //message
+    float X;
     float *H;//frequency response filter (magnitude based)
     float *W;//windowing function (time domain)
     float *work_buffer, **input, **overlap_accum;
@@ -111,6 +112,7 @@ struct userdata {
     fftwf_plan forward_plan, inverse_plan;
     //size_t samplings;
 
+    float Xs[2];
     float *Hs[2];//thread updatable copies
     pa_aupdate *a_H;
     pa_memchunk conv_buffer;
@@ -138,6 +140,8 @@ static const char* const valid_modargs[] = {
 #define v_size 4
 #define SINKLIST "equalized_sinklist"
 #define EQDB "equalizer_db"
+#define FILTER_SIZE (u->fft_size / 2 + 1)
+#define PROFILE_SIZE (FILTER_SIZE + 1)
 static void dbus_init(struct userdata *u);
 static void dbus_done(struct userdata *u);
 
@@ -199,7 +203,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
     switch (code) {
 
         case PA_SINK_MESSAGE_GET_LATENCY: {
-            size_t fs=pa_frame_size(&u->sink->sample_spec);
+            //size_t fs=pa_frame_size(&u->sink->sample_spec);
 
             /* The sink is _put() before the sink input is, so let's
              * make sure we don't access it in that time. Also, the
@@ -215,8 +219,8 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
                 pa_sink_get_latency_within_thread(u->sink_input->sink) +
 
                 /* Add the latency internal to our sink input on top */
-                pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->sink_input->sink->sample_spec) +
-                pa_bytes_to_usec(u->samples_gathered * fs, &u->sink->sample_spec);
+                pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->sink_input->sink->sample_spec);
+            //    pa_bytes_to_usec(u->samples_gathered * fs, &u->sink->sample_spec);
             //+ pa_bytes_to_usec(u->latency * fs, ss)
             //+ pa_bytes_to_usec(pa_memblockq_get_length(u->input_q), ss);
             return 0;
@@ -274,15 +278,6 @@ static void sink_update_requested_latency(pa_sink *s) {
             pa_sink_get_requested_latency_within_thread(s));
 }
 
-typedef float v4sf __attribute__ ((__aligned__(v_size * sizeof(float))));
-typedef union float_vector {
-    float f[v_size];
-    v4sf v;
-#ifdef __SSE2__
-    __m128 m;
-#endif
-} float_vector_t;
-
 //reference implementation
 static void dsp_logic(
     float * restrict dst,//used as a temp array too, needs to be fft_length!
@@ -299,13 +294,13 @@ static void dsp_logic(
     memset(dst + u->window_size, 0, (u->fft_size - u->window_size) * sizeof(float));
     //window the data
     for(size_t j = 0;j < u->window_size; ++j){
-        dst[j] = W[j] * src[j];
+        dst[j] = u->X * W[j] * src[j];
     }
     //Processing is done here!
     //do fft
     fftwf_execute_dft_r2c(u->forward_plan, dst, output_window);
     //perform filtering
-    for(size_t j = 0; j < u->fft_size / 2 + 1; ++j){
+    for(size_t j = 0; j < FILTER_SIZE; ++j){
         u->output_window[j][0] *= u->H[j];
         u->output_window[j][1] *= u->H[j];
     }
@@ -335,6 +330,15 @@ static void dsp_logic(
     );
 }
 
+typedef float v4sf __attribute__ ((__aligned__(v_size * sizeof(float))));
+typedef union float_vector {
+    float f[v_size];
+    v4sf v;
+#ifdef __SSE2__
+    __m128 m;
+#endif
+} float_vector_t;
+
 ////regardless of sse enabled, the loops in here assume
 ////16 byte aligned addresses and memory allocations divisible by v_size
 //void dsp_logic(
@@ -347,11 +351,12 @@ static void dsp_logic(
 //    const float * restrict W,//The windowing function
 //    fftwf_complex * restrict output_window,//The transformed window'd src
 //    struct userdata *u){//Collection of constants
-//
+      //float_vector_t x = {u->X, u->X, u->X, u->X};
 //    const size_t window_size = PA_ROUND_UP(u->window_size,v_size);
-//    const size_t fft_h = PA_ROUND_UP(u->fft_size / 2 + 1, v_size / 2);
+//    const size_t fft_h = PA_ROUND_UP(FILTER_SIZE, v_size / 2);
 //    //const size_t R = PA_ROUND_UP(u->R, v_size);
 //    const size_t overlap_size = PA_ROUND_UP(u->overlap_size, v_size);
+//     overlap_size = PA_ROUND_UP(u->overlap_size, v_size);
 //
 //    //assert(u->samples_gathered >= u->R);
 //    //zero out the bit beyond the real overlap so we don't add garbage
@@ -368,9 +373,9 @@ static void dsp_logic(
 //        float_vector_t *w = (float_vector_t*) (W+j);
 //        float_vector_t *s = (float_vector_t*) (src+j);
 //#if __SSE2__
-//        d->m = _mm_mul_ps(w->m, s->m);
+//        d->m = _mm_mul_ps(x->m, _mm_mul_ps(w->m, s->m));
 //#else
-//        d->v = w->v * s->v;
+//        d->v = x->v * w->v * s->v;
 //#endif
 //    }
 //    //Processing is done here!
@@ -498,6 +503,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
     struct userdata *u;
     size_t fs;
     struct timeval start, end;
+    unsigned a_i;
     pa_memchunk tchunk;
     pa_sink_input_assert_ref(i);
     pa_assert_se(u = i->userdata);
@@ -548,7 +554,9 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
     pa_assert(u->fft_size >= u->window_size);
     pa_assert(u->R < u->window_size);
     /* set the H filter */
-    u->H = u->Hs[pa_aupdate_read_begin(u->a_H)];
+    a_i = pa_aupdate_read_begin(u->a_H);
+    u->X = u->Xs[a_i];
+    u->H = u->Hs[a_i];
     pa_rtclock_get(&start);
     /* process a block */
     process_samples(u, chunk);
@@ -727,18 +735,25 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s
 }
 
 static void save_profile(struct userdata *u, char *name){
-    float *H_n = pa_xmalloc((u->fft_size / 2 + 1) * sizeof(float));
-    const float *H = u->Hs[pa_aupdate_read_begin(u->a_H)];
+    unsigned a_i;
+    const size_t profile_size = PROFILE_SIZE * sizeof(float);
+    float *H_n, *profile;
+    const float *H;
     pa_datum key, data;
-    for(size_t i = 0 ; i <= u->fft_size / 2 + 1; ++i){
+    profile = pa_xnew0(float, profile_size);
+    a_i = pa_aupdate_read_begin(u->a_H);
+    H_n = profile + 1;
+    H = u->Hs[a_i];
+    profile[0] = u->Xs[a_i];
+    for(size_t i = 0 ; i <= FILTER_SIZE; ++i){
         //H_n[i] = H[i] * u->fft_size;
         H_n[i] = H[i];
     }
     pa_aupdate_read_end(u->a_H);
     key.data=name;
     key.size = strlen(key.data);
-    data.data = H_n;
-    data.size = (u->fft_size / 2 + 1) * sizeof(float);
+    data.data = profile;
+    data.size = profile_size;
     pa_database_set(u->database, &key, &data, TRUE);
     pa_database_sync(u->database);
 }
@@ -760,13 +775,17 @@ static void remove_profile(pa_core *c, char *name){
 }
 
 static const char* load_profile(struct userdata *u, char *name){
-    pa_datum key,value;
+    unsigned a_i;
+    pa_datum key, value;
+    const size_t profile_size = PROFILE_SIZE * sizeof(float);
     key.data = name;
     key.size = strlen(key.data);
     if(pa_database_get(u->database, &key, &value) != NULL){
-        if(value.size == (u->fft_size / 2 + 1) * sizeof(float)){
-            float *H=u->Hs[pa_aupdate_write_begin(u->a_H)];
-            memcpy(H, value.data, value.size);
+        if(value.size == profile_size){
+            float *H = (float *) value.data;
+            a_i = pa_aupdate_write_begin(u->a_H);
+            u->Xs[a_i] = H[0];
+            memcpy(u->Hs[a_i], H + 1, (FILTER_SIZE) * sizeof(float));
             pa_aupdate_write_end(u->a_H);
         }else{
             return "incompatible size";
@@ -831,6 +850,7 @@ int pa__init(pa_module*m) {
     pa_bool_t *use_default = NULL;
     size_t fs;
     float *H;
+    unsigned a_i;
 
     pa_assert(m);
 
@@ -867,7 +887,7 @@ int pa__init(pa_module*m) {
     u->a_H = pa_aupdate_new();
     u->latency = u->window_size - u->R;
     for(size_t i = 0; i < 2; ++i){
-        u->Hs[i] = alloc((u->fft_size / 2 + 1), sizeof(float));
+        u->Hs[i] = alloc((FILTER_SIZE), sizeof(float));
     }
     u->W = alloc(u->window_size, sizeof(float));
     u->work_buffer = alloc(u->fft_size, sizeof(float));
@@ -880,7 +900,7 @@ int pa__init(pa_module*m) {
         u->overlap_accum[c] = alloc(u->overlap_size, sizeof(float));
         memset(u->overlap_accum[c], 0, u->overlap_size*sizeof(float));
     }
-    u->output_window = alloc((u->fft_size / 2 + 1), sizeof(fftwf_complex));
+    u->output_window = alloc((FILTER_SIZE), sizeof(fftwf_complex));
     u->forward_plan = fftwf_plan_dft_r2c_1d(u->fft_size, u->work_buffer, u->output_window, FFTW_ESTIMATE);
     u->inverse_plan = fftwf_plan_dft_c2r_1d(u->fft_size, u->output_window, u->work_buffer, FFTW_ESTIMATE);
 
@@ -964,8 +984,10 @@ int pa__init(pa_module*m) {
     dbus_init(u);
 
     //default filter to these
-    H=u->Hs[pa_aupdate_write_begin(u->a_H)];
-    for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){
+    a_i = pa_aupdate_write_begin(u->a_H);
+    H = u->Hs[a_i];
+    u->Xs[a_i] = 1.0f;
+    for(size_t i = 0; i < FILTER_SIZE; ++i){
         H[i] = 1.0 / sqrtf(2.0f);
     }
     fix_filter(H, u->fft_size);
@@ -1053,9 +1075,7 @@ void pa__done(pa_module*m) {
 #define MANAGER_IFACE EXTNAME ".Manager"
 #define EQUALIZER_IFACE EXTNAME ".Equalizer"
 static void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u);
-static void get_sinks(pa_core *u, char ***names, unsigned *n_sinks);
 static void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u);
-static void get_profiles(pa_core *u, char ***names, unsigned *n_sinks);
 static void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u);
 static void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u);
 static void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void *_u);
@@ -1064,14 +1084,12 @@ static void equalizer_get_sample_rate(DBusConnection *conn, DBusMessage *msg, vo
 static void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u);
 static void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u);
 static void equalizer_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u);
-static void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u);
 static void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u);
 static void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *_u);
 static void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u);
 static void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u);
 static void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u);
-static void get_filter(struct userdata *u, double **H_);
-static void set_filter(struct userdata *u, double **H_);
 enum manager_method_index {
     MANAGER_METHOD_REMOVE_PROFILE,
     MANAGER_METHOD_MAX
@@ -1144,16 +1162,19 @@ enum equalizer_handler_index {
     EQUALIZER_HANDLER_FILTERSAMPLERATE,
     EQUALIZER_HANDLER_N_COEFS,
     EQUALIZER_HANDLER_FILTER,
+    EQUALIZER_HANDLER_PREAMP,
     EQUALIZER_HANDLER_MAX
 };
 
 pa_dbus_arg_info filter_points_args[]={
     {"xs", "au","in"},
     {"ys", "ad","out"},
+    {"preamp", "d","out"},
 };
 pa_dbus_arg_info seed_filter_args[]={
     {"xs", "au","in"},
     {"ys", "ad","in"},
+    {"preamp", "d","in"},
 };
 pa_dbus_arg_info save_profile_args[]={
     {"name", "s","in"},
@@ -1190,7 +1211,7 @@ static pa_dbus_property_handler equalizer_handlers[EQUALIZER_HANDLER_MAX]={
     [EQUALIZER_HANDLER_SAMPLERATE]{.property_name="SampleRate",.type="u",.get_cb=equalizer_get_sample_rate,.set_cb=NULL},
     [EQUALIZER_HANDLER_FILTERSAMPLERATE]{.property_name="FilterSampleRate",.type="u",.get_cb=equalizer_get_filter_rate,.set_cb=NULL},
     [EQUALIZER_HANDLER_N_COEFS]{.property_name="NFilterCoefficients",.type="u",.get_cb=equalizer_get_n_coefs,.set_cb=NULL},
-    [EQUALIZER_HANDLER_FILTER]{.property_name="Filter",.type="ad",.get_cb=equalizer_get_filter,.set_cb=equalizer_set_filter},
+    [EQUALIZER_HANDLER_FILTER]{.property_name="Filter",.type="ad",.get_cb=equalizer_get_filter,.set_cb=equalizer_set_filter}
 };
 
 enum equalizer_signal_index{
@@ -1215,7 +1236,7 @@ static pa_dbus_interface_info equalizer_info={
     .n_signals=EQUALIZER_SIGNAL_MAX
 };
 
-static void dbus_init(struct userdata *u){
+void dbus_init(struct userdata *u){
     uint32_t dummy;
     DBusMessage *signal = NULL;
     pa_idxset *sink_list = NULL;
@@ -1244,7 +1265,7 @@ static void dbus_init(struct userdata *u){
     dbus_message_unref(signal);
 }
 
-static void dbus_done(struct userdata *u){
+void dbus_done(struct userdata *u){
     pa_idxset *sink_list;
     uint32_t dummy;
 
@@ -1269,7 +1290,7 @@ static void dbus_done(struct userdata *u){
     pa_dbus_protocol_unref(u->dbus_protocol);
 }
 
-static void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void *_u) {
+void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg, void *_u) {
     DBusError error;
     pa_core *c = (pa_core *)_u;
     DBusMessage *signal = NULL;
@@ -1296,7 +1317,7 @@ static void manager_handle_remove_profile(DBusConnection *conn, DBusMessage *msg
     dbus_message_unref(signal);
 }
 
-static void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){
+void manager_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){
     uint32_t rev=1;
     pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_UINT32, &rev);
 }
@@ -1319,7 +1340,7 @@ static void get_sinks(pa_core *u, char ***names, unsigned *n_sinks){
     }
 }
 
-static void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u){
+void manager_get_sinks(DBusConnection *conn, DBusMessage *msg, void *_u){
     unsigned n;
     char **names = NULL;
     pa_assert(conn);
@@ -1367,7 +1388,7 @@ static void get_profiles(pa_core *c, char ***names, unsigned *n){
     pa_strlist_free(head);
 }
 
-static void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u){
+void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_u){
     char **names;
     unsigned n;
     pa_assert(conn);
@@ -1382,7 +1403,7 @@ static void manager_get_profiles(DBusConnection *conn, DBusMessage *msg, void *_
     pa_xfree(names);
 }
 
-static void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){
+void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){
     pa_core *c;
     char **names = NULL;
     unsigned n;
@@ -1418,14 +1439,14 @@ static void manager_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){
     dbus_message_unref(reply);
 }
 
-static void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *_u) {
+void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *_u) {
     struct userdata *u=(struct userdata *) _u;
     DBusError error;
     DBusMessage *signal = NULL;
     float *ys;
     uint32_t *xs;
-    double *_ys;
-    unsigned x_npoints, y_npoints;
+    double *_ys, preamp;
+    unsigned x_npoints, y_npoints, a_i;
     float *H;
     pa_bool_t points_good = TRUE;
     pa_assert(conn);
@@ -1437,13 +1458,14 @@ static void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg,
     if(!dbus_message_get_args(msg, &error,
                 DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &xs, &x_npoints,
                 DBUS_TYPE_ARRAY, DBUS_TYPE_DOUBLE, &_ys, &y_npoints,
+                DBUS_TYPE_DOUBLE, &preamp,
                 DBUS_TYPE_INVALID)){
         pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
         dbus_error_free(&error);
         return;
     }
     for(size_t i = 0; i < x_npoints; ++i){
-        if(xs[i] >= u->fft_size / 2 + 1){
+        if(xs[i] >= FILTER_SIZE){
             points_good = FALSE;
             break;
         }
@@ -1453,8 +1475,8 @@ static void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg,
         dbus_error_free(&error);
         return;
 
-    }else if(x_npoints != y_npoints || x_npoints < 2 || x_npoints > u->fft_size / 2 +1  ){
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs and ys must be the same length and 2<=l<=%ld!", u->fft_size / 2 + 1);
+    }else if(x_npoints != y_npoints || x_npoints < 2 || x_npoints > FILTER_SIZE  ){
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs and ys must be the same length and 2<=l<=%ld!", FILTER_SIZE);
         dbus_error_free(&error);
         return;
     }else if(xs[0] != 0 || xs[x_npoints - 1] != u->fft_size / 2){
@@ -1467,9 +1489,10 @@ static void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg,
     for(uint32_t i = 0; i < x_npoints; ++i){
         ys[i] = (float) _ys[i];
     }
-
-    H = u->Hs[pa_aupdate_write_begin(u->a_H)];
-    interpolate(H, u->fft_size / 2 + 1, xs, ys, x_npoints);
+    a_i = pa_aupdate_write_begin(u->a_H);
+    H = u->Hs[a_i];
+    u->Xs[a_i] = preamp;
+    interpolate(H, FILTER_SIZE, xs, ys, x_npoints);
     fix_filter(H, u->fft_size);
     pa_aupdate_write_end(u->a_H);
     pa_xfree(ys);
@@ -1484,14 +1507,16 @@ static void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg,
     dbus_message_unref(signal);
 }
 
-static void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u) {
+void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u) {
     struct userdata *u = (struct userdata *) _u;
     DBusError error;
     uint32_t *xs;
-    double *ys;
-    unsigned x_npoints;
+    double *ys, preamp;
+    unsigned x_npoints, a_i;
     float *H;
     pa_bool_t points_good=TRUE;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
 
     pa_assert(conn);
     pa_assert(msg);
@@ -1507,30 +1532,39 @@ static void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage
         return;
     }
     for(size_t i = 0; i < x_npoints; ++i){
-        if(xs[i] >= u->fft_size / 2 + 1){
+        if(xs[i] >= FILTER_SIZE){
             points_good=FALSE;
             break;
         }
     }
 
-    if(x_npoints > u->fft_size / 2 +1 || !points_good){
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs indices/length must be <= %ld!", u->fft_size / 2 + 1);
+    if(x_npoints > FILTER_SIZE || !points_good){
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs indices/length must be <= %ld!", FILTER_SIZE);
         dbus_error_free(&error);
         return;
     }
 
     ys = pa_xmalloc(x_npoints * sizeof(double));
-    H = u->Hs[pa_aupdate_read_begin(u->a_H)];
+    a_i = pa_aupdate_read_begin(u->a_H);
+    H = u->Hs[a_i];
+    preamp = u->Xs[a_i];
     for(uint32_t i = 0; i < x_npoints; ++i){
         ys[i] = H[xs[i]] * u->fft_size;
     }
     pa_aupdate_read_end(u->a_H);
 
-    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_DOUBLE, ys, x_npoints);
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+    dbus_message_iter_init_append(reply, &msg_iter);
+
+    pa_dbus_append_basic_array(&msg_iter, DBUS_TYPE_DOUBLE, ys, x_npoints);
+    pa_dbus_append_basic_variant(&msg_iter, DBUS_TYPE_DOUBLE, &preamp);
+
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+    dbus_message_unref(reply);
     pa_xfree(ys);
 }
 
-static void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u) {
+void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u) {
     struct userdata *u = (struct userdata *) _u;
     char *name;
     DBusMessage *signal = NULL;
@@ -1555,7 +1589,7 @@ static void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg
     dbus_message_unref(signal);
 }
 
-static void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u) {
+void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u) {
     struct userdata *u=(struct userdata *) _u;
     char *name;
     DBusError error;
@@ -1574,7 +1608,7 @@ static void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg
         dbus_error_free(&error);
         return;
     }
-    err_msg = load_profile(u,name);
+    err_msg = load_profile(u, name);
     if(err_msg != NULL){
         pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "error loading profile %s: %s", name, err_msg);
         dbus_error_free(&error);
@@ -1587,23 +1621,23 @@ static void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg
     dbus_message_unref(signal);
 }
 
-static void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){
+void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){
     uint32_t rev=1;
     pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_UINT32, &rev);
 }
 
-static void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u){
+void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u){
     struct userdata *u;
     uint32_t n_coefs;
     pa_assert_se(u = (struct userdata *) _u);
     pa_assert(conn);
     pa_assert(msg);
 
-    n_coefs = (uint32_t) (u->fft_size / 2 + 1);
+    n_coefs = (uint32_t) PROFILE_SIZE;
     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &n_coefs);
 }
 
-static void equalizer_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u){
+void equalizer_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u){
     struct userdata *u;
     uint32_t rate;
     pa_assert_se(u = (struct userdata *) _u);
@@ -1614,7 +1648,7 @@ static void equalizer_get_sample_rate(DBusConnection *conn, DBusMessage *msg, vo
     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &rate);
 }
 
-static void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u){
+void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u){
     struct userdata *u;
     uint32_t fft_size;
     pa_assert_se(u = (struct userdata *) _u);
@@ -1625,71 +1659,47 @@ static void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, vo
     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &fft_size);
 }
 
-static void get_filter(struct userdata *u, double **H_){
+static double * get_filter(struct userdata *u){
     float *H;
-    *H_ = pa_xnew0(double, u->fft_size / 2 + 1);
-    H = u->Hs[pa_aupdate_read_begin(u->a_H)];
-    for(size_t i = 0;i < u->fft_size / 2 + 1; ++i){
-        (*H_)[i] = H[i] * u->fft_size;
+    double *H_;
+    unsigned a_i;
+    H_ = pa_xnew0(double, PROFILE_SIZE);
+    a_i = pa_aupdate_read_begin(u->a_H);
+    H = u->Hs[a_i];
+    H_[0] = u->Xs[a_i];
+    for(size_t i = 0;i < FILTER_SIZE; ++i){
+        H_[i + 1] = H[i] * u->fft_size;
     }
     pa_aupdate_read_end(u->a_H);
+    return H_;
 }
 
-static void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){
-    struct userdata *u;
-    DBusMessage *reply = NULL;
-    DBusMessageIter msg_iter, dict_iter;
-    uint32_t rev, n_coefs, rate, fft_size;
-    double *H;
-    pa_assert_se(u = (struct userdata *) _u);
-    pa_assert(msg);
-
-    rev = 1;
-    n_coefs = (uint32_t) (u->fft_size / 2 + 1);
-    rate = (uint32_t) u->sink->sample_spec.rate;
-    fft_size = (uint32_t) u->fft_size;
-
-    pa_assert_se((reply = dbus_message_new_method_return(msg)));
-    dbus_message_iter_init_append(reply, &msg_iter);
-    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
-
-    pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_REVISION].property_name, DBUS_TYPE_UINT32, &rev);
-    pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_SAMPLERATE].property_name, DBUS_TYPE_UINT32, &rate);
-    pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_FILTERSAMPLERATE].property_name, DBUS_TYPE_UINT32, &fft_size);
-    pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_N_COEFS].property_name, DBUS_TYPE_UINT32, &n_coefs);
-    get_filter(u, &H);
-    pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_FILTER].property_name, DBUS_TYPE_UINT32, &H);
-    pa_xfree(H);
-
-    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
-    pa_assert_se(dbus_connection_send(conn, reply, NULL));
-    dbus_message_unref(reply);
-}
-
-static void equalizer_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u){
+void equalizer_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u){
     struct userdata *u;
     unsigned n_coefs;
     double *H_;
     pa_assert_se(u = (struct userdata *) _u);
 
-    n_coefs = u->fft_size / 2 + 1;
+    n_coefs = PROFILE_SIZE;
     pa_assert(conn);
     pa_assert(msg);
-    get_filter(u, &H_);
+    H_ = get_filter(u);
     pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_DOUBLE, H_, n_coefs);
     pa_xfree(H_);
 }
 
-static void set_filter(struct userdata *u, double **H_){
-    float *H = u->Hs[pa_aupdate_write_begin(u->a_H)];
-    for(size_t i = 0; i < u->fft_size / 2 + 1; ++i){
-        H[i] = (float) (*H_)[i];
+static void set_filter(struct userdata *u, double *H_){
+    unsigned a_i= pa_aupdate_write_begin(u->a_H);
+    float *H = u->Hs[a_i];
+    u->Xs[a_i] = H_[0];
+    for(size_t i = 0; i < FILTER_SIZE; ++i){
+        H[i] = (float) H_[i + 1];
     }
-    fix_filter(H, u->fft_size);
+    fix_filter(H + 1, u->fft_size);
     pa_aupdate_write_end(u->a_H);
 }
 
-static void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){
+void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){
     struct userdata *u;
     double *H;
     unsigned _n_coefs;
@@ -1701,13 +1711,11 @@ static void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_
     if(pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_DOUBLE, &H, &_n_coefs)){
         return;
     }
-    if(_n_coefs!=u->fft_size / 2 + 1){
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "This filter takes exactly %ld coefficients, you gave %d", u->fft_size / 2 + 1, _n_coefs);
+    if(_n_coefs != PROFILE_SIZE){
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "This filter takes exactly %ld coefficients, you gave %d", PROFILE_SIZE, _n_coefs);
         return;
     }
-    set_filter(u, &H);
-    //Stupid for IO reasons?  Add a save signal to dbus instead
-    //save_state(u);
+    set_filter(u, H);
 
     pa_dbus_send_empty_reply(conn, msg);
 
@@ -1715,3 +1723,34 @@ static void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_
     pa_dbus_protocol_send_signal(u->dbus_protocol, signal);
     dbus_message_unref(signal);
 }
+
+void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){
+    struct userdata *u;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter, dict_iter;
+    uint32_t rev, n_coefs, rate, fft_size;
+    double *H;
+    pa_assert_se(u = (struct userdata *) _u);
+    pa_assert(msg);
+
+    rev = 1;
+    n_coefs = (uint32_t) PROFILE_SIZE;
+    rate = (uint32_t) u->sink->sample_spec.rate;
+    fft_size = (uint32_t) u->fft_size;
+
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+    dbus_message_iter_init_append(reply, &msg_iter);
+    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
+
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_REVISION].property_name, DBUS_TYPE_UINT32, &rev);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_SAMPLERATE].property_name, DBUS_TYPE_UINT32, &rate);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_FILTERSAMPLERATE].property_name, DBUS_TYPE_UINT32, &fft_size);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_N_COEFS].property_name, DBUS_TYPE_UINT32, &n_coefs);
+    H = get_filter(u);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_FILTER].property_name, DBUS_TYPE_UINT32, &H);
+    pa_xfree(H);
+
+    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+    dbus_message_unref(reply);
+}

commit 2f6fd32cc5726b47d332ad71c0f04f16614118ba
Author: Jason Newton <nevion at gmail.com>
Date:   Wed Aug 19 00:30:07 2009 -0700

    module-equalizer-sink: fixed a bug w/ new zero-latency input scheme (that was an interesting/cool bug!)

diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
index 0d82e01..67a3d74 100755
--- a/src/modules/module-equalizer-sink.c
+++ b/src/modules/module-equalizer-sink.c
@@ -104,8 +104,6 @@ struct userdata {
     size_t overlap_size;//window_size-R
     size_t samples_gathered;
     //message
-    float X;
-    float *H;//frequency response filter (magnitude based)
     float *W;//windowing function (time domain)
     float *work_buffer, **input, **overlap_accum;
     fftwf_complex *output_window;
@@ -113,7 +111,7 @@ struct userdata {
     //size_t samplings;
 
     float Xs[2];
-    float *Hs[2];//thread updatable copies
+    float *Hs[2];//thread updatable copies of the freq response filters (magintude based)
     pa_aupdate *a_H;
     pa_memchunk conv_buffer;
     pa_memblockq *input_q;
@@ -284,7 +282,8 @@ static void dsp_logic(
     float * restrict src,/*input data w/ overlap at start,
                                *automatically cycled in routine
                                */
-    float * restrict overlap,//The size of the overlap
+    float * restrict overlap,
+    const float X,//multipliar
     const float * restrict H,//The freq. magnitude scalers filter
     const float * restrict W,//The windowing function
     fftwf_complex * restrict output_window,//The transformed window'd src
@@ -293,16 +292,16 @@ static void dsp_logic(
     //zero padd the data
     memset(dst + u->window_size, 0, (u->fft_size - u->window_size) * sizeof(float));
     //window the data
-    for(size_t j = 0;j < u->window_size; ++j){
-        dst[j] = u->X * W[j] * src[j];
+    for(size_t j = 0; j < u->window_size; ++j){
+        dst[j] = X * W[j] * src[j];
     }
     //Processing is done here!
     //do fft
     fftwf_execute_dft_r2c(u->forward_plan, dst, output_window);
     //perform filtering
     for(size_t j = 0; j < FILTER_SIZE; ++j){
-        u->output_window[j][0] *= u->H[j];
-        u->output_window[j][1] *= u->H[j];
+        u->output_window[j][0] *= H[j];
+        u->output_window[j][1] *= H[j];
     }
     //inverse fft
     fftwf_execute_dft_c2r(u->inverse_plan, output_window, dst);
@@ -314,9 +313,9 @@ static void dsp_logic(
     //}
 
     //overlap add and preserve overlap component from this window (linear phase)
-    for(size_t j = 0;j < u->overlap_size; ++j){
+    for(size_t j = 0; j < u->overlap_size; ++j){
         u->work_buffer[j] += overlap[j];
-        overlap[j] = dst[u->R+j];
+        overlap[j] = dst[u->R + j];
     }
     ////debug: tests if basic buffering works
     ////shouldn't modify the signal AT ALL (beyond roundoff)
@@ -325,8 +324,8 @@ static void dsp_logic(
     //}
 
     //preseve the needed input for the next window's overlap
-    memmove(src, src+u->R,
-        ((u->overlap_size + u->samples_gathered) - u->R)*sizeof(float)
+    memmove(src, src + u->R,
+        u->overlap_size * sizeof(float)
     );
 }
 
@@ -347,11 +346,12 @@ typedef union float_vector {
 //                               *automatically cycled in routine
 //                               */
 //    float * restrict overlap,//The size of the overlap
+//    const float X,//multipliar
 //    const float * restrict H,//The freq. magnitude scalers filter
 //    const float * restrict W,//The windowing function
 //    fftwf_complex * restrict output_window,//The transformed window'd src
 //    struct userdata *u){//Collection of constants
-      //float_vector_t x = {u->X, u->X, u->X, u->X};
+      //float_vector_t x = {X, X, X, X};
 //    const size_t window_size = PA_ROUND_UP(u->window_size,v_size);
 //    const size_t fft_h = PA_ROUND_UP(FILTER_SIZE, v_size / 2);
 //    //const size_t R = PA_ROUND_UP(u->R, v_size);
@@ -430,25 +430,33 @@ typedef union float_vector {
 //    //}
 //
 //    //preseve the needed input for the next window's overlap
-//    memmove(src, src+u->R,
-//        ((u->overlap_size+u->samples_gathered)+-u->R)*sizeof(float)
+//    memmove(src, src + u->R,
+//        u->overlap_size * sizeof(float)
 //    );
 //}
 
 static void process_samples(struct userdata *u, pa_memchunk *tchunk){
     size_t fs=pa_frame_size(&(u->sink->sample_spec));
     float *dst;
+    unsigned a_i;
+    float *H, X;
     pa_assert(u->samples_gathered >= u->R);
     tchunk->index = 0;
     tchunk->length = u->R * fs;
     tchunk->memblock = pa_memblock_new(u->sink->core->mempool, tchunk->length);
     dst = ((float*)pa_memblock_acquire(tchunk->memblock));
+    /* set the H filter */
+    a_i = pa_aupdate_read_begin(u->a_H);
+    X = u->Xs[a_i];
+    H = u->Hs[a_i];
+
     for(size_t c=0;c < u->channels; c++) {
         dsp_logic(
             u->work_buffer,
             u->input[c],
             u->overlap_accum[c],
-            u->H,
+            X,
+            H,
             u->W,
             u->output_window,
             u
@@ -465,6 +473,8 @@ static void process_samples(struct userdata *u, pa_memchunk *tchunk){
     }
     pa_memblock_release(tchunk->memblock);
     u->samples_gathered -= u->R;
+
+    pa_aupdate_read_end(u->a_H);
 }
 
 static void initialize_buffer(struct userdata *u, pa_memchunk *in){
@@ -492,7 +502,7 @@ static void input_buffer(struct userdata *u, pa_memchunk *in){
         pa_assert_se(
             u->input[c]+u->samples_gathered+samples <= u->input[c]+u->window_size
         );
-        pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c]+u->overlap_size+u->samples_gathered, sizeof(float), src + c, fs, samples);
+        pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c]+u->samples_gathered, sizeof(float), src + c, fs, samples);
     }
     u->samples_gathered += samples;
     pa_memblock_release(in->memblock);
@@ -503,7 +513,6 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
     struct userdata *u;
     size_t fs;
     struct timeval start, end;
-    unsigned a_i;
     pa_memchunk tchunk;
     pa_sink_input_assert_ref(i);
     pa_assert_se(u = i->userdata);
@@ -554,15 +563,11 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
     pa_assert(u->fft_size >= u->window_size);
     pa_assert(u->R < u->window_size);
     /* set the H filter */
-    a_i = pa_aupdate_read_begin(u->a_H);
-    u->X = u->Xs[a_i];
-    u->H = u->Hs[a_i];
     pa_rtclock_get(&start);
     /* process a block */
     process_samples(u, chunk);
     pa_rtclock_get(&end);
     pa_log_debug("Took %0.6f seconds to process", (double) pa_timeval_diff(&end, &start) / PA_USEC_PER_SEC);
-    pa_aupdate_read_end(u->a_H);
 
     pa_assert(chunk->memblock);
     //pa_log_debug("gave %ld", chunk->length/fs);
@@ -1162,7 +1167,6 @@ enum equalizer_handler_index {
     EQUALIZER_HANDLER_FILTERSAMPLERATE,
     EQUALIZER_HANDLER_N_COEFS,
     EQUALIZER_HANDLER_FILTER,
-    EQUALIZER_HANDLER_PREAMP,
     EQUALIZER_HANDLER_MAX
 };
 

commit b028e4e917441f138197a5c6a7b86b50b26bc35b
Author: Jason Newton <nevion at gmail.com>
Date:   Wed Aug 19 23:22:33 2009 -0700

    module-equalizer-sink: per-channel filtering support + profiles, easier default configuration

diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
index 67a3d74..98b6b89 100755
--- a/src/modules/module-equalizer-sink.c
+++ b/src/modules/module-equalizer-sink.c
@@ -110,15 +110,16 @@ struct userdata {
     fftwf_plan forward_plan, inverse_plan;
     //size_t samplings;
 
-    float Xs[2];
-    float *Hs[2];//thread updatable copies of the freq response filters (magintude based)
-    pa_aupdate *a_H;
+    float **Xs;
+    float ***Hs;//thread updatable copies of the freq response filters (magintude based)
+    pa_aupdate **a_H;
     pa_memchunk conv_buffer;
     pa_memblockq *input_q;
     pa_bool_t first_iteration;
 
     pa_dbus_protocol *dbus_protocol;
     char *dbus_path;
+    pa_bool_t set_default;
 
     pa_database *database;
 };
@@ -129,6 +130,7 @@ static const char* const valid_modargs[] = {
     "master",
     "format",
     "rate",
+    "set_default",
     "channels",
     "channel_map",
     NULL
@@ -138,8 +140,10 @@ static const char* const valid_modargs[] = {
 #define v_size 4
 #define SINKLIST "equalized_sinklist"
 #define EQDB "equalizer_db"
+#define EQ_STATE_DB "equalizer-state"
 #define FILTER_SIZE (u->fft_size / 2 + 1)
-#define PROFILE_SIZE (FILTER_SIZE + 1)
+#define CHANNEL_PROFILE_SIZE (FILTER_SIZE + 1)
+#define STATE_SIZE (CHANNEL_PROFILE_SIZE * u->channels)
 static void dbus_init(struct userdata *u);
 static void dbus_done(struct userdata *u);
 
@@ -445,12 +449,11 @@ static void process_samples(struct userdata *u, pa_memchunk *tchunk){
     tchunk->length = u->R * fs;
     tchunk->memblock = pa_memblock_new(u->sink->core->mempool, tchunk->length);
     dst = ((float*)pa_memblock_acquire(tchunk->memblock));
-    /* set the H filter */
-    a_i = pa_aupdate_read_begin(u->a_H);
-    X = u->Xs[a_i];
-    H = u->Hs[a_i];
 
     for(size_t c=0;c < u->channels; c++) {
+        a_i = pa_aupdate_read_begin(u->a_H[c]);
+        X = u->Xs[c][a_i];
+        H = u->Hs[c][a_i];
         dsp_logic(
             u->work_buffer,
             u->input[c],
@@ -461,6 +464,7 @@ static void process_samples(struct userdata *u, pa_memchunk *tchunk){
             u->output_window,
             u
         );
+        pa_aupdate_read_end(u->a_H[c]);
         if(u->first_iteration){
             /* The windowing function will make the audio ramped in, as a cheap fix we can
              * undo the windowing (for non-zero window values)
@@ -473,8 +477,6 @@ static void process_samples(struct userdata *u, pa_memchunk *tchunk){
     }
     pa_memblock_release(tchunk->memblock);
     u->samples_gathered -= u->R;
-
-    pa_aupdate_read_end(u->a_H);
 }
 
 static void initialize_buffer(struct userdata *u, pa_memchunk *in){
@@ -538,6 +540,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
         //pa_sink_render_into(u->sink, buffer);
         while(pa_memblockq_peek(u->input_q, &tchunk) < 0){
             pa_sink_render(u->sink, input_remaining*fs, &tchunk);
+            //pa_sink_render_full(u->sink, input_remaining*fs, &tchunk);
             pa_assert(tchunk.memblock);
             pa_memblockq_push(u->input_q, &tchunk);
             pa_memblock_unref(tchunk.memblock);
@@ -699,6 +702,9 @@ static void sink_input_attach_cb(pa_sink_input *i) {
     //TODO: this guy causes dropouts constantly+rewinds, it's unusable
     //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency);
     pa_sink_attach_within_thread(u->sink);
+    if(u->set_default){
+        pa_namereg_set_default_sink(u->module->core, u->sink);
+    }
 }
 
 /* Called from main context */
@@ -739,22 +745,22 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s
     }
 }
 
-static void save_profile(struct userdata *u, char *name){
+static void save_profile(struct userdata *u, size_t channel, char *name){
     unsigned a_i;
-    const size_t profile_size = PROFILE_SIZE * sizeof(float);
+    const size_t profile_size = CHANNEL_PROFILE_SIZE * sizeof(float);
     float *H_n, *profile;
     const float *H;
     pa_datum key, data;
     profile = pa_xnew0(float, profile_size);
-    a_i = pa_aupdate_read_begin(u->a_H);
+    a_i = pa_aupdate_read_begin(u->a_H[channel]);
+    profile[0] = u->Xs[a_i][channel];
+    H = u->Hs[channel][a_i];
     H_n = profile + 1;
-    H = u->Hs[a_i];
-    profile[0] = u->Xs[a_i];
     for(size_t i = 0 ; i <= FILTER_SIZE; ++i){
-        //H_n[i] = H[i] * u->fft_size;
-        H_n[i] = H[i];
+        H_n[i] = H[i] * u->fft_size;
+        //H_n[i] = H[i];
     }
-    pa_aupdate_read_end(u->a_H);
+    pa_aupdate_read_end(u->a_H[channel]);
     key.data=name;
     key.size = strlen(key.data);
     data.data = profile;
@@ -764,9 +770,38 @@ static void save_profile(struct userdata *u, char *name){
 }
 
 static void save_state(struct userdata *u){
-    char *state_name = pa_sprintf_malloc("%s-previous-state", u->name);
-    save_profile(u, state_name);
-    pa_xfree(state_name);
+    unsigned a_i;
+    const size_t state_size = STATE_SIZE * sizeof(float);
+    float *H_n, *state;
+    float *H;
+    pa_datum key, data;
+    pa_database *database;
+    char *dbname;
+    char *state_name = u->name;
+    state = pa_xnew0(float, STATE_SIZE);
+
+    for(size_t c = 0; c < u->channels; ++c){
+        a_i = pa_aupdate_read_begin(u->a_H[c]);
+        state[c * CHANNEL_PROFILE_SIZE] = u->Xs[a_i][c];
+        H = u->Hs[c][a_i];
+        H_n = state + c * CHANNEL_PROFILE_SIZE + 1;
+        memcpy(H_n, H, FILTER_SIZE * sizeof(float));
+        pa_aupdate_read_end(u->a_H[c]);
+    }
+
+    key.data = state_name;
+    key.size = strlen(key.data);
+    data.data = state;
+    data.size = state_size;
+    //thread safety for 0.9.17?
+    pa_assert_se(dbname = pa_state_path(EQ_STATE_DB, TRUE));
+    pa_assert_se(database = pa_database_open(dbname, TRUE));
+    pa_xfree(dbname);
+
+    pa_database_set(database, &key, &data, TRUE);
+    pa_database_sync(database);
+    pa_database_close(database);
+    pa_xfree(state);
 }
 
 static void remove_profile(pa_core *c, char *name){
@@ -779,19 +814,20 @@ static void remove_profile(pa_core *c, char *name){
     pa_database_sync(database);
 }
 
-static const char* load_profile(struct userdata *u, char *name){
+static const char* load_profile(struct userdata *u, size_t channel, char *name){
     unsigned a_i;
     pa_datum key, value;
-    const size_t profile_size = PROFILE_SIZE * sizeof(float);
+    const size_t profile_size = CHANNEL_PROFILE_SIZE * sizeof(float);
     key.data = name;
     key.size = strlen(key.data);
     if(pa_database_get(u->database, &key, &value) != NULL){
         if(value.size == profile_size){
-            float *H = (float *) value.data;
-            a_i = pa_aupdate_write_begin(u->a_H);
-            u->Xs[a_i] = H[0];
-            memcpy(u->Hs[a_i], H + 1, (FILTER_SIZE) * sizeof(float));
-            pa_aupdate_write_end(u->a_H);
+            float *profile = (float *) value.data;
+            a_i = pa_aupdate_write_begin(u->a_H[channel]);
+            u->Xs[channel][a_i] = profile[0];
+            memcpy(u->Hs[channel][a_i], profile + 1, CHANNEL_PROFILE_SIZE * sizeof(float));
+            fix_filter(u->Hs[channel][a_i], u->fft_size);
+            pa_aupdate_write_end(u->a_H[channel]);
         }else{
             return "incompatible size";
         }
@@ -800,13 +836,39 @@ static const char* load_profile(struct userdata *u, char *name){
         return "profile doesn't exist";
     }
     return NULL;
-    //fix_filter(u->H, u->fft_size);
 }
 
 static void load_state(struct userdata *u){
-    char *state_name=pa_sprintf_malloc("%s-previous-state", u->name);
-    load_profile(u,state_name);
-    pa_xfree(state_name);
+    unsigned a_i;
+    float *H;
+    pa_datum key, value;
+    pa_database *database;
+    char *dbname;
+    char *state_name = u->name;
+
+    pa_assert_se(dbname = pa_state_path(EQ_STATE_DB, FALSE));
+    database = pa_database_open(dbname, FALSE);
+    pa_xfree(dbname);
+    if(!database){
+        return;
+    }
+
+    key.data = state_name;
+    key.size = strlen(key.data);
+
+    if(pa_database_get(database, &key, &value) != NULL){
+        size_t states = PA_MIN(value.size / (CHANNEL_PROFILE_SIZE * sizeof(float)), u->channels);
+        float *state = (float *) value.data;
+        for(size_t c = 0; c < states; ++c){
+            a_i = pa_aupdate_write_begin(u->a_H[c]);
+            H = state + c * CHANNEL_PROFILE_SIZE + 1;
+            u->Xs[c][a_i] = state[c * CHANNEL_PROFILE_SIZE];
+            memcpy(u->Hs[c][a_i], H, FILTER_SIZE * sizeof(float));
+            pa_aupdate_write_end(u->a_H[c]);
+        }
+        pa_datum_free(&value);
+    }
+    pa_database_close(database);
 }
 
 /* Called from main context */
@@ -865,8 +927,12 @@ int pa__init(pa_module*m) {
     }
 
     if (!(master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "master", NULL), PA_NAMEREG_SINK))) {
-        pa_log("Master sink not found");
-        goto fail;
+        pa_log("Master sink not found, trying default");
+        master = pa_namereg_get_default_sink(m->core);
+        if(!master){
+            pa_log("no default sink found!");
+            goto fail;
+        }
     }
 
     ss = master->sample_spec;
@@ -882,6 +948,9 @@ int pa__init(pa_module*m) {
     u->module = m;
     m->userdata = u;
 
+    u->set_default = TRUE;
+    u->set_default = pa_modargs_get_value_boolean(ma, "set_default", &u->set_default);
+
     u->channels = ss.channels;
     u->fft_size = pow(2, ceil(log(ss.rate)/log(2)));
     pa_log_debug("fft size: %ld", u->fft_size);
@@ -889,10 +958,16 @@ int pa__init(pa_module*m) {
     u->R = (u->window_size + 1) / 2;
     u->overlap_size = u->window_size - u->R;
     u->samples_gathered = 0;
-    u->a_H = pa_aupdate_new();
+    u->a_H = pa_xnew0(pa_aupdate *, u->channels);
     u->latency = u->window_size - u->R;
-    for(size_t i = 0; i < 2; ++i){
-        u->Hs[i] = alloc((FILTER_SIZE), sizeof(float));
+    u->Xs = pa_xnew0(float *, u->channels);
+    u->Hs = pa_xnew0(float **, u->channels);
+    for(size_t c = 0; c < u->channels; ++c){
+        u->Xs[c] = pa_xnew0(float, 2);
+        u->Hs[c] = pa_xnew0(float *, 2);
+        for(size_t i = 0; i < 2; ++i){
+            u->Hs[c][i] = alloc((FILTER_SIZE), sizeof(float));
+        }
     }
     u->W = alloc(u->window_size, sizeof(float));
     u->work_buffer = alloc(u->fft_size, sizeof(float));
@@ -900,6 +975,7 @@ int pa__init(pa_module*m) {
     u->input = pa_xnew0(float *, u->channels);
     u->overlap_accum = pa_xnew0(float *, u->channels);
     for(size_t c = 0; c < u->channels; ++c){
+        u->a_H[c] = pa_aupdate_new();
         u->input[c] = alloc(u->window_size, sizeof(float));
         memset(u->input[c], 0, (u->window_size)*sizeof(float));
         u->overlap_accum[c] = alloc(u->overlap_size, sizeof(float));
@@ -989,14 +1065,16 @@ int pa__init(pa_module*m) {
     dbus_init(u);
 
     //default filter to these
-    a_i = pa_aupdate_write_begin(u->a_H);
-    H = u->Hs[a_i];
-    u->Xs[a_i] = 1.0f;
-    for(size_t i = 0; i < FILTER_SIZE; ++i){
-        H[i] = 1.0 / sqrtf(2.0f);
+    for(size_t c = 0; c< u->channels; ++c){
+        a_i = pa_aupdate_write_begin(u->a_H[c]);
+        H = u->Hs[c][a_i];
+        u->Xs[c][a_i] = 1.0f;
+        for(size_t i = 0; i < FILTER_SIZE; ++i){
+            H[i] = 1.0 / sqrtf(2.0f);
+        }
+        fix_filter(H, u->fft_size);
+        pa_aupdate_write_end(u->a_H[c]);
     }
-    fix_filter(H, u->fft_size);
-    pa_aupdate_write_end(u->a_H);
     //load old parameters
     load_state(u);
 
@@ -1049,23 +1127,30 @@ void pa__done(pa_module*m) {
     if (u->sink)
         pa_sink_unref(u->sink);
 
-    pa_aupdate_free(u->a_H);
     pa_memblockq_free(u->input_q);
 
     fftwf_destroy_plan(u->inverse_plan);
     fftwf_destroy_plan(u->forward_plan);
     pa_xfree(u->output_window);
     for(size_t c=0; c < u->channels; ++c){
+        pa_aupdate_free(u->a_H[c]);
         pa_xfree(u->overlap_accum[c]);
         pa_xfree(u->input[c]);
     }
+    pa_xfree(u->a_H);
     pa_xfree(u->overlap_accum);
     pa_xfree(u->input);
     pa_xfree(u->work_buffer);
     pa_xfree(u->W);
-    for(size_t i = 0; i < 2; ++i){
-        pa_xfree(u->Hs[i]);
+    for(size_t c = 0; c < u->channels; ++c){
+        pa_xfree(u->Xs[c]);
+        for(size_t i = 0; i < 2; ++i){
+            pa_xfree(u->Hs[c][i]);
+        }
+        pa_xfree(u->Hs[c]);
     }
+    pa_xfree(u->Xs);
+    pa_xfree(u->Hs);
 
     pa_xfree(u->name);
 
@@ -1088,11 +1173,12 @@ static void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void
 static void equalizer_get_sample_rate(DBusConnection *conn, DBusMessage *msg, void *_u);
 static void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u);
 static void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u);
-static void equalizer_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u);
-static void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_get_n_channels(DBusConnection *conn, DBusMessage *msg, void *_u);
 static void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u);
 static void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *_u);
 static void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_handle_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_handle_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u);
 static void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u);
 static void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u);
 enum manager_method_index {
@@ -1158,6 +1244,8 @@ enum equalizer_method_index {
     EQUALIZER_METHOD_SEED_FILTER,
     EQUALIZER_METHOD_SAVE_PROFILE,
     EQUALIZER_METHOD_LOAD_PROFILE,
+    EQUALIZER_METHOD_SET_FILTER,
+    EQUALIZER_METHOD_GET_FILTER,
     EQUALIZER_METHOD_MAX
 };
 
@@ -1166,25 +1254,41 @@ enum equalizer_handler_index {
     EQUALIZER_HANDLER_SAMPLERATE,
     EQUALIZER_HANDLER_FILTERSAMPLERATE,
     EQUALIZER_HANDLER_N_COEFS,
-    EQUALIZER_HANDLER_FILTER,
+    EQUALIZER_HANDLER_N_CHANNELS,
     EQUALIZER_HANDLER_MAX
 };
 
 pa_dbus_arg_info filter_points_args[]={
+    {"channel", "u","in"},
     {"xs", "au","in"},
     {"ys", "ad","out"},
-    {"preamp", "d","out"},
+    {"preamp", "d","out"}
 };
 pa_dbus_arg_info seed_filter_args[]={
+    {"channel", "u","in"},
     {"xs", "au","in"},
     {"ys", "ad","in"},
-    {"preamp", "d","in"},
+    {"preamp", "d","in"}
+};
+
+pa_dbus_arg_info set_filter_args[]={
+    {"channel", "u","in"},
+    {"ys", "ad","in"},
+    {"preamp", "d","in"}
+};
+pa_dbus_arg_info get_filter_args[]={
+    {"channel", "u","in"},
+    {"ys", "ad","out"},
+    {"preamp", "d","out"}
 };
+
 pa_dbus_arg_info save_profile_args[]={
-    {"name", "s","in"},
+    {"channel", "u","in"},
+    {"name", "s","in"}
 };
 pa_dbus_arg_info load_profile_args[]={
-    {"name", "s","in"},
+    {"channel", "u","in"},
+    {"name", "s","in"}
 };
 
 static pa_dbus_method_handler equalizer_methods[EQUALIZER_METHOD_MAX]={
@@ -1198,6 +1302,16 @@ static pa_dbus_method_handler equalizer_methods[EQUALIZER_METHOD_MAX]={
         .arguments=filter_points_args,
         .n_arguments=sizeof(filter_points_args)/sizeof(pa_dbus_arg_info),
         .receive_cb=equalizer_handle_get_filter_points},
+    [EQUALIZER_METHOD_SET_FILTER]{
+        .method_name="SetFilter",
+        .arguments=set_filter_args,
+        .n_arguments=sizeof(set_filter_args)/sizeof(pa_dbus_arg_info),
+        .receive_cb=equalizer_handle_set_filter},
+    [EQUALIZER_METHOD_GET_FILTER]{
+        .method_name="GetFilter",
+        .arguments=get_filter_args,
+        .n_arguments=sizeof(get_filter_args)/sizeof(pa_dbus_arg_info),
+        .receive_cb=equalizer_handle_get_filter},
     [EQUALIZER_METHOD_SAVE_PROFILE]{
         .method_name="SaveProfile",
         .arguments=save_profile_args,
@@ -1207,7 +1321,7 @@ static pa_dbus_method_handler equalizer_methods[EQUALIZER_METHOD_MAX]={
         .method_name="LoadProfile",
         .arguments=load_profile_args,
         .n_arguments=sizeof(load_profile_args)/sizeof(pa_dbus_arg_info),
-        .receive_cb=equalizer_handle_load_profile},
+        .receive_cb=equalizer_handle_load_profile}
 };
 
 static pa_dbus_property_handler equalizer_handlers[EQUALIZER_HANDLER_MAX]={
@@ -1215,7 +1329,7 @@ static pa_dbus_property_handler equalizer_handlers[EQUALIZER_HANDLER_MAX]={
     [EQUALIZER_HANDLER_SAMPLERATE]{.property_name="SampleRate",.type="u",.get_cb=equalizer_get_sample_rate,.set_cb=NULL},
     [EQUALIZER_HANDLER_FILTERSAMPLERATE]{.property_name="FilterSampleRate",.type="u",.get_cb=equalizer_get_filter_rate,.set_cb=NULL},
     [EQUALIZER_HANDLER_N_COEFS]{.property_name="NFilterCoefficients",.type="u",.get_cb=equalizer_get_n_coefs,.set_cb=NULL},
-    [EQUALIZER_HANDLER_FILTER]{.property_name="Filter",.type="ad",.get_cb=equalizer_get_filter,.set_cb=equalizer_set_filter}
+    [EQUALIZER_HANDLER_N_CHANNELS]{.property_name="NChannels",.type="u",.get_cb=equalizer_get_n_channels,.set_cb=NULL},
 };
 
 enum equalizer_signal_index{
@@ -1249,15 +1363,15 @@ void dbus_init(struct userdata *u){
 
     pa_dbus_protocol_add_interface(u->dbus_protocol, u->dbus_path, &equalizer_info, u);
     sink_list = pa_shared_get(u->sink->core, SINKLIST);
-    u->database=pa_shared_get(u->sink->core, EQDB);
-    if(sink_list==NULL){
+    u->database = pa_shared_get(u->sink->core, EQDB);
+    if(sink_list == NULL){
         char *dbname;
         sink_list=pa_idxset_new(&pa_idxset_trivial_hash_func, &pa_idxset_trivial_compare_func);
         pa_shared_set(u->sink->core, SINKLIST, sink_list);
-        pa_assert_se(dbname = pa_state_path("equalizers", TRUE));
+        pa_assert_se(dbname = pa_state_path("equalizer-presets", FALSE));
         pa_assert_se(u->database = pa_database_open(dbname, TRUE));
         pa_xfree(dbname);
-        pa_shared_set(u->sink->core,EQDB,u->database);
+        pa_shared_set(u->sink->core, EQDB, u->database);
         pa_dbus_protocol_add_interface(u->dbus_protocol, MANAGER_PATH, &manager_info, u->sink->core);
         pa_dbus_protocol_register_extension(u->dbus_protocol, EXTNAME);
     }
@@ -1448,7 +1562,7 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *
     DBusError error;
     DBusMessage *signal = NULL;
     float *ys;
-    uint32_t *xs;
+    uint32_t *xs, channel, r_channel;
     double *_ys, preamp;
     unsigned x_npoints, y_npoints, a_i;
     float *H;
@@ -1460,6 +1574,7 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *
     dbus_error_init(&error);
 
     if(!dbus_message_get_args(msg, &error,
+                DBUS_TYPE_UINT32, &channel,
                 DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &xs, &x_npoints,
                 DBUS_TYPE_ARRAY, DBUS_TYPE_DOUBLE, &_ys, &y_npoints,
                 DBUS_TYPE_DOUBLE, &preamp,
@@ -1468,6 +1583,11 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *
         dbus_error_free(&error);
         return;
     }
+    if(channel > u->channels){
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "invalid channel: %d", channel);
+        dbus_error_free(&error);
+        return;
+    }
     for(size_t i = 0; i < x_npoints; ++i){
         if(xs[i] >= FILTER_SIZE){
             points_good = FALSE;
@@ -1478,7 +1598,6 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *
         pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs must be monotonic and 0<=x<=%ld", u->fft_size / 2);
         dbus_error_free(&error);
         return;
-
     }else if(x_npoints != y_npoints || x_npoints < 2 || x_npoints > FILTER_SIZE  ){
         pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "xs and ys must be the same length and 2<=l<=%ld!", FILTER_SIZE);
         dbus_error_free(&error);
@@ -1493,14 +1612,25 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *
     for(uint32_t i = 0; i < x_npoints; ++i){
         ys[i] = (float) _ys[i];
     }
-    a_i = pa_aupdate_write_begin(u->a_H);
-    H = u->Hs[a_i];
-    u->Xs[a_i] = preamp;
+    r_channel = channel == u->channels ? 0 : channel;
+    a_i = pa_aupdate_write_begin(u->a_H[r_channel]);
+    H = u->Hs[r_channel][a_i];
+    u->Xs[r_channel][a_i] = preamp;
     interpolate(H, FILTER_SIZE, xs, ys, x_npoints);
     fix_filter(H, u->fft_size);
-    pa_aupdate_write_end(u->a_H);
+    if(channel == u->channels){
+        for(size_t c = 1; c < u->channels; ++c){
+            unsigned b_i = pa_aupdate_write_begin(u->a_H[c]);
+            float *H_p = u->Hs[c][b_i];
+            u->Xs[c][b_i] = preamp;
+            memcpy(H_p, H, FILTER_SIZE * sizeof(float));
+            pa_aupdate_write_end(u->a_H[c]);
+        }
+    }
+    pa_aupdate_write_end(u->a_H[r_channel]);
     pa_xfree(ys);
 
+
     //Stupid for IO reasons?  Add a save signal to dbus instead
     //save_state(u);
 
@@ -1513,28 +1643,34 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *
 
 void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg, void *_u) {
     struct userdata *u = (struct userdata *) _u;
-    DBusError error;
-    uint32_t *xs;
+    uint32_t *xs, channel, r_channel;
     double *ys, preamp;
     unsigned x_npoints, a_i;
     float *H;
     pa_bool_t points_good=TRUE;
     DBusMessage *reply = NULL;
     DBusMessageIter msg_iter;
+    DBusError error;
 
     pa_assert(conn);
     pa_assert(msg);
     pa_assert(u);
 
     dbus_error_init(&error);
-
     if(!dbus_message_get_args(msg, &error,
+                DBUS_TYPE_UINT32, &channel,
                 DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &xs, &x_npoints,
                 DBUS_TYPE_INVALID)){
         pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
         dbus_error_free(&error);
         return;
     }
+    if(channel > u->channels){
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "invalid channel: %d", channel);
+        dbus_error_free(&error);
+        return;
+    }
+
     for(size_t i = 0; i < x_npoints; ++i){
         if(xs[i] >= FILTER_SIZE){
             points_good=FALSE;
@@ -1548,14 +1684,15 @@ void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg,
         return;
     }
 
+    r_channel = channel == u->channels ? 0 : channel;
     ys = pa_xmalloc(x_npoints * sizeof(double));
-    a_i = pa_aupdate_read_begin(u->a_H);
-    H = u->Hs[a_i];
-    preamp = u->Xs[a_i];
+    a_i = pa_aupdate_read_begin(u->a_H[r_channel]);
+    H = u->Hs[r_channel][a_i];
+    preamp = u->Xs[r_channel][a_i];
     for(uint32_t i = 0; i < x_npoints; ++i){
         ys[i] = H[xs[i]] * u->fft_size;
     }
-    pa_aupdate_read_end(u->a_H);
+    pa_aupdate_read_end(u->a_H[r_channel]);
 
     pa_assert_se((reply = dbus_message_new_method_return(msg)));
     dbus_message_iter_init_append(reply, &msg_iter);
@@ -1568,9 +1705,128 @@ void equalizer_handle_get_filter_points(DBusConnection *conn, DBusMessage *msg,
     pa_xfree(ys);
 }
 
+static void get_filter(struct userdata *u, size_t channel, double **H_, double *preamp){
+    float *H;
+    unsigned a_i;
+    size_t r_channel = channel == u->channels ? 0 : channel;
+    *H_ = pa_xnew0(double, FILTER_SIZE);
+    a_i = pa_aupdate_read_begin(u->a_H[r_channel]);
+    H = u->Hs[r_channel][a_i];
+    for(size_t i = 0;i < FILTER_SIZE; ++i){
+        (*H_)[i] = H[i] * u->fft_size;
+    }
+    *preamp = u->Xs[r_channel][a_i];
+
+    pa_aupdate_read_end(u->a_H[r_channel]);
+}
+
+void equalizer_handle_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u){
+    struct userdata *u;
+    unsigned n_coefs;
+    uint32_t channel;
+    double *H_, preamp;
+    DBusMessage *reply = NULL;
+    DBusMessageIter msg_iter;
+    DBusError error;
+    pa_assert_se(u = (struct userdata *) _u);
+    pa_assert(conn);
+    pa_assert(msg);
+
+    dbus_error_init(&error);
+    if(!dbus_message_get_args(msg, &error,
+                DBUS_TYPE_UINT32, &channel,
+                DBUS_TYPE_INVALID)){
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
+        dbus_error_free(&error);
+        return;
+    }
+    if(channel > u->channels){
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "invalid channel: %d", channel);
+        dbus_error_free(&error);
+        return;
+    }
+
+    n_coefs = CHANNEL_PROFILE_SIZE;
+    pa_assert(conn);
+    pa_assert(msg);
+    get_filter(u, channel, &H_, &preamp);
+    pa_assert_se((reply = dbus_message_new_method_return(msg)));
+    dbus_message_iter_init_append(reply, &msg_iter);
+
+    pa_dbus_append_basic_array(&msg_iter, DBUS_TYPE_DOUBLE, H_, n_coefs);
+    pa_dbus_append_basic_variant(&msg_iter, DBUS_TYPE_DOUBLE, &preamp);
+
+    pa_assert_se(dbus_connection_send(conn, reply, NULL));
+    dbus_message_unref(reply);
+    pa_xfree(H_);
+}
+
+static void set_filter(struct userdata *u, size_t channel, double *H_, double preamp){
+    unsigned a_i;
+    size_t r_channel = channel == u->channels ? 0 : channel;
+    float *H;
+    //all channels
+    a_i = pa_aupdate_write_begin(u->a_H[r_channel]);
+    u->Xs[r_channel][a_i] = (float) preamp;
+    H = u->Hs[r_channel][a_i];
+    for(size_t i = 0; i < FILTER_SIZE; ++i){
+        H[i] = (float) H_[i];
+    }
+    fix_filter(H, u->fft_size);
+    if(channel == u->channels){
+        for(size_t c = 1; c < u->channels; ++c){
+            unsigned b_i = pa_aupdate_write_begin(u->a_H[c]);
+            u->Xs[c][b_i] = u->Xs[r_channel][a_i];
+            memcpy(u->Hs[c][b_i], u->Hs[r_channel][a_i], FILTER_SIZE * sizeof(float));
+            pa_aupdate_write_end(u->a_H[c]);
+        }
+    }
+    pa_aupdate_write_end(u->a_H[r_channel]);
+}
+
+void equalizer_handle_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){
+    struct userdata *u;
+    double *H, preamp;
+    uint32_t channel;
+    unsigned _n_coefs;
+    DBusMessage *signal = NULL;
+    DBusError error;
+    pa_assert_se(u = (struct userdata *) _u);
+    pa_assert(conn);
+    pa_assert(msg);
+
+    dbus_error_init(&error);
+    if(!dbus_message_get_args(msg, &error,
+                DBUS_TYPE_UINT32, &channel,
+                DBUS_TYPE_ARRAY, DBUS_TYPE_DOUBLE, &H, &_n_coefs,
+                DBUS_TYPE_DOUBLE, &preamp,
+                DBUS_TYPE_INVALID)){
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
+        dbus_error_free(&error);
+        return;
+    }
+    if(channel > u->channels){
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "invalid channel: %d", channel);
+        dbus_error_free(&error);
+        return;
+    }
+    if(_n_coefs != FILTER_SIZE){
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "This filter takes exactly %ld coefficients, you gave %d", FILTER_SIZE, _n_coefs);
+        return;
+    }
+    set_filter(u, channel, H, preamp);
+
+    pa_dbus_send_empty_reply(conn, msg);
+
+    pa_assert_se((signal = dbus_message_new_signal(u->dbus_path, EQUALIZER_IFACE, equalizer_signals[EQUALIZER_SIGNAL_FILTER_CHANGED].name)));
+    pa_dbus_protocol_send_signal(u->dbus_protocol, signal);
+    dbus_message_unref(signal);
+}
+
 void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u) {
     struct userdata *u = (struct userdata *) _u;
     char *name;
+    uint32_t channel, r_channel;
     DBusMessage *signal = NULL;
     DBusError error;
     pa_assert(conn);
@@ -1579,13 +1835,20 @@ void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void
     dbus_error_init(&error);
 
     if(!dbus_message_get_args(msg, &error,
-                 DBUS_TYPE_STRING, &name,
+                DBUS_TYPE_UINT32, &channel,
+                DBUS_TYPE_STRING, &name,
                 DBUS_TYPE_INVALID)){
         pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
         dbus_error_free(&error);
         return;
     }
-    save_profile(u,name);
+    if(channel > u->channels){
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "invalid channel: %d", channel);
+        dbus_error_free(&error);
+        return;
+    }
+    r_channel = channel == u->channels ? 0 : channel;
+    save_profile(u, r_channel, name);
     pa_dbus_send_empty_reply(conn, msg);
 
     pa_assert_se((signal = dbus_message_new_signal(MANAGER_PATH, MANAGER_IFACE, manager_signals[MANAGER_SIGNAL_PROFILES_CHANGED].name)));
@@ -1594,9 +1857,10 @@ void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void
 }
 
 void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u) {
-    struct userdata *u=(struct userdata *) _u;
+    struct userdata *u = (struct userdata *) _u;
     char *name;
     DBusError error;
+    uint32_t channel, r_channel;
     const char *err_msg = NULL;
     DBusMessage *signal = NULL;
 
@@ -1606,18 +1870,31 @@ void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void
     dbus_error_init(&error);
 
     if(!dbus_message_get_args(msg, &error,
-                 DBUS_TYPE_STRING, &name,
+                DBUS_TYPE_UINT32, &channel,
+                DBUS_TYPE_STRING, &name,
                 DBUS_TYPE_INVALID)){
         pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
         dbus_error_free(&error);
         return;
     }
-    err_msg = load_profile(u, name);
+    if(channel > u->channels){
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "invalid channel: %d", channel);
+        dbus_error_free(&error);
+        return;
+    }
+    r_channel = channel == u->channels ? 0 : channel;
+
+    err_msg = load_profile(u, r_channel, name);
     if(err_msg != NULL){
         pa_dbus_send_error(conn, msg, DBUS_ERROR_FAILED, "error loading profile %s: %s", name, err_msg);
         dbus_error_free(&error);
         return;
     }
+    if(channel == u->channels){
+        for(uint32_t c = 1; c < u->channels; ++c){
+            load_profile(u, c, name);
+        }
+    }
     pa_dbus_send_empty_reply(conn, msg);
 
     pa_assert_se((signal = dbus_message_new_signal(u->dbus_path, EQUALIZER_IFACE, equalizer_signals[EQUALIZER_SIGNAL_FILTER_CHANGED].name)));
@@ -1630,6 +1907,17 @@ void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){
     pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_UINT32, &rev);
 }
 
+void equalizer_get_n_channels(DBusConnection *conn, DBusMessage *msg, void *_u){
+    struct userdata *u;
+    uint32_t channels;
+    pa_assert_se(u = (struct userdata *) _u);
+    pa_assert(conn);
+    pa_assert(msg);
+
+    channels = (uint32_t) u->channels;
+    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &channels);
+}
+
 void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u){
     struct userdata *u;
     uint32_t n_coefs;
@@ -1637,7 +1925,7 @@ void equalizer_get_n_coefs(DBusConnection *conn, DBusMessage *msg, void *_u){
     pa_assert(conn);
     pa_assert(msg);
 
-    n_coefs = (uint32_t) PROFILE_SIZE;
+    n_coefs = (uint32_t) CHANNEL_PROFILE_SIZE;
     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &n_coefs);
 }
 
@@ -1663,84 +1951,19 @@ void equalizer_get_filter_rate(DBusConnection *conn, DBusMessage *msg, void *_u)
     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &fft_size);
 }
 
-static double * get_filter(struct userdata *u){
-    float *H;
-    double *H_;
-    unsigned a_i;
-    H_ = pa_xnew0(double, PROFILE_SIZE);
-    a_i = pa_aupdate_read_begin(u->a_H);
-    H = u->Hs[a_i];
-    H_[0] = u->Xs[a_i];
-    for(size_t i = 0;i < FILTER_SIZE; ++i){
-        H_[i + 1] = H[i] * u->fft_size;
-    }
-    pa_aupdate_read_end(u->a_H);
-    return H_;
-}
-
-void equalizer_get_filter(DBusConnection *conn, DBusMessage *msg, void *_u){
-    struct userdata *u;
-    unsigned n_coefs;
-    double *H_;
-    pa_assert_se(u = (struct userdata *) _u);
-
-    n_coefs = PROFILE_SIZE;
-    pa_assert(conn);
-    pa_assert(msg);
-    H_ = get_filter(u);
-    pa_dbus_send_basic_array_variant_reply(conn, msg, DBUS_TYPE_DOUBLE, H_, n_coefs);
-    pa_xfree(H_);
-}
-
-static void set_filter(struct userdata *u, double *H_){
-    unsigned a_i= pa_aupdate_write_begin(u->a_H);
-    float *H = u->Hs[a_i];
-    u->Xs[a_i] = H_[0];
-    for(size_t i = 0; i < FILTER_SIZE; ++i){
-        H[i] = (float) H_[i + 1];
-    }
-    fix_filter(H + 1, u->fft_size);
-    pa_aupdate_write_end(u->a_H);
-}
-
-void equalizer_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u){
-    struct userdata *u;
-    double *H;
-    unsigned _n_coefs;
-    DBusMessage *signal = NULL;
-    pa_assert_se(u = (struct userdata *) _u);
-    pa_assert(conn);
-    pa_assert(msg);
-
-    if(pa_dbus_get_fixed_array_set_property_arg(conn, msg, DBUS_TYPE_DOUBLE, &H, &_n_coefs)){
-        return;
-    }
-    if(_n_coefs != PROFILE_SIZE){
-        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "This filter takes exactly %ld coefficients, you gave %d", PROFILE_SIZE, _n_coefs);
-        return;
-    }
-    set_filter(u, H);
-
-    pa_dbus_send_empty_reply(conn, msg);
-
-    pa_assert_se((signal = dbus_message_new_signal(u->dbus_path, EQUALIZER_IFACE, equalizer_signals[EQUALIZER_SIGNAL_FILTER_CHANGED].name)));
-    pa_dbus_protocol_send_signal(u->dbus_protocol, signal);
-    dbus_message_unref(signal);
-}
-
 void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){
     struct userdata *u;
     DBusMessage *reply = NULL;
     DBusMessageIter msg_iter, dict_iter;
-    uint32_t rev, n_coefs, rate, fft_size;
-    double *H;
+    uint32_t rev, n_coefs, rate, fft_size, channels;
     pa_assert_se(u = (struct userdata *) _u);
     pa_assert(msg);
 
     rev = 1;
-    n_coefs = (uint32_t) PROFILE_SIZE;
+    n_coefs = (uint32_t) CHANNEL_PROFILE_SIZE;
     rate = (uint32_t) u->sink->sample_spec.rate;
     fft_size = (uint32_t) u->fft_size;
+    channels = (uint32_t) u->channels;
 
     pa_assert_se((reply = dbus_message_new_method_return(msg)));
     dbus_message_iter_init_append(reply, &msg_iter);
@@ -1750,9 +1973,7 @@ void equalizer_get_all(DBusConnection *conn, DBusMessage *msg, void *_u){
     pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_SAMPLERATE].property_name, DBUS_TYPE_UINT32, &rate);
     pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_FILTERSAMPLERATE].property_name, DBUS_TYPE_UINT32, &fft_size);
     pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_N_COEFS].property_name, DBUS_TYPE_UINT32, &n_coefs);
-    H = get_filter(u);
-    pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_FILTER].property_name, DBUS_TYPE_UINT32, &H);
-    pa_xfree(H);
+    pa_dbus_append_basic_variant_dict_entry(&dict_iter, equalizer_handlers[EQUALIZER_HANDLER_N_CHANNELS].property_name, DBUS_TYPE_UINT32, &channels);
 
     pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
     pa_assert_se(dbus_connection_send(conn, reply, NULL));

commit f5ceed8151214a95ca2192ed1c681eb31cb0bc98
Author: Jason Newton <nevion at gmail.com>
Date:   Thu Aug 20 18:24:19 2009 -0700

    module-equalizer-sink: added server side persistance of profile names

diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
index 98b6b89..c531468 100755
--- a/src/modules/module-equalizer-sink.c
+++ b/src/modules/module-equalizer-sink.c
@@ -122,6 +122,7 @@ struct userdata {
     pa_bool_t set_default;
 
     pa_database *database;
+    char **base_profiles;
 };
 
 static const char* const valid_modargs[] = {
@@ -143,7 +144,7 @@ static const char* const valid_modargs[] = {
 #define EQ_STATE_DB "equalizer-state"
 #define FILTER_SIZE (u->fft_size / 2 + 1)
 #define CHANNEL_PROFILE_SIZE (FILTER_SIZE + 1)
-#define STATE_SIZE (CHANNEL_PROFILE_SIZE * u->channels)
+#define FILTER_STATE_SIZE (CHANNEL_PROFILE_SIZE * u->channels)
 static void dbus_init(struct userdata *u);
 static void dbus_done(struct userdata *u);
 
@@ -199,7 +200,7 @@ static int is_monotonic(const uint32_t *xs,size_t length){
 
 
 /* Called from I/O thread context */
-static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+static int sink_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
     struct userdata *u = PA_SINK(o)->userdata;
 
     switch (code) {
@@ -234,7 +235,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 
 
 /* Called from main context */
-static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
+static int sink_set_state_cb(pa_sink *s, pa_sink_state_t state) {
     struct userdata *u;
 
     pa_sink_assert_ref(s);
@@ -249,7 +250,7 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
 }
 
 /* Called from I/O thread context */
-static void sink_request_rewind(pa_sink *s) {
+static void sink_request_rewind_cb(pa_sink *s) {
     struct userdata *u;
 
     pa_sink_assert_ref(s);
@@ -264,7 +265,7 @@ static void sink_request_rewind(pa_sink *s) {
 }
 
 /* Called from I/O thread context */
-static void sink_update_requested_latency(pa_sink *s) {
+static void sink_update_requested_latency_cb(pa_sink *s) {
     struct userdata *u;
 
     pa_sink_assert_ref(s);
@@ -280,6 +281,35 @@ static void sink_update_requested_latency(pa_sink *s) {
             pa_sink_get_requested_latency_within_thread(s));
 }
 
+/* Called from main context */
+static void sink_set_volume_cb(pa_sink *s) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SINK_IS_LINKED(pa_sink_get_state(s)) ||
+        !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input)))
+        return;
+
+    pa_sink_input_set_volume(u->sink_input, &s->real_volume, s->save_volume, TRUE);
+}
+
+/* Called from main context */
+static void sink_set_mute_cb(pa_sink *s) {
+    struct userdata *u;
+
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    if (!PA_SINK_IS_LINKED(pa_sink_get_state(s)) ||
+        !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input)))
+        return;
+
+    pa_sink_input_set_mute(u->sink_input, s->muted, s->save_muted);
+}
+
+
 //reference implementation
 static void dsp_logic(
     float * restrict dst,//used as a temp array too, needs to be fft_length!
@@ -581,6 +611,26 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
     return 0;
 }
 
+/* Called from main context */
+static void sink_input_volume_changed_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_sink_volume_changed(u->sink, &i->volume);
+}
+
+/* Called from main context */
+static void sink_input_mute_changed_cb(pa_sink_input *i) {
+    struct userdata *u;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(u = i->userdata);
+
+    pa_sink_mute_changed(u->sink, i->muted);
+}
+
 static void reset_filter(struct userdata *u){
     u->samples_gathered = 0;
     for(size_t i = 0;i < u->channels; ++i){
@@ -745,6 +795,41 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s
     }
 }
 
+static void pack(char **strs, size_t len, char **packed, size_t *length){
+    size_t t_len = 0;
+    size_t headers = (1+len) * sizeof(uint16_t);
+    size_t offset = sizeof(uint16_t);
+    for(size_t i = 0; i < len; ++i){
+        t_len += strlen(strs[i]);
+    }
+    *length = headers + t_len;
+    *packed = pa_xmalloc0(*length);
+    ((uint16_t *) *packed)[0] = (uint16_t) len;
+    for(size_t i = 0; i < len; ++i){
+        uint16_t l = strlen(strs[i]);
+        *((uint16_t *)(*packed + offset)) = l;
+        offset += sizeof(uint16_t);
+        memcpy(*packed + offset, strs[i], l);
+        offset += l;
+    }
+}
+static void unpack(char *str, size_t length, char ***strs, size_t *len){
+    size_t offset = sizeof(uint16_t);
+    *len = ((uint16_t *)str)[0];
+    *strs = pa_xnew(char *, *len);
+    for(size_t i = 0; i < *len; ++i){
+        size_t l = *((uint16_t *)(str+offset));
+        size_t e = PA_MIN(offset + l, length) - offset;
+        offset = PA_MIN(offset + sizeof(uint16_t), length);
+        if(e > 0){
+            (*strs)[i] = pa_xnew(char, e + 1);
+            memcpy((*strs)[i], strs + offset, e);
+            (*strs)[i][e] = '\0';
+        }else{
+            (*strs)[i]=NULL;
+        }
+    }
+}
 static void save_profile(struct userdata *u, size_t channel, char *name){
     unsigned a_i;
     const size_t profile_size = CHANNEL_PROFILE_SIZE * sizeof(float);
@@ -767,18 +852,26 @@ static void save_profile(struct userdata *u, size_t channel, char *name){
     data.size = profile_size;
     pa_database_set(u->database, &key, &data, TRUE);
     pa_database_sync(u->database);
+    if(u->base_profiles[channel]){
+        pa_xfree(u->base_profiles[channel]);
+    }
+    u->base_profiles[channel] = pa_xstrdup(name);
 }
 
 static void save_state(struct userdata *u){
     unsigned a_i;
-    const size_t state_size = STATE_SIZE * sizeof(float);
+    const size_t filter_state_size = FILTER_STATE_SIZE * sizeof(float);
     float *H_n, *state;
     float *H;
     pa_datum key, data;
     pa_database *database;
     char *dbname;
     char *state_name = u->name;
-    state = pa_xnew0(float, STATE_SIZE);
+    char *packed;
+    size_t packed_length;
+
+    pack(u->base_profiles, u->channels, &packed, &packed_length);
+    state = (float *) pa_xmalloc0(filter_state_size + packed_length);
 
     for(size_t c = 0; c < u->channels; ++c){
         a_i = pa_aupdate_read_begin(u->a_H[c]);
@@ -788,11 +881,13 @@ static void save_state(struct userdata *u){
         memcpy(H_n, H, FILTER_SIZE * sizeof(float));
         pa_aupdate_read_end(u->a_H[c]);
     }
+    memcpy(((char *)state) + filter_state_size, packed, packed_length);
+    pa_xfree(packed);
 
     key.data = state_name;
     key.size = strlen(key.data);
     data.data = state;
-    data.size = state_size;
+    data.size = filter_state_size + packed_length;
     //thread safety for 0.9.17?
     pa_assert_se(dbname = pa_state_path(EQ_STATE_DB, TRUE));
     pa_assert_se(database = pa_database_open(dbname, TRUE));
@@ -828,6 +923,10 @@ static const char* load_profile(struct userdata *u, size_t channel, char *name){
             memcpy(u->Hs[channel][a_i], profile + 1, CHANNEL_PROFILE_SIZE * sizeof(float));
             fix_filter(u->Hs[channel][a_i], u->fft_size);
             pa_aupdate_write_end(u->a_H[channel]);
+            if(u->base_profiles[channel]){
+                pa_xfree(u->base_profiles[channel]);
+            }
+            u->base_profiles[channel] = pa_xstrdup(name);
         }else{
             return "incompatible size";
         }
@@ -845,7 +944,6 @@ static void load_state(struct userdata *u){
     pa_database *database;
     char *dbname;
     char *state_name = u->name;
-
     pa_assert_se(dbname = pa_state_path(EQ_STATE_DB, FALSE));
     database = pa_database_open(dbname, FALSE);
     pa_xfree(dbname);
@@ -857,14 +955,26 @@ static void load_state(struct userdata *u){
     key.size = strlen(key.data);
 
     if(pa_database_get(database, &key, &value) != NULL){
-        size_t states = PA_MIN(value.size / (CHANNEL_PROFILE_SIZE * sizeof(float)), u->channels);
-        float *state = (float *) value.data;
-        for(size_t c = 0; c < states; ++c){
-            a_i = pa_aupdate_write_begin(u->a_H[c]);
-            H = state + c * CHANNEL_PROFILE_SIZE + 1;
-            u->Xs[c][a_i] = state[c * CHANNEL_PROFILE_SIZE];
-            memcpy(u->Hs[c][a_i], H, FILTER_SIZE * sizeof(float));
-            pa_aupdate_write_end(u->a_H[c]);
+        if(value.size > FILTER_STATE_SIZE * sizeof(float) + sizeof(uint16_t)){
+            float *state = (float *) value.data;
+            size_t n_profs;
+            char **names;
+            for(size_t c = 0; c < u->channels; ++c){
+                a_i = pa_aupdate_write_begin(u->a_H[c]);
+                H = state + c * CHANNEL_PROFILE_SIZE + 1;
+                u->Xs[c][a_i] = state[c * CHANNEL_PROFILE_SIZE];
+                memcpy(u->Hs[c][a_i], H, FILTER_SIZE * sizeof(float));
+                pa_aupdate_write_end(u->a_H[c]);
+            }
+            unpack(((char *)value.data) + FILTER_STATE_SIZE, value.size - FILTER_STATE_SIZE, &names, &n_profs);
+            n_profs = PA_MIN(n_profs, u->channels);
+            for(size_t c = 0; c < n_profs; ++c){
+                if(u->base_profiles[c]){
+                    pa_xfree(u->base_profiles[c]);
+                }
+                u->base_profiles[c] = names[c];
+            }
+            pa_xfree(names);
         }
         pa_datum_free(&value);
     }
@@ -898,8 +1008,6 @@ static void * alloc(size_t x,size_t s){
     size_t f = PA_ROUND_UP(x*s, sizeof(float)*v_size);
     float *t;
     pa_assert(f >= x*s);
-    //printf("requested %ld floats=%ld bytes, rem=%ld\n", x, x*sizeof(float), x*sizeof(float)%16);
-    //printf("giving %ld floats=%ld bytes, rem=%ld\n", f, f*sizeof(float), f*sizeof(float)%16);
     t = fftwf_malloc(f);
     memset(t, 0, f);
     return t;
@@ -914,7 +1022,6 @@ int pa__init(pa_module*m) {
     pa_sink *master;
     pa_sink_input_new_data sink_input_data;
     pa_sink_new_data sink_data;
-    pa_bool_t *use_default = NULL;
     size_t fs;
     float *H;
     unsigned a_i;
@@ -988,6 +1095,8 @@ int pa__init(pa_module*m) {
     hanning_window(u->W, u->window_size);
     u->first_iteration = TRUE;
 
+    u->base_profiles = pa_xnew0(char *, u->channels);
+
     /* Create sink */
     pa_sink_new_data_init(&sink_data);
     sink_data.driver = __FILE__;
@@ -1007,7 +1116,9 @@ int pa__init(pa_module*m) {
         goto fail;
     }
 
-    u->sink = pa_sink_new(m->core, &sink_data, master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY));
+    u->sink = pa_sink_new(m->core, &sink_data,
+                          PA_SINK_HW_MUTE_CTRL|PA_SINK_HW_VOLUME_CTRL|PA_SINK_DECIBEL_VOLUME|
+                          (master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY)));
     pa_sink_new_data_done(&sink_data);
 
     if (!u->sink) {
@@ -1015,10 +1126,12 @@ int pa__init(pa_module*m) {
         goto fail;
     }
     u->name=pa_xstrdup(u->sink->name);
-    u->sink->parent.process_msg = sink_process_msg;
-    u->sink->set_state = sink_set_state;
-    u->sink->update_requested_latency = sink_update_requested_latency;
-    u->sink->request_rewind = sink_request_rewind;
+    u->sink->parent.process_msg = sink_process_msg_cb;
+    u->sink->set_state = sink_set_state_cb;
+    u->sink->update_requested_latency = sink_update_requested_latency_cb;
+    u->sink->request_rewind = sink_request_rewind_cb;
+    u->sink->set_volume = sink_set_volume_cb;
+    u->sink->set_mute = sink_set_mute_cb;
     u->sink->userdata = u;
     u->input_q = pa_memblockq_new(0,  MEMBLOCKQ_MAXLENGTH, 0, fs, 1, 1, 0, &u->sink->silence);
 
@@ -1053,6 +1166,9 @@ int pa__init(pa_module*m) {
     u->sink_input->state_change = sink_input_state_change_cb;
     u->sink_input->may_move_to = sink_input_may_move_to_cb;
     u->sink_input->moving = sink_input_moving_cb;
+    u->sink_input->volume_changed = sink_input_volume_changed_cb;
+    u->sink_input->mute_changed = sink_input_mute_changed_cb;
+
     u->sink_input->userdata = u;
 
     pa_sink_put(u->sink);
@@ -1060,7 +1176,6 @@ int pa__init(pa_module*m) {
 
     pa_modargs_free(ma);
 
-    pa_xfree(use_default);
 
     dbus_init(u);
 
@@ -1084,7 +1199,6 @@ fail:
     if (ma)
         pa_modargs_free(ma);
 
-    pa_xfree(use_default);
 
     pa__done(m);
 
@@ -1112,6 +1226,13 @@ void pa__done(pa_module*m) {
 
     dbus_done(u);
 
+    for(size_t c = 0; c < u->channels; ++c){
+        if(u->base_profiles[c]){
+            pa_xfree(u->base_profiles[c]);
+        }
+    }
+    pa_xfree(u->base_profiles);
+
     /* See comments in sink_input_kill_cb() above regarding
      * destruction order! */
 
@@ -1181,6 +1302,7 @@ static void equalizer_handle_get_filter(DBusConnection *conn, DBusMessage *msg,
 static void equalizer_handle_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u);
 static void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u);
 static void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_handle_get_profile_name(DBusConnection *conn, DBusMessage *msg, void *_u);
 enum manager_method_index {
     MANAGER_METHOD_REMOVE_PROFILE,
     MANAGER_METHOD_MAX
@@ -1246,6 +1368,7 @@ enum equalizer_method_index {
     EQUALIZER_METHOD_LOAD_PROFILE,
     EQUALIZER_METHOD_SET_FILTER,
     EQUALIZER_METHOD_GET_FILTER,
+    EQUALIZER_METHOD_GET_PROFILE_NAME,
     EQUALIZER_METHOD_MAX
 };
 
@@ -1290,6 +1413,10 @@ pa_dbus_arg_info load_profile_args[]={
     {"channel", "u","in"},
     {"name", "s","in"}
 };
+pa_dbus_arg_info base_profile_name_args[]={
+    {"channel", "u","in"},
+    {"name", "s","out"}
+};
 
 static pa_dbus_method_handler equalizer_methods[EQUALIZER_METHOD_MAX]={
     [EQUALIZER_METHOD_SEED_FILTER]{
@@ -1321,7 +1448,12 @@ static pa_dbus_method_handler equalizer_methods[EQUALIZER_METHOD_MAX]={
         .method_name="LoadProfile",
         .arguments=load_profile_args,
         .n_arguments=sizeof(load_profile_args)/sizeof(pa_dbus_arg_info),
-        .receive_cb=equalizer_handle_load_profile}
+        .receive_cb=equalizer_handle_load_profile},
+    [EQUALIZER_METHOD_GET_PROFILE_NAME]{
+        .method_name="BaseProfile",
+        .arguments=base_profile_name_args,
+        .n_arguments=sizeof(base_profile_name_args)/sizeof(pa_dbus_arg_info),
+        .receive_cb=equalizer_handle_get_profile_name}
 };
 
 static pa_dbus_property_handler equalizer_handlers[EQUALIZER_HANDLER_MAX]={
@@ -1631,9 +1763,6 @@ void equalizer_handle_seed_filter(DBusConnection *conn, DBusMessage *msg, void *
     pa_xfree(ys);
 
 
-    //Stupid for IO reasons?  Add a save signal to dbus instead
-    //save_state(u);
-
     pa_dbus_send_empty_reply(conn, msg);
 
     pa_assert_se((signal = dbus_message_new_signal(u->dbus_path, EQUALIZER_IFACE, equalizer_signals[EQUALIZER_SIGNAL_FILTER_CHANGED].name)));
@@ -1902,6 +2031,36 @@ void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void
     dbus_message_unref(signal);
 }
 
+void equalizer_handle_get_profile_name(DBusConnection *conn, DBusMessage *msg, void *_u){
+    struct userdata *u = (struct userdata *) _u;
+    DBusError error;
+    uint32_t channel, r_channel;
+
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(u);
+    dbus_error_init(&error);
+
+    if(!dbus_message_get_args(msg, &error,
+                DBUS_TYPE_UINT32, &channel,
+                DBUS_TYPE_INVALID)){
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "%s", error.message);
+        dbus_error_free(&error);
+        return;
+    }
+    if(channel > u->channels){
+        pa_dbus_send_error(conn, msg, DBUS_ERROR_INVALID_ARGS, "invalid channel: %d", channel);
+        dbus_error_free(&error);
+        return;
+    }
+    r_channel = channel == u->channels ? 0 : channel;
+    if(u->base_profiles[r_channel]){
+        pa_dbus_send_basic_value_reply(conn,msg, DBUS_TYPE_STRING, &u->base_profiles[r_channel]);
+    }else{
+        pa_dbus_send_empty_reply(conn, msg);
+    }
+}
+
 void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){
     uint32_t rev=1;
     pa_dbus_send_basic_value_reply(conn, msg, DBUS_TYPE_UINT32, &rev);

commit 263b683437b9a88722f80fd0abea9ca1998fbd36
Author: Jason Newton <nevion at gmail.com>
Date:   Thu Aug 20 23:55:02 2009 -0700

    module-equalizer-sink: fix improper usage of pa_modargs_get_value_boolean for u->set_default

diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
index c531468..9a79cb9 100755
--- a/src/modules/module-equalizer-sink.c
+++ b/src/modules/module-equalizer-sink.c
@@ -753,6 +753,7 @@ static void sink_input_attach_cb(pa_sink_input *i) {
     //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency);
     pa_sink_attach_within_thread(u->sink);
     if(u->set_default){
+        pa_log("Setting default sink to %s", u->sink->name);
         pa_namereg_set_default_sink(u->module->core, u->sink);
     }
 }
@@ -1056,7 +1057,7 @@ int pa__init(pa_module*m) {
     m->userdata = u;
 
     u->set_default = TRUE;
-    u->set_default = pa_modargs_get_value_boolean(ma, "set_default", &u->set_default);
+    pa_modargs_get_value_boolean(ma, "set_default", &u->set_default);
 
     u->channels = ss.channels;
     u->fft_size = pow(2, ceil(log(ss.rate)/log(2)));

commit 3053badf0684e077fca8e8fddb43b4e9f2a5c30c
Author: Jason Newton <nevion at gmail.com>
Date:   Sun Aug 23 15:49:27 2009 -0700

    module-equalizer-sink: resync with ladspa parent sink

diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
index 9a79cb9..f634d5e 100755
--- a/src/modules/module-equalizer-sink.c
+++ b/src/modules/module-equalizer-sink.c
@@ -998,9 +998,11 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
 
     pa_sink_input_assert_ref(i);
     pa_assert_se(u = i->userdata);
-
-    pa_sink_set_asyncmsgq(u->sink, dest->asyncmsgq);
-    pa_sink_update_flags(u->sink, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY, dest->flags);
+    if (dest) {
+        pa_sink_set_asyncmsgq(u->sink, dest->asyncmsgq);
+        pa_sink_update_flags(u->sink, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY, dest->flags);
+    } else
+        pa_sink_set_asyncmsgq(u->sink, NULL);
 }
 
 //ensure's memory allocated is a multiple of v_size

commit bc869b5b28a0e0d4d53bc0a56174cda8212da1ca
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Sat Jun 27 21:03:37 2009 +0100

    device-manager: Add a new module to keep track of the names and descriptions of various sinks.
    
    This will be used as the basis for a queryable system for past and present devices, initially for use in KDE.
    Currently all this module does is save lists of sinks/sources and their descriptions, so it needs to
    gain a protocol extension to make this queryable.
    
    As things stand it will save the device descriptions of all sinks and restore them if they differ from whats on record.

diff --git a/src/Makefile.am b/src/Makefile.am
index 6544e2a..6e3d79b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -990,7 +990,8 @@ modlibexec_LTLIBRARIES += \
 		module-sine-source.la \
 		module-detect.la \
 		module-volume-restore.la \
-		module-device-restore.la \
+    module-device-manager.la \
+    module-device-restore.la \
 		module-stream-restore.la \
 		module-card-restore.la \
 		module-default-device-restore.la \
@@ -1231,7 +1232,8 @@ SYMDEF_FILES = \
 		modules/jack/module-jack-sink-symdef.h \
 		modules/jack/module-jack-source-symdef.h \
 		modules/module-volume-restore-symdef.h \
-		modules/module-device-restore-symdef.h \
+    modules/module-device-manager-symdef.h \
+    modules/module-device-restore-symdef.h \
 		modules/module-stream-restore-symdef.h \
 		modules/module-card-restore-symdef.h \
 		modules/module-default-device-restore-symdef.h \
@@ -1539,6 +1541,12 @@ module_cork_music_on_phone_la_LDFLAGS = $(MODULE_LDFLAGS)
 module_cork_music_on_phone_la_LIBADD = $(AM_LIBADD) libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
 module_cork_music_on_phone_la_CFLAGS = $(AM_CFLAGS)
 
+# Device description restore module
+module_device_manager_la_SOURCES = modules/module-device-manager.c
+module_device_manager_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_device_manager_la_LIBADD = $(AM_LIBADD) libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
+module_device_manager_la_CFLAGS = $(AM_CFLAGS)
+
 # Device volume/muted restore module
 module_device_restore_la_SOURCES = modules/module-device-restore.c
 module_device_restore_la_LDFLAGS = $(MODULE_LDFLAGS)
diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
new file mode 100644
index 0000000..96d4a66
--- /dev/null
+++ b/src/modules/module-device-manager.c
@@ -0,0 +1,352 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006-2008 Lennart Poettering
+  Copyright 2009 Colin Guthrie
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/volume.h>
+#include <pulse/timeval.h>
+#include <pulse/util.h>
+#include <pulse/rtclock.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/source-output.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/database.h>
+
+#include "module-device-manager-symdef.h"
+
+PA_MODULE_AUTHOR("Colin Guthrie");
+PA_MODULE_DESCRIPTION("Keep track of devices (and their descriptions) both past and present");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE("This module does not take any arguments");
+
+#define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
+
+static const char* const valid_modargs[] = {
+    NULL
+};
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    pa_subscription *subscription;
+    pa_hook_slot
+        *sink_new_hook_slot,
+        *source_new_hook_slot;
+    pa_time_event *save_time_event;
+    pa_database *database;
+};
+
+#define ENTRY_VERSION 1
+
+struct entry {
+    uint8_t version;
+    char description[PA_NAME_MAX];
+} PA_GCC_PACKED;
+
+static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(a);
+    pa_assert(e);
+    pa_assert(u);
+
+    pa_assert(e == u->save_time_event);
+    u->core->mainloop->time_free(u->save_time_event);
+    u->save_time_event = NULL;
+
+    pa_database_sync(u->database);
+    pa_log_info("Synced.");
+}
+
+static struct entry* read_entry(struct userdata *u, const char *name) {
+    pa_datum key, data;
+    struct entry *e;
+
+    pa_assert(u);
+    pa_assert(name);
+
+    key.data = (char*) name;
+    key.size = strlen(name);
+
+    pa_zero(data);
+
+    if (!pa_database_get(u->database, &key, &data))
+        goto fail;
+
+    if (data.size != sizeof(struct entry)) {
+        pa_log_debug("Database contains entry for device %s of wrong size %lu != %lu. Probably due to upgrade, ignoring.", name, (unsigned long) data.size, (unsigned long) sizeof(struct entry));
+        goto fail;
+    }
+
+    e = (struct entry*) data.data;
+
+    if (e->version != ENTRY_VERSION) {
+        pa_log_debug("Version of database entry for device %s doesn't match our version. Probably due to upgrade, ignoring.", name);
+        goto fail;
+    }
+
+    if (!memchr(e->description, 0, sizeof(e->description))) {
+        pa_log_warn("Database contains entry for device %s with missing NUL byte in description", name);
+        goto fail;
+    }
+
+    return e;
+
+fail:
+
+    pa_datum_free(&data);
+    return NULL;
+}
+
+static void trigger_save(struct userdata *u) {
+    if (u->save_time_event)
+        return;
+
+    u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
+}
+
+static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
+    if (strncmp(a->description, b->description, sizeof(a->description)))
+        return FALSE;
+
+    return TRUE;
+}
+
+static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
+    struct userdata *u = userdata;
+    struct entry entry, *old;
+    char *name;
+    pa_datum key, data;
+
+    pa_assert(c);
+    pa_assert(u);
+
+    if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
+        t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
+        t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
+        t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE))
+        return;
+
+    pa_zero(entry);
+    entry.version = ENTRY_VERSION;
+
+    if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
+        pa_sink *sink;
+
+        if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
+            return;
+
+        name = pa_sprintf_malloc("sink:%s", sink->name);
+
+        if ((old = read_entry(u, name)))
+            entry = *old;
+
+        pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
+
+    } else {
+        pa_source *source;
+
+        pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
+
+        if (!(source = pa_idxset_get_by_index(c->sources, idx)))
+            return;
+
+        name = pa_sprintf_malloc("source:%s", source->name);
+
+        if ((old = read_entry(u, name)))
+            entry = *old;
+
+        pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
+    }
+
+    if (old) {
+
+        if (entries_equal(old, &entry)) {
+            pa_xfree(old);
+            pa_xfree(name);
+            return;
+        }
+
+        pa_xfree(old);
+    }
+
+    key.data = name;
+    key.size = strlen(name);
+
+    data.data = &entry;
+    data.size = sizeof(entry);
+
+    pa_log_info("Storing device description for %s.", name);
+
+    pa_database_set(u->database, &key, &data, TRUE);
+
+    pa_xfree(name);
+
+    trigger_save(u);
+}
+
+static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
+    char *name;
+    struct entry *e;
+
+    pa_assert(c);
+    pa_assert(new_data);
+    pa_assert(u);
+
+    name = pa_sprintf_malloc("sink:%s", new_data->name);
+
+    if ((e = read_entry(u, name))) {
+        if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
+            pa_log_info("Restoring description for sink %s.", name);
+            pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
+        }
+
+        pa_xfree(e);
+    }
+
+    pa_xfree(name);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
+    char *name;
+    struct entry *e;
+
+    pa_assert(c);
+    pa_assert(new_data);
+    pa_assert(u);
+
+    name = pa_sprintf_malloc("source:%s", new_data->name);
+
+    if ((e = read_entry(u, name))) {
+
+        if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
+            pa_log_info("Restoring description for sink %s.", name);
+            pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
+        }
+
+        pa_xfree(e);
+    }
+
+    pa_xfree(name);
+
+    return PA_HOOK_OK;
+}
+
+int pa__init(pa_module*m) {
+    pa_modargs *ma = NULL;
+    struct userdata *u;
+    char *fname;
+    pa_sink *sink;
+    pa_source *source;
+    uint32_t idx;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+
+    u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE, subscribe_callback, u);
+
+    u->sink_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) sink_new_hook_callback, u);
+    u->source_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) source_new_hook_callback, u);
+
+    if (!(fname = pa_state_path("device-manager", TRUE)))
+        goto fail;
+
+    if (!(u->database = pa_database_open(fname, TRUE))) {
+        pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
+        pa_xfree(fname);
+        goto fail;
+    }
+
+    pa_log_info("Sucessfully opened database file '%s'.", fname);
+    pa_xfree(fname);
+
+    for (sink = pa_idxset_first(m->core->sinks, &idx); sink; sink = pa_idxset_next(m->core->sinks, &idx))
+        subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
+
+    for (source = pa_idxset_first(m->core->sources, &idx); source; source = pa_idxset_next(m->core->sources, &idx))
+        subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
+
+    pa_modargs_free(ma);
+    return 0;
+
+fail:
+    pa__done(m);
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    return  -1;
+}
+
+void pa__done(pa_module*m) {
+    struct userdata* u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->subscription)
+        pa_subscription_free(u->subscription);
+
+    if (u->sink_new_hook_slot)
+        pa_hook_slot_free(u->sink_new_hook_slot);
+    if (u->source_new_hook_slot)
+        pa_hook_slot_free(u->source_new_hook_slot);
+
+    if (u->save_time_event)
+        u->core->mainloop->time_free(u->save_time_event);
+
+    if (u->database)
+        pa_database_close(u->database);
+
+    pa_xfree(u);
+}

commit 37e82cec0ad13923a5db259a88bd00a2840112c6
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Sat Jun 27 22:08:07 2009 +0100

    device-manager: Add an untested protocol extension.
    
    This is effectively copied from the stream restore extension.

diff --git a/src/Makefile.am b/src/Makefile.am
index 6e3d79b..b7ebac5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -689,6 +689,7 @@ pulseinclude_HEADERS = \
 		pulse/context.h \
 		pulse/def.h \
 		pulse/error.h \
+		pulse/ext-device-manager.h \
 		pulse/ext-stream-restore.h \
 		pulse/gccmacro.h \
 		pulse/introspect.h \
@@ -739,6 +740,7 @@ libpulse_la_SOURCES = \
 		pulse/context.c pulse/context.h \
 		pulse/def.h \
 		pulse/error.c pulse/error.h \
+		pulse/ext-device-manager.c pulse/ext-device-manager.h \
 		pulse/ext-stream-restore.c pulse/ext-stream-restore.h \
 		pulse/gccmacro.h \
 		pulse/internal.h \
diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index 96d4a66..b41f71c 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -47,6 +47,9 @@
 #include <pulsecore/sink-input.h>
 #include <pulsecore/source-output.h>
 #include <pulsecore/namereg.h>
+#include <pulsecore/protocol-native.h>
+#include <pulsecore/pstream.h>
+#include <pulsecore/pstream-util.h>
 #include <pulsecore/database.h>
 
 #include "module-device-manager-symdef.h"
@@ -69,9 +72,13 @@ struct userdata {
     pa_subscription *subscription;
     pa_hook_slot
         *sink_new_hook_slot,
-        *source_new_hook_slot;
+        *source_new_hook_slot,
+        *connection_unlink_hook_slot;
     pa_time_event *save_time_event;
     pa_database *database;
+
+    pa_native_protocol *protocol;
+    pa_idxset *subscribed;
 };
 
 #define ENTRY_VERSION 1
@@ -81,6 +88,15 @@ struct entry {
     char description[PA_NAME_MAX];
 } PA_GCC_PACKED;
 
+enum {
+    SUBCOMMAND_TEST,
+    SUBCOMMAND_READ,
+    SUBCOMMAND_WRITE,
+    SUBCOMMAND_DELETE,
+    SUBCOMMAND_SUBSCRIBE,
+    SUBCOMMAND_EVENT
+};
+
 static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
     struct userdata *u = userdata;
 
@@ -272,6 +288,230 @@ static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data
     return PA_HOOK_OK;
 }
 
+static char *get_name(const char *key, const char *prefix) {
+  char *t;
+
+  if (strncmp(key, prefix, sizeof(prefix)))
+    return NULL;
+
+  t = pa_xstrdup(key + sizeof(prefix));
+  return t;
+}
+
+static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
+  pa_sink *sink;
+  pa_source *source;
+  uint32_t idx;
+
+  pa_assert(u);
+  pa_assert(name);
+  pa_assert(e);
+
+  for (sink = pa_idxset_first(u->core->sinks, &idx); sink; sink = pa_idxset_next(u->core->sinks, &idx)) {
+    char *n;
+
+    if (!(n = get_name(name, "sink")))
+      continue;
+
+    if (!pa_streq(sink->name, n)) {
+      pa_xfree(n);
+      continue;
+    }
+    pa_xfree(n);
+
+    pa_log_info("Restoring description for sink %s.", sink->name);
+    pa_proplist_sets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
+  }
+
+  for (source = pa_idxset_first(u->core->sources, &idx); source; source = pa_idxset_next(u->core->sources, &idx)) {
+    char *n;
+
+    if (!(n = get_name(name, "source")))
+      continue;
+
+    if (!pa_streq(source->name, n)) {
+      pa_xfree(n);
+      continue;
+    }
+    pa_xfree(n);
+
+    pa_log_info("Restoring description for source %s.", source->name);
+    pa_proplist_sets(source->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
+  }
+}
+
+#define EXT_VERSION 1
+
+static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
+  struct userdata *u;
+  uint32_t command;
+  pa_tagstruct *reply = NULL;
+
+  pa_assert(p);
+  pa_assert(m);
+  pa_assert(c);
+  pa_assert(t);
+
+  u = m->userdata;
+
+  if (pa_tagstruct_getu32(t, &command) < 0)
+    goto fail;
+
+  reply = pa_tagstruct_new(NULL, 0);
+  pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
+  pa_tagstruct_putu32(reply, tag);
+
+  switch (command) {
+    case SUBCOMMAND_TEST: {
+      if (!pa_tagstruct_eof(t))
+        goto fail;
+
+      pa_tagstruct_putu32(reply, EXT_VERSION);
+      break;
+    }
+
+    case SUBCOMMAND_READ: {
+      pa_datum key;
+      pa_bool_t done;
+
+      if (!pa_tagstruct_eof(t))
+        goto fail;
+
+      done = !pa_database_first(u->database, &key, NULL);
+
+      while (!done) {
+        pa_datum next_key;
+        struct entry *e;
+        char *name;
+
+        done = !pa_database_next(u->database, &key, &next_key, NULL);
+
+        name = pa_xstrndup(key.data, key.size);
+        pa_datum_free(&key);
+
+        if ((e = read_entry(u, name))) {
+          pa_tagstruct_puts(reply, name);
+          pa_tagstruct_puts(reply, e->description);
+
+          pa_xfree(e);
+        }
+
+        pa_xfree(name);
+
+        key = next_key;
+      }
+
+      break;
+    }
+
+    case SUBCOMMAND_WRITE: {
+      uint32_t mode;
+      pa_bool_t apply_immediately = FALSE;
+
+      if (pa_tagstruct_getu32(t, &mode) < 0 ||
+        pa_tagstruct_get_boolean(t, &apply_immediately) < 0)
+        goto fail;
+
+      if (mode != PA_UPDATE_MERGE &&
+        mode != PA_UPDATE_REPLACE &&
+        mode != PA_UPDATE_SET)
+        goto fail;
+
+      if (mode == PA_UPDATE_SET)
+        pa_database_clear(u->database);
+
+      while (!pa_tagstruct_eof(t)) {
+        const char *name, *description;
+        struct entry entry;
+        pa_datum key, data;
+
+        pa_zero(entry);
+        entry.version = ENTRY_VERSION;
+
+        if (pa_tagstruct_gets(t, &name) < 0 ||
+          pa_tagstruct_gets(reply, &description) < 0)
+          goto fail;
+
+        if (!name || !*name)
+          goto fail;
+
+        pa_strlcpy(entry.description, description, sizeof(entry.description));
+
+        key.data = (char*) name;
+        key.size = strlen(name);
+
+        data.data = &entry;
+        data.size = sizeof(entry);
+
+        if (pa_database_set(u->database, &key, &data, mode == PA_UPDATE_REPLACE) == 0)
+          if (apply_immediately)
+            apply_entry(u, name, &entry);
+      }
+
+      trigger_save(u);
+
+      break;
+    }
+
+    case SUBCOMMAND_DELETE:
+
+      while (!pa_tagstruct_eof(t)) {
+        const char *name;
+        pa_datum key;
+
+        if (pa_tagstruct_gets(t, &name) < 0)
+          goto fail;
+
+        key.data = (char*) name;
+        key.size = strlen(name);
+
+        pa_database_unset(u->database, &key);
+      }
+
+      trigger_save(u);
+
+      break;
+
+    case SUBCOMMAND_SUBSCRIBE: {
+
+      pa_bool_t enabled;
+
+      if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
+        !pa_tagstruct_eof(t))
+        goto fail;
+
+      if (enabled)
+        pa_idxset_put(u->subscribed, c, NULL);
+      else
+        pa_idxset_remove_by_data(u->subscribed, c, NULL);
+
+      break;
+    }
+
+    default:
+      goto fail;
+  }
+
+  pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
+  return 0;
+
+  fail:
+
+  if (reply)
+    pa_tagstruct_free(reply);
+
+  return -1;
+}
+
+static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
+    pa_assert(p);
+    pa_assert(c);
+    pa_assert(u);
+
+    pa_idxset_remove_by_data(u->subscribed, c, NULL);
+    return PA_HOOK_OK;
+}
+
 int pa__init(pa_module*m) {
     pa_modargs *ma = NULL;
     struct userdata *u;
@@ -290,6 +530,12 @@ int pa__init(pa_module*m) {
     m->userdata = u = pa_xnew0(struct userdata, 1);
     u->core = m->core;
     u->module = m;
+    u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+    u->protocol = pa_native_protocol_get(m->core);
+    pa_native_protocol_install_ext(u->protocol, m, extension_cb);
+
+    u->connection_unlink_hook_slot = pa_hook_connect(&pa_native_protocol_hooks(u->protocol)[PA_NATIVE_HOOK_CONNECTION_UNLINK], PA_HOOK_NORMAL, (pa_hook_cb_t) connection_unlink_hook_cb, u);
 
     u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE, subscribe_callback, u);
 
@@ -348,5 +594,13 @@ void pa__done(pa_module*m) {
     if (u->database)
         pa_database_close(u->database);
 
+    if (u->protocol) {
+        pa_native_protocol_remove_ext(u->protocol, m);
+        pa_native_protocol_unref(u->protocol);
+    }
+
+    if (u->subscribed)
+        pa_idxset_free(u->subscribed, NULL, NULL);
+
     pa_xfree(u);
 }
diff --git a/src/pulse/context.c b/src/pulse/context.c
index 23ae30c..7468d0a 100644
--- a/src/pulse/context.c
+++ b/src/pulse/context.c
@@ -128,6 +128,9 @@ static void reset_callbacks(pa_context *c) {
     c->event_callback = NULL;
     c->event_userdata = NULL;
 
+    c->ext_device_manager.callback = NULL;
+    c->ext_device_manager.userdata = NULL;
+
     c->ext_stream_restore.callback = NULL;
     c->ext_stream_restore.userdata = NULL;
 }
@@ -1434,6 +1437,8 @@ void pa_command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_t
 
     if (!strcmp(name, "module-stream-restore"))
         pa_ext_stream_restore_command(c, tag, t);
+    else if (!strcmp(name, "module-device-manager"))
+        pa_ext_device_manager_command(c, tag, t);
     else
         pa_log(_("Received message for unknown extension '%s'"), name);
 
diff --git a/src/pulse/ext-device-manager.c b/src/pulse/ext-device-manager.c
new file mode 100644
index 0000000..1c6eee5
--- /dev/null
+++ b/src/pulse/ext-device-manager.c
@@ -0,0 +1,358 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Lennart Poettering
+  Copyright 2009 Colin Guthrie
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/context.h>
+#include <pulse/gccmacro.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/pstream-util.h>
+
+#include "internal.h"
+#include "operation.h"
+#include "fork-detect.h"
+
+#include "ext-device-manager.h"
+
+enum {
+    SUBCOMMAND_TEST,
+    SUBCOMMAND_READ,
+    SUBCOMMAND_WRITE,
+    SUBCOMMAND_DELETE,
+    SUBCOMMAND_SUBSCRIBE,
+    SUBCOMMAND_EVENT
+};
+
+static void ext_device_manager_test_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    uint32_t version = PA_INVALID_INDEX;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+            goto finish;
+
+    } else if (pa_tagstruct_getu32(t, &version) < 0 ||
+               !pa_tagstruct_eof(t)) {
+
+        pa_context_fail(o->context, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (o->callback) {
+        pa_ext_device_manager_test_cb_t cb = (pa_ext_device_manager_test_cb_t) o->callback;
+        cb(o->context, version, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation *pa_ext_device_manager_test(
+        pa_context *c,
+        pa_ext_device_manager_test_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o;
+    pa_tagstruct *t;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-device-manager");
+    pa_tagstruct_putu32(t, SUBCOMMAND_TEST);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, ext_device_manager_test_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+static void ext_device_manager_read_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    int eol = 1;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+            goto finish;
+
+        eol = -1;
+    } else {
+
+        while (!pa_tagstruct_eof(t)) {
+            pa_ext_device_manager_info i;
+
+            memset(&i, 0, sizeof(i));
+
+            if (pa_tagstruct_gets(t, &i.name) < 0 ||
+                pa_tagstruct_gets(t, &i.description) < 0) {
+
+                pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+
+            if (o->callback) {
+                pa_ext_device_manager_read_cb_t cb = (pa_ext_device_manager_read_cb_t) o->callback;
+                cb(o->context, &i, 0, o->userdata);
+            }
+        }
+    }
+
+    if (o->callback) {
+        pa_ext_device_manager_read_cb_t cb = (pa_ext_device_manager_read_cb_t) o->callback;
+        cb(o->context, NULL, eol, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation *pa_ext_device_manager_read(
+        pa_context *c,
+        pa_ext_device_manager_read_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o;
+    pa_tagstruct *t;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-device-manager");
+    pa_tagstruct_putu32(t, SUBCOMMAND_READ);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, ext_device_manager_read_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation *pa_ext_device_manager_write(
+        pa_context *c,
+        pa_update_mode_t mode,
+        const pa_ext_device_manager_info data[],
+        unsigned n,
+        int apply_immediately,
+        pa_context_success_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o = NULL;
+    pa_tagstruct *t = NULL;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE || mode == PA_UPDATE_SET);
+    pa_assert(data);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-device-manager");
+    pa_tagstruct_putu32(t, SUBCOMMAND_WRITE);
+
+    pa_tagstruct_putu32(t, mode);
+    pa_tagstruct_put_boolean(t, apply_immediately);
+
+    for (; n > 0; n--, data++) {
+        if (!data->name || !*data->name)
+            goto fail;
+
+        pa_tagstruct_puts(t, data->name);
+        pa_tagstruct_puts(t, data->description);
+    }
+
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+
+fail:
+    if (o) {
+        pa_operation_cancel(o);
+        pa_operation_unref(o);
+    }
+
+    if (t)
+        pa_tagstruct_free(t);
+
+    pa_context_set_error(c, PA_ERR_INVALID);
+    return NULL;
+}
+
+pa_operation *pa_ext_device_manager_delete(
+        pa_context *c,
+        const char *const s[],
+        pa_context_success_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o = NULL;
+    pa_tagstruct *t = NULL;
+    const char *const *k;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(s);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-device-manager");
+    pa_tagstruct_putu32(t, SUBCOMMAND_DELETE);
+
+    for (k = s; *k; k++) {
+        if (!*k || !**k)
+            goto fail;
+
+        pa_tagstruct_puts(t, *k);
+    }
+
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+
+fail:
+    if (o) {
+        pa_operation_cancel(o);
+        pa_operation_unref(o);
+    }
+
+    if (t)
+        pa_tagstruct_free(t);
+
+    pa_context_set_error(c, PA_ERR_INVALID);
+    return NULL;
+}
+
+pa_operation *pa_ext_device_manager_subscribe(
+        pa_context *c,
+        int enable,
+        pa_context_success_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o;
+    pa_tagstruct *t;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-device-manager");
+    pa_tagstruct_putu32(t, SUBCOMMAND_SUBSCRIBE);
+    pa_tagstruct_put_boolean(t, enable);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+void pa_ext_device_manager_set_subscribe_cb(
+        pa_context *c,
+        pa_ext_device_manager_subscribe_cb_t cb,
+        void *userdata) {
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    if (pa_detect_fork())
+        return;
+
+    c->ext_device_manager.callback = cb;
+    c->ext_device_manager.userdata = userdata;
+}
+
+void pa_ext_device_manager_command(pa_context *c, uint32_t tag, pa_tagstruct *t) {
+    uint32_t subcommand;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &subcommand) < 0 ||
+        !pa_tagstruct_eof(t)) {
+
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        return;
+    }
+
+    if (subcommand != SUBCOMMAND_EVENT) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        return;
+    }
+
+    if (c->ext_device_manager.callback)
+        c->ext_device_manager.callback(c, c->ext_device_manager.userdata);
+}
diff --git a/src/pulse/ext-device-manager.h b/src/pulse/ext-device-manager.h
new file mode 100644
index 0000000..eed0c50
--- /dev/null
+++ b/src/pulse/ext-device-manager.h
@@ -0,0 +1,106 @@
+#ifndef foopulseextdevicemanagerhfoo
+#define foopulseextdevicemanagerhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Lennart Poettering
+  Copyright 2009 Colin Guthrie
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulse/context.h>
+#include <pulse/version.h>
+
+/** \file
+ *
+ * Routines for controlling module-stream-restore
+ */
+
+PA_C_DECL_BEGIN
+
+/** Stores information about one device in the device database that is
+ * maintained by module-device-manager. \since 0.9.17 */
+typedef struct pa_ext_device_manager_info {
+    const char *name;            /**< Identifier string of the device. A string like "sink:" or similar followed by the name of the device. */
+    const char *description;     /**< The description of the device when it was last seen, if applicable and saved */
+} pa_ext_device_manager_info;
+
+/** Callback prototype for pa_ext_device_manager_test(). \since 0.9.17 */
+typedef void (*pa_ext_device_manager_test_cb_t)(
+        pa_context *c,
+        uint32_t version,
+        void *userdata);
+
+/** Test if this extension module is available in the server. \since 0.9.17 */
+pa_operation *pa_ext_device_manager_test(
+        pa_context *c,
+        pa_ext_device_manager_test_cb_t cb,
+        void *userdata);
+
+/** Callback prototype for pa_ext_device_manager_read(). \since 0.9.17 */
+typedef void (*pa_ext_device_manager_read_cb_t)(
+        pa_context *c,
+        const pa_ext_device_manager_info *info,
+        int eol,
+        void *userdata);
+
+/** Read all entries from the device database. \since 0.9.17 */
+pa_operation *pa_ext_device_manager_read(
+        pa_context *c,
+        pa_ext_device_manager_read_cb_t cb,
+        void *userdata);
+
+/** Store entries in the device database. \since 0.9.17 */
+pa_operation *pa_ext_device_manager_write(
+        pa_context *c,
+        pa_update_mode_t mode,
+        const pa_ext_device_manager_info data[],
+        unsigned n,
+        int apply_immediately,
+        pa_context_success_cb_t cb,
+        void *userdata);
+
+/** Delete entries from the device database. \since 0.9.17 */
+pa_operation *pa_ext_device_manager_delete(
+        pa_context *c,
+        const char *const s[],
+        pa_context_success_cb_t cb,
+        void *userdata);
+
+/** Subscribe to changes in the device database. \since 0.9.17 */
+pa_operation *pa_ext_device_manager_subscribe(
+        pa_context *c,
+        int enable,
+        pa_context_success_cb_t cb,
+        void *userdata);
+
+/** Callback prototype for pa_ext_device_manager_set_subscribe_cb(). \since 0.9.17 */
+typedef void (*pa_ext_device_manager_subscribe_cb_t)(
+        pa_context *c,
+        void *userdata);
+
+/** Set the subscription callback that is called when
+ * pa_ext_device_manager_subscribe() was called. \since 0.9.17 */
+void pa_ext_device_manager_set_subscribe_cb(
+        pa_context *c,
+        pa_ext_device_manager_subscribe_cb_t cb,
+        void *userdata);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/internal.h b/src/pulse/internal.h
index e069c9e..b371bfc 100644
--- a/src/pulse/internal.h
+++ b/src/pulse/internal.h
@@ -28,6 +28,7 @@
 #include <pulse/stream.h>
 #include <pulse/operation.h>
 #include <pulse/subscribe.h>
+#include <pulse/ext-device-manager.h>
 #include <pulse/ext-stream-restore.h>
 
 #include <pulsecore/socket-client.h>
@@ -102,6 +103,10 @@ struct pa_context {
 
     /* Extension specific data */
     struct {
+        pa_ext_device_manager_subscribe_cb_t callback;
+        void *userdata;
+    } ext_device_manager;
+    struct {
         pa_ext_stream_restore_subscribe_cb_t callback;
         void *userdata;
     } ext_stream_restore;
@@ -283,6 +288,7 @@ pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *ta
 #define PA_FAIL_RETURN_NULL(context, error)     \
     PA_FAIL_RETURN_ANY(context, error, NULL)
 
+void pa_ext_device_manager_command(pa_context *c, uint32_t tag, pa_tagstruct *t);
 void pa_ext_stream_restore_command(pa_context *c, uint32_t tag, pa_tagstruct *t);
 
 pa_bool_t pa_mainloop_is_our_api(pa_mainloop_api*m);

commit 93c3c655e436862e2340a9d8a90d6b8a58c9a6e1
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Sat Jun 27 22:09:00 2009 +0100

    device-manager: Fix indentation

diff --git a/src/Makefile.am b/src/Makefile.am
index b7ebac5..0a041eb 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -992,8 +992,8 @@ modlibexec_LTLIBRARIES += \
 		module-sine-source.la \
 		module-detect.la \
 		module-volume-restore.la \
-    module-device-manager.la \
-    module-device-restore.la \
+		module-device-manager.la \
+		module-device-restore.la \
 		module-stream-restore.la \
 		module-card-restore.la \
 		module-default-device-restore.la \
@@ -1234,8 +1234,8 @@ SYMDEF_FILES = \
 		modules/jack/module-jack-sink-symdef.h \
 		modules/jack/module-jack-source-symdef.h \
 		modules/module-volume-restore-symdef.h \
-    modules/module-device-manager-symdef.h \
-    modules/module-device-restore-symdef.h \
+		modules/module-device-manager-symdef.h \
+		modules/module-device-restore-symdef.h \
 		modules/module-stream-restore-symdef.h \
 		modules/module-card-restore-symdef.h \
 		modules/module-default-device-restore-symdef.h \

commit 0b3b037e222e076e506bbcbdbeae4cd1dad96c40
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Sun Jun 28 13:08:17 2009 +0100

    device-manager: Export device-manager extension functions

diff --git a/src/map-file b/src/map-file
index 95b2803..cec688f 100644
--- a/src/map-file
+++ b/src/map-file
@@ -144,6 +144,12 @@ pa_cvolume_set_fade;
 pa_cvolume_set_position;
 pa_cvolume_snprint;
 pa_cvolume_valid;
+pa_ext_device_manager_delete;
+pa_ext_device_manager_read;
+pa_ext_device_manager_set_subscribe_cb;
+pa_ext_device_manager_subscribe;
+pa_ext_device_manager_test;
+pa_ext_device_manager_write;
 pa_ext_stream_restore_delete;
 pa_ext_stream_restore_read;
 pa_ext_stream_restore_set_subscribe_cb;

commit 70accbbd61ae1205a009a4bfa6ae7514bc0bd940
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Sun Jun 28 13:21:43 2009 +0100

    device-manager: Link native protocol library.

diff --git a/src/Makefile.am b/src/Makefile.am
index 0a041eb..8ed7482 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1546,7 +1546,7 @@ module_cork_music_on_phone_la_CFLAGS = $(AM_CFLAGS)
 # Device description restore module
 module_device_manager_la_SOURCES = modules/module-device-manager.c
 module_device_manager_la_LDFLAGS = $(MODULE_LDFLAGS)
-module_device_manager_la_LIBADD = $(AM_LIBADD) libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
+module_device_manager_la_LIBADD = $(AM_LIBADD) libprotocol-native.la libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
 module_device_manager_la_CFLAGS = $(AM_CFLAGS)
 
 # Device volume/muted restore module

commit 40e97eb698e0211045818c73d03d55e985f329d5
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Sun Jun 28 14:40:00 2009 +0100

    device-manager: Fix tagstruct description extraction (copy+paste blunder)

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index b41f71c..68ed951 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -429,7 +429,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
         entry.version = ENTRY_VERSION;
 
         if (pa_tagstruct_gets(t, &name) < 0 ||
-          pa_tagstruct_gets(reply, &description) < 0)
+          pa_tagstruct_gets(t, &description) < 0)
           goto fail;
 
         if (!name || !*name)

commit 64979385e09ba0a411669f9feeea56c93bf14d38
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Sun Jun 28 15:33:38 2009 +0100

    device-restore: Fix the application of an entry to allow changing the name of devices.
    
    This fixes a few bugs in the copy+pasted implementation of apply_entry()/get_name().

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index 68ed951..3ebdd48 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -289,55 +289,47 @@ static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data
 }
 
 static char *get_name(const char *key, const char *prefix) {
-  char *t;
+    char *t;
 
-  if (strncmp(key, prefix, sizeof(prefix)))
-    return NULL;
+    if (strncmp(key, prefix, strlen(prefix)))
+        return NULL;
 
-  t = pa_xstrdup(key + sizeof(prefix));
-  return t;
+    t = pa_xstrdup(key + strlen(prefix));
+    return t;
 }
 
 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
-  pa_sink *sink;
-  pa_source *source;
-  uint32_t idx;
-
-  pa_assert(u);
-  pa_assert(name);
-  pa_assert(e);
-
-  for (sink = pa_idxset_first(u->core->sinks, &idx); sink; sink = pa_idxset_next(u->core->sinks, &idx)) {
+    pa_sink *sink;
+    pa_source *source;
+    uint32_t idx;
     char *n;
 
-    if (!(n = get_name(name, "sink")))
-      continue;
-
-    if (!pa_streq(sink->name, n)) {
-      pa_xfree(n);
-      continue;
-    }
-    pa_xfree(n);
-
-    pa_log_info("Restoring description for sink %s.", sink->name);
-    pa_proplist_sets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
-  }
-
-  for (source = pa_idxset_first(u->core->sources, &idx); source; source = pa_idxset_next(u->core->sources, &idx)) {
-    char *n;
+    pa_assert(u);
+    pa_assert(name);
+    pa_assert(e);
 
-    if (!(n = get_name(name, "source")))
-      continue;
+    if ((n = get_name(name, "sink:"))) {
+        for (sink = pa_idxset_first(u->core->sinks, &idx); sink; sink = pa_idxset_next(u->core->sinks, &idx)) {
+            if (!pa_streq(sink->name, n)) {
+                continue;
+            }
 
-    if (!pa_streq(source->name, n)) {
-      pa_xfree(n);
-      continue;
+            pa_log_info("Setting description for sink %s.", sink->name);
+            pa_sink_set_description(sink, e->description);
+        }
+        pa_xfree(n);
+    }
+    else if ((n = get_name(name, "source:"))) {
+        for (source = pa_idxset_first(u->core->sources, &idx); source; source = pa_idxset_next(u->core->sources, &idx)) {
+            if (!pa_streq(source->name, n)) {
+                continue;
+            }
+
+            pa_log_info("Setting description for source %s.", source->name);
+            pa_source_set_description(source, e->description);
+        }
+        pa_xfree(n);
     }
-    pa_xfree(n);
-
-    pa_log_info("Restoring description for source %s.", source->name);
-    pa_proplist_sets(source->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
-  }
 }
 
 #define EXT_VERSION 1

commit 42b30e1aa2a134ccd90486b3dc73d1b13a7636a6
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Mon Jun 29 19:55:34 2009 +0100

    stream-restore: Preventative initialistion to NULL
    
    There is not technically a bug here due to the early return and the knowledge that one of the if blocks
    will definitely be run, but this makes sure we don't call free on uninitialised data or do
    anything else suitibly daft. Also helps when you copy the code and change it slightly and don't realise
    you've left things open...

diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c
index 9b6f914..b7b36be 100644
--- a/src/modules/module-stream-restore.c
+++ b/src/modules/module-stream-restore.c
@@ -281,8 +281,8 @@ static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
 
 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
     struct userdata *u = userdata;
-    struct entry entry, *old;
-    char *name;
+    struct entry entry, *old = NULL;
+    char *name = NULL;
     pa_datum key, data;
 
     pa_assert(c);

commit aa5d56ba752490abb9a8a18081196d9de2f6976b
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Mon Jun 29 20:10:04 2009 +0100

    device-manager: Only store and save details for non-monitor sources

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index 3ebdd48..77b6f2f 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -168,8 +168,8 @@ static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
 
 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
     struct userdata *u = userdata;
-    struct entry entry, *old;
-    char *name;
+    struct entry entry, *old = NULL;
+    char *name = NULL;
     pa_datum key, data;
 
     pa_assert(c);
@@ -205,6 +205,9 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
         if (!(source = pa_idxset_get_by_index(c->sources, idx)))
             return;
 
+        if (source->monitor_of)
+            return;
+
         name = pa_sprintf_malloc("source:%s", source->name);
 
         if ((old = read_entry(u, name)))
@@ -251,7 +254,7 @@ static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new
 
     if ((e = read_entry(u, name))) {
         if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
-            pa_log_info("Restoring description for sink %s.", name);
+            pa_log_info("Restoring description for sink %s.", new_data->name);
             pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
         }
 
@@ -276,7 +279,8 @@ static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data
     if ((e = read_entry(u, name))) {
 
         if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
-            pa_log_info("Restoring description for sink %s.", name);
+            /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
+            pa_log_info("Restoring description for sink %s.", new_data->name);
             pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
         }
 
@@ -325,6 +329,11 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
                 continue;
             }
 
+            if (source->monitor_of) {
+                pa_log_warn("Cowardly refusing to set the description for monitor source %s.", source->name);
+                continue;
+            }
+
             pa_log_info("Setting description for source %s.", source->name);
             pa_source_set_description(source, e->description);
         }

commit 464e1a89868b7c68927d7c95b4ff7d8fe3dbb0f0
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Sun Jul 5 19:40:06 2009 +0100

    device-manager: Fix copy+paste leftover

diff --git a/src/pulse/ext-device-manager.h b/src/pulse/ext-device-manager.h
index eed0c50..422691f 100644
--- a/src/pulse/ext-device-manager.h
+++ b/src/pulse/ext-device-manager.h
@@ -28,7 +28,7 @@
 
 /** \file
  *
- * Routines for controlling module-stream-restore
+ * Routines for controlling module-device-manager
  */
 
 PA_C_DECL_BEGIN

commit 9357bdf4e7c069e29d2fa403a01a892d80d2d89f
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Sat Sep 19 15:32:13 2009 +0100

    device-manager: Update docs version -> 0.9.19 (predicted)

diff --git a/src/pulse/ext-device-manager.h b/src/pulse/ext-device-manager.h
index 422691f..33bcbfa 100644
--- a/src/pulse/ext-device-manager.h
+++ b/src/pulse/ext-device-manager.h
@@ -34,38 +34,38 @@
 PA_C_DECL_BEGIN
 
 /** Stores information about one device in the device database that is
- * maintained by module-device-manager. \since 0.9.17 */
+ * maintained by module-device-manager. \since 0.9.19 */
 typedef struct pa_ext_device_manager_info {
     const char *name;            /**< Identifier string of the device. A string like "sink:" or similar followed by the name of the device. */
     const char *description;     /**< The description of the device when it was last seen, if applicable and saved */
 } pa_ext_device_manager_info;
 
-/** Callback prototype for pa_ext_device_manager_test(). \since 0.9.17 */
+/** Callback prototype for pa_ext_device_manager_test(). \since 0.9.19 */
 typedef void (*pa_ext_device_manager_test_cb_t)(
         pa_context *c,
         uint32_t version,
         void *userdata);
 
-/** Test if this extension module is available in the server. \since 0.9.17 */
+/** Test if this extension module is available in the server. \since 0.9.19 */
 pa_operation *pa_ext_device_manager_test(
         pa_context *c,
         pa_ext_device_manager_test_cb_t cb,
         void *userdata);
 
-/** Callback prototype for pa_ext_device_manager_read(). \since 0.9.17 */
+/** Callback prototype for pa_ext_device_manager_read(). \since 0.9.19 */
 typedef void (*pa_ext_device_manager_read_cb_t)(
         pa_context *c,
         const pa_ext_device_manager_info *info,
         int eol,
         void *userdata);
 
-/** Read all entries from the device database. \since 0.9.17 */
+/** Read all entries from the device database. \since 0.9.19 */
 pa_operation *pa_ext_device_manager_read(
         pa_context *c,
         pa_ext_device_manager_read_cb_t cb,
         void *userdata);
 
-/** Store entries in the device database. \since 0.9.17 */
+/** Store entries in the device database. \since 0.9.19 */
 pa_operation *pa_ext_device_manager_write(
         pa_context *c,
         pa_update_mode_t mode,
@@ -75,27 +75,27 @@ pa_operation *pa_ext_device_manager_write(
         pa_context_success_cb_t cb,
         void *userdata);
 
-/** Delete entries from the device database. \since 0.9.17 */
+/** Delete entries from the device database. \since 0.9.19 */
 pa_operation *pa_ext_device_manager_delete(
         pa_context *c,
         const char *const s[],
         pa_context_success_cb_t cb,
         void *userdata);
 
-/** Subscribe to changes in the device database. \since 0.9.17 */
+/** Subscribe to changes in the device database. \since 0.9.19 */
 pa_operation *pa_ext_device_manager_subscribe(
         pa_context *c,
         int enable,
         pa_context_success_cb_t cb,
         void *userdata);
 
-/** Callback prototype for pa_ext_device_manager_set_subscribe_cb(). \since 0.9.17 */
+/** Callback prototype for pa_ext_device_manager_set_subscribe_cb(). \since 0.9.19 */
 typedef void (*pa_ext_device_manager_subscribe_cb_t)(
         pa_context *c,
         void *userdata);
 
 /** Set the subscription callback that is called when
- * pa_ext_device_manager_subscribe() was called. \since 0.9.17 */
+ * pa_ext_device_manager_subscribe() was called. \since 0.9.19 */
 void pa_ext_device_manager_set_subscribe_cb(
         pa_context *c,
         pa_ext_device_manager_subscribe_cb_t cb,

commit 103897a1e33fe83f6ba0b7d521ccc2e36da43881
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Sat Sep 19 16:13:25 2009 +0100

    device-manager: Provide a way for clients to enable/disable role-based device-priority routing.
    
    The routing logic itself does not yet exist, but the command currently will unload/load module-stream-restore as approriate.
    (module-stream-restore would conflict with the role-based priority-routing).

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index 77b6f2f..5685dbb 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -79,6 +79,10 @@ struct userdata {
 
     pa_native_protocol *protocol;
     pa_idxset *subscribed;
+
+    pa_bool_t role_device_priority_routing;
+    pa_bool_t stream_restore_used;
+    pa_bool_t checked_stream_restore;
 };
 
 #define ENTRY_VERSION 1
@@ -93,6 +97,7 @@ enum {
     SUBCOMMAND_READ,
     SUBCOMMAND_WRITE,
     SUBCOMMAND_DELETE,
+    SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING,
     SUBCOMMAND_SUBSCRIBE,
     SUBCOMMAND_EVENT
 };
@@ -473,6 +478,57 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
 
       break;
 
+    case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING:
+
+        while (!pa_tagstruct_eof(t)) {
+            pa_bool_t enable;
+            uint32_t sridx = PA_INVALID_INDEX;
+            uint32_t idx;
+            pa_module *module;
+
+            if (pa_tagstruct_get_boolean(t, &enable) < 0)
+                goto fail;
+
+            /* If this is the first run, check for stream restore module */
+            if (!u->checked_stream_restore) {
+                u->checked_stream_restore = TRUE;
+
+                for (module = pa_idxset_first(u->core->modules, &idx); module; module = pa_idxset_next(u->core->modules, &idx)) {
+                    if (strcmp(module->name, "module-stream-restore") == 0) {
+                        pa_log_debug("Detected module-stream-restore is currently in use");
+                        u->stream_restore_used = TRUE;
+                        sridx = module->index;
+                    }
+                }
+            }
+
+            u->role_device_priority_routing = enable;
+            if (enable) {
+                if (u->stream_restore_used) {
+                    if (PA_INVALID_INDEX == sridx) {
+                        /* As a shortcut on first load, we have sridx filled in, but otherwise we search for it. */
+                        for (module = pa_idxset_first(u->core->modules, &idx); module; module = pa_idxset_next(u->core->modules, &idx)) {
+                            if (strcmp(module->name, "module-stream-restore") == 0) {
+                                sridx = module->index;
+                            }
+                        }
+                    }
+                    if (PA_INVALID_INDEX != sridx) {
+                        pa_log_debug("Unloading module-stream-restore to enable role-based device-priority routing");
+                        pa_module_unload_request_by_index(u->core, sridx, TRUE);
+                    }
+                }
+            } else if (u->stream_restore_used) {
+                /* We want to reload module-stream-restore */
+                if (!pa_module_load(u->core, "module-stream-restore", ""))
+                    pa_log_warn("Failed to load module-stream-restore while disabling role-based device-priority routing");
+            }
+        }
+
+        trigger_save(u);
+
+        break;
+
     case SUBCOMMAND_SUBSCRIBE: {
 
       pa_bool_t enabled;
diff --git a/src/pulse/ext-device-manager.c b/src/pulse/ext-device-manager.c
index 1c6eee5..0603a89 100644
--- a/src/pulse/ext-device-manager.c
+++ b/src/pulse/ext-device-manager.c
@@ -41,6 +41,7 @@ enum {
     SUBCOMMAND_READ,
     SUBCOMMAND_WRITE,
     SUBCOMMAND_DELETE,
+    SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING,
     SUBCOMMAND_SUBSCRIBE,
     SUBCOMMAND_EVENT
 };
@@ -289,6 +290,37 @@ fail:
     return NULL;
 }
 
+pa_operation *pa_ext_device_manager_enable_role_device_priority_routing(
+        pa_context *c,
+        int enable,
+        pa_context_success_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o = NULL;
+    pa_tagstruct *t = NULL;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-device-manager");
+    pa_tagstruct_putu32(t, SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING);
+    pa_tagstruct_put_boolean(t, !!enable);
+
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
 pa_operation *pa_ext_device_manager_subscribe(
         pa_context *c,
         int enable,
diff --git a/src/pulse/ext-device-manager.h b/src/pulse/ext-device-manager.h
index 33bcbfa..29d56ba 100644
--- a/src/pulse/ext-device-manager.h
+++ b/src/pulse/ext-device-manager.h
@@ -83,6 +83,13 @@ pa_operation *pa_ext_device_manager_delete(
         void *userdata);
 
 /** Subscribe to changes in the device database. \since 0.9.19 */
+pa_operation *pa_ext_device_manager_enable_role_device_priority_routing(
+        pa_context *c,
+        int enable,
+        pa_context_success_cb_t cb,
+        void *userdata);
+
+/** Subscribe to changes in the device database. \since 0.9.19 */
 pa_operation *pa_ext_device_manager_subscribe(
         pa_context *c,
         int enable,

commit 95f28393ab413c797e2f16d2caf1f8caf0283b71
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Sat Sep 19 16:46:18 2009 +0100

    device-manager: Fix copy+paste code that looped over the tagstruct when not necessary

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index 5685dbb..b3c407c 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -478,56 +478,53 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
 
       break;
 
-    case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING:
-
-        while (!pa_tagstruct_eof(t)) {
-            pa_bool_t enable;
-            uint32_t sridx = PA_INVALID_INDEX;
-            uint32_t idx;
-            pa_module *module;
-
-            if (pa_tagstruct_get_boolean(t, &enable) < 0)
-                goto fail;
-
-            /* If this is the first run, check for stream restore module */
-            if (!u->checked_stream_restore) {
-                u->checked_stream_restore = TRUE;
-
-                for (module = pa_idxset_first(u->core->modules, &idx); module; module = pa_idxset_next(u->core->modules, &idx)) {
-                    if (strcmp(module->name, "module-stream-restore") == 0) {
-                        pa_log_debug("Detected module-stream-restore is currently in use");
-                        u->stream_restore_used = TRUE;
-                        sridx = module->index;
-                    }
+    case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING: {
+
+        pa_bool_t enable;
+        uint32_t sridx = PA_INVALID_INDEX;
+        uint32_t idx;
+        pa_module *module;
+
+        if (pa_tagstruct_get_boolean(t, &enable) < 0)
+            goto fail;
+
+        /* If this is the first run, check for stream restore module */
+        if (!u->checked_stream_restore) {
+            u->checked_stream_restore = TRUE;
+
+            for (module = pa_idxset_first(u->core->modules, &idx); module; module = pa_idxset_next(u->core->modules, &idx)) {
+                if (strcmp(module->name, "module-stream-restore") == 0) {
+                    pa_log_debug("Detected module-stream-restore is currently in use");
+                    u->stream_restore_used = TRUE;
+                    sridx = module->index;
                 }
             }
+        }
 
-            u->role_device_priority_routing = enable;
-            if (enable) {
-                if (u->stream_restore_used) {
-                    if (PA_INVALID_INDEX == sridx) {
-                        /* As a shortcut on first load, we have sridx filled in, but otherwise we search for it. */
-                        for (module = pa_idxset_first(u->core->modules, &idx); module; module = pa_idxset_next(u->core->modules, &idx)) {
-                            if (strcmp(module->name, "module-stream-restore") == 0) {
-                                sridx = module->index;
-                            }
+        u->role_device_priority_routing = enable;
+        if (enable) {
+            if (u->stream_restore_used) {
+                if (PA_INVALID_INDEX == sridx) {
+                    /* As a shortcut on first load, we have sridx filled in, but otherwise we search for it. */
+                    for (module = pa_idxset_first(u->core->modules, &idx); module; module = pa_idxset_next(u->core->modules, &idx)) {
+                        if (strcmp(module->name, "module-stream-restore") == 0) {
+                            sridx = module->index;
                         }
                     }
-                    if (PA_INVALID_INDEX != sridx) {
-                        pa_log_debug("Unloading module-stream-restore to enable role-based device-priority routing");
-                        pa_module_unload_request_by_index(u->core, sridx, TRUE);
-                    }
                 }
-            } else if (u->stream_restore_used) {
-                /* We want to reload module-stream-restore */
-                if (!pa_module_load(u->core, "module-stream-restore", ""))
-                    pa_log_warn("Failed to load module-stream-restore while disabling role-based device-priority routing");
+                if (PA_INVALID_INDEX != sridx) {
+                    pa_log_debug("Unloading module-stream-restore to enable role-based device-priority routing");
+                    pa_module_unload_request_by_index(u->core, sridx, TRUE);
+                }
             }
+        } else if (u->stream_restore_used) {
+            /* We want to reload module-stream-restore */
+            if (!pa_module_load(u->core, "module-stream-restore", ""))
+                pa_log_warn("Failed to load module-stream-restore while disabling role-based device-priority routing");
         }
 
-        trigger_save(u);
-
         break;
+    }
 
     case SUBCOMMAND_SUBSCRIBE: {
 

commit aebe4787f293cc6810c54db751bee7df3a5d1ea2
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Sat Sep 19 17:48:10 2009 +0100

    device-manager: Provide a method for prefering/defering a device.
    
    This allows clients to edit the priroity order. What is not yet in place is the initialisation of that priority list
    when new devices are detected or the cleaning (remove holes) when devices are removed.
    
    In order to keep the storage transparent I will likely remove the write functionality and replace it with a
    simple rename method.
    
    I also still need to expose the priority itself when reading the data.

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index b3c407c..740b98f 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -87,9 +87,23 @@ struct userdata {
 
 #define ENTRY_VERSION 1
 
+#define NUM_ROLES 9
+enum {
+    ROLE_NONE,
+    ROLE_VIDEO,
+    ROLE_MUSIC,
+    ROLE_GAME,
+    ROLE_EVENT,
+    ROLE_PHONE,
+    ROLE_ANIMATION,
+    ROLE_PRODUCTION,
+    ROLE_A11Y,
+};
+
 struct entry {
     uint8_t version;
     char description[PA_NAME_MAX];
+    uint32_t priority[NUM_ROLES];
 } PA_GCC_PACKED;
 
 enum {
@@ -98,6 +112,8 @@ enum {
     SUBCOMMAND_WRITE,
     SUBCOMMAND_DELETE,
     SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING,
+    SUBCOMMAND_PREFER_DEVICE,
+    SUBCOMMAND_DEFER_DEVICE,
     SUBCOMMAND_SUBSCRIBE,
     SUBCOMMAND_EVENT
 };
@@ -346,6 +362,31 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
     }
 }
 
+
+static uint32_t get_role_index(const char* role) {
+    pa_assert(role);
+
+    if (strcmp(role, "") == 0)
+        return ROLE_NONE;
+    if (strcmp(role, "video") == 0)
+        return ROLE_VIDEO;
+    if (strcmp(role, "music") == 0)
+        return ROLE_MUSIC;
+    if (strcmp(role, "game") == 0)
+        return ROLE_GAME;
+    if (strcmp(role, "event") == 0)
+        return ROLE_EVENT;
+    if (strcmp(role, "phone") == 0)
+        return ROLE_PHONE;
+    if (strcmp(role, "animation") == 0)
+        return ROLE_ANIMATION;
+    if (strcmp(role, "production") == 0)
+        return ROLE_PRODUCTION;
+    if (strcmp(role, "a11y") == 0)
+        return ROLE_A11Y;
+    return PA_INVALID_INDEX;
+}
+
 #define EXT_VERSION 1
 
 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
@@ -526,6 +567,113 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
         break;
     }
 
+    case SUBCOMMAND_PREFER_DEVICE:
+    case SUBCOMMAND_DEFER_DEVICE: {
+
+        const char *role, *device;
+        struct entry *e;
+        uint32_t role_index;
+
+        if (pa_tagstruct_gets(t, &role) < 0 ||
+            pa_tagstruct_gets(t, &device) < 0)
+            goto fail;
+
+        if (!role || !device || !*device)
+            goto fail;
+
+        role_index = get_role_index(role);
+        if (PA_INVALID_INDEX == role_index)
+            goto fail;
+
+        if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) {
+            pa_datum key;
+            pa_datum data;
+            pa_bool_t done;
+            char* prefix;
+            uint32_t priority;
+            pa_bool_t haschanged = FALSE;
+
+            if (strncmp(device, "sink:", 5) == 0)
+                prefix = pa_xstrdup("sink:");
+            else
+                prefix = pa_xstrdup("source:");
+
+            priority = e->priority[role_index];
+
+            /* Now we need to load up all the other entries of this type and shuffle the priroities around */
+
+            done = !pa_database_first(u->database, &key, NULL);
+
+            while (!done && !haschanged) {
+                pa_datum next_key;
+
+                done = !pa_database_next(u->database, &key, &next_key, NULL);
+
+                /* Only read devices with the right prefix */
+                if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
+                    char *name;
+                    struct entry *e2;
+
+                    name = pa_xstrndup(key.data, key.size);
+                    pa_datum_free(&key);
+
+                    if ((e2 = read_entry(u, name))) {
+                        if (SUBCOMMAND_PREFER_DEVICE == command) {
+                            /* PREFER */
+                            if (e2->priority[role_index] == (priority - 1)) {
+                                e2->priority[role_index]++;
+                                haschanged = TRUE;
+                            }
+                        } else {
+                            /* DEFER */
+                            if (e2->priority[role_index] == (priority + 1)) {
+                                e2->priority[role_index]--;
+                                haschanged = TRUE;
+                            }
+                        }
+
+                        if (haschanged) {
+                            data.data = e2;
+                            data.size = sizeof(*e2);
+
+                            if (pa_database_set(u->database, &key, &data, FALSE))
+                                pa_log_warn("Could not save device");
+                        }
+                        pa_xfree(e2);
+                    }
+
+                    pa_xfree(name);
+                }
+
+                key = next_key;
+            }
+
+            /* Now write out our actual entry */
+            if (haschanged) {
+                if (SUBCOMMAND_PREFER_DEVICE == command)
+                    e->priority[role_index]--;
+                else
+                    e->priority[role_index]++;
+
+                key.data = (char *) device;
+                key.size = strlen(device);
+
+                data.data = e;
+                data.size = sizeof(*e);
+
+                if (pa_database_set(u->database, &key, &data, FALSE))
+                    pa_log_warn("Could not save device");
+
+                trigger_save(u);
+            }
+
+            pa_xfree(e);
+
+            pa_xfree(prefix);
+        }
+        break;
+    }
+
     case SUBCOMMAND_SUBSCRIBE: {
 
       pa_bool_t enabled;
diff --git a/src/pulse/ext-device-manager.c b/src/pulse/ext-device-manager.c
index 0603a89..a634e21 100644
--- a/src/pulse/ext-device-manager.c
+++ b/src/pulse/ext-device-manager.c
@@ -42,6 +42,8 @@ enum {
     SUBCOMMAND_WRITE,
     SUBCOMMAND_DELETE,
     SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING,
+    SUBCOMMAND_PREFER_DEVICE,
+    SUBCOMMAND_DEFER_DEVICE,
     SUBCOMMAND_SUBSCRIBE,
     SUBCOMMAND_EVENT
 };
@@ -321,6 +323,78 @@ pa_operation *pa_ext_device_manager_enable_role_device_priority_routing(
     return o;
 }
 
+pa_operation *pa_ext_device_manager_prefer_device(
+        pa_context *c,
+        const char* role,
+        const char* device,
+        pa_context_success_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o = NULL;
+    pa_tagstruct *t = NULL;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    pa_assert(role);
+    pa_assert(device);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-device-manager");
+    pa_tagstruct_putu32(t, SUBCOMMAND_PREFER_DEVICE);
+    pa_tagstruct_puts(t, role);
+    pa_tagstruct_puts(t, device);
+
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation *pa_ext_device_manager_defer_device(
+        pa_context *c,
+        const char* role,
+        const char* device,
+        pa_context_success_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o = NULL;
+    pa_tagstruct *t = NULL;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    pa_assert(role);
+    pa_assert(device);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-device-manager");
+    pa_tagstruct_putu32(t, SUBCOMMAND_DEFER_DEVICE);
+    pa_tagstruct_puts(t, role);
+    pa_tagstruct_puts(t, device);
+
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
 pa_operation *pa_ext_device_manager_subscribe(
         pa_context *c,
         int enable,
diff --git a/src/pulse/ext-device-manager.h b/src/pulse/ext-device-manager.h
index 29d56ba..d6de132 100644
--- a/src/pulse/ext-device-manager.h
+++ b/src/pulse/ext-device-manager.h
@@ -90,6 +90,22 @@ pa_operation *pa_ext_device_manager_enable_role_device_priority_routing(
         void *userdata);
 
 /** Subscribe to changes in the device database. \since 0.9.19 */
+pa_operation *pa_ext_device_manager_prefer_device(
+        pa_context *c,
+        const char* role,
+        const char* device,
+        pa_context_success_cb_t cb,
+        void *userdata);
+
+/** Subscribe to changes in the device database. \since 0.9.19 */
+pa_operation *pa_ext_device_manager_defer_device(
+        pa_context *c,
+        const char* role,
+        const char* device,
+        pa_context_success_cb_t cb,
+        void *userdata);
+
+/** Subscribe to changes in the device database. \since 0.9.19 */
 pa_operation *pa_ext_device_manager_subscribe(
         pa_context *c,
         int enable,

commit f8ec8f382ff1e9a05296b5a656195d44e8d05b44
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Sun Sep 20 12:44:02 2009 +0100

    device-manager: Change the write function to a rename function.
    
    The structure itself will contain various bits of info so exposing this fully to the client is a bad idea.
    By keeping to a rename operation we keep what we do store abstracted from the clients.
    
    Also fix some doxy comments.

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index 740b98f..0a0c39d 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -109,7 +109,7 @@ struct entry {
 enum {
     SUBCOMMAND_TEST,
     SUBCOMMAND_READ,
-    SUBCOMMAND_WRITE,
+    SUBCOMMAND_RENAME,
     SUBCOMMAND_DELETE,
     SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING,
     SUBCOMMAND_PREFER_DEVICE,
@@ -451,51 +451,41 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
       break;
     }
 
-    case SUBCOMMAND_WRITE: {
-      uint32_t mode;
-      pa_bool_t apply_immediately = FALSE;
+    case SUBCOMMAND_RENAME: {
 
-      if (pa_tagstruct_getu32(t, &mode) < 0 ||
-        pa_tagstruct_get_boolean(t, &apply_immediately) < 0)
-        goto fail;
-
-      if (mode != PA_UPDATE_MERGE &&
-        mode != PA_UPDATE_REPLACE &&
-        mode != PA_UPDATE_SET)
-        goto fail;
-
-      if (mode == PA_UPDATE_SET)
-        pa_database_clear(u->database);
-
-      while (!pa_tagstruct_eof(t)) {
-        const char *name, *description;
-        struct entry entry;
-        pa_datum key, data;
-
-        pa_zero(entry);
-        entry.version = ENTRY_VERSION;
+        struct entry *e;
+        const char *device, *description;
 
-        if (pa_tagstruct_gets(t, &name) < 0 ||
+        if (pa_tagstruct_gets(t, &device) < 0 ||
           pa_tagstruct_gets(t, &description) < 0)
           goto fail;
 
-        if (!name || !*name)
+        if (!device || !*device || !description || !*description)
           goto fail;
 
-        pa_strlcpy(entry.description, description, sizeof(entry.description));
+        if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) {
+            pa_datum key, data;
 
-        key.data = (char*) name;
-        key.size = strlen(name);
+            pa_strlcpy(e->description, description, sizeof(e->description));
 
-        data.data = &entry;
-        data.size = sizeof(entry);
+            key.data = (char *) device;
+            key.size = strlen(device);
 
-        if (pa_database_set(u->database, &key, &data, mode == PA_UPDATE_REPLACE) == 0)
-          if (apply_immediately)
-            apply_entry(u, name, &entry);
-      }
+            data.data = e;
+            data.size = sizeof(*e);
 
-      trigger_save(u);
+            if (pa_database_set(u->database, &key, &data, FALSE) == 0) {
+                apply_entry(u, device, e);
+
+                trigger_save(u);
+            }
+            else
+                pa_log_warn("Could not save device");
+
+            pa_xfree(e);
+        }
+        else
+            pa_log_warn("Could not rename device %s, no entry in database", device);
 
       break;
     }
@@ -671,6 +661,9 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
 
             pa_xfree(prefix);
         }
+        else
+            pa_log_warn("Could not reorder device %s, no entry in database", device);
+
         break;
     }
 
diff --git a/src/pulse/ext-device-manager.c b/src/pulse/ext-device-manager.c
index a634e21..bc6301c 100644
--- a/src/pulse/ext-device-manager.c
+++ b/src/pulse/ext-device-manager.c
@@ -39,7 +39,7 @@
 enum {
     SUBCOMMAND_TEST,
     SUBCOMMAND_READ,
-    SUBCOMMAND_WRITE,
+    SUBCOMMAND_RENAME,
     SUBCOMMAND_DELETE,
     SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING,
     SUBCOMMAND_PREFER_DEVICE,
@@ -183,12 +183,10 @@ pa_operation *pa_ext_device_manager_read(
     return o;
 }
 
-pa_operation *pa_ext_device_manager_write(
+pa_operation *pa_ext_device_manager_set_device_description(
         pa_context *c,
-        pa_update_mode_t mode,
-        const pa_ext_device_manager_info data[],
-        unsigned n,
-        int apply_immediately,
+        const char* device,
+        const char* description,
         pa_context_success_cb_t cb,
         void *userdata) {
 
@@ -198,8 +196,8 @@ pa_operation *pa_ext_device_manager_write(
 
     pa_assert(c);
     pa_assert(PA_REFCNT_VALUE(c) >= 1);
-    pa_assert(mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE || mode == PA_UPDATE_SET);
-    pa_assert(data);
+    pa_assert(device);
+    pa_assert(description);
 
     PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
     PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
@@ -210,35 +208,15 @@ pa_operation *pa_ext_device_manager_write(
     t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
     pa_tagstruct_putu32(t, PA_INVALID_INDEX);
     pa_tagstruct_puts(t, "module-device-manager");
-    pa_tagstruct_putu32(t, SUBCOMMAND_WRITE);
-
-    pa_tagstruct_putu32(t, mode);
-    pa_tagstruct_put_boolean(t, apply_immediately);
-
-    for (; n > 0; n--, data++) {
-        if (!data->name || !*data->name)
-            goto fail;
+    pa_tagstruct_putu32(t, SUBCOMMAND_RENAME);
 
-        pa_tagstruct_puts(t, data->name);
-        pa_tagstruct_puts(t, data->description);
-    }
+    pa_tagstruct_puts(t, device);
+    pa_tagstruct_puts(t, description);
 
     pa_pstream_send_tagstruct(c->pstream, t);
     pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
 
     return o;
-
-fail:
-    if (o) {
-        pa_operation_cancel(o);
-        pa_operation_unref(o);
-    }
-
-    if (t)
-        pa_tagstruct_free(t);
-
-    pa_context_set_error(c, PA_ERR_INVALID);
-    return NULL;
 }
 
 pa_operation *pa_ext_device_manager_delete(
diff --git a/src/pulse/ext-device-manager.h b/src/pulse/ext-device-manager.h
index d6de132..686c8d2 100644
--- a/src/pulse/ext-device-manager.h
+++ b/src/pulse/ext-device-manager.h
@@ -65,13 +65,11 @@ pa_operation *pa_ext_device_manager_read(
         pa_ext_device_manager_read_cb_t cb,
         void *userdata);
 
-/** Store entries in the device database. \since 0.9.19 */
-pa_operation *pa_ext_device_manager_write(
+/** Sets the description for a device. \since 0.9.19 */
+pa_operation *pa_ext_device_manager_set_device_description(
         pa_context *c,
-        pa_update_mode_t mode,
-        const pa_ext_device_manager_info data[],
-        unsigned n,
-        int apply_immediately,
+        const char* device,
+        const char* description,
         pa_context_success_cb_t cb,
         void *userdata);
 
@@ -82,14 +80,14 @@ pa_operation *pa_ext_device_manager_delete(
         pa_context_success_cb_t cb,
         void *userdata);
 
-/** Subscribe to changes in the device database. \since 0.9.19 */
+/** Enable the role-based device-priority routing mode. \since 0.9.19 */
 pa_operation *pa_ext_device_manager_enable_role_device_priority_routing(
         pa_context *c,
         int enable,
         pa_context_success_cb_t cb,
         void *userdata);
 
-/** Subscribe to changes in the device database. \since 0.9.19 */
+/** Prefer a given device in the priority list. \since 0.9.19 */
 pa_operation *pa_ext_device_manager_prefer_device(
         pa_context *c,
         const char* role,
@@ -97,7 +95,7 @@ pa_operation *pa_ext_device_manager_prefer_device(
         pa_context_success_cb_t cb,
         void *userdata);
 
-/** Subscribe to changes in the device database. \since 0.9.19 */
+/** Defer a given device in the priority list. \since 0.9.19 */
 pa_operation *pa_ext_device_manager_defer_device(
         pa_context *c,
         const char* role,

commit a64f0f719ff4154cbd3d8f78dbb41c8e816eb672
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Sun Sep 20 13:57:10 2009 +0100

    device-manager: Let subscribed clients know when something changes.

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index 0a0c39d..59aedd6 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -174,6 +174,22 @@ fail:
 }
 
 static void trigger_save(struct userdata *u) {
+    pa_native_connection *c;
+    uint32_t idx;
+
+    for (c = pa_idxset_first(u->subscribed, &idx); c; c = pa_idxset_next(u->subscribed, &idx)) {
+        pa_tagstruct *t;
+
+        t = pa_tagstruct_new(NULL, 0);
+        pa_tagstruct_putu32(t, PA_COMMAND_EXTENSION);
+        pa_tagstruct_putu32(t, 0);
+        pa_tagstruct_putu32(t, u->module->index);
+        pa_tagstruct_puts(t, u->module->name);
+        pa_tagstruct_putu32(t, SUBCOMMAND_EVENT);
+
+        pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t);
+    }
+
     if (u->save_time_event)
         return;
 

commit 180250096765ccbabfd4194b186c2d00110df70d
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Sun Sep 20 14:36:20 2009 +0100

    device-manager: When a new device is encountered, initialise the priority list to an appropriate value

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index 59aedd6..f759e34 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -203,6 +203,60 @@ static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
     return TRUE;
 }
 
+static inline struct entry *load_or_initialize_entry(struct userdata *u, struct entry *entry, const char *name, const char *prefix) {
+    struct entry *old;
+
+    pa_assert(u);
+    pa_assert(entry);
+    pa_assert(name);
+    pa_assert(prefix);
+
+    if ((old = read_entry(u, name)))
+        *entry = *old;
+    else {
+        /* This is a new device, so make sure we write it's priority list correctly */
+        uint32_t max_priority[NUM_ROLES];
+        pa_datum key;
+        pa_bool_t done;
+
+        pa_zero(max_priority);
+        done = !pa_database_first(u->database, &key, NULL);
+
+        /* Find all existing devices with the same prefix so we calculate the current max priority for each role */
+        while (!done) {
+            pa_datum next_key;
+
+            done = !pa_database_next(u->database, &key, &next_key, NULL);
+
+            if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
+                char *name2;
+                struct entry *e;
+
+                name2 = pa_xstrndup(key.data, key.size);
+
+                if ((e = read_entry(u, name2))) {
+                    for (uint32_t i = 0; i < NUM_ROLES; ++i) {
+                        max_priority[i] = PA_MAX(max_priority[i], e->priority[i]);
+                    }
+
+                    pa_xfree(e);
+                }
+
+                pa_xfree(name2);
+            }
+            pa_datum_free(&key);
+            key = next_key;
+        }
+
+        /* Actually initialise our entry now we've calculated it */
+        for (uint32_t i = 0; i < NUM_ROLES; ++i) {
+            entry->priority[i] = max_priority[i] + 1;
+        }
+    }
+
+    return old;
+}
+
 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
     struct userdata *u = userdata;
     struct entry entry, *old = NULL;
@@ -229,8 +283,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
 
         name = pa_sprintf_malloc("sink:%s", sink->name);
 
-        if ((old = read_entry(u, name)))
-            entry = *old;
+        old = load_or_initialize_entry(u, &entry, name, "sink:");
 
         pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
 
@@ -247,8 +300,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
 
         name = pa_sprintf_malloc("source:%s", source->name);
 
-        if ((old = read_entry(u, name)))
-            entry = *old;
+        old = load_or_initialize_entry(u, &entry, name, "source:");
 
         pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
     }

commit e589f38e227a53e1ed57491528c1290ddf8c1da7
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Sun Sep 20 14:39:41 2009 +0100

    device-manager: Fix the freeing of the datum on prefer/defer.
    
    Also fix a log typo

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index f759e34..75059f7 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -366,10 +366,9 @@ static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data
     name = pa_sprintf_malloc("source:%s", new_data->name);
 
     if ((e = read_entry(u, name))) {
-
         if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
             /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
-            pa_log_info("Restoring description for sink %s.", new_data->name);
+            pa_log_info("Restoring description for source %s.", new_data->name);
             pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
         }
 
@@ -644,8 +643,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
             goto fail;
 
         if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) {
-            pa_datum key;
-            pa_datum data;
+            pa_datum key, data;
             pa_bool_t done;
             char* prefix;
             uint32_t priority;
@@ -673,7 +671,6 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
                     struct entry *e2;
 
                     name = pa_xstrndup(key.data, key.size);
-                    pa_datum_free(&key);
 
                     if ((e2 = read_entry(u, name))) {
                         if (SUBCOMMAND_PREFER_DEVICE == command) {
@@ -697,12 +694,14 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
                             if (pa_database_set(u->database, &key, &data, FALSE))
                                 pa_log_warn("Could not save device");
                         }
+
                         pa_xfree(e2);
                     }
 
                     pa_xfree(name);
                 }
 
+                pa_datum_free(&key);
                 key = next_key;
             }
 

commit faae33d808480a34f02bea6e66ba0da0523694cf
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Sun Sep 20 14:43:53 2009 +0100

    device-manager: debug and comments

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index 75059f7..e029c1d 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -322,7 +322,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
     data.data = &entry;
     data.size = sizeof(entry);
 
-    pa_log_info("Storing device description for %s.", name);
+    pa_log_info("Storing device %s.", name);
 
     pa_database_set(u->database, &key, &data, TRUE);
 
@@ -569,6 +569,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
         key.data = (char*) name;
         key.size = strlen(name);
 
+        /** @todo: Reindex the priorities */
         pa_database_unset(u->database, &key);
       }
 

commit ed8af7c8fd7bf845a243518357b6b73667d209c0
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Sun Sep 20 17:29:38 2009 +0100

    device-manager: Rough framework (slots etc.) for handling routing.
    
    This is incomplete, it just adds the slots in question and assigns noops to them.
    Some minor cleanup of types.
    
    Due to the priority of the hooks, it seems we can actually coexist with module-stream restore so
    the code to detect and unload it will be removed shortly.

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index e029c1d..4c4e3f0 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -58,11 +58,15 @@ PA_MODULE_AUTHOR("Colin Guthrie");
 PA_MODULE_DESCRIPTION("Keep track of devices (and their descriptions) both past and present");
 PA_MODULE_VERSION(PACKAGE_VERSION);
 PA_MODULE_LOAD_ONCE(TRUE);
-PA_MODULE_USAGE("This module does not take any arguments");
+PA_MODULE_USAGE(
+    "on_hotplug=<When new device becomes available, recheck streams?> "
+    "on_rescue=<When device becomes unavailable, recheck streams?>");
 
 #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
 
 static const char* const valid_modargs[] = {
+    "on_hotplug",
+    "on_rescue",
     NULL
 };
 
@@ -73,6 +77,12 @@ struct userdata {
     pa_hook_slot
         *sink_new_hook_slot,
         *source_new_hook_slot,
+        *sink_input_new_hook_slot,
+        *source_output_new_hook_slot,
+        *sink_put_hook_slot,
+        *source_put_hook_slot,
+        *sink_unlink_hook_slot,
+        *source_unlink_hook_slot,
         *connection_unlink_hook_slot;
     pa_time_event *save_time_event;
     pa_database *database;
@@ -80,6 +90,8 @@ struct userdata {
     pa_native_protocol *protocol;
     pa_idxset *subscribed;
 
+    pa_bool_t on_hotplug;
+    pa_bool_t on_rescue;
     pa_bool_t role_device_priority_routing;
     pa_bool_t stream_restore_used;
     pa_bool_t checked_stream_restore;
@@ -100,10 +112,12 @@ enum {
     ROLE_A11Y,
 };
 
+typedef uint32_t role_indexes_t[NUM_ROLES];
+
 struct entry {
     uint8_t version;
     char description[PA_NAME_MAX];
-    uint32_t priority[NUM_ROLES];
+    role_indexes_t priority;
 } PA_GCC_PACKED;
 
 enum {
@@ -215,7 +229,7 @@ static inline struct entry *load_or_initialize_entry(struct userdata *u, struct
         *entry = *old;
     else {
         /* This is a new device, so make sure we write it's priority list correctly */
-        uint32_t max_priority[NUM_ROLES];
+        role_indexes_t max_priority;
         pa_datum key;
         pa_bool_t done;
 
@@ -390,6 +404,138 @@ static char *get_name(const char *key, const char *prefix) {
     return t;
 }
 
+static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
+    char *name;
+    struct entry *e;
+
+    pa_assert(c);
+    pa_assert(new_data);
+    pa_assert(u);
+
+    if (!u->role_device_priority_routing)
+        return PA_HOOK_OK;
+
+    /*if (!(name = get_name(new_data->proplist, "sink-input")))
+        return PA_HOOK_OK;
+
+    if (new_data->sink)
+        pa_log_debug("Not restoring device for stream %s, because already set.", name);
+    else if ((e = read_entry(u, name))) {
+
+        pa_xfree(e);
+    }
+
+    pa_xfree(name);*/
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
+    char *name;
+    struct entry *e;
+
+    pa_assert(c);
+    pa_assert(new_data);
+    pa_assert(u);
+
+    if (!u->role_device_priority_routing)
+        return PA_HOOK_OK;
+
+    if (new_data->direct_on_input)
+        return PA_HOOK_OK;
+
+    /*if (!(name = get_name(new_data->proplist, "source-output")))
+        return PA_HOOK_OK;
+
+    if (new_data->source)
+        pa_log_debug("Not restoring device for stream %s, because already set", name);
+    else if ((e = read_entry(u, name))) {
+
+        pa_xfree(e);
+    }
+
+    pa_xfree(name);*/
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
+    pa_sink_input *si;
+    uint32_t idx;
+
+    pa_assert(c);
+    pa_assert(sink);
+    pa_assert(u);
+    pa_assert(u->on_hotplug);
+
+    if (!u->role_device_priority_routing)
+        return PA_HOOK_OK;
+
+    /** @todo Ensure redo the routing based on the priorities */
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
+    pa_source_output *so;
+    uint32_t idx;
+
+    pa_assert(c);
+    pa_assert(source);
+    pa_assert(u);
+    pa_assert(u->on_hotplug);
+
+    if (!u->role_device_priority_routing)
+        return PA_HOOK_OK;
+
+    /** @todo Ensure redo the routing based on the priorities */
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
+    pa_sink_input *si;
+    uint32_t idx;
+
+    pa_assert(c);
+    pa_assert(sink);
+    pa_assert(u);
+    pa_assert(u->on_rescue);
+
+    /* There's no point in doing anything if the core is shut down anyway */
+    if (c->state == PA_CORE_SHUTDOWN)
+        return PA_HOOK_OK;
+
+    if (!u->role_device_priority_routing)
+        return PA_HOOK_OK;
+
+    /** @todo Ensure redo the routing based on the priorities */
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
+    pa_source_output *so;
+    uint32_t idx;
+
+    pa_assert(c);
+    pa_assert(source);
+    pa_assert(u);
+    pa_assert(u->on_rescue);
+
+    /* There's no point in doing anything if the core is shut down anyway */
+    if (c->state == PA_CORE_SHUTDOWN)
+        return PA_HOOK_OK;
+
+    if (!u->role_device_priority_routing)
+        return PA_HOOK_OK;
+
+    /** @todo Ensure redo the routing based on the priorities */
+
+    return PA_HOOK_OK;
+}
+
+
 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
     pa_sink *sink;
     pa_source *source;
@@ -781,7 +927,10 @@ int pa__init(pa_module*m) {
     char *fname;
     pa_sink *sink;
     pa_source *source;
+    pa_sink_input *si;
+    pa_source_output *so;
     uint32_t idx;
+    pa_bool_t on_hotplug = TRUE, on_rescue = TRUE;
 
     pa_assert(m);
 
@@ -790,9 +939,17 @@ int pa__init(pa_module*m) {
         goto fail;
     }
 
+    if (pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
+        pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) {
+        pa_log("on_hotplug= and on_rescue= expect boolean arguments");
+        goto fail;
+    }
+
     m->userdata = u = pa_xnew0(struct userdata, 1);
     u->core = m->core;
     u->module = m;
+    u->on_hotplug = on_hotplug;
+    u->on_rescue = on_rescue;
     u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 
     u->protocol = pa_native_protocol_get(m->core);
@@ -802,9 +959,27 @@ int pa__init(pa_module*m) {
 
     u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE, subscribe_callback, u);
 
+    /* Used to handle device description management */
     u->sink_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) sink_new_hook_callback, u);
     u->source_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) source_new_hook_callback, u);
 
+    /* The following slots are used to deal with routing */
+    /* A little bit later than module-stream-restore, module-intended-roles */
+    u->sink_input_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY+15, (pa_hook_cb_t) sink_input_new_hook_callback, u);
+    u->source_output_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_EARLY+15, (pa_hook_cb_t) source_output_new_hook_callback, u);
+
+    if (on_hotplug) {
+        /* A little bit later than module-stream-restore, module-intended-roles */
+        u->sink_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE+15, (pa_hook_cb_t) sink_put_hook_callback, u);
+        u->source_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE+15, (pa_hook_cb_t) source_put_hook_callback, u);
+    }
+
+    if (on_rescue) {
+        /* A little bit later than module-stream-restore, module-intended-roles, a little bit earlier than module-rescue-streams, ... */
+        u->sink_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE+15, (pa_hook_cb_t) sink_unlink_hook_callback, u);
+        u->source_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE+15, (pa_hook_cb_t) source_unlink_hook_callback, u);
+    }
+
     if (!(fname = pa_state_path("device-manager", TRUE)))
         goto fail;
 
@@ -817,12 +992,18 @@ int pa__init(pa_module*m) {
     pa_log_info("Sucessfully opened database file '%s'.", fname);
     pa_xfree(fname);
 
-    for (sink = pa_idxset_first(m->core->sinks, &idx); sink; sink = pa_idxset_next(m->core->sinks, &idx))
+    PA_IDXSET_FOREACH(sink, m->core->sinks, idx)
         subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
 
-    for (source = pa_idxset_first(m->core->sources, &idx); source; source = pa_idxset_next(m->core->sources, &idx))
+    PA_IDXSET_FOREACH(source, m->core->sources, idx)
         subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
 
+    PA_IDXSET_FOREACH(si, m->core->sink_inputs, idx)
+        subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, si->index, u);
+
+    PA_IDXSET_FOREACH(so, m->core->source_outputs, idx)
+        subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, so->index, u);
+
     pa_modargs_free(ma);
     return 0;
 
@@ -851,6 +1032,21 @@ void pa__done(pa_module*m) {
     if (u->source_new_hook_slot)
         pa_hook_slot_free(u->source_new_hook_slot);
 
+    if (u->sink_input_new_hook_slot)
+        pa_hook_slot_free(u->sink_input_new_hook_slot);
+    if (u->source_output_new_hook_slot)
+        pa_hook_slot_free(u->source_output_new_hook_slot);
+
+    if (u->sink_put_hook_slot)
+        pa_hook_slot_free(u->sink_put_hook_slot);
+    if (u->source_put_hook_slot)
+        pa_hook_slot_free(u->source_put_hook_slot);
+
+    if (u->sink_unlink_hook_slot)
+        pa_hook_slot_free(u->sink_unlink_hook_slot);
+    if (u->source_unlink_hook_slot)
+        pa_hook_slot_free(u->source_unlink_hook_slot);
+
     if (u->save_time_event)
         u->core->mainloop->time_free(u->save_time_event);
 

commit ca68105c8f9920fa18016b24e26ba9365d8f94b6
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Sun Sep 20 17:33:18 2009 +0100

    device-manager: Remove unneeded logic for checking for and (un)loading module-stream-restore. We can co-exist

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index 4c4e3f0..38b4c02 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -93,8 +93,6 @@ struct userdata {
     pa_bool_t on_hotplug;
     pa_bool_t on_rescue;
     pa_bool_t role_device_priority_routing;
-    pa_bool_t stream_restore_used;
-    pa_bool_t checked_stream_restore;
 };
 
 #define ENTRY_VERSION 1
@@ -733,40 +731,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
         if (pa_tagstruct_get_boolean(t, &enable) < 0)
             goto fail;
 
-        /* If this is the first run, check for stream restore module */
-        if (!u->checked_stream_restore) {
-            u->checked_stream_restore = TRUE;
-
-            for (module = pa_idxset_first(u->core->modules, &idx); module; module = pa_idxset_next(u->core->modules, &idx)) {
-                if (strcmp(module->name, "module-stream-restore") == 0) {
-                    pa_log_debug("Detected module-stream-restore is currently in use");
-                    u->stream_restore_used = TRUE;
-                    sridx = module->index;
-                }
-            }
-        }
-
         u->role_device_priority_routing = enable;
-        if (enable) {
-            if (u->stream_restore_used) {
-                if (PA_INVALID_INDEX == sridx) {
-                    /* As a shortcut on first load, we have sridx filled in, but otherwise we search for it. */
-                    for (module = pa_idxset_first(u->core->modules, &idx); module; module = pa_idxset_next(u->core->modules, &idx)) {
-                        if (strcmp(module->name, "module-stream-restore") == 0) {
-                            sridx = module->index;
-                        }
-                    }
-                }
-                if (PA_INVALID_INDEX != sridx) {
-                    pa_log_debug("Unloading module-stream-restore to enable role-based device-priority routing");
-                    pa_module_unload_request_by_index(u->core, sridx, TRUE);
-                }
-            }
-        } else if (u->stream_restore_used) {
-            /* We want to reload module-stream-restore */
-            if (!pa_module_load(u->core, "module-stream-restore", ""))
-                pa_log_warn("Failed to load module-stream-restore while disabling role-based device-priority routing");
-        }
 
         break;
     }

commit 678d8e963d7a9ce4ecd27f38ba49112b6b7663d4
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Sun Sep 20 17:34:17 2009 +0100

    device-manager: Add a function to get a list of the highest priority device indexes for each role.

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index 38b4c02..f4d00b5 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -402,6 +402,87 @@ static char *get_name(const char *key, const char *prefix) {
     return t;
 }
 
+static role_indexes_t *get_highest_priority_device_indexes(struct userdata *u, const char *prefix) {
+    role_indexes_t *indexes, highest_priority_available;
+    pa_datum key;
+    pa_bool_t done;
+
+    pa_assert(u);
+    pa_assert(prefix);
+
+    indexes = pa_xnew(role_indexes_t, 1);
+    for (uint32_t i = 0; i < NUM_ROLES; ++i) {
+        *indexes[i] = PA_INVALID_INDEX;
+    }
+    pa_zero(highest_priority_available);
+
+    done = !pa_database_first(u->database, &key, NULL);
+
+    /* Find all existing devices with the same prefix so we find the highest priority device for each role */
+    while (!done) {
+        pa_datum next_key;
+
+        done = !pa_database_next(u->database, &key, &next_key, NULL);
+
+        if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
+            char *name;
+            struct entry *e;
+
+            name = pa_xstrndup(key.data, key.size);
+
+            if ((e = read_entry(u, name))) {
+                for (uint32_t i = 0; i < NUM_ROLES; ++i) {
+                    if (highest_priority_available[i] && e->priority[i] < highest_priority_available[i]) {
+                        /* We've found a device with a higher priority than that we've currently got,
+                           so see if it is currently available or not and update our list */
+                        uint32_t idx;
+                        pa_bool_t found = FALSE;
+                        char *device_name = get_name(name, prefix);
+
+                        if (strcmp(prefix, "sink:") == 0) {
+                            pa_sink *sink;
+
+                            PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
+                                if (strcmp(sink->name, device_name) == 0) {
+                                    found = TRUE;
+                                    idx = sink->index; /* Is this needed? */
+                                    break;
+                                }
+                            }
+                        } else {
+                            pa_source *source;
+
+                            PA_IDXSET_FOREACH(source, u->core->sources, idx) {
+                                if (strcmp(source->name, device_name) == 0) {
+                                    found = TRUE;
+                                    idx = source->index; /* Is this needed? */
+                                    break;
+                                }
+                            }
+                        }
+                        if (found) {
+                            highest_priority_available[i] = e->priority[i];
+                            *indexes[i] = idx;
+                        }
+
+                        pa_xfree(device_name);
+                    }
+                }
+
+                pa_xfree(e);
+            }
+
+            pa_xfree(name);
+        }
+
+        pa_datum_free(&key);
+        key = next_key;
+    }
+
+    return indexes;
+}
+
+
 static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
     char *name;
     struct entry *e;

commit 74c1c27eaac2e9d40902442f4b3671e12499494b
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Sun Sep 20 18:08:40 2009 +0100

    device-manager: Add routing functions that are triggered when sinks/soruces are added/removed.

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index f4d00b5..87588ae 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -402,6 +402,30 @@ static char *get_name(const char *key, const char *prefix) {
     return t;
 }
 
+static uint32_t get_role_index(const char* role) {
+    pa_assert(role);
+
+    if (strcmp(role, "") == 0)
+        return ROLE_NONE;
+    if (strcmp(role, "video") == 0)
+        return ROLE_VIDEO;
+    if (strcmp(role, "music") == 0)
+        return ROLE_MUSIC;
+    if (strcmp(role, "game") == 0)
+        return ROLE_GAME;
+    if (strcmp(role, "event") == 0)
+        return ROLE_EVENT;
+    if (strcmp(role, "phone") == 0)
+        return ROLE_PHONE;
+    if (strcmp(role, "animation") == 0)
+        return ROLE_ANIMATION;
+    if (strcmp(role, "production") == 0)
+        return ROLE_PRODUCTION;
+    if (strcmp(role, "a11y") == 0)
+        return ROLE_A11Y;
+    return PA_INVALID_INDEX;
+}
+
 static role_indexes_t *get_highest_priority_device_indexes(struct userdata *u, const char *prefix) {
     role_indexes_t *indexes, highest_priority_available;
     pa_datum key;
@@ -484,9 +508,6 @@ static role_indexes_t *get_highest_priority_device_indexes(struct userdata *u, c
 
 
 static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
-    char *name;
-    struct entry *e;
-
     pa_assert(c);
     pa_assert(new_data);
     pa_assert(u);
@@ -510,9 +531,6 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
 }
 
 static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
-    char *name;
-    struct entry *e;
-
     pa_assert(c);
     pa_assert(new_data);
     pa_assert(u);
@@ -538,80 +556,161 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou
     return PA_HOOK_OK;
 }
 
-static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
+static pa_hook_result_t reroute_sinks(struct userdata *u) {
     pa_sink_input *si;
+    role_indexes_t *indexes;
     uint32_t idx;
 
-    pa_assert(c);
-    pa_assert(sink);
     pa_assert(u);
-    pa_assert(u->on_hotplug);
 
     if (!u->role_device_priority_routing)
         return PA_HOOK_OK;
 
-    /** @todo Ensure redo the routing based on the priorities */
+    pa_assert_se(indexes = get_highest_priority_device_indexes(u, "sink:"));
+
+    PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
+        const char *role;
+        uint32_t role_index, device_index;
+        pa_sink *sink;
+
+        if (si->save_sink)
+            continue;
+
+        /* Skip this if it is already in the process of being moved
+        * anyway */
+        if (!si->sink)
+            continue;
+
+        /* It might happen that a stream and a sink are set up at the
+        same time, in which case we want to make sure we don't
+        interfere with that */
+        if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si)))
+            continue;
+
+        if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE)))
+            role_index = get_role_index("");
+        else
+            role_index = get_role_index(role);
+
+        if (PA_INVALID_INDEX == role_index)
+            continue;
+
+        device_index = *indexes[role_index];
+        if (PA_INVALID_INDEX == device_index)
+            continue;
+
+        if (!(sink = pa_idxset_get_by_index(u->core->sinks, device_index)))
+            continue;
+
+        if (si->sink != sink)
+            pa_sink_input_move_to(si, sink, TRUE);
+    }
+
+    pa_xfree(indexes);
 
     return PA_HOOK_OK;
 }
 
-static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
+static pa_hook_result_t reroute_sources(struct userdata *u) {
     pa_source_output *so;
+    role_indexes_t *indexes;
     uint32_t idx;
 
-    pa_assert(c);
-    pa_assert(source);
     pa_assert(u);
-    pa_assert(u->on_hotplug);
 
     if (!u->role_device_priority_routing)
         return PA_HOOK_OK;
 
-    /** @todo Ensure redo the routing based on the priorities */
+    pa_assert_se(indexes = get_highest_priority_device_indexes(u, "source:"));
+
+    PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) {
+        const char *role;
+        uint32_t role_index, device_index;
+        pa_source *source;
+
+        if (so->save_source)
+            continue;
+
+        if (so->direct_on_input)
+            continue;
+
+        /* Skip this if it is already in the process of being moved
+        * anyway */
+        if (!so->source)
+            continue;
+
+        /* It might happen that a stream and a source are set up at the
+        same time, in which case we want to make sure we don't
+        interfere with that */
+        if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so)))
+            continue;
+
+        if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE)))
+            role_index = get_role_index("");
+        else
+            role_index = get_role_index(role);
+
+        if (PA_INVALID_INDEX == role_index)
+            continue;
+
+        device_index = *indexes[role_index];
+        if (PA_INVALID_INDEX == device_index)
+            continue;
+
+        if (!(source = pa_idxset_get_by_index(u->core->sources, device_index)))
+            continue;
+
+        if (so->source != source)
+            pa_source_output_move_to(so, source, TRUE);
+    }
+
+    pa_xfree(indexes);
 
     return PA_HOOK_OK;
 }
 
-static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
-    pa_sink_input *si;
-    uint32_t idx;
+static pa_hook_result_t sink_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink *sink, struct userdata *u) {
+    pa_assert(c);
+    pa_assert(u);
+    pa_assert(u->core == c);
+    pa_assert(u->on_hotplug);
+
+    return reroute_sinks(u);
+}
+
+static pa_hook_result_t source_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_source *source, struct userdata *u) {
+    pa_assert(c);
+    pa_assert(u);
+    pa_assert(u->core == c);
+    pa_assert(u->on_hotplug);
 
+    return reroute_sources(u);
+}
+
+static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink *sink, struct userdata *u) {
     pa_assert(c);
-    pa_assert(sink);
     pa_assert(u);
+    pa_assert(u->core == c);
     pa_assert(u->on_rescue);
 
     /* There's no point in doing anything if the core is shut down anyway */
     if (c->state == PA_CORE_SHUTDOWN)
         return PA_HOOK_OK;
 
-    if (!u->role_device_priority_routing)
-        return PA_HOOK_OK;
-
-    /** @todo Ensure redo the routing based on the priorities */
-
-    return PA_HOOK_OK;
+    return reroute_sinks(u);
 }
 
-static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
-    pa_source_output *so;
-    uint32_t idx;
-
+static pa_hook_result_t source_unlink_hook_callback(pa_core *c, PA_GCC_UNUSED pa_source *source, struct userdata *u) {
     pa_assert(c);
-    pa_assert(source);
     pa_assert(u);
+    pa_assert(u->core == c);
     pa_assert(u->on_rescue);
 
     /* There's no point in doing anything if the core is shut down anyway */
     if (c->state == PA_CORE_SHUTDOWN)
         return PA_HOOK_OK;
 
-    if (!u->role_device_priority_routing)
-        return PA_HOOK_OK;
-
-    /** @todo Ensure redo the routing based on the priorities */
-
-    return PA_HOOK_OK;
+    return reroute_sinks(u);
 }
 
 
@@ -655,30 +754,6 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
 }
 
 
-static uint32_t get_role_index(const char* role) {
-    pa_assert(role);
-
-    if (strcmp(role, "") == 0)
-        return ROLE_NONE;
-    if (strcmp(role, "video") == 0)
-        return ROLE_VIDEO;
-    if (strcmp(role, "music") == 0)
-        return ROLE_MUSIC;
-    if (strcmp(role, "game") == 0)
-        return ROLE_GAME;
-    if (strcmp(role, "event") == 0)
-        return ROLE_EVENT;
-    if (strcmp(role, "phone") == 0)
-        return ROLE_PHONE;
-    if (strcmp(role, "animation") == 0)
-        return ROLE_ANIMATION;
-    if (strcmp(role, "production") == 0)
-        return ROLE_PRODUCTION;
-    if (strcmp(role, "a11y") == 0)
-        return ROLE_A11Y;
-    return PA_INVALID_INDEX;
-}
-
 #define EXT_VERSION 1
 
 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {

commit 1d04c353ea61f47961c2f25aff20b75c602e8c93
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Sun Sep 20 18:23:52 2009 +0100

    device-manager: Set the most appropriate sink/source when new streams are created

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index 87588ae..5c3c395 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -515,17 +515,34 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
     if (!u->role_device_priority_routing)
         return PA_HOOK_OK;
 
-    /*if (!(name = get_name(new_data->proplist, "sink-input")))
-        return PA_HOOK_OK;
-
     if (new_data->sink)
-        pa_log_debug("Not restoring device for stream %s, because already set.", name);
-    else if ((e = read_entry(u, name))) {
+        pa_log_debug("Not restoring device for stream, because already set.");
+    else {
+        const char *role;
+        uint32_t role_index;
 
-        pa_xfree(e);
-    }
+        if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
+            role_index = get_role_index("");
+        else
+            role_index = get_role_index(role);
+
+        if (PA_INVALID_INDEX != role_index) {
+            role_indexes_t *indexes;
+            uint32_t device_index;
+
+            pa_assert_se(indexes = get_highest_priority_device_indexes(u, "sink:"));
+
+            device_index = *indexes[role_index];
+            if (PA_INVALID_INDEX != device_index) {
+                pa_sink *sink;
 
-    pa_xfree(name);*/
+                if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) {
+                    new_data->sink = sink;
+                    new_data->save_sink = TRUE;
+                }
+            }
+        }
+    }
 
     return PA_HOOK_OK;
 }
@@ -541,17 +558,34 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou
     if (new_data->direct_on_input)
         return PA_HOOK_OK;
 
-    /*if (!(name = get_name(new_data->proplist, "source-output")))
-        return PA_HOOK_OK;
-
     if (new_data->source)
-        pa_log_debug("Not restoring device for stream %s, because already set", name);
-    else if ((e = read_entry(u, name))) {
+        pa_log_debug("Not restoring device for stream, because already set");
+    else {
+        const char *role;
+        uint32_t role_index;
 
-        pa_xfree(e);
-    }
+        if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
+            role_index = get_role_index("");
+        else
+            role_index = get_role_index(role);
+
+        if (PA_INVALID_INDEX != role_index) {
+            role_indexes_t *indexes;
+            uint32_t device_index;
+
+            pa_assert_se(indexes = get_highest_priority_device_indexes(u, "source:"));
+
+            device_index = *indexes[role_index];
+            if (PA_INVALID_INDEX != device_index) {
+                pa_source *source;
 
-    pa_xfree(name);*/
+                if ((source = pa_idxset_get_by_index(u->core->sources, device_index))) {
+                    new_data->source = source;
+                    new_data->save_source = TRUE;
+                }
+            }
+        }
+    }
 
     return PA_HOOK_OK;
 }

commit 4fb9dafaf8cf844ecf981d243d35a2d57b780275
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Sun Sep 20 18:24:51 2009 +0100

    device-manager: Remove unused variables

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index 5c3c395..ec981bb 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -914,9 +914,6 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
     case SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING: {
 
         pa_bool_t enable;
-        uint32_t sridx = PA_INVALID_INDEX;
-        uint32_t idx;
-        pa_module *module;
 
         if (pa_tagstruct_get_boolean(t, &enable) < 0)
             goto fail;

commit e47f385b09b2bd6e4c5dd2ba4c1b93a213ec1b5c
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Sun Sep 20 18:31:10 2009 +0100

    device-manager: Allow the routing component to be turned on via a module argument as well as via protocol extn.

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index ec981bb..fba4ebe 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -59,12 +59,14 @@ PA_MODULE_DESCRIPTION("Keep track of devices (and their descriptions) both past
 PA_MODULE_VERSION(PACKAGE_VERSION);
 PA_MODULE_LOAD_ONCE(TRUE);
 PA_MODULE_USAGE(
+    "do_routing=<Automatically route streams based on a priority list (unique per-role)?> "
     "on_hotplug=<When new device becomes available, recheck streams?> "
     "on_rescue=<When device becomes unavailable, recheck streams?>");
 
 #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
 
 static const char* const valid_modargs[] = {
+    "do_routing",
     "on_hotplug",
     "on_rescue",
     NULL
@@ -92,7 +94,7 @@ struct userdata {
 
     pa_bool_t on_hotplug;
     pa_bool_t on_rescue;
-    pa_bool_t role_device_priority_routing;
+    pa_bool_t do_routing;
 };
 
 #define ENTRY_VERSION 1
@@ -512,7 +514,7 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
     pa_assert(new_data);
     pa_assert(u);
 
-    if (!u->role_device_priority_routing)
+    if (!u->do_routing)
         return PA_HOOK_OK;
 
     if (new_data->sink)
@@ -552,7 +554,7 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou
     pa_assert(new_data);
     pa_assert(u);
 
-    if (!u->role_device_priority_routing)
+    if (!u->do_routing)
         return PA_HOOK_OK;
 
     if (new_data->direct_on_input)
@@ -597,7 +599,7 @@ static pa_hook_result_t reroute_sinks(struct userdata *u) {
 
     pa_assert(u);
 
-    if (!u->role_device_priority_routing)
+    if (!u->do_routing)
         return PA_HOOK_OK;
 
     pa_assert_se(indexes = get_highest_priority_device_indexes(u, "sink:"));
@@ -610,8 +612,7 @@ static pa_hook_result_t reroute_sinks(struct userdata *u) {
         if (si->save_sink)
             continue;
 
-        /* Skip this if it is already in the process of being moved
-        * anyway */
+        /* Skip this if it is already in the process of being moved anyway */
         if (!si->sink)
             continue;
 
@@ -652,7 +653,7 @@ static pa_hook_result_t reroute_sources(struct userdata *u) {
 
     pa_assert(u);
 
-    if (!u->role_device_priority_routing)
+    if (!u->do_routing)
         return PA_HOOK_OK;
 
     pa_assert_se(indexes = get_highest_priority_device_indexes(u, "source:"));
@@ -668,8 +669,7 @@ static pa_hook_result_t reroute_sources(struct userdata *u) {
         if (so->direct_on_input)
             continue;
 
-        /* Skip this if it is already in the process of being moved
-        * anyway */
+        /* Skip this if it is already in the process of being moved anyway */
         if (!so->source)
             continue;
 
@@ -918,7 +918,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
         if (pa_tagstruct_get_boolean(t, &enable) < 0)
             goto fail;
 
-        u->role_device_priority_routing = enable;
+        u->do_routing = enable;
 
         break;
     }
@@ -1082,7 +1082,7 @@ int pa__init(pa_module*m) {
     pa_sink_input *si;
     pa_source_output *so;
     uint32_t idx;
-    pa_bool_t on_hotplug = TRUE, on_rescue = TRUE;
+    pa_bool_t do_routing = FALSE, on_hotplug = TRUE, on_rescue = TRUE;
 
     pa_assert(m);
 
@@ -1091,7 +1091,8 @@ int pa__init(pa_module*m) {
         goto fail;
     }
 
-    if (pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
+    if (pa_modargs_get_value_boolean(ma, "do_routing", &do_routing) < 0 ||
+        pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
         pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) {
         pa_log("on_hotplug= and on_rescue= expect boolean arguments");
         goto fail;
@@ -1100,6 +1101,7 @@ int pa__init(pa_module*m) {
     m->userdata = u = pa_xnew0(struct userdata, 1);
     u->core = m->core;
     u->module = m;
+    u->do_routing = do_routing;
     u->on_hotplug = on_hotplug;
     u->on_rescue = on_rescue;
     u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);

commit 9e447978eb9ed246762a07e52466384580834566
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Sun Sep 20 18:39:50 2009 +0100

    device-manager: Some efficiency and safety tweaks

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index fba4ebe..5abcc75 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -431,7 +431,7 @@ static uint32_t get_role_index(const char* role) {
 static role_indexes_t *get_highest_priority_device_indexes(struct userdata *u, const char *prefix) {
     role_indexes_t *indexes, highest_priority_available;
     pa_datum key;
-    pa_bool_t done;
+    pa_bool_t done, sink_mode;
 
     pa_assert(u);
     pa_assert(prefix);
@@ -442,6 +442,8 @@ static role_indexes_t *get_highest_priority_device_indexes(struct userdata *u, c
     }
     pa_zero(highest_priority_available);
 
+    sink_mode = (strcmp(prefix, "sink:") == 0);
+
     done = !pa_database_first(u->database, &key, NULL);
 
     /* Find all existing devices with the same prefix so we find the highest priority device for each role */
@@ -465,7 +467,7 @@ static role_indexes_t *get_highest_priority_device_indexes(struct userdata *u, c
                         pa_bool_t found = FALSE;
                         char *device_name = get_name(name, prefix);
 
-                        if (strcmp(prefix, "sink:") == 0) {
+                        if (sink_mode) {
                             pa_sink *sink;
 
                             PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
@@ -944,15 +946,18 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
         if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) {
             pa_datum key, data;
             pa_bool_t done;
-            char* prefix;
+            char* prefix = NULL;
             uint32_t priority;
             pa_bool_t haschanged = FALSE;
 
             if (strncmp(device, "sink:", 5) == 0)
                 prefix = pa_xstrdup("sink:");
-            else
+            else if (strncmp(device, "source:", 7) == 0)
                 prefix = pa_xstrdup("source:");
 
+            if (!prefix)
+                goto fail;
+
             priority = e->priority[role_index];
 
             /* Now we need to load up all the other entries of this type and shuffle the priroities around */

commit 1e2d236b9984dbc25080b5a97add86043a524ea4
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Sun Sep 20 19:18:22 2009 +0100

    device-manager: Update exports

diff --git a/src/map-file b/src/map-file
index cec688f..d7b341d 100644
--- a/src/map-file
+++ b/src/map-file
@@ -144,12 +144,15 @@ pa_cvolume_set_fade;
 pa_cvolume_set_position;
 pa_cvolume_snprint;
 pa_cvolume_valid;
+pa_ext_device_manager_defer_device;
 pa_ext_device_manager_delete;
+pa_ext_device_manager_enable_role_device_priority_routing;
+pa_ext_device_manager_prefer_device;
 pa_ext_device_manager_read;
+pa_ext_device_manager_set_device_description;
 pa_ext_device_manager_set_subscribe_cb;
 pa_ext_device_manager_subscribe;
 pa_ext_device_manager_test;
-pa_ext_device_manager_write;
 pa_ext_stream_restore_delete;
 pa_ext_stream_restore_read;
 pa_ext_stream_restore_set_subscribe_cb;

commit ce0b2bdc0718cfaec58d9809bd97a123a9fe07a4
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Sun Sep 20 19:30:31 2009 +0100

    device-manager: Fix the database write mode

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index 5abcc75..3f4418a 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -877,7 +877,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
             data.data = e;
             data.size = sizeof(*e);
 
-            if (pa_database_set(u->database, &key, &data, FALSE) == 0) {
+            if (pa_database_set(u->database, &key, &data, TRUE) == 0) {
                 apply_entry(u, device, e);
 
                 trigger_save(u);
@@ -995,7 +995,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
                             data.data = e2;
                             data.size = sizeof(*e2);
 
-                            if (pa_database_set(u->database, &key, &data, FALSE))
+                            if (pa_database_set(u->database, &key, &data, TRUE))
                                 pa_log_warn("Could not save device");
                         }
 
@@ -1022,7 +1022,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
                 data.data = e;
                 data.size = sizeof(*e);
 
-                if (pa_database_set(u->database, &key, &data, FALSE))
+                if (pa_database_set(u->database, &key, &data, TRUE))
                     pa_log_warn("Could not save device");
 
                 trigger_save(u);

commit 0016b5e2655ec8e5a415d02bf3ccb97c641a60bb
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Sun Sep 20 20:34:52 2009 +0100

    device-manager: Keep a cache of the highest priority devices for each role.
    
    Rather than querying our database on every new stream, we keep a cache and only update it when a sink/source is added/removed.

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index 3f4418a..3f3f4a1 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -72,6 +72,21 @@ static const char* const valid_modargs[] = {
     NULL
 };
 
+#define NUM_ROLES 9
+enum {
+    ROLE_NONE,
+    ROLE_VIDEO,
+    ROLE_MUSIC,
+    ROLE_GAME,
+    ROLE_EVENT,
+    ROLE_PHONE,
+    ROLE_ANIMATION,
+    ROLE_PRODUCTION,
+    ROLE_A11Y,
+};
+
+typedef uint32_t role_indexes_t[NUM_ROLES];
+
 struct userdata {
     pa_core *core;
     pa_module *module;
@@ -95,24 +110,12 @@ struct userdata {
     pa_bool_t on_hotplug;
     pa_bool_t on_rescue;
     pa_bool_t do_routing;
-};
 
-#define ENTRY_VERSION 1
-
-#define NUM_ROLES 9
-enum {
-    ROLE_NONE,
-    ROLE_VIDEO,
-    ROLE_MUSIC,
-    ROLE_GAME,
-    ROLE_EVENT,
-    ROLE_PHONE,
-    ROLE_ANIMATION,
-    ROLE_PRODUCTION,
-    ROLE_A11Y,
+    role_indexes_t preferred_sinks;
+    role_indexes_t preferred_sources;
 };
 
-typedef uint32_t role_indexes_t[NUM_ROLES];
+#define ENTRY_VERSION 1
 
 struct entry {
     uint8_t version;
@@ -211,6 +214,7 @@ static void trigger_save(struct userdata *u) {
 }
 
 static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
+    /** @todo: Compare the priority lists too */
     if (strncmp(a->description, b->description, sizeof(a->description)))
         return FALSE;
 
@@ -428,7 +432,7 @@ static uint32_t get_role_index(const char* role) {
     return PA_INVALID_INDEX;
 }
 
-static role_indexes_t *get_highest_priority_device_indexes(struct userdata *u, const char *prefix) {
+static void update_highest_priority_device_indexes(struct userdata *u, const char *prefix, void *ignore_device) {
     role_indexes_t *indexes, highest_priority_available;
     pa_datum key;
     pa_bool_t done, sink_mode;
@@ -436,14 +440,18 @@ static role_indexes_t *get_highest_priority_device_indexes(struct userdata *u, c
     pa_assert(u);
     pa_assert(prefix);
 
-    indexes = pa_xnew(role_indexes_t, 1);
+    sink_mode = (strcmp(prefix, "sink:") == 0);
+
+    if (sink_mode)
+        indexes = &u->preferred_sinks;
+    else
+        indexes = &u->preferred_sources;
+
     for (uint32_t i = 0; i < NUM_ROLES; ++i) {
         *indexes[i] = PA_INVALID_INDEX;
     }
     pa_zero(highest_priority_available);
 
-    sink_mode = (strcmp(prefix, "sink:") == 0);
-
     done = !pa_database_first(u->database, &key, NULL);
 
     /* Find all existing devices with the same prefix so we find the highest priority device for each role */
@@ -471,6 +479,8 @@ static role_indexes_t *get_highest_priority_device_indexes(struct userdata *u, c
                             pa_sink *sink;
 
                             PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
+                                if ((pa_sink*) ignore_device == sink)
+                                    continue;
                                 if (strcmp(sink->name, device_name) == 0) {
                                     found = TRUE;
                                     idx = sink->index; /* Is this needed? */
@@ -481,6 +491,8 @@ static role_indexes_t *get_highest_priority_device_indexes(struct userdata *u, c
                             pa_source *source;
 
                             PA_IDXSET_FOREACH(source, u->core->sources, idx) {
+                                if ((pa_source*) ignore_device == source)
+                                    continue;
                                 if (strcmp(source->name, device_name) == 0) {
                                     found = TRUE;
                                     idx = source->index; /* Is this needed? */
@@ -506,8 +518,6 @@ static role_indexes_t *get_highest_priority_device_indexes(struct userdata *u, c
         pa_datum_free(&key);
         key = next_key;
     }
-
-    return indexes;
 }
 
 
@@ -531,12 +541,9 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
             role_index = get_role_index(role);
 
         if (PA_INVALID_INDEX != role_index) {
-            role_indexes_t *indexes;
             uint32_t device_index;
 
-            pa_assert_se(indexes = get_highest_priority_device_indexes(u, "sink:"));
-
-            device_index = *indexes[role_index];
+            device_index = u->preferred_sinks[role_index];
             if (PA_INVALID_INDEX != device_index) {
                 pa_sink *sink;
 
@@ -574,12 +581,9 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou
             role_index = get_role_index(role);
 
         if (PA_INVALID_INDEX != role_index) {
-            role_indexes_t *indexes;
             uint32_t device_index;
 
-            pa_assert_se(indexes = get_highest_priority_device_indexes(u, "source:"));
-
-            device_index = *indexes[role_index];
+            device_index = u->preferred_sources[role_index];
             if (PA_INVALID_INDEX != device_index) {
                 pa_source *source;
 
@@ -594,9 +598,8 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou
     return PA_HOOK_OK;
 }
 
-static pa_hook_result_t reroute_sinks(struct userdata *u) {
+static pa_hook_result_t reroute_sinks(struct userdata *u, pa_sink *ignore_sink) {
     pa_sink_input *si;
-    role_indexes_t *indexes;
     uint32_t idx;
 
     pa_assert(u);
@@ -604,7 +607,7 @@ static pa_hook_result_t reroute_sinks(struct userdata *u) {
     if (!u->do_routing)
         return PA_HOOK_OK;
 
-    pa_assert_se(indexes = get_highest_priority_device_indexes(u, "sink:"));
+    update_highest_priority_device_indexes(u, "sink:", ignore_sink);
 
     PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
         const char *role;
@@ -632,7 +635,7 @@ static pa_hook_result_t reroute_sinks(struct userdata *u) {
         if (PA_INVALID_INDEX == role_index)
             continue;
 
-        device_index = *indexes[role_index];
+        device_index = u->preferred_sinks[role_index];
         if (PA_INVALID_INDEX == device_index)
             continue;
 
@@ -643,14 +646,11 @@ static pa_hook_result_t reroute_sinks(struct userdata *u) {
             pa_sink_input_move_to(si, sink, TRUE);
     }
 
-    pa_xfree(indexes);
-
     return PA_HOOK_OK;
 }
 
-static pa_hook_result_t reroute_sources(struct userdata *u) {
+static pa_hook_result_t reroute_sources(struct userdata *u, pa_source* ignore_source) {
     pa_source_output *so;
-    role_indexes_t *indexes;
     uint32_t idx;
 
     pa_assert(u);
@@ -658,7 +658,7 @@ static pa_hook_result_t reroute_sources(struct userdata *u) {
     if (!u->do_routing)
         return PA_HOOK_OK;
 
-    pa_assert_se(indexes = get_highest_priority_device_indexes(u, "source:"));
+    update_highest_priority_device_indexes(u, "source:", ignore_source);
 
     PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) {
         const char *role;
@@ -689,7 +689,7 @@ static pa_hook_result_t reroute_sources(struct userdata *u) {
         if (PA_INVALID_INDEX == role_index)
             continue;
 
-        device_index = *indexes[role_index];
+        device_index = u->preferred_sources[role_index];
         if (PA_INVALID_INDEX == device_index)
             continue;
 
@@ -700,8 +700,6 @@ static pa_hook_result_t reroute_sources(struct userdata *u) {
             pa_source_output_move_to(so, source, TRUE);
     }
 
-    pa_xfree(indexes);
-
     return PA_HOOK_OK;
 }
 
@@ -711,7 +709,7 @@ static pa_hook_result_t sink_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink
     pa_assert(u->core == c);
     pa_assert(u->on_hotplug);
 
-    return reroute_sinks(u);
+    return reroute_sinks(u, NULL);
 }
 
 static pa_hook_result_t source_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_source *source, struct userdata *u) {
@@ -720,11 +718,12 @@ static pa_hook_result_t source_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_so
     pa_assert(u->core == c);
     pa_assert(u->on_hotplug);
 
-    return reroute_sources(u);
+    return reroute_sources(u, NULL);
 }
 
-static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink *sink, struct userdata *u) {
+static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
     pa_assert(c);
+    pa_assert(sink);
     pa_assert(u);
     pa_assert(u->core == c);
     pa_assert(u->on_rescue);
@@ -733,11 +732,12 @@ static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, PA_GCC_UNUSED pa_s
     if (c->state == PA_CORE_SHUTDOWN)
         return PA_HOOK_OK;
 
-    return reroute_sinks(u);
+    return reroute_sinks(u, sink);
 }
 
-static pa_hook_result_t source_unlink_hook_callback(pa_core *c, PA_GCC_UNUSED pa_source *source, struct userdata *u) {
+static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
     pa_assert(c);
+    pa_assert(source);
     pa_assert(u);
     pa_assert(u->core == c);
     pa_assert(u->on_rescue);
@@ -746,7 +746,7 @@ static pa_hook_result_t source_unlink_hook_callback(pa_core *c, PA_GCC_UNUSED pa
     if (c->state == PA_CORE_SHUTDOWN)
         return PA_HOOK_OK;
 
-    return reroute_sinks(u);
+    return reroute_sources(u, source);
 }
 
 
@@ -1151,12 +1151,17 @@ int pa__init(pa_module*m) {
     pa_log_info("Sucessfully opened database file '%s'.", fname);
     pa_xfree(fname);
 
+    /* We cycle over all the available sinks so that they are added to our database if they are not in it yet */
     PA_IDXSET_FOREACH(sink, m->core->sinks, idx)
         subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW, sink->index, u);
 
     PA_IDXSET_FOREACH(source, m->core->sources, idx)
         subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
 
+    /* Update our caches (all available devices will be present in our database now */
+    update_highest_priority_device_indexes(u, "sink:", NULL);
+    update_highest_priority_device_indexes(u, "source:", NULL);
+
     PA_IDXSET_FOREACH(si, m->core->sink_inputs, idx)
         subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, si->index, u);
 

commit 25f75342d4e3e2885d751b9625a8eda4c0c1380c
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Sun Sep 20 20:48:58 2009 +0100

    device-manager: Reroute the streams on startup and update our cache on enable.

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index 3f3f4a1..745961c 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -920,7 +920,11 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
         if (pa_tagstruct_get_boolean(t, &enable) < 0)
             goto fail;
 
-        u->do_routing = enable;
+        if ((u->do_routing = enable)) {
+            /* Update our caches */
+            update_highest_priority_device_indexes(u, "sink:", NULL);
+            update_highest_priority_device_indexes(u, "source:", NULL);
+        }
 
         break;
     }
@@ -1158,15 +1162,9 @@ int pa__init(pa_module*m) {
     PA_IDXSET_FOREACH(source, m->core->sources, idx)
         subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
 
-    /* Update our caches (all available devices will be present in our database now */
-    update_highest_priority_device_indexes(u, "sink:", NULL);
-    update_highest_priority_device_indexes(u, "source:", NULL);
-
-    PA_IDXSET_FOREACH(si, m->core->sink_inputs, idx)
-        subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, si->index, u);
-
-    PA_IDXSET_FOREACH(so, m->core->source_outputs, idx)
-        subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, so->index, u);
+    /* Perform the routing (if it's enabled) which will update our priority list cache too */
+    reroute_sinks(u, NULL);
+    reroute_sources(u, NULL);
 
     pa_modargs_free(ma);
     return 0;

commit 8d0787c1d5a0552e52ad91c2a6f0630f96216a69
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Sun Sep 20 20:50:23 2009 +0100

    device-manager: More sensible names for internal functions

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index 745961c..c2bc041 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -598,7 +598,7 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou
     return PA_HOOK_OK;
 }
 
-static pa_hook_result_t reroute_sinks(struct userdata *u, pa_sink *ignore_sink) {
+static pa_hook_result_t route_sink_inputs(struct userdata *u, pa_sink *ignore_sink) {
     pa_sink_input *si;
     uint32_t idx;
 
@@ -649,7 +649,7 @@ static pa_hook_result_t reroute_sinks(struct userdata *u, pa_sink *ignore_sink)
     return PA_HOOK_OK;
 }
 
-static pa_hook_result_t reroute_sources(struct userdata *u, pa_source* ignore_source) {
+static pa_hook_result_t route_source_outputs(struct userdata *u, pa_source* ignore_source) {
     pa_source_output *so;
     uint32_t idx;
 
@@ -709,7 +709,7 @@ static pa_hook_result_t sink_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink
     pa_assert(u->core == c);
     pa_assert(u->on_hotplug);
 
-    return reroute_sinks(u, NULL);
+    return route_sink_inputs(u, NULL);
 }
 
 static pa_hook_result_t source_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_source *source, struct userdata *u) {
@@ -718,7 +718,7 @@ static pa_hook_result_t source_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_so
     pa_assert(u->core == c);
     pa_assert(u->on_hotplug);
 
-    return reroute_sources(u, NULL);
+    return route_source_outputs(u, NULL);
 }
 
 static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
@@ -732,7 +732,7 @@ static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, str
     if (c->state == PA_CORE_SHUTDOWN)
         return PA_HOOK_OK;
 
-    return reroute_sinks(u, sink);
+    return route_sink_inputs(u, sink);
 }
 
 static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
@@ -746,7 +746,7 @@ static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *sourc
     if (c->state == PA_CORE_SHUTDOWN)
         return PA_HOOK_OK;
 
-    return reroute_sources(u, source);
+    return route_source_outputs(u, source);
 }
 
 
@@ -1088,8 +1088,6 @@ int pa__init(pa_module*m) {
     char *fname;
     pa_sink *sink;
     pa_source *source;
-    pa_sink_input *si;
-    pa_source_output *so;
     uint32_t idx;
     pa_bool_t do_routing = FALSE, on_hotplug = TRUE, on_rescue = TRUE;
 
@@ -1163,8 +1161,8 @@ int pa__init(pa_module*m) {
         subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
 
     /* Perform the routing (if it's enabled) which will update our priority list cache too */
-    reroute_sinks(u, NULL);
-    reroute_sources(u, NULL);
+    route_sink_inputs(u, NULL);
+    route_source_outputs(u, NULL);
 
     pa_modargs_free(ma);
     return 0;

commit d3460e349893f6dc75eda1f03ee7690b0911dcc2
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Sun Sep 20 20:57:34 2009 +0100

    device-manager: Refactor the routing method to allow the routing of a single stream

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index c2bc041..d672197 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -598,6 +598,47 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou
     return PA_HOOK_OK;
 }
 
+
+static void route_sink_input(struct userdata *u, pa_sink_input *si) {
+    const char *role;
+    uint32_t role_index, device_index;
+    pa_sink *sink;
+
+    pa_assert(u);
+    pa_assert(u->do_routing);
+
+    if (si->save_sink)
+        return;
+
+    /* Skip this if it is already in the process of being moved anyway */
+    if (!si->sink)
+        return;
+
+    /* It might happen that a stream and a sink are set up at the
+    same time, in which case we want to make sure we don't
+    interfere with that */
+    if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si)))
+        return;
+
+    if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE)))
+        role_index = get_role_index("");
+    else
+        role_index = get_role_index(role);
+
+    if (PA_INVALID_INDEX == role_index)
+        return;
+
+    device_index = u->preferred_sinks[role_index];
+    if (PA_INVALID_INDEX == device_index)
+        return;
+
+    if (!(sink = pa_idxset_get_by_index(u->core->sinks, device_index)))
+        return;
+
+    if (si->sink != sink)
+        pa_sink_input_move_to(si, sink, TRUE);
+}
+
 static pa_hook_result_t route_sink_inputs(struct userdata *u, pa_sink *ignore_sink) {
     pa_sink_input *si;
     uint32_t idx;
@@ -610,43 +651,53 @@ static pa_hook_result_t route_sink_inputs(struct userdata *u, pa_sink *ignore_si
     update_highest_priority_device_indexes(u, "sink:", ignore_sink);
 
     PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
-        const char *role;
-        uint32_t role_index, device_index;
-        pa_sink *sink;
+        route_sink_input(u, si);
+    }
 
-        if (si->save_sink)
-            continue;
+    return PA_HOOK_OK;
+}
 
-        /* Skip this if it is already in the process of being moved anyway */
-        if (!si->sink)
-            continue;
+static void route_source_output(struct userdata *u, pa_source_output *so) {
+    const char *role;
+    uint32_t role_index, device_index;
+    pa_source *source;
 
-        /* It might happen that a stream and a sink are set up at the
-        same time, in which case we want to make sure we don't
-        interfere with that */
-        if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si)))
-            continue;
+    pa_assert(u);
+    pa_assert(u->do_routing);
 
-        if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE)))
-            role_index = get_role_index("");
-        else
-            role_index = get_role_index(role);
+    if (so->save_source)
+        return;
 
-        if (PA_INVALID_INDEX == role_index)
-            continue;
+    if (so->direct_on_input)
+        return;
 
-        device_index = u->preferred_sinks[role_index];
-        if (PA_INVALID_INDEX == device_index)
-            continue;
+    /* Skip this if it is already in the process of being moved anyway */
+    if (!so->source)
+        return;
 
-        if (!(sink = pa_idxset_get_by_index(u->core->sinks, device_index)))
-            continue;
+    /* It might happen that a stream and a source are set up at the
+    same time, in which case we want to make sure we don't
+    interfere with that */
+    if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so)))
+        return;
 
-        if (si->sink != sink)
-            pa_sink_input_move_to(si, sink, TRUE);
-    }
+    if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE)))
+        role_index = get_role_index("");
+    else
+        role_index = get_role_index(role);
 
-    return PA_HOOK_OK;
+    if (PA_INVALID_INDEX == role_index)
+        return;
+
+    device_index = u->preferred_sources[role_index];
+    if (PA_INVALID_INDEX == device_index)
+        return;
+
+    if (!(source = pa_idxset_get_by_index(u->core->sources, device_index)))
+        return;
+
+    if (so->source != source)
+        pa_source_output_move_to(so, source, TRUE);
 }
 
 static pa_hook_result_t route_source_outputs(struct userdata *u, pa_source* ignore_source) {
@@ -661,43 +712,7 @@ static pa_hook_result_t route_source_outputs(struct userdata *u, pa_source* igno
     update_highest_priority_device_indexes(u, "source:", ignore_source);
 
     PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) {
-        const char *role;
-        uint32_t role_index, device_index;
-        pa_source *source;
-
-        if (so->save_source)
-            continue;
-
-        if (so->direct_on_input)
-            continue;
-
-        /* Skip this if it is already in the process of being moved anyway */
-        if (!so->source)
-            continue;
-
-        /* It might happen that a stream and a source are set up at the
-        same time, in which case we want to make sure we don't
-        interfere with that */
-        if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so)))
-            continue;
-
-        if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE)))
-            role_index = get_role_index("");
-        else
-            role_index = get_role_index(role);
-
-        if (PA_INVALID_INDEX == role_index)
-            continue;
-
-        device_index = u->preferred_sources[role_index];
-        if (PA_INVALID_INDEX == device_index)
-            continue;
-
-        if (!(source = pa_idxset_get_by_index(u->core->sources, device_index)))
-            continue;
-
-        if (so->source != source)
-            pa_source_output_move_to(so, source, TRUE);
+        route_source_output(u, so);
     }
 
     return PA_HOOK_OK;

commit 1d43230006e639170fbdbfa8797f2ba783ae5ca2
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Sun Sep 20 21:19:41 2009 +0100

    device-manager: Reroute streams when they change allowing the media.role to be updated mid-stream.
    
    We do not handle the _EVENT_NEW subscription here as the PA_CORE_HOOK_SINK_INPUT_NEW/PA_CORE_HOOK_SOURCE_OUTPUT_NEW
    hook should handle the initial routing.

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index d672197..72b473c 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -221,6 +221,16 @@ static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
     return TRUE;
 }
 
+static char *get_name(const char *key, const char *prefix) {
+    char *t;
+
+    if (strncmp(key, prefix, strlen(prefix)))
+        return NULL;
+
+    t = pa_xstrdup(key + strlen(prefix));
+    return t;
+}
+
 static inline struct entry *load_or_initialize_entry(struct userdata *u, struct entry *entry, const char *name, const char *prefix) {
     struct entry *old;
 
@@ -275,139 +285,6 @@ static inline struct entry *load_or_initialize_entry(struct userdata *u, struct
     return old;
 }
 
-static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
-    struct userdata *u = userdata;
-    struct entry entry, *old = NULL;
-    char *name = NULL;
-    pa_datum key, data;
-
-    pa_assert(c);
-    pa_assert(u);
-
-    if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
-        t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
-        t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
-        t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE))
-        return;
-
-    pa_zero(entry);
-    entry.version = ENTRY_VERSION;
-
-    if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
-        pa_sink *sink;
-
-        if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
-            return;
-
-        name = pa_sprintf_malloc("sink:%s", sink->name);
-
-        old = load_or_initialize_entry(u, &entry, name, "sink:");
-
-        pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
-
-    } else {
-        pa_source *source;
-
-        pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
-
-        if (!(source = pa_idxset_get_by_index(c->sources, idx)))
-            return;
-
-        if (source->monitor_of)
-            return;
-
-        name = pa_sprintf_malloc("source:%s", source->name);
-
-        old = load_or_initialize_entry(u, &entry, name, "source:");
-
-        pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
-    }
-
-    if (old) {
-
-        if (entries_equal(old, &entry)) {
-            pa_xfree(old);
-            pa_xfree(name);
-            return;
-        }
-
-        pa_xfree(old);
-    }
-
-    key.data = name;
-    key.size = strlen(name);
-
-    data.data = &entry;
-    data.size = sizeof(entry);
-
-    pa_log_info("Storing device %s.", name);
-
-    pa_database_set(u->database, &key, &data, TRUE);
-
-    pa_xfree(name);
-
-    trigger_save(u);
-}
-
-static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
-    char *name;
-    struct entry *e;
-
-    pa_assert(c);
-    pa_assert(new_data);
-    pa_assert(u);
-
-    name = pa_sprintf_malloc("sink:%s", new_data->name);
-
-    if ((e = read_entry(u, name))) {
-        if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
-            pa_log_info("Restoring description for sink %s.", new_data->name);
-            pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
-        }
-
-        pa_xfree(e);
-    }
-
-    pa_xfree(name);
-
-    return PA_HOOK_OK;
-}
-
-static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
-    char *name;
-    struct entry *e;
-
-    pa_assert(c);
-    pa_assert(new_data);
-    pa_assert(u);
-
-    name = pa_sprintf_malloc("source:%s", new_data->name);
-
-    if ((e = read_entry(u, name))) {
-        if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
-            /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
-            pa_log_info("Restoring description for source %s.", new_data->name);
-            pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
-        }
-
-        pa_xfree(e);
-    }
-
-    pa_xfree(name);
-
-    return PA_HOOK_OK;
-}
-
-static char *get_name(const char *key, const char *prefix) {
-    char *t;
-
-    if (strncmp(key, prefix, strlen(prefix)))
-        return NULL;
-
-    t = pa_xstrdup(key + strlen(prefix));
-    return t;
-}
-
 static uint32_t get_role_index(const char* role) {
     pa_assert(role);
 
@@ -521,84 +398,6 @@ static void update_highest_priority_device_indexes(struct userdata *u, const cha
 }
 
 
-static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
-    pa_assert(c);
-    pa_assert(new_data);
-    pa_assert(u);
-
-    if (!u->do_routing)
-        return PA_HOOK_OK;
-
-    if (new_data->sink)
-        pa_log_debug("Not restoring device for stream, because already set.");
-    else {
-        const char *role;
-        uint32_t role_index;
-
-        if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
-            role_index = get_role_index("");
-        else
-            role_index = get_role_index(role);
-
-        if (PA_INVALID_INDEX != role_index) {
-            uint32_t device_index;
-
-            device_index = u->preferred_sinks[role_index];
-            if (PA_INVALID_INDEX != device_index) {
-                pa_sink *sink;
-
-                if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) {
-                    new_data->sink = sink;
-                    new_data->save_sink = TRUE;
-                }
-            }
-        }
-    }
-
-    return PA_HOOK_OK;
-}
-
-static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
-    pa_assert(c);
-    pa_assert(new_data);
-    pa_assert(u);
-
-    if (!u->do_routing)
-        return PA_HOOK_OK;
-
-    if (new_data->direct_on_input)
-        return PA_HOOK_OK;
-
-    if (new_data->source)
-        pa_log_debug("Not restoring device for stream, because already set");
-    else {
-        const char *role;
-        uint32_t role_index;
-
-        if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
-            role_index = get_role_index("");
-        else
-            role_index = get_role_index(role);
-
-        if (PA_INVALID_INDEX != role_index) {
-            uint32_t device_index;
-
-            device_index = u->preferred_sources[role_index];
-            if (PA_INVALID_INDEX != device_index) {
-                pa_source *source;
-
-                if ((source = pa_idxset_get_by_index(u->core->sources, device_index))) {
-                    new_data->source = source;
-                    new_data->save_source = TRUE;
-                }
-            }
-        }
-    }
-
-    return PA_HOOK_OK;
-}
-
-
 static void route_sink_input(struct userdata *u, pa_sink_input *si) {
     const char *role;
     uint32_t role_index, device_index;
@@ -718,6 +517,238 @@ static pa_hook_result_t route_source_outputs(struct userdata *u, pa_source* igno
     return PA_HOOK_OK;
 }
 
+static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
+    struct userdata *u = userdata;
+    struct entry entry, *old = NULL;
+    char *name = NULL;
+    pa_datum key, data;
+
+    pa_assert(c);
+    pa_assert(u);
+
+    if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
+        t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE) &&
+        t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW) &&
+        t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE) &&
+
+        /*t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
+        t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
+        /*t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW) &&*/
+        t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
+        return;
+
+    pa_zero(entry);
+    entry.version = ENTRY_VERSION;
+
+    if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
+        pa_sink_input *si;
+
+        if (!u->do_routing)
+            return;
+        if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx)))
+            return;
+
+        /* The role may change mid-stream, so we reroute */
+        route_sink_input(u, si);
+
+        return;
+    } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT) {
+        pa_source_output *so;
+
+        if (!u->do_routing)
+            return;
+        if (!(so = pa_idxset_get_by_index(c->source_outputs, idx)))
+            return;
+
+        /* The role may change mid-stream, so we reroute */
+        route_source_output(u, so);
+
+        return;
+    } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
+        pa_sink *sink;
+
+        if (!(sink = pa_idxset_get_by_index(c->sinks, idx)))
+            return;
+
+        name = pa_sprintf_malloc("sink:%s", sink->name);
+
+        old = load_or_initialize_entry(u, &entry, name, "sink:");
+
+        pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
+
+    } else  if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) {
+        pa_source *source;
+
+        pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE);
+
+        if (!(source = pa_idxset_get_by_index(c->sources, idx)))
+            return;
+
+        if (source->monitor_of)
+            return;
+
+        name = pa_sprintf_malloc("source:%s", source->name);
+
+        old = load_or_initialize_entry(u, &entry, name, "source:");
+
+        pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
+    }
+
+    pa_assert(name);
+
+    if (old) {
+
+        if (entries_equal(old, &entry)) {
+            pa_xfree(old);
+            pa_xfree(name);
+            return;
+        }
+
+        pa_xfree(old);
+    }
+
+    key.data = name;
+    key.size = strlen(name);
+
+    data.data = &entry;
+    data.size = sizeof(entry);
+
+    pa_log_info("Storing device %s.", name);
+
+    pa_database_set(u->database, &key, &data, TRUE);
+
+    pa_xfree(name);
+
+    trigger_save(u);
+}
+
+static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
+    char *name;
+    struct entry *e;
+
+    pa_assert(c);
+    pa_assert(new_data);
+    pa_assert(u);
+
+    name = pa_sprintf_malloc("sink:%s", new_data->name);
+
+    if ((e = read_entry(u, name))) {
+        if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
+            pa_log_info("Restoring description for sink %s.", new_data->name);
+            pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
+        }
+
+        pa_xfree(e);
+    }
+
+    pa_xfree(name);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
+    char *name;
+    struct entry *e;
+
+    pa_assert(c);
+    pa_assert(new_data);
+    pa_assert(u);
+
+    name = pa_sprintf_malloc("source:%s", new_data->name);
+
+    if ((e = read_entry(u, name))) {
+        if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
+            /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
+            pa_log_info("Restoring description for source %s.", new_data->name);
+            pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
+        }
+
+        pa_xfree(e);
+    }
+
+    pa_xfree(name);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
+    pa_assert(c);
+    pa_assert(new_data);
+    pa_assert(u);
+
+    if (!u->do_routing)
+        return PA_HOOK_OK;
+
+    if (new_data->sink)
+        pa_log_debug("Not restoring device for stream, because already set.");
+    else {
+        const char *role;
+        uint32_t role_index;
+
+        if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
+            role_index = get_role_index("");
+        else
+            role_index = get_role_index(role);
+
+        if (PA_INVALID_INDEX != role_index) {
+            uint32_t device_index;
+
+            device_index = u->preferred_sinks[role_index];
+            if (PA_INVALID_INDEX != device_index) {
+                pa_sink *sink;
+
+                if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) {
+                    new_data->sink = sink;
+                    new_data->save_sink = TRUE;
+                }
+            }
+        }
+    }
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
+    pa_assert(c);
+    pa_assert(new_data);
+    pa_assert(u);
+
+    if (!u->do_routing)
+        return PA_HOOK_OK;
+
+    if (new_data->direct_on_input)
+        return PA_HOOK_OK;
+
+    if (new_data->source)
+        pa_log_debug("Not restoring device for stream, because already set");
+    else {
+        const char *role;
+        uint32_t role_index;
+
+        if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
+            role_index = get_role_index("");
+        else
+            role_index = get_role_index(role);
+
+        if (PA_INVALID_INDEX != role_index) {
+            uint32_t device_index;
+
+            device_index = u->preferred_sources[role_index];
+            if (PA_INVALID_INDEX != device_index) {
+                pa_source *source;
+
+                if ((source = pa_idxset_get_by_index(u->core->sources, device_index))) {
+                    new_data->source = source;
+                    new_data->save_source = TRUE;
+                }
+            }
+        }
+    }
+
+    return PA_HOOK_OK;
+}
+
+
 static pa_hook_result_t sink_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink *sink, struct userdata *u) {
     pa_assert(c);
     pa_assert(u);
@@ -1133,7 +1164,7 @@ int pa__init(pa_module*m) {
 
     u->connection_unlink_hook_slot = pa_hook_connect(&pa_native_protocol_hooks(u->protocol)[PA_NATIVE_HOOK_CONNECTION_UNLINK], PA_HOOK_NORMAL, (pa_hook_cb_t) connection_unlink_hook_cb, u);
 
-    u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE, subscribe_callback, u);
+    u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE|PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscribe_callback, u);
 
     /* Used to handle device description management */
     u->sink_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) sink_new_hook_callback, u);

commit 4dedba73a6183fbc9eb51d0577a6dfd402f1e135
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Mon Sep 21 18:50:00 2009 +0100

    device-manager: Add a function to dump the database which we do whenever we save it (and on startup)

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index 72b473c..baff560 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -55,7 +55,7 @@
 #include "module-device-manager-symdef.h"
 
 PA_MODULE_AUTHOR("Colin Guthrie");
-PA_MODULE_DESCRIPTION("Keep track of devices (and their descriptions) both past and present");
+PA_MODULE_DESCRIPTION("Keep track of devices (and their descriptions) both past and present and prioritise by role");
 PA_MODULE_VERSION(PACKAGE_VERSION);
 PA_MODULE_LOAD_ONCE(TRUE);
 PA_MODULE_USAGE(
@@ -64,6 +64,7 @@ PA_MODULE_USAGE(
     "on_rescue=<When device becomes unavailable, recheck streams?>");
 
 #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
+#define DUMP_DATABASE
 
 static const char* const valid_modargs[] = {
     "do_routing",
@@ -135,20 +136,6 @@ enum {
     SUBCOMMAND_EVENT
 };
 
-static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
-    struct userdata *u = userdata;
-
-    pa_assert(a);
-    pa_assert(e);
-    pa_assert(u);
-
-    pa_assert(e == u->save_time_event);
-    u->core->mainloop->time_free(u->save_time_event);
-    u->save_time_event = NULL;
-
-    pa_database_sync(u->database);
-    pa_log_info("Synced.");
-}
 
 static struct entry* read_entry(struct userdata *u, const char *name) {
     pa_datum key, data;
@@ -190,6 +177,107 @@ fail:
     return NULL;
 }
 
+#ifdef DUMP_DATABASE
+static void dump_database_helper(struct userdata *u, uint32_t role_index, const char* human, pa_bool_t sink_mode) {
+    pa_assert(u);
+    pa_assert(human);
+
+    if (sink_mode) {
+        pa_sink *s;
+        if (PA_INVALID_INDEX != u->preferred_sinks[role_index] && (s = pa_idxset_get_by_index(u->core->sinks, u->preferred_sinks[role_index])))
+            pa_log_debug("   %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name);
+        else
+            pa_log_debug("   %s No sink specified", human);
+    } else {
+        pa_source *s;
+        if (PA_INVALID_INDEX != u->preferred_sinks[role_index] && (s = pa_idxset_get_by_index(u->core->sinks, u->preferred_sinks[role_index])))
+            pa_log_debug("   %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name);
+        else
+            pa_log_debug("   %s No source specified", human);
+    }
+}
+
+static void dump_database(struct userdata *u) {
+    pa_datum key;
+    pa_bool_t done;
+
+    pa_assert(u);
+
+    done = !pa_database_first(u->database, &key, NULL);
+
+    pa_log_debug("Dumping database");
+    while (!done) {
+        char *name;
+        struct entry *e;
+        pa_datum next_key;
+
+        done = !pa_database_next(u->database, &key, &next_key, NULL);
+
+        name = pa_xstrndup(key.data, key.size);
+
+        if ((e = read_entry(u, name))) {
+            pa_log_debug(" Got entry: %s", name);
+            pa_log_debug("  Description: %s", e->description);
+            pa_log_debug("  Priorities: None:   %3u, Video: %3u, Music:  %3u, Game: %3u, Event: %3u",
+                         e->priority[ROLE_NONE], e->priority[ROLE_VIDEO], e->priority[ROLE_MUSIC], e->priority[ROLE_GAME], e->priority[ROLE_EVENT]);
+            pa_log_debug("              Phone:  %3u, Anim:  %3u, Prodtn: %3u, A11y: %3u",
+                         e->priority[ROLE_PHONE], e->priority[ROLE_ANIMATION], e->priority[ROLE_PRODUCTION], e->priority[ROLE_A11Y]);
+            pa_xfree(e);
+        }
+
+        pa_xfree(name);
+
+        pa_datum_free(&key);
+        key = next_key;
+    }
+
+    pa_log_debug(" Highest priority devices per-role:");
+
+    pa_log_debug("  Sinks:");
+    dump_database_helper(u, ROLE_NONE, "None:  ", TRUE);
+    dump_database_helper(u, ROLE_NONE, "Video: ", TRUE);
+    dump_database_helper(u, ROLE_NONE, "Music: ", TRUE);
+    dump_database_helper(u, ROLE_NONE, "Game:  ", TRUE);
+    dump_database_helper(u, ROLE_NONE, "Event: ", TRUE);
+    dump_database_helper(u, ROLE_NONE, "Phone: ", TRUE);
+    dump_database_helper(u, ROLE_NONE, "Anim:  ", TRUE);
+    dump_database_helper(u, ROLE_NONE, "Prodtn:", TRUE);
+    dump_database_helper(u, ROLE_NONE, "Ally:  ", TRUE);
+
+    pa_log_debug("  Sources:");
+    dump_database_helper(u, ROLE_NONE, "None:  ", FALSE);
+    dump_database_helper(u, ROLE_NONE, "Video: ", FALSE);
+    dump_database_helper(u, ROLE_NONE, "Music: ", FALSE);
+    dump_database_helper(u, ROLE_NONE, "Game:  ", FALSE);
+    dump_database_helper(u, ROLE_NONE, "Event: ", FALSE);
+    dump_database_helper(u, ROLE_NONE, "Phone: ", FALSE);
+    dump_database_helper(u, ROLE_NONE, "Anim:  ", FALSE);
+    dump_database_helper(u, ROLE_NONE, "Prodtn:", FALSE);
+    dump_database_helper(u, ROLE_NONE, "Ally:  ", FALSE);
+
+    pa_log_debug("Completed database dump");
+}
+#endif
+
+static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(a);
+    pa_assert(e);
+    pa_assert(u);
+
+    pa_assert(e == u->save_time_event);
+    u->core->mainloop->time_free(u->save_time_event);
+    u->save_time_event = NULL;
+
+    pa_database_sync(u->database);
+    pa_log_info("Synced.");
+
+#ifdef DUMP_DATABASE
+    dump_database(u);
+#endif
+}
+
 static void trigger_save(struct userdata *u) {
     pa_native_connection *c;
     uint32_t idx;
@@ -1210,6 +1298,10 @@ int pa__init(pa_module*m) {
     route_sink_inputs(u, NULL);
     route_source_outputs(u, NULL);
 
+#ifdef DUMP_DATABASE
+    dump_database(u);
+#endif
+
     pa_modargs_free(ma);
     return 0;
 

commit 8b2cc4def30327a72d95365c671d2adcae1a77a8
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Sat Sep 26 14:36:36 2009 +0100

    device-manager: Expose the priority lists in the protocol extension.
    
    Also leave space for 'icon' and 'available' details too, althought currently this info is dummy.

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index baff560..8a3a6f8 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -88,6 +88,18 @@ enum {
 
 typedef uint32_t role_indexes_t[NUM_ROLES];
 
+static const char* role_names[NUM_ROLES] = {
+    "none",
+    "video",
+    "music",
+    "game",
+    "event",
+    "phone",
+    "animation",
+    "production",
+    "a11y",
+};
+
 struct userdata {
     pa_core *core;
     pa_module *module;
@@ -234,26 +246,28 @@ static void dump_database(struct userdata *u) {
     pa_log_debug(" Highest priority devices per-role:");
 
     pa_log_debug("  Sinks:");
-    dump_database_helper(u, ROLE_NONE, "None:  ", TRUE);
-    dump_database_helper(u, ROLE_NONE, "Video: ", TRUE);
-    dump_database_helper(u, ROLE_NONE, "Music: ", TRUE);
-    dump_database_helper(u, ROLE_NONE, "Game:  ", TRUE);
-    dump_database_helper(u, ROLE_NONE, "Event: ", TRUE);
-    dump_database_helper(u, ROLE_NONE, "Phone: ", TRUE);
-    dump_database_helper(u, ROLE_NONE, "Anim:  ", TRUE);
-    dump_database_helper(u, ROLE_NONE, "Prodtn:", TRUE);
-    dump_database_helper(u, ROLE_NONE, "Ally:  ", TRUE);
+    for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
+        char name[13];
+        uint32_t len = PA_MAX(12u, strlen(role_names[role]));
+        for (int i = 0; i < 12; ++i) name[i] = ' ';
+        strncpy(name, role_names[role], len);
+        name[len] = ':';
+        name[0] -= 32;
+        name[12] = '\0';
+        dump_database_helper(u, role, name, TRUE);
+    }
 
     pa_log_debug("  Sources:");
-    dump_database_helper(u, ROLE_NONE, "None:  ", FALSE);
-    dump_database_helper(u, ROLE_NONE, "Video: ", FALSE);
-    dump_database_helper(u, ROLE_NONE, "Music: ", FALSE);
-    dump_database_helper(u, ROLE_NONE, "Game:  ", FALSE);
-    dump_database_helper(u, ROLE_NONE, "Event: ", FALSE);
-    dump_database_helper(u, ROLE_NONE, "Phone: ", FALSE);
-    dump_database_helper(u, ROLE_NONE, "Anim:  ", FALSE);
-    dump_database_helper(u, ROLE_NONE, "Prodtn:", FALSE);
-    dump_database_helper(u, ROLE_NONE, "Ally:  ", FALSE);
+    for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
+        char name[13];
+        uint32_t len = PA_MAX(12u, strlen(role_names[role]));
+        for (int i = 0; i < 12; ++i) name[i] = ' ';
+        strncpy(name, role_names[role], len);
+        name[len] = ':';
+        name[0] -= 32;
+        name[12] = '\0';
+        dump_database_helper(u, role, name, FALSE);
+    }
 
     pa_log_debug("Completed database dump");
 }
@@ -378,22 +392,10 @@ static uint32_t get_role_index(const char* role) {
 
     if (strcmp(role, "") == 0)
         return ROLE_NONE;
-    if (strcmp(role, "video") == 0)
-        return ROLE_VIDEO;
-    if (strcmp(role, "music") == 0)
-        return ROLE_MUSIC;
-    if (strcmp(role, "game") == 0)
-        return ROLE_GAME;
-    if (strcmp(role, "event") == 0)
-        return ROLE_EVENT;
-    if (strcmp(role, "phone") == 0)
-        return ROLE_PHONE;
-    if (strcmp(role, "animation") == 0)
-        return ROLE_ANIMATION;
-    if (strcmp(role, "production") == 0)
-        return ROLE_PRODUCTION;
-    if (strcmp(role, "a11y") == 0)
-        return ROLE_A11Y;
+    for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i)
+        if (strcmp(role, role_names[i]) == 0)
+            return i;
+
     return PA_INVALID_INDEX;
 }
 
@@ -974,10 +976,18 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
         pa_datum_free(&key);
 
         if ((e = read_entry(u, name))) {
-          pa_tagstruct_puts(reply, name);
-          pa_tagstruct_puts(reply, e->description);
+            pa_tagstruct_puts(reply, name);
+            pa_tagstruct_puts(reply, e->description);
+            pa_tagstruct_puts(reply, "audio-card"); /** @todo store the icon */
+            pa_tagstruct_put_boolean(reply, TRUE); /** @todo show current available */
+            pa_tagstruct_putu32(reply, NUM_ROLES);
+
+            for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i) {
+                pa_tagstruct_puts(reply, role_names[i]);
+                pa_tagstruct_putu32(reply, e->priority[i]);
+            }
 
-          pa_xfree(e);
+            pa_xfree(e);
         }
 
         pa_xfree(name);
diff --git a/src/pulse/ext-device-manager.c b/src/pulse/ext-device-manager.c
index bc6301c..01e4594 100644
--- a/src/pulse/ext-device-manager.c
+++ b/src/pulse/ext-device-manager.c
@@ -26,6 +26,7 @@
 
 #include <pulse/context.h>
 #include <pulse/gccmacro.h>
+#include <pulse/xmalloc.h>
 
 #include <pulsecore/macro.h>
 #include <pulsecore/pstream-util.h>
@@ -128,20 +129,48 @@ static void ext_device_manager_read_cb(pa_pdispatch *pd, uint32_t command, uint3
 
         while (!pa_tagstruct_eof(t)) {
             pa_ext_device_manager_info i;
+            pa_bool_t available;
 
             memset(&i, 0, sizeof(i));
+            available = FALSE;
 
             if (pa_tagstruct_gets(t, &i.name) < 0 ||
-                pa_tagstruct_gets(t, &i.description) < 0) {
+                pa_tagstruct_gets(t, &i.description) < 0 ||
+                pa_tagstruct_gets(t, &i.icon) < 0 ||
+                pa_tagstruct_get_boolean(t, &available) < 0 ||
+                pa_tagstruct_getu32(t, &i.n_role_priorities) < 0) {
 
                 pa_context_fail(o->context, PA_ERR_PROTOCOL);
                 goto finish;
             }
+            i.available = (uint8_t)available;
+
+            if (i.n_role_priorities > 0) {
+                uint32_t j;
+                i.role_priorities = pa_xnew0(pa_ext_device_manager_role_priority_info, i.n_role_priorities+1);
+
+                for (j = 0; j < i.n_role_priorities; j++) {
+
+                    if (pa_tagstruct_gets(t, &i.role_priorities[j].role) < 0 ||
+                        pa_tagstruct_getu32(t, &i.role_priorities[j].priority) < 0) {
+
+                        pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                        pa_xfree(i.role_priorities);
+                        goto finish;
+                    }
+                }
+
+                /* Terminate with an extra NULL entry, just to make sure */
+                i.role_priorities[j].role = NULL;
+                i.role_priorities[j].priority = 0;
+            }
 
             if (o->callback) {
                 pa_ext_device_manager_read_cb_t cb = (pa_ext_device_manager_read_cb_t) o->callback;
                 cb(o->context, &i, 0, o->userdata);
             }
+
+            pa_xfree(i.role_priorities);
         }
     }
 
diff --git a/src/pulse/ext-device-manager.h b/src/pulse/ext-device-manager.h
index 686c8d2..bd52331 100644
--- a/src/pulse/ext-device-manager.h
+++ b/src/pulse/ext-device-manager.h
@@ -33,11 +33,20 @@
 
 PA_C_DECL_BEGIN
 
+typedef struct pa_ext_device_manager_role_priority_info {
+    const char *role;
+    uint32_t priority;
+} pa_ext_device_manager_role_priority_info;
+
 /** Stores information about one device in the device database that is
  * maintained by module-device-manager. \since 0.9.19 */
 typedef struct pa_ext_device_manager_info {
     const char *name;            /**< Identifier string of the device. A string like "sink:" or similar followed by the name of the device. */
     const char *description;     /**< The description of the device when it was last seen, if applicable and saved */
+    const char *icon;            /**< The icon given to the device */
+    uint8_t available;           /**< Is the device currently available? */
+    uint32_t n_role_priorities;  /**< How many role priorities do we have? */
+    pa_ext_device_manager_role_priority_info *role_priorities; /**< An array of role priority structures or NULL */
 } pa_ext_device_manager_info;
 
 /** Callback prototype for pa_ext_device_manager_test(). \since 0.9.19 */

commit bbf67019df9a16fa27594054c0df222acd408b12
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Sun Sep 27 03:11:44 2009 +0100

    device-manager: Save icon and report current availability over protocol.
    
    This also ensures we let clients know whenver a sink changes in some capacity.
    Also correct some debug code.

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index 8a3a6f8..0764ad0 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -133,6 +133,7 @@ struct userdata {
 struct entry {
     uint8_t version;
     char description[PA_NAME_MAX];
+    char icon[PA_NAME_MAX];
     role_indexes_t priority;
 } PA_GCC_PACKED;
 
@@ -248,24 +249,20 @@ static void dump_database(struct userdata *u) {
     pa_log_debug("  Sinks:");
     for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
         char name[13];
-        uint32_t len = PA_MAX(12u, strlen(role_names[role]));
-        for (int i = 0; i < 12; ++i) name[i] = ' ';
+        uint32_t len = PA_MIN(12u, strlen(role_names[role]));
         strncpy(name, role_names[role], len);
-        name[len] = ':';
-        name[0] -= 32;
-        name[12] = '\0';
+        for (int i = len+1; i < 12; ++i) name[i] = ' ';
+        name[len] = ':'; name[0] -= 32; name[12] = '\0';
         dump_database_helper(u, role, name, TRUE);
     }
 
     pa_log_debug("  Sources:");
     for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
         char name[13];
-        uint32_t len = PA_MAX(12u, strlen(role_names[role]));
-        for (int i = 0; i < 12; ++i) name[i] = ' ';
+        uint32_t len = PA_MIN(12u, strlen(role_names[role]));
         strncpy(name, role_names[role], len);
-        name[len] = ':';
-        name[0] -= 32;
-        name[12] = '\0';
+        for (int i = len+1; i < 12; ++i) name[i] = ' ';
+        name[len] = ':'; name[0] -= 32; name[12] = '\0';
         dump_database_helper(u, role, name, FALSE);
     }
 
@@ -292,10 +289,13 @@ static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct
 #endif
 }
 
-static void trigger_save(struct userdata *u) {
+static void notify_subscribers(struct userdata *u) {
+
     pa_native_connection *c;
     uint32_t idx;
 
+    pa_assert(u);
+
     for (c = pa_idxset_first(u->subscribed, &idx); c; c = pa_idxset_next(u->subscribed, &idx)) {
         pa_tagstruct *t;
 
@@ -308,6 +308,13 @@ static void trigger_save(struct userdata *u) {
 
         pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t);
     }
+}
+
+static void trigger_save(struct userdata *u) {
+
+    pa_assert(u);
+
+    notify_subscribers(u);
 
     if (u->save_time_event)
         return;
@@ -390,8 +397,6 @@ static inline struct entry *load_or_initialize_entry(struct userdata *u, struct
 static uint32_t get_role_index(const char* role) {
     pa_assert(role);
 
-    if (strcmp(role, "") == 0)
-        return ROLE_NONE;
     for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i)
         if (strcmp(role, role_names[i]) == 0)
             return i;
@@ -510,7 +515,7 @@ static void route_sink_input(struct userdata *u, pa_sink_input *si) {
         return;
 
     if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE)))
-        role_index = get_role_index("");
+        role_index = get_role_index("none");
     else
         role_index = get_role_index(role);
 
@@ -571,7 +576,7 @@ static void route_source_output(struct userdata *u, pa_source_output *so) {
         return;
 
     if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE)))
-        role_index = get_role_index("");
+        role_index = get_role_index("none");
     else
         role_index = get_role_index(role);
 
@@ -665,6 +670,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
         old = load_or_initialize_entry(u, &entry, name, "sink:");
 
         pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
+        pa_strlcpy(entry.icon, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_ICON_NAME)), sizeof(entry.icon));
 
     } else  if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) {
         pa_source *source;
@@ -682,6 +688,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
         old = load_or_initialize_entry(u, &entry, name, "source:");
 
         pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
+        pa_strlcpy(entry.icon, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_ICON_NAME)), sizeof(entry.icon));
     }
 
     pa_assert(name);
@@ -691,6 +698,11 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
         if (entries_equal(old, &entry)) {
             pa_xfree(old);
             pa_xfree(name);
+
+            /* Even if the entries are equal, the availability or otherwise
+               of the sink/source may have changed so we notify clients all the same */
+            notify_subscribers(u);
+
             return;
         }
 
@@ -976,10 +988,34 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
         pa_datum_free(&key);
 
         if ((e = read_entry(u, name))) {
+            uint32_t idx;
+            char *devname;
+            pa_bool_t available = FALSE;
+
+            if ((devname = get_name(name, "sink:"))) {
+                pa_sink* s;
+                PA_IDXSET_FOREACH(s, u->core->sinks, idx) {
+                    if (strcmp(s->name, devname) == 0) {
+                        available = TRUE;
+                        break;
+                    }
+                }
+                pa_xfree(devname);
+            } else if ((devname = get_name(name, "source:"))) {
+                pa_source* s;
+                PA_IDXSET_FOREACH(s, u->core->sources, idx) {
+                    if (strcmp(s->name, devname) == 0) {
+                        available = TRUE;
+                        break;
+                    }
+                }
+                pa_xfree(devname);
+            }
+
             pa_tagstruct_puts(reply, name);
             pa_tagstruct_puts(reply, e->description);
-            pa_tagstruct_puts(reply, "audio-card"); /** @todo store the icon */
-            pa_tagstruct_put_boolean(reply, TRUE); /** @todo show current available */
+            pa_tagstruct_puts(reply, e->icon);
+            pa_tagstruct_put_boolean(reply, available);
             pa_tagstruct_putu32(reply, NUM_ROLES);
 
             for (uint32_t i = ROLE_NONE; i < NUM_ROLES; ++i) {

commit 8977abdc840989975d79d041ffcaf48804d7a52b
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Sun Sep 27 16:55:31 2009 +0100

    device-manager: Don't notify clients on every subscription (it happens all the time).
    
    Also compare the entries fully before saving.

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index 0764ad0..89fb460 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -323,10 +323,18 @@ static void trigger_save(struct userdata *u) {
 }
 
 static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
-    /** @todo: Compare the priority lists too */
-    if (strncmp(a->description, b->description, sizeof(a->description)))
+
+    pa_assert(a);
+    pa_assert(b);
+
+    if (strncmp(a->description, b->description, sizeof(a->description))
+        || strncmp(a->icon, b->icon, sizeof(a->icon)))
         return FALSE;
 
+    for (int i=0; i < NUM_ROLES; ++i)
+        if (a->priority[i] != b->priority[i])
+            return FALSE;
+
     return TRUE;
 }
 
@@ -699,10 +707,6 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
             pa_xfree(old);
             pa_xfree(name);
 
-            /* Even if the entries are equal, the availability or otherwise
-               of the sink/source may have changed so we notify clients all the same */
-            notify_subscribers(u);
-
             return;
         }
 
@@ -857,6 +861,8 @@ static pa_hook_result_t sink_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_sink
     pa_assert(u->core == c);
     pa_assert(u->on_hotplug);
 
+    notify_subscribers(u);
+
     return route_sink_inputs(u, NULL);
 }
 
@@ -866,6 +872,8 @@ static pa_hook_result_t source_put_hook_callback(pa_core *c, PA_GCC_UNUSED pa_so
     pa_assert(u->core == c);
     pa_assert(u->on_hotplug);
 
+    notify_subscribers(u);
+
     return route_source_outputs(u, NULL);
 }
 
@@ -880,6 +888,8 @@ static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, str
     if (c->state == PA_CORE_SHUTDOWN)
         return PA_HOOK_OK;
 
+    notify_subscribers(u);
+
     return route_sink_inputs(u, sink);
 }
 
@@ -894,6 +904,8 @@ static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *sourc
     if (c->state == PA_CORE_SHUTDOWN)
         return PA_HOOK_OK;
 
+    notify_subscribers(u);
+
     return route_source_outputs(u, source);
 }
 

commit f9b2d6500b75445b66c83ad1d6700e042f2f8d2a
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Thu Oct 1 01:27:02 2009 +0100

    device-manager: Change the prefer/defer options to a single 'reorder' command.
    
    We put in the devices from the wire into a hashmap and then add all like type device in the database
    and then order them based on priority (with the ones specified on the wire always being in that order at
    the top of the list.

diff --git a/src/map-file b/src/map-file
index d7b341d..3fc934c 100644
--- a/src/map-file
+++ b/src/map-file
@@ -144,11 +144,10 @@ pa_cvolume_set_fade;
 pa_cvolume_set_position;
 pa_cvolume_snprint;
 pa_cvolume_valid;
-pa_ext_device_manager_defer_device;
 pa_ext_device_manager_delete;
 pa_ext_device_manager_enable_role_device_priority_routing;
-pa_ext_device_manager_prefer_device;
 pa_ext_device_manager_read;
+pa_ext_device_manager_reorder_devices_for_role;
 pa_ext_device_manager_set_device_description;
 pa_ext_device_manager_set_subscribe_cb;
 pa_ext_device_manager_subscribe;
diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index 89fb460..407d76d 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -143,8 +143,7 @@ enum {
     SUBCOMMAND_RENAME,
     SUBCOMMAND_DELETE,
     SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING,
-    SUBCOMMAND_PREFER_DEVICE,
-    SUBCOMMAND_DEFER_DEVICE,
+    SUBCOMMAND_REORDER,
     SUBCOMMAND_SUBSCRIBE,
     SUBCOMMAND_EVENT
 };
@@ -1121,115 +1120,174 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
         break;
     }
 
-    case SUBCOMMAND_PREFER_DEVICE:
-    case SUBCOMMAND_DEFER_DEVICE: {
+    case SUBCOMMAND_REORDER: {
 
-        const char *role, *device;
+        const char *role;
         struct entry *e;
-        uint32_t role_index;
+        uint32_t role_index, n_devices;
+        pa_datum key, data;
+        pa_bool_t done, sink_mode = TRUE;
+        struct device_t { uint32_t prio; char *device; };
+        struct device_t *device;
+        struct device_t **devices;
+        uint32_t i, idx, offset;
+        pa_hashmap *h;
+        pa_bool_t first;
 
         if (pa_tagstruct_gets(t, &role) < 0 ||
-            pa_tagstruct_gets(t, &device) < 0)
-            goto fail;
-
-        if (!role || !device || !*device)
-            goto fail;
-
-        role_index = get_role_index(role);
-        if (PA_INVALID_INDEX == role_index)
+            pa_tagstruct_getu32(t, &n_devices) < 0 ||
+            n_devices < 1)
             goto fail;
 
-        if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) {
-            pa_datum key, data;
-            pa_bool_t done;
-            char* prefix = NULL;
-            uint32_t priority;
-            pa_bool_t haschanged = FALSE;
-
-            if (strncmp(device, "sink:", 5) == 0)
-                prefix = pa_xstrdup("sink:");
-            else if (strncmp(device, "source:", 7) == 0)
-                prefix = pa_xstrdup("source:");
+        if (PA_INVALID_INDEX == (role_index = get_role_index(role)))
+           goto fail;
+
+        /* Cycle through the devices given and make sure they exist */
+        h = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+        first = TRUE;
+        idx = 0;
+        for (i = 0; i < n_devices; ++i) {
+            const char *s;
+            if (pa_tagstruct_gets(t, &s) < 0) {
+                while ((device = pa_hashmap_steal_first(h))) {
+                    pa_xfree(device->device);
+                    pa_xfree(device);
+                }
 
-            if (!prefix)
+                pa_hashmap_free(h, NULL, NULL);
+                pa_log_error("Protocol error on reorder");
                 goto fail;
+            }
 
-            priority = e->priority[role_index];
+            /* Ensure this is a valid entry */
+            if (!(e = read_entry(u, s))) {
+                while ((device = pa_hashmap_steal_first(h))) {
+                    pa_xfree(device->device);
+                    pa_xfree(device);
+                }
 
-            /* Now we need to load up all the other entries of this type and shuffle the priroities around */
+                pa_hashmap_free(h, NULL, NULL);
+                pa_log_error("Client specified an unknown device in it's reorder list.");
+                goto fail;
+            }
+            pa_xfree(e);
 
-            done = !pa_database_first(u->database, &key, NULL);
+            if (first) {
+                first = FALSE;
+                sink_mode = (0 == strncmp("sink:", s, 5));
+            } else if ((sink_mode && 0 != strncmp("sink:", s, 5))
+                       || (!sink_mode && 0 != strncmp("source:", s, 7)))
+            {
+                while ((device = pa_hashmap_steal_first(h))) {
+                    pa_xfree(device->device);
+                    pa_xfree(device);
+                }
 
-            while (!done && !haschanged) {
-                pa_datum next_key;
+                pa_hashmap_free(h, NULL, NULL);
+                pa_log_error("Attempted to reorder mixed devices (sinks and sources)");
+                goto fail;
+            }
 
-                done = !pa_database_next(u->database, &key, &next_key, NULL);
+            /* Add the device to our hashmap. If it's alredy in it, free it now and carry on */
+            device = pa_xnew(struct device_t, 1);
+            device->device = pa_xstrdup(s);
+            if (pa_hashmap_put(h, device->device, device) == 0) {
+                device->prio = idx;
+                idx++;
+            } else {
+                pa_xfree(device->device);
+                pa_xfree(device);
+            }
+        }
 
-                /* Only read devices with the right prefix */
-                if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
-                    char *name;
-                    struct entry *e2;
+        /* Now cycle through our list and add all the devices.
+           This has the effect of addign in any in our DB,
+           not specified in the device list (and thus will be
+           tacked on at the end) */
+        offset = idx;
+        done = !pa_database_first(u->database, &key, NULL);
 
-                    name = pa_xstrndup(key.data, key.size);
+        while (!done && idx < 256) {
+            pa_datum next_key;
 
-                    if ((e2 = read_entry(u, name))) {
-                        if (SUBCOMMAND_PREFER_DEVICE == command) {
-                            /* PREFER */
-                            if (e2->priority[role_index] == (priority - 1)) {
-                                e2->priority[role_index]++;
-                                haschanged = TRUE;
-                            }
-                        } else {
-                            /* DEFER */
-                            if (e2->priority[role_index] == (priority + 1)) {
-                                e2->priority[role_index]--;
-                                haschanged = TRUE;
-                            }
-                        }
+            done = !pa_database_next(u->database, &key, &next_key, NULL);
 
-                        if (haschanged) {
-                            data.data = e2;
-                            data.size = sizeof(*e2);
+            device = pa_xnew(struct device_t, 1);
+            device->device = pa_xstrndup(key.data, key.size);
+            if ((sink_mode && 0 == strncmp("sink:", device->device, 5))
+                || (!sink_mode && 0 == strncmp("source:", device->device, 7))) {
+
+                /* Add the device to our hashmap. If it's alredy in it, free it now and carry on */
+                if (pa_hashmap_put(h, device->device, device) == 0
+                    && (e = read_entry(u, device->device)) && ENTRY_VERSION == e->version) {
+                    /* We add offset on to the existing priorirty so that when we order, the
+                       existing entries are always lower priority than the new ones. */
+                    device->prio = (offset + e->priority[role_index]);
+                    pa_xfree(e);
+                }
+                else {
+                    pa_xfree(device->device);
+                    pa_xfree(device);
+                }
+            } else {
+                pa_xfree(device->device);
+                pa_xfree(device);
+            }
 
-                            if (pa_database_set(u->database, &key, &data, TRUE))
-                                pa_log_warn("Could not save device");
-                        }
+            pa_datum_free(&key);
 
-                        pa_xfree(e2);
-                    }
+            key = next_key;
+        }
 
-                    pa_xfree(name);
+        /* Now we put all the entries in a simple list for sorting it. */
+        n_devices = pa_hashmap_size(h);
+        devices = pa_xnew(struct device_t *,  n_devices);
+        idx = 0;
+        while ((device = pa_hashmap_steal_first(h))) {
+            devices[idx++] = device;
+        }
+        pa_hashmap_free(h, NULL, NULL);
+
+        /* Simple bubble sort */
+        for (i = 0; i < n_devices; ++i) {
+            for (uint32_t j = i; j < n_devices; ++j) {
+                if (devices[i]->prio > devices[j]->prio) {
+                    struct device_t *tmp;
+                    tmp = devices[i];
+                    devices[i] = devices[j];
+                    devices[j] = tmp;
                 }
-
-                pa_datum_free(&key);
-                key = next_key;
             }
+        }
 
-            /* Now write out our actual entry */
-            if (haschanged) {
-                if (SUBCOMMAND_PREFER_DEVICE == command)
-                    e->priority[role_index]--;
-                else
-                    e->priority[role_index]++;
+        /* Go through in order and write the new entry and cleanup our own list */
+        i = 0; idx = 1;
+        first = TRUE;
+        for (i = 0; i < n_devices; ++i) {
+            if ((e = read_entry(u, devices[i]->device)) && ENTRY_VERSION == e->version) {
+                if (e->priority[role_index] != idx) {
+                    e->priority[role_index] = idx;
 
-                key.data = (char *) device;
-                key.size = strlen(device);
+                    key.data = (char *) devices[i]->device;
+                    key.size = strlen(devices[i]->device);
 
-                data.data = e;
-                data.size = sizeof(*e);
+                    data.data = e;
+                    data.size = sizeof(*e);
 
-                if (pa_database_set(u->database, &key, &data, TRUE))
-                    pa_log_warn("Could not save device");
+                    if (pa_database_set(u->database, &key, &data, TRUE) == 0) {
+                        first = FALSE;
+                        idx++;
+                    }
+                }
 
-                trigger_save(u);
+                pa_xfree(e);
             }
-
-            pa_xfree(e);
-
-            pa_xfree(prefix);
+            pa_xfree(devices[i]->device);
+            pa_xfree(devices[i]);
         }
-        else
-            pa_log_warn("Could not reorder device %s, no entry in database", device);
+
+        if (!first)
+            trigger_save(u);
 
         break;
     }
diff --git a/src/pulse/ext-device-manager.c b/src/pulse/ext-device-manager.c
index 01e4594..138ed83 100644
--- a/src/pulse/ext-device-manager.c
+++ b/src/pulse/ext-device-manager.c
@@ -43,8 +43,7 @@ enum {
     SUBCOMMAND_RENAME,
     SUBCOMMAND_DELETE,
     SUBCOMMAND_ROLE_DEVICE_PRIORITY_ROUTING,
-    SUBCOMMAND_PREFER_DEVICE,
-    SUBCOMMAND_DEFER_DEVICE,
+    SUBCOMMAND_REORDER,
     SUBCOMMAND_SUBSCRIBE,
     SUBCOMMAND_EVENT
 };
@@ -330,14 +329,14 @@ pa_operation *pa_ext_device_manager_enable_role_device_priority_routing(
     return o;
 }
 
-pa_operation *pa_ext_device_manager_prefer_device(
+pa_operation *pa_ext_device_manager_reorder_devices_for_role(
         pa_context *c,
         const char* role,
-        const char* device,
+        const char** devices,
         pa_context_success_cb_t cb,
         void *userdata) {
 
-    uint32_t tag;
+    uint32_t tag, i;
     pa_operation *o = NULL;
     pa_tagstruct *t = NULL;
 
@@ -349,52 +348,22 @@ pa_operation *pa_ext_device_manager_prefer_device(
     PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
 
     pa_assert(role);
-    pa_assert(device);
+    pa_assert(devices);
 
     o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
 
     t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
     pa_tagstruct_putu32(t, PA_INVALID_INDEX);
     pa_tagstruct_puts(t, "module-device-manager");
-    pa_tagstruct_putu32(t, SUBCOMMAND_PREFER_DEVICE);
+    pa_tagstruct_putu32(t, SUBCOMMAND_REORDER);
     pa_tagstruct_puts(t, role);
-    pa_tagstruct_puts(t, device);
-
-    pa_pstream_send_tagstruct(c->pstream, t);
-    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
-
-    return o;
-}
-
-pa_operation *pa_ext_device_manager_defer_device(
-        pa_context *c,
-        const char* role,
-        const char* device,
-        pa_context_success_cb_t cb,
-        void *userdata) {
-
-    uint32_t tag;
-    pa_operation *o = NULL;
-    pa_tagstruct *t = NULL;
-
-    pa_assert(c);
-    pa_assert(PA_REFCNT_VALUE(c) >= 1);
-
-    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
-    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
-    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
 
-    pa_assert(role);
-    pa_assert(device);
+    i = 0; while (devices[i]) i++;
+    pa_tagstruct_putu32(t, i);
 
-    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
-
-    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
-    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
-    pa_tagstruct_puts(t, "module-device-manager");
-    pa_tagstruct_putu32(t, SUBCOMMAND_DEFER_DEVICE);
-    pa_tagstruct_puts(t, role);
-    pa_tagstruct_puts(t, device);
+    i = 0;
+    while (devices[i])
+        pa_tagstruct_puts(t, devices[i++]);
 
     pa_pstream_send_tagstruct(c->pstream, t);
     pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
diff --git a/src/pulse/ext-device-manager.h b/src/pulse/ext-device-manager.h
index bd52331..13538f0 100644
--- a/src/pulse/ext-device-manager.h
+++ b/src/pulse/ext-device-manager.h
@@ -97,18 +97,10 @@ pa_operation *pa_ext_device_manager_enable_role_device_priority_routing(
         void *userdata);
 
 /** Prefer a given device in the priority list. \since 0.9.19 */
-pa_operation *pa_ext_device_manager_prefer_device(
+pa_operation *pa_ext_device_manager_reorder_devices_for_role(
         pa_context *c,
         const char* role,
-        const char* device,
-        pa_context_success_cb_t cb,
-        void *userdata);
-
-/** Defer a given device in the priority list. \since 0.9.19 */
-pa_operation *pa_ext_device_manager_defer_device(
-        pa_context *c,
-        const char* role,
-        const char* device,
+        const char** devices,
         pa_context_success_cb_t cb,
         void *userdata);
 

commit 7633bb8a290f1748d8953ce7558b80aa77084140
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Thu Oct 1 09:07:42 2009 +0100

    device-manager: Add extra debug messages

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index 407d76d..00389b6 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -1132,6 +1132,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
         struct device_t **devices;
         uint32_t i, idx, offset;
         pa_hashmap *h;
+        void *state;
         pa_bool_t first;
 
         if (pa_tagstruct_gets(t, &role) < 0 ||
@@ -1200,6 +1201,11 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
             }
         }
 
+        pa_log_debug("Hashmap contents (received from client)");
+        PA_HASHMAP_FOREACH(device, h, state) {
+            pa_log_debug("  - %s (%d)", device->device, device->prio);
+        }
+
         /* Now cycle through our list and add all the devices.
            This has the effect of addign in any in our DB,
            not specified in the device list (and thus will be
@@ -1239,6 +1245,11 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
             key = next_key;
         }
 
+        pa_log_debug("Hashmap contents (combined with database)");
+        PA_HASHMAP_FOREACH(device, h, state) {
+            pa_log_debug("  - %s (%d)", device->device, device->prio);
+        }
+
         /* Now we put all the entries in a simple list for sorting it. */
         n_devices = pa_hashmap_size(h);
         devices = pa_xnew(struct device_t *,  n_devices);
@@ -1260,8 +1271,13 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
             }
         }
 
+        pa_log_debug("Sorted device list");
+        for (i = 0; i < n_devices; ++i) {
+            pa_log_debug("  - %s (%d)", devices[i]->device, devices[i]->prio);
+        }
+
         /* Go through in order and write the new entry and cleanup our own list */
-        i = 0; idx = 1;
+        idx = 1;
         first = TRUE;
         for (i = 0; i < n_devices; ++i) {
             if ((e = read_entry(u, devices[i]->device)) && ENTRY_VERSION == e->version) {
@@ -1274,7 +1290,9 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
                     data.data = e;
                     data.size = sizeof(*e);
 
+                    pa_log_debug("Attempting to write record: %d. %s", e->priority[role_index], e->description);
                     if (pa_database_set(u->database, &key, &data, TRUE) == 0) {
+                        pa_log_debug("..... write successfull");
                         first = FALSE;
                         idx++;
                     }

commit b8a6436d4be0c78405b21dbf6ee192a527713388
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Thu Oct 1 20:13:38 2009 +0100

    device-manager: Fix the writing of the database when priority doesn't change.

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index 00389b6..6b815bd 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -1281,7 +1281,9 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
         first = TRUE;
         for (i = 0; i < n_devices; ++i) {
             if ((e = read_entry(u, devices[i]->device)) && ENTRY_VERSION == e->version) {
-                if (e->priority[role_index] != idx) {
+                if (e->priority[role_index] == idx)
+                    idx++;
+                else {
                     e->priority[role_index] = idx;
 
                     key.data = (char *) devices[i]->device;

commit 3a20cf0b9f69e5f9b1012cc46b0918dd5f8173dd
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Thu Oct 1 21:30:24 2009 +0100

    device-manager: Misc fixes.
    
     * Fix a s/sink/source/ copy paste issue when dumping the database.
     * Only show priority list when routing is enabled (as the list is not updated if not)
     * Fix a memory access issue when finding the highest priority sinks/sources
     * key name->device name efficiency fix.
     * Silence noisy debug on reorder - it seems to work :)
     * Reroute after reordering.
     * Initialise preferred lists to PA_INVALID_INDEX

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index 6b815bd..5b5b6ca 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -202,7 +202,7 @@ static void dump_database_helper(struct userdata *u, uint32_t role_index, const
             pa_log_debug("   %s No sink specified", human);
     } else {
         pa_source *s;
-        if (PA_INVALID_INDEX != u->preferred_sinks[role_index] && (s = pa_idxset_get_by_index(u->core->sinks, u->preferred_sinks[role_index])))
+        if (PA_INVALID_INDEX != u->preferred_sources[role_index] && (s = pa_idxset_get_by_index(u->core->sources, u->preferred_sources[role_index])))
             pa_log_debug("   %s %s (%s)", human, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION)), s->name);
         else
             pa_log_debug("   %s No source specified", human);
@@ -243,26 +243,28 @@ static void dump_database(struct userdata *u) {
         key = next_key;
     }
 
-    pa_log_debug(" Highest priority devices per-role:");
-
-    pa_log_debug("  Sinks:");
-    for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
-        char name[13];
-        uint32_t len = PA_MIN(12u, strlen(role_names[role]));
-        strncpy(name, role_names[role], len);
-        for (int i = len+1; i < 12; ++i) name[i] = ' ';
-        name[len] = ':'; name[0] -= 32; name[12] = '\0';
-        dump_database_helper(u, role, name, TRUE);
-    }
+    if (u->do_routing) {
+        pa_log_debug(" Highest priority devices per-role:");
+
+        pa_log_debug("  Sinks:");
+        for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
+            char name[13];
+            uint32_t len = PA_MIN(12u, strlen(role_names[role]));
+            strncpy(name, role_names[role], len);
+            for (int i = len+1; i < 12; ++i) name[i] = ' ';
+            name[len] = ':'; name[0] -= 32; name[12] = '\0';
+            dump_database_helper(u, role, name, TRUE);
+        }
 
-    pa_log_debug("  Sources:");
-    for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
-        char name[13];
-        uint32_t len = PA_MIN(12u, strlen(role_names[role]));
-        strncpy(name, role_names[role], len);
-        for (int i = len+1; i < 12; ++i) name[i] = ' ';
-        name[len] = ':'; name[0] -= 32; name[12] = '\0';
-        dump_database_helper(u, role, name, FALSE);
+        pa_log_debug("  Sources:");
+        for (uint32_t role = ROLE_NONE; role < NUM_ROLES; ++role) {
+            char name[13];
+            uint32_t len = PA_MIN(12u, strlen(role_names[role]));
+            strncpy(name, role_names[role], len);
+            for (int i = len+1; i < 12; ++i) name[i] = ' ';
+            name[len] = ':'; name[0] -= 32; name[12] = '\0';
+            dump_database_helper(u, role, name, FALSE);
+        }
     }
 
     pa_log_debug("Completed database dump");
@@ -427,7 +429,7 @@ static void update_highest_priority_device_indexes(struct userdata *u, const cha
         indexes = &u->preferred_sources;
 
     for (uint32_t i = 0; i < NUM_ROLES; ++i) {
-        *indexes[i] = PA_INVALID_INDEX;
+        (*indexes)[i] = PA_INVALID_INDEX;
     }
     pa_zero(highest_priority_available);
 
@@ -440,19 +442,19 @@ static void update_highest_priority_device_indexes(struct userdata *u, const cha
         done = !pa_database_next(u->database, &key, &next_key, NULL);
 
         if (key.size > strlen(prefix) && strncmp(key.data, prefix, strlen(prefix)) == 0) {
-            char *name;
+            char *name, *device_name;
             struct entry *e;
 
             name = pa_xstrndup(key.data, key.size);
+            device_name = get_name(name, prefix);
 
-            if ((e = read_entry(u, name))) {
+            if ((e = read_entry(u, name)) && ENTRY_VERSION == e->version) {
                 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
-                    if (highest_priority_available[i] && e->priority[i] < highest_priority_available[i]) {
+                    if (!highest_priority_available[i] || e->priority[i] < highest_priority_available[i]) {
                         /* We've found a device with a higher priority than that we've currently got,
                            so see if it is currently available or not and update our list */
                         uint32_t idx;
                         pa_bool_t found = FALSE;
-                        char *device_name = get_name(name, prefix);
 
                         if (sink_mode) {
                             pa_sink *sink;
@@ -481,10 +483,9 @@ static void update_highest_priority_device_indexes(struct userdata *u, const cha
                         }
                         if (found) {
                             highest_priority_available[i] = e->priority[i];
-                            *indexes[i] = idx;
+                            (*indexes)[i] = idx;
                         }
 
-                        pa_xfree(device_name);
                     }
                 }
 
@@ -492,6 +493,7 @@ static void update_highest_priority_device_indexes(struct userdata *u, const cha
             }
 
             pa_xfree(name);
+            pa_xfree(device_name);
         }
 
         pa_datum_free(&key);
@@ -1132,7 +1134,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
         struct device_t **devices;
         uint32_t i, idx, offset;
         pa_hashmap *h;
-        void *state;
+        /*void *state;*/
         pa_bool_t first;
 
         if (pa_tagstruct_gets(t, &role) < 0 ||
@@ -1201,10 +1203,10 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
             }
         }
 
-        pa_log_debug("Hashmap contents (received from client)");
+        /*pa_log_debug("Hashmap contents (received from client)");
         PA_HASHMAP_FOREACH(device, h, state) {
             pa_log_debug("  - %s (%d)", device->device, device->prio);
-        }
+        }*/
 
         /* Now cycle through our list and add all the devices.
            This has the effect of addign in any in our DB,
@@ -1245,10 +1247,10 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
             key = next_key;
         }
 
-        pa_log_debug("Hashmap contents (combined with database)");
+        /*pa_log_debug("Hashmap contents (combined with database)");
         PA_HASHMAP_FOREACH(device, h, state) {
             pa_log_debug("  - %s (%d)", device->device, device->prio);
-        }
+        }*/
 
         /* Now we put all the entries in a simple list for sorting it. */
         n_devices = pa_hashmap_size(h);
@@ -1271,10 +1273,10 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
             }
         }
 
-        pa_log_debug("Sorted device list");
+        /*pa_log_debug("Sorted device list");
         for (i = 0; i < n_devices; ++i) {
             pa_log_debug("  - %s (%d)", devices[i]->device, devices[i]->prio);
-        }
+        }*/
 
         /* Go through in order and write the new entry and cleanup our own list */
         idx = 1;
@@ -1292,9 +1294,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
                     data.data = e;
                     data.size = sizeof(*e);
 
-                    pa_log_debug("Attempting to write record: %d. %s", e->priority[role_index], e->description);
                     if (pa_database_set(u->database, &key, &data, TRUE) == 0) {
-                        pa_log_debug("..... write successfull");
                         first = FALSE;
                         idx++;
                     }
@@ -1306,9 +1306,15 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
             pa_xfree(devices[i]);
         }
 
-        if (!first)
+        if (!first) {
             trigger_save(u);
 
+            if (sink_mode)
+                route_sink_inputs(u, NULL);
+            else
+                route_source_outputs(u, NULL);
+        }
+
         break;
     }
 
@@ -1431,6 +1437,10 @@ int pa__init(pa_module*m) {
         subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_NEW, source->index, u);
 
     /* Perform the routing (if it's enabled) which will update our priority list cache too */
+    for (uint32_t i = 0; i < NUM_ROLES; ++i) {
+        u->preferred_sinks[i] = u->preferred_sources[i] = PA_INVALID_INDEX;
+    }
+
     route_sink_inputs(u, NULL);
     route_source_outputs(u, NULL);
 

commit 20eedb24163884612c0fe81846ccf2983f336b7c
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Thu Oct 1 22:12:16 2009 +0100

    device-manager: Misc fixes to co-exist with other stream management/routing modules.
    
     * Do not read or set the save_sink/save_source flags. This seems to be for module-stream-restore only...
     * Even if a sink is already set by an earlier module, still move it to the sink we dictate.

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index 5b5b6ca..86ea95d 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -510,9 +510,6 @@ static void route_sink_input(struct userdata *u, pa_sink_input *si) {
     pa_assert(u);
     pa_assert(u->do_routing);
 
-    if (si->save_sink)
-        return;
-
     /* Skip this if it is already in the process of being moved anyway */
     if (!si->sink)
         return;
@@ -568,9 +565,6 @@ static void route_source_output(struct userdata *u, pa_source_output *so) {
     pa_assert(u);
     pa_assert(u->do_routing);
 
-    if (so->save_source)
-        return;
-
     if (so->direct_on_input)
         return;
 
@@ -779,6 +773,9 @@ static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data
 }
 
 static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
+    const char *role;
+    uint32_t role_index;
+
     pa_assert(c);
     pa_assert(new_data);
     pa_assert(u);
@@ -787,27 +784,22 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
         return PA_HOOK_OK;
 
     if (new_data->sink)
-        pa_log_debug("Not restoring device for stream, because already set.");
-    else {
-        const char *role;
-        uint32_t role_index;
+        pa_log_debug("Overriding device for stream, even although it is already set. I am evil that way...");
 
-        if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
-            role_index = get_role_index("");
-        else
-            role_index = get_role_index(role);
+    if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
+        role_index = get_role_index("none");
+    else
+        role_index = get_role_index(role);
 
-        if (PA_INVALID_INDEX != role_index) {
-            uint32_t device_index;
+    if (PA_INVALID_INDEX != role_index) {
+        uint32_t device_index;
 
-            device_index = u->preferred_sinks[role_index];
-            if (PA_INVALID_INDEX != device_index) {
-                pa_sink *sink;
+        device_index = u->preferred_sinks[role_index];
+        if (PA_INVALID_INDEX != device_index) {
+            pa_sink *sink;
 
-                if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) {
-                    new_data->sink = sink;
-                    new_data->save_sink = TRUE;
-                }
+            if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) {
+                new_data->sink = sink;
             }
         }
     }
@@ -816,6 +808,9 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
 }
 
 static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
+    const char *role;
+    uint32_t role_index;
+
     pa_assert(c);
     pa_assert(new_data);
     pa_assert(u);
@@ -827,27 +822,22 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou
         return PA_HOOK_OK;
 
     if (new_data->source)
-        pa_log_debug("Not restoring device for stream, because already set");
-    else {
-        const char *role;
-        uint32_t role_index;
+        pa_log_debug("Overriding device for stream, even although it is already set. I am evil that way...");
 
-        if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
-            role_index = get_role_index("");
-        else
-            role_index = get_role_index(role);
+    if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
+        role_index = get_role_index("none");
+    else
+        role_index = get_role_index(role);
 
-        if (PA_INVALID_INDEX != role_index) {
-            uint32_t device_index;
+    if (PA_INVALID_INDEX != role_index) {
+        uint32_t device_index;
 
-            device_index = u->preferred_sources[role_index];
-            if (PA_INVALID_INDEX != device_index) {
-                pa_source *source;
+        device_index = u->preferred_sources[role_index];
+        if (PA_INVALID_INDEX != device_index) {
+            pa_source *source;
 
-                if ((source = pa_idxset_get_by_index(u->core->sources, device_index))) {
-                    new_data->source = source;
-                    new_data->save_source = TRUE;
-                }
+            if ((source = pa_idxset_get_by_index(u->core->sources, device_index))) {
+                new_data->source = source;
             }
         }
     }

commit 42e28ce31c583ae9d431d2f1dd033d82cc63eb36
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Thu Oct 1 22:33:22 2009 +0100

    device-manager: Add some scripts that are only run under KDE to load/initialise module-device-manager with routing turned on.

diff --git a/src/Makefile.am b/src/Makefile.am
index 8ed7482..3f874f7 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -117,9 +117,11 @@ EXTRA_DIST = \
 		depmod.py \
 		daemon/esdcompat.in \
 		daemon/start-pulseaudio-x11.in \
+		daemon/start-pulseaudio-kde.in \
 		utils/padsp \
 		modules/module-defs.h.m4 \
 		daemon/pulseaudio.desktop.in \
+		daemon/pulseaudio-kde.desktop.in \
 		map-file \
 		daemon/pulseaudio-system.conf \
 		modules/alsa/mixer/profile-sets/default.conf \
@@ -153,7 +155,8 @@ dbuspolicy_DATA = \
 
 if HAVE_X11
 xdgautostart_in_files = \
-		daemon/pulseaudio.desktop.in
+		daemon/pulseaudio.desktop.in \
+		daemon/pulseaudio-kde.desktop.in
 endif
 xdgautostart_DATA = $(xdgautostart_in_files:.desktop.in=.desktop)
 @INTLTOOL_DESKTOP_RULE@
@@ -215,7 +218,7 @@ if HAVE_AVAHI
 bin_PROGRAMS += pabrowse
 endif
 
-bin_SCRIPTS = esdcompat start-pulseaudio-x11
+bin_SCRIPTS = esdcompat start-pulseaudio-x11 start-pulseaudio-kde
 
 pacat_SOURCES = utils/pacat.c
 pacat_LDADD = $(AM_LDADD) libpulse.la libpulsecommon- at PA_MAJORMINORMICRO@.la $(LIBSNDFILE_LIBS)
@@ -1711,7 +1714,7 @@ module_rygel_media_server_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
 #        Some minor stuff         #
 ###################################
 
-CLEANFILES = esdcompat client.conf default.pa system.pa daemon.conf start-pulseaudio-x11 daemon/pulseaudio.desktop
+CLEANFILES = esdcompat client.conf default.pa system.pa daemon.conf start-pulseaudio-x11 start-pulseaudio-kde daemon/pulseaudio.desktop daemon/pulseaudio-kde.desktop
 
 esdcompat: daemon/esdcompat.in Makefile
 	sed -e 's, at PACKAGE_VERSION\@,$(PACKAGE_VERSION),g' \
@@ -1724,6 +1727,11 @@ start-pulseaudio-x11: daemon/start-pulseaudio-x11.in Makefile
 		-e 's, at PACTL_BINARY\@,$(bindir)/pactl,g' < $< > $@
 	chmod +x start-pulseaudio-x11
 
+start-pulseaudio-kde: daemon/start-pulseaudio-kde.in Makefile
+	sed -e 's, at PA_BINARY\@,$(PA_BINARY),g' \
+		-e 's, at PACTL_BINARY\@,$(bindir)/pactl,g' < $< > $@
+	chmod +x start-pulseaudio-kde
+
 client.conf: pulse/client.conf.in Makefile
 	sed -e 's, at PA_BINARY\@,$(PA_BINARY),g' < $< > $@
 
diff --git a/src/daemon/pulseaudio-kde.desktop.in b/src/daemon/pulseaudio-kde.desktop.in
new file mode 100644
index 0000000..0684642
--- /dev/null
+++ b/src/daemon/pulseaudio-kde.desktop.in
@@ -0,0 +1,11 @@
+[Desktop Entry]
+Version=1.0
+Encoding=UTF-8
+_Name=PulseAudio Sound System KDE Routing Policy
+_Comment=Start the PulseAudio Sound System with KDE Routing Policy
+Exec=start-pulseaudio-kde
+Terminal=false
+Type=Application
+Categories=
+GenericName=
+OnlyShowIn=KDE;
diff --git a/src/daemon/start-pulseaudio-kde.in b/src/daemon/start-pulseaudio-kde.in
new file mode 100755
index 0000000..a79a50b
--- /dev/null
+++ b/src/daemon/start-pulseaudio-kde.in
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+# This file is part of PulseAudio.
+#
+# PulseAudio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# PulseAudio is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with PulseAudio; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA.
+
+set -e
+
+[ -z "$PULSE_SERVER" ]
+
+ at PA_BINARY@ --start "$@"
+
+if [ x"$DISPLAY" != x ] ; then
+
+    @PACTL_BINARY@ load-module module-module-device-manager "do_routing=1" > /dev/null
+
+fi

commit 50db81c8603e1274b1bfe8836d134bf8ad6fb0a1
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Fri Oct 2 00:49:50 2009 +0100

    device-manager: Fix typo in module loading script.

diff --git a/src/daemon/start-pulseaudio-kde.in b/src/daemon/start-pulseaudio-kde.in
index a79a50b..c319e7d 100755
--- a/src/daemon/start-pulseaudio-kde.in
+++ b/src/daemon/start-pulseaudio-kde.in
@@ -25,6 +25,6 @@ set -e
 
 if [ x"$DISPLAY" != x ] ; then
 
-    @PACTL_BINARY@ load-module module-module-device-manager "do_routing=1" > /dev/null
+    @PACTL_BINARY@ load-module module-device-manager "do_routing=1" > /dev/null
 
 fi

commit a434f4c6af1ba421f8c689fa8bf9e040268205b2
Author: Jason Newton <nevion at gmail.com>
Date:   Wed Aug 26 01:15:49 2009 -0700

    module-equalizer-sink: resyncing with head and fix invalid writes
        * pa_log->debug for default equalizer notification
        * partially fixed infinite rewind bug
        * set max_request to window_size first iteration
        * swap order inside ROUND_UP calls
        * resync pa_sink_input_new changes
        * change pa_sample_clamp parameters to be correct to fix invalid writes
        * reenable proper reset logic + proper request size

diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
index f634d5e..f556be7 100755
--- a/src/modules/module-equalizer-sink.c
+++ b/src/modules/module-equalizer-sink.c
@@ -99,10 +99,10 @@ struct userdata {
               * the latency of the filter, calculated from window_size
               * based on constraints of COLA and window function
               */
-    size_t latency;//Really just R but made into it's own variable
     //for twiddling with pulseaudio
     size_t overlap_size;//window_size-R
     size_t samples_gathered;
+    size_t input_buffer_max;
     //message
     float *W;//windowing function (time domain)
     float *work_buffer, **input, **overlap_accum;
@@ -198,6 +198,34 @@ static int is_monotonic(const uint32_t *xs,size_t length){
     return 1;
 }
 
+//ensure's memory allocated is a multiple of v_size
+//and aligned
+static void * alloc(size_t x,size_t s){
+    size_t f = PA_ROUND_UP(x*s, sizeof(float)*v_size);
+    float *t;
+    pa_assert(f >= x*s);
+    t = fftwf_malloc(f);
+    memset(t, 0, f);
+    return t;
+}
+
+static void alloc_input_buffers(struct userdata *u, size_t min_buffer_length){
+    if(min_buffer_length <= u->input_buffer_max){
+        return;
+    }
+    pa_assert(min_buffer_length >= u->window_size);
+    for(size_t c = 0; c < u->channels; ++c){
+        float *tmp = alloc(min_buffer_length, sizeof(float));
+        if(u->input[c]){
+            if(!u->first_iteration){
+                memcpy(tmp, u->input[c], u->overlap_size * sizeof(float));
+            }
+            free(u->input[c]);
+        }
+        u->input[c] = tmp;
+    }
+    u->input_buffer_max = min_buffer_length;
+}
 
 /* Called from I/O thread context */
 static int sink_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
@@ -359,7 +387,7 @@ static void dsp_logic(
 
     //preseve the needed input for the next window's overlap
     memmove(src, src + u->R,
-        u->overlap_size * sizeof(float)
+        (u->samples_gathered - u->R) * sizeof(float)
     );
 }
 
@@ -470,71 +498,64 @@ typedef union float_vector {
 //}
 
 static void process_samples(struct userdata *u, pa_memchunk *tchunk){
-    size_t fs=pa_frame_size(&(u->sink->sample_spec));
+    size_t fs = pa_frame_size(&(u->sink->sample_spec));
     float *dst;
     unsigned a_i;
     float *H, X;
-    pa_assert(u->samples_gathered >= u->R);
+    size_t iterations, offset;
+    pa_assert(u->samples_gathered >= u->window_size);
+    iterations = (u->samples_gathered - u->overlap_size) / u->R;
     tchunk->index = 0;
-    tchunk->length = u->R * fs;
+    tchunk->length = iterations * u->R * fs;
     tchunk->memblock = pa_memblock_new(u->sink->core->mempool, tchunk->length);
-    dst = ((float*)pa_memblock_acquire(tchunk->memblock));
-
-    for(size_t c=0;c < u->channels; c++) {
-        a_i = pa_aupdate_read_begin(u->a_H[c]);
-        X = u->Xs[c][a_i];
-        H = u->Hs[c][a_i];
-        dsp_logic(
-            u->work_buffer,
-            u->input[c],
-            u->overlap_accum[c],
-            X,
-            H,
-            u->W,
-            u->output_window,
-            u
-        );
-        pa_aupdate_read_end(u->a_H[c]);
-        if(u->first_iteration){
-            /* The windowing function will make the audio ramped in, as a cheap fix we can
-             * undo the windowing (for non-zero window values)
-             */
-            for(size_t i = 0;i < u->overlap_size; ++i){
-                u->work_buffer[i] = u->W[i] <= FLT_EPSILON ? u->work_buffer[i] : u->work_buffer[i] / u->W[i];
+    dst = ((float*) pa_memblock_acquire(tchunk->memblock));
+    for(size_t iter = 0; iter < iterations; ++iter){
+        offset = iter * u->R * fs;
+        for(size_t c = 0;c < u->channels; c++) {
+            a_i = pa_aupdate_read_begin(u->a_H[c]);
+            X = u->Xs[c][a_i];
+            H = u->Hs[c][a_i];
+            dsp_logic(
+                u->work_buffer,
+                u->input[c],
+                u->overlap_accum[c],
+                X,
+                H,
+                u->W,
+                u->output_window,
+                u
+            );
+            pa_aupdate_read_end(u->a_H[c]);
+            if(u->first_iteration){
+                /* The windowing function will make the audio ramped in, as a cheap fix we can
+                 * undo the windowing (for non-zero window values)
+                 */
+                for(size_t i = 0; i < u->overlap_size; ++i){
+                    u->work_buffer[i] = u->W[i] <= FLT_EPSILON ? u->work_buffer[i] : u->work_buffer[i] / u->W[i];
+                }
             }
+            pa_sample_clamp(PA_SAMPLE_FLOAT32NE, (uint8_t *) (dst + c) + offset, fs, u->work_buffer, sizeof(float), u->R);
         }
-        pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst + c, fs, u->work_buffer, sizeof(float), u->R);
+        if(u->first_iteration){
+            u->first_iteration = FALSE;
+        }
+        u->samples_gathered -= u->R;
     }
     pa_memblock_release(tchunk->memblock);
-    u->samples_gathered -= u->R;
-}
-
-static void initialize_buffer(struct userdata *u, pa_memchunk *in){
-    size_t fs = pa_frame_size(&u->sink->sample_spec);
-    size_t samples = in->length / fs;
-    float *src = (float*) ((uint8_t*) pa_memblock_acquire(in->memblock) + in->index);
-    pa_assert_se(u->samples_gathered + samples <= u->window_size);
-    for(size_t c = 0; c < u->channels; c++) {
-        //buffer with an offset after the overlap from previous
-        //iterations
-        pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c] + u->samples_gathered, sizeof(float), src + c, fs, samples);
-    }
-    u->samples_gathered += samples;
-    pa_memblock_release(in->memblock);
 }
 
 static void input_buffer(struct userdata *u, pa_memchunk *in){
     size_t fs = pa_frame_size(&(u->sink->sample_spec));
     size_t samples = in->length/fs;
     float *src = (float*) ((uint8_t*) pa_memblock_acquire(in->memblock) + in->index);
-    pa_assert_se(samples <= u->window_size - u->samples_gathered);
+    pa_assert(u->samples_gathered + samples <= u->input_buffer_max);
     for(size_t c = 0; c < u->channels; c++) {
         //buffer with an offset after the overlap from previous
         //iterations
         pa_assert_se(
-            u->input[c]+u->samples_gathered+samples <= u->input[c]+u->window_size
+            u->input[c] + u->samples_gathered + samples <= u->input[c] + u->input_buffer_max
         );
-        pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c]+u->samples_gathered, sizeof(float), src + c, fs, samples);
+        pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c] + u->samples_gathered, sizeof(float), src + c, fs, samples);
     }
     u->samples_gathered += samples;
     pa_memblock_release(in->memblock);
@@ -543,7 +564,7 @@ static void input_buffer(struct userdata *u, pa_memchunk *in){
 /* Called from I/O thread context */
 static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
     struct userdata *u;
-    size_t fs;
+    size_t fs, target_samples;
     struct timeval start, end;
     pa_memchunk tchunk;
     pa_sink_input_assert_ref(i);
@@ -551,6 +572,16 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
     pa_assert(chunk);
     pa_assert(u->sink);
     fs = pa_frame_size(&(u->sink->sample_spec));
+    target_samples = PA_ROUND_UP(nbytes / fs, u->R);
+    if(u->first_iteration){
+        //allocate request_size
+        target_samples = PA_MAX(target_samples, u->window_size);
+    }else{
+        //allocate request_size + overlap
+        target_samples += u->overlap_size;
+        alloc_input_buffers(u, target_samples);
+    }
+    alloc_input_buffers(u, target_samples);
     chunk->memblock = NULL;
 
     /* Hmm, process any rewind request that might be queued up */
@@ -559,18 +590,11 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
     //pa_log_debug("start output-buffered %ld, input-buffered %ld, requested %ld",buffered_samples,u->samples_gathered,samples_requested);
     pa_rtclock_get(&start);
     do{
-        size_t input_remaining = u->window_size - u->samples_gathered;
+        size_t input_remaining = target_samples - u->samples_gathered;
         pa_assert(input_remaining > 0);
-        //collect samples
-
-        //buffer = &u->conv_buffer;
-        //buffer->length = input_remaining*fs;
-        //buffer->index = 0;
-        //pa_memblock_ref(buffer->memblock);
-        //pa_sink_render_into(u->sink, buffer);
         while(pa_memblockq_peek(u->input_q, &tchunk) < 0){
-            pa_sink_render(u->sink, input_remaining*fs, &tchunk);
-            //pa_sink_render_full(u->sink, input_remaining*fs, &tchunk);
+            //pa_sink_render(u->sink, input_remaining * fs, &tchunk);
+            pa_sink_render_full(u->sink, input_remaining * fs, &tchunk);
             pa_assert(tchunk.memblock);
             pa_memblockq_push(u->input_q, &tchunk);
             pa_memblock_unref(tchunk.memblock);
@@ -581,15 +605,12 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
         //pa_log_debug("asked for %ld input samples, got %ld samples",input_remaining,buffer->length/fs);
         /* copy new input */
         //pa_rtclock_get(start);
-        if(u->first_iteration){
-            initialize_buffer(u, &tchunk);
-        }else{
-            input_buffer(u, &tchunk);
-        }
+        input_buffer(u, &tchunk);
         //pa_rtclock_get(&end);
         //pa_log_debug("Took %0.5f seconds to setup", pa_timeval_diff(end, start) / (double) PA_USEC_PER_SEC);
         pa_memblock_unref(tchunk.memblock);
-    }while(u->samples_gathered < u->window_size);
+    }while(u->samples_gathered < target_samples);
+
     pa_rtclock_get(&end);
     pa_log_debug("Took %0.6f seconds to get data", (double) pa_timeval_diff(&end, &start) / PA_USEC_PER_SEC);
 
@@ -605,9 +626,6 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
     pa_assert(chunk->memblock);
     //pa_log_debug("gave %ld", chunk->length/fs);
     //pa_log_debug("end pop");
-    if(u->first_iteration){
-        u->first_iteration = FALSE;
-    }
     return 0;
 }
 
@@ -632,11 +650,17 @@ static void sink_input_mute_changed_cb(pa_sink_input *i) {
 }
 
 static void reset_filter(struct userdata *u){
+    size_t fs = pa_frame_size(&u->sink->sample_spec);
+    size_t max_request;
     u->samples_gathered = 0;
-    for(size_t i = 0;i < u->channels; ++i){
+    for(size_t i = 0; i < u->channels; ++i){
         memset(u->overlap_accum[i], 0, u->overlap_size * sizeof(float));
     }
     u->first_iteration = TRUE;
+    //set buffer size to max request, no overlap copy
+    max_request = PA_ROUND_UP(pa_sink_input_get_max_request(u->sink_input) / fs , u->R);
+    max_request = PA_MAX(max_request, u->window_size);
+    pa_sink_set_max_request_within_thread(u->sink, max_request * fs);
 }
 
 /* Called from I/O thread context */
@@ -658,11 +682,8 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
         u->sink->thread_info.rewind_nbytes = 0;
 
         if (amount > 0) {
-            //pa_sample_spec *ss = &u->sink->sample_spec;
             //invalidate the output q
             pa_memblockq_seek(u->input_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE);
-            //pa_memblockq_drop(u->input_q, pa_memblockq_get_length(u->input_q));
-            //pa_memblockq_seek(u->input_q, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE);
             pa_log("Resetting filter");
             reset_filter(u);
         }
@@ -689,11 +710,11 @@ static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
     size_t fs;
     pa_sink_input_assert_ref(i);
     pa_assert_se(u = i->userdata);
-
+    //if(u->first_iteration){
+    //    return;
+    //}
     fs = pa_frame_size(&(u->sink->sample_spec));
-    //pa_sink_set_max_request_within_thread(u->sink, nbytes);
-    //pa_sink_set_max_request_within_thread(u->sink, u->R*fs);
-    pa_sink_set_max_request_within_thread(u->sink, ((nbytes+u->R*fs-1)/(u->R*fs))*(u->R*fs));
+    pa_sink_set_max_request_within_thread(u->sink, PA_ROUND_UP(nbytes / fs, u->R) * fs);
 }
 
 /* Called from I/O thread context */
@@ -703,8 +724,6 @@ static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) {
     pa_sink_input_assert_ref(i);
     pa_assert_se(u = i->userdata);
 
-    //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs);
-    //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs );
     pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
 }
 
@@ -733,7 +752,7 @@ static void sink_input_detach_cb(pa_sink_input *i) {
 /* Called from I/O thread context */
 static void sink_input_attach_cb(pa_sink_input *i) {
     struct userdata *u;
-    size_t fs;
+    size_t fs, max_request;
     pa_sink_input_assert_ref(i);
     pa_assert_se(u = i->userdata);
 
@@ -741,19 +760,15 @@ static void sink_input_attach_cb(pa_sink_input *i) {
     pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
 
     pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency);
-    fs = pa_frame_size(&(u->sink->sample_spec));
-    pa_sink_set_max_request_within_thread(u->sink, PA_ROUND_UP(pa_sink_input_get_max_request(i), u->R*fs));
-
-    //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->latency*fs);
-    //pa_sink_set_latency_range_within_thread(u->sink, u->latency*fs, u->master->thread_info.max_latency);
-    //TODO: setting this guy minimizes drop outs but doesn't get rid
-    //of them completely, figure out why
-    //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->latency*fs);
-    //TODO: this guy causes dropouts constantly+rewinds, it's unusable
-    //pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency);
+    fs = pa_frame_size(&u->sink->sample_spec);
+    //set buffer size to max request, no overlap copy
+    max_request = PA_ROUND_UP(pa_sink_input_get_max_request(u->sink_input) / fs , u->R);
+    max_request = PA_MAX(max_request, u->window_size);
+    pa_sink_set_max_request_within_thread(u->sink, max_request * fs);
+    pa_sink_set_max_rewind_within_thread(u->sink, pa_sink_input_get_max_rewind(i));
     pa_sink_attach_within_thread(u->sink);
     if(u->set_default){
-        pa_log("Setting default sink to %s", u->sink->name);
+        pa_log_debug("Setting default sink to %s", u->sink->name);
         pa_namereg_set_default_sink(u->module->core, u->sink);
     }
 }
@@ -1005,17 +1020,6 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
         pa_sink_set_asyncmsgq(u->sink, NULL);
 }
 
-//ensure's memory allocated is a multiple of v_size
-//and aligned
-static void * alloc(size_t x,size_t s){
-    size_t f = PA_ROUND_UP(x*s, sizeof(float)*v_size);
-    float *t;
-    pa_assert(f >= x*s);
-    t = fftwf_malloc(f);
-    memset(t, 0, f);
-    return t;
-}
-
 int pa__init(pa_module*m) {
     struct userdata *u;
     pa_sample_spec ss;
@@ -1068,15 +1072,15 @@ int pa__init(pa_module*m) {
     u->R = (u->window_size + 1) / 2;
     u->overlap_size = u->window_size - u->R;
     u->samples_gathered = 0;
+    u->input_buffer_max = 0;
     u->a_H = pa_xnew0(pa_aupdate *, u->channels);
-    u->latency = u->window_size - u->R;
     u->Xs = pa_xnew0(float *, u->channels);
     u->Hs = pa_xnew0(float **, u->channels);
     for(size_t c = 0; c < u->channels; ++c){
         u->Xs[c] = pa_xnew0(float, 2);
         u->Hs[c] = pa_xnew0(float *, 2);
         for(size_t i = 0; i < 2; ++i){
-            u->Hs[c][i] = alloc((FILTER_SIZE), sizeof(float));
+            u->Hs[c][i] = alloc(FILTER_SIZE, sizeof(float));
         }
     }
     u->W = alloc(u->window_size, sizeof(float));
@@ -1086,8 +1090,7 @@ int pa__init(pa_module*m) {
     u->overlap_accum = pa_xnew0(float *, u->channels);
     for(size_t c = 0; c < u->channels; ++c){
         u->a_H[c] = pa_aupdate_new();
-        u->input[c] = alloc(u->window_size, sizeof(float));
-        memset(u->input[c], 0, (u->window_size)*sizeof(float));
+        u->input[c] = NULL;
         u->overlap_accum[c] = alloc(u->overlap_size, sizeof(float));
         memset(u->overlap_accum[c], 0, u->overlap_size*sizeof(float));
     }
@@ -1151,7 +1154,7 @@ int pa__init(pa_module*m) {
     pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss);
     pa_sink_input_new_data_set_channel_map(&sink_input_data, &map);
 
-    pa_sink_input_new(&u->sink_input, m->core, &sink_input_data, 0);
+    pa_sink_input_new(&u->sink_input, m->core, &sink_input_data);
     pa_sink_input_new_data_done(&sink_input_data);
 
     if (!u->sink_input)

commit 97056d2a0e4364fad86f4be5e26625592aef3d55
Author: Jason Newton <nevion at gmail.com>
Date:   Fri Sep 18 05:48:01 2009 -0700

    module-equalizer-sink: *added client initiated sync support for filter state *added note of possible unstable behavior with next-power-of-2 sample rate calculation

diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
index f556be7..74d7497 100755
--- a/src/modules/module-equalizer-sink.c
+++ b/src/modules/module-equalizer-sink.c
@@ -837,13 +837,10 @@ static void unpack(char *str, size_t length, char ***strs, size_t *len){
         size_t l = *((uint16_t *)(str+offset));
         size_t e = PA_MIN(offset + l, length) - offset;
         offset = PA_MIN(offset + sizeof(uint16_t), length);
-        if(e > 0){
-            (*strs)[i] = pa_xnew(char, e + 1);
-            memcpy((*strs)[i], strs + offset, e);
-            (*strs)[i][e] = '\0';
-        }else{
-            (*strs)[i]=NULL;
-        }
+        (*strs)[i] = pa_xnew(char, e + 1);
+        memcpy((*strs)[i], str + offset, e);
+        (*strs)[i][e] = '\0';
+        offset += l;
     }
 }
 static void save_profile(struct userdata *u, size_t channel, char *name){
@@ -905,7 +902,7 @@ static void save_state(struct userdata *u){
     data.data = state;
     data.size = filter_state_size + packed_length;
     //thread safety for 0.9.17?
-    pa_assert_se(dbname = pa_state_path(EQ_STATE_DB, TRUE));
+    pa_assert_se(dbname = pa_state_path(EQ_STATE_DB, FALSE));
     pa_assert_se(database = pa_database_open(dbname, TRUE));
     pa_xfree(dbname);
 
@@ -936,12 +933,10 @@ static const char* load_profile(struct userdata *u, size_t channel, char *name){
             float *profile = (float *) value.data;
             a_i = pa_aupdate_write_begin(u->a_H[channel]);
             u->Xs[channel][a_i] = profile[0];
-            memcpy(u->Hs[channel][a_i], profile + 1, CHANNEL_PROFILE_SIZE * sizeof(float));
+            memcpy(u->Hs[channel][a_i], profile + 1, FILTER_SIZE * sizeof(float));
             fix_filter(u->Hs[channel][a_i], u->fft_size);
             pa_aupdate_write_end(u->a_H[channel]);
-            if(u->base_profiles[channel]){
-                pa_xfree(u->base_profiles[channel]);
-            }
+            pa_xfree(u->base_profiles[channel]);
             u->base_profiles[channel] = pa_xstrdup(name);
         }else{
             return "incompatible size";
@@ -964,6 +959,7 @@ static void load_state(struct userdata *u){
     database = pa_database_open(dbname, FALSE);
     pa_xfree(dbname);
     if(!database){
+        pa_log("No resume state");
         return;
     }
 
@@ -985,14 +981,14 @@ static void load_state(struct userdata *u){
             unpack(((char *)value.data) + FILTER_STATE_SIZE, value.size - FILTER_STATE_SIZE, &names, &n_profs);
             n_profs = PA_MIN(n_profs, u->channels);
             for(size_t c = 0; c < n_profs; ++c){
-                if(u->base_profiles[c]){
-                    pa_xfree(u->base_profiles[c]);
-                }
+                pa_xfree(u->base_profiles[c]);
                 u->base_profiles[c] = names[c];
             }
             pa_xfree(names);
         }
         pa_datum_free(&value);
+    }else{
+        pa_log("resume state exists but is wrong size!");
     }
     pa_database_close(database);
 }
@@ -1066,7 +1062,7 @@ int pa__init(pa_module*m) {
     pa_modargs_get_value_boolean(ma, "set_default", &u->set_default);
 
     u->channels = ss.channels;
-    u->fft_size = pow(2, ceil(log(ss.rate)/log(2)));
+    u->fft_size = pow(2, ceil(log(ss.rate)/log(2)));//probably unstable near corner cases of powers of 2
     pa_log_debug("fft size: %ld", u->fft_size);
     u->window_size = 15999;
     u->R = (u->window_size + 1) / 2;
@@ -1102,6 +1098,9 @@ int pa__init(pa_module*m) {
     u->first_iteration = TRUE;
 
     u->base_profiles = pa_xnew0(char *, u->channels);
+    for(size_t c = 0; c < u->channels; ++c){
+        u->base_profiles[c] = pa_xstrdup("default");
+    }
 
     /* Create sink */
     pa_sink_new_data_init(&sink_data);
@@ -1233,9 +1232,7 @@ void pa__done(pa_module*m) {
     dbus_done(u);
 
     for(size_t c = 0; c < u->channels; ++c){
-        if(u->base_profiles[c]){
-            pa_xfree(u->base_profiles[c]);
-        }
+        pa_xfree(u->base_profiles[c]);
     }
     pa_xfree(u->base_profiles);
 
@@ -1308,6 +1305,7 @@ static void equalizer_handle_get_filter(DBusConnection *conn, DBusMessage *msg,
 static void equalizer_handle_set_filter(DBusConnection *conn, DBusMessage *msg, void *_u);
 static void equalizer_handle_save_profile(DBusConnection *conn, DBusMessage *msg, void *_u);
 static void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void *_u);
+static void equalizer_handle_save_state(DBusConnection *conn, DBusMessage *msg, void *_u);
 static void equalizer_handle_get_profile_name(DBusConnection *conn, DBusMessage *msg, void *_u);
 enum manager_method_index {
     MANAGER_METHOD_REMOVE_PROFILE,
@@ -1374,6 +1372,7 @@ enum equalizer_method_index {
     EQUALIZER_METHOD_LOAD_PROFILE,
     EQUALIZER_METHOD_SET_FILTER,
     EQUALIZER_METHOD_GET_FILTER,
+    EQUALIZER_METHOD_SAVE_STATE,
     EQUALIZER_METHOD_GET_PROFILE_NAME,
     EQUALIZER_METHOD_MAX
 };
@@ -1455,6 +1454,11 @@ static pa_dbus_method_handler equalizer_methods[EQUALIZER_METHOD_MAX]={
         .arguments=load_profile_args,
         .n_arguments=sizeof(load_profile_args)/sizeof(pa_dbus_arg_info),
         .receive_cb=equalizer_handle_load_profile},
+    [EQUALIZER_METHOD_SAVE_STATE]{
+        .method_name="SaveState",
+        .arguments=NULL,
+        .n_arguments=0,
+        .receive_cb=equalizer_handle_save_state},
     [EQUALIZER_METHOD_GET_PROFILE_NAME]{
         .method_name="BaseProfile",
         .arguments=base_profile_name_args,
@@ -2037,6 +2041,16 @@ void equalizer_handle_load_profile(DBusConnection *conn, DBusMessage *msg, void
     dbus_message_unref(signal);
 }
 
+void equalizer_handle_save_state(DBusConnection *conn, DBusMessage *msg, void *_u) {
+    struct userdata *u = (struct userdata *) _u;
+    pa_assert(conn);
+    pa_assert(msg);
+    pa_assert(u);
+
+    save_state(u);
+    pa_dbus_send_empty_reply(conn, msg);
+}
+
 void equalizer_handle_get_profile_name(DBusConnection *conn, DBusMessage *msg, void *_u){
     struct userdata *u = (struct userdata *) _u;
     DBusError error;
@@ -2060,11 +2074,8 @@ void equalizer_handle_get_profile_name(DBusConnection *conn, DBusMessage *msg, v
         return;
     }
     r_channel = channel == u->channels ? 0 : channel;
-    if(u->base_profiles[r_channel]){
-        pa_dbus_send_basic_value_reply(conn,msg, DBUS_TYPE_STRING, &u->base_profiles[r_channel]);
-    }else{
-        pa_dbus_send_empty_reply(conn, msg);
-    }
+    pa_assert(u->base_profiles[r_channel]);
+    pa_dbus_send_basic_value_reply(conn,msg, DBUS_TYPE_STRING, &u->base_profiles[r_channel]);
 }
 
 void equalizer_get_revision(DBusConnection *conn, DBusMessage *msg, void *_u){

commit e8952001699317952318918835252f63c7a899f9
Author: Jason Newton <nevion at gmail.com>
Date:   Sat Sep 19 11:15:05 2009 -0700

    module-equalizer-sink: disable active profile name restoration as something in pack/unpack is funky and I don't have time for a
    proper fix

diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
index 74d7497..3a28b49 100755
--- a/src/modules/module-equalizer-sink.c
+++ b/src/modules/module-equalizer-sink.c
@@ -978,13 +978,13 @@ static void load_state(struct userdata *u){
                 memcpy(u->Hs[c][a_i], H, FILTER_SIZE * sizeof(float));
                 pa_aupdate_write_end(u->a_H[c]);
             }
-            unpack(((char *)value.data) + FILTER_STATE_SIZE, value.size - FILTER_STATE_SIZE, &names, &n_profs);
-            n_profs = PA_MIN(n_profs, u->channels);
-            for(size_t c = 0; c < n_profs; ++c){
-                pa_xfree(u->base_profiles[c]);
-                u->base_profiles[c] = names[c];
-            }
-            pa_xfree(names);
+            //unpack(((char *)value.data) + FILTER_STATE_SIZE, value.size - FILTER_STATE_SIZE, &names, &n_profs);
+            //n_profs = PA_MIN(n_profs, u->channels);
+            //for(size_t c = 0; c < n_profs; ++c){
+            //    pa_xfree(u->base_profiles[c]);
+            //    u->base_profiles[c] = names[c];
+            //}
+            //pa_xfree(names);
         }
         pa_datum_free(&value);
     }else{

commit 019331d25b6af107fb8cacc3ada552e7567a64bf
Merge: 5871319 afd1b6d
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Fri Oct 2 17:24:44 2009 +0300

    Merge branch 'master' into dbus-work
    
    Conflicts:
    	src/daemon/daemon-conf.c

diff --cc src/Makefile.am
index 623cef5,6544e2a..69711ac
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@@ -175,11 -175,10 +175,11 @@@ pulseaudio_SOURCES = 
  		daemon/daemon-conf.c daemon/daemon-conf.h \
  		daemon/dumpmodules.c daemon/dumpmodules.h \
  		daemon/ltdl-bind-now.c daemon/ltdl-bind-now.h \
 -		daemon/main.c
 +		daemon/main.c \
 +		daemon/server-lookup.c daemon/server-lookup.h
  
- pulseaudio_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSPEEX_CFLAGS) $(LIBSNDFILE_CFLAGS) $(CAP_CFLAGS) $(LIBOIL_CFLAGS) $(DBUS_CFLAGS)
- pulseaudio_LDADD = $(AM_LDADD) libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSPEEX_LIBS) $(LIBSNDFILE_LIBS) $(CAP_LIBS) $(LIBOIL_LIBS) $(DBUS_LIBS)
+ pulseaudio_CFLAGS = $(AM_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSPEEX_CFLAGS) $(LIBSNDFILE_CFLAGS) $(CAP_CFLAGS) $(DBUS_CFLAGS)
+ pulseaudio_LDADD = $(AM_LDADD) libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSPEEX_LIBS) $(LIBSNDFILE_LIBS) $(CAP_LIBS) $(DBUS_LIBS)
  # This is needed because automake doesn't properly expand the foreach below
  pulseaudio_DEPENDENCIES = libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la $(PREOPEN_LIBS)
  
diff --cc src/daemon/daemon-conf.c
index 3428f80,6e7926f..571faae
--- a/src/daemon/daemon-conf.c
+++ b/src/daemon/daemon-conf.c
@@@ -83,10 -83,7 +83,10 @@@ static const pa_daemon_conf default_con
      .config_file = NULL,
      .use_pid_file = TRUE,
      .system_instance = FALSE,
 +#ifdef HAVE_DBUS
 +    .local_server_type = PA_SERVER_TYPE_UNSET, /* The actual default is _USER, but we have to detect when the user doesn't specify this option. */
 +#endif
-     .no_cpu_limit = FALSE,
+     .no_cpu_limit = TRUE,
      .disable_shm = FALSE,
      .lock_memory = FALSE,
      .default_n_fragments = 4,

commit 6468dcf9d13c49299eba8c76ee41f4ff5fdba80f
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Fri Oct 2 19:12:10 2009 +0100

    device-manager: No need to check the version after calling read_entry()

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index 86ea95d..587def4 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -448,7 +448,7 @@ static void update_highest_priority_device_indexes(struct userdata *u, const cha
             name = pa_xstrndup(key.data, key.size);
             device_name = get_name(name, prefix);
 
-            if ((e = read_entry(u, name)) && ENTRY_VERSION == e->version) {
+            if ((e = read_entry(u, name))) {
                 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
                     if (!highest_priority_available[i] || e->priority[i] < highest_priority_available[i]) {
                         /* We've found a device with a higher priority than that we've currently got,
@@ -1049,7 +1049,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
         if (!device || !*device || !description || !*description)
           goto fail;
 
-        if ((e = read_entry(u, device)) && ENTRY_VERSION == e->version) {
+        if ((e = read_entry(u, device))) {
             pa_datum key, data;
 
             pa_strlcpy(e->description, description, sizeof(e->description));
@@ -1217,7 +1217,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
 
                 /* Add the device to our hashmap. If it's alredy in it, free it now and carry on */
                 if (pa_hashmap_put(h, device->device, device) == 0
-                    && (e = read_entry(u, device->device)) && ENTRY_VERSION == e->version) {
+                    && (e = read_entry(u, device->device))) {
                     /* We add offset on to the existing priorirty so that when we order, the
                        existing entries are always lower priority than the new ones. */
                     device->prio = (offset + e->priority[role_index]);
@@ -1272,7 +1272,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
         idx = 1;
         first = TRUE;
         for (i = 0; i < n_devices; ++i) {
-            if ((e = read_entry(u, devices[i]->device)) && ENTRY_VERSION == e->version) {
+            if ((e = read_entry(u, devices[i]->device))) {
                 if (e->priority[role_index] == idx)
                     idx++;
                 else {

commit fdbb5500634bbe3481fb60ce373ad4f9c2063f75
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Fri Oct 2 21:01:19 2009 +0100

    device-manager: Keep track as to whether or not the user specifically renamed the device.
    
    If the user has not (via our protocol extension) renamed a device, but it happens to now have
    a different name (e.g. module-combine automatically updating the description for us or udev-db
    getting better etc.) then make sure we update our cache with this updated version.
    
    If the user has set a name, enforce it's use, even if the description is updated by some other
    means (e.g. the user manually editing the proplist or another module doing it for them).

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index 587def4..776a687 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -133,6 +133,7 @@ struct userdata {
 struct entry {
     uint8_t version;
     char description[PA_NAME_MAX];
+    pa_bool_t user_set_description;
     char icon[PA_NAME_MAX];
     role_indexes_t priority;
 } PA_GCC_PACKED;
@@ -181,6 +182,11 @@ static struct entry* read_entry(struct userdata *u, const char *name) {
         goto fail;
     }
 
+    if (!memchr(e->icon, 0, sizeof(e->icon))) {
+        pa_log_warn("Database contains entry for device %s with missing NUL byte in icon", name);
+        goto fail;
+    }
+
     return e;
 
 fail:
@@ -329,6 +335,7 @@ static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
     pa_assert(b);
 
     if (strncmp(a->description, b->description, sizeof(a->description))
+        || a->user_set_description != b->user_set_description
         || strncmp(a->icon, b->icon, sizeof(a->icon)))
         return FALSE;
 
@@ -398,6 +405,7 @@ static inline struct entry *load_or_initialize_entry(struct userdata *u, struct
         for (uint32_t i = 0; i < NUM_ROLES; ++i) {
             entry->priority[i] = max_priority[i] + 1;
         }
+        entry->user_set_description = FALSE;
     }
 
     return old;
@@ -672,7 +680,16 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
 
         old = load_or_initialize_entry(u, &entry, name, "sink:");
 
-        pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
+        if (!entry.user_set_description)
+            pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
+        else if (strncmp(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description)) != 0) {
+            /* Warning: If two modules fight over the description, this could cause an infinite loop.
+               by changing the description here, we retrigger this subscription callback. The only thing stopping us from
+               looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage
+               the description, this will fail... */
+            pa_sink_set_description(sink, entry.description);
+        }
+
         pa_strlcpy(entry.icon, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_ICON_NAME)), sizeof(entry.icon));
 
     } else  if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) {
@@ -690,7 +707,16 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
 
         old = load_or_initialize_entry(u, &entry, name, "source:");
 
-        pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
+        if (!entry.user_set_description)
+            pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
+        else if (strncmp(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description)) != 0) {
+            /* Warning: If two modules fight over the description, this could cause an infinite loop.
+               by changing the description here, we retrigger this subscription callback. The only thing stopping us from
+               looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage
+               the description, this will fail... */
+            pa_source_set_description(source, entry.description);
+        }
+
         pa_strlcpy(entry.icon, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_ICON_NAME)), sizeof(entry.icon));
     }
 
@@ -716,11 +742,12 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
 
     pa_log_info("Storing device %s.", name);
 
-    pa_database_set(u->database, &key, &data, TRUE);
+    if (pa_database_set(u->database, &key, &data, TRUE) == 0)
+        trigger_save(u);
+    else
+        pa_log_warn("Could not save device");;
 
     pa_xfree(name);
-
-    trigger_save(u);
 }
 
 static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
@@ -734,7 +761,7 @@ static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new
     name = pa_sprintf_malloc("sink:%s", new_data->name);
 
     if ((e = read_entry(u, name))) {
-        if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
+        if (e->user_set_description && strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
             pa_log_info("Restoring description for sink %s.", new_data->name);
             pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
         }
@@ -758,7 +785,7 @@ static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data
     name = pa_sprintf_malloc("source:%s", new_data->name);
 
     if ((e = read_entry(u, name))) {
-        if (strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
+        if (e->user_set_description && strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
             /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
             pa_log_info("Restoring description for source %s.", new_data->name);
             pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
@@ -911,13 +938,16 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
     pa_assert(name);
     pa_assert(e);
 
+    if (!e->user_set_description)
+        return;
+
     if ((n = get_name(name, "sink:"))) {
         for (sink = pa_idxset_first(u->core->sinks, &idx); sink; sink = pa_idxset_next(u->core->sinks, &idx)) {
             if (!pa_streq(sink->name, n)) {
                 continue;
             }
 
-            pa_log_info("Setting description for sink %s.", sink->name);
+            pa_log_info("Setting description for sink %s to '%s'", sink->name, e->description);
             pa_sink_set_description(sink, e->description);
         }
         pa_xfree(n);
@@ -933,7 +963,7 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
                 continue;
             }
 
-            pa_log_info("Setting description for source %s.", source->name);
+            pa_log_info("Setting description for source %s to '%s'", source->name, e->description);
             pa_source_set_description(source, e->description);
         }
         pa_xfree(n);
@@ -1053,6 +1083,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
             pa_datum key, data;
 
             pa_strlcpy(e->description, description, sizeof(e->description));
+            e->user_set_description = TRUE;
 
             key.data = (char *) device;
             key.size = strlen(device);

commit cc31d7c35a509d398494e13a9020513ddf8b667f
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Fri Oct 2 21:04:03 2009 +0100

    device-manager: Make use of PA_IDXSET_FOREACH when applying entries.

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index 776a687..8d17bb0 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -929,8 +929,6 @@ static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *sourc
 
 
 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
-    pa_sink *sink;
-    pa_source *source;
     uint32_t idx;
     char *n;
 
@@ -942,29 +940,31 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
         return;
 
     if ((n = get_name(name, "sink:"))) {
-        for (sink = pa_idxset_first(u->core->sinks, &idx); sink; sink = pa_idxset_next(u->core->sinks, &idx)) {
-            if (!pa_streq(sink->name, n)) {
+        pa_sink *s;
+        PA_IDXSET_FOREACH(s, u->core->sinks, idx) {
+            if (!pa_streq(s->name, n)) {
                 continue;
             }
 
-            pa_log_info("Setting description for sink %s to '%s'", sink->name, e->description);
-            pa_sink_set_description(sink, e->description);
+            pa_log_info("Setting description for sink %s to '%s'", s->name, e->description);
+            pa_sink_set_description(s, e->description);
         }
         pa_xfree(n);
     }
     else if ((n = get_name(name, "source:"))) {
-        for (source = pa_idxset_first(u->core->sources, &idx); source; source = pa_idxset_next(u->core->sources, &idx)) {
-            if (!pa_streq(source->name, n)) {
+        pa_source *s;
+        PA_IDXSET_FOREACH(s, u->core->sources, idx) {
+            if (!pa_streq(s->name, n)) {
                 continue;
             }
 
-            if (source->monitor_of) {
-                pa_log_warn("Cowardly refusing to set the description for monitor source %s.", source->name);
+            if (s->monitor_of) {
+                pa_log_warn("Cowardly refusing to set the description for monitor source %s.", s->name);
                 continue;
             }
 
-            pa_log_info("Setting description for source %s to '%s'", source->name, e->description);
-            pa_source_set_description(source, e->description);
+            pa_log_info("Setting description for source %s to '%s'", s->name, e->description);
+            pa_source_set_description(s, e->description);
         }
         pa_xfree(n);
     }

commit 9d7a27ec8867ed7907944dbe52ed67bf252c00ee
Author: Colin Guthrie <cguthrie at mandriva.org>
Date:   Fri Oct 2 22:44:56 2009 +0100

    device-manager: Play nice with module-stream-restore.
    
    If m-s-r sets the device we let it do so. Otherwise we handle the routing. We run before
    module-intended-roles as the priority list will likely be configured appropriately
    to do the same job, albeit with manual setup.

diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index 8d17bb0..bfcbfea 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -518,6 +518,9 @@ static void route_sink_input(struct userdata *u, pa_sink_input *si) {
     pa_assert(u);
     pa_assert(u->do_routing);
 
+    if (si->save_sink)
+        return;
+
     /* Skip this if it is already in the process of being moved anyway */
     if (!si->sink)
         return;
@@ -544,7 +547,7 @@ static void route_sink_input(struct userdata *u, pa_sink_input *si) {
         return;
 
     if (si->sink != sink)
-        pa_sink_input_move_to(si, sink, TRUE);
+        pa_sink_input_move_to(si, sink, FALSE);
 }
 
 static pa_hook_result_t route_sink_inputs(struct userdata *u, pa_sink *ignore_sink) {
@@ -573,6 +576,9 @@ static void route_source_output(struct userdata *u, pa_source_output *so) {
     pa_assert(u);
     pa_assert(u->do_routing);
 
+    if (so->save_source)
+        return;
+
     if (so->direct_on_input)
         return;
 
@@ -602,7 +608,7 @@ static void route_source_output(struct userdata *u, pa_source_output *so) {
         return;
 
     if (so->source != source)
-        pa_source_output_move_to(so, source, TRUE);
+        pa_source_output_move_to(so, source, FALSE);
 }
 
 static pa_hook_result_t route_source_outputs(struct userdata *u, pa_source* ignore_source) {
@@ -811,22 +817,24 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
         return PA_HOOK_OK;
 
     if (new_data->sink)
-        pa_log_debug("Overriding device for stream, even although it is already set. I am evil that way...");
-
-    if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
-        role_index = get_role_index("none");
-    else
-        role_index = get_role_index(role);
+        pa_log_debug("Not restoring device for stream because already set.");
+    else {
+        if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
+            role_index = get_role_index("none");
+        else
+            role_index = get_role_index(role);
 
-    if (PA_INVALID_INDEX != role_index) {
-        uint32_t device_index;
+        if (PA_INVALID_INDEX != role_index) {
+            uint32_t device_index;
 
-        device_index = u->preferred_sinks[role_index];
-        if (PA_INVALID_INDEX != device_index) {
-            pa_sink *sink;
+            device_index = u->preferred_sinks[role_index];
+            if (PA_INVALID_INDEX != device_index) {
+                pa_sink *sink;
 
-            if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) {
-                new_data->sink = sink;
+                if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) {
+                    new_data->sink = sink;
+                    new_data->save_sink = FALSE;
+                }
             }
         }
     }
@@ -849,22 +857,24 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou
         return PA_HOOK_OK;
 
     if (new_data->source)
-        pa_log_debug("Overriding device for stream, even although it is already set. I am evil that way...");
-
-    if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
-        role_index = get_role_index("none");
-    else
-        role_index = get_role_index(role);
+        pa_log_debug("Not restoring device for stream because already set.");
+    else {
+        if (!(role = pa_proplist_gets(new_data->proplist, PA_PROP_MEDIA_ROLE)))
+            role_index = get_role_index("none");
+        else
+            role_index = get_role_index(role);
 
-    if (PA_INVALID_INDEX != role_index) {
-        uint32_t device_index;
+        if (PA_INVALID_INDEX != role_index) {
+            uint32_t device_index;
 
-        device_index = u->preferred_sources[role_index];
-        if (PA_INVALID_INDEX != device_index) {
-            pa_source *source;
+            device_index = u->preferred_sources[role_index];
+            if (PA_INVALID_INDEX != device_index) {
+                pa_source *source;
 
-            if ((source = pa_idxset_get_by_index(u->core->sources, device_index))) {
-                new_data->source = source;
+                if ((source = pa_idxset_get_by_index(u->core->sources, device_index))) {
+                    new_data->source = source;
+                    new_data->save_source = FALSE;
+                }
             }
         }
     }
@@ -1422,20 +1432,20 @@ int pa__init(pa_module*m) {
     u->source_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) source_new_hook_callback, u);
 
     /* The following slots are used to deal with routing */
-    /* A little bit later than module-stream-restore, module-intended-roles */
-    u->sink_input_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY+15, (pa_hook_cb_t) sink_input_new_hook_callback, u);
-    u->source_output_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_EARLY+15, (pa_hook_cb_t) source_output_new_hook_callback, u);
+    /* A little bit later than module-stream-restore, but before module-intended-roles */
+    u->sink_input_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY+5, (pa_hook_cb_t) sink_input_new_hook_callback, u);
+    u->source_output_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_EARLY+5, (pa_hook_cb_t) source_output_new_hook_callback, u);
 
     if (on_hotplug) {
-        /* A little bit later than module-stream-restore, module-intended-roles */
-        u->sink_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE+15, (pa_hook_cb_t) sink_put_hook_callback, u);
-        u->source_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE+15, (pa_hook_cb_t) source_put_hook_callback, u);
+        /* A little bit later than module-stream-restore, but before module-intended-roles */
+        u->sink_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE+5, (pa_hook_cb_t) sink_put_hook_callback, u);
+        u->source_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE+5, (pa_hook_cb_t) source_put_hook_callback, u);
     }
 
     if (on_rescue) {
-        /* A little bit later than module-stream-restore, module-intended-roles, a little bit earlier than module-rescue-streams, ... */
-        u->sink_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE+15, (pa_hook_cb_t) sink_unlink_hook_callback, u);
-        u->source_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE+15, (pa_hook_cb_t) source_unlink_hook_callback, u);
+        /* A little bit later than module-stream-restore, a little bit earlier than module-intended-roles, module-rescue-streams, ... */
+        u->sink_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE+5, (pa_hook_cb_t) sink_unlink_hook_callback, u);
+        u->source_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE+5, (pa_hook_cb_t) source_unlink_hook_callback, u);
     }
 
     if (!(fname = pa_state_path("device-manager", TRUE)))

commit 692ce73899285c6ed07e93084a5f830a9a8effcc
Merge: b3592a1 019331d
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Oct 7 03:39:30 2009 +0200

    Merge remote branch 'tanuk/dbus-work'


commit 9f226d25d6c088627fbccefab5867b596a7d31f9
Merge: 692ce73 e895200
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Oct 7 03:43:24 2009 +0200

    Merge remote branch 'phish3/master'


commit d0b478e9a6ecb5c6b74180b1845069946d8f6f10
Merge: 9f226d2 9d7a27e
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Oct 7 03:46:06 2009 +0200

    Merge remote branch 'coling/history'

diff --cc src/modules/module-stream-restore.c
index 1e2dc4d,b7b36be..788f458
--- a/src/modules/module-stream-restore.c
+++ b/src/modules/module-stream-restore.c
@@@ -1122,21 -281,10 +1122,21 @@@ static pa_bool_t entries_equal(const st
  
  static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
      struct userdata *u = userdata;
-     struct entry entry, *old;
-     char *name;
+     struct entry entry, *old = NULL;
+     char *name = NULL;
      pa_datum key, data;
  
 +    /* These are only used when D-Bus is enabled, but in order to reduce ifdef
 +     * clutter these are defined here unconditionally. */
 +    pa_bool_t created_new_entry = TRUE;
 +    pa_bool_t device_updated = FALSE;
 +    pa_bool_t volume_updated = FALSE;
 +    pa_bool_t mute_updated = FALSE;
 +
 +#ifdef HAVE_DBUS
 +    struct dbus_entry *de = NULL;
 +#endif
 +
      pa_assert(c);
      pa_assert(u);
  

-- 
hooks/post-receive
PulseAudio Sound Server



More information about the pulseaudio-commits mailing list