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

Lennart Poettering gitmailer-noreply at 0pointer.de
Tue Jan 27 19:25:59 PST 2009


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

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

- Log -----------------------------------------------------------------
f7c3ca7... Merge commit 'origin/master-tx'
b56e038... Merge commit '12db687acf3befe485bfff3700111999c95247fa'
a5401a5... store the default sink/source in proper pa_sink*/pa_source* pointers instead of a string
fc3ff11... fix two typos
98821c7... print the right software volume
12db687... bluetooth: cold hsp/a2dp device detection
a6a1b42... bluetooth: hsp volume control
611154c... Merge commit 'coling/master'
3affa7e... make m-v-r a stub that simply load m-s-r
63157a6... add missing usage strings
6e31178... Fix the message processing for PA_SINK_MESSAGE_GET_LATENCY by returning rather than breaking and falling through.
514661e... don't make m-e-s hit an assert when the latency is queried
0f664b7... instead of making the volume relative our own, let' pa_sink_input_new() do it for us
e439c18... make m-p-e-s use pa_cvolume_set_balance()
d1b754d... only store volume/device information that has been flagged for saving, and store both relative and absolute volumes
64b0543... when changing volume, store whether it is worth remembering or no
ee17772... add missing 'const'
0ca16ca... add new paramter ignore_dB= to alsa modules
d5f46e8... move flat volume logic into the core. while doing so add n_volume_steps field to sinks/sources
4bfa5d7... fix size calculation
eca3223... get rid of module-flat-volumes since we are moving this into the core
1be39e4... allow samples to be played with 'default' (i.e. unspecified) volume.
5449d79... swap argument order of pa_cvolume_get_balance() to be a bit more systematic
df8ad5d... add a few missing doxygen comments
6058530... import version.h in all header files to make sure that version-based feature testing works
1249cf6... always define PA_MAJOR/PA_MINOR/PA_MICRO to ease feature checking in client applications
948be36... invert an ill-placed assert
0658d9a... show pretty channel map name if possible
07db64b... remove redundant cast
9ba4084... store requested resampling method in a seperate field and use it when create a new resampler after a move
ccd21f3... make a few comments appear in doxygen
3bcbe1d... check for availability of RLIMIT_NOFILE and RLIMIT_AS before we make use of it
4e31e00... implement pa_cvolume_scale()
e52c5ea... implement new API functions pa_channel_map_can_balance(), pa_channel_map_to_name() and pa_channel_map_to_pretty_name()
24b3a74... add a bitset implementation
afd817a... rate limit a warning
a365c82... include a few HAL properties in our card/sink/source properties for ALSA devices
54dad91... use pa_log_ratelimit() at a few places
77c4ccf... add pa_log_rate_limit()
3dfe70c... add generic rate limiting implementation
e960125... add support for static mutexes
db27c63... make module-alsa-card move streams between the old and new sink/source, allowing 'hot' switching between profiles
640d317... add functions to move all inputs of a sink away/similar for source outputs
29cb778... move sink input/source output move functions into two parts so that we can start the move, delete the original sink, create a new sink, finish the move; similar for source outputs
cf24b57... in most cases we can use i->core instead of i->sink->core and o->coure instead of o->source->core
d5e088d... include list of sinks/source in card dump
967c17a... teach module-rescue-streams and module-always-sink to not do anything if we are shutting down anyway
a3162a3... maintain a pa_core state variable
88c9f9f... allow sample spec/channel map to be queried for pa_resampler objects
7bdbcd0... drop --ltdl from the libtoolize invocation, since we don't ship ltdl anymore
f6fcbed... Merge commit 'flameeyes/flameeyes'
5cb29f3... add a simple abstraction for SIMD operations
2a238b2... don't overflow when we do digital amplification of 16 bit samples
3e5d9fd... Use #ifdef to avoid warning about undefined macro.
ddbe612... use pthread_setaffinity_np() only when it is available
a257448... Improve the ltdl discovery code by checking for libtool 2.x functions.
3293251... Move the safety check about pkg-config in bootstrap.sh.
1b20d28... Fix logic thinko.
c65d3a9... Remove support for internal distributing and bundling of libltdl.
7c7133e... NetBSD sometimes doesn't know SNDCTL_DSP_GETODELAY
cef5f48... make rtstutter use pa_ncpus()
4dc1916... add API pa_ncpus()
c0e4e5a... NetBSD doesn't know getgrnam_r()/getpwnam_r()
61075a7... NetBSD doesn't know ENOLINK
ca6b791... It is more portable to assume that SO_RCVBUF/SO_SNDBUF takes and int instead of a size_t
8d89ccd... NetBSD specific atomic operation implementation
cc425ed... NetBSD doesn't know RLIMIT_AS
75eeea6... NetBSD needs to include sys/uio.h for some socket functions
bb23932... When resuming an OSS device ask for the very same fragment settings as we did the first time
3be4c31... rework module-hal-detect and make it use module-alsa-card instead of module-alsa-sink/-source
b2ef19a... include PA_SINK_INVALID_STATE in all switch/case statements to make gcc shut up
4b2a682... fix minor memleak in prober
b606c09... rework logic how alsa sinks/sources/cards are named
7c11554... make gcc shut up
1c84251... fix segfault when in record-only mode
8519f54... only reread volume if we actually have a good mixer. Closes #466
40f2e21... make gcc shut up a bit more
a5c9546... fix copy'n'paste error
251f720... add new function pa_strna
36362f6... add new function pa_card_suspend()
bdfec1f... mark a few more ALSA dB values as 'valid' for valgrind
0f7954a... don't include full path in driver name.
bf7217b... require autoconf 2.63
601293d... implement pactl set-card-profile
996bba7... implement PA_COMMAND_SET_CARD_PROFILE
1375a9a... enable module-card-restore by default
13315a7... add a card profile restore module
c512ebf... minor cleanups
10e5c70... don't restore mute/volume when already set
9661cd0... make pa_card_new_data::active_profile a string
e8f93b1... make implementation of module-alsa-card complete
7ca0e00... fill in dev_id properly
28f05e0... remove leftover define
cba4c6b... when changing profiles do the actual assignment in the generic implementation
1d0bd6e... remove bogus pa_core_check_idle() call
dc2a4bd... add set-card-profile CLI command
9a0dbda... allow cards be referenced by their index
b6b0e07... fix copy/paste error
16d200e... add an API to create arbitrary alsa sinks/sources dynamically without having to load/unload modules
b88b89a... add new call pa_alsa_open_by_device_id_profile()
04e9214... export pa_channel_map_superset()
7368a6e... add priority logic to find best default profile
b3a043f... always add 'disabled' profile
e0f8c13... remove unused variable
86f3fb8... show active profile
a65c2c7... add client API for querying card information
85bc5eb... dump active profile
7aa7a7b... fix destruction when no profiles are defined
9368623... don't divide by zero if no left resp. no right channels are defined
67fcc76... fix profile names to include input/output specifier
b23efc0... add missing eof checks
47a2f9e... Merge commit 'flameeyes/buildfixes-2'
bc41fdb... Include the alsa/ subdirectory for modules in the search path.
bd70e80... Allow to opt-out from building tests.
d5e895d... document that I am a retard
8839d86... remove misplaced whitespace
8c4e2be... include sink/source state in pactl output
8886e66... Document explicitly that the internal sink/source states are not considered part of the ABI/API
310f433... pulse: share private enum values with client side
a3762a2... cli: fix broken array access with signed state enums
6374f8e... sink: trigger subscribe event on sink state change
9c4f8e6... pulse: introspect sink state
f83111d... Merge commit 'vudentz/master'
96f01bf... Merge commit '7104d54bbce8f9bd2553e16f45f3a0f69ac75b8b'
5f6641c... Beef pactl output up a bit
ed65081... show dB and balance for cached samples
033791c... fix up balance format string a bit
23cd942... fix doxygen version references
b987e5e... fix bad free()
b43a45d... allow setting properties for modules, too
fe70301... show balance value in CLI listings
723d71a... add api for manipulating volume balances
7104d54... Add proper -I directives for out-of-tree builds.
348c2ca... Create only the directory the current target should be created into.
4460a5d... Fix hsp rate and channels.
606cf8a... get rid of pa_module_get_info because it is not used
76ca5b8... beautify cli output a bit
f8ba3a9... dump profiles when listing cards
c06e43d... actually create pa_card object in module-alsa-card
c560aea... Don't enumerate invalid profile
f03a7e4... Split up pa_alsa_init_proplist into two seperate functions for the card and snd_pcm_t specific parts
a45f971... add pa_proplist_to_string_sep()
4a66837... add pa_strbuf_isempty
b4d8046... add card profile prober
b2b2eb1... remvoe a bit of duplicate code
5793f93... make use of PR_SET_TIMERSLACK
4a13763... Add support for 24bit samples encoded in the LSB of 32 bit words
6dc76d1... add support for 24bit packed samples
9955398... fix version info in protocol history
4d4956e... Add SPDIF/HDMI ALSA devices and device descriptions to device search table
33c22b0... rename card config to card profile
d4bda31... include libcli.la in libprotocol-cli's dependencies
4210119... add stub makefiles for oss and alsa subdirs
c7fff97... move alsa and oss modules into their own subdirectories
bae221c... rework module usage counter stuff to be pull based
edf88a5... don't show autoload flag anymore since it is obsolete
47a2b17... make proplist inheritance scheme automatic and implicit
e68e4a5... make things compile again
395523a... we don't support glib1.2 anymore
29c7a28... kill autoload stuff as planned
43762ed... flat-volume: use pa_sink_get_volume(s, TRUE) to work with slaved sink
a861ffa... Merge commit 'e0f8ffe41f99789fafac575e944acf02e940bbf7'
d2757c9... redirect folks to the ALSA developers not me when their sound drivers are broken
615e055... add functionality to dump list of cards
344c934... maintain a list of sink inputs/source outputs as part of the pa_client object
b6deb0c... add new pa_card object as a way to logically combine multiple sinks and sources
aeb0707... fix bad memory access
0b0b3d8... make PA_CONTEXT_IS_GOOD/PA_STREAM_IS_GOOD a macro so that we can easily check for its availability
a3695dd... port missing modules to new pa_client_new() API
5abda63... convert pa_client instantiation to use a pa_client_new_data struct and add hooks for manipulating it
75119e9... add new dont_rewind_render flag to allow quick starts of newly created streams
06de639... don't rely on PA_SINK_RUNNING vs. PA_SINK_IDLE for optimizations since it might not be fully up to date
d1cf0e7... fix a potential format string vulnerability
fd3f5db... document that PA_API_VERSION is only for incompatible API changes
e0f8ffe... match: add "key" argument to match different properties
e97ed21... match: can now change properties also
6ec0162... sink: add a virtual_volume to sink
20edd84... make pa_asyncq_push() fail under no circumstances.
587a08b... Fix a typo I know owe Marc-Andre a beer for.
df56404... Fix a potentially non-returning function in base64 code.
f310113... Merge commit 'elmarco/master'
ab97364... remove calc_sine() since we don't need it anymore
cd45cd9... include new proplist functions in export list
407b4fe... fix calculation of avail_min
1872526... add pa_proplist_size() and pa_proplist_isempty()
ef5a2b5... Fix version info
09641cc... build: fix few warnings
49ae383... cli: add missing update-*-proplist
2204bbe... core: add source, si, so proplist_update
3d631df... build: print more informations about preopen
9d6e9f5... cli: update-sink-proplist
01f71ac... libpulse: add proplist_from_string
9e978c9... core: report remaining shared objects when cleanup
ebb903a... core: add pa_source_update_proplist
9444347... core: add pa_sink_update_proplist
f6ac7b4... bump version/soname
-----------------------------------------------------------------------

Summary of changes:
 Makefile.am                                        |    2 +-
 PROTOCOL                                           |   10 +-
 bootstrap.sh                                       |   15 +-
 configure.ac                                       |  212 ++++---
 man/pulse-daemon.conf.5.xml.in                     |    9 +-
 src/.gitignore                                     |    1 +
 src/Makefile.am                                    |   89 ++-
 src/daemon/cmdline.c                               |    6 -
 src/daemon/daemon-conf.c                           |   28 +-
 src/daemon/daemon-conf.h                           |   13 +-
 src/daemon/daemon.conf.in                          |    2 +
 src/daemon/default.pa.in                           |    4 +-
 src/daemon/main.c                                  |   15 +-
 src/map-file                                       |   16 +
 src/modules/{bluetooth => alsa}/Makefile           |    0 
 .../{module-alsa-sink.c => alsa/alsa-sink.c}       |  250 ++++----
 src/{pulse/error.h => modules/alsa/alsa-sink.h}    |   19 +-
 .../{module-alsa-source.c => alsa/alsa-source.c}   |  249 ++++----
 src/{pulse/error.h => modules/alsa/alsa-source.h}  |   19 +-
 src/modules/{ => alsa}/alsa-util.c                 |  526 +++++++++++-----
 src/modules/{ => alsa}/alsa-util.h                 |   42 ++-
 src/modules/alsa/module-alsa-card.c                |  360 +++++++++++
 src/modules/alsa/module-alsa-sink.c                |  125 ++++
 src/modules/alsa/module-alsa-source.c              |  149 +++++
 src/modules/bluetooth/module-bluetooth-device.c    |  233 +++++++-
 src/modules/bluetooth/module-bluetooth-discover.c  |  291 +++++++++-
 src/modules/bluetooth/module-bluetooth-proximity.c |    4 +-
 src/modules/dbus-util.h                            |    2 +
 src/modules/hal-util.c                             |  127 ++++
 src/{pulsecore/avahi-wrap.h => modules/hal-util.h} |   12 +-
 src/modules/module-always-sink.c                   |   10 +-
 src/modules/module-card-restore.c                  |  284 +++++++++
 src/modules/module-combine.c                       |    5 +-
 src/modules/module-console-kit.c                   |   22 +-
 src/modules/module-default-device-restore.c        |   22 +-
 src/modules/module-defs.h.m4                       |    2 +
 src/modules/module-detect.c                        |    2 +-
 src/modules/module-device-restore.c                |   40 +-
 src/modules/module-esound-sink.c                   |   12 +-
 src/modules/module-flat-volume.c                   |  224 -------
 src/modules/module-hal-detect.c                    |  657 +++++++++-----------
 src/modules/module-jack-sink.c                     |    9 +
 src/modules/module-jack-source.c                   |    9 +
 src/modules/module-ladspa-sink.c                   |   15 +-
 src/modules/module-lirc.c                          |    6 +-
 src/modules/module-match.c                         |   66 ++-
 src/modules/module-mmkbd-evdev.c                   |    6 +-
 src/modules/module-null-sink.c                     |    9 +
 src/modules/module-pipe-sink.c                     |    9 +
 src/modules/module-pipe-source.c                   |    9 +
 src/modules/module-position-event-sounds.c         |   42 +-
 src/modules/module-raop-sink.c                     |   44 +-
 src/modules/module-remap-sink.c                    |   15 +-
 src/modules/module-rescue-streams.c                |   16 +-
 src/modules/module-sine-source.c                   |   22 +-
 src/modules/module-sine.c                          |    4 +-
 src/modules/module-stream-restore.c                |  228 ++++++--
 src/modules/module-suspend-on-idle.c               |   68 ++-
 src/modules/module-tunnel.c                        |   18 +-
 src/modules/module-volume-restore.c                |  507 +---------------
 src/modules/module-x11-bell.c                      |    2 +-
 src/modules/module-x11-xsmp.c                      |   19 +-
 src/modules/{bluetooth => oss}/Makefile            |    0 
 src/modules/{ => oss}/module-oss.c                 |  100 ++--
 src/modules/{ => oss}/oss-util.c                   |   17 +-
 src/modules/{ => oss}/oss-util.h                   |    0 
 src/modules/raop/base64.c                          |    9 +-
 src/modules/rtp/module-rtp-recv.c                  |    4 +-
 src/modules/rtp/module-rtp-send.c                  |    2 +-
 src/modules/rtp/rtp.c                              |    4 +
 src/modules/rtp/sap.c                              |    4 +
 src/pulse/browser.h                                |    1 +
 src/pulse/channelmap.c                             |  200 ++++++
 src/pulse/channelmap.h                             |   25 +-
 src/pulse/context.c                                |    3 +-
 src/pulse/context.h                                |    5 +-
 src/pulse/def.h                                    |  135 ++++-
 src/pulse/error.h                                  |    1 +
 src/pulse/ext-stream-restore.h                     |    1 +
 src/pulse/gccmacro.h                               |    4 +
 src/pulse/glib-mainloop.h                          |    1 +
 src/pulse/i18n.h                                   |    1 +
 src/pulse/internal.h                               |   37 +-
 src/pulse/introspect.c                             |  388 +++++++-----
 src/pulse/introspect.h                             |  100 ++-
 src/pulse/mainloop-api.h                           |    1 +
 src/pulse/mainloop-signal.h                        |    2 +
 src/pulse/operation.h                              |    1 +
 src/pulse/proplist.c                               |  117 ++++-
 src/pulse/proplist.h                               |   38 +-
 src/pulse/pulseaudio.h                             |    1 -
 src/pulse/sample.c                                 |   26 +-
 src/pulse/sample.h                                 |   55 ++-
 src/pulse/scache.c                                 |    8 +
 src/pulse/scache.h                                 |    5 +-
 src/pulse/simple.h                                 |    1 +
 src/pulse/stream.c                                 |    2 +
 src/pulse/subscribe.h                              |    1 +
 src/pulse/thread-mainloop.h                        |    1 +
 src/pulse/timeval.h                                |   12 +
 src/pulse/utf8.h                                   |    1 +
 src/pulse/util.h                                   |    1 +
 src/pulse/version.h.in                             |   12 +-
 src/pulse/volume.c                                 |  119 ++++-
 src/pulse/volume.h                                 |   30 +-
 src/pulse/xmalloc.h                                |    1 +
 src/pulsecore/asyncq.c                             |   16 +-
 src/pulsecore/atomic.h                             |   73 +++
 src/pulsecore/autoload.c                           |  202 ------
 src/pulsecore/autoload.h                           |   58 --
 src/pulsecore/{semaphore-win32.c => bitset.c}      |   58 +-
 src/pulsecore/{sioman.c => bitset.h}               |   26 +-
 src/pulsecore/card.c                               |  254 ++++++++
 src/pulsecore/card.h                               |  100 +++
 src/pulsecore/cli-command.c                        |  374 ++++++++----
 src/pulsecore/cli-text.c                           |  264 ++++++---
 src/pulsecore/cli-text.h                           |    3 +-
 src/pulsecore/cli.c                                |   19 +-
 src/pulsecore/client.c                             |   48 ++-
 src/pulsecore/client.h                             |   17 +-
 src/pulsecore/core-scache.c                        |   42 +-
 src/pulsecore/core-scache.h                        |    3 +-
 src/pulsecore/core-util.c                          |   60 ++-
 src/pulsecore/core-util.h                          |    8 +-
 src/pulsecore/core.c                               |   23 +-
 src/pulsecore/core.h                               |   41 +-
 src/pulsecore/endianmacros.h                       |   38 ++
 src/pulsecore/envelope.c                           |    1 -
 src/pulsecore/log.c                                |    8 +
 src/pulsecore/log.h                                |    2 +
 src/pulsecore/module.c                             |  101 +---
 src/pulsecore/module.h                             |   14 +-
 src/pulsecore/mutex-posix.c                        |   20 +
 src/pulsecore/mutex.h                              |    7 +
 src/pulsecore/namereg.c                            |  120 ++--
 src/pulsecore/namereg.h                            |   13 +-
 src/pulsecore/native-common.h                      |   14 +-
 src/pulsecore/pdispatch.c                          |    8 +-
 src/pulsecore/play-memblockq.c                     |    4 +-
 src/pulsecore/protocol-esound.c                    |   40 +-
 src/pulsecore/protocol-http.c                      |   10 +-
 src/pulsecore/protocol-native.c                    |  386 ++++++------
 src/pulsecore/protocol-simple.c                    |   24 +-
 src/pulsecore/ratelimit.c                          |   75 +++
 src/pulsecore/ratelimit.h                          |   46 ++
 src/pulsecore/resampler.c                          |   32 +-
 src/pulsecore/resampler.h                          |    4 +
 src/pulsecore/rtclock.c                            |   26 +
 src/pulsecore/rtclock.h                            |    1 +
 src/pulsecore/sample-util.c                        |  329 +++++++++-
 src/pulsecore/sconv-s16be.c                        |   30 +-
 src/pulsecore/sconv-s16be.h                        |   30 +-
 src/pulsecore/sconv-s16le.c                        |  225 +++++++-
 src/pulsecore/sconv-s16le.h                        |   30 +-
 src/pulsecore/sconv.c                              |   16 +
 src/pulsecore/shared.c                             |    9 +-
 src/pulsecore/sink-input.c                         |  391 ++++++++----
 src/pulsecore/sink-input.h                         |   78 ++--
 src/pulsecore/sink.c                               |  348 +++++++++--
 src/pulsecore/sink.h                               |   64 +-
 src/pulsecore/socket-util.c                        |    8 +-
 src/pulsecore/sound-file-stream.c                  |    4 +-
 src/pulsecore/source-output.c                      |  199 +++++--
 src/pulsecore/source-output.h                      |   38 +-
 src/pulsecore/source.c                             |  170 ++++--
 src/pulsecore/source.h                             |   51 +-
 src/pulsecore/strbuf.c                             |    6 +
 src/pulsecore/strbuf.h                             |    3 +
 src/pulsecore/tagstruct.h                          |    4 +
 src/pulsecore/vector.h                             |   97 +++
 src/tests/proplist-test.c                          |   11 +-
 src/tests/rtstutter.c                              |   19 +-
 src/tests/vector-test.c                            |   83 +++
 src/tests/voltest.c                                |   29 +
 src/utils/pactl.c                                  |  368 +++++++----
 175 files changed, 8325 insertions(+), 3708 deletions(-)
 copy src/modules/{bluetooth => alsa}/Makefile (100%)
 rename src/modules/{module-alsa-sink.c => alsa/alsa-sink.c} (91%)
 copy src/{pulse/error.h => modules/alsa/alsa-sink.h} (72%)
 rename src/modules/{module-alsa-source.c => alsa/alsa-source.c} (90%)
 copy src/{pulse/error.h => modules/alsa/alsa-source.h} (72%)
 rename src/modules/{ => alsa}/alsa-util.c (74%)
 rename src/modules/{ => alsa}/alsa-util.h (71%)
 create mode 100644 src/modules/alsa/module-alsa-card.c
 create mode 100644 src/modules/alsa/module-alsa-sink.c
 create mode 100644 src/modules/alsa/module-alsa-source.c
 create mode 100644 src/modules/hal-util.c
 copy src/{pulsecore/avahi-wrap.h => modules/hal-util.h} (75%)
 create mode 100644 src/modules/module-card-restore.c
 delete mode 100644 src/modules/module-flat-volume.c
 copy src/modules/{bluetooth => oss}/Makefile (100%)
 rename src/modules/{ => oss}/module-oss.c (94%)
 rename src/modules/{ => oss}/oss-util.c (95%)
 rename src/modules/{ => oss}/oss-util.h (100%)
 delete mode 100644 src/pulsecore/autoload.c
 delete mode 100644 src/pulsecore/autoload.h
 copy src/pulsecore/{semaphore-win32.c => bitset.c} (51%)
 copy src/pulsecore/{sioman.c => bitset.h} (64%)
 create mode 100644 src/pulsecore/card.c
 create mode 100644 src/pulsecore/card.h
 create mode 100644 src/pulsecore/ratelimit.c
 create mode 100644 src/pulsecore/ratelimit.h
 create mode 100644 src/pulsecore/vector.h
 create mode 100644 src/tests/vector-test.c

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

commit f6ac7b4ed6ad2d4f10def0f5657e25692bcb2e2b
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Jan 12 23:36:28 2009 +0100

    bump version/soname

diff --git a/configure.ac b/configure.ac
index c24658b..bd40948 100644
--- a/configure.ac
+++ b/configure.ac
@@ -24,7 +24,7 @@ AC_PREREQ(2.63)
 
 m4_define(PA_MAJOR, [0])
 m4_define(PA_MINOR, [9])
-m4_define(PA_MICRO, [14])
+m4_define(PA_MICRO, [15])
 
 AC_INIT([pulseaudio],[PA_MAJOR.PA_MINOR.PA_MICRO],[mzchyfrnhqvb (at) 0pointer (dot) net])
 AC_CONFIG_SRCDIR([src/daemon/main.c])
@@ -41,7 +41,7 @@ AC_SUBST(PA_PROTOCOL_VERSION, 15)
 
 # The stable ABI for client applications, for the version info x:y:z
 # always will hold y=z
-AC_SUBST(LIBPULSE_VERSION_INFO, [7:0:7])
+AC_SUBST(LIBPULSE_VERSION_INFO, [7:1:7])
 
 # A simplified, synchronous, ABI-stable interface for client
 # applications, for the version info x:y:z always will hold y=z

commit 9444347c061f271ac74d550b5f3f4b7a803a26d6
Author: Marc-André Lureau <marcandre.lureau at gmail.com>
Date:   Fri Oct 31 02:33:28 2008 +0200

    core: add pa_sink_update_proplist

diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index a4d993c..d5082d5 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -953,6 +953,20 @@ pa_bool_t pa_sink_get_mute(pa_sink *s, pa_bool_t force_refresh) {
     return s->muted;
 }
 
+pa_bool_t pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p) {
+
+    pa_sink_assert_ref(s);
+
+    pa_proplist_update(s->proplist, mode, p);
+
+    if (PA_SINK_IS_LINKED(s->state)) {
+        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], s);
+        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+    }
+
+    return TRUE;
+}
+
 /* Called from main thread */
 void pa_sink_set_description(pa_sink *s, const char *description) {
     const char *old;
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index 254be3b..092e30f 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -252,6 +252,8 @@ const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_bool_t force_refresh);
 void pa_sink_set_mute(pa_sink *sink, pa_bool_t mute);
 pa_bool_t pa_sink_get_mute(pa_sink *sink, pa_bool_t force_refres);
 
+pa_bool_t pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p);
+
 unsigned pa_sink_linked_by(pa_sink *s); /* Number of connected streams */
 unsigned pa_sink_used_by(pa_sink *s); /* Number of connected streams which are not corked */
 unsigned pa_sink_check_suspend(pa_sink *s); /* Returns how many streams are active that don't allow suspensions */

commit ebb903a37697430f7e2d1ca5314ca23e922a3411
Author: Marc-André Lureau <marcandre.lureau at gmail.com>
Date:   Fri Oct 31 02:38:00 2008 +0200

    core: add pa_source_update_proplist

diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index 815ec27..e65c5ce 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -604,6 +604,20 @@ pa_bool_t pa_source_get_mute(pa_source *s, pa_bool_t force_refresh) {
     return s->muted;
 }
 
+pa_bool_t pa_source_update_proplist(pa_source *s, pa_update_mode_t mode, pa_proplist *p) {
+
+    pa_source_assert_ref(s);
+
+    pa_proplist_update(s->proplist, mode, p);
+
+    if (PA_SOURCE_IS_LINKED(s->state)) {
+        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED], s);
+        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+    }
+
+    return TRUE;
+}
+
 /* Called from main thread */
 void pa_source_set_description(pa_source *s, const char *description) {
     const char *old;
diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
index b93e4ad..fd8c4bd 100644
--- a/src/pulsecore/source.h
+++ b/src/pulsecore/source.h
@@ -234,6 +234,8 @@ const pa_cvolume *pa_source_get_volume(pa_source *source, pa_bool_t force_refres
 void pa_source_set_mute(pa_source *source, pa_bool_t mute);
 pa_bool_t pa_source_get_mute(pa_source *source, pa_bool_t force_refresh);
 
+pa_bool_t pa_source_update_proplist(pa_source *s, pa_update_mode_t mode, pa_proplist *p);
+
 unsigned pa_source_linked_by(pa_source *s); /* Number of connected streams */
 unsigned pa_source_used_by(pa_source *s); /* Number of connected streams that are not corked */
 unsigned pa_source_check_suspend(pa_source *s); /* Returns how many streams are active that don't allow suspensions */

commit 9e978c97702ef199764671b68def5c0e42a32250
Author: Marc-André Lureau <marcandre.lureau at gmail.com>
Date:   Thu Dec 4 16:23:06 2008 +0200

    core: report remaining shared objects when cleanup

diff --git a/src/pulsecore/shared.c b/src/pulsecore/shared.c
index 77d919d..4c1ad80 100644
--- a/src/pulsecore/shared.c
+++ b/src/pulsecore/shared.c
@@ -111,7 +111,14 @@ void pa_shared_cleanup(pa_core *c) {
     if (!c->shared)
         return;
 
-    pa_assert(pa_hashmap_isempty(c->shared));
+    if (!pa_hashmap_isempty(c->shared)) {
+        pa_strbuf *s = pa_strbuf_new();
+
+        pa_shared_dump(c, s);
+        pa_log_debug(pa_strbuf_tostring(s));
+        pa_strbuf_free(s);
+        pa_assert(pa_hashmap_isempty(c->shared));
+    }
 
     pa_hashmap_free(c->shared, NULL, NULL);
     c->shared = NULL;

commit 01f71ac7a1fc2cb2f7b29e563a3468c2ffe05313
Author: Marc-André Lureau <marcandre.lureau at gmail.com>
Date:   Mon Oct 27 21:14:50 2008 +0200

    libpulse: add proplist_from_string

diff --git a/src/map-file b/src/map-file
index 82b9c38..59006fe 100644
--- a/src/map-file
+++ b/src/map-file
@@ -156,6 +156,7 @@ pa_proplist_set;
 pa_proplist_setf;
 pa_proplist_sets;
 pa_proplist_to_string;
+pa_proplist_from_string;
 pa_proplist_unset;
 pa_proplist_unset_many;
 pa_proplist_update;
diff --git a/src/pulse/proplist.c b/src/pulse/proplist.c
index 93bc003..1694284 100644
--- a/src/pulse/proplist.c
+++ b/src/pulse/proplist.c
@@ -291,6 +291,90 @@ char *pa_proplist_to_string(pa_proplist *p) {
     return pa_strbuf_tostring_free(buf);
 }
 
+/* Remove all whitepsapce from the beginning and the end of *s. *s may
+ * be modified. (from conf-parser.c) */
+#define WHITESPACE " \t\n"
+#define in_string(c,s) (strchr(s,c) != NULL)
+
+static char *strip(char *s) {
+    char *b = s+strspn(s, WHITESPACE);
+    char *e, *l = NULL;
+
+    for (e = b; *e; e++)
+        if (!in_string(*e, WHITESPACE))
+            l = e;
+
+    if (l)
+        *(l+1) = 0;
+
+    return b;
+}
+
+pa_proplist *pa_proplist_from_string(const char *str) {
+    pa_proplist *p;
+    char *s, *v, *k, *e;
+
+    pa_assert(str);
+    pa_assert_se(p = pa_proplist_new());
+    pa_assert_se(s = strdup(str));
+
+    for (k = s; *k; k = e) {
+        k = k+strspn(k, WHITESPACE);
+
+        if (!*k)
+            break;
+
+        if (!(v = strchr(k, '='))) {
+            pa_log("Missing '='.");
+            break;
+        }
+
+        *v++ = '\0';
+        k = strip(k);
+
+        v = v+strspn(v, WHITESPACE);
+        if (*v == '"') {
+            v++;
+            if (!(e = strchr(v, '"'))) { /* FIXME: handle escape */
+                pa_log("Missing '\"' at end of string value.");
+                break;
+            }
+            *e++ = '\0';
+            pa_proplist_sets(p, k, v);
+        } else {
+            uint8_t *blob;
+
+            if (*v++ != 'h' || *v++ != 'e' || *v++ != 'x' || *v++ != ':') {
+                pa_log("Value must be a string or \"hex:\"");
+                break;
+            }
+
+            e = v;
+            while (in_string(*e, "0123456789abcdefABCDEF"))
+                ++e;
+
+            if ((e - v) % 2) {
+                pa_log("Invalid \"hex:\" value data");
+                break;
+            }
+
+            blob = pa_xmalloc((size_t)(e-v)/2);
+            if (pa_parsehex(v, blob, (e-v)/2) != ((e-v)/2)) {
+                pa_log("Invalid \"hex:\" value data");
+                pa_xfree(blob);
+                break;
+            }
+
+            pa_proplist_set(p, k, blob, (e-v)/2);
+            pa_xfree(blob);
+        }
+    }
+
+    pa_xfree(s);
+
+    return p;
+}
+
 int pa_proplist_contains(pa_proplist *p, const char *key) {
     pa_assert(p);
     pa_assert(key);
diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h
index c23ef23..4f1a1ec 100644
--- a/src/pulse/proplist.h
+++ b/src/pulse/proplist.h
@@ -213,7 +213,11 @@ const char *pa_proplist_iterate(pa_proplist *p, void **state);
  * 0.9.11 */
 char *pa_proplist_to_string(pa_proplist *p);
 
-/** Returns 1 if an entry for the specified key is existant in the
+/** Allocate a new property list and assign key/value from a human readable string. \since
+ * 0.9.14 */
+pa_proplist *pa_proplist_from_string(const char *str);
+
+  /** 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);
 
diff --git a/src/tests/proplist-test.c b/src/tests/proplist-test.c
index 20041af..f69fa68 100644
--- a/src/tests/proplist-test.c
+++ b/src/tests/proplist-test.c
@@ -29,8 +29,8 @@
 #include <pulsecore/core-util.h>
 
 int main(int argc, char*argv[]) {
-    pa_proplist *a, *b;
-    char *s, *t;
+    pa_proplist *a, *b, *c;
+    char *s, *t, *u;
 
     a = pa_proplist_new();
     pa_assert_se(pa_proplist_sets(a, PA_PROP_MEDIA_TITLE, "Brandenburgische Konzerte") == 0);
@@ -50,11 +50,18 @@ int main(int argc, char*argv[]) {
     s = pa_proplist_to_string(a);
     t = pa_proplist_to_string(b);
     printf("---\n%s---\n%s", s, t);
+
+    c = pa_proplist_from_string(s);
+    u = pa_proplist_to_string(c);
+    pa_assert_se(pa_streq(s, u));
+
     pa_xfree(s);
     pa_xfree(t);
+    pa_xfree(u);
 
     pa_proplist_free(a);
     pa_proplist_free(b);
+    pa_proplist_free(c);
 
     return 0;
 }

commit 9d6e9f51df19c161646448cf922d6b216ea5c3f6
Author: Marc-André Lureau <marcandre.lureau at gmail.com>
Date:   Fri Oct 31 18:43:38 2008 +0200

    cli: update-sink-proplist

diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c
index b5ff98d..3cb2a14 100644
--- a/src/pulsecore/cli-command.c
+++ b/src/pulsecore/cli-command.c
@@ -121,6 +121,7 @@ static int pa_cli_command_log_level(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,
 static int pa_cli_command_log_meta(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
 static int pa_cli_command_log_time(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
 static int pa_cli_command_log_backtrace(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_update_sink_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
 
 /* A method table for all available commands */
 
@@ -146,6 +147,7 @@ static const struct command commands[] = {
     { "set-sink-mute",           pa_cli_command_sink_mute,          "Set the mute switch of a sink (args: index|name, bool)", 3},
     { "set-sink-input-mute",     pa_cli_command_sink_input_mute,    "Set the mute switch of a sink input (args: index, bool)", 3},
     { "set-source-mute",         pa_cli_command_source_mute,        "Set the mute switch of a source (args: index|name, bool)", 3},
+    { "update-sink-proplist",    pa_cli_command_update_sink_proplist, "Update the properties of a sink (args: index|name, properties)", 3},
     { "set-default-sink",        pa_cli_command_sink_default,       "Set the default sink (args: index|name)", 2},
     { "set-default-source",      pa_cli_command_source_default,     "Set the default source (args: index|name)", 2},
     { "kill-client",             pa_cli_command_kill_client,        "Kill a client (args: index)", 2},
@@ -650,6 +652,38 @@ static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
     return 0;
 }
 
+static int pa_cli_command_update_sink_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    const char *n, *s;
+    pa_sink *sink;
+    pa_proplist *p;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
+        return -1;
+    }
+
+    if (!(s = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a \"key=value\" argument.\n");
+        return -1;
+    }
+
+    if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK, 1))) {
+        pa_strbuf_puts(buf, "No sink found by this name or index.\n");
+        return -1;
+    }
+
+    p = pa_proplist_from_string(s);
+
+    pa_sink_update_proplist(sink, PA_UPDATE_REPLACE, p);
+
+    return 0;
+}
+
 static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
     const char *n, *v;
     pa_sink_input *si;
@@ -1513,7 +1547,7 @@ int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *b
         size_t l;
 
         if (ifstate && *ifstate == IFSTATE_FALSE)
-             return 0;
+            return 0;
 
         l = strcspn(cs, whitespace);
 

commit 3d631df8dd4017a17bfd196783bfe3d4898feb63
Author: Marc-André Lureau <marcandre.lureau at gmail.com>
Date:   Mon Nov 10 19:11:40 2008 +0200

    build: print more informations about preopen

diff --git a/configure.ac b/configure.ac
index bd40948..8586dcd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1139,8 +1139,8 @@ AC_SUBST(modlibexecdir)
 AC_ARG_ENABLE(
         [force-preopen],
         AS_HELP_STRING([--enable-force-preopen],[Preopen modules, even when dlopen() is supported.]),
-        [FORCE_PREOPEN=1], [FORCE_PREOPEN=0])
-AM_CONDITIONAL([FORCE_PREOPEN], [test "x$FORCE_PREOPEN" = "x1"])
+        [FORCE_PREOPEN=$enableval], [FORCE_PREOPEN=no])
+AM_CONDITIONAL([FORCE_PREOPEN], [test "x$FORCE_PREOPEN" = "xyes"])
 
 AC_CONFIG_FILES([
 Makefile
@@ -1255,6 +1255,7 @@ echo "
     System Config Path:            ${PA_SYSTEM_CONFIG_PATH}
     Compiler:                      ${CC}
     CFLAGS:                        ${CFLAGS}
+
     Have X11:                      ${ENABLE_X11}
     Enable OSS:                    ${ENABLE_OSS}
     Enable Alsa:                   ${ENABLE_ALSA}
@@ -1271,9 +1272,12 @@ echo "
     Enable libsamplerate:          ${ENABLE_LIBSAMPLERATE}
     Enable PolicyKit:              ${ENABLE_POLKIT}
     Enable OpenSSL (for Airtunes): ${ENABLE_OPENSSL}
+
     System User:                   ${PA_SYSTEM_USER}
     System Group:                  ${PA_SYSTEM_GROUP}
     Realtime Group:                ${PA_REALTIME_GROUP}
     Access Group:                  ${PA_ACCESS_GROUP}
     Enable per-user EsounD socket: ${ENABLE_PER_USER_ESOUND_SOCKET}
+    Force preopen:                 ${FORCE_PREOPEN}
+    Preopened modules:             ${PREOPEN_MODS}
 "

commit 2204bbebf2cd6c72ed436b9bd303f904060c1157
Author: Marc-André Lureau <marcandre.lureau at gmail.com>
Date:   Tue Jan 13 19:06:10 2009 +0200

    core: add source, si, so proplist_update

diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index d25cd79..33490cc 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -859,6 +859,21 @@ pa_bool_t pa_sink_input_get_mute(pa_sink_input *i) {
     return i->muted;
 }
 
+/* Called from main thread */
+pa_bool_t pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode, pa_proplist *p) {
+
+  pa_sink_input_assert_ref(i);
+
+  pa_proplist_update(i->proplist, mode, p);
+
+  if (PA_SINK_IS_LINKED(i->state)) {
+      pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], i);
+      pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+  }
+
+  return TRUE;
+}
+
 /* Called from main context */
 void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b) {
     pa_sink_input_assert_ref(i);
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index 2712598..8cfe32b 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -294,6 +294,7 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume);
 const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i);
 void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute);
 pa_bool_t pa_sink_input_get_mute(pa_sink_input *i);
+pa_bool_t pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode, pa_proplist *p);
 
 pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i);
 
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index d5082d5..48c8f79 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -953,6 +953,7 @@ pa_bool_t pa_sink_get_mute(pa_sink *s, pa_bool_t force_refresh) {
     return s->muted;
 }
 
+/* Called from main thread */
 pa_bool_t pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p) {
 
     pa_sink_assert_ref(s);
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index c92c5ab..e3d0d8b 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -592,6 +592,21 @@ void pa_source_output_set_name(pa_source_output *o, const char *name) {
     }
 }
 
+/* Called from main thread */
+pa_bool_t pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode, pa_proplist *p) {
+
+  pa_source_output_assert_ref(o);
+
+  pa_proplist_update(o->proplist, mode, p);
+
+  if (PA_SINK_IS_LINKED(o->state)) {
+    pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o);
+    pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+  }
+
+  return TRUE;
+}
+
 /* Called from main context */
 pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o) {
     pa_source_output_assert_ref(o);
diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
index f011f9b..2fdebae 100644
--- a/src/pulsecore/source-output.h
+++ b/src/pulsecore/source-output.h
@@ -209,11 +209,11 @@ pa_source_output* pa_source_output_new(
 void pa_source_output_put(pa_source_output *o);
 void pa_source_output_unlink(pa_source_output*o);
 
-void pa_source_output_set_name(pa_source_output *i, const char *name);
+void pa_source_output_set_name(pa_source_output *o, const char *name);
 
-pa_usec_t pa_source_output_set_requested_latency(pa_source_output *i, pa_usec_t usec);
+pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec);
 
-void pa_source_output_cork(pa_source_output *i, pa_bool_t b);
+void pa_source_output_cork(pa_source_output *o, pa_bool_t b);
 
 int pa_source_output_set_rate(pa_source_output *o, uint32_t rate);
 
@@ -222,7 +222,9 @@ int pa_source_output_set_rate(pa_source_output *o, uint32_t rate);
 /* External code may request disconnection with this funcion */
 void pa_source_output_kill(pa_source_output*o);
 
-pa_usec_t pa_source_output_get_latency(pa_source_output *i, pa_usec_t *source_latency);
+pa_usec_t pa_source_output_get_latency(pa_source_output *o, pa_usec_t *source_latency);
+
+pa_bool_t pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode, pa_proplist *p);
 
 pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o);
 

commit 49ae38347cbf9646e69ee4dd8ebe353e04aa650f
Author: Marc-André Lureau <marcandre.lureau at gmail.com>
Date:   Tue Jan 13 19:07:59 2009 +0200

    cli: add missing update-*-proplist

diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c
index 3cb2a14..07d55d0 100644
--- a/src/pulsecore/cli-command.c
+++ b/src/pulsecore/cli-command.c
@@ -122,6 +122,9 @@ static int pa_cli_command_log_meta(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,
 static int pa_cli_command_log_time(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
 static int pa_cli_command_log_backtrace(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
 static int pa_cli_command_update_sink_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_update_source_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_update_sink_input_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_update_source_output_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
 
 /* A method table for all available commands */
 
@@ -148,6 +151,9 @@ static const struct command commands[] = {
     { "set-sink-input-mute",     pa_cli_command_sink_input_mute,    "Set the mute switch of a sink input (args: index, bool)", 3},
     { "set-source-mute",         pa_cli_command_source_mute,        "Set the mute switch of a source (args: index|name, bool)", 3},
     { "update-sink-proplist",    pa_cli_command_update_sink_proplist, "Update the properties of a sink (args: index|name, properties)", 3},
+    { "update-source-proplist",  pa_cli_command_update_source_proplist, "Update the properties of a source (args: index|name, properties)", 3},
+    { "update-sink-input-proplist", pa_cli_command_update_sink_input_proplist, "Update the properties of a sink input (args: index, properties)", 3},
+    { "update-source-output-proplist", pa_cli_command_update_source_output_proplist, "Update the properties of a source_output (args: index, properties)", 3},
     { "set-default-sink",        pa_cli_command_sink_default,       "Set the default sink (args: index|name)", 2},
     { "set-default-source",      pa_cli_command_source_default,     "Set the default source (args: index|name)", 2},
     { "kill-client",             pa_cli_command_kill_client,        "Kill a client (args: index)", 2},
@@ -681,6 +687,122 @@ static int pa_cli_command_update_sink_proplist(pa_core *c, pa_tokenizer *t, pa_s
 
     pa_sink_update_proplist(sink, PA_UPDATE_REPLACE, p);
 
+    pa_proplist_free(p);
+
+    return 0;
+}
+
+static int pa_cli_command_update_source_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    const char *n, *s;
+    pa_source *source;
+    pa_proplist *p;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
+        return -1;
+    }
+
+    if (!(s = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a \"key=value\" argument.\n");
+        return -1;
+    }
+
+    if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE, 1))) {
+        pa_strbuf_puts(buf, "No source found by this name or index.\n");
+        return -1;
+    }
+
+    p = pa_proplist_from_string(s);
+
+    pa_source_update_proplist(source, PA_UPDATE_REPLACE, p);
+
+    pa_proplist_free(p);
+
+    return 0;
+}
+
+static int pa_cli_command_update_sink_input_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    const char *n, *s;
+    pa_sink_input *si;
+    uint32_t idx;
+    pa_proplist *p;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a sink input either by index.\n");
+        return -1;
+    }
+
+    if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
+        pa_strbuf_puts(buf, "Failed to parse index.\n");
+        return -1;
+    }
+
+    if (!(s = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a \"key=value\" argument.\n");
+        return -1;
+    }
+
+    if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) idx))) {
+        pa_strbuf_puts(buf, "No sink input found with this index.\n");
+        return -1;
+    }
+
+    p = pa_proplist_from_string(s);
+
+    pa_sink_input_update_proplist(si, PA_UPDATE_REPLACE, p);
+
+    pa_proplist_free(p);
+
+    return 0;
+}
+
+static int pa_cli_command_update_source_output_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    const char *n, *s;
+    pa_source_output *so;
+    uint32_t idx;
+    pa_proplist *p;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a source output by its index.\n");
+        return -1;
+    }
+
+    if ((idx = parse_index(n)) == PA_IDXSET_INVALID) {
+        pa_strbuf_puts(buf, "Failed to parse index.\n");
+        return -1;
+    }
+
+    if (!(s = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a \"key=value\" argument.\n");
+        return -1;
+    }
+
+    if (!(so = pa_idxset_get_by_index(c->source_outputs, (uint32_t) idx))) {
+        pa_strbuf_puts(buf, "No source output found with this index.\n");
+        return -1;
+    }
+
+    p = pa_proplist_from_string(s);
+
+    pa_source_output_update_proplist(so, PA_UPDATE_REPLACE, p);
+
+    pa_proplist_free(p);
+
     return 0;
 }
 

commit 09641cc707b0cf45368fa9e61ca8607483b4989f
Author: Marc-André Lureau <marcandre.lureau at gmail.com>
Date:   Tue Jan 13 19:08:22 2009 +0200

    build: fix few warnings

diff --git a/src/pulse/def.h b/src/pulse/def.h
index ace5657..7a715b6 100644
--- a/src/pulse/def.h
+++ b/src/pulse/def.h
@@ -428,7 +428,7 @@ typedef enum pa_subscription_event_type {
     PA_SUBSCRIPTION_EVENT_REMOVE = 0x0020U,
     /**< An object was removed */
 
-    PA_SUBSCRIPTION_EVENT_TYPE_MASK = 0x0030U,
+    PA_SUBSCRIPTION_EVENT_TYPE_MASK = 0x0030U
     /**< A mask to extract the event operation from an event value */
 
 } pa_subscription_event_type_t;
diff --git a/src/pulse/proplist.c b/src/pulse/proplist.c
index 1694284..909df9a 100644
--- a/src/pulse/proplist.c
+++ b/src/pulse/proplist.c
@@ -359,7 +359,7 @@ pa_proplist *pa_proplist_from_string(const char *str) {
             }
 
             blob = pa_xmalloc((size_t)(e-v)/2);
-            if (pa_parsehex(v, blob, (e-v)/2) != ((e-v)/2)) {
+            if (pa_parsehex(v, blob, (e-v)/2) != (size_t)((e-v)/2)) {
                 pa_log("Invalid \"hex:\" value data");
                 pa_xfree(blob);
                 break;
diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h
index d9fad11..167d073 100644
--- a/src/pulsecore/core-util.h
+++ b/src/pulsecore/core-util.h
@@ -27,6 +27,7 @@
 #include <inttypes.h>
 #include <stdarg.h>
 #include <stdio.h>
+#include <string.h>
 
 #ifdef HAVE_SYS_RESOURCE_H
 #include <sys/resource.h>

commit ef5a2b5f2c5eca12946c538dc2a6b686b3f2f3ed
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jan 14 00:05:54 2009 +0100

    Fix version info

diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h
index 4f1a1ec..217a770 100644
--- a/src/pulse/proplist.h
+++ b/src/pulse/proplist.h
@@ -214,7 +214,7 @@ const char *pa_proplist_iterate(pa_proplist *p, void **state);
 char *pa_proplist_to_string(pa_proplist *p);
 
 /** Allocate a new property list and assign key/value from a human readable string. \since
- * 0.9.14 */
+ * 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

commit 18725265087dd7f8a8bf8e460b848826361d3f50
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jan 14 00:06:26 2009 +0100

    add pa_proplist_size() and pa_proplist_isempty()

diff --git a/src/pulse/proplist.c b/src/pulse/proplist.c
index 1694284..eec9d31 100644
--- a/src/pulse/proplist.c
+++ b/src/pulse/proplist.c
@@ -406,3 +406,15 @@ pa_proplist* pa_proplist_copy(pa_proplist *template) {
 
     return p;
 }
+
+unsigned pa_proplist_size(pa_proplist *p) {
+    pa_assert(p);
+
+    return pa_hashmap_size(MAKE_HASHMAP(p));
+}
+
+int pa_proplist_isempty(pa_proplist *p) {
+    pa_assert(p);
+
+    return pa_hashmap_isempty(MAKE_HASHMAP(p));
+}
diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h
index 217a770..5d65ff3 100644
--- a/src/pulse/proplist.h
+++ b/src/pulse/proplist.h
@@ -228,6 +228,12 @@ void pa_proplist_clear(pa_proplist *p);
  * the specific list. \since 0.9.11 */
 pa_proplist* pa_proplist_copy(pa_proplist *t);
 
+/** Return the number of entries on the property list. \since 0.9.15 */
+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);
+
 PA_C_DECL_END
 
 #endif

commit 407b4fe5bee1539bae8007daa6532bcf78454590
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jan 14 00:06:40 2009 +0100

    fix calculation of avail_min

diff --git a/src/modules/module-alsa-sink.c b/src/modules/module-alsa-sink.c
index f9fb9bd..6fa45e3 100644
--- a/src/modules/module-alsa-sink.c
+++ b/src/modules/module-alsa-sink.c
@@ -613,7 +613,7 @@ static int update_sw_params(struct userdata *u) {
         pa_usec_t sleep_usec, process_usec;
 
         hw_sleep_time(u, &sleep_usec, &process_usec);
-        avail_min += pa_usec_to_bytes(sleep_usec, &u->sink->sample_spec);
+        avail_min += pa_usec_to_bytes(sleep_usec, &u->sink->sample_spec) / u->frame_size;
     }
 
     pa_log_debug("setting avail_min=%lu", (unsigned long) avail_min);
diff --git a/src/modules/module-alsa-source.c b/src/modules/module-alsa-source.c
index a6e4c90..768c8c1 100644
--- a/src/modules/module-alsa-source.c
+++ b/src/modules/module-alsa-source.c
@@ -558,7 +558,7 @@ static int update_sw_params(struct userdata *u) {
         pa_usec_t sleep_usec, process_usec;
 
         hw_sleep_time(u, &sleep_usec, &process_usec);
-        avail_min += pa_usec_to_bytes(sleep_usec, &u->source->sample_spec);
+        avail_min += pa_usec_to_bytes(sleep_usec, &u->source->sample_spec) / u->frame_size;
     }
 
     pa_log_debug("setting avail_min=%lu", (unsigned long) avail_min);

commit cd45cd9e3ebba2d137ddd90fb181fdde07e65c18
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jan 14 00:07:32 2009 +0100

    include new proplist functions in export list

diff --git a/src/map-file b/src/map-file
index 59006fe..0247933 100644
--- a/src/map-file
+++ b/src/map-file
@@ -148,15 +148,17 @@ pa_proplist_clear;
 pa_proplist_contains;
 pa_proplist_copy;
 pa_proplist_free;
+pa_proplist_from_string;
 pa_proplist_get;
 pa_proplist_gets;
+pa_proplist_isempty;
 pa_proplist_iterate;
 pa_proplist_new;
 pa_proplist_set;
 pa_proplist_setf;
 pa_proplist_sets;
+pa_proplist_size;
 pa_proplist_to_string;
-pa_proplist_from_string;
 pa_proplist_unset;
 pa_proplist_unset_many;
 pa_proplist_update;

commit ab9736421013d6eb7ccac1a6cce57a33fcf2c599
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jan 14 00:07:50 2009 +0100

    remove calc_sine() since we don't need it anymore

diff --git a/src/modules/module-sine-source.c b/src/modules/module-sine-source.c
index be95cc3..1b8e7b3 100644
--- a/src/modules/module-sine-source.c
+++ b/src/modules/module-sine-source.c
@@ -201,15 +201,6 @@ finish:
     pa_log_debug("Thread shutting down");
 }
 
-static void calc_sine(float *f, size_t l, double freq) {
-    size_t i;
-
-    l /= sizeof(float);
-
-    for (i = 0; i < l; i++)
-        *(f++) = (float) 0.5f * sin((double) i*M_PI*2*freq / (double) l);
-}
-
 int pa__init(pa_module*m) {
     struct userdata *u;
     pa_modargs *ma;

commit f3101133d7c1dec9f4e79c9f3508c692c6b6b682
Merge: ab97364... 09641cc...
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jan 14 00:22:27 2009 +0100

    Merge commit 'elmarco/master'


commit df564040b5f0e530730b11d0ad04abae12edb06f
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Tue Jan 13 23:34:09 2009 +0000

    Fix a potentially non-returning function in base64 code.

diff --git a/src/modules/raop/base64.c b/src/modules/raop/base64.c
index 8918def..059c702 100644
--- a/src/modules/raop/base64.c
+++ b/src/modules/raop/base64.c
@@ -45,6 +45,7 @@ static int pos(char c)
     if (c >= '0' && c <= '9') return c - '0' + 52;
     if (c == '+') return 62;
     if (c == '/') return 63;
+    return -1;
 }
 
 int pa_base64_encode(const void *data, int size, char **str)
@@ -97,8 +98,12 @@ static unsigned int token_decode(const char *token)
             marker++;
         else if (marker > 0)
             return DECODE_ERROR;
-        else
-            val += pos(token[i]);
+        else {
+            int lpos = pos(token[i]);
+            if (lpos < 0)
+                return DECODE_ERROR;
+            val += lpos;
+        }
     }
     if (marker > 2)
         return DECODE_ERROR;

commit 587a08bae417d65f5b51da50706fe1c964275f1d
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jan 14 00:54:38 2009 +0100

    Fix a typo I know owe Marc-Andre a beer for.

diff --git a/src/modules/module-sine-source.c b/src/modules/module-sine-source.c
index 1b8e7b3..79d3b13 100644
--- a/src/modules/module-sine-source.c
+++ b/src/modules/module-sine-source.c
@@ -96,9 +96,9 @@ static int source_process_msg(
 
     switch (code) {
 
-        case PA_SINK_MESSAGE_SET_STATE:
+        case PA_SOURCE_MESSAGE_SET_STATE:
 
-            if (PA_PTR_TO_UINT(data) == PA_SINK_RUNNING)
+            if (PA_PTR_TO_UINT(data) == PA_SOURCE_RUNNING)
                 u->timestamp = pa_rtclock_usec();
 
             break;

commit 20edd846d073507adcfd30d2eb678f965de37e2d
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jan 14 17:36:44 2009 +0100

    make pa_asyncq_push() fail under no circumstances.

diff --git a/src/pulsecore/asyncq.c b/src/pulsecore/asyncq.c
index f64931a..c981505 100644
--- a/src/pulsecore/asyncq.c
+++ b/src/pulsecore/asyncq.c
@@ -163,14 +163,14 @@ static int push(pa_asyncq*l, void *p, pa_bool_t wait) {
     return 0;
 }
 
-static pa_bool_t flush_postq(pa_asyncq *l) {
+static pa_bool_t flush_postq(pa_asyncq *l, pa_bool_t wait) {
     struct localq *q;
 
     pa_assert(l);
 
     while ((q = l->last_localq)) {
 
-        if (push(l, q->data, FALSE) < 0)
+        if (push(l, q->data, wait) < 0)
             return FALSE;
 
         l->last_localq = q->prev;
@@ -187,7 +187,7 @@ static pa_bool_t flush_postq(pa_asyncq *l) {
 int pa_asyncq_push(pa_asyncq*l, void *p, pa_bool_t wait) {
     pa_assert(l);
 
-    if (!flush_postq(l))
+    if (!flush_postq(l, wait))
         return -1;
 
     return push(l, p, wait);
@@ -199,8 +199,9 @@ void pa_asyncq_post(pa_asyncq*l, void *p) {
     pa_assert(l);
     pa_assert(p);
 
-    if (pa_asyncq_push(l, p, FALSE) >= 0)
-        return;
+    if (flush_postq(l, FALSE))
+        if (pa_asyncq_push(l, p, FALSE) >= 0)
+            return;
 
     /* OK, we couldn't push anything in the queue. So let's queue it
      * locally and push it later */
@@ -299,7 +300,7 @@ void pa_asyncq_write_before_poll(pa_asyncq *l) {
 
     for (;;) {
 
-        if (flush_postq(l))
+        if (flush_postq(l, FALSE))
             break;
 
         if (pa_fdsem_before_poll(l->read_fdsem) >= 0) {

commit 6ec01626869763720c88b7eea605c14dc7c6ab91
Author: Marc-André Lureau <marcandre.lureau at gmail.com>
Date:   Mon Nov 10 16:59:09 2008 +0200

    sink: add a virtual_volume to sink

diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 48c8f79..809e827 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -192,6 +192,8 @@ pa_sink* pa_sink_new(
 
     s->volume = data->volume;
     s->base_volume = PA_VOLUME_NORM;
+    s->virtual_volume = s->volume;
+
     s->muted = data->muted;
     s->refresh_volume = s->refresh_muted = FALSE;
 
@@ -856,24 +858,26 @@ void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume) {
     pa_assert(pa_cvolume_compatible(volume, &s->sample_spec));
 
     data.sink = s;
-    data.volume = *volume;
+    data.virtual_volume = data.volume = *volume;
 
-    changed = !pa_cvolume_equal(&data.volume, &s->volume);
+    changed = !pa_cvolume_equal(&data.virtual_volume, &s->virtual_volume) ||
+        !pa_cvolume_equal(&data.volume, &s->volume);
 
     if (changed) {
         if (pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_SET_VOLUME], &data) < 0)
             return;
 
-        changed = !pa_cvolume_equal(&data.volume, &s->volume);
+        changed = !pa_cvolume_equal(&data.virtual_volume, &s->virtual_volume); /* from client-side view */
     }
 
     s->volume = data.volume;
+    s->virtual_volume = data.virtual_volume;
 
     if (s->set_volume && s->set_volume(s) < 0)
         s->set_volume = NULL;
 
     if (!s->set_volume)
-        pa_sink_set_soft_volume(s, volume);
+        pa_sink_set_soft_volume(s, &s->volume);
 
     if (changed)
         pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
@@ -896,19 +900,21 @@ const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh) {
     pa_assert(PA_SINK_IS_LINKED(s->state));
 
     if (s->refresh_volume || force_refresh) {
-        struct pa_cvolume old_volume = s->volume;
+        struct pa_cvolume old_volume = s->virtual_volume;
 
         if (s->get_volume && s->get_volume(s) < 0)
             s->get_volume = NULL;
 
-        if (!s->get_volume)
+        if (!s->get_volume) {
             pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_VOLUME, &s->volume, 0, NULL);
+            s->virtual_volume = s->volume;
+        }
 
-        if (!pa_cvolume_equal(&old_volume, &s->volume))
+        if (!pa_cvolume_equal(&old_volume, &s->virtual_volume))
             pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
     }
 
-    return &s->volume;
+    return &s->virtual_volume;
 }
 
 /* Called from main thread */
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index 092e30f..fb5e1e8 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -79,6 +79,7 @@ struct pa_sink {
     pa_source *monitor_source;
 
     pa_cvolume volume;
+    pa_cvolume virtual_volume;
     pa_bool_t muted;
 
     pa_volume_t base_volume;  /* shall be constant */
@@ -211,6 +212,7 @@ void pa_sink_new_data_done(pa_sink_new_data *data);
 typedef struct pa_sink_set_volume_data {
   pa_sink *sink;
   pa_cvolume volume;
+  pa_cvolume virtual_volume;
 } pa_sink_set_volume_data;
 
 /* To be called exclusively by the sink driver, from main context */

commit e97ed21892402a09873b61a5cc200ce61c187488
Author: Marc-André Lureau <marcandre.lureau at gmail.com>
Date:   Mon Nov 17 21:09:42 2008 +0200

    match: can now change properties also

diff --git a/src/modules/module-match.c b/src/modules/module-match.c
index 769a6b5..5c36fe0 100644
--- a/src/modules/module-match.c
+++ b/src/modules/module-match.c
@@ -63,6 +63,7 @@ static const char* const valid_modargs[] = {
 struct rule {
     regex_t regex;
     pa_volume_t volume;
+    pa_proplist *proplist;
     struct rule *next;
 };
 
@@ -95,11 +96,12 @@ static int load_rules(struct userdata *u, const char *filename) {
 
     while (!feof(f)) {
         char *d, *v;
-        pa_volume_t volume;
+        pa_volume_t volume = PA_VOLUME_NORM;
         uint32_t k;
         regex_t regex;
         char ln[256];
         struct rule *rule;
+        pa_proplist *proplist = NULL;
 
         if (!fgets(ln, sizeof(ln), f))
             break;
@@ -121,14 +123,33 @@ static int load_rules(struct userdata *u, const char *filename) {
         }
 
         *d = 0;
-        if (pa_atou(v, &k) < 0) {
-            pa_log("[%s:%u] failed to parse volume", filename, n);
-            goto finish;
+        if (pa_atou(v, &k) >= 0) {
+            volume = (pa_volume_t) k;
+        } else if (*v == '"') {
+            char *e;
+
+            e = strchr(v+1, '"');
+            if (!e) {
+                pa_log(__FILE__ ": [%s:%u] failed to parse line - missing role closing quote", filename, n);
+                goto finish;
+            }
+
+            *e = '\0';
+            e = pa_sprintf_malloc("media.role=\"%s\"", v+1);
+            proplist = pa_proplist_from_string(e);
+            pa_xfree(e);
+        } else {
+            char *e;
+
+            e = v+strspn(v, WHITESPACE);
+            if (!*e) {
+                pa_log(__FILE__ ": [%s:%u] failed to parse line - missing end of property list", filename, n);
+                goto finish;
+            }
+            *e = '\0';
+            proplist = pa_proplist_from_string(v);
         }
 
-        volume = (pa_volume_t) k;
-
-
         if (regcomp(&regex, ln, REG_EXTENDED|REG_NOSUB) != 0) {
             pa_log("[%s:%u] invalid regular expression", filename, n);
             goto finish;
@@ -136,6 +157,7 @@ static int load_rules(struct userdata *u, const char *filename) {
 
         rule = pa_xnew(struct rule, 1);
         rule->regex = regex;
+        rule->proplist = proplist;
         rule->volume = volume;
         rule->next = NULL;
 
@@ -180,12 +202,19 @@ static void callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, v
     if (!(n = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_NAME)))
         return;
 
+    pa_log_debug("Matching with %s", n);
+
     for (r = u->rules; r; r = r->next) {
         if (!regexec(&r->regex, n, 0, NULL, 0)) {
-            pa_cvolume cv;
-            pa_log_debug("changing volume of sink input '%s' to 0x%03x", n, r->volume);
-            pa_cvolume_set(&cv, si->sample_spec.channels, r->volume);
-            pa_sink_input_set_volume(si, &cv);
+            if (r->proplist) {
+                pa_log_debug("updating proplist of sink input '%s'", n);
+                pa_proplist_update(si->proplist, PA_UPDATE_MERGE, r->proplist);
+            } else {
+                pa_cvolume cv;
+                pa_log_debug("changing volume of sink input '%s' to 0x%03x", n, r->volume);
+                pa_cvolume_set(&cv, si->sample_spec.channels, r->volume);
+                pa_sink_input_set_volume(si, &cv);
+            }
         }
     }
 }
@@ -238,6 +267,8 @@ void pa__done(pa_module*m) {
         n = r->next;
 
         regfree(&r->regex);
+        if (r->proplist)
+            pa_proplist_free(r->proplist);
         pa_xfree(r);
     }
 

commit e0f8ffe41f99789fafac575e944acf02e940bbf7
Author: Marc-André Lureau <marcandre.lureau at gmail.com>
Date:   Mon Nov 17 21:37:25 2008 +0200

    match: add "key" argument to match different properties

diff --git a/src/modules/module-match.c b/src/modules/module-match.c
index 5c36fe0..8c825c4 100644
--- a/src/modules/module-match.c
+++ b/src/modules/module-match.c
@@ -48,7 +48,8 @@ PA_MODULE_AUTHOR("Lennart Poettering");
 PA_MODULE_DESCRIPTION("Playback stream expression matching module");
 PA_MODULE_VERSION(PACKAGE_VERSION);
 PA_MODULE_LOAD_ONCE(TRUE);
-PA_MODULE_USAGE("table=<filename>");
+PA_MODULE_USAGE("table=<filename> "
+                "key=<property_key>");
 
 #define WHITESPACE "\n\r \t"
 
@@ -57,6 +58,7 @@ PA_MODULE_USAGE("table=<filename>");
 
 static const char* const valid_modargs[] = {
     "table",
+    "key",
     NULL,
 };
 
@@ -69,6 +71,7 @@ struct rule {
 
 struct userdata {
     struct rule *rules;
+    char *property_key;
     pa_subscription *subscription;
 };
 
@@ -199,7 +202,7 @@ static void callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, v
     if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx)))
         return;
 
-    if (!(n = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_NAME)))
+    if (!(n = pa_proplist_gets(si->proplist, u->property_key)))
         return;
 
     pa_log_debug("Matching with %s", n);
@@ -230,11 +233,14 @@ int pa__init(pa_module*m) {
         goto fail;
     }
 
+
     u = pa_xnew(struct userdata, 1);
     u->rules = NULL;
     u->subscription = NULL;
     m->userdata = u;
 
+    u->property_key = pa_xstrdup(pa_modargs_get_value(ma, "key", PA_PROP_MEDIA_NAME));
+
     if (load_rules(u, pa_modargs_get_value(ma, "table", NULL)) < 0)
         goto fail;
 
@@ -263,6 +269,9 @@ void pa__done(pa_module*m) {
     if (u->subscription)
         pa_subscription_free(u->subscription);
 
+    if (u->property_key)
+        pa_xfree(u->property_key);
+
     for (r = u->rules; r; r = n) {
         n = r->next;
 

commit fd3f5db63c322f6a01bfd62895add2eac94f61b4
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 15 00:03:33 2009 +0100

    document that PA_API_VERSION is only for incompatible API changes

diff --git a/src/pulse/version.h.in b/src/pulse/version.h.in
index e6226c4..0e37f98 100644
--- a/src/pulse/version.h.in
+++ b/src/pulse/version.h.in
@@ -43,7 +43,8 @@ const char* pa_get_library_version(void);
 
 /** The current API version. Version 6 relates to Polypaudio
  * 0.6. Prior versions (i.e. Polypaudio 0.5.1 and older) have
- * PA_API_VERSION undefined.  */
+ * PA_API_VERSION undefined. Please note that this is only ever
+ * increased on incompatible API changes!  */
 #define PA_API_VERSION @PA_API_VERSION@
 
 /** The current protocol version. Version 8 relates to Polypaudio

commit d1cf0e7845b3944fb676a46b528bea519b41e42b
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 15 00:03:56 2009 +0100

    fix a potential format string vulnerability

diff --git a/src/pulsecore/shared.c b/src/pulsecore/shared.c
index 4c1ad80..d6e42dd 100644
--- a/src/pulsecore/shared.c
+++ b/src/pulsecore/shared.c
@@ -115,7 +115,7 @@ void pa_shared_cleanup(pa_core *c) {
         pa_strbuf *s = pa_strbuf_new();
 
         pa_shared_dump(c, s);
-        pa_log_debug(pa_strbuf_tostring(s));
+        pa_log_debug("%s", pa_strbuf_tostring(s));
         pa_strbuf_free(s);
         pa_assert(pa_hashmap_isempty(c->shared));
     }

commit 06de6393d1426110c2b0d20a6bbc2685c2138e25
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 15 00:07:38 2009 +0100

    don't rely on PA_SINK_RUNNING vs. PA_SINK_IDLE for optimizations since it might not be fully up to date

diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 48c8f79..3a66238 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -642,7 +642,7 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
 
     pa_assert(length > 0);
 
-    n = s->thread_info.state == PA_SINK_RUNNING ? fill_mix_info(s, &length, info, MAX_MIX_CHANNELS) : 0;
+    n = fill_mix_info(s, &length, info, MAX_MIX_CHANNELS);
 
     if (n == 0) {
 
@@ -685,8 +685,7 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
         result->index = 0;
     }
 
-    if (s->thread_info.state == PA_SINK_RUNNING)
-        inputs_drop(s, info, n, result);
+    inputs_drop(s, info, n, result);
 
     pa_sink_unref(s);
 }
@@ -716,7 +715,7 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
 
     pa_assert(length > 0);
 
-    n = s->thread_info.state == PA_SINK_RUNNING ? fill_mix_info(s, &length, info, MAX_MIX_CHANNELS) : 0;
+    n = fill_mix_info(s, &length, info, MAX_MIX_CHANNELS);
 
     if (n == 0) {
         if (target->length > length)
@@ -765,8 +764,7 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
         pa_memblock_release(target->memblock);
     }
 
-    if (s->thread_info.state == PA_SINK_RUNNING)
-        inputs_drop(s, info, n, target);
+    inputs_drop(s, info, n, target);
 
     pa_sink_unref(s);
 }
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index e65c5ce..dee6f3d 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -429,9 +429,6 @@ void pa_source_post(pa_source*s, const pa_memchunk *chunk) {
     pa_assert(PA_SOURCE_IS_OPENED(s->thread_info.state));
     pa_assert(chunk);
 
-    if (s->thread_info.state != PA_SOURCE_RUNNING)
-        return;
-
     if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&s->thread_info.soft_volume)) {
         pa_memchunk vchunk = *chunk;
 
@@ -470,9 +467,6 @@ void pa_source_post_direct(pa_source*s, pa_source_output *o, const pa_memchunk *
     pa_assert(o->thread_info.direct_on_input);
     pa_assert(chunk);
 
-    if (s->thread_info.state != PA_SOURCE_RUNNING)
-        return;
-
     if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&s->thread_info.soft_volume)) {
         pa_memchunk vchunk = *chunk;
 

commit 75119e91cdb4f29b0567689d07d00ddc17a98b5c
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 15 00:40:06 2009 +0100

    add new dont_rewind_render flag to allow quick starts of newly created streams

diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c
index d61d127..fa80781 100644
--- a/src/modules/module-combine.c
+++ b/src/modules/module-combine.c
@@ -504,7 +504,7 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s
      * we are heard right-away. */
     if (PA_SINK_INPUT_IS_LINKED(state) &&
         i->thread_info.state == PA_SINK_INPUT_INIT)
-        pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
+        pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE);
 }
 
 /* Called from thread context */
diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c
index a27ed71..4209e99 100644
--- a/src/modules/module-ladspa-sink.c
+++ b/src/modules/module-ladspa-sink.c
@@ -144,7 +144,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);
+    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 */
@@ -355,7 +355,7 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s
     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);
+        pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE);
     }
 }
 
diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c
index 976a8ce..ae05f07 100644
--- a/src/modules/module-remap-sink.c
+++ b/src/modules/module-remap-sink.c
@@ -119,7 +119,7 @@ static void sink_request_rewind(pa_sink *s) {
     pa_sink_assert_ref(s);
     pa_assert_se(u = s->userdata);
 
-    pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes, TRUE, FALSE);
+    pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes, TRUE, FALSE, FALSE);
 }
 
 /* Called from I/O thread context */
@@ -270,7 +270,7 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s
     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);
+        pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE);
     }
 }
 
diff --git a/src/modules/module-sine.c b/src/modules/module-sine.c
index 3b0dc35..2ea78da 100644
--- a/src/modules/module-sine.c
+++ b/src/modules/module-sine.c
@@ -115,7 +115,7 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s
      * we are heard right-away. */
     if (PA_SINK_INPUT_IS_LINKED(state) &&
         i->thread_info.state == PA_SINK_INPUT_INIT)
-        pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
+        pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE);
 }
 
 int pa__init(pa_module*m) {
diff --git a/src/modules/rtp/module-rtp-recv.c b/src/modules/rtp/module-rtp-recv.c
index e35773c..478e0a3 100644
--- a/src/modules/rtp/module-rtp-recv.c
+++ b/src/modules/rtp/module-rtp-recv.c
@@ -313,7 +313,7 @@ static int rtpoll_work_cb(pa_rtpoll_item *i) {
     if (pa_memblockq_is_readable(s->memblockq) &&
         s->sink_input->thread_info.underrun_for > 0) {
         pa_log_debug("Requesting rewind due to end of underrun");
-        pa_sink_input_request_rewind(s->sink_input, 0, FALSE, TRUE);
+        pa_sink_input_request_rewind(s->sink_input, 0, FALSE, TRUE, FALSE);
     }
 
     return 1;
diff --git a/src/pulsecore/play-memblockq.c b/src/pulsecore/play-memblockq.c
index 86edfe9..758c0de 100644
--- a/src/pulsecore/play-memblockq.c
+++ b/src/pulsecore/play-memblockq.c
@@ -109,7 +109,7 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s
      * we are heard right-away. */
     if (PA_SINK_INPUT_IS_LINKED(state) &&
         i->thread_info.state == PA_SINK_INPUT_INIT)
-        pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
+        pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE);
 }
 
 static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c
index 460119a..2f01482 100644
--- a/src/pulsecore/protocol-esound.c
+++ b/src/pulsecore/protocol-esound.c
@@ -1238,7 +1238,7 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
 
             if (pa_memblockq_is_readable(c->input_memblockq) && c->playback.underrun) {
                 pa_log_debug("Requesting rewind due to end of underrun.");
-                pa_sink_input_request_rewind(c->sink_input, 0, FALSE, TRUE);
+                pa_sink_input_request_rewind(c->sink_input, 0, FALSE, TRUE, FALSE);
             }
 
 /*             pa_log("got data, %u", pa_memblockq_get_length(c->input_memblockq)); */
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 3c1e576..7cce3db 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -1240,7 +1240,7 @@ static void handle_seek(playback_stream *s, int64_t indexw) {
             pa_log_debug("Requesting rewind due to end of underrun.");
             pa_sink_input_request_rewind(s->sink_input,
                                          (size_t) (s->sink_input->thread_info.underrun_for == (size_t) -1 ? 0 : s->sink_input->thread_info.underrun_for),
-                                         FALSE, TRUE);
+                                         FALSE, TRUE, FALSE);
         }
 
     } else {
@@ -1253,7 +1253,7 @@ static void handle_seek(playback_stream *s, int64_t indexw) {
              * let's have it usk us again */
 
             pa_log_debug("Requesting rewind due to rewrite.");
-            pa_sink_input_request_rewind(s->sink_input, (size_t) (indexr - indexw), TRUE, FALSE);
+            pa_sink_input_request_rewind(s->sink_input, (size_t) (indexr - indexw), TRUE, FALSE, FALSE);
         }
     }
 
diff --git a/src/pulsecore/protocol-simple.c b/src/pulsecore/protocol-simple.c
index 743bf2e..9c4a538 100644
--- a/src/pulsecore/protocol-simple.c
+++ b/src/pulsecore/protocol-simple.c
@@ -323,7 +323,7 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int
 
             if (pa_memblockq_is_readable(c->input_memblockq) && c->playback.underrun) {
                 pa_log_debug("Requesting rewind due to end of underrun.");
-                pa_sink_input_request_rewind(c->sink_input, 0, FALSE, TRUE);
+                pa_sink_input_request_rewind(c->sink_input, 0, FALSE, TRUE, FALSE);
             }
 
 /*             pa_log("got data, %u", pa_memblockq_get_length(c->input_memblockq)); */
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 33490cc..f5a1cb8 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -265,6 +265,7 @@ pa_sink_input* pa_sink_input_new(
     i->thread_info.requested_sink_latency = (pa_usec_t) -1;
     i->thread_info.rewrite_nbytes = 0;
     i->thread_info.rewrite_flush = FALSE;
+    i->thread_info.dont_rewind_render = FALSE;
     i->thread_info.underrun_for = (uint64_t) -1;
     i->thread_info.playing_for = 0;
     i->thread_info.direct_outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
@@ -658,7 +659,7 @@ void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sam
 
     lbq = pa_memblockq_get_length(i->thread_info.render_memblockq);
 
-    if (nbytes > 0) {
+    if (nbytes > 0 && !i->thread_info.dont_rewind_render) {
         pa_log_debug("Have to rewind %lu bytes on render memblockq.", (unsigned long) nbytes);
         pa_memblockq_rewind(i->thread_info.render_memblockq, nbytes);
     }
@@ -714,6 +715,7 @@ void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sam
 
     i->thread_info.rewrite_nbytes = 0;
     i->thread_info.rewrite_flush = FALSE;
+    i->thread_info.dont_rewind_render = FALSE;
 }
 
 /* Called from thread context */
@@ -1091,7 +1093,7 @@ void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state
 
         /* This will tell the implementing sink input driver to rewind
          * so that the unplayed already mixed data is not lost */
-        pa_sink_input_request_rewind(i, 0, TRUE, TRUE);
+        pa_sink_input_request_rewind(i, 0, TRUE, TRUE, FALSE);
 
     } else if (uncorking) {
 
@@ -1102,7 +1104,7 @@ void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state
 
         /* OK, we're being uncorked. Make sure we're not rewound when
          * the hw buffer is remixed and request a remix. */
-        pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
+        pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE);
     }
 }
 
@@ -1115,12 +1117,12 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t
 
         case PA_SINK_INPUT_MESSAGE_SET_VOLUME:
             i->thread_info.volume = *((pa_cvolume*) userdata);
-            pa_sink_input_request_rewind(i, 0, TRUE, FALSE);
+            pa_sink_input_request_rewind(i, 0, TRUE, FALSE, FALSE);
             return 0;
 
         case PA_SINK_INPUT_MESSAGE_SET_MUTE:
             i->thread_info.muted = PA_PTR_TO_UINT(userdata);
-            pa_sink_input_request_rewind(i, 0, TRUE, FALSE);
+            pa_sink_input_request_rewind(i, 0, TRUE, FALSE, FALSE);
             return 0;
 
         case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
@@ -1195,7 +1197,7 @@ pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i) {
 }
 
 /* Called from IO context */
-void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes  /* in our sample spec */, pa_bool_t rewrite, pa_bool_t flush) {
+void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes  /* in our sample spec */, pa_bool_t rewrite, pa_bool_t flush, pa_bool_t dont_rewind_render) {
     size_t lbq;
 
     /* If 'rewrite' is TRUE the sink is rewound as far as requested
@@ -1206,7 +1208,9 @@ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes  /* in our sam
      * If 'rewrite' is FALSE the sink is rewound as far as requested
      * and possible and the already rendered data is dropped so that
      * in the next iteration we read new data from the
-     * implementor. This implies 'flush' is TRUE. */
+     * implementor. This implies 'flush' is TRUE.  If
+     * dont_rewind_render is TRUE then the render memblockq is not
+     * rewound. */
 
     pa_sink_input_assert_ref(i);
 
@@ -1219,6 +1223,7 @@ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes  /* in our sam
         return;
 
     pa_assert(rewrite || flush);
+    pa_assert(!dont_rewind_render || !rewrite);
 
     /* Calculate how much we can rewind locally without having to
      * touch the sink */
@@ -1253,6 +1258,10 @@ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes  /* in our sam
         i->thread_info.rewrite_flush ||
         (flush && i->thread_info.rewrite_nbytes != 0);
 
+    i->thread_info.dont_rewind_render =
+        i->thread_info.dont_rewind_render ||
+        dont_rewind_render;
+
     if (nbytes != (size_t) -1) {
 
         /* Transform to sink domain */
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index 8cfe32b..3d2a8c0 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -179,8 +179,9 @@ struct pa_sink_input {
         /* We maintain a history of resampled audio data here. */
         pa_memblockq *render_memblockq;
 
+        /* 0: rewrite nothing, (size_t) -1: rewrite everything, otherwise how many bytes to rewrite */
         size_t rewrite_nbytes;
-        pa_bool_t rewrite_flush;
+        pa_bool_t rewrite_flush, dont_rewind_render;
         uint64_t underrun_for, playing_for;
 
         pa_sink_input *sync_prev, *sync_next;
@@ -277,7 +278,7 @@ fully -- or at all. If the request for a rewrite was successful, the
 sink driver will call ->rewind() and pass the number of bytes that
 could be rewound in the HW device. This functionality is required for
 implementing the "zero latency" write-through functionality. */
-void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes, pa_bool_t rewrite, pa_bool_t flush);
+void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes, pa_bool_t rewrite, pa_bool_t flush, pa_bool_t dont_rewind_render);
 
 void pa_sink_input_cork(pa_sink_input *i, pa_bool_t b);
 
diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c
index c30c16e..b78afca 100644
--- a/src/pulsecore/sound-file-stream.c
+++ b/src/pulsecore/sound-file-stream.c
@@ -133,7 +133,7 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s
      * we are heard right-away. */
     if (PA_SINK_INPUT_IS_LINKED(state) &&
         i->thread_info.state == PA_SINK_INPUT_INIT)
-        pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
+        pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE);
 }
 
 /* Called from IO thread context */

commit 5abda63a1fa4d2c867af8b33b0090cd651ba8599
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 15 17:52:29 2009 +0100

    convert pa_client instantiation to use a pa_client_new_data struct and add hooks for manipulating it

diff --git a/src/pulsecore/cli.c b/src/pulsecore/cli.c
index 67bf1e7..25a4f74 100644
--- a/src/pulsecore/cli.c
+++ b/src/pulsecore/cli.c
@@ -67,20 +67,33 @@ static void client_kill(pa_client *c);
 pa_cli* pa_cli_new(pa_core *core, pa_iochannel *io, pa_module *m) {
     char cname[256];
     pa_cli *c;
+    pa_client_new_data data;
+    pa_client *client;
+
     pa_assert(io);
 
+    pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname));
+
+    pa_client_new_data_init(&data);
+    data.driver = __FILE__;
+    data.module = m;
+    pa_proplist_sets(data.proplist, PA_PROP_APPLICATION_NAME, cname);
+    client = pa_client_new(core, &data);
+    pa_client_new_data_done(&data);
+
+    if (!client)
+        return NULL;
+
     c = pa_xnew(pa_cli, 1);
     c->core = core;
+    c->client = client;
     pa_assert_se(c->line = pa_ioline_new(io));
 
     c->userdata = NULL;
     c->eof_callback = NULL;
 
-    pa_iochannel_socket_peer_to_string(io, cname, sizeof(cname));
-    pa_assert_se(c->client = pa_client_new(core, __FILE__, cname));
     c->client->kill = client_kill;
     c->client->userdata = c;
-    c->client->module = m;
 
     pa_ioline_set_callback(c->line, line_callback, c);
     pa_ioline_puts(c->line, "Welcome to PulseAudio! Use \"help\" for usage information.\n"PROMPT);
diff --git a/src/pulsecore/client.c b/src/pulsecore/client.c
index ab6e5df..31631e1 100644
--- a/src/pulsecore/client.c
+++ b/src/pulsecore/client.c
@@ -37,27 +37,46 @@
 
 #include "client.h"
 
-pa_client *pa_client_new(pa_core *core, const char *driver, const char *name) {
+pa_client_new_data* pa_client_new_data_init(pa_client_new_data *data) {
+    pa_assert(data);
+
+    memset(data, 0, sizeof(*data));
+    data->proplist = pa_proplist_new();
+
+    return data;
+}
+
+void pa_client_new_data_done(pa_client_new_data *data) {
+    pa_assert(data);
+
+    pa_proplist_free(data->proplist);
+}
+
+pa_client *pa_client_new(pa_core *core, pa_client_new_data *data) {
     pa_client *c;
 
     pa_core_assert_ref(core);
+    pa_assert(data);
+
+    if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_CLIENT_NEW], data) < 0)
+        return NULL;
 
     c = pa_xnew(pa_client, 1);
     c->core = core;
-    c->proplist = pa_proplist_new();
-    if (name)
-        pa_proplist_sets(c->proplist, PA_PROP_APPLICATION_NAME, name);
-    c->driver = pa_xstrdup(driver);
-    c->module = NULL;
+    c->proplist = pa_proplist_copy(data->proplist);
+    c->driver = pa_xstrdup(data->driver);
+    c->module = data->module;
 
-    c->kill = NULL;
     c->userdata = NULL;
+    c->kill = NULL;
 
     pa_assert_se(pa_idxset_put(core->clients, c, &c->index) >= 0);
 
-    pa_log_info("Created %u \"%s\"", c->index, pa_strnull(name));
+    pa_log_info("Created %u \"%s\"", c->index, pa_strnull(pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME)));
     pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_NEW, c->index);
 
+    pa_hook_fire(&core->hooks[PA_CORE_HOOK_CLIENT_PUT], c);
+
     pa_core_check_idle(core);
 
     return c;
@@ -69,11 +88,14 @@ void pa_client_free(pa_client *c) {
     pa_assert(c);
     pa_assert(c->core);
 
+    pa_hook_fire(&core->hooks[PA_CORE_HOOK_CLIENT_UNLINK], c);
+
     core = c->core;
     pa_idxset_remove_by_data(c->core->clients, c, NULL);
 
     pa_log_info("Freed %u \"%s\"", c->index, pa_strnull(pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME)));
     pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_REMOVE, c->index);
+
     pa_proplist_free(c->proplist);
     pa_xfree(c->driver);
     pa_xfree(c);
diff --git a/src/pulsecore/client.h b/src/pulsecore/client.h
index 28d1fe5..8e72f32 100644
--- a/src/pulsecore/client.h
+++ b/src/pulsecore/client.h
@@ -42,11 +42,21 @@ struct pa_client {
     pa_module *module;
     char *driver;
 
-    void (*kill)(pa_client *c);
     void *userdata;
+
+    void (*kill)(pa_client *c);
 };
 
-pa_client *pa_client_new(pa_core *c, const char *driver, const char *name);
+typedef struct pa_client_new_data {
+    pa_proplist *proplist;
+    const char *driver;
+    pa_module *module;
+} pa_client_new_data;
+
+pa_client_new_data *pa_client_new_data_init(pa_client_new_data *data);
+void pa_client_new_data_done(pa_client_new_data *data);
+
+pa_client *pa_client_new(pa_core *c, pa_client_new_data *data);
 
 /* This function should be called only by the code that created the client */
 void pa_client_free(pa_client *c);
diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index f796fb9..f1f38ef 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -76,6 +76,9 @@ typedef enum pa_core_hook {
     PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_POST,
     PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED,
     PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED,
+    PA_CORE_HOOK_CLIENT_NEW,
+    PA_CORE_HOOK_CLIENT_PUT,
+    PA_CORE_HOOK_CLIENT_UNLINK,
     PA_CORE_HOOK_MAX
 } pa_core_hook_t;
 
diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c
index 2f01482..6524b68 100644
--- a/src/pulsecore/protocol-esound.c
+++ b/src/pulsecore/protocol-esound.c
@@ -1377,7 +1377,9 @@ static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timev
 
 void pa_esound_protocol_connect(pa_esound_protocol *p, pa_iochannel *io, pa_esound_options *o) {
     connection *c;
-    char cname[256], pname[128];
+    char pname[128];
+    pa_client_new_data data;
+    pa_client *client;
 
     pa_assert(p);
     pa_assert(io);
@@ -1389,6 +1391,18 @@ void pa_esound_protocol_connect(pa_esound_protocol *p, pa_iochannel *io, pa_esou
         return;
     }
 
+    pa_client_new_data_init(&data);
+    data.module = o->module;
+    data.driver = __FILE__;
+    pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
+    pa_proplist_setf(data.proplist, PA_PROP_APPLICATION_NAME, "EsounD client (%s)", pname);
+    pa_proplist_sets(data.proplist, "esound-protocol.peer", pname);
+    client = pa_client_new(p->core, &data);
+    pa_client_new_data_done(&data);
+
+    if (!client)
+        return;
+
     c = pa_msgobject_new(connection);
     c->parent.parent.free = connection_free;
     c->parent.process_msg = connection_process_msg;
@@ -1396,11 +1410,7 @@ void pa_esound_protocol_connect(pa_esound_protocol *p, pa_iochannel *io, pa_esou
     c->io = io;
     pa_iochannel_set_callback(c->io, io_callback, c);
 
-    pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
-    pa_snprintf(cname, sizeof(cname), "EsounD client (%s)", pname);
-    c->client = pa_client_new(p->core, __FILE__, cname);
-    pa_proplist_sets(c->client->proplist, "esound-protocol.peer", pname);
-    c->client->module = o->module;
+    c->client = client;
     c->client->kill = client_kill_cb;
     c->client->userdata = c;
 
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 7cce3db..d99e212 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -4214,7 +4214,9 @@ static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timev
 
 void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_native_options *o) {
     pa_native_connection *c;
-    char cname[256], pname[128];
+    char pname[128];
+    pa_client *client;
+    pa_client_new_data data;
 
     pa_assert(p);
     pa_assert(io);
@@ -4226,6 +4228,18 @@ void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_nati
         return;
     }
 
+    pa_client_new_data_init(&data);
+    data.module = o->module;
+    data.driver = __FILE__;
+    pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
+    pa_proplist_setf(data.proplist, PA_PROP_APPLICATION_NAME, "Native client (%s)", pname);
+    pa_proplist_sets(data.proplist, "native-protocol.peer", pname);
+    client = pa_client_new(p->core, &data);
+    pa_client_new_data_done(&data);
+
+    if (!client)
+        return;
+
     c = pa_msgobject_new(pa_native_connection);
     c->parent.parent.free = native_connection_free;
     c->parent.process_msg = native_connection_process_msg;
@@ -4257,13 +4271,9 @@ void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_nati
     c->is_local = pa_iochannel_socket_is_local(io);
     c->version = 8;
 
-    pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
-    pa_snprintf(cname, sizeof(cname), "Native client (%s)", pname);
-    c->client = pa_client_new(p->core, __FILE__, cname);
-    pa_proplist_sets(c->client->proplist, "native-protocol.peer", pname);
+    c->client = client;
     c->client->kill = client_kill_cb;
     c->client->userdata = c;
-    c->client->module = o->module;
 
     c->pstream = pa_pstream_new(p->core->mainloop, io, p->core->mempool);
     pa_pstream_set_recieve_packet_callback(c->pstream, pstream_packet_callback, c);
diff --git a/src/pulsecore/protocol-simple.c b/src/pulsecore/protocol-simple.c
index 9c4a538..a754669 100644
--- a/src/pulsecore/protocol-simple.c
+++ b/src/pulsecore/protocol-simple.c
@@ -476,7 +476,8 @@ static void io_callback(pa_iochannel*io, void *userdata) {
 
 void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simple_options *o) {
     connection *c = NULL;
-    char cname[256], pname[128];
+    char pname[128];
+    pa_client_new_data client_data;
 
     pa_assert(p);
     pa_assert(io);
@@ -505,11 +506,18 @@ void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simp
     c->playback.underrun = TRUE;
     pa_atomic_store(&c->playback.missing, 0);
 
+    pa_client_new_data_init(&client_data);
+    client_data.module = o->module;
+    client_data.driver = __FILE__;
     pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
-    pa_snprintf(cname, sizeof(cname), "Simple client (%s)", pname);
-    pa_assert_se(c->client = pa_client_new(p->core, __FILE__, cname));
-    pa_proplist_sets(c->client->proplist, "simple-protocol.peer", pname);
-    c->client->module = o->module;
+    pa_proplist_setf(client_data.proplist, PA_PROP_APPLICATION_NAME, "Simple client (%s)", pname);
+    pa_proplist_sets(client_data.proplist, "simple-protocol.peer", pname);
+    c->client = pa_client_new(p->core, &client_data);
+    pa_client_new_data_done(&client_data);
+
+    if (!c->client)
+        goto fail;
+
     c->client->kill = client_kill_cb;
     c->client->userdata = c;
 

commit a3695dd9eb67d4e5ba8a331b9c5432a10317105c
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 15 18:21:57 2009 +0100

    port missing modules to new pa_client_new() API

diff --git a/src/modules/module-console-kit.c b/src/modules/module-console-kit.c
index e1933c2..4f3ed8d 100644
--- a/src/modules/module-console-kit.c
+++ b/src/modules/module-console-kit.c
@@ -63,6 +63,7 @@ struct session {
 };
 
 struct userdata {
+    pa_module *module;
     pa_core *core;
     pa_dbus_connection *connection;
     pa_hashmap *sessions;
@@ -73,7 +74,7 @@ static void add_session(struct userdata *u, const char *id) {
     DBusMessage *m = NULL, *reply = NULL;
     uint32_t uid;
     struct session *session;
-    char *t;
+    pa_client_new_data data;
 
     dbus_error_init (&error);
 
@@ -109,11 +110,19 @@ static void add_session(struct userdata *u, const char *id) {
     session = pa_xnew(struct session, 1);
     session->id = pa_xstrdup(id);
 
-    t = pa_sprintf_malloc("ConsoleKit Session %s", id);
-    session->client = pa_client_new(u->core, __FILE__, t);
-    pa_xfree(t);
-
-    pa_proplist_sets(session->client->proplist, "console-kit.session", id);
+    pa_client_new_data_init(&data);
+    data.module = u->module;
+    data.driver = __FILE__;
+    pa_proplist_setf(data.proplist, PA_PROP_APPLICATION_NAME, "ConsoleKit Session %s", id);
+    pa_proplist_sets(data.proplist, "console-kit.session", id);
+    session->client = pa_client_new(u->core, &data);
+    pa_client_new_data_done(&data);
+
+    if (!session->client) {
+        pa_xfree(session->id);
+        pa_xfree(session);
+        goto fail;
+    }
 
     pa_hashmap_put(u->sessions, session->id, session);
 
@@ -295,6 +304,7 @@ int pa__init(pa_module*m) {
 
     m->userdata = u = pa_xnew(struct userdata, 1);
     u->core = m->core;
+    u->module = m;
     u->connection = connection;
     u->sessions = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
 
diff --git a/src/modules/module-x11-xsmp.c b/src/modules/module-x11-xsmp.c
index 57d182f..5fc8047 100644
--- a/src/modules/module-x11-xsmp.c
+++ b/src/modules/module-x11-xsmp.c
@@ -117,13 +117,14 @@ static void new_ice_connection(IceConn connection, IcePointer client_data, Bool
 int pa__init(pa_module*m) {
 
     pa_modargs *ma = NULL;
-    char t[256], *vendor, *client_id, *k;
+    char t[256], *vendor, *client_id;
     SmcCallbacks callbacks;
     SmProp prop_program, prop_user;
     SmProp *prop_list[2];
     SmPropValue val_program, val_user;
     struct userdata *u;
     const char *e;
+    pa_client_new_data data;
 
     pa_assert(m);
 
@@ -198,16 +199,22 @@ int pa__init(pa_module*m) {
     SmcSetProperties(u->connection, PA_ELEMENTSOF(prop_list), prop_list);
 
     pa_log_info("Connected to session manager '%s' as '%s'.", vendor = SmcVendor(u->connection), client_id);
-    k = pa_sprintf_malloc("XSMP Session on %s as %s", vendor, client_id);
-    u->client = pa_client_new(u->core, __FILE__, k);
-    pa_xfree(k);
 
-    pa_proplist_sets(u->client->proplist, "xsmp.vendor", vendor);
-    pa_proplist_sets(u->client->proplist, "xsmp.client.id", client_id);
+    pa_client_new_data_init(&data);
+    data.module = m;
+    data.driver = __FILE__;
+    pa_proplist_setf(data.proplist, PA_PROP_APPLICATION_NAME, "XSMP Session on %s as %s", vendor, client_id);
+    pa_proplist_sets(data.proplist, "xsmp.vendor", vendor);
+    pa_proplist_sets(data.proplist, "xsmp.client.id", client_id);
+    u->client = pa_client_new(u->core, &data);
+    pa_client_new_data_done(&data);
 
     free(vendor);
     free(client_id);
 
+    if (!u->client)
+        goto fail;
+
     pa_modargs_free(ma);
 
     return 0;

commit 0b0b3d895d291446112f4c77bf1d9388e675af55
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 15 18:23:32 2009 +0100

    make PA_CONTEXT_IS_GOOD/PA_STREAM_IS_GOOD a macro so that we can easily check for its availability

diff --git a/src/pulse/def.h b/src/pulse/def.h
index 7a715b6..a2e2914 100644
--- a/src/pulse/def.h
+++ b/src/pulse/def.h
@@ -46,7 +46,7 @@ typedef enum pa_context_state {
     PA_CONTEXT_TERMINATED      /**< The connection was terminated cleanly */
 } pa_context_state_t;
 
-/** Return non-zero if the passed state is one of the connected states */
+/** Return non-zero if the passed state is one of the connected states. \since 0.9.11 */
 static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) {
     return
         x == PA_CONTEXT_CONNECTING ||
@@ -55,6 +55,10 @@ static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) {
         x == PA_CONTEXT_READY;
 }
 
+/** \cond fulldocs */
+#define PA_CONTEXT_IS_GOOD PA_CONTEXT_IS_GOOD
+/** \endcond */
+
 /** The state of a stream */
 typedef enum pa_stream_state {
     PA_STREAM_UNCONNECTED,  /**< The stream is not yet connected to any sink or source */
@@ -64,13 +68,17 @@ typedef enum pa_stream_state {
     PA_STREAM_TERMINATED    /**< The stream has been terminated cleanly */
 } pa_stream_state_t;
 
-/** Return non-zero if the passed state is one of the connected states */
+/** Return non-zero if the passed state is one of the connected states. \since 0.9.11 */
 static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x) {
     return
         x == PA_STREAM_CREATING ||
         x == PA_STREAM_READY;
 }
 
+/** \cond fulldocs */
+#define PA_STREAM_IS_GOOD PA_STREAM_IS_GOOD
+/** \endcond */
+
 /** The state of an operation */
 typedef enum pa_operation_state {
     PA_OPERATION_RUNNING,      /**< The operation is still running */

commit aeb0707f1287c481d407d8e8f4a43f86661fdb23
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 15 18:27:43 2009 +0100

    fix bad memory access

diff --git a/src/pulsecore/client.c b/src/pulsecore/client.c
index 31631e1..445e876 100644
--- a/src/pulsecore/client.c
+++ b/src/pulsecore/client.c
@@ -88,9 +88,10 @@ void pa_client_free(pa_client *c) {
     pa_assert(c);
     pa_assert(c->core);
 
+    core = c->core;
+
     pa_hook_fire(&core->hooks[PA_CORE_HOOK_CLIENT_UNLINK], c);
 
-    core = c->core;
     pa_idxset_remove_by_data(c->core->clients, c, NULL);
 
     pa_log_info("Freed %u \"%s\"", c->index, pa_strnull(pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME)));

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

    add new pa_card object as a way to logically combine multiple sinks and sources

diff --git a/src/Makefile.am b/src/Makefile.am
index dd9035b..e570a6d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -716,6 +716,7 @@ libpulsecore_ at PA_MAJORMINORMICRO@_la_SOURCES = \
 		pulsecore/cli-command.c pulsecore/cli-command.h \
 		pulsecore/cli-text.c pulsecore/cli-text.h \
 		pulsecore/client.c pulsecore/client.h \
+		pulsecore/card.c pulsecore/card.h \
 		pulsecore/core-scache.c pulsecore/core-scache.h \
 		pulsecore/core-subscribe.c pulsecore/core-subscribe.h \
 		pulsecore/core.c pulsecore/core.h \
diff --git a/src/pulse/def.h b/src/pulse/def.h
index a2e2914..03e8416 100644
--- a/src/pulse/def.h
+++ b/src/pulse/def.h
@@ -391,7 +391,10 @@ typedef enum pa_subscription_mask {
     PA_SUBSCRIPTION_MASK_AUTOLOAD = 0x0100U,
     /**< Autoload table events. */
 
-    PA_SUBSCRIPTION_MASK_ALL = 0x01ffU
+    PA_SUBSCRIPTION_MASK_CARD = 0x0200U,
+    /**< Card events. \since 0.9.15 */
+
+    PA_SUBSCRIPTION_MASK_ALL = 0x03ffU
     /**< Catch all events */
 } pa_subscription_mask_t;
 
@@ -424,6 +427,9 @@ typedef enum pa_subscription_event_type {
     PA_SUBSCRIPTION_EVENT_AUTOLOAD = 0x0008U,
     /**< Event type: Autoload table changes. */
 
+    PA_SUBSCRIPTION_EVENT_CARD = 0x0009U,
+    /**< Event type: Card \since 0.9.15 */
+
     PA_SUBSCRIPTION_EVENT_FACILITY_MASK = 0x000FU,
     /**< A mask to extract the event type from an event value */
 
diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c
new file mode 100644
index 0000000..4f3548b
--- /dev/null
+++ b/src/pulsecore/card.c
@@ -0,0 +1,196 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 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 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/namereg.h>
+
+#include "card.h"
+
+pa_card_config *pa_card_config_new(const char *name) {
+    pa_card_config *c;
+
+    pa_assert(name);
+
+    c = pa_xnew0(pa_card_config, 1);
+    c->name = pa_xstrdup(name);
+
+    return c;
+}
+
+void pa_card_config_free(pa_card_config *c) {
+    pa_assert(c);
+
+    pa_xfree(c->name);
+    pa_xfree(c);
+}
+
+pa_card_new_data* pa_card_new_data_init(pa_card_new_data *data) {
+    pa_assert(data);
+
+    memset(data, 0, sizeof(*data));
+    data->proplist = pa_proplist_new();
+
+    return data;
+}
+
+void pa_card_new_data_set_name(pa_card_new_data *data, const char *name) {
+    pa_assert(data);
+
+    pa_xfree(data->name);
+    data->name = pa_xstrdup(name);
+}
+
+void pa_card_new_data_done(pa_card_new_data *data) {
+
+    pa_assert(data);
+
+    pa_proplist_free(data->proplist);
+
+    if (data->configs) {
+        pa_card_config *c;
+
+        while ((c = pa_hashmap_steal_first(data->configs)))
+            pa_card_config_free(c);
+
+        pa_hashmap_free(data->configs, NULL, NULL);
+    }
+
+    pa_xfree(data->name);
+}
+
+pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) {
+    pa_card *c;
+    const char *name;
+
+    pa_core_assert_ref(core);
+    pa_assert(data);
+    pa_assert(data->name);
+
+    c = pa_xnew(pa_card, 1);
+
+    if (!(name = pa_namereg_register(core, data->name, PA_NAMEREG_CARD, c, data->namereg_fail))) {
+        pa_xfree(c);
+        return NULL;
+    }
+
+    pa_card_new_data_set_name(data, name);
+
+    if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_CARD_NEW], data) < 0) {
+        pa_xfree(c);
+        pa_namereg_unregister(core, name);
+        return NULL;
+    }
+
+    c->core = core;
+    c->name = pa_xstrdup(data->name);
+    c->proplist = pa_proplist_copy(data->proplist);
+    c->driver = pa_xstrdup(data->driver);
+    c->module = data->module;
+
+    c->sinks = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+    c->sources = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+    c->configs = data->configs;
+    data->configs = NULL;
+    c->active_config = data->active_config;
+    data->active_config = NULL;
+
+    c->userdata = NULL;
+    c->set_config = NULL;
+
+    pa_assert_se(pa_idxset_put(core->cards, c, &c->index) >= 0);
+
+    pa_log_info("Created %u \"%s\"", c->index, c->name);
+    pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_NEW, c->index);
+
+    pa_hook_fire(&core->hooks[PA_CORE_HOOK_CARD_PUT], c);
+    return c;
+}
+
+void pa_card_free(pa_card *c) {
+    pa_core *core;
+    pa_card_config *config;
+
+    pa_assert(c);
+    pa_assert(c->core);
+
+    core = c->core;
+
+    pa_hook_fire(&core->hooks[PA_CORE_HOOK_CARD_UNLINK], c);
+
+    pa_namereg_unregister(core, c->name);
+
+    pa_idxset_remove_by_data(c->core->cards, c, NULL);
+
+    pa_log_info("Freed %u \"%s\"", c->index, c->name);
+
+    pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_REMOVE, c->index);
+
+    pa_idxset_free(c->sinks, NULL, NULL);
+    pa_idxset_free(c->sources, NULL, NULL);
+
+    while ((config = pa_hashmap_steal_first(c->configs)))
+        pa_card_config_free(config);
+
+    pa_hashmap_free(c->configs, NULL, NULL);
+
+    pa_proplist_free(c->proplist);
+    pa_xfree(c->driver);
+    pa_xfree(c->name);
+    pa_xfree(c);
+
+    pa_core_check_idle(core);
+}
+
+int pa_card_set_config(pa_card *c, const char *name) {
+    pa_card_config *config;
+    pa_assert(c);
+
+    if (!c->set_config) {
+        pa_log_warn("set_config() operation not implemented for card %u", c->index);
+        return -1;
+    }
+
+    if (!c->configs)
+        return -1;
+
+    if (!(config = pa_hashmap_get(c->configs, name)))
+        return -1;
+
+    if (c->set_config(c, config) < 0)
+        return -1;
+
+    pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, c->index);
+
+    return 0;
+}
diff --git a/src/pulsecore/card.h b/src/pulsecore/card.h
new file mode 100644
index 0000000..40e4a3e
--- /dev/null
+++ b/src/pulsecore/card.h
@@ -0,0 +1,91 @@
+#ifndef foopulsecardhfoo
+#define foopulsecardhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 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 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.
+***/
+
+typedef struct pa_card pa_card;
+
+#include <pulse/proplist.h>
+#include <pulsecore/core.h>
+#include <pulsecore/module.h>
+#include <pulsecore/idxset.h>
+
+typedef struct pa_card_config {
+    char *name;
+
+    pa_bool_t optical_sink:1;
+    pa_bool_t optical_source:1;
+
+    unsigned n_sinks;
+    unsigned n_sources;
+
+    unsigned max_sink_channels;
+    unsigned max_source_channels;
+} pa_card_config;
+
+struct pa_card {
+    uint32_t index;
+    pa_core *core;
+
+    char *name;
+
+    pa_proplist *proplist;
+    pa_module *module;
+    char *driver;
+
+    pa_idxset *sinks;
+    pa_idxset *sources;
+
+    pa_hashmap *configs;
+    pa_card_config *active_config;
+
+    void *userdata;
+
+    int (*set_config)(pa_card *c, pa_card_config *config);
+};
+
+typedef struct pa_card_new_data {
+    char *name;
+
+    pa_proplist *proplist;
+    const char *driver;
+    pa_module *module;
+
+    pa_hashmap *configs;
+    pa_card_config *active_config;
+
+    pa_bool_t namereg_fail:1;
+} pa_card_new_data;
+
+pa_card_config *pa_card_config_new(const char *name);
+void pa_card_config_free(pa_card_config *c);
+
+pa_card_new_data *pa_card_new_data_init(pa_card_new_data *data);
+void pa_card_new_data_set_name(pa_card_new_data *data, const char *name);
+void pa_card_new_data_done(pa_card_new_data *data);
+
+pa_card *pa_card_new(pa_core *c, pa_card_new_data *data);
+void pa_card_free(pa_card *c);
+
+int pa_card_set_config(pa_card *c, const char *name);
+
+#endif
diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c
index 5761bbc..2076432 100644
--- a/src/pulsecore/core.c
+++ b/src/pulsecore/core.c
@@ -97,6 +97,7 @@ pa_core* pa_core_new(pa_mainloop_api *m, pa_bool_t shared, size_t shm_size) {
     c->sources = pa_idxset_new(NULL, NULL);
     c->source_outputs = pa_idxset_new(NULL, NULL);
     c->sink_inputs = pa_idxset_new(NULL, NULL);
+    c->cards = pa_idxset_new(NULL, NULL);
 
     c->default_source_name = c->default_sink_name = NULL;
 
@@ -167,6 +168,9 @@ static void core_free(pa_object *o) {
     pa_assert(pa_idxset_isempty(c->clients));
     pa_idxset_free(c->clients, NULL, NULL);
 
+    pa_assert(pa_idxset_isempty(c->cards));
+    pa_idxset_free(c->cards, NULL, NULL);
+
     pa_assert(pa_idxset_isempty(c->sinks));
     pa_idxset_free(c->sinks, NULL, NULL);
 
diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index f1f38ef..87ea4ab 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -79,6 +79,9 @@ typedef enum pa_core_hook {
     PA_CORE_HOOK_CLIENT_NEW,
     PA_CORE_HOOK_CLIENT_PUT,
     PA_CORE_HOOK_CLIENT_UNLINK,
+    PA_CORE_HOOK_CARD_NEW,
+    PA_CORE_HOOK_CARD_PUT,
+    PA_CORE_HOOK_CARD_UNLINK,
     PA_CORE_HOOK_MAX
 } pa_core_hook_t;
 
@@ -96,7 +99,7 @@ struct pa_core {
     pa_mainloop_api *mainloop;
 
     /* idxset of all kinds of entities */
-    pa_idxset *clients, *sinks, *sources, *sink_inputs, *source_outputs, *modules, *scache, *autoload_idxset;
+    pa_idxset *clients, *cards, *sinks, *sources, *sink_inputs, *source_outputs, *modules, *scache, *autoload_idxset;
 
     /* Some hashmaps for all sorts of entities */
     pa_hashmap *namereg, *autoload_hashmap, *shared;
diff --git a/src/pulsecore/namereg.c b/src/pulsecore/namereg.c
index ecd8def..c1a434a 100644
--- a/src/pulsecore/namereg.c
+++ b/src/pulsecore/namereg.c
@@ -109,7 +109,7 @@ const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t
     if (!*name)
         return NULL;
 
-    if ((type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE) &&
+    if ((type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE || type == PA_NAMEREG_CARD) &&
         !pa_namereg_is_valid_name(name)) {
 
         if (fail)
diff --git a/src/pulsecore/namereg.h b/src/pulsecore/namereg.h
index f458100..8ce548a 100644
--- a/src/pulsecore/namereg.h
+++ b/src/pulsecore/namereg.h
@@ -30,7 +30,8 @@
 typedef enum pa_namereg_type {
     PA_NAMEREG_SINK,
     PA_NAMEREG_SOURCE,
-    PA_NAMEREG_SAMPLE
+    PA_NAMEREG_SAMPLE,
+    PA_NAMEREG_CARD
 } pa_namereg_type_t;
 
 void pa_namereg_free(pa_core *c);
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 3a66238..dbc72fb 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -183,6 +183,7 @@ pa_sink* pa_sink_new(
     s->proplist = pa_proplist_copy(data->proplist);
     s->driver = pa_xstrdup(data->driver);
     s->module = data->module;
+    s->card = data->card;
 
     s->sample_spec = data->sample_spec;
     s->channel_map = data->channel_map;
@@ -223,6 +224,9 @@ pa_sink* pa_sink_new(
 
     pa_assert_se(pa_idxset_put(core->sinks, s, &s->index) >= 0);
 
+    if (s->card)
+        pa_assert_se(pa_idxset_put(s->card->sinks, s, NULL) >= 0);
+
     pa_log_info("Created sink %u \"%s\" with sample spec %s and channel map %s",
                 s->index,
                 s->name,
@@ -235,6 +239,7 @@ pa_sink* pa_sink_new(
     source_data.name = pa_sprintf_malloc("%s.monitor", name);
     source_data.driver = data->driver;
     source_data.module = data->module;
+    source_data.card = data->card;
 
     dn = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_DESCRIPTION);
     pa_proplist_setf(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Monitor of %s", dn ? dn : s->name);
@@ -358,6 +363,9 @@ void pa_sink_unlink(pa_sink* s) {
         pa_namereg_unregister(s->core, s->name);
     pa_idxset_remove_by_data(s->core->sinks, s, NULL);
 
+    if (s->card)
+        pa_idxset_remove_by_data(s->card->sinks, s, NULL);
+
     while ((i = pa_idxset_first(s->inputs, NULL))) {
         pa_assert(i != j);
         pa_sink_input_kill(i);
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index 092e30f..2bd83b6 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -38,6 +38,7 @@ typedef struct pa_sink pa_sink;
 #include <pulsecore/refcnt.h>
 #include <pulsecore/msgobject.h>
 #include <pulsecore/rtpoll.h>
+#include <pulsecore/card.h>
 
 #define PA_MAX_INPUTS_PER_SINK 32
 
@@ -70,6 +71,7 @@ struct pa_sink {
     pa_proplist *proplist;
 
     pa_module *module;                      /* may be NULL */
+    pa_card *card;                          /* may be NULL */
 
     pa_sample_spec sample_spec;
     pa_channel_map channel_map;
@@ -186,6 +188,7 @@ typedef struct pa_sink_new_data {
 
     const char *driver;
     pa_module *module;
+    pa_card *card;
 
     pa_sample_spec sample_spec;
     pa_channel_map channel_map;
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index dee6f3d..676a6b4 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -174,6 +174,7 @@ pa_source* pa_source_new(
     s->proplist = pa_proplist_copy(data->proplist);
     s->driver = pa_xstrdup(data->driver);
     s->module = data->module;
+    s->card = data->card;
 
     s->sample_spec = data->sample_spec;
     s->channel_map = data->channel_map;
@@ -212,6 +213,9 @@ pa_source* pa_source_new(
 
     pa_assert_se(pa_idxset_put(core->sources, s, &s->index) >= 0);
 
+    if (s->card)
+        pa_assert_se(pa_idxset_put(s->card->sources, s, NULL) >= 0);
+
     pa_log_info("Created source %u \"%s\" with sample spec %s and channel map %s",
                 s->index,
                 s->name,
@@ -314,6 +318,9 @@ void pa_source_unlink(pa_source *s) {
         pa_namereg_unregister(s->core, s->name);
     pa_idxset_remove_by_data(s->core->sources, s, NULL);
 
+    if (s->card)
+        pa_idxset_remove_by_data(s->card->sinks, s, NULL);
+
     while ((o = pa_idxset_first(s->outputs, NULL))) {
         pa_assert(o != j);
         pa_source_output_kill(o);
diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
index fd8c4bd..4824099 100644
--- a/src/pulsecore/source.h
+++ b/src/pulsecore/source.h
@@ -41,6 +41,7 @@ typedef struct pa_source pa_source;
 #include <pulsecore/msgobject.h>
 #include <pulsecore/rtpoll.h>
 #include <pulsecore/source-output.h>
+#include <pulsecore/card.h>
 
 #define PA_MAX_OUTPUTS_PER_SOURCE 32
 
@@ -73,6 +74,7 @@ struct pa_source {
     pa_proplist *proplist;
 
     pa_module *module;                        /* may be NULL */
+    pa_card *card;                            /* may be NULL */
 
     pa_sample_spec sample_spec;
     pa_channel_map channel_map;
@@ -174,6 +176,7 @@ typedef struct pa_source_new_data {
 
     const char *driver;
     pa_module *module;
+    pa_card *card;
 
     pa_sample_spec sample_spec;
     pa_channel_map channel_map;

commit 344c934edbd780e1efe9ac413402ee6d70fe29e3
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 15 18:38:20 2009 +0100

    maintain a list of sink inputs/source outputs as part of the pa_client object

diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c
index 4f3548b..03b9ebd 100644
--- a/src/pulsecore/card.c
+++ b/src/pulsecore/card.c
@@ -117,8 +117,8 @@ pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) {
     c->driver = pa_xstrdup(data->driver);
     c->module = data->module;
 
-    c->sinks = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
-    c->sources = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+    c->sinks = pa_idxset_new(NULL, NULL);
+    c->sources = pa_idxset_new(NULL, NULL);
 
     c->configs = data->configs;
     data->configs = NULL;
@@ -156,7 +156,9 @@ void pa_card_free(pa_card *c) {
 
     pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_REMOVE, c->index);
 
+    pa_assert(pa_idxset_isempty(c->sinks));
     pa_idxset_free(c->sinks, NULL, NULL);
+    pa_assert(pa_idxset_isempty(c->sources));
     pa_idxset_free(c->sources, NULL, NULL);
 
     while ((config = pa_hashmap_steal_first(c->configs)))
diff --git a/src/pulsecore/client.c b/src/pulsecore/client.c
index 445e876..1e65fcd 100644
--- a/src/pulsecore/client.c
+++ b/src/pulsecore/client.c
@@ -67,6 +67,9 @@ pa_client *pa_client_new(pa_core *core, pa_client_new_data *data) {
     c->driver = pa_xstrdup(data->driver);
     c->module = data->module;
 
+    c->sink_inputs = pa_idxset_new(NULL, NULL);
+    c->source_outputs = pa_idxset_new(NULL, NULL);
+
     c->userdata = NULL;
     c->kill = NULL;
 
@@ -97,6 +100,11 @@ void pa_client_free(pa_client *c) {
     pa_log_info("Freed %u \"%s\"", c->index, pa_strnull(pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME)));
     pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_REMOVE, c->index);
 
+    pa_assert(pa_idxset_isempty(c->sink_inputs));
+    pa_idxset_free(c->sink_inputs, NULL, NULL);
+    pa_assert(pa_idxset_isempty(c->source_outputs));
+    pa_idxset_free(c->source_outputs, NULL, NULL);
+
     pa_proplist_free(c->proplist);
     pa_xfree(c->driver);
     pa_xfree(c);
diff --git a/src/pulsecore/client.h b/src/pulsecore/client.h
index 8e72f32..48e9bc7 100644
--- a/src/pulsecore/client.h
+++ b/src/pulsecore/client.h
@@ -42,6 +42,9 @@ struct pa_client {
     pa_module *module;
     char *driver;
 
+    pa_idxset *sink_inputs;
+    pa_idxset *source_outputs;
+
     void *userdata;
 
     void (*kill)(pa_client *c);
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index f5a1cb8..185350f 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -283,6 +283,9 @@ pa_sink_input* pa_sink_input_new(
     pa_assert_se(pa_idxset_put(core->sink_inputs, pa_sink_input_ref(i), &i->index) == 0);
     pa_assert_se(pa_idxset_put(i->sink->inputs, i, NULL) == 0);
 
+    if (i->client)
+        pa_assert_se(pa_idxset_put(i->client->sink_inputs, i, NULL) >= 0);
+
     pa_log_info("Created input %u \"%s\" on %s with sample spec %s and channel map %s",
                 i->index,
                 pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME)),
@@ -372,6 +375,9 @@ void pa_sink_input_unlink(pa_sink_input *i) {
     if (pa_idxset_remove_by_data(i->sink->inputs, i, NULL))
         pa_sink_input_unref(i);
 
+    if (i->client)
+        pa_idxset_remove_by_data(i->client->sink_inputs, i, NULL);
+
     while ((o = pa_idxset_first(i->direct_outputs, NULL))) {
         pa_assert(o != p);
         pa_source_output_kill(o);
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index e3d0d8b..b1c65d1 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -223,6 +223,9 @@ pa_source_output* pa_source_output_new(
     pa_assert_se(pa_idxset_put(core->source_outputs, o, &o->index) == 0);
     pa_assert_se(pa_idxset_put(o->source->outputs, pa_source_output_ref(o), NULL) == 0);
 
+    if (o->client)
+        pa_assert_se(pa_idxset_put(o->client->source_outputs, o, NULL) >= 0);
+
     if (o->direct_on_input)
         pa_assert_se(pa_idxset_put(o->direct_on_input->direct_outputs, o, NULL) == 0);
 
@@ -290,6 +293,9 @@ void pa_source_output_unlink(pa_source_output*o) {
     if (pa_idxset_remove_by_data(o->source->outputs, o, NULL))
         pa_source_output_unref(o);
 
+    if (o->client)
+        pa_idxset_remove_by_data(o->client->source_outputs, o, NULL);
+
     update_n_corked(o, PA_SOURCE_OUTPUT_UNLINKED);
     o->state = PA_SOURCE_OUTPUT_UNLINKED;
 

commit 615e05584e3fbc154cab3c6c7f8148b83b298739
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 15 18:52:11 2009 +0100

    add functionality to dump list of cards

diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c
index 07d55d0..93d6bbe 100644
--- a/src/pulsecore/cli-command.c
+++ b/src/pulsecore/cli-command.c
@@ -80,6 +80,7 @@ static int pa_cli_command_exit(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
 static int pa_cli_command_help(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
 static int pa_cli_command_modules(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
 static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_cards(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
 static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
 static int pa_cli_command_sources(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
 static int pa_cli_command_sink_inputs(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
@@ -137,6 +138,7 @@ static const struct command commands[] = {
     { "list-clients",            pa_cli_command_clients,            "List loaded clients",          1 },
     { "list-sink-inputs",        pa_cli_command_sink_inputs,        "List sink inputs",             1 },
     { "list-source-outputs",     pa_cli_command_source_outputs,     "List source outputs",          1 },
+    { "list-cards",              pa_cli_command_cards,              "List cards",                   1 },
     { "stat",                    pa_cli_command_stat,               "Show memory block statistics", 1 },
     { "info",                    pa_cli_command_info,               "Show comprehensive status",    1 },
     { "ls",                      pa_cli_command_info,               NULL,                           1 },
@@ -254,6 +256,20 @@ static int pa_cli_command_clients(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, p
     return 0;
 }
 
+static int pa_cli_command_cards(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    char *s;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    pa_assert_se(s = pa_card_list_to_string(c));
+    pa_strbuf_puts(buf, s);
+    pa_xfree(s);
+    return 0;
+}
+
 static int pa_cli_command_sinks(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
     char *s;
 
@@ -382,6 +398,7 @@ static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
     pa_cli_command_sinks(c, t, buf, fail);
     pa_cli_command_sources(c, t, buf, fail);
     pa_cli_command_clients(c, t, buf, fail);
+    pa_cli_command_cards(c, t, buf, fail);
     pa_cli_command_sink_inputs(c, t, buf, fail);
     pa_cli_command_source_outputs(c, t, buf, fail);
     pa_cli_command_scache_list(c, t, buf, fail);
diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index 362a979..5d78ce6 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -97,6 +97,38 @@ char *pa_client_list_to_string(pa_core *c) {
     return pa_strbuf_tostring_free(s);
 }
 
+char *pa_card_list_to_string(pa_core *c) {
+    pa_strbuf *s;
+    pa_card *card;
+    uint32_t idx = PA_IDXSET_INVALID;
+    pa_assert(c);
+
+    s = pa_strbuf_new();
+
+    pa_strbuf_printf(s, "%u card(s) available in.\n", pa_idxset_size(c->cards));
+
+    for (card = pa_idxset_first(c->cards, &idx); card; card = pa_idxset_next(c->cards, &idx)) {
+        char *t;
+        pa_strbuf_printf(
+                s,
+                "    index: %u\n"
+                "\tname: <%s>\n"
+                "\tdriver: <%s>\n",
+                card->index,
+                card->name,
+                card->driver);
+
+        if (card->module)
+            pa_strbuf_printf(s, "\towner module: %u\n", card->module->index);
+
+        t = pa_proplist_to_string(card->proplist);
+        pa_strbuf_printf(s, "\tproperties:\n%s", t);
+        pa_xfree(t);
+    }
+
+    return pa_strbuf_tostring_free(s);
+}
+
 char *pa_sink_list_to_string(pa_core *c) {
     pa_strbuf *s;
     pa_sink *sink;
@@ -174,6 +206,8 @@ char *pa_sink_list_to_string(pa_core *c) {
             pa_sink_used_by(sink),
             pa_sink_linked_by(sink));
 
+        if (sink->card)
+            pa_strbuf_printf(s, "\tcard: %u <%s>\n", sink->card->index, sink->card->name);
         if (sink->module)
             pa_strbuf_printf(s, "\tmodule: %u\n", sink->module->index);
 
@@ -260,6 +294,8 @@ char *pa_source_list_to_string(pa_core *c) {
 
         if (source->monitor_of)
             pa_strbuf_printf(s, "\tmonitor_of: %u\n", source->monitor_of->index);
+        if (source->card)
+            pa_strbuf_printf(s, "\tcard: %u <%s>\n", source->card->index, source->card->name);
         if (source->module)
             pa_strbuf_printf(s, "\tmodule: %u\n", source->module->index);
 
@@ -508,7 +544,7 @@ char *pa_full_status_string(pa_core *c) {
 
     s = pa_strbuf_new();
 
-    for (i = 0; i < 8; i++) {
+    for (i = 0; i < 9; i++) {
         char *t = NULL;
 
         switch (i) {
@@ -528,12 +564,15 @@ char *pa_full_status_string(pa_core *c) {
                 t = pa_client_list_to_string(c);
                 break;
             case 5:
-                t = pa_module_list_to_string(c);
+                t = pa_card_list_to_string(c);
                 break;
             case 6:
-                t = pa_scache_list_to_string(c);
+                t = pa_module_list_to_string(c);
                 break;
             case 7:
+                t = pa_scache_list_to_string(c);
+                break;
+            case 8:
                 t = pa_autoload_list_to_string(c);
                 break;
         }
diff --git a/src/pulsecore/cli-text.h b/src/pulsecore/cli-text.h
index f4cb97a..167565e 100644
--- a/src/pulsecore/cli-text.h
+++ b/src/pulsecore/cli-text.h
@@ -31,6 +31,7 @@ char *pa_sink_input_list_to_string(pa_core *c);
 char *pa_source_output_list_to_string(pa_core *c);
 char *pa_sink_list_to_string(pa_core *core);
 char *pa_source_list_to_string(pa_core *c);
+char *pa_card_list_to_string(pa_core *c);
 char *pa_client_list_to_string(pa_core *c);
 char *pa_module_list_to_string(pa_core *c);
 char *pa_scache_list_to_string(pa_core *c);
@@ -39,4 +40,3 @@ char *pa_autoload_list_to_string(pa_core *c);
 char *pa_full_status_string(pa_core *c);
 
 #endif
-

commit d2757c9842312687f0cb07380a823ce39849a2b2
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 15 19:16:55 2009 +0100

    redirect folks to the ALSA developers not me when their sound drivers are broken

diff --git a/src/modules/module-alsa-sink.c b/src/modules/module-alsa-sink.c
index 6fa45e3..3fa0b5d 100644
--- a/src/modules/module-alsa-sink.c
+++ b/src/modules/module-alsa-sink.c
@@ -287,7 +287,8 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle
 
             if (polled)
                 pa_log("ALSA woke us up to write new data to the device, but there was actually nothing to write! "
-                       "Most likely this is an ALSA driver bug. Please report this issue to the PulseAudio developers.");
+                       "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. "
+                       "We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail_update() returned 0.");
 
             break;
         }
@@ -409,7 +410,8 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle
 
             if (polled)
                 pa_log("ALSA woke us up to write new data to the device, but there was actually nothing to write! "
-                       "Most likely this is an ALSA driver bug. Please report this issue to the PulseAudio developers.");
+                       "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. "
+                       "We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail_update() returned 0.");
 
             break;
         }
diff --git a/src/modules/module-alsa-source.c b/src/modules/module-alsa-source.c
index 768c8c1..22e9ebf 100644
--- a/src/modules/module-alsa-source.c
+++ b/src/modules/module-alsa-source.c
@@ -274,7 +274,8 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
 
             if (polled)
                 pa_log("ALSA woke us up to read new data from the device, but there was actually nothing to read! "
-                       "Most likely this is an ALSA driver bug. Please report this issue to the PulseAudio device.");
+                       "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. "
+                       "We were woken up with POLLIN set -- however a subsequent snd_pcm_avail_update() returned 0.");
 
             break;
         }
@@ -381,7 +382,8 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
 
             if (polled)
                 pa_log("ALSA woke us up to read new data from the device, but there was actually nothing to read! "
-                       "Most likely this is an ALSA driver bug. Please report this issue to the PulseAudio developers.");
+                       "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. "
+                       "We were woken up with POLLIN set -- however a subsequent snd_pcm_avail_update() returned 0.");
 
             return work_done;
         }

commit a861ffacc490c690da8f05077be1efb7d4e2895c
Merge: d2757c9... e0f8ffe...
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 15 19:19:45 2009 +0100

    Merge commit 'e0f8ffe41f99789fafac575e944acf02e940bbf7'


commit 43762ed620589a9007b0c6b77022eb2ea1c6d926
Author: Marc-André Lureau <marcandre.lureau at gmail.com>
Date:   Mon Nov 10 15:43:05 2008 +0200

    flat-volume: use pa_sink_get_volume(s, TRUE) to work with slaved sink

diff --git a/src/modules/module-flat-volume.c b/src/modules/module-flat-volume.c
index 9bc8055..fe6dc92 100644
--- a/src/modules/module-flat-volume.c
+++ b/src/modules/module-flat-volume.c
@@ -94,11 +94,11 @@ static void process_input_volume_change(
     }
 
     /* Set the master volume, and normalize inputs */
-    if (!pa_cvolume_equal(&max_volume, &sink->volume)) {
+    if (!pa_cvolume_equal(&max_volume, pa_sink_get_volume(sink, TRUE))) {
 
         pa_sink_set_volume(sink, &max_volume);
 
-        pa_log_debug("sink = %.2f (changed)", (double)pa_cvolume_avg(&sink->volume)/PA_VOLUME_NORM);
+        pa_log_debug("sink = %.2f (changed)", (double)pa_cvolume_avg(pa_sink_get_volume(sink, TRUE))/PA_VOLUME_NORM);
 
         /* Now, normalize each of the internal volume (client sink-input volume / sink master volume) */
         for (i = PA_SINK_INPUT(pa_idxset_first(sink->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(sink->inputs, &idx))) {
@@ -116,7 +116,7 @@ static void process_input_volume_change(
             pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_VOLUME, pa_xnewdup(struct pa_cvolume, &i->volume, 1), 0, NULL, pa_xfree);
         }
     } else
-        pa_log_debug("sink = %.2f", (double)pa_cvolume_avg(&sink->volume)/PA_VOLUME_NORM);
+        pa_log_debug("sink = %.2f", (double)pa_cvolume_avg(pa_sink_get_volume(sink, TRUE))/PA_VOLUME_NORM);
 
     /* and this one */
 
@@ -170,9 +170,9 @@ static void subscribe_callback(pa_core *core, pa_subscription_event_type_t t, ui
         return;
 
     pa_log_debug("Sink volume changed");
-    pa_log_debug("sink = %.2f", (double)pa_cvolume_avg(pa_sink_get_volume(sink, FALSE)) / PA_VOLUME_NORM);
+    pa_log_debug("sink = %.2f", (double)pa_cvolume_avg(pa_sink_get_volume(sink, TRUE)) / PA_VOLUME_NORM);
 
-    sink_volume = *pa_sink_get_volume(sink, FALSE);
+    sink_volume = *pa_sink_get_volume(sink, TRUE);
 
     for (i = PA_SINK_INPUT(pa_idxset_first(sink->inputs, &iidx)); i; i = PA_SINK_INPUT(pa_idxset_next(sink->inputs, &iidx))) {
         pa_cvolume si_volume;

commit 29c7a288177c260cf2b3d8f80e807305b96594ba
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 15 20:07:13 2009 +0100

    kill autoload stuff as planned

diff --git a/src/Makefile.am b/src/Makefile.am
index e570a6d..4b36f6f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -712,7 +712,6 @@ libpulsecore_ at PA_MAJORMINORMICRO@_la_SOURCES = \
 		pulsecore/asyncmsgq.c pulsecore/asyncmsgq.h \
 		pulsecore/asyncq.c pulsecore/asyncq.h \
 		pulsecore/auth-cookie.c pulsecore/auth-cookie.h \
-		pulsecore/autoload.c pulsecore/autoload.h \
 		pulsecore/cli-command.c pulsecore/cli-command.h \
 		pulsecore/cli-text.c pulsecore/cli-text.h \
 		pulsecore/client.c pulsecore/client.h \
diff --git a/src/daemon/cmdline.c b/src/daemon/cmdline.c
index cc3d714..43a4a32 100644
--- a/src/daemon/cmdline.c
+++ b/src/daemon/cmdline.c
@@ -52,7 +52,6 @@ enum {
     ARG_DISALLOW_MODULE_LOADING,
     ARG_DISALLOW_EXIT,
     ARG_EXIT_IDLE_TIME,
-    ARG_MODULE_IDLE_TIME,
     ARG_SCACHE_IDLE_TIME,
     ARG_LOG_TARGET,
     ARG_LOG_META,
@@ -88,7 +87,6 @@ static const struct option long_options[] = {
     {"disallow-module-loading",     2, 0, ARG_DISALLOW_MODULE_LOADING},
     {"disallow-exit",               2, 0, ARG_DISALLOW_EXIT},
     {"exit-idle-time",              2, 0, ARG_EXIT_IDLE_TIME},
-    {"module-idle-time",            2, 0, ARG_MODULE_IDLE_TIME},
     {"scache-idle-time",            2, 0, ARG_SCACHE_IDLE_TIME},
     {"log-target",                  1, 0, ARG_LOG_TARGET},
     {"log-meta",                    2, 0, ARG_LOG_META},
@@ -352,10 +350,6 @@ int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d
                 conf->exit_idle_time = atoi(optarg);
                 break;
 
-            case ARG_MODULE_IDLE_TIME:
-                conf->module_idle_time = atoi(optarg);
-                break;
-
             case ARG_SCACHE_IDLE_TIME:
                 conf->scache_idle_time = atoi(optarg);
                 break;
diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c
index d7ffc10..c3abc09 100644
--- a/src/daemon/daemon-conf.c
+++ b/src/daemon/daemon-conf.c
@@ -65,7 +65,6 @@ static const pa_daemon_conf default_conf = {
     .disallow_module_loading = FALSE,
     .disallow_exit = FALSE,
     .exit_idle_time = 20,
-    .module_idle_time = 20,
     .scache_idle_time = 20,
     .auto_log_target = 1,
     .script_commands = NULL,
@@ -416,7 +415,6 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
         { "no-cpu-limit",               pa_config_parse_bool,     NULL },
         { "disable-shm",                pa_config_parse_bool,     NULL },
         { "exit-idle-time",             pa_config_parse_int,      NULL },
-        { "module-idle-time",           pa_config_parse_int,      NULL },
         { "scache-idle-time",           pa_config_parse_int,      NULL },
         { "realtime-priority",          parse_rtprio,             NULL },
         { "dl-search-path",             pa_config_parse_string,   NULL },
@@ -485,7 +483,6 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
     table[i++].data = &c->no_cpu_limit;
     table[i++].data = &c->disable_shm;
     table[i++].data = &c->exit_idle_time;
-    table[i++].data = &c->module_idle_time;
     table[i++].data = &c->scache_idle_time;
     table[i++].data = c;
     table[i++].data = &c->dl_search_path;
@@ -642,7 +639,6 @@ char *pa_daemon_conf_dump(pa_daemon_conf *c) {
     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, "exit-idle-time = %i\n", c->exit_idle_time);
-    pa_strbuf_printf(s, "module-idle-time = %i\n", c->module_idle_time);
     pa_strbuf_printf(s, "scache-idle-time = %i\n", c->scache_idle_time);
     pa_strbuf_printf(s, "dl-search-path = %s\n", pa_strempty(c->dl_search_path));
     pa_strbuf_printf(s, "default-script-file = %s\n", pa_strempty(pa_daemon_conf_get_default_script_file(c)));
diff --git a/src/daemon/daemon-conf.h b/src/daemon/daemon-conf.h
index 04a4ebe..fffa35e 100644
--- a/src/daemon/daemon-conf.h
+++ b/src/daemon/daemon-conf.h
@@ -72,7 +72,6 @@ typedef struct pa_daemon_conf {
         log_meta,
         log_time;
     int exit_idle_time,
-        module_idle_time,
         scache_idle_time,
         auto_log_target,
         realtime_priority,
diff --git a/src/daemon/main.c b/src/daemon/main.c
index 9c419ef..08f65e6 100644
--- a/src/daemon/main.c
+++ b/src/daemon/main.c
@@ -901,7 +901,6 @@ int main(int argc, char *argv[]) {
     c->default_n_fragments = conf->default_n_fragments;
     c->default_fragment_size_msec = conf->default_fragment_size_msec;
     c->exit_idle_time = conf->exit_idle_time;
-    c->module_idle_time = conf->module_idle_time;
     c->scache_idle_time = conf->scache_idle_time;
     c->resample_method = conf->resample_method;
     c->realtime_priority = conf->realtime_priority;
@@ -963,7 +962,7 @@ int main(int argc, char *argv[]) {
         goto finish;
     }
 
-    if (c->default_sink_name && !pa_namereg_get(c, c->default_sink_name, PA_NAMEREG_SINK, TRUE) && conf->fail) {
+    if (c->default_sink_name && !pa_namereg_get(c, c->default_sink_name, PA_NAMEREG_SINK) && conf->fail) {
         pa_log_error(_("Default sink name (%s) does not exist in name register."), c->default_sink_name);
         goto finish;
     }
diff --git a/src/modules/bluetooth/module-bluetooth-proximity.c b/src/modules/bluetooth/module-bluetooth-proximity.c
index 4cfaaf5..f30d39f 100644
--- a/src/modules/bluetooth/module-bluetooth-proximity.c
+++ b/src/modules/bluetooth/module-bluetooth-proximity.c
@@ -103,7 +103,7 @@ static void update_volume(struct userdata *u) {
 
         u->muted = FALSE;
 
-        if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, FALSE))) {
+        if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK))) {
             pa_log_warn("Sink device '%s' not available for unmuting.", pa_strnull(u->sink_name));
             return;
         }
@@ -116,7 +116,7 @@ static void update_volume(struct userdata *u) {
 
         u->muted = TRUE;
 
-        if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, FALSE))) {
+        if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK))) {
             pa_log_warn("Sink device '%s' not available for muting.", pa_strnull(u->sink_name));
             return;
         }
diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c
index fa80781..b1dc820 100644
--- a/src/modules/module-combine.c
+++ b/src/modules/module-combine.c
@@ -1103,7 +1103,7 @@ int pa__init(pa_module*m) {
         while ((n = pa_split(slaves, ",", &split_state))) {
             pa_sink *slave_sink;
 
-            if (!(slave_sink = pa_namereg_get(m->core, n, PA_NAMEREG_SINK, TRUE)) || slave_sink == u->sink) {
+            if (!(slave_sink = pa_namereg_get(m->core, n, PA_NAMEREG_SINK)) || slave_sink == u->sink) {
                 pa_log("Invalid slave sink '%s'", n);
                 pa_xfree(n);
                 goto fail;
diff --git a/src/modules/module-default-device-restore.c b/src/modules/module-default-device-restore.c
index d2cc24f..97d3fb2 100644
--- a/src/modules/module-default-device-restore.c
+++ b/src/modules/module-default-device-restore.c
@@ -68,7 +68,7 @@ static void load(struct userdata *u) {
 
         if (!ln[0])
             pa_log_info("No previous default sink setting, ignoring.");
-        else if (pa_namereg_get(u->core, ln, PA_NAMEREG_SINK, TRUE)) {
+        else if (pa_namereg_get(u->core, ln, PA_NAMEREG_SINK)) {
             pa_namereg_set_default(u->core, ln, PA_NAMEREG_SINK);
             pa_log_info("Restored default sink '%s'.", ln);
         } else
@@ -88,7 +88,7 @@ static void load(struct userdata *u) {
 
         if (!ln[0])
             pa_log_info("No previous default source setting, ignoring.");
-        else if (pa_namereg_get(u->core, ln, PA_NAMEREG_SOURCE, TRUE)) {
+        else if (pa_namereg_get(u->core, ln, PA_NAMEREG_SOURCE)) {
             pa_namereg_set_default(u->core, ln, PA_NAMEREG_SOURCE);
             pa_log_info("Restored default source '%s'.", ln);
         } else
diff --git a/src/modules/module-hal-detect.c b/src/modules/module-hal-detect.c
index 8c1ab32..e287a5d 100644
--- a/src/modules/module-hal-detect.c
+++ b/src/modules/module-hal-detect.c
@@ -378,7 +378,7 @@ static int hal_device_add_all(struct userdata *u, const char *capability) {
                 pa_log_debug("Not loaded device %s", udis[i]);
             else {
                 if (d->sink_name)
-                    pa_scache_play_item_by_name(u->core, "pulse-coldplug", d->sink_name, FALSE, PA_VOLUME_NORM, NULL, NULL);
+                    pa_scache_play_item_by_name(u->core, "pulse-coldplug", d->sink_name, PA_VOLUME_NORM, NULL, NULL);
                 count++;
             }
         }
@@ -418,7 +418,7 @@ static void device_added_time_cb(pa_mainloop_api *ea, pa_time_event *ev, const s
                 pa_log_debug("Not loaded device %s", td->udi);
             else {
                 if (d->sink_name)
-                    pa_scache_play_item_by_name(td->u->core, "pulse-hotplug", d->sink_name, FALSE, PA_VOLUME_NORM, NULL, NULL);
+                    pa_scache_play_item_by_name(td->u->core, "pulse-hotplug", d->sink_name, PA_VOLUME_NORM, NULL, NULL);
             }
         }
     }
@@ -575,13 +575,13 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
                 if (d->sink_name) {
                     pa_sink *sink;
 
-                    if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK, 0))) {
+                    if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK))) {
                         int prev_suspended = pa_sink_get_state(sink) == PA_SINK_SUSPENDED;
 
                         if (prev_suspended && !suspend) {
                             /* resume */
                             if (pa_sink_suspend(sink, 0) >= 0)
-                                pa_scache_play_item_by_name(u->core, "pulse-access", d->sink_name, FALSE, PA_VOLUME_NORM, NULL, NULL);
+                                pa_scache_play_item_by_name(u->core, "pulse-access", d->sink_name, PA_VOLUME_NORM, NULL, NULL);
                             else
                                 d->acl_race_fix = TRUE;
 
@@ -596,7 +596,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
                 if (d->source_name) {
                     pa_source *source;
 
-                    if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE, 0))) {
+                    if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE))) {
                         int prev_suspended = pa_source_get_state(source) == PA_SOURCE_SUSPENDED;
 
                         if (prev_suspended && !suspend) {
@@ -644,14 +644,14 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
             if (d->sink_name) {
                 pa_sink *sink;
 
-                if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK, 0))) {
+                if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK))) {
 
                     int prev_suspended = pa_sink_get_state(sink) == PA_SINK_SUSPENDED;
 
                     if (prev_suspended) {
                         /* resume */
                         if (pa_sink_suspend(sink, 0) >= 0)
-                            pa_scache_play_item_by_name(u->core, "pulse-access", d->sink_name, FALSE, PA_VOLUME_NORM, NULL, NULL);
+                            pa_scache_play_item_by_name(u->core, "pulse-access", d->sink_name, PA_VOLUME_NORM, NULL, NULL);
                     }
                 }
             }
@@ -659,7 +659,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
             if (d->source_name) {
                 pa_source *source;
 
-                if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE, 0))) {
+                if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE))) {
 
                     int prev_suspended = pa_source_get_state(source) == PA_SOURCE_SUSPENDED;
 
diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c
index 4209e99..496e9ea 100644
--- a/src/modules/module-ladspa-sink.c
+++ b/src/modules/module-ladspa-sink.c
@@ -396,7 +396,7 @@ int pa__init(pa_module*m) {
         goto fail;
     }
 
-    if (!(master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "master", NULL), PA_NAMEREG_SINK, 1))) {
+    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;
     }
diff --git a/src/modules/module-lirc.c b/src/modules/module-lirc.c
index 97e97dc..bbe4f7c 100644
--- a/src/modules/module-lirc.c
+++ b/src/modules/module-lirc.c
@@ -118,7 +118,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event
             else {
                 pa_sink *s;
 
-                if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, 1)))
+                if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK)))
                     pa_log("Failed to get sink '%s'", u->sink_name);
                 else {
                     int i;
diff --git a/src/modules/module-mmkbd-evdev.c b/src/modules/module-mmkbd-evdev.c
index 21f176a..aa6832b 100644
--- a/src/modules/module-mmkbd-evdev.c
+++ b/src/modules/module-mmkbd-evdev.c
@@ -109,7 +109,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event
             if (volchange != INVALID) {
                 pa_sink *s;
 
-                if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, 1)))
+                if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK)))
                     pa_log("Failed to get sink '%s'", u->sink_name);
                 else {
                     int i;
diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c
index ae05f07..aa91406 100644
--- a/src/modules/module-remap-sink.c
+++ b/src/modules/module-remap-sink.c
@@ -302,7 +302,7 @@ int pa__init(pa_module*m) {
         goto fail;
     }
 
-    if (!(master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "master", NULL), PA_NAMEREG_SINK, 1))) {
+    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;
     }
diff --git a/src/modules/module-rescue-streams.c b/src/modules/module-rescue-streams.c
index cc6717c..de07225 100644
--- a/src/modules/module-rescue-streams.c
+++ b/src/modules/module-rescue-streams.c
@@ -59,7 +59,7 @@ static pa_hook_result_t sink_hook_callback(pa_core *c, pa_sink *sink, void* user
         return PA_HOOK_OK;
     }
 
-    if (!(target = pa_namereg_get(c, NULL, PA_NAMEREG_SINK, 0)) || target == sink) {
+    if (!(target = pa_namereg_get(c, NULL, PA_NAMEREG_SINK)) || target == sink) {
         uint32_t idx;
 
         for (target = pa_idxset_first(c->sinks, &idx); target; target = pa_idxset_next(c->sinks, &idx))
@@ -97,7 +97,7 @@ static pa_hook_result_t source_hook_callback(pa_core *c, pa_source *source, void
         return PA_HOOK_OK;
     }
 
-    if (!(target = pa_namereg_get(c, NULL, PA_NAMEREG_SOURCE, 0)) || target == source) {
+    if (!(target = pa_namereg_get(c, NULL, PA_NAMEREG_SOURCE)) || target == source) {
         uint32_t idx;
 
         for (target = pa_idxset_first(c->sources, &idx); target; target = pa_idxset_next(c->sources, &idx))
diff --git a/src/modules/module-sine.c b/src/modules/module-sine.c
index 2ea78da..b0782c3 100644
--- a/src/modules/module-sine.c
+++ b/src/modules/module-sine.c
@@ -131,7 +131,7 @@ int pa__init(pa_module*m) {
         goto fail;
     }
 
-    if (!(sink = pa_namereg_get(m->core, pa_modargs_get_value(ma, "sink", NULL), PA_NAMEREG_SINK, TRUE))) {
+    if (!(sink = pa_namereg_get(m->core, pa_modargs_get_value(ma, "sink", NULL), PA_NAMEREG_SINK))) {
         pa_log("No such sink.");
         goto fail;
     }
diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c
index fdf69a2..dd54a79 100644
--- a/src/modules/module-stream-restore.c
+++ b/src/modules/module-stream-restore.c
@@ -304,7 +304,7 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
         pa_sink *s;
 
         if (u->restore_device &&
-            (s = pa_namereg_get(c, e->device, PA_NAMEREG_SINK, TRUE))) {
+            (s = pa_namereg_get(c, e->device, PA_NAMEREG_SINK))) {
 
             if (!new_data->sink) {
                 pa_log_info("Restoring device for stream %s.", name);
@@ -371,7 +371,7 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou
 
         if (u->restore_device &&
             !new_data->direct_on_input &&
-            (s = pa_namereg_get(c, e->device, PA_NAMEREG_SOURCE, TRUE))) {
+            (s = pa_namereg_get(c, e->device, PA_NAMEREG_SOURCE))) {
 
             if (!new_data->source) {
                 pa_log_info("Restoring device for stream %s.", name);
@@ -442,7 +442,7 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
         }
 
         if (u->restore_device &&
-            (s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SOURCE, TRUE))) {
+            (s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SOURCE))) {
 
             pa_log_info("Restoring device for stream %s.", name);
             pa_sink_input_move_to(si, s);
@@ -462,7 +462,7 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
         }
 
         if (u->restore_device &&
-            (s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SOURCE, TRUE))) {
+            (s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SOURCE))) {
 
             pa_log_info("Restoring device for stream %s.", name);
             pa_source_output_move_to(so, s);
diff --git a/src/modules/module-volume-restore.c b/src/modules/module-volume-restore.c
index aac0d04..bdfd381 100644
--- a/src/modules/module-volume-restore.c
+++ b/src/modules/module-volume-restore.c
@@ -417,7 +417,7 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
 
     if ((r = pa_hashmap_get(u->hashmap, name))) {
         if (!data->sink && r->sink) {
-            if ((data->sink = pa_namereg_get(c, r->sink, PA_NAMEREG_SINK, 1)))
+            if ((data->sink = pa_namereg_get(c, r->sink, PA_NAMEREG_SINK)))
                 pa_log_info("Restoring sink for <%s>", r->name);
         }
     }
@@ -463,7 +463,7 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou
 
     if ((r = pa_hashmap_get(u->hashmap, name))) {
         if (!data->source && r->source) {
-            if ((data->source = pa_namereg_get(c, r->source, PA_NAMEREG_SOURCE, 1)))
+            if ((data->source = pa_namereg_get(c, r->source, PA_NAMEREG_SOURCE)))
                 pa_log_info("Restoring source for <%s>", r->name);
         }
     }
diff --git a/src/modules/module-x11-bell.c b/src/modules/module-x11-bell.c
index e93721c..bef0253 100644
--- a/src/modules/module-x11-bell.c
+++ b/src/modules/module-x11-bell.c
@@ -82,7 +82,7 @@ static int x11_event_cb(pa_x11_wrapper *w, XEvent *e, void *userdata) {
 
     bne = (XkbBellNotifyEvent*) e;
 
-    if (pa_scache_play_item_by_name(u->core, u->scache_item, u->sink_name, TRUE, ((pa_volume_t) bne->percent*PA_VOLUME_NORM)/100U, NULL, NULL) < 0) {
+    if (pa_scache_play_item_by_name(u->core, u->scache_item, u->sink_name, ((pa_volume_t) bne->percent*PA_VOLUME_NORM)/100U, NULL, NULL) < 0) {
         pa_log_info("Ringing bell failed, reverting to X11 device bell.");
         XkbForceDeviceBell(pa_x11_wrapper_get_display(w), bne->device, bne->bell_class, bne->bell_id, bne->percent);
     }
diff --git a/src/modules/rtp/module-rtp-recv.c b/src/modules/rtp/module-rtp-recv.c
index 478e0a3..00d2125 100644
--- a/src/modules/rtp/module-rtp-recv.c
+++ b/src/modules/rtp/module-rtp-recv.c
@@ -415,7 +415,7 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in
         goto fail;
     }
 
-    if (!(sink = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, TRUE))) {
+    if (!(sink = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK))) {
         pa_log("Sink does not exist.");
         goto fail;
     }
diff --git a/src/modules/rtp/module-rtp-send.c b/src/modules/rtp/module-rtp-send.c
index 9c0f07f..a6d682b 100644
--- a/src/modules/rtp/module-rtp-send.c
+++ b/src/modules/rtp/module-rtp-send.c
@@ -196,7 +196,7 @@ int pa__init(pa_module*m) {
         goto fail;
     }
 
-    if (!(s = pa_namereg_get(m->core, pa_modargs_get_value(ma, "source", NULL), PA_NAMEREG_SOURCE, 1))) {
+    if (!(s = pa_namereg_get(m->core, pa_modargs_get_value(ma, "source", NULL), PA_NAMEREG_SOURCE))) {
         pa_log("Source does not exist.");
         goto fail;
     }
diff --git a/src/pulse/def.h b/src/pulse/def.h
index 03e8416..9e424d4 100644
--- a/src/pulse/def.h
+++ b/src/pulse/def.h
@@ -356,6 +356,7 @@ enum {
     PA_ERR_NOTSUPPORTED,           /**< Operation not supported \since 0.9.5 */
     PA_ERR_UNKNOWN,                /**< The error code was unknown to the client */
     PA_ERR_NOEXTENSION,            /**< Extension does not exist. \since 0.9.12 */
+    PA_ERR_OBSOLETE,               /**< Obsolete functionality. \since 0.9.15 */
     PA_ERR_MAX                     /**< Not really an error but the first invalid error code */
 };
 
@@ -388,13 +389,15 @@ typedef enum pa_subscription_mask {
     PA_SUBSCRIPTION_MASK_SERVER = 0x0080U,
     /**< Other global server changes. */
 
+/** \cond fulldocs */
     PA_SUBSCRIPTION_MASK_AUTOLOAD = 0x0100U,
-    /**< Autoload table events. */
+    /**< \deprecated Autoload table events. */
+/** \endcond */
 
     PA_SUBSCRIPTION_MASK_CARD = 0x0200U,
     /**< Card events. \since 0.9.15 */
 
-    PA_SUBSCRIPTION_MASK_ALL = 0x03ffU
+    PA_SUBSCRIPTION_MASK_ALL = 0x02ffU
     /**< Catch all events */
 } pa_subscription_mask_t;
 
@@ -424,8 +427,10 @@ typedef enum pa_subscription_event_type {
     PA_SUBSCRIPTION_EVENT_SERVER = 0x0007U,
     /**< Event type: Global server change, only occuring with PA_SUBSCRIPTION_EVENT_CHANGE. */
 
+/** \cond fulldocs */
     PA_SUBSCRIPTION_EVENT_AUTOLOAD = 0x0008U,
-    /**< Event type: Autoload table changes. */
+    /**< \deprecated Event type: Autoload table changes. */
+/** \endcond */
 
     PA_SUBSCRIPTION_EVENT_CARD = 0x0009U,
     /**< Event type: Card \since 0.9.15 */
diff --git a/src/pulse/internal.h b/src/pulse/internal.h
index 5fe4210..9a2d645 100644
--- a/src/pulse/internal.h
+++ b/src/pulse/internal.h
@@ -225,20 +225,37 @@ void pa_stream_set_state(pa_stream *s, pa_stream_state_t st);
 
 pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *tag);
 
-#define PA_CHECK_VALIDITY(context, expression, error) do { \
-        if (!(expression)) \
+#define PA_CHECK_VALIDITY(context, expression, error)         \
+    do {                                                      \
+        if (!(expression))                                    \
             return -pa_context_set_error((context), (error)); \
-} while(0)
+    } while(FALSE)
 
 
-#define PA_CHECK_VALIDITY_RETURN_ANY(context, expression, error, value) do { \
-        if (!(expression)) { \
-            pa_context_set_error((context), (error)); \
-            return value; \
-        } \
-} while(0)
+#define PA_CHECK_VALIDITY_RETURN_ANY(context, expression, error, value) \
+    do {                                                                \
+        if (!(expression)) {                                            \
+            pa_context_set_error((context), (error));                   \
+            return value;                                               \
+        }                                                               \
+    } while(FALSE)
 
-#define PA_CHECK_VALIDITY_RETURN_NULL(context, expression, error) PA_CHECK_VALIDITY_RETURN_ANY(context, expression, error, NULL)
+#define PA_CHECK_VALIDITY_RETURN_NULL(context, expression, error)       \
+    PA_CHECK_VALIDITY_RETURN_ANY(context, expression, error, NULL)
+
+#define PA_FAIL(context, error)                                 \
+    do {                                                        \
+        return -pa_context_set_error((context), (error));       \
+    } while(FALSE)
+
+#define PA_FAIL_RETURN_ANY(context, error, value)      \
+    do {                                               \
+        pa_context_set_error((context), (error));      \
+        return value;                                  \
+    } while(FALSE)
+
+#define PA_FAIL_RETURN_NULL(context, error)     \
+    PA_FAIL_RETURN_ANY(context, error, NULL)
 
 void pa_ext_stream_restore_command(pa_context *c, uint32_t tag, pa_tagstruct *t);
 
diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c
index 8056a5a..bdc50e2 100644
--- a/src/pulse/introspect.c
+++ b/src/pulse/introspect.c
@@ -1167,186 +1167,59 @@ pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, pa_context_s
 
 /*** Autoload stuff ***/
 
-static void context_get_autoload_info_callback(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_autoload_info i;
-
-            memset(&i, 0, sizeof(i));
-
-            if (pa_tagstruct_getu32(t, &i.index) < 0 ||
-                pa_tagstruct_gets(t, &i.name) < 0 ||
-                pa_tagstruct_getu32(t, &i.type) < 0 ||
-                pa_tagstruct_gets(t, &i.module) < 0 ||
-                pa_tagstruct_gets(t, &i.argument) < 0) {
-                pa_context_fail(o->context, PA_ERR_PROTOCOL);
-                goto finish;
-            }
-
-            if (o->callback) {
-                pa_autoload_info_cb_t cb = (pa_autoload_info_cb_t) o->callback;
-                cb(o->context, &i, 0, o->userdata);
-            }
-        }
-    }
-
-    if (o->callback) {
-        pa_autoload_info_cb_t cb = (pa_autoload_info_cb_t) o->callback;
-        cb(o->context, NULL, eol, o->userdata);
-    }
-
-finish:
-    pa_operation_done(o);
-    pa_operation_unref(o);
-}
-
-PA_WARN_REFERENCE(pa_context_get_autoload_info_by_name, "Autoload will no longer be implemented by future versions of the PulseAudio server.");
+PA_WARN_REFERENCE(pa_context_get_autoload_info_by_name, "Module auto-loading no longer supported.");
 
 pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_autoload_info_cb_t cb, void *userdata) {
-    pa_tagstruct *t;
-    pa_operation *o;
-    uint32_t tag;
 
     pa_assert(c);
     pa_assert(PA_REFCNT_VALUE(c) >= 1);
-    pa_assert(cb);
-
-    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
-    PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
-    PA_CHECK_VALIDITY_RETURN_NULL(c, type == PA_AUTOLOAD_SINK || type == PA_AUTOLOAD_SOURCE, PA_ERR_INVALID);
-
-    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
 
-    t = pa_tagstruct_command(c, PA_COMMAND_GET_AUTOLOAD_INFO, &tag);
-    pa_tagstruct_puts(t, name);
-    pa_tagstruct_putu32(t, type);
-    pa_pstream_send_tagstruct(c->pstream, t);
-    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_autoload_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
-
-    return o;
+    PA_FAIL_RETURN_NULL(c, PA_ERR_OBSOLETE);
 }
 
-PA_WARN_REFERENCE(pa_context_get_autoload_info_by_index, "Autoload will no longer be implemented by future versions of the PulseAudio server.");
+PA_WARN_REFERENCE(pa_context_get_autoload_info_by_index, "Module auto-loading no longer supported.");
 
 pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, pa_autoload_info_cb_t cb, void *userdata) {
-    pa_tagstruct *t;
-    pa_operation *o;
-    uint32_t tag;
-
     pa_assert(c);
     pa_assert(PA_REFCNT_VALUE(c) >= 1);
-    pa_assert(cb);
-
-    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
-    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
-
-    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
 
-    t = pa_tagstruct_command(c, PA_COMMAND_GET_AUTOLOAD_INFO, &tag);
-    pa_tagstruct_putu32(t, idx);
-    pa_pstream_send_tagstruct(c->pstream, t);
-    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_autoload_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
-
-    return o;
+    PA_FAIL_RETURN_NULL(c, PA_ERR_OBSOLETE);
 }
 
-
-PA_WARN_REFERENCE(pa_context_get_autoload_info_list, "Autoload will no longer be implemented by future versions of the PulseAudio server.");
+PA_WARN_REFERENCE(pa_context_get_autoload_info_list, "Module auto-loading no longer supported.");
 
 pa_operation* pa_context_get_autoload_info_list(pa_context *c, pa_autoload_info_cb_t cb, void *userdata) {
-    return pa_context_send_simple_command(c, PA_COMMAND_GET_AUTOLOAD_INFO_LIST, context_get_autoload_info_callback, (pa_operation_cb_t) cb, userdata);
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_FAIL_RETURN_NULL(c, PA_ERR_OBSOLETE);
 }
 
-PA_WARN_REFERENCE(pa_context_add_autoload, "Autoload will no longer be implemented by future versions of the PulseAudio server.");
+PA_WARN_REFERENCE(pa_context_add_autoload, "Module auto-loading no longer supported.");
 
 pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, pa_context_index_cb_t cb, void* userdata) {
-    pa_operation *o;
-    pa_tagstruct *t;
-    uint32_t tag;
-
     pa_assert(c);
     pa_assert(PA_REFCNT_VALUE(c) >= 1);
 
-    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
-    PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
-    PA_CHECK_VALIDITY_RETURN_NULL(c, type == PA_AUTOLOAD_SINK || type == PA_AUTOLOAD_SOURCE, PA_ERR_INVALID);
-    PA_CHECK_VALIDITY_RETURN_NULL(c, module && *module, PA_ERR_INVALID);
-
-    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
-
-    t = pa_tagstruct_command(c, PA_COMMAND_ADD_AUTOLOAD, &tag);
-    pa_tagstruct_puts(t, name);
-    pa_tagstruct_putu32(t, type);
-    pa_tagstruct_puts(t, module);
-    pa_tagstruct_puts(t, argument);
-    pa_pstream_send_tagstruct(c->pstream, t);
-    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_index_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
-
-    return o;
+    PA_FAIL_RETURN_NULL(c, PA_ERR_OBSOLETE);
 }
 
-PA_WARN_REFERENCE(pa_context_remove_autoload_by_name, "Autoload will no longer be implemented by future versions of the PulseAudio server.");
+PA_WARN_REFERENCE(pa_context_remove_autoload_by_name, "Module auto-loading no longer supported.");
 
 pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_context_success_cb_t cb, void* userdata) {
-    pa_operation *o;
-    pa_tagstruct *t;
-    uint32_t tag;
-
     pa_assert(c);
     pa_assert(PA_REFCNT_VALUE(c) >= 1);
 
-    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
-    PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
-    PA_CHECK_VALIDITY_RETURN_NULL(c, type == PA_AUTOLOAD_SINK || type == PA_AUTOLOAD_SOURCE, PA_ERR_INVALID);
-
-    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
-
-    t = pa_tagstruct_command(c, PA_COMMAND_REMOVE_AUTOLOAD, &tag);
-    pa_tagstruct_puts(t, name);
-    pa_tagstruct_putu32(t, type);
-    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_FAIL_RETURN_NULL(c, PA_ERR_OBSOLETE);
 }
 
-PA_WARN_REFERENCE(pa_context_remove_autoload_by_index, "Autoload will no longer be implemented by future versions of the PulseAudio server.");
+PA_WARN_REFERENCE(pa_context_remove_autoload_by_index, "Module auto-loading no longer supported.");
 
 pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void* userdata) {
-    pa_operation *o;
-    pa_tagstruct *t;
-    uint32_t tag;
-
     pa_assert(c);
     pa_assert(PA_REFCNT_VALUE(c) >= 1);
 
-    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
-    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
-
-    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
-
-    t = pa_tagstruct_command(c, PA_COMMAND_REMOVE_AUTOLOAD, &tag);
-    pa_tagstruct_putu32(t, idx);
-    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_FAIL_RETURN_NULL(c, PA_ERR_OBSOLETE);
 }
 
 pa_operation* pa_context_move_sink_input_by_name(pa_context *c, uint32_t idx, const char *sink_name, pa_context_success_cb_t cb, void* userdata) {
diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h
index b409cad..ae9bd5b 100644
--- a/src/pulse/introspect.h
+++ b/src/pulse/introspect.h
@@ -128,18 +128,6 @@
  * pa_context_get_module_info() or pa_context_get_module_info_list(). The
  * information structure is called pa_module_info.
  *
- * \subsection autoload_subsec Autoload Entries
- *
- * Modules can be autoloaded as a result of a client requesting a
- * certain sink or source. Please note that autoloading is deprecated
- * in 0.9.11. and is likely to be removed from the API in a later
- * version. This mapping between sink/source names and modules can be
- * queried from the server:
- *
- * \li By index - pa_context_get_autoload_info_by_index()
- * \li By sink/source name - pa_context_get_autoload_info_by_name()
- * \li All - pa_context_get_autoload_info_list()
- *
  * \subsection client_subsec Clients
  *
  * PulseAudio clients are also identified by index and are retrieved using
@@ -189,14 +177,6 @@
  * Server modules can be remotely loaded and unloaded using
  * pa_context_load_module() and pa_context_unload_module().
  *
- * \subsection autoload_subsec Autoload Entries
- *
- * New module autoloading rules can be added, and existing can be removed
- * using pa_context_add_autoload() and pa_context_remove_autoload_by_index()
- * / pa_context_remove_autoload_by_name(). Please note that autoloading is deprecated
- * in 0.9.11. and is likely to be removed from the API in a later
- * version.
- *
  * \subsection client_subsec Clients
  *
  * The only operation supported on clients, is the possibility of kicking
@@ -350,7 +330,9 @@ typedef struct pa_module_info {
     const char*name,                    /**< Name of the module */
         *argument;                      /**< Argument string of the module */
     uint32_t n_used;                    /**< Usage counter or PA_INVALID_INDEX */
-    int auto_unload;                    /**< Non-zero if this is an autoloaded module */
+/** \cond fulldocs */
+    int auto_unload;                    /**< \deprecated Non-zero if this is an autoloaded module */
+/** \endcond */
 } pa_module_info;
 
 /** Callback prototype for pa_context_get_module_info() and firends*/
@@ -551,13 +533,13 @@ pa_operation* pa_context_get_sample_info_list(pa_context *c, pa_sample_info_cb_t
 
 /** @{ \name Autoload Entries */
 
-/** Type of an autoload entry. */
+/** \deprecated Type of an autoload entry. */
 typedef enum pa_autoload_type {
     PA_AUTOLOAD_SINK = 0,
     PA_AUTOLOAD_SOURCE = 1
 } pa_autoload_type_t;
 
-/** Stores information about autoload entries. Please note that this structure
+/** \deprecated Stores information about autoload entries. Please note that this structure
  * can be extended as part of evolutionary API updates at any time in
  * any new release. */
 typedef struct pa_autoload_info {
@@ -568,25 +550,25 @@ typedef struct pa_autoload_info {
     const char *argument;         /**< Argument string for module */
 } pa_autoload_info;
 
-/** Callback prototype for pa_context_get_autoload_info_by_name() and firends */
+/** \deprecated Callback prototype for pa_context_get_autoload_info_by_name() and firends */
 typedef void (*pa_autoload_info_cb_t)(pa_context *c, const pa_autoload_info *i, int eol, void *userdata);
 
-/** Get info about a specific autoload entry. */
+/** \deprecated Get info about a specific autoload entry. */
 pa_operation* pa_context_get_autoload_info_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_autoload_info_cb_t cb, void *userdata) PA_GCC_DEPRECATED;
 
-/** Get info about a specific autoload entry. */
+/** \deprecated Get info about a specific autoload entry. */
 pa_operation* pa_context_get_autoload_info_by_index(pa_context *c, uint32_t idx, pa_autoload_info_cb_t cb, void *userdata) PA_GCC_DEPRECATED;
 
-/** Get the complete list of autoload entries. */
+/** \deprecated Get the complete list of autoload entries. */
 pa_operation* pa_context_get_autoload_info_list(pa_context *c, pa_autoload_info_cb_t cb, void *userdata) PA_GCC_DEPRECATED;
 
-/** Add a new autoload entry. */
+/** \deprecated Add a new autoload entry. */
 pa_operation* pa_context_add_autoload(pa_context *c, const char *name, pa_autoload_type_t type, const char *module, const char*argument, pa_context_index_cb_t, void* userdata) PA_GCC_DEPRECATED;
 
-/** Remove an autoload entry. */
+/** \deprecated Remove an autoload entry. */
 pa_operation* pa_context_remove_autoload_by_name(pa_context *c, const char *name, pa_autoload_type_t type, pa_context_success_cb_t cb, void* userdata) PA_GCC_DEPRECATED;
 
-/** Remove an autoload entry. */
+/** \deprecated Remove an autoload entry. */
 pa_operation* pa_context_remove_autoload_by_index(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void* userdata) PA_GCC_DEPRECATED;
 
 /** @} */
diff --git a/src/pulsecore/autoload.c b/src/pulsecore/autoload.c
deleted file mode 100644
index 8c84cee..0000000
--- a/src/pulsecore/autoload.c
+++ /dev/null
@@ -1,202 +0,0 @@
-/***
-  This file is part of PulseAudio.
-
-  Copyright 2004-2006 Lennart Poettering
-  Copyright 2006 Pierre Ossman <ossman at cendio.se> for Cendio AB
-
-  PulseAudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-
-  PulseAudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with PulseAudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-
-#include <pulse/xmalloc.h>
-
-#include <pulsecore/module.h>
-#include <pulsecore/memchunk.h>
-#include <pulsecore/sound-file.h>
-#include <pulsecore/log.h>
-#include <pulsecore/macro.h>
-#include <pulsecore/core-scache.h>
-#include <pulsecore/core-subscribe.h>
-
-#include "autoload.h"
-
-static void entry_free(pa_autoload_entry *e) {
-    pa_assert(e);
-    pa_subscription_post(e->core, PA_SUBSCRIPTION_EVENT_AUTOLOAD|PA_SUBSCRIPTION_EVENT_REMOVE, PA_INVALID_INDEX);
-    pa_xfree(e->name);
-    pa_xfree(e->module);
-    pa_xfree(e->argument);
-    pa_xfree(e);
-}
-
-static void entry_remove_and_free(pa_autoload_entry *e) {
-    pa_assert(e);
-    pa_assert(e->core);
-
-    pa_idxset_remove_by_data(e->core->autoload_idxset, e, NULL);
-    pa_hashmap_remove(e->core->autoload_hashmap, e->name);
-    entry_free(e);
-}
-
-static pa_autoload_entry* entry_new(pa_core *c, const char *name) {
-    pa_autoload_entry *e = NULL;
-
-    pa_core_assert_ref(c);
-    pa_assert(name);
-
-    if (c->autoload_hashmap && (e = pa_hashmap_get(c->autoload_hashmap, name)))
-        return NULL;
-
-    e = pa_xnew(pa_autoload_entry, 1);
-    e->core = c;
-    e->name = pa_xstrdup(name);
-    e->module = e->argument = NULL;
-    e->in_action = 0;
-
-    if (!c->autoload_hashmap)
-        c->autoload_hashmap = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
-    pa_assert(c->autoload_hashmap);
-
-    pa_hashmap_put(c->autoload_hashmap, e->name, e);
-
-    if (!c->autoload_idxset)
-        c->autoload_idxset = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
-    pa_idxset_put(c->autoload_idxset, e, &e->index);
-
-    pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_AUTOLOAD|PA_SUBSCRIPTION_EVENT_NEW, e->index);
-
-    return e;
-}
-
-int pa_autoload_add(pa_core *c, const char*name, pa_namereg_type_t type, const char*module, const char *argument, uint32_t *idx) {
-    pa_autoload_entry *e = NULL;
-
-    pa_assert(c);
-    pa_assert(name);
-    pa_assert(module);
-    pa_assert(type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE);
-
-    if (!(e = entry_new(c, name)))
-        return -1;
-
-    e->module = pa_xstrdup(module);
-    e->argument = pa_xstrdup(argument);
-    e->type = type;
-
-    if (idx)
-        *idx = e->index;
-
-    return 0;
-}
-
-int pa_autoload_remove_by_name(pa_core *c, const char*name, pa_namereg_type_t type) {
-    pa_autoload_entry *e;
-
-    pa_assert(c);
-    pa_assert(name);
-    pa_assert(type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE);
-
-    if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || e->type != type)
-        return -1;
-
-    entry_remove_and_free(e);
-    return 0;
-}
-
-int pa_autoload_remove_by_index(pa_core *c, uint32_t idx) {
-    pa_autoload_entry *e;
-
-    pa_assert(c);
-    pa_assert(idx != PA_IDXSET_INVALID);
-
-    if (!c->autoload_idxset || !(e = pa_idxset_get_by_index(c->autoload_idxset, idx)))
-        return -1;
-
-    entry_remove_and_free(e);
-    return 0;
-}
-
-void pa_autoload_request(pa_core *c, const char *name, pa_namereg_type_t type) {
-    pa_autoload_entry *e;
-    pa_module *m;
-
-    pa_assert(c);
-    pa_assert(name);
-
-    if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || (e->type != type))
-        return;
-
-    if (e->in_action)
-        return;
-
-    e->in_action = 1;
-
-    if (type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE) {
-        if ((m = pa_module_load(c, e->module, e->argument)))
-            m->auto_unload = 1;
-    }
-
-    e->in_action = 0;
-}
-
-static void free_func(void *p, void *userdata) {
-    pa_autoload_entry *e = p;
-    pa_idxset_remove_by_data(e->core->autoload_idxset, e, NULL);
-    entry_free(e);
-}
-
-void pa_autoload_free(pa_core *c) {
-
-    if (c->autoload_hashmap) {
-        pa_hashmap_free(c->autoload_hashmap, free_func, NULL);
-        c->autoload_hashmap = NULL;
-    }
-
-    if (c->autoload_idxset) {
-        pa_idxset_free(c->autoload_idxset, NULL, NULL);
-        c->autoload_idxset = NULL;
-    }
-}
-
-const pa_autoload_entry* pa_autoload_get_by_name(pa_core *c, const char*name, pa_namereg_type_t type) {
-    pa_autoload_entry *e;
-
-    pa_core_assert_ref(c);
-    pa_assert(name);
-
-    if (!c->autoload_hashmap || !(e = pa_hashmap_get(c->autoload_hashmap, name)) || e->type != type)
-        return NULL;
-
-    return e;
-}
-
-const pa_autoload_entry* pa_autoload_get_by_index(pa_core *c, uint32_t idx) {
-    pa_autoload_entry *e;
-
-    pa_core_assert_ref(c);
-    pa_assert(idx != PA_IDXSET_INVALID);
-
-    if (!c->autoload_idxset || !(e = pa_idxset_get_by_index(c->autoload_idxset, idx)))
-        return NULL;
-
-    return e;
-}
diff --git a/src/pulsecore/autoload.h b/src/pulsecore/autoload.h
deleted file mode 100644
index 3926351..0000000
--- a/src/pulsecore/autoload.h
+++ /dev/null
@@ -1,58 +0,0 @@
-#ifndef fooautoloadhfoo
-#define fooautoloadhfoo
-
-/***
-  This file is part of PulseAudio.
-
-  Copyright 2004-2006 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 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 <pulsecore/namereg.h>
-
-/* Using the autoloading facility, modules by be loaded on-demand and
- * synchronously. The user may register a "ghost sink" or "ghost
- * source". Whenever this sink/source is requested but not available a
- * specified module is loaded. */
-
-/* An autoload entry, or "ghost" sink/source */
-typedef struct pa_autoload_entry {
-    pa_core *core;
-    uint32_t index;
-    char *name;
-    pa_namereg_type_t type; /* Type of the autoload entry */
-    int in_action; /* The module is currently being loaded */
-    char *module, *argument;
-} pa_autoload_entry;
-
-/* Add a new autoload entry of the given time, with the speicified
- * sink/source name, module name and argument. Return the entry's
- * index in *index */
-int pa_autoload_add(pa_core *c, const char*name, pa_namereg_type_t type, const char*module, const char *argument, uint32_t *idx);
-
-/* Free all autoload entries */
-void pa_autoload_free(pa_core *c);
-int pa_autoload_remove_by_name(pa_core *c, const char*name, pa_namereg_type_t type);
-int pa_autoload_remove_by_index(pa_core *c, uint32_t idx);
-
-/* Request an autoload entry by its name, effectively causing a module to be loaded */
-void pa_autoload_request(pa_core *c, const char *name, pa_namereg_type_t type);
-
-const pa_autoload_entry* pa_autoload_get_by_name(pa_core *c, const char*name, pa_namereg_type_t type);
-const pa_autoload_entry* pa_autoload_get_by_index(pa_core *c, uint32_t idx);
-
-#endif
diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c
index 93d6bbe..8f5d9bd 100644
--- a/src/pulsecore/cli-command.c
+++ b/src/pulsecore/cli-command.c
@@ -47,7 +47,6 @@
 #include <pulsecore/sample-util.h>
 #include <pulsecore/sound-file.h>
 #include <pulsecore/play-memchunk.h>
-#include <pulsecore/autoload.h>
 #include <pulsecore/sound-file-stream.h>
 #include <pulsecore/shared.h>
 #include <pulsecore/core-util.h>
@@ -107,9 +106,6 @@ static int pa_cli_command_scache_list(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
 static int pa_cli_command_scache_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
 static int pa_cli_command_scache_load_dir(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
 static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
-static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
-static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
-static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
 static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
 static int pa_cli_command_list_shared_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
 static int pa_cli_command_move_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
@@ -168,11 +164,6 @@ static const struct command commands[] = {
     { "load-sample-lazy",        pa_cli_command_scache_load,        "Lazily load a sound file into the sample cache (args: name, filename)", 3},
     { "load-sample-dir-lazy",    pa_cli_command_scache_load_dir,    "Lazily load all files in a directory into the sample cache (args: pathname)", 2},
     { "play-file",               pa_cli_command_play_file,          "Play a sound file (args: filename, sink|index)", 3},
-    { "list-autoload",           pa_cli_command_autoload_list,      "List autoload entries", 1},
-    { "add-autoload-sink",       pa_cli_command_autoload_add,       NULL /*"Add autoload entry for a sink (args: sink, module name, arguments)"*/, 4},
-    { "add-autoload-source",     pa_cli_command_autoload_add,       NULL /*"Add autoload entry for a source (args: source, module name, arguments)"*/, 4},
-    { "remove-autoload-sink",    pa_cli_command_autoload_remove,    NULL /*"Remove autoload entry for a sink (args: name)"*/, 2},
-    { "remove-autoload-source",  pa_cli_command_autoload_remove,    NULL /*"Remove autoload entry for a source (args: name)"*/, 2},
     { "dump",                    pa_cli_command_dump,               "Dump daemon configuration", 1},
     { "shared",                  pa_cli_command_list_shared_props,  NULL, 1},
     { "move-sink-input",         pa_cli_command_move_sink_input,    "Move sink input to another sink (args: index, sink)", 3},
@@ -402,7 +393,6 @@ static int pa_cli_command_info(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
     pa_cli_command_sink_inputs(c, t, buf, fail);
     pa_cli_command_source_outputs(c, t, buf, fail);
     pa_cli_command_scache_list(c, t, buf, fail);
-/*     pa_cli_command_autoload_list(c, t, buf, fail); */
     return 0;
 }
 
@@ -519,7 +509,7 @@ static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
         return -1;
     }
 
-    if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK, 1))) {
+    if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) {
         pa_strbuf_puts(buf, "No sink found by this name or index.\n");
         return -1;
     }
@@ -597,7 +587,7 @@ static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *
         return -1;
     }
 
-    if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE, 1))) {
+    if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE))) {
         pa_strbuf_puts(buf, "No source found by this name or index.\n");
         return -1;
     }
@@ -632,7 +622,7 @@ static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,
         return -1;
     }
 
-    if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK, 1))) {
+    if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) {
         pa_strbuf_puts(buf, "No sink found by this name or index.\n");
         return -1;
     }
@@ -666,7 +656,7 @@ static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
         return -1;
     }
 
-    if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE, 1))) {
+    if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE))) {
         pa_strbuf_puts(buf, "No sink found by this name or index.\n");
         return -1;
     }
@@ -695,7 +685,7 @@ static int pa_cli_command_update_sink_proplist(pa_core *c, pa_tokenizer *t, pa_s
         return -1;
     }
 
-    if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK, 1))) {
+    if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) {
         pa_strbuf_puts(buf, "No sink found by this name or index.\n");
         return -1;
     }
@@ -729,7 +719,7 @@ static int pa_cli_command_update_source_proplist(pa_core *c, pa_tokenizer *t, pa
         return -1;
     }
 
-    if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE, 1))) {
+    if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE))) {
         pa_strbuf_puts(buf, "No source found by this name or index.\n");
         return -1;
     }
@@ -1014,7 +1004,7 @@ static int pa_cli_command_scache_play(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
         return -1;
     }
 
-    if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, 1))) {
+    if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK))) {
         pa_strbuf_puts(buf, "No sink by that name.\n");
         return -1;
     }
@@ -1110,7 +1100,7 @@ static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,
         return -1;
     }
 
-    if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, 1))) {
+    if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK))) {
         pa_strbuf_puts(buf, "No sink by that name.\n");
         return -1;
     }
@@ -1119,66 +1109,6 @@ static int pa_cli_command_play_file(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,
     return pa_play_file(sink, fname, NULL);
 }
 
-static int pa_cli_command_autoload_add(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
-    const char *a, *b;
-
-    pa_core_assert_ref(c);
-    pa_assert(t);
-    pa_assert(buf);
-    pa_assert(fail);
-
-    pa_log_warn("Autoload will no longer be implemented by future versions of the PulseAudio server.");
-
-    if (!(a = pa_tokenizer_get(t, 1)) || !(b = pa_tokenizer_get(t, 2))) {
-        pa_strbuf_puts(buf, "You need to specify a device name, a filename or a module name and optionally module arguments\n");
-        return -1;
-    }
-
-    pa_autoload_add(c, a, strstr(pa_tokenizer_get(t, 0), "sink") ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE, b, pa_tokenizer_get(t, 3), NULL);
-
-    return 0;
-}
-
-static int pa_cli_command_autoload_remove(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
-    const char *name;
-
-    pa_core_assert_ref(c);
-    pa_assert(t);
-    pa_assert(buf);
-    pa_assert(fail);
-
-    pa_log_warn("Autoload will no longer be implemented by future versions of the PulseAudio server.");
-
-    if (!(name = pa_tokenizer_get(t, 1))) {
-        pa_strbuf_puts(buf, "You need to specify a device name\n");
-        return -1;
-    }
-
-    if (pa_autoload_remove_by_name(c, name, strstr(pa_tokenizer_get(t, 0), "sink") ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE) < 0) {
-        pa_strbuf_puts(buf, "Failed to remove autload entry\n");
-        return -1;
-    }
-
-    return 0;
-}
-
-static int pa_cli_command_autoload_list(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
-    char *s;
-
-    pa_core_assert_ref(c);
-    pa_assert(t);
-    pa_assert(buf);
-    pa_assert(fail);
-
-    pa_log_warn("Autoload will no longer be implemented by future versions of the PulseAudio server.");
-
-    pa_assert_se(s = pa_autoload_list_to_string(c));
-    pa_strbuf_puts(buf, s);
-    pa_xfree(s);
-
-    return 0;
-}
-
 static int pa_cli_command_list_shared_props(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
     pa_core_assert_ref(c);
     pa_assert(t);
@@ -1231,7 +1161,7 @@ static int pa_cli_command_move_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf
         return -1;
     }
 
-    if (!(sink = pa_namereg_get(c, k, PA_NAMEREG_SINK, 1))) {
+    if (!(sink = pa_namereg_get(c, k, PA_NAMEREG_SINK))) {
         pa_strbuf_puts(buf, "No sink found by this name or index.\n");
         return -1;
     }
@@ -1274,7 +1204,7 @@ static int pa_cli_command_move_source_output(pa_core *c, pa_tokenizer *t, pa_str
         return -1;
     }
 
-    if (!(source = pa_namereg_get(c, k, PA_NAMEREG_SOURCE, 1))) {
+    if (!(source = pa_namereg_get(c, k, PA_NAMEREG_SOURCE))) {
         pa_strbuf_puts(buf, "No source found by this name or index.\n");
         return -1;
     }
@@ -1311,7 +1241,7 @@ static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *b
         return -1;
     }
 
-    if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK, 1))) {
+    if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) {
         pa_strbuf_puts(buf, "No sink found by this name or index.\n");
         return -1;
     }
@@ -1345,7 +1275,7 @@ static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf
         return -1;
     }
 
-    if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE, 1))) {
+    if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE))) {
         pa_strbuf_puts(buf, "No source found by this name or index.\n");
         return -1;
     }
@@ -1489,8 +1419,6 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
     uint32_t idx;
     char txt[256];
     time_t now;
-    void *i;
-    pa_autoload_entry *a;
 
     pa_core_assert_ref(c);
     pa_assert(t);
@@ -1506,8 +1434,6 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
 #endif
 
     for (m = pa_idxset_first(c->modules, &idx); m; m = pa_idxset_next(c->modules, &idx)) {
-        if (m->auto_unload)
-            continue;
 
         pa_strbuf_printf(buf, "load-module %s", m->name);
 
@@ -1520,8 +1446,6 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
     nl = 0;
 
     for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) {
-        if (sink->module && sink->module->auto_unload)
-            continue;
 
         if (!nl) {
             pa_strbuf_puts(buf, "\n");
@@ -1534,8 +1458,6 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
     }
 
     for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) {
-        if (source->module && source->module->auto_unload)
-            continue;
 
         if (!nl) {
             pa_strbuf_puts(buf, "\n");
@@ -1548,26 +1470,6 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
     }
 
 
-    if (c->autoload_hashmap) {
-        nl = 0;
-
-        i = NULL;
-        while ((a = pa_hashmap_iterate(c->autoload_hashmap, &i, NULL))) {
-
-            if (!nl) {
-                pa_strbuf_puts(buf, "\n");
-                nl = 1;
-            }
-
-            pa_strbuf_printf(buf, "add-autoload-%s %s %s", a->type == PA_NAMEREG_SINK ? "sink" : "source", a->name, a->module);
-
-            if (a->argument)
-                pa_strbuf_printf(buf, " %s", a->argument);
-
-            pa_strbuf_puts(buf, "\n");
-        }
-    }
-
     nl = 0;
 
     if ((p = pa_namereg_get_default_sink_name(c))) {
diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index 5d78ce6..27db56e 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -38,7 +38,6 @@
 #include <pulsecore/strbuf.h>
 #include <pulsecore/sample-util.h>
 #include <pulsecore/core-scache.h>
-#include <pulsecore/autoload.h>
 #include <pulsecore/macro.h>
 #include <pulsecore/core-util.h>
 
@@ -59,9 +58,12 @@ char *pa_module_list_to_string(pa_core *c) {
                          "\tname: <%s>\n"
                          "\targument: <%s>\n"
                          "\tused: %i\n"
-                         "\tauto unload: %s\n",
-                         m->index, m->name, m->argument ? m->argument : "", m->n_used,
-                         pa_yes_no(m->auto_unload));
+                         "\tload once: %s\n",
+                         m->index,
+                         m->name,
+                         pa_strempty(m->argument),
+                         m->n_used,
+                         pa_yes_no(m->load_once));
     }
 
     return pa_strbuf_tostring_free(s);
@@ -506,45 +508,13 @@ char *pa_scache_list_to_string(pa_core *c) {
     return pa_strbuf_tostring_free(s);
 }
 
-char *pa_autoload_list_to_string(pa_core *c) {
-    pa_strbuf *s;
-    pa_assert(c);
-
-    s = pa_strbuf_new();
-
-    pa_strbuf_printf(s, "%u autoload entries available.\n", c->autoload_hashmap ? pa_hashmap_size(c->autoload_hashmap) : 0);
-
-    if (c->autoload_hashmap) {
-        pa_autoload_entry *e;
-        void *state = NULL;
-
-        while ((e = pa_hashmap_iterate(c->autoload_hashmap, &state, NULL))) {
-            pa_strbuf_printf(
-                s,
-                "    name: <%s>\n"
-                "\ttype: %s\n"
-                "\tindex: %u\n"
-                "\tmodule_name: <%s>\n"
-                "\targuments: <%s>\n",
-                e->name,
-                e->type == PA_NAMEREG_SOURCE ? "source" : "sink",
-                e->index,
-                e->module,
-                e->argument ? e->argument : "");
-
-        }
-    }
-
-    return pa_strbuf_tostring_free(s);
-}
-
 char *pa_full_status_string(pa_core *c) {
     pa_strbuf *s;
     int i;
 
     s = pa_strbuf_new();
 
-    for (i = 0; i < 9; i++) {
+    for (i = 0; i < 8; i++) {
         char *t = NULL;
 
         switch (i) {
@@ -572,9 +542,6 @@ char *pa_full_status_string(pa_core *c) {
             case 7:
                 t = pa_scache_list_to_string(c);
                 break;
-            case 8:
-                t = pa_autoload_list_to_string(c);
-                break;
         }
 
         pa_strbuf_puts(s, t);
diff --git a/src/pulsecore/cli-text.h b/src/pulsecore/cli-text.h
index 167565e..aad5164 100644
--- a/src/pulsecore/cli-text.h
+++ b/src/pulsecore/cli-text.h
@@ -35,7 +35,6 @@ char *pa_card_list_to_string(pa_core *c);
 char *pa_client_list_to_string(pa_core *c);
 char *pa_module_list_to_string(pa_core *c);
 char *pa_scache_list_to_string(pa_core *c);
-char *pa_autoload_list_to_string(pa_core *c);
 
 char *pa_full_status_string(pa_core *c);
 
diff --git a/src/pulsecore/core-scache.c b/src/pulsecore/core-scache.c
index 1d080e1..0f34c9d 100644
--- a/src/pulsecore/core-scache.c
+++ b/src/pulsecore/core-scache.c
@@ -98,7 +98,7 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {
     pa_assert(c);
     pa_assert(name);
 
-    if ((e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, FALSE))) {
+    if ((e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE))) {
         if (e->memchunk.memblock)
             pa_memblock_unref(e->memchunk.memblock);
 
@@ -273,7 +273,7 @@ int pa_scache_remove_item(pa_core *c, const char *name) {
     pa_assert(c);
     pa_assert(name);
 
-    if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, 0)))
+    if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE)))
         return -1;
 
     pa_assert_se(pa_idxset_remove_by_data(c->scache, e, NULL) == e);
@@ -313,7 +313,7 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t
     pa_assert(name);
     pa_assert(sink);
 
-    if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, FALSE)))
+    if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE)))
         return -1;
 
     if (e->lazy && !e->memchunk.memblock) {
@@ -360,13 +360,13 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t
     return 0;
 }
 
-int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_bool_t autoload, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx) {
+int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx) {
     pa_sink *sink;
 
     pa_assert(c);
     pa_assert(name);
 
-    if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK, autoload)))
+    if (!(sink = pa_namereg_get(c, sink_name, PA_NAMEREG_SINK)))
         return -1;
 
     return pa_scache_play_item(c, name, sink, volume, p, sink_input_idx);
@@ -390,7 +390,7 @@ uint32_t pa_scache_get_id_by_name(pa_core *c, const char *name) {
     pa_assert(c);
     pa_assert(name);
 
-    if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE, FALSE)))
+    if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE)))
         return PA_IDXSET_INVALID;
 
     return e->index;
diff --git a/src/pulsecore/core-scache.h b/src/pulsecore/core-scache.h
index 80e0fd0..ba65a96 100644
--- a/src/pulsecore/core-scache.h
+++ b/src/pulsecore/core-scache.h
@@ -56,7 +56,7 @@ int pa_scache_add_directory_lazy(pa_core *c, const char *pathname);
 
 int pa_scache_remove_item(pa_core *c, const char *name);
 int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx);
-int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_bool_t autoload, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx);
+int pa_scache_play_item_by_name(pa_core *c, const char *name, const char*sink_name, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx);
 void pa_scache_free(pa_core *c);
 
 const char *pa_scache_get_name_by_id(pa_core *c, uint32_t id);
diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c
index 2076432..0b78bc4 100644
--- a/src/pulsecore/core.c
+++ b/src/pulsecore/core.c
@@ -37,7 +37,6 @@
 #include <pulsecore/namereg.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/core-scache.h>
-#include <pulsecore/autoload.h>
 #include <pulsecore/core-subscribe.h>
 #include <pulsecore/shared.h>
 #include <pulsecore/random.h>
@@ -104,8 +103,6 @@ pa_core* pa_core_new(pa_mainloop_api *m, pa_bool_t shared, size_t shm_size) {
     c->modules = NULL;
     c->namereg = NULL;
     c->scache = NULL;
-    c->autoload_idxset = NULL;
-    c->autoload_hashmap = NULL;
     c->running_as_daemon = FALSE;
 
     c->default_sample_spec.format = PA_SAMPLE_S16NE;
@@ -114,7 +111,6 @@ pa_core* pa_core_new(pa_mainloop_api *m, pa_bool_t shared, size_t shm_size) {
     c->default_n_fragments = 4;
     c->default_fragment_size_msec = 25;
 
-    c->module_auto_unload_event = NULL;
     c->module_defer_unload_event = NULL;
     c->scache_auto_unload_event = NULL;
 
@@ -129,7 +125,6 @@ pa_core* pa_core_new(pa_mainloop_api *m, pa_bool_t shared, size_t shm_size) {
     c->exit_event = NULL;
 
     c->exit_idle_time = -1;
-    c->module_idle_time = 20;
     c->scache_idle_time = 20;
 
     c->resample_method = PA_RESAMPLER_SPEEX_FLOAT_BASE + 3;
@@ -185,7 +180,6 @@ static void core_free(pa_object *o) {
 
     pa_scache_free(c);
     pa_namereg_free(c);
-    pa_autoload_free(c);
     pa_subscription_free_all(c);
 
     if (c->exit_event)
diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index 87ea4ab..9f463d6 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -99,10 +99,10 @@ struct pa_core {
     pa_mainloop_api *mainloop;
 
     /* idxset of all kinds of entities */
-    pa_idxset *clients, *cards, *sinks, *sources, *sink_inputs, *source_outputs, *modules, *scache, *autoload_idxset;
+    pa_idxset *clients, *cards, *sinks, *sources, *sink_inputs, *source_outputs, *modules, *scache;
 
     /* Some hashmaps for all sorts of entities */
-    pa_hashmap *namereg, *autoload_hashmap, *shared;
+    pa_hashmap *namereg, *shared;
 
     /* The name of the default sink/source */
     char *default_source_name, *default_sink_name;
@@ -110,7 +110,6 @@ struct pa_core {
     pa_sample_spec default_sample_spec;
     unsigned default_n_fragments, default_fragment_size_msec;
 
-    pa_time_event *module_auto_unload_event;
     pa_defer_event *module_defer_unload_event;
 
     pa_defer_event *subscription_defer_event;
@@ -121,7 +120,7 @@ struct pa_core {
     pa_mempool *mempool;
     pa_silence_cache silence_cache;
 
-    int exit_idle_time, module_idle_time, scache_idle_time;
+    int exit_idle_time, scache_idle_time;
 
     pa_time_event *exit_event;
 
diff --git a/src/pulsecore/module.c b/src/pulsecore/module.c
index 56ed2c5..b197ba0 100644
--- a/src/pulsecore/module.c
+++ b/src/pulsecore/module.c
@@ -48,21 +48,6 @@
 
 #define UNLOAD_POLL_TIME 2
 
-static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, const struct timeval *tv, void *userdata) {
-    pa_core *c = PA_CORE(userdata);
-    struct timeval ntv;
-
-    pa_core_assert_ref(c);
-    pa_assert(c->mainloop == m);
-    pa_assert(c->module_auto_unload_event == e);
-
-    pa_module_unload_unused(c);
-
-    pa_gettimeofday(&ntv);
-    pa_timeval_add(&ntv, UNLOAD_POLL_TIME*PA_USEC_PER_SEC);
-    m->time_restart(e, &ntv);
-}
-
 pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
     pa_module *m = NULL;
     pa_bool_t (*load_once)(void);
@@ -110,7 +95,6 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
     m->userdata = NULL;
     m->core = c;
     m->n_used = -1;
-    m->auto_unload = FALSE;
     m->unload_requested = FALSE;
 
     if (m->init(m) < 0) {
@@ -121,13 +105,6 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
     if (!c->modules)
         c->modules = pa_idxset_new(NULL, NULL);
 
-    if (m->auto_unload && !c->module_auto_unload_event) {
-        struct timeval ntv;
-        pa_gettimeofday(&ntv);
-        pa_timeval_add(&ntv, UNLOAD_POLL_TIME*PA_USEC_PER_SEC);
-        c->module_auto_unload_event = c->mainloop->time_new(c->mainloop, &ntv, timeout_callback, c);
-    }
-
     pa_assert_se(pa_idxset_put(c->modules, m, &m->index) >= 0);
     pa_assert(m->index != PA_IDXSET_INVALID);
 
@@ -212,44 +189,12 @@ void pa_module_unload_all(pa_core *c) {
         c->modules = NULL;
     }
 
-    if (c->module_auto_unload_event) {
-        c->mainloop->time_free(c->module_auto_unload_event);
-        c->module_auto_unload_event = NULL;
-    }
-
     if (c->module_defer_unload_event) {
         c->mainloop->defer_free(c->module_defer_unload_event);
         c->module_defer_unload_event = NULL;
     }
 }
 
-void pa_module_unload_unused(pa_core *c) {
-    void *state = NULL;
-    time_t now;
-    pa_module *m;
-
-    pa_assert(c);
-
-    if (!c->modules)
-        return;
-
-    time(&now);
-
-    while ((m = pa_idxset_iterate(c->modules, &state, NULL))) {
-
-        if (m->n_used > 0)
-            continue;
-
-        if (!m->auto_unload)
-            continue;
-
-        if (m->last_used_time + m->core->module_idle_time > now)
-            continue;
-
-        pa_module_unload(c, m, FALSE);
-    }
-}
-
 static void defer_cb(pa_mainloop_api*api, pa_defer_event *e, void *userdata) {
     void *state = NULL;
     pa_core *c = PA_CORE(userdata);
@@ -296,9 +241,6 @@ void pa_module_set_used(pa_module*m, int used) {
     if (m->n_used != used)
         pa_subscription_post(m->core, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_CHANGE, m->index);
 
-    if (used == 0 && m->n_used > 0)
-        time(&m->last_used_time);
-
     m->n_used = used;
 }
 
diff --git a/src/pulsecore/module.h b/src/pulsecore/module.h
index 661b2dd..c54169b 100644
--- a/src/pulsecore/module.h
+++ b/src/pulsecore/module.h
@@ -44,11 +44,8 @@ struct pa_module {
 
     int n_used;
 
-    pa_bool_t auto_unload:1;
     pa_bool_t load_once:1;
     pa_bool_t unload_requested:1;
-
-    time_t last_used_time;
 };
 
 pa_module* pa_module_load(pa_core *c, const char *name, const char*argument);
@@ -60,7 +57,6 @@ void pa_module_unload_request(pa_module *m, pa_bool_t force);
 void pa_module_unload_request_by_index(pa_core *c, uint32_t idx, pa_bool_t force);
 
 void pa_module_unload_all(pa_core *c);
-void pa_module_unload_unused(pa_core *c);
 
 void pa_module_set_used(pa_module*m, int used);
 
diff --git a/src/pulsecore/namereg.c b/src/pulsecore/namereg.c
index c1a434a..3b9ce88 100644
--- a/src/pulsecore/namereg.c
+++ b/src/pulsecore/namereg.c
@@ -178,7 +178,7 @@ void pa_namereg_unregister(pa_core *c, const char *name) {
     pa_xfree(e);
 }
 
-void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, pa_bool_t autoload) {
+void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type) {
     struct namereg_entry *e;
     uint32_t idx;
     pa_assert(c);
@@ -202,7 +202,7 @@ void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, pa_bo
         if (type == PA_NAMEREG_SOURCE) {
             pa_sink *k;
 
-            if ((k = pa_namereg_get(c, NULL, PA_NAMEREG_SINK, autoload)))
+            if ((k = pa_namereg_get(c, NULL, PA_NAMEREG_SINK)))
                 return k->monitor_source;
         }
     } else if (*name == '@')
@@ -215,18 +215,8 @@ void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, pa_bo
         if (e->type == type)
             return e->data;
 
-    if (pa_atou(name, &idx) < 0) {
-
-        if (autoload) {
-            pa_autoload_request(c, name, type);
-
-            if (c->namereg && (e = pa_hashmap_get(c->namereg, name)))
-                if (e->type == type)
-                    return e->data;
-        }
-
+    if (pa_atou(name, &idx) < 0)
         return NULL;
-    }
 
     if (type == PA_NAMEREG_SINK)
         return pa_idxset_get_by_index(c->sinks, idx);
diff --git a/src/pulsecore/namereg.h b/src/pulsecore/namereg.h
index 8ce548a..4205f2f 100644
--- a/src/pulsecore/namereg.h
+++ b/src/pulsecore/namereg.h
@@ -38,7 +38,7 @@ void pa_namereg_free(pa_core *c);
 
 const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t type, void *data, pa_bool_t fail);
 void pa_namereg_unregister(pa_core *c, const char *name);
-void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type, pa_bool_t autoload);
+void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type);
 int pa_namereg_set_default(pa_core*c, const char *name, pa_namereg_type_t type);
 
 const char *pa_namereg_get_default_sink_name(pa_core *c);
diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h
index 8138b7a..11643a7 100644
--- a/src/pulsecore/native-common.h
+++ b/src/pulsecore/native-common.h
@@ -94,10 +94,11 @@ enum {
     PA_COMMAND_LOAD_MODULE,
     PA_COMMAND_UNLOAD_MODULE,
 
-    PA_COMMAND_ADD_AUTOLOAD,
-    PA_COMMAND_REMOVE_AUTOLOAD,
-    PA_COMMAND_GET_AUTOLOAD_INFO,
-    PA_COMMAND_GET_AUTOLOAD_INFO_LIST,
+    /* Obsolete */
+    PA_COMMAND_ADD_AUTOLOAD___OBSOLETE,
+    PA_COMMAND_REMOVE_AUTOLOAD___OBSOLETE,
+    PA_COMMAND_GET_AUTOLOAD_INFO___OBSOLETE,
+    PA_COMMAND_GET_AUTOLOAD_INFO_LIST___OBSOLETE,
 
     PA_COMMAND_GET_RECORD_LATENCY,
     PA_COMMAND_CORK_RECORD_STREAM,
diff --git a/src/pulsecore/pdispatch.c b/src/pulsecore/pdispatch.c
index a5b33d6..305941a 100644
--- a/src/pulsecore/pdispatch.c
+++ b/src/pulsecore/pdispatch.c
@@ -110,10 +110,10 @@ static const char *command_names[PA_COMMAND_MAX] = {
     [PA_COMMAND_LOAD_MODULE] = "LOAD_MODULE",
     [PA_COMMAND_UNLOAD_MODULE] = "UNLOAD_MODULE",
 
-    [PA_COMMAND_ADD_AUTOLOAD] = "ADD_AUTOLOAD",
-    [PA_COMMAND_REMOVE_AUTOLOAD] = "REMOVE_AUTOLOAD",
-    [PA_COMMAND_GET_AUTOLOAD_INFO] = "GET_AUTOLOAD_INFO",
-    [PA_COMMAND_GET_AUTOLOAD_INFO_LIST] = "GET_AUTOLOAD_INFO_LIST",
+    [PA_COMMAND_ADD_AUTOLOAD___OBSOLETE] = "ADD_AUTOLOAD (obsolete)",
+    [PA_COMMAND_REMOVE_AUTOLOAD___OBSOLETE] = "REMOVE_AUTOLOAD (obsolete)",
+    [PA_COMMAND_GET_AUTOLOAD_INFO___OBSOLETE] = "GET_AUTOLOAD_INFO (obsolete)",
+    [PA_COMMAND_GET_AUTOLOAD_INFO_LIST___OBSOLETE] = "GET_AUTOLOAD_INFO_LIST (obsolete)",
 
     [PA_COMMAND_GET_RECORD_LATENCY] = "GET_RECORD_LATENCY",
     [PA_COMMAND_CORK_RECORD_STREAM] = "CORK_RECORD_STREAM",
diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c
index 6524b68..97d10df 100644
--- a/src/pulsecore/protocol-esound.c
+++ b/src/pulsecore/protocol-esound.c
@@ -403,7 +403,7 @@ static int esd_proto_stream_play(connection *c, esd_proto_t request, const void
     CHECK_VALIDITY(pa_sample_spec_valid(&ss), "Invalid sample specification");
 
     if (c->options->default_sink) {
-        sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK, 1);
+        sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK);
         CHECK_VALIDITY(sink, "No such sink: %s", c->options->default_sink);
     }
 
@@ -490,7 +490,7 @@ static int esd_proto_stream_record(connection *c, esd_proto_t request, const voi
     if (request == ESD_PROTO_STREAM_MON) {
         pa_sink* sink;
 
-        if (!(sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK, 1))) {
+        if (!(sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK))) {
             pa_log("no such sink.");
             return -1;
         }
@@ -503,7 +503,7 @@ static int esd_proto_stream_record(connection *c, esd_proto_t request, const voi
         pa_assert(request == ESD_PROTO_STREAM_REC);
 
         if (c->options->default_source) {
-            if (!(source = pa_namereg_get(c->protocol->core, c->options->default_source, PA_NAMEREG_SOURCE, 1))) {
+            if (!(source = pa_namereg_get(c->protocol->core, c->options->default_source, PA_NAMEREG_SOURCE))) {
                 pa_log("no such source.");
                 return -1;
             }
@@ -569,7 +569,7 @@ static int esd_proto_get_latency(connection *c, esd_proto_t request, const void
     pa_assert(!data);
     pa_assert(length == 0);
 
-    if (!(sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK, 1)))
+    if (!(sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK)))
         latency = 0;
     else {
         double usec = (double) pa_sink_get_latency(sink);
@@ -590,7 +590,7 @@ static int esd_proto_server_info(connection *c, esd_proto_t request, const void
     pa_assert(data);
     pa_assert(length == sizeof(int32_t));
 
-    if ((sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK, 1))) {
+    if ((sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK))) {
         rate = (int32_t) sink->sample_spec.rate;
         format = format_native2esd(&sink->sample_spec);
     }
@@ -865,7 +865,7 @@ static int esd_proto_sample_free_or_play(connection *c, esd_proto_t request, con
         if (request == ESD_PROTO_SAMPLE_PLAY) {
             pa_sink *sink;
 
-            if ((sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK, 1)))
+            if ((sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK)))
                 if (pa_scache_play_item(c->protocol->core, name, sink, PA_VOLUME_NORM, c->client->proplist, NULL) >= 0)
                     ok = (int32_t) idx + 1;
         } else {
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index d99e212..2179752 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -49,7 +49,6 @@
 #include <pulsecore/core-scache.h>
 #include <pulsecore/core-subscribe.h>
 #include <pulsecore/log.h>
-#include <pulsecore/autoload.h>
 #include <pulsecore/strlist.h>
 #include <pulsecore/shared.h>
 #include <pulsecore/sample-util.h>
@@ -248,10 +247,6 @@ static void command_set_stream_name(pa_pdispatch *pd, uint32_t command, uint32_t
 static void command_kill(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 static void command_load_module(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 static void command_unload_module(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
-static void command_add_autoload(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
-static void command_remove_autoload(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
-static void command_get_autoload_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
-static void command_get_autoload_info_list(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 static void command_cork_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 static void command_flush_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
@@ -330,10 +325,11 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
     [PA_COMMAND_KILL_SOURCE_OUTPUT] = command_kill,
     [PA_COMMAND_LOAD_MODULE] = command_load_module,
     [PA_COMMAND_UNLOAD_MODULE] = command_unload_module,
-    [PA_COMMAND_GET_AUTOLOAD_INFO] = command_get_autoload_info,
-    [PA_COMMAND_GET_AUTOLOAD_INFO_LIST] = command_get_autoload_info_list,
-    [PA_COMMAND_ADD_AUTOLOAD] = command_add_autoload,
-    [PA_COMMAND_REMOVE_AUTOLOAD] = command_remove_autoload,
+
+    [PA_COMMAND_GET_AUTOLOAD_INFO___OBSOLETE] = NULL,
+    [PA_COMMAND_GET_AUTOLOAD_INFO_LIST___OBSOLETE] = NULL,
+    [PA_COMMAND_ADD_AUTOLOAD___OBSOLETE] = NULL,
+    [PA_COMMAND_REMOVE_AUTOLOAD___OBSOLETE] = NULL,
 
     [PA_COMMAND_MOVE_SINK_INPUT] = command_move_stream,
     [PA_COMMAND_MOVE_SOURCE_OUTPUT] = command_move_stream,
@@ -1799,7 +1795,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
 
     } else if (sink_name) {
 
-        if (!(sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK, 1))) {
+        if (!(sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK))) {
             pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
             pa_proplist_free(p);
             return;
@@ -2040,7 +2036,7 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
 
     } else if (source_name) {
 
-        if (!(source = pa_namereg_get(c->protocol->core, source_name, PA_NAMEREG_SOURCE, 1))) {
+        if (!(source = pa_namereg_get(c->protocol->core, source_name, PA_NAMEREG_SOURCE))) {
             pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
             pa_proplist_free(p);
             return;
@@ -2315,12 +2311,12 @@ static void command_lookup(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_
 
     if (command == PA_COMMAND_LOOKUP_SINK) {
         pa_sink *sink;
-        if ((sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1)))
+        if ((sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK)))
             idx = sink->index;
     } else {
         pa_source *source;
         pa_assert(command == PA_COMMAND_LOOKUP_SOURCE);
-        if ((source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1)))
+        if ((source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE)))
             idx = source->index;
     }
 
@@ -2575,7 +2571,7 @@ static void command_play_sample(pa_pdispatch *pd, uint32_t command, uint32_t tag
     if (sink_index != PA_INVALID_INDEX)
         sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index);
     else
-        sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK, 1);
+        sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK);
 
     CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
 
@@ -2738,7 +2734,7 @@ static void module_fill_tagstruct(pa_tagstruct *t, pa_module *module) {
     pa_tagstruct_puts(t, module->name);
     pa_tagstruct_puts(t, module->argument);
     pa_tagstruct_putu32(t, (uint32_t) module->n_used);
-    pa_tagstruct_put_boolean(t, module->auto_unload);
+    pa_tagstruct_put_boolean(t, FALSE);
 }
 
 static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sink_input *s) {
@@ -2855,12 +2851,12 @@ static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, p
         if (idx != PA_INVALID_INDEX)
             sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
         else
-            sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1);
+            sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK);
     } else if (command == PA_COMMAND_GET_SOURCE_INFO) {
         if (idx != PA_INVALID_INDEX)
             source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
         else
-            source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1);
+            source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE);
     } else if (command == PA_COMMAND_GET_CLIENT_INFO)
         client = pa_idxset_get_by_index(c->protocol->core->clients, idx);
     else if (command == PA_COMMAND_GET_MODULE_INFO)
@@ -2874,7 +2870,7 @@ static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, p
         if (idx != PA_INVALID_INDEX)
             sce = pa_idxset_get_by_index(c->protocol->core->scache, idx);
         else
-            sce = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SAMPLE, 0);
+            sce = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SAMPLE);
     }
 
     if (!sink && !source && !client && !module && !si && !so && !sce) {
@@ -3078,14 +3074,14 @@ static void command_set_volume(
             if (idx != PA_INVALID_INDEX)
                 sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
             else
-                sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1);
+                sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK);
             break;
 
         case PA_COMMAND_SET_SOURCE_VOLUME:
             if (idx != PA_INVALID_INDEX)
                 source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
             else
-                source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1);
+                source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE);
             break;
 
         case PA_COMMAND_SET_SINK_INPUT_VOLUME:
@@ -3148,7 +3144,7 @@ static void command_set_mute(
             if (idx != PA_INVALID_INDEX)
                 sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
             else
-                sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1);
+                sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK);
 
             break;
 
@@ -3156,7 +3152,7 @@ static void command_set_mute(
             if (idx != PA_INVALID_INDEX)
                 source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
             else
-                source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1);
+                source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE);
 
             break;
 
@@ -3748,143 +3744,6 @@ static void command_unload_module(pa_pdispatch *pd, uint32_t command, uint32_t t
     pa_pstream_send_simple_ack(c->pstream, tag);
 }
 
-static void command_add_autoload(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
-    const char *name, *module, *argument;
-    uint32_t type;
-    uint32_t idx;
-    pa_tagstruct *reply;
-
-    pa_native_connection_assert_ref(c);
-    pa_assert(t);
-
-    if (pa_tagstruct_gets(t, &name) < 0 ||
-        pa_tagstruct_getu32(t, &type) < 0 ||
-        pa_tagstruct_gets(t, &module) < 0 ||
-        pa_tagstruct_gets(t, &argument) < 0 ||
-        !pa_tagstruct_eof(t)) {
-        protocol_error(c);
-        return;
-    }
-
-    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
-    CHECK_VALIDITY(c->pstream, name && *name && pa_utf8_valid(name), tag, PA_ERR_INVALID);
-    CHECK_VALIDITY(c->pstream, type == 0 || type == 1, tag, PA_ERR_INVALID);
-    CHECK_VALIDITY(c->pstream, module && *module && pa_utf8_valid(module), tag, PA_ERR_INVALID);
-    CHECK_VALIDITY(c->pstream, !argument || pa_utf8_valid(argument), tag, PA_ERR_INVALID);
-
-    if (pa_autoload_add(c->protocol->core, name, type == 0 ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE, module, argument, &idx) < 0) {
-        pa_pstream_send_error(c->pstream, tag, PA_ERR_EXIST);
-        return;
-    }
-
-    reply = reply_new(tag);
-    pa_tagstruct_putu32(reply, idx);
-    pa_pstream_send_tagstruct(c->pstream, reply);
-}
-
-static void command_remove_autoload(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
-    const char *name = NULL;
-    uint32_t type, idx = PA_IDXSET_INVALID;
-    int r;
-
-    pa_native_connection_assert_ref(c);
-    pa_assert(t);
-
-    if ((pa_tagstruct_getu32(t, &idx) < 0 &&
-        (pa_tagstruct_gets(t, &name) < 0 ||
-         pa_tagstruct_getu32(t, &type) < 0)) ||
-        !pa_tagstruct_eof(t)) {
-        protocol_error(c);
-        return;
-    }
-
-    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
-    CHECK_VALIDITY(c->pstream, name || idx != PA_IDXSET_INVALID, tag, PA_ERR_INVALID);
-    CHECK_VALIDITY(c->pstream, !name || (*name && pa_utf8_valid(name) && (type == 0 || type == 1)), tag, PA_ERR_INVALID);
-
-    if (name)
-        r = pa_autoload_remove_by_name(c->protocol->core, name, type == 0 ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE);
-    else
-        r = pa_autoload_remove_by_index(c->protocol->core, idx);
-
-    CHECK_VALIDITY(c->pstream, r >= 0, tag, PA_ERR_NOENTITY);
-
-    pa_pstream_send_simple_ack(c->pstream, tag);
-}
-
-static void autoload_fill_tagstruct(pa_tagstruct *t, const pa_autoload_entry *e) {
-    pa_assert(t && e);
-
-    pa_tagstruct_putu32(t, e->index);
-    pa_tagstruct_puts(t, e->name);
-    pa_tagstruct_putu32(t, e->type == PA_NAMEREG_SINK ? 0U : 1U);
-    pa_tagstruct_puts(t, e->module);
-    pa_tagstruct_puts(t, e->argument);
-}
-
-static void command_get_autoload_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
-    const pa_autoload_entry *a = NULL;
-    uint32_t type, idx;
-    const char *name;
-    pa_tagstruct *reply;
-
-    pa_native_connection_assert_ref(c);
-    pa_assert(t);
-
-    if ((pa_tagstruct_getu32(t, &idx) < 0 &&
-        (pa_tagstruct_gets(t, &name) < 0 ||
-         pa_tagstruct_getu32(t, &type) < 0)) ||
-        !pa_tagstruct_eof(t)) {
-        protocol_error(c);
-        return;
-    }
-
-    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
-    CHECK_VALIDITY(c->pstream, name || idx != PA_IDXSET_INVALID, tag, PA_ERR_INVALID);
-    CHECK_VALIDITY(c->pstream, !name || (*name && (type == 0 || type == 1) && pa_utf8_valid(name)), tag, PA_ERR_INVALID);
-
-    if (name)
-        a = pa_autoload_get_by_name(c->protocol->core, name, type == 0 ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE);
-    else
-        a = pa_autoload_get_by_index(c->protocol->core, idx);
-
-    CHECK_VALIDITY(c->pstream, a, tag, PA_ERR_NOENTITY);
-
-    reply = reply_new(tag);
-    autoload_fill_tagstruct(reply, a);
-    pa_pstream_send_tagstruct(c->pstream, reply);
-}
-
-static void command_get_autoload_info_list(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
-    pa_tagstruct *reply;
-
-    pa_native_connection_assert_ref(c);
-    pa_assert(t);
-
-    if (!pa_tagstruct_eof(t)) {
-        protocol_error(c);
-        return;
-    }
-
-    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
-
-    reply = reply_new(tag);
-
-    if (c->protocol->core->autoload_hashmap) {
-        pa_autoload_entry *a;
-        void *state = NULL;
-
-        while ((a = pa_hashmap_iterate(c->protocol->core->autoload_hashmap, &state, NULL)))
-            autoload_fill_tagstruct(reply, a);
-    }
-
-    pa_pstream_send_tagstruct(c->pstream, reply);
-}
-
 static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
     pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     uint32_t idx = PA_INVALID_INDEX, idx_device = PA_INVALID_INDEX;
@@ -3918,7 +3777,7 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag
         if (idx_device != PA_INVALID_INDEX)
             sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx_device);
         else
-            sink = pa_namereg_get(c->protocol->core, name_device, PA_NAMEREG_SINK, 1);
+            sink = pa_namereg_get(c->protocol->core, name_device, PA_NAMEREG_SINK);
 
         CHECK_VALIDITY(c->pstream, si && sink, tag, PA_ERR_NOENTITY);
 
@@ -3937,7 +3796,7 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag
         if (idx_device != PA_INVALID_INDEX)
             source = pa_idxset_get_by_index(c->protocol->core->sources, idx_device);
         else
-            source = pa_namereg_get(c->protocol->core, name_device, PA_NAMEREG_SOURCE, 1);
+            source = pa_namereg_get(c->protocol->core, name_device, PA_NAMEREG_SOURCE);
 
         CHECK_VALIDITY(c->pstream, so && source, tag, PA_ERR_NOENTITY);
 
@@ -3989,7 +3848,7 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa
             if (idx != PA_INVALID_INDEX)
                 sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
             else
-                sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK, 1);
+                sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK);
 
             CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
 
@@ -4017,7 +3876,7 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa
             if (idx != PA_INVALID_INDEX)
                 source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
             else
-                source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE, 1);
+                source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE);
 
             CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
 
diff --git a/src/pulsecore/protocol-simple.c b/src/pulsecore/protocol-simple.c
index a754669..dd8002a 100644
--- a/src/pulsecore/protocol-simple.c
+++ b/src/pulsecore/protocol-simple.c
@@ -526,7 +526,7 @@ void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simp
         size_t l;
         pa_sink *sink;
 
-        if (!(sink = pa_namereg_get(c->protocol->core, o->default_sink, PA_NAMEREG_SINK, TRUE))) {
+        if (!(sink = pa_namereg_get(c->protocol->core, o->default_sink, PA_NAMEREG_SINK))) {
             pa_log("Failed to get sink.");
             goto fail;
         }
@@ -578,7 +578,7 @@ void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simp
         size_t l;
         pa_source *source;
 
-        if (!(source = pa_namereg_get(c->protocol->core, o->default_source, PA_NAMEREG_SOURCE, TRUE))) {
+        if (!(source = pa_namereg_get(c->protocol->core, o->default_source, PA_NAMEREG_SOURCE))) {
             pa_log("Failed to get source.");
             goto fail;
         }
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 185350f..8cdc928 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -131,7 +131,7 @@ pa_sink_input* pa_sink_input_new(
     pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
 
     if (!data->sink)
-        data->sink = pa_namereg_get(core, NULL, PA_NAMEREG_SINK, TRUE);
+        data->sink = pa_namereg_get(core, NULL, PA_NAMEREG_SINK);
 
     pa_return_null_if_fail(data->sink);
     pa_return_null_if_fail(pa_sink_get_state(data->sink) != PA_SINK_UNLINKED);
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index b1c65d1..e04acf9 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -112,7 +112,7 @@ pa_source_output* pa_source_output_new(
     pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
 
     if (!data->source)
-        data->source = pa_namereg_get(core, NULL, PA_NAMEREG_SOURCE, TRUE);
+        data->source = pa_namereg_get(core, NULL, PA_NAMEREG_SOURCE);
 
     pa_return_null_if_fail(data->source);
     pa_return_null_if_fail(pa_source_get_state(data->source) != PA_SOURCE_UNLINKED);
diff --git a/src/utils/pactl.c b/src/utils/pactl.c
index 2f430ca..65342bb 100644
--- a/src/utils/pactl.c
+++ b/src/utils/pactl.c
@@ -476,36 +476,6 @@ static void get_sample_info_callback(pa_context *c, const pa_sample_info *i, int
     pa_xfree(pl);
 }
 
-static void get_autoload_info_callback(pa_context *c, const pa_autoload_info *i, int is_last, void *userdata) {
-    if (is_last < 0) {
-        fprintf(stderr, _("Failed to get autoload information: %s\n"), pa_strerror(pa_context_errno(c)));
-        quit(1);
-        return;
-    }
-
-    if (is_last) {
-        complete_action();
-        return;
-    }
-
-    assert(i);
-
-    if (nl)
-        printf("\n");
-    nl = 1;
-
-    printf(_("*** Autoload Entry #%u ***\n"
-           "Name: %s\n"
-           "Type: %s\n"
-           "Module: %s\n"
-             "Argument: %s\n"),
-           i->index,
-           i->name,
-           i->type == PA_AUTOLOAD_SINK ? _("sink") : _("source"),
-           i->module,
-           i->argument ? i->argument : "");
-}
-
 static void simple_callback(pa_context *c, int success, void *userdata) {
     if (!success) {
         fprintf(stderr, _("Failure: %s\n"), pa_strerror(pa_context_errno(c)));
@@ -619,7 +589,6 @@ static void context_state_callback(pa_context *c, void *userdata) {
                     pa_operation_unref(pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL));
                     pa_operation_unref(pa_context_get_client_info_list(c, get_client_info_callback, NULL));
                     pa_operation_unref(pa_context_get_sample_info_list(c, get_sample_info_callback, NULL));
-                    pa_operation_unref(pa_context_get_autoload_info_list(c, get_autoload_info_callback, NULL));
                     break;
 
                 case MOVE_SINK_INPUT:

commit 395523a0bba400aadae41f6adaa59e6d9b183da2
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 15 20:14:58 2009 +0100

    we don't support glib1.2 anymore

diff --git a/src/pulse/pulseaudio.h b/src/pulse/pulseaudio.h
index e09caca..5086783 100644
--- a/src/pulse/pulseaudio.h
+++ b/src/pulse/pulseaudio.h
@@ -108,7 +108,6 @@
  * modules:
  *
  * \li libpulse - The asynchronous API and the internal main loop implementation.
- * \li libpulse-mainloop-glib12 - GLIB 1.2 main loop bindings.
  * \li libpulse-mainloop-glib - GLIB 2.x main loop bindings.
  * \li libpulse-simple - The simple PulseAudio API.
  */

commit e68e4a57bfc5211309db9bec8057a8b038a0bb14
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 15 20:26:41 2009 +0100

    make things compile again

diff --git a/src/pulsecore/namereg.c b/src/pulsecore/namereg.c
index 3b9ce88..2734bab 100644
--- a/src/pulsecore/namereg.c
+++ b/src/pulsecore/namereg.c
@@ -30,7 +30,6 @@
 
 #include <pulse/xmalloc.h>
 
-#include <pulsecore/autoload.h>
 #include <pulsecore/source.h>
 #include <pulsecore/sink.h>
 #include <pulsecore/core-subscribe.h>

commit 47a2b17d01cc0dceb964df07d17e406726d50d45
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 15 20:27:07 2009 +0100

    make proplist inheritance scheme automatic and implicit

diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c
index 97d10df..b1d3909 100644
--- a/src/pulsecore/protocol-esound.c
+++ b/src/pulsecore/protocol-esound.c
@@ -422,7 +422,6 @@ static int esd_proto_stream_play(connection *c, esd_proto_t request, const void
     sdata.module = c->options->module;
     sdata.client = c->client;
     sdata.sink = sink;
-    pa_proplist_update(sdata.proplist, PA_UPDATE_MERGE, c->client->proplist);
     pa_sink_input_new_data_set_sample_spec(&sdata, &ss);
 
     c->sink_input = pa_sink_input_new(c->protocol->core, &sdata, 0);
@@ -525,7 +524,6 @@ static int esd_proto_stream_record(connection *c, esd_proto_t request, const voi
     sdata.module = c->options->module;
     sdata.client = c->client;
     sdata.source = source;
-    pa_proplist_update(sdata.proplist, PA_UPDATE_MERGE, c->client->proplist);
     pa_source_output_new_data_set_sample_spec(&sdata, &ss);
 
     c->source_output = pa_source_output_new(c->protocol->core, &sdata, 0);
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 2179752..0a070f4 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -601,7 +601,6 @@ static record_stream* record_stream_new(
     pa_source_output_new_data_init(&data);
 
     pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p);
-    pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
     data.driver = __FILE__;
     data.module = c->options->module;
     data.client = c->client;
@@ -1001,7 +1000,6 @@ static playback_stream* playback_stream_new(
     pa_sink_input_new_data_init(&data);
 
     pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p);
-    pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
     data.driver = __FILE__;
     data.module = c->options->module;
     data.client = c->client;
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 8cdc928..8f18266 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -190,6 +190,9 @@ pa_sink_input* pa_sink_input_new(
 
     pa_return_null_if_fail(data->resample_method < PA_RESAMPLER_MAX);
 
+    if (data->client)
+        pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist);
+
     if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], data) < 0)
         return NULL;
 
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index d1d68cd..2abc848 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -167,6 +167,9 @@ pa_sink* pa_sink_new(
     if (!data->muted_is_set)
         data->muted = FALSE;
 
+    if (data->card)
+        pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->card->proplist);
+
     if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_FIXATE], data) < 0) {
         pa_xfree(s);
         pa_namereg_unregister(core, name);
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index e04acf9..e314656 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -153,6 +153,9 @@ pa_source_output* pa_source_output_new(
 
     pa_return_null_if_fail(data->resample_method < PA_RESAMPLER_MAX);
 
+    if (data->client)
+        pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist);
+
     if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], data) < 0)
         return NULL;
 
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index 676a6b4..57018a2 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -158,6 +158,9 @@ pa_source* pa_source_new(
     if (!data->muted_is_set)
         data->muted = FALSE;
 
+    if (data->card)
+        pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->card->proplist);
+
     if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_FIXATE], data) < 0) {
         pa_xfree(s);
         pa_namereg_unregister(core, name);

commit edf88a515035e0544abc328c7b93053bfe475622
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 15 20:48:29 2009 +0100

    don't show autoload flag anymore since it is obsolete

diff --git a/src/utils/pactl.c b/src/utils/pactl.c
index 65342bb..2fc1733 100644
--- a/src/utils/pactl.c
+++ b/src/utils/pactl.c
@@ -281,13 +281,11 @@ static void get_module_info_callback(pa_context *c, const pa_module_info *i, int
     printf(_("*** Module #%u ***\n"
            "Name: %s\n"
            "Argument: %s\n"
-           "Usage counter: %s\n"
-           "Auto unload: %s\n"),
+           "Usage counter: %s\n"),
            i->index,
            i->name,
            i->argument ? i->argument : "",
-           i->n_used != PA_INVALID_INDEX ? t : _("n/a"),
-           pa_yes_no(i->auto_unload));
+           i->n_used != PA_INVALID_INDEX ? t : _("n/a"));
 }
 
 static void get_client_info_callback(pa_context *c, const pa_client_info *i, int is_last, void *userdata) {

commit bae221cca97816e0ae0395ac1e561aa208cb0346
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 15 20:49:12 2009 +0100

    rework module usage counter stuff to be pull based

diff --git a/src/modules/module-alsa-sink.c b/src/modules/module-alsa-sink.c
index 3fa0b5d..a3e818d 100644
--- a/src/modules/module-alsa-sink.c
+++ b/src/modules/module-alsa-sink.c
@@ -1610,6 +1610,15 @@ fail:
     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;
 
diff --git a/src/modules/module-alsa-source.c b/src/modules/module-alsa-source.c
index 22e9ebf..901db01 100644
--- a/src/modules/module-alsa-source.c
+++ b/src/modules/module-alsa-source.c
@@ -1431,6 +1431,15 @@ fail:
     return -1;
 }
 
+int pa__get_n_used(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+    pa_assert_se(u = m->userdata);
+
+    return pa_source_linked_by(u->source);
+}
+
 void pa__done(pa_module*m) {
     struct userdata *u;
 
diff --git a/src/modules/module-defs.h.m4 b/src/modules/module-defs.h.m4
index 64ce192..f9924cf 100644
--- a/src/modules/module-defs.h.m4
+++ b/src/modules/module-defs.h.m4
@@ -18,9 +18,11 @@ gen_symbol(pa__get_description)
 gen_symbol(pa__get_usage)
 gen_symbol(pa__get_version)
 gen_symbol(pa__load_once)
+gen_symbol(pa__get_n_used)
 
 int pa__init(pa_module*m);
 void pa__done(pa_module*m);
+int pa__get_n_used(pa_module*m);
 
 const char* pa__get_author(void);
 const char* pa__get_description(void);
diff --git a/src/modules/module-esound-sink.c b/src/modules/module-esound-sink.c
index 14f1810..7c7f828 100644
--- a/src/modules/module-esound-sink.c
+++ b/src/modules/module-esound-sink.c
@@ -621,6 +621,15 @@ fail:
     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);
diff --git a/src/modules/module-jack-sink.c b/src/modules/module-jack-sink.c
index 555cb82..b448e84 100644
--- a/src/modules/module-jack-sink.c
+++ b/src/modules/module-jack-sink.c
@@ -430,6 +430,15 @@ fail:
     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;
 
diff --git a/src/modules/module-jack-source.c b/src/modules/module-jack-source.c
index 9eccbbf..0c7ee53 100644
--- a/src/modules/module-jack-source.c
+++ b/src/modules/module-jack-source.c
@@ -398,6 +398,15 @@ fail:
     return -1;
 }
 
+int pa__get_n_used(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+    pa_assert_se(u = m->userdata);
+
+    return pa_source_linked_by(u->source);
+}
+
 void pa__done(pa_module*m) {
     struct userdata *u;
     pa_assert(m);
diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c
index 496e9ea..e746f34 100644
--- a/src/modules/module-ladspa-sink.c
+++ b/src/modules/module-ladspa-sink.c
@@ -770,6 +770,15 @@ fail:
     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;
     unsigned c;
diff --git a/src/modules/module-null-sink.c b/src/modules/module-null-sink.c
index daf9767..570f8be 100644
--- a/src/modules/module-null-sink.c
+++ b/src/modules/module-null-sink.c
@@ -324,6 +324,15 @@ fail:
     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;
 
diff --git a/src/modules/module-pipe-sink.c b/src/modules/module-pipe-sink.c
index 2b55c82..03e2717 100644
--- a/src/modules/module-pipe-sink.c
+++ b/src/modules/module-pipe-sink.c
@@ -315,6 +315,15 @@ fail:
     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;
 
diff --git a/src/modules/module-pipe-source.c b/src/modules/module-pipe-source.c
index 77310ca..975090c 100644
--- a/src/modules/module-pipe-source.c
+++ b/src/modules/module-pipe-source.c
@@ -302,6 +302,15 @@ fail:
     return -1;
 }
 
+int pa__get_n_used(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+    pa_assert_se(u = m->userdata);
+
+    return pa_source_linked_by(u->source);
+}
+
 void pa__done(pa_module*m) {
     struct userdata *u;
 
diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c
index 62f0a73..c324437 100644
--- a/src/modules/module-raop-sink.c
+++ b/src/modules/module-raop-sink.c
@@ -627,6 +627,15 @@ fail:
     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);
diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c
index aa91406..e17fef0 100644
--- a/src/modules/module-remap-sink.c
+++ b/src/modules/module-remap-sink.c
@@ -415,6 +415,15 @@ fail:
     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;
 
diff --git a/src/modules/module-sine-source.c b/src/modules/module-sine-source.c
index 79d3b13..5626c2a 100644
--- a/src/modules/module-sine-source.c
+++ b/src/modules/module-sine-source.c
@@ -286,6 +286,15 @@ fail:
     return -1;
 }
 
+int pa__get_n_used(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+    pa_assert_se(u = m->userdata);
+
+    return pa_source_linked_by(u->source);
+}
+
 void pa__done(pa_module*m) {
     struct userdata *u;
 
diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index 27db56e..0f4a273 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -62,7 +62,7 @@ char *pa_module_list_to_string(pa_core *c) {
                          m->index,
                          m->name,
                          pa_strempty(m->argument),
-                         m->n_used,
+                         pa_module_get_n_used(m),
                          pa_yes_no(m->load_once));
     }
 
diff --git a/src/pulsecore/module.c b/src/pulsecore/module.c
index b197ba0..c4dcb47 100644
--- a/src/pulsecore/module.c
+++ b/src/pulsecore/module.c
@@ -45,8 +45,7 @@
 #define PA_SYMBOL_INIT "pa__init"
 #define PA_SYMBOL_DONE "pa__done"
 #define PA_SYMBOL_LOAD_ONCE "pa__load_once"
-
-#define UNLOAD_POLL_TIME 2
+#define PA_SYMBOL_GET_N_USED "pa__get_n_used"
 
 pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
     pa_module *m = NULL;
@@ -92,9 +91,9 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
     }
 
     m->done = (void (*)(pa_module*_m)) pa_load_sym(m->dl, name, PA_SYMBOL_DONE);
+    m->get_n_used = (int (*)(pa_module*_m)) pa_load_sym(m->dl, name, PA_SYMBOL_GET_N_USED);
     m->userdata = NULL;
     m->core = c;
-    m->n_used = -1;
     m->unload_requested = FALSE;
 
     if (m->init(m) < 0) {
@@ -235,17 +234,17 @@ void pa_module_unload_request_by_index(pa_core *c, uint32_t idx, pa_bool_t force
     pa_module_unload_request(m, force);
 }
 
-void pa_module_set_used(pa_module*m, int used) {
+pa_modinfo *pa_module_get_info(pa_module *m) {
     pa_assert(m);
 
-    if (m->n_used != used)
-        pa_subscription_post(m->core, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_CHANGE, m->index);
-
-    m->n_used = used;
+    return pa_modinfo_get_by_handle(m->dl, m->name);
 }
 
-pa_modinfo *pa_module_get_info(pa_module *m) {
+int pa_module_get_n_used(pa_module*m) {
     pa_assert(m);
 
-    return pa_modinfo_get_by_handle(m->dl, m->name);
+    if (!m->get_n_used)
+        return -1;
+
+    return m->get_n_used(m);
 }
diff --git a/src/pulsecore/module.h b/src/pulsecore/module.h
index c54169b..986f0d2 100644
--- a/src/pulsecore/module.h
+++ b/src/pulsecore/module.h
@@ -39,11 +39,10 @@ struct pa_module {
 
     int (*init)(pa_module*m);
     void (*done)(pa_module*m);
+    int (*get_n_used)(pa_module *m);
 
     void *userdata;
 
-    int n_used;
-
     pa_bool_t load_once:1;
     pa_bool_t unload_requested:1;
 };
@@ -58,7 +57,7 @@ void pa_module_unload_request_by_index(pa_core *c, uint32_t idx, pa_bool_t force
 
 void pa_module_unload_all(pa_core *c);
 
-void pa_module_set_used(pa_module*m, int used);
+int pa_module_get_n_used(pa_module*m);
 
 #define PA_MODULE_AUTHOR(s)                                     \
     const char *pa__get_author(void) { return s; }              \
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 0a070f4..af013da 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -2731,8 +2731,8 @@ static void module_fill_tagstruct(pa_tagstruct *t, pa_module *module) {
     pa_tagstruct_putu32(t, module->index);
     pa_tagstruct_puts(t, module->name);
     pa_tagstruct_puts(t, module->argument);
-    pa_tagstruct_putu32(t, (uint32_t) module->n_used);
-    pa_tagstruct_put_boolean(t, FALSE);
+    pa_tagstruct_putu32(t, (uint32_t) pa_module_get_n_used(module));
+    pa_tagstruct_put_boolean(t, FALSE); /* autoload is obsolete */
 }
 
 static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sink_input *s) {

commit c7fff97782bb59815e7ebd38895cc1480719e24c
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 15 21:02:01 2009 +0100

    move alsa and oss modules into their own subdirectories

diff --git a/src/Makefile.am b/src/Makefile.am
index 4b36f6f..62df962 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1036,9 +1036,9 @@ SYMDEF_FILES = \
 		modules/module-x11-bell-symdef.h \
 		modules/module-x11-publish-symdef.h \
 		modules/module-x11-xsmp-symdef.h \
-		modules/module-oss-symdef.h \
-		modules/module-alsa-sink-symdef.h \
-		modules/module-alsa-source-symdef.h \
+		modules/oss/module-oss-symdef.h \
+		modules/alsa/module-alsa-sink-symdef.h \
+		modules/alsa/module-alsa-source-symdef.h \
 		modules/module-solaris-symdef.h \
 		modules/module-waveout-symdef.h \
 		modules/module-detect-symdef.h \
@@ -1232,27 +1232,27 @@ module_x11_xsmp_la_LIBADD = $(AM_LIBADD) $(X11_LIBS) libpulsecore- at PA_MAJORMINOR
 
 # OSS
 
-liboss_util_la_SOURCES = modules/oss-util.c modules/oss-util.h
+liboss_util_la_SOURCES = modules/oss/oss-util.c modules/oss/oss-util.h
 liboss_util_la_LDFLAGS = -avoid-version
 liboss_util_la_LIBADD = libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
 
-module_oss_la_SOURCES = modules/module-oss.c
+module_oss_la_SOURCES = modules/oss/module-oss.c
 module_oss_la_LDFLAGS = $(MODULE_LDFLAGS)
 module_oss_la_LIBADD = $(AM_LIBADD) liboss-util.la libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
 
 # ALSA
 
-libalsa_util_la_SOURCES = modules/alsa-util.c modules/alsa-util.h
+libalsa_util_la_SOURCES = modules/alsa/alsa-util.c modules/alsa/alsa-util.h
 libalsa_util_la_LDFLAGS = -avoid-version
 libalsa_util_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
 libalsa_util_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
 
-module_alsa_sink_la_SOURCES = modules/module-alsa-sink.c
+module_alsa_sink_la_SOURCES = modules/alsa/module-alsa-sink.c
 module_alsa_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
 module_alsa_sink_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libalsa-util.la libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
 module_alsa_sink_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
 
-module_alsa_source_la_SOURCES = modules/module-alsa-source.c
+module_alsa_source_la_SOURCES = modules/alsa/module-alsa-source.c
 module_alsa_source_la_LDFLAGS = $(MODULE_LDFLAGS)
 module_alsa_source_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libalsa-util.la libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
 module_alsa_source_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
diff --git a/src/modules/alsa-util.c b/src/modules/alsa/alsa-util.c
similarity index 100%
rename from src/modules/alsa-util.c
rename to src/modules/alsa/alsa-util.c
diff --git a/src/modules/alsa-util.h b/src/modules/alsa/alsa-util.h
similarity index 100%
rename from src/modules/alsa-util.h
rename to src/modules/alsa/alsa-util.h
diff --git a/src/modules/module-alsa-sink.c b/src/modules/alsa/module-alsa-sink.c
similarity index 100%
rename from src/modules/module-alsa-sink.c
rename to src/modules/alsa/module-alsa-sink.c
diff --git a/src/modules/module-alsa-source.c b/src/modules/alsa/module-alsa-source.c
similarity index 100%
rename from src/modules/module-alsa-source.c
rename to src/modules/alsa/module-alsa-source.c
diff --git a/src/modules/module-oss.c b/src/modules/oss/module-oss.c
similarity index 100%
rename from src/modules/module-oss.c
rename to src/modules/oss/module-oss.c
diff --git a/src/modules/oss-util.c b/src/modules/oss/oss-util.c
similarity index 100%
rename from src/modules/oss-util.c
rename to src/modules/oss/oss-util.c
diff --git a/src/modules/oss-util.h b/src/modules/oss/oss-util.h
similarity index 100%
rename from src/modules/oss-util.h
rename to src/modules/oss/oss-util.h

commit 421011930aec61ac5ec8883b1084c439312fdaa3
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 15 21:03:32 2009 +0100

    add stub makefiles for oss and alsa subdirs

diff --git a/src/modules/alsa/Makefile b/src/modules/alsa/Makefile
new file mode 120000
index 0000000..efe5a33
--- /dev/null
+++ b/src/modules/alsa/Makefile
@@ -0,0 +1 @@
+../../pulse/Makefile
\ No newline at end of file
diff --git a/src/modules/oss/Makefile b/src/modules/oss/Makefile
new file mode 120000
index 0000000..efe5a33
--- /dev/null
+++ b/src/modules/oss/Makefile
@@ -0,0 +1 @@
+../../pulse/Makefile
\ No newline at end of file

commit d4bda31ba12e66ebd246daf0d928f7a6d259022d
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 15 21:29:18 2009 +0100

    include libcli.la in libprotocol-cli's dependencies

diff --git a/src/Makefile.am b/src/Makefile.am
index 62df962..99ed7b2 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -801,7 +801,7 @@ libcli_la_LIBADD = $(AM_LIBADD) libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecom
 
 libprotocol_cli_la_SOURCES = pulsecore/protocol-cli.c pulsecore/protocol-cli.h
 libprotocol_cli_la_LDFLAGS = -avoid-version
-libprotocol_cli_la_LIBADD = $(AM_LIBADD) libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
+libprotocol_cli_la_LIBADD = $(AM_LIBADD) libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la libcli.la
 
 libprotocol_http_la_SOURCES = pulsecore/protocol-http.c pulsecore/protocol-http.h
 libprotocol_http_la_LDFLAGS = -avoid-version

commit 33c22b01022b0450aee011ddd9d6d2b19da74191
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 15 23:44:46 2009 +0100

    rename card config to card profile

diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c
index 03b9ebd..99c0cc5 100644
--- a/src/pulsecore/card.c
+++ b/src/pulsecore/card.c
@@ -36,18 +36,18 @@
 
 #include "card.h"
 
-pa_card_config *pa_card_config_new(const char *name) {
-    pa_card_config *c;
+pa_card_profile *pa_card_profile_new(const char *name) {
+    pa_card_profile *c;
 
     pa_assert(name);
 
-    c = pa_xnew0(pa_card_config, 1);
+    c = pa_xnew0(pa_card_profile, 1);
     c->name = pa_xstrdup(name);
 
     return c;
 }
 
-void pa_card_config_free(pa_card_config *c) {
+void pa_card_profile_free(pa_card_profile *c) {
     pa_assert(c);
 
     pa_xfree(c->name);
@@ -76,13 +76,13 @@ void pa_card_new_data_done(pa_card_new_data *data) {
 
     pa_proplist_free(data->proplist);
 
-    if (data->configs) {
-        pa_card_config *c;
+    if (data->profiles) {
+        pa_card_profile *c;
 
-        while ((c = pa_hashmap_steal_first(data->configs)))
-            pa_card_config_free(c);
+        while ((c = pa_hashmap_steal_first(data->profiles)))
+            pa_card_profile_free(c);
 
-        pa_hashmap_free(data->configs, NULL, NULL);
+        pa_hashmap_free(data->profiles, NULL, NULL);
     }
 
     pa_xfree(data->name);
@@ -120,13 +120,13 @@ pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) {
     c->sinks = pa_idxset_new(NULL, NULL);
     c->sources = pa_idxset_new(NULL, NULL);
 
-    c->configs = data->configs;
-    data->configs = NULL;
-    c->active_config = data->active_config;
-    data->active_config = NULL;
+    c->profiles = data->profiles;
+    data->profiles = NULL;
+    c->active_profile = data->active_profile;
+    data->active_profile = NULL;
 
     c->userdata = NULL;
-    c->set_config = NULL;
+    c->set_profile = NULL;
 
     pa_assert_se(pa_idxset_put(core->cards, c, &c->index) >= 0);
 
@@ -139,7 +139,7 @@ pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) {
 
 void pa_card_free(pa_card *c) {
     pa_core *core;
-    pa_card_config *config;
+    pa_card_profile *profile;
 
     pa_assert(c);
     pa_assert(c->core);
@@ -161,10 +161,10 @@ void pa_card_free(pa_card *c) {
     pa_assert(pa_idxset_isempty(c->sources));
     pa_idxset_free(c->sources, NULL, NULL);
 
-    while ((config = pa_hashmap_steal_first(c->configs)))
-        pa_card_config_free(config);
+    while ((profile = pa_hashmap_steal_first(c->profiles)))
+        pa_card_profile_free(profile);
 
-    pa_hashmap_free(c->configs, NULL, NULL);
+    pa_hashmap_free(c->profiles, NULL, NULL);
 
     pa_proplist_free(c->proplist);
     pa_xfree(c->driver);
@@ -174,22 +174,22 @@ void pa_card_free(pa_card *c) {
     pa_core_check_idle(core);
 }
 
-int pa_card_set_config(pa_card *c, const char *name) {
-    pa_card_config *config;
+int pa_card_set_profile(pa_card *c, const char *name) {
+    pa_card_profile *profile;
     pa_assert(c);
 
-    if (!c->set_config) {
-        pa_log_warn("set_config() operation not implemented for card %u", c->index);
+    if (!c->set_profile) {
+        pa_log_warn("set_profile() operation not implemented for card %u", c->index);
         return -1;
     }
 
-    if (!c->configs)
+    if (!c->profiles)
         return -1;
 
-    if (!(config = pa_hashmap_get(c->configs, name)))
+    if (!(profile = pa_hashmap_get(c->profiles, name)))
         return -1;
 
-    if (c->set_config(c, config) < 0)
+    if (c->set_profile(c, profile) < 0)
         return -1;
 
     pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, c->index);
diff --git a/src/pulsecore/card.h b/src/pulsecore/card.h
index 40e4a3e..e32e880 100644
--- a/src/pulsecore/card.h
+++ b/src/pulsecore/card.h
@@ -29,7 +29,7 @@ typedef struct pa_card pa_card;
 #include <pulsecore/module.h>
 #include <pulsecore/idxset.h>
 
-typedef struct pa_card_config {
+typedef struct pa_card_profile {
     char *name;
 
     pa_bool_t optical_sink:1;
@@ -40,7 +40,7 @@ typedef struct pa_card_config {
 
     unsigned max_sink_channels;
     unsigned max_source_channels;
-} pa_card_config;
+} pa_card_profile;
 
 struct pa_card {
     uint32_t index;
@@ -55,12 +55,12 @@ struct pa_card {
     pa_idxset *sinks;
     pa_idxset *sources;
 
-    pa_hashmap *configs;
-    pa_card_config *active_config;
+    pa_hashmap *profiles;
+    pa_card_profile *active_profile;
 
     void *userdata;
 
-    int (*set_config)(pa_card *c, pa_card_config *config);
+    int (*set_profile)(pa_card *c, pa_card_profile *profile);
 };
 
 typedef struct pa_card_new_data {
@@ -70,14 +70,14 @@ typedef struct pa_card_new_data {
     const char *driver;
     pa_module *module;
 
-    pa_hashmap *configs;
-    pa_card_config *active_config;
+    pa_hashmap *profiles;
+    pa_card_profile *active_profile;
 
     pa_bool_t namereg_fail:1;
 } pa_card_new_data;
 
-pa_card_config *pa_card_config_new(const char *name);
-void pa_card_config_free(pa_card_config *c);
+pa_card_profile *pa_card_profile_new(const char *name);
+void pa_card_profile_free(pa_card_profile *c);
 
 pa_card_new_data *pa_card_new_data_init(pa_card_new_data *data);
 void pa_card_new_data_set_name(pa_card_new_data *data, const char *name);
@@ -86,6 +86,6 @@ void pa_card_new_data_done(pa_card_new_data *data);
 pa_card *pa_card_new(pa_core *c, pa_card_new_data *data);
 void pa_card_free(pa_card *c);
 
-int pa_card_set_config(pa_card *c, const char *name);
+int pa_card_set_profile(pa_card *c, const char *name);
 
 #endif

commit 4d4956ea2f8df5f770f2137d8576afc6e5f161dd
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 15 23:46:42 2009 +0100

    Add SPDIF/HDMI ALSA devices and device descriptions to device search table

diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index ff3af19..871471f 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -474,33 +474,81 @@ int pa_alsa_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min) {
 
 struct device_info {
     pa_channel_map map;
+    const char *alsa_name;
+    const char *description;
     const char *name;
 };
 
 static const struct device_info device_table[] = {
-    {{ 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT } }, "front" },
+    {{ 1, { PA_CHANNEL_POSITION_MONO }},
+     "hw",
+     "Analog Mono",
+     "analog-mono" },
+
+    {{ 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT }},
+     "front",
+     "Analog Stereo",
+     "analog-stereo" },
+
+    {{ 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT }},
+     "iec958",
+     "IEC958 Digital Stereo",
+     "iec958-stereo" },
+
+    {{ 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT }},
+     "hdmi",
+     "HDMI Digital Stereo",
+     "hdmi-stereo"},
 
     {{ 4, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
-            PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT }}, "surround40" },
+            PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT }},
+     "surround40",
+     "Analog Surround 4.0",
+     "analog-surround-40" },
+
+    {{ 4, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+            PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT }},
+     "a52",
+     "IEC958/AC3 Digital Surround 4.0",
+     "iec958-ac3-surround-40" },
 
     {{ 5, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
             PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
-            PA_CHANNEL_POSITION_LFE }}, "surround41" },
+            PA_CHANNEL_POSITION_LFE }},
+     "surround41",
+     "Analog Surround 4.1",
+     "analog-surround-41"},
 
     {{ 5, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
             PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
-            PA_CHANNEL_POSITION_CENTER }}, "surround50" },
+            PA_CHANNEL_POSITION_CENTER }},
+     "surround50",
+     "Analog Surround 5.0",
+     "analog-surround-50" },
 
     {{ 6, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
             PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
-            PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_LFE }}, "surround51" },
+            PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_LFE }},
+     "surround51",
+     "Analog Surround 5.1",
+     "analog-surround-51" },
+
+    {{ 6, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_CENTER,
+            PA_CHANNEL_POSITION_FRONT_RIGHT, PA_CHANNEL_POSITION_REAR_LEFT,
+            PA_CHANNEL_POSITION_REAR_RIGHT, PA_CHANNEL_POSITION_LFE}},
+     "a52",
+     "IEC958/AC3 Digital Surround 5.1",
+     "iec958-ac3-surround-51" },
 
     {{ 8, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
             PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
             PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_LFE,
-            PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT }} , "surround71" },
+            PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT }},
+     "surround71",
+     "Analog Surround 7.1",
+     "analog-surround-71" },
 
-    {{ 0, { 0 }}, NULL }
+    {{ 0, { 0 }}, NULL, NULL, NULL }
 };
 
 static pa_bool_t channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) {
@@ -532,7 +580,9 @@ snd_pcm_t *pa_alsa_open_by_device_id(
         snd_pcm_uframes_t *period_size,
         snd_pcm_uframes_t tsched_size,
         pa_bool_t *use_mmap,
-        pa_bool_t *use_tsched) {
+        pa_bool_t *use_tsched,
+        const char**config_description,
+        const char **config_name) {
 
     int i;
     int direction = 1;
@@ -554,90 +604,122 @@ snd_pcm_t *pa_alsa_open_by_device_id(
      * way, we iterate backwards, and check all devices that do not
      * provide a superset of the requested channel map.*/
 
-    for (i = 0;; i += direction) {
+    i = 0;
+    for (;;) {
         pa_sample_spec try_ss;
         pa_bool_t reformat;
 
-        if (i < 0) {
-            pa_assert(direction == -1);
-
-            /* OK, so we iterated backwards, and now are at the
-             * beginning of our list. */
+        if ((direction > 0) == channel_map_superset(&device_table[i].map, map)) {
+            pa_log_debug("Checking for %s (%s)", device_table[i].name, device_table[i].alsa_name);
 
-            break;
+            d = pa_sprintf_malloc("%s:%s", device_table[i].alsa_name, dev_id);
 
-        } else if (!device_table[i].name) {
-            pa_assert(direction == 1);
+            reformat = FALSE;
+            for (;;) {
+                pa_log_debug("Trying %s %s SND_PCM_NO_AUTO_FORMAT ...", d, reformat ? "without" : "with");
 
-            /* OK, so we are at the end of our list. at iterated
-             * forwards. */
+                /* We don't pass SND_PCM_NONBLOCK here, since alsa-lib <=
+                 * 1.0.17a would then ignore the SND_PCM_NO_xxx
+                 * flags. Instead we enable nonblock mode afterwards via
+                 * snd_pcm_nonblock(). Also see
+                 * http://mailman.alsa-project.org/pipermail/alsa-devel/2008-August/010258.html */
 
-            i--;
-            direction = -1;
-        }
+                if ((err = snd_pcm_open(&pcm_handle, d, mode,
+                                        /* SND_PCM_NONBLOCK| */
+                                        SND_PCM_NO_AUTO_RESAMPLE|
+                                        SND_PCM_NO_AUTO_CHANNELS|
+                                        (reformat ? 0 : SND_PCM_NO_AUTO_FORMAT))) < 0) {
+                    pa_log_info("Couldn't open PCM device %s: %s", d, snd_strerror(err));
+                    break;
+                }
 
-        if ((direction > 0) == !channel_map_superset(&device_table[i].map, map))
-            continue;
+                try_ss.channels = device_table[i].map.channels;
+                try_ss.rate = ss->rate;
+                try_ss.format = ss->format;
 
-        d = pa_sprintf_malloc("%s:%s", device_table[i].name, dev_id);
+                if ((err = pa_alsa_set_hw_params(pcm_handle, &try_ss, nfrags, period_size, tsched_size, use_mmap, use_tsched, TRUE)) < 0) {
 
-        reformat = FALSE;
-        for (;;) {
-            pa_log_debug("Trying %s %s SND_PCM_NO_AUTO_FORMAT ...", d, reformat ? "without" : "with");
+                    if (!reformat) {
+                        reformat = TRUE;
+                        snd_pcm_close(pcm_handle);
+                        continue;
+                    }
 
-            /* We don't pass SND_PCM_NONBLOCK here, since alsa-lib <=
-             * 1.0.17a would then ignore the SND_PCM_NO_xxx
-             * flags. Instead we enable nonblock mode afterwards via
-             * snd_pcm_nonblock(). Also see
-             * http://mailman.alsa-project.org/pipermail/alsa-devel/2008-August/010258.html */
+                    if (!pa_startswith(d, "plug:") && !pa_startswith(d, "plughw:")) {
+                        char *t;
 
-            if ((err = snd_pcm_open(&pcm_handle, d, mode,
-                                    /* SND_PCM_NONBLOCK| */
-                                    SND_PCM_NO_AUTO_RESAMPLE|
-                                    SND_PCM_NO_AUTO_CHANNELS|
-                                    (reformat ? 0 : SND_PCM_NO_AUTO_FORMAT))) < 0) {
-                pa_log_info("Couldn't open PCM device %s: %s", d, snd_strerror(err));
-                break;
-            }
+                        t = pa_sprintf_malloc("plug:%s", d);
+                        pa_xfree(d);
+                        d = t;
 
-            try_ss.channels = device_table[i].map.channels;
-            try_ss.rate = ss->rate;
-            try_ss.format = ss->format;
+                        reformat = FALSE;
 
-            if ((err = pa_alsa_set_hw_params(pcm_handle, &try_ss, nfrags, period_size, tsched_size, use_mmap, use_tsched, TRUE)) < 0) {
+                        snd_pcm_close(pcm_handle);
+                        continue;
+                    }
 
-                if (!reformat) {
-                    reformat = TRUE;
+                    pa_log_info("PCM device %s refused our hw parameters: %s", d, snd_strerror(err));
                     snd_pcm_close(pcm_handle);
-                    continue;
+                    break;
                 }
 
-                if (!pa_startswith(d, "plug:") && !pa_startswith(d, "plughw:")) {
-                    char *t;
-
-                    t = pa_sprintf_malloc("plug:%s", d);
-                    pa_xfree(d);
-                    d = t;
+                *ss = try_ss;
+                *map = device_table[i].map;
+                pa_assert(map->channels == ss->channels);
+                *dev = d;
+                if (config_description)
+                    *config_description = device_table[i].description;
+                if (config_name)
+                    *config_name = device_table[i].name;
 
-                    reformat = FALSE;
+                return pcm_handle;
+            }
 
-                    snd_pcm_close(pcm_handle);
-                    continue;
-                }
+            pa_xfree(d);
+        }
 
-                pa_log_info("PCM device %s refused our hw parameters: %s", d, snd_strerror(err));
-                snd_pcm_close(pcm_handle);
-                break;
+        if (direction > 0) {
+            if (!device_table[i+1].alsa_name) {
+                /* OK, so we are at the end of our list. Let's turn
+                 * back. */
+                direction = -1;
+            } else {
+                /* We are not at the end of the list, so let's simply
+                 * try the next entry */
+                i++;
             }
-
-            *ss = try_ss;
-            *map = device_table[i].map;
-            pa_assert(map->channels == ss->channels);
-            *dev = d;
-            return pcm_handle;
         }
 
-        pa_xfree(d);
+        if (direction < 0) {
+
+            if (device_table[i+1].alsa_name &&
+                device_table[i].map.channels == device_table[i+1].map.channels) {
+
+                /* OK, the next entry has the same number of channels,
+                 * let's try it */
+                i++;
+
+            } else {
+                /* Hmm, so the next entry does not have the same
+                 * number of channels, so let's go backwards until we
+                 * find the next entry with a differnt number of
+                 * channels */
+
+                for (i--; i >= 0; i--)
+                    if (device_table[i].map.channels != device_table[i+1].map.channels)
+                        break;
+
+                /* Hmm, there is no entry with a different number of
+                 * entries, then we're done */
+                if (i < 0)
+                    break;
+
+                /* OK, now lets find go back as long as we have the same number of channels */
+                for (; i > 0; i--)
+                    if (device_table[i].map.channels != device_table[i-1].map.channels)
+                        break;
+            }
+        }
     }
 
     /* OK, we didn't find any good device, so let's try the raw plughw: stuff */
@@ -647,6 +729,11 @@ snd_pcm_t *pa_alsa_open_by_device_id(
     pcm_handle = pa_alsa_open_by_device_string(d, dev, ss, map, mode, nfrags, period_size, tsched_size, use_mmap, use_tsched);
     pa_xfree(d);
 
+    if (pcm_handle) {
+        *config_description = NULL;
+        *config_name = NULL;
+    }
+
     return pcm_handle;
 }
 
diff --git a/src/modules/alsa/alsa-util.h b/src/modules/alsa/alsa-util.h
index 95bb983..ce5f0eb 100644
--- a/src/modules/alsa/alsa-util.h
+++ b/src/modules/alsa/alsa-util.h
@@ -64,7 +64,9 @@ snd_pcm_t *pa_alsa_open_by_device_id(
         snd_pcm_uframes_t *period_size,
         snd_pcm_uframes_t tsched_size,
         pa_bool_t *use_mmap,
-        pa_bool_t *use_tsched);
+        pa_bool_t *use_tsched,
+        const char **config_name,
+        const char **config_description);
 
 snd_pcm_t *pa_alsa_open_by_device_string(
         const char *device,
diff --git a/src/modules/alsa/module-alsa-sink.c b/src/modules/alsa/module-alsa-sink.c
index a3e818d..dfa2055 100644
--- a/src/modules/alsa/module-alsa-sink.c
+++ b/src/modules/alsa/module-alsa-sink.c
@@ -1253,6 +1253,7 @@ int pa__init(pa_module*m) {
     pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d;
     pa_usec_t usec;
     pa_sink_new_data data;
+    const char *profile_description = NULL, *profile_name = NULL;
 
     snd_pcm_info_alloca(&pcm_info);
 
@@ -1338,7 +1339,7 @@ int pa__init(pa_module*m) {
                       &ss, &map,
                       SND_PCM_STREAM_PLAYBACK,
                       &nfrags, &period_frames, tsched_frames,
-                      &b, &d)))
+                      &b, &d, &profile_description, &profile_name)))
 
             goto fail;
 
@@ -1358,6 +1359,9 @@ int pa__init(pa_module*m) {
     pa_assert(u->device_name);
     pa_log_info("Successfully opened device %s.", u->device_name);
 
+    if (profile_description)
+        pa_log_info("Selected configuration '%s' (%s).", profile_description, profile_name);
+
     if (use_mmap && !b) {
         pa_log_info("Device doesn't support mmap(), falling back to UNIX read/write mode.");
         u->use_mmap = use_mmap = FALSE;
@@ -1441,6 +1445,11 @@ int pa__init(pa_module*m) {
     pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size));
     pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial"));
 
+    if (profile_name)
+        pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_NAME, profile_name);
+    if (profile_description)
+        pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_DESCRIPTION, profile_description);
+
     u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
     pa_sink_new_data_done(&data);
     pa_xfree(name_buf);
diff --git a/src/modules/alsa/module-alsa-source.c b/src/modules/alsa/module-alsa-source.c
index 901db01..f89b6e2 100644
--- a/src/modules/alsa/module-alsa-source.c
+++ b/src/modules/alsa/module-alsa-source.c
@@ -1087,6 +1087,7 @@ int pa__init(pa_module*m) {
     pa_bool_t namereg_fail;
     pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d;
     pa_source_new_data data;
+    const char *profile_description = NULL, *profile_name = NULL;
 
     snd_pcm_info_alloca(&pcm_info);
 
@@ -1167,7 +1168,7 @@ int pa__init(pa_module*m) {
                       &ss, &map,
                       SND_PCM_STREAM_CAPTURE,
                       &nfrags, &period_frames, tsched_frames,
-                      &b, &d)))
+                      &b, &d, &profile_description, &profile_name)))
             goto fail;
 
     } else {
@@ -1185,6 +1186,9 @@ int pa__init(pa_module*m) {
     pa_assert(u->device_name);
     pa_log_info("Successfully opened device %s.", u->device_name);
 
+    if (profile_description)
+        pa_log_info("Selected configuration '%s' (%s).", profile_description, profile_name);
+
     if (use_mmap && !b) {
         pa_log_info("Device doesn't support mmap(), falling back to UNIX read/write mode.");
         u->use_mmap = use_mmap = FALSE;
@@ -1268,6 +1272,11 @@ int pa__init(pa_module*m) {
     pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size));
     pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial"));
 
+    if (profile_name)
+        pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_NAME, profile_name);
+    if (profile_description)
+        pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_DESCRIPTION, profile_description);
+
     u->source = pa_source_new(m->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
     pa_source_new_data_done(&data);
     pa_xfree(name_buf);
diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h
index 5d65ff3..77f0399 100644
--- a/src/pulse/proplist.h
+++ b/src/pulse/proplist.h
@@ -75,8 +75,10 @@ PA_C_DECL_BEGIN
  *    device.connector              isa, pci, usb, firewire, bluetooth
  *    device.access_mode            mmap, mmap_rewrite, serial
  *    device.master_device
- *    device.bufferin.buffer_size
- *    device.bufferin.fragment_size
+ *    device.buffering.buffer_size
+ *    device.buffering.fragment_size
+ *    device.profile.name           analog-stereo, analog-surround-40, iec958-stereo, ...
+ *    device.profile.description    "Analog Stereo", ...
  */
 #define PA_PROP_MEDIA_NAME                     "media.name"
 #define PA_PROP_MEDIA_TITLE                    "media.title"
@@ -124,6 +126,8 @@ PA_C_DECL_BEGIN
 #define PA_PROP_DEVICE_MASTER_DEVICE           "device.master_device"
 #define PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE   "device.buffering.buffer_size"
 #define PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE "device.buffering.fragment_size"
+#define PA_PROP_DEVICE_PROFILE_NAME            "device.profile.name"
+#define PA_PROP_DEVICE_PROFILE_DESCRIPTION     "device.profile.description"
 
 /** A property list object. Basically a dictionary with UTF-8 strings
  * as keys and arbitrary data as values. \since 0.9.11 */

commit 9955398fdafc58f23a86fc05f4832b5846b3b040
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jan 16 03:13:42 2009 +0100

    fix version info in protocol history

diff --git a/PROTOCOL b/PROTOCOL
index 80a0c33..37f289a 100644
--- a/PROTOCOL
+++ b/PROTOCOL
@@ -155,7 +155,7 @@ PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR at the end:
   early_requests (bool)
 
 
-### v15, implemented by >= 0.9.14
+### v15, implemented by >= 0.9.15
 
 PA_COMMAND_CREATE_PLAYBACK_STREAM
 

commit 6dc76d11583979ba73dbe4bbf54f52fc1af901e2
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jan 16 03:15:39 2009 +0100

    add support for 24bit packed samples

diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index 871471f..7616ad1 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -232,6 +232,8 @@ static int set_format(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, pa_s
         [PA_SAMPLE_FLOAT32BE] = SND_PCM_FORMAT_FLOAT_BE,
         [PA_SAMPLE_S32LE] = SND_PCM_FORMAT_S32_LE,
         [PA_SAMPLE_S32BE] = SND_PCM_FORMAT_S32_BE,
+        [PA_SAMPLE_S24LE] = SND_PCM_FORMAT_S24_3LE,
+        [PA_SAMPLE_S24BE] = SND_PCM_FORMAT_S24_3BE,
     };
 
     static const pa_sample_format_t try_order[] = {
@@ -239,6 +241,8 @@ static int set_format(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, pa_s
         PA_SAMPLE_FLOAT32RE,
         PA_SAMPLE_S32NE,
         PA_SAMPLE_S32RE,
+        PA_SAMPLE_S24NE,
+        PA_SAMPLE_S24RE,
         PA_SAMPLE_S16NE,
         PA_SAMPLE_S16RE,
         PA_SAMPLE_ALAW,
@@ -259,6 +263,10 @@ static int set_format(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, pa_s
         *f = PA_SAMPLE_FLOAT32LE;
     else if (*f == PA_SAMPLE_FLOAT32LE)
         *f = PA_SAMPLE_FLOAT32BE;
+    else if (*f == PA_SAMPLE_S24BE)
+        *f = PA_SAMPLE_S24LE;
+    else if (*f == PA_SAMPLE_S24LE)
+        *f = PA_SAMPLE_S24BE;
     else if (*f == PA_SAMPLE_S16BE)
         *f = PA_SAMPLE_S16LE;
     else if (*f == PA_SAMPLE_S16LE)
diff --git a/src/modules/oss/oss-util.c b/src/modules/oss/oss-util.c
index f766030..fbe930c 100644
--- a/src/modules/oss/oss-util.c
+++ b/src/modules/oss/oss-util.c
@@ -164,6 +164,8 @@ int pa_oss_auto_format(int fd, pa_sample_spec *ss) {
         [PA_SAMPLE_FLOAT32BE] = AFMT_QUERY, /* not supported */
         [PA_SAMPLE_S32LE] = AFMT_QUERY, /* not supported */
         [PA_SAMPLE_S32BE] = AFMT_QUERY, /* not supported */
+        [PA_SAMPLE_S24LE] = AFMT_QUERY, /* not supported */
+        [PA_SAMPLE_S24BE] = AFMT_QUERY, /* not supported */
     };
 
     pa_assert(fd >= 0);
diff --git a/src/pulse/sample.c b/src/pulse/sample.c
index 2950159..8228a9b 100644
--- a/src/pulse/sample.c
+++ b/src/pulse/sample.c
@@ -48,6 +48,8 @@ size_t pa_sample_size(const pa_sample_spec *spec) {
         [PA_SAMPLE_FLOAT32BE] = 4,
         [PA_SAMPLE_S32LE] = 4,
         [PA_SAMPLE_S32BE] = 4,
+        [PA_SAMPLE_S24LE] = 3,
+        [PA_SAMPLE_S24BE] = 3
     };
 
     pa_assert(spec);
@@ -125,6 +127,8 @@ const char *pa_sample_format_to_string(pa_sample_format_t f) {
         [PA_SAMPLE_FLOAT32BE] = "float32be",
         [PA_SAMPLE_S32LE] = "s32le",
         [PA_SAMPLE_S32BE] = "s32be",
+        [PA_SAMPLE_S24LE] = "s24le",
+        [PA_SAMPLE_S24BE] = "s24be",
     };
 
     if (f < 0 || f >= PA_SAMPLE_MAX)
@@ -194,8 +198,16 @@ pa_sample_format_t pa_parse_sample_format(const char *format) {
         return PA_SAMPLE_S32BE;
     else if (strcasecmp(format, "s32ne") == 0 || strcasecmp(format, "s32") == 0 || strcasecmp(format, "32") == 0)
         return PA_SAMPLE_S32NE;
-    else if (strcasecmp(format, "s32re") == 0)
-        return PA_SAMPLE_S32RE;
+    else if (strcasecmp(format, "s24re") == 0)
+        return PA_SAMPLE_S24RE;
+    else if (strcasecmp(format, "s24le") == 0)
+        return PA_SAMPLE_S24LE;
+    else if (strcasecmp(format, "s24be") == 0)
+        return PA_SAMPLE_S24BE;
+    else if (strcasecmp(format, "s24ne") == 0 || strcasecmp(format, "s24") == 0 || strcasecmp(format, "24") == 0)
+        return PA_SAMPLE_S24NE;
+    else if (strcasecmp(format, "s24re") == 0)
+        return PA_SAMPLE_S24RE;
 
     return -1;
 }
diff --git a/src/pulse/sample.h b/src/pulse/sample.h
index 3c7dd0e..8d09d32 100644
--- a/src/pulse/sample.h
+++ b/src/pulse/sample.h
@@ -51,6 +51,8 @@
  * \li PA_SAMPLE_ULAW - 8 bit mu-Law.
  * \li PA_SAMPLE_S32LE - Signed 32 bit integer PCM, little endian.
  * \li PA_SAMPLE_S32BE - Signed 32 bit integer PCM, big endian.
+ * \li PA_SAMPLE_242LE - Signed 24 bit integer PCM packed, little endian.
+ * \li PA_SAMPLE_242BE - Signed 24 bit integer PCM packed, big endian.
  *
  * The floating point sample formats have the range from -1.0 to 1.0.
  *
@@ -145,7 +147,13 @@ typedef enum pa_sample_format {
     /**< Signed 32 Bit PCM, little endian (PC) */
 
     PA_SAMPLE_S32BE,
-    /**< Signed 32 Bit PCM, big endian (PC) */
+    /**< Signed 32 Bit PCM, big endian */
+
+    PA_SAMPLE_S24LE,
+    /**< Signed 24 Bit PCM packed, little endian (PC) */
+
+    PA_SAMPLE_S24BE,
+    /**< Signed 24 Bit PCM packed, big endian */
 
     PA_SAMPLE_MAX,
     /**< Upper limit of valid sample types */
@@ -161,12 +169,16 @@ typedef enum pa_sample_format {
 #define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32BE
 /** Signed 32 Bit PCM, native endian */
 #define PA_SAMPLE_S32NE PA_SAMPLE_S32BE
+/** Signed 24 Bit PCM packed, native endian */
+#define PA_SAMPLE_S24NE PA_SAMPLE_S24BE
 /** Signed 16 Bit PCM reverse endian */
 #define PA_SAMPLE_S16RE PA_SAMPLE_S16LE
 /** 32 Bit IEEE floating point, reverse endian */
 #define PA_SAMPLE_FLOAT32RE PA_SAMPLE_FLOAT32LE
-/** Signed 32 Bit PCM reverse endian */
+/** Signed 32 Bit PCM, reverse endian */
 #define PA_SAMPLE_S32RE PA_SAMPLE_S32LE
+/** Signed 24 Bit PCM, packed reverse endian */
+#define PA_SAMPLE_S24RE PA_SAMPLE_242LE
 #else
 /** Signed 16 Bit PCM, native endian */
 #define PA_SAMPLE_S16NE PA_SAMPLE_S16LE
@@ -174,12 +186,16 @@ typedef enum pa_sample_format {
 #define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32LE
 /** Signed 32 Bit PCM, native endian */
 #define PA_SAMPLE_S32NE PA_SAMPLE_S32LE
-/** Signed 16 Bit PCM reverse endian */
+/** Signed 24 Bit PCM packed, native endian */
+#define PA_SAMPLE_S24NE PA_SAMPLE_S24LE
+/** Signed 16 Bit PCM, reverse endian */
 #define PA_SAMPLE_S16RE PA_SAMPLE_S16BE
 /** 32 Bit IEEE floating point, reverse endian */
 #define PA_SAMPLE_FLOAT32RE PA_SAMPLE_FLOAT32BE
-/** Signed 32 Bit PCM reverse endian */
+/** Signed 32 Bit PCM, reverse endian */
 #define PA_SAMPLE_S32RE PA_SAMPLE_S32BE
+/** Signed 24 Bit PCM packed, reverse endian */
+#define PA_SAMPLE_S24RE PA_SAMPLE_S24BE
 #endif
 
 /** A Shortcut for PA_SAMPLE_FLOAT32NE */
@@ -196,6 +212,8 @@ typedef enum pa_sample_format {
 #define PA_SAMPLE_FLOAT32BE PA_SAMPLE_FLOAT32BE
 #define PA_SAMPLE_S32LE PA_SAMPLE_S32LE
 #define PA_SAMPLE_S32BE PA_SAMPLE_S32BE
+#define PA_SAMPLE_S24LE PA_SAMPLE_S24LE
+#define PA_SAMPLE_S24BE PA_SAMPLE_S24BE
 /** \endcond */
 
 /** A sample format and attribute specification */
diff --git a/src/pulse/stream.c b/src/pulse/stream.c
index c0ae4ac..2a958e0 100644
--- a/src/pulse/stream.c
+++ b/src/pulse/stream.c
@@ -87,6 +87,7 @@ pa_stream *pa_stream_new_with_proplist(
 
     PA_CHECK_VALIDITY_RETURN_NULL(c, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID);
     PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 12 || (ss->format != PA_SAMPLE_S32LE && ss->format != PA_SAMPLE_S32BE), PA_ERR_NOTSUPPORTED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15 || (ss->format != PA_SAMPLE_S24LE && ss->format != PA_SAMPLE_S24BE), PA_ERR_NOTSUPPORTED);
     PA_CHECK_VALIDITY_RETURN_NULL(c, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID);
     PA_CHECK_VALIDITY_RETURN_NULL(c, name || (p && pa_proplist_contains(p, PA_PROP_MEDIA_NAME)), PA_ERR_INVALID);
 
diff --git a/src/pulsecore/endianmacros.h b/src/pulsecore/endianmacros.h
index 85bebd6..eea1c74 100644
--- a/src/pulsecore/endianmacros.h
+++ b/src/pulsecore/endianmacros.h
@@ -45,6 +45,32 @@
 #define PA_UINT32_SWAP(x) ( (uint32_t) ( ((uint32_t) (x) >> 24) | ((uint32_t) (x) << 24) | (((uint32_t) (x) & 0xFF00) << 8) | ((((uint32_t) (x)) >> 8) & 0xFF00) ) )
 #endif
 
+static inline uint32_t PA_READ24LE(const uint8_t *p) {
+    return
+        ((uint32_t) p[0] << 16) |
+        ((uint32_t) p[1] << 8) |
+        ((uint32_t) p[2]);
+}
+
+static inline uint32_t PA_READ24BE(const uint8_t *p) {
+    return
+        ((uint32_t) p[2] << 16) |
+        ((uint32_t) p[1] << 8) |
+        ((uint32_t) p[0]);
+}
+
+static inline void PA_WRITE24LE(uint8_t *p, uint32_t u) {
+    p[0] = (uint8_t) (u >> 16);
+    p[1] = (uint8_t) (u >> 8);
+    p[2] = (uint8_t) u;
+}
+
+static inline void PA_WRITE24BE(uint8_t *p, uint32_t u) {
+    p[2] = (uint8_t) (u >> 16);
+    p[1] = (uint8_t) (u >> 8);
+    p[0] = (uint8_t) u;
+}
+
 static inline float PA_FLOAT32_SWAP(float x) {
     union {
         float f;
@@ -91,6 +117,12 @@ static inline float PA_FLOAT32_SWAP(float x) {
 
  #define PA_FLOAT32_TO_LE(x) PA_FLOAT32_SWAP(x)
  #define PA_FLOAT32_TO_BE(x) ((float) (x))
+
+ #define PA_READ24NE(x) PA_READ24BE(x)
+ #define PA_WRITE24NE(x,y) PA_WRITE24BE((x),(y))
+
+ #define PA_READ24RE(x) PA_READ24LE(x)
+ #define PA_WRITE24RE(x,y) PA_WRITE24LE((x),(y))
 #else
  #define PA_INT16_FROM_LE(x) ((int16_t)(x))
  #define PA_INT16_FROM_BE(x) PA_INT16_SWAP(x)
@@ -118,6 +150,12 @@ static inline float PA_FLOAT32_SWAP(float x) {
 
  #define PA_FLOAT32_TO_LE(x) ((float) (x))
  #define PA_FLOAT32_TO_BE(x) PA_FLOAT32_SWAP(x)
+
+ #define PA_READ24NE(x) PA_READ24LE(x)
+ #define PA_WRITE24NE(x,y) PA_WRITE24LE((x),(y))
+
+ #define PA_READ24RE(x) PA_READ24BE(x)
+ #define PA_WRITE24RE(x,y) PA_WRITE24BE((x),(y))
 #endif
 
 #endif
diff --git a/src/pulsecore/envelope.c b/src/pulsecore/envelope.c
index 7f2252e..f872d34 100644
--- a/src/pulsecore/envelope.c
+++ b/src/pulsecore/envelope.c
@@ -600,7 +600,6 @@ void pa_envelope_apply(pa_envelope *e, pa_memchunk *chunk) {
         switch (e->sample_spec.format) {
 
 
-
             case PA_SAMPLE_U8: {
                 uint8_t *t;
 
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index af013da..b1ef64f 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -2640,6 +2640,13 @@ static void fixup_sample_spec(pa_native_connection *c, pa_sample_spec *fixed, co
         if (fixed->format == PA_SAMPLE_S32BE)
             fixed->format = PA_SAMPLE_FLOAT32BE;
     }
+
+    if (c->version < 15) {
+        if (fixed->format == PA_SAMPLE_S24LE)
+            fixed->format = PA_SAMPLE_FLOAT32LE;
+        if (fixed->format == PA_SAMPLE_S24BE)
+            fixed->format = PA_SAMPLE_FLOAT32BE;
+    }
 }
 
 static void sink_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sink *sink) {
diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c
index f0515eb..67c3758 100644
--- a/src/pulsecore/resampler.c
+++ b/src/pulsecore/resampler.c
@@ -257,8 +257,10 @@ pa_resampler* pa_resampler_new(
 
             if (a->format == PA_SAMPLE_S32NE || a->format == PA_SAMPLE_S32RE ||
                 a->format == PA_SAMPLE_FLOAT32NE || a->format == PA_SAMPLE_FLOAT32RE ||
+                a->format == PA_SAMPLE_S24NE || a->format == PA_SAMPLE_S24RE ||
                 b->format == PA_SAMPLE_S32NE || b->format == PA_SAMPLE_S32RE ||
-                b->format == PA_SAMPLE_FLOAT32NE || b->format == PA_SAMPLE_FLOAT32RE)
+                b->format == PA_SAMPLE_FLOAT32NE || b->format == PA_SAMPLE_FLOAT32RE ||
+                b->format == PA_SAMPLE_S24NE || b->format == PA_SAMPLE_S24RE)
                 r->work_format = PA_SAMPLE_FLOAT32NE;
             else
                 r->work_format = PA_SAMPLE_S16NE;
diff --git a/src/pulsecore/sample-util.c b/src/pulsecore/sample-util.c
index 3be08ef..533181b 100644
--- a/src/pulsecore/sample-util.c
+++ b/src/pulsecore/sample-util.c
@@ -83,6 +83,8 @@ static uint8_t silence_byte(pa_sample_format_t format) {
         case PA_SAMPLE_S32BE:
         case PA_SAMPLE_FLOAT32LE:
         case PA_SAMPLE_FLOAT32BE:
+        case PA_SAMPLE_S24LE:
+        case PA_SAMPLE_S24BE:
             return 0;
         case PA_SAMPLE_ALAW:
             return 0xd5;
@@ -340,6 +342,78 @@ size_t pa_mix(
             break;
         }
 
+        case PA_SAMPLE_S24NE: {
+            unsigned channel = 0;
+
+            calc_linear_integer_stream_volumes(streams, nstreams, volume, spec);
+
+            while (data < end) {
+                int64_t sum = 0;
+                unsigned i;
+
+                for (i = 0; i < nstreams; i++) {
+                    pa_mix_info *m = streams + i;
+                    int32_t cv = m->linear[channel].i;
+                    int64_t v;
+
+                    if (PA_UNLIKELY(cv <= 0))
+                        continue;
+
+                    v = (int32_t) (PA_READ24NE(m->ptr) << 8);
+                    v = (v * cv) / 0x10000;
+                    sum += v;
+
+                    m->ptr = (uint8_t*) m->ptr + 3;
+                }
+
+                sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL);
+                PA_WRITE24NE(data, ((uint32_t) sum) >> 8);
+
+                data = (uint8_t*) data + 3;
+
+                if (PA_UNLIKELY(++channel >= spec->channels))
+                    channel = 0;
+            }
+
+            break;
+        }
+
+        case PA_SAMPLE_S24RE: {
+            unsigned channel = 0;
+
+            calc_linear_integer_stream_volumes(streams, nstreams, volume, spec);
+
+            while (data < end) {
+                int64_t sum = 0;
+                unsigned i;
+
+                for (i = 0; i < nstreams; i++) {
+                    pa_mix_info *m = streams + i;
+                    int32_t cv = m->linear[channel].i;
+                    int64_t v;
+
+                    if (PA_UNLIKELY(cv <= 0))
+                        continue;
+
+                    v = (int32_t) (PA_READ24RE(m->ptr) << 8);
+                    v = (v * cv) / 0x10000;
+                    sum += v;
+
+                    m->ptr = (uint8_t*) m->ptr + 3;
+                }
+
+                sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL);
+                PA_WRITE24RE(data, ((uint32_t) sum) >> 8);
+
+                data = (uint8_t*) data + 3;
+
+                if (PA_UNLIKELY(++channel >= spec->channels))
+                    channel = 0;
+            }
+
+            break;
+        }
+
         case PA_SAMPLE_U8: {
             unsigned channel = 0;
 
@@ -642,7 +716,52 @@ void pa_volume_memchunk(
                 if (PA_UNLIKELY(++channel >= spec->channels))
                     channel = 0;
             }
+            break;
+        }
+
+        case PA_SAMPLE_S24NE: {
+            uint8_t *d, *e;
+            unsigned channel;
+            int32_t linear[PA_CHANNELS_MAX];
+
+            calc_linear_integer_volume(linear, volume);
+
+            e = (uint8_t*) ptr + c->length/3;
+
+            for (channel = 0, d = ptr; d < e; d++) {
+                int64_t t;
 
+                t = (int64_t)((int32_t) (PA_READ24NE(d) << 8));
+                t = (t * linear[channel]) / 0x10000;
+                t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
+                PA_WRITE24NE(d, ((uint32_t) (int32_t) t) >> 8);
+
+                if (PA_UNLIKELY(++channel >= spec->channels))
+                    channel = 0;
+            }
+            break;
+        }
+
+        case PA_SAMPLE_S24RE: {
+            uint8_t *d, *e;
+            unsigned channel;
+            int32_t linear[PA_CHANNELS_MAX];
+
+            calc_linear_integer_volume(linear, volume);
+
+            e = (uint8_t*) ptr + c->length/3;
+
+            for (channel = 0, d = ptr; d < e; d++) {
+                int64_t t;
+
+                t = (int64_t)((int32_t) (PA_READ24RE(d) << 8));
+                t = (t * linear[channel]) / 0x10000;
+                t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
+                PA_WRITE24RE(d, ((uint32_t) (int32_t) t) >> 8);
+
+                if (PA_UNLIKELY(++channel >= spec->channels))
+                    channel = 0;
+            }
             break;
         }
 
@@ -900,12 +1019,16 @@ pa_memchunk* pa_silence_memchunk_get(pa_silence_cache *cache, pa_mempool *pool,
             case PA_SAMPLE_S16BE:
             case PA_SAMPLE_S32LE:
             case PA_SAMPLE_S32BE:
+            case PA_SAMPLE_S24LE:
+            case PA_SAMPLE_S24BE:
             case PA_SAMPLE_FLOAT32LE:
             case PA_SAMPLE_FLOAT32BE:
                 cache->blocks[PA_SAMPLE_S16LE] = b = silence_memblock_new(pool, 0);
                 cache->blocks[PA_SAMPLE_S16BE] = pa_memblock_ref(b);
                 cache->blocks[PA_SAMPLE_S32LE] = pa_memblock_ref(b);
                 cache->blocks[PA_SAMPLE_S32BE] = pa_memblock_ref(b);
+                cache->blocks[PA_SAMPLE_S24LE] = pa_memblock_ref(b);
+                cache->blocks[PA_SAMPLE_S24BE] = pa_memblock_ref(b);
                 cache->blocks[PA_SAMPLE_FLOAT32LE] = pa_memblock_ref(b);
                 cache->blocks[PA_SAMPLE_FLOAT32BE] = pa_memblock_ref(b);
                 break;
diff --git a/src/pulsecore/sconv-s16be.c b/src/pulsecore/sconv-s16be.c
index e7e1449..d6137d3 100644
--- a/src/pulsecore/sconv-s16be.c
+++ b/src/pulsecore/sconv-s16be.c
@@ -31,24 +31,34 @@
 #define INT32_FROM PA_INT32_FROM_BE
 #define INT32_TO PA_INT32_TO_BE
 
+#define READ24 PA_READ24BE
+#define WRITE24 PA_WRITE24BE
+
 #define pa_sconv_s16le_to_float32ne pa_sconv_s16be_to_float32ne
 #define pa_sconv_s16le_from_float32ne pa_sconv_s16be_from_float32ne
-
 #define pa_sconv_s16le_to_float32re pa_sconv_s16be_to_float32re
 #define pa_sconv_s16le_from_float32re pa_sconv_s16be_from_float32re
 
 #define pa_sconv_s32le_to_float32ne pa_sconv_s32be_to_float32ne
 #define pa_sconv_s32le_from_float32ne pa_sconv_s32be_from_float32ne
-
 #define pa_sconv_s32le_to_float32re pa_sconv_s32be_to_float32re
 #define pa_sconv_s32le_from_float32re pa_sconv_s32be_from_float32re
 
+#define pa_sconv_s24le_to_float32ne pa_sconv_s24be_to_float32ne
+#define pa_sconv_s24le_from_float32ne pa_sconv_s24be_from_float32ne
+#define pa_sconv_s24le_to_float32re pa_sconv_s24be_to_float32re
+#define pa_sconv_s24le_from_float32re pa_sconv_s24be_from_float32re
+
 #define pa_sconv_s32le_to_s16ne pa_sconv_s32be_to_s16ne
 #define pa_sconv_s32le_from_s16ne pa_sconv_s32be_from_s16ne
-
 #define pa_sconv_s32le_to_s16re pa_sconv_s32be_to_s16re
 #define pa_sconv_s32le_from_s16re pa_sconv_s32be_from_s16re
 
+#define pa_sconv_s24le_to_s16ne pa_sconv_s24be_to_s16ne
+#define pa_sconv_s24le_from_s16ne pa_sconv_s24be_from_s16ne
+#define pa_sconv_s24le_to_s16re pa_sconv_s24be_to_s16re
+#define pa_sconv_s24le_from_s16re pa_sconv_s24be_from_s16re
+
 #ifdef WORDS_BIGENDIAN
 #define SWAP_WORDS 0
 #else
diff --git a/src/pulsecore/sconv-s16be.h b/src/pulsecore/sconv-s16be.h
index 6058081..1b88fd4 100644
--- a/src/pulsecore/sconv-s16be.h
+++ b/src/pulsecore/sconv-s16be.h
@@ -34,26 +34,26 @@ void pa_sconv_s32be_from_float32ne(unsigned n, const float *a, int32_t *b);
 void pa_sconv_s32be_to_float32re(unsigned n, const int32_t *a, float *b);
 void pa_sconv_s32be_from_float32re(unsigned n, const float *a, int32_t *b);
 
+void pa_sconv_s24be_to_float32ne(unsigned n, const uint8_t *a, float *b);
+void pa_sconv_s24be_from_float32ne(unsigned n, const float *a, uint8_t *b);
+void pa_sconv_s24be_to_float32re(unsigned n, const uint8_t *a, float *b);
+void pa_sconv_s24be_from_float32re(unsigned n, const float *a, uint8_t *b);
+
 void pa_sconv_s32be_to_s16ne(unsigned n, const int32_t *a, int16_t *b);
 void pa_sconv_s32be_from_s16ne(unsigned n, const int16_t *a, int32_t *b);
 void pa_sconv_s32be_to_s16re(unsigned n, const int32_t *a, int16_t *b);
 void pa_sconv_s32be_from_s16re(unsigned n, const int16_t *a, int32_t *b);
 
+void pa_sconv_s24be_to_s16ne(unsigned n, const uint8_t *a, int16_t *b);
+void pa_sconv_s24be_from_s16ne(unsigned n, const int16_t *a, uint8_t *b);
+void pa_sconv_s24be_to_s16re(unsigned n, const uint8_t *a, int16_t *b);
+void pa_sconv_s24be_from_s16re(unsigned n, const int16_t *a, uint8_t *b);
+
 #ifdef WORDS_BIGENDIAN
 #define pa_sconv_float32be_to_s16ne pa_sconv_s16be_from_float32ne
 #define pa_sconv_float32be_from_s16ne pa_sconv_s16be_to_float32ne
 #define pa_sconv_float32le_to_s16ne pa_sconv_s16be_from_float32re
 #define pa_sconv_float32le_from_s16ne pa_sconv_s16be_to_float32re
-
-#define pa_sconv_float32be_to_s32ne pa_sconv_s32be_from_float32ne
-#define pa_sconv_float32be_from_s32ne pa_sconv_s32be_to_float32ne
-#define pa_sconv_float32le_to_s32ne pa_sconv_s32be_from_float32re
-#define pa_sconv_float32le_from_s32ne pa_sconv_s32be_to_float32re
-
-#define pa_sconv_s16be_to_s32ne pa_sconv_s32be_from_s16ne
-#define pa_sconv_s16be_from_s32ne pa_sconv_s32be_to_s16ne
-#define pa_sconv_s16le_to_s32ne pa_sconv_s32be_from_s16re
-#define pa_sconv_s16le_from_s32ne pa_sconv_s32be_to_s16re
 #endif
 
 #endif
diff --git a/src/pulsecore/sconv-s16le.c b/src/pulsecore/sconv-s16le.c
index 159c465..37ebc98 100644
--- a/src/pulsecore/sconv-s16le.c
+++ b/src/pulsecore/sconv-s16le.c
@@ -23,7 +23,7 @@
 #include <config.h>
 #endif
 
-/* Despite the name of this file we implement S32 handling here, too. */
+/* Despite the name of this file we implement S32 and S24 handling here, too. */
 
 #include <inttypes.h>
 #include <stdio.h>
@@ -54,6 +54,14 @@
 #define INT32_TO PA_INT32_TO_LE
 #endif
 
+#ifndef READ24
+#define READ24 PA_READ24LE
+#endif
+
+#ifndef WRITE24
+#define WRITE24 PA_WRITE24LE
+#endif
+
 #ifndef SWAP_WORDS
 #ifdef WORDS_BIGENDIAN
 #define SWAP_WORDS 1
@@ -243,3 +251,105 @@ void pa_sconv_s32le_from_s16re(unsigned n, const int16_t *a, int32_t *b) {
         b++;
     }
 }
+
+void pa_sconv_s24le_to_s16ne(unsigned n, const uint8_t *a, int16_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        *b = (int16_t) (READ24(a) >> 8);
+        a += 3;
+        b++;
+    }
+}
+
+void pa_sconv_s24le_from_s16ne(unsigned n, const int16_t *a, uint8_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        WRITE24(b, ((uint32_t) *a) << 8);
+        a++;
+        b += 3;
+    }
+}
+
+void pa_sconv_s24le_to_s16re(unsigned n, const uint8_t *a, int16_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        int16_t s = (int16_t) (READ24(a) >> 8);
+        *b = PA_INT16_SWAP(s);
+        a += 3;
+        b++;
+    }
+}
+
+void pa_sconv_s24le_from_s16re(unsigned n, const int16_t *a, uint8_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        uint32_t s = ((uint32_t) PA_INT16_SWAP(*a)) << 8;
+        WRITE24(b, s);
+        a++;
+        b += 3;
+    }
+}
+
+void pa_sconv_s24le_to_float32ne(unsigned n, const uint8_t *a, float *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        int32_t s = READ24(a) << 8;
+        *b = ((float) s) / 0x7FFFFFFF;
+        a += 3;
+        b ++;
+    }
+}
+
+void pa_sconv_s24le_from_float32ne(unsigned n, const float *a, uint8_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        int32_t s;
+        float v = *a;
+        v = PA_CLAMP_UNLIKELY(v, -1.0f, 1.0f);
+        s = (int32_t) lrint((double) v * (double) 0x7FFFFFFF);
+        WRITE24(b, ((uint32_t) s) >> 8);
+        a++;
+        b+=3;
+    }
+}
+
+void pa_sconv_s24le_to_float32re(unsigned n, const uint8_t *a, float *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        int32_t s = READ24(a) << 8;
+        float k = ((float) s) / 0x7FFFFFFF;
+        *b = PA_FLOAT32_SWAP(k);
+        a += 3;
+        b ++;
+    }
+}
+
+void pa_sconv_s24le_from_float32re(unsigned n, const float *a, uint8_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        int32_t s;
+        float v = *a;
+        v = PA_FLOAT32_SWAP(v);
+        v = PA_CLAMP_UNLIKELY(v, -1.0f, 1.0f);
+        s = (int32_t) lrint((double) v * (double) 0x7FFFFFFF);
+        WRITE24(b, ((uint32_t) s) >> 8);
+        a++;
+        b+=3;
+    }
+}
diff --git a/src/pulsecore/sconv-s16le.h b/src/pulsecore/sconv-s16le.h
index 8d4c87c..b853515 100644
--- a/src/pulsecore/sconv-s16le.h
+++ b/src/pulsecore/sconv-s16le.h
@@ -34,26 +34,26 @@ void pa_sconv_s32le_from_float32ne(unsigned n, const float *a, int32_t *b);
 void pa_sconv_s32le_to_float32re(unsigned n, const int32_t *a, float *b);
 void pa_sconv_s32le_from_float32re(unsigned n, const float *a, int32_t *b);
 
+void pa_sconv_s24le_to_float32ne(unsigned n, const uint8_t *a, float *b);
+void pa_sconv_s24le_from_float32ne(unsigned n, const float *a, uint8_t *b);
+void pa_sconv_s24le_to_float32re(unsigned n, const uint8_t *a, float *b);
+void pa_sconv_s24le_from_float32re(unsigned n, const float *a, uint8_t *b);
+
 void pa_sconv_s32le_to_s16ne(unsigned n, const int32_t *a, int16_t *b);
 void pa_sconv_s32le_from_s16ne(unsigned n, const int16_t *a, int32_t *b);
 void pa_sconv_s32le_to_s16re(unsigned n, const int32_t *a, int16_t *b);
 void pa_sconv_s32le_from_s16re(unsigned n, const int16_t *a, int32_t *b);
 
+void pa_sconv_s24le_to_s16ne(unsigned n, const uint8_t *a, int16_t *b);
+void pa_sconv_s24le_from_s16ne(unsigned n, const int16_t *a, uint8_t *b);
+void pa_sconv_s24le_to_s16re(unsigned n, const uint8_t *a, int16_t *b);
+void pa_sconv_s24le_from_s16re(unsigned n, const int16_t *a, uint8_t *b);
+
 #ifndef WORDS_BIGENDIAN
 #define pa_sconv_float32be_to_s16ne pa_sconv_s16le_from_float32re
 #define pa_sconv_float32be_from_s16ne pa_sconv_s16le_to_float32re
 #define pa_sconv_float32le_to_s16ne pa_sconv_s16le_from_float32ne
 #define pa_sconv_float32le_from_s16ne pa_sconv_s16le_to_float32ne
-
-#define pa_sconv_float32be_to_s32ne pa_sconv_s32le_from_float32re
-#define pa_sconv_float32be_from_s32ne pa_sconv_s32le_to_float32re
-#define pa_sconv_float32le_to_s32ne pa_sconv_s32le_from_float32ne
-#define pa_sconv_float32le_from_s32ne pa_sconv_s32le_to_float32ne
-
-#define pa_sconv_s16be_to_s32ne pa_sconv_s32le_from_s16re
-#define pa_sconv_s16be_from_s32ne pa_sconv_s32le_to_s16re
-#define pa_sconv_s16le_to_s32ne pa_sconv_s32le_from_s16ne
-#define pa_sconv_s16le_from_s32ne pa_sconv_s32le_to_s16ne
 #endif
 
 #endif
diff --git a/src/pulsecore/sconv.c b/src/pulsecore/sconv.c
index 6c4d420..13700c1 100644
--- a/src/pulsecore/sconv.c
+++ b/src/pulsecore/sconv.c
@@ -198,6 +198,8 @@ pa_convert_func_t pa_get_convert_to_float32ne_function(pa_sample_format_t f) {
         [PA_SAMPLE_S16BE]     = (pa_convert_func_t) pa_sconv_s16be_to_float32ne,
         [PA_SAMPLE_S32LE]     = (pa_convert_func_t) pa_sconv_s32le_to_float32ne,
         [PA_SAMPLE_S32BE]     = (pa_convert_func_t) pa_sconv_s32be_to_float32ne,
+        [PA_SAMPLE_S24LE]     = (pa_convert_func_t) pa_sconv_s24le_to_float32ne,
+        [PA_SAMPLE_S24BE]     = (pa_convert_func_t) pa_sconv_s24be_to_float32ne,
         [PA_SAMPLE_FLOAT32NE] = (pa_convert_func_t) float32ne_to_float32ne,
         [PA_SAMPLE_FLOAT32RE] = (pa_convert_func_t) float32re_to_float32ne,
     };
@@ -216,6 +218,8 @@ pa_convert_func_t pa_get_convert_from_float32ne_function(pa_sample_format_t f) {
         [PA_SAMPLE_S16BE]     = (pa_convert_func_t) pa_sconv_s16be_from_float32ne,
         [PA_SAMPLE_S32LE]     = (pa_convert_func_t) pa_sconv_s32le_from_float32ne,
         [PA_SAMPLE_S32BE]     = (pa_convert_func_t) pa_sconv_s32be_from_float32ne,
+        [PA_SAMPLE_S24LE]     = (pa_convert_func_t) pa_sconv_s24le_from_float32ne,
+        [PA_SAMPLE_S24BE]     = (pa_convert_func_t) pa_sconv_s24be_from_float32ne,
         [PA_SAMPLE_FLOAT32NE] = (pa_convert_func_t) float32ne_to_float32ne,
         [PA_SAMPLE_FLOAT32RE] = (pa_convert_func_t) float32re_to_float32ne,
         [PA_SAMPLE_ALAW]      = (pa_convert_func_t) alaw_from_float32ne,
@@ -238,6 +242,8 @@ pa_convert_func_t pa_get_convert_to_s16ne_function(pa_sample_format_t f) {
         [PA_SAMPLE_FLOAT32LE] = (pa_convert_func_t) pa_sconv_float32le_to_s16ne,
         [PA_SAMPLE_S32BE]     = (pa_convert_func_t) pa_sconv_s32be_to_s16ne,
         [PA_SAMPLE_S32LE]     = (pa_convert_func_t) pa_sconv_s32le_to_s16ne,
+        [PA_SAMPLE_S24BE]     = (pa_convert_func_t) pa_sconv_s24be_to_s16ne,
+        [PA_SAMPLE_S24LE]     = (pa_convert_func_t) pa_sconv_s24le_to_s16ne,
         [PA_SAMPLE_ALAW]      = (pa_convert_func_t) alaw_to_s16ne,
         [PA_SAMPLE_ULAW]      = (pa_convert_func_t) ulaw_to_s16ne
     };
@@ -258,6 +264,8 @@ pa_convert_func_t pa_get_convert_from_s16ne_function(pa_sample_format_t f) {
         [PA_SAMPLE_FLOAT32LE] = (pa_convert_func_t) pa_sconv_float32le_from_s16ne,
         [PA_SAMPLE_S32BE]     = (pa_convert_func_t) pa_sconv_s32be_from_s16ne,
         [PA_SAMPLE_S32LE]     = (pa_convert_func_t) pa_sconv_s32le_from_s16ne,
+        [PA_SAMPLE_S24BE]     = (pa_convert_func_t) pa_sconv_s24be_from_s16ne,
+        [PA_SAMPLE_S24LE]     = (pa_convert_func_t) pa_sconv_s24le_from_s16ne,
         [PA_SAMPLE_ALAW]      = (pa_convert_func_t) alaw_from_s16ne,
         [PA_SAMPLE_ULAW]      = (pa_convert_func_t) ulaw_from_s16ne,
     };

commit 4a137637976360e94dfc304c291b3166b3c03970
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jan 16 18:39:36 2009 +0100

    Add support for 24bit samples encoded in the LSB of 32 bit words

diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index 7616ad1..04d23e0 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -234,6 +234,8 @@ static int set_format(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, pa_s
         [PA_SAMPLE_S32BE] = SND_PCM_FORMAT_S32_BE,
         [PA_SAMPLE_S24LE] = SND_PCM_FORMAT_S24_3LE,
         [PA_SAMPLE_S24BE] = SND_PCM_FORMAT_S24_3BE,
+        [PA_SAMPLE_S24_32LE] = SND_PCM_FORMAT_S24_LE,
+        [PA_SAMPLE_S24_32BE] = SND_PCM_FORMAT_S24_BE,
     };
 
     static const pa_sample_format_t try_order[] = {
@@ -241,6 +243,8 @@ static int set_format(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, pa_s
         PA_SAMPLE_FLOAT32RE,
         PA_SAMPLE_S32NE,
         PA_SAMPLE_S32RE,
+        PA_SAMPLE_S24_32NE,
+        PA_SAMPLE_S24_32RE,
         PA_SAMPLE_S24NE,
         PA_SAMPLE_S24RE,
         PA_SAMPLE_S16NE,
@@ -267,6 +271,10 @@ static int set_format(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, pa_s
         *f = PA_SAMPLE_S24LE;
     else if (*f == PA_SAMPLE_S24LE)
         *f = PA_SAMPLE_S24BE;
+    else if (*f == PA_SAMPLE_S24_32BE)
+        *f = PA_SAMPLE_S24_32LE;
+    else if (*f == PA_SAMPLE_S24_32LE)
+        *f = PA_SAMPLE_S24_32BE;
     else if (*f == PA_SAMPLE_S16BE)
         *f = PA_SAMPLE_S16LE;
     else if (*f == PA_SAMPLE_S16LE)
diff --git a/src/modules/oss/oss-util.c b/src/modules/oss/oss-util.c
index fbe930c..f8e11fd 100644
--- a/src/modules/oss/oss-util.c
+++ b/src/modules/oss/oss-util.c
@@ -166,6 +166,8 @@ int pa_oss_auto_format(int fd, pa_sample_spec *ss) {
         [PA_SAMPLE_S32BE] = AFMT_QUERY, /* not supported */
         [PA_SAMPLE_S24LE] = AFMT_QUERY, /* not supported */
         [PA_SAMPLE_S24BE] = AFMT_QUERY, /* not supported */
+        [PA_SAMPLE_S24_32LE] = AFMT_QUERY, /* not supported */
+        [PA_SAMPLE_S24_32BE] = AFMT_QUERY, /* not supported */
     };
 
     pa_assert(fd >= 0);
diff --git a/src/pulse/sample.c b/src/pulse/sample.c
index 8228a9b..a6c7734 100644
--- a/src/pulse/sample.c
+++ b/src/pulse/sample.c
@@ -49,7 +49,9 @@ size_t pa_sample_size(const pa_sample_spec *spec) {
         [PA_SAMPLE_S32LE] = 4,
         [PA_SAMPLE_S32BE] = 4,
         [PA_SAMPLE_S24LE] = 3,
-        [PA_SAMPLE_S24BE] = 3
+        [PA_SAMPLE_S24BE] = 3,
+        [PA_SAMPLE_S24_32LE] = 4,
+        [PA_SAMPLE_S24_32BE] = 4
     };
 
     pa_assert(spec);
@@ -129,6 +131,8 @@ const char *pa_sample_format_to_string(pa_sample_format_t f) {
         [PA_SAMPLE_S32BE] = "s32be",
         [PA_SAMPLE_S24LE] = "s24le",
         [PA_SAMPLE_S24BE] = "s24be",
+        [PA_SAMPLE_S24_32LE] = "s24-32le",
+        [PA_SAMPLE_S24_32BE] = "s24-32be",
     };
 
     if (f < 0 || f >= PA_SAMPLE_MAX)
@@ -198,7 +202,7 @@ pa_sample_format_t pa_parse_sample_format(const char *format) {
         return PA_SAMPLE_S32BE;
     else if (strcasecmp(format, "s32ne") == 0 || strcasecmp(format, "s32") == 0 || strcasecmp(format, "32") == 0)
         return PA_SAMPLE_S32NE;
-    else if (strcasecmp(format, "s24re") == 0)
+    else if (strcasecmp(format, "s32re") == 0)
         return PA_SAMPLE_S24RE;
     else if (strcasecmp(format, "s24le") == 0)
         return PA_SAMPLE_S24LE;
@@ -208,6 +212,14 @@ pa_sample_format_t pa_parse_sample_format(const char *format) {
         return PA_SAMPLE_S24NE;
     else if (strcasecmp(format, "s24re") == 0)
         return PA_SAMPLE_S24RE;
+    else if (strcasecmp(format, "s24-32le") == 0)
+        return PA_SAMPLE_S24LE;
+    else if (strcasecmp(format, "s24-32be") == 0)
+        return PA_SAMPLE_S24BE;
+    else if (strcasecmp(format, "s24-32ne") == 0 || strcasecmp(format, "s24-32") == 0)
+        return PA_SAMPLE_S24NE;
+    else if (strcasecmp(format, "s24-32re") == 0)
+        return PA_SAMPLE_S24RE;
 
     return -1;
 }
diff --git a/src/pulse/sample.h b/src/pulse/sample.h
index 8d09d32..3ba13db 100644
--- a/src/pulse/sample.h
+++ b/src/pulse/sample.h
@@ -51,8 +51,10 @@
  * \li PA_SAMPLE_ULAW - 8 bit mu-Law.
  * \li PA_SAMPLE_S32LE - Signed 32 bit integer PCM, little endian.
  * \li PA_SAMPLE_S32BE - Signed 32 bit integer PCM, big endian.
- * \li PA_SAMPLE_242LE - Signed 24 bit integer PCM packed, little endian.
- * \li PA_SAMPLE_242BE - Signed 24 bit integer PCM packed, big endian.
+ * \li PA_SAMPLE_S24LE - Signed 24 bit integer PCM packed, little endian.
+ * \li PA_SAMPLE_S24BE - Signed 24 bit integer PCM packed, big endian.
+ * \li PA_SAMPLE_S24_32LE - Signed 24 bit integer PCM in LSB of 32 bit words, little endian.
+ * \li PA_SAMPLE_S24_32BE - Signed 24 bit integer PCM in LSB of 32 bit words, big endian.
  *
  * The floating point sample formats have the range from -1.0 to 1.0.
  *
@@ -61,14 +63,14 @@
  *
  * \section rate_sec Sample Rates
  *
- * PulseAudio supports any sample rate between 1 Hz and 4 GHz. There is no
+ * PulseAudio supports any sample rate between 1 Hz and 192000 Hz. There is no
  * point trying to exceed the sample rate of the output device though as the
  * signal will only get downsampled, consuming CPU on the machine running the
  * server.
  *
  * \section chan_sec Channels
  *
- * PulseAudio supports up to 16 individiual channels. The order of the
+ * PulseAudio supports up to 32 individiual channels. The order of the
  * channels is up to the application, but they must be continous. To map
  * channels to speakers, see \ref channelmap.
  *
@@ -138,10 +140,10 @@ typedef enum pa_sample_format {
     /**< Signed 16 Bit PCM, big endian */
 
     PA_SAMPLE_FLOAT32LE,
-    /**< 32 Bit IEEE floating point, little endian, range -1 to 1 */
+    /**< 32 Bit IEEE floating point, little endian (PC), range -1.0 to 1.0 */
 
     PA_SAMPLE_FLOAT32BE,
-    /**< 32 Bit IEEE floating point, big endian, range -1 to 1 */
+    /**< 32 Bit IEEE floating point, big endian, range -1.0 to 1.0 */
 
     PA_SAMPLE_S32LE,
     /**< Signed 32 Bit PCM, little endian (PC) */
@@ -150,10 +152,16 @@ typedef enum pa_sample_format {
     /**< Signed 32 Bit PCM, big endian */
 
     PA_SAMPLE_S24LE,
-    /**< Signed 24 Bit PCM packed, little endian (PC) */
+    /**< Signed 24 Bit PCM packed, little endian (PC). \since 0.9.15 */
 
     PA_SAMPLE_S24BE,
-    /**< Signed 24 Bit PCM packed, big endian */
+    /**< Signed 24 Bit PCM packed, big endian. \since 0.9.15 */
+
+    PA_SAMPLE_S24_32LE,
+    /**< Signed 24 Bit PCM in LSB of 32 Bit words, little endian (PC). \since 0.9.15 */
+
+    PA_SAMPLE_S24_32BE,
+    /**< Signed 24 Bit PCM in LSB of 32 Bit words, big endian. \since 0.9.15 */
 
     PA_SAMPLE_MAX,
     /**< Upper limit of valid sample types */
@@ -169,16 +177,21 @@ typedef enum pa_sample_format {
 #define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32BE
 /** Signed 32 Bit PCM, native endian */
 #define PA_SAMPLE_S32NE PA_SAMPLE_S32BE
-/** Signed 24 Bit PCM packed, native endian */
+/** Signed 24 Bit PCM packed, native endian. \since 0.9.15 */
 #define PA_SAMPLE_S24NE PA_SAMPLE_S24BE
+/** Signed 24 Bit PCM in LSB of 32 Bit words, native endian. \since 0.9.15 */
+#define PA_SAMPLE_S24_32NE PA_SAMPLE_S24_32BE
+
 /** Signed 16 Bit PCM reverse endian */
 #define PA_SAMPLE_S16RE PA_SAMPLE_S16LE
 /** 32 Bit IEEE floating point, reverse endian */
 #define PA_SAMPLE_FLOAT32RE PA_SAMPLE_FLOAT32LE
 /** Signed 32 Bit PCM, reverse endian */
 #define PA_SAMPLE_S32RE PA_SAMPLE_S32LE
-/** Signed 24 Bit PCM, packed reverse endian */
-#define PA_SAMPLE_S24RE PA_SAMPLE_242LE
+/** Signed 24 Bit PCM, packed reverse endian. \since 0.9.15 */
+#define PA_SAMPLE_S24RE PA_SAMPLE_S24LE
+/** Signed 24 Bit PCM, in LSB of 32 Bit words, reverse endian. \since 0.9.15 */
+#define PA_SAMPLE_S24_32RE PA_SAMPLE_S24_32LE
 #else
 /** Signed 16 Bit PCM, native endian */
 #define PA_SAMPLE_S16NE PA_SAMPLE_S16LE
@@ -186,16 +199,21 @@ typedef enum pa_sample_format {
 #define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32LE
 /** Signed 32 Bit PCM, native endian */
 #define PA_SAMPLE_S32NE PA_SAMPLE_S32LE
-/** Signed 24 Bit PCM packed, native endian */
+/** Signed 24 Bit PCM packed, native endian. \since 0.9.15 */
 #define PA_SAMPLE_S24NE PA_SAMPLE_S24LE
+/** Signed 24 Bit PCM in LSB of 32 Bit words, native endian. \since 0.9.15 */
+#define PA_SAMPLE_S24_32NE PA_SAMPLE_S24_32LE
+
 /** Signed 16 Bit PCM, reverse endian */
 #define PA_SAMPLE_S16RE PA_SAMPLE_S16BE
 /** 32 Bit IEEE floating point, reverse endian */
 #define PA_SAMPLE_FLOAT32RE PA_SAMPLE_FLOAT32BE
 /** Signed 32 Bit PCM, reverse endian */
 #define PA_SAMPLE_S32RE PA_SAMPLE_S32BE
-/** Signed 24 Bit PCM packed, reverse endian */
+/** Signed 24 Bit PCM, packed reverse endian. \since 0.9.15 */
 #define PA_SAMPLE_S24RE PA_SAMPLE_S24BE
+/** Signed 24 Bit PCM, in LSB of 32 Bit words, reverse endian. \since 0.9.15 */
+#define PA_SAMPLE_S24_32RE PA_SAMPLE_S24_32BE
 #endif
 
 /** A Shortcut for PA_SAMPLE_FLOAT32NE */
@@ -214,6 +232,8 @@ typedef enum pa_sample_format {
 #define PA_SAMPLE_S32BE PA_SAMPLE_S32BE
 #define PA_SAMPLE_S24LE PA_SAMPLE_S24LE
 #define PA_SAMPLE_S24BE PA_SAMPLE_S24BE
+#define PA_SAMPLE_S24_32LE PA_SAMPLE_S24_32LE
+#define PA_SAMPLE_S24_32BE PA_SAMPLE_S24_32BE
 /** \endcond */
 
 /** A sample format and attribute specification */
diff --git a/src/pulse/stream.c b/src/pulse/stream.c
index 2a958e0..5a29bd6 100644
--- a/src/pulse/stream.c
+++ b/src/pulse/stream.c
@@ -88,6 +88,7 @@ pa_stream *pa_stream_new_with_proplist(
     PA_CHECK_VALIDITY_RETURN_NULL(c, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID);
     PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 12 || (ss->format != PA_SAMPLE_S32LE && ss->format != PA_SAMPLE_S32BE), PA_ERR_NOTSUPPORTED);
     PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15 || (ss->format != PA_SAMPLE_S24LE && ss->format != PA_SAMPLE_S24BE), PA_ERR_NOTSUPPORTED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15 || (ss->format != PA_SAMPLE_S24_32LE && ss->format != PA_SAMPLE_S24_32BE), PA_ERR_NOTSUPPORTED);
     PA_CHECK_VALIDITY_RETURN_NULL(c, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID);
     PA_CHECK_VALIDITY_RETURN_NULL(c, name || (p && pa_proplist_contains(p, PA_PROP_MEDIA_NAME)), PA_ERR_INVALID);
 
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index b1ef64f..5412267 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -2642,9 +2642,9 @@ static void fixup_sample_spec(pa_native_connection *c, pa_sample_spec *fixed, co
     }
 
     if (c->version < 15) {
-        if (fixed->format == PA_SAMPLE_S24LE)
+        if (fixed->format == PA_SAMPLE_S24LE || fixed->format == PA_SAMPLE_S24_32LE)
             fixed->format = PA_SAMPLE_FLOAT32LE;
-        if (fixed->format == PA_SAMPLE_S24BE)
+        if (fixed->format == PA_SAMPLE_S24BE || fixed->format == PA_SAMPLE_S24_32BE)
             fixed->format = PA_SAMPLE_FLOAT32BE;
     }
 }
diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c
index 67c3758..6b3836e 100644
--- a/src/pulsecore/resampler.c
+++ b/src/pulsecore/resampler.c
@@ -258,9 +258,11 @@ pa_resampler* pa_resampler_new(
             if (a->format == PA_SAMPLE_S32NE || a->format == PA_SAMPLE_S32RE ||
                 a->format == PA_SAMPLE_FLOAT32NE || a->format == PA_SAMPLE_FLOAT32RE ||
                 a->format == PA_SAMPLE_S24NE || a->format == PA_SAMPLE_S24RE ||
+                a->format == PA_SAMPLE_S24_32NE || a->format == PA_SAMPLE_S24_32RE ||
                 b->format == PA_SAMPLE_S32NE || b->format == PA_SAMPLE_S32RE ||
                 b->format == PA_SAMPLE_FLOAT32NE || b->format == PA_SAMPLE_FLOAT32RE ||
-                b->format == PA_SAMPLE_S24NE || b->format == PA_SAMPLE_S24RE)
+                b->format == PA_SAMPLE_S24NE || b->format == PA_SAMPLE_S24RE ||
+                b->format == PA_SAMPLE_S24_32NE || b->format == PA_SAMPLE_S24_32RE)
                 r->work_format = PA_SAMPLE_FLOAT32NE;
             else
                 r->work_format = PA_SAMPLE_S16NE;
diff --git a/src/pulsecore/sample-util.c b/src/pulsecore/sample-util.c
index 533181b..cf7b4d5 100644
--- a/src/pulsecore/sample-util.c
+++ b/src/pulsecore/sample-util.c
@@ -85,6 +85,8 @@ static uint8_t silence_byte(pa_sample_format_t format) {
         case PA_SAMPLE_FLOAT32BE:
         case PA_SAMPLE_S24LE:
         case PA_SAMPLE_S24BE:
+        case PA_SAMPLE_S24_32LE:
+        case PA_SAMPLE_S24_32BE:
             return 0;
         case PA_SAMPLE_ALAW:
             return 0xd5;
@@ -414,6 +416,78 @@ size_t pa_mix(
             break;
         }
 
+        case PA_SAMPLE_S24_32NE: {
+            unsigned channel = 0;
+
+            calc_linear_integer_stream_volumes(streams, nstreams, volume, spec);
+
+            while (data < end) {
+                int64_t sum = 0;
+                unsigned i;
+
+                for (i = 0; i < nstreams; i++) {
+                    pa_mix_info *m = streams + i;
+                    int32_t cv = m->linear[channel].i;
+                    int64_t v;
+
+                    if (PA_UNLIKELY(cv <= 0))
+                        continue;
+
+                    v = (int32_t) (*((uint32_t*)m->ptr) << 8);
+                    v = (v * cv) / 0x10000;
+                    sum += v;
+
+                    m->ptr = (uint8_t*) m->ptr + sizeof(int32_t);
+                }
+
+                sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL);
+                *((uint32_t*) data) = ((uint32_t) (int32_t) sum) >> 8;
+
+                data = (uint8_t*) data + sizeof(uint32_t);
+
+                if (PA_UNLIKELY(++channel >= spec->channels))
+                    channel = 0;
+            }
+
+            break;
+        }
+
+        case PA_SAMPLE_S24_32RE: {
+            unsigned channel = 0;
+
+            calc_linear_integer_stream_volumes(streams, nstreams, volume, spec);
+
+            while (data < end) {
+                int64_t sum = 0;
+                unsigned i;
+
+                for (i = 0; i < nstreams; i++) {
+                    pa_mix_info *m = streams + i;
+                    int32_t cv = m->linear[channel].i;
+                    int64_t v;
+
+                    if (PA_UNLIKELY(cv <= 0))
+                        continue;
+
+                    v = (int32_t) (PA_UINT32_SWAP(*((uint32_t*) m->ptr)) << 8);
+                    v = (v * cv) / 0x10000;
+                    sum += v;
+
+                    m->ptr = (uint8_t*) m->ptr + 3;
+                }
+
+                sum = PA_CLAMP_UNLIKELY(sum, -0x80000000LL, 0x7FFFFFFFLL);
+                *((uint32_t*) data) = PA_INT32_SWAP(((uint32_t) (int32_t) sum) >> 8);
+
+                data = (uint8_t*) data + sizeof(uint32_t);
+
+                if (PA_UNLIKELY(++channel >= spec->channels))
+                    channel = 0;
+            }
+
+            break;
+        }
+
         case PA_SAMPLE_U8: {
             unsigned channel = 0;
 
@@ -765,6 +839,52 @@ void pa_volume_memchunk(
             break;
         }
 
+        case PA_SAMPLE_S24_32NE: {
+            uint32_t *d, *e;
+            unsigned channel;
+            int32_t linear[PA_CHANNELS_MAX];
+
+            calc_linear_integer_volume(linear, volume);
+
+            e = (uint32_t*) ptr + c->length/sizeof(uint32_t);
+
+            for (channel = 0, d = ptr; d < e; d++) {
+                int64_t t;
+
+                t = (int64_t) ((int32_t) (*d << 8));
+                t = (t * linear[channel]) / 0x10000;
+                t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
+                *d = ((uint32_t) ((int32_t) t)) >> 8;
+
+                if (PA_UNLIKELY(++channel >= spec->channels))
+                    channel = 0;
+            }
+            break;
+        }
+
+        case PA_SAMPLE_S24_32RE: {
+            uint32_t *d, *e;
+            unsigned channel;
+            int32_t linear[PA_CHANNELS_MAX];
+
+            calc_linear_integer_volume(linear, volume);
+
+            e = (uint32_t*) ptr + c->length/sizeof(uint32_t);
+
+            for (channel = 0, d = ptr; d < e; d++) {
+                int64_t t;
+
+                t = (int64_t) ((int32_t) (PA_UINT32_SWAP(*d) << 8));
+                t = (t * linear[channel]) / 0x10000;
+                t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
+                *d = PA_UINT32_SWAP(((uint32_t) ((int32_t) t)) >> 8);
+
+                if (PA_UNLIKELY(++channel >= spec->channels))
+                    channel = 0;
+            }
+            break;
+        }
+
         case PA_SAMPLE_U8: {
             uint8_t *d, *e;
             unsigned channel;
diff --git a/src/pulsecore/sconv-s16be.c b/src/pulsecore/sconv-s16be.c
index d6137d3..0d5146a 100644
--- a/src/pulsecore/sconv-s16be.c
+++ b/src/pulsecore/sconv-s16be.c
@@ -27,9 +27,13 @@
 
 #define INT16_FROM PA_INT16_FROM_BE
 #define INT16_TO PA_INT16_TO_BE
+#define UINT16_FROM PA_UINT16_FROM_BE
+#define UINT16_TO PA_UINT16_TO_BE
 
 #define INT32_FROM PA_INT32_FROM_BE
 #define INT32_TO PA_INT32_TO_BE
+#define UINT32_FROM PA_UINT32_FROM_BE
+#define UINT32_TO PA_UINT32_TO_BE
 
 #define READ24 PA_READ24BE
 #define WRITE24 PA_WRITE24BE
@@ -49,6 +53,11 @@
 #define pa_sconv_s24le_to_float32re pa_sconv_s24be_to_float32re
 #define pa_sconv_s24le_from_float32re pa_sconv_s24be_from_float32re
 
+#define pa_sconv_s24_32le_to_float32ne pa_sconv_s24_32be_to_float32ne
+#define pa_sconv_s24_32le_from_float32ne pa_sconv_s24_32be_from_float32ne
+#define pa_sconv_s24_32le_to_float32re pa_sconv_s24_32be_to_float32re
+#define pa_sconv_s24_32le_from_float32re pa_sconv_s24_32be_from_float32re
+
 #define pa_sconv_s32le_to_s16ne pa_sconv_s32be_to_s16ne
 #define pa_sconv_s32le_from_s16ne pa_sconv_s32be_from_s16ne
 #define pa_sconv_s32le_to_s16re pa_sconv_s32be_to_s16re
@@ -59,6 +68,11 @@
 #define pa_sconv_s24le_to_s16re pa_sconv_s24be_to_s16re
 #define pa_sconv_s24le_from_s16re pa_sconv_s24be_from_s16re
 
+#define pa_sconv_s24_32le_to_s16ne pa_sconv_s24_32be_to_s16ne
+#define pa_sconv_s24_32le_from_s16ne pa_sconv_s24_32be_from_s16ne
+#define pa_sconv_s24_32le_to_s16re pa_sconv_s24_32be_to_s16re
+#define pa_sconv_s24_32le_from_s16re pa_sconv_s24_32be_from_s16re
+
 #ifdef WORDS_BIGENDIAN
 #define SWAP_WORDS 0
 #else
diff --git a/src/pulsecore/sconv-s16be.h b/src/pulsecore/sconv-s16be.h
index 1b88fd4..0263333 100644
--- a/src/pulsecore/sconv-s16be.h
+++ b/src/pulsecore/sconv-s16be.h
@@ -39,6 +39,11 @@ void pa_sconv_s24be_from_float32ne(unsigned n, const float *a, uint8_t *b);
 void pa_sconv_s24be_to_float32re(unsigned n, const uint8_t *a, float *b);
 void pa_sconv_s24be_from_float32re(unsigned n, const float *a, uint8_t *b);
 
+void pa_sconv_s24_32be_to_float32ne(unsigned n, const uint8_t *a, float *b);
+void pa_sconv_s24_32be_from_float32ne(unsigned n, const float *a, uint8_t *b);
+void pa_sconv_s24_32be_to_float32re(unsigned n, const uint8_t *a, float *b);
+void pa_sconv_s24_32be_from_float32re(unsigned n, const float *a, uint8_t *b);
+
 void pa_sconv_s32be_to_s16ne(unsigned n, const int32_t *a, int16_t *b);
 void pa_sconv_s32be_from_s16ne(unsigned n, const int16_t *a, int32_t *b);
 void pa_sconv_s32be_to_s16re(unsigned n, const int32_t *a, int16_t *b);
@@ -49,6 +54,11 @@ void pa_sconv_s24be_from_s16ne(unsigned n, const int16_t *a, uint8_t *b);
 void pa_sconv_s24be_to_s16re(unsigned n, const uint8_t *a, int16_t *b);
 void pa_sconv_s24be_from_s16re(unsigned n, const int16_t *a, uint8_t *b);
 
+void pa_sconv_s24_32be_to_s16ne(unsigned n, const uint8_t *a, int16_t *b);
+void pa_sconv_s24_32be_from_s16ne(unsigned n, const int16_t *a, uint8_t *b);
+void pa_sconv_s24_32be_to_s16re(unsigned n, const uint8_t *a, int16_t *b);
+void pa_sconv_s24_32be_from_s16re(unsigned n, const int16_t *a, uint8_t *b);
+
 #ifdef WORDS_BIGENDIAN
 #define pa_sconv_float32be_to_s16ne pa_sconv_s16be_from_float32ne
 #define pa_sconv_float32be_from_s16ne pa_sconv_s16be_to_float32ne
diff --git a/src/pulsecore/sconv-s16le.c b/src/pulsecore/sconv-s16le.c
index 37ebc98..79f0391 100644
--- a/src/pulsecore/sconv-s16le.c
+++ b/src/pulsecore/sconv-s16le.c
@@ -41,23 +41,34 @@
 #ifndef INT16_FROM
 #define INT16_FROM PA_INT16_FROM_LE
 #endif
+#ifndef UINT16_FROM
+#define UINT16_FROM PA_UINT16_FROM_LE
+#endif
 
 #ifndef INT16_TO
 #define INT16_TO PA_INT16_TO_LE
 #endif
+#ifndef UINT16_TO
+#define UINT16_TO PA_UINT16_TO_LE
+#endif
 
 #ifndef INT32_FROM
 #define INT32_FROM PA_INT32_FROM_LE
 #endif
+#ifndef UINT32_FROM
+#define UINT32_FROM PA_UINT32_FROM_LE
+#endif
 
 #ifndef INT32_TO
 #define INT32_TO PA_INT32_TO_LE
 #endif
+#ifndef UINT32_TO
+#define UINT32_TO PA_UINT32_TO_LE
+#endif
 
 #ifndef READ24
 #define READ24 PA_READ24LE
 #endif
-
 #ifndef WRITE24
 #define WRITE24 PA_WRITE24LE
 #endif
@@ -353,3 +364,105 @@ void pa_sconv_s24le_from_float32re(unsigned n, const float *a, uint8_t *b) {
         b+=3;
     }
 }
+
+void pa_sconv_s24_32le_to_s16ne(unsigned n, const uint32_t *a, int16_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        *b = (int16_t) ((int32_t) (UINT32_FROM(*a) << 8) >> 16);
+        a++;
+        b++;
+    }
+}
+
+void pa_sconv_s24_32le_to_s16re(unsigned n, const uint32_t *a, int16_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        int16_t s = (int16_t) ((int32_t) (UINT32_FROM(*a) << 8) >> 16);
+        *b = PA_INT16_SWAP(s);
+        a++;
+        b++;
+    }
+}
+
+void pa_sconv_s24_32le_from_s16ne(unsigned n, const int16_t *a, uint32_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        *b = UINT32_TO(((uint32_t) ((int32_t) *a << 16)) >> 8);
+        a++;
+        b++;
+    }
+}
+
+void pa_sconv_s24_32le_from_s16re(unsigned n, const int16_t *a, uint32_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        uint32_t s = ((uint32_t) ((int32_t) PA_INT16_SWAP(*a) << 16)) >> 8;
+        *b = UINT32_TO(s);
+        a++;
+        b++;
+    }
+}
+
+void pa_sconv_s24_32le_to_float32ne(unsigned n, const uint32_t *a, float *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        int32_t s = (int16_t) ((int32_t) (UINT32_FROM(*a) << 8));
+        *b = ((float) s) / 0x7FFFFFFF;
+        a ++;
+        b ++;
+    }
+}
+
+void pa_sconv_s24_32le_to_float32re(unsigned n, const uint32_t *a, float *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        int32_t s = (int16_t) ((int32_t) (UINT32_FROM(*a) << 8));
+        float k = ((float) s) / 0x7FFFFFFF;
+        *b = PA_FLOAT32_SWAP(k);
+        a ++;
+        b ++;
+    }
+}
+
+void pa_sconv_s24_32le_from_float32ne(unsigned n, const float *a, uint32_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        int32_t s;
+        float v = *a;
+        v = PA_CLAMP_UNLIKELY(v, -1.0f, 1.0f);
+        s = (int32_t) lrint((double) v * (double) 0x7FFFFFFF);
+        *b = UINT32_TO(((uint32_t) s) >> 8);
+        a++;
+        b++;
+    }
+}
+
+void pa_sconv_s24_32le_from_float32re(unsigned n, const float *a, uint32_t *b) {
+    pa_assert(a);
+    pa_assert(b);
+
+    for (; n > 0; n--) {
+        int32_t s;
+        float v = *a;
+        v = PA_FLOAT32_SWAP(v);
+        v = PA_CLAMP_UNLIKELY(v, -1.0f, 1.0f);
+        s = (int32_t) lrint((double) v * (double) 0x7FFFFFFF);
+        *b = UINT32_TO(((uint32_t) s) >> 8);
+        a++;
+        b++;
+    }
+}
diff --git a/src/pulsecore/sconv-s16le.h b/src/pulsecore/sconv-s16le.h
index b853515..f7b0064 100644
--- a/src/pulsecore/sconv-s16le.h
+++ b/src/pulsecore/sconv-s16le.h
@@ -39,6 +39,11 @@ void pa_sconv_s24le_from_float32ne(unsigned n, const float *a, uint8_t *b);
 void pa_sconv_s24le_to_float32re(unsigned n, const uint8_t *a, float *b);
 void pa_sconv_s24le_from_float32re(unsigned n, const float *a, uint8_t *b);
 
+void pa_sconv_s24_32le_to_float32ne(unsigned n, const uint32_t *a, float *b);
+void pa_sconv_s24_32le_from_float32ne(unsigned n, const float *a, uint32_t *b);
+void pa_sconv_s24_32le_to_float32re(unsigned n, const uint32_t *a, float *b);
+void pa_sconv_s24_32le_from_float32re(unsigned n, const float *a, uint32_t *b);
+
 void pa_sconv_s32le_to_s16ne(unsigned n, const int32_t *a, int16_t *b);
 void pa_sconv_s32le_from_s16ne(unsigned n, const int16_t *a, int32_t *b);
 void pa_sconv_s32le_to_s16re(unsigned n, const int32_t *a, int16_t *b);
@@ -49,6 +54,11 @@ void pa_sconv_s24le_from_s16ne(unsigned n, const int16_t *a, uint8_t *b);
 void pa_sconv_s24le_to_s16re(unsigned n, const uint8_t *a, int16_t *b);
 void pa_sconv_s24le_from_s16re(unsigned n, const int16_t *a, uint8_t *b);
 
+void pa_sconv_s24_32le_to_s16ne(unsigned n, const uint32_t *a, int16_t *b);
+void pa_sconv_s24_32le_from_s16ne(unsigned n, const int16_t *a, uint32_t *b);
+void pa_sconv_s24_32le_to_s16re(unsigned n, const uint32_t *a, int16_t *b);
+void pa_sconv_s24_32le_from_s16re(unsigned n, const int16_t *a, uint32_t *b);
+
 #ifndef WORDS_BIGENDIAN
 #define pa_sconv_float32be_to_s16ne pa_sconv_s16le_from_float32re
 #define pa_sconv_float32be_from_s16ne pa_sconv_s16le_to_float32re
diff --git a/src/pulsecore/sconv.c b/src/pulsecore/sconv.c
index 13700c1..fcd0309 100644
--- a/src/pulsecore/sconv.c
+++ b/src/pulsecore/sconv.c
@@ -200,6 +200,8 @@ pa_convert_func_t pa_get_convert_to_float32ne_function(pa_sample_format_t f) {
         [PA_SAMPLE_S32BE]     = (pa_convert_func_t) pa_sconv_s32be_to_float32ne,
         [PA_SAMPLE_S24LE]     = (pa_convert_func_t) pa_sconv_s24le_to_float32ne,
         [PA_SAMPLE_S24BE]     = (pa_convert_func_t) pa_sconv_s24be_to_float32ne,
+        [PA_SAMPLE_S24_32LE]  = (pa_convert_func_t) pa_sconv_s24_32le_to_float32ne,
+        [PA_SAMPLE_S24_32BE]  = (pa_convert_func_t) pa_sconv_s24_32be_to_float32ne,
         [PA_SAMPLE_FLOAT32NE] = (pa_convert_func_t) float32ne_to_float32ne,
         [PA_SAMPLE_FLOAT32RE] = (pa_convert_func_t) float32re_to_float32ne,
     };
@@ -220,6 +222,8 @@ pa_convert_func_t pa_get_convert_from_float32ne_function(pa_sample_format_t f) {
         [PA_SAMPLE_S32BE]     = (pa_convert_func_t) pa_sconv_s32be_from_float32ne,
         [PA_SAMPLE_S24LE]     = (pa_convert_func_t) pa_sconv_s24le_from_float32ne,
         [PA_SAMPLE_S24BE]     = (pa_convert_func_t) pa_sconv_s24be_from_float32ne,
+        [PA_SAMPLE_S24_32LE]  = (pa_convert_func_t) pa_sconv_s24_32le_from_float32ne,
+        [PA_SAMPLE_S24_32BE]  = (pa_convert_func_t) pa_sconv_s24_32be_from_float32ne,
         [PA_SAMPLE_FLOAT32NE] = (pa_convert_func_t) float32ne_to_float32ne,
         [PA_SAMPLE_FLOAT32RE] = (pa_convert_func_t) float32re_to_float32ne,
         [PA_SAMPLE_ALAW]      = (pa_convert_func_t) alaw_from_float32ne,
@@ -244,6 +248,8 @@ pa_convert_func_t pa_get_convert_to_s16ne_function(pa_sample_format_t f) {
         [PA_SAMPLE_S32LE]     = (pa_convert_func_t) pa_sconv_s32le_to_s16ne,
         [PA_SAMPLE_S24BE]     = (pa_convert_func_t) pa_sconv_s24be_to_s16ne,
         [PA_SAMPLE_S24LE]     = (pa_convert_func_t) pa_sconv_s24le_to_s16ne,
+        [PA_SAMPLE_S24_32BE]  = (pa_convert_func_t) pa_sconv_s24_32be_to_s16ne,
+        [PA_SAMPLE_S24_32LE]  = (pa_convert_func_t) pa_sconv_s24_32le_to_s16ne,
         [PA_SAMPLE_ALAW]      = (pa_convert_func_t) alaw_to_s16ne,
         [PA_SAMPLE_ULAW]      = (pa_convert_func_t) ulaw_to_s16ne
     };
@@ -266,6 +272,8 @@ pa_convert_func_t pa_get_convert_from_s16ne_function(pa_sample_format_t f) {
         [PA_SAMPLE_S32LE]     = (pa_convert_func_t) pa_sconv_s32le_from_s16ne,
         [PA_SAMPLE_S24BE]     = (pa_convert_func_t) pa_sconv_s24be_from_s16ne,
         [PA_SAMPLE_S24LE]     = (pa_convert_func_t) pa_sconv_s24le_from_s16ne,
+        [PA_SAMPLE_S24_32BE]  = (pa_convert_func_t) pa_sconv_s24_32be_from_s16ne,
+        [PA_SAMPLE_S24_32LE]  = (pa_convert_func_t) pa_sconv_s24_32le_from_s16ne,
         [PA_SAMPLE_ALAW]      = (pa_convert_func_t) alaw_from_s16ne,
         [PA_SAMPLE_ULAW]      = (pa_convert_func_t) ulaw_from_s16ne,
     };

commit 5793f93350dd8f29b7bc97eb1ad38873e4ecebde
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jan 16 19:57:58 2009 +0100

    make use of PR_SET_TIMERSLACK

diff --git a/src/daemon/main.c b/src/daemon/main.c
index 08f65e6..b01689f 100644
--- a/src/daemon/main.c
+++ b/src/daemon/main.c
@@ -885,6 +885,8 @@ int main(int argc, char *argv[]) {
     else
         pa_log_info(_("Dude, your kernel stinks! The chef's recommendation today is Linux with high-resolution timers enabled!"));
 
+    pa_rtclock_hrtimer_enable();
+
 #ifdef SIGRTMIN
     /* Valgrind uses SIGRTMAX. To easy debugging we don't use it here */
     pa_rtsig_configure(SIGRTMIN, SIGRTMAX-1);
diff --git a/src/pulsecore/rtclock.c b/src/pulsecore/rtclock.c
index f33de83..5fc6da2 100644
--- a/src/pulsecore/rtclock.c
+++ b/src/pulsecore/rtclock.c
@@ -27,9 +27,12 @@
 #include <stddef.h>
 #include <time.h>
 #include <sys/time.h>
+#include <sys/prctl.h>
+#include <errno.h>
 
 #include <pulse/timeval.h>
 #include <pulsecore/macro.h>
+#include <pulsecore/core-error.h>
 
 #include "rtclock.h"
 
@@ -89,6 +92,29 @@ pa_bool_t pa_rtclock_hrtimer(void) {
 #endif
 }
 
+void pa_rtclock_hrtimer_enable(void) {
+#ifdef PR_SET_TIMERSLACK
+    int slack_ns;
+
+    if ((slack_ns = prctl(PR_GET_TIMERSLACK, 0, 0, 0, 0)) < 0) {
+        pa_log_info("PR_GET_TIMERSLACK/PR_SET_TIMERSLACK not supported.");
+        return;
+    }
+
+    pa_log_debug("Timer slack set to %i us.", slack_ns/1000);
+
+    slack_ns = 500000000;
+
+    pa_log_debug("Setting timer slack to %i us.", slack_ns/1000);
+
+    if (prctl(PR_SET_TIMERSLACK, slack_ns, 0, 0, 0) < 0) {
+        pa_log_warn("PR_SET_TIMERSLACK failed: %s", pa_cstrerror(errno));
+        return;
+    }
+
+#endif
+}
+
 pa_usec_t pa_rtclock_usec(void) {
     struct timeval tv;
 
diff --git a/src/pulsecore/rtclock.h b/src/pulsecore/rtclock.h
index aa2cdac..281461d 100644
--- a/src/pulsecore/rtclock.h
+++ b/src/pulsecore/rtclock.h
@@ -35,6 +35,7 @@ pa_usec_t pa_rtclock_usec(void);
 
 pa_usec_t pa_rtclock_age(const struct timeval *tv);
 pa_bool_t pa_rtclock_hrtimer(void);
+void pa_rtclock_hrtimer_enable(void);
 
 /* timer with a resolution better than this are considered high-resolution */
 #define PA_HRTIMER_THRESHOLD_USEC 10

commit b2b2eb1ee7e30de651b9fb2cbc04329e21b1eb68
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jan 16 22:01:45 2009 +0100

    remvoe a bit of duplicate code

diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index 04d23e0..5fabf64 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -622,72 +622,45 @@ snd_pcm_t *pa_alsa_open_by_device_id(
 
     i = 0;
     for (;;) {
-        pa_sample_spec try_ss;
-        pa_bool_t reformat;
 
         if ((direction > 0) == channel_map_superset(&device_table[i].map, map)) {
+            pa_sample_spec try_ss;
+
             pa_log_debug("Checking for %s (%s)", device_table[i].name, device_table[i].alsa_name);
 
             d = pa_sprintf_malloc("%s:%s", device_table[i].alsa_name, dev_id);
 
-            reformat = FALSE;
-            for (;;) {
-                pa_log_debug("Trying %s %s SND_PCM_NO_AUTO_FORMAT ...", d, reformat ? "without" : "with");
-
-                /* We don't pass SND_PCM_NONBLOCK here, since alsa-lib <=
-                 * 1.0.17a would then ignore the SND_PCM_NO_xxx
-                 * flags. Instead we enable nonblock mode afterwards via
-                 * snd_pcm_nonblock(). Also see
-                 * http://mailman.alsa-project.org/pipermail/alsa-devel/2008-August/010258.html */
-
-                if ((err = snd_pcm_open(&pcm_handle, d, mode,
-                                        /* SND_PCM_NONBLOCK| */
-                                        SND_PCM_NO_AUTO_RESAMPLE|
-                                        SND_PCM_NO_AUTO_CHANNELS|
-                                        (reformat ? 0 : SND_PCM_NO_AUTO_FORMAT))) < 0) {
-                    pa_log_info("Couldn't open PCM device %s: %s", d, snd_strerror(err));
-                    break;
-                }
-
-                try_ss.channels = device_table[i].map.channels;
-                try_ss.rate = ss->rate;
-                try_ss.format = ss->format;
-
-                if ((err = pa_alsa_set_hw_params(pcm_handle, &try_ss, nfrags, period_size, tsched_size, use_mmap, use_tsched, TRUE)) < 0) {
-
-                    if (!reformat) {
-                        reformat = TRUE;
-                        snd_pcm_close(pcm_handle);
-                        continue;
-                    }
-
-                    if (!pa_startswith(d, "plug:") && !pa_startswith(d, "plughw:")) {
-                        char *t;
-
-                        t = pa_sprintf_malloc("plug:%s", d);
-                        pa_xfree(d);
-                        d = t;
+            try_ss.channels = device_table[i].map.channels;
+            try_ss.rate = ss->rate;
+            try_ss.format = ss->format;
+
+            pcm_handle = pa_alsa_open_by_device_string(
+                    d,
+                    dev,
+                    &try_ss,
+                    map,
+                    mode,
+                    nfrags,
+                    period_size,
+                    tsched_size,
+                    use_mmap,
+                    use_tsched,
+                    TRUE);
 
-                        reformat = FALSE;
-
-                        snd_pcm_close(pcm_handle);
-                        continue;
-                    }
+            pa_xfree(d);
 
-                    pa_log_info("PCM device %s refused our hw parameters: %s", d, snd_strerror(err));
-                    snd_pcm_close(pcm_handle);
-                    break;
-                }
+            if (pcm_handle) {
 
                 *ss = try_ss;
                 *map = device_table[i].map;
                 pa_assert(map->channels == ss->channels);
-                *dev = d;
+
                 if (config_description)
                     *config_description = device_table[i].description;
                 if (config_name)
                     *config_name = device_table[i].name;
 
+
                 return pcm_handle;
             }
 
@@ -742,7 +715,7 @@ snd_pcm_t *pa_alsa_open_by_device_id(
 
     d = pa_sprintf_malloc("hw:%s", dev_id);
     pa_log_debug("Trying %s as last resort...", d);
-    pcm_handle = pa_alsa_open_by_device_string(d, dev, ss, map, mode, nfrags, period_size, tsched_size, use_mmap, use_tsched);
+    pcm_handle = pa_alsa_open_by_device_string(d, dev, ss, map, mode, nfrags, period_size, tsched_size, use_mmap, use_tsched, FALSE);
     pa_xfree(d);
 
     if (pcm_handle) {
@@ -763,7 +736,8 @@ snd_pcm_t *pa_alsa_open_by_device_string(
         snd_pcm_uframes_t *period_size,
         snd_pcm_uframes_t tsched_size,
         pa_bool_t *use_mmap,
-        pa_bool_t *use_tsched) {
+        pa_bool_t *use_tsched,
+        pa_bool_t require_exact_channel_number) {
 
     int err;
     char *d;
@@ -798,7 +772,7 @@ snd_pcm_t *pa_alsa_open_by_device_string(
             return NULL;
         }
 
-        if ((err = pa_alsa_set_hw_params(pcm_handle, ss, nfrags, period_size, tsched_size, use_mmap, use_tsched, FALSE)) < 0) {
+        if ((err = pa_alsa_set_hw_params(pcm_handle, ss, nfrags, period_size, tsched_size, use_mmap, use_tsched, require_exact_channel_number)) < 0) {
 
             if (!reformat) {
                 reformat = TRUE;
diff --git a/src/modules/alsa/alsa-util.h b/src/modules/alsa/alsa-util.h
index ce5f0eb..51de285 100644
--- a/src/modules/alsa/alsa-util.h
+++ b/src/modules/alsa/alsa-util.h
@@ -78,7 +78,8 @@ snd_pcm_t *pa_alsa_open_by_device_string(
         snd_pcm_uframes_t *period_size,
         snd_pcm_uframes_t tsched_size,
         pa_bool_t *use_mmap,
-        pa_bool_t *use_tsched);
+        pa_bool_t *use_tsched,
+        pa_bool_t require_exact_channel_number);
 
 int pa_alsa_calc_mixer_map(snd_mixer_elem_t *elem, const pa_channel_map *channel_map, snd_mixer_selem_channel_id_t mixer_map[], pa_bool_t playback);
 
diff --git a/src/modules/alsa/module-alsa-sink.c b/src/modules/alsa/module-alsa-sink.c
index dfa2055..977d7e4 100644
--- a/src/modules/alsa/module-alsa-sink.c
+++ b/src/modules/alsa/module-alsa-sink.c
@@ -1351,7 +1351,7 @@ int pa__init(pa_module*m) {
                       &ss, &map,
                       SND_PCM_STREAM_PLAYBACK,
                       &nfrags, &period_frames, tsched_frames,
-                      &b, &d)))
+                      &b, &d, FALSE)))
             goto fail;
 
     }
diff --git a/src/modules/alsa/module-alsa-source.c b/src/modules/alsa/module-alsa-source.c
index f89b6e2..5ad7601 100644
--- a/src/modules/alsa/module-alsa-source.c
+++ b/src/modules/alsa/module-alsa-source.c
@@ -1179,7 +1179,7 @@ int pa__init(pa_module*m) {
                       &ss, &map,
                       SND_PCM_STREAM_CAPTURE,
                       &nfrags, &period_frames, tsched_frames,
-                      &b, &d)))
+                      &b, &d, FALSE)))
             goto fail;
     }
 

commit b4d80462bf66530ead1e4877f848c63f7693bd58
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jan 16 23:33:15 2009 +0100

    add card profile prober

diff --git a/src/Makefile.am b/src/Makefile.am
index 99ed7b2..839cce0 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -920,7 +920,8 @@ if HAVE_ALSA
 modlibexec_LTLIBRARIES += \
 		libalsa-util.la \
 		module-alsa-sink.la \
-		module-alsa-source.la
+		module-alsa-source.la \
+		module-alsa-card.la
 endif
 
 if HAVE_SOLARIS
@@ -1039,6 +1040,7 @@ SYMDEF_FILES = \
 		modules/oss/module-oss-symdef.h \
 		modules/alsa/module-alsa-sink-symdef.h \
 		modules/alsa/module-alsa-source-symdef.h \
+		modules/alsa/module-alsa-card-symdef.h \
 		modules/module-solaris-symdef.h \
 		modules/module-waveout-symdef.h \
 		modules/module-detect-symdef.h \
@@ -1257,6 +1259,11 @@ module_alsa_source_la_LDFLAGS = $(MODULE_LDFLAGS)
 module_alsa_source_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libalsa-util.la libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
 module_alsa_source_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
 
+module_alsa_card_la_SOURCES = modules/alsa/module-alsa-card.c
+module_alsa_card_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_alsa_card_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libalsa-util.la libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
+module_alsa_card_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
+
 # Solaris
 
 module_solaris_la_SOURCES = modules/module-solaris.c
diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index 5fabf64..37b12dc 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -314,8 +314,8 @@ int pa_alsa_set_hw_params(
         pa_bool_t require_exact_channel_number) {
 
     int ret = -1;
-    snd_pcm_uframes_t _period_size = *period_size;
-    unsigned int _periods = *periods;
+    snd_pcm_uframes_t _period_size = period_size ? *period_size : 0;
+    unsigned int _periods = periods ? *periods : 0;
     snd_pcm_uframes_t buffer_size;
     unsigned int r = ss->rate;
     unsigned int c = ss->channels;
@@ -327,8 +327,6 @@ int pa_alsa_set_hw_params(
 
     pa_assert(pcm_handle);
     pa_assert(ss);
-    pa_assert(periods);
-    pa_assert(period_size);
 
     snd_pcm_hw_params_alloca(&hwparams);
 
@@ -361,10 +359,6 @@ int pa_alsa_set_hw_params(
     if ((ret = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &r, NULL)) < 0)
         goto finish;
 
-    /* Adjust the buffer sizes, if we didn't get the rate we were asking for */
-    _period_size = (snd_pcm_uframes_t) (((uint64_t) _period_size * r) / ss->rate);
-    tsched_size = (snd_pcm_uframes_t) (((uint64_t) tsched_size * r) / ss->rate);
-
     if (require_exact_channel_number) {
         if ((ret = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, c)) < 0)
             goto finish;
@@ -373,50 +367,56 @@ int pa_alsa_set_hw_params(
             goto finish;
     }
 
-    if (_use_tsched) {
-        _period_size = tsched_size;
-        _periods = 1;
+    if ((ret = snd_pcm_hw_params_set_periods_integer(pcm_handle, hwparams)) < 0)
+        goto finish;
 
-        pa_assert_se(snd_pcm_hw_params_get_buffer_size_max(hwparams, &buffer_size) == 0);
-        pa_log_debug("Maximum hw buffer size is %u ms", (unsigned) buffer_size * 1000 / r);
-    }
+    if (_period_size && tsched_size && _periods) {
+        /* Adjust the buffer sizes, if we didn't get the rate we were asking for */
+        _period_size = (snd_pcm_uframes_t) (((uint64_t) _period_size * r) / ss->rate);
+        tsched_size = (snd_pcm_uframes_t) (((uint64_t) tsched_size * r) / ss->rate);
 
-    buffer_size = _periods * _period_size;
+        if (_use_tsched) {
+            _period_size = tsched_size;
+            _periods = 1;
 
-    if ((ret = snd_pcm_hw_params_set_periods_integer(pcm_handle, hwparams)) < 0)
-        goto finish;
+            pa_assert_se(snd_pcm_hw_params_get_buffer_size_max(hwparams, &buffer_size) == 0);
+            pa_log_debug("Maximum hw buffer size is %u ms", (unsigned) buffer_size * 1000 / r);
+        }
+
+        buffer_size = _periods * _period_size;
 
-    if (_periods > 0) {
+        if (_periods > 0) {
 
-        /* First we pass 0 as direction to get exactly what we asked
-         * for. That this is necessary is presumably a bug in ALSA */
+            /* First we pass 0 as direction to get exactly what we asked
+             * for. That this is necessary is presumably a bug in ALSA */
 
-        dir = 0;
-        if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir)) < 0) {
-            dir = 1;
+            dir = 0;
             if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir)) < 0) {
-                dir = -1;
-                if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir)) < 0)
-                    goto finish;
+                dir = 1;
+                if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir)) < 0) {
+                    dir = -1;
+                    if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir)) < 0)
+                        goto finish;
+                }
             }
         }
-    }
 
-    if (_period_size > 0)
-        if ((ret = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &buffer_size)) < 0)
-            goto finish;
+        if (_period_size > 0)
+            if ((ret = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &buffer_size)) < 0)
+                goto finish;
+    }
 
     if  ((ret = snd_pcm_hw_params(pcm_handle, hwparams)) < 0)
         goto finish;
 
     if (ss->rate != r)
-        pa_log_warn("Device %s doesn't support %u Hz, changed to %u Hz.", snd_pcm_name(pcm_handle), ss->rate, r);
+        pa_log_info("Device %s doesn't support %u Hz, changed to %u Hz.", snd_pcm_name(pcm_handle), ss->rate, r);
 
     if (ss->channels != c)
-        pa_log_warn("Device %s doesn't support %u channels, changed to %u.", snd_pcm_name(pcm_handle), ss->channels, c);
+        pa_log_info("Device %s doesn't support %u channels, changed to %u.", snd_pcm_name(pcm_handle), ss->channels, c);
 
     if (ss->format != f)
-        pa_log_warn("Device %s doesn't support sample format %s, changed to %s.", snd_pcm_name(pcm_handle), pa_sample_format_to_string(ss->format), pa_sample_format_to_string(f));
+        pa_log_info("Device %s doesn't support sample format %s, changed to %s.", snd_pcm_name(pcm_handle), pa_sample_format_to_string(ss->format), pa_sample_format_to_string(f));
 
     if ((ret = snd_pcm_prepare(pcm_handle)) < 0)
         goto finish;
@@ -434,8 +434,11 @@ int pa_alsa_set_hw_params(
     pa_assert(_periods > 0);
     pa_assert(_period_size > 0);
 
-    *periods = _periods;
-    *period_size = _period_size;
+    if (periods)
+        *periods = _periods;
+
+    if (period_size)
+        *period_size = _period_size;
 
     if (use_mmap)
         *use_mmap = _use_mmap;
@@ -488,14 +491,7 @@ int pa_alsa_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min) {
     return 0;
 }
 
-struct device_info {
-    pa_channel_map map;
-    const char *alsa_name;
-    const char *description;
-    const char *name;
-};
-
-static const struct device_info device_table[] = {
+static const struct pa_alsa_profile_info device_table[] = {
     {{ 1, { PA_CHANNEL_POSITION_MONO }},
      "hw",
      "Analog Mono",
@@ -602,7 +598,6 @@ snd_pcm_t *pa_alsa_open_by_device_id(
 
     int i;
     int direction = 1;
-    int err;
     char *d;
     snd_pcm_t *pcm_handle;
 
@@ -745,11 +740,8 @@ snd_pcm_t *pa_alsa_open_by_device_string(
     pa_bool_t reformat = FALSE;
 
     pa_assert(device);
-    pa_assert(dev);
     pa_assert(ss);
     pa_assert(map);
-    pa_assert(nfrags);
-    pa_assert(period_size);
 
     d = pa_xstrdup(device);
 
@@ -767,7 +759,7 @@ snd_pcm_t *pa_alsa_open_by_device_string(
                                 SND_PCM_NO_AUTO_RESAMPLE|
                                 SND_PCM_NO_AUTO_CHANNELS|
                                 (reformat ? 0 : SND_PCM_NO_AUTO_FORMAT))) < 0) {
-            pa_log("Error opening PCM device %s: %s", d, snd_strerror(err));
+            pa_log_info("Error opening PCM device %s: %s", d, snd_strerror(err));
             pa_xfree(d);
             return NULL;
         }
@@ -796,13 +788,14 @@ snd_pcm_t *pa_alsa_open_by_device_string(
                 continue;
             }
 
-            pa_log("Failed to set hardware parameters on %s: %s", d, snd_strerror(err));
+            pa_log_info("Failed to set hardware parameters on %s: %s", d, snd_strerror(err));
             pa_xfree(d);
             snd_pcm_close(pcm_handle);
             return NULL;
         }
 
-        *dev = d;
+        if (dev)
+            *dev = d;
 
         if (ss->channels != map->channels)
             pa_channel_map_init_extend(map, ss->channels, PA_CHANNEL_MAP_ALSA);
@@ -811,6 +804,93 @@ snd_pcm_t *pa_alsa_open_by_device_string(
     }
 }
 
+int pa_alsa_probe_profiles(
+        const char *dev_id,
+        const pa_sample_spec *ss,
+        void (*cb)(const pa_alsa_profile_info *sink, const pa_alsa_profile_info *source, void *userdata),
+        void *userdata) {
+
+    const pa_alsa_profile_info *i;
+
+    pa_assert(dev_id);
+    pa_assert(ss);
+    pa_assert(cb);
+
+    /* We try each combination of playback/capture. We also try to
+     * open only for capture resp. only for sink. Don't get confused
+     * by the trailing entry in device_table we use for this! */
+
+    for (i = device_table; i < device_table + PA_ELEMENTSOF(device_table); i++) {
+        const pa_alsa_profile_info *j;
+        snd_pcm_t *pcm_i = NULL;
+
+        if (i->alsa_name) {
+            char *id;
+            pa_sample_spec try_ss;
+            pa_channel_map try_map;
+
+            pa_log_debug("Checking for playback on %s (%s)", i->name, i->alsa_name);
+            id = pa_sprintf_malloc("%s:%s", i->alsa_name, dev_id);
+
+            try_ss = *ss;
+            try_ss.channels = i->map.channels;
+            try_map = i->map;
+
+            pcm_i = pa_alsa_open_by_device_string(
+                    id, NULL,
+                    &try_ss, &try_map,
+                    SND_PCM_STREAM_PLAYBACK,
+                    NULL, NULL, 0, NULL, NULL,
+                    TRUE);
+
+            pa_xfree(id);
+
+            if (!pcm_i)
+                continue;
+        }
+
+        for (j = device_table; j < device_table + PA_ELEMENTSOF(device_table); j++) {
+            snd_pcm_t *pcm_j = NULL;
+
+            if (j->alsa_name) {
+                char *jd;
+                pa_sample_spec try_ss;
+                pa_channel_map try_map;
+
+                pa_log_debug("Checking for capture on %s (%s)", j->name, j->alsa_name);
+                jd = pa_sprintf_malloc("%s:%s", j->alsa_name, dev_id);
+
+                try_ss = *ss;
+                try_ss.channels = j->map.channels;
+                try_map = j->map;
+
+                pcm_j = pa_alsa_open_by_device_string(
+                        jd, NULL,
+                        &try_ss, &try_map,
+                        SND_PCM_STREAM_CAPTURE,
+                        NULL, NULL, 0, NULL, NULL,
+                        TRUE);
+
+                pa_xfree(jd);
+
+                if (!pcm_j)
+                    continue;
+            }
+
+            if (pcm_j)
+                snd_pcm_close(pcm_j);
+
+            cb(i->alsa_name ? i : NULL,
+               j->alsa_name ? j : NULL, userdata);
+        }
+
+        if (pcm_i)
+            snd_pcm_close(pcm_i);
+    }
+
+    return TRUE;
+}
+
 int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev) {
     int err;
 
diff --git a/src/modules/alsa/alsa-util.h b/src/modules/alsa/alsa-util.h
index 51de285..86b76b7 100644
--- a/src/modules/alsa/alsa-util.h
+++ b/src/modules/alsa/alsa-util.h
@@ -81,6 +81,19 @@ snd_pcm_t *pa_alsa_open_by_device_string(
         pa_bool_t *use_tsched,
         pa_bool_t require_exact_channel_number);
 
+typedef struct pa_alsa_profile_info {
+    pa_channel_map map;
+    const char *alsa_name;
+    const char *description;
+    const char *name;
+} pa_alsa_profile_info;
+
+int pa_alsa_probe_profiles(
+        const char *dev_id,
+        const pa_sample_spec *ss,
+        void (*cb)(const pa_alsa_profile_info *sink, const pa_alsa_profile_info *source, void *userdata),
+        void *userdata);
+
 int pa_alsa_calc_mixer_map(snd_mixer_elem_t *elem, const pa_channel_map *channel_map, snd_mixer_selem_channel_id_t mixer_map[], pa_bool_t playback);
 
 void pa_alsa_dump(snd_pcm_t *pcm);
diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
new file mode 100644
index 0000000..64559c4
--- /dev/null
+++ b/src/modules/alsa/module-alsa-card.c
@@ -0,0 +1,56 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 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 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 "alsa-util.h"
+#include "module-alsa-card-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("ALSA Card");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+
+static void enumerate_cb(
+        const pa_alsa_profile_info *sink,
+        const pa_alsa_profile_info *source,
+        void *userdata) {
+
+    if (sink && source)
+        pa_log("Found Output %s + Input %s", sink->description, source->description);
+    else if (sink)
+        pa_log("Found Output %s", sink->description);
+    else if (source)
+        pa_log("Found Input %s", source->description);
+
+}
+
+int pa__init(pa_module*m) {
+    pa_alsa_redirect_errors_inc();
+    pa_alsa_probe_profiles("1", &m->core->default_sample_spec, enumerate_cb, m);
+    return 0;
+}
+
+void pa__done(pa_module*m) {
+    pa_alsa_redirect_errors_dec();
+}

commit 4a66837b83d85ef9cad61f1d0e9fb776b0236368
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sat Jan 17 01:57:17 2009 +0100

    add pa_strbuf_isempty

diff --git a/src/pulsecore/strbuf.c b/src/pulsecore/strbuf.c
index 540faef..8b95278 100644
--- a/src/pulsecore/strbuf.c
+++ b/src/pulsecore/strbuf.c
@@ -180,3 +180,9 @@ size_t pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) {
             size *= 2;
     }
 }
+
+pa_bool_t pa_strbuf_isempty(pa_strbuf *sb) {
+    pa_assert(sb);
+
+    return sb->length <= 0;
+}
diff --git a/src/pulsecore/strbuf.h b/src/pulsecore/strbuf.h
index ac68d7b..1d2a588 100644
--- a/src/pulsecore/strbuf.h
+++ b/src/pulsecore/strbuf.h
@@ -23,6 +23,7 @@
 ***/
 
 #include <pulse/gccmacro.h>
+#include <pulsecore/macro.h>
 
 typedef struct pa_strbuf pa_strbuf;
 
@@ -35,4 +36,6 @@ size_t pa_strbuf_printf(pa_strbuf *sb, const char *format, ...)  PA_GCC_PRINTF_A
 void pa_strbuf_puts(pa_strbuf *sb, const char *t);
 void pa_strbuf_putsn(pa_strbuf *sb, const char *t, size_t m);
 
+pa_bool_t pa_strbuf_isempty(pa_strbuf *sb);
+
 #endif

commit a45f971e43ed22f73c681bb9962aa9717534d0a2
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sat Jan 17 01:59:37 2009 +0100

    add pa_proplist_to_string_sep()

diff --git a/src/map-file b/src/map-file
index 0247933..06ee4e1 100644
--- a/src/map-file
+++ b/src/map-file
@@ -159,6 +159,7 @@ pa_proplist_setf;
 pa_proplist_sets;
 pa_proplist_size;
 pa_proplist_to_string;
+pa_proplist_to_string_sep;
 pa_proplist_unset;
 pa_proplist_unset_many;
 pa_proplist_update;
diff --git a/src/pulse/proplist.c b/src/pulse/proplist.c
index 60a92d4..282fe5c 100644
--- a/src/pulse/proplist.c
+++ b/src/pulse/proplist.c
@@ -259,21 +259,24 @@ const char *pa_proplist_iterate(pa_proplist *p, void **state) {
     return prop->key;
 }
 
-char *pa_proplist_to_string(pa_proplist *p) {
+char *pa_proplist_to_string_sep(pa_proplist *p, const char *sep) {
     const char *key;
     void *state = NULL;
     pa_strbuf *buf;
 
     pa_assert(p);
+    pa_assert(sep);
 
     buf = pa_strbuf_new();
 
     while ((key = pa_proplist_iterate(p, &state))) {
-
         const char *v;
 
+        if (!pa_strbuf_isempty(buf))
+            pa_strbuf_puts(buf, sep);
+
         if ((v = pa_proplist_gets(p, key)))
-            pa_strbuf_printf(buf, "%s = \"%s\"\n", key, v);
+            pa_strbuf_printf(buf, "%s = \"%s\"", key, v);
         else {
             const void *value;
             size_t nbytes;
@@ -283,7 +286,7 @@ char *pa_proplist_to_string(pa_proplist *p) {
             c = pa_xmalloc(nbytes*2+1);
             pa_hexstr((const uint8_t*) value, nbytes, c, nbytes*2+1);
 
-            pa_strbuf_printf(buf, "%s = hex:%s\n", key, c);
+            pa_strbuf_printf(buf, "%s = hex:%s", key, c);
             pa_xfree(c);
         }
     }
@@ -291,6 +294,16 @@ char *pa_proplist_to_string(pa_proplist *p) {
     return pa_strbuf_tostring_free(buf);
 }
 
+char *pa_proplist_to_string(pa_proplist *p) {
+    char *s, *t;
+
+    s = pa_proplist_to_string_sep(p, "\n");
+    t = pa_sprintf_malloc("%s\n", s);
+    pa_xfree(s);
+
+    return t;
+}
+
 /* Remove all whitepsapce from the beginning and the end of *s. *s may
  * be modified. (from conf-parser.c) */
 #define WHITESPACE " \t\n"
diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h
index 77f0399..8f44df2 100644
--- a/src/pulse/proplist.h
+++ b/src/pulse/proplist.h
@@ -213,12 +213,19 @@ int pa_proplist_unset_many(pa_proplist *p, const char * const keys[]);
  * have any particular order. \since 0.9.11 */
 const char *pa_proplist_iterate(pa_proplist *p, void **state);
 
-/** Format the property list nicely as a human readable string. Call pa_xfree() on the result. \since
- * 0.9.11 */
+/** Format the property list nicely as a human readable string. This
+ * works very much like pa_proplist_to_string_sep() and uses a newline
+ * as seperator and appends one final one. Call pa_xfree() on the
+ * result. \since 0.9.11 */
 char *pa_proplist_to_string(pa_proplist *p);
 
-/** Allocate a new property list and assign key/value from a human readable string. \since
+/** Format the property list nicely as a human readable string and
+ * choose the seperator. Call pa_xfree() on the result. \since
  * 0.9.15 */
+char *pa_proplist_to_string_sep(pa_proplist *p, const char *sep);
+
+/** Allocate a new property list and assign key/value from a human
+ * 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

commit f03a7e43db145273408414d32fbae49800ab7a29
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sat Jan 17 02:00:57 2009 +0100

    Split up pa_alsa_init_proplist into two seperate functions for the card and snd_pcm_t specific parts

diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index 37b12dc..a4301f4 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -1205,7 +1205,26 @@ void pa_alsa_redirect_errors_dec(void) {
         snd_lib_error_set_handler(NULL);
 }
 
-void pa_alsa_init_proplist(pa_proplist *p, snd_pcm_info_t *pcm_info) {
+void pa_alsa_init_proplist_card(pa_proplist *p, int card) {
+    char *cn, *lcn;
+
+    pa_assert(p);
+    pa_assert(card >= 0);
+
+    pa_proplist_setf(p, "alsa.card", "%i", card);
+
+    if (snd_card_get_name(card, &cn) >= 0) {
+        pa_proplist_sets(p, "alsa.card_name", cn);
+        free(cn);
+    }
+
+    if (snd_card_get_longname(card, &lcn) >= 0) {
+        pa_proplist_sets(p, "alsa.long_card_name", lcn);
+        free(lcn);
+    }
+}
+
+void pa_alsa_init_proplist_pcm(pa_proplist *p, snd_pcm_info_t *pcm_info) {
 
     static const char * const alsa_class_table[SND_PCM_CLASS_LAST+1] = {
         [SND_PCM_CLASS_GENERIC] = "generic",
@@ -1226,8 +1245,7 @@ void pa_alsa_init_proplist(pa_proplist *p, snd_pcm_info_t *pcm_info) {
 
     snd_pcm_class_t class;
     snd_pcm_subclass_t subclass;
-    const char *n, *id, *sdn;
-    char *cn = NULL, *lcn = NULL;
+    const char *n, *id, *sdn, *cn;
     int card;
 
     pa_assert(p);
@@ -1260,13 +1278,8 @@ void pa_alsa_init_proplist(pa_proplist *p, snd_pcm_info_t *pcm_info) {
     pa_proplist_setf(p, "alsa.device", "%u", snd_pcm_info_get_device(pcm_info));
 
     if ((card = snd_pcm_info_get_card(pcm_info)) >= 0) {
-        pa_proplist_setf(p, "alsa.card", "%i", card);
-
-        if (snd_card_get_name(card, &cn) >= 0)
-            pa_proplist_sets(p, "alsa.card_name", cn);
-
-        if (snd_card_get_longname(card, &lcn) >= 0)
-            pa_proplist_sets(p, "alsa.long_card_name", lcn);
+        pa_alsa_init_proplist_card(p, card);
+        cn = pa_proplist_gets(p, "alsa.card_name");
     }
 
     if (cn && n)
@@ -1275,9 +1288,6 @@ void pa_alsa_init_proplist(pa_proplist *p, snd_pcm_info_t *pcm_info) {
         pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, cn);
     else if (n)
         pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, n);
-
-    free(lcn);
-    free(cn);
 }
 
 int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents) {
diff --git a/src/modules/alsa/alsa-util.h b/src/modules/alsa/alsa-util.h
index 86b76b7..f58ec8e 100644
--- a/src/modules/alsa/alsa-util.h
+++ b/src/modules/alsa/alsa-util.h
@@ -102,7 +102,8 @@ void pa_alsa_dump_status(snd_pcm_t *pcm);
 void pa_alsa_redirect_errors_inc(void);
 void pa_alsa_redirect_errors_dec(void);
 
-void pa_alsa_init_proplist(pa_proplist *p, snd_pcm_info_t *pcm_info);
+void pa_alsa_init_proplist_pcm(pa_proplist *p, snd_pcm_info_t *pcm_info);
+void pa_alsa_init_proplist_card(pa_proplist *p, int card);
 
 int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents);
 
diff --git a/src/modules/alsa/module-alsa-sink.c b/src/modules/alsa/module-alsa-sink.c
index 977d7e4..62ce89c 100644
--- a/src/modules/alsa/module-alsa-sink.c
+++ b/src/modules/alsa/module-alsa-sink.c
@@ -1308,10 +1308,9 @@ int pa__init(pa_module*m) {
         use_tsched = FALSE;
     }
 
-    u = pa_xnew0(struct userdata, 1);
+    m->userdata = u = pa_xnew0(struct userdata, 1);
     u->core = m->core;
     u->module = m;
-    m->userdata = u;
     u->use_mmap = use_mmap;
     u->use_tsched = use_tsched;
     u->first = TRUE;
@@ -1439,7 +1438,7 @@ int pa__init(pa_module*m) {
     pa_sink_new_data_set_sample_spec(&data, &ss);
     pa_sink_new_data_set_channel_map(&data, &map);
 
-    pa_alsa_init_proplist(data.proplist, pcm_info);
+    pa_alsa_init_proplist_pcm(data.proplist, pcm_info);
     pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name);
     pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (period_frames * frame_size * nfrags));
     pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size));
@@ -1633,10 +1632,8 @@ void pa__done(pa_module*m) {
 
     pa_assert(m);
 
-    if (!(u = m->userdata)) {
-        pa_alsa_redirect_errors_dec();
-        return;
-    }
+    if (!(u = m->userdata))
+        goto finish;
 
     if (u->sink)
         pa_sink_unlink(u->sink);
@@ -1677,7 +1674,8 @@ void pa__done(pa_module*m) {
     pa_xfree(u->device_name);
     pa_xfree(u);
 
-    snd_config_update_free_global();
+finish:
 
+    snd_config_update_free_global();
     pa_alsa_redirect_errors_dec();
 }
diff --git a/src/modules/alsa/module-alsa-source.c b/src/modules/alsa/module-alsa-source.c
index 5ad7601..7ca305f 100644
--- a/src/modules/alsa/module-alsa-source.c
+++ b/src/modules/alsa/module-alsa-source.c
@@ -1142,10 +1142,9 @@ int pa__init(pa_module*m) {
         use_tsched = FALSE;
     }
 
-    u = pa_xnew0(struct userdata, 1);
+    m->userdata = u = pa_xnew0(struct userdata, 1);
     u->core = m->core;
     u->module = m;
-    m->userdata = u;
     u->use_mmap = use_mmap;
     u->use_tsched = use_tsched;
     u->rtpoll = pa_rtpoll_new();
@@ -1266,7 +1265,7 @@ int pa__init(pa_module*m) {
     pa_source_new_data_set_sample_spec(&data, &ss);
     pa_source_new_data_set_channel_map(&data, &map);
 
-    pa_alsa_init_proplist(data.proplist, pcm_info);
+    pa_alsa_init_proplist_pcm(data.proplist, pcm_info);
     pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name);
     pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (period_frames * frame_size * nfrags));
     pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size));
@@ -1454,10 +1453,8 @@ void pa__done(pa_module*m) {
 
     pa_assert(m);
 
-    if (!(u = m->userdata)) {
-        pa_alsa_redirect_errors_dec();
-        return;
-    }
+    if (!(u = m->userdata))
+        goto finish;
 
     if (u->source)
         pa_source_unlink(u->source);
@@ -1495,6 +1492,7 @@ void pa__done(pa_module*m) {
     pa_xfree(u->device_name);
     pa_xfree(u);
 
+finish:
     snd_config_update_free_global();
     pa_alsa_redirect_errors_dec();
 }

commit c560aea4c9668c751a96460a52f7a981eef60572
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sat Jan 17 02:01:37 2009 +0100

    Don't enumerate invalid profile

diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index a4301f4..feaeb31 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -880,8 +880,9 @@ int pa_alsa_probe_profiles(
             if (pcm_j)
                 snd_pcm_close(pcm_j);
 
-            cb(i->alsa_name ? i : NULL,
-               j->alsa_name ? j : NULL, userdata);
+            if (i->alsa_name || j->alsa_name)
+                cb(i->alsa_name ? i : NULL,
+                   j->alsa_name ? j : NULL, userdata);
         }
 
         if (pcm_i)

commit c06e43d7ff5eff33af416a35ef4ca962a0cc0a2e
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sat Jan 17 02:03:35 2009 +0100

    actually create pa_card object in module-alsa-card

diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
index 64559c4..2cc8a15 100644
--- a/src/modules/alsa/module-alsa-card.c
+++ b/src/modules/alsa/module-alsa-card.c
@@ -23,6 +23,10 @@
 #include <config.h>
 #endif
 
+#include <pulse/xmalloc.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+
 #include "alsa-util.h"
 #include "module-alsa-card-symdef.h"
 
@@ -30,27 +34,163 @@ PA_MODULE_AUTHOR("Lennart Poettering");
 PA_MODULE_DESCRIPTION("ALSA Card");
 PA_MODULE_VERSION(PACKAGE_VERSION);
 PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE(
+        "name=<name for the sink/source> "
+        "device_id=<ALSA card index> "
+        "format=<sample format> "
+        "rate=<sample rate> "
+        "fragments=<number of fragments> "
+        "fragment_size=<fragment size> "
+        "mmap=<enable memory mapping?> "
+        "tsched=<enable system timer based scheduling mode?> "
+        "tsched_buffer_size=<buffer size when using timer based scheduling> "
+        "tsched_buffer_watermark=<lower fill watermark> "
+        "profile=<profile name>");
+
+static const char* const valid_modargs[] = {
+    "sink_name",
+    "device",
+    "device_id",
+    "format",
+    "rate",
+    "channels",
+    "channel_map",
+    "fragments",
+    "fragment_size",
+    "mmap",
+    "tsched",
+    "tsched_buffer_size",
+    "tsched_buffer_watermark",
+    NULL
+};
+
+#define DEFAULT_DEVICE_ID "0"
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+
+    char *device_id;
+
+    pa_card *card;
+};
+
+struct profile_data {
+    const pa_alsa_profile_info *sink, *source;
+};
 
 static void enumerate_cb(
         const pa_alsa_profile_info *sink,
         const pa_alsa_profile_info *source,
         void *userdata) {
 
-    if (sink && source)
-        pa_log("Found Output %s + Input %s", sink->description, source->description);
-    else if (sink)
-        pa_log("Found Output %s", sink->description);
-    else if (source)
-        pa_log("Found Input %s", source->description);
+    pa_hashmap *profiles = (pa_hashmap *) userdata;
+    char *t, *n;
+    pa_card_profile *p;
+    struct profile_data *d;
+
+    if (sink && source) {
+        n = pa_sprintf_malloc("%s+%s", sink->name, source->name);
+        t = pa_sprintf_malloc("Output %s + Input %s", sink->description, source->description);
+    } else if (sink) {
+        n = pa_xstrdup(sink->name);
+        t = pa_sprintf_malloc("Output %s", sink->description);
+    } else {
+        pa_assert(source);
+        n = pa_xstrdup(source->name);
+        t = pa_sprintf_malloc("Input %s", source->description);
+    }
 
+    pa_log_info("Found output profile '%s'", t);
+
+    p = pa_card_profile_new(n, t, sizeof(struct profile_data));
+
+    pa_xfree(t);
+    pa_xfree(n);
+
+    p->n_sinks = !!sink;
+    p->n_sources = !!source;
+
+    if (sink)
+        p->max_sink_channels = sink->map.channels;
+    if (source)
+        p->max_source_channels = source->map.channels;
+
+    d = PA_CARD_PROFILE_DATA(p);
+
+    d->sink = sink;
+    d->source = source;
+
+    pa_hashmap_put(profiles, p->name, p);
 }
 
 int pa__init(pa_module*m) {
+    pa_card_new_data data;
+    pa_modargs *ma;
+    int alsa_card_index;
+    struct userdata *u;
+
     pa_alsa_redirect_errors_inc();
-    pa_alsa_probe_profiles("1", &m->core->default_sample_spec, enumerate_cb, m);
+
+    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->device_id = pa_xstrdup(pa_modargs_get_value(ma, "device_id", DEFAULT_DEVICE_ID));
+
+    if ((alsa_card_index = snd_card_get_index(u->device_id)) < 0) {
+        pa_log("Card '%s' doesn't exist: %s", u->device_id, snd_strerror(alsa_card_index));
+        goto fail;
+    }
+
+    pa_card_new_data_init(&data);
+    data.driver = __FILE__;
+    data.module = m;
+    pa_alsa_init_proplist_card(data.proplist, alsa_card_index);
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_id);
+    pa_card_new_data_set_name(&data, pa_modargs_get_value(ma, "name", u->device_id));
+
+    data.profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+    if (pa_alsa_probe_profiles(u->device_id, &m->core->default_sample_spec, enumerate_cb, data.profiles) < 0) {
+        pa_card_new_data_done(&data);
+        goto fail;
+    }
+
+    u->card = pa_card_new(m->core, &data);
+    pa_card_new_data_done(&data);
+
     return 0;
+
+fail:
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    pa__done(m);
+    return -1;
 }
 
 void pa__done(pa_module*m) {
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        goto finish;
+
+    if (u->card)
+        pa_card_free(u->card);
+
+    pa_xfree(u->device_id);
+    pa_xfree(u);
+
+finish:
+    snd_config_update_free_global();
     pa_alsa_redirect_errors_dec();
 }
diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c
index 99c0cc5..ec4a50c 100644
--- a/src/pulsecore/card.c
+++ b/src/pulsecore/card.c
@@ -36,13 +36,14 @@
 
 #include "card.h"
 
-pa_card_profile *pa_card_profile_new(const char *name) {
+pa_card_profile *pa_card_profile_new(const char *name, const char *description, size_t extra) {
     pa_card_profile *c;
 
     pa_assert(name);
 
-    c = pa_xnew0(pa_card_profile, 1);
+    c = pa_xmalloc(PA_ALIGN(sizeof(pa_card_profile)) + extra);
     c->name = pa_xstrdup(name);
+    c->description = pa_xstrdup(description);
 
     return c;
 }
@@ -51,6 +52,7 @@ void pa_card_profile_free(pa_card_profile *c) {
     pa_assert(c);
 
     pa_xfree(c->name);
+    pa_xfree(c->description);
     pa_xfree(c);
 }
 
@@ -122,7 +124,9 @@ pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) {
 
     c->profiles = data->profiles;
     data->profiles = NULL;
-    c->active_profile = data->active_profile;
+    if (!(c->active_profile = data->active_profile))
+        if (c->profiles)
+            c->active_profile = pa_hashmap_first(c->profiles);
     data->active_profile = NULL;
 
     c->userdata = NULL;
@@ -189,6 +193,9 @@ int pa_card_set_profile(pa_card *c, const char *name) {
     if (!(profile = pa_hashmap_get(c->profiles, name)))
         return -1;
 
+    if (c->active_profile == profile)
+        return 0;
+
     if (c->set_profile(c, profile) < 0)
         return -1;
 
diff --git a/src/pulsecore/card.h b/src/pulsecore/card.h
index e32e880..b4e68b0 100644
--- a/src/pulsecore/card.h
+++ b/src/pulsecore/card.h
@@ -31,17 +31,20 @@ typedef struct pa_card pa_card;
 
 typedef struct pa_card_profile {
     char *name;
+    char *description;
 
-    pa_bool_t optical_sink:1;
-    pa_bool_t optical_source:1;
-
+    /* We probably want to have different properties later on here */
     unsigned n_sinks;
     unsigned n_sources;
 
     unsigned max_sink_channels;
     unsigned max_source_channels;
+
+    /* .. followed by some implementation specific data */
 } pa_card_profile;
 
+#define PA_CARD_PROFILE_DATA(d) ((void*) ((uint8_t*) d + PA_ALIGN(sizeof(pa_card_profile))))
+
 struct pa_card {
     uint32_t index;
     pa_core *core;
@@ -65,6 +68,7 @@ struct pa_card {
 
 typedef struct pa_card_new_data {
     char *name;
+    char *description;
 
     pa_proplist *proplist;
     const char *driver;
@@ -76,7 +80,7 @@ typedef struct pa_card_new_data {
     pa_bool_t namereg_fail:1;
 } pa_card_new_data;
 
-pa_card_profile *pa_card_profile_new(const char *name);
+pa_card_profile *pa_card_profile_new(const char *name, const char *description, size_t extra);
 void pa_card_profile_free(pa_card_profile *c);
 
 pa_card_new_data *pa_card_new_data_init(pa_card_new_data *data);

commit f8ba3a9b078297db8dfcd6c4576567712162e0b8
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sat Jan 17 02:03:59 2009 +0100

    dump profiles when listing cards

diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index 0f4a273..c9a82cb 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -123,9 +123,22 @@ char *pa_card_list_to_string(pa_core *c) {
         if (card->module)
             pa_strbuf_printf(s, "\towner module: %u\n", card->module->index);
 
-        t = pa_proplist_to_string(card->proplist);
-        pa_strbuf_printf(s, "\tproperties:\n%s", t);
+        t = pa_proplist_to_string_sep(card->proplist, "\n\t\t");
+        pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t);
         pa_xfree(t);
+
+        if (card->profiles) {
+            pa_card_profile *p;
+            void *state = NULL;
+
+            pa_strbuf_puts(
+                    s,
+                    "\tprofiles:\n");
+
+            while ((p = pa_hashmap_iterate(card->profiles, &state, NULL)))
+                pa_strbuf_printf(s, "\t\t%s: %s\n", p->name, p->description);
+        }
+
     }
 
     return pa_strbuf_tostring_free(s);

commit 76ca5b8578aae04753c6ca2c5a87bbb39188f7b5
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sat Jan 17 02:09:02 2009 +0100

    beautify cli output a bit

diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index c9a82cb..cf08371 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -91,8 +91,8 @@ char *pa_client_list_to_string(pa_core *c) {
         if (client->module)
             pa_strbuf_printf(s, "\towner module: %u\n", client->module->index);
 
-        t = pa_proplist_to_string(client->proplist);
-        pa_strbuf_printf(s, "\tproperties:\n%s", t);
+        t = pa_proplist_to_string_sep(client->proplist, "\n\t\t");
+        pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t);
         pa_xfree(t);
     }
 
@@ -107,7 +107,7 @@ char *pa_card_list_to_string(pa_core *c) {
 
     s = pa_strbuf_new();
 
-    pa_strbuf_printf(s, "%u card(s) available in.\n", pa_idxset_size(c->cards));
+    pa_strbuf_printf(s, "%u card(s) available.\n", pa_idxset_size(c->cards));
 
     for (card = pa_idxset_first(c->cards, &idx); card; card = pa_idxset_next(c->cards, &idx)) {
         char *t;
@@ -226,8 +226,8 @@ char *pa_sink_list_to_string(pa_core *c) {
         if (sink->module)
             pa_strbuf_printf(s, "\tmodule: %u\n", sink->module->index);
 
-        t = pa_proplist_to_string(sink->proplist);
-        pa_strbuf_printf(s, "\tproperties:\n%s", t);
+        t = pa_proplist_to_string_sep(sink->proplist, "\n\t\t");
+        pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t);
         pa_xfree(t);
     }
 
@@ -314,8 +314,8 @@ char *pa_source_list_to_string(pa_core *c) {
         if (source->module)
             pa_strbuf_printf(s, "\tmodule: %u\n", source->module->index);
 
-        t = pa_proplist_to_string(source->proplist);
-        pa_strbuf_printf(s, "\tproperties:\n%s", t);
+        t = pa_proplist_to_string_sep(source->proplist, "\n\t\t");
+        pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t);
         pa_xfree(t);
     }
 
@@ -386,8 +386,8 @@ char *pa_source_output_list_to_string(pa_core *c) {
         if (o->direct_on_input)
             pa_strbuf_printf(s, "\tdirect on input: %u\n", o->direct_on_input->index);
 
-        t = pa_proplist_to_string(o->proplist);
-        pa_strbuf_printf(s, "\tproperties:\n%s", t);
+        t = pa_proplist_to_string_sep(o->proplist, "\n\t\t");
+        pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t);
         pa_xfree(t);
     }
 
@@ -461,8 +461,8 @@ char *pa_sink_input_list_to_string(pa_core *c) {
         if (i->client)
             pa_strbuf_printf(s, "\tclient: %u <%s>\n", i->client->index, pa_strnull(pa_proplist_gets(i->client->proplist, PA_PROP_APPLICATION_NAME)));
 
-        t = pa_proplist_to_string(i->proplist);
-        pa_strbuf_printf(s, "\tproperties:\n%s", t);
+        t = pa_proplist_to_string_sep(i->proplist, "\n\t\t");
+        pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t);
         pa_xfree(t);
     }
 
@@ -475,7 +475,7 @@ char *pa_scache_list_to_string(pa_core *c) {
 
     s = pa_strbuf_new();
 
-    pa_strbuf_printf(s, "%u cache entries available.\n", c->scache ? pa_idxset_size(c->scache) : 0);
+    pa_strbuf_printf(s, "%u cache entrie(s) available.\n", c->scache ? pa_idxset_size(c->scache) : 0);
 
     if (c->scache) {
         pa_scache_entry *e;
@@ -512,8 +512,8 @@ char *pa_scache_list_to_string(pa_core *c) {
                 pa_yes_no(e->lazy),
                 e->filename ? e->filename : "n/a");
 
-            t = pa_proplist_to_string(e->proplist);
-            pa_strbuf_printf(s, "\tproperties:\n%s", t);
+            t = pa_proplist_to_string_sep(e->proplist, "\n\t\t");
+            pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t);
             pa_xfree(t);
         }
     }

commit 606cf8a2ec9d5f84b88039b14e17a93b706be9da
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sat Jan 17 02:17:32 2009 +0100

    get rid of pa_module_get_info because it is not used

diff --git a/src/pulsecore/module.c b/src/pulsecore/module.c
index c4dcb47..0a8c8f5 100644
--- a/src/pulsecore/module.c
+++ b/src/pulsecore/module.c
@@ -234,12 +234,6 @@ void pa_module_unload_request_by_index(pa_core *c, uint32_t idx, pa_bool_t force
     pa_module_unload_request(m, force);
 }
 
-pa_modinfo *pa_module_get_info(pa_module *m) {
-    pa_assert(m);
-
-    return pa_modinfo_get_by_handle(m->dl, m->name);
-}
-
 int pa_module_get_n_used(pa_module*m) {
     pa_assert(m);
 
diff --git a/src/pulsecore/module.h b/src/pulsecore/module.h
index 986f0d2..5c49bd2 100644
--- a/src/pulsecore/module.h
+++ b/src/pulsecore/module.h
@@ -79,6 +79,4 @@ int pa_module_get_n_used(pa_module*m);
     pa_bool_t pa__load_once(void) { return b; }                 \
     struct __stupid_useless_struct_to_allow_trailing_semicolon
 
-pa_modinfo *pa_module_get_info(pa_module *m);
-
 #endif

commit 4460a5d5d5015b003c703f3e81c00ad0ed3ac838
Author: Luiz Augusto von Dentz <luiz.dentz at openbossa.org>
Date:   Mon Jan 19 10:19:53 2009 -0300

    Fix hsp rate and channels.

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index cb4746a..b22578e 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -448,8 +448,11 @@ static int bt_setconf(struct userdata *u) {
         }
         u->ss.format = PA_SAMPLE_S16LE;
     }
-    else
-        u->ss.format = PA_SAMPLE_U8;
+    else {
+        u->ss.format = PA_SAMPLE_S16LE;
+        u->ss.channels = 1;
+        u->ss.rate = 8000;
+    }
 
     memset(msg.buf, 0, BT_SUGGESTED_BUFFER_SIZE);
     msg.setconf_req.h.type = BT_REQUEST;

commit 348c2cab712203f23310b56952d9b42284af8482
Author: Diego E. 'Flameeyes' Pettenò <flameeyes at gmail.com>
Date:   Mon Jan 19 17:20:18 2009 +0100

    Create only the directory the current target should be created into.
    
    With this change, instead of running multiple `mkdir -p` commands in all
    the rules for all the generated definition files, only the call for the
    current generated file is executed.
    
    Not only it should shorten build time (especially for parallel make) but it
    also fixes out-of-tree builds when new directories are added.
    
    The $(dir $@) construct is GNU make-specific, but the rest of the
    buildsystem is already GNU make-dependent so there should be no problem.

diff --git a/src/Makefile.am b/src/Makefile.am
index 99ed7b2..e6feddd 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1068,10 +1068,7 @@ EXTRA_DIST += $(SYMDEF_FILES)
 BUILT_SOURCES += $(SYMDEF_FILES)
 
 $(SYMDEF_FILES): modules/module-defs.h.m4
-	$(MKDIR_P) modules
-	$(MKDIR_P) modules/gconf
-	$(MKDIR_P) modules/rtp
-	$(MKDIR_P) modules/bluetooth
+	$(MKDIR_P) $(dir $@)
 	$(M4) -Dfname="$@" $< > $@
 
 # Flat volume

commit 7104d54bbce8f9bd2553e16f45f3a0f69ac75b8b
Author: Diego E. 'Flameeyes' Pettenò <flameeyes at gmail.com>
Date:   Mon Jan 19 17:30:41 2009 +0100

    Add proper -I directives for out-of-tree builds.
    
    When building out of tree, the generated files are put in builddir rather
    than srcdir, so handle that properly.

diff --git a/src/Makefile.am b/src/Makefile.am
index e6feddd..9dd4465 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -47,10 +47,15 @@ endif
 
 AM_CFLAGS = \
 	-I$(top_srcdir)/src \
+	-I$(top_builddir)/src \
 	-I$(top_srcdir)/src/modules \
+	-I$(top_builddir)/src/modules \
 	-I$(top_srcdir)/src/modules/rtp \
+	-I$(top_builddir)/src/modules/rtp \
 	-I$(top_srcdir)/src/modules/gconf \
+	-I$(top_builddir)/src/modules/gconf \
 	-I$(top_srcdir)/src/modules/bluetooth \
+	-I$(top_builddir)/src/modules/bluetooth \
 	-I$(top_srcdir)/src/modules/raop \
 	$(PTHREAD_CFLAGS) -D_POSIX_PTHREAD_SEMANTICS \
 	$(LTDLINCL) \

commit 723d71a021d3693941c98648c24d0db17ea16117
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Jan 19 21:45:58 2009 +0100

    add api for manipulating volume balances

diff --git a/src/map-file b/src/map-file
index 06ee4e1..9c9e1c7 100644
--- a/src/map-file
+++ b/src/map-file
@@ -100,10 +100,12 @@ pa_cvolume_avg;
 pa_cvolume_channels_equal_to;
 pa_cvolume_compatible;
 pa_cvolume_equal;
+pa_cvolume_get_balance;
 pa_cvolume_init;
 pa_cvolume_max;
 pa_cvolume_remap;
 pa_cvolume_set;
+pa_cvolume_set_balance;
 pa_cvolume_snprint;
 pa_cvolume_valid;
 pa_ext_stream_restore_delete;
diff --git a/src/pulse/volume.c b/src/pulse/volume.c
index ace5c4d..d4b1fd9 100644
--- a/src/pulse/volume.c
+++ b/src/pulse/volume.c
@@ -402,3 +402,94 @@ int pa_cvolume_compatible(const pa_cvolume *v, const pa_sample_spec *ss) {
 
     return v->channels == ss->channels;
 }
+
+static void get_avg_lr(const pa_channel_map *map, const pa_cvolume *v, pa_volume_t *l, pa_volume_t *r) {
+    int c;
+    pa_volume_t left = 0, right = 0;
+    unsigned n_left = 0, n_right = 0;
+
+    pa_assert(v);
+    pa_assert(map);
+    pa_assert(map->channels == v->channels);
+    pa_assert(l);
+    pa_assert(r);
+
+    for (c = 0; c < map->channels; c++) {
+        if (on_left(map->map[c])) {
+            left += v->values[c];
+            n_left++;
+        } else if (on_right(map->map[c])) {
+            right += v->values[c];
+            n_right++;
+        }
+    }
+
+    *l = left / n_left;
+    *r = right / n_right;
+}
+
+float pa_cvolume_get_balance(const pa_channel_map *map, const pa_cvolume *v) {
+    pa_volume_t left, right;
+
+    pa_assert(v);
+    pa_assert(map);
+    pa_assert(map->channels == v->channels);
+
+    get_avg_lr(map, v, &left, &right);
+
+    if (left == right)
+        return 0.0f;
+
+    /*   1.0,  0.0  =>  -1.0
+         0.0,  1.0  =>   1.0
+         0.0,  0.0  =>   0.0
+         0.5,  0.5  =>   0.0
+         1.0,  0.5  =>  -0.5
+         1.0,  0.25 => -0.75
+         0.75, 0.25 => -0.66
+         0.5,  0.25 => -0.5   */
+
+    if (left > right)
+        return -1.0f + ((float) right / (float) left);
+    else
+        return 1.0f - ((float) left / (float) right);
+}
+
+pa_cvolume* pa_cvolume_set_balance(const pa_channel_map *map, pa_cvolume *v, float new_balance) {
+    pa_volume_t left, nleft, right, nright, m;
+    unsigned c;
+    pa_assert(map->channels == v->channels);
+
+    pa_assert(map);
+    pa_assert(v);
+    pa_assert(new_balance >= -1.0f);
+    pa_assert(new_balance <= 1.0f);
+
+    get_avg_lr(map, v, &left, &right);
+
+    m = PA_MAX(left, right);
+
+    if (new_balance <= 0) {
+        nright  = (new_balance + 1.0f) * m;
+        nleft = m;
+    } else  {
+        nleft = (1.0f - new_balance) * m;
+        nright = m;
+    }
+
+    for (c = 0; c < map->channels; c++) {
+        if (on_left(map->map[c])) {
+            if (left == 0)
+                v->values[c] = 0;
+            else
+                v->values[c] = (pa_volume_t) (((uint64_t) v->values[c] * (uint64_t) nleft) / (uint64_t) left);
+        } else if (on_right(map->map[c])) {
+            if (right == 0)
+                v->values[c] = 0;
+            else
+                v->values[c] = (pa_volume_t) (((uint64_t) v->values[c] * (uint64_t) nright) / (uint64_t) right);
+        }
+    }
+
+    return v;
+}
diff --git a/src/pulse/volume.h b/src/pulse/volume.h
index 5815c90..08683ac 100644
--- a/src/pulse/volume.h
+++ b/src/pulse/volume.h
@@ -233,6 +233,20 @@ pa_cvolume *pa_cvolume_remap(pa_cvolume *v, pa_channel_map *from, pa_channel_map
  * the specified sample spec. \since 0.9.13 */
 int pa_cvolume_compatible(const pa_cvolume *v, const pa_sample_spec *ss) PA_GCC_PURE;
 
+/** Calculate a 'balance' value for the specified volume with the
+ * specified channel map. The return value will range from -1.0f
+ * (left) to +1.0f (right) \since 0.9.15 */
+float pa_cvolume_get_balance(const pa_channel_map *map, const pa_cvolume *v) PA_GCC_PURE;
+
+/** Adjust the 'balance' value for the specified volume with the
+ * specified channel map. v will be modified in place and
+ * returned. The balance is a value between -1.0f and +1.0f. This
+ * operation might not be reversable! Also, after this call
+ * pa_cvolume_get_balance() is not guaranteed to actually return the
+ * requested balance (e.g. when the input volume was zero anyway for
+ * all channels)- \since 0.9.15 */
+pa_cvolume* pa_cvolume_set_balance(const pa_channel_map *map, pa_cvolume *v, float new_balance);
+
 PA_C_DECL_END
 
 #endif
diff --git a/src/tests/voltest.c b/src/tests/voltest.c
index 5bfc97e..879d86b 100644
--- a/src/tests/voltest.c
+++ b/src/tests/voltest.c
@@ -6,6 +6,8 @@
 int main(int argc, char *argv[]) {
     pa_volume_t v;
     pa_cvolume cv;
+    float b;
+    pa_channel_map map;
 
     for (v = PA_VOLUME_MUTED; v <= PA_VOLUME_NORM*2; v += 256) {
 
@@ -28,5 +30,32 @@ int main(int argc, char *argv[]) {
 
     }
 
+    map.channels = cv.channels = 2;
+    map.map[0] = PA_CHANNEL_POSITION_LEFT;
+    map.map[1] = PA_CHANNEL_POSITION_RIGHT;
+
+    for (cv.values[0] = PA_VOLUME_MUTED; cv.values[0] <= PA_VOLUME_NORM*2; cv.values[0] += 4096)
+        for (cv.values[1] = PA_VOLUME_MUTED; cv.values[1] <= PA_VOLUME_NORM*2; cv.values[1] += 4096) {
+            char s[PA_CVOLUME_SNPRINT_MAX];
+
+            printf("Volume: [%s]; balance: %2.1f\n", pa_cvolume_snprint(s, sizeof(s), &cv), pa_cvolume_get_balance(&map, &cv));
+        }
+
+    for (cv.values[0] = PA_VOLUME_MUTED+4096; cv.values[0] <= PA_VOLUME_NORM*2; cv.values[0] += 4096)
+        for (cv.values[1] = PA_VOLUME_MUTED; cv.values[1] <= PA_VOLUME_NORM*2; cv.values[1] += 4096)
+            for (b = -1.0f; b <= 1.0f; b += 0.2f) {
+                char s[PA_CVOLUME_SNPRINT_MAX];
+                pa_cvolume r;
+                float k;
+
+                printf("Before: volume: [%s]; balance: %2.1f\n", pa_cvolume_snprint(s, sizeof(s), &cv), pa_cvolume_get_balance(&map, &cv));
+
+                r = cv;
+                pa_cvolume_set_balance(&map, &r, b);
+
+                k = pa_cvolume_get_balance(&map, &r);
+                printf("After: volume: [%s]; balance: %2.1f (intended: %2.1f) %s\n", pa_cvolume_snprint(s, sizeof(s), &r), k, b, k < b-.05 || k > b+.5 ? "MISMATCH" : "");
+            }
+
     return 0;
 }

commit fe703013add1f577d62a63233d3232a4d386109d
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Jan 19 21:59:57 2009 +0100

    show balance value in CLI listings

diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index cf08371..5c3d3af 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -180,6 +180,7 @@ char *pa_sink_list_to_string(pa_core *c) {
             "\tflags: %s%s%s%s%s%s\n"
             "\tstate: %s\n"
             "\tvolume: %s%s%s\n"
+            "\t        balance %-20.2f\n"
             "\tbase volume: %s%s%s\n"
             "\tmuted: %s\n"
             "\tcurrent latency: %0.2f ms\n"
@@ -205,6 +206,7 @@ char *pa_sink_list_to_string(pa_core *c) {
             pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink, FALSE)),
             sink->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t        " : "",
             sink->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), pa_sink_get_volume(sink, FALSE)) : "",
+            pa_cvolume_get_balance(&sink->channel_map, pa_sink_get_volume(sink, FALSE)),
             pa_volume_snprint(v, sizeof(v), sink->base_volume),
             sink->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t             " : "",
             sink->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_volume_snprint_dB(vdb, sizeof(vdb), sink->base_volume) : "",
@@ -270,6 +272,7 @@ char *pa_source_list_to_string(pa_core *c) {
             "\tflags: %s%s%s%s%s%s\n"
             "\tstate: %s\n"
             "\tvolume: %s%s%s\n"
+            "\t        balance %-20.2f\n"
             "\tbase volume: %s%s%s\n"
             "\tmuted: %s\n"
             "\tcurrent latency: %0.2f ms\n"
@@ -293,6 +296,7 @@ char *pa_source_list_to_string(pa_core *c) {
             pa_cvolume_snprint(cv, sizeof(cv), pa_source_get_volume(source, FALSE)),
             source->flags & PA_SOURCE_DECIBEL_VOLUME ? "\n\t        " : "",
             source->flags & PA_SOURCE_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), pa_source_get_volume(source, FALSE)) : "",
+            pa_cvolume_get_balance(&source->channel_map, pa_source_get_volume(source, FALSE)),
             pa_volume_snprint(v, sizeof(v), source->base_volume),
             source->flags & PA_SOURCE_DECIBEL_VOLUME ? "\n\t             " : "",
             source->flags & PA_SOURCE_DECIBEL_VOLUME ? pa_sw_volume_snprint_dB(vdb, sizeof(vdb), source->base_volume) : "",
@@ -412,7 +416,7 @@ char *pa_sink_input_list_to_string(pa_core *c) {
     pa_strbuf_printf(s, "%u sink input(s) available.\n", pa_idxset_size(c->sink_inputs));
 
     for (i = pa_idxset_first(c->sink_inputs, &idx); i; i = pa_idxset_next(c->sink_inputs, &idx)) {
-        char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t, clt[28];
+        char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t, clt[28];
         pa_usec_t cl;
 
         if ((cl = pa_sink_input_get_requested_latency(i)) == (pa_usec_t) -1)
@@ -430,6 +434,8 @@ char *pa_sink_input_list_to_string(pa_core *c) {
             "\tstate: %s\n"
             "\tsink: %u <%s>\n"
             "\tvolume: %s\n"
+            "\t        %s\n"
+            "\t        balance %-20.2f\n"
             "\tmuted: %s\n"
             "\tcurrent latency: %0.2f ms\n"
             "\trequested latency: %s\n"
@@ -449,6 +455,8 @@ char *pa_sink_input_list_to_string(pa_core *c) {
             state_table[pa_sink_input_get_state(i)],
             i->sink->index, i->sink->name,
             pa_cvolume_snprint(cv, sizeof(cv), pa_sink_input_get_volume(i)),
+            pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), pa_sink_input_get_volume(i)),
+            pa_cvolume_get_balance(&i->channel_map, pa_sink_input_get_volume(i)),
             pa_yes_no(pa_sink_input_get_mute(i)),
             (double) pa_sink_input_get_latency(i, NULL) / PA_USEC_PER_MSEC,
             clt,

commit b43a45d1847f2eff096cc69f70efedd2b94c3aaa
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Jan 19 22:02:28 2009 +0100

    allow setting properties for modules, too

diff --git a/PROTOCOL b/PROTOCOL
index 37f289a..8c5937b 100644
--- a/PROTOCOL
+++ b/PROTOCOL
@@ -154,7 +154,6 @@ PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR at the end:
 
   early_requests (bool)
 
-
 ### v15, implemented by >= 0.9.15
 
 PA_COMMAND_CREATE_PLAYBACK_STREAM
@@ -163,4 +162,9 @@ PA_COMMAND_CREATE_PLAYBACK_STREAM
 
 PA_COMMAND_CREATE_PLAYBACK_STREAM, PA_COMMAND_CREATE_RECORD_STREAM:
 
-  bool dont_inhibit_auto_suspend ate the end
+  bool dont_inhibit_auto_suspend at the end
+
+PA_COMMAND_GET_MODULE_INFO_LIST
+
+  remove bool auto_unload
+  add proplist at the end
diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c
index bdc50e2..e7fa6d7 100644
--- a/src/pulse/introspect.c
+++ b/src/pulse/introspect.c
@@ -480,13 +480,16 @@ static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command,
         while (!pa_tagstruct_eof(t)) {
             pa_module_info i;
             pa_bool_t auto_unload = FALSE;
+
             memset(&i, 0, sizeof(i));
+            i.proplist = pa_proplist_new();
 
             if (pa_tagstruct_getu32(t, &i.index) < 0 ||
                 pa_tagstruct_gets(t, &i.name) < 0 ||
                 pa_tagstruct_gets(t, &i.argument) < 0 ||
                 pa_tagstruct_getu32(t, &i.n_used) < 0 ||
-                pa_tagstruct_get_boolean(t, &auto_unload) < 0) {
+                (o->context->version < 15 && pa_tagstruct_get_boolean(t, &auto_unload) < 0) ||
+                (o->context->version >= 15 && pa_tagstruct_get_proplist(t, i.proplist) < 0)) {
                 pa_context_fail(o->context, PA_ERR_PROTOCOL);
                 goto finish;
             }
@@ -497,6 +500,8 @@ static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command,
                 pa_module_info_cb_t cb = (pa_module_info_cb_t) o->callback;
                 cb(o->context, &i, 0, o->userdata);
             }
+
+            pa_proplist_free(i.proplist);
         }
     }
 
diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h
index ae9bd5b..428826e 100644
--- a/src/pulse/introspect.h
+++ b/src/pulse/introspect.h
@@ -333,6 +333,7 @@ typedef struct pa_module_info {
 /** \cond fulldocs */
     int auto_unload;                    /**< \deprecated Non-zero if this is an autoloaded module */
 /** \endcond */
+    pa_proplist *proplist;              /**< Property list \since 0.9.15 */
 } pa_module_info;
 
 /** Callback prototype for pa_context_get_module_info() and firends*/
diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h
index 8f44df2..529871f 100644
--- a/src/pulse/proplist.h
+++ b/src/pulse/proplist.h
@@ -128,6 +128,10 @@ PA_C_DECL_BEGIN
 #define PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE "device.buffering.fragment_size"
 #define PA_PROP_DEVICE_PROFILE_NAME            "device.profile.name"
 #define PA_PROP_DEVICE_PROFILE_DESCRIPTION     "device.profile.description"
+#define PA_PROP_MODULE_AUTHOR                  "module.author"
+#define PA_PROP_MODULE_DESCRIPTION             "module.description"
+#define PA_PROP_MODULE_USAGE                   "module.usage"
+#define PA_PROP_MODULE_VERSION                 "module.version"
 
 /** A property list object. Basically a dictionary with UTF-8 strings
  * as keys and arbitrary data as values. \since 0.9.11 */
diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c
index 8f5d9bd..f810579 100644
--- a/src/pulsecore/cli-command.c
+++ b/src/pulsecore/cli-command.c
@@ -51,6 +51,7 @@
 #include <pulsecore/shared.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/core-error.h>
+#include <pulsecore/modinfo.h>
 
 #include "cli-command.h"
 
diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index 5c3d3af..cbd36e2 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -54,6 +54,8 @@ char *pa_module_list_to_string(pa_core *c) {
     pa_strbuf_printf(s, "%u module(s) loaded.\n", pa_idxset_size(c->modules));
 
     for (m = pa_idxset_first(c->modules, &idx); m; m = pa_idxset_next(c->modules, &idx)) {
+        char *t;
+
         pa_strbuf_printf(s, "    index: %u\n"
                          "\tname: <%s>\n"
                          "\targument: <%s>\n"
@@ -64,6 +66,10 @@ char *pa_module_list_to_string(pa_core *c) {
                          pa_strempty(m->argument),
                          pa_module_get_n_used(m),
                          pa_yes_no(m->load_once));
+
+        t = pa_proplist_to_string_sep(m->proplist, "\n\t\t");
+        pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t);
+        pa_xfree(t);
     }
 
     return pa_strbuf_tostring_free(s);
diff --git a/src/pulsecore/module.c b/src/pulsecore/module.c
index 0a8c8f5..d470bb0 100644
--- a/src/pulsecore/module.c
+++ b/src/pulsecore/module.c
@@ -33,12 +33,14 @@
 
 #include <pulse/timeval.h>
 #include <pulse/xmalloc.h>
+#include <pulse/proplist.h>
 
 #include <pulsecore/core-subscribe.h>
 #include <pulsecore/log.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/macro.h>
 #include <pulsecore/ltdl-helper.h>
+#include <pulsecore/modinfo.h>
 
 #include "module.h"
 
@@ -50,6 +52,7 @@
 pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
     pa_module *m = NULL;
     pa_bool_t (*load_once)(void);
+    pa_modinfo *mi;
 
     pa_assert(c);
     pa_assert(name);
@@ -61,6 +64,7 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
     m->name = pa_xstrdup(name);
     m->argument = pa_xstrdup(argument);
     m->load_once = FALSE;
+    m->proplist = pa_proplist_new();
 
     if (!(m->dl = lt_dlopenext(name))) {
         pa_log("Failed to open module \"%s\": %s", name, lt_dlerror());
@@ -111,11 +115,28 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
 
     pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_NEW, m->index);
 
+    if ((mi = pa_modinfo_get_by_handle(m->dl, name))) {
+
+        if (mi->author && !pa_proplist_contains(m->proplist, PA_PROP_MODULE_AUTHOR))
+            pa_proplist_sets(m->proplist, PA_PROP_MODULE_AUTHOR, mi->author);
+
+        if (mi->description && !pa_proplist_contains(m->proplist, PA_PROP_MODULE_DESCRIPTION))
+            pa_proplist_sets(m->proplist, PA_PROP_MODULE_DESCRIPTION, mi->description);
+
+        if (mi->version && !pa_proplist_contains(m->proplist, PA_PROP_MODULE_VERSION))
+            pa_proplist_sets(m->proplist, PA_PROP_MODULE_VERSION, mi->version);
+
+        pa_modinfo_free(mi);
+    }
+
     return m;
 
 fail:
 
     if (m) {
+        if (m->proplist)
+            pa_proplist_free(m->proplist);
+
         pa_xfree(m->argument);
         pa_xfree(m->name);
 
@@ -137,6 +158,9 @@ static void pa_module_free(pa_module *m) {
     if (m->done)
         m->done(m);
 
+    if (m->proplist)
+        pa_proplist_free(m->proplist);
+
     lt_dlclose(m->dl);
 
     pa_log_info("Unloaded \"%s\" (index: #%u).", m->name, m->index);
diff --git a/src/pulsecore/module.h b/src/pulsecore/module.h
index 5c49bd2..6ab43dc 100644
--- a/src/pulsecore/module.h
+++ b/src/pulsecore/module.h
@@ -27,8 +27,9 @@
 
 typedef struct pa_module pa_module;
 
+#include <pulse/proplist.h>
+
 #include <pulsecore/core.h>
-#include <pulsecore/modinfo.h>
 
 struct pa_module {
     pa_core *core;
@@ -45,6 +46,8 @@ struct pa_module {
 
     pa_bool_t load_once:1;
     pa_bool_t unload_requested:1;
+
+    pa_proplist *proplist;
 };
 
 pa_module* pa_module_load(pa_core *c, const char *name, const char*argument);
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 5412267..eb55505 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -2728,10 +2728,9 @@ static void client_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_c
 
     if (c->version >= 13)
         pa_tagstruct_put_proplist(t, client->proplist);
-
 }
 
-static void module_fill_tagstruct(pa_tagstruct *t, pa_module *module) {
+static void module_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_module *module) {
     pa_assert(t);
     pa_assert(module);
 
@@ -2739,7 +2738,12 @@ static void module_fill_tagstruct(pa_tagstruct *t, pa_module *module) {
     pa_tagstruct_puts(t, module->name);
     pa_tagstruct_puts(t, module->argument);
     pa_tagstruct_putu32(t, (uint32_t) pa_module_get_n_used(module));
-    pa_tagstruct_put_boolean(t, FALSE); /* autoload is obsolete */
+
+    if (c->version < 15)
+        pa_tagstruct_put_boolean(t, FALSE); /* autoload is obsolete */
+
+    if (c->version >= 15)
+        pa_tagstruct_put_proplist(t, module->proplist);
 }
 
 static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sink_input *s) {
@@ -2891,7 +2895,7 @@ static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, p
     else if (client)
         client_fill_tagstruct(c, reply, client);
     else if (module)
-        module_fill_tagstruct(reply, module);
+        module_fill_tagstruct(c, reply, module);
     else if (si)
         sink_input_fill_tagstruct(c, reply, si);
     else if (so)
@@ -2946,7 +2950,7 @@ static void command_get_info_list(pa_pdispatch *pd, uint32_t command, uint32_t t
             else if (command == PA_COMMAND_GET_CLIENT_INFO_LIST)
                 client_fill_tagstruct(c, reply, p);
             else if (command == PA_COMMAND_GET_MODULE_INFO_LIST)
-                module_fill_tagstruct(reply, p);
+                module_fill_tagstruct(c, reply, p);
             else if (command == PA_COMMAND_GET_SINK_INPUT_INFO_LIST)
                 sink_input_fill_tagstruct(c, reply, p);
             else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST)

commit b987e5eeb88be8b9e041899abc7616482564f070
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Jan 19 22:02:40 2009 +0100

    fix bad free()

diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index feaeb31..6522176 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -658,8 +658,6 @@ snd_pcm_t *pa_alsa_open_by_device_id(
 
                 return pcm_handle;
             }
-
-            pa_xfree(d);
         }
 
         if (direction > 0) {

commit 23cd942a0da6db301a2166d4d51c730e4388d271
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Jan 19 23:03:22 2009 +0100

    fix doxygen version references

diff --git a/src/pulse/def.h b/src/pulse/def.h
index 9e424d4..d43c070 100644
--- a/src/pulse/def.h
+++ b/src/pulse/def.h
@@ -234,13 +234,13 @@ typedef enum pa_stream_flags {
     PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND = 0x8000U,
     /**< If set this stream won't be taken into account when we it is
      * checked whether the device this stream is connected to should
-     * auto-suspend. \ since 0.9.14 */
+     * auto-suspend. \ since 0.9.15 */
 
     PA_STREAM_START_UNMUTED = 0x10000U
     /**< Create in unmuted state. If neither PA_STREAM_START_UNMUTED
      * nor PA_STREAM_START_MUTED it is left to the server to decide
      * whether to create the stream in muted or in unmuted
-     * state. \since 0.9.14 */
+     * state. \since 0.9.15 */
 
 } pa_stream_flags_t;
 
diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h
index 428826e..a656d1c 100644
--- a/src/pulse/introspect.h
+++ b/src/pulse/introspect.h
@@ -211,7 +211,7 @@ typedef struct pa_sink_info {
     pa_sink_flags_t flags;             /**< Flags */
     pa_proplist *proplist;             /**< Property list \since 0.9.11 */
     pa_usec_t configured_latency;      /**< The latency this device has been configured to. \since 0.9.11 */
-    pa_volume_t base_volume;           /**< Some kind of "base" volume that refers to unamplified/unattenuated volume in the context of the output device. \since 0.9.14 */
+    pa_volume_t base_volume;           /**< Some kind of "base" volume that refers to unamplified/unattenuated volume in the context of the output device. \since 0.9.15 */
 } pa_sink_info;
 
 /** Callback prototype for pa_context_get_sink_info_by_name() and friends */
@@ -267,7 +267,7 @@ typedef struct pa_source_info {
     pa_source_flags_t flags;            /**< Flags */
     pa_proplist *proplist;              /**< Property list \since 0.9.11 */
     pa_usec_t configured_latency;       /**< The latency this device has been configured to. \since 0.9.11 */
-    pa_volume_t base_volume;           /**< Some kind of "base" volume that refers to unamplified/unattenuated volume in the context of the input device. \since 0.9.14 */
+    pa_volume_t base_volume;           /**< Some kind of "base" volume that refers to unamplified/unattenuated volume in the context of the input device. \since 0.9.15 */
 } pa_source_info;
 
 /** Callback prototype for pa_context_get_source_info_by_name() and friends */
diff --git a/src/pulse/volume.h b/src/pulse/volume.h
index 08683ac..38da5df 100644
--- a/src/pulse/volume.h
+++ b/src/pulse/volume.h
@@ -154,20 +154,20 @@ char *pa_sw_cvolume_snprint_dB(char *s, size_t l, const pa_cvolume *c);
  * pa_volume_snprint(). Please note that this value can change with
  * any release without warning and without being considered API or ABI
  * breakage. You should not use this definition anywhere where it
- * might become part of an ABI. \since 0.9.14 */
+ * might become part of an ABI. \since 0.9.15 */
 #define PA_VOLUME_SNPRINT_MAX 10
 
-/** Pretty print a volume \since 0.9.14 */
+/** Pretty print a volume \since 0.9.15 */
 char *pa_volume_snprint(char *s, size_t l, pa_volume_t v);
 
 /** Maximum length of the strings returned by
  * pa_volume_snprint_dB(). Please note that this value can change with
  * any release without warning and without being considered API or ABI
  * breakage. You should not use this definition anywhere where it
- * might become part of an ABI. \since 0.9.14 */
+ * might become part of an ABI. \since 0.9.15 */
 #define PA_SW_VOLUME_SNPRINT_DB_MAX 10
 
-/** Pretty print a volume but show dB values. \since 0.9.14 */
+/** Pretty print a volume but show dB values. \since 0.9.15 */
 char *pa_sw_volume_snprint_dB(char *s, size_t l, pa_volume_t v);
 
 /** Return the average volume of all channels */

commit 033791ca9ff583eb2f1eb1b14424cfee57c1af47
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Jan 19 23:06:37 2009 +0100

    fix up balance format string a bit

diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index cbd36e2..31d2b35 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -186,7 +186,7 @@ char *pa_sink_list_to_string(pa_core *c) {
             "\tflags: %s%s%s%s%s%s\n"
             "\tstate: %s\n"
             "\tvolume: %s%s%s\n"
-            "\t        balance %-20.2f\n"
+            "\t        balance %0.2f\n"
             "\tbase volume: %s%s%s\n"
             "\tmuted: %s\n"
             "\tcurrent latency: %0.2f ms\n"
@@ -278,7 +278,7 @@ char *pa_source_list_to_string(pa_core *c) {
             "\tflags: %s%s%s%s%s%s\n"
             "\tstate: %s\n"
             "\tvolume: %s%s%s\n"
-            "\t        balance %-20.2f\n"
+            "\t        balance %0.2f\n"
             "\tbase volume: %s%s%s\n"
             "\tmuted: %s\n"
             "\tcurrent latency: %0.2f ms\n"
@@ -441,7 +441,7 @@ char *pa_sink_input_list_to_string(pa_core *c) {
             "\tsink: %u <%s>\n"
             "\tvolume: %s\n"
             "\t        %s\n"
-            "\t        balance %-20.2f\n"
+            "\t        balance %0.2f\n"
             "\tmuted: %s\n"
             "\tcurrent latency: %0.2f ms\n"
             "\trequested latency: %s\n"

commit ed65081dd82cf60160b4800fd4a6e11b39e5695e
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Jan 19 23:07:13 2009 +0100

    show dB and balance for cached samples

diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index 31d2b35..7962039 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -497,7 +497,7 @@ char *pa_scache_list_to_string(pa_core *c) {
 
         for (e = pa_idxset_first(c->scache, &idx); e; e = pa_idxset_next(c->scache, &idx)) {
             double l = 0;
-            char ss[PA_SAMPLE_SPEC_SNPRINT_MAX] = "n/a", cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX] = "n/a", *t;
+            char ss[PA_SAMPLE_SPEC_SNPRINT_MAX] = "n/a", cv[PA_CVOLUME_SNPRINT_MAX], cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX] = "n/a", *t;
 
             if (e->memchunk.memblock) {
                 pa_sample_spec_snprint(ss, sizeof(ss), &e->sample_spec);
@@ -514,6 +514,8 @@ char *pa_scache_list_to_string(pa_core *c) {
                 "\tlength: %lu\n"
                 "\tduration: %0.1f s\n"
                 "\tvolume: %s\n"
+                "\t        %s\n"
+                "\t        balance %0.2f\n"
                 "\tlazy: %s\n"
                 "\tfilename: <%s>\n",
                 e->name,
@@ -523,6 +525,8 @@ char *pa_scache_list_to_string(pa_core *c) {
                 (long unsigned)(e->memchunk.memblock ? e->memchunk.length : 0),
                 l,
                 pa_cvolume_snprint(cv, sizeof(cv), &e->volume),
+                pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &e->volume),
+                e->memchunk.memblock ? pa_cvolume_get_balance(&e->channel_map, &e->volume) : 0.0f,
                 pa_yes_no(e->lazy),
                 e->filename ? e->filename : "n/a");
 

commit 5f6641cfdd49d64b1cbc68ed025f9d22edc2665c
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Jan 19 23:07:34 2009 +0100

    Beef pactl output up a bit

diff --git a/src/pulse/volume.c b/src/pulse/volume.c
index d4b1fd9..9191a07 100644
--- a/src/pulse/volume.c
+++ b/src/pulse/volume.c
@@ -458,8 +458,8 @@ float pa_cvolume_get_balance(const pa_channel_map *map, const pa_cvolume *v) {
 pa_cvolume* pa_cvolume_set_balance(const pa_channel_map *map, pa_cvolume *v, float new_balance) {
     pa_volume_t left, nleft, right, nright, m;
     unsigned c;
-    pa_assert(map->channels == v->channels);
 
+    pa_assert(map->channels == v->channels);
     pa_assert(map);
     pa_assert(v);
     pa_assert(new_balance >= -1.0f);
diff --git a/src/utils/pactl.c b/src/utils/pactl.c
index 2fc1733..1ff979e 100644
--- a/src/utils/pactl.c
+++ b/src/utils/pactl.c
@@ -153,7 +153,13 @@ static void get_server_info_callback(pa_context *c, const pa_server_info *i, voi
 }
 
 static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata) {
-    char s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+    char
+        s[PA_SAMPLE_SPEC_SNPRINT_MAX],
+        cv[PA_CVOLUME_SNPRINT_MAX],
+        cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX],
+        v[PA_VOLUME_SNPRINT_MAX],
+        vdb[PA_SW_VOLUME_SNPRINT_DB_MAX],
+        cm[PA_CHANNEL_MAP_SNPRINT_MAX];
     char *pl;
 
     if (is_last < 0) {
@@ -173,24 +179,36 @@ static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_
         printf("\n");
     nl = 1;
 
-    printf(_("*** Sink #%u ***\n"
-           "Name: %s\n"
-           "Driver: %s\n"
-           "Sample Specification: %s\n"
-           "Channel Map: %s\n"
-           "Owner Module: %u\n"
-           "Volume: %s\n"
-           "Monitor Source: %s\n"
-           "Latency: %0.0f usec, configured %0.0f usec\n"
-           "Flags: %s%s%s%s%s%s\n"
-           "Properties:\n%s"),
+    printf(_("Sink #%u\n"
+             "\tName: %s\n"
+             "\tDescription: %s\n"
+             "\tDriver: %s\n"
+             "\tSample Specification: %s\n"
+             "\tChannel Map: %s\n"
+             "\tOwner Module: %u\n"
+             "\tMute: %s\n"
+             "\tVolume: %s%s%s\n"
+             "\t        balance %0.2f\n"
+             "\tBase Volume: %s%s%s\n"
+             "\tMonitor Source: %s\n"
+             "\tLatency: %0.0f usec, configured %0.0f usec\n"
+             "\tFlags: %s%s%s%s%s%s\n"
+             "\tProperties:\n\t\t%s\n"),
            i->index,
            i->name,
+           pa_strnull(i->description),
            pa_strnull(i->driver),
            pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
            pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
            i->owner_module,
-           i->mute ? _("muted") : pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
+           pa_yes_no(i->mute),
+           pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
+           i->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t        " : "",
+           i->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &i->volume) : "",
+           pa_cvolume_get_balance(&i->channel_map, &i->volume),
+           pa_volume_snprint(v, sizeof(v), i->base_volume),
+           i->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t             " : "",
+           i->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_volume_snprint_dB(vdb, sizeof(vdb), i->base_volume) : "",
            pa_strnull(i->monitor_source_name),
            (double) i->latency, (double) i->configured_latency,
            i->flags & PA_SINK_HARDWARE ? "HARDWARE " : "",
@@ -199,13 +217,19 @@ static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_
            i->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
            i->flags & PA_SINK_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
            i->flags & PA_SINK_LATENCY ? "LATENCY " : "",
-           pl = pa_proplist_to_string(i->proplist));
+           pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
 
     pa_xfree(pl);
 }
 
 static void get_source_info_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) {
-    char s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+    char
+        s[PA_SAMPLE_SPEC_SNPRINT_MAX],
+        cv[PA_CVOLUME_SNPRINT_MAX],
+        cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX],
+        v[PA_VOLUME_SNPRINT_MAX],
+        vdb[PA_SW_VOLUME_SNPRINT_DB_MAX],
+        cm[PA_CHANNEL_MAP_SNPRINT_MAX];
     char *pl;
 
     if (is_last < 0) {
@@ -225,24 +249,36 @@ static void get_source_info_callback(pa_context *c, const pa_source_info *i, int
         printf("\n");
     nl = 1;
 
-    printf(_("*** Source #%u ***\n"
-           "Name: %s\n"
-           "Driver: %s\n"
-           "Sample Specification: %s\n"
-           "Channel Map: %s\n"
-           "Owner Module: %u\n"
-           "Volume: %s\n"
-           "Monitor of Sink: %s\n"
-           "Latency: %0.0f usec, configured %0.0f usec\n"
-           "Flags: %s%s%s%s%s%s\n"
-           "Properties:\n%s"),
+    printf(_("Source #%u\n"
+             "\tName: %s\n"
+             "\tDescription: %s\n"
+             "\tDriver: %s\n"
+             "\tSample Specification: %s\n"
+             "\tChannel Map: %s\n"
+             "\tOwner Module: %u\n"
+             "\tMute: %s\n"
+             "\tVolume: %s%s%s\n"
+             "\t        balance %0.2f\n"
+             "\tBase Volume: %s%s%s\n"
+             "\tMonitor of Sink: %s\n"
+             "\tLatency: %0.0f usec, configured %0.0f usec\n"
+             "\tFlags: %s%s%s%s%s%s\n"
+             "\tProperties:\n\t\t%s\n"),
            i->index,
            i->name,
+           pa_strnull(i->description),
            pa_strnull(i->driver),
            pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
            pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
            i->owner_module,
-           i->mute ? "muted" : pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
+           pa_yes_no(i->mute),
+           pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
+           i->flags & PA_SOURCE_DECIBEL_VOLUME ? "\n\t        " : "",
+           i->flags & PA_SOURCE_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &i->volume) : "",
+           pa_cvolume_get_balance(&i->channel_map, &i->volume),
+           pa_volume_snprint(v, sizeof(v), i->base_volume),
+           i->flags & PA_SOURCE_DECIBEL_VOLUME ? "\n\t             " : "",
+           i->flags & PA_SOURCE_DECIBEL_VOLUME ? pa_sw_volume_snprint_dB(vdb, sizeof(vdb), i->base_volume) : "",
            i->monitor_of_sink_name ? i->monitor_of_sink_name : _("n/a"),
            (double) i->latency, (double) i->configured_latency,
            i->flags & PA_SOURCE_HARDWARE ? "HARDWARE " : "",
@@ -251,13 +287,14 @@ static void get_source_info_callback(pa_context *c, const pa_source_info *i, int
            i->flags & PA_SOURCE_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
            i->flags & PA_SOURCE_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
            i->flags & PA_SOURCE_LATENCY ? "LATENCY " : "",
-           pl = pa_proplist_to_string(i->proplist));
+           pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
 
     pa_xfree(pl);
 }
 
 static void get_module_info_callback(pa_context *c, const pa_module_info *i, int is_last, void *userdata) {
     char t[32];
+    char *pl;
 
     if (is_last < 0) {
         fprintf(stderr, _("Failed to get module information: %s\n"), pa_strerror(pa_context_errno(c)));
@@ -278,14 +315,18 @@ static void get_module_info_callback(pa_context *c, const pa_module_info *i, int
 
     snprintf(t, sizeof(t), "%u", i->n_used);
 
-    printf(_("*** Module #%u ***\n"
-           "Name: %s\n"
-           "Argument: %s\n"
-           "Usage counter: %s\n"),
+    printf(_("Module #%u\n"
+             "\tName: %s\n"
+             "\tArgument: %s\n"
+             "\tUsage counter: %s\n"
+             "\tProperties:\n\t\t%s\n"),
            i->index,
            i->name,
            i->argument ? i->argument : "",
-           i->n_used != PA_INVALID_INDEX ? t : _("n/a"));
+           i->n_used != PA_INVALID_INDEX ? t : _("n/a"),
+           pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
+
+    pa_xfree(pl);
 }
 
 static void get_client_info_callback(pa_context *c, const pa_client_info *i, int is_last, void *userdata) {
@@ -311,20 +352,20 @@ static void get_client_info_callback(pa_context *c, const pa_client_info *i, int
 
     snprintf(t, sizeof(t), "%u", i->owner_module);
 
-    printf(_("*** Client #%u ***\n"
-           "Driver: %s\n"
-           "Owner Module: %s\n"
-           "Properties:\n%s"),
+    printf(_("Client #%u\n"
+             "\tDriver: %s\n"
+             "\tOwner Module: %s\n"
+             "\tProperties:\n\t\t%s\n"),
            i->index,
            pa_strnull(i->driver),
            i->owner_module != PA_INVALID_INDEX ? t : _("n/a"),
-           pl = pa_proplist_to_string(i->proplist));
+           pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
 
     pa_xfree(pl);
 }
 
 static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info *i, int is_last, void *userdata) {
-    char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+    char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
     char *pl;
 
     if (is_last < 0) {
@@ -347,18 +388,21 @@ static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info
     snprintf(t, sizeof(t), "%u", i->owner_module);
     snprintf(k, sizeof(k), "%u", i->client);
 
-    printf(_("*** Sink Input #%u ***\n"
-           "Driver: %s\n"
-           "Owner Module: %s\n"
-           "Client: %s\n"
-           "Sink: %u\n"
-           "Sample Specification: %s\n"
-           "Channel Map: %s\n"
-           "Volume: %s\n"
-           "Buffer Latency: %0.0f usec\n"
-           "Sink Latency: %0.0f usec\n"
-           "Resample method: %s\n"
-             "Properties:\n%s"),
+    printf(_("Sink Input #%u\n"
+             "\tDriver: %s\n"
+             "\tOwner Module: %s\n"
+             "\tClient: %s\n"
+             "\tSink: %u\n"
+             "\tSample Specification: %s\n"
+             "\tChannel Map: %s\n"
+             "\tMute: %s\n"
+             "\tVolume: %s\n"
+             "\t        %s\n"
+             "\t        balance %0.2f\n"
+             "\tBuffer Latency: %0.0f usec\n"
+             "\tSink Latency: %0.0f usec\n"
+             "\tResample method: %s\n"
+             "\tProperties:\n\t\t%s\n"),
            i->index,
            pa_strnull(i->driver),
            i->owner_module != PA_INVALID_INDEX ? t : _("n/a"),
@@ -366,11 +410,14 @@ static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info
            i->sink,
            pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
            pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
-           i->mute ? _("muted") : pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
+           pa_yes_no(i->mute),
+           pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
+           pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &i->volume),
+           pa_cvolume_get_balance(&i->channel_map, &i->volume),
            (double) i->buffer_usec,
            (double) i->sink_usec,
            i->resample_method ? i->resample_method : _("n/a"),
-           pl = pa_proplist_to_string(i->proplist));
+           pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
 
     pa_xfree(pl);
 }
@@ -400,17 +447,17 @@ static void get_source_output_info_callback(pa_context *c, const pa_source_outpu
     snprintf(t, sizeof(t), "%u", i->owner_module);
     snprintf(k, sizeof(k), "%u", i->client);
 
-    printf(_("*** Source Output #%u ***\n"
-           "Driver: %s\n"
-           "Owner Module: %s\n"
-           "Client: %s\n"
-           "Source: %u\n"
-           "Sample Specification: %s\n"
-           "Channel Map: %s\n"
-           "Buffer Latency: %0.0f usec\n"
-           "Source Latency: %0.0f usec\n"
-           "Resample method: %s\n"
-           "Properties:\n%s"),
+    printf(_("Source Output #%u\n"
+             "\tDriver: %s\n"
+             "\tOwner Module: %s\n"
+             "\tClient: %s\n"
+             "\tSource: %u\n"
+             "\tSample Specification: %s\n"
+             "\tChannel Map: %s\n"
+             "\tBuffer Latency: %0.0f usec\n"
+             "\tSource Latency: %0.0f usec\n"
+             "\tResample method: %s\n"
+             "\tProperties:\n\t\t%s\n"),
            i->index,
            pa_strnull(i->driver),
            i->owner_module != PA_INVALID_INDEX ? t : _("n/a"),
@@ -421,13 +468,13 @@ static void get_source_output_info_callback(pa_context *c, const pa_source_outpu
            (double) i->buffer_usec,
            (double) i->source_usec,
            i->resample_method ? i->resample_method : _("n/a"),
-           pl = pa_proplist_to_string(i->proplist));
+           pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
 
     pa_xfree(pl);
 }
 
 static void get_sample_info_callback(pa_context *c, const pa_sample_info *i, int is_last, void *userdata) {
-    char t[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+    char t[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
     char *pl;
 
     if (is_last < 0) {
@@ -447,29 +494,32 @@ static void get_sample_info_callback(pa_context *c, const pa_sample_info *i, int
         printf("\n");
     nl = 1;
 
-
     pa_bytes_snprint(t, sizeof(t), i->bytes);
 
-    printf(_("*** Sample #%u ***\n"
-           "Name: %s\n"
-           "Volume: %s\n"
-           "Sample Specification: %s\n"
-           "Channel Map: %s\n"
-           "Duration: %0.1fs\n"
-           "Size: %s\n"
-           "Lazy: %s\n"
-           "Filename: %s\n"
-           "Properties:\n%s"),
+    printf(_("Sample #%u\n"
+             "\tName: %s\n"
+             "\tSample Specification: %s\n"
+             "\tChannel Map: %s\n"
+             "\tVolume: %s\n"
+             "\t        %s\n"
+             "\t        balance %0.2f\n"
+             "\tDuration: %0.1fs\n"
+             "\tSize: %s\n"
+             "\tLazy: %s\n"
+             "\tFilename: %s\n"
+             "\tProperties:\n\t\t%s\n"),
            i->index,
            i->name,
-           pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
            pa_sample_spec_valid(&i->sample_spec) ? pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec) : _("n/a"),
            pa_sample_spec_valid(&i->sample_spec) ? pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map) : _("n/a"),
-           (double) i->duration/1000000,
+           pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
+           pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &i->volume),
+           pa_cvolume_get_balance(&i->channel_map, &i->volume),
+           (double) i->duration/1000000.0,
            t,
            pa_yes_no(i->lazy),
            i->filename ? i->filename : _("n/a"),
-           pl = pa_proplist_to_string(i->proplist));
+           pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
 
     pa_xfree(pl);
 }
@@ -579,7 +629,7 @@ static void context_state_callback(pa_context *c, void *userdata) {
                     break;
 
                 case LIST:
-                    actions = 8;
+                    actions = 7;
                     pa_operation_unref(pa_context_get_module_info_list(c, get_module_info_callback, NULL));
                     pa_operation_unref(pa_context_get_sink_info_list(c, get_sink_info_callback, NULL));
                     pa_operation_unref(pa_context_get_source_info_list(c, get_source_info_callback, NULL));

commit 96f01bf73fb8321f7dde732ab1b7ce52b5121043
Merge: 5f6641c... 7104d54...
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Jan 19 23:09:58 2009 +0100

    Merge commit '7104d54bbce8f9bd2553e16f45f3a0f69ac75b8b'


commit f83111dd1710d7e0a3240879217e6d0c783c4a4b
Merge: 96f01bf... 4460a5d...
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Jan 19 23:12:23 2009 +0100

    Merge commit 'vudentz/master'


commit 9c4f8e627a70fc610a81da2703eeddfde4a7e4fa
Author: Marc-André Lureau <marcandre.lureau at gmail.com>
Date:   Thu Jan 15 17:16:31 2009 +0200

    pulse: introspect sink state

diff --git a/src/pulse/def.h b/src/pulse/def.h
index d43c070..7dc753f 100644
--- a/src/pulse/def.h
+++ b/src/pulse/def.h
@@ -613,6 +613,35 @@ typedef enum pa_sink_flags {
 #define PA_SINK_DECIBEL_VOLUME PA_SINK_DECIBEL_VOLUME
 /** \endcond */
 
+/** Sink state. \since 0.9.15 */
+typedef enum pa_sink_state { /* enum serialized in u8 */
+    PA_SINK_INVALID_STATE = -1,
+    /**< This state is used when the server does not support sink state introspection \since 0.9.15 */
+
+    PA_SINK_RUNNING = 0,
+    /**< Running, sink is playing and used by at least one non-corked sink-input \since 0.9.15 */
+
+    PA_SINK_IDLE = 1,
+    /**< When idle, the sink is playing but there is no non-corked sink-input attached to it \since 0.9.15 */
+
+    PA_SINK_SUSPENDED = 2
+    /**< When suspended, actual sink access can be closed, for instance \since 0.9.15 */
+
+} pa_sink_state_t;
+
+/** Returns non-zero if sink is playing: running or idle. \since 0.9.15 */
+static inline int PA_SINK_IS_OPENED(pa_sink_state_t x) {
+    return x == PA_SINK_RUNNING || x == PA_SINK_IDLE;
+}
+
+/** \cond fulldocs */
+#define PA_SINK_INVALID_STATE PA_SINK_INVALID_STATE
+#define PA_SINK_RUNNING PA_SINK_RUNNING
+#define PA_SINK_IDLE PA_SINK_IDLE
+#define PA_SINK_SUSPENDED PA_SINK_SUSPENDED
+#define PA_SINK_IS_OPENED PA_SINK_IS_OPENED
+/** \endcond */
+
 /** Special source flags.  */
 typedef enum pa_source_flags {
     PA_SOURCE_HW_VOLUME_CTRL = 0x0001U,
@@ -626,7 +655,7 @@ typedef enum pa_source_flags {
      * "virtual"/software source \since 0.9.3 */
 
     PA_SOURCE_NETWORK = 0x0008U,
-    /**< Is a networked sink of some kind. \since 0.9.7 */
+    /**< Is a networked source of some kind. \since 0.9.7 */
 
     PA_SOURCE_HW_MUTE_CTRL = 0x0010U,
     /**< Supports hardware mute control \since 0.9.11 */
@@ -645,6 +674,35 @@ typedef enum pa_source_flags {
 #define PA_SOURCE_DECIBEL_VOLUME PA_SOURCE_DECIBEL_VOLUME
 /** \endcond */
 
+/** Source state. \since 0.9.15 */
+typedef enum pa_source_state {
+    PA_SOURCE_INVALID_STATE = -1,
+    /**< This state is used when the server does not support source state introspection \since 0.9.15 */
+
+    PA_SOURCE_RUNNING = 0,
+    /**< Running, source is recording and used by at least one non-corked source-output \since 0.9.15 */
+
+    PA_SOURCE_IDLE = 1,
+    /**< When idle, the source is still recording but there is no non-corked source-output \since 0.9.15 */
+
+    PA_SOURCE_SUSPENDED = 2
+    /**< When suspended, actual source access can be closed, for instance \since 0.9.15 */
+
+} pa_source_state_t;
+
+/** Returns non-zero if source is recording: running or idle. \since 0.9.15 */
+static inline int PA_SOURCE_IS_OPENED(pa_source_state_t x) {
+    return x == PA_SOURCE_RUNNING || x == PA_SOURCE_IDLE;
+}
+
+/** \cond fulldocs */
+#define PA_SOURCE_INVALID_STATE PA_SOURCE_INVALID_STATE
+#define PA_SOURCE_RUNNING PA_SOURCE_RUNNING
+#define PA_SOURCE_IDLE PA_SOURCE_IDLE
+#define PA_SOURCE_SUSPENDED PA_SOURCE_SUSPENDED
+#define PA_SOURCE_IS_OPENED PA_SOURCE_IS_OPENED
+/** \endcond */
+
 /** A generic free() like callback prototype */
 typedef void (*pa_free_cb_t)(void *p);
 
diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c
index e7fa6d7..a6a228e 100644
--- a/src/pulse/introspect.c
+++ b/src/pulse/introspect.c
@@ -146,15 +146,18 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u
 
         eol = -1;
     } else {
-        uint32_t flags;
 
         while (!pa_tagstruct_eof(t)) {
             pa_sink_info i;
-            pa_bool_t mute = FALSE;
+            pa_bool_t mute;
+            uint32_t flags;
+            uint32_t state;
 
             memset(&i, 0, sizeof(i));
             i.proplist = pa_proplist_new();
             i.base_volume = PA_VOLUME_NORM;
+            mute = FALSE;
+            state = PA_SINK_INVALID_STATE;
 
             if (pa_tagstruct_getu32(t, &i.index) < 0 ||
                 pa_tagstruct_gets(t, &i.name) < 0 ||
@@ -173,7 +176,8 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u
                  (pa_tagstruct_get_proplist(t, i.proplist) < 0 ||
                   pa_tagstruct_get_usec(t, &i.configured_latency) < 0)) ||
                 (o->context->version >= 15 &&
-                 pa_tagstruct_get_volume(t, &i.base_volume) < 0)) {
+                 (pa_tagstruct_get_volume(t, &i.base_volume) < 0 ||
+                  pa_tagstruct_getu32(t, &state) < 0))) {
 
                 pa_context_fail(o->context, PA_ERR_PROTOCOL);
                 pa_proplist_free(i.proplist);
@@ -182,6 +186,7 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u
 
             i.mute = (int) mute;
             i.flags = (pa_sink_flags_t) flags;
+            i.state = (pa_sink_state_t) state;
 
             if (o->callback) {
                 pa_sink_info_cb_t cb = (pa_sink_info_cb_t) o->callback;
@@ -273,12 +278,15 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,
 
         while (!pa_tagstruct_eof(t)) {
             pa_source_info i;
+            pa_bool_t mute;
             uint32_t flags;
-            pa_bool_t mute = FALSE;
+            uint32_t state;
 
             memset(&i, 0, sizeof(i));
             i.proplist = pa_proplist_new();
             i.base_volume = PA_VOLUME_NORM;
+            mute = FALSE;
+            state = PA_SOURCE_INVALID_STATE;
 
             if (pa_tagstruct_getu32(t, &i.index) < 0 ||
                 pa_tagstruct_gets(t, &i.name) < 0 ||
@@ -297,7 +305,8 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,
                  (pa_tagstruct_get_proplist(t, i.proplist) < 0 ||
                   pa_tagstruct_get_usec(t, &i.configured_latency) < 0)) ||
                 (o->context->version >= 15 &&
-                 pa_tagstruct_get_volume(t, &i.base_volume) < 0)) {
+                 (pa_tagstruct_get_volume(t, &i.base_volume) < 0 ||
+                  pa_tagstruct_getu32(t, &state) < 0))) {
 
                 pa_context_fail(o->context, PA_ERR_PROTOCOL);
                 pa_proplist_free(i.proplist);
@@ -306,6 +315,7 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,
 
             i.mute = (int) mute;
             i.flags = (pa_source_flags_t) flags;
+            i.state = (pa_source_state_t) state;
 
             if (o->callback) {
                 pa_source_info_cb_t cb = (pa_source_info_cb_t) o->callback;
diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h
index a656d1c..07318c7 100644
--- a/src/pulse/introspect.h
+++ b/src/pulse/introspect.h
@@ -212,6 +212,7 @@ typedef struct pa_sink_info {
     pa_proplist *proplist;             /**< Property list \since 0.9.11 */
     pa_usec_t configured_latency;      /**< The latency this device has been configured to. \since 0.9.11 */
     pa_volume_t base_volume;           /**< Some kind of "base" volume that refers to unamplified/unattenuated volume in the context of the output device. \since 0.9.15 */
+    pa_sink_state_t state;             /**< State \since 0.9.15 */
 } pa_sink_info;
 
 /** Callback prototype for pa_context_get_sink_info_by_name() and friends */
@@ -268,6 +269,7 @@ typedef struct pa_source_info {
     pa_proplist *proplist;              /**< Property list \since 0.9.11 */
     pa_usec_t configured_latency;       /**< The latency this device has been configured to. \since 0.9.11 */
     pa_volume_t base_volume;           /**< Some kind of "base" volume that refers to unamplified/unattenuated volume in the context of the input device. \since 0.9.15 */
+    pa_source_state_t state;             /**< State \since 0.9.15 */
 } pa_source_info;
 
 /** Callback prototype for pa_context_get_source_info_by_name() and friends */
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index eb55505..a49d5df 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -2679,8 +2679,12 @@ static void sink_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sin
         pa_tagstruct_put_usec(t, pa_sink_get_requested_latency(sink));
     }
 
-    if (c->version >= 15)
+    if (c->version >= 15) {
         pa_tagstruct_put_volume(t, sink->base_volume);
+        if (PA_UNLIKELY(pa_sink_get_state(sink) == PA_SINK_INVALID_STATE))
+            pa_log_error("Internal sink state is invalid.");
+        pa_tagstruct_putu32(t, pa_sink_get_state(sink));
+    }
 }
 
 static void source_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_source *source) {
@@ -2713,8 +2717,12 @@ static void source_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_s
         pa_tagstruct_put_usec(t, pa_source_get_requested_latency(source));
     }
 
-    if (c->version >= 15)
+    if (c->version >= 15) {
         pa_tagstruct_put_volume(t, source->base_volume);
+        if (PA_UNLIKELY(pa_source_get_state(source) == PA_SOURCE_INVALID_STATE))
+            pa_log_error("Internal source state is invalid.");
+        pa_tagstruct_putu32(t, pa_source_get_state(source));
+    }
 }
 
 static void client_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_client *client) {
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index 507c160..382b2d0 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -27,6 +27,7 @@ typedef struct pa_sink pa_sink;
 
 #include <inttypes.h>
 
+#include <pulse/def.h>
 #include <pulse/sample.h>
 #include <pulse/channelmap.h>
 #include <pulse/volume.h>
@@ -42,18 +43,16 @@ typedef struct pa_sink pa_sink;
 
 #define PA_MAX_INPUTS_PER_SINK 32
 
-typedef enum pa_sink_state {
-    PA_SINK_INIT,
-    PA_SINK_RUNNING,
-    PA_SINK_SUSPENDED,
-    PA_SINK_IDLE,
-    PA_SINK_UNLINKED
-} pa_sink_state_t;
+/* anonymous enum extending pa_sink_state_t */
+enum {
+    PA_SINK_INIT = -2,
+    /* Initialization state */
 
-static inline pa_bool_t PA_SINK_IS_OPENED(pa_sink_state_t x) {
-    return x == PA_SINK_RUNNING || x == PA_SINK_IDLE;
-}
+    PA_SINK_UNLINKED = -3
+    /* The state when the sink is getting unregistered and removed from client access */
+};
 
+/* Returns true if sink is linked: registered and accessible from client side. */
 static inline pa_bool_t PA_SINK_IS_LINKED(pa_sink_state_t x) {
     return x == PA_SINK_RUNNING || x == PA_SINK_IDLE || x == PA_SINK_SUSPENDED;
 }
diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
index 4824099..369da43 100644
--- a/src/pulsecore/source.h
+++ b/src/pulsecore/source.h
@@ -45,18 +45,16 @@ typedef struct pa_source pa_source;
 
 #define PA_MAX_OUTPUTS_PER_SOURCE 32
 
-typedef enum pa_source_state {
-    PA_SOURCE_INIT,
-    PA_SOURCE_RUNNING,
-    PA_SOURCE_SUSPENDED,
-    PA_SOURCE_IDLE,
-    PA_SOURCE_UNLINKED
-} pa_source_state_t;
-
-static inline pa_bool_t PA_SOURCE_IS_OPENED(pa_source_state_t x) {
-    return x == PA_SOURCE_RUNNING || x == PA_SOURCE_IDLE;
-}
+/* anonymous enum extending pa_source_state_t */
+enum {
+    PA_SOURCE_INIT = -2,
+    /* Initialization state */
+
+    PA_SOURCE_UNLINKED = -3
+    /* The state when the source is getting unregistered and removed from client access */
+};
 
+/* Returns true if source is linked: registered and accessible from client side. */
 static inline pa_bool_t PA_SOURCE_IS_LINKED(pa_source_state_t x) {
     return x == PA_SOURCE_RUNNING || x == PA_SOURCE_IDLE || x == PA_SOURCE_SUSPENDED;
 }

commit 6374f8e427269c16afcd9d8a2536eec260d28ef6
Author: Marc-André Lureau <marcandre.lureau at gmail.com>
Date:   Thu Jan 15 17:28:33 2009 +0200

    sink: trigger subscribe event on sink state change

diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 2abc848..bd90cf5 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -311,8 +311,10 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
                 i->suspend(i, state == PA_SINK_SUSPENDED);
     }
 
-    if (state != PA_SINK_UNLINKED) /* if we enter UNLINKED state pa_sink_unlink() will fire the apropriate events */
+    if (state != PA_SINK_UNLINKED) { /* if we enter UNLINKED state pa_sink_unlink() will fire the apropriate events */
         pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], s);
+        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+    }
 
     return 0;
 }

commit a3762a2f9836b1a5f94eb0c69b24bd1839cb1205
Author: Marc-André Lureau <marcandre.lureau at gmail.com>
Date:   Thu Jan 15 20:58:04 2009 +0200

    cli: fix broken array access with signed state enums
    
    I wish I could have merge sink_to_string and source_to_string, but the
    enum values are equal, and we cannot assume they will always be.

diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index 7962039..b97c870 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -150,17 +150,44 @@ char *pa_card_list_to_string(pa_core *c) {
     return pa_strbuf_tostring_free(s);
 }
 
+static const char *sink_state_to_string(pa_sink_state_t state) {
+    switch (state) {
+        case PA_SINK_INIT:
+            return "INIT";
+        case PA_SINK_RUNNING:
+            return "RUNNING";
+        case PA_SINK_SUSPENDED:
+            return "SUSPENDED";
+        case PA_SINK_IDLE:
+            return "IDLE";
+        case PA_SINK_UNLINKED:
+            return "UNLINKED";
+        default:
+            return "INVALID";
+    }
+}
+
+static const char *source_state_to_string(pa_source_state_t state) {
+    switch (state) {
+        case PA_SOURCE_INIT:
+            return "INIT";
+        case PA_SOURCE_RUNNING:
+            return "RUNNING";
+        case PA_SOURCE_SUSPENDED:
+            return "SUSPENDED";
+        case PA_SOURCE_IDLE:
+            return "IDLE";
+        case PA_SOURCE_UNLINKED:
+            return "UNLINKED";
+        default:
+            return "INVALID";
+    }
+}
+
 char *pa_sink_list_to_string(pa_core *c) {
     pa_strbuf *s;
     pa_sink *sink;
     uint32_t idx = PA_IDXSET_INVALID;
-    static const char* const state_table[] = {
-        [PA_SINK_INIT] = "INIT",
-        [PA_SINK_RUNNING] = "RUNNING",
-        [PA_SINK_SUSPENDED] = "SUSPENDED",
-        [PA_SINK_IDLE] = "IDLE",
-        [PA_SINK_UNLINKED] = "UNLINKED"
-    };
     pa_assert(c);
 
     s = pa_strbuf_new();
@@ -208,7 +235,7 @@ char *pa_sink_list_to_string(pa_core *c) {
             sink->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
             sink->flags & PA_SINK_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
             sink->flags & PA_SINK_LATENCY ? "LATENCY " : "",
-            state_table[pa_sink_get_state(sink)],
+            sink_state_to_string(pa_sink_get_state(sink)),
             pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink, FALSE)),
             sink->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t        " : "",
             sink->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), pa_sink_get_volume(sink, FALSE)) : "",
@@ -246,13 +273,6 @@ char *pa_source_list_to_string(pa_core *c) {
     pa_strbuf *s;
     pa_source *source;
     uint32_t idx = PA_IDXSET_INVALID;
-    static const char* const state_table[] = {
-        [PA_SOURCE_INIT] = "INIT",
-        [PA_SOURCE_RUNNING] = "RUNNING",
-        [PA_SOURCE_SUSPENDED] = "SUSPENDED",
-        [PA_SOURCE_IDLE] = "IDLE",
-        [PA_SOURCE_UNLINKED] = "UNLINKED"
-    };
     pa_assert(c);
 
     s = pa_strbuf_new();
@@ -298,7 +318,7 @@ char *pa_source_list_to_string(pa_core *c) {
             source->flags & PA_SOURCE_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
             source->flags & PA_SOURCE_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
             source->flags & PA_SOURCE_LATENCY ? "LATENCY " : "",
-            state_table[pa_source_get_state(source)],
+            source_state_to_string(pa_source_get_state(source)),
             pa_cvolume_snprint(cv, sizeof(cv), pa_source_get_volume(source, FALSE)),
             source->flags & PA_SOURCE_DECIBEL_VOLUME ? "\n\t        " : "",
             source->flags & PA_SOURCE_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), pa_source_get_volume(source, FALSE)) : "",

commit 310f433848b21a53def0a5a5ca0b3b2e6a2008ef
Author: Marc-André Lureau <marcandre.lureau at gmail.com>
Date:   Fri Jan 16 15:53:27 2009 +0200

    pulse: share private enum values with client side

diff --git a/src/pulse/def.h b/src/pulse/def.h
index 7dc753f..8e8857c 100644
--- a/src/pulse/def.h
+++ b/src/pulse/def.h
@@ -624,9 +624,17 @@ typedef enum pa_sink_state { /* enum serialized in u8 */
     PA_SINK_IDLE = 1,
     /**< When idle, the sink is playing but there is no non-corked sink-input attached to it \since 0.9.15 */
 
-    PA_SINK_SUSPENDED = 2
+    PA_SINK_SUSPENDED = 2,
     /**< When suspended, actual sink access can be closed, for instance \since 0.9.15 */
 
+    /* *** PRIVATE: server-side values *** */
+
+    PA_SINK_INIT = -2,
+    /* Initialization state */
+
+    PA_SINK_UNLINKED = -3
+    /* The state when the sink is getting unregistered and removed from client access */
+
 } pa_sink_state_t;
 
 /** Returns non-zero if sink is playing: running or idle. \since 0.9.15 */
@@ -685,9 +693,17 @@ typedef enum pa_source_state {
     PA_SOURCE_IDLE = 1,
     /**< When idle, the source is still recording but there is no non-corked source-output \since 0.9.15 */
 
-    PA_SOURCE_SUSPENDED = 2
+    PA_SOURCE_SUSPENDED = 2,
     /**< When suspended, actual source access can be closed, for instance \since 0.9.15 */
 
+    /* *** PRIVATE: server-side values *** */
+
+    PA_SOURCE_INIT = -2,
+    /* Initialization state */
+
+    PA_SOURCE_UNLINKED = -3
+    /* The state when the source is getting unregistered and removed from client access */
+
 } pa_source_state_t;
 
 /** Returns non-zero if source is recording: running or idle. \since 0.9.15 */
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index 382b2d0..89ed6d4 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -43,15 +43,6 @@ typedef struct pa_sink pa_sink;
 
 #define PA_MAX_INPUTS_PER_SINK 32
 
-/* anonymous enum extending pa_sink_state_t */
-enum {
-    PA_SINK_INIT = -2,
-    /* Initialization state */
-
-    PA_SINK_UNLINKED = -3
-    /* The state when the sink is getting unregistered and removed from client access */
-};
-
 /* Returns true if sink is linked: registered and accessible from client side. */
 static inline pa_bool_t PA_SINK_IS_LINKED(pa_sink_state_t x) {
     return x == PA_SINK_RUNNING || x == PA_SINK_IDLE || x == PA_SINK_SUSPENDED;
diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
index 369da43..336599d 100644
--- a/src/pulsecore/source.h
+++ b/src/pulsecore/source.h
@@ -45,15 +45,6 @@ typedef struct pa_source pa_source;
 
 #define PA_MAX_OUTPUTS_PER_SOURCE 32
 
-/* anonymous enum extending pa_source_state_t */
-enum {
-    PA_SOURCE_INIT = -2,
-    /* Initialization state */
-
-    PA_SOURCE_UNLINKED = -3
-    /* The state when the source is getting unregistered and removed from client access */
-};
-
 /* Returns true if source is linked: registered and accessible from client side. */
 static inline pa_bool_t PA_SOURCE_IS_LINKED(pa_source_state_t x) {
     return x == PA_SOURCE_RUNNING || x == PA_SOURCE_IDLE || x == PA_SOURCE_SUSPENDED;

commit 8886e66ff68f5f23a2cacb06df6cb85a0175b7f1
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Jan 19 23:54:44 2009 +0100

    Document explicitly that the internal sink/source states are not considered part of the ABI/API

diff --git a/src/pulse/def.h b/src/pulse/def.h
index 8e8857c..21a02bc 100644
--- a/src/pulse/def.h
+++ b/src/pulse/def.h
@@ -627,13 +627,19 @@ typedef enum pa_sink_state { /* enum serialized in u8 */
     PA_SINK_SUSPENDED = 2,
     /**< When suspended, actual sink access can be closed, for instance \since 0.9.15 */
 
-    /* *** PRIVATE: server-side values *** */
+/** \cond fulldocs */
+    /* PRIVATE: Server-side values -- DO NOT USE THIS ON THE CLIENT
+     * SIDE! These values are *not* considered part of the official PA
+     * API/ABI. If you use them your application might break when PA
+     * is upgraded. Also, please note that these values are not useful
+     * on the client side anyway. */
 
     PA_SINK_INIT = -2,
-    /* Initialization state */
+    /**< Initialization state */
 
     PA_SINK_UNLINKED = -3
-    /* The state when the sink is getting unregistered and removed from client access */
+    /**< The state when the sink is getting unregistered and removed from client access */
+/** \endcond */
 
 } pa_sink_state_t;
 
@@ -696,13 +702,19 @@ typedef enum pa_source_state {
     PA_SOURCE_SUSPENDED = 2,
     /**< When suspended, actual source access can be closed, for instance \since 0.9.15 */
 
-    /* *** PRIVATE: server-side values *** */
+/** \cond fulldocs */
+    /* PRIVATE: Server-side values -- DO NOT USE THIS ON THE CLIENT
+     * SIDE! These values are *not* considered part of the official PA
+     * API/ABI. If you use them your application might break when PA
+     * is upgraded. Also, please note that these values are not useful
+     * on the client side anyway. */
 
     PA_SOURCE_INIT = -2,
-    /* Initialization state */
+    /**< Initialization state */
 
     PA_SOURCE_UNLINKED = -3
-    /* The state when the source is getting unregistered and removed from client access */
+    /**< The state when the source is getting unregistered and removed from client access */
+/** \endcond */
 
 } pa_source_state_t;
 

commit 8c4e2be05b519c54cb51c161b7cd08bf9e46ab2b
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Jan 19 23:55:22 2009 +0100

    include sink/source state in pactl output

diff --git a/src/utils/pactl.c b/src/utils/pactl.c
index 1ff979e..dbc9ba7 100644
--- a/src/utils/pactl.c
+++ b/src/utils/pactl.c
@@ -153,6 +153,14 @@ static void get_server_info_callback(pa_context *c, const pa_server_info *i, voi
 }
 
 static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata) {
+
+    static const char *state_table[] = {
+        [1+PA_SINK_INVALID_STATE] = "n/a",
+        [1+PA_SINK_RUNNING] = "RUNNING",
+        [1+PA_SINK_IDLE] = "IDLE",
+        [1+PA_SINK_SUSPENDED] = "SUSPENDED"
+    };
+
     char
         s[PA_SAMPLE_SPEC_SNPRINT_MAX],
         cv[PA_CVOLUME_SNPRINT_MAX],
@@ -180,6 +188,7 @@ static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_
     nl = 1;
 
     printf(_("Sink #%u\n"
+             "\tState: %s\n"
              "\tName: %s\n"
              "\tDescription: %s\n"
              "\tDriver: %s\n"
@@ -195,6 +204,7 @@ static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_
              "\tFlags: %s%s%s%s%s%s\n"
              "\tProperties:\n\t\t%s\n"),
            i->index,
+           state_table[1+i->state],
            i->name,
            pa_strnull(i->description),
            pa_strnull(i->driver),
@@ -223,6 +233,14 @@ static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_
 }
 
 static void get_source_info_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) {
+
+    static const char *state_table[] = {
+        [1+PA_SOURCE_INVALID_STATE] = "n/a",
+        [1+PA_SOURCE_RUNNING] = "RUNNING",
+        [1+PA_SOURCE_IDLE] = "IDLE",
+        [1+PA_SOURCE_SUSPENDED] = "SUSPENDED"
+    };
+
     char
         s[PA_SAMPLE_SPEC_SNPRINT_MAX],
         cv[PA_CVOLUME_SNPRINT_MAX],
@@ -250,6 +268,7 @@ static void get_source_info_callback(pa_context *c, const pa_source_info *i, int
     nl = 1;
 
     printf(_("Source #%u\n"
+             "\tState: %s\n"
              "\tName: %s\n"
              "\tDescription: %s\n"
              "\tDriver: %s\n"
@@ -265,6 +284,7 @@ static void get_source_info_callback(pa_context *c, const pa_source_info *i, int
              "\tFlags: %s%s%s%s%s%s\n"
              "\tProperties:\n\t\t%s\n"),
            i->index,
+           state_table[1+i->state],
            i->name,
            pa_strnull(i->description),
            pa_strnull(i->driver),

commit 8839d8667240fb4204410f4470eb8cd53e5b9e3c
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 20 00:14:50 2009 +0100

    remove misplaced whitespace

diff --git a/src/pulse/def.h b/src/pulse/def.h
index 21a02bc..fb1bd27 100644
--- a/src/pulse/def.h
+++ b/src/pulse/def.h
@@ -234,7 +234,7 @@ typedef enum pa_stream_flags {
     PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND = 0x8000U,
     /**< If set this stream won't be taken into account when we it is
      * checked whether the device this stream is connected to should
-     * auto-suspend. \ since 0.9.15 */
+     * auto-suspend. \since 0.9.15 */
 
     PA_STREAM_START_UNMUTED = 0x10000U
     /**< Create in unmuted state. If neither PA_STREAM_START_UNMUTED

commit d5e895d5cbf9a9a658c66347cb034f1c97133280
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 20 00:49:45 2009 +0100

    document that I am a retard

diff --git a/src/pulsecore/tagstruct.h b/src/pulsecore/tagstruct.h
index 19288ee..1a99e92 100644
--- a/src/pulsecore/tagstruct.h
+++ b/src/pulsecore/tagstruct.h
@@ -37,6 +37,10 @@
 
 typedef struct pa_tagstruct pa_tagstruct;
 
+/* Due to a stupid design flaw, proplists may only be at the END of a
+ * packet or not before a STRING! Don't forget that! We can't really
+ * fix this without breaking compat. */
+
 enum {
     PA_TAG_INVALID = 0,
     PA_TAG_STRING = 't',

commit bd70e8053165bdee620356cdff02c3979d7138e7
Author: Diego E. 'Flameeyes' Pettenò <flameeyes at gmail.com>
Date:   Tue Jan 20 00:55:39 2009 +0100

    Allow to opt-out from building tests.
    
    Since the tests are only useful either if you're hacking at pulseaudio as
    a developer, or when running "make check", allow users to opt-out from
    their build.
    
    This for instance allows for Gentoo users not to build the tests when
    installing the ebuild with tests disabled, and also allow for skipping over
    eventually broken tests when trying to get the basic build going on a port.

diff --git a/configure.ac b/configure.ac
index 8586dcd..411a14d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -101,6 +101,12 @@ for flag in $DESIRED_FLAGS ; do
   CC_CHECK_CFLAGS([$flag], [CFLAGS="$CFLAGS $flag"])
 done
 
+dnl Check whether to build tests by default (as compile-test) or not
+AC_ARG_ENABLE([default-build-tests],
+    AS_HELP_STRING([--disable-default-build-tests], [Build test programs only during make check]))
+
+AM_CONDITIONAL([BUILD_TESTS_DEFAULT], [test "x$enable_default_build_tests" = "xno"])
+
 # Native atomic operation support
 AC_ARG_ENABLE([atomic-arm-linux-helpers],
     AS_HELP_STRING([--disable-atomic-arm-linux-helpers],[use inline asm or libatomic_ops instead]),
diff --git a/src/Makefile.am b/src/Makefile.am
index e8f0a82..fbe9e91 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -253,7 +253,7 @@ TESTS = \
 		lock-autospawn-test \
 		prioq-test
 
-noinst_PROGRAMS = \
+TESTS_BINARIES = \
 		mainloop-test \
 		mcalign-test \
 		pacat-simple \
@@ -293,7 +293,7 @@ if HAVE_SIGXCPU
 #TESTS += \
 #		cpulimit-test \
 #		cpulimit-test2
-noinst_PROGRAMS += \
+TESTS_BINARIES += \
 		cpulimit-test \
 		cpulimit-test2
 endif
@@ -301,10 +301,16 @@ endif
 if HAVE_GLIB20
 TESTS += \
 		mainloop-test-glib
-noinst_PROGRAMS += \
+TESTS_BINARIES += \
 		mainloop-test-glib
 endif
 
+if BUILD_TESTS_DEFAULT
+noinst_PROGRAMS = $(TESTS_BINARIES)
+else
+check_PROGRAMS = $(TESTS_BINARIES)
+endif
+
 mainloop_test_SOURCES = tests/mainloop-test.c
 mainloop_test_CFLAGS = $(AM_CFLAGS)
 mainloop_test_LDADD = $(AM_LDADD) libpulse.la

commit bc41fdb20d04ad7f42d5ff28f32a9b3ff3ca52f3
Author: Diego E. 'Flameeyes' Pettenò <flameeyes at gmail.com>
Date:   Tue Jan 20 00:55:42 2009 +0100

    Include the alsa/ subdirectory for modules in the search path.
    
    Without this, out of tree builds fails.

diff --git a/src/Makefile.am b/src/Makefile.am
index fbe9e91..17e14be 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -56,6 +56,8 @@ AM_CFLAGS = \
 	-I$(top_builddir)/src/modules/gconf \
 	-I$(top_srcdir)/src/modules/bluetooth \
 	-I$(top_builddir)/src/modules/bluetooth \
+	-I$(top_srcdir)/src/modules/alsa \
+	-I$(top_builddir)/src/modules/alsa \
 	-I$(top_srcdir)/src/modules/raop \
 	$(PTHREAD_CFLAGS) -D_POSIX_PTHREAD_SEMANTICS \
 	$(LTDLINCL) \

commit 47a2f9e3da9a66a308f311340410640f2f4cb28f
Merge: d5e895d... bc41fdb...
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 20 00:55:36 2009 +0100

    Merge commit 'flameeyes/buildfixes-2'


commit b23efc0a4d409792c49110d01377f4d76f3aef25
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 20 02:15:43 2009 +0100

    add missing eof checks

diff --git a/src/pulse/context.c b/src/pulse/context.c
index 3145d9c..d41e62e 100644
--- a/src/pulse/context.c
+++ b/src/pulse/context.c
@@ -366,7 +366,8 @@ int pa_context_handle_error(pa_context *c, uint32_t command, pa_tagstruct *t, pa
     if (command == PA_COMMAND_ERROR) {
         pa_assert(t);
 
-        if (pa_tagstruct_getu32(t, &err) < 0) {
+        if (pa_tagstruct_getu32(t, &err) < 0 ||
+            !pa_tagstruct_eof(t)) {
             pa_context_fail(c, PA_ERR_PROTOCOL);
             return -1;
         }
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index a49d5df..f1735a7 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -2484,7 +2484,9 @@ static void command_create_upload_stream(pa_pdispatch *pd, uint32_t command, uin
 
     p = pa_proplist_new();
 
-    if (c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0) {
+    if ((c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0) ||
+        !pa_tagstruct_eof(t)) {
+
         protocol_error(c);
         pa_proplist_free(p);
         return;

commit 67fcc760930960b5f7494287ce3ad422de31f99c
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 20 03:23:32 2009 +0100

    fix profile names to include input/output specifier

diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
index 2cc8a15..1e3c0c1 100644
--- a/src/modules/alsa/module-alsa-card.c
+++ b/src/modules/alsa/module-alsa-card.c
@@ -90,14 +90,15 @@ static void enumerate_cb(
     struct profile_data *d;
 
     if (sink && source) {
-        n = pa_sprintf_malloc("%s+%s", sink->name, source->name);
+        n = pa_sprintf_malloc("output-%s+input-%s", sink->name, source->name);
         t = pa_sprintf_malloc("Output %s + Input %s", sink->description, source->description);
     } else if (sink) {
-        n = pa_xstrdup(sink->name);
+        n = pa_sprintf_malloc("output-%s", sink->name);
         t = pa_sprintf_malloc("Output %s", sink->description);
     } else {
         pa_assert(source);
         n = pa_xstrdup(source->name);
+        n = pa_sprintf_malloc("input-%s", source->name);
         t = pa_sprintf_malloc("Input %s", source->description);
     }
 

commit 936862362c30c22f42f33bd717b5f915f9c04657
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 20 03:24:11 2009 +0100

    don't divide by zero if no left resp. no right channels are defined

diff --git a/src/pulse/volume.c b/src/pulse/volume.c
index 9191a07..10a44da 100644
--- a/src/pulse/volume.c
+++ b/src/pulse/volume.c
@@ -424,8 +424,15 @@ static void get_avg_lr(const pa_channel_map *map, const pa_cvolume *v, pa_volume
         }
     }
 
-    *l = left / n_left;
-    *r = right / n_right;
+    if (n_left <= 0)
+        *l = PA_VOLUME_NORM;
+    else
+        *l = left / n_left;
+
+    if (n_right <= 0)
+        *r = PA_VOLUME_NORM;
+    else
+        *r = right / n_right;
 }
 
 float pa_cvolume_get_balance(const pa_channel_map *map, const pa_cvolume *v) {

commit 7aa7a7b6ac39371bbc7cd46ddf5d50ffd6e2a965
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 20 03:24:40 2009 +0100

    fix destruction when no profiles are defined

diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c
index ec4a50c..8e29f0a 100644
--- a/src/pulsecore/card.c
+++ b/src/pulsecore/card.c
@@ -165,10 +165,12 @@ void pa_card_free(pa_card *c) {
     pa_assert(pa_idxset_isempty(c->sources));
     pa_idxset_free(c->sources, NULL, NULL);
 
-    while ((profile = pa_hashmap_steal_first(c->profiles)))
-        pa_card_profile_free(profile);
+    if (c->profiles) {
+        while ((profile = pa_hashmap_steal_first(c->profiles)))
+            pa_card_profile_free(profile);
 
-    pa_hashmap_free(c->profiles, NULL, NULL);
+        pa_hashmap_free(c->profiles, NULL, NULL);
+    }
 
     pa_proplist_free(c->proplist);
     pa_xfree(c->driver);

commit 85bc5eb39aa19d0b6fab26d388415a3c066043f0
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 20 03:24:59 2009 +0100

    dump active profile

diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index b97c870..f52968d 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -145,6 +145,11 @@ char *pa_card_list_to_string(pa_core *c) {
                 pa_strbuf_printf(s, "\t\t%s: %s\n", p->name, p->description);
         }
 
+        if (card->active_profile)
+            pa_strbuf_printf(
+                    s,
+                    "\tactive profile: <%s>\n",
+                    card->active_profile->name);
     }
 
     return pa_strbuf_tostring_free(s);

commit a65c2c73369e6b2ce9350dfb542f5d529de38334
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 20 03:25:29 2009 +0100

    add client API for querying card information

diff --git a/src/map-file b/src/map-file
index 9c9e1c7..042e2ad 100644
--- a/src/map-file
+++ b/src/map-file
@@ -30,6 +30,9 @@ pa_context_exit_daemon;
 pa_context_get_autoload_info_by_index;
 pa_context_get_autoload_info_by_name;
 pa_context_get_autoload_info_list;
+pa_context_get_card_info_by_index;
+pa_context_get_card_info_by_name;
+pa_context_get_card_info_list;
 pa_context_get_client_info;
 pa_context_get_client_info_list;
 pa_context_get_index;
@@ -73,6 +76,8 @@ pa_context_ref;
 pa_context_remove_autoload_by_index;
 pa_context_remove_autoload_by_name;
 pa_context_remove_sample;
+pa_context_set_card_profile_by_index;
+pa_context_set_card_profile_by_name;
 pa_context_set_default_sink;
 pa_context_set_default_source;
 pa_context_set_name;
diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c
index a6a228e..c5c9678 100644
--- a/src/pulse/introspect.c
+++ b/src/pulse/introspect.c
@@ -28,8 +28,10 @@
 
 #include <pulse/context.h>
 #include <pulse/gccmacro.h>
+#include <pulse/xmalloc.h>
 
 #include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
 #include <pulsecore/pstream-util.h>
 
 #include "internal.h"
@@ -60,7 +62,8 @@ static void context_stat_callback(pa_pdispatch *pd, uint32_t command, uint32_t t
                pa_tagstruct_getu32(t, &i.memblock_total_size) < 0 ||
                pa_tagstruct_getu32(t, &i.memblock_allocated) < 0 ||
                pa_tagstruct_getu32(t, &i.memblock_allocated_size) < 0 ||
-               pa_tagstruct_getu32(t, &i.scache_size) < 0) {
+               pa_tagstruct_getu32(t, &i.scache_size) < 0 ||
+               !pa_tagstruct_eof(t)) {
         pa_context_fail(o->context, PA_ERR_PROTOCOL);
         goto finish;
     }
@@ -467,6 +470,201 @@ pa_operation* pa_context_get_client_info_list(pa_context *c, pa_client_info_cb_t
     return pa_context_send_simple_command(c, PA_COMMAND_GET_CLIENT_INFO_LIST, context_get_client_info_callback, (pa_operation_cb_t) cb, userdata);
 }
 
+/*** Card info ***/
+
+static void context_get_card_info_callback(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_card_info i;
+            uint32_t j;
+            const char*ap;
+
+            memset(&i, 0, sizeof(i));
+
+            if (pa_tagstruct_getu32(t, &i.index) < 0 ||
+                pa_tagstruct_gets(t, &i.name) < 0 ||
+                pa_tagstruct_getu32(t, &i.owner_module) < 0 ||
+                pa_tagstruct_gets(t, &i.driver) < 0 ||
+                pa_tagstruct_getu32(t, &i.n_profiles) < 0) {
+
+                pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+
+            if (i.n_profiles > 0) {
+                i.profiles = pa_xnew(pa_card_profile_info, i.n_profiles+1);
+
+                for (j = 0; j < i.n_profiles; j++) {
+
+                    if (pa_tagstruct_gets(t, &i.profiles[j].name) < 0 ||
+                        pa_tagstruct_gets(t, &i.profiles[j].description) < 0) {
+
+                        pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                        pa_xfree(i.profiles);
+                        goto finish;
+                    }
+                }
+
+                /* Terminate with an extra NULL entry, just to make sure */
+                i.profiles[j].name = NULL;
+                i.profiles[j].description = NULL;
+            }
+
+            i.proplist = pa_proplist_new();
+
+            if (pa_tagstruct_gets(t, &ap) < 0 ||
+                pa_tagstruct_get_proplist(t, i.proplist) < 0) {
+
+                pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                pa_xfree(i.profiles);
+                pa_proplist_free(i.proplist);
+                goto finish;
+            }
+
+            if (ap) {
+                for (j = 0; j < i.n_profiles; j++)
+                    if (pa_streq(i.profiles[j].name, ap)) {
+                        i.active_profile = &i.profiles[j];
+                        break;
+                    }
+            }
+
+            if (o->callback) {
+                pa_card_info_cb_t cb = (pa_card_info_cb_t) o->callback;
+                cb(o->context, &i, 0, o->userdata);
+            }
+
+            pa_proplist_free(i.proplist);
+            pa_xfree(i.profiles);
+        }
+    }
+
+    if (o->callback) {
+        pa_card_info_cb_t cb = (pa_card_info_cb_t) o->callback;
+        cb(o->context, NULL, eol, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation* pa_context_get_card_info_by_index(pa_context *c, uint32_t idx, pa_card_info_cb_t cb, void *userdata) {
+    pa_tagstruct *t;
+    pa_operation *o;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(cb);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_GET_CARD_INFO, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_puts(t, NULL);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_card_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_get_card_info_by_name(pa_context *c, const char*name, pa_card_info_cb_t cb, void *userdata) {
+    pa_tagstruct *t;
+    pa_operation *o;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(cb);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_GET_CARD_INFO, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, name);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_card_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_get_card_info_list(pa_context *c, pa_card_info_cb_t cb, void *userdata) {
+    return pa_context_send_simple_command(c, PA_COMMAND_GET_CARD_INFO_LIST, context_get_card_info_callback, (pa_operation_cb_t) cb, userdata);
+}
+
+pa_operation* pa_context_set_card_profile_by_index(pa_context *c, uint32_t idx, const char*profile, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_CARD_PROFILE, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_puts(t, NULL);
+    pa_tagstruct_puts(t, profile);
+    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_context_set_card_profile_by_name(pa_context *c, const char *name, const char*profile, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !name || *name, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_CARD_PROFILE, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, name);
+    pa_tagstruct_puts(t, profile);
+    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;
+}
+
 /*** Module info ***/
 
 static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h
index 07318c7..972b454 100644
--- a/src/pulse/introspect.h
+++ b/src/pulse/introspect.h
@@ -222,7 +222,7 @@ typedef void (*pa_sink_info_cb_t)(pa_context *c, const pa_sink_info *i, int eol,
 pa_operation* pa_context_get_sink_info_by_name(pa_context *c, const char *name, pa_sink_info_cb_t cb, void *userdata);
 
 /** Get information about a sink by its index */
-pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t id, pa_sink_info_cb_t cb, void *userdata);
+pa_operation* pa_context_get_sink_info_by_index(pa_context *c, uint32_t idx, pa_sink_info_cb_t cb, void *userdata);
 
 /** Get the complete sink list */
 pa_operation* pa_context_get_sink_info_list(pa_context *c, pa_sink_info_cb_t cb, void *userdata);
@@ -279,7 +279,7 @@ typedef void (*pa_source_info_cb_t)(pa_context *c, const pa_source_info *i, int
 pa_operation* pa_context_get_source_info_by_name(pa_context *c, const char *name, pa_source_info_cb_t cb, void *userdata);
 
 /** Get information about a source by its index */
-pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t id, pa_source_info_cb_t cb, void *userdata);
+pa_operation* pa_context_get_source_info_by_index(pa_context *c, uint32_t idx, pa_source_info_cb_t cb, void *userdata);
 
 /** Get the complete source list */
 pa_operation* pa_context_get_source_info_list(pa_context *c, pa_source_info_cb_t cb, void *userdata);
@@ -385,6 +385,50 @@ pa_operation* pa_context_kill_client(pa_context *c, uint32_t idx, pa_context_suc
 
 /** @} */
 
+/** @{ \name Cards */
+
+/** Stores information about a specific profile of a card.  Please
+ * note that this structure can be extended as part of evolutionary
+ * API updates at any time in any new release. \since 0.9.15 */
+typedef struct pa_card_profile_info {
+    const char *name;                   /**< Name of this profile */
+    const char *description;            /**< Description of this profile */
+} pa_card_profile_info;
+
+/** Stores information about cards. Please note that this structure
+ * can be extended as part of evolutionary API updates at any time in
+ * any new release.  \since 0.9.15 */
+typedef struct pa_card_info {
+    uint32_t index;                      /**< Index of this card */
+    const char *name;                    /**< Name of this card */
+    uint32_t owner_module;               /**< Index of the owning module, or PA_INVALID_INDEX */
+    const char *driver;                  /**< Driver name */
+    uint32_t n_profiles;                 /**< Number of entries in profile array */
+    pa_card_profile_info* profiles;      /**< Array of available profile, or NULL. Array is terminated by an entry with name set to NULL. Number of entries is stored in n_profiles */
+    pa_card_profile_info* active_profile; /**< Pointer to active profile in the array, or NULL */
+    pa_proplist *proplist;               /**< Property list */
+} pa_card_info;
+
+/** Callback prototype for pa_context_get_card_info() and firends \since 0.9.15 */
+typedef void (*pa_card_info_cb_t) (pa_context *c, const pa_card_info*i, int eol, void *userdata);
+
+/** Get information about a card by its index \since 0.9.15 */
+pa_operation* pa_context_get_card_info_by_index(pa_context *c, uint32_t idx, pa_card_info_cb_t cb, void *userdata);
+
+/** Get information about a card by its name \since 0.9.15 */
+pa_operation* pa_context_get_card_info_by_name(pa_context *c, const char *name, pa_card_info_cb_t cb, void *userdata);
+
+/** Get the complete card list \since 0.9.15 */
+pa_operation* pa_context_get_card_info_list(pa_context *c, pa_card_info_cb_t cb, void *userdata);
+
+/** Change the profile of a card. \since 0.9.15 */
+pa_operation* pa_context_set_card_profile_by_index(pa_context *c, uint32_t idx, const char*profile, pa_context_success_cb_t cb, void *userdata);
+
+/** Change the profile of a card. \since 0.9.15 */
+pa_operation* pa_context_set_card_profile_by_name(pa_context *c, const char*name, const char*profile, pa_context_success_cb_t cb, void *userdata);
+
+/** @} */
+
 /** @{ \name Sink Inputs */
 
 /** Stores information about sink inputs. Please note that this structure
diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h
index 11643a7..b31a5da 100644
--- a/src/pulsecore/native-common.h
+++ b/src/pulsecore/native-common.h
@@ -152,6 +152,11 @@ enum {
     /* Supported since protocol v14 (0.9.12) */
     PA_COMMAND_EXTENSION,
 
+    /* Supported since protocol v15 (0.9.15*/
+    PA_COMMAND_GET_CARD_INFO,
+    PA_COMMAND_GET_CARD_INFO_LIST,
+    PA_COMMAND_SET_CARD_PROFILE,
+
     PA_COMMAND_MAX
 };
 
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index f1735a7..87cf583 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -283,6 +283,7 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
     [PA_COMMAND_GET_SINK_INFO] = command_get_info,
     [PA_COMMAND_GET_SOURCE_INFO] = command_get_info,
     [PA_COMMAND_GET_CLIENT_INFO] = command_get_info,
+    [PA_COMMAND_GET_CARD_INFO] = command_get_info,
     [PA_COMMAND_GET_MODULE_INFO] = command_get_info,
     [PA_COMMAND_GET_SINK_INPUT_INFO] = command_get_info,
     [PA_COMMAND_GET_SOURCE_OUTPUT_INFO] = command_get_info,
@@ -291,6 +292,7 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
     [PA_COMMAND_GET_SOURCE_INFO_LIST] = command_get_info_list,
     [PA_COMMAND_GET_MODULE_INFO_LIST] = command_get_info_list,
     [PA_COMMAND_GET_CLIENT_INFO_LIST] = command_get_info_list,
+    [PA_COMMAND_GET_CARD_INFO_LIST] = command_get_info_list,
     [PA_COMMAND_GET_SINK_INPUT_INFO_LIST] = command_get_info_list,
     [PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST] = command_get_info_list,
     [PA_COMMAND_GET_SAMPLE_INFO_LIST] = command_get_info_list,
@@ -2740,6 +2742,31 @@ static void client_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_c
         pa_tagstruct_put_proplist(t, client->proplist);
 }
 
+static void card_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_card *card) {
+    void *state = NULL;
+    pa_card_profile *p;
+
+    pa_assert(t);
+    pa_assert(card);
+
+    pa_tagstruct_putu32(t, card->index);
+    pa_tagstruct_puts(t, card->name);
+    pa_tagstruct_putu32(t, card->module ? card->module->index : PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, card->driver);
+
+    pa_tagstruct_putu32(t, card->profiles ? pa_hashmap_size(card->profiles) : 0);
+
+    if (card->profiles) {
+        while ((p = pa_hashmap_iterate(card->profiles, &state, NULL))) {
+            pa_tagstruct_puts(t, p->name);
+            pa_tagstruct_puts(t, p->description);
+        }
+    }
+
+    pa_tagstruct_puts(t, card->active_profile ? card->active_profile->name : NULL);
+    pa_tagstruct_put_proplist(t, card->proplist);
+}
+
 static void module_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_module *module) {
     pa_assert(t);
     pa_assert(module);
@@ -2839,6 +2866,8 @@ static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, p
     pa_sink *sink = NULL;
     pa_source *source = NULL;
     pa_client *client = NULL;
+    pa_card *card = NULL;
+    pa_core *core = NULL;
     pa_module *module = NULL;
     pa_sink_input *si = NULL;
     pa_source_output *so = NULL;
@@ -2851,6 +2880,7 @@ static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, p
 
     if (pa_tagstruct_getu32(t, &idx) < 0 ||
         (command != PA_COMMAND_GET_CLIENT_INFO &&
+         command != PA_COMMAND_GET_CARD_INFO &&
          command != PA_COMMAND_GET_MODULE_INFO &&
          command != PA_COMMAND_GET_SINK_INPUT_INFO &&
          command != PA_COMMAND_GET_SOURCE_OUTPUT_INFO &&
@@ -2876,6 +2906,11 @@ static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, p
             source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
         else
             source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE);
+    } else if (command == PA_COMMAND_GET_CARD_INFO) {
+        if (idx != PA_INVALID_INDEX)
+            card = pa_idxset_get_by_index(c->protocol->core->cards, idx);
+        else
+            card = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_CARD);
     } else if (command == PA_COMMAND_GET_CLIENT_INFO)
         client = pa_idxset_get_by_index(c->protocol->core->clients, idx);
     else if (command == PA_COMMAND_GET_MODULE_INFO)
@@ -2892,7 +2927,7 @@ static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, p
             sce = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SAMPLE);
     }
 
-    if (!sink && !source && !client && !module && !si && !so && !sce) {
+    if (!sink && !source && !client && !card && !module && !si && !so && !sce) {
         pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
         return;
     }
@@ -2904,6 +2939,8 @@ static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, p
         source_fill_tagstruct(c, reply, source);
     else if (client)
         client_fill_tagstruct(c, reply, client);
+    else if (client)
+        card_fill_tagstruct(c, reply, card);
     else if (module)
         module_fill_tagstruct(c, reply, module);
     else if (si)
@@ -2940,6 +2977,8 @@ static void command_get_info_list(pa_pdispatch *pd, uint32_t command, uint32_t t
         i = c->protocol->core->sources;
     else if (command == PA_COMMAND_GET_CLIENT_INFO_LIST)
         i = c->protocol->core->clients;
+    else if (command == PA_COMMAND_GET_CARD_INFO_LIST)
+        i = c->protocol->core->cards;
     else if (command == PA_COMMAND_GET_MODULE_INFO_LIST)
         i = c->protocol->core->modules;
     else if (command == PA_COMMAND_GET_SINK_INPUT_INFO_LIST)
@@ -2959,6 +2998,8 @@ static void command_get_info_list(pa_pdispatch *pd, uint32_t command, uint32_t t
                 source_fill_tagstruct(c, reply, p);
             else if (command == PA_COMMAND_GET_CLIENT_INFO_LIST)
                 client_fill_tagstruct(c, reply, p);
+            else if (command == PA_COMMAND_GET_CARD_INFO_LIST)
+                card_fill_tagstruct(c, reply, p);
             else if (command == PA_COMMAND_GET_MODULE_INFO_LIST)
                 module_fill_tagstruct(c, reply, p);
             else if (command == PA_COMMAND_GET_SINK_INPUT_INFO_LIST)
diff --git a/src/utils/pactl.c b/src/utils/pactl.c
index dbc9ba7..75b46d2 100644
--- a/src/utils/pactl.c
+++ b/src/utils/pactl.c
@@ -384,6 +384,51 @@ static void get_client_info_callback(pa_context *c, const pa_client_info *i, int
     pa_xfree(pl);
 }
 
+static void get_card_info_callback(pa_context *c, const pa_card_info *i, int is_last, void *userdata) {
+    char t[32];
+    char *pl;
+
+    if (is_last < 0) {
+        fprintf(stderr, _("Failed to get card information: %s\n"), pa_strerror(pa_context_errno(c)));
+        complete_action();
+        return;
+    }
+
+    if (is_last) {
+        complete_action();
+        return;
+    }
+
+    assert(i);
+
+    if (nl)
+        printf("\n");
+    nl = 1;
+
+    snprintf(t, sizeof(t), "%u", i->owner_module);
+
+    printf(_("Card #%u\n"
+             "\tName: %s\n"
+             "\tDriver: %s\n"
+             "\tOwner Module: %s\n"
+             "\tProperties:\n\t\t%s\n"),
+           i->index,
+           i->name,
+           pa_strnull(i->driver),
+           i->owner_module != PA_INVALID_INDEX ? t : _("n/a"),
+           pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
+
+    if (i->profiles) {
+        pa_card_profile_info *p;
+
+        printf(_("\tProfiles:\n"));
+        for (p = i->profiles; p->name; p++)
+            printf("\t\t%s: %s\n", p->name, p->description);
+    }
+
+    pa_xfree(pl);
+}
+
 static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info *i, int is_last, void *userdata) {
     char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
     char *pl;
@@ -649,7 +694,7 @@ static void context_state_callback(pa_context *c, void *userdata) {
                     break;
 
                 case LIST:
-                    actions = 7;
+                    actions = 8;
                     pa_operation_unref(pa_context_get_module_info_list(c, get_module_info_callback, NULL));
                     pa_operation_unref(pa_context_get_sink_info_list(c, get_sink_info_callback, NULL));
                     pa_operation_unref(pa_context_get_source_info_list(c, get_source_info_callback, NULL));
@@ -657,6 +702,7 @@ static void context_state_callback(pa_context *c, void *userdata) {
                     pa_operation_unref(pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL));
                     pa_operation_unref(pa_context_get_client_info_list(c, get_client_info_callback, NULL));
                     pa_operation_unref(pa_context_get_sample_info_list(c, get_sample_info_callback, NULL));
+                    pa_operation_unref(pa_context_get_card_info_list(c, get_card_info_callback, NULL));
                     break;
 
                 case MOVE_SINK_INPUT:

commit 86f3fb8b12790ba1b7d160d83d0b62ac7abddc78
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 20 20:33:28 2009 +0100

    show active profile

diff --git a/src/utils/pactl.c b/src/utils/pactl.c
index 75b46d2..ba91f50 100644
--- a/src/utils/pactl.c
+++ b/src/utils/pactl.c
@@ -426,6 +426,10 @@ static void get_card_info_callback(pa_context *c, const pa_card_info *i, int is_
             printf("\t\t%s: %s\n", p->name, p->description);
     }
 
+    if (i->active_profile)
+        printf(_("\tActive Profile: %s\n"),
+               i->active_profile->name);
+
     pa_xfree(pl);
 }
 

commit e0f8c1301240e4c49e22032e4edf8dcbe1844ccb
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 20 20:33:47 2009 +0100

    remove unused variable

diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 87cf583..aa541a8 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -2867,7 +2867,6 @@ static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, p
     pa_source *source = NULL;
     pa_client *client = NULL;
     pa_card *card = NULL;
-    pa_core *core = NULL;
     pa_module *module = NULL;
     pa_sink_input *si = NULL;
     pa_source_output *so = NULL;

commit b3a043fd3179fcb60730466ae43f16ffe14a9b4c
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 20 20:34:46 2009 +0100

    always add 'disabled' profile

diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
index 1e3c0c1..ac6083d 100644
--- a/src/modules/alsa/module-alsa-card.c
+++ b/src/modules/alsa/module-alsa-card.c
@@ -97,7 +97,6 @@ static void enumerate_cb(
         t = pa_sprintf_malloc("Output %s", sink->description);
     } else {
         pa_assert(source);
-        n = pa_xstrdup(source->name);
         n = pa_sprintf_malloc("input-%s", source->name);
         t = pa_sprintf_malloc("Input %s", source->description);
     }
@@ -125,6 +124,18 @@ static void enumerate_cb(
     pa_hashmap_put(profiles, p->name, p);
 }
 
+static void add_disabled_profile(pa_hashmap *profiles) {
+    pa_card_profile *p;
+    struct profile_data *d;
+
+    p = pa_card_profile_new("off", "Off", sizeof(struct profile_data));
+
+    d = PA_CARD_PROFILE_DATA(p);
+    d->sink = d->source = NULL;
+
+    pa_hashmap_put(profiles, p->name, p);
+}
+
 int pa__init(pa_module*m) {
     pa_card_new_data data;
     pa_modargs *ma;
@@ -163,6 +174,14 @@ int pa__init(pa_module*m) {
         goto fail;
     }
 
+    if (pa_hashmap_isempty(data.profiles)) {
+        pa_log("Failed to find a working profile.");
+        pa_card_new_data_done(&data);
+        goto fail;
+    }
+
+    add_disabled_profile(data.profiles);
+
     u->card = pa_card_new(m->core, &data);
     pa_card_new_data_done(&data);
 

commit 7368a6e6be5dbbdc8e13003ef6841fe3fe1840bc
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 20 20:35:18 2009 +0100

    add priority logic to find best default profile

diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index 6522176..eb7042b 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -495,62 +495,72 @@ static const struct pa_alsa_profile_info device_table[] = {
     {{ 1, { PA_CHANNEL_POSITION_MONO }},
      "hw",
      "Analog Mono",
-     "analog-mono" },
+     "analog-mono",
+     1 },
 
     {{ 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT }},
      "front",
      "Analog Stereo",
-     "analog-stereo" },
+     "analog-stereo",
+     10 },
 
     {{ 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT }},
      "iec958",
      "IEC958 Digital Stereo",
-     "iec958-stereo" },
+     "iec958-stereo",
+     5 },
 
     {{ 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT }},
      "hdmi",
      "HDMI Digital Stereo",
-     "hdmi-stereo"},
+     "hdmi-stereo",
+     4 },
 
     {{ 4, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
             PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT }},
      "surround40",
      "Analog Surround 4.0",
-     "analog-surround-40" },
+     "analog-surround-40",
+     7 },
 
     {{ 4, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
             PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT }},
      "a52",
      "IEC958/AC3 Digital Surround 4.0",
-     "iec958-ac3-surround-40" },
+     "iec958-ac3-surround-40",
+     2 },
 
     {{ 5, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
             PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
             PA_CHANNEL_POSITION_LFE }},
      "surround41",
      "Analog Surround 4.1",
-     "analog-surround-41"},
+     "analog-surround-41",
+     7 },
 
     {{ 5, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
             PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
             PA_CHANNEL_POSITION_CENTER }},
      "surround50",
      "Analog Surround 5.0",
-     "analog-surround-50" },
+     "analog-surround-50",
+     7 },
 
     {{ 6, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
             PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
             PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_LFE }},
      "surround51",
      "Analog Surround 5.1",
-     "analog-surround-51" },
+     "analog-surround-51",
+     8 },
 
     {{ 6, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_CENTER,
             PA_CHANNEL_POSITION_FRONT_RIGHT, PA_CHANNEL_POSITION_REAR_LEFT,
             PA_CHANNEL_POSITION_REAR_RIGHT, PA_CHANNEL_POSITION_LFE}},
      "a52",
      "IEC958/AC3 Digital Surround 5.1",
-     "iec958-ac3-surround-51" },
+     "iec958-ac3-surround-51",
+     3 },
 
     {{ 8, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
             PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
@@ -558,9 +568,10 @@ static const struct pa_alsa_profile_info device_table[] = {
             PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT }},
      "surround71",
      "Analog Surround 7.1",
-     "analog-surround-71" },
+     "analog-surround-71",
+     7 },
 
-    {{ 0, { 0 }}, NULL, NULL, NULL }
+    {{ 0, { 0 }}, NULL, NULL, NULL, 0 }
 };
 
 static pa_bool_t channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) {
diff --git a/src/modules/alsa/alsa-util.h b/src/modules/alsa/alsa-util.h
index f58ec8e..5965625 100644
--- a/src/modules/alsa/alsa-util.h
+++ b/src/modules/alsa/alsa-util.h
@@ -86,6 +86,7 @@ typedef struct pa_alsa_profile_info {
     const char *alsa_name;
     const char *description;
     const char *name;
+    unsigned priority;
 } pa_alsa_profile_info;
 
 int pa_alsa_probe_profiles(
diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
index ac6083d..6c947c0 100644
--- a/src/modules/alsa/module-alsa-card.c
+++ b/src/modules/alsa/module-alsa-card.c
@@ -108,6 +108,7 @@ static void enumerate_cb(
     pa_xfree(t);
     pa_xfree(n);
 
+    p->priority = (sink ? sink->priority : 0)*100 + (source ? source->priority : 0);
     p->n_sinks = !!sink;
     p->n_sources = !!source;
 
diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c
index 8e29f0a..cb77553 100644
--- a/src/pulsecore/card.c
+++ b/src/pulsecore/card.c
@@ -45,6 +45,10 @@ pa_card_profile *pa_card_profile_new(const char *name, const char *description,
     c->name = pa_xstrdup(name);
     c->description = pa_xstrdup(description);
 
+    c->priority = 0;
+    c->n_sinks = c->n_sources = 0;
+    c->max_sink_channels = c->max_source_channels = 0;
+
     return c;
 }
 
@@ -125,8 +129,17 @@ pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) {
     c->profiles = data->profiles;
     data->profiles = NULL;
     if (!(c->active_profile = data->active_profile))
-        if (c->profiles)
-            c->active_profile = pa_hashmap_first(c->profiles);
+        if (c->profiles) {
+            void *state = NULL;
+            pa_card_profile *p;
+
+            while ((p = pa_hashmap_iterate(c->profiles, &state, NULL))) {
+                if (!c->active_profile ||
+                    p->priority > c->active_profile->priority)
+
+                    c->active_profile = p;
+            }
+        }
     data->active_profile = NULL;
 
     c->userdata = NULL;
diff --git a/src/pulsecore/card.h b/src/pulsecore/card.h
index b4e68b0..17733c5 100644
--- a/src/pulsecore/card.h
+++ b/src/pulsecore/card.h
@@ -33,6 +33,8 @@ typedef struct pa_card_profile {
     char *name;
     char *description;
 
+    unsigned priority;
+
     /* We probably want to have different properties later on here */
     unsigned n_sinks;
     unsigned n_sources;
diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index f52968d..947598b 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -142,7 +142,7 @@ char *pa_card_list_to_string(pa_core *c) {
                     "\tprofiles:\n");
 
             while ((p = pa_hashmap_iterate(card->profiles, &state, NULL)))
-                pa_strbuf_printf(s, "\t\t%s: %s\n", p->name, p->description);
+                pa_strbuf_printf(s, "\t\t%s: %s (priority %u)\n", p->name, p->description, p->priority);
         }
 
         if (card->active_profile)

commit 04e9214065637a6ad17983d18475eb4e67c2c680
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 20 21:00:35 2009 +0100

    export pa_channel_map_superset()

diff --git a/src/map-file b/src/map-file
index 042e2ad..55b13e1 100644
--- a/src/map-file
+++ b/src/map-file
@@ -18,6 +18,7 @@ pa_channel_map_init_mono;
 pa_channel_map_init_stereo;
 pa_channel_map_parse;
 pa_channel_map_snprint;
+pa_channel_map_superset;
 pa_channel_map_valid;
 pa_channel_position_to_pretty_string;
 pa_channel_position_to_string;
diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index eb7042b..b7f44b8 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -574,25 +574,6 @@ static const struct pa_alsa_profile_info device_table[] = {
     {{ 0, { 0 }}, NULL, NULL, NULL, 0 }
 };
 
-static pa_bool_t channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) {
-    pa_bool_t in_a[PA_CHANNEL_POSITION_MAX];
-    unsigned i;
-
-    pa_assert(a);
-    pa_assert(b);
-
-    memset(in_a, 0, sizeof(in_a));
-
-    for (i = 0; i < a->channels; i++)
-        in_a[a->map[i]] = TRUE;
-
-    for (i = 0; i < b->channels; i++)
-        if (!in_a[b->map[i]])
-            return FALSE;
-
-    return TRUE;
-}
-
 snd_pcm_t *pa_alsa_open_by_device_id(
         const char *dev_id,
         char **dev,
@@ -629,7 +610,7 @@ snd_pcm_t *pa_alsa_open_by_device_id(
     i = 0;
     for (;;) {
 
-        if ((direction > 0) == channel_map_superset(&device_table[i].map, map)) {
+        if ((direction > 0) == pa_channel_map_superset(&device_table[i].map, map)) {
             pa_sample_spec try_ss;
 
             pa_log_debug("Checking for %s (%s)", device_table[i].name, device_table[i].alsa_name);
diff --git a/src/pulse/channelmap.c b/src/pulse/channelmap.c
index fd313bd..26eae59 100644
--- a/src/pulse/channelmap.c
+++ b/src/pulse/channelmap.c
@@ -577,3 +577,22 @@ int pa_channel_map_compatible(const pa_channel_map *map, const pa_sample_spec *s
 
     return map->channels == ss->channels;
 }
+
+int pa_channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) {
+    pa_bool_t in_a[PA_CHANNEL_POSITION_MAX];
+    unsigned i;
+
+    pa_assert(a);
+    pa_assert(b);
+
+    memset(in_a, 0, sizeof(in_a));
+
+    for (i = 0; i < a->channels; i++)
+        in_a[a->map[i]] = TRUE;
+
+    for (i = 0; i < b->channels; i++)
+        if (!in_a[b->map[i]])
+            return 0;
+
+    return 1;
+}
diff --git a/src/pulse/channelmap.h b/src/pulse/channelmap.h
index d7d19d7..a6c044f 100644
--- a/src/pulse/channelmap.h
+++ b/src/pulse/channelmap.h
@@ -227,6 +227,9 @@ int pa_channel_map_valid(const pa_channel_map *map) PA_GCC_PURE;
  * the specified sample spec. \since 0.9.12 */
 int pa_channel_map_compatible(const pa_channel_map *map, const pa_sample_spec *ss) PA_GCC_PURE;
 
+/** Returns non-zero if every channel defined in b is also defined in a. \since 0.9.15 */
+int pa_channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) PA_GCC_PURE;
+
 PA_C_DECL_END
 
 #endif

commit b88b89a676da1238972886f54780af62262a0043
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 20 21:36:57 2009 +0100

    add new call pa_alsa_open_by_device_id_profile()

diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index b7f44b8..e154f6e 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -574,7 +574,7 @@ static const struct pa_alsa_profile_info device_table[] = {
     {{ 0, { 0 }}, NULL, NULL, NULL, 0 }
 };
 
-snd_pcm_t *pa_alsa_open_by_device_id(
+snd_pcm_t *pa_alsa_open_by_device_id_auto(
         const char *dev_id,
         char **dev,
         pa_sample_spec *ss,
@@ -585,8 +585,7 @@ snd_pcm_t *pa_alsa_open_by_device_id(
         snd_pcm_uframes_t tsched_size,
         pa_bool_t *use_mmap,
         pa_bool_t *use_tsched,
-        const char**config_description,
-        const char **config_name) {
+        const pa_alsa_profile_info **profile) {
 
     int i;
     int direction = 1;
@@ -642,11 +641,8 @@ snd_pcm_t *pa_alsa_open_by_device_id(
                 *map = device_table[i].map;
                 pa_assert(map->channels == ss->channels);
 
-                if (config_description)
-                    *config_description = device_table[i].description;
-                if (config_name)
-                    *config_name = device_table[i].name;
-
+                if (profile)
+                    *profile = &device_table[i];
 
                 return pcm_handle;
             }
@@ -703,10 +699,64 @@ snd_pcm_t *pa_alsa_open_by_device_id(
     pcm_handle = pa_alsa_open_by_device_string(d, dev, ss, map, mode, nfrags, period_size, tsched_size, use_mmap, use_tsched, FALSE);
     pa_xfree(d);
 
-    if (pcm_handle) {
-        *config_description = NULL;
-        *config_name = NULL;
-    }
+    if (pcm_handle && profile)
+        *profile = NULL;
+
+    return pcm_handle;
+}
+
+snd_pcm_t *pa_alsa_open_by_device_id_profile(
+        const char *dev_id,
+        char **dev,
+        pa_sample_spec *ss,
+        pa_channel_map* map,
+        int mode,
+        uint32_t *nfrags,
+        snd_pcm_uframes_t *period_size,
+        snd_pcm_uframes_t tsched_size,
+        pa_bool_t *use_mmap,
+        pa_bool_t *use_tsched,
+        const pa_alsa_profile_info *profile) {
+
+    char *d;
+    snd_pcm_t *pcm_handle;
+    pa_sample_spec try_ss;
+
+    pa_assert(dev_id);
+    pa_assert(dev);
+    pa_assert(ss);
+    pa_assert(map);
+    pa_assert(nfrags);
+    pa_assert(period_size);
+    pa_assert(profile);
+
+    d = pa_sprintf_malloc("%s:%s", profile->alsa_name, dev_id);
+
+    try_ss.channels = profile->map.channels;
+    try_ss.rate = ss->rate;
+    try_ss.format = ss->format;
+
+    pcm_handle = pa_alsa_open_by_device_string(
+            d,
+            dev,
+            &try_ss,
+            map,
+            mode,
+            nfrags,
+            period_size,
+            tsched_size,
+            use_mmap,
+            use_tsched,
+            TRUE);
+
+    pa_xfree(d);
+
+    if (!pcm_handle)
+        return NULL;
+
+    *ss = try_ss;
+    *map = profile->map;
+    pa_assert(map->channels == ss->channels);
 
     return pcm_handle;
 }
diff --git a/src/modules/alsa/alsa-util.h b/src/modules/alsa/alsa-util.h
index 5965625..18b0402 100644
--- a/src/modules/alsa/alsa-util.h
+++ b/src/modules/alsa/alsa-util.h
@@ -54,7 +54,16 @@ int pa_alsa_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min);
 int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev);
 snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const char *fallback, pa_bool_t playback);
 
-snd_pcm_t *pa_alsa_open_by_device_id(
+typedef struct pa_alsa_profile_info {
+    pa_channel_map map;
+    const char *alsa_name;
+    const char *description;
+    const char *name;
+    unsigned priority;
+} pa_alsa_profile_info;
+
+/* Picks a working profile based on the specified ss/map */
+snd_pcm_t *pa_alsa_open_by_device_id_auto(
         const char *dev_id,
         char **dev,
         pa_sample_spec *ss,
@@ -65,9 +74,23 @@ snd_pcm_t *pa_alsa_open_by_device_id(
         snd_pcm_uframes_t tsched_size,
         pa_bool_t *use_mmap,
         pa_bool_t *use_tsched,
-        const char **config_name,
-        const char **config_description);
+        const pa_alsa_profile_info **profile);
 
+/* Uses the specified profile */
+snd_pcm_t *pa_alsa_open_by_device_id_profile(
+        const char *dev_id,
+        char **dev,
+        pa_sample_spec *ss,
+        pa_channel_map* map,
+        int mode,
+        uint32_t *nfrags,
+        snd_pcm_uframes_t *period_size,
+        snd_pcm_uframes_t tsched_size,
+        pa_bool_t *use_mmap,
+        pa_bool_t *use_tsched,
+        const pa_alsa_profile_info *profile);
+
+/* Opens the explicit ALSA device */
 snd_pcm_t *pa_alsa_open_by_device_string(
         const char *device,
         char **dev,
@@ -81,14 +104,6 @@ snd_pcm_t *pa_alsa_open_by_device_string(
         pa_bool_t *use_tsched,
         pa_bool_t require_exact_channel_number);
 
-typedef struct pa_alsa_profile_info {
-    pa_channel_map map;
-    const char *alsa_name;
-    const char *description;
-    const char *name;
-    unsigned priority;
-} pa_alsa_profile_info;
-
 int pa_alsa_probe_profiles(
         const char *dev_id,
         const pa_sample_spec *ss,
diff --git a/src/modules/alsa/module-alsa-sink.c b/src/modules/alsa/module-alsa-sink.c
index 62ce89c..12d3407 100644
--- a/src/modules/alsa/module-alsa-sink.c
+++ b/src/modules/alsa/module-alsa-sink.c
@@ -1253,7 +1253,7 @@ int pa__init(pa_module*m) {
     pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d;
     pa_usec_t usec;
     pa_sink_new_data data;
-    const char *profile_description = NULL, *profile_name = NULL;
+    const pa_alsa_profile_info *profile = NULL;
 
     snd_pcm_info_alloca(&pcm_info);
 
@@ -1332,13 +1332,13 @@ int pa__init(pa_module*m) {
 
     if ((dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
 
-        if (!(u->pcm_handle = pa_alsa_open_by_device_id(
+        if (!(u->pcm_handle = pa_alsa_open_by_device_id_auto(
                       dev_id,
                       &u->device_name,
                       &ss, &map,
                       SND_PCM_STREAM_PLAYBACK,
                       &nfrags, &period_frames, tsched_frames,
-                      &b, &d, &profile_description, &profile_name)))
+                      &b, &d, &profile)))
 
             goto fail;
 
@@ -1358,8 +1358,8 @@ int pa__init(pa_module*m) {
     pa_assert(u->device_name);
     pa_log_info("Successfully opened device %s.", u->device_name);
 
-    if (profile_description)
-        pa_log_info("Selected configuration '%s' (%s).", profile_description, profile_name);
+    if (profile)
+        pa_log_info("Selected configuration '%s' (%s).", profile->description, profile->name);
 
     if (use_mmap && !b) {
         pa_log_info("Device doesn't support mmap(), falling back to UNIX read/write mode.");
@@ -1444,10 +1444,10 @@ int pa__init(pa_module*m) {
     pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size));
     pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial"));
 
-    if (profile_name)
-        pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_NAME, profile_name);
-    if (profile_description)
-        pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_DESCRIPTION, profile_description);
+    if (profile) {
+        pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_NAME, profile->name);
+        pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_DESCRIPTION, profile->description);
+    }
 
     u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
     pa_sink_new_data_done(&data);
diff --git a/src/modules/alsa/module-alsa-source.c b/src/modules/alsa/module-alsa-source.c
index 7ca305f..08ef12f 100644
--- a/src/modules/alsa/module-alsa-source.c
+++ b/src/modules/alsa/module-alsa-source.c
@@ -1087,7 +1087,7 @@ int pa__init(pa_module*m) {
     pa_bool_t namereg_fail;
     pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d;
     pa_source_new_data data;
-    const char *profile_description = NULL, *profile_name = NULL;
+    const pa_alsa_profile_info *profile = NULL;
 
     snd_pcm_info_alloca(&pcm_info);
 
@@ -1161,13 +1161,13 @@ int pa__init(pa_module*m) {
 
     if ((dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
 
-        if (!(u->pcm_handle = pa_alsa_open_by_device_id(
+        if (!(u->pcm_handle = pa_alsa_open_by_device_id_auto(
                       dev_id,
                       &u->device_name,
                       &ss, &map,
                       SND_PCM_STREAM_CAPTURE,
                       &nfrags, &period_frames, tsched_frames,
-                      &b, &d, &profile_description, &profile_name)))
+                      &b, &d, &profile)))
             goto fail;
 
     } else {
@@ -1185,8 +1185,8 @@ int pa__init(pa_module*m) {
     pa_assert(u->device_name);
     pa_log_info("Successfully opened device %s.", u->device_name);
 
-    if (profile_description)
-        pa_log_info("Selected configuration '%s' (%s).", profile_description, profile_name);
+    if (profile)
+        pa_log_info("Selected configuration '%s' (%s).", profile->description, profile->name);
 
     if (use_mmap && !b) {
         pa_log_info("Device doesn't support mmap(), falling back to UNIX read/write mode.");
@@ -1271,10 +1271,10 @@ int pa__init(pa_module*m) {
     pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size));
     pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial"));
 
-    if (profile_name)
-        pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_NAME, profile_name);
-    if (profile_description)
-        pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_DESCRIPTION, profile_description);
+    if (profile) {
+        pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_NAME, profile->name);
+        pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_DESCRIPTION, profile->description);
+    }
 
     u->source = pa_source_new(m->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
     pa_source_new_data_done(&data);

commit 16d200e3694658267ff4e04a296ffdb5668e8af6
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jan 21 00:23:18 2009 +0100

    add an API to create arbitrary alsa sinks/sources dynamically without having to load/unload modules

diff --git a/src/Makefile.am b/src/Makefile.am
index 17e14be..3151b49 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1254,7 +1254,7 @@ module_oss_la_LIBADD = $(AM_LIBADD) liboss-util.la libpulsecore- at PA_MAJORMINORMI
 
 # ALSA
 
-libalsa_util_la_SOURCES = modules/alsa/alsa-util.c modules/alsa/alsa-util.h
+libalsa_util_la_SOURCES = modules/alsa/alsa-util.c modules/alsa/alsa-util.h modules/alsa/alsa-sink.c modules/alsa/alsa-sink.h modules/alsa/alsa-source.c modules/alsa/alsa-source.h
 libalsa_util_la_LDFLAGS = -avoid-version
 libalsa_util_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
 libalsa_util_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
diff --git a/src/modules/alsa/module-alsa-sink.c b/src/modules/alsa/alsa-sink.c
similarity index 96%
copy from src/modules/alsa/module-alsa-sink.c
copy to src/modules/alsa/alsa-sink.c
index 12d3407..9556915 100644
--- a/src/modules/alsa/module-alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -53,43 +53,7 @@
 #include <pulsecore/time-smoother.h>
 
 #include "alsa-util.h"
-#include "module-alsa-sink-symdef.h"
-
-PA_MODULE_AUTHOR("Lennart Poettering");
-PA_MODULE_DESCRIPTION("ALSA Sink");
-PA_MODULE_VERSION(PACKAGE_VERSION);
-PA_MODULE_LOAD_ONCE(FALSE);
-PA_MODULE_USAGE(
-        "sink_name=<name for the sink> "
-        "device=<ALSA device> "
-        "device_id=<ALSA card index> "
-        "format=<sample format> "
-        "rate=<sample rate> "
-        "channels=<number of channels> "
-        "channel_map=<channel map> "
-        "fragments=<number of fragments> "
-        "fragment_size=<fragment size> "
-        "mmap=<enable memory mapping?> "
-        "tsched=<enable system timer based scheduling mode?> "
-        "tsched_buffer_size=<buffer size when using timer based scheduling> "
-        "tsched_buffer_watermark=<lower fill watermark>");
-
-static const char* const valid_modargs[] = {
-    "sink_name",
-    "device",
-    "device_id",
-    "format",
-    "rate",
-    "channels",
-    "channel_map",
-    "fragments",
-    "fragment_size",
-    "mmap",
-    "tsched",
-    "tsched_buffer_size",
-    "tsched_buffer_watermark",
-    NULL
-};
+#include "alsa-sink.h"
 
 #define DEFAULT_DEVICE "default"
 #define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC)            /* 2s */
@@ -138,6 +102,8 @@ struct userdata {
     snd_pcm_sframes_t hwbuf_unused_frames;
 };
 
+static void userdata_free(struct userdata *u);
+
 static void fix_tsched_watermark(struct userdata *u) {
     size_t max_use;
     size_t min_sleep, min_wakeup;
@@ -1235,9 +1201,8 @@ finish:
     pa_log_debug("Thread shutting down");
 }
 
-int pa__init(pa_module*m) {
+pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const pa_alsa_profile_info *profile) {
 
-    pa_modargs *ma = NULL;
     struct userdata *u = NULL;
     const char *dev_id;
     pa_sample_spec ss;
@@ -1253,19 +1218,11 @@ int pa__init(pa_module*m) {
     pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d;
     pa_usec_t usec;
     pa_sink_new_data data;
-    const pa_alsa_profile_info *profile = NULL;
 
     snd_pcm_info_alloca(&pcm_info);
 
     pa_assert(m);
 
-    pa_alsa_redirect_errors_inc();
-
-    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
-        pa_log("Failed to parse module arguments");
-        goto fail;
-    }
-
     ss = m->core->default_sample_spec;
     if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_ALSA) < 0) {
         pa_log("Failed to parse sample specification and channel map");
@@ -1308,7 +1265,7 @@ int pa__init(pa_module*m) {
         use_tsched = FALSE;
     }
 
-    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u = pa_xnew0(struct userdata, 1);
     u->core = m->core;
     u->module = m;
     u->use_mmap = use_mmap;
@@ -1325,12 +1282,22 @@ int pa__init(pa_module*m) {
     pa_smoother_set_time_offset(u->smoother, usec);
     pa_smoother_pause(u->smoother, usec);
 
-    snd_config_update_free_global();
-
     b = use_mmap;
     d = use_tsched;
 
-    if ((dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
+    if (profile) {
+
+        if (!(u->pcm_handle = pa_alsa_open_by_device_id_profile(
+                      dev_id,
+                      &u->device_name,
+                      &ss, &map,
+                      SND_PCM_STREAM_PLAYBACK,
+                      &nfrags, &period_frames, tsched_frames,
+                      &b, &d, profile)))
+
+            goto fail;
+
+    } else if ((dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
 
         if (!(u->pcm_handle = pa_alsa_open_by_device_id_auto(
                       dev_id,
@@ -1425,7 +1392,10 @@ int pa__init(pa_module*m) {
 
     if ((name = pa_modargs_get_value(ma, "sink_name", NULL)))
         namereg_fail = TRUE;
-    else {
+    else if ((name = pa_modargs_get_value(ma, "name", NULL))) {
+        name = name_buf = pa_sprintf_malloc("alsa_output.%s", name);
+        namereg_fail = TRUE;
+    } else {
         name = name_buf = pa_sprintf_malloc("alsa_output.%s", u->device_name);
         namereg_fail = FALSE;
     }
@@ -1604,36 +1574,17 @@ int pa__init(pa_module*m) {
 
     pa_sink_put(u->sink);
 
-    pa_modargs_free(ma);
-
-    return 0;
+    return u->sink;
 
 fail:
 
-    if (ma)
-        pa_modargs_free(ma);
-
-    pa__done(m);
+    userdata_free(u);
 
-    return -1;
+    return NULL;
 }
 
-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))
-        goto finish;
+static void userdata_free(struct userdata *u) {
+    pa_assert(u);
 
     if (u->sink)
         pa_sink_unlink(u->sink);
@@ -1673,9 +1624,13 @@ void pa__done(pa_module*m) {
 
     pa_xfree(u->device_name);
     pa_xfree(u);
+}
 
-finish:
+void pa_alsa_sink_free(pa_sink *s) {
+    struct userdata *u;
 
-    snd_config_update_free_global();
-    pa_alsa_redirect_errors_dec();
+    pa_sink_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    userdata_free(u);
 }
diff --git a/src/modules/alsa/alsa-sink.h b/src/modules/alsa/alsa-sink.h
new file mode 100644
index 0000000..e503201
--- /dev/null
+++ b/src/modules/alsa/alsa-sink.h
@@ -0,0 +1,36 @@
+#ifndef fooalsasinkhfoo
+#define fooalsasinkhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman at cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/sink.h>
+
+#include "alsa-util.h"
+
+pa_sink* pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const pa_alsa_profile_info *profile);
+
+void pa_alsa_sink_free(pa_sink *s);
+
+#endif
diff --git a/src/modules/alsa/module-alsa-source.c b/src/modules/alsa/alsa-source.c
similarity index 95%
copy from src/modules/alsa/module-alsa-source.c
copy to src/modules/alsa/alsa-source.c
index 08ef12f..c0bf20d 100644
--- a/src/modules/alsa/module-alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -54,43 +54,7 @@
 #include <pulsecore/rtclock.h>
 
 #include "alsa-util.h"
-#include "module-alsa-source-symdef.h"
-
-PA_MODULE_AUTHOR("Lennart Poettering");
-PA_MODULE_DESCRIPTION("ALSA Source");
-PA_MODULE_VERSION(PACKAGE_VERSION);
-PA_MODULE_LOAD_ONCE(FALSE);
-PA_MODULE_USAGE(
-        "source_name=<name for the source> "
-        "device=<ALSA device> "
-        "device_id=<ALSA card index> "
-        "format=<sample format> "
-        "rate=<sample rate> "
-        "channels=<number of channels> "
-        "channel_map=<channel map> "
-        "fragments=<number of fragments> "
-        "fragment_size=<fragment size> "
-        "mmap=<enable memory mapping?> "
-        "tsched=<enable system timer based scheduling mode?> "
-        "tsched_buffer_size=<buffer size when using timer based scheduling> "
-        "tsched_buffer_watermark=<upper fill watermark>");
-
-static const char* const valid_modargs[] = {
-    "source_name",
-    "device",
-    "device_id",
-    "format",
-    "rate",
-    "channels",
-    "channel_map",
-    "fragments",
-    "fragment_size",
-    "mmap",
-    "tsched",
-    "tsched_buffer_size",
-    "tsched_buffer_watermark",
-    NULL
-};
+#include "alsa-source.h"
 
 #define DEFAULT_DEVICE "default"
 #define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC)       /* 2s */
@@ -136,6 +100,8 @@ struct userdata {
     snd_pcm_sframes_t hwbuf_unused_frames;
 };
 
+static void userdata_free(struct userdata *u);
+
 static void fix_tsched_watermark(struct userdata *u) {
     size_t max_use;
     size_t min_sleep, min_wakeup;
@@ -1070,9 +1036,8 @@ finish:
     pa_log_debug("Thread shutting down");
 }
 
-int pa__init(pa_module*m) {
+pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const pa_alsa_profile_info *profile) {
 
-    pa_modargs *ma = NULL;
     struct userdata *u = NULL;
     const char *dev_id;
     pa_sample_spec ss;
@@ -1087,19 +1052,11 @@ int pa__init(pa_module*m) {
     pa_bool_t namereg_fail;
     pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d;
     pa_source_new_data data;
-    const pa_alsa_profile_info *profile = NULL;
 
     snd_pcm_info_alloca(&pcm_info);
 
     pa_assert(m);
 
-    pa_alsa_redirect_errors_inc();
-
-    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
-        pa_log("Failed to parse module arguments");
-        goto fail;
-    }
-
     ss = m->core->default_sample_spec;
     if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_ALSA) < 0) {
         pa_log("Failed to parse sample specification");
@@ -1142,7 +1099,7 @@ int pa__init(pa_module*m) {
         use_tsched = FALSE;
     }
 
-    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u = pa_xnew0(struct userdata, 1);
     u->core = m->core;
     u->module = m;
     u->use_mmap = use_mmap;
@@ -1154,12 +1111,21 @@ int pa__init(pa_module*m) {
     u->smoother = pa_smoother_new(DEFAULT_TSCHED_WATERMARK_USEC, DEFAULT_TSCHED_WATERMARK_USEC, TRUE, 5);
     pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec());
 
-    snd_config_update_free_global();
-
     b = use_mmap;
     d = use_tsched;
 
-    if ((dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
+    if (profile) {
+
+        if (!(u->pcm_handle = pa_alsa_open_by_device_id_profile(
+                      dev_id,
+                      &u->device_name,
+                      &ss, &map,
+                      SND_PCM_STREAM_CAPTURE,
+                      &nfrags, &period_frames, tsched_frames,
+                      &b, &d, profile)))
+            goto fail;
+
+    } else if ((dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
 
         if (!(u->pcm_handle = pa_alsa_open_by_device_id_auto(
                       dev_id,
@@ -1425,36 +1391,17 @@ int pa__init(pa_module*m) {
 
     pa_source_put(u->source);
 
-    pa_modargs_free(ma);
-
-    return 0;
+    return u->source;
 
 fail:
 
-    if (ma)
-        pa_modargs_free(ma);
+    userdata_free(u);
 
-    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_source_linked_by(u->source);
+    return NULL;
 }
 
-void pa__done(pa_module*m) {
-    struct userdata *u;
-
-    pa_assert(m);
-
-    if (!(u = m->userdata))
-        goto finish;
+static void userdata_free(struct userdata *u) {
+    pa_assert(u);
 
     if (u->source)
         pa_source_unlink(u->source);
@@ -1491,8 +1438,13 @@ void pa__done(pa_module*m) {
 
     pa_xfree(u->device_name);
     pa_xfree(u);
+}
 
-finish:
-    snd_config_update_free_global();
-    pa_alsa_redirect_errors_dec();
+void pa_alsa_source_free(pa_source *s) {
+    struct userdata *u;
+
+    pa_source_assert_ref(s);
+    pa_assert_se(u = s->userdata);
+
+    userdata_free(u);
 }
diff --git a/src/modules/alsa/alsa-source.h b/src/modules/alsa/alsa-source.h
new file mode 100644
index 0000000..bdb24d1
--- /dev/null
+++ b/src/modules/alsa/alsa-source.h
@@ -0,0 +1,36 @@
+#ifndef fooalsasourcehfoo
+#define fooalsasourcehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman at cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/source.h>
+
+#include "alsa-util.h"
+
+pa_source* pa_alsa_source_new(pa_module *m, pa_modargs *ma, const pa_alsa_profile_info *profile);
+
+void pa_alsa_source_free(pa_source *s);
+
+#endif
diff --git a/src/modules/alsa/module-alsa-sink.c b/src/modules/alsa/module-alsa-sink.c
index 12d3407..f8303a5 100644
--- a/src/modules/alsa/module-alsa-sink.c
+++ b/src/modules/alsa/module-alsa-sink.c
@@ -24,35 +24,13 @@
 #include <config.h>
 #endif
 
-#include <stdio.h>
-
-#include <asoundlib.h>
-
-#ifdef HAVE_VALGRIND_MEMCHECK_H
-#include <valgrind/memcheck.h>
-#endif
-
-#include <pulse/xmalloc.h>
-#include <pulse/util.h>
-#include <pulse/timeval.h>
-
 #include <pulsecore/core.h>
 #include <pulsecore/module.h>
-#include <pulsecore/memchunk.h>
 #include <pulsecore/sink.h>
 #include <pulsecore/modargs.h>
-#include <pulsecore/core-util.h>
-#include <pulsecore/sample-util.h>
-#include <pulsecore/log.h>
-#include <pulsecore/macro.h>
-#include <pulsecore/thread.h>
-#include <pulsecore/core-error.h>
-#include <pulsecore/thread-mq.h>
-#include <pulsecore/rtpoll.h>
-#include <pulsecore/rtclock.h>
-#include <pulsecore/time-smoother.h>
 
 #include "alsa-util.h"
+#include "alsa-sink.h"
 #include "module-alsa-sink-symdef.h"
 
 PA_MODULE_AUTHOR("Lennart Poettering");
@@ -91,1518 +69,21 @@ static const char* const valid_modargs[] = {
     NULL
 };
 
-#define DEFAULT_DEVICE "default"
-#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC)            /* 2s */
-#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC)       /* 20ms */
-#define TSCHED_MIN_SLEEP_USEC (3*PA_USEC_PER_MSEC)                /* 3ms */
-#define TSCHED_MIN_WAKEUP_USEC (3*PA_USEC_PER_MSEC)               /* 3ms */
-
-struct userdata {
-    pa_core *core;
-    pa_module *module;
-    pa_sink *sink;
-
-    pa_thread *thread;
-    pa_thread_mq thread_mq;
-    pa_rtpoll *rtpoll;
-
-    snd_pcm_t *pcm_handle;
-
-    pa_alsa_fdlist *mixer_fdl;
-    snd_mixer_t *mixer_handle;
-    snd_mixer_elem_t *mixer_elem;
-    long hw_volume_max, hw_volume_min;
-    long hw_dB_max, hw_dB_min;
-    pa_bool_t hw_dB_supported;
-    pa_bool_t mixer_seperate_channels;
-    pa_cvolume hardware_volume;
-
-    size_t frame_size, fragment_size, hwbuf_size, tsched_watermark;
-    unsigned nfragments;
-    pa_memchunk memchunk;
-
-    char *device_name;
-
-    pa_bool_t use_mmap, use_tsched;
-
-    pa_bool_t first, after_rewind;
-
-    pa_rtpoll_item *alsa_rtpoll_item;
-
-    snd_mixer_selem_channel_id_t mixer_map[SND_MIXER_SCHN_LAST];
-
-    pa_smoother *smoother;
-    int64_t frame_index;
-    uint64_t since_start;
-
-    snd_pcm_sframes_t hwbuf_unused_frames;
-};
-
-static void fix_tsched_watermark(struct userdata *u) {
-    size_t max_use;
-    size_t min_sleep, min_wakeup;
-    pa_assert(u);
-
-    max_use = u->hwbuf_size - (size_t) u->hwbuf_unused_frames * u->frame_size;
-
-    min_sleep = pa_usec_to_bytes(TSCHED_MIN_SLEEP_USEC, &u->sink->sample_spec);
-    min_wakeup = pa_usec_to_bytes(TSCHED_MIN_WAKEUP_USEC, &u->sink->sample_spec);
-
-    if (min_sleep > max_use/2)
-        min_sleep = pa_frame_align(max_use/2, &u->sink->sample_spec);
-    if (min_sleep < u->frame_size)
-        min_sleep = u->frame_size;
-
-    if (min_wakeup > max_use/2)
-        min_wakeup = pa_frame_align(max_use/2, &u->sink->sample_spec);
-    if (min_wakeup < u->frame_size)
-        min_wakeup = u->frame_size;
-
-    if (u->tsched_watermark > max_use-min_sleep)
-        u->tsched_watermark = max_use-min_sleep;
-
-    if (u->tsched_watermark < min_wakeup)
-        u->tsched_watermark = min_wakeup;
-}
-
-static void hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_usec_t*process_usec) {
-    pa_usec_t usec, wm;
-
-    pa_assert(sleep_usec);
-    pa_assert(process_usec);
-
-    pa_assert(u);
-
-    usec = pa_sink_get_requested_latency_within_thread(u->sink);
-
-    if (usec == (pa_usec_t) -1)
-        usec = pa_bytes_to_usec(u->hwbuf_size, &u->sink->sample_spec);
-
-/*     pa_log_debug("hw buffer time: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); */
-
-    wm = pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec);
-
-    if (usec >= wm) {
-        *sleep_usec = usec - wm;
-        *process_usec = wm;
-    } else
-        *process_usec = *sleep_usec = usec / 2;
-
-/*     pa_log_debug("after watermark: %u ms", (unsigned) (*sleep_usec / PA_USEC_PER_MSEC)); */
-}
-
-static int try_recover(struct userdata *u, const char *call, int err) {
-    pa_assert(u);
-    pa_assert(call);
-    pa_assert(err < 0);
-
-    pa_log_debug("%s: %s", call, snd_strerror(err));
-
-    pa_assert(err != -EAGAIN);
-
-    if (err == -EPIPE)
-        pa_log_debug("%s: Buffer underrun!", call);
-
-    if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0) {
-        u->first = TRUE;
-        u->since_start = 0;
-        return 0;
-    }
-
-    pa_log("%s: %s", call, snd_strerror(err));
-    return -1;
-}
-
-static size_t check_left_to_play(struct userdata *u, snd_pcm_sframes_t n) {
-    size_t left_to_play;
-
-    if ((size_t) n*u->frame_size < u->hwbuf_size)
-        left_to_play = u->hwbuf_size - ((size_t) n*u->frame_size);
-    else
-        left_to_play = 0;
-
-    if (left_to_play > 0) {
-/*         pa_log_debug("%0.2f ms left to play", (double) pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) / PA_USEC_PER_MSEC); */
-    } else if (!u->first && !u->after_rewind) {
-        pa_log_info("Underrun!");
-
-        if (u->use_tsched) {
-            size_t old_watermark = u->tsched_watermark;
-
-            u->tsched_watermark *= 2;
-            fix_tsched_watermark(u);
-
-            if (old_watermark != u->tsched_watermark)
-                pa_log_notice("Increasing wakeup watermark to %0.2f ms",
-                              (double) pa_bytes_to_usec(u->tsched_watermark, &u->sink->sample_spec) / PA_USEC_PER_MSEC);
-        }
-    }
-
-    return left_to_play;
-}
-
-static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled) {
-    int work_done = 0;
-    pa_usec_t max_sleep_usec = 0, process_usec = 0;
-    size_t left_to_play;
-
-    pa_assert(u);
-    pa_sink_assert_ref(u->sink);
-
-    if (u->use_tsched)
-        hw_sleep_time(u, &max_sleep_usec, &process_usec);
-
-    for (;;) {
-        snd_pcm_sframes_t n;
-        int r;
-
-        snd_pcm_hwsync(u->pcm_handle);
-
-        /* First we determine how many samples are missing to fill the
-         * buffer up to 100% */
-
-        if (PA_UNLIKELY((n = pa_alsa_safe_avail_update(u->pcm_handle, u->hwbuf_size, &u->sink->sample_spec)) < 0)) {
-
-            if ((r = try_recover(u, "snd_pcm_avail_update", (int) n)) == 0)
-                continue;
-
-            return r;
-        }
-
-        left_to_play = check_left_to_play(u, n);
-
-        if (u->use_tsched)
-
-            /* We won't fill up the playback buffer before at least
-            * half the sleep time is over because otherwise we might
-            * ask for more data from the clients then they expect. We
-            * need to guarantee that clients only have to keep around
-            * a single hw buffer length. */
-
-            if (!polled &&
-                pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) > process_usec+max_sleep_usec/2)
-                break;
-
-        if (PA_UNLIKELY(n <= u->hwbuf_unused_frames)) {
-
-            if (polled)
-                pa_log("ALSA woke us up to write new data to the device, but there was actually nothing to write! "
-                       "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. "
-                       "We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail_update() returned 0.");
-
-            break;
-        }
-
-        n -= u->hwbuf_unused_frames;
-
-        polled = FALSE;
-
-/*         pa_log_debug("Filling up"); */
-
-        for (;;) {
-            pa_memchunk chunk;
-            void *p;
-            int err;
-            const snd_pcm_channel_area_t *areas;
-            snd_pcm_uframes_t offset, frames = (snd_pcm_uframes_t) n;
-            snd_pcm_sframes_t sframes;
-
-/*             pa_log_debug("%lu frames to write", (unsigned long) frames); */
-
-            if (PA_UNLIKELY((err = pa_alsa_safe_mmap_begin(u->pcm_handle, &areas, &offset, &frames, u->hwbuf_size, &u->sink->sample_spec)) < 0)) {
-
-                if ((r = try_recover(u, "snd_pcm_mmap_begin", err)) == 0)
-                    continue;
-
-                return r;
-            }
-
-            /* Make sure that if these memblocks need to be copied they will fit into one slot */
-            if (frames > pa_mempool_block_size_max(u->sink->core->mempool)/u->frame_size)
-                frames = pa_mempool_block_size_max(u->sink->core->mempool)/u->frame_size;
-
-            /* Check these are multiples of 8 bit */
-            pa_assert((areas[0].first & 7) == 0);
-            pa_assert((areas[0].step & 7)== 0);
-
-            /* We assume a single interleaved memory buffer */
-            pa_assert((areas[0].first >> 3) == 0);
-            pa_assert((areas[0].step >> 3) == u->frame_size);
-
-            p = (uint8_t*) areas[0].addr + (offset * u->frame_size);
-
-            chunk.memblock = pa_memblock_new_fixed(u->core->mempool, p, frames * u->frame_size, TRUE);
-            chunk.length = pa_memblock_get_length(chunk.memblock);
-            chunk.index = 0;
-
-            pa_sink_render_into_full(u->sink, &chunk);
-
-            /* FIXME: Maybe we can do something to keep this memory block
-             * a little bit longer around? */
-            pa_memblock_unref_fixed(chunk.memblock);
-
-            if (PA_UNLIKELY((sframes = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0)) {
-
-                if ((r = try_recover(u, "snd_pcm_mmap_commit", (int) sframes)) == 0)
-                    continue;
-
-                return r;
-            }
-
-            work_done = 1;
-
-            u->frame_index += (int64_t) frames;
-            u->since_start += frames * u->frame_size;
-
-/*             pa_log_debug("wrote %lu frames", (unsigned long) frames); */
-
-            if (frames >= (snd_pcm_uframes_t) n)
-                break;
-
-            n -= (snd_pcm_sframes_t) frames;
-        }
-    }
-
-    *sleep_usec = pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) - process_usec;
-    return work_done;
-}
-
-static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled) {
-    int work_done = 0;
-    pa_usec_t max_sleep_usec = 0, process_usec = 0;
-    size_t left_to_play;
-
-    pa_assert(u);
-    pa_sink_assert_ref(u->sink);
-
-    if (u->use_tsched)
-        hw_sleep_time(u, &max_sleep_usec, &process_usec);
-
-    for (;;) {
-        snd_pcm_sframes_t n;
-        int r;
-
-        snd_pcm_hwsync(u->pcm_handle);
-
-        if (PA_UNLIKELY((n = pa_alsa_safe_avail_update(u->pcm_handle, u->hwbuf_size, &u->sink->sample_spec)) < 0)) {
-
-            if ((r = try_recover(u, "snd_pcm_avail_update", (int) n)) == 0)
-                continue;
-
-            return r;
-        }
-
-        left_to_play = check_left_to_play(u, n);
-
-        if (u->use_tsched)
-
-            /* We won't fill up the playback buffer before at least
-            * half the sleep time is over because otherwise we might
-            * ask for more data from the clients then they expect. We
-            * need to guarantee that clients only have to keep around
-            * a single hw buffer length. */
-
-            if (!polled &&
-                pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) > process_usec+max_sleep_usec/2)
-                break;
-
-        if (PA_UNLIKELY(n <= u->hwbuf_unused_frames)) {
-
-            if (polled)
-                pa_log("ALSA woke us up to write new data to the device, but there was actually nothing to write! "
-                       "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. "
-                       "We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail_update() returned 0.");
-
-            break;
-        }
-
-        n -= u->hwbuf_unused_frames;
-
-        polled = FALSE;
-
-        for (;;) {
-            snd_pcm_sframes_t frames;
-            void *p;
-
-/*         pa_log_debug("%lu frames to write", (unsigned long) frames); */
-
-            if (u->memchunk.length <= 0)
-                pa_sink_render(u->sink, (size_t) n * u->frame_size, &u->memchunk);
-
-            pa_assert(u->memchunk.length > 0);
-
-            frames = (snd_pcm_sframes_t) (u->memchunk.length / u->frame_size);
-
-            if (frames > n)
-                frames = n;
-
-            p = pa_memblock_acquire(u->memchunk.memblock);
-            frames = snd_pcm_writei(u->pcm_handle, (const uint8_t*) p + u->memchunk.index, (snd_pcm_uframes_t) frames);
-            pa_memblock_release(u->memchunk.memblock);
-
-            pa_assert(frames != 0);
-
-            if (PA_UNLIKELY(frames < 0)) {
-
-                if ((r = try_recover(u, "snd_pcm_writei", (int) frames)) == 0)
-                    continue;
-
-                return r;
-            }
-
-            u->memchunk.index += (size_t) frames * u->frame_size;
-            u->memchunk.length -= (size_t) frames * u->frame_size;
-
-            if (u->memchunk.length <= 0) {
-                pa_memblock_unref(u->memchunk.memblock);
-                pa_memchunk_reset(&u->memchunk);
-            }
-
-            work_done = 1;
-
-            u->frame_index += frames;
-            u->since_start += (size_t) frames * u->frame_size;
-
-/*         pa_log_debug("wrote %lu frames", (unsigned long) frames); */
-
-            if (frames >= n)
-                break;
-
-            n -= frames;
-        }
-    }
-
-    *sleep_usec = pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) - process_usec;
-    return work_done;
-}
-
-static void update_smoother(struct userdata *u) {
-    snd_pcm_sframes_t delay  = 0;
-    int64_t frames;
-    int err;
-    pa_usec_t now1, now2;
-/*     struct timeval timestamp; */
-    snd_pcm_status_t *status;
-
-    snd_pcm_status_alloca(&status);
-
-    pa_assert(u);
-    pa_assert(u->pcm_handle);
-
-    /* Let's update the time smoother */
-
-    snd_pcm_hwsync(u->pcm_handle);
-    snd_pcm_avail_update(u->pcm_handle);
-
-/*     if (PA_UNLIKELY((err = snd_pcm_status(u->pcm_handle, status)) < 0)) { */
-/*         pa_log("Failed to query DSP status data: %s", snd_strerror(err)); */
-/*         return; */
-/*     } */
-
-/*     delay = snd_pcm_status_get_delay(status); */
-
-    if (PA_UNLIKELY((err = snd_pcm_delay(u->pcm_handle, &delay)) < 0)) {
-        pa_log("Failed to query DSP status data: %s", snd_strerror(err));
-        return;
-    }
-
-    frames = u->frame_index - delay;
-
-/*     pa_log_debug("frame_index = %llu, delay = %llu, p = %llu", (unsigned long long) u->frame_index, (unsigned long long) delay, (unsigned long long) frames); */
-
-/*     snd_pcm_status_get_tstamp(status, &timestamp); */
-/*     pa_rtclock_from_wallclock(&timestamp); */
-/*     now1 = pa_timeval_load(&timestamp); */
-
-    now1 = pa_rtclock_usec();
-    now2 = pa_bytes_to_usec((uint64_t) frames * u->frame_size, &u->sink->sample_spec);
-    pa_smoother_put(u->smoother, now1, now2);
-}
-
-static pa_usec_t sink_get_latency(struct userdata *u) {
-    pa_usec_t r = 0;
-    int64_t delay;
-    pa_usec_t now1, now2;
-
-    pa_assert(u);
-
-    now1 = pa_rtclock_usec();
-    now2 = pa_smoother_get(u->smoother, now1);
-
-    delay = (int64_t) pa_bytes_to_usec((uint64_t) u->frame_index * u->frame_size, &u->sink->sample_spec) - (int64_t) now2;
-
-    if (delay > 0)
-        r = (pa_usec_t) delay;
-
-    if (u->memchunk.memblock)
-        r += pa_bytes_to_usec(u->memchunk.length, &u->sink->sample_spec);
-
-    return r;
-}
-
-static int build_pollfd(struct userdata *u) {
-    pa_assert(u);
-    pa_assert(u->pcm_handle);
-
-    if (u->alsa_rtpoll_item)
-        pa_rtpoll_item_free(u->alsa_rtpoll_item);
-
-    if (!(u->alsa_rtpoll_item = pa_alsa_build_pollfd(u->pcm_handle, u->rtpoll)))
-        return -1;
-
-    return 0;
-}
-
-static int suspend(struct userdata *u) {
-    pa_assert(u);
-    pa_assert(u->pcm_handle);
-
-    pa_smoother_pause(u->smoother, pa_rtclock_usec());
-
-    /* Let's suspend -- we don't call snd_pcm_drain() here since that might
-     * take awfully long with our long buffer sizes today. */
-    snd_pcm_close(u->pcm_handle);
-    u->pcm_handle = NULL;
-
-    if (u->alsa_rtpoll_item) {
-        pa_rtpoll_item_free(u->alsa_rtpoll_item);
-        u->alsa_rtpoll_item = NULL;
-    }
-
-    pa_log_info("Device suspended...");
-
-    return 0;
-}
-
-static int update_sw_params(struct userdata *u) {
-    snd_pcm_uframes_t avail_min;
-    int err;
-
-    pa_assert(u);
-
-    /* Use the full buffer if noone asked us for anything specific */
-    u->hwbuf_unused_frames = 0;
-
-    if (u->use_tsched) {
-        pa_usec_t latency;
-
-        if ((latency = pa_sink_get_requested_latency_within_thread(u->sink)) != (pa_usec_t) -1) {
-            size_t b;
-
-            pa_log_debug("latency set to %0.2fms", (double) latency / PA_USEC_PER_MSEC);
-
-            b = pa_usec_to_bytes(latency, &u->sink->sample_spec);
-
-            /* We need at least one sample in our buffer */
-
-            if (PA_UNLIKELY(b < u->frame_size))
-                b = u->frame_size;
-
-            u->hwbuf_unused_frames = (snd_pcm_sframes_t)
-                (PA_LIKELY(b < u->hwbuf_size) ?
-                 ((u->hwbuf_size - b) / u->frame_size) : 0);
-        }
-
-        fix_tsched_watermark(u);
-    }
-
-    pa_log_debug("hwbuf_unused_frames=%lu", (unsigned long) u->hwbuf_unused_frames);
-
-    /* We need at last one frame in the used part of the buffer */
-    avail_min = (snd_pcm_uframes_t) u->hwbuf_unused_frames + 1;
-
-    if (u->use_tsched) {
-        pa_usec_t sleep_usec, process_usec;
-
-        hw_sleep_time(u, &sleep_usec, &process_usec);
-        avail_min += pa_usec_to_bytes(sleep_usec, &u->sink->sample_spec) / u->frame_size;
-    }
-
-    pa_log_debug("setting avail_min=%lu", (unsigned long) avail_min);
-
-    if ((err = pa_alsa_set_sw_params(u->pcm_handle, avail_min)) < 0) {
-        pa_log("Failed to set software parameters: %s", snd_strerror(err));
-        return err;
-    }
-
-    pa_sink_set_max_request(u->sink, u->hwbuf_size - (size_t) u->hwbuf_unused_frames * u->frame_size);
-
-    return 0;
-}
-
-static int unsuspend(struct userdata *u) {
-    pa_sample_spec ss;
-    int err;
-    pa_bool_t b, d;
-    unsigned nfrags;
-    snd_pcm_uframes_t period_size;
-
-    pa_assert(u);
-    pa_assert(!u->pcm_handle);
-
-    pa_log_info("Trying resume...");
-
-    snd_config_update_free_global();
-    if ((err = snd_pcm_open(&u->pcm_handle, u->device_name, SND_PCM_STREAM_PLAYBACK,
-                            /*SND_PCM_NONBLOCK|*/
-                            SND_PCM_NO_AUTO_RESAMPLE|
-                            SND_PCM_NO_AUTO_CHANNELS|
-                            SND_PCM_NO_AUTO_FORMAT)) < 0) {
-        pa_log("Error opening PCM device %s: %s", u->device_name, snd_strerror(err));
-        goto fail;
-    }
-
-    ss = u->sink->sample_spec;
-    nfrags = u->nfragments;
-    period_size = u->fragment_size / u->frame_size;
-    b = u->use_mmap;
-    d = u->use_tsched;
-
-    if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &nfrags, &period_size, u->hwbuf_size / u->frame_size, &b, &d, TRUE)) < 0) {
-        pa_log("Failed to set hardware parameters: %s", snd_strerror(err));
-        goto fail;
-    }
-
-    if (b != u->use_mmap || d != u->use_tsched) {
-        pa_log_warn("Resume failed, couldn't get original access mode.");
-        goto fail;
-    }
-
-    if (!pa_sample_spec_equal(&ss, &u->sink->sample_spec)) {
-        pa_log_warn("Resume failed, couldn't restore original sample settings.");
-        goto fail;
-    }
-
-    if (nfrags != u->nfragments || period_size*u->frame_size != u->fragment_size) {
-        pa_log_warn("Resume failed, couldn't restore original fragment settings. (Old: %lu*%lu, New %lu*%lu)",
-                    (unsigned long) u->nfragments, (unsigned long) u->fragment_size,
-                    (unsigned long) nfrags, period_size * u->frame_size);
-        goto fail;
-    }
-
-    if (update_sw_params(u) < 0)
-        goto fail;
-
-    if (build_pollfd(u) < 0)
-        goto fail;
-
-    /* FIXME: We need to reload the volume somehow */
-
-    u->first = TRUE;
-    u->since_start = 0;
-
-    pa_log_info("Resumed successfully...");
-
-    return 0;
-
-fail:
-    if (u->pcm_handle) {
-        snd_pcm_close(u->pcm_handle);
-        u->pcm_handle = NULL;
-    }
-
-    return -1;
-}
-
-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 r = 0;
-
-            if (u->pcm_handle)
-                r = sink_get_latency(u);
-
-            *((pa_usec_t*) data) = r;
-
-            return 0;
-        }
-
-        case PA_SINK_MESSAGE_SET_STATE:
-
-            switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
-
-                case PA_SINK_SUSPENDED:
-                    pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
-
-                    if (suspend(u) < 0)
-                        return -1;
-
-                    break;
-
-                case PA_SINK_IDLE:
-                case PA_SINK_RUNNING:
-
-                    if (u->sink->thread_info.state == PA_SINK_INIT) {
-                        if (build_pollfd(u) < 0)
-                            return -1;
-                    }
-
-                    if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
-                        if (unsuspend(u) < 0)
-                            return -1;
-                    }
-
-                    break;
-
-                case PA_SINK_UNLINKED:
-                case PA_SINK_INIT:
-                    ;
-            }
-
-            break;
-    }
-
-    return pa_sink_process_msg(o, code, data, offset, chunk);
-}
-
-static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
-    struct userdata *u = snd_mixer_elem_get_callback_private(elem);
-
-    pa_assert(u);
-    pa_assert(u->mixer_handle);
-
-    if (mask == SND_CTL_EVENT_MASK_REMOVE)
-        return 0;
-
-    if (mask & SND_CTL_EVENT_MASK_VALUE) {
-        pa_sink_get_volume(u->sink, TRUE);
-        pa_sink_get_mute(u->sink, TRUE);
-    }
-
-    return 0;
-}
-
-static pa_volume_t from_alsa_volume(struct userdata *u, long alsa_vol) {
-
-    return (pa_volume_t) round(((double) (alsa_vol - u->hw_volume_min) * PA_VOLUME_NORM) /
-                               (double) (u->hw_volume_max - u->hw_volume_min));
-}
-
-static long to_alsa_volume(struct userdata *u, pa_volume_t vol) {
-    long alsa_vol;
-
-    alsa_vol = (long) round(((double) vol * (double) (u->hw_volume_max - u->hw_volume_min))
-                            / PA_VOLUME_NORM) + u->hw_volume_min;
-
-    return PA_CLAMP_UNLIKELY(alsa_vol, u->hw_volume_min, u->hw_volume_max);
-}
-
-static int sink_get_volume_cb(pa_sink *s) {
-    struct userdata *u = s->userdata;
-    int err;
-    unsigned i;
-    pa_cvolume r;
-    char t[PA_CVOLUME_SNPRINT_MAX];
-
-    pa_assert(u);
-    pa_assert(u->mixer_elem);
-
-    if (u->mixer_seperate_channels) {
-
-        r.channels = s->sample_spec.channels;
-
-        for (i = 0; i < s->sample_spec.channels; i++) {
-            long alsa_vol;
-
-            if (u->hw_dB_supported) {
-
-                if ((err = snd_mixer_selem_get_playback_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0)
-                    goto fail;
-
-#ifdef HAVE_VALGRIND_MEMCHECK_H
-                VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol));
-#endif
-
-                r.values[i] = pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0);
-            } else {
-
-                if ((err = snd_mixer_selem_get_playback_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0)
-                    goto fail;
-
-                r.values[i] = from_alsa_volume(u, alsa_vol);
-            }
-        }
-
-    } else {
-        long alsa_vol;
-
-        if (u->hw_dB_supported) {
-
-            if ((err = snd_mixer_selem_get_playback_dB(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0)
-                goto fail;
-
-#ifdef HAVE_VALGRIND_MEMCHECK_H
-            VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol));
-#endif
-
-            pa_cvolume_set(&r, s->sample_spec.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0));
-
-        } else {
-
-            if ((err = snd_mixer_selem_get_playback_volume(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0)
-                goto fail;
-
-            pa_cvolume_set(&r, s->sample_spec.channels, from_alsa_volume(u, alsa_vol));
-        }
-    }
-
-    pa_log_debug("Read hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &r));
-
-    if (!pa_cvolume_equal(&u->hardware_volume, &r)) {
-
-        u->hardware_volume = s->volume = r;
-
-        if (u->hw_dB_supported) {
-            pa_cvolume reset;
-
-            /* Hmm, so the hardware volume changed, let's reset our software volume */
-
-            pa_cvolume_reset(&reset, s->sample_spec.channels);
-            pa_sink_set_soft_volume(s, &reset);
-        }
-    }
-
-    return 0;
-
-fail:
-    pa_log_error("Unable to read volume: %s", snd_strerror(err));
-
-    return -1;
-}
-
-static int sink_set_volume_cb(pa_sink *s) {
-    struct userdata *u = s->userdata;
-    int err;
-    unsigned i;
-    pa_cvolume r;
-
-    pa_assert(u);
-    pa_assert(u->mixer_elem);
-
-    if (u->mixer_seperate_channels) {
-
-        r.channels = s->sample_spec.channels;
-
-        for (i = 0; i < s->sample_spec.channels; i++) {
-            long alsa_vol;
-            pa_volume_t vol;
-
-            vol = s->volume.values[i];
-
-            if (u->hw_dB_supported) {
-
-                alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100);
-                alsa_vol += u->hw_dB_max;
-                alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_dB_min, u->hw_dB_max);
-
-                if ((err = snd_mixer_selem_set_playback_dB(u->mixer_elem, u->mixer_map[i], alsa_vol, 1)) < 0)
-                    goto fail;
-
-                if ((err = snd_mixer_selem_get_playback_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0)
-                    goto fail;
-
-                r.values[i] = pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0);
-
-            } else {
-                alsa_vol = to_alsa_volume(u, vol);
-
-                if ((err = snd_mixer_selem_set_playback_volume(u->mixer_elem, u->mixer_map[i], alsa_vol)) < 0)
-                    goto fail;
-
-                if ((err = snd_mixer_selem_get_playback_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0)
-                    goto fail;
-
-                r.values[i] = from_alsa_volume(u, alsa_vol);
-            }
-        }
-
-    } else {
-        pa_volume_t vol;
-        long alsa_vol;
-
-        vol = pa_cvolume_max(&s->volume);
-
-        if (u->hw_dB_supported) {
-            alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100);
-            alsa_vol += u->hw_dB_max;
-            alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_dB_min, u->hw_dB_max);
-
-            if ((err = snd_mixer_selem_set_playback_dB_all(u->mixer_elem, alsa_vol, 1)) < 0)
-                goto fail;
-
-            if ((err = snd_mixer_selem_get_playback_dB(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0)
-                goto fail;
-
-            pa_cvolume_set(&r, s->volume.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0));
-
-        } else {
-            alsa_vol = to_alsa_volume(u, vol);
-
-            if ((err = snd_mixer_selem_set_playback_volume_all(u->mixer_elem, alsa_vol)) < 0)
-                goto fail;
-
-            if ((err = snd_mixer_selem_get_playback_volume(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0)
-                goto fail;
-
-            pa_cvolume_set(&r, s->sample_spec.channels, from_alsa_volume(u, alsa_vol));
-        }
-    }
-
-    u->hardware_volume = r;
-
-    if (u->hw_dB_supported) {
-        char t[PA_CVOLUME_SNPRINT_MAX];
-
-        /* Match exactly what the user requested by software */
-
-        pa_sw_cvolume_divide(&r, &s->volume, &r);
-        pa_sink_set_soft_volume(s, &r);
-
-        pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->volume));
-        pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &u->hardware_volume));
-        pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &r));
-
-    } else
-
-        /* We can't match exactly what the user requested, hence let's
-         * at least tell the user about it */
-
-        s->volume = r;
-
-    return 0;
-
-fail:
-    pa_log_error("Unable to set volume: %s", snd_strerror(err));
-
-    return -1;
-}
-
-static int sink_get_mute_cb(pa_sink *s) {
-    struct userdata *u = s->userdata;
-    int err, sw;
-
-    pa_assert(u);
-    pa_assert(u->mixer_elem);
-
-    if ((err = snd_mixer_selem_get_playback_switch(u->mixer_elem, 0, &sw)) < 0) {
-        pa_log_error("Unable to get switch: %s", snd_strerror(err));
-        return -1;
-    }
-
-    s->muted = !sw;
-
-    return 0;
-}
-
-static int sink_set_mute_cb(pa_sink *s) {
-    struct userdata *u = s->userdata;
-    int err;
-
-    pa_assert(u);
-    pa_assert(u->mixer_elem);
-
-    if ((err = snd_mixer_selem_set_playback_switch_all(u->mixer_elem, !s->muted)) < 0) {
-        pa_log_error("Unable to set switch: %s", snd_strerror(err));
-        return -1;
-    }
-
-    return 0;
-}
-
-static void sink_update_requested_latency_cb(pa_sink *s) {
-    struct userdata *u = s->userdata;
-    snd_pcm_sframes_t before;
-    pa_assert(u);
-
-    if (!u->pcm_handle)
-        return;
-
-    before = u->hwbuf_unused_frames;
-    update_sw_params(u);
-
-    /* Let's check whether we now use only a smaller part of the
-    buffer then before. If so, we need to make sure that subsequent
-    rewinds are relative to the new maxium fill level and not to the
-    current fill level. Thus, let's do a full rewind once, to clear
-    things up. */
-
-    if (u->hwbuf_unused_frames > before) {
-        pa_log_debug("Requesting rewind due to latency change.");
-        pa_sink_request_rewind(s, (size_t) -1);
-    }
-}
-
-static int process_rewind(struct userdata *u) {
-    snd_pcm_sframes_t unused;
-    size_t rewind_nbytes, unused_nbytes, limit_nbytes;
-    pa_assert(u);
-
-    /* Figure out how much we shall rewind and reset the counter */
-    rewind_nbytes = u->sink->thread_info.rewind_nbytes;
-    u->sink->thread_info.rewind_nbytes = 0;
-
-    if (rewind_nbytes <= 0)
-        goto finish;
-
-    pa_assert(rewind_nbytes > 0);
-    pa_log_debug("Requested to rewind %lu bytes.", (unsigned long) rewind_nbytes);
-
-    snd_pcm_hwsync(u->pcm_handle);
-    if ((unused = snd_pcm_avail_update(u->pcm_handle)) < 0) {
-        pa_log("snd_pcm_avail_update() failed: %s", snd_strerror((int) unused));
-        return -1;
-    }
-
-    unused_nbytes = u->tsched_watermark + (size_t) unused * u->frame_size;
-
-    if (u->hwbuf_size > unused_nbytes)
-        limit_nbytes = u->hwbuf_size - unused_nbytes;
-    else
-        limit_nbytes = 0;
-
-    if (rewind_nbytes > limit_nbytes)
-        rewind_nbytes = limit_nbytes;
-
-    if (rewind_nbytes > 0) {
-        snd_pcm_sframes_t in_frames, out_frames;
-
-        pa_log_debug("Limited to %lu bytes.", (unsigned long) rewind_nbytes);
-
-        in_frames = (snd_pcm_sframes_t) (rewind_nbytes / u->frame_size);
-        pa_log_debug("before: %lu", (unsigned long) in_frames);
-        if ((out_frames = snd_pcm_rewind(u->pcm_handle, (snd_pcm_uframes_t) in_frames)) < 0) {
-            pa_log("snd_pcm_rewind() failed: %s", snd_strerror((int) out_frames));
-            return -1;
-        }
-        pa_log_debug("after: %lu", (unsigned long) out_frames);
-
-        rewind_nbytes = (size_t) out_frames * u->frame_size;
-
-        if (rewind_nbytes <= 0)
-            pa_log_info("Tried rewind, but was apparently not possible.");
-        else {
-            u->frame_index -= out_frames;
-            pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes);
-            pa_sink_process_rewind(u->sink, rewind_nbytes);
-
-            u->after_rewind = TRUE;
-            return 0;
-        }
-    } else
-        pa_log_debug("Mhmm, actually there is nothing to rewind.");
-
-finish:
-
-    pa_sink_process_rewind(u->sink, 0);
-
-    return 0;
-
-}
-
-static void thread_func(void *userdata) {
-    struct userdata *u = userdata;
-    unsigned short revents = 0;
-
-    pa_assert(u);
-
-    pa_log_debug("Thread starting up");
-
-    if (u->core->realtime_scheduling)
-        pa_make_realtime(u->core->realtime_priority);
-
-    pa_thread_mq_install(&u->thread_mq);
-    pa_rtpoll_install(u->rtpoll);
-
-    for (;;) {
-        int ret;
-
-/*         pa_log_debug("loop"); */
-
-        /* Render some data and write it to the dsp */
-        if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
-            int work_done;
-            pa_usec_t sleep_usec = 0;
-
-            if (u->sink->thread_info.rewind_requested)
-                if (process_rewind(u) < 0)
-                        goto fail;
-
-            if (u->use_mmap)
-                work_done = mmap_write(u, &sleep_usec, revents & POLLOUT);
-            else
-                work_done = unix_write(u, &sleep_usec, revents & POLLOUT);
-
-            if (work_done < 0)
-                goto fail;
-
-/*             pa_log_debug("work_done = %i", work_done); */
-
-            if (work_done) {
-
-                if (u->first) {
-                    pa_log_info("Starting playback.");
-                    snd_pcm_start(u->pcm_handle);
-
-                    pa_smoother_resume(u->smoother, pa_rtclock_usec());
-                }
-
-                update_smoother(u);
-            }
-
-            if (u->use_tsched) {
-                pa_usec_t cusec;
-
-                if (u->since_start <= u->hwbuf_size) {
-
-                    /* USB devices on ALSA seem to hit a buffer
-                     * underrun during the first iterations much
-                     * quicker then we calculate here, probably due to
-                     * the transport latency. To accomodate for that
-                     * we artificially decrease the sleep time until
-                     * we have filled the buffer at least once
-                     * completely.*/
-
-                    /*pa_log_debug("Cutting sleep time for the initial iterations by half.");*/
-                    sleep_usec /= 2;
-                }
-
-                /* OK, the playback buffer is now full, let's
-                 * calculate when to wake up next */
-/*                 pa_log_debug("Waking up in %0.2fms (sound card clock).", (double) sleep_usec / PA_USEC_PER_MSEC); */
-
-                /* Convert from the sound card time domain to the
-                 * system time domain */
-                cusec = pa_smoother_translate(u->smoother, pa_rtclock_usec(), sleep_usec);
-
-/*                 pa_log_debug("Waking up in %0.2fms (system clock).", (double) cusec / PA_USEC_PER_MSEC); */
-
-                /* We don't trust the conversion, so we wake up whatever comes first */
-                pa_rtpoll_set_timer_relative(u->rtpoll, PA_MIN(sleep_usec, cusec));
-            }
-
-            u->first = FALSE;
-            u->after_rewind = FALSE;
-
-        } else if (u->use_tsched)
-
-            /* OK, we're in an invalid state, let's disable our timers */
-            pa_rtpoll_set_timer_disabled(u->rtpoll);
-
-        /* Hmm, nothing to do. Let's sleep */
-        if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
-            goto fail;
-
-        if (ret == 0)
-            goto finish;
-
-        /* Tell ALSA about this and process its response */
-        if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
-            struct pollfd *pollfd;
-            int err;
-            unsigned n;
-
-            pollfd = pa_rtpoll_item_get_pollfd(u->alsa_rtpoll_item, &n);
-
-            if ((err = snd_pcm_poll_descriptors_revents(u->pcm_handle, pollfd, n, &revents)) < 0) {
-                pa_log("snd_pcm_poll_descriptors_revents() failed: %s", snd_strerror(err));
-                goto fail;
-            }
-
-            if (revents & (POLLIN|POLLERR|POLLNVAL|POLLHUP|POLLPRI)) {
-                if (pa_alsa_recover_from_poll(u->pcm_handle, revents) < 0)
-                    goto fail;
-
-                u->first = TRUE;
-                u->since_start = 0;
-            }
-
-            if (revents && u->use_tsched)
-                pa_log_debug("Wakeup from ALSA!%s%s", (revents & POLLIN) ? " INPUT" : "", (revents & POLLOUT) ? " OUTPUT" : "");
-        } else
-            revents = 0;
-    }
-
-fail:
-    /* If this was no regular exit from the loop we have to continue
-     * processing messages until we received PA_MESSAGE_SHUTDOWN */
-    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
-    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
-
-finish:
-    pa_log_debug("Thread shutting down");
-}
-
 int pa__init(pa_module*m) {
-
     pa_modargs *ma = NULL;
-    struct userdata *u = NULL;
-    const char *dev_id;
-    pa_sample_spec ss;
-    pa_channel_map map;
-    uint32_t nfrags, hwbuf_size, frag_size, tsched_size, tsched_watermark;
-    snd_pcm_uframes_t period_frames, tsched_frames;
-    size_t frame_size;
-    snd_pcm_info_t *pcm_info = NULL;
-    int err;
-    const char *name;
-    char *name_buf = NULL;
-    pa_bool_t namereg_fail;
-    pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d;
-    pa_usec_t usec;
-    pa_sink_new_data data;
-    const pa_alsa_profile_info *profile = NULL;
-
-    snd_pcm_info_alloca(&pcm_info);
 
     pa_assert(m);
 
     pa_alsa_redirect_errors_inc();
+    snd_config_update_free_global();
 
     if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
         pa_log("Failed to parse module arguments");
         goto fail;
     }
 
-    ss = m->core->default_sample_spec;
-    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_ALSA) < 0) {
-        pa_log("Failed to parse sample specification and channel map");
-        goto fail;
-    }
-
-    frame_size = pa_frame_size(&ss);
-
-    nfrags = m->core->default_n_fragments;
-    frag_size = (uint32_t) pa_usec_to_bytes(m->core->default_fragment_size_msec*PA_USEC_PER_MSEC, &ss);
-    if (frag_size <= 0)
-        frag_size = (uint32_t) frame_size;
-    tsched_size = (uint32_t) pa_usec_to_bytes(DEFAULT_TSCHED_BUFFER_USEC, &ss);
-    tsched_watermark = (uint32_t) pa_usec_to_bytes(DEFAULT_TSCHED_WATERMARK_USEC, &ss);
-
-    if (pa_modargs_get_value_u32(ma, "fragments", &nfrags) < 0 ||
-        pa_modargs_get_value_u32(ma, "fragment_size", &frag_size) < 0 ||
-        pa_modargs_get_value_u32(ma, "tsched_buffer_size", &tsched_size) < 0 ||
-        pa_modargs_get_value_u32(ma, "tsched_buffer_watermark", &tsched_watermark) < 0) {
-        pa_log("Failed to parse buffer metrics");
-        goto fail;
-    }
-
-    hwbuf_size = frag_size * nfrags;
-    period_frames = frag_size/frame_size;
-    tsched_frames = tsched_size/frame_size;
-
-    if (pa_modargs_get_value_boolean(ma, "mmap", &use_mmap) < 0) {
-        pa_log("Failed to parse mmap argument.");
-        goto fail;
-    }
-
-    if (pa_modargs_get_value_boolean(ma, "tsched", &use_tsched) < 0) {
-        pa_log("Failed to parse tsched argument.");
-        goto fail;
-    }
-
-    if (use_tsched && !pa_rtclock_hrtimer()) {
-        pa_log_notice("Disabling timer-based scheduling because high-resolution timers are not available from the kernel.");
-        use_tsched = FALSE;
-    }
-
-    m->userdata = u = pa_xnew0(struct userdata, 1);
-    u->core = m->core;
-    u->module = m;
-    u->use_mmap = use_mmap;
-    u->use_tsched = use_tsched;
-    u->first = TRUE;
-    u->since_start = 0;
-    u->after_rewind = FALSE;
-    u->rtpoll = pa_rtpoll_new();
-    pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
-    u->alsa_rtpoll_item = NULL;
-
-    u->smoother = pa_smoother_new(DEFAULT_TSCHED_BUFFER_USEC*2, DEFAULT_TSCHED_BUFFER_USEC*2, TRUE, 5);
-    usec = pa_rtclock_usec();
-    pa_smoother_set_time_offset(u->smoother, usec);
-    pa_smoother_pause(u->smoother, usec);
-
-    snd_config_update_free_global();
-
-    b = use_mmap;
-    d = use_tsched;
-
-    if ((dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
-
-        if (!(u->pcm_handle = pa_alsa_open_by_device_id_auto(
-                      dev_id,
-                      &u->device_name,
-                      &ss, &map,
-                      SND_PCM_STREAM_PLAYBACK,
-                      &nfrags, &period_frames, tsched_frames,
-                      &b, &d, &profile)))
-
-            goto fail;
-
-    } else {
-
-        if (!(u->pcm_handle = pa_alsa_open_by_device_string(
-                      pa_modargs_get_value(ma, "device", DEFAULT_DEVICE),
-                      &u->device_name,
-                      &ss, &map,
-                      SND_PCM_STREAM_PLAYBACK,
-                      &nfrags, &period_frames, tsched_frames,
-                      &b, &d, FALSE)))
-            goto fail;
-
-    }
-
-    pa_assert(u->device_name);
-    pa_log_info("Successfully opened device %s.", u->device_name);
-
-    if (profile)
-        pa_log_info("Selected configuration '%s' (%s).", profile->description, profile->name);
-
-    if (use_mmap && !b) {
-        pa_log_info("Device doesn't support mmap(), falling back to UNIX read/write mode.");
-        u->use_mmap = use_mmap = FALSE;
-    }
-
-    if (use_tsched && (!b || !d)) {
-        pa_log_info("Cannot enabled timer-based scheduling, falling back to sound IRQ scheduling.");
-        u->use_tsched = use_tsched = FALSE;
-    }
-
-    if (u->use_mmap)
-        pa_log_info("Successfully enabled mmap() mode.");
-
-    if (u->use_tsched)
-        pa_log_info("Successfully enabled timer-based scheduling mode.");
-
-    if ((err = snd_pcm_info(u->pcm_handle, pcm_info)) < 0) {
-        pa_log("Error fetching PCM info: %s", snd_strerror(err));
+    if (!(m->userdata = pa_alsa_sink_new(m, ma, NULL)))
         goto fail;
-    }
-
-    /* ALSA might tweak the sample spec, so recalculate the frame size */
-    frame_size = pa_frame_size(&ss);
-
-    if ((err = snd_mixer_open(&u->mixer_handle, 0)) < 0)
-        pa_log_warn("Error opening mixer: %s", snd_strerror(err));
-    else {
-        pa_bool_t found = FALSE;
-
-        if (pa_alsa_prepare_mixer(u->mixer_handle, u->device_name) >= 0)
-            found = TRUE;
-        else {
-            snd_pcm_info_t *info;
-
-            snd_pcm_info_alloca(&info);
-
-            if (snd_pcm_info(u->pcm_handle, info) >= 0) {
-                char *md;
-                int card;
-
-                if ((card = snd_pcm_info_get_card(info)) >= 0) {
-
-                    md = pa_sprintf_malloc("hw:%i", card);
-
-                    if (strcmp(u->device_name, md))
-                        if (pa_alsa_prepare_mixer(u->mixer_handle, md) >= 0)
-                            found = TRUE;
-                    pa_xfree(md);
-                }
-            }
-        }
-
-        if (found)
-            if (!(u->mixer_elem = pa_alsa_find_elem(u->mixer_handle, "Master", "PCM", TRUE)))
-                found = FALSE;
-
-        if (!found) {
-            snd_mixer_close(u->mixer_handle);
-            u->mixer_handle = NULL;
-        }
-    }
-
-    if ((name = pa_modargs_get_value(ma, "sink_name", NULL)))
-        namereg_fail = TRUE;
-    else {
-        name = name_buf = pa_sprintf_malloc("alsa_output.%s", u->device_name);
-        namereg_fail = FALSE;
-    }
-
-    pa_sink_new_data_init(&data);
-    data.driver = __FILE__;
-    data.module = m;
-    pa_sink_new_data_set_name(&data, name);
-    data.namereg_fail = namereg_fail;
-    pa_sink_new_data_set_sample_spec(&data, &ss);
-    pa_sink_new_data_set_channel_map(&data, &map);
-
-    pa_alsa_init_proplist_pcm(data.proplist, pcm_info);
-    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name);
-    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (period_frames * frame_size * nfrags));
-    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size));
-    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial"));
-
-    if (profile) {
-        pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_NAME, profile->name);
-        pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_DESCRIPTION, profile->description);
-    }
-
-    u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
-    pa_sink_new_data_done(&data);
-    pa_xfree(name_buf);
-
-    if (!u->sink) {
-        pa_log("Failed to create sink object");
-        goto fail;
-    }
-
-    u->sink->parent.process_msg = sink_process_msg;
-    u->sink->update_requested_latency = sink_update_requested_latency_cb;
-    u->sink->userdata = u;
-
-    pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
-    pa_sink_set_rtpoll(u->sink, u->rtpoll);
-
-    u->frame_size = frame_size;
-    u->fragment_size = frag_size = (uint32_t) (period_frames * frame_size);
-    u->nfragments = nfrags;
-    u->hwbuf_size = u->fragment_size * nfrags;
-    u->hwbuf_unused_frames = 0;
-    u->tsched_watermark = tsched_watermark;
-    u->frame_index = 0;
-    u->hw_dB_supported = FALSE;
-    u->hw_dB_min = u->hw_dB_max = 0;
-    u->hw_volume_min = u->hw_volume_max = 0;
-    u->mixer_seperate_channels = FALSE;
-    pa_cvolume_mute(&u->hardware_volume, u->sink->sample_spec.channels);
-
-    if (use_tsched)
-        fix_tsched_watermark(u);
-
-    u->sink->thread_info.max_rewind = use_tsched ? u->hwbuf_size : 0;
-    u->sink->thread_info.max_request = u->hwbuf_size;
-
-    pa_sink_set_latency_range(u->sink,
-                              !use_tsched ? pa_bytes_to_usec(u->hwbuf_size, &ss) : (pa_usec_t) -1,
-                              pa_bytes_to_usec(u->hwbuf_size, &ss));
-
-    pa_log_info("Using %u fragments of size %lu bytes, buffer time is %0.2fms",
-                nfrags, (long unsigned) u->fragment_size,
-                (double) pa_bytes_to_usec(u->hwbuf_size, &ss) / PA_USEC_PER_MSEC);
-
-    if (use_tsched)
-        pa_log_info("Time scheduling watermark is %0.2fms",
-                    (double) pa_bytes_to_usec(u->tsched_watermark, &ss) / PA_USEC_PER_MSEC);
-
-    if (update_sw_params(u) < 0)
-        goto fail;
-
-    pa_memchunk_reset(&u->memchunk);
-
-    if (u->mixer_handle) {
-        pa_assert(u->mixer_elem);
-
-        if (snd_mixer_selem_has_playback_volume(u->mixer_elem)) {
-            pa_bool_t suitable = FALSE;
-
-            if (snd_mixer_selem_get_playback_volume_range(u->mixer_elem, &u->hw_volume_min, &u->hw_volume_max) < 0)
-                pa_log_info("Failed to get volume range. Falling back to software volume control.");
-            else if (u->hw_volume_min >= u->hw_volume_max)
-                pa_log_warn("Your kernel driver is broken: it reports a volume range from %li to %li which makes no sense.", u->hw_volume_min, u->hw_volume_max);
-            else {
-                pa_log_info("Volume ranges from %li to %li.", u->hw_volume_min, u->hw_volume_max);
-                suitable = TRUE;
-            }
-
-            if (suitable) {
-                if (snd_mixer_selem_get_playback_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) < 0)
-                    pa_log_info("Mixer doesn't support dB information.");
-                else {
-#ifdef HAVE_VALGRIND_MEMCHECK_H
-                    VALGRIND_MAKE_MEM_DEFINED(&u->hw_dB_min, sizeof(u->hw_dB_min));
-                    VALGRIND_MAKE_MEM_DEFINED(&u->hw_dB_max, sizeof(u->hw_dB_max));
-#endif
-
-                    if (u->hw_dB_min >= u->hw_dB_max)
-                        pa_log_warn("Your kernel driver is broken: it reports a volume range from %0.2f dB to %0.2f dB which makes no sense.", (double) u->hw_dB_min/100.0, (double) u->hw_dB_max/100.0);
-                    else {
-                        pa_log_info("Volume ranges from %0.2f dB to %0.2f dB.", (double) u->hw_dB_min/100.0, (double) u->hw_dB_max/100.0);
-                        u->hw_dB_supported = TRUE;
-
-                        if (u->hw_dB_max > 0) {
-                            u->sink->base_volume = pa_sw_volume_from_dB(- (double) u->hw_dB_max/100.0);
-                            pa_log_info("Fixing base volume to %0.2f dB", pa_sw_volume_to_dB(u->sink->base_volume));
-                        } else
-                            pa_log_info("No particular base volume set, fixing to 0 dB");
-                    }
-                }
-
-                if (!u->hw_dB_supported &&
-                    u->hw_volume_max - u->hw_volume_min < 3) {
-
-                    pa_log_info("Device doesn't do dB volume and has less than 4 volume levels. Falling back to software volume control.");
-                    suitable = FALSE;
-                }
-            }
-
-            if (suitable) {
-                u->mixer_seperate_channels = pa_alsa_calc_mixer_map(u->mixer_elem, &map, u->mixer_map, TRUE) >= 0;
-
-                u->sink->get_volume = sink_get_volume_cb;
-                u->sink->set_volume = sink_set_volume_cb;
-                u->sink->flags |= PA_SINK_HW_VOLUME_CTRL | (u->hw_dB_supported ? PA_SINK_DECIBEL_VOLUME : 0);
-                pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->hw_dB_supported ? "supported" : "not supported");
-
-            } else
-                pa_log_info("Using software volume control.");
-        }
-
-        if (snd_mixer_selem_has_playback_switch(u->mixer_elem)) {
-            u->sink->get_mute = sink_get_mute_cb;
-            u->sink->set_mute = sink_set_mute_cb;
-            u->sink->flags |= PA_SINK_HW_MUTE_CTRL;
-        } else
-            pa_log_info("Using software mute control.");
-
-        u->mixer_fdl = pa_alsa_fdlist_new();
-
-        if (pa_alsa_fdlist_set_mixer(u->mixer_fdl, u->mixer_handle, m->core->mainloop) < 0) {
-            pa_log("Failed to initialize file descriptor monitoring");
-            goto fail;
-        }
-
-        snd_mixer_elem_set_callback(u->mixer_elem, mixer_callback);
-        snd_mixer_elem_set_callback_private(u->mixer_elem, u);
-    } else
-        u->mixer_fdl = NULL;
-
-    pa_alsa_dump(u->pcm_handle);
-
-    if (!(u->thread = pa_thread_new(thread_func, u))) {
-        pa_log("Failed to create thread.");
-        goto fail;
-    }
-
-    /* Get initial mixer settings */
-    if (data.volume_is_set) {
-        if (u->sink->set_volume)
-            u->sink->set_volume(u->sink);
-    } else {
-        if (u->sink->get_volume)
-            u->sink->get_volume(u->sink);
-    }
-
-    if (data.muted_is_set) {
-        if (u->sink->set_mute)
-            u->sink->set_mute(u->sink);
-    } else {
-        if (u->sink->get_mute)
-            u->sink->get_mute(u->sink);
-    }
-
-    pa_sink_put(u->sink);
 
     pa_modargs_free(ma);
 
@@ -1619,62 +100,21 @@ fail:
 }
 
 int pa__get_n_used(pa_module *m) {
-    struct userdata *u;
+    pa_sink *sink;
 
     pa_assert(m);
-    pa_assert_se(u = m->userdata);
+    pa_assert_se(sink = m->userdata);
 
-    return pa_sink_linked_by(u->sink);
+    return pa_sink_linked_by(sink);
 }
 
 void pa__done(pa_module*m) {
-    struct userdata *u;
+    pa_sink *sink;
 
     pa_assert(m);
 
-    if (!(u = m->userdata))
-        goto finish;
-
-    if (u->sink)
-        pa_sink_unlink(u->sink);
-
-    if (u->thread) {
-        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
-        pa_thread_free(u->thread);
-    }
-
-    pa_thread_mq_done(&u->thread_mq);
-
-    if (u->sink)
-        pa_sink_unref(u->sink);
-
-    if (u->memchunk.memblock)
-        pa_memblock_unref(u->memchunk.memblock);
-
-    if (u->alsa_rtpoll_item)
-        pa_rtpoll_item_free(u->alsa_rtpoll_item);
-
-    if (u->rtpoll)
-        pa_rtpoll_free(u->rtpoll);
-
-    if (u->mixer_fdl)
-        pa_alsa_fdlist_free(u->mixer_fdl);
-
-    if (u->mixer_handle)
-        snd_mixer_close(u->mixer_handle);
-
-    if (u->pcm_handle) {
-        snd_pcm_drop(u->pcm_handle);
-        snd_pcm_close(u->pcm_handle);
-    }
-
-    if (u->smoother)
-        pa_smoother_free(u->smoother);
-
-    pa_xfree(u->device_name);
-    pa_xfree(u);
-
-finish:
+    if ((sink = m->userdata))
+        pa_alsa_sink_free(sink);
 
     snd_config_update_free_global();
     pa_alsa_redirect_errors_dec();
diff --git a/src/modules/alsa/module-alsa-source.c b/src/modules/alsa/module-alsa-source.c
index 08ef12f..a3b5715 100644
--- a/src/modules/alsa/module-alsa-source.c
+++ b/src/modules/alsa/module-alsa-source.c
@@ -54,6 +54,7 @@
 #include <pulsecore/rtclock.h>
 
 #include "alsa-util.h"
+#include "alsa-source.h"
 #include "module-alsa-source-symdef.h"
 
 PA_MODULE_AUTHOR("Lennart Poettering");
@@ -93,1337 +94,22 @@ static const char* const valid_modargs[] = {
 };
 
 #define DEFAULT_DEVICE "default"
-#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC)       /* 2s */
-#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC)  /* 20ms */
-#define TSCHED_MIN_SLEEP_USEC (3*PA_USEC_PER_MSEC)           /* 3ms */
-#define TSCHED_MIN_WAKEUP_USEC (3*PA_USEC_PER_MSEC)          /* 3ms */
-
-struct userdata {
-    pa_core *core;
-    pa_module *module;
-    pa_source *source;
-
-    pa_thread *thread;
-    pa_thread_mq thread_mq;
-    pa_rtpoll *rtpoll;
-
-    snd_pcm_t *pcm_handle;
-
-    pa_alsa_fdlist *mixer_fdl;
-    snd_mixer_t *mixer_handle;
-    snd_mixer_elem_t *mixer_elem;
-    long hw_volume_max, hw_volume_min;
-    long hw_dB_max, hw_dB_min;
-    pa_bool_t hw_dB_supported;
-    pa_bool_t mixer_seperate_channels;
-
-    pa_cvolume hardware_volume;
-
-    size_t frame_size, fragment_size, hwbuf_size, tsched_watermark;
-    unsigned nfragments;
-
-    char *device_name;
-
-    pa_bool_t use_mmap, use_tsched;
-
-    pa_rtpoll_item *alsa_rtpoll_item;
-
-    snd_mixer_selem_channel_id_t mixer_map[SND_MIXER_SCHN_LAST];
-
-    pa_smoother *smoother;
-    int64_t frame_index;
-
-    snd_pcm_sframes_t hwbuf_unused_frames;
-};
-
-static void fix_tsched_watermark(struct userdata *u) {
-    size_t max_use;
-    size_t min_sleep, min_wakeup;
-    pa_assert(u);
-
-    max_use = u->hwbuf_size - (size_t) u->hwbuf_unused_frames * u->frame_size;
-
-    min_sleep = pa_usec_to_bytes(TSCHED_MIN_SLEEP_USEC, &u->source->sample_spec);
-    min_wakeup = pa_usec_to_bytes(TSCHED_MIN_WAKEUP_USEC, &u->source->sample_spec);
-
-    if (min_sleep > max_use/2)
-        min_sleep = pa_frame_align(max_use/2, &u->source->sample_spec);
-    if (min_sleep < u->frame_size)
-        min_sleep = u->frame_size;
-
-    if (min_wakeup > max_use/2)
-        min_wakeup = pa_frame_align(max_use/2, &u->source->sample_spec);
-    if (min_wakeup < u->frame_size)
-        min_wakeup = u->frame_size;
-
-    if (u->tsched_watermark > max_use-min_sleep)
-        u->tsched_watermark = max_use-min_sleep;
-
-    if (u->tsched_watermark < min_wakeup)
-        u->tsched_watermark = min_wakeup;
-}
-
-static pa_usec_t hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_usec_t*process_usec) {
-    pa_usec_t wm, usec;
-
-    pa_assert(u);
-
-    usec = pa_source_get_requested_latency_within_thread(u->source);
-
-    if (usec == (pa_usec_t) -1)
-        usec = pa_bytes_to_usec(u->hwbuf_size, &u->source->sample_spec);
-
-/*     pa_log_debug("hw buffer time: %u ms", (unsigned) (usec / PA_USEC_PER_MSEC)); */
-
-    wm = pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec);
-
-    if (usec >= wm) {
-        *sleep_usec = usec - wm;
-        *process_usec = wm;
-    } else
-        *process_usec = *sleep_usec = usec /= 2;
-
-/*     pa_log_debug("after watermark: %u ms", (unsigned) (*sleep_usec / PA_USEC_PER_MSEC)); */
-
-    return usec;
-}
-
-static int try_recover(struct userdata *u, const char *call, int err) {
-    pa_assert(u);
-    pa_assert(call);
-    pa_assert(err < 0);
-
-    pa_log_debug("%s: %s", call, snd_strerror(err));
-
-    pa_assert(err != -EAGAIN);
-
-    if (err == -EPIPE)
-        pa_log_debug("%s: Buffer overrun!", call);
-
-    if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0) {
-        snd_pcm_start(u->pcm_handle);
-        return 0;
-    }
-
-    pa_log("%s: %s", call, snd_strerror(err));
-    return -1;
-}
-
-static size_t check_left_to_record(struct userdata *u, snd_pcm_sframes_t n) {
-    size_t left_to_record;
-    size_t rec_space = u->hwbuf_size - (size_t) u->hwbuf_unused_frames*u->frame_size;
-
-    if ((size_t) n*u->frame_size < rec_space)
-        left_to_record = rec_space - ((size_t) n*u->frame_size);
-    else
-        left_to_record = 0;
-
-    if (left_to_record > 0) {
-/*         pa_log_debug("%0.2f ms left to record", (double) pa_bytes_to_usec(left_to_record, &u->source->sample_spec) / PA_USEC_PER_MSEC); */
-    } else {
-        pa_log_info("Overrun!");
-
-        if (u->use_tsched) {
-            size_t old_watermark = u->tsched_watermark;
-
-            u->tsched_watermark *= 2;
-            fix_tsched_watermark(u);
-
-            if (old_watermark != u->tsched_watermark)
-                pa_log_notice("Increasing wakeup watermark to %0.2f ms",
-                              (double) pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec) / PA_USEC_PER_MSEC);
-        }
-    }
-
-    return left_to_record;
-}
-
-static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled) {
-    int work_done = 0;
-    pa_usec_t max_sleep_usec = 0, process_usec = 0;
-    size_t left_to_record;
-
-    pa_assert(u);
-    pa_source_assert_ref(u->source);
-
-    if (u->use_tsched)
-        hw_sleep_time(u, &max_sleep_usec, &process_usec);
-
-    for (;;) {
-        snd_pcm_sframes_t n;
-        int r;
-
-        snd_pcm_hwsync(u->pcm_handle);
-
-        if (PA_UNLIKELY((n = pa_alsa_safe_avail_update(u->pcm_handle, u->hwbuf_size, &u->source->sample_spec)) < 0)) {
-
-            if ((r = try_recover(u, "snd_pcm_avail_update", (int) n)) == 0)
-                continue;
-
-            return r;
-        }
-
-        left_to_record = check_left_to_record(u, n);
-
-        if (u->use_tsched)
-            if (!polled &&
-                pa_bytes_to_usec(left_to_record, &u->source->sample_spec) > process_usec+max_sleep_usec/2)
-                break;
-
-        if (PA_UNLIKELY(n <= 0)) {
-
-            if (polled)
-                pa_log("ALSA woke us up to read new data from the device, but there was actually nothing to read! "
-                       "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. "
-                       "We were woken up with POLLIN set -- however a subsequent snd_pcm_avail_update() returned 0.");
-
-            break;
-        }
-
-        polled = FALSE;
-
-        for (;;) {
-            int err;
-            const snd_pcm_channel_area_t *areas;
-            snd_pcm_uframes_t offset, frames = (snd_pcm_uframes_t) n;
-            pa_memchunk chunk;
-            void *p;
-            snd_pcm_sframes_t sframes;
-
-/*             pa_log_debug("%lu frames to read", (unsigned long) frames); */
-
-            if (PA_UNLIKELY((err = pa_alsa_safe_mmap_begin(u->pcm_handle, &areas, &offset, &frames, u->hwbuf_size, &u->source->sample_spec)) < 0)) {
-
-                if ((r = try_recover(u, "snd_pcm_mmap_begin", err)) == 0)
-                    continue;
-
-                return r;
-            }
-
-            /* Make sure that if these memblocks need to be copied they will fit into one slot */
-            if (frames > pa_mempool_block_size_max(u->source->core->mempool)/u->frame_size)
-                frames = pa_mempool_block_size_max(u->source->core->mempool)/u->frame_size;
-
-            /* Check these are multiples of 8 bit */
-            pa_assert((areas[0].first & 7) == 0);
-            pa_assert((areas[0].step & 7)== 0);
-
-            /* We assume a single interleaved memory buffer */
-            pa_assert((areas[0].first >> 3) == 0);
-            pa_assert((areas[0].step >> 3) == u->frame_size);
-
-            p = (uint8_t*) areas[0].addr + (offset * u->frame_size);
-
-            chunk.memblock = pa_memblock_new_fixed(u->core->mempool, p, frames * u->frame_size, TRUE);
-            chunk.length = pa_memblock_get_length(chunk.memblock);
-            chunk.index = 0;
-
-            pa_source_post(u->source, &chunk);
-            pa_memblock_unref_fixed(chunk.memblock);
-
-            if (PA_UNLIKELY((sframes = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0)) {
-
-                if ((r = try_recover(u, "snd_pcm_mmap_commit", (int) sframes)) == 0)
-                    continue;
-
-                return r;
-            }
-
-            work_done = 1;
-
-            u->frame_index += (int64_t) frames;
-
-/*             pa_log_debug("read %lu frames", (unsigned long) frames); */
-
-            if (frames >= (snd_pcm_uframes_t) n)
-                break;
-
-            n -= (snd_pcm_sframes_t) frames;
-        }
-    }
-
-    *sleep_usec = pa_bytes_to_usec(left_to_record, &u->source->sample_spec) - process_usec;
-    return work_done;
-}
-
-static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled) {
-    int work_done = 0;
-    pa_usec_t max_sleep_usec = 0, process_usec = 0;
-    size_t left_to_record;
-
-    pa_assert(u);
-    pa_source_assert_ref(u->source);
-
-    if (u->use_tsched)
-        hw_sleep_time(u, &max_sleep_usec, &process_usec);
-
-    for (;;) {
-        snd_pcm_sframes_t n;
-        int r;
-
-        snd_pcm_hwsync(u->pcm_handle);
-
-        if (PA_UNLIKELY((n = pa_alsa_safe_avail_update(u->pcm_handle, u->hwbuf_size, &u->source->sample_spec)) < 0)) {
-
-            if ((r = try_recover(u, "snd_pcm_avail_update", (int) n)) == 0)
-                continue;
-
-            return r;
-        }
-
-        left_to_record = check_left_to_record(u, n);
-
-        if (u->use_tsched)
-            if (!polled &&
-                pa_bytes_to_usec(left_to_record, &u->source->sample_spec) > process_usec+max_sleep_usec/2)
-                break;
-
-        if (PA_UNLIKELY(n <= 0)) {
-
-            if (polled)
-                pa_log("ALSA woke us up to read new data from the device, but there was actually nothing to read! "
-                       "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. "
-                       "We were woken up with POLLIN set -- however a subsequent snd_pcm_avail_update() returned 0.");
-
-            return work_done;
-        }
-
-        polled = FALSE;
-
-        for (;;) {
-            void *p;
-            snd_pcm_sframes_t frames;
-            pa_memchunk chunk;
-
-            chunk.memblock = pa_memblock_new(u->core->mempool, (size_t) -1);
-
-            frames = (snd_pcm_sframes_t) (pa_memblock_get_length(chunk.memblock) / u->frame_size);
-
-            if (frames > n)
-                frames = n;
-
-/*             pa_log_debug("%lu frames to read", (unsigned long) n); */
-
-            p = pa_memblock_acquire(chunk.memblock);
-            frames = snd_pcm_readi(u->pcm_handle, (uint8_t*) p, (snd_pcm_uframes_t) frames);
-            pa_memblock_release(chunk.memblock);
-
-            pa_assert(frames != 0);
-
-            if (PA_UNLIKELY(frames < 0)) {
-                pa_memblock_unref(chunk.memblock);
-
-                if ((r = try_recover(u, "snd_pcm_readi", (int) (frames))) == 0)
-                    continue;
-
-                return r;
-            }
-
-            chunk.index = 0;
-            chunk.length = (size_t) frames * u->frame_size;
-
-            pa_source_post(u->source, &chunk);
-            pa_memblock_unref(chunk.memblock);
-
-            work_done = 1;
-
-            u->frame_index += frames;
-
-/*             pa_log_debug("read %lu frames", (unsigned long) frames); */
-
-            if (frames >= n)
-                break;
-
-            n -= frames;
-        }
-    }
-
-    *sleep_usec = pa_bytes_to_usec(left_to_record, &u->source->sample_spec) - process_usec;
-    return work_done;
-}
-
-static void update_smoother(struct userdata *u) {
-    snd_pcm_sframes_t delay = 0;
-    int64_t frames;
-    int err;
-    pa_usec_t now1, now2;
-
-    pa_assert(u);
-    pa_assert(u->pcm_handle);
-
-    /* Let's update the time smoother */
-
-    snd_pcm_hwsync(u->pcm_handle);
-    snd_pcm_avail_update(u->pcm_handle);
-
-    if (PA_UNLIKELY((err = snd_pcm_delay(u->pcm_handle, &delay)) < 0)) {
-        pa_log_warn("Failed to get delay: %s", snd_strerror(err));
-        return;
-    }
-
-    frames = u->frame_index + delay;
-
-    now1 = pa_rtclock_usec();
-    now2 = pa_bytes_to_usec((uint64_t) frames * u->frame_size, &u->source->sample_spec);
-
-    pa_smoother_put(u->smoother, now1, now2);
-}
-
-static pa_usec_t source_get_latency(struct userdata *u) {
-    pa_usec_t r = 0;
-    int64_t delay;
-    pa_usec_t now1, now2;
-
-    pa_assert(u);
-
-    now1 = pa_rtclock_usec();
-    now2 = pa_smoother_get(u->smoother, now1);
-
-    delay = (int64_t) now2 - (int64_t) pa_bytes_to_usec((uint64_t) u->frame_index * u->frame_size, &u->source->sample_spec);
-
-    if (delay > 0)
-        r = (pa_usec_t) delay;
-
-    return r;
-}
-
-static int build_pollfd(struct userdata *u) {
-    pa_assert(u);
-    pa_assert(u->pcm_handle);
-
-    if (u->alsa_rtpoll_item)
-        pa_rtpoll_item_free(u->alsa_rtpoll_item);
-
-    if (!(u->alsa_rtpoll_item = pa_alsa_build_pollfd(u->pcm_handle, u->rtpoll)))
-        return -1;
-
-    return 0;
-}
-
-static int suspend(struct userdata *u) {
-    pa_assert(u);
-    pa_assert(u->pcm_handle);
-
-    pa_smoother_pause(u->smoother, pa_rtclock_usec());
-
-    /* Let's suspend */
-    snd_pcm_close(u->pcm_handle);
-    u->pcm_handle = NULL;
-
-    if (u->alsa_rtpoll_item) {
-        pa_rtpoll_item_free(u->alsa_rtpoll_item);
-        u->alsa_rtpoll_item = NULL;
-    }
-
-    pa_log_info("Device suspended...");
-
-    return 0;
-}
-
-static int update_sw_params(struct userdata *u) {
-    snd_pcm_uframes_t avail_min;
-    int err;
-
-    pa_assert(u);
-
-    /* Use the full buffer if noone asked us for anything specific */
-    u->hwbuf_unused_frames = 0;
-
-    if (u->use_tsched) {
-        pa_usec_t latency;
-
-        if ((latency = pa_source_get_requested_latency_within_thread(u->source)) != (pa_usec_t) -1) {
-            size_t b;
-
-            pa_log_debug("latency set to %0.2fms", (double) latency / PA_USEC_PER_MSEC);
-
-            b = pa_usec_to_bytes(latency, &u->source->sample_spec);
-
-            /* We need at least one sample in our buffer */
-
-            if (PA_UNLIKELY(b < u->frame_size))
-                b = u->frame_size;
-
-            u->hwbuf_unused_frames = (snd_pcm_sframes_t)
-                (PA_LIKELY(b < u->hwbuf_size) ?
-                 ((u->hwbuf_size - b) / u->frame_size) : 0);
-        }
-
-        fix_tsched_watermark(u);
-    }
-
-    pa_log_debug("hwbuf_unused_frames=%lu", (unsigned long) u->hwbuf_unused_frames);
-
-    avail_min = 1;
-
-    if (u->use_tsched) {
-        pa_usec_t sleep_usec, process_usec;
-
-        hw_sleep_time(u, &sleep_usec, &process_usec);
-        avail_min += pa_usec_to_bytes(sleep_usec, &u->source->sample_spec) / u->frame_size;
-    }
-
-    pa_log_debug("setting avail_min=%lu", (unsigned long) avail_min);
-
-    if ((err = pa_alsa_set_sw_params(u->pcm_handle, avail_min)) < 0) {
-        pa_log("Failed to set software parameters: %s", snd_strerror(err));
-        return err;
-    }
-
-    return 0;
-}
-
-static int unsuspend(struct userdata *u) {
-    pa_sample_spec ss;
-    int err;
-    pa_bool_t b, d;
-    unsigned nfrags;
-    snd_pcm_uframes_t period_size;
-
-    pa_assert(u);
-    pa_assert(!u->pcm_handle);
-
-    pa_log_info("Trying resume...");
-
-    snd_config_update_free_global();
-
-    if ((err = snd_pcm_open(&u->pcm_handle, u->device_name, SND_PCM_STREAM_CAPTURE,
-                            /*SND_PCM_NONBLOCK|*/
-                            SND_PCM_NO_AUTO_RESAMPLE|
-                            SND_PCM_NO_AUTO_CHANNELS|
-                            SND_PCM_NO_AUTO_FORMAT)) < 0) {
-        pa_log("Error opening PCM device %s: %s", u->device_name, snd_strerror(err));
-        goto fail;
-    }
-
-    ss = u->source->sample_spec;
-    nfrags = u->nfragments;
-    period_size = u->fragment_size / u->frame_size;
-    b = u->use_mmap;
-    d = u->use_tsched;
-
-    if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &nfrags, &period_size, u->hwbuf_size / u->frame_size, &b, &d, TRUE)) < 0) {
-        pa_log("Failed to set hardware parameters: %s", snd_strerror(err));
-        goto fail;
-    }
-
-    if (b != u->use_mmap || d != u->use_tsched) {
-        pa_log_warn("Resume failed, couldn't get original access mode.");
-        goto fail;
-    }
-
-    if (!pa_sample_spec_equal(&ss, &u->source->sample_spec)) {
-        pa_log_warn("Resume failed, couldn't restore original sample settings.");
-        goto fail;
-    }
-
-    if (nfrags != u->nfragments || period_size*u->frame_size != u->fragment_size) {
-        pa_log_warn("Resume failed, couldn't restore original fragment settings. (Old: %lu*%lu, New %lu*%lu)",
-                    (unsigned long) u->nfragments, (unsigned long) u->fragment_size,
-                    (unsigned long) nfrags, period_size * u->frame_size);
-        goto fail;
-    }
-
-    if (update_sw_params(u) < 0)
-        goto fail;
-
-    if (build_pollfd(u) < 0)
-        goto fail;
-
-    /* FIXME: We need to reload the volume somehow */
-
-    snd_pcm_start(u->pcm_handle);
-    pa_smoother_resume(u->smoother, pa_rtclock_usec());
-
-    pa_log_info("Resumed successfully...");
-
-    return 0;
-
-fail:
-    if (u->pcm_handle) {
-        snd_pcm_close(u->pcm_handle);
-        u->pcm_handle = NULL;
-    }
-
-    return -1;
-}
-
-static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
-    struct userdata *u = PA_SOURCE(o)->userdata;
-
-    switch (code) {
-
-        case PA_SOURCE_MESSAGE_GET_LATENCY: {
-            pa_usec_t r = 0;
-
-            if (u->pcm_handle)
-                r = source_get_latency(u);
-
-            *((pa_usec_t*) data) = r;
-
-            return 0;
-        }
-
-        case PA_SOURCE_MESSAGE_SET_STATE:
-
-            switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) {
-
-                case PA_SOURCE_SUSPENDED:
-                    pa_assert(PA_SOURCE_IS_OPENED(u->source->thread_info.state));
-
-                    if (suspend(u) < 0)
-                        return -1;
-
-                    break;
-
-                case PA_SOURCE_IDLE:
-                case PA_SOURCE_RUNNING:
-
-                    if (u->source->thread_info.state == PA_SOURCE_INIT) {
-                        if (build_pollfd(u) < 0)
-                            return -1;
-
-                        snd_pcm_start(u->pcm_handle);
-                    }
-
-                    if (u->source->thread_info.state == PA_SOURCE_SUSPENDED) {
-                        if (unsuspend(u) < 0)
-                            return -1;
-                    }
-
-                    break;
-
-                case PA_SOURCE_UNLINKED:
-                case PA_SOURCE_INIT:
-                    ;
-            }
-
-            break;
-    }
-
-    return pa_source_process_msg(o, code, data, offset, chunk);
-}
-
-static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
-    struct userdata *u = snd_mixer_elem_get_callback_private(elem);
-
-    pa_assert(u);
-    pa_assert(u->mixer_handle);
-
-    if (mask == SND_CTL_EVENT_MASK_REMOVE)
-        return 0;
-
-    if (mask & SND_CTL_EVENT_MASK_VALUE) {
-        pa_source_get_volume(u->source, TRUE);
-        pa_source_get_mute(u->source, TRUE);
-    }
-
-    return 0;
-}
-
-static pa_volume_t from_alsa_volume(struct userdata *u, long alsa_vol) {
-
-    return (pa_volume_t) round(((double) (alsa_vol - u->hw_volume_min) * PA_VOLUME_NORM) /
-                               (double) (u->hw_volume_max - u->hw_volume_min));
-}
-
-static long to_alsa_volume(struct userdata *u, pa_volume_t vol) {
-    long alsa_vol;
-
-    alsa_vol = (long) round(((double) vol * (double) (u->hw_volume_max - u->hw_volume_min))
-                            / PA_VOLUME_NORM) + u->hw_volume_min;
-
-    return PA_CLAMP_UNLIKELY(alsa_vol, u->hw_volume_min, u->hw_volume_max);
-}
-
-static int source_get_volume_cb(pa_source *s) {
-    struct userdata *u = s->userdata;
-    int err;
-    unsigned i;
-    pa_cvolume r;
-    char t[PA_CVOLUME_SNPRINT_MAX];
-
-    pa_assert(u);
-    pa_assert(u->mixer_elem);
-
-    if (u->mixer_seperate_channels) {
-
-        r.channels = s->sample_spec.channels;
-
-        for (i = 0; i < s->sample_spec.channels; i++) {
-            long alsa_vol;
-
-            if (u->hw_dB_supported) {
-
-                if ((err = snd_mixer_selem_get_capture_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0)
-                    goto fail;
-
-#ifdef HAVE_VALGRIND_MEMCHECK_H
-                VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol));
-#endif
-
-                r.values[i] = pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0);
-            } else {
-
-                if ((err = snd_mixer_selem_get_capture_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0)
-                    goto fail;
-
-                r.values[i] = from_alsa_volume(u, alsa_vol);
-            }
-        }
-
-    } else {
-        long alsa_vol;
-
-        if (u->hw_dB_supported) {
-
-            if ((err = snd_mixer_selem_get_capture_dB(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0)
-                goto fail;
-
-#ifdef HAVE_VALGRIND_MEMCHECK_H
-            VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol));
-#endif
-
-            pa_cvolume_set(&r, s->sample_spec.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0));
-
-        } else {
-
-            if ((err = snd_mixer_selem_get_capture_volume(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0)
-                goto fail;
-
-            pa_cvolume_set(&r, s->sample_spec.channels, from_alsa_volume(u, alsa_vol));
-        }
-    }
-
-    pa_log_debug("Read hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &r));
-
-    if (!pa_cvolume_equal(&u->hardware_volume, &r)) {
-
-        u->hardware_volume = s->volume = r;
-
-        if (u->hw_dB_supported) {
-            pa_cvolume reset;
-
-            /* Hmm, so the hardware volume changed, let's reset our software volume */
-
-            pa_cvolume_reset(&reset, s->sample_spec.channels);
-            pa_source_set_soft_volume(s, &reset);
-        }
-    }
-
-    return 0;
-
-fail:
-    pa_log_error("Unable to read volume: %s", snd_strerror(err));
-
-    return -1;
-}
-
-static int source_set_volume_cb(pa_source *s) {
-    struct userdata *u = s->userdata;
-    int err;
-    unsigned i;
-    pa_cvolume r;
-
-    pa_assert(u);
-    pa_assert(u->mixer_elem);
-
-    if (u->mixer_seperate_channels) {
-
-        r.channels = s->sample_spec.channels;
-
-        for (i = 0; i < s->sample_spec.channels; i++) {
-            long alsa_vol;
-            pa_volume_t vol;
-
-            vol = s->volume.values[i];
-
-            if (u->hw_dB_supported) {
-
-                alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100);
-                alsa_vol += u->hw_dB_max;
-                alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_dB_min, u->hw_dB_max);
-
-                if ((err = snd_mixer_selem_set_capture_dB(u->mixer_elem, u->mixer_map[i], alsa_vol, 1)) < 0)
-                    goto fail;
-
-                if ((err = snd_mixer_selem_get_capture_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0)
-                    goto fail;
-
-                r.values[i] = pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0);
-
-            } else {
-                alsa_vol = to_alsa_volume(u, vol);
-
-                if ((err = snd_mixer_selem_set_capture_volume(u->mixer_elem, u->mixer_map[i], alsa_vol)) < 0)
-                    goto fail;
-
-                if ((err = snd_mixer_selem_get_capture_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0)
-                    goto fail;
-
-                r.values[i] = from_alsa_volume(u, alsa_vol);
-            }
-        }
-
-    } else {
-        pa_volume_t vol;
-        long alsa_vol;
-
-        vol = pa_cvolume_max(&s->volume);
-
-        if (u->hw_dB_supported) {
-            alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100);
-            alsa_vol += u->hw_dB_max;
-            alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_dB_min, u->hw_dB_max);
-
-            if ((err = snd_mixer_selem_set_capture_dB_all(u->mixer_elem, alsa_vol, 1)) < 0)
-                goto fail;
-
-            if ((err = snd_mixer_selem_get_capture_dB(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0)
-                goto fail;
-
-            pa_cvolume_set(&r, s->volume.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0));
-
-        } else {
-            alsa_vol = to_alsa_volume(u, vol);
-
-            if ((err = snd_mixer_selem_set_capture_volume_all(u->mixer_elem, alsa_vol)) < 0)
-                goto fail;
-
-            if ((err = snd_mixer_selem_get_capture_volume(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0)
-                goto fail;
-
-            pa_cvolume_set(&r, s->sample_spec.channels, from_alsa_volume(u, alsa_vol));
-        }
-    }
-
-    u->hardware_volume = r;
-
-    if (u->hw_dB_supported) {
-        char t[PA_CVOLUME_SNPRINT_MAX];
-
-        /* Match exactly what the user requested by software */
-
-        pa_sw_cvolume_divide(&r, &s->volume, &r);
-        pa_source_set_soft_volume(s, &r);
-
-        pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->volume));
-        pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &u->hardware_volume));
-        pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &r));
-
-    } else
-
-        /* We can't match exactly what the user requested, hence let's
-         * at least tell the user about it */
-
-        s->volume = r;
-
-    return 0;
-
-fail:
-    pa_log_error("Unable to set volume: %s", snd_strerror(err));
-
-    return -1;
-}
-
-static int source_get_mute_cb(pa_source *s) {
-    struct userdata *u = s->userdata;
-    int err, sw;
-
-    pa_assert(u);
-    pa_assert(u->mixer_elem);
-
-    if ((err = snd_mixer_selem_get_capture_switch(u->mixer_elem, 0, &sw)) < 0) {
-        pa_log_error("Unable to get switch: %s", snd_strerror(err));
-        return -1;
-    }
-
-    s->muted = !sw;
-
-    return 0;
-}
-
-static int source_set_mute_cb(pa_source *s) {
-    struct userdata *u = s->userdata;
-    int err;
-
-    pa_assert(u);
-    pa_assert(u->mixer_elem);
-
-    if ((err = snd_mixer_selem_set_capture_switch_all(u->mixer_elem, !s->muted)) < 0) {
-        pa_log_error("Unable to set switch: %s", snd_strerror(err));
-        return -1;
-    }
-
-    return 0;
-}
-
-static void source_update_requested_latency_cb(pa_source *s) {
-    struct userdata *u = s->userdata;
-    pa_assert(u);
-
-    if (!u->pcm_handle)
-        return;
-
-    update_sw_params(u);
-}
-
-static void thread_func(void *userdata) {
-    struct userdata *u = userdata;
-    unsigned short revents = 0;
-
-    pa_assert(u);
-
-    pa_log_debug("Thread starting up");
-
-    if (u->core->realtime_scheduling)
-        pa_make_realtime(u->core->realtime_priority);
-
-    pa_thread_mq_install(&u->thread_mq);
-    pa_rtpoll_install(u->rtpoll);
-
-    for (;;) {
-        int ret;
-
-/*         pa_log_debug("loop"); */
-
-        /* Read some data and pass it to the sources */
-        if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
-            int work_done = 0;
-            pa_usec_t sleep_usec = 0;
-
-            if (u->use_mmap)
-                work_done = mmap_read(u, &sleep_usec, revents & POLLIN);
-            else
-                work_done = unix_read(u, &sleep_usec, revents & POLLIN);
-
-            if (work_done < 0)
-                goto fail;
-
-/*             pa_log_debug("work_done = %i", work_done); */
-
-            if (work_done)
-                update_smoother(u);
-
-            if (u->use_tsched) {
-                pa_usec_t cusec;
-
-                /* OK, the capture buffer is now empty, let's
-                 * calculate when to wake up next */
-
-/*                 pa_log_debug("Waking up in %0.2fms (sound card clock).", (double) sleep_usec / PA_USEC_PER_MSEC); */
-
-                /* Convert from the sound card time domain to the
-                 * system time domain */
-                cusec = pa_smoother_translate(u->smoother, pa_rtclock_usec(), sleep_usec);
-
-/*                 pa_log_debug("Waking up in %0.2fms (system clock).", (double) cusec / PA_USEC_PER_MSEC); */
-
-                /* We don't trust the conversion, so we wake up whatever comes first */
-                pa_rtpoll_set_timer_relative(u->rtpoll, PA_MIN(sleep_usec, cusec));
-            }
-        } else if (u->use_tsched)
-
-            /* OK, we're in an invalid state, let's disable our timers */
-            pa_rtpoll_set_timer_disabled(u->rtpoll);
-
-        /* Hmm, nothing to do. Let's sleep */
-        if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
-            goto fail;
-
-        if (ret == 0)
-            goto finish;
-
-        /* Tell ALSA about this and process its response */
-        if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
-            struct pollfd *pollfd;
-            int err;
-            unsigned n;
-
-            pollfd = pa_rtpoll_item_get_pollfd(u->alsa_rtpoll_item, &n);
-
-            if ((err = snd_pcm_poll_descriptors_revents(u->pcm_handle, pollfd, n, &revents)) < 0) {
-                pa_log("snd_pcm_poll_descriptors_revents() failed: %s", snd_strerror(err));
-                goto fail;
-            }
-
-            if (revents & (POLLOUT|POLLERR|POLLNVAL|POLLHUP|POLLPRI)) {
-                if (pa_alsa_recover_from_poll(u->pcm_handle, revents) < 0)
-                    goto fail;
-
-                snd_pcm_start(u->pcm_handle);
-            }
-
-            if (revents && u->use_tsched)
-                pa_log_debug("Wakeup from ALSA!%s%s", (revents & POLLIN) ? " INPUT" : "", (revents & POLLOUT) ? " OUTPUT" : "");
-        } else
-            revents = 0;
-    }
-
-fail:
-    /* If this was no regular exit from the loop we have to continue
-     * processing messages until we received PA_MESSAGE_SHUTDOWN */
-    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
-    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
-
-finish:
-    pa_log_debug("Thread shutting down");
-}
 
 int pa__init(pa_module*m) {
-
     pa_modargs *ma = NULL;
-    struct userdata *u = NULL;
-    const char *dev_id;
-    pa_sample_spec ss;
-    pa_channel_map map;
-    uint32_t nfrags, hwbuf_size, frag_size, tsched_size, tsched_watermark;
-    snd_pcm_uframes_t period_frames, tsched_frames;
-    size_t frame_size;
-    snd_pcm_info_t *pcm_info = NULL;
-    int err;
-    const char *name;
-    char *name_buf = NULL;
-    pa_bool_t namereg_fail;
-    pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d;
-    pa_source_new_data data;
-    const pa_alsa_profile_info *profile = NULL;
-
-    snd_pcm_info_alloca(&pcm_info);
 
     pa_assert(m);
 
     pa_alsa_redirect_errors_inc();
+    snd_config_update_free_global();
 
     if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
         pa_log("Failed to parse module arguments");
         goto fail;
     }
 
-    ss = m->core->default_sample_spec;
-    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_ALSA) < 0) {
-        pa_log("Failed to parse sample specification");
+    if (!(m->userdata = pa_alsa_source_new(m, ma, NULL)))
         goto fail;
-    }
-
-    frame_size = pa_frame_size(&ss);
-
-    nfrags = m->core->default_n_fragments;
-    frag_size = (uint32_t) pa_usec_to_bytes(m->core->default_fragment_size_msec*PA_USEC_PER_MSEC, &ss);
-    if (frag_size <= 0)
-        frag_size = (uint32_t) frame_size;
-    tsched_size = (uint32_t) pa_usec_to_bytes(DEFAULT_TSCHED_BUFFER_USEC, &ss);
-    tsched_watermark = (uint32_t) pa_usec_to_bytes(DEFAULT_TSCHED_WATERMARK_USEC, &ss);
-
-    if (pa_modargs_get_value_u32(ma, "fragments", &nfrags) < 0 ||
-        pa_modargs_get_value_u32(ma, "fragment_size", &frag_size) < 0 ||
-        pa_modargs_get_value_u32(ma, "tsched_buffer_size", &tsched_size) < 0 ||
-        pa_modargs_get_value_u32(ma, "tsched_buffer_watermark", &tsched_watermark) < 0) {
-        pa_log("Failed to parse buffer metrics");
-        goto fail;
-    }
-
-    hwbuf_size = frag_size * nfrags;
-    period_frames = frag_size/frame_size;
-    tsched_frames = tsched_size/frame_size;
-
-    if (pa_modargs_get_value_boolean(ma, "mmap", &use_mmap) < 0) {
-        pa_log("Failed to parse mmap argument.");
-        goto fail;
-    }
-
-    if (pa_modargs_get_value_boolean(ma, "tsched", &use_tsched) < 0) {
-        pa_log("Failed to parse timer_scheduling argument.");
-        goto fail;
-    }
-
-    if (use_tsched && !pa_rtclock_hrtimer()) {
-        pa_log_notice("Disabling timer-based scheduling because high-resolution timers are not available from the kernel.");
-        use_tsched = FALSE;
-    }
-
-    m->userdata = u = pa_xnew0(struct userdata, 1);
-    u->core = m->core;
-    u->module = m;
-    u->use_mmap = use_mmap;
-    u->use_tsched = use_tsched;
-    u->rtpoll = pa_rtpoll_new();
-    pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
-    u->alsa_rtpoll_item = NULL;
-
-    u->smoother = pa_smoother_new(DEFAULT_TSCHED_WATERMARK_USEC, DEFAULT_TSCHED_WATERMARK_USEC, TRUE, 5);
-    pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec());
-
-    snd_config_update_free_global();
-
-    b = use_mmap;
-    d = use_tsched;
-
-    if ((dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
-
-        if (!(u->pcm_handle = pa_alsa_open_by_device_id_auto(
-                      dev_id,
-                      &u->device_name,
-                      &ss, &map,
-                      SND_PCM_STREAM_CAPTURE,
-                      &nfrags, &period_frames, tsched_frames,
-                      &b, &d, &profile)))
-            goto fail;
-
-    } else {
-
-        if (!(u->pcm_handle = pa_alsa_open_by_device_string(
-                      pa_modargs_get_value(ma, "device", DEFAULT_DEVICE),
-                      &u->device_name,
-                      &ss, &map,
-                      SND_PCM_STREAM_CAPTURE,
-                      &nfrags, &period_frames, tsched_frames,
-                      &b, &d, FALSE)))
-            goto fail;
-    }
-
-    pa_assert(u->device_name);
-    pa_log_info("Successfully opened device %s.", u->device_name);
-
-    if (profile)
-        pa_log_info("Selected configuration '%s' (%s).", profile->description, profile->name);
-
-    if (use_mmap && !b) {
-        pa_log_info("Device doesn't support mmap(), falling back to UNIX read/write mode.");
-        u->use_mmap = use_mmap = FALSE;
-    }
-
-    if (use_tsched && (!b || !d)) {
-        pa_log_info("Cannot enabled timer-based scheduling, falling back to sound IRQ scheduling.");
-        u->use_tsched = use_tsched = FALSE;
-    }
-
-    if (u->use_mmap)
-        pa_log_info("Successfully enabled mmap() mode.");
-
-    if (u->use_tsched)
-        pa_log_info("Successfully enabled timer-based scheduling mode.");
-
-    if ((err = snd_pcm_info(u->pcm_handle, pcm_info)) < 0) {
-        pa_log("Error fetching PCM info: %s", snd_strerror(err));
-        goto fail;
-    }
-
-    /* ALSA might tweak the sample spec, so recalculate the frame size */
-    frame_size = pa_frame_size(&ss);
-
-    if ((err = snd_mixer_open(&u->mixer_handle, 0)) < 0)
-        pa_log("Error opening mixer: %s", snd_strerror(err));
-    else {
-        pa_bool_t found = FALSE;
-
-        if (pa_alsa_prepare_mixer(u->mixer_handle, u->device_name) >= 0)
-            found = TRUE;
-        else {
-            snd_pcm_info_t* info;
-
-            snd_pcm_info_alloca(&info);
-
-            if (snd_pcm_info(u->pcm_handle, info) >= 0) {
-                char *md;
-                int card;
-
-                if ((card = snd_pcm_info_get_card(info)) >= 0) {
-
-                    md = pa_sprintf_malloc("hw:%i", card);
-
-                    if (strcmp(u->device_name, md))
-                        if (pa_alsa_prepare_mixer(u->mixer_handle, md) >= 0)
-                            found = TRUE;
-                    pa_xfree(md);
-                }
-            }
-        }
-
-        if (found)
-            if (!(u->mixer_elem = pa_alsa_find_elem(u->mixer_handle, "Capture", "Mic", FALSE)))
-                found = FALSE;
-
-        if (!found) {
-            snd_mixer_close(u->mixer_handle);
-            u->mixer_handle = NULL;
-        }
-    }
-
-    if ((name = pa_modargs_get_value(ma, "source_name", NULL)))
-        namereg_fail = TRUE;
-    else {
-        name = name_buf = pa_sprintf_malloc("alsa_input.%s", u->device_name);
-        namereg_fail = FALSE;
-    }
-
-    pa_source_new_data_init(&data);
-    data.driver = __FILE__;
-    data.module = m;
-    pa_source_new_data_set_name(&data, name);
-    data.namereg_fail = namereg_fail;
-    pa_source_new_data_set_sample_spec(&data, &ss);
-    pa_source_new_data_set_channel_map(&data, &map);
-
-    pa_alsa_init_proplist_pcm(data.proplist, pcm_info);
-    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name);
-    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (period_frames * frame_size * nfrags));
-    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size));
-    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial"));
-
-    if (profile) {
-        pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_NAME, profile->name);
-        pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_DESCRIPTION, profile->description);
-    }
-
-    u->source = pa_source_new(m->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
-    pa_source_new_data_done(&data);
-    pa_xfree(name_buf);
-
-    if (!u->source) {
-        pa_log("Failed to create source object");
-        goto fail;
-    }
-
-    u->source->parent.process_msg = source_process_msg;
-    u->source->update_requested_latency = source_update_requested_latency_cb;
-    u->source->userdata = u;
-
-    pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
-    pa_source_set_rtpoll(u->source, u->rtpoll);
-
-    u->frame_size = frame_size;
-    u->fragment_size = frag_size = (uint32_t) (period_frames * frame_size);
-    u->nfragments = nfrags;
-    u->hwbuf_size = u->fragment_size * nfrags;
-    u->hwbuf_unused_frames = 0;
-    u->tsched_watermark = tsched_watermark;
-    u->frame_index = 0;
-    u->hw_dB_supported = FALSE;
-    u->hw_dB_min = u->hw_dB_max = 0;
-    u->hw_volume_min = u->hw_volume_max = 0;
-    u->mixer_seperate_channels = FALSE;
-    pa_cvolume_mute(&u->hardware_volume, u->source->sample_spec.channels);
-
-    if (use_tsched)
-        fix_tsched_watermark(u);
-
-    pa_source_set_latency_range(u->source,
-                                !use_tsched ? pa_bytes_to_usec(u->hwbuf_size, &ss) : (pa_usec_t) -1,
-                                pa_bytes_to_usec(u->hwbuf_size, &ss));
-
-    pa_log_info("Using %u fragments of size %lu bytes, buffer time is %0.2fms",
-                nfrags, (long unsigned) u->fragment_size,
-                (double) pa_bytes_to_usec(u->hwbuf_size, &ss) / PA_USEC_PER_MSEC);
-
-    if (use_tsched)
-        pa_log_info("Time scheduling watermark is %0.2fms",
-                    (double) pa_bytes_to_usec(u->tsched_watermark, &ss) / PA_USEC_PER_MSEC);
-
-    if (update_sw_params(u) < 0)
-        goto fail;
-
-    if (u->mixer_handle) {
-        pa_assert(u->mixer_elem);
-
-        if (snd_mixer_selem_has_capture_volume(u->mixer_elem)) {
-            pa_bool_t suitable = FALSE;
-
-            if (snd_mixer_selem_get_capture_volume_range(u->mixer_elem, &u->hw_volume_min, &u->hw_volume_max) < 0)
-                pa_log_info("Failed to get volume range. Falling back to software volume control.");
-            else if (u->hw_volume_min >= u->hw_volume_max)
-                pa_log_warn("Your kernel driver is broken: it reports a volume range from %li to %li which makes no sense.", u->hw_volume_min, u->hw_volume_max);
-            else {
-                pa_log_info("Volume ranges from %li to %li.", u->hw_volume_min, u->hw_volume_max);
-                suitable = TRUE;
-            }
-
-            if (suitable) {
-                if (snd_mixer_selem_get_capture_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) < 0)
-                    pa_log_info("Mixer doesn't support dB information.");
-                else {
-#ifdef HAVE_VALGRIND_MEMCHECK_H
-                    VALGRIND_MAKE_MEM_DEFINED(&u->hw_dB_min, sizeof(u->hw_dB_min));
-                    VALGRIND_MAKE_MEM_DEFINED(&u->hw_dB_max, sizeof(u->hw_dB_max));
-#endif
-
-                    if (u->hw_dB_min >= u->hw_dB_max)
-                        pa_log_warn("Your kernel driver is broken: it reports a volume range from %0.2f dB to %0.2f dB which makes no sense.", (double) u->hw_dB_min/100.0, (double) u->hw_dB_max/100.0);
-                    else {
-                        pa_log_info("Volume ranges from %0.2f dB to %0.2f dB.", (double) u->hw_dB_min/100.0, (double) u->hw_dB_max/100.0);
-                        u->hw_dB_supported = TRUE;
-
-                        if (u->hw_dB_max > 0) {
-                            u->source->base_volume = pa_sw_volume_from_dB(- (double) u->hw_dB_max/100.0);
-                            pa_log_info("Fixing base volume to %0.2f dB", pa_sw_volume_to_dB(u->source->base_volume));
-                        } else
-                            pa_log_info("No particular base volume set, fixing to 0 dB");
-
-                    }
-                }
-
-                if (!u->hw_dB_supported &&
-                    u->hw_volume_max - u->hw_volume_min < 3) {
-
-                    pa_log_info("Device has less than 4 volume levels. Falling back to software volume control.");
-                    suitable = FALSE;
-                }
-            }
-
-            if (suitable) {
-                u->mixer_seperate_channels = pa_alsa_calc_mixer_map(u->mixer_elem, &map, u->mixer_map, FALSE) >= 0;
-
-                u->source->get_volume = source_get_volume_cb;
-                u->source->set_volume = source_set_volume_cb;
-                u->source->flags |= PA_SOURCE_HW_VOLUME_CTRL | (u->hw_dB_supported ? PA_SOURCE_DECIBEL_VOLUME : 0);
-                pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->hw_dB_supported ? "supported" : "not supported");
-            } else
-                pa_log_info("Using software volume control.");
-        }
-
-        if (snd_mixer_selem_has_capture_switch(u->mixer_elem)) {
-            u->source->get_mute = source_get_mute_cb;
-            u->source->set_mute = source_set_mute_cb;
-            u->source->flags |= PA_SOURCE_HW_MUTE_CTRL;
-        } else
-            pa_log_info("Using software mute control.");
-
-        u->mixer_fdl = pa_alsa_fdlist_new();
-
-        if (pa_alsa_fdlist_set_mixer(u->mixer_fdl, u->mixer_handle, m->core->mainloop) < 0) {
-            pa_log("Failed to initialize file descriptor monitoring");
-            goto fail;
-        }
-
-        snd_mixer_elem_set_callback(u->mixer_elem, mixer_callback);
-        snd_mixer_elem_set_callback_private(u->mixer_elem, u);
-    } else
-        u->mixer_fdl = NULL;
-
-    pa_alsa_dump(u->pcm_handle);
-
-    if (!(u->thread = pa_thread_new(thread_func, u))) {
-        pa_log("Failed to create thread.");
-        goto fail;
-    }
-    /* Get initial mixer settings */
-    if (data.volume_is_set) {
-        if (u->source->set_volume)
-            u->source->set_volume(u->source);
-    } else {
-        if (u->source->get_volume)
-            u->source->get_volume(u->source);
-    }
-
-    if (data.muted_is_set) {
-        if (u->source->set_mute)
-            u->source->set_mute(u->source);
-    } else {
-        if (u->source->get_mute)
-            u->source->get_mute(u->source);
-    }
-
-    pa_source_put(u->source);
 
     pa_modargs_free(ma);
 
@@ -1440,59 +126,22 @@ fail:
 }
 
 int pa__get_n_used(pa_module *m) {
-    struct userdata *u;
+    pa_source *source;
 
     pa_assert(m);
-    pa_assert_se(u = m->userdata);
+    pa_assert_se(source = m->userdata);
 
-    return pa_source_linked_by(u->source);
+    return pa_source_linked_by(source);
 }
 
 void pa__done(pa_module*m) {
-    struct userdata *u;
+    pa_source *source;
 
     pa_assert(m);
 
-    if (!(u = m->userdata))
-        goto finish;
-
-    if (u->source)
-        pa_source_unlink(u->source);
-
-    if (u->thread) {
-        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
-        pa_thread_free(u->thread);
-    }
-
-    pa_thread_mq_done(&u->thread_mq);
-
-    if (u->source)
-        pa_source_unref(u->source);
-
-    if (u->alsa_rtpoll_item)
-        pa_rtpoll_item_free(u->alsa_rtpoll_item);
-
-    if (u->rtpoll)
-        pa_rtpoll_free(u->rtpoll);
-
-    if (u->mixer_fdl)
-        pa_alsa_fdlist_free(u->mixer_fdl);
-
-    if (u->mixer_handle)
-        snd_mixer_close(u->mixer_handle);
-
-    if (u->pcm_handle) {
-        snd_pcm_drop(u->pcm_handle);
-        snd_pcm_close(u->pcm_handle);
-    }
-
-    if (u->smoother)
-        pa_smoother_free(u->smoother);
-
-    pa_xfree(u->device_name);
-    pa_xfree(u);
+    if ((source = m->userdata))
+        pa_alsa_source_free(source);
 
-finish:
     snd_config_update_free_global();
     pa_alsa_redirect_errors_dec();
 }

commit b6b0e07c316be822ced5136fd29d96976ccab0f2
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jan 21 01:52:10 2009 +0100

    fix copy/paste error

diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index 57018a2..a30ef95 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -322,7 +322,7 @@ void pa_source_unlink(pa_source *s) {
     pa_idxset_remove_by_data(s->core->sources, s, NULL);
 
     if (s->card)
-        pa_idxset_remove_by_data(s->card->sinks, s, NULL);
+        pa_idxset_remove_by_data(s->card->sources, s, NULL);
 
     while ((o = pa_idxset_first(s->outputs, NULL))) {
         pa_assert(o != j);

commit 9a0dbda654614214d1f0a3556d814a415ef212eb
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jan 21 01:53:09 2009 +0100

    allow cards be referenced by their index

diff --git a/src/pulsecore/namereg.c b/src/pulsecore/namereg.c
index 2734bab..ed652ab 100644
--- a/src/pulsecore/namereg.c
+++ b/src/pulsecore/namereg.c
@@ -223,6 +223,8 @@ void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type) {
         return pa_idxset_get_by_index(c->sources, idx);
     else if (type == PA_NAMEREG_SAMPLE && c->scache)
         return pa_idxset_get_by_index(c->scache, idx);
+    else if (type == PA_NAMEREG_CARD)
+        return pa_idxset_get_by_index(c->cards, idx);
 
     return NULL;
 }

commit dc2a4bd887083b2374ed96a9e4943f0b55373ac0
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jan 21 01:54:14 2009 +0100

    add set-card-profile CLI command

diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c
index f810579..da3ef1e 100644
--- a/src/pulsecore/cli-command.c
+++ b/src/pulsecore/cli-command.c
@@ -123,6 +123,7 @@ static int pa_cli_command_update_sink_proplist(pa_core *c, pa_tokenizer *t, pa_s
 static int pa_cli_command_update_source_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
 static int pa_cli_command_update_sink_input_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
 static int pa_cli_command_update_source_output_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_card_profile(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
 
 /* A method table for all available commands */
 
@@ -173,6 +174,7 @@ static const struct command commands[] = {
     { "suspend-sink",            pa_cli_command_suspend_sink,       "Suspend sink (args: index|name, bool)", 3},
     { "suspend-source",          pa_cli_command_suspend_source,     "Suspend source (args: index|name, bool)", 3},
     { "suspend",                 pa_cli_command_suspend,            "Suspend all sinks and all sources (args: bool)", 2},
+    { "set-card-profile",        pa_cli_command_card_profile,       "Change the profile of a card (aargs: index, name)", 3},
     { "set-log-level",           pa_cli_command_log_level,          "Change the log level (args: numeric level)", 2},
     { "set-log-meta",            pa_cli_command_log_meta,           "Show source code location in log messages (args: bool)", 2},
     { "set-log-time",            pa_cli_command_log_time,           "Show timestamps in log messages (args: bool)", 2},
@@ -1411,10 +1413,43 @@ static int pa_cli_command_log_backtrace(pa_core *c, pa_tokenizer *t, pa_strbuf *
     return 0;
 }
 
+static int pa_cli_command_card_profile(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+    const char *n, *p;
+    pa_card *card;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(n = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a card either by its name or its index.\n");
+        return -1;
+    }
+
+    if (!(p = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify a profile by its name.\n");
+        return -1;
+    }
+
+    if (!(card = pa_namereg_get(c, n, PA_NAMEREG_CARD))) {
+        pa_strbuf_puts(buf, "No card found by this name or index.\n");
+        return -1;
+    }
+
+    if (pa_card_set_profile(card, p) < 0) {
+        pa_strbuf_printf(buf, "Failed to set card profile to '%s'.\n", p);
+        return -1;
+    }
+
+    return 0;
+}
+
 static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
     pa_module *m;
     pa_sink *sink;
     pa_source *source;
+    pa_card *card;
     int nl;
     const char *p;
     uint32_t idx;
@@ -1470,6 +1505,16 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
         pa_strbuf_printf(buf, "suspend-source %s %s\n", source->name, pa_yes_no(pa_source_get_state(source) == PA_SOURCE_SUSPENDED));
     }
 
+    for (card = pa_idxset_first(c->cards, &idx); card; card = pa_idxset_next(c->cards, &idx)) {
+
+        if (!nl) {
+            pa_strbuf_puts(buf, "\n");
+            nl = 1;
+        }
+
+        if (card->active_profile)
+            pa_strbuf_printf(buf, "set-card-profile %s %s\n", card->name, card->active_profile->name);
+    }
 
     nl = 0;
 

commit 1d0bd6e5b77b372cd5168d2d19713c201df71d66
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jan 21 01:55:26 2009 +0100

    remove bogus pa_core_check_idle() call

diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c
index cb77553..f42ad91 100644
--- a/src/pulsecore/card.c
+++ b/src/pulsecore/card.c
@@ -189,8 +189,6 @@ void pa_card_free(pa_card *c) {
     pa_xfree(c->driver);
     pa_xfree(c->name);
     pa_xfree(c);
-
-    pa_core_check_idle(core);
 }
 
 int pa_card_set_profile(pa_card *c, const char *name) {

commit cba4c6b9ebec0979d3dd98be5756b6a04859d146
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jan 21 01:59:15 2009 +0100

    when changing profiles do the actual assignment in the generic implementation

diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c
index f42ad91..af2a178 100644
--- a/src/pulsecore/card.c
+++ b/src/pulsecore/card.c
@@ -196,7 +196,7 @@ int pa_card_set_profile(pa_card *c, const char *name) {
     pa_assert(c);
 
     if (!c->set_profile) {
-        pa_log_warn("set_profile() operation not implemented for card %u", c->index);
+        pa_log_warn("set_profile() operation not implemented for card %u \"%s\"", c->index, c->name);
         return -1;
     }
 
@@ -214,5 +214,9 @@ int pa_card_set_profile(pa_card *c, const char *name) {
 
     pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, c->index);
 
+    pa_log_info("Successfully changed profile of card %u \"%s\" to %s", c->index, c->name, profile->name);
+
+    c->active_profile = profile;
+
     return 0;
 }

commit 28f05e0435360e517c9c0426f14a9090f07787ec
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jan 21 01:59:59 2009 +0100

    remove leftover define

diff --git a/src/modules/alsa/module-alsa-source.c b/src/modules/alsa/module-alsa-source.c
index a3b5715..468a126 100644
--- a/src/modules/alsa/module-alsa-source.c
+++ b/src/modules/alsa/module-alsa-source.c
@@ -93,8 +93,6 @@ static const char* const valid_modargs[] = {
     NULL
 };
 
-#define DEFAULT_DEVICE "default"
-
 int pa__init(pa_module*m) {
     pa_modargs *ma = NULL;
 

commit 7ca0e00a2c81bed61b0f5d0185b2458c416991fb
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jan 21 02:01:44 2009 +0100

    fill in dev_id properly

diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index 9556915..e890ef7 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -1204,7 +1204,7 @@ finish:
 pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const pa_alsa_profile_info *profile) {
 
     struct userdata *u = NULL;
-    const char *dev_id;
+    const char *dev_id = NULL;
     pa_sample_spec ss;
     pa_channel_map map;
     uint32_t nfrags, hwbuf_size, frag_size, tsched_size, tsched_watermark;
@@ -1222,6 +1222,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const pa_alsa_profile_in
     snd_pcm_info_alloca(&pcm_info);
 
     pa_assert(m);
+    pa_assert(ma);
 
     ss = m->core->default_sample_spec;
     if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_ALSA) < 0) {
@@ -1287,6 +1288,11 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const pa_alsa_profile_in
 
     if (profile) {
 
+        if (!(dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
+            pa_log("device_id= not set");
+            goto fail;
+        }
+
         if (!(u->pcm_handle = pa_alsa_open_by_device_id_profile(
                       dev_id,
                       &u->device_name,
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index c0bf20d..f4d9738 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -1039,7 +1039,7 @@ finish:
 pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const pa_alsa_profile_info *profile) {
 
     struct userdata *u = NULL;
-    const char *dev_id;
+    const char *dev_id = NULL;
     pa_sample_spec ss;
     pa_channel_map map;
     uint32_t nfrags, hwbuf_size, frag_size, tsched_size, tsched_watermark;
@@ -1116,6 +1116,11 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const pa_alsa_profil
 
     if (profile) {
 
+        if (!(dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
+            pa_log("device_id= not set");
+            goto fail;
+        }
+
         if (!(u->pcm_handle = pa_alsa_open_by_device_id_profile(
                       dev_id,
                       &u->device_name,

commit e8f93b125e0e0776024ae78a2bdd33daf4442326
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jan 21 02:02:30 2009 +0100

    make implementation of module-alsa-card complete

diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index e890ef7..e6b3e0e 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -1201,7 +1201,7 @@ finish:
     pa_log_debug("Thread shutting down");
 }
 
-pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const pa_alsa_profile_info *profile) {
+pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, const pa_alsa_profile_info *profile) {
 
     struct userdata *u = NULL;
     const char *dev_id = NULL;
@@ -1372,11 +1372,11 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const pa_alsa_profile_in
 
             if (snd_pcm_info(u->pcm_handle, info) >= 0) {
                 char *md;
-                int card;
+                int card_idx;
 
-                if ((card = snd_pcm_info_get_card(info)) >= 0) {
+                if ((card_idx = snd_pcm_info_get_card(info)) >= 0) {
 
-                    md = pa_sprintf_malloc("hw:%i", card);
+                    md = pa_sprintf_malloc("hw:%i", card_idx);
 
                     if (strcmp(u->device_name, md))
                         if (pa_alsa_prepare_mixer(u->mixer_handle, md) >= 0)
@@ -1407,8 +1407,9 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const pa_alsa_profile_in
     }
 
     pa_sink_new_data_init(&data);
-    data.driver = __FILE__;
+    data.driver = driver;
     data.module = m;
+    data.card = card;
     pa_sink_new_data_set_name(&data, name);
     data.namereg_fail = namereg_fail;
     pa_sink_new_data_set_sample_spec(&data, &ss);
diff --git a/src/modules/alsa/alsa-sink.h b/src/modules/alsa/alsa-sink.h
index e503201..47ece9e 100644
--- a/src/modules/alsa/alsa-sink.h
+++ b/src/modules/alsa/alsa-sink.h
@@ -29,7 +29,7 @@
 
 #include "alsa-util.h"
 
-pa_sink* pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const pa_alsa_profile_info *profile);
+pa_sink* pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, const pa_alsa_profile_info *profile);
 
 void pa_alsa_sink_free(pa_sink *s);
 
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index f4d9738..29145f6 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -1036,7 +1036,7 @@ finish:
     pa_log_debug("Thread shutting down");
 }
 
-pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const pa_alsa_profile_info *profile) {
+pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, const pa_alsa_profile_info *profile) {
 
     struct userdata *u = NULL;
     const char *dev_id = NULL;
@@ -1197,11 +1197,11 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const pa_alsa_profil
 
             if (snd_pcm_info(u->pcm_handle, info) >= 0) {
                 char *md;
-                int card;
+                int card_idx;
 
-                if ((card = snd_pcm_info_get_card(info)) >= 0) {
+                if ((card_idx = snd_pcm_info_get_card(info)) >= 0) {
 
-                    md = pa_sprintf_malloc("hw:%i", card);
+                    md = pa_sprintf_malloc("hw:%i", card_idx);
 
                     if (strcmp(u->device_name, md))
                         if (pa_alsa_prepare_mixer(u->mixer_handle, md) >= 0)
@@ -1223,14 +1223,18 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const pa_alsa_profil
 
     if ((name = pa_modargs_get_value(ma, "source_name", NULL)))
         namereg_fail = TRUE;
-    else {
+    else if ((name = pa_modargs_get_value(ma, "name", NULL))) {
+        name = name_buf = pa_sprintf_malloc("alsa_input.%s", name);
+        namereg_fail = TRUE;
+    } else {
         name = name_buf = pa_sprintf_malloc("alsa_input.%s", u->device_name);
         namereg_fail = FALSE;
     }
 
     pa_source_new_data_init(&data);
-    data.driver = __FILE__;
+    data.driver = driver;
     data.module = m;
+    data.card = card;
     pa_source_new_data_set_name(&data, name);
     data.namereg_fail = namereg_fail;
     pa_source_new_data_set_sample_spec(&data, &ss);
diff --git a/src/modules/alsa/alsa-source.h b/src/modules/alsa/alsa-source.h
index bdb24d1..5fed6cc 100644
--- a/src/modules/alsa/alsa-source.h
+++ b/src/modules/alsa/alsa-source.h
@@ -29,7 +29,7 @@
 
 #include "alsa-util.h"
 
-pa_source* pa_alsa_source_new(pa_module *m, pa_modargs *ma, const pa_alsa_profile_info *profile);
+pa_source* pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, const pa_alsa_profile_info *profile);
 
 void pa_alsa_source_free(pa_source *s);
 
diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
index 6c947c0..f250602 100644
--- a/src/modules/alsa/module-alsa-card.c
+++ b/src/modules/alsa/module-alsa-card.c
@@ -28,6 +28,8 @@
 #include <pulsecore/modargs.h>
 
 #include "alsa-util.h"
+#include "alsa-sink.h"
+#include "alsa-source.h"
 #include "module-alsa-card-symdef.h"
 
 PA_MODULE_AUTHOR("Lennart Poettering");
@@ -48,19 +50,17 @@ PA_MODULE_USAGE(
         "profile=<profile name>");
 
 static const char* const valid_modargs[] = {
-    "sink_name",
-    "device",
+    "name",
     "device_id",
     "format",
     "rate",
-    "channels",
-    "channel_map",
     "fragments",
     "fragment_size",
     "mmap",
     "tsched",
     "tsched_buffer_size",
     "tsched_buffer_watermark",
+    "profile",
     NULL
 };
 
@@ -73,10 +73,14 @@ struct userdata {
     char *device_id;
 
     pa_card *card;
+    pa_sink *sink;
+    pa_source *source;
+
+    pa_modargs *modargs;
 };
 
 struct profile_data {
-    const pa_alsa_profile_info *sink, *source;
+    const pa_alsa_profile_info *sink_profile, *source_profile;
 };
 
 static void enumerate_cb(
@@ -119,8 +123,8 @@ static void enumerate_cb(
 
     d = PA_CARD_PROFILE_DATA(p);
 
-    d->sink = sink;
-    d->source = source;
+    d->sink_profile = sink;
+    d->source_profile = source;
 
     pa_hashmap_put(profiles, p->name, p);
 }
@@ -132,11 +136,59 @@ static void add_disabled_profile(pa_hashmap *profiles) {
     p = pa_card_profile_new("off", "Off", sizeof(struct profile_data));
 
     d = PA_CARD_PROFILE_DATA(p);
-    d->sink = d->source = NULL;
+    d->sink_profile = d->source_profile = NULL;
 
     pa_hashmap_put(profiles, p->name, p);
 }
 
+static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
+    struct userdata *u;
+    struct profile_data *nd, *od;
+
+    pa_assert(c);
+    pa_assert(new_profile);
+    pa_assert_se(u = c->userdata);
+
+    nd = PA_CARD_PROFILE_DATA(new_profile);
+    od = PA_CARD_PROFILE_DATA(c->active_profile);
+
+    if (od->sink_profile != nd->sink_profile) {
+        if (u->sink) {
+            pa_alsa_sink_free(u->sink);
+            u->sink = NULL;
+        }
+
+        if (nd->sink_profile)
+            u->sink = pa_alsa_sink_new(c->module, u->modargs, __FILE__, c, nd->sink_profile);
+    }
+
+    if (od->source_profile != nd->source_profile) {
+        if (u->source) {
+            pa_alsa_source_free(u->source);
+            u->source = NULL;
+        }
+
+        if (nd->source_profile)
+            u->source = pa_alsa_source_new(c->module, u->modargs, __FILE__, c, nd->source_profile);
+    }
+
+    return 0;
+}
+
+static void init_profile(struct userdata *u) {
+    struct profile_data *d;
+
+    pa_assert(u);
+
+    d = PA_CARD_PROFILE_DATA(u->card->active_profile);
+
+    if (d->sink_profile)
+        u->sink = pa_alsa_sink_new(u->module, u->modargs, __FILE__, u->card, d->sink_profile);
+
+    if (d->source_profile)
+        u->source = pa_alsa_source_new(u->module, u->modargs, __FILE__, u->card, d->source_profile);
+}
+
 int pa__init(pa_module*m) {
     pa_card_new_data data;
     pa_modargs *ma;
@@ -144,6 +196,7 @@ int pa__init(pa_module*m) {
     struct userdata *u;
 
     pa_alsa_redirect_errors_inc();
+    snd_config_update_free_global();
 
     pa_assert(m);
 
@@ -152,10 +205,14 @@ int pa__init(pa_module*m) {
         goto fail;
     }
 
-    m->userdata = u = pa_xnew0(struct userdata, 1);
+    m->userdata = u = pa_xnew(struct userdata, 1);
     u->core = m->core;
     u->module = m;
     u->device_id = pa_xstrdup(pa_modargs_get_value(ma, "device_id", DEFAULT_DEVICE_ID));
+    u->card = NULL;
+    u->sink = NULL;
+    u->source = NULL;
+    u->modargs = ma;
 
     if ((alsa_card_index = snd_card_get_index(u->device_id)) < 0) {
         pa_log("Card '%s' doesn't exist: %s", u->device_id, snd_strerror(alsa_card_index));
@@ -186,17 +243,33 @@ int pa__init(pa_module*m) {
     u->card = pa_card_new(m->core, &data);
     pa_card_new_data_done(&data);
 
+    if (!u->card)
+        goto fail;
+
+    u->card->userdata = u;
+    u->card->set_profile = card_set_profile;
+
+    init_profile(u);
+
     return 0;
 
 fail:
 
-    if (ma)
-        pa_modargs_free(ma);
-
     pa__done(m);
     return -1;
 }
 
+int pa__get_n_used(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+    pa_assert_se(u = m->userdata);
+
+    return
+        (u->sink ? pa_sink_linked_by(u->sink) : 0) +
+        (u->source ? pa_source_linked_by(u->source) : 0);
+}
+
 void pa__done(pa_module*m) {
     struct userdata *u;
 
@@ -205,9 +278,18 @@ void pa__done(pa_module*m) {
     if (!(u = m->userdata))
         goto finish;
 
+    if (u->sink)
+        pa_alsa_sink_free(u->sink);
+
+    if (u->source)
+        pa_alsa_source_free(u->source);
+
     if (u->card)
         pa_card_free(u->card);
 
+    if (u->modargs)
+        pa_modargs_free(u->modargs);
+
     pa_xfree(u->device_id);
     pa_xfree(u);
 
diff --git a/src/modules/alsa/module-alsa-sink.c b/src/modules/alsa/module-alsa-sink.c
index f8303a5..6a66ad3 100644
--- a/src/modules/alsa/module-alsa-sink.c
+++ b/src/modules/alsa/module-alsa-sink.c
@@ -82,7 +82,7 @@ int pa__init(pa_module*m) {
         goto fail;
     }
 
-    if (!(m->userdata = pa_alsa_sink_new(m, ma, NULL)))
+    if (!(m->userdata = pa_alsa_sink_new(m, ma, __FILE__, NULL, NULL)))
         goto fail;
 
     pa_modargs_free(ma);
diff --git a/src/modules/alsa/module-alsa-source.c b/src/modules/alsa/module-alsa-source.c
index 468a126..7dc22fe 100644
--- a/src/modules/alsa/module-alsa-source.c
+++ b/src/modules/alsa/module-alsa-source.c
@@ -106,7 +106,7 @@ int pa__init(pa_module*m) {
         goto fail;
     }
 
-    if (!(m->userdata = pa_alsa_source_new(m, ma, NULL)))
+    if (!(m->userdata = pa_alsa_source_new(m, ma, __FILE__, NULL, NULL)))
         goto fail;
 
     pa_modargs_free(ma);

commit 9661cd04442e88fa500654a4b7ccea68ede2e123
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jan 21 02:46:36 2009 +0100

    make pa_card_new_data::active_profile a string

diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c
index af2a178..397c6d7 100644
--- a/src/pulsecore/card.c
+++ b/src/pulsecore/card.c
@@ -76,6 +76,13 @@ void pa_card_new_data_set_name(pa_card_new_data *data, const char *name) {
     data->name = pa_xstrdup(name);
 }
 
+void pa_card_new_data_set_profile(pa_card_new_data *data, const char *profile) {
+    pa_assert(data);
+
+    pa_xfree(data->active_profile);
+    data->active_profile = pa_xstrdup(profile);
+}
+
 void pa_card_new_data_done(pa_card_new_data *data) {
 
     pa_assert(data);
@@ -92,6 +99,7 @@ void pa_card_new_data_done(pa_card_new_data *data) {
     }
 
     pa_xfree(data->name);
+    pa_xfree(data->active_profile);
 }
 
 pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) {
@@ -126,21 +134,27 @@ pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) {
     c->sinks = pa_idxset_new(NULL, NULL);
     c->sources = pa_idxset_new(NULL, NULL);
 
+    /* As a minor optimization we just steal the list instead of
+     * copying it here */
     c->profiles = data->profiles;
     data->profiles = NULL;
-    if (!(c->active_profile = data->active_profile))
-        if (c->profiles) {
-            void *state = NULL;
-            pa_card_profile *p;
 
-            while ((p = pa_hashmap_iterate(c->profiles, &state, NULL))) {
-                if (!c->active_profile ||
-                    p->priority > c->active_profile->priority)
+    c->active_profile = NULL;
 
-                    c->active_profile = p;
-            }
+    if (data->active_profile && c->profiles)
+        c->active_profile = pa_hashmap_get(c->profiles, data->active_profile);
+
+    if (!c->active_profile && c->profiles) {
+        void *state = NULL;
+        pa_card_profile *p;
+
+        while ((p = pa_hashmap_iterate(c->profiles, &state, NULL))) {
+            if (!c->active_profile ||
+                p->priority > c->active_profile->priority)
+
+                c->active_profile = p;
         }
-    data->active_profile = NULL;
+    }
 
     c->userdata = NULL;
     c->set_profile = NULL;
diff --git a/src/pulsecore/card.h b/src/pulsecore/card.h
index 17733c5..9aa8f37 100644
--- a/src/pulsecore/card.h
+++ b/src/pulsecore/card.h
@@ -77,7 +77,7 @@ typedef struct pa_card_new_data {
     pa_module *module;
 
     pa_hashmap *profiles;
-    pa_card_profile *active_profile;
+    char *active_profile;
 
     pa_bool_t namereg_fail:1;
 } pa_card_new_data;
@@ -87,6 +87,7 @@ void pa_card_profile_free(pa_card_profile *c);
 
 pa_card_new_data *pa_card_new_data_init(pa_card_new_data *data);
 void pa_card_new_data_set_name(pa_card_new_data *data, const char *name);
+void pa_card_new_data_set_profile(pa_card_new_data *data, const char *profile);
 void pa_card_new_data_done(pa_card_new_data *data);
 
 pa_card *pa_card_new(pa_core *c, pa_card_new_data *data);

commit 10e5c70286c05afc1a80209a81acad83c59a8961
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jan 21 02:47:26 2009 +0100

    don't restore mute/volume when already set

diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c
index c0cb0dc..a24c0b3 100644
--- a/src/modules/module-device-restore.c
+++ b/src/modules/module-device-restore.c
@@ -98,14 +98,14 @@ static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct
     pa_log_info("Synced.");
 }
 
-static struct entry* read_entry(struct userdata *u, char *name) {
+static struct entry* read_entry(struct userdata *u, const char *name) {
     datum key, data;
     struct entry *e;
 
     pa_assert(u);
     pa_assert(name);
 
-    key.dptr = name;
+    key.dptr = (char*) name;
     key.dsize = (int) strlen(name);
 
     data = gdbm_fetch(u->gdbm_file, key);
@@ -235,13 +235,22 @@ static pa_hook_result_t sink_fixate_hook_callback(pa_core *c, pa_sink_new_data *
     if ((e = read_entry(u, name))) {
 
         if (u->restore_volume) {
-            pa_log_info("Restoring volume for sink %s.", new_data->name);
-            pa_sink_new_data_set_volume(new_data, pa_cvolume_remap(&e->volume, &e->channel_map, &new_data->channel_map));
+
+            if (!new_data->volume_is_set) {
+                pa_log_info("Restoring volume for sink %s.", new_data->name);
+                pa_sink_new_data_set_volume(new_data, pa_cvolume_remap(&e->volume, &e->channel_map, &new_data->channel_map));
+            } else
+                pa_log_debug("Not restoring volume for sink %s, because already set.", new_data->name);
+
         }
 
         if (u->restore_muted) {
-            pa_log_info("Restoring mute state for sink %s.", new_data->name);
-            pa_sink_new_data_set_muted(new_data, e->muted);
+
+            if (!new_data->muted_is_set) {
+                pa_log_info("Restoring mute state for sink %s.", new_data->name);
+                pa_sink_new_data_set_muted(new_data, e->muted);
+            } else
+                pa_log_debug("Not restoring mute state for sink %s, because already set.", new_data->name);
         }
 
         pa_xfree(e);
@@ -263,13 +272,21 @@ static pa_hook_result_t source_fixate_hook_callback(pa_core *c, pa_source_new_da
     if ((e = read_entry(u, name))) {
 
         if (u->restore_volume) {
-            pa_log_info("Restoring volume for source %s.", new_data->name);
-            pa_source_new_data_set_volume(new_data, pa_cvolume_remap(&e->volume, &e->channel_map, &new_data->channel_map));
+
+            if (!new_data->volume_is_set) {
+                pa_log_info("Restoring volume for source %s.", new_data->name);
+                pa_source_new_data_set_volume(new_data, pa_cvolume_remap(&e->volume, &e->channel_map, &new_data->channel_map));
+            } else
+                pa_log_debug("Not restoring volume for source %s, because already set.", new_data->name);
         }
 
         if (u->restore_muted) {
-            pa_log_info("Restoring mute state for source %s.", new_data->name);
-            pa_source_new_data_set_muted(new_data, e->muted);
+
+            if (!new_data->muted_is_set) {
+                pa_log_info("Restoring mute state for source %s.", new_data->name);
+                pa_source_new_data_set_muted(new_data, e->muted);
+            } else
+                pa_log_debug("Not restoring mute state for source %s, because already set.", new_data->name);
         }
 
         pa_xfree(e);

commit c512ebf4ab2b6363b810eb592dec7386ed144c4b
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jan 21 02:47:49 2009 +0100

    minor cleanups

diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c
index dd54a79..734b2c5 100644
--- a/src/modules/module-stream-restore.c
+++ b/src/modules/module-stream-restore.c
@@ -137,14 +137,14 @@ static char *get_name(pa_proplist *p, const char *prefix) {
     return pa_sprintf_malloc("%s-fallback:%s", prefix, r);
 }
 
-static struct entry* read_entry(struct userdata *u, char *name) {
+static struct entry* read_entry(struct userdata *u, const char *name) {
     datum key, data;
     struct entry *e;
 
     pa_assert(u);
     pa_assert(name);
 
-    key.dptr = name;
+    key.dptr = (char*) name;
     key.dsize = (int) strlen(name);
 
     data = gdbm_fetch(u->gdbm_file, key);
@@ -266,7 +266,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
 
         if (pa_cvolume_equal(pa_cvolume_remap(&old->volume, &old->channel_map, &entry.channel_map), &entry.volume) &&
             !old->muted == !entry.muted &&
-            strcmp(old->device, entry.device) == 0) {
+            strncmp(old->device, entry.device, sizeof(entry.device)) == 0) {
 
             pa_xfree(old);
             pa_xfree(name);

commit 13315a7e4c58f63e5f03b1aec13d0e79a8ee86af
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jan 21 02:49:42 2009 +0100

    add a card profile restore module

diff --git a/src/Makefile.am b/src/Makefile.am
index 3151b49..8d1271c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -865,6 +865,7 @@ modlibexec_LTLIBRARIES += \
 		module-volume-restore.la \
 		module-device-restore.la \
 		module-stream-restore.la \
+		module-card-restore.la \
 		module-default-device-restore.la \
 		module-always-sink.la \
 		module-rescue-streams.la \
@@ -1064,6 +1065,7 @@ SYMDEF_FILES = \
 		modules/module-volume-restore-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 \
 		modules/module-always-sink-symdef.h \
 		modules/module-rescue-streams-symdef.h \
@@ -1343,6 +1345,12 @@ module_stream_restore_la_LDFLAGS = $(MODULE_LDFLAGS)
 module_stream_restore_la_LIBADD = $(AM_LIBADD) libprotocol-native.la libpulsecore- at PA_MAJORMINORMICRO@.la -lgdbm libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
 module_stream_restore_la_CFLAGS = $(AM_CFLAGS)
 
+# Card profile restore module
+module_card_restore_la_SOURCES = modules/module-card-restore.c
+module_card_restore_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_card_restore_la_LIBADD = $(AM_LIBADD) libpulsecore- at PA_MAJORMINORMICRO@.la -lgdbm libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
+module_card_restore_la_CFLAGS = $(AM_CFLAGS)
+
 # Default sink/source restore module
 module_default_device_restore_la_SOURCES = modules/module-default-device-restore.c
 module_default_device_restore_la_LDFLAGS = $(MODULE_LDFLAGS)
diff --git a/src/modules/module-card-restore.c b/src/modules/module-card-restore.c
new file mode 100644
index 0000000..02e973c
--- /dev/null
+++ b/src/modules/module-card-restore.c
@@ -0,0 +1,284 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2006-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 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 <gdbm.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/volume.h>
+#include <pulse/timeval.h>
+#include <pulse/util.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/card.h>
+#include <pulsecore/namereg.h>
+
+#include "module-card-restore-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Automatically restore profile of cards");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+
+#define SAVE_INTERVAL 10
+
+static const char* const valid_modargs[] = {
+    NULL
+};
+
+struct userdata {
+    pa_core *core;
+    pa_module *module;
+    pa_subscription *subscription;
+    pa_hook_slot *card_new_hook_slot;
+    pa_time_event *save_time_event;
+    GDBM_FILE gdbm_file;
+};
+
+struct entry {
+    char profile[PA_NAME_MAX];
+};
+
+static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(a);
+    pa_assert(e);
+    pa_assert(tv);
+    pa_assert(u);
+
+    pa_assert(e == u->save_time_event);
+    u->core->mainloop->time_free(u->save_time_event);
+    u->save_time_event = NULL;
+
+    gdbm_sync(u->gdbm_file);
+    pa_log_info("Synced.");
+}
+
+static struct entry* read_entry(struct userdata *u, const char *name) {
+    datum key, data;
+    struct entry *e;
+
+    pa_assert(u);
+    pa_assert(name);
+
+    key.dptr = (char*) name;
+    key.dsize = (int) strlen(name);
+
+    data = gdbm_fetch(u->gdbm_file, key);
+
+    if (!data.dptr)
+        goto fail;
+
+    if (data.dsize != sizeof(struct entry)) {
+        pa_log_warn("Database contains entry for card %s of wrong size %lu != %lu", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry));
+        goto fail;
+    }
+
+    e = (struct entry*) data.dptr;
+
+    if (!memchr(e->profile, 0, sizeof(e->profile))) {
+        pa_log_warn("Database contains entry for card %s with missing NUL byte in profile name", name);
+        goto fail;
+    }
+
+    return e;
+
+fail:
+
+    pa_xfree(data.dptr);
+    return NULL;
+}
+
+static void trigger_save(struct userdata *u) {
+    struct timeval tv;
+
+    if (u->save_time_event)
+        return;
+
+    pa_gettimeofday(&tv);
+    tv.tv_sec += SAVE_INTERVAL;
+    u->save_time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, save_time_callback, u);
+}
+
+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;
+    datum key, data;
+    pa_card *card;
+
+    pa_assert(c);
+    pa_assert(u);
+
+    if (t != (PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_NEW) &&
+        t != (PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE))
+        return;
+
+    memset(&entry, 0, sizeof(entry));
+
+    if (!(card = pa_idxset_get_by_index(c->cards, idx)))
+        return;
+
+    pa_strlcpy(entry.profile, card->active_profile ? card->active_profile->name : "", sizeof(entry.profile));
+
+    if ((old = read_entry(u, card->name))) {
+
+        if (strncmp(old->profile, entry.profile, sizeof(entry.profile)) == 0) {
+            pa_xfree(old);
+            return;
+        }
+
+        pa_xfree(old);
+    }
+
+    key.dptr = card->name;
+    key.dsize = (int) strlen(card->name);
+
+    data.dptr = (void*) &entry;
+    data.dsize = sizeof(entry);
+
+    pa_log_info("Storing profile for card %s.", card->name);
+
+    gdbm_store(u->gdbm_file, key, data, GDBM_REPLACE);
+
+    trigger_save(u);
+}
+
+static pa_hook_result_t card_new_hook_callback(pa_core *c, pa_card_new_data *new_data, struct userdata *u) {
+    struct entry *e;
+
+    pa_assert(new_data);
+
+    if ((e = read_entry(u, new_data->name)) && e->profile) {
+
+        if (!new_data->active_profile) {
+            pa_card_new_data_set_profile(new_data, e->profile);
+            pa_log_info("Restoring profile for card %s.", new_data->name);
+        } else
+            pa_log_debug("Not restoring profile for card %s, because already set.", new_data->name);
+
+        pa_xfree(e);
+    }
+
+    return PA_HOOK_OK;
+}
+
+int pa__init(pa_module*m) {
+    pa_modargs *ma = NULL;
+    struct userdata *u;
+    char *fname, *fn;
+    pa_card *card;
+    uint32_t idx;
+    int gdbm_cache_size;
+
+    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_xnew(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    u->save_time_event = NULL;
+    u->gdbm_file = NULL;
+
+    u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_CARD, subscribe_callback, u);
+
+    u->card_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_CARD_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) card_new_hook_callback, u);
+
+    /* We include the host identifier in the file name because gdbm
+     * files are CPU dependant, and we don't want things to go wrong
+     * if we are on a multiarch system. */
+
+    fn = pa_sprintf_malloc("card-database."CANONICAL_HOST".gdbm");
+    fname = pa_state_path(fn, TRUE);
+    pa_xfree(fn);
+
+    if (!fname)
+        goto fail;
+
+    if (!(u->gdbm_file = gdbm_open(fname, 0, GDBM_WRCREAT|GDBM_NOLOCK, 0600, NULL))) {
+        pa_log("Failed to open volume database '%s': %s", fname, gdbm_strerror(gdbm_errno));
+        pa_xfree(fname);
+        goto fail;
+    }
+
+    /* By default the cache of gdbm is rather large, let's reduce it a bit to save memory */
+    gdbm_cache_size = 10;
+    gdbm_setopt(u->gdbm_file, GDBM_CACHESIZE, &gdbm_cache_size, sizeof(gdbm_cache_size));
+
+    pa_log_info("Sucessfully opened database file '%s'.", fname);
+    pa_xfree(fname);
+
+    for (card = pa_idxset_first(m->core->cards, &idx); card; card = pa_idxset_next(m->core->cards, &idx))
+        subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_NEW, card->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->card_new_hook_slot)
+        pa_hook_slot_free(u->card_new_hook_slot);
+
+    if (u->save_time_event)
+        u->core->mainloop->time_free(u->save_time_event);
+
+    if (u->gdbm_file)
+        gdbm_close(u->gdbm_file);
+
+    pa_xfree(u);
+}

commit 1375a9a0c2c7141d7ce9a404fa323dc5c3620a16
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jan 21 02:50:29 2009 +0100

    enable module-card-restore by default

diff --git a/src/daemon/default.pa.in b/src/daemon/default.pa.in
index 7de4c07..d761f5f 100755
--- a/src/daemon/default.pa.in
+++ b/src/daemon/default.pa.in
@@ -32,6 +32,7 @@
 ### Automatically restore the volume of streams and devices
 load-module module-device-restore
 load-module module-stream-restore
+load-module module-card-restore
 
 ### Load audio drivers statically (it's probably better to not load
 ### these drivers manually, but instead use module-hal-detect --

commit 996bba7522aa0fa4192103ae53ca1980a6a59a52
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jan 21 03:04:04 2009 +0100

    implement PA_COMMAND_SET_CARD_PROFILE

diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index aa541a8..af9c05b 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -256,6 +256,7 @@ static void command_update_stream_sample_rate(pa_pdispatch *pd, uint32_t command
 static void command_update_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 static void command_remove_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 static void command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_set_card_profile(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 
 static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
     [PA_COMMAND_ERROR] = NULL,
@@ -350,6 +351,8 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
     [PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST] = command_remove_proplist,
     [PA_COMMAND_REMOVE_CLIENT_PROPLIST] = command_remove_proplist,
 
+    [PA_COMMAND_SET_CARD_PROFILE] = command_set_card_profile,
+
     [PA_COMMAND_EXTENSION] = command_extension
 };
 
@@ -3989,6 +3992,43 @@ static void command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag,
         protocol_error(c);
 }
 
+static void command_set_card_profile(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+    uint32_t idx = PA_INVALID_INDEX;
+    const char *name = NULL, *profile = NULL;
+    pa_card *card = NULL;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        pa_tagstruct_gets(t, &name) < 0 ||
+        pa_tagstruct_gets(t, &profile) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || name, tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, idx == PA_INVALID_INDEX || !name, tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, !name || idx == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
+
+    if (idx != PA_INVALID_INDEX)
+        card = pa_idxset_get_by_index(c->protocol->core->cards, idx);
+    else
+        card = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_CARD);
+
+    CHECK_VALIDITY(c->pstream, card, tag, PA_ERR_NOENTITY);
+
+    if (pa_card_set_profile(card, profile) < 0) {
+        pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
+        return;
+    }
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
 
 /*** pstream callbacks ***/
 

commit 601293d346ef4bfc8f2ff80d6c13a0d66d9bdd57
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jan 21 03:04:19 2009 +0100

    implement pactl set-card-profile

diff --git a/src/utils/pactl.c b/src/utils/pactl.c
index ba91f50..0820641 100644
--- a/src/utils/pactl.c
+++ b/src/utils/pactl.c
@@ -45,7 +45,7 @@
 static pa_context *context = NULL;
 static pa_mainloop_api *mainloop_api = NULL;
 
-static char *device = NULL, *sample_name = NULL, *sink_name = NULL, *source_name = NULL, *module_name = NULL, *module_args = NULL;
+static char *device = NULL, *sample_name = NULL, *sink_name = NULL, *source_name = NULL, *module_name = NULL, *module_args = NULL, *card_name = NULL, *profile_name = NULL;
 static uint32_t sink_input_idx = PA_INVALID_INDEX, source_output_idx = PA_INVALID_INDEX;
 static uint32_t module_index;
 static int suspend;
@@ -73,6 +73,7 @@ static enum {
     UNLOAD_MODULE,
     SUSPEND_SINK,
     SUSPEND_SOURCE,
+    SET_CARD_PROFILE
 } action = NONE;
 
 static void quit(int ret) {
@@ -739,6 +740,10 @@ static void context_state_callback(pa_context *c, void *userdata) {
                         pa_operation_unref(pa_context_suspend_source_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL));
                     break;
 
+                case SET_CARD_PROFILE:
+                    pa_operation_unref(pa_context_set_card_profile_by_name(c, card_name, profile_name, simple_callback, NULL));
+                    break;
+
                 default:
                     assert(0);
             }
@@ -763,22 +768,23 @@ static void exit_signal_callback(pa_mainloop_api *m, pa_signal_event *e, int sig
 static void help(const char *argv0) {
 
     printf(_("%s [options] stat\n"
-           "%s [options] list\n"
-           "%s [options] exit\n"
-           "%s [options] upload-sample FILENAME [NAME]\n"
-           "%s [options] play-sample NAME [SINK]\n"
-           "%s [options] remove-sample NAME\n"
-           "%s [options] move-sink-input ID SINK\n"
-           "%s [options] move-source-output ID SOURCE\n"
-           "%s [options] load-module NAME [ARGS ...]\n"
-           "%s [options] unload-module ID\n"
-           "%s [options] suspend-sink [SINK] 1|0\n"
-           "%s [options] suspend-source [SOURCE] 1|0\n\n"
-           "  -h, --help                            Show this help\n"
-           "      --version                         Show version\n\n"
-           "  -s, --server=SERVER                   The name of the server to connect to\n"
-           "  -n, --client-name=NAME                How to call this client on the server\n"),
-           argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0);
+             "%s [options] list\n"
+             "%s [options] exit\n"
+             "%s [options] upload-sample FILENAME [NAME]\n"
+             "%s [options] play-sample NAME [SINK]\n"
+             "%s [options] remove-sample NAME\n"
+             "%s [options] move-sink-input ID SINK\n"
+             "%s [options] move-source-output ID SOURCE\n"
+             "%s [options] load-module NAME [ARGS ...]\n"
+             "%s [options] unload-module ID\n"
+             "%s [options] suspend-sink [SINK] 1|0\n"
+             "%s [options] suspend-source [SOURCE] 1|0\n"
+             "%s [options] set-card-profile [CARD] [PROFILE] \n\n"
+             "  -h, --help                            Show this help\n"
+             "      --version                         Show version\n\n"
+             "  -s, --server=SERVER                   The name of the server to connect to\n"
+             "  -n, --client-name=NAME                How to call this client on the server\n"),
+           argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0);
 }
 
 enum { ARG_VERSION = 256 };
@@ -959,7 +965,7 @@ int main(int argc, char *argv[]) {
             action = SUSPEND_SINK;
 
             if (argc > optind+3 || optind+1 >= argc) {
-                fprintf(stderr, _("You may not specify more than one sink. You have to specify at least one boolean value.\n"));
+                fprintf(stderr, _("You may not specify more than one sink. You have to specify a boolean value.\n"));
                 goto quit;
             }
 
@@ -972,7 +978,7 @@ int main(int argc, char *argv[]) {
             action = SUSPEND_SOURCE;
 
             if (argc > optind+3 || optind+1 >= argc) {
-                fprintf(stderr, _("You may not specify more than one source. You have to specify at least one boolean value.\n"));
+                fprintf(stderr, _("You may not specify more than one source. You have to specify a boolean value.\n"));
                 goto quit;
             }
 
@@ -980,6 +986,17 @@ int main(int argc, char *argv[]) {
 
             if (argc > optind+2)
                 source_name = pa_xstrdup(argv[optind+1]);
+        } else if (!strcmp(argv[optind], "set-card-profile")) {
+            action = SET_CARD_PROFILE;
+
+            if (argc != optind+3) {
+                fprintf(stderr, _("You have to specify a card name/index and a profile name\n"));
+                goto quit;
+            }
+
+            card_name = pa_xstrdup(argv[optind+1]);
+            profile_name = pa_xstrdup(argv[optind+2]);
+
         } else if (!strcmp(argv[optind], "help")) {
             help(bn);
             ret = 0;
@@ -1041,6 +1058,8 @@ quit:
     pa_xfree(source_name);
     pa_xfree(module_args);
     pa_xfree(client_name);
+    pa_xfree(card_name);
+    pa_xfree(profile_name);
 
     return ret;
 }

commit bf7217b6a5ef2cfd9c3399ba055f5643f89e0375
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 22 00:11:58 2009 +0100

    require autoconf 2.63

diff --git a/bootstrap.sh b/bootstrap.sh
index 4eacc5a..6698f68 100755
--- a/bootstrap.sh
+++ b/bootstrap.sh
@@ -59,8 +59,8 @@ else
     intltoolize --copy --force --automake
     "$LIBTOOLIZE" -c --force --ltdl --recursive
     run_versioned aclocal "$VERSION" -I m4
-    run_versioned autoconf 2.62 -Wall
-    run_versioned autoheader 2.62
+    run_versioned autoconf 2.63 -Wall
+    run_versioned autoheader 2.63
     run_versioned automake "$VERSION" --copy --foreign --add-missing
 
     if test "x$NOCONFIGURE" = "x"; then

commit 0f7954a9f5291ba865ff87b6884c16a3014c1b76
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 22 00:15:19 2009 +0100

    don't include full path in driver name.

diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c
index 397c6d7..ac9e6d9 100644
--- a/src/pulsecore/card.c
+++ b/src/pulsecore/card.c
@@ -28,6 +28,7 @@
 #include <string.h>
 
 #include <pulse/xmalloc.h>
+#include <pulse/util.h>
 
 #include <pulsecore/log.h>
 #include <pulsecore/macro.h>
@@ -128,7 +129,7 @@ pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) {
     c->core = core;
     c->name = pa_xstrdup(data->name);
     c->proplist = pa_proplist_copy(data->proplist);
-    c->driver = pa_xstrdup(data->driver);
+    c->driver = pa_xstrdup(pa_path_get_filename(data->driver));
     c->module = data->module;
 
     c->sinks = pa_idxset_new(NULL, NULL);
diff --git a/src/pulsecore/client.c b/src/pulsecore/client.c
index 1e65fcd..1800441 100644
--- a/src/pulsecore/client.c
+++ b/src/pulsecore/client.c
@@ -29,6 +29,7 @@
 #include <string.h>
 
 #include <pulse/xmalloc.h>
+#include <pulse/util.h>
 
 #include <pulsecore/core-subscribe.h>
 #include <pulsecore/log.h>
@@ -64,7 +65,7 @@ pa_client *pa_client_new(pa_core *core, pa_client_new_data *data) {
     c = pa_xnew(pa_client, 1);
     c->core = core;
     c->proplist = pa_proplist_copy(data->proplist);
-    c->driver = pa_xstrdup(data->driver);
+    c->driver = pa_xstrdup(pa_path_get_filename(data->driver));
     c->module = data->module;
 
     c->sink_inputs = pa_idxset_new(NULL, NULL);
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 8f18266..5667114 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -30,6 +30,7 @@
 
 #include <pulse/utf8.h>
 #include <pulse/xmalloc.h>
+#include <pulse/util.h>
 
 #include <pulsecore/sample-util.h>
 #include <pulsecore/core-subscribe.h>
@@ -229,7 +230,7 @@ pa_sink_input* pa_sink_input_new(
     i->state = PA_SINK_INPUT_INIT;
     i->flags = flags;
     i->proplist = pa_proplist_copy(data->proplist);
-    i->driver = pa_xstrdup(data->driver);
+    i->driver = pa_xstrdup(pa_path_get_filename(data->driver));
     i->module = data->module;
     i->sink = data->sink;
     i->client = data->client;
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index bd90cf5..df46a2c 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -32,6 +32,7 @@
 #include <pulse/utf8.h>
 #include <pulse/xmalloc.h>
 #include <pulse/timeval.h>
+#include <pulse/util.h>
 
 #include <pulsecore/sink-input.h>
 #include <pulsecore/namereg.h>
@@ -184,7 +185,7 @@ pa_sink* pa_sink_new(
     s->flags = flags;
     s->name = pa_xstrdup(name);
     s->proplist = pa_proplist_copy(data->proplist);
-    s->driver = pa_xstrdup(data->driver);
+    s->driver = pa_xstrdup(pa_path_get_filename(data->driver));
     s->module = data->module;
     s->card = data->card;
 
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index e314656..cb8ba28 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -29,6 +29,7 @@
 
 #include <pulse/utf8.h>
 #include <pulse/xmalloc.h>
+#include <pulse/util.h>
 
 #include <pulsecore/sample-util.h>
 #include <pulsecore/core-subscribe.h>
@@ -192,7 +193,7 @@ pa_source_output* pa_source_output_new(
     o->state = PA_SOURCE_OUTPUT_INIT;
     o->flags = flags;
     o->proplist = pa_proplist_copy(data->proplist);
-    o->driver = pa_xstrdup(data->driver);
+    o->driver = pa_xstrdup(pa_path_get_filename(data->driver));
     o->module = data->module;
     o->source = data->source;
     o->client = data->client;
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index a30ef95..fea66b7 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -31,6 +31,7 @@
 #include <pulse/utf8.h>
 #include <pulse/xmalloc.h>
 #include <pulse/timeval.h>
+#include <pulse/util.h>
 
 #include <pulsecore/source-output.h>
 #include <pulsecore/namereg.h>
@@ -175,7 +176,7 @@ pa_source* pa_source_new(
     s->flags = flags;
     s->name = pa_xstrdup(name);
     s->proplist = pa_proplist_copy(data->proplist);
-    s->driver = pa_xstrdup(data->driver);
+    s->driver = pa_xstrdup(pa_path_get_filename(data->driver));
     s->module = data->module;
     s->card = data->card;
 

commit bdfec1feba2585aaa5ad88a2760da5c38f3466e9
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 22 00:16:28 2009 +0100

    mark a few more ALSA dB values as 'valid' for valgrind

diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index e6b3e0e..61fb408 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -870,6 +870,10 @@ static int sink_set_volume_cb(pa_sink *s) {
                 if ((err = snd_mixer_selem_get_playback_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0)
                     goto fail;
 
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+                VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol));
+#endif
+
                 r.values[i] = pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0);
 
             } else {
@@ -902,6 +906,10 @@ static int sink_set_volume_cb(pa_sink *s) {
             if ((err = snd_mixer_selem_get_playback_dB(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0)
                 goto fail;
 
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+            VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol));
+#endif
+
             pa_cvolume_set(&r, s->volume.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0));
 
         } else {
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index 29145f6..3a7b308 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -816,6 +816,10 @@ static int source_set_volume_cb(pa_source *s) {
                 if ((err = snd_mixer_selem_get_capture_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0)
                     goto fail;
 
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+                VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol));
+#endif
+
                 r.values[i] = pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0);
 
             } else {
@@ -848,6 +852,10 @@ static int source_set_volume_cb(pa_source *s) {
             if ((err = snd_mixer_selem_get_capture_dB(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0)
                 goto fail;
 
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+            VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol));
+#endif
+
             pa_cvolume_set(&r, s->volume.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0));
 
         } else {

commit 36362f624f985c14f757884c6d4fd6a079a4701b
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 22 00:17:11 2009 +0100

    add new function pa_card_suspend()

diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c
index ac9e6d9..8e1ba53 100644
--- a/src/pulsecore/card.c
+++ b/src/pulsecore/card.c
@@ -235,3 +235,20 @@ int pa_card_set_profile(pa_card *c, const char *name) {
 
     return 0;
 }
+
+int pa_card_suspend(pa_card *c, pa_bool_t suspend) {
+    pa_sink *sink;
+    pa_source *source;
+    uint32_t idx;
+    int ret = 0;
+
+    pa_assert(c);
+
+    for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx))
+        ret -= pa_sink_suspend(sink, suspend) < 0;
+
+    for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx))
+        ret -= pa_source_suspend(source, suspend) < 0;
+
+    return ret;
+}
diff --git a/src/pulsecore/card.h b/src/pulsecore/card.h
index 9aa8f37..b179831 100644
--- a/src/pulsecore/card.h
+++ b/src/pulsecore/card.h
@@ -95,4 +95,6 @@ void pa_card_free(pa_card *c);
 
 int pa_card_set_profile(pa_card *c, const char *name);
 
+int pa_card_suspend(pa_card *c, pa_bool_t suspend);
+
 #endif

commit 251f720b05a92294f96347b5da1a3f2ca9b55d29
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 22 00:17:31 2009 +0100

    add new function pa_strna

diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h
index 167d073..44b3af3 100644
--- a/src/pulsecore/core-util.h
+++ b/src/pulsecore/core-util.h
@@ -97,6 +97,10 @@ static inline const char *pa_strempty(const char *x) {
     return x ? x : "";
 }
 
+static inline const char *pa_strna(const char *x) {
+    return x ? x : "n/a";
+}
+
 char *pa_split(const char *c, const char*delimiters, const char **state);
 char *pa_split_spaces(const char *c, const char **state);
 
@@ -198,7 +202,6 @@ pa_bool_t pa_in_system_mode(void);
 char *pa_machine_id(void);
 char *pa_uname_string(void);
 
-
 #ifdef HAVE_VALGRIND_MEMCHECK_H
 pa_bool_t pa_in_valgrind(void);
 #else

commit a5c9546fc7e20cb1ea26fbe80b0ec26aab11b76e
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 22 00:17:55 2009 +0100

    fix copy'n'paste error

diff --git a/src/pulse/def.h b/src/pulse/def.h
index fb1bd27..be5cc23 100644
--- a/src/pulse/def.h
+++ b/src/pulse/def.h
@@ -609,7 +609,7 @@ typedef enum pa_sink_flags {
 #define PA_SINK_LATENCY PA_SINK_LATENCY
 #define PA_SINK_HARDWARE PA_SINK_HARDWARE
 #define PA_SINK_NETWORK PA_SINK_NETWORK
-#define PA_SINK_HW_VOLUME_CTRL PA_SINK_HW_VOLUME_CTRL
+#define PA_SINK_HW_MUTE_CTRL PA_SINK_HW_MUTE_CTRL
 #define PA_SINK_DECIBEL_VOLUME PA_SINK_DECIBEL_VOLUME
 /** \endcond */
 
@@ -684,7 +684,7 @@ typedef enum pa_source_flags {
 #define PA_SOURCE_LATENCY PA_SOURCE_LATENCY
 #define PA_SOURCE_HARDWARE PA_SOURCE_HARDWARE
 #define PA_SOURCE_NETWORK PA_SOURCE_NETWORK
-#define PA_SOURCE_HW_VOLUME_CTRL PA_SOURCE_HW_VOLUME_CTRL
+#define PA_SOURCE_HW_MUTE_CTRL PA_SOURCE_HW_MUTE_CTRL
 #define PA_SOURCE_DECIBEL_VOLUME PA_SOURCE_DECIBEL_VOLUME
 /** \endcond */
 

commit 40f2e21aa731c56353dc75a4ad4effcb88b41916
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 22 00:19:04 2009 +0100

    make gcc shut up a bit more

diff --git a/src/modules/oss/oss-util.c b/src/modules/oss/oss-util.c
index f8e11fd..ea8d811 100644
--- a/src/modules/oss/oss-util.c
+++ b/src/modules/oss/oss-util.c
@@ -45,6 +45,7 @@
 int pa_oss_open(const char *device, int *mode, int* pcaps) {
     int fd = -1;
     int caps;
+    char *t;
 
     pa_assert(device);
     pa_assert(mode);
@@ -92,7 +93,8 @@ int pa_oss_open(const char *device, int *mode, int* pcaps) {
 
 success:
 
-    pa_log_debug("capabilities:%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+    t = pa_sprintf_malloc(
+            "%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
                  *pcaps & DSP_CAP_BATCH ? " BATCH" : "",
 #ifdef DSP_CAP_BIND
                  *pcaps & DSP_CAP_BIND ? " BIND" : "",
@@ -140,6 +142,9 @@ success:
 #endif
                  *pcaps & DSP_CAP_TRIGGER ? " TRIGGER" : "");
 
+    pa_log_debug("capabilities:%s", t);
+    pa_xfree(t);
+
     pa_make_fd_cloexec(fd);
 
     return fd;

commit 8519f54a0eaefb0e84586b354c260a999a595e22
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 22 00:20:15 2009 +0100

    only reread volume if we actually have a good mixer. Closes #466

diff --git a/src/modules/oss/module-oss.c b/src/modules/oss/module-oss.c
index 23a3254..79cbf67 100644
--- a/src/modules/oss/module-oss.c
+++ b/src/modules/oss/module-oss.c
@@ -601,10 +601,10 @@ static int unsuspend(struct userdata *u) {
 
     build_pollfd(u);
 
-    if (u->sink)
-        sink_get_volume(u->sink);
-    if (u->source)
-        source_get_volume(u->source);
+    if (u->sink && u->sink->get_volume)
+        u->sink->get_volume(u->sink);
+    if (u->source && u->source->get_volume)
+        u->source->get_volume(u->source);
 
     pa_log_info("Resumed successfully...");
 
@@ -1201,12 +1201,12 @@ int pa__init(pa_module*m) {
 
     if (use_mmap && (!(caps & DSP_CAP_MMAP) || !(caps & DSP_CAP_TRIGGER))) {
         pa_log_info("OSS device not mmap capable, falling back to UNIX read/write mode.");
-        use_mmap = 0;
+        use_mmap = FALSE;
     }
 
     if (use_mmap && mode == O_WRONLY) {
         pa_log_info("Device opened for playback only, cannot do memory mapping, falling back to UNIX write() mode.");
-        use_mmap = 0;
+        use_mmap = FALSE;
     }
 
     if (pa_oss_get_hw_description(dev, hwdesc, sizeof(hwdesc)) >= 0)
@@ -1235,6 +1235,7 @@ int pa__init(pa_module*m) {
     m->userdata = u;
     u->fd = fd;
     u->mixer_fd = -1;
+    u->mixer_devmask = 0;
     u->use_getospace = u->use_getispace = TRUE;
     u->use_getodelay = TRUE;
     u->mode = mode;
@@ -1383,7 +1384,6 @@ int pa__init(pa_module*m) {
 
     if ((u->mixer_fd = pa_oss_open_mixer_for_device(u->device_name)) >= 0) {
         pa_bool_t do_close = TRUE;
-        u->mixer_devmask = 0;
 
         if (ioctl(fd, SOUND_MIXER_READ_DEVMASK, &u->mixer_devmask) < 0)
             pa_log_warn("SOUND_MIXER_READ_DEVMASK failed: %s", pa_cstrerror(errno));
@@ -1409,6 +1409,7 @@ int pa__init(pa_module*m) {
         if (do_close) {
             pa_close(u->mixer_fd);
             u->mixer_fd = -1;
+            u->mixer_devmask = 0;
         }
     }
 

commit 1c84251ec5c0f7b60d12e2cca73871d779e4e0d3
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 22 00:22:29 2009 +0100

    fix segfault when in record-only mode

diff --git a/src/modules/oss/module-oss.c b/src/modules/oss/module-oss.c
index 79cbf67..da9245a 100644
--- a/src/modules/oss/module-oss.c
+++ b/src/modules/oss/module-oss.c
@@ -877,7 +877,7 @@ static void thread_func(void *userdata) {
 
 /*        pa_log("loop");    */
 
-        if (PA_SINK_IS_OPENED(u->sink->thread_info.state))
+        if (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state))
             if (u->sink->thread_info.rewind_requested)
                 pa_sink_process_rewind(u->sink, 0);
 

commit 7c11554daaf273a04fded3e06f75c7668182a135
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 22 00:22:46 2009 +0100

    make gcc shut up

diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index af9c05b..7ddc010 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -3985,7 +3985,7 @@ static void command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag,
     CHECK_VALIDITY(c->pstream, m, tag, PA_ERR_NOEXTENSION);
     CHECK_VALIDITY(c->pstream, m->load_once || idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID);
 
-    cb = (pa_native_protocol_ext_cb_t) pa_hashmap_get(c->protocol->extensions, m);
+    cb = (pa_native_protocol_ext_cb_t) (unsigned long) pa_hashmap_get(c->protocol->extensions, m);
     CHECK_VALIDITY(c->pstream, m, tag, PA_ERR_NOEXTENSION);
 
     if (cb(c->protocol, m, c, tag, t) < 0)
@@ -4380,7 +4380,7 @@ int pa_native_protocol_install_ext(pa_native_protocol *p, pa_module *m, pa_nativ
     pa_assert(cb);
     pa_assert(!pa_hashmap_get(p->extensions, m));
 
-    pa_assert_se(pa_hashmap_put(p->extensions, m, (void*) cb) == 0);
+    pa_assert_se(pa_hashmap_put(p->extensions, m, (void*) (unsigned long) cb) == 0);
     return 0;
 }
 

commit b606c0940a1309a7034d5c479e8601965075985d
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 22 00:24:13 2009 +0100

    rework logic how alsa sinks/sources/cards are named

diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index 61fb408..749396f 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -1209,6 +1209,32 @@ finish:
     pa_log_debug("Thread shutting down");
 }
 
+static void set_sink_name(pa_sink_new_data *data, pa_modargs *ma, const char *device_id, const char *device_name) {
+    const char *n;
+    char *t;
+
+    pa_assert(data);
+    pa_assert(ma);
+    pa_assert(device_name);
+
+    if ((n = pa_modargs_get_value(ma, "sink_name", NULL))) {
+        pa_sink_new_data_set_name(data, n);
+        data->namereg_fail = TRUE;
+        return;
+    }
+
+    if ((n = pa_modargs_get_value(ma, "name", NULL)))
+        data->namereg_fail = TRUE;
+    else {
+        n = device_id ? device_id : device_name;
+        data->namereg_fail = FALSE;
+    }
+
+    t = pa_sprintf_malloc("alsa_output.%s", n);
+    pa_sink_new_data_set_name(data, t);
+    pa_xfree(t);
+}
+
 pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, const pa_alsa_profile_info *profile) {
 
     struct userdata *u = NULL;
@@ -1220,9 +1246,6 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
     size_t frame_size;
     snd_pcm_info_t *pcm_info = NULL;
     int err;
-    const char *name;
-    char *name_buf = NULL;
-    pa_bool_t namereg_fail;
     pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d;
     pa_usec_t usec;
     pa_sink_new_data data;
@@ -1404,22 +1427,11 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
         }
     }
 
-    if ((name = pa_modargs_get_value(ma, "sink_name", NULL)))
-        namereg_fail = TRUE;
-    else if ((name = pa_modargs_get_value(ma, "name", NULL))) {
-        name = name_buf = pa_sprintf_malloc("alsa_output.%s", name);
-        namereg_fail = TRUE;
-    } else {
-        name = name_buf = pa_sprintf_malloc("alsa_output.%s", u->device_name);
-        namereg_fail = FALSE;
-    }
-
     pa_sink_new_data_init(&data);
     data.driver = driver;
     data.module = m;
     data.card = card;
-    pa_sink_new_data_set_name(&data, name);
-    data.namereg_fail = namereg_fail;
+    set_sink_name(&data, ma, dev_id, u->device_name);
     pa_sink_new_data_set_sample_spec(&data, &ss);
     pa_sink_new_data_set_channel_map(&data, &map);
 
@@ -1436,7 +1448,6 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
 
     u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
     pa_sink_new_data_done(&data);
-    pa_xfree(name_buf);
 
     if (!u->sink) {
         pa_log("Failed to create sink object");
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index 3a7b308..fe4eddb 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -1044,6 +1044,32 @@ finish:
     pa_log_debug("Thread shutting down");
 }
 
+static void set_source_name(pa_source_new_data *data, pa_modargs *ma, const char *device_id, const char *device_name) {
+    const char *n;
+    char *t;
+
+    pa_assert(data);
+    pa_assert(ma);
+    pa_assert(device_name);
+
+    if ((n = pa_modargs_get_value(ma, "source_name", NULL))) {
+        pa_source_new_data_set_name(data, n);
+        data->namereg_fail = TRUE;
+        return;
+    }
+
+    if ((n = pa_modargs_get_value(ma, "name", NULL)))
+        data->namereg_fail = TRUE;
+    else {
+        n = device_id ? device_id : device_name;
+        data->namereg_fail = FALSE;
+    }
+
+    t = pa_sprintf_malloc("alsa_input.%s", n);
+    pa_source_new_data_set_name(data, t);
+    pa_xfree(t);
+}
+
 pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, const pa_alsa_profile_info *profile) {
 
     struct userdata *u = NULL;
@@ -1055,9 +1081,6 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
     size_t frame_size;
     snd_pcm_info_t *pcm_info = NULL;
     int err;
-    const char *name;
-    char *name_buf = NULL;
-    pa_bool_t namereg_fail;
     pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d;
     pa_source_new_data data;
 
@@ -1229,22 +1252,11 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
         }
     }
 
-    if ((name = pa_modargs_get_value(ma, "source_name", NULL)))
-        namereg_fail = TRUE;
-    else if ((name = pa_modargs_get_value(ma, "name", NULL))) {
-        name = name_buf = pa_sprintf_malloc("alsa_input.%s", name);
-        namereg_fail = TRUE;
-    } else {
-        name = name_buf = pa_sprintf_malloc("alsa_input.%s", u->device_name);
-        namereg_fail = FALSE;
-    }
-
     pa_source_new_data_init(&data);
     data.driver = driver;
     data.module = m;
     data.card = card;
-    pa_source_new_data_set_name(&data, name);
-    data.namereg_fail = namereg_fail;
+    set_source_name(&data, ma, dev_id, u->device_name);
     pa_source_new_data_set_sample_spec(&data, &ss);
     pa_source_new_data_set_channel_map(&data, &map);
 
@@ -1261,7 +1273,6 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
 
     u->source = pa_source_new(m->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
     pa_source_new_data_done(&data);
-    pa_xfree(name_buf);
 
     if (!u->source) {
         pa_log("Failed to create source object");
diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
index f250602..9a00edd 100644
--- a/src/modules/alsa/module-alsa-card.c
+++ b/src/modules/alsa/module-alsa-card.c
@@ -37,7 +37,10 @@ PA_MODULE_DESCRIPTION("ALSA Card");
 PA_MODULE_VERSION(PACKAGE_VERSION);
 PA_MODULE_LOAD_ONCE(FALSE);
 PA_MODULE_USAGE(
-        "name=<name for the sink/source> "
+        "name=<name for the card/sink/source, to be prefixed> "
+        "card_name=<name for card> "
+        "sink_name=<name for sink> "
+        "source_name=<name for source> "
         "device_id=<ALSA card index> "
         "format=<sample format> "
         "rate=<sample rate> "
@@ -51,6 +54,9 @@ PA_MODULE_USAGE(
 
 static const char* const valid_modargs[] = {
     "name",
+    "card_name",
+    "sink_name",
+    "source_name",
     "device_id",
     "format",
     "rate",
@@ -189,6 +195,32 @@ static void init_profile(struct userdata *u) {
         u->source = pa_alsa_source_new(u->module, u->modargs, __FILE__, u->card, d->source_profile);
 }
 
+static void set_card_name(pa_card_new_data *data, pa_modargs *ma, const char *device_id) {
+    char *t;
+    const char *n;
+
+    pa_assert(data);
+    pa_assert(ma);
+    pa_assert(device_id);
+
+    if ((n = pa_modargs_get_value(ma, "card_name", NULL))) {
+        pa_card_new_data_set_name(data, n);
+        data->namereg_fail = TRUE;
+        return;
+    }
+
+    if ((n = pa_modargs_get_value(ma, "name", NULL)))
+        data->namereg_fail = TRUE;
+    else {
+        n = device_id;
+        data->namereg_fail = FALSE;
+    }
+
+    t = pa_sprintf_malloc("alsa_card.%s", n);
+    pa_card_new_data_set_name(data, t);
+    pa_xfree(t);
+}
+
 int pa__init(pa_module*m) {
     pa_card_new_data data;
     pa_modargs *ma;
@@ -224,7 +256,7 @@ int pa__init(pa_module*m) {
     data.module = m;
     pa_alsa_init_proplist_card(data.proplist, alsa_card_index);
     pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_id);
-    pa_card_new_data_set_name(&data, pa_modargs_get_value(ma, "name", u->device_id));
+    set_card_name(&data, ma, u->device_id);
 
     data.profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
     if (pa_alsa_probe_profiles(u->device_id, &m->core->default_sample_spec, enumerate_cb, data.profiles) < 0) {
diff --git a/src/modules/alsa/module-alsa-sink.c b/src/modules/alsa/module-alsa-sink.c
index 6a66ad3..6cf48b5 100644
--- a/src/modules/alsa/module-alsa-sink.c
+++ b/src/modules/alsa/module-alsa-sink.c
@@ -38,6 +38,7 @@ PA_MODULE_DESCRIPTION("ALSA Sink");
 PA_MODULE_VERSION(PACKAGE_VERSION);
 PA_MODULE_LOAD_ONCE(FALSE);
 PA_MODULE_USAGE(
+        "name=<name of the sink, to be prefixed> "
         "sink_name=<name for the sink> "
         "device=<ALSA device> "
         "device_id=<ALSA card index> "
@@ -53,6 +54,7 @@ PA_MODULE_USAGE(
         "tsched_buffer_watermark=<lower fill watermark>");
 
 static const char* const valid_modargs[] = {
+    "name",
     "sink_name",
     "device",
     "device_id",
diff --git a/src/modules/alsa/module-alsa-source.c b/src/modules/alsa/module-alsa-source.c
index 7dc22fe..1e7c387 100644
--- a/src/modules/alsa/module-alsa-source.c
+++ b/src/modules/alsa/module-alsa-source.c
@@ -62,6 +62,7 @@ PA_MODULE_DESCRIPTION("ALSA Source");
 PA_MODULE_VERSION(PACKAGE_VERSION);
 PA_MODULE_LOAD_ONCE(FALSE);
 PA_MODULE_USAGE(
+        "name=<name for the source, to be prefixed> "
         "source_name=<name for the source> "
         "device=<ALSA device> "
         "device_id=<ALSA card index> "
@@ -77,6 +78,7 @@ PA_MODULE_USAGE(
         "tsched_buffer_watermark=<upper fill watermark>");
 
 static const char* const valid_modargs[] = {
+    "name",
     "source_name",
     "device",
     "device_id",

commit 4b2a6827419d8c45878c53eb6fa8f1424836d774
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 22 00:24:28 2009 +0100

    fix minor memleak in prober

diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index e154f6e..599079c 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -836,6 +836,8 @@ snd_pcm_t *pa_alsa_open_by_device_string(
 
         if (dev)
             *dev = d;
+        else
+            pa_xfree(d);
 
         if (ss->channels != map->channels)
             pa_channel_map_init_extend(map, ss->channels, PA_CHANNEL_MAP_ALSA);

commit b2ef19acc550dbfbd9e9a215fbda4cd9f8b0c977
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 22 00:25:36 2009 +0100

    include PA_SINK_INVALID_STATE in all switch/case statements to make gcc shut up

diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index 749396f..f092b5d 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -715,6 +715,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 
                 case PA_SINK_UNLINKED:
                 case PA_SINK_INIT:
+                case PA_SINK_INVALID_STATE:
                     ;
             }
 
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index fe4eddb..0c4e5bc 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -661,6 +661,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
 
                 case PA_SOURCE_UNLINKED:
                 case PA_SOURCE_INIT:
+                case PA_SOURCE_INVALID_STATE:
                     ;
             }
 
diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c
index b1dc820..82c8871 100644
--- a/src/modules/module-combine.c
+++ b/src/modules/module-combine.c
@@ -627,6 +627,7 @@ static int sink_set_state(pa_sink *sink, pa_sink_state_t state) {
 
         case PA_SINK_UNLINKED:
         case PA_SINK_INIT:
+        case PA_SINK_INVALID_STATE:
             ;
     }
 
diff --git a/src/modules/module-esound-sink.c b/src/modules/module-esound-sink.c
index 7c7f828..f6b90a4 100644
--- a/src/modules/module-esound-sink.c
+++ b/src/modules/module-esound-sink.c
@@ -156,6 +156,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 
                 case PA_SINK_UNLINKED:
                 case PA_SINK_INIT:
+                case PA_SINK_INVALID_STATE:
                     ;
             }
 
diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c
index c324437..bb93ca8 100644
--- a/src/modules/module-raop-sink.c
+++ b/src/modules/module-raop-sink.c
@@ -195,6 +195,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 
                 case PA_SINK_UNLINKED:
                 case PA_SINK_INIT:
+                case PA_SINK_INVALID_STATE:
                     ;
             }
 
diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c
index a46d6e5..61b9516 100644
--- a/src/modules/module-tunnel.c
+++ b/src/modules/module-tunnel.c
@@ -494,6 +494,7 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
 
         case PA_SINK_UNLINKED:
         case PA_SINK_INIT:
+        case PA_SINK_INVALID_STATE:
             ;
     }
 
@@ -581,6 +582,7 @@ static int source_set_state(pa_source *s, pa_source_state_t state) {
 
         case PA_SOURCE_UNLINKED:
         case PA_SOURCE_INIT:
+        case PA_SINK_INVALID_STATE:
             ;
     }
 
diff --git a/src/modules/oss/module-oss.c b/src/modules/oss/module-oss.c
index da9245a..b5b9e2a 100644
--- a/src/modules/oss/module-oss.c
+++ b/src/modules/oss/module-oss.c
@@ -681,6 +681,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 
                     break;
 
+                case PA_SINK_INVALID_STATE:
                 case PA_SINK_UNLINKED:
                 case PA_SINK_INIT:
                     ;
@@ -762,6 +763,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
 
                 case PA_SOURCE_UNLINKED:
                 case PA_SOURCE_INIT:
+                case PA_SOURCE_INVALID_STATE:
                     ;
 
             }

commit 3be4c31ee0f8bac99cec69ae97668333882fc1f8
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 22 00:26:16 2009 +0100

    rework module-hal-detect and make it use module-alsa-card instead of module-alsa-sink/-source

diff --git a/configure.ac b/configure.ac
index 411a14d..1d891b7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -876,7 +876,7 @@ AC_ARG_ENABLE([hal],
         ],
         [hal=auto])
 if test "x${hal}" != xno -a \( "x$HAVE_OSS" = "x1" -o "x$HAVE_ALSA" = "x1" \) ; then
-    PKG_CHECK_MODULES(HAL, [ hal >= 0.5.7 ],
+    PKG_CHECK_MODULES(HAL, [ hal >= 0.5.11 ],
         HAVE_HAL=1,
         [
             HAVE_HAL=0
diff --git a/src/modules/module-hal-detect.c b/src/modules/module-hal-detect.c
index e287a5d..309b006 100644
--- a/src/modules/module-hal-detect.c
+++ b/src/modules/module-hal-detect.c
@@ -66,9 +66,9 @@ PA_MODULE_USAGE("api=<oss>");
 #endif
 
 struct device {
-    uint32_t index;
-    char *udi;
-    char *sink_name, *source_name;
+    char *udi, *originating_udi;
+    char *card_name, *sink_name, *source_name;
+    uint32_t module;
     pa_bool_t acl_race_fix;
 };
 
@@ -76,18 +76,13 @@ struct userdata {
     pa_core *core;
     LibHalContext *context;
     pa_dbus_connection *connection;
-    pa_hashmap *devices;
+    pa_hashmap *devices; /* Every entry is indexed twice in this table: by the udi we found the device with and by the originating device's udi */
     const char *capability;
 #ifdef HAVE_ALSA
     pa_bool_t use_tsched;
 #endif
 };
 
-struct timerdata {
-    struct userdata *u;
-    char *udi;
-};
-
 #define CAPABILITY_ALSA "alsa"
 #define CAPABILITY_OSS "oss"
 
@@ -99,22 +94,22 @@ static const char* const valid_modargs[] = {
     NULL
 };
 
-static void hal_device_free(struct device* d) {
+static void device_free(struct device* d) {
     pa_assert(d);
 
     pa_xfree(d->udi);
+    pa_xfree(d->originating_udi);
     pa_xfree(d->sink_name);
     pa_xfree(d->source_name);
+    pa_xfree(d->card_name);
     pa_xfree(d);
 }
 
-static void hal_device_free_cb(void *d, void *data) {
-    hal_device_free(d);
-}
-
 static const char *strip_udi(const char *udi) {
     const char *slash;
 
+    pa_assert(udi);
+
     if ((slash = strrchr(udi, '/')))
         return slash+1;
 
@@ -123,405 +118,380 @@ static const char *strip_udi(const char *udi) {
 
 #ifdef HAVE_ALSA
 
-typedef enum {
-    ALSA_TYPE_SINK,
-    ALSA_TYPE_SOURCE,
-    ALSA_TYPE_OTHER,
-    ALSA_TYPE_MAX
-} alsa_type_t;
+enum alsa_type {
+    ALSA_TYPE_PLAYBACK,
+    ALSA_TYPE_CAPTURE,
+    ALSA_TYPE_OTHER
+};
 
-static alsa_type_t hal_alsa_device_get_type(LibHalContext *context, const char *udi, DBusError *error) {
+static enum alsa_type hal_alsa_device_get_type(LibHalContext *context, const char *udi) {
     char *type;
-    alsa_type_t t;
+    enum alsa_type t = ALSA_TYPE_OTHER;
+    DBusError error;
+
+    dbus_error_init(&error);
 
-    if (!(type = libhal_device_get_property_string(context, udi, "alsa.type", error)))
-        return ALSA_TYPE_OTHER;
+    pa_assert(context);
+    pa_assert(udi);
 
-    if (!strcmp(type, "playback"))
-        t = ALSA_TYPE_SINK;
-    else if (!strcmp(type, "capture"))
-        t = ALSA_TYPE_SOURCE;
-    else
-        t = ALSA_TYPE_OTHER;
+    if (!(type = libhal_device_get_property_string(context, udi, "alsa.type", &error)))
+        goto finish;
+
+    if (pa_streq(type, "playback"))
+        t = ALSA_TYPE_PLAYBACK;
+    else if (pa_streq(type, "capture"))
+        t = ALSA_TYPE_CAPTURE;
 
     libhal_free_string(type);
 
+finish:
+    if (dbus_error_is_set(&error)) {
+        pa_log_error("D-Bus error while parsing HAL ALSA data: %s: %s", error.name, error.message);
+        dbus_error_free(&error);
+    }
+
     return t;
 }
 
-static int hal_alsa_device_is_modem(LibHalContext *context, const char *udi, DBusError *error) {
+static pa_bool_t hal_alsa_device_is_modem(LibHalContext *context, const char *udi) {
     char *class;
-    int r;
+    pa_bool_t r = FALSE;
+    DBusError error;
 
-    if (!(class = libhal_device_get_property_string(context, udi, "alsa.pcm_class", error)))
-        return 0;
+    dbus_error_init(&error);
+
+    pa_assert(context);
+    pa_assert(udi);
+
+    if (!(class = libhal_device_get_property_string(context, udi, "alsa.pcm_class", &error)))
+        goto finish;
+
+    r = pa_streq(class, "modem");
+    libhal_free_string(class);
 
-    r = strcmp(class, "modem") == 0;
-    pa_xfree(class);
+finish:
+    if (dbus_error_is_set(&error)) {
+        pa_log_error("D-Bus error while parsing HAL ALSA data: %s: %s", error.name, error.message);
+        dbus_error_free(&error);
+    }
 
     return r;
 }
 
-static pa_module* hal_device_load_alsa(struct userdata *u, const char *udi, char **sink_name, char **source_name) {
-    char *args;
-    alsa_type_t type;
+static int hal_device_load_alsa(struct userdata *u, const char *udi, struct device *d) {
+    enum alsa_type type;
     int device, card;
-    const char *module_name;
     DBusError error;
     pa_module *m;
+    char *args, *originating_udi = NULL, *card_name = NULL;
 
     dbus_error_init(&error);
 
     pa_assert(u);
-    pa_assert(sink_name);
-    pa_assert(source_name);
+    pa_assert(udi);
+    pa_assert(d);
 
-    *sink_name = *source_name = NULL;
+    /* We only care for PCM devices */
+    type = hal_alsa_device_get_type(u->context, udi);
+    if (type == ALSA_TYPE_OTHER)
+        goto fail;
 
-    type = hal_alsa_device_get_type(u->context, udi, &error);
-    if (dbus_error_is_set(&error) || type == ALSA_TYPE_OTHER)
+    /* We don't care for modems */
+    if (hal_alsa_device_is_modem(u->context, udi))
         goto fail;
 
+    /* We only care for the main device */
     device = libhal_device_get_property_int(u->context, udi, "alsa.device", &error);
     if (dbus_error_is_set(&error) || device != 0)
         goto fail;
 
-    card = libhal_device_get_property_int(u->context, udi, "alsa.card", &error);
-    if (dbus_error_is_set(&error))
+    /* We store only one entry per card, hence we look for the originating device */
+    originating_udi = libhal_device_get_property_string(u->context, udi, "alsa.originating_device", &error);
+    if (dbus_error_is_set(&error) || !originating_udi)
         goto fail;
 
-    if (hal_alsa_device_is_modem(u->context, udi, &error))
+    /* Make sure we only load one module per card */
+    if (pa_hashmap_get(u->devices, originating_udi))
         goto fail;
 
-    if (type == ALSA_TYPE_SINK) {
-        *sink_name = pa_sprintf_malloc("alsa_output.%s", strip_udi(udi));
-
-        module_name = "module-alsa-sink";
-        args = pa_sprintf_malloc("device_id=%u sink_name=%s tsched=%i", card, *sink_name, (int) u->use_tsched);
-    } else {
-        *source_name = pa_sprintf_malloc("alsa_input.%s", strip_udi(udi));
-
-        module_name = "module-alsa-source";
-        args = pa_sprintf_malloc("device_id=%u source_name=%s tsched=%i", card, *source_name, (int) u->use_tsched);
-    }
-
-    pa_log_debug("Loading %s with arguments '%s'", module_name, args);
+    /* We need the identifier */
+    card = libhal_device_get_property_int(u->context, udi, "alsa.card", &error);
+    if (dbus_error_is_set(&error))
+        goto fail;
 
-    m = pa_module_load(u->core, module_name, args);
+    card_name = pa_sprintf_malloc("alsa_card.%s", strip_udi(originating_udi));
+    args = pa_sprintf_malloc("device_id=%u name=%s card_name=%s tsched=%i", card, strip_udi(originating_udi), card_name, (int) u->use_tsched);
 
+    pa_log_debug("Loading module-alsa-card with arguments '%s'", args);
+    m = pa_module_load(u->core, "module-alsa-card", args);
     pa_xfree(args);
 
-    if (!m) {
-        pa_xfree(*sink_name);
-        pa_xfree(*source_name);
-        *sink_name = *source_name = NULL;
-    }
+    if (!m)
+        goto fail;
+
+    d->originating_udi = originating_udi;
+    d->module = m->index;
+    d->card_name = card_name;
 
-    return m;
+    return 0;
 
 fail:
     if (dbus_error_is_set(&error)) {
-        pa_log_error("D-Bus error while parsing ALSA data: %s: %s", error.name, error.message);
+        pa_log_error("D-Bus error while parsing HAL ALSA data: %s: %s", error.name, error.message);
         dbus_error_free(&error);
     }
 
-    return NULL;
+    pa_xfree(originating_udi);
+    pa_xfree(card_name);
+
+    return -1;
 }
 
 #endif
 
 #ifdef HAVE_OSS
 
-static int hal_oss_device_is_pcm(LibHalContext *context, const char *udi, DBusError *error) {
+static pa_bool_t hal_oss_device_is_pcm(LibHalContext *context, const char *udi) {
     char *class = NULL, *dev = NULL, *e;
     int device;
-    int r = 0;
+    pa_bool_t r = FALSE;
+    DBusError error;
+
+    dbus_error_init(&error);
+
+    pa_assert(context);
+    pa_assert(udi);
 
-    class = libhal_device_get_property_string(context, udi, "oss.type", error);
-    if (dbus_error_is_set(error) || !class)
+    /* We only care for PCM devices */
+    class = libhal_device_get_property_string(context, udi, "oss.type", &error);
+    if (dbus_error_is_set(&error) || !class)
         goto finish;
 
-    if (strcmp(class, "pcm"))
+    if (!pa_streq(class, "pcm"))
         goto finish;
 
-    dev = libhal_device_get_property_string(context, udi, "oss.device_file", error);
-    if (dbus_error_is_set(error) || !dev)
+    /* We don't like /dev/audio */
+    dev = libhal_device_get_property_string(context, udi, "oss.device_file", &error);
+    if (dbus_error_is_set(&error) || !dev)
         goto finish;
 
     if ((e = strrchr(dev, '/')))
         if (pa_startswith(e + 1, "audio"))
             goto finish;
 
-    device = libhal_device_get_property_int(context, udi, "oss.device", error);
-    if (dbus_error_is_set(error) || device != 0)
+    /* We only care for the main device */
+    device = libhal_device_get_property_int(context, udi, "oss.device", &error);
+    if (dbus_error_is_set(&error) || device != 0)
         goto finish;
 
-    r = 1;
+    r = TRUE;
 
 finish:
 
+    if (dbus_error_is_set(&error)) {
+        pa_log_error("D-Bus error while parsing HAL OSS data: %s: %s", error.name, error.message);
+        dbus_error_free(&error);
+    }
+
     libhal_free_string(class);
     libhal_free_string(dev);
 
     return r;
 }
 
-static pa_module* hal_device_load_oss(struct userdata *u, const char *udi, char **sink_name, char **source_name) {
-    char* args;
-    char* device;
+static int hal_device_load_oss(struct userdata *u, const char *udi, struct device *d) {
     DBusError error;
     pa_module *m;
+    char *args, *originating_udi = NULL, *device, *sink_name = NULL, *source_name = NULL;
 
     dbus_error_init(&error);
 
     pa_assert(u);
-    pa_assert(sink_name);
-    pa_assert(source_name);
+    pa_assert(udi);
+    pa_assert(d);
+
+    /* We only care for OSS PCM devices */
+    if (!hal_oss_device_is_pcm(u->context, udi))
+        goto fail;
 
-    *sink_name = *source_name = NULL;
+    /* We store only one entry per card, hence we look for the originating device */
+    originating_udi = libhal_device_get_property_string(u->context, udi, "oss.originating_device", &error);
+    if (dbus_error_is_set(&error) || !originating_udi)
+        goto fail;
 
-    if (!hal_oss_device_is_pcm(u->context, udi, &error) || dbus_error_is_set(&error))
+    /* Make sure we only load one module per card */
+    if (pa_hashmap_get(u->devices, originating_udi))
         goto fail;
 
+    /* We need the device file */
     device = libhal_device_get_property_string(u->context, udi, "oss.device_file", &error);
     if (!device || dbus_error_is_set(&error))
         goto fail;
 
-    *sink_name = pa_sprintf_malloc("oss_output.%s", strip_udi(udi));
-    *source_name = pa_sprintf_malloc("oss_input.%s", strip_udi(udi));
+    sink_name = pa_sprintf_malloc("oss_output.%s", strip_udi(udi));
+    source_name = pa_sprintf_malloc("oss_input.%s", strip_udi(udi));
+    args = pa_sprintf_malloc("device=%s sink_name=%s source_name=%s", device, sink_name, source_name);
 
-    args = pa_sprintf_malloc("device=%s sink_name=%s source_name=%s", device, *sink_name, *source_name);
     libhal_free_string(device);
 
     pa_log_debug("Loading module-oss with arguments '%s'", args);
     m = pa_module_load(u->core, "module-oss", args);
     pa_xfree(args);
 
-    if (!m) {
-        pa_xfree(*sink_name);
-        pa_xfree(*source_name);
-        *sink_name = *source_name = NULL;
-    }
+    if (!m)
+        goto fail;
+
+    d->originating_udi = originating_udi;
+    d->module = m->index;
+    d->sink_name = sink_name;
+    d->source_name = source_name;
 
-    return m;
+    return 0;
 
 fail:
     if (dbus_error_is_set(&error)) {
-        pa_log_error("D-Bus error while parsing OSS data: %s: %s", error.name, error.message);
+        pa_log_error("D-Bus error while parsing OSS HAL data: %s: %s", error.name, error.message);
         dbus_error_free(&error);
     }
 
-    return NULL;
+    pa_xfree(originating_udi);
+    pa_xfree(source_name);
+    pa_xfree(sink_name);
+
+    return -1;
 }
 #endif
 
 static struct device* hal_device_add(struct userdata *u, const char *udi) {
-    pa_module* m = NULL;
     struct device *d;
-    char *sink_name = NULL, *source_name = NULL;
+    int r;
 
     pa_assert(u);
     pa_assert(u->capability);
-    pa_assert(!pa_hashmap_get(u->devices, udi));
+
+    d = pa_xnew(struct device, 1);
+    d->acl_race_fix = FALSE;
+    d->udi = pa_xstrdup(udi);
+    d->originating_udi = NULL;
+    d->module = PA_INVALID_INDEX;
+    d->sink_name = d->source_name = d->card_name = NULL;
 
 #ifdef HAVE_ALSA
-    if (strcmp(u->capability, CAPABILITY_ALSA) == 0)
-        m = hal_device_load_alsa(u, udi, &sink_name, &source_name);
+    if (pa_streq(u->capability, CAPABILITY_ALSA))
+        r = hal_device_load_alsa(u, udi,  d);
 #endif
 #ifdef HAVE_OSS
-    if (strcmp(u->capability, CAPABILITY_OSS) == 0)
-        m = hal_device_load_oss(u, udi, &sink_name, &source_name);
+    if (pa_streq(u->capability, CAPABILITY_OSS))
+        r = hal_device_load_oss(u, udi, d);
 #endif
 
-    if (!m)
+    if (r < 0) {
+        device_free(d);
         return NULL;
+    }
 
-    d = pa_xnew(struct device, 1);
-    d->acl_race_fix = FALSE;
-    d->udi = pa_xstrdup(udi);
-    d->index = m->index;
-    d->sink_name = sink_name;
-    d->source_name = source_name;
     pa_hashmap_put(u->devices, d->udi, d);
+    pa_hashmap_put(u->devices, d->originating_udi, d);
 
     return d;
 }
 
-static int hal_device_add_all(struct userdata *u, const char *capability) {
-    DBusError error;
-    int i, n, count = 0;
+static int hal_device_add_all(struct userdata *u) {
+    int n, count = 0;
     char** udis;
-
-    pa_assert(u);
+    DBusError error;
 
     dbus_error_init(&error);
 
-    if (u->capability && strcmp(u->capability, capability) != 0)
-        return 0;
-
-    pa_log_info("Trying capability %s", capability);
+    pa_assert(u);
 
-    udis = libhal_find_device_by_capability(u->context, capability, &n, &error);
-    if (dbus_error_is_set(&error)) {
-        pa_log_error("Error finding devices: %s: %s", error.name, error.message);
-        dbus_error_free(&error);
-        return -1;
-    }
+    udis = libhal_find_device_by_capability(u->context, u->capability, &n, &error);
+    if (dbus_error_is_set(&error) || !udis)
+        goto fail;
 
     if (n > 0) {
-        u->capability = capability;
+        int i;
 
         for (i = 0; i < n; i++) {
             struct device *d;
 
-            if (!(d = hal_device_add(u, udis[i])))
-                pa_log_debug("Not loaded device %s", udis[i]);
-            else {
-                if (d->sink_name)
-                    pa_scache_play_item_by_name(u->core, "pulse-coldplug", d->sink_name, PA_VOLUME_NORM, NULL, NULL);
+            if ((d = hal_device_add(u, udis[i])))
                 count++;
-            }
+            else
+                pa_log_debug("Not loaded device %s", udis[i]);
         }
     }
 
     libhal_free_string_array(udis);
-    return count;
-}
-
-static dbus_bool_t device_has_capability(LibHalContext *context, const char *udi, const char* cap, DBusError *error){
-    dbus_bool_t has_prop;
-
-    has_prop = libhal_device_property_exists(context, udi, "info.capabilities", error);
-    if (!has_prop || dbus_error_is_set(error))
-        return FALSE;
 
-    return libhal_device_query_capability(context, udi, cap, error);
-}
-
-static void device_added_time_cb(pa_mainloop_api *ea, pa_time_event *ev, const struct timeval *tv, void *userdata) {
-    DBusError error;
-    struct timerdata *td = userdata;
-
-    dbus_error_init(&error);
-
-    if (!pa_hashmap_get(td->u->devices, td->udi)) {
-        dbus_bool_t b;
-        struct device *d;
+    return count;
 
-        b = libhal_device_exists(td->u->context, td->udi, &error);
-
-        if (dbus_error_is_set(&error)) {
-            pa_log_error("Error adding device: %s: %s", error.name, error.message);
-            dbus_error_free(&error);
-        } else if (b) {
-            if (!(d = hal_device_add(td->u, td->udi)))
-                pa_log_debug("Not loaded device %s", td->udi);
-            else {
-                if (d->sink_name)
-                    pa_scache_play_item_by_name(td->u->core, "pulse-hotplug", d->sink_name, PA_VOLUME_NORM, NULL, NULL);
-            }
-        }
+fail:
+    if (dbus_error_is_set(&error)) {
+        pa_log_error("D-Bus error while parsing HAL data: %s: %s", error.name, error.message);
+        dbus_error_free(&error);
     }
 
-    pa_xfree(td->udi);
-    pa_xfree(td);
-    ea->time_free(ev);
+    return -1;
 }
 
 static void device_added_cb(LibHalContext *context, const char *udi) {
     DBusError error;
-    struct timeval tv;
-    struct timerdata *t;
     struct userdata *u;
     pa_bool_t good = FALSE;
 
-    pa_assert_se(u = libhal_ctx_get_user_data(context));
-
-    if (pa_hashmap_get(u->devices, udi))
-        return;
-
-    pa_log_debug("HAL Device added: %s", udi);
-
     dbus_error_init(&error);
 
-    if (u->capability) {
-
-        good = device_has_capability(context, udi, u->capability, &error);
-
-        if (dbus_error_is_set(&error)) {
-            pa_log_error("Error getting capability: %s: %s", error.name, error.message);
-            dbus_error_free(&error);
-            return;
-        }
-
-    } else {
-
-#ifdef HAVE_ALSA
-        good = device_has_capability(context, udi, CAPABILITY_ALSA, &error);
-
-        if (dbus_error_is_set(&error)) {
-            pa_log_error("Error getting capability: %s: %s", error.name, error.message);
-            dbus_error_free(&error);
-            return;
-        }
+    pa_assert(context);
+    pa_assert(udi);
 
-        if (good)
-            u->capability = CAPABILITY_ALSA;
-#endif
-#if defined(HAVE_OSS) && defined(HAVE_ALSA)
-        if (!good) {
-#endif
-#ifdef HAS_OSS
-            good = device_has_capability(context, udi, CAPABILITY_OSS, &error);
+    pa_assert_se(u = libhal_ctx_get_user_data(context));
 
-            if (dbus_error_is_set(&error)) {
-                pa_log_error("Error getting capability: %s: %s", error.name, error.message);
-                dbus_error_free(&error);
-                return;
-            }
+    good = libhal_device_query_capability(context, udi, u->capability, &error);
+    if (dbus_error_is_set(&error) || !good)
+        goto finish;
 
-            if (good)
-                u->capability = CAPABILITY_OSS;
+    if (!hal_device_add(u, udi))
+        pa_log_debug("Not loaded device %s", udi);
 
-#endif
-#if defined(HAVE_OSS) && defined(HAVE_ALSA)
-        }
-#endif
+finish:
+    if (dbus_error_is_set(&error)) {
+        pa_log_error("D-Bus error while parsing HAL data: %s: %s", error.name, error.message);
+        dbus_error_free(&error);
     }
-
-    if (!good)
-        return;
-
-    /* actually add the device 1/2 second later */
-    t = pa_xnew(struct timerdata, 1);
-    t->u = u;
-    t->udi = pa_xstrdup(udi);
-
-    pa_gettimeofday(&tv);
-    pa_timeval_add(&tv, 500000);
-    u->core->mainloop->time_new(u->core->mainloop, &tv, device_added_time_cb, t);
 }
 
 static void device_removed_cb(LibHalContext* context, const char *udi) {
     struct device *d;
     struct userdata *u;
 
+    pa_assert(context);
+    pa_assert(udi);
+
     pa_assert_se(u = libhal_ctx_get_user_data(context));
 
-    pa_log_debug("Device removed: %s", udi);
+    if (!(d = pa_hashmap_get(u->devices, udi)))
+        return;
+
+    pa_hashmap_remove(u->devices, d->originating_udi);
+    pa_hashmap_remove(u->devices, d->udi);
 
-    if ((d = pa_hashmap_remove(u->devices, udi))) {
-        pa_module_unload_request_by_index(u->core, d->index, TRUE);
-        hal_device_free(d);
-    }
+    pa_log_debug("Removing HAL device: %s", d->originating_udi);
+
+    pa_module_unload_request_by_index(u->core, d->module, TRUE);
+    device_free(d);
 }
 
 static void new_capability_cb(LibHalContext *context, const char *udi, const char* capability) {
     struct userdata *u;
 
+    pa_assert(context);
+    pa_assert(udi);
+    pa_assert(capability);
+
     pa_assert_se(u = libhal_ctx_get_user_data(context));
 
-    if (!u->capability || strcmp(u->capability, capability) == 0)
+    if (pa_streq(u->capability, capability))
         /* capability we care about, pretend it's a new device */
         device_added_cb(context, udi);
 }
@@ -529,20 +499,24 @@ static void new_capability_cb(LibHalContext *context, const char *udi, const cha
 static void lost_capability_cb(LibHalContext *context, const char *udi, const char* capability) {
     struct userdata *u;
 
+    pa_assert(context);
+    pa_assert(udi);
+    pa_assert(capability);
+
     pa_assert_se(u = libhal_ctx_get_user_data(context));
 
-    if (u->capability && strcmp(u->capability, capability) == 0)
+    if (pa_streq(u->capability, capability))
         /* capability we care about, pretend it was removed */
         device_removed_cb(context, udi);
 }
 
 static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, void *userdata) {
-    struct userdata*u = userdata;
+    struct userdata*u;
     DBusError error;
 
     pa_assert(bus);
     pa_assert(message);
-    pa_assert(u);
+    pa_assert_se(u = userdata);
 
     dbus_error_init(&error);
 
@@ -554,13 +528,14 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
     if (dbus_message_is_signal(message, "org.freedesktop.Hal.Device.AccessControl", "ACLAdded") ||
         dbus_message_is_signal(message, "org.freedesktop.Hal.Device.AccessControl", "ACLRemoved")) {
         uint32_t uid;
-        int suspend = strcmp(dbus_message_get_member(message), "ACLRemoved") == 0;
+        pa_bool_t suspend = strcmp(dbus_message_get_member(message), "ACLRemoved") == 0;
 
         if (!dbus_message_get_args(message, &error, DBUS_TYPE_UINT32, &uid, DBUS_TYPE_INVALID) || dbus_error_is_set(&error)) {
             pa_log_error("Failed to parse ACL message: %s: %s", error.name, error.message);
             goto finish;
         }
 
+        /* Check if this is about us? */
         if (uid == getuid() || uid == geteuid()) {
             struct device *d;
             const char *udi;
@@ -569,27 +544,18 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
 
             if ((d = pa_hashmap_get(u->devices, udi))) {
                 pa_bool_t send_acl_race_fix_message = FALSE;
-
                 d->acl_race_fix = FALSE;
 
                 if (d->sink_name) {
                     pa_sink *sink;
 
                     if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK))) {
-                        int prev_suspended = pa_sink_get_state(sink) == PA_SINK_SUSPENDED;
-
-                        if (prev_suspended && !suspend) {
-                            /* resume */
-                            if (pa_sink_suspend(sink, 0) >= 0)
-                                pa_scache_play_item_by_name(u->core, "pulse-access", d->sink_name, PA_VOLUME_NORM, NULL, NULL);
-                            else
-                                d->acl_race_fix = TRUE;
-
-                        } else if (!prev_suspended && suspend) {
-                            /* suspend */
-                            if (pa_sink_suspend(sink, 1) >= 0)
-                                send_acl_race_fix_message = TRUE;
-                        }
+                        pa_bool_t success = pa_sink_suspend(sink, suspend) >= 0;
+
+                        if (!success && !suspend)
+                            d->acl_race_fix = TRUE; /* resume failed, let's try again */
+                        else if (suspend)
+                            send_acl_race_fix_message = TRUE; /* suspend finished, let's tell everyone to try again */
                     }
                 }
 
@@ -597,18 +563,25 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
                     pa_source *source;
 
                     if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE))) {
-                        int prev_suspended = pa_source_get_state(source) == PA_SOURCE_SUSPENDED;
-
-                        if (prev_suspended && !suspend) {
-                            /* resume */
-                            if (pa_source_suspend(source, 0) < 0)
-                                d->acl_race_fix = TRUE;
-
-                        } else if (!prev_suspended && suspend) {
-                            /* suspend */
-                            if (pa_source_suspend(source, 0) >= 0)
-                                send_acl_race_fix_message = TRUE;
-                        }
+                        pa_bool_t success = pa_source_suspend(source, suspend) >= 0;
+
+                        if (!success && !suspend)
+                            d->acl_race_fix = TRUE; /* resume failed, let's try again */
+                        else if (suspend)
+                            send_acl_race_fix_message = TRUE; /* suspend finished, let's tell everyone to try again */
+                    }
+                }
+
+                if (d->card_name) {
+                    pa_card *card;
+
+                    if ((card = pa_namereg_get(u->core, d->card_name, PA_NAMEREG_CARD))) {
+                        pa_bool_t success = pa_card_suspend(card, suspend) >= 0;
+
+                        if (!success && !suspend)
+                            d->acl_race_fix = TRUE; /* resume failed, let's try again */
+                        else if (suspend)
+                            send_acl_race_fix_message = TRUE; /* suspend finished, let's tell everyone to try again */
                     }
                 }
 
@@ -621,6 +594,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
 
             } else if (!suspend)
                 device_added_cb(u->context, udi);
+
         }
 
         return DBUS_HANDLER_RESULT_HANDLED;
@@ -631,40 +605,36 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
            sever has closed the device. We can remove this as
            soon as HAL learns frevoke() */
 
-        const char *udi;
         struct device *d;
+        const char *udi;
 
         udi = dbus_message_get_path(message);
 
-        if ((d = pa_hashmap_get(u->devices, udi)) && d->acl_race_fix) {
-            pa_log_debug("Got dirty give up message for '%s', trying resume ...", udi);
-
-            d->acl_race_fix = FALSE;
-
-            if (d->sink_name) {
-                pa_sink *sink;
+        if ((d = pa_hashmap_get(u->devices, udi))) {
 
-                if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK))) {
+            if (d->acl_race_fix) {
+                d->acl_race_fix = FALSE;
+                pa_log_debug("Got dirty give up message for '%s', trying resume ...", udi);
 
-                    int prev_suspended = pa_sink_get_state(sink) == PA_SINK_SUSPENDED;
+                if (d->sink_name) {
+                    pa_sink *sink;
 
-                    if (prev_suspended) {
-                        /* resume */
-                        if (pa_sink_suspend(sink, 0) >= 0)
-                            pa_scache_play_item_by_name(u->core, "pulse-access", d->sink_name, PA_VOLUME_NORM, NULL, NULL);
-                    }
+                    if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK)))
+                        pa_sink_suspend(sink, FALSE);
                 }
-            }
 
-            if (d->source_name) {
-                pa_source *source;
+                if (d->source_name) {
+                    pa_source *source;
 
-                if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE))) {
+                    if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE)))
+                        pa_source_suspend(source, FALSE);
+                }
 
-                    int prev_suspended = pa_source_get_state(source) == PA_SOURCE_SUSPENDED;
+                if (d->card_name) {
+                    pa_card *card;
 
-                    if (prev_suspended)
-                        pa_source_suspend(source, 0);
+                    if ((card = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_CARD)))
+                        pa_card_suspend(card, FALSE);
                 }
             }
 
@@ -692,18 +662,20 @@ static void hal_context_free(LibHalContext* hal_context) {
     dbus_error_free(&error);
 }
 
-static LibHalContext* hal_context_new(pa_core* c, DBusConnection *conn) {
+static LibHalContext* hal_context_new(DBusConnection *connection) {
     DBusError error;
     LibHalContext *hal_context = NULL;
 
     dbus_error_init(&error);
 
+    pa_assert(connection);
+
     if (!(hal_context = libhal_ctx_new())) {
         pa_log_error("libhal_ctx_new() failed");
         goto fail;
     }
 
-    if (!libhal_ctx_set_dbus_connection(hal_context, conn)) {
+    if (!libhal_ctx_set_dbus_connection(hal_context, connection)) {
         pa_log_error("Error establishing DBUS connection: %s: %s", error.name, error.message);
         goto fail;
     }
@@ -726,13 +698,10 @@ fail:
 
 int pa__init(pa_module*m) {
     DBusError error;
-    pa_dbus_connection *conn;
     struct userdata *u = NULL;
-    LibHalContext *hal_context = NULL;
     int n = 0;
     pa_modargs *ma;
     const char *api;
-    pa_bool_t use_tsched = TRUE;
 
     pa_assert(m);
 
@@ -743,90 +712,74 @@ int pa__init(pa_module*m) {
         goto fail;
     }
 
-    if (pa_modargs_get_value_boolean(ma, "tsched", &use_tsched) < 0) {
+    m->userdata = u = pa_xnew(struct userdata, 1);
+    u->core = m->core;
+    u->context = NULL;
+    u->connection = NULL;
+    u->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+    u->capability = NULL;
+
+#ifdef HAVE_ALSA
+    u->use_tsched = TRUE;
+
+    if (pa_modargs_get_value_boolean(ma, "tsched", &u->use_tsched) < 0) {
         pa_log("Failed to parse tsched argument.");
         goto fail;
     }
 
-    if ((api = pa_modargs_get_value(ma, "api", NULL))) {
-        pa_bool_t good = FALSE;
+    api = pa_modargs_get_value(ma, "api", "alsa");
 
-#ifdef HAVE_ALSA
-        if (strcmp(api, CAPABILITY_ALSA) == 0) {
-            good = TRUE;
-            api = CAPABILITY_ALSA;
-        }
+    if (pa_streq(api, "alsa"))
+        u->capability = CAPABILITY_ALSA;
+#else
+    api = pa_modargs_get_value(ma, "api", "oss");
 #endif
+
 #ifdef HAVE_OSS
-        if (strcmp(api, CAPABILITY_OSS) == 0) {
-            good = TRUE;
-            api = CAPABILITY_OSS;
-        }
+    if (pa_streq(api, "oss"))
+        u->capability = CAPABILITY_OSS;
 #endif
 
-        if (!good) {
-            pa_log_error("Invalid API specification.");
-            goto fail;
-        }
+    if (!u->capability) {
+        pa_log_error("Invalid API specification.");
+        goto fail;
     }
 
-    if (!(conn = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &error)) || dbus_error_is_set(&error)) {
-        if (conn)
-            pa_dbus_connection_unref(conn);
+    if (!(u->connection = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &error)) || dbus_error_is_set(&error)) {
         pa_log_error("Unable to contact DBUS system bus: %s: %s", error.name, error.message);
         goto fail;
     }
 
-    if (!(hal_context = hal_context_new(m->core, pa_dbus_connection_get(conn)))) {
+    if (!(u->context = hal_context_new(pa_dbus_connection_get(u->connection)))) {
         /* pa_hal_context_new() logs appropriate errors */
-        pa_dbus_connection_unref(conn);
         goto fail;
     }
 
-    u = pa_xnew(struct userdata, 1);
-    u->core = m->core;
-    u->context = hal_context;
-    u->connection = conn;
-    u->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
-    u->capability = api;
-#ifdef HAVE_ALSA
-    u->use_tsched = use_tsched;
-#endif
-    m->userdata = u;
+    n = hal_device_add_all(u);
 
-#ifdef HAVE_ALSA
-    n = hal_device_add_all(u, CAPABILITY_ALSA);
-#endif
-#if defined(HAVE_ALSA) && defined(HAVE_OSS)
-    if (n <= 0)
-#endif
-#ifdef HAVE_OSS
-        n += hal_device_add_all(u, CAPABILITY_OSS);
-#endif
+    libhal_ctx_set_user_data(u->context, u);
+    libhal_ctx_set_device_added(u->context, device_added_cb);
+    libhal_ctx_set_device_removed(u->context, device_removed_cb);
+    libhal_ctx_set_device_new_capability(u->context, new_capability_cb);
+    libhal_ctx_set_device_lost_capability(u->context, lost_capability_cb);
 
-    libhal_ctx_set_user_data(hal_context, u);
-    libhal_ctx_set_device_added(hal_context, device_added_cb);
-    libhal_ctx_set_device_removed(hal_context, device_removed_cb);
-    libhal_ctx_set_device_new_capability(hal_context, new_capability_cb);
-    libhal_ctx_set_device_lost_capability(hal_context, lost_capability_cb);
-
-    if (!libhal_device_property_watch_all(hal_context, &error)) {
+    if (!libhal_device_property_watch_all(u->context, &error)) {
         pa_log_error("Error monitoring device list: %s: %s", error.name, error.message);
         goto fail;
     }
 
-    if (!dbus_connection_add_filter(pa_dbus_connection_get(conn), filter_cb, u, NULL)) {
+    if (!dbus_connection_add_filter(pa_dbus_connection_get(u->connection), filter_cb, u, NULL)) {
         pa_log_error("Failed to add filter function");
         goto fail;
     }
 
-    dbus_bus_add_match(pa_dbus_connection_get(conn), "type='signal',sender='org.freedesktop.Hal', interface='org.freedesktop.Hal.Device.AccessControl'", &error);
+    dbus_bus_add_match(pa_dbus_connection_get(u->connection), "type='signal',sender='org.freedesktop.Hal', interface='org.freedesktop.Hal.Device.AccessControl'", &error);
     if (dbus_error_is_set(&error)) {
         pa_log_error("Unable to subscribe to HAL ACL signals: %s: %s", error.name, error.message);
         goto fail;
     }
 
-    dbus_bus_add_match(pa_dbus_connection_get(conn), "type='signal',interface='org.pulseaudio.Server'", &error);
+    dbus_bus_add_match(pa_dbus_connection_get(u->connection), "type='signal',interface='org.pulseaudio.Server'", &error);
     if (dbus_error_is_set(&error)) {
         pa_log_error("Unable to subscribe to PulseAudio signals: %s: %s", error.name, error.message);
         goto fail;
@@ -860,8 +813,17 @@ void pa__done(pa_module *m) {
     if (u->context)
         hal_context_free(u->context);
 
-    if (u->devices)
-        pa_hashmap_free(u->devices, hal_device_free_cb, NULL);
+    if (u->devices) {
+        struct device *d;
+
+        while ((d = pa_hashmap_first(u->devices))) {
+            pa_hashmap_remove(u->devices, d->udi);
+            pa_hashmap_remove(u->devices, d->originating_udi);
+            device_free(d);
+        }
+
+        pa_hashmap_free(u->devices, NULL, NULL);
+    }
 
     if (u->connection) {
         DBusError error;

commit bb23932e9a95f4b87d305f043f52fadd176c80bc
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 22 01:15:49 2009 +0100

    When resuming an OSS device ask for the very same fragment settings as we did the first time
    
    In OSS it is only possible to request fragment sizes that are powers of
    2. However actually selected fragment sizes may be arbitrary values.
    This means that it is not always possible to request the same fragment
    size after a suspend that was used before the suspend because we simply
    cannot express it in the request. To work around that we should issue
    the same request as we did the first time.

diff --git a/src/modules/oss/module-oss.c b/src/modules/oss/module-oss.c
index b5b9e2a..f277d63 100644
--- a/src/modules/oss/module-oss.c
+++ b/src/modules/oss/module-oss.c
@@ -121,7 +121,7 @@ struct userdata {
     int mixer_fd;
     int mixer_devmask;
 
-    int nfrags, frag_size;
+    int nfrags, frag_size, orig_frag_size;
 
     pa_bool_t use_mmap;
     unsigned out_mmap_current, in_mmap_current;
@@ -536,7 +536,7 @@ static int unsuspend(struct userdata *u) {
     }
 
     if (u->nfrags >= 2 && u->frag_size >= 1)
-        if (pa_oss_set_fragments(u->fd, u->nfrags, u->frag_size) < 0) {
+        if (pa_oss_set_fragments(u->fd, u->nfrags, u->orig_frag_size) < 0) {
             pa_log_warn("Resume failed, couldn't set original fragment settings.");
             goto fail;
         }
@@ -1146,7 +1146,7 @@ int pa__init(pa_module*m) {
     struct userdata *u = NULL;
     const char *dev;
     int fd = -1;
-    int nfrags, frag_size;
+    int nfrags, orig_frag_size, frag_size;
     int mode, caps;
     pa_bool_t record = TRUE, playback = TRUE, use_mmap = TRUE;
     pa_sample_spec ss;
@@ -1218,6 +1218,7 @@ int pa__init(pa_module*m) {
 
     pa_log_info("Device opened in %s mode.", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));
 
+    orig_frag_size = frag_size;
     if (nfrags >= 2 && frag_size >= 1)
         if (pa_oss_set_fragments(fd, nfrags, frag_size) < 0)
             goto fail;
@@ -1245,6 +1246,7 @@ int pa__init(pa_module*m) {
     u->device_name = pa_xstrdup(dev);
     u->in_nfrags = u->out_nfrags = (uint32_t) (u->nfrags = nfrags);
     u->out_fragment_size = u->in_fragment_size = (uint32_t) (u->frag_size = frag_size);
+    u->orig_frag_size = orig_frag_size;
     u->use_mmap = use_mmap;
     u->rtpoll = pa_rtpoll_new();
     pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
diff --git a/src/modules/oss/oss-util.c b/src/modules/oss/oss-util.c
index ea8d811..41092d8 100644
--- a/src/modules/oss/oss-util.c
+++ b/src/modules/oss/oss-util.c
@@ -250,6 +250,8 @@ int pa_oss_set_fragments(int fd, int nfrags, int frag_size) {
     int arg;
     arg = ((int) nfrags << 16) | simple_log2(frag_size);
 
+    pa_log_debug("Asking for %i fragments of size %i (requested %i)", nfrags, 1 << simple_log2(frag_size), frag_size);
+
     if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &arg) < 0) {
         pa_log("SNDCTL_DSP_SETFRAGMENT: %s", pa_cstrerror(errno));
         return -1;

commit 75eeea65bd7007587a2465f014cacc9472f10bde
Author: Jared D. McNeill <jmcneill at NetBSD.org>
Date:   Thu Jan 22 01:37:19 2009 +0100

    NetBSD needs to include sys/uio.h for some socket functions
    
    Signed-off-by: Lennart Poettering <lennart at poettering.net>

diff --git a/configure.ac b/configure.ac
index 1d891b7..57b3d92 100644
--- a/configure.ac
+++ b/configure.ac
@@ -246,7 +246,7 @@ AC_HEADER_STDC
 AC_CHECK_HEADERS([arpa/inet.h glob.h grp.h netdb.h netinet/in.h \
     netinet/in_systm.h netinet/tcp.h poll.h pwd.h sched.h \
     sys/mman.h sys/resource.h sys/select.h sys/socket.h sys/wait.h \
-    syslog.h sys/dl.h dlfcn.h linux/sockios.h])
+    sys/uio.h syslog.h sys/dl.h dlfcn.h linux/sockios.h])
 AC_CHECK_HEADERS([netinet/ip.h], [], [],
 		 [#include <sys/types.h>
 		  #if HAVE_NETINET_IN_H
diff --git a/src/modules/rtp/rtp.c b/src/modules/rtp/rtp.c
index 8835101..c09c321 100644
--- a/src/modules/rtp/rtp.c
+++ b/src/modules/rtp/rtp.c
@@ -35,6 +35,10 @@
 #include <sys/filio.h>
 #endif
 
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+
 #include <pulsecore/core-error.h>
 #include <pulsecore/log.h>
 #include <pulsecore/macro.h>
diff --git a/src/modules/rtp/sap.c b/src/modules/rtp/sap.c
index b0c95aa..7764f7b 100644
--- a/src/modules/rtp/sap.c
+++ b/src/modules/rtp/sap.c
@@ -38,6 +38,10 @@
 #include <sys/filio.h>
 #endif
 
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+
 #include <pulse/xmalloc.h>
 
 #include <pulsecore/core-error.h>

commit cc425ed260bdaa94ba5a7369efcb7bbe630a8c05
Author: Jared D. McNeill <jmcneill at NetBSD.org>
Date:   Thu Jan 22 01:39:54 2009 +0100

    NetBSD doesn't know RLIMIT_AS
    
    Signed-off-by: Lennart Poettering <lennart at poettering.net>

diff --git a/src/daemon/main.c b/src/daemon/main.c
index b01689f..68e64c1 100644
--- a/src/daemon/main.c
+++ b/src/daemon/main.c
@@ -302,7 +302,9 @@ static void set_all_rlimits(const pa_daemon_conf *conf) {
 #ifdef RLIMIT_MEMLOCK
     set_one_rlimit(&conf->rlimit_memlock, RLIMIT_MEMLOCK, "RLIMIT_MEMLOCK");
 #endif
+#ifdef RLIMIT_AS
     set_one_rlimit(&conf->rlimit_as, RLIMIT_AS, "RLIMIT_AS");
+#endif
 #ifdef RLIMIT_LOCKS
     set_one_rlimit(&conf->rlimit_locks, RLIMIT_LOCKS, "RLIMIT_LOCKS");
 #endif

commit 8d89ccdcf2d3aed41688a2933ebc58942e93bb45
Author: Jared D. McNeill <jmcneill at NetBSD.org>
Date:   Thu Jan 22 01:52:35 2009 +0100

    NetBSD specific atomic operation implementation
    
    Signed-off-by: Lennart Poettering <lennart at poettering.net>

diff --git a/configure.ac b/configure.ac
index 57b3d92..568e580 100644
--- a/configure.ac
+++ b/configure.ac
@@ -129,16 +129,31 @@ AC_ARG_ENABLE([atomic-arm-memory-barrier],
             esac
         ],)
 
+AC_ARG_ENABLE([netbsd-atomic-ops],
+    AS_HELP_STRING([--enable-netbsd-atomic-ops],[Use the native NetBSD atomic_ops implementation]),
+        [
+            case "${enableval}" in
+                yes) atomic_netbsd_helpers=yes ;;
+                no) atomic_netbsd_helpers=no ;;
+                *) AC_MSG_ERROR(bad value ${enableval} for --enable-netbsd-atomic-ops) ;;
+            esac
+        ],
+        [atomic_netbsd_helpers=auto])
+
 AC_MSG_CHECKING([target operating system])
 case $host in
-    	*-*-linux*)
-	    AC_MSG_RESULT([linux])
-	    pulse_target_os=linux
-    	;;
-	*)
-	    AC_MSG_RESULT([unknown])
-	    pulse_target_os=unknown
-	;;
+        *-*-linux*)
+            AC_MSG_RESULT([linux])
+            pulse_target_os=linux
+        ;;
+        *-*-netbsd*)
+            AC_MSG_RESULT([netbsd])
+            pulse_target_os=netbsd
+        ;;
+        *)
+            AC_MSG_RESULT([unknown])
+            pulse_target_os=unknown
+        ;;
 esac
 
 # If everything else fails use libatomic_ops
@@ -160,42 +175,48 @@ else
     AC_MSG_CHECKING([architecture for native atomic operations])
     case $host_cpu in
         arm*)
-	    AC_MSG_RESULT([arm])
-	    AC_MSG_CHECKING([whether we can use Linux kernel helpers])
-	    # The Linux kernel helper functions have been there since 2.6.16. However
-  	    # compile time checking for kernel version in cross compile environment
-	    # (which is usually the case for arm cpu) is tricky (or impossible).
-	    if test "x$pulse_target_os" = "xlinux" && test "x$atomic_arm_linux_helpers" != "xno"; then
-	        AC_MSG_RESULT([yes])
-        	AC_DEFINE_UNQUOTED(ATOMIC_ARM_LINUX_HELPERS, 1, [special arm linux implementation])
-    		need_libatomic_ops=no
-	    else
-	       AC_MSG_RESULT([no])
-	       AC_CACHE_CHECK([compiler support for arm inline asm atomic operations],
-	         pulseaudio_cv_support_arm_atomic_ops,
-		 [AC_COMPILE_IFELSE(
-		    AC_LANG_PROGRAM([],
-		      [[volatile int a=0;
-	                int o=0, n=1, r;
-	                asm volatile ("ldrex	%0, [%1]\n"
-		        	 	 "subs	%0, %0, %2\n"
-		        	 	 "strexeq %0, %3, [%1]\n"
-		        	 	 : "=&r" (r)
-		        	 	 : "r" (&a), "Ir" (o), "r" (n)
-		        	 	 : "cc");
+            AC_MSG_RESULT([arm])
+            AC_MSG_CHECKING([whether we can use Linux kernel helpers])
+            # The Linux kernel helper functions have been there since 2.6.16. However
+            # compile time checking for kernel version in cross compile environment
+            # (which is usually the case for arm cpu) is tricky (or impossible).
+            if test "x$pulse_target_os" = "xlinux" && test "x$atomic_arm_linux_helpers" != "xno"; then
+                AC_MSG_RESULT([yes])
+                AC_DEFINE_UNQUOTED(ATOMIC_ARM_LINUX_HELPERS, 1, [special arm linux implementation])
+                need_libatomic_ops=no
+            else
+               AC_MSG_RESULT([no])
+               AC_CACHE_CHECK([compiler support for arm inline asm atomic operations],
+                 pulseaudio_cv_support_arm_atomic_ops,
+                 [AC_COMPILE_IFELSE(
+                    AC_LANG_PROGRAM([],
+                      [[volatile int a=0;
+                        int o=0, n=1, r;
+                        asm volatile ("ldrex    %0, [%1]\n"
+                                         "subs  %0, %0, %2\n"
+                                         "strexeq %0, %3, [%1]\n"
+                                         : "=&r" (r)
+                                         : "r" (&a), "Ir" (o), "r" (n)
+                                         : "cc");
                         return (a==1 ? 0 : -1);
-		      ]]),
-		    [pulseaudio_cv_support_arm_atomic_ops=yes],
-		    [pulseaudio_cv_support_arm_atomic_ops=no])
-		 ])
-	       AS_IF([test "$pulseaudio_cv_support_arm_atomic_ops" = "yes"], [
-	       	   AC_DEFINE([ATOMIC_ARM_INLINE_ASM], 1, [Have ARMv6 instructions.])
-		   need_libatomic_ops=no
-	         ])
-	   fi
-      	;;
+                      ]]),
+                    [pulseaudio_cv_support_arm_atomic_ops=yes],
+                    [pulseaudio_cv_support_arm_atomic_ops=no])
+                 ])
+               AS_IF([test "$pulseaudio_cv_support_arm_atomic_ops" = "yes"], [
+                   AC_DEFINE([ATOMIC_ARM_INLINE_ASM], 1, [Have ARMv6 instructions.])
+                   need_libatomic_ops=no
+                 ])
+           fi
+        ;;
         *)
-	    AC_MSG_RESULT([unknown])
+            if test "x$pulse_target_os" = "xnetbsd" && test "x$atomic_netbsd_helpers" = "xyes"; then
+                AC_MSG_RESULT([yes])
+                AC_DEFINE_UNQUOTED(NETBSD_ATOMIC_OPS, 1, [netbsd implementation])
+                need_libatomic_ops=no
+            else
+                AC_MSG_RESULT([unknown])
+            fi
         ;;
     esac
 fi
@@ -225,11 +246,11 @@ LTDL_INIT([convenience recursive])
 os_is_win32=0
 
 case "$host_os" in
-	mingw*)
+        mingw*)
         AC_DEFINE([OS_IS_WIN32], 1, [Build target is Windows.])
         os_is_win32=1
-		;;
-	esac
+                ;;
+        esac
 
 AM_CONDITIONAL(OS_IS_WIN32, test "x$os_is_win32" = "x1")
 
@@ -248,14 +269,14 @@ AC_CHECK_HEADERS([arpa/inet.h glob.h grp.h netdb.h netinet/in.h \
     sys/mman.h sys/resource.h sys/select.h sys/socket.h sys/wait.h \
     sys/uio.h syslog.h sys/dl.h dlfcn.h linux/sockios.h])
 AC_CHECK_HEADERS([netinet/ip.h], [], [],
-		 [#include <sys/types.h>
-		  #if HAVE_NETINET_IN_H
-		  # include <netinet/in.h>
-		  #endif
-		  #if HAVE_NETINET_IN_SYSTM_H
-		  # include <netinet/in_systm.h>
-		  #endif
-		 ])
+                 [#include <sys/types.h>
+                  #if HAVE_NETINET_IN_H
+                  # include <netinet/in.h>
+                  #endif
+                  #if HAVE_NETINET_IN_SYSTM_H
+                  # include <netinet/in_systm.h>
+                  #endif
+                 ])
 AC_CHECK_HEADERS([regex.h], [HAVE_REGEX=1], [HAVE_REGEX=0])
 AC_CHECK_HEADERS([sys/un.h], [HAVE_AF_UNIX=1], [HAVE_AF_UNIX=0])
 
@@ -275,6 +296,9 @@ AC_CHECK_HEADERS([sys/filio.h])
 # Windows
 AC_CHECK_HEADERS([windows.h winsock2.h ws2tcpip.h])
 
+# NetBSD
+AC_CHECK_HEADERS([sys/atomic.h])
+
 # Other
 AC_CHECK_HEADERS([sys/ioctl.h])
 AC_CHECK_HEADERS([byteswap.h])
diff --git a/src/pulsecore/atomic.h b/src/pulsecore/atomic.h
index 9c58c66..6e33a0e 100644
--- a/src/pulsecore/atomic.h
+++ b/src/pulsecore/atomic.h
@@ -107,6 +107,79 @@ static inline pa_bool_t pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, v
     return __sync_bool_compare_and_swap(&a->value, (long) old_p, (long) new_p);
 }
 
+#elif defined(__NetBSD__) && defined(HAVE_SYS_ATOMIC_H)
+
+/* NetBSD 5.0+ atomic_ops(3) implementation */
+
+#include <sys/atomic.h>
+
+typedef struct pa_atomic {
+    volatile unsigned int value;
+} pa_atomic_t;
+
+#define PA_ATOMIC_INIT(v) { .value = (unsigned int) (v) }
+
+static inline int pa_atomic_load(const pa_atomic_t *a) {
+    membar_sync();
+    return (int) a->value;
+}
+
+static inline void pa_atomic_store(pa_atomic_t *a, int i) {
+    a->value = (unsigned int) i;
+    membar_sync();
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_add(pa_atomic_t *a, int i) {
+    int nv = (int) atomic_add_int_nv(&a->value, i);
+    return nv - i;
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_sub(pa_atomic_t *a, int i) {
+    int nv = (int) atomic_add_int_nv(&a->value, -i);
+    return nv + i;
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_inc(pa_atomic_t *a) {
+    int nv = (int) atomic_inc_uint_nv(&a->value);
+    return nv - 1;
+}
+
+/* Returns the previously set value */
+static inline int pa_atomic_dec(pa_atomic_t *a) {
+    int nv = (int) atomic_dec_uint_nv(&a->value);
+    return nv + 1;
+}
+
+/* Returns TRUE when the operation was successful. */
+static inline pa_bool_t pa_atomic_cmpxchg(pa_atomic_t *a, int old_i, int new_i) {
+    unsigned int r = atomic_cas_uint(&a->value, (unsigned int) old_i, (unsigned int) new_i);
+    return (int) r == old_i;
+}
+
+typedef struct pa_atomic_ptr {
+    volatile void *value;
+} pa_atomic_ptr_t;
+
+#define PA_ATOMIC_PTR_INIT(v) { .value = (v) }
+
+static inline void* pa_atomic_ptr_load(const pa_atomic_ptr_t *a) {
+    membar_sync();
+    return (void *) a->value;
+}
+
+static inline void pa_atomic_ptr_store(pa_atomic_ptr_t *a, void *p) {
+    a->value = p;
+    membar_sync();
+}
+
+static inline pa_bool_t pa_atomic_ptr_cmpxchg(pa_atomic_ptr_t *a, void *old_p, void* new_p) {
+    void *r = atomic_cas_ptr(&a->value, old_p, new_p);
+    return r == old_p;
+}
+
 #elif defined(__GNUC__) && (defined(__amd64__) || defined(__x86_64__))
 
 #warn "The native atomic operations implementation for AMD64 has not been tested thoroughly. libatomic_ops is known to not work properly on AMD64 and your gcc version is too old for the gcc-builtin atomic ops support. You have three options now: test the native atomic operations implementation for AMD64, fix libatomic_ops, or upgrade your GCC."

commit ca6b79141bfa36231420b7adc6080faee5b63077
Author: Jared D. McNeill <jmcneill at NetBSD.org>
Date:   Thu Jan 22 01:55:21 2009 +0100

    It is more portable to assume that SO_RCVBUF/SO_SNDBUF takes and int instead of a size_t
    
    Signed-off-by: Lennart Poettering <lennart at poettering.net>

diff --git a/src/pulsecore/socket-util.c b/src/pulsecore/socket-util.c
index f721f69..a092002 100644
--- a/src/pulsecore/socket-util.c
+++ b/src/pulsecore/socket-util.c
@@ -202,9 +202,11 @@ void pa_make_udp_socket_low_delay(int fd) {
 }
 
 int pa_socket_set_rcvbuf(int fd, size_t l) {
+    int bufsz = (int)l;
+
     pa_assert(fd >= 0);
 
-    if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void*)&l, sizeof(l)) < 0) {
+    if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void*)&bufsz, sizeof(bufsz)) < 0) {
         pa_log_warn("SO_RCVBUF: %s", pa_cstrerror(errno));
         return -1;
     }
@@ -213,9 +215,11 @@ int pa_socket_set_rcvbuf(int fd, size_t l) {
 }
 
 int pa_socket_set_sndbuf(int fd, size_t l) {
+    int bufsz = (int)l;
+
     pa_assert(fd >= 0);
 
-    if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (void*)&l, sizeof(l)) < 0) {
+    if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (void*)&bufsz, sizeof(bufsz)) < 0) {
         pa_log("SO_SNDBUF: %s", pa_cstrerror(errno));
         return -1;
     }

commit 61075a78f7c6f1a2242e14b2fa347c5d611b1b5b
Author: Jared D. McNeill <jmcneill at NetBSD.org>
Date:   Thu Jan 22 01:57:52 2009 +0100

    NetBSD doesn't know ENOLINK
    
    Signed-off-by: Lennart Poettering <lennart at poettering.net>

diff --git a/src/modules/oss/oss-util.c b/src/modules/oss/oss-util.c
index 41092d8..f04b875 100644
--- a/src/modules/oss/oss-util.c
+++ b/src/modules/oss/oss-util.c
@@ -309,7 +309,11 @@ static int get_device_number(const char *dev) {
     int r;
 
     if (!(p = rp = pa_readlink(dev))) {
+#ifdef ENOLINK
         if (errno != EINVAL && errno != ENOLINK) {
+#else
+        if (errno != EINVAL) {
+#endif
             r = -1;
             goto finish;
         }

commit c0e4e5a86828cb63d43c56db3a37d4e2c4db7a46
Author: Jared D. McNeill <jmcneill at NetBSD.org>
Date:   Thu Jan 22 02:15:50 2009 +0100

    NetBSD doesn't know getgrnam_r()/getpwnam_r()
    
    Signed-off-by: Lennart Poettering <lennart at poettering.net>

diff --git a/configure.ac b/configure.ac
index 568e580..9912843 100644
--- a/configure.ac
+++ b/configure.ac
@@ -367,8 +367,8 @@ AC_CHECK_FUNCS([lrintf strtof])
 AC_FUNC_FORK
 AC_FUNC_GETGROUPS
 AC_FUNC_SELECT_ARGTYPES
-AC_CHECK_FUNCS([chmod chown clock_gettime getaddrinfo getgrgid_r \
-    getpwuid_r gettimeofday getuid inet_ntop inet_pton mlock nanosleep \
+AC_CHECK_FUNCS([chmod chown clock_gettime getaddrinfo getgrgid_r getgrnam_r \
+    getpwnam_r getpwuid_r gettimeofday getuid inet_ntop inet_pton mlock nanosleep \
     pipe posix_fadvise posix_madvise posix_memalign setpgid setsid shm_open \
     sigaction sleep sysconf])
 AC_CHECK_FUNCS([mkfifo], [HAVE_MKFIFO=1], [HAVE_MKFIFO=0])
diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
index 6f31566..fcb5c42 100644
--- a/src/pulsecore/core-util.c
+++ b/src/pulsecore/core-util.c
@@ -935,7 +935,7 @@ static int is_group(gid_t gid, const char *name) {
 #else
     n = -1;
 #endif
-    if (n < 0)
+    if (n <= 0)
         n = 512;
 
     data = pa_xmalloc((size_t) n);
@@ -959,7 +959,7 @@ finish:
      * support getgrgid_r. */
 
     errno = 0;
-    if ((result = getgrgid(gid)) == NULL) {
+    if (!(result = getgrgid(gid))) {
         pa_log("getgrgid(%u): %s", gid, pa_cstrerror(errno));
 
         if (!errno)
@@ -1026,18 +1026,35 @@ int pa_uid_in_group(uid_t uid, const char *name) {
     char **i;
     int r = -1;
 
+#ifdef _SC_GETGR_R_SIZE_MAX
     g_n = sysconf(_SC_GETGR_R_SIZE_MAX);
+#else
+    g_n = -1;
+#endif
+    if (g_n <= 0)
+        g_n = 512;
+
     g_buf = pa_xmalloc((size_t) g_n);
 
+#ifdef _SC_GETPW_R_SIZE_MAX
     p_n = sysconf(_SC_GETPW_R_SIZE_MAX);
+#else
+    p_n = -1;
+#endif
+    if (p_n <= 0)
+        p_n = 512;
+
     p_buf = pa_xmalloc((size_t) p_n);
 
     errno = 0;
-    if (getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr) != 0 || !gr) {
-
+#ifdef HAVE_GETGRNAM_R
+    if (getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr) != 0 || !gr)
+#else
+    if (!(gr = getgrnam(name)))
+#endif
+    {
         if (!errno)
             errno = ENOENT;
-
         goto finish;
     }
 
@@ -1045,8 +1062,11 @@ int pa_uid_in_group(uid_t uid, const char *name) {
     for (i = gr->gr_mem; *i; i++) {
         struct passwd pwbuf, *pw;
 
-        errno = 0;
+#ifdef HAVE_GETPWNAM_R
         if (getpwnam_r(*i, &pwbuf, p_buf, (size_t) p_n, &pw) != 0 || !pw)
+#else
+        if (!(pw = getpwnam(*i)))
+#endif
             continue;
 
         if (pw->pw_uid == uid) {
@@ -1069,15 +1089,25 @@ gid_t pa_get_gid_of_group(const char *name) {
     long g_n;
     struct group grbuf, *gr;
 
+#ifdef _SC_GETGR_R_SIZE_MAX
     g_n = sysconf(_SC_GETGR_R_SIZE_MAX);
+#else
+    g_n = -1;
+#endif
+    if (g_n <= 0)
+        g_n = 512;
+
     g_buf = pa_xmalloc((size_t) g_n);
 
     errno = 0;
-    if (getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr) != 0 || !gr) {
-
+#ifdef HAVE_GETGRNAM_R
+    if (getgrnam_r(name, &grbuf, g_buf, (size_t) g_n, &gr) != 0 || !gr)
+#else
+    if (!(gr = getgrnam(name)))
+#endif
+    {
         if (!errno)
             errno = ENOENT;
-
         goto finish;
     }
 

commit 4dc191646750a15c7f92f945816b59cd91c5728c
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 22 02:16:53 2009 +0100

    add API pa_ncpus()

diff --git a/src/daemon/main.c b/src/daemon/main.c
index 68e64c1..f483607 100644
--- a/src/daemon/main.c
+++ b/src/daemon/main.c
@@ -823,6 +823,8 @@ int main(int argc, char *argv[]) {
     pa_log_debug(_("Running on host: %s"), s);
     pa_xfree(s);
 
+    pa_log_debug(_("Found %u CPUs."), pa_ncpus());
+
     pa_log_info(_("Page size is %lu bytes"), (unsigned long) PA_PAGE_SIZE);
 
 #ifdef HAVE_VALGRIND_MEMCHECK_H
diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
index fcb5c42..e65b179 100644
--- a/src/pulsecore/core-util.c
+++ b/src/pulsecore/core-util.c
@@ -2541,3 +2541,15 @@ void pa_reduce(unsigned *num, unsigned *den) {
 
     pa_assert(pa_gcd(*num, *den) == 1);
 }
+
+unsigned pa_ncpus(void) {
+    long ncpus;
+
+#ifdef _SC_NPROCESSORS_CONF
+    ncpus = sysconf(_SC_NPROCESSORS_CONF);
+#else
+    ncpus = 1;
+#endif
+
+    return ncpus <= 0 ? 1 : (unsigned) ncpus;
+}
diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h
index 44b3af3..18901f4 100644
--- a/src/pulsecore/core-util.h
+++ b/src/pulsecore/core-util.h
@@ -213,4 +213,6 @@ static inline pa_bool_t pa_in_valgrind(void) {
 unsigned pa_gcd(unsigned a, unsigned b);
 void pa_reduce(unsigned *num, unsigned *den);
 
+unsigned pa_ncpus(void);
+
 #endif

commit cef5f48b501700a8202193a240e0988b52f45fc9
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 22 02:20:00 2009 +0100

    make rtstutter use pa_ncpus()

diff --git a/src/tests/rtstutter.c b/src/tests/rtstutter.c
index 6b0cb8f..fc23d95 100644
--- a/src/tests/rtstutter.c
+++ b/src/tests/rtstutter.c
@@ -36,6 +36,7 @@
 
 #include <pulsecore/log.h>
 #include <pulsecore/macro.h>
+#include <pulsecore/core-util.h>
 
 static int msec_lower, msec_upper;
 
@@ -106,7 +107,7 @@ int main(int argc, char*argv[]) {
 
     pa_log_notice("Creating random latencies in the range of %ims to  %ims.", msec_lower, msec_upper);
 
-    for (n = 1; n < sysconf(_SC_NPROCESSORS_CONF); n++) {
+    for (n = 1; n < pa_ncpus(); n++) {
         pthread_t t;
         pa_assert_se(pthread_create(&t, NULL, work, PA_INT_TO_PTR(n)) == 0);
     }

commit 7c7133e09ded0f3fc004737be0443eb9c4dae4c8
Author: Jared D. McNeill <jmcneill at NetBSD.org>
Date:   Thu Jan 22 02:24:04 2009 +0100

    NetBSD sometimes doesn't know SNDCTL_DSP_GETODELAY
    
    Signed-off-by: Lennart Poettering <lennart at poettering.net>

diff --git a/src/modules/oss/module-oss.c b/src/modules/oss/module-oss.c
index f277d63..7271a08 100644
--- a/src/modules/oss/module-oss.c
+++ b/src/modules/oss/module-oss.c
@@ -71,6 +71,11 @@
 #include <pulsecore/thread-mq.h>
 #include <pulsecore/rtpoll.h>
 
+#if defined(__NetBSD__) && !defined(SNDCTL_DSP_GETODELAY)
+#include <sys/audioio.h>
+#include <sys/syscall.h>
+#endif
+
 #include "oss-util.h"
 #include "module-oss-symdef.h"
 
@@ -399,13 +404,27 @@ static pa_usec_t io_sink_get_latency(struct userdata *u) {
 
     if (u->use_getodelay) {
         int arg;
-
+#if defined(__NetBSD__) && !defined(SNDCTL_DSP_GETODELAY)
+#if defined(AUDIO_GETBUFINFO)
+        struct audio_info info;
+        if (syscall(SYS_ioctl, u->fd, AUDIO_GETBUFINFO, &info) < 0) {
+            pa_log_info("Device doesn't support AUDIO_GETBUFINFO: %s", pa_cstrerror(errno));
+            u->use_getodelay = 0;
+        } else {
+            arg = info.play.seek + info.blocksize / 2;
+            r = pa_bytes_to_usec((size_t) arg, &u->sink->sample_spec);
+        }
+#else
+        pa_log_info("System doesn't support AUDIO_GETBUFINFO");
+        u->use_getodelay = 0;
+#endif
+#else
         if (ioctl(u->fd, SNDCTL_DSP_GETODELAY, &arg) < 0) {
             pa_log_info("Device doesn't support SNDCTL_DSP_GETODELAY: %s", pa_cstrerror(errno));
             u->use_getodelay = 0;
         } else
             r = pa_bytes_to_usec((size_t) arg, &u->sink->sample_spec);
-
+#endif
     }
 
     if (!u->use_getodelay && u->use_getospace) {

commit c65d3a9fb04d2c598b5c7fec3a060ec97eb778b9
Author: Diego E. 'Flameeyes' Pettenò <flameeyes at gmail.com>
Date:   Thu Jan 22 16:37:40 2009 +0100

    Remove support for internal distributing and bundling of libltdl.
    
    Standing to what the libtool documentation says, the LTDL_INIT macro and
    the related configure options are only useful when intending to distribute
    libltdl, and is superfluous if the system copy were always to be used.
    
    Which makes it very easy to just drop the internal copy and use the system
    library, just do it like any other library lacking pkg-config files to
    identify its presence.
    
    If this tries to build against an older libtool version it might fail at
    link time, so for now this is not an user-proof solution. But it at least
    should provide a working environment for packagers.

diff --git a/Makefile.am b/Makefile.am
index f4dd998..8735d31 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -18,7 +18,7 @@
 ACLOCAL_AMFLAGS = -I m4
 
 EXTRA_DIST = bootstrap.sh LICENSE GPL LGPL doxygen/Makefile.am doxygen/Makefile.in doxygen/doxygen.conf.in README todo
-SUBDIRS = libltdl src doxygen man po
+SUBDIRS = src doxygen man po
 
 MAINTAINERCLEANFILES =
 noinst_DATA =
diff --git a/configure.ac b/configure.ac
index 9912843..f659d49 100644
--- a/configure.ac
+++ b/configure.ac
@@ -237,9 +237,28 @@ AS_IF([test "$pulseaudio_cv__Bool" = "yes"], [
 
 #### libtool stuff ####
 LT_PREREQ(2.2)
-LT_CONFIG_LTDL_DIR([libltdl])
 LT_INIT([dlopen win32-dll disable-static])
-LTDL_INIT([convenience recursive])
+
+dnl Unfortunately, even up to libtool 2.2.6a there is no way to know
+dnl exactly which version of libltdl is present in the system, so we
+dnl just assume that it's a working version as long as we have the
+dnl library and the header files.
+dnl
+dnl Check the header files first since the system may have a
+dnl libltdl.so for runtime, but no headers, and we want to bail out as
+dnl soon as possible.
+dnl
+dnl We don't need any special variable for this though, since the user
+dnl can give the proper place to find libltdl through the standard
+dnl variables like LDFLAGS and CPPFLAGS.
+
+AC_CHECK_HEADER([ltdl.h],
+    [AC_CHECK_LIB([ltdl], [lt_dlopen], [LIBLTDL=-lltdl], [LIBLTDL=])],
+    [LIBLTDL=])
+
+AS_IF([test "x$LIBLTDL" = "x"],
+    [AC_MSG_ERROR([Unable to find libltdl.])])
+AC_SUBST([LIBLTDL])
 
 #### Determine build environment ####
 
@@ -1174,7 +1193,6 @@ AM_CONDITIONAL([FORCE_PREOPEN], [test "x$FORCE_PREOPEN" = "xyes"])
 
 AC_CONFIG_FILES([
 Makefile
-libltdl/Makefile
 src/Makefile
 man/Makefile
 libpulse.pc
diff --git a/src/Makefile.am b/src/Makefile.am
index 8d1271c..d77f4dc 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -60,7 +60,6 @@ AM_CFLAGS = \
 	-I$(top_builddir)/src/modules/alsa \
 	-I$(top_srcdir)/src/modules/raop \
 	$(PTHREAD_CFLAGS) -D_POSIX_PTHREAD_SEMANTICS \
-	$(LTDLINCL) \
 	$(LIBSAMPLERATE_CFLAGS) \
 	$(LIBSNDFILE_CFLAGS) \
 	$(LIBSPEEX_CFLAGS) \

commit 1b20d287b9ff496a643c9256870c5cff0e3389f9
Author: Diego E. 'Flameeyes' Pettenò <flameeyes at gmail.com>
Date:   Thu Jan 22 16:41:45 2009 +0100

    Fix logic thinko.

diff --git a/configure.ac b/configure.ac
index f659d49..2b378c1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -105,7 +105,7 @@ dnl Check whether to build tests by default (as compile-test) or not
 AC_ARG_ENABLE([default-build-tests],
     AS_HELP_STRING([--disable-default-build-tests], [Build test programs only during make check]))
 
-AM_CONDITIONAL([BUILD_TESTS_DEFAULT], [test "x$enable_default_build_tests" = "xno"])
+AM_CONDITIONAL([BUILD_TESTS_DEFAULT], [test "x$enable_default_build_tests" != "xno"])
 
 # Native atomic operation support
 AC_ARG_ENABLE([atomic-arm-linux-helpers],

commit 3293251198effe635f4afa291b2f99219162db9d
Author: Diego E. 'Flameeyes' Pettenò <flameeyes at gmail.com>
Date:   Thu Jan 22 16:52:41 2009 +0100

    Move the safety check about pkg-config in bootstrap.sh.
    
    Don't check twice for pkg-config during configure, since the undefined
    macro would be possibly caused on a different system.

diff --git a/bootstrap.sh b/bootstrap.sh
index 6698f68..4ca2b33 100755
--- a/bootstrap.sh
+++ b/bootstrap.sh
@@ -40,6 +40,15 @@ run_versioned() {
 
 set -ex
 
+# We check for this here, because if pkg-config is not found in the
+# system, it's likely that the pkg.m4 macro file is also not present,
+# which will make PKG_PROG_PKG_CONFIG be undefined and the generated
+# configure file faulty.
+if ! pkg-config --version &>/dev/null; then
+    echo "pkg-config is required to bootstrap this program" &>/dev/null
+    exit 1
+fi
+
 if [ "x$1" = "xam" ] ; then
     run_versioned automake "$VERSION" -a -c --foreign
     ./config.status
diff --git a/configure.ac b/configure.ac
index 2b378c1..11f9876 100644
--- a/configure.ac
+++ b/configure.ac
@@ -462,14 +462,6 @@ AC_SUBST(pulselocaledir)
 
 #### pkg-config ####
 
-# Check for pkg-config manually first, as if its not installed the
-# PKG_PROG_PKG_CONFIG macro won't be defined.
-AC_CHECK_PROG(have_pkg_config, pkg-config, yes, no)
-
-if test x"$have_pkg_config" = "xno"; then
-    AC_MSG_ERROR(pkg-config is required to install this program)
-fi
-
 PKG_PROG_PKG_CONFIG
 
 #### X11 (optional) ####

commit a257448ceb6c763e05bfbcdbd3c45dd61df95155
Author: Diego E. 'Flameeyes' Pettenò <flameeyes at gmail.com>
Date:   Thu Jan 22 20:02:42 2009 +0100

    Improve the ltdl discovery code by checking for libtool 2.x functions.
    
    The lt_dladvise_* interfaces are implemented only in the 2.x series and are
    not implemented in 1.4, so we can rely on their presence to know that the
    version is good enough.

diff --git a/configure.ac b/configure.ac
index 11f9876..bc6c1ce 100644
--- a/configure.ac
+++ b/configure.ac
@@ -244,6 +244,10 @@ dnl exactly which version of libltdl is present in the system, so we
 dnl just assume that it's a working version as long as we have the
 dnl library and the header files.
 dnl
+dnl As an extra safety device, check for lt_dladvise_init() which is
+dnl only implemented in libtool 2.x, and refine as we go if we have
+dnl refined requirements.
+dnl
 dnl Check the header files first since the system may have a
 dnl libltdl.so for runtime, but no headers, and we want to bail out as
 dnl soon as possible.
@@ -253,7 +257,7 @@ dnl can give the proper place to find libltdl through the standard
 dnl variables like LDFLAGS and CPPFLAGS.
 
 AC_CHECK_HEADER([ltdl.h],
-    [AC_CHECK_LIB([ltdl], [lt_dlopen], [LIBLTDL=-lltdl], [LIBLTDL=])],
+    [AC_CHECK_LIB([ltdl], [lt_dladvise_init], [LIBLTDL=-lltdl], [LIBLTDL=])],
     [LIBLTDL=])
 
 AS_IF([test "x$LIBLTDL" = "x"],

commit ddbe6126d365a77ed94aee0b961d7cd334ea2a9b
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 22 22:50:03 2009 +0100

    use pthread_setaffinity_np() only when it is available

diff --git a/configure.ac b/configure.ac
index 9912843..8e926f3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -370,7 +370,7 @@ AC_FUNC_SELECT_ARGTYPES
 AC_CHECK_FUNCS([chmod chown clock_gettime getaddrinfo getgrgid_r getgrnam_r \
     getpwnam_r getpwuid_r gettimeofday getuid inet_ntop inet_pton mlock nanosleep \
     pipe posix_fadvise posix_madvise posix_memalign setpgid setsid shm_open \
-    sigaction sleep sysconf])
+    sigaction sleep sysconf pthread_setaffinity_np])
 AC_CHECK_FUNCS([mkfifo], [HAVE_MKFIFO=1], [HAVE_MKFIFO=0])
 
 AM_CONDITIONAL(HAVE_MKFIFO, test "x$HAVE_MKFIFO" = "x1")
diff --git a/src/tests/rtstutter.c b/src/tests/rtstutter.c
index fc23d95..d8aff34 100644
--- a/src/tests/rtstutter.c
+++ b/src/tests/rtstutter.c
@@ -43,24 +43,28 @@ static int msec_lower, msec_upper;
 static void* work(void *p) PA_GCC_NORETURN;
 
 static void* work(void *p) {
+#ifdef HAVE_PTHREAD_SETAFFINITY_NP
     cpu_set_t mask;
+#endif
     struct sched_param param;
 
-    pa_log_notice("CPU%i: Created thread.", PA_PTR_TO_INT(p));
+    pa_log_notice("CPU%i: Created thread.", PA_PTR_TO_UINT(p));
 
     memset(&param, 0, sizeof(param));
     param.sched_priority = 12;
     pa_assert_se(pthread_setschedparam(pthread_self(), SCHED_FIFO, &param) == 0);
 
+#ifdef HAVE_PTHREAD_SETAFFINITY_NP
     CPU_ZERO(&mask);
-    CPU_SET((size_t) PA_PTR_TO_INT(p), &mask);
+    CPU_SET((size_t) PA_PTR_TO_UINT(p), &mask);
     pa_assert_se(pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) == 0);
+#endif
 
     for (;;) {
         struct timespec now, end;
         uint64_t nsec;
 
-        pa_log_notice("CPU%i: Sleeping for 1s", PA_PTR_TO_INT(p));
+        pa_log_notice("CPU%i: Sleeping for 1s", PA_PTR_TO_UINT(p));
         sleep(1);
 
         pa_assert_se(clock_gettime(CLOCK_REALTIME, &end) == 0);
@@ -69,7 +73,7 @@ static void* work(void *p) {
             (uint64_t) ((((double) rand())*(double)(msec_upper-msec_lower)*PA_NSEC_PER_MSEC)/RAND_MAX) +
             (uint64_t) ((uint64_t) msec_lower*PA_NSEC_PER_MSEC);
 
-        pa_log_notice("CPU%i: Freezing for %ims", PA_PTR_TO_INT(p), (int) (nsec/PA_NSEC_PER_MSEC));
+        pa_log_notice("CPU%i: Freezing for %ims", PA_PTR_TO_UINT(p), (int) (nsec/PA_NSEC_PER_MSEC));
 
         end.tv_sec += (time_t) (nsec / PA_NSEC_PER_SEC);
         end.tv_nsec += (long int) (nsec % PA_NSEC_PER_SEC);
@@ -87,7 +91,7 @@ static void* work(void *p) {
 }
 
 int main(int argc, char*argv[]) {
-    int n;
+    unsigned n;
 
     srand((unsigned) time(NULL));
 
@@ -109,7 +113,7 @@ int main(int argc, char*argv[]) {
 
     for (n = 1; n < pa_ncpus(); n++) {
         pthread_t t;
-        pa_assert_se(pthread_create(&t, NULL, work, PA_INT_TO_PTR(n)) == 0);
+        pa_assert_se(pthread_create(&t, NULL, work, PA_UINT_TO_PTR(n)) == 0);
     }
 
     work(PA_INT_TO_PTR(0));

commit 3e5d9fd37ab2ebe7160c7ebc4393b15ba68f28e8
Author: Diego E. 'Flameeyes' Pettenò <flameeyes at gmail.com>
Date:   Thu Jan 22 23:38:07 2009 +0100

    Use #ifdef to avoid warning about undefined macro.

diff --git a/src/modules/module-detect.c b/src/modules/module-detect.c
index 1616d47..9ed262d 100644
--- a/src/modules/module-detect.c
+++ b/src/modules/module-detect.c
@@ -239,7 +239,7 @@ int pa__init(pa_module*m) {
 #ifdef HAVE_ALSA
     if ((n = detect_alsa(m->core, just_one)) <= 0)
 #endif
-#if HAVE_OSS
+#ifdef HAVE_OSS
     if ((n = detect_oss(m->core, just_one)) <= 0)
 #endif
 #ifdef HAVE_SOLARIS
diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c
index 6b3836e..ff87284 100644
--- a/src/pulsecore/resampler.c
+++ b/src/pulsecore/resampler.c
@@ -25,7 +25,7 @@
 
 #include <string.h>
 
-#if HAVE_LIBSAMPLERATE
+#ifdef HAVE_LIBSAMPLERATE
 #include <samplerate.h>
 #endif
 

commit 2a238b2d7f000dfd2c7b0db82f557951f93f3809
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jan 23 19:40:01 2009 +0100

    don't overflow when we do digital amplification of 16 bit samples

diff --git a/src/pulsecore/sample-util.c b/src/pulsecore/sample-util.c
index cf7b4d5..905ba5d 100644
--- a/src/pulsecore/sample-util.c
+++ b/src/pulsecore/sample-util.c
@@ -213,13 +213,22 @@ size_t pa_mix(
 
                 for (i = 0; i < nstreams; i++) {
                     pa_mix_info *m = streams + i;
-                    int32_t v, cv = m->linear[channel].i;
+                    int32_t v, lo, hi, cv = m->linear[channel].i;
 
                     if (PA_UNLIKELY(cv <= 0))
                         continue;
 
+                    /* Multiplying the 32bit volume factor with the
+                     * 16bit sample might result in an 48bit value. We
+                     * want to do without 64 bit integers and hence do
+                     * the multiplication independantly for the HI and
+                     * LO part of the volume. */
+
+                    hi = cv >> 16;
+                    lo = cv & 0xFFFF;
+
                     v = *((int16_t*) m->ptr);
-                    v = (v * cv) / 0x10000;
+                    v = ((v * lo) >> 16) + (v * hi);
                     sum += v;
 
                     m->ptr = (uint8_t*) m->ptr + sizeof(int16_t);
@@ -248,13 +257,16 @@ size_t pa_mix(
 
                 for (i = 0; i < nstreams; i++) {
                     pa_mix_info *m = streams + i;
-                    int32_t v, cv = m->linear[channel].i;
+                    int32_t v, lo, hi, cv = m->linear[channel].i;
 
                     if (PA_UNLIKELY(cv <= 0))
                         continue;
 
+                    hi = cv >> 16;
+                    lo = cv & 0xFFFF;
+
                     v = PA_INT16_SWAP(*((int16_t*) m->ptr));
-                    v = (v * cv) / 0x10000;
+                    v = ((v * lo) >> 16) + (v * hi);
                     sum += v;
 
                     m->ptr = (uint8_t*) m->ptr + sizeof(int16_t);
@@ -290,7 +302,7 @@ size_t pa_mix(
                         continue;
 
                     v = *((int32_t*) m->ptr);
-                    v = (v * cv) / 0x10000;
+                    v = (v * cv) >> 16;
                     sum += v;
 
                     m->ptr = (uint8_t*) m->ptr + sizeof(int32_t);
@@ -326,7 +338,7 @@ size_t pa_mix(
                         continue;
 
                     v = PA_INT32_SWAP(*((int32_t*) m->ptr));
-                    v = (v * cv) / 0x10000;
+                    v = (v * cv) >> 16;
                     sum += v;
 
                     m->ptr = (uint8_t*) m->ptr + sizeof(int32_t);
@@ -362,7 +374,7 @@ size_t pa_mix(
                         continue;
 
                     v = (int32_t) (PA_READ24NE(m->ptr) << 8);
-                    v = (v * cv) / 0x10000;
+                    v = (v * cv) >> 16;
                     sum += v;
 
                     m->ptr = (uint8_t*) m->ptr + 3;
@@ -398,7 +410,7 @@ size_t pa_mix(
                         continue;
 
                     v = (int32_t) (PA_READ24RE(m->ptr) << 8);
-                    v = (v * cv) / 0x10000;
+                    v = (v * cv) >> 16;
                     sum += v;
 
                     m->ptr = (uint8_t*) m->ptr + 3;
@@ -434,7 +446,7 @@ size_t pa_mix(
                         continue;
 
                     v = (int32_t) (*((uint32_t*)m->ptr) << 8);
-                    v = (v * cv) / 0x10000;
+                    v = (v * cv) >> 16;
                     sum += v;
 
                     m->ptr = (uint8_t*) m->ptr + sizeof(int32_t);
@@ -470,7 +482,7 @@ size_t pa_mix(
                         continue;
 
                     v = (int32_t) (PA_UINT32_SWAP(*((uint32_t*) m->ptr)) << 8);
-                    v = (v * cv) / 0x10000;
+                    v = (v * cv) >> 16;
                     sum += v;
 
                     m->ptr = (uint8_t*) m->ptr + 3;
@@ -505,7 +517,7 @@ size_t pa_mix(
                         continue;
 
                     v = (int32_t) *((uint8_t*) m->ptr) - 0x80;
-                    v = (v * cv) / 0x10000;
+                    v = (v * cv) >> 16;
                     sum += v;
 
                     m->ptr = (uint8_t*) m->ptr + 1;
@@ -534,13 +546,16 @@ size_t pa_mix(
 
                 for (i = 0; i < nstreams; i++) {
                     pa_mix_info *m = streams + i;
-                    int32_t v, cv = m->linear[channel].i;
+                    int32_t v, hi, lo, cv = m->linear[channel].i;
 
                     if (PA_UNLIKELY(cv <= 0))
                         continue;
 
+                    hi = cv >> 16;
+                    lo = cv & 0xFFFF;
+
                     v = (int32_t) st_ulaw2linear16(*((uint8_t*) m->ptr));
-                    v = (v * cv) / 0x10000;
+                    v = ((v * lo) >> 16) + (v * hi);
                     sum += v;
 
                     m->ptr = (uint8_t*) m->ptr + 1;
@@ -569,13 +584,16 @@ size_t pa_mix(
 
                 for (i = 0; i < nstreams; i++) {
                     pa_mix_info *m = streams + i;
-                    int32_t v, cv = m->linear[channel].i;
+                    int32_t v, hi, lo, cv = m->linear[channel].i;
 
                     if (PA_UNLIKELY(cv <= 0))
                         continue;
 
+                    hi = cv >> 16;
+                    lo = cv & 0xFFFF;
+
                     v = (int32_t) st_alaw2linear16(*((uint8_t*) m->ptr));
-                    v = (v * cv) / 0x10000;
+                    v = ((v * lo) >> 16) + (v * hi);
                     sum += v;
 
                     m->ptr = (uint8_t*) m->ptr + 1;
@@ -710,16 +728,26 @@ void pa_volume_memchunk(
             e = (int16_t*) ptr + c->length/sizeof(int16_t);
 
             for (channel = 0, d = ptr; d < e; d++) {
-                int32_t t;
+                int32_t t, hi, lo;
+
+                /* Multiplying the 32bit volume factor with the 16bit
+                 * sample might result in an 48bit value. We want to
+                 * do without 64 bit integers and hence do the
+                 * multiplication independantly for the HI and LO part
+                 * of the volume. */
+
+                hi = linear[channel] >> 16;
+                lo = linear[channel] & 0xFFFF;
 
                 t = (int32_t)(*d);
-                t = (t * linear[channel]) / 0x10000;
+                t = ((t * lo) >> 16) + (t * hi);
                 t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
                 *d = (int16_t) t;
 
                 if (PA_UNLIKELY(++channel >= spec->channels))
                     channel = 0;
             }
+
             break;
         }
 
@@ -733,10 +761,13 @@ void pa_volume_memchunk(
             e = (int16_t*) ptr + c->length/sizeof(int16_t);
 
             for (channel = 0, d = ptr; d < e; d++) {
-                int32_t t;
+                int32_t t, hi, lo;
+
+                hi = linear[channel] >> 16;
+                lo = linear[channel] & 0xFFFF;
 
                 t = (int32_t) PA_INT16_SWAP(*d);
-                t = (t * linear[channel]) / 0x10000;
+                t = ((t * lo) >> 16) + (t * hi);
                 t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
                 *d = PA_INT16_SWAP((int16_t) t);
 
@@ -760,7 +791,7 @@ void pa_volume_memchunk(
                 int64_t t;
 
                 t = (int64_t)(*d);
-                t = (t * linear[channel]) / 0x10000;
+                t = (t * linear[channel]) >> 16;
                 t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
                 *d = (int32_t) t;
 
@@ -783,7 +814,7 @@ void pa_volume_memchunk(
                 int64_t t;
 
                 t = (int64_t) PA_INT32_SWAP(*d);
-                t = (t * linear[channel]) / 0x10000;
+                t = (t * linear[channel]) >> 16;
                 t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
                 *d = PA_INT32_SWAP((int32_t) t);
 
@@ -806,7 +837,7 @@ void pa_volume_memchunk(
                 int64_t t;
 
                 t = (int64_t)((int32_t) (PA_READ24NE(d) << 8));
-                t = (t * linear[channel]) / 0x10000;
+                t = (t * linear[channel]) >> 16;
                 t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
                 PA_WRITE24NE(d, ((uint32_t) (int32_t) t) >> 8);
 
@@ -829,7 +860,7 @@ void pa_volume_memchunk(
                 int64_t t;
 
                 t = (int64_t)((int32_t) (PA_READ24RE(d) << 8));
-                t = (t * linear[channel]) / 0x10000;
+                t = (t * linear[channel]) >> 16;
                 t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
                 PA_WRITE24RE(d, ((uint32_t) (int32_t) t) >> 8);
 
@@ -852,7 +883,7 @@ void pa_volume_memchunk(
                 int64_t t;
 
                 t = (int64_t) ((int32_t) (*d << 8));
-                t = (t * linear[channel]) / 0x10000;
+                t = (t * linear[channel]) >> 16;
                 t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
                 *d = ((uint32_t) ((int32_t) t)) >> 8;
 
@@ -875,7 +906,7 @@ void pa_volume_memchunk(
                 int64_t t;
 
                 t = (int64_t) ((int32_t) (PA_UINT32_SWAP(*d) << 8));
-                t = (t * linear[channel]) / 0x10000;
+                t = (t * linear[channel]) >> 16;
                 t = PA_CLAMP_UNLIKELY(t, -0x80000000LL, 0x7FFFFFFFLL);
                 *d = PA_UINT32_SWAP(((uint32_t) ((int32_t) t)) >> 8);
 
@@ -895,10 +926,13 @@ void pa_volume_memchunk(
             e = (uint8_t*) ptr + c->length;
 
             for (channel = 0, d = ptr; d < e; d++) {
-                int32_t t;
+                int32_t t, hi, lo;
+
+                hi = linear[channel] >> 16;
+                lo = linear[channel] & 0xFFFF;
 
                 t = (int32_t) *d - 0x80;
-                t = (t * linear[channel]) / 0x10000;
+                t = ((t * lo) >> 16) + (t * hi);
                 t = PA_CLAMP_UNLIKELY(t, -0x80, 0x7F);
                 *d = (uint8_t) (t + 0x80);
 
@@ -918,10 +952,13 @@ void pa_volume_memchunk(
             e = (uint8_t*) ptr + c->length;
 
             for (channel = 0, d = ptr; d < e; d++) {
-                int32_t t;
+                int32_t t, hi, lo;
+
+                hi = linear[channel] >> 16;
+                lo = linear[channel] & 0xFFFF;
 
                 t = (int32_t) st_ulaw2linear16(*d);
-                t = (t * linear[channel]) / 0x10000;
+                t = ((t * lo) >> 16) + (t * hi);
                 t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
                 *d = (uint8_t) st_14linear2ulaw((int16_t) t >> 2);
 
@@ -941,10 +978,13 @@ void pa_volume_memchunk(
             e = (uint8_t*) ptr + c->length;
 
             for (channel = 0, d = ptr; d < e; d++) {
-                int32_t t;
+                int32_t t, hi, lo;
+
+                hi = linear[channel] >> 16;
+                lo = linear[channel] & 0xFFFF;
 
                 t = (int32_t) st_alaw2linear16(*d);
-                t = (t * linear[channel]) / 0x10000;
+                t = ((t * lo) >> 16) + (t * hi);
                 t = PA_CLAMP_UNLIKELY(t, -0x8000, 0x7FFF);
                 *d = (uint8_t) st_13linear2alaw((int16_t) t >> 3);
 

commit 5cb29f3a606c8d5e66fea561b8a4ab1fe623a53d
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jan 23 19:45:11 2009 +0100

    add a simple abstraction for SIMD operations

diff --git a/src/.gitignore b/src/.gitignore
index 72c38cc..66738d0 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -58,3 +58,4 @@ thread-test
 utf8-test
 voltest
 start-pulseaudio-x11
+vector-test
diff --git a/src/Makefile.am b/src/Makefile.am
index 8d1271c..b59e50a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -233,6 +233,7 @@ TESTS = \
 		strlist-test \
 		close-test \
 		voltest \
+		vector-test \
 		memblockq-test \
 		channelmap-test \
 		thread-mainloop-test \
@@ -263,6 +264,7 @@ TESTS_BINARIES = \
 		strlist-test \
 		close-test \
 		voltest \
+		vector-test \
 		memblockq-test \
 		sync-playback \
 		interpol-test \
@@ -408,6 +410,11 @@ voltest_CFLAGS = $(AM_CFLAGS)
 voltest_LDADD = $(AM_LDADD) libpulse.la
 voltest_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
 
+vector_test_SOURCES = tests/vector-test.c
+vector_test_CFLAGS = $(AM_CFLAGS)
+vector_test_LDADD = $(AM_LDADD) libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la
+vector_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
 channelmap_test_SOURCES = tests/channelmap-test.c
 channelmap_test_CFLAGS = $(AM_CFLAGS)
 channelmap_test_LDADD = $(AM_LDADD) libpulse.la
@@ -527,7 +534,7 @@ libpulsecommon_ at PA_MAJORMINORMICRO@_la_SOURCES = \
 		pulsecore/llist.h \
 		pulsecore/lock-autospawn.c pulsecore/lock-autospawn.h \
 		pulsecore/log.c pulsecore/log.h \
-		pulsecore/macro.h \
+		pulsecore/macro.h pulsecore/vector.h \
 		pulsecore/mcalign.c pulsecore/mcalign.h \
 		pulsecore/memblock.c pulsecore/memblock.h \
 		pulsecore/memblockq.c pulsecore/memblockq.h \
diff --git a/src/pulsecore/vector.h b/src/pulsecore/vector.h
new file mode 100644
index 0000000..076bd6c
--- /dev/null
+++ b/src/pulsecore/vector.h
@@ -0,0 +1,97 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2004-2006 Lennart Poettering
+  Copyright 2006 Pierre Ossman <ossman at cendio.se> for Cendio AB
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <inttypes.h>
+
+/* First, define HAVE_VECTOR if we have the gcc vector extensions at all */
+#if defined(__SSE2__) || defined(__ALTIVEC__)
+#define HAVE_VECTOR
+
+
+/* This is supposed to be portable to different SIMD instruction
+ * sets. We define vector types for different base types: uint8_t,
+ * int16_t, int32_t, float. The vector type is a union. The fields .i,
+ * .u, .f are arrays for accessing the separate elements of a
+ * vector. .v is a gcc vector type of the right format. .m is the
+ * vector in the type the SIMD extenstion specific intrinsics API
+ * expects. PA_xxx_VECTOR_SIZE is the size of the
+ * entries. PA_xxxx_VECTOR_MAKE constructs a gcc vector variable with
+ * the same value in all elements. */
+
+#ifdef __SSE2__
+
+#include <xmmintrin.h>
+#include <emmintrin.h>
+
+#define PA_UINT8_VECTOR_SIZE 16
+#define PA_INT16_VECTOR_SIZE 8
+#define PA_INT32_VECTOR_SIZE 4
+#define PA_FLOAT_VECTOR_SIZE 4
+
+#define PA_UINT8_VECTOR_MAKE(x) (pa_v16qi) { x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x }
+#define PA_INT16_VECTOR_MAKE(x) (pa_v8hi) { x, x, x, x, x, x, x, x }
+#define PA_INT32_VECTOR_MAKE(x) (pa_v4si) { x, x, x, x }
+#define PA_FLOAT_VECTOR_MAKE(x) (pa_v4fi) { x, x, x, x }
+
+#endif
+
+/* uint8_t vector */
+typedef uint8_t pa_v16qi __attribute__ ((vector_size (PA_UINT8_VECTOR_SIZE * sizeof(uint8_t))));
+typedef union pa_uint8_vector {
+    uint8_t u[PA_UINT8_VECTOR_SIZE];
+    pa_v16qi v;
+#ifdef __SSE2__
+    __m128i m;
+#endif
+} pa_uint8_vector_t;
+
+/* int16_t vector*/
+typedef int16_t pa_v8hi __attribute__ ((vector_size (PA_INT16_VECTOR_SIZE * sizeof(int16_t))));
+typedef union pa_int16_vector {
+    int16_t i[PA_INT16_VECTOR_SIZE];
+    pa_v8hi v;
+#ifdef __SSE2__
+    __m128i m;
+#endif
+} pa_int16_vector_t;
+
+/* int32_t vector */
+typedef int32_t pa_v4si __attribute__ ((vector_size (PA_INT32_VECTOR_SIZE * sizeof(int32_t))));
+typedef union pa_int32_vector {
+    int32_t i[PA_INT32_VECTOR_SIZE];
+    pa_v4si v;
+#ifdef __SSE2__
+    __m128i m;
+#endif
+} pa_int32_vector_t;
+
+/* float vector */
+typedef float pa_v4sf __attribute__ ((vector_size (PA_FLOAT_VECTOR_SIZE * sizeof(float))));
+typedef union pa_float_vector {
+    float f[PA_FLOAT_VECTOR_SIZE];
+    pa_v4sf v;
+#ifdef __SSE2__
+    __m128 m;
+#endif
+} pa_float_vector_t;
+
+#endif
diff --git a/src/tests/vector-test.c b/src/tests/vector-test.c
new file mode 100644
index 0000000..f734417
--- /dev/null
+++ b/src/tests/vector-test.c
@@ -0,0 +1,83 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 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 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/vector.h>
+#include <pulsecore/log.h>
+
+int main(int argc, char *argv[]) {
+
+#ifdef __SSE2__
+    pa_int16_vector_t input, zero;
+    pa_int32_vector_t unpacked1, unpacked2;
+    pa_int32_vector_t volume1, volume2, volume1_hi, volume1_lo, volume2_hi, volume2_lo, reduce, mask;
+    pa_int16_vector_t output;
+
+    unsigned u;
+
+    zero.v = PA_INT16_VECTOR_MAKE(0);
+    reduce.v = PA_INT32_VECTOR_MAKE(0x10000);
+    volume1.v = volume2.v = PA_INT32_VECTOR_MAKE(0x10000*2+7);
+    mask.v = PA_INT32_VECTOR_MAKE(0xFFFF);
+
+    volume1_lo.m = _mm_and_si128(volume1.m, mask.m);
+    volume2_lo.m = _mm_and_si128(volume2.m, mask.m);
+    volume1_hi.m = _mm_srli_epi32(volume1.m, 16);
+    volume2_hi.m = _mm_srli_epi32(volume2.m, 16);
+
+    input.v = PA_INT16_VECTOR_MAKE(32000);
+
+    for (u = 0; u < PA_INT16_VECTOR_SIZE; u++)
+        pa_log("input=%i\n", input.i[u]);
+
+    unpacked1.m = _mm_unpackhi_epi16(zero.m, input.m);
+    unpacked2.m = _mm_unpacklo_epi16(zero.m, input.m);
+
+    for (u = 0; u < PA_INT32_VECTOR_SIZE; u++)
+        pa_log("unpacked1=%i\n", unpacked1.i[u]);
+
+    unpacked1.v /= reduce.v;
+    unpacked2.v /= reduce.v;
+
+    for (u = 0; u < PA_INT32_VECTOR_SIZE; u++)
+        pa_log("unpacked1=%i\n", unpacked1.i[u]);
+
+    for (u = 0; u < PA_INT32_VECTOR_SIZE; u++)
+        pa_log("volume1=%i\n", volume1.i[u]);
+
+    unpacked1.v = (unpacked1.v * volume1_lo.v) / reduce.v + unpacked1.v * volume1_hi.v;
+    unpacked2.v = (unpacked2.v * volume2_lo.v) / reduce.v + unpacked2.v * volume2_hi.v;
+
+    for (u = 0; u < PA_INT32_VECTOR_SIZE; u++)
+        pa_log("unpacked1=%i\n", unpacked1.i[u]);
+
+    output.m = _mm_packs_epi32(unpacked1.m, unpacked2.m);
+
+    for (u = 0; u < PA_INT16_VECTOR_SIZE; u++)
+        pa_log("output=%i\n", output.i[u]);
+
+#endif
+
+    return 0;
+}

commit f6fcbed6d0fd04308341fa27465284212143824b
Merge: 5cb29f3... 3e5d9fd...
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jan 23 19:46:52 2009 +0100

    Merge commit 'flameeyes/flameeyes'


commit 7bdbcd0da82104e678d66ba68b27c7ce946712a8
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jan 23 19:50:47 2009 +0100

    drop --ltdl from the libtoolize invocation, since we don't ship ltdl anymore

diff --git a/bootstrap.sh b/bootstrap.sh
index 4ca2b33..e64f342 100755
--- a/bootstrap.sh
+++ b/bootstrap.sh
@@ -66,7 +66,7 @@ else
     test "x$LIBTOOLIZE" = "x" && LIBTOOLIZE=libtoolize
 
     intltoolize --copy --force --automake
-    "$LIBTOOLIZE" -c --force --ltdl --recursive
+    "$LIBTOOLIZE" -c --force
     run_versioned aclocal "$VERSION" -I m4
     run_versioned autoconf 2.63 -Wall
     run_versioned autoheader 2.63

commit 88c9f9fba631d30ba7dbca38b2aca3abe3bd4ac6
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jan 23 22:28:11 2009 +0100

    allow sample spec/channel map to be queried for pa_resampler objects

diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c
index ff87284..be390db 100644
--- a/src/pulsecore/resampler.c
+++ b/src/pulsecore/resampler.c
@@ -403,6 +403,30 @@ pa_resample_method_t pa_resampler_get_method(pa_resampler *r) {
     return r->method;
 }
 
+const pa_channel_map* pa_resampler_input_channel_map(pa_resampler *r) {
+    pa_assert(r);
+
+    return &r->i_cm;
+}
+
+const pa_sample_spec* pa_resampler_input_sample_spec(pa_resampler *r) {
+    pa_assert(r);
+
+    return &r->i_ss;
+}
+
+const pa_channel_map* pa_resampler_output_channel_map(pa_resampler *r) {
+    pa_assert(r);
+
+    return &r->o_cm;
+}
+
+const pa_sample_spec* pa_resampler_output_sample_spec(pa_resampler *r) {
+    pa_assert(r);
+
+    return &r->o_ss;
+}
+
 static const char * const resample_methods[] = {
     "src-sinc-best-quality",
     "src-sinc-medium-quality",
diff --git a/src/pulsecore/resampler.h b/src/pulsecore/resampler.h
index 87110cc..54dfa55 100644
--- a/src/pulsecore/resampler.h
+++ b/src/pulsecore/resampler.h
@@ -99,5 +99,9 @@ const char *pa_resample_method_to_string(pa_resample_method_t m);
 /* Return 1 when the specified resampling method is supported */
 int pa_resample_method_supported(pa_resample_method_t m);
 
+const pa_channel_map* pa_resampler_input_channel_map(pa_resampler *r);
+const pa_sample_spec* pa_resampler_input_sample_spec(pa_resampler *r);
+const pa_channel_map* pa_resampler_output_channel_map(pa_resampler *r);
+const pa_sample_spec* pa_resampler_output_sample_spec(pa_resampler *r);
 
 #endif

commit a3162a396e2344b9e48fe27e406e5d92ba94af9b
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jan 23 22:29:02 2009 +0100

    maintain a pa_core state variable

diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c
index 0b78bc4..c6e96c1 100644
--- a/src/pulsecore/core.c
+++ b/src/pulsecore/core.c
@@ -90,6 +90,7 @@ pa_core* pa_core_new(pa_mainloop_api *m, pa_bool_t shared, size_t shm_size) {
     c->parent.parent.free = core_free;
     c->parent.process_msg = core_process_msg;
 
+    c->state = PA_CORE_STARTUP;
     c->mainloop = m;
     c->clients = pa_idxset_new(NULL, NULL);
     c->sinks = pa_idxset_new(NULL, NULL);
@@ -149,6 +150,8 @@ pa_core* pa_core_new(pa_mainloop_api *m, pa_bool_t shared, size_t shm_size) {
 
     pa_core_check_idle(c);
 
+    c->state = PA_CORE_RUNNING;
+
     return c;
 }
 
@@ -157,6 +160,8 @@ static void core_free(pa_object *o) {
     int j;
     pa_assert(c);
 
+    c->state = PA_CORE_SHUTDOWN;
+
     pa_module_unload_all(c);
     pa_assert(!c->modules);
 
diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index 9f463d6..a91d752 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -41,6 +41,12 @@ typedef struct pa_core pa_core;
 #include <pulsecore/sink-input.h>
 #include <pulsecore/msgobject.h>
 
+typedef enum pa_core_state {
+    PA_CORE_STARTUP,
+    PA_CORE_RUNNING,
+    PA_CORE_SHUTDOWN
+} pa_core_state_t;
+
 typedef enum pa_core_hook {
     PA_CORE_HOOK_SINK_NEW,
     PA_CORE_HOOK_SINK_FIXATE,
@@ -92,6 +98,8 @@ typedef enum pa_core_hook {
 struct pa_core {
     pa_msgobject parent;
 
+    pa_core_state_t state;
+
     /* A random value which may be used to identify this instance of
      * PulseAudio. Not cryptographically secure in any way. */
     uint32_t cookie;

commit 967c17a1900b7547e471c9f1399107fc8287fcdc
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jan 23 22:30:02 2009 +0100

    teach module-rescue-streams and module-always-sink to not do anything if we are shutting down anyway

diff --git a/src/modules/module-always-sink.c b/src/modules/module-always-sink.c
index cd3f311..591695f 100644
--- a/src/modules/module-always-sink.c
+++ b/src/modules/module-always-sink.c
@@ -100,6 +100,10 @@ static pa_hook_result_t put_hook_callback(pa_core *c, pa_sink *sink, void* userd
     if (u->ignore)
         return PA_HOOK_OK;
 
+    /* There's no point in doing anything if the core is shut down anyway */
+    if (c->state == PA_CORE_SHUTDOWN)
+        return PA_HOOK_OK;
+
     /* Auto-loaded null-sink not active, so ignoring newly detected sink. */
     if (u->null_module == PA_INVALID_INDEX)
         return PA_HOOK_OK;
@@ -130,6 +134,10 @@ static pa_hook_result_t unlink_hook_callback(pa_core *c, pa_sink *sink, void* us
         return PA_HOOK_OK;
     }
 
+    /* There's no point in doing anything if the core is shut down anyway */
+    if (c->state == PA_CORE_SHUTDOWN)
+        return PA_HOOK_OK;
+
     load_null_sink_if_needed(c, sink, u);
 
     return PA_HOOK_OK;
@@ -172,7 +180,7 @@ void pa__done(pa_module*m) {
         pa_hook_slot_free(u->put_slot);
     if (u->unlink_slot)
         pa_hook_slot_free(u->unlink_slot);
-    if (u->null_module != PA_INVALID_INDEX)
+    if (u->null_module != PA_INVALID_INDEX && m->core->state != PA_CORE_SHUTDOWN)
         pa_module_unload_request_by_index(m->core, u->null_module, TRUE);
 
     pa_xfree(u->sink_name);
diff --git a/src/modules/module-rescue-streams.c b/src/modules/module-rescue-streams.c
index de07225..07a0237 100644
--- a/src/modules/module-rescue-streams.c
+++ b/src/modules/module-rescue-streams.c
@@ -54,6 +54,10 @@ static pa_hook_result_t sink_hook_callback(pa_core *c, pa_sink *sink, void* user
     pa_assert(c);
     pa_assert(sink);
 
+    /* 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 (!pa_idxset_size(sink->inputs)) {
         pa_log_debug("No sink inputs to move away.");
         return PA_HOOK_OK;
@@ -92,6 +96,10 @@ static pa_hook_result_t source_hook_callback(pa_core *c, pa_source *source, void
     pa_assert(c);
     pa_assert(source);
 
+    /* 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 (!pa_idxset_size(source->outputs)) {
         pa_log_debug("No source outputs to move away.");
         return PA_HOOK_OK;

commit d5e088ded7a6d9cf19b3dbc88ff2b73601896202
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jan 23 22:30:31 2009 +0100

    include list of sinks/source in card dump

diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index 947598b..1a3778a 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -117,6 +117,10 @@ char *pa_card_list_to_string(pa_core *c) {
 
     for (card = pa_idxset_first(c->cards, &idx); card; card = pa_idxset_next(c->cards, &idx)) {
         char *t;
+        pa_sink *sink;
+        pa_source *source;
+        uint32_t sidx;
+
         pa_strbuf_printf(
                 s,
                 "    index: %u\n"
@@ -137,9 +141,7 @@ char *pa_card_list_to_string(pa_core *c) {
             pa_card_profile *p;
             void *state = NULL;
 
-            pa_strbuf_puts(
-                    s,
-                    "\tprofiles:\n");
+            pa_strbuf_puts(s, "\tprofiles:\n");
 
             while ((p = pa_hashmap_iterate(card->profiles, &state, NULL)))
                 pa_strbuf_printf(s, "\t\t%s: %s (priority %u)\n", p->name, p->description, p->priority);
@@ -150,6 +152,18 @@ char *pa_card_list_to_string(pa_core *c) {
                     s,
                     "\tactive profile: <%s>\n",
                     card->active_profile->name);
+
+        if (!pa_idxset_isempty(card->sinks)) {
+            pa_strbuf_puts(s, "\tsinks:\n");
+            for (sink = pa_idxset_first(card->sinks, &sidx); sink; sink = pa_idxset_next(card->sinks, &sidx))
+                pa_strbuf_printf(s, "\t\t%s/#%u: %s\n", sink->name, sink->index, pa_strna(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)));
+        }
+
+        if (!pa_idxset_isempty(card->sources)) {
+            pa_strbuf_puts(s, "\tsources:\n");
+            for (source = pa_idxset_first(card->sources, &sidx); source; source = pa_idxset_next(card->sources, &sidx))
+                pa_strbuf_printf(s, "\t\t%s/#%u: %s\n", source->name, source->index, pa_strna(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)));
+        }
     }
 
     return pa_strbuf_tostring_free(s);

commit cf24b57279f17c41843687301bb5882fcc8aa5b3
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jan 23 22:35:19 2009 +0100

    in most cases we can use i->core instead of i->sink->core and o->coure instead of o->source->core

diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 5667114..0323bb8 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -338,13 +338,13 @@ static int sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state) {
     }
 
     if (state != PA_SINK_INPUT_UNLINKED) {
-        pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], i);
+        pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], i);
 
         for (ssync = i->sync_prev; ssync; ssync = ssync->sync_prev)
-            pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], ssync);
+            pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], ssync);
 
         for (ssync = i->sync_next; ssync; ssync = ssync->sync_next)
-            pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], ssync);
+            pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], ssync);
     }
 
     pa_sink_update_status(i->sink);
@@ -366,7 +366,7 @@ void pa_sink_input_unlink(pa_sink_input *i) {
     linked = PA_SINK_INPUT_IS_LINKED(i->state);
 
     if (linked)
-        pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], i);
+        pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], i);
 
     if (i->sync_prev)
         i->sync_prev->sync_next = i->sync_next;
@@ -398,8 +398,8 @@ void pa_sink_input_unlink(pa_sink_input *i) {
     reset_callbacks(i);
 
     if (linked) {
-        pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_REMOVE, i->index);
-        pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK_POST], i);
+        pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_REMOVE, i->index);
+        pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK_POST], i);
     }
 
     pa_sink_update_status(i->sink);
@@ -463,8 +463,8 @@ void pa_sink_input_put(pa_sink_input *i) {
 
     pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_ADD_INPUT, i, 0, NULL) == 0);
 
-    pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, i->index);
-    pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], i);
+    pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, i->index);
+    pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], i);
 
     pa_sink_update_status(i->sink);
 }
@@ -519,9 +519,9 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa
 
     block_size_max_sink_input = i->thread_info.resampler ?
         pa_resampler_max_block_size(i->thread_info.resampler) :
-        pa_frame_align(pa_mempool_block_size_max(i->sink->core->mempool), &i->sample_spec);
+        pa_frame_align(pa_mempool_block_size_max(i->core->mempool), &i->sample_spec);
 
-    block_size_max_sink = pa_frame_align(pa_mempool_block_size_max(i->sink->core->mempool), &i->sink->sample_spec);
+    block_size_max_sink = pa_frame_align(pa_mempool_block_size_max(i->core->mempool), &i->sink->sample_spec);
 
     /* Default buffer size */
     if (slength <= 0)
@@ -836,7 +836,7 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume) {
 
     if (!pa_cvolume_equal(&i->virtual_volume, &data.virtual_volume)) {
         i->virtual_volume = data.virtual_volume;
-        pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+        pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
     }
 }
 
@@ -860,7 +860,7 @@ void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute) {
     i->muted = mute;
 
     pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_MUTE, PA_UINT_TO_PTR(mute), 0, NULL, NULL);
-    pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+    pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
 }
 
 /* Called from main context */
@@ -879,8 +879,8 @@ pa_bool_t pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode,
   pa_proplist_update(i->proplist, mode, p);
 
   if (PA_SINK_IS_LINKED(i->state)) {
-      pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], i);
-      pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+      pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], i);
+      pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
   }
 
   return TRUE;
@@ -907,7 +907,7 @@ int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate) {
 
     pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_RATE, PA_UINT_TO_PTR(rate), 0, NULL, NULL);
 
-    pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+    pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
     return 0;
 }
 
@@ -930,8 +930,8 @@ void pa_sink_input_set_name(pa_sink_input *i, const char *name) {
         pa_proplist_unset(i->proplist, PA_PROP_MEDIA_NAME);
 
     if (PA_SINK_INPUT_IS_LINKED(i->state)) {
-        pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], i);
-        pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+        pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], i);
+        pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
     }
 }
 
@@ -1011,7 +1011,7 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest) {
         /* Okey, we need a new resampler for the new sink */
 
         if (!(new_resampler = pa_resampler_new(
-                      dest->core->mempool,
+                      i->core->mempool,
                       &i->sample_spec, &i->channel_map,
                       &dest->sample_spec, &dest->channel_map,
                       i->resample_method,
@@ -1292,8 +1292,8 @@ pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret) {
     pa_assert(ret);
 
     pa_silence_memchunk_get(
-                &i->sink->core->silence_cache,
-                i->sink->core->mempool,
+                &i->core->silence_cache,
+                i->core->mempool,
                 ret,
                 &i->sample_spec,
                 i->thread_info.resampler ? pa_resampler_max_block_size(i->thread_info.resampler) : 0);
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index cb8ba28..c832dad 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -269,7 +269,7 @@ static int source_output_set_state(pa_source_output *o, pa_source_output_state_t
     o->state = state;
 
     if (state != PA_SOURCE_OUTPUT_UNLINKED)
-        pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], o);
+        pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], o);
 
     pa_source_update_status(o->source);
 
@@ -289,7 +289,7 @@ void pa_source_output_unlink(pa_source_output*o) {
     linked = PA_SOURCE_OUTPUT_IS_LINKED(o->state);
 
     if (linked)
-        pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], o);
+        pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], o);
 
     if (o->direct_on_input)
         pa_idxset_remove_by_data(o->direct_on_input->direct_outputs, o, NULL);
@@ -310,8 +310,8 @@ void pa_source_output_unlink(pa_source_output*o) {
     reset_callbacks(o);
 
     if (linked) {
-        pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_REMOVE, o->index);
-        pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST], o);
+        pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_REMOVE, o->index);
+        pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST], o);
     }
 
     pa_source_update_status(o->source);
@@ -365,8 +365,8 @@ void pa_source_output_put(pa_source_output *o) {
 
     pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL) == 0);
 
-    pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, o->index);
-    pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], o);
+    pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, o->index);
+    pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], o);
 
     pa_source_update_status(o->source);
 }
@@ -574,7 +574,7 @@ int pa_source_output_set_rate(pa_source_output *o, uint32_t rate) {
 
     pa_asyncmsgq_post(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_RATE, PA_UINT_TO_PTR(rate), 0, NULL, NULL);
 
-    pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+    pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
     return 0;
 }
 
@@ -597,8 +597,8 @@ void pa_source_output_set_name(pa_source_output *o, const char *name) {
         pa_proplist_unset(o->proplist, PA_PROP_MEDIA_NAME);
 
     if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) {
-        pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o);
-        pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+        pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o);
+        pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
     }
 }
 
@@ -610,8 +610,8 @@ pa_bool_t pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t
   pa_proplist_update(o->proplist, mode, p);
 
   if (PA_SINK_IS_LINKED(o->state)) {
-    pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o);
-    pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+    pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o);
+    pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
   }
 
   return TRUE;
@@ -682,7 +682,7 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
         /* Okey, we need a new resampler for the new source */
 
         if (!(new_resampler = pa_resampler_new(
-                      dest->core->mempool,
+                      o->core->mempool,
                       &dest->sample_spec, &dest->channel_map,
                       &o->sample_spec, &o->channel_map,
                       o->resample_method,

commit 29cb778dcc3ceff2bb16520a16388cc21cd32884
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jan 23 22:38:30 2009 +0100

    move sink input/source output move functions into two parts so that we can start the move, delete the original sink, create a new sink, finish the move; similar for source outputs

diff --git a/src/modules/module-suspend-on-idle.c b/src/modules/module-suspend-on-idle.c
index 8ab84e0..5e5e53e 100644
--- a/src/modules/module-suspend-on-idle.c
+++ b/src/modules/module-suspend-on-idle.c
@@ -62,8 +62,10 @@ struct userdata {
         *source_output_new_slot,
         *sink_input_unlink_slot,
         *source_output_unlink_slot,
-        *sink_input_move_slot,
-        *source_output_move_slot,
+        *sink_input_move_start_slot,
+        *source_output_move_start_slot,
+        *sink_input_move_finish_slot,
+        *source_output_move_finish_slot,
         *sink_input_state_changed_slot,
         *source_output_state_changed_slot;
 };
@@ -181,40 +183,60 @@ static pa_hook_result_t source_output_unlink_hook_cb(pa_core *c, pa_source_outpu
     return PA_HOOK_OK;
 }
 
-static pa_hook_result_t sink_input_move_hook_cb(pa_core *c, pa_sink_input_move_hook_data *data, struct userdata *u) {
+static pa_hook_result_t sink_input_move_start_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) {
     struct device_info *d;
 
     pa_assert(c);
-    pa_assert(data);
+    pa_sink_input_assert_ref(s);
     pa_assert(u);
 
-    if ((d = pa_hashmap_get(u->device_infos, data->destination)))
-        resume(d);
-
-    if (pa_sink_check_suspend(data->sink_input->sink) <= 1)
-        if ((d = pa_hashmap_get(u->device_infos, data->sink_input->sink)))
+    if (pa_sink_check_suspend(s->sink) <= 1)
+        if ((d = pa_hashmap_get(u->device_infos, s->sink)))
             restart(d);
 
     return PA_HOOK_OK;
 }
 
-static pa_hook_result_t source_output_move_hook_cb(pa_core *c, pa_source_output_move_hook_data *data, struct userdata *u) {
+static pa_hook_result_t sink_input_move_finish_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) {
     struct device_info *d;
 
     pa_assert(c);
-    pa_assert(data);
+    pa_sink_input_assert_ref(s);
     pa_assert(u);
 
-    if ((d = pa_hashmap_get(u->device_infos, data->destination)))
+    if ((d = pa_hashmap_get(u->device_infos, s->sink)))
         resume(d);
 
-    if (pa_source_check_suspend(data->source_output->source) <= 1)
-        if ((d = pa_hashmap_get(u->device_infos, data->source_output->source)))
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_move_start_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) {
+    struct device_info *d;
+
+    pa_assert(c);
+    pa_source_output_assert_ref(s);
+    pa_assert(u);
+
+    if (pa_source_check_suspend(s->source) <= 1)
+        if ((d = pa_hashmap_get(u->device_infos, s->source)))
             restart(d);
 
     return PA_HOOK_OK;
 }
 
+static pa_hook_result_t source_output_move_finish_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) {
+    struct device_info *d;
+
+    pa_assert(c);
+    pa_source_output_assert_ref(s);
+    pa_assert(u);
+
+    if ((d = pa_hashmap_get(u->device_infos, s->source)))
+        resume(d);
+
+    return PA_HOOK_OK;
+}
+
 static pa_hook_result_t sink_input_state_changed_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) {
     struct device_info *d;
     pa_sink_input_state_t state;
@@ -376,8 +398,10 @@ int pa__init(pa_module*m) {
     u->source_output_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_fixate_hook_cb, u);
     u->sink_input_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK_POST], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_unlink_hook_cb, u);
     u->source_output_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_unlink_hook_cb, u);
-    u->sink_input_move_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_move_hook_cb, u);
-    u->source_output_move_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_move_hook_cb, u);
+    u->sink_input_move_start_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_START], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_move_start_hook_cb, u);
+    u->source_output_move_start_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_START], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_move_start_hook_cb, u);
+    u->sink_input_move_finish_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_move_finish_hook_cb, u);
+    u->source_output_move_finish_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_move_finish_hook_cb, u);
     u->sink_input_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_state_changed_hook_cb, u);
     u->source_output_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_state_changed_hook_cb, u);
 
@@ -421,8 +445,10 @@ void pa__done(pa_module*m) {
         pa_hook_slot_free(u->sink_input_new_slot);
     if (u->sink_input_unlink_slot)
         pa_hook_slot_free(u->sink_input_unlink_slot);
-    if (u->sink_input_move_slot)
-        pa_hook_slot_free(u->sink_input_move_slot);
+    if (u->sink_input_move_start_slot)
+        pa_hook_slot_free(u->sink_input_move_start_slot);
+    if (u->sink_input_move_finish_slot)
+        pa_hook_slot_free(u->sink_input_move_finish_slot);
     if (u->sink_input_state_changed_slot)
         pa_hook_slot_free(u->sink_input_state_changed_slot);
 
@@ -430,8 +456,10 @@ void pa__done(pa_module*m) {
         pa_hook_slot_free(u->source_output_new_slot);
     if (u->source_output_unlink_slot)
         pa_hook_slot_free(u->source_output_unlink_slot);
-    if (u->source_output_move_slot)
-        pa_hook_slot_free(u->source_output_move_slot);
+    if (u->source_output_move_start_slot)
+        pa_hook_slot_free(u->source_output_move_start_slot);
+    if (u->source_output_move_finish_slot)
+        pa_hook_slot_free(u->source_output_move_finish_slot);
     if (u->source_output_state_changed_slot)
         pa_hook_slot_free(u->source_output_state_changed_slot);
 
diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index a91d752..ffac6d3 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -68,8 +68,8 @@ typedef enum pa_core_hook {
     PA_CORE_HOOK_SINK_INPUT_PUT,
     PA_CORE_HOOK_SINK_INPUT_UNLINK,
     PA_CORE_HOOK_SINK_INPUT_UNLINK_POST,
-    PA_CORE_HOOK_SINK_INPUT_MOVE,
-    PA_CORE_HOOK_SINK_INPUT_MOVE_POST,
+    PA_CORE_HOOK_SINK_INPUT_MOVE_START,
+    PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH,
     PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED,
     PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED,
     PA_CORE_HOOK_SINK_INPUT_SET_VOLUME,
@@ -78,8 +78,8 @@ typedef enum pa_core_hook {
     PA_CORE_HOOK_SOURCE_OUTPUT_PUT,
     PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK,
     PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST,
-    PA_CORE_HOOK_SOURCE_OUTPUT_MOVE,
-    PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_POST,
+    PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_START,
+    PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH,
     PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED,
     PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED,
     PA_CORE_HOOK_CLIENT_NEW,
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 0323bb8..e25e0c2 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -306,6 +306,9 @@ pa_sink_input* pa_sink_input_new(
 static void update_n_corked(pa_sink_input *i, pa_sink_input_state_t state) {
     pa_assert(i);
 
+    if (!i->sink)
+        return;
+
     if (i->state == PA_SINK_INPUT_CORKED && state != PA_SINK_INPUT_CORKED)
         pa_assert_se(i->sink->n_corked -- >= 1);
     else if (i->state != PA_SINK_INPUT_CORKED && state == PA_SINK_INPUT_CORKED)
@@ -375,9 +378,11 @@ void pa_sink_input_unlink(pa_sink_input *i) {
 
     i->sync_prev = i->sync_next = NULL;
 
-    pa_idxset_remove_by_data(i->sink->core->sink_inputs, i, NULL);
-    if (pa_idxset_remove_by_data(i->sink->inputs, i, NULL))
-        pa_sink_input_unref(i);
+    pa_idxset_remove_by_data(i->core->sink_inputs, i, NULL);
+
+    if (i->sink)
+        if (pa_idxset_remove_by_data(i->sink->inputs, i, NULL))
+            pa_sink_input_unref(i);
 
     if (i->client)
         pa_idxset_remove_by_data(i->client->sink_inputs, i, NULL);
@@ -391,7 +396,7 @@ void pa_sink_input_unlink(pa_sink_input *i) {
     update_n_corked(i, PA_SINK_INPUT_UNLINKED);
     i->state = PA_SINK_INPUT_UNLINKED;
 
-    if (linked)
+    if (linked && i->sink)
         if (i->sink->asyncmsgq)
             pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_REMOVE_INPUT, i, 0, NULL) == 0);
 
@@ -402,9 +407,11 @@ void pa_sink_input_unlink(pa_sink_input *i) {
         pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK_POST], i);
     }
 
-    pa_sink_update_status(i->sink);
+    if (i->sink) {
+        pa_sink_update_status(i->sink);
+        i->sink = NULL;
+    }
 
-    i->sink = NULL;
     pa_sink_input_unref(i);
 }
 
@@ -943,13 +950,9 @@ pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i) {
 }
 
 /* Called from main context */
-pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) {
+pa_bool_t pa_sink_input_may_move(pa_sink_input *i) {
     pa_sink_input_assert_ref(i);
     pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
-    pa_sink_assert_ref(dest);
-
-    if (dest == i->sink)
-        return TRUE;
 
     if (i->flags & PA_SINK_INPUT_DONT_MOVE)
         return FALSE;
@@ -959,6 +962,21 @@ pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) {
         return FALSE;
     }
 
+    return TRUE;
+}
+
+/* Called from main context */
+pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) {
+    pa_sink_input_assert_ref(i);
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+    pa_sink_assert_ref(dest);
+
+    if (dest == i->sink)
+        return TRUE;
+
+    if (!pa_sink_input_may_move(i))
+        return FALSE;
+
     if (pa_idxset_size(dest->inputs) >= PA_MAX_INPUTS_PER_SINK) {
         pa_log_warn("Failed to move sink input: too many inputs per sink.");
         return FALSE;
@@ -972,34 +990,64 @@ pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) {
 }
 
 /* Called from main context */
-int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest) {
-    pa_resampler *new_resampler;
-    pa_sink *origin;
-    pa_sink_input_move_hook_data hook_data;
+int pa_sink_input_start_move(pa_sink_input *i) {
     pa_source_output *o, *p = NULL;
+    pa_sink *origin;
 
     pa_sink_input_assert_ref(i);
     pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
-    pa_sink_assert_ref(dest);
-
-    origin = i->sink;
+    pa_assert(i->sink);
 
-    if (dest == origin)
-        return 0;
+    if (!pa_sink_input_may_move(i))
+        return -1;
 
-    if (!pa_sink_input_may_move_to(i, dest))
+    if (pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_START], i) < 0)
         return -1;
 
+    origin = i->sink;
+
     /* Kill directly connected outputs */
     while ((o = pa_idxset_first(i->direct_outputs, NULL))) {
         pa_assert(o != p);
         pa_source_output_kill(o);
         p = o;
     }
+    pa_assert(pa_idxset_isempty(i->direct_outputs));
+
+    pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL) == 0);
+
+    if (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED)
+        pa_assert_se(i->sink->n_corked-- >= 1);
+
+    pa_idxset_remove_by_data(i->sink->inputs, i, NULL);
+    i->sink = NULL;
+
+    pa_sink_update_status(origin);
+
+    return 0;
+}
+
+/* Called from main context */
+int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest) {
+    pa_resampler *new_resampler;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+    pa_assert(!i->sink);
+    pa_sink_assert_ref(dest);
+
+    if (!pa_sink_input_may_move_to(i, dest))
+        return -1;
+
+    i->sink = dest;
+    pa_idxset_put(dest->inputs, i, NULL);
+
+    if (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED)
+        i->sink->n_corked++;
 
     if (i->thread_info.resampler &&
-        pa_sample_spec_equal(&origin->sample_spec, &dest->sample_spec) &&
-        pa_channel_map_equal(&origin->channel_map, &dest->channel_map))
+        pa_sample_spec_equal(pa_resampler_output_sample_spec(i->thread_info.resampler), &dest->sample_spec) &&
+        pa_channel_map_equal(pa_resampler_output_channel_map(i->thread_info.resampler), &dest->channel_map))
 
         /* Try to reuse the old resampler if possible */
         new_resampler = i->thread_info.resampler;
@@ -1024,21 +1072,6 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest) {
     } else
         new_resampler = NULL;
 
-    hook_data.sink_input = i;
-    hook_data.destination = dest;
-    pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE], &hook_data);
-
-    pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL) == 0);
-
-    pa_idxset_remove_by_data(origin->inputs, i, NULL);
-    pa_idxset_put(dest->inputs, i, NULL);
-    i->sink = dest;
-
-    if (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED) {
-        pa_assert_se(origin->n_corked-- >= 1);
-        dest->n_corked++;
-    }
-
     /* Replace resampler and render queue */
     if (new_resampler != i->thread_info.resampler) {
 
@@ -1059,20 +1092,39 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest) {
                 &i->sink->silence);
     }
 
-    pa_sink_update_status(origin);
     pa_sink_update_status(dest);
-
     pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_FINISH_MOVE, i, 0, NULL) == 0);
 
+    pa_log_debug("Successfully moved sink input %i to %s.", i->index, dest->name);
+
+    /* Notify everyone */
     if (i->moved)
         i->moved(i);
 
-    pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_POST], i);
+    pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], i);
+    pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
 
-    pa_log_debug("Successfully moved sink input %i from %s to %s.", i->index, origin->name, dest->name);
+    return 0;
+}
 
-    /* Notify everyone */
-    pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+/* Called from main context */
+int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest) {
+    pa_sink_input_assert_ref(i);
+    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+    pa_assert(!i->sink);
+    pa_sink_assert_ref(dest);
+
+    if (dest == i->sink)
+        return 0;
+
+    if (!pa_sink_input_may_move_to(i, dest))
+        return -1;
+
+    if (pa_sink_input_start_move(i) < 0)
+        return -1;
+
+    if (pa_sink_input_finish_move(i, dest) < 0)
+        return -1;
 
     return 0;
 }
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index 3d2a8c0..a533046 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -78,7 +78,7 @@ struct pa_sink_input {
     pa_module *module;                  /* may be NULL */
     pa_client *client;                  /* may be NULL */
 
-    pa_sink *sink;
+    pa_sink *sink; /* NULL while we are being moved */
 
     /* A sink input may be connected to multiple source outputs
      * directly, so that they don't get mixed data of the entire
@@ -246,11 +246,6 @@ void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cv
 void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute);
 void pa_sink_input_new_data_done(pa_sink_input_new_data *data);
 
-typedef struct pa_sink_input_move_hook_data {
-    pa_sink_input *sink_input;
-    pa_sink *destination;
-} pa_sink_input_move_hook_data;
-
 typedef struct pa_sink_set_input_volume_data {
   pa_sink_input *sink_input;
   pa_cvolume virtual_volume;
@@ -300,7 +295,14 @@ pa_bool_t pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode,
 pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i);
 
 int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest);
-pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest);
+pa_bool_t pa_sink_input_may_move(pa_sink_input *i); /* may this sink input move at all? */
+pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest); /* may this sink input move to this sink? */
+
+/* The same as pa_sink_input_move_to() but in two seperate steps,
+ * first the detaching from the old sink, then the attaching to the
+ * new sink */
+int pa_sink_input_start_move(pa_sink_input *i);
+int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest);
 
 pa_sink_input_state_t pa_sink_input_get_state(pa_sink_input *i);
 
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index c832dad..d4f0367 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -249,11 +249,13 @@ pa_source_output* pa_source_output_new(
 static void update_n_corked(pa_source_output *o, pa_source_output_state_t state) {
     pa_assert(o);
 
+    if (!o->source)
+        return;
+
     if (o->state == PA_SOURCE_OUTPUT_CORKED && state != PA_SOURCE_OUTPUT_CORKED)
         pa_assert_se(o->source->n_corked -- >= 1);
     else if (o->state != PA_SOURCE_OUTPUT_CORKED && state == PA_SOURCE_OUTPUT_CORKED)
         o->source->n_corked++;
-
 }
 
 /* Called from main context */
@@ -293,9 +295,12 @@ void pa_source_output_unlink(pa_source_output*o) {
 
     if (o->direct_on_input)
         pa_idxset_remove_by_data(o->direct_on_input->direct_outputs, o, NULL);
-    pa_idxset_remove_by_data(o->source->core->source_outputs, o, NULL);
-    if (pa_idxset_remove_by_data(o->source->outputs, o, NULL))
-        pa_source_output_unref(o);
+
+    pa_idxset_remove_by_data(o->core->source_outputs, o, NULL);
+
+    if (o->source)
+        if (pa_idxset_remove_by_data(o->source->outputs, o, NULL))
+            pa_source_output_unref(o);
 
     if (o->client)
         pa_idxset_remove_by_data(o->client->source_outputs, o, NULL);
@@ -303,7 +308,7 @@ void pa_source_output_unlink(pa_source_output*o) {
     update_n_corked(o, PA_SOURCE_OUTPUT_UNLINKED);
     o->state = PA_SOURCE_OUTPUT_UNLINKED;
 
-    if (linked)
+    if (linked && o->source)
         if (o->source->asyncmsgq)
             pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL) == 0);
 
@@ -314,9 +319,11 @@ void pa_source_output_unlink(pa_source_output*o) {
         pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST], o);
     }
 
-    pa_source_update_status(o->source);
+    if (o->source) {
+        pa_source_update_status(o->source);
+        o->source = NULL;
+    }
 
-    o->source = NULL;
     pa_source_output_unref(o);
 }
 
@@ -624,6 +631,21 @@ pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o) {
     return o->resample_method;
 }
 
+/* Called from main context */
+pa_bool_t pa_source_output_may_move(pa_source_output *o) {
+    pa_source_output_assert_ref(o);
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+
+    if (o->flags & PA_SOURCE_OUTPUT_DONT_MOVE)
+        return FALSE;
+
+    if (o->direct_on_input)
+        return FALSE;
+
+    return TRUE;
+}
+
+/* Called from main context */
 pa_bool_t pa_source_output_may_move_to(pa_source_output *o, pa_source *dest) {
     pa_source_output_assert_ref(o);
     pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
@@ -632,10 +654,7 @@ pa_bool_t pa_source_output_may_move_to(pa_source_output *o, pa_source *dest) {
     if (dest == o->source)
         return TRUE;
 
-    if (o->flags & PA_SOURCE_OUTPUT_DONT_MOVE)
-        return FALSE;
-
-    if (o->direct_on_input)
+    if (!pa_source_output_may_move(o))
         return FALSE;
 
     if (pa_idxset_size(dest->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) {
@@ -651,26 +670,52 @@ pa_bool_t pa_source_output_may_move_to(pa_source_output *o, pa_source *dest) {
 }
 
 /* Called from main context */
-int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
+int pa_source_output_start_move(pa_source_output *o) {
     pa_source *origin;
-    pa_resampler *new_resampler;
-    pa_source_output_move_hook_data hook_data;
 
     pa_source_output_assert_ref(o);
     pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
-    pa_source_assert_ref(dest);
+    pa_assert(o->source);
+
+    if (!pa_source_output_may_move(o))
+        return -1;
+
+    if (pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_START], o) < 0)
+        return -1;
 
     origin = o->source;
 
-    if (dest == origin)
-        return 0;
+    pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL) == 0);
 
-    if (!pa_source_output_may_move_to(o, dest))
-        return -1;
+    if (pa_source_output_get_state(o) == PA_SOURCE_OUTPUT_CORKED)
+        pa_assert_se(origin->n_corked-- >= 1);
+
+    pa_idxset_remove_by_data(o->source->outputs, o, NULL);
+    o->source = NULL;
+
+    pa_source_update_status(origin);
+
+    return 0;
+}
+
+/* Called from main context */
+int pa_source_output_finish_move(pa_source_output *o, pa_source *dest) {
+    pa_resampler *new_resampler;
+
+    pa_source_output_assert_ref(o);
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+    pa_assert(!o->source);
+    pa_source_assert_ref(dest);
+
+    o->source = dest;
+    pa_idxset_put(o->source->outputs, o, NULL);
+
+    if (pa_source_output_get_state(o) == PA_SOURCE_OUTPUT_CORKED)
+        o->source->n_corked++;
 
     if (o->thread_info.resampler &&
-        pa_sample_spec_equal(&origin->sample_spec, &dest->sample_spec) &&
-        pa_channel_map_equal(&origin->channel_map, &dest->channel_map))
+        pa_sample_spec_equal(pa_resampler_input_sample_spec(o->thread_info.resampler), &dest->sample_spec) &&
+        pa_channel_map_equal(pa_resampler_input_channel_map(o->thread_info.resampler), &dest->channel_map))
 
         /* Try to reuse the old resampler if possible */
         new_resampler = o->thread_info.resampler;
@@ -695,22 +740,6 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
     } else
         new_resampler = NULL;
 
-    hook_data.source_output = o;
-    hook_data.destination = dest;
-    pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE], &hook_data);
-
-    /* Okey, let's move it */
-    pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL) == 0);
-
-    pa_idxset_remove_by_data(origin->outputs, o, NULL);
-    pa_idxset_put(dest->outputs, o, NULL);
-    o->source = dest;
-
-    if (pa_source_output_get_state(o) == PA_SOURCE_OUTPUT_CORKED) {
-        pa_assert_se(origin->n_corked-- >= 1);
-        dest->n_corked++;
-    }
-
     /* Replace resampler */
     if (new_resampler != o->thread_info.resampler) {
         if (o->thread_info.resampler)
@@ -730,20 +759,40 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
                 &o->source->silence);
     }
 
-    pa_source_update_status(origin);
     pa_source_update_status(dest);
-
     pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL) == 0);
 
+    pa_log_debug("Successfully moved source output %i to %s.", o->index, dest->name);
+
+    /* Notify everyone */
     if (o->moved)
         o->moved(o);
 
-    pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_POST], o);
+    pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH], o);
+    pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
 
-    pa_log_debug("Successfully moved source output %i from %s to %s.", o->index, origin->name, dest->name);
+    return 0;
+}
 
-    /* Notify everyone */
-    pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+/* Called from main context */
+int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
+
+    pa_source_output_assert_ref(o);
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+    pa_assert(o->source);
+    pa_source_assert_ref(dest);
+
+    if (dest == o->source)
+        return 0;
+
+    if (!pa_source_output_may_move_to(o, dest))
+        return -1;
+
+    if (pa_source_output_start_move(o) < 0)
+        return -1;
+
+    if (pa_source_output_finish_move(o, dest) < 0)
+        return -1;
 
     return 0;
 }
diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
index 2fdebae..a27602e 100644
--- a/src/pulsecore/source-output.h
+++ b/src/pulsecore/source-output.h
@@ -71,7 +71,7 @@ struct pa_source_output {
     pa_module *module;                    /* may be NULL */
     pa_client *client;                    /* may be NULL */
 
-    pa_source *source;
+    pa_source *source; /* NULL while being moved */
 
     /* A source output can monitor just a single input of a sink, in which case we find it here */
     pa_sink_input *direct_on_input;       /* may be NULL */
@@ -194,11 +194,6 @@ void pa_source_output_new_data_set_sample_spec(pa_source_output_new_data *data,
 void pa_source_output_new_data_set_channel_map(pa_source_output_new_data *data, const pa_channel_map *map);
 void pa_source_output_new_data_done(pa_source_output_new_data *data);
 
-typedef struct pa_source_output_move_hook_data {
-    pa_source_output *source_output;
-    pa_source *destination;
-} pa_source_output_move_hook_data;
-
 /* To be called by the implementing module only */
 
 pa_source_output* pa_source_output_new(
@@ -228,9 +223,16 @@ pa_bool_t pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t
 
 pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o);
 
+pa_bool_t pa_source_output_may_move(pa_source_output *o);
 pa_bool_t pa_source_output_may_move_to(pa_source_output *o, pa_source *dest);
 int pa_source_output_move_to(pa_source_output *o, pa_source *dest);
 
+/* The same as pa_source_output_move_to() but in two seperate steps,
+ * first the detaching from the old source, then the attaching to the
+ * new source */
+int pa_source_output_start_move(pa_source_output *o);
+int pa_source_output_finish_move(pa_source_output *o, pa_source *dest);
+
 #define pa_source_output_get_state(o) ((o)->state)
 
 pa_usec_t pa_source_output_get_requested_latency(pa_source_output *o);

commit 640d317df93f205d5830b9f7b106233ddb6d2f9e
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jan 23 22:40:02 2009 +0100

    add functions to move all inputs of a sink away/similar for source outputs

diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index df46a2c..083dbaa 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -474,6 +474,58 @@ int pa_sink_suspend(pa_sink *s, pa_bool_t suspend) {
         return sink_set_state(s, pa_sink_used_by(s) ? PA_SINK_RUNNING : PA_SINK_IDLE);
 }
 
+/* Called from main context */
+pa_queue *pa_sink_move_all_start(pa_sink *s) {
+    pa_queue *q;
+    pa_sink_input *i, *n;
+    uint32_t idx;
+
+    pa_sink_assert_ref(s);
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+
+    q = pa_queue_new();
+
+    for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = n) {
+        n = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx));
+
+        if (pa_sink_input_start_move(i) >= 0)
+            pa_queue_push(q, pa_sink_input_ref(i));
+    }
+
+    return q;
+}
+
+/* Called from main context */
+void pa_sink_move_all_finish(pa_sink *s, pa_queue *q) {
+    pa_sink_input *i;
+
+    pa_sink_assert_ref(s);
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+    pa_assert(q);
+
+    while ((i = PA_SINK_INPUT(pa_queue_pop(q)))) {
+        if (pa_sink_input_finish_move(i, s) < 0)
+            pa_sink_input_unlink(i);
+
+        pa_sink_input_unref(i);
+    }
+
+    pa_queue_free(q, NULL, NULL);
+}
+
+/* Called from main context */
+void pa_sink_move_all_fail(pa_queue *q) {
+    pa_sink_input *i;
+    pa_assert(q);
+
+    while ((i = PA_SINK_INPUT(pa_queue_pop(q)))) {
+        pa_sink_input_unlink(i);
+        pa_sink_input_unref(i);
+    }
+
+    pa_queue_free(q, NULL, NULL);
+}
+
 /* Called from IO thread context */
 void pa_sink_process_rewind(pa_sink *s, size_t nbytes) {
     pa_sink_input *i;
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index 89ed6d4..a30245d 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -40,6 +40,7 @@ typedef struct pa_sink pa_sink;
 #include <pulsecore/msgobject.h>
 #include <pulsecore/rtpoll.h>
 #include <pulsecore/card.h>
+#include <pulsecore/queue.h>
 
 #define PA_MAX_INPUTS_PER_SINK 32
 
@@ -254,6 +255,11 @@ unsigned pa_sink_used_by(pa_sink *s); /* Number of connected streams which are n
 unsigned pa_sink_check_suspend(pa_sink *s); /* Returns how many streams are active that don't allow suspensions */
 #define pa_sink_get_state(s) ((s)->state)
 
+/* Moves all inputs away, and stores them in pa_queue */
+pa_queue *pa_sink_move_all_start(pa_sink *s);
+void pa_sink_move_all_finish(pa_sink *s, pa_queue *q);
+void pa_sink_move_all_fail(pa_queue *q);
+
 /* To be called exclusively by the sink driver, from IO context */
 
 void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result);
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index fea66b7..3cddd09 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -412,6 +412,58 @@ int pa_source_suspend(pa_source *s, pa_bool_t suspend) {
         return source_set_state(s, pa_source_used_by(s) ? PA_SOURCE_RUNNING : PA_SOURCE_IDLE);
 }
 
+/* Called from main context */
+pa_queue *pa_source_move_all_start(pa_source *s) {
+    pa_queue *q;
+    pa_source_output *o, *n;
+    uint32_t idx;
+
+    pa_source_assert_ref(s);
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+
+    q = pa_queue_new();
+
+    for (o = PA_SOURCE_OUTPUT(pa_idxset_first(s->outputs, &idx)); o; o = n) {
+        n = PA_SOURCE_OUTPUT(pa_idxset_next(s->outputs, &idx));
+
+        if (pa_source_output_start_move(o) >= 0)
+            pa_queue_push(q, pa_source_output_ref(o));
+    }
+
+    return q;
+}
+
+/* Called from main context */
+void pa_source_move_all_finish(pa_source *s, pa_queue *q) {
+    pa_source_output *o;
+
+    pa_source_assert_ref(s);
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+    pa_assert(q);
+
+    while ((o = PA_SOURCE_OUTPUT(pa_queue_pop(q)))) {
+        if (pa_source_output_finish_move(o, s) < 0)
+            pa_source_output_unlink(o);
+
+        pa_source_output_unref(o);
+    }
+
+    pa_queue_free(q, NULL, NULL);
+}
+
+/* Called from main context */
+void pa_source_move_all_fail(pa_queue *q) {
+    pa_source_output *o;
+    pa_assert(q);
+
+    while ((o = PA_SOURCE_OUTPUT(pa_queue_pop(q)))) {
+        pa_source_output_unlink(o);
+        pa_source_output_unref(o);
+    }
+
+    pa_queue_free(q, NULL, NULL);
+}
+
 /* Called from IO thread context */
 void pa_source_process_rewind(pa_source *s, size_t nbytes) {
     pa_source_output *o;
diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
index 336599d..479cade 100644
--- a/src/pulsecore/source.h
+++ b/src/pulsecore/source.h
@@ -42,6 +42,7 @@ typedef struct pa_source pa_source;
 #include <pulsecore/rtpoll.h>
 #include <pulsecore/source-output.h>
 #include <pulsecore/card.h>
+#include <pulsecore/queue.h>
 
 #define PA_MAX_OUTPUTS_PER_SOURCE 32
 
@@ -233,6 +234,11 @@ unsigned pa_source_used_by(pa_source *s); /* Number of connected streams that ar
 unsigned pa_source_check_suspend(pa_source *s); /* Returns how many streams are active that don't allow suspensions */
 #define pa_source_get_state(s) ((pa_source_state_t) (s)->state)
 
+/* Moves all inputs away, and stores them in pa_queue */
+pa_queue *pa_source_move_all_start(pa_source *s);
+void pa_source_move_all_finish(pa_source *s, pa_queue *q);
+void pa_source_move_all_fail(pa_queue *q);
+
 /* To be called exclusively by the source driver, from IO context */
 
 void pa_source_post(pa_source*s, const pa_memchunk *chunk);

commit db27c6347ebe3316bb5f80518c6cc86eea67779b
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jan 23 22:40:32 2009 +0100

    make module-alsa-card move streams between the old and new sink/source, allowing 'hot' switching between profiles

diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
index 9a00edd..d8a37fe 100644
--- a/src/modules/alsa/module-alsa-card.c
+++ b/src/modules/alsa/module-alsa-card.c
@@ -26,6 +26,7 @@
 #include <pulse/xmalloc.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/modargs.h>
+#include <pulsecore/queue.h>
 
 #include "alsa-util.h"
 #include "alsa-sink.h"
@@ -159,23 +160,49 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
     od = PA_CARD_PROFILE_DATA(c->active_profile);
 
     if (od->sink_profile != nd->sink_profile) {
+        pa_queue *inputs = NULL;
+
         if (u->sink) {
+            if (nd->sink_profile)
+                inputs = pa_sink_move_all_start(u->sink);
+
             pa_alsa_sink_free(u->sink);
             u->sink = NULL;
         }
 
-        if (nd->sink_profile)
+        if (nd->sink_profile) {
             u->sink = pa_alsa_sink_new(c->module, u->modargs, __FILE__, c, nd->sink_profile);
+
+            if (inputs) {
+                if (u->sink)
+                    pa_sink_move_all_finish(u->sink, inputs);
+                else
+                    pa_sink_move_all_fail(inputs);
+            }
+        }
     }
 
     if (od->source_profile != nd->source_profile) {
+        pa_queue *outputs = NULL;
+
         if (u->source) {
+            if (nd->source_profile)
+                outputs = pa_source_move_all_start(u->source);
+
             pa_alsa_source_free(u->source);
             u->source = NULL;
         }
 
-        if (nd->source_profile)
+        if (nd->source_profile) {
             u->source = pa_alsa_source_new(c->module, u->modargs, __FILE__, c, nd->source_profile);
+
+            if (outputs) {
+                if (u->source)
+                    pa_source_move_all_finish(u->source, outputs);
+                else
+                    pa_source_move_all_fail(outputs);
+            }
+        }
     }
 
     return 0;

commit e9601250110decc8f4323c76be8549acff091966
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jan 23 23:57:47 2009 +0100

    add support for static mutexes

diff --git a/src/pulsecore/mutex-posix.c b/src/pulsecore/mutex-posix.c
index 35465b7..c3ead97 100644
--- a/src/pulsecore/mutex-posix.c
+++ b/src/pulsecore/mutex-posix.c
@@ -138,3 +138,23 @@ int pa_cond_wait(pa_cond *c, pa_mutex *m) {
 
     return pthread_cond_wait(&c->cond, &m->mutex);
 }
+
+pa_mutex* pa_static_mutex_get(pa_static_mutex *s, pa_bool_t recursive, pa_bool_t inherit_priority) {
+    pa_mutex *m;
+
+    pa_assert(s);
+
+    /* First, check if already initialized and short cut */
+    if ((m = pa_atomic_ptr_load(&s->ptr)))
+        return m;
+
+    /* OK, not initialized, so let's allocate, and fill in */
+    m = pa_mutex_new(recursive, inherit_priority);
+    if ((pa_atomic_ptr_cmpxchg(&s->ptr, NULL, m)))
+        return m;
+
+    /* Him, filling in failed, so someone else must have filled in
+     * already */
+    pa_assert_se(m = pa_atomic_ptr_load(&s->ptr));
+    return m;
+}
diff --git a/src/pulsecore/mutex.h b/src/pulsecore/mutex.h
index 36e1d63..8e0b1f2 100644
--- a/src/pulsecore/mutex.h
+++ b/src/pulsecore/mutex.h
@@ -23,6 +23,7 @@
 ***/
 
 #include <pulsecore/macro.h>
+#include <pulsecore/atomic.h>
 
 typedef struct pa_mutex pa_mutex;
 
@@ -45,4 +46,10 @@ void pa_cond_free(pa_cond *c);
 void pa_cond_signal(pa_cond *c, int broadcast);
 int pa_cond_wait(pa_cond *c, pa_mutex *m);
 
+typedef struct pa_static_mutex {
+    pa_atomic_ptr_t ptr;
+} pa_static_mutex;
+
+pa_mutex* pa_static_mutex_get(pa_static_mutex *m, pa_bool_t recursive, pa_bool_t inherit_priority);
+
 #endif

commit 3dfe70cf7887cfb288e4423358350ef4511506b5
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jan 23 23:58:36 2009 +0100

    add generic rate limiting implementation

diff --git a/src/Makefile.am b/src/Makefile.am
index 24623d3..e27bfba 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -533,6 +533,7 @@ libpulsecommon_ at PA_MAJORMINORMICRO@_la_SOURCES = \
 		pulsecore/llist.h \
 		pulsecore/lock-autospawn.c pulsecore/lock-autospawn.h \
 		pulsecore/log.c pulsecore/log.h \
+		pulsecore/ratelimit.c pulsecore/ratelimit.h \
 		pulsecore/macro.h pulsecore/vector.h \
 		pulsecore/mcalign.c pulsecore/mcalign.h \
 		pulsecore/memblock.c pulsecore/memblock.h \
diff --git a/src/pulsecore/ratelimit.c b/src/pulsecore/ratelimit.c
new file mode 100644
index 0000000..8ce7857
--- /dev/null
+++ b/src/pulsecore/ratelimit.c
@@ -0,0 +1,75 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 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 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/rtclock.h>
+#include <pulsecore/log.h>
+#include <pulsecore/mutex.h>
+
+#include "ratelimit.h"
+
+static pa_static_mutex mutex;
+
+/* Modelled after Linux' lib/ratelimit.c by Dave Young
+ * <hidave.darkstar at gmail.com>, which is licensed GPLv2. */
+
+pa_bool_t pa_ratelimit_test(pa_ratelimit *r) {
+    pa_usec_t now;
+    pa_mutex *m;
+
+    now = pa_rtclock_usec();
+
+    m = pa_static_mutex_get(&mutex, FALSE, FALSE);
+    pa_mutex_lock(m);
+
+    pa_assert(r);
+    pa_assert(r->interval > 0);
+    pa_assert(r->burst > 0);
+
+    if (r->begin <= 0 ||
+        r->begin + r->interval < now) {
+
+        if (r->n_missed > 0)
+            pa_log_warn("%u events suppressed", r->n_missed);
+
+        r->begin = now;
+
+        /* Reset counters */
+        r->n_printed = 0;
+        r->n_missed = 0;
+        goto good;
+    }
+
+    if (r->n_printed <= r->burst)
+        goto good;
+
+    r->n_missed++;
+    pa_mutex_unlock(m);
+    return FALSE;
+
+good:
+    r->n_printed++;
+    pa_mutex_unlock(m);
+    return TRUE;
+}
diff --git a/src/pulsecore/ratelimit.h b/src/pulsecore/ratelimit.h
new file mode 100644
index 0000000..e652c52
--- /dev/null
+++ b/src/pulsecore/ratelimit.h
@@ -0,0 +1,46 @@
+#ifndef foopulsecoreratelimithfoo
+#define foopulsecoreratelimithfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 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 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/sample.h>
+#include <pulsecore/macro.h>
+
+typedef struct pa_ratelimit {
+    const pa_usec_t interval;
+    const unsigned burst;
+    unsigned n_printed, n_missed;
+    pa_usec_t begin;
+} pa_ratelimit;
+
+#define PA_DEFINE_RATELIMIT(_name, _interval, _burst)   \
+    pa_ratelimit _name = {                              \
+        .interval = _interval,                          \
+        .burst = _burst,                                \
+        .n_printed = 0,                                 \
+        .n_missed = 0,                                  \
+        .begin = 0                                      \
+    }
+
+pa_bool_t pa_ratelimit_test(pa_ratelimit *r);
+
+#endif

commit 77c4ccfcaff95f25be373e036999869746ed81a7
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jan 23 23:58:57 2009 +0100

    add pa_log_rate_limit()

diff --git a/src/pulsecore/log.c b/src/pulsecore/log.c
index adf2f11..9a7f7ca 100644
--- a/src/pulsecore/log.c
+++ b/src/pulsecore/log.c
@@ -47,6 +47,7 @@
 #include <pulsecore/core-util.h>
 #include <pulsecore/rtclock.h>
 #include <pulsecore/once.h>
+#include <pulsecore/ratelimit.h>
 
 #include "log.h"
 
@@ -375,3 +376,10 @@ void pa_log_level(pa_log_level_t level, const char *format, ...) {
     pa_log_levelv_meta(level, NULL, 0, NULL, format, ap);
     va_end(ap);
 }
+
+pa_bool_t pa_log_ratelimit(void) {
+    /* Not more than 10 messages every 5s */
+    static PA_DEFINE_RATELIMIT(ratelimit, 5 * PA_USEC_PER_SEC, 10);
+
+    return pa_ratelimit_test(&ratelimit);
+}
diff --git a/src/pulsecore/log.h b/src/pulsecore/log.h
index 3d66e90..77adb79 100644
--- a/src/pulsecore/log.h
+++ b/src/pulsecore/log.h
@@ -109,4 +109,6 @@ LOG_FUNC(error, PA_LOG_ERROR)
 
 #define pa_log pa_log_error
 
+pa_bool_t pa_log_ratelimit(void);
+
 #endif

commit 54dad91f079a88f7f7dfb17692ff09af70d30e2e
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sat Jan 24 00:12:12 2009 +0100

    use pa_log_ratelimit() at a few places

diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index f092b5d..94b289f 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -251,7 +251,7 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle
 
         if (PA_UNLIKELY(n <= u->hwbuf_unused_frames)) {
 
-            if (polled)
+            if (polled && pa_log_ratelimit())
                 pa_log("ALSA woke us up to write new data to the device, but there was actually nothing to write! "
                        "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. "
                        "We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail_update() returned 0.");
@@ -374,7 +374,7 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle
 
         if (PA_UNLIKELY(n <= u->hwbuf_unused_frames)) {
 
-            if (polled)
+            if (polled && pa_log_ratelimit())
                 pa_log("ALSA woke us up to write new data to the device, but there was actually nothing to write! "
                        "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. "
                        "We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail_update() returned 0.");
@@ -1194,7 +1194,7 @@ static void thread_func(void *userdata) {
                 u->since_start = 0;
             }
 
-            if (revents && u->use_tsched)
+            if (revents && u->use_tsched && pa_log_ratelimit())
                 pa_log_debug("Wakeup from ALSA!%s%s", (revents & POLLIN) ? " INPUT" : "", (revents & POLLOUT) ? " OUTPUT" : "");
         } else
             revents = 0;
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index 0c4e5bc..b8ad2e2 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -238,7 +238,7 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
 
         if (PA_UNLIKELY(n <= 0)) {
 
-            if (polled)
+            if (polled && pa_log_ratelimit())
                 pa_log("ALSA woke us up to read new data from the device, but there was actually nothing to read! "
                        "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. "
                        "We were woken up with POLLIN set -- however a subsequent snd_pcm_avail_update() returned 0.");
@@ -346,7 +346,7 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
 
         if (PA_UNLIKELY(n <= 0)) {
 
-            if (polled)
+            if (polled && pa_log_ratelimit())
                 pa_log("ALSA woke us up to read new data from the device, but there was actually nothing to read! "
                        "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. "
                        "We were woken up with POLLIN set -- however a subsequent snd_pcm_avail_update() returned 0.");
@@ -1029,7 +1029,7 @@ static void thread_func(void *userdata) {
                 snd_pcm_start(u->pcm_handle);
             }
 
-            if (revents && u->use_tsched)
+            if (revents && u->use_tsched && pa_log_ratelimit())
                 pa_log_debug("Wakeup from ALSA!%s%s", (revents & POLLIN) ? " INPUT" : "", (revents & POLLOUT) ? " OUTPUT" : "");
         } else
             revents = 0;

commit a365c8212d24f80acbdadc927b696dd083507b53
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sat Jan 24 01:25:11 2009 +0100

    include a few HAL properties in our card/sink/source properties for ALSA devices

diff --git a/configure.ac b/configure.ac
index be79759..34ce25a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -916,7 +916,10 @@ AC_ARG_ENABLE([hal],
         [hal=auto])
 if test "x${hal}" != xno -a \( "x$HAVE_OSS" = "x1" -o "x$HAVE_ALSA" = "x1" \) ; then
     PKG_CHECK_MODULES(HAL, [ hal >= 0.5.11 ],
-        HAVE_HAL=1,
+        [
+            HAVE_HAL=1
+            AC_DEFINE([HAVE_HAL], 1, [Have HAL.])
+        ],
         [
             HAVE_HAL=0
             if test "x$hal" = xyes ; then
diff --git a/src/Makefile.am b/src/Makefile.am
index e27bfba..d82d8a6 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1268,6 +1268,12 @@ libalsa_util_la_LDFLAGS = -avoid-version
 libalsa_util_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
 libalsa_util_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
 
+if HAVE_HAL
+libalsa_util_la_SOURCES += modules/hal-util.h modules/hal-util.c
+libalsa_util_la_LIBADD += $(HAL_LIBS) libdbus-util.la
+libalsa_util_la_CFLAGS += $(HAL_CFLAGS)
+endif
+
 module_alsa_sink_la_SOURCES = modules/alsa/module-alsa-sink.c
 module_alsa_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
 module_alsa_sink_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libalsa-util.la libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index 94b289f..5020eac 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -1436,7 +1436,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
     pa_sink_new_data_set_sample_spec(&data, &ss);
     pa_sink_new_data_set_channel_map(&data, &map);
 
-    pa_alsa_init_proplist_pcm(data.proplist, pcm_info);
+    pa_alsa_init_proplist_pcm(m->core, data.proplist, pcm_info);
     pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name);
     pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (period_frames * frame_size * nfrags));
     pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size));
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index b8ad2e2..96e0d89 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -1261,7 +1261,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
     pa_source_new_data_set_sample_spec(&data, &ss);
     pa_source_new_data_set_channel_map(&data, &map);
 
-    pa_alsa_init_proplist_pcm(data.proplist, pcm_info);
+    pa_alsa_init_proplist_pcm(m->core, data.proplist, pcm_info);
     pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name);
     pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (period_frames * frame_size * nfrags));
     pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size));
diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index 599079c..7e5a350 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -39,6 +39,10 @@
 
 #include "alsa-util.h"
 
+#ifdef HAVE_HAL
+#include "hal-util.h"
+#endif
+
 struct pa_alsa_fdlist {
     unsigned num_fds;
     struct pollfd *fds;
@@ -1248,7 +1252,7 @@ void pa_alsa_redirect_errors_dec(void) {
         snd_lib_error_set_handler(NULL);
 }
 
-void pa_alsa_init_proplist_card(pa_proplist *p, int card) {
+void pa_alsa_init_proplist_card(pa_core *c, pa_proplist *p, int card) {
     char *cn, *lcn;
 
     pa_assert(p);
@@ -1265,9 +1269,13 @@ void pa_alsa_init_proplist_card(pa_proplist *p, int card) {
         pa_proplist_sets(p, "alsa.long_card_name", lcn);
         free(lcn);
     }
+
+#ifdef HAVE_HAL
+    pa_hal_get_info(c, p, card);
+#endif
 }
 
-void pa_alsa_init_proplist_pcm(pa_proplist *p, snd_pcm_info_t *pcm_info) {
+void pa_alsa_init_proplist_pcm(pa_core *c, pa_proplist *p, snd_pcm_info_t *pcm_info) {
 
     static const char * const alsa_class_table[SND_PCM_CLASS_LAST+1] = {
         [SND_PCM_CLASS_GENERIC] = "generic",
@@ -1321,7 +1329,7 @@ void pa_alsa_init_proplist_pcm(pa_proplist *p, snd_pcm_info_t *pcm_info) {
     pa_proplist_setf(p, "alsa.device", "%u", snd_pcm_info_get_device(pcm_info));
 
     if ((card = snd_pcm_info_get_card(pcm_info)) >= 0) {
-        pa_alsa_init_proplist_card(p, card);
+        pa_alsa_init_proplist_card(c, p, card);
         cn = pa_proplist_gets(p, "alsa.card_name");
     }
 
diff --git a/src/modules/alsa/alsa-util.h b/src/modules/alsa/alsa-util.h
index 18b0402..f2d3278 100644
--- a/src/modules/alsa/alsa-util.h
+++ b/src/modules/alsa/alsa-util.h
@@ -32,6 +32,7 @@
 #include <pulse/proplist.h>
 
 #include <pulsecore/rtpoll.h>
+#include <pulsecore/core.h>
 
 typedef struct pa_alsa_fdlist pa_alsa_fdlist;
 
@@ -118,8 +119,8 @@ void pa_alsa_dump_status(snd_pcm_t *pcm);
 void pa_alsa_redirect_errors_inc(void);
 void pa_alsa_redirect_errors_dec(void);
 
-void pa_alsa_init_proplist_pcm(pa_proplist *p, snd_pcm_info_t *pcm_info);
-void pa_alsa_init_proplist_card(pa_proplist *p, int card);
+void pa_alsa_init_proplist_pcm(pa_core *c, pa_proplist *p, snd_pcm_info_t *pcm_info);
+void pa_alsa_init_proplist_card(pa_core *c, pa_proplist *p, int card);
 
 int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents);
 
diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
index d8a37fe..891195c 100644
--- a/src/modules/alsa/module-alsa-card.c
+++ b/src/modules/alsa/module-alsa-card.c
@@ -281,7 +281,7 @@ int pa__init(pa_module*m) {
     pa_card_new_data_init(&data);
     data.driver = __FILE__;
     data.module = m;
-    pa_alsa_init_proplist_card(data.proplist, alsa_card_index);
+    pa_alsa_init_proplist_card(m->core, data.proplist, alsa_card_index);
     pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_id);
     set_card_name(&data, ma, u->device_id);
 
diff --git a/src/modules/dbus-util.h b/src/modules/dbus-util.h
index 2b24ac6..c4794da 100644
--- a/src/modules/dbus-util.h
+++ b/src/modules/dbus-util.h
@@ -24,6 +24,8 @@
 
 #include <dbus/dbus.h>
 
+#include <pulsecore/core.h>
+
 typedef struct pa_dbus_connection pa_dbus_connection;
 
 /* return the DBusConnection of the specified type for the given core,
diff --git a/src/modules/hal-util.c b/src/modules/hal-util.c
new file mode 100644
index 0000000..82bbc57
--- /dev/null
+++ b/src/modules/hal-util.c
@@ -0,0 +1,127 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 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 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/log.h>
+
+#include <hal/libhal.h>
+
+#include "dbus-util.h"
+#include "hal-util.h"
+
+int pa_hal_get_info(pa_core *core, pa_proplist *p, int card) {
+    pa_dbus_connection *c = NULL;
+    LibHalContext *hal = NULL;
+    DBusError error;
+    int r = -1;
+    char **udis = NULL, *t;
+    int n, i;
+
+    pa_assert(core);
+    pa_assert(p);
+    pa_assert(card >= 0);
+
+    dbus_error_init(&error);
+
+    if (!(c = pa_dbus_bus_get(core, DBUS_BUS_SYSTEM, &error)) || dbus_error_is_set(&error)) {
+        pa_log_error("Unable to contact DBUS system bus: %s: %s", error.name, error.message);
+        goto finish;
+    }
+
+
+    if (!(hal = libhal_ctx_new())) {
+        pa_log_error("libhal_ctx_new() finished");
+        goto finish;
+    }
+
+    if (!libhal_ctx_set_dbus_connection(hal, pa_dbus_connection_get(c))) {
+        pa_log_error("Error establishing DBUS connection: %s: %s", error.name, error.message);
+        goto finish;
+    }
+
+    if (!libhal_ctx_init(hal, &error)) {
+        pa_log_error("Couldn't connect to hald: %s: %s", error.name, error.message);
+        goto finish;
+    }
+
+    if (!(udis = libhal_find_device_by_capability(hal, "sound", &n, &error)) < 0) {
+        pa_log_error("Couldn't find devices: %s: %s", error.name, error.message);
+        goto finish;
+    }
+
+    for (i = 0; i < n; i++) {
+        dbus_int32_t this_card;
+
+        this_card = libhal_device_get_property_int(hal, udis[i], "sound.card", &error);
+        if (dbus_error_is_set(&error)) {
+            dbus_error_free(&error);
+            continue;
+        }
+
+        if (this_card == card)
+            break;
+
+    }
+
+    if (i >= n)
+        goto finish;
+
+    pa_proplist_sets(p, "hal.udi", udis[i]);
+
+    t = libhal_device_get_property_string(hal, udis[i], "info.product", &error);
+    if (dbus_error_is_set(&error))
+        dbus_error_free(&error);
+    if (t) {
+        pa_proplist_sets(p, "hal.product", t);
+        libhal_free_string(t);
+    }
+
+    t = libhal_device_get_property_string(hal, udis[i], "sound.card_id", &error);
+    if (dbus_error_is_set(&error))
+        dbus_error_free(&error);
+    if (t) {
+        pa_proplist_sets(p, "hal.card_id", t);
+        libhal_free_string(t);
+    }
+
+    r = 0;
+
+finish:
+
+    if (udis)
+        libhal_free_string_array(udis);
+
+    dbus_error_free(&error);
+
+    if (hal) {
+        libhal_ctx_shutdown(hal, &error);
+        libhal_ctx_free(hal);
+        dbus_error_free(&error);
+    }
+
+    if (c)
+        pa_dbus_connection_unref(c);
+
+    return r;
+}
diff --git a/src/modules/hal-util.h b/src/modules/hal-util.h
new file mode 100644
index 0000000..3c0e094
--- /dev/null
+++ b/src/modules/hal-util.h
@@ -0,0 +1,30 @@
+#ifndef foohalutilhfoo
+#define foohalutilhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 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 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 <pulsecore/core.h>
+
+int pa_hal_get_info(pa_core *core, pa_proplist *p, int card);
+
+#endif
diff --git a/src/modules/module-hal-detect.c b/src/modules/module-hal-detect.c
index 309b006..d3b351a 100644
--- a/src/modules/module-hal-detect.c
+++ b/src/modules/module-hal-detect.c
@@ -801,7 +801,6 @@ fail:
     return -1;
 }
 
-
 void pa__done(pa_module *m) {
     struct userdata *u;
 

commit afd817a0b607d732a7bfd2b31cef8a5b8cf9ab09
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sat Jan 24 01:36:43 2009 +0100

    rate limit a warning

diff --git a/src/pulsecore/asyncq.c b/src/pulsecore/asyncq.c
index c981505..67f661f 100644
--- a/src/pulsecore/asyncq.c
+++ b/src/pulsecore/asyncq.c
@@ -206,7 +206,8 @@ void pa_asyncq_post(pa_asyncq*l, void *p) {
     /* OK, we couldn't push anything in the queue. So let's queue it
      * locally and push it later */
 
-    pa_log("q overrun, queuing locally");
+    if (pa_log_ratelimit())
+        pa_log_warn("q overrun, queuing locally");
 
     if (!(q = pa_flist_pop(PA_STATIC_FLIST_GET(localq))))
         q = pa_xnew(struct localq, 1);

commit 24b3a743bd8a18741b2c1e0370f18afb90ed1ea5
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 27 00:44:45 2009 +0100

    add a bitset implementation

diff --git a/src/Makefile.am b/src/Makefile.am
index d82d8a6..f313bb5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -556,6 +556,7 @@ libpulsecommon_ at PA_MAJORMINORMICRO@_la_SOURCES = \
 		pulsecore/refcnt.h \
 		pulsecore/rtclock.c pulsecore/rtclock.h \
 		pulsecore/shm.c pulsecore/shm.h \
+		pulsecore/bitset.c pulsecore/bitset.h \
 		pulsecore/socket-client.c pulsecore/socket-client.h \
 		pulsecore/socket-server.c pulsecore/socket-server.h \
 		pulsecore/socket-util.c pulsecore/socket-util.h \
diff --git a/src/pulsecore/bitset.c b/src/pulsecore/bitset.c
new file mode 100644
index 0000000..4beeb1c
--- /dev/null
+++ b/src/pulsecore/bitset.c
@@ -0,0 +1,67 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 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 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 <string.h>
+
+#include <pulse/xmalloc.h>
+
+#include "bitset.h"
+
+void pa_bitset_set(pa_bitset_t *b, unsigned k, pa_bool_t v) {
+    pa_assert(b);
+
+    if (v)
+        b[k >> 5] |= 1 << (k & 31);
+    else
+        b[k >> 5] &= ~((uint32_t) (1 << (k & 31)));
+}
+
+pa_bool_t pa_bitset_get(const pa_bitset_t *b, unsigned k) {
+    return !!(b[k >> 5] & (1 << (k & 31)));
+}
+
+pa_bool_t pa_bitset_equals(const pa_bitset_t *b, unsigned n, ...) {
+    va_list ap;
+    pa_bitset_t *a;
+    pa_bool_t equal;
+
+    a = pa_xnew0(pa_bitset_t, PA_BITSET_ELEMENTS(n));
+
+    va_start(ap, n);
+    for (;;) {
+        int j = va_arg(ap, int);
+
+        if (j < 0)
+            break;
+
+        pa_bitset_set(a, j, TRUE);
+    }
+    va_end(ap);
+
+    equal = memcmp(a, b, PA_BITSET_SIZE(n)) == 0;
+    pa_xfree(a);
+
+    return equal;
+}
diff --git a/src/pulsecore/bitset.h b/src/pulsecore/bitset.h
new file mode 100644
index 0000000..21e840a
--- /dev/null
+++ b/src/pulsecore/bitset.h
@@ -0,0 +1,37 @@
+#ifndef foopulsecorebitsethfoo
+#define foopulsecorebitsethfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 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 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 <inttypes.h>
+#include <pulsecore/macro.h>
+
+#define PA_BITSET_ELEMENTS(n) (((n)+31)/32)
+#define PA_BITSET_SIZE(n) (PA_BITSET_ELEMENTS(n)*32)
+
+typedef uint32_t pa_bitset_t;
+
+void pa_bitset_set(pa_bitset_t *b, unsigned k, pa_bool_t v);
+pa_bool_t pa_bitset_get(const pa_bitset_t *b, unsigned k);
+pa_bool_t pa_bitset_equals(const pa_bitset_t *b, unsigned n, ...);
+
+#endif

commit e52c5ea68a9c3bf6e7c4b30cb6c2d4706f214cd3
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 27 00:46:39 2009 +0100

    implement new API functions pa_channel_map_can_balance(), pa_channel_map_to_name() and pa_channel_map_to_pretty_name()

diff --git a/src/map-file b/src/map-file
index 55b13e1..cb5c749 100644
--- a/src/map-file
+++ b/src/map-file
@@ -9,6 +9,7 @@ pa_browser_unref;
 pa_bytes_per_second;
 pa_bytes_snprint;
 pa_bytes_to_usec;
+pa_channel_map_can_balance;
 pa_channel_map_compatible;
 pa_channel_map_equal;
 pa_channel_map_init;
@@ -19,6 +20,8 @@ pa_channel_map_init_stereo;
 pa_channel_map_parse;
 pa_channel_map_snprint;
 pa_channel_map_superset;
+pa_channel_map_to_name;
+pa_channel_map_to_pretty_name;
 pa_channel_map_valid;
 pa_channel_position_to_pretty_string;
 pa_channel_position_to_string;
diff --git a/src/pulse/channelmap.c b/src/pulse/channelmap.c
index 26eae59..455bda1 100644
--- a/src/pulse/channelmap.c
+++ b/src/pulse/channelmap.c
@@ -32,6 +32,7 @@
 #include <pulse/i18n.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/macro.h>
+#include <pulsecore/bitset.h>
 
 #include "channelmap.h"
 
@@ -497,11 +498,58 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) {
 
     pa_channel_map_init(&map);
 
+    /* We don't need to match against the well known channel mapping
+     * "mono" here explicitly, because that can be understood as
+     * listing with one channel called "mono". */
+
     if (strcmp(s, "stereo") == 0) {
         map.channels = 2;
         map.map[0] = PA_CHANNEL_POSITION_LEFT;
         map.map[1] = PA_CHANNEL_POSITION_RIGHT;
         goto finish;
+    } else if (strcmp(s, "surround-40") == 0) {
+        map.channels = 4;
+        map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+        map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+        map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
+        map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
+        goto finish;
+    } else if (strcmp(s, "surround-41") == 0) {
+        map.channels = 5;
+        map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+        map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+        map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
+        map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
+        map.map[4] = PA_CHANNEL_POSITION_LFE;
+        goto finish;
+    } else if (strcmp(s, "surround-50") == 0) {
+        map.channels = 5;
+        map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+        map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+        map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
+        map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
+        map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
+        goto finish;
+    } else if (strcmp(s, "surround-51") == 0) {
+        map.channels = 6;
+        map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+        map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+        map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
+        map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
+        map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
+        map.map[5] = PA_CHANNEL_POSITION_LFE;
+        goto finish;
+    } else if (strcmp(s, "surround-71") == 0) {
+        map.channels = 8;
+        map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+        map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+        map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
+        map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
+        map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
+        map.map[5] = PA_CHANNEL_POSITION_LFE;
+        map.map[6] = PA_CHANNEL_POSITION_SIDE_LEFT;
+        map.map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT;
+        goto finish;
     }
 
     state = NULL;
@@ -579,7 +627,7 @@ int pa_channel_map_compatible(const pa_channel_map *map, const pa_sample_spec *s
 }
 
 int pa_channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) {
-    pa_bool_t in_a[PA_CHANNEL_POSITION_MAX];
+    pa_bitset_t in_a[PA_BITSET_ELEMENTS(PA_CHANNEL_POSITION_MAX)];
     unsigned i;
 
     pa_assert(a);
@@ -588,11 +636,144 @@ int pa_channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) {
     memset(in_a, 0, sizeof(in_a));
 
     for (i = 0; i < a->channels; i++)
-        in_a[a->map[i]] = TRUE;
+        pa_bitset_set(in_a, a->map[i], TRUE);
 
     for (i = 0; i < b->channels; i++)
-        if (!in_a[b->map[i]])
+        if (!pa_bitset_get(in_a, b->map[i]))
             return 0;
 
     return 1;
 }
+
+int pa_channel_map_can_balance(const pa_channel_map *map) {
+    unsigned c;
+
+    pa_assert(map);
+
+    for (c = 0; c < map->channels; c++)
+
+        switch (map->map[c]) {
+            case PA_CHANNEL_POSITION_LEFT:
+            case PA_CHANNEL_POSITION_RIGHT:
+            case PA_CHANNEL_POSITION_REAR_LEFT:
+            case PA_CHANNEL_POSITION_REAR_RIGHT:
+            case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
+            case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
+            case PA_CHANNEL_POSITION_SIDE_LEFT:
+            case PA_CHANNEL_POSITION_SIDE_RIGHT:
+            case PA_CHANNEL_POSITION_TOP_FRONT_LEFT:
+            case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT:
+            case PA_CHANNEL_POSITION_TOP_REAR_LEFT:
+            case PA_CHANNEL_POSITION_TOP_REAR_RIGHT:
+                return 1;
+
+            default:
+                ;
+        }
+
+    return 0;
+}
+
+const char* pa_channel_map_to_name(const pa_channel_map *map) {
+    pa_bitset_t in_map[PA_BITSET_ELEMENTS(PA_CHANNEL_POSITION_MAX)];
+    unsigned c;
+
+    pa_assert(map);
+
+    memset(in_map, 0, sizeof(in_map));
+
+    for (c = 0; c < map->channels; c++)
+        pa_bitset_set(in_map, map->map[c], TRUE);
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_MONO, -1))
+        return "mono";
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, -1))
+        return "stereo";
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, -1))
+        return "surround-40";
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+                         PA_CHANNEL_POSITION_LFE, -1))
+        return "surround-41";
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+                         PA_CHANNEL_POSITION_FRONT_CENTER, -1))
+        return "surround-50";
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+                         PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE, -1))
+        return "surround-51";
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+                         PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
+                         PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT, -1))
+        return "surround-71";
+
+    return NULL;
+}
+
+const char* pa_channel_map_to_pretty_name(const pa_channel_map *map) {
+    pa_bitset_t in_map[PA_BITSET_ELEMENTS(PA_CHANNEL_POSITION_MAX)];
+    unsigned c;
+
+    pa_assert(map);
+
+    memset(in_map, 0, sizeof(in_map));
+
+    for (c = 0; c < map->channels; c++)
+        pa_bitset_set(in_map, map->map[c], TRUE);
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_MONO, -1))
+        return _("Mono");
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT, -1))
+        return _("Stereo");
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, -1))
+        return _("Surround 4.0");
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+                         PA_CHANNEL_POSITION_LFE, -1))
+        return _("Surround 4.1");
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+                         PA_CHANNEL_POSITION_FRONT_CENTER, -1))
+        return _("Surround 5.0");
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+                         PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE, -1))
+        return _("Surround 5.1");
+
+    if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX,
+                         PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+                         PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
+                         PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE,
+                         PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT, -1))
+        return _("Surround 7.1");
+
+    return NULL;
+}
diff --git a/src/pulse/channelmap.h b/src/pulse/channelmap.h
index a6c044f..6e92e76 100644
--- a/src/pulse/channelmap.h
+++ b/src/pulse/channelmap.h
@@ -214,7 +214,10 @@ const char* pa_channel_position_to_pretty_string(pa_channel_position_t pos);
 /** Make a humand readable string from the specified channel map */
 char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map);
 
-/** Parse a channel position list into a channel map structure. */
+/** Parse a channel position list or well-known mapping name into a
+ * channel map structure. This turns the output of
+ * pa_channel_map_snprint() and pa_channel_map_to_name() back into a
+ * pa_channel_map */
 pa_channel_map *pa_channel_map_parse(pa_channel_map *map, const char *s);
 
 /** Compare two channel maps. Return 1 if both match. */
@@ -230,6 +233,22 @@ int pa_channel_map_compatible(const pa_channel_map *map, const pa_sample_spec *s
 /** Returns non-zero if every channel defined in b is also defined in a. \since 0.9.15 */
 int pa_channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) PA_GCC_PURE;
 
+/** Returns non-zero if it makes sense to apply a volume 'balance'
+ * with this mapping, i.e. if there are left/right channels
+ * available. \since 0.9.15 */
+int pa_channel_map_can_balance(const pa_channel_map *map) PA_GCC_PURE;
+
+/** Tries to find a well-known channel mapping name for this channel
+ * mapping. I.e. "stereo", "surround-71" and so on. If the channel
+ * mapping is unknown NULL will be returned. This name can be parsed
+ * with pa_channel_map_parse() \since 0.9.15 */
+const char* pa_channel_map_to_name(const pa_channel_map *map) PA_GCC_PURE;
+
+/** Tries to find a human readable text label for this channel
+mapping. I.e. "Stereo", "Surround 7.1" and so on. If the channel
+mapping is unknown NULL will be returned. \since 0.9.15 */
+const char* pa_channel_map_to_pretty_name(const pa_channel_map *map) PA_GCC_PURE;
+
 PA_C_DECL_END
 
 #endif

commit 4e31e00b63117f36df6b8ed4850e7ad6264e3da7
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 27 00:47:07 2009 +0100

    implement pa_cvolume_scale()

diff --git a/src/map-file b/src/map-file
index cb5c749..d613759 100644
--- a/src/map-file
+++ b/src/map-file
@@ -113,6 +113,7 @@ pa_cvolume_get_balance;
 pa_cvolume_init;
 pa_cvolume_max;
 pa_cvolume_remap;
+pa_cvolume_scale;
 pa_cvolume_set;
 pa_cvolume_set_balance;
 pa_cvolume_snprint;
diff --git a/src/pulse/volume.c b/src/pulse/volume.c
index 10a44da..2c97784 100644
--- a/src/pulse/volume.c
+++ b/src/pulse/volume.c
@@ -500,3 +500,22 @@ pa_cvolume* pa_cvolume_set_balance(const pa_channel_map *map, pa_cvolume *v, flo
 
     return v;
 }
+
+pa_cvolume* pa_cvolume_scale(pa_cvolume *v, pa_volume_t max) {
+    unsigned c;
+    pa_volume_t t = 0;
+
+    pa_assert(c);
+
+    for (c = 0; c < v->channels; c++)
+        if (v->values[c] > t)
+            t = v->values[c];
+
+    if (t <= 0)
+        return pa_cvolume_set(v, v->channels, max);
+
+    for (c = 0; c < v->channels; c++)
+        v->values[c] = (pa_volume_t) (((uint64_t)  v->values[c] * (uint64_t) max) / (uint64_t) t);
+
+    return v;
+}
diff --git a/src/pulse/volume.h b/src/pulse/volume.h
index 38da5df..c8b73f4 100644
--- a/src/pulse/volume.h
+++ b/src/pulse/volume.h
@@ -244,9 +244,14 @@ float pa_cvolume_get_balance(const pa_channel_map *map, const pa_cvolume *v) PA_
  * operation might not be reversable! Also, after this call
  * pa_cvolume_get_balance() is not guaranteed to actually return the
  * requested balance (e.g. when the input volume was zero anyway for
- * all channels)- \since 0.9.15 */
+ * all channels) \since 0.9.15 */
 pa_cvolume* pa_cvolume_set_balance(const pa_channel_map *map, pa_cvolume *v, float new_balance);
 
+/** Scale the passed pa_cvolume structure so that the maximum volume
+ * of all channels equals max. The proportions between the channel
+ * volumes are kept. \since 0.9.15 */
+pa_cvolume* pa_cvolume_scale(pa_cvolume *v, pa_volume_t max);
+
 PA_C_DECL_END
 
 #endif

commit 3bcbe1d18f08667ca7b9d690e5c211c25831e0ad
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 27 00:48:53 2009 +0100

    check for availability of RLIMIT_NOFILE and RLIMIT_AS before we make use of it

diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c
index c3abc09..c8c1b6f 100644
--- a/src/daemon/daemon-conf.c
+++ b/src/daemon/daemon-conf.c
@@ -97,11 +97,15 @@ static const pa_daemon_conf default_conf = {
 #ifdef RLIMIT_NPROC
    ,.rlimit_nproc = { .value = 0, .is_set = FALSE }
 #endif
+#ifdef RLIMIT_NOFILE
    ,.rlimit_nofile = { .value = 256, .is_set = TRUE }
+#endif
 #ifdef RLIMIT_MEMLOCK
    ,.rlimit_memlock = { .value = 0, .is_set = FALSE }
 #endif
+#ifdef RLIMIT_AS
    ,.rlimit_as = { .value = 0, .is_set = FALSE }
+#endif
 #ifdef RLIMIT_LOCKS
    ,.rlimit_locks = { .value = 0, .is_set = FALSE }
 #endif
@@ -442,8 +446,12 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
         { "rlimit-stack",               parse_rlimit,             NULL },
         { "rlimit-core",                parse_rlimit,             NULL },
         { "rlimit-rss",                 parse_rlimit,             NULL },
+#ifdef RLIMIT_NOFILE
         { "rlimit-nofile",              parse_rlimit,             NULL },
+#endif
+#ifdef RLIMIT_AS
         { "rlimit-as",                  parse_rlimit,             NULL },
+#endif
 #ifdef RLIMIT_NPROC
         { "rlimit-nproc",               parse_rlimit,             NULL },
 #endif
@@ -508,10 +516,14 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
     table[i++].data = &c->rlimit_fsize;
     table[i++].data = &c->rlimit_data;
     table[i++].data = &c->rlimit_stack;
-    table[i++].data = &c->rlimit_as;
     table[i++].data = &c->rlimit_core;
+    table[i++].data = &c->rlimit_rss;
+#ifdef RLIMIT_NOFILE
     table[i++].data = &c->rlimit_nofile;
+#endif
+#ifdef RLIMIT_AS
     table[i++].data = &c->rlimit_as;
+#endif
 #ifdef RLIMIT_NPROC
     table[i++].data = &c->rlimit_nproc;
 #endif
@@ -662,12 +674,16 @@ char *pa_daemon_conf_dump(pa_daemon_conf *c) {
     pa_strbuf_printf(s, "rlimit-data = %li\n", c->rlimit_data.is_set ? (long int) c->rlimit_data.value : -1);
     pa_strbuf_printf(s, "rlimit-stack = %li\n", c->rlimit_stack.is_set ? (long int) c->rlimit_stack.value : -1);
     pa_strbuf_printf(s, "rlimit-core = %li\n", c->rlimit_core.is_set ? (long int) c->rlimit_core.value : -1);
-    pa_strbuf_printf(s, "rlimit-as = %li\n", c->rlimit_as.is_set ? (long int) c->rlimit_as.value : -1);
     pa_strbuf_printf(s, "rlimit-rss = %li\n", c->rlimit_rss.is_set ? (long int) c->rlimit_rss.value : -1);
+#ifdef RLIMIT_AS
+    pa_strbuf_printf(s, "rlimit-as = %li\n", c->rlimit_as.is_set ? (long int) c->rlimit_as.value : -1);
+#endif
 #ifdef RLIMIT_NPROC
     pa_strbuf_printf(s, "rlimit-nproc = %li\n", c->rlimit_nproc.is_set ? (long int) c->rlimit_nproc.value : -1);
 #endif
+#ifdef RLIMIT_NOFILE
     pa_strbuf_printf(s, "rlimit-nofile = %li\n", c->rlimit_nofile.is_set ? (long int) c->rlimit_nofile.value : -1);
+#endif
 #ifdef RLIMIT_MEMLOCK
     pa_strbuf_printf(s, "rlimit-memlock = %li\n", c->rlimit_memlock.is_set ? (long int) c->rlimit_memlock.value : -1);
 #endif
diff --git a/src/daemon/daemon-conf.h b/src/daemon/daemon-conf.h
index fffa35e..3b75b83 100644
--- a/src/daemon/daemon-conf.h
+++ b/src/daemon/daemon-conf.h
@@ -84,7 +84,14 @@ typedef struct pa_daemon_conf {
     char *config_file;
 
 #ifdef HAVE_SYS_RESOURCE_H
-    pa_rlimit rlimit_fsize, rlimit_data, rlimit_stack, rlimit_core, rlimit_rss, rlimit_nofile, rlimit_as;
+    pa_rlimit rlimit_fsize, rlimit_data, rlimit_stack, rlimit_core, rlimit_rss;
+
+#ifdef RLIMIT_NOFILE
+    pa_rlimit rlimit_nofile;
+#endif
+#ifdef RLIMIT_AS
+    pa_rlimit rlimit_as;
+#endif
 #ifdef RLIMIT_NPROC
     pa_rlimit rlimit_nproc;
 #endif
diff --git a/src/daemon/main.c b/src/daemon/main.c
index f483607..9705f4c 100644
--- a/src/daemon/main.c
+++ b/src/daemon/main.c
@@ -298,7 +298,9 @@ static void set_all_rlimits(const pa_daemon_conf *conf) {
 #ifdef RLIMIT_NPROC
     set_one_rlimit(&conf->rlimit_nproc, RLIMIT_NPROC, "RLIMIT_NPROC");
 #endif
+#ifdef RLIMIT_NOFILE
     set_one_rlimit(&conf->rlimit_nofile, RLIMIT_NOFILE, "RLIMIT_NOFILE");
+#endif
 #ifdef RLIMIT_MEMLOCK
     set_one_rlimit(&conf->rlimit_memlock, RLIMIT_MEMLOCK, "RLIMIT_MEMLOCK");
 #endif

commit ccd21f33cf254cad6348769b42646fe8b0c5d0a5
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 27 00:49:58 2009 +0100

    make a few comments appear in doxygen

diff --git a/src/pulse/context.h b/src/pulse/context.h
index 3b51397..fd791b9 100644
--- a/src/pulse/context.h
+++ b/src/pulse/context.h
@@ -232,14 +232,14 @@ uint32_t pa_context_get_protocol_version(pa_context *c);
 /** Return the protocol version of the connected server. */
 uint32_t pa_context_get_server_protocol_version(pa_context *c);
 
-/* Update the property list of the client, adding new entries. Please
+/** Update the property list of the client, adding new entries. Please
  * note that it is highly recommended to set as much properties
  * initially via pa_context_new_with_proplist() as possible instead a
  * posteriori with this function, since that information may then be
  * used to route streams of the client to the right device. \since 0.9.11 */
 pa_operation *pa_context_proplist_update(pa_context *c, pa_update_mode_t mode, pa_proplist *p, pa_context_success_cb_t cb, void *userdata);
 
-/* Update the property list of the client, remove entries. \since 0.9.11 */
+/** Update the property list of the client, remove entries. \since 0.9.11 */
 pa_operation *pa_context_proplist_remove(pa_context *c, const char *const keys[], pa_context_success_cb_t cb, void *userdata);
 
 /** Return the client index this context is

commit 9ba408415c28c1113291062b4dbb38cf90a3c232
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 27 00:52:28 2009 +0100

    store requested resampling method in a seperate field and use it when create a new resampler after a move

diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index e25e0c2..a33d62f 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -218,8 +218,6 @@ pa_sink_input* pa_sink_input_new(
             pa_log_warn("Unsupported resampling operation.");
             return NULL;
         }
-
-        data->resample_method = pa_resampler_get_method(resampler);
     }
 
     i = pa_msgobject_new(pa_sink_input);
@@ -235,7 +233,8 @@ pa_sink_input* pa_sink_input_new(
     i->sink = data->sink;
     i->client = data->client;
 
-    i->resample_method = data->resample_method;
+    i->requested_resample_method = data->resample_method;
+    i->actual_resample_method = resampler ? pa_resampler_get_method(resampler) : PA_RESAMPLER_INVALID;
     i->sample_spec = data->sample_spec;
     i->channel_map = data->channel_map;
 
@@ -946,7 +945,7 @@ void pa_sink_input_set_name(pa_sink_input *i, const char *name) {
 pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i) {
     pa_sink_input_assert_ref(i);
 
-    return i->resample_method;
+    return i->actual_resample_method;
 }
 
 /* Called from main context */
@@ -1062,7 +1061,7 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest) {
                       i->core->mempool,
                       &i->sample_spec, &i->channel_map,
                       &dest->sample_spec, &dest->channel_map,
-                      i->resample_method,
+                      i->requested_resample_method,
                       ((i->flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
                       ((i->flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
                       (i->core->disable_remixing || (i->flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) {
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index a533046..66ec613 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -95,7 +95,7 @@ struct pa_sink_input {
     pa_cvolume volume;
     pa_bool_t muted;
 
-    pa_resample_method_t resample_method;
+    pa_resample_method_t requested_resample_method, actual_resample_method;
 
     /* Returns the chunk of audio data and drops it from the
      * queue. Returns -1 on failure. Called from IO thread context. If
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index d4f0367..1d21ffb 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -181,8 +181,6 @@ pa_source_output* pa_source_output_new(
             pa_log_warn("Unsupported resampling operation.");
             return NULL;
         }
-
-        data->resample_method = pa_resampler_get_method(resampler);
     }
 
     o = pa_msgobject_new(pa_source_output);
@@ -198,7 +196,9 @@ pa_source_output* pa_source_output_new(
     o->source = data->source;
     o->client = data->client;
 
-    o->resample_method = data->resample_method;
+
+    o->actual_resample_method = resampler ? pa_resampler_get_method(resampler) : PA_RESAMPLER_INVALID;
+    o->requested_resample_method = data->resample_method;
     o->sample_spec = data->sample_spec;
     o->channel_map = data->channel_map;
 
@@ -628,7 +628,7 @@ pa_bool_t pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t
 pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o) {
     pa_source_output_assert_ref(o);
 
-    return o->resample_method;
+    return o->actual_resample_method;
 }
 
 /* Called from main context */
@@ -730,7 +730,7 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest) {
                       o->core->mempool,
                       &dest->sample_spec, &dest->channel_map,
                       &o->sample_spec, &o->channel_map,
-                      o->resample_method,
+                      o->requested_resample_method,
                       ((o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
                       ((o->flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
                       (o->core->disable_remixing || (o->flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) {
diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
index a27602e..d60e425 100644
--- a/src/pulsecore/source-output.h
+++ b/src/pulsecore/source-output.h
@@ -79,7 +79,7 @@ struct pa_source_output {
     pa_sample_spec sample_spec;
     pa_channel_map channel_map;
 
-    pa_resample_method_t resample_method;
+    pa_resample_method_t requested_resample_method, actual_resample_method;
 
     /* Pushes a new memchunk into the output. Called from IO thread
      * context. */
@@ -139,7 +139,7 @@ struct pa_source_output {
     struct {
         pa_source_output_state_t state;
 
-        pa_bool_t attached; /* True only between ->attach() and ->detach() calls */
+        pa_bool_t attached:1; /* True only between ->attach() and ->detach() calls */
 
         pa_sample_spec sample_spec;
 

commit 07db64b9d31cb2d7315200058e4b3680b88db408
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 27 00:53:31 2009 +0100

    remove redundant cast

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index b22578e..804be35 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -600,7 +600,7 @@ static int sco_process_render(struct userdata *u) {
     for (;;) {
         ssize_t l;
 
-        l = pa_loop_write(u->stream_fd, (uint8_t*) p, memchunk.length, NULL);
+        l = pa_loop_write(u->stream_fd, p, memchunk.length, NULL);
         pa_log_debug("Memblock written to socket: %li bytes", (long) l);
 
         pa_assert(l != 0);

commit 0658d9ae929d4d2f79c5cdbc650745cafc3f8217
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 27 00:55:35 2009 +0100

    show pretty channel map name if possible

diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index 1a3778a..7f3a745 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -221,6 +221,9 @@ char *pa_sink_list_to_string(pa_core *c) {
             vdb[PA_SW_VOLUME_SNPRINT_DB_MAX],
             cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t;
         pa_usec_t min_latency, max_latency;
+        const char *cmn;
+
+        cmn = pa_channel_map_to_pretty_name(&sink->channel_map);
 
         pa_sink_get_latency_range(sink, &min_latency, &max_latency);
 
@@ -241,7 +244,7 @@ char *pa_sink_list_to_string(pa_core *c) {
             "\tmax rewind: %lu KiB\n"
             "\tmonitor source: %u\n"
             "\tsample spec: %s\n"
-            "\tchannel map: %s\n"
+            "\tchannel map: %s%s%s\n"
             "\tused by: %u\n"
             "\tlinked by: %u\n",
             c->default_sink_name && !strcmp(sink->name, c->default_sink_name) ? '*' : ' ',
@@ -272,6 +275,8 @@ char *pa_sink_list_to_string(pa_core *c) {
             sink->monitor_source ? sink->monitor_source->index : PA_INVALID_INDEX,
             pa_sample_spec_snprint(ss, sizeof(ss), &sink->sample_spec),
             pa_channel_map_snprint(cm, sizeof(cm), &sink->channel_map),
+            cmn ? "\n\t            " : "",
+            cmn ? cmn : "",
             pa_sink_used_by(sink),
             pa_sink_linked_by(sink));
 
@@ -306,6 +311,9 @@ char *pa_source_list_to_string(pa_core *c) {
             vdb[PA_SW_VOLUME_SNPRINT_DB_MAX],
             cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t;
         pa_usec_t min_latency, max_latency;
+        const char *cmn;
+
+        cmn = pa_channel_map_to_pretty_name(&source->channel_map);
 
         pa_source_get_latency_range(source, &min_latency, &max_latency);
 
@@ -324,7 +332,7 @@ char *pa_source_list_to_string(pa_core *c) {
             "\tconfigured latency: %0.2f ms; range is %0.2f .. %0.2f ms\n"
             "\tmax rewind: %lu KiB\n"
             "\tsample spec: %s\n"
-            "\tchannel map: %s\n"
+            "\tchannel map: %s%s%s\n"
             "\tused by: %u\n"
             "\tlinked by: %u\n",
             c->default_source_name && !strcmp(source->name, c->default_source_name) ? '*' : ' ',
@@ -353,6 +361,8 @@ char *pa_source_list_to_string(pa_core *c) {
             (unsigned long) pa_source_get_max_rewind(source) / 1024,
             pa_sample_spec_snprint(ss, sizeof(ss), &source->sample_spec),
             pa_channel_map_snprint(cm, sizeof(cm), &source->channel_map),
+            cmn ? "\n\t            " : "",
+            cmn ? cmn : "",
             pa_source_used_by(source),
             pa_source_linked_by(source));
 
@@ -391,6 +401,9 @@ char *pa_source_output_list_to_string(pa_core *c) {
     for (o = pa_idxset_first(c->source_outputs, &idx); o; o = pa_idxset_next(c->source_outputs, &idx)) {
         char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t, clt[28];
         pa_usec_t cl;
+        const char *cmn;
+
+        cmn = pa_channel_map_to_pretty_name(&o->channel_map);
 
         if ((cl = pa_source_output_get_requested_latency(o)) == (pa_usec_t) -1)
             pa_snprintf(clt, sizeof(clt), "n/a");
@@ -409,7 +422,7 @@ char *pa_source_output_list_to_string(pa_core *c) {
             "\tcurrent latency: %0.2f ms\n"
             "\trequested latency: %s\n"
             "\tsample spec: %s\n"
-            "\tchannel map: %s\n"
+            "\tchannel map: %s%s%s\n"
             "\tresample method: %s\n",
             o->index,
             o->driver,
@@ -427,6 +440,8 @@ char *pa_source_output_list_to_string(pa_core *c) {
             clt,
             pa_sample_spec_snprint(ss, sizeof(ss), &o->sample_spec),
             pa_channel_map_snprint(cm, sizeof(cm), &o->channel_map),
+            cmn ? "\n\t            " : "",
+            cmn ? cmn : "",
             pa_resample_method_to_string(pa_source_output_get_resample_method(o)));
         if (o->module)
             pa_strbuf_printf(s, "\towner module: %u\n", o->module->index);
@@ -463,6 +478,9 @@ char *pa_sink_input_list_to_string(pa_core *c) {
     for (i = pa_idxset_first(c->sink_inputs, &idx); i; i = pa_idxset_next(c->sink_inputs, &idx)) {
         char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t, clt[28];
         pa_usec_t cl;
+        const char *cmn;
+
+        cmn = pa_channel_map_to_pretty_name(&i->channel_map);
 
         if ((cl = pa_sink_input_get_requested_latency(i)) == (pa_usec_t) -1)
             pa_snprintf(clt, sizeof(clt), "n/a");
@@ -485,7 +503,7 @@ char *pa_sink_input_list_to_string(pa_core *c) {
             "\tcurrent latency: %0.2f ms\n"
             "\trequested latency: %s\n"
             "\tsample spec: %s\n"
-            "\tchannel map: %s\n"
+            "\tchannel map: %s%s%s\n"
             "\tresample method: %s\n",
             i->index,
             i->driver,
@@ -507,6 +525,8 @@ char *pa_sink_input_list_to_string(pa_core *c) {
             clt,
             pa_sample_spec_snprint(ss, sizeof(ss), &i->sample_spec),
             pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
+            cmn ? "\n\t            " : "",
+            cmn ? cmn : "",
             pa_resample_method_to_string(pa_sink_input_get_resample_method(i)));
 
         if (i->module)
@@ -537,6 +557,9 @@ char *pa_scache_list_to_string(pa_core *c) {
         for (e = pa_idxset_first(c->scache, &idx); e; e = pa_idxset_next(c->scache, &idx)) {
             double l = 0;
             char ss[PA_SAMPLE_SPEC_SNPRINT_MAX] = "n/a", cv[PA_CVOLUME_SNPRINT_MAX], cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX] = "n/a", *t;
+            const char *cmn;
+
+            cmn = pa_channel_map_to_pretty_name(&e->channel_map);
 
             if (e->memchunk.memblock) {
                 pa_sample_spec_snprint(ss, sizeof(ss), &e->sample_spec);
@@ -549,7 +572,7 @@ char *pa_scache_list_to_string(pa_core *c) {
                 "    name: <%s>\n"
                 "\tindex: %u\n"
                 "\tsample spec: %s\n"
-                "\tchannel map: %s\n"
+                "\tchannel map: %s%s%s\n"
                 "\tlength: %lu\n"
                 "\tduration: %0.1f s\n"
                 "\tvolume: %s\n"
@@ -561,6 +584,8 @@ char *pa_scache_list_to_string(pa_core *c) {
                 e->index,
                 ss,
                 cm,
+                cmn ? "\n\t            " : "",
+                cmn ? cmn : "",
                 (long unsigned)(e->memchunk.memblock ? e->memchunk.length : 0),
                 l,
                 pa_cvolume_snprint(cv, sizeof(cv), &e->volume),

commit 948be361c44f16f9982a92d33259625b7256dd3e
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 27 00:56:57 2009 +0100

    invert an ill-placed assert

diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index a33d62f..d1ffb0f 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -1110,7 +1110,7 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest) {
 int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest) {
     pa_sink_input_assert_ref(i);
     pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
-    pa_assert(!i->sink);
+    pa_assert(i->sink);
     pa_sink_assert_ref(dest);
 
     if (dest == i->sink)

commit 1249cf6dc9032810575fc5b94c002bf633316bfb
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 27 02:16:20 2009 +0100

    always define PA_MAJOR/PA_MINOR/PA_MICRO to ease feature checking in client applications

diff --git a/configure.ac b/configure.ac
index 34ce25a..c02b20e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -22,18 +22,21 @@
 
 AC_PREREQ(2.63)
 
-m4_define(PA_MAJOR, [0])
-m4_define(PA_MINOR, [9])
-m4_define(PA_MICRO, [15])
+m4_define(pa_major, [0])
+m4_define(pa_minor, [9])
+m4_define(pa_micro, [15])
 
-AC_INIT([pulseaudio],[PA_MAJOR.PA_MINOR.PA_MICRO],[mzchyfrnhqvb (at) 0pointer (dot) net])
+AC_INIT([pulseaudio],[pa_major.pa_minor.pa_micro],[mzchyfrnhqvb (at) 0pointer (dot) net])
 AC_CONFIG_SRCDIR([src/daemon/main.c])
 AC_CONFIG_MACRO_DIR([m4])
 AC_CONFIG_HEADERS([config.h])
 AM_INIT_AUTOMAKE([foreign 1.10 -Wall])
 
-AC_SUBST(PA_MAJORMINOR, PA_MAJOR.PA_MINOR)
-AC_SUBST(PA_MAJORMINORMICRO, PA_MAJOR.PA_MINOR.PA_MICRO)
+AC_SUBST(PA_MAJOR, pa_major)
+AC_SUBST(PA_MINOR, pa_minor)
+AC_SUBST(PA_MICRO, pa_micro)
+AC_SUBST(PA_MAJORMINOR, pa_major.pa_minor)
+AC_SUBST(PA_MAJORMINORMICRO, pa_major.pa_minor.pa_micro)
 AC_SUBST(PACKAGE_URL, [http://pulseaudio.org/])
 
 AC_SUBST(PA_API_VERSION, 12)
diff --git a/src/pulse/version.h.in b/src/pulse/version.h.in
index 0e37f98..566dd55 100644
--- a/src/pulse/version.h.in
+++ b/src/pulse/version.h.in
@@ -51,6 +51,15 @@ const char* pa_get_library_version(void);
  * 0.8/PulseAudio 0.9. */
 #define PA_PROTOCOL_VERSION @PA_PROTOCOL_VERSION@
 
+/** The major version of PA. \since 0.9.15 */
+#define PA_MAJOR @PA_MAJOR@
+
+/** The minor version of PA. \since 0.9.15 */
+#define PA_MINOR @PA_MINOR@
+
+/** The micro version of PA. \since 0.9.15 */
+#define PA_MICRO @PA_MICRO@
+
 PA_C_DECL_END
 
 #endif

commit 605853057114892c8e6b2e1ef1b5f745d36f88a4
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 27 02:36:42 2009 +0100

    import version.h in all header files to make sure that version-based feature testing works

diff --git a/src/pulse/browser.h b/src/pulse/browser.h
index c4e0a17..499fae2 100644
--- a/src/pulse/browser.h
+++ b/src/pulse/browser.h
@@ -26,6 +26,7 @@
 #include <pulse/sample.h>
 #include <pulse/channelmap.h>
 #include <pulse/cdecl.h>
+#include <pulse/version.h>
 
 /** \file
  * An abstract interface for Zeroconf browsing of PulseAudio servers */
diff --git a/src/pulse/channelmap.h b/src/pulse/channelmap.h
index 6e92e76..de2d712 100644
--- a/src/pulse/channelmap.h
+++ b/src/pulse/channelmap.h
@@ -26,6 +26,7 @@
 #include <pulse/sample.h>
 #include <pulse/cdecl.h>
 #include <pulse/gccmacro.h>
+#include <pulse/version.h>
 
 /** \page channelmap Channel Maps
  *
diff --git a/src/pulse/context.h b/src/pulse/context.h
index fd791b9..dfb7e4a 100644
--- a/src/pulse/context.h
+++ b/src/pulse/context.h
@@ -29,6 +29,7 @@
 #include <pulse/cdecl.h>
 #include <pulse/operation.h>
 #include <pulse/proplist.h>
+#include <pulse/version.h>
 
 /** \page async Asynchronous API
  *
diff --git a/src/pulse/def.h b/src/pulse/def.h
index be5cc23..c2c90e9 100644
--- a/src/pulse/def.h
+++ b/src/pulse/def.h
@@ -29,6 +29,7 @@
 
 #include <pulse/cdecl.h>
 #include <pulse/sample.h>
+#include <pulse/version.h>
 
 /** \file
  * Global definitions */
diff --git a/src/pulse/error.h b/src/pulse/error.h
index 9f9e3d3..c30b80b 100644
--- a/src/pulse/error.h
+++ b/src/pulse/error.h
@@ -25,6 +25,7 @@
 
 #include <inttypes.h>
 #include <pulse/cdecl.h>
+#include <pulse/version.h>
 
 /** \file
  * Error management */
diff --git a/src/pulse/ext-stream-restore.h b/src/pulse/ext-stream-restore.h
index 2038eb4..ac01d23 100644
--- a/src/pulse/ext-stream-restore.h
+++ b/src/pulse/ext-stream-restore.h
@@ -23,6 +23,7 @@
 ***/
 
 #include <pulse/context.h>
+#include <pulse/version.h>
 
 /** \file
  *
diff --git a/src/pulse/glib-mainloop.h b/src/pulse/glib-mainloop.h
index 60fd61a..fd68f8a 100644
--- a/src/pulse/glib-mainloop.h
+++ b/src/pulse/glib-mainloop.h
@@ -27,6 +27,7 @@
 
 #include <pulse/mainloop-api.h>
 #include <pulse/cdecl.h>
+#include <pulse/version.h>
 
 /** \page glib-mainloop GLIB Main Loop Bindings
  *
diff --git a/src/pulse/i18n.h b/src/pulse/i18n.h
index 4c0ef9d..f91c0bf 100644
--- a/src/pulse/i18n.h
+++ b/src/pulse/i18n.h
@@ -24,6 +24,7 @@
 
 #include <pulse/cdecl.h>
 #include <pulse/gccmacro.h>
+#include <pulse/version.h>
 
 PA_C_DECL_BEGIN
 
diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h
index 972b454..d8a28ff 100644
--- a/src/pulse/introspect.h
+++ b/src/pulse/introspect.h
@@ -32,6 +32,7 @@
 #include <pulse/channelmap.h>
 #include <pulse/volume.h>
 #include <pulse/proplist.h>
+#include <pulse/version.h>
 
 /** \page introspect Server Query and Control
  *
diff --git a/src/pulse/mainloop-api.h b/src/pulse/mainloop-api.h
index 53c7411..e353ed9 100644
--- a/src/pulse/mainloop-api.h
+++ b/src/pulse/mainloop-api.h
@@ -27,6 +27,7 @@
 #include <time.h>
 
 #include <pulse/cdecl.h>
+#include <pulse/version.h>
 
 /** \file
  *
diff --git a/src/pulse/operation.h b/src/pulse/operation.h
index 188e2cb..b68e781 100644
--- a/src/pulse/operation.h
+++ b/src/pulse/operation.h
@@ -24,6 +24,7 @@
 
 #include <pulse/cdecl.h>
 #include <pulse/def.h>
+#include <pulse/version.h>
 
 /** \file
  * Asynchronous operations */
diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h
index 529871f..984a656 100644
--- a/src/pulse/proplist.h
+++ b/src/pulse/proplist.h
@@ -26,6 +26,7 @@
 
 #include <pulse/cdecl.h>
 #include <pulse/gccmacro.h>
+#include <pulse/version.h>
 
 PA_C_DECL_BEGIN
 
diff --git a/src/pulse/sample.h b/src/pulse/sample.h
index 3ba13db..45a481f 100644
--- a/src/pulse/sample.h
+++ b/src/pulse/sample.h
@@ -30,6 +30,7 @@
 
 #include <pulse/gccmacro.h>
 #include <pulse/cdecl.h>
+#include <pulse/version.h>
 
 /** \page sample Sample Format Specifications
  *
diff --git a/src/pulse/scache.h b/src/pulse/scache.h
index f380b4e..69e813c 100644
--- a/src/pulse/scache.h
+++ b/src/pulse/scache.h
@@ -28,6 +28,7 @@
 #include <pulse/context.h>
 #include <pulse/stream.h>
 #include <pulse/cdecl.h>
+#include <pulse/version.h>
 
 /** \page scache Sample Cache
  *
diff --git a/src/pulse/simple.h b/src/pulse/simple.h
index a1380a0..3f57a65 100644
--- a/src/pulse/simple.h
+++ b/src/pulse/simple.h
@@ -29,6 +29,7 @@
 #include <pulse/channelmap.h>
 #include <pulse/def.h>
 #include <pulse/cdecl.h>
+#include <pulse/version.h>
 
 /** \page simple Simple API
  *
diff --git a/src/pulse/subscribe.h b/src/pulse/subscribe.h
index 0e4be8c..2707cec 100644
--- a/src/pulse/subscribe.h
+++ b/src/pulse/subscribe.h
@@ -28,6 +28,7 @@
 #include <pulse/def.h>
 #include <pulse/context.h>
 #include <pulse/cdecl.h>
+#include <pulse/version.h>
 
 /** \page subscribe Event Subscription
  *
diff --git a/src/pulse/thread-mainloop.h b/src/pulse/thread-mainloop.h
index 521e29b..4de338a 100644
--- a/src/pulse/thread-mainloop.h
+++ b/src/pulse/thread-mainloop.h
@@ -25,6 +25,7 @@
 
 #include <pulse/mainloop-api.h>
 #include <pulse/cdecl.h>
+#include <pulse/version.h>
 
 PA_C_DECL_BEGIN
 
diff --git a/src/pulse/utf8.h b/src/pulse/utf8.h
index 6c7e7a5..4d75195 100644
--- a/src/pulse/utf8.h
+++ b/src/pulse/utf8.h
@@ -25,6 +25,7 @@
 
 #include <pulse/cdecl.h>
 #include <pulse/gccmacro.h>
+#include <pulse/version.h>
 
 /** \file
  * UTF8 Validation functions
diff --git a/src/pulse/util.h b/src/pulse/util.h
index cf06d4f..f6dd40c 100644
--- a/src/pulse/util.h
+++ b/src/pulse/util.h
@@ -27,6 +27,7 @@
 
 #include <pulse/cdecl.h>
 #include <pulse/gccmacro.h>
+#include <pulse/version.h>
 
 /** \file
  * Assorted utility functions */
diff --git a/src/pulse/volume.h b/src/pulse/volume.h
index c8b73f4..e8670ab 100644
--- a/src/pulse/volume.h
+++ b/src/pulse/volume.h
@@ -29,6 +29,7 @@
 #include <pulse/gccmacro.h>
 #include <pulse/sample.h>
 #include <pulse/channelmap.h>
+#include <pulse/version.h>
 
 /** \page volume Volume Control
  *
diff --git a/src/pulse/xmalloc.h b/src/pulse/xmalloc.h
index b264358..c30d4df 100644
--- a/src/pulse/xmalloc.h
+++ b/src/pulse/xmalloc.h
@@ -29,6 +29,7 @@
 
 #include <pulse/cdecl.h>
 #include <pulse/gccmacro.h>
+#include <pulse/version.h>
 
 /** \file
  * Memory allocation functions.

commit df8ad5d18fe67b93393fb6f81d7598a95d021680
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 27 02:37:13 2009 +0100

    add a few missing doxygen comments

diff --git a/src/pulse/gccmacro.h b/src/pulse/gccmacro.h
index 0533b10..0b1a1a6 100644
--- a/src/pulse/gccmacro.h
+++ b/src/pulse/gccmacro.h
@@ -22,6 +22,9 @@
   USA.
 ***/
 
+/** \file
+ * GCC attribute macros */
+
 #ifdef __GNUC__
 #define PA_GCC_PRINTF_ATTR(a,b) __attribute__ ((format (printf, a, b)))
 #else
@@ -100,6 +103,7 @@
 #else
 /** Macro for usage of GCC's alloc_size attribute */
 #define PA_GCC_ALLOC_SIZE(x)
+/** Macro for usage of GCC's alloc_size attribute */
 #define PA_GCC_ALLOC_SIZE2(x,y)
 #endif
 #endif
diff --git a/src/pulse/mainloop-signal.h b/src/pulse/mainloop-signal.h
index a6c16f2..a9e250b 100644
--- a/src/pulse/mainloop-signal.h
+++ b/src/pulse/mainloop-signal.h
@@ -40,8 +40,10 @@ PA_C_DECL_BEGIN
 /** An opaque UNIX signal event source object */
 typedef struct pa_signal_event pa_signal_event;
 
+/** Callback prototype for signal events */
 typedef void (*pa_signal_cb_t) (pa_mainloop_api *api, pa_signal_event*e, int sig, void *userdata);
 
+/** Destroy callback prototype for signal events */
 typedef void (*pa_signal_destroy_cb_t) (pa_mainloop_api *api, pa_signal_event*e, void *userdata);
 
 /** Initialize the UNIX signal subsystem and bind it to the specified main loop */
diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h
index 984a656..203a28c 100644
--- a/src/pulse/proplist.h
+++ b/src/pulse/proplist.h
@@ -162,7 +162,7 @@ int pa_proplist_setf(pa_proplist *p, const char *key, const char *format, ...) P
  * internal copy of the data passed is made. \since 0.9.11 */
 int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nbytes);
 
-/* Return a string entry for the specified key. Will return NULL if
+/** Return a string entry for the specified key. Will return NULL if
  * the data is not valid UTF-8. Will return a NUL-terminated string in
  * an internally allocated buffer. The caller should make a copy of
  * the data before accessing the property list again. \since 0.9.11 */
diff --git a/src/pulse/timeval.h b/src/pulse/timeval.h
index ee39829..2b3faf1 100644
--- a/src/pulse/timeval.h
+++ b/src/pulse/timeval.h
@@ -26,17 +26,29 @@
 #include <pulse/cdecl.h>
 #include <pulse/gccmacro.h>
 #include <pulse/sample.h>
+#include <pulse/version.h>
 
 /** \file
  * Utility functions for handling timeval calculations */
 
 PA_C_DECL_BEGIN
 
+/** The number of milliseconds in a second */
 #define PA_MSEC_PER_SEC ((pa_usec_t) 1000ULL)
+
+/** The number of microseconds in a second */
 #define PA_USEC_PER_SEC ((pa_usec_t) 1000000ULL)
+
+/** The number of nanoseconds in a second */
 #define PA_NSEC_PER_SEC ((pa_usec_t) 1000000000ULL)
+
+/** The number of microseconds in a millisecond */
 #define PA_USEC_PER_MSEC ((pa_usec_t) 1000ULL)
+
+/** The number of nanoseconds in a millisecond */
 #define PA_NSEC_PER_MSEC ((pa_usec_t) 1000000ULL)
+
+/** The number of nanoseconds in a microsecond */
 #define PA_NSEC_PER_USEC ((pa_usec_t) 1000ULL)
 
 struct timeval;

commit 5449d793ae7800ad236f027f6c6893a1d2db3929
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 27 02:45:37 2009 +0100

    swap argument order of pa_cvolume_get_balance() to be a bit more systematic

diff --git a/src/pulse/volume.c b/src/pulse/volume.c
index 2c97784..822fe39 100644
--- a/src/pulse/volume.c
+++ b/src/pulse/volume.c
@@ -435,7 +435,7 @@ static void get_avg_lr(const pa_channel_map *map, const pa_cvolume *v, pa_volume
         *r = right / n_right;
 }
 
-float pa_cvolume_get_balance(const pa_channel_map *map, const pa_cvolume *v) {
+float pa_cvolume_get_balance(const pa_cvolume *v, const pa_channel_map *map) {
     pa_volume_t left, right;
 
     pa_assert(v);
@@ -462,7 +462,7 @@ float pa_cvolume_get_balance(const pa_channel_map *map, const pa_cvolume *v) {
         return 1.0f - ((float) left / (float) right);
 }
 
-pa_cvolume* pa_cvolume_set_balance(const pa_channel_map *map, pa_cvolume *v, float new_balance) {
+pa_cvolume* pa_cvolume_set_balance(pa_cvolume *v, const pa_channel_map *map, float new_balance) {
     pa_volume_t left, nleft, right, nright, m;
     unsigned c;
 
diff --git a/src/pulse/volume.h b/src/pulse/volume.h
index e8670ab..c1967b3 100644
--- a/src/pulse/volume.h
+++ b/src/pulse/volume.h
@@ -237,7 +237,7 @@ int pa_cvolume_compatible(const pa_cvolume *v, const pa_sample_spec *ss) PA_GCC_
 /** Calculate a 'balance' value for the specified volume with the
  * specified channel map. The return value will range from -1.0f
  * (left) to +1.0f (right) \since 0.9.15 */
-float pa_cvolume_get_balance(const pa_channel_map *map, const pa_cvolume *v) PA_GCC_PURE;
+float pa_cvolume_get_balance(const pa_cvolume *v, const pa_channel_map *map) PA_GCC_PURE;
 
 /** Adjust the 'balance' value for the specified volume with the
  * specified channel map. v will be modified in place and
@@ -246,7 +246,7 @@ float pa_cvolume_get_balance(const pa_channel_map *map, const pa_cvolume *v) PA_
  * pa_cvolume_get_balance() is not guaranteed to actually return the
  * requested balance (e.g. when the input volume was zero anyway for
  * all channels) \since 0.9.15 */
-pa_cvolume* pa_cvolume_set_balance(const pa_channel_map *map, pa_cvolume *v, float new_balance);
+pa_cvolume* pa_cvolume_set_balance(pa_cvolume *v, const pa_channel_map *map, float new_balance);
 
 /** Scale the passed pa_cvolume structure so that the maximum volume
  * of all channels equals max. The proportions between the channel
diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index 7f3a745..e87abe0 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -261,7 +261,7 @@ char *pa_sink_list_to_string(pa_core *c) {
             pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink, FALSE)),
             sink->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t        " : "",
             sink->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), pa_sink_get_volume(sink, FALSE)) : "",
-            pa_cvolume_get_balance(&sink->channel_map, pa_sink_get_volume(sink, FALSE)),
+            pa_cvolume_get_balance(pa_sink_get_volume(sink, FALSE), &sink->channel_map),
             pa_volume_snprint(v, sizeof(v), sink->base_volume),
             sink->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t             " : "",
             sink->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_volume_snprint_dB(vdb, sizeof(vdb), sink->base_volume) : "",
@@ -349,7 +349,7 @@ char *pa_source_list_to_string(pa_core *c) {
             pa_cvolume_snprint(cv, sizeof(cv), pa_source_get_volume(source, FALSE)),
             source->flags & PA_SOURCE_DECIBEL_VOLUME ? "\n\t        " : "",
             source->flags & PA_SOURCE_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), pa_source_get_volume(source, FALSE)) : "",
-            pa_cvolume_get_balance(&source->channel_map, pa_source_get_volume(source, FALSE)),
+            pa_cvolume_get_balance(pa_source_get_volume(source, FALSE), &source->channel_map),
             pa_volume_snprint(v, sizeof(v), source->base_volume),
             source->flags & PA_SOURCE_DECIBEL_VOLUME ? "\n\t             " : "",
             source->flags & PA_SOURCE_DECIBEL_VOLUME ? pa_sw_volume_snprint_dB(vdb, sizeof(vdb), source->base_volume) : "",
@@ -519,7 +519,7 @@ char *pa_sink_input_list_to_string(pa_core *c) {
             i->sink->index, i->sink->name,
             pa_cvolume_snprint(cv, sizeof(cv), pa_sink_input_get_volume(i)),
             pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), pa_sink_input_get_volume(i)),
-            pa_cvolume_get_balance(&i->channel_map, pa_sink_input_get_volume(i)),
+            pa_cvolume_get_balance(pa_sink_input_get_volume(i), &i->channel_map),
             pa_yes_no(pa_sink_input_get_mute(i)),
             (double) pa_sink_input_get_latency(i, NULL) / PA_USEC_PER_MSEC,
             clt,
@@ -590,7 +590,7 @@ char *pa_scache_list_to_string(pa_core *c) {
                 l,
                 pa_cvolume_snprint(cv, sizeof(cv), &e->volume),
                 pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &e->volume),
-                e->memchunk.memblock ? pa_cvolume_get_balance(&e->channel_map, &e->volume) : 0.0f,
+                e->memchunk.memblock ? pa_cvolume_get_balance(&e->volume, &e->channel_map) : 0.0f,
                 pa_yes_no(e->lazy),
                 e->filename ? e->filename : "n/a");
 
diff --git a/src/tests/voltest.c b/src/tests/voltest.c
index 879d86b..0c6d2ea 100644
--- a/src/tests/voltest.c
+++ b/src/tests/voltest.c
@@ -38,7 +38,7 @@ int main(int argc, char *argv[]) {
         for (cv.values[1] = PA_VOLUME_MUTED; cv.values[1] <= PA_VOLUME_NORM*2; cv.values[1] += 4096) {
             char s[PA_CVOLUME_SNPRINT_MAX];
 
-            printf("Volume: [%s]; balance: %2.1f\n", pa_cvolume_snprint(s, sizeof(s), &cv), pa_cvolume_get_balance(&map, &cv));
+            printf("Volume: [%s]; balance: %2.1f\n", pa_cvolume_snprint(s, sizeof(s), &cv), pa_cvolume_get_balance(&cv, &map));
         }
 
     for (cv.values[0] = PA_VOLUME_MUTED+4096; cv.values[0] <= PA_VOLUME_NORM*2; cv.values[0] += 4096)
@@ -48,12 +48,12 @@ int main(int argc, char *argv[]) {
                 pa_cvolume r;
                 float k;
 
-                printf("Before: volume: [%s]; balance: %2.1f\n", pa_cvolume_snprint(s, sizeof(s), &cv), pa_cvolume_get_balance(&map, &cv));
+                printf("Before: volume: [%s]; balance: %2.1f\n", pa_cvolume_snprint(s, sizeof(s), &cv), pa_cvolume_get_balance(&cv, &map));
 
                 r = cv;
-                pa_cvolume_set_balance(&map, &r, b);
+                pa_cvolume_set_balance(&r, &map,b);
 
-                k = pa_cvolume_get_balance(&map, &r);
+                k = pa_cvolume_get_balance(&r, &map);
                 printf("After: volume: [%s]; balance: %2.1f (intended: %2.1f) %s\n", pa_cvolume_snprint(s, sizeof(s), &r), k, b, k < b-.05 || k > b+.5 ? "MISMATCH" : "");
             }
 
diff --git a/src/utils/pactl.c b/src/utils/pactl.c
index 0820641..154e7f9 100644
--- a/src/utils/pactl.c
+++ b/src/utils/pactl.c
@@ -216,7 +216,7 @@ static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_
            pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
            i->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t        " : "",
            i->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &i->volume) : "",
-           pa_cvolume_get_balance(&i->channel_map, &i->volume),
+           pa_cvolume_get_balance(&i->volume, &i->channel_map),
            pa_volume_snprint(v, sizeof(v), i->base_volume),
            i->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t             " : "",
            i->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_volume_snprint_dB(vdb, sizeof(vdb), i->base_volume) : "",
@@ -296,7 +296,7 @@ static void get_source_info_callback(pa_context *c, const pa_source_info *i, int
            pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
            i->flags & PA_SOURCE_DECIBEL_VOLUME ? "\n\t        " : "",
            i->flags & PA_SOURCE_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &i->volume) : "",
-           pa_cvolume_get_balance(&i->channel_map, &i->volume),
+           pa_cvolume_get_balance(&i->volume, &i->channel_map),
            pa_volume_snprint(v, sizeof(v), i->base_volume),
            i->flags & PA_SOURCE_DECIBEL_VOLUME ? "\n\t             " : "",
            i->flags & PA_SOURCE_DECIBEL_VOLUME ? pa_sw_volume_snprint_dB(vdb, sizeof(vdb), i->base_volume) : "",
@@ -483,7 +483,7 @@ static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info
            pa_yes_no(i->mute),
            pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
            pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &i->volume),
-           pa_cvolume_get_balance(&i->channel_map, &i->volume),
+           pa_cvolume_get_balance(&i->volume, &i->channel_map),
            (double) i->buffer_usec,
            (double) i->sink_usec,
            i->resample_method ? i->resample_method : _("n/a"),
@@ -584,7 +584,7 @@ static void get_sample_info_callback(pa_context *c, const pa_sample_info *i, int
            pa_sample_spec_valid(&i->sample_spec) ? pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map) : _("n/a"),
            pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
            pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &i->volume),
-           pa_cvolume_get_balance(&i->channel_map, &i->volume),
+           pa_cvolume_get_balance(&i->volume, &i->channel_map),
            (double) i->duration/1000000.0,
            t,
            pa_yes_no(i->lazy),

commit 1be39e4fa5b8f6a2d995593a4c1fd66eeafd6328
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 27 03:05:40 2009 +0100

    allow samples to be played with 'default' (i.e. unspecified) volume.

diff --git a/src/pulse/scache.c b/src/pulse/scache.c
index fd3b987..c96c42a 100644
--- a/src/pulse/scache.c
+++ b/src/pulse/scache.c
@@ -188,6 +188,10 @@ pa_operation *pa_context_play_sample(pa_context *c, const char *name, const char
     t = pa_tagstruct_command(c, PA_COMMAND_PLAY_SAMPLE, &tag);
     pa_tagstruct_putu32(t, PA_INVALID_INDEX);
     pa_tagstruct_puts(t, dev);
+
+    if (volume == (pa_volume_t) -1 && c->version < 15)
+        volume = PA_VOLUME_NORM;
+
     pa_tagstruct_putu32(t, volume);
     pa_tagstruct_puts(t, name);
 
@@ -225,6 +229,10 @@ pa_operation *pa_context_play_sample_with_proplist(pa_context *c, const char *na
     t = pa_tagstruct_command(c, PA_COMMAND_PLAY_SAMPLE, &tag);
     pa_tagstruct_putu32(t, PA_INVALID_INDEX);
     pa_tagstruct_puts(t, dev);
+
+    if (volume == (pa_volume_t) -1 && c->version < 15)
+        volume = PA_VOLUME_NORM;
+
     pa_tagstruct_putu32(t, volume);
     pa_tagstruct_puts(t, name);
     pa_tagstruct_put_proplist(t, p);
diff --git a/src/pulse/scache.h b/src/pulse/scache.h
index 69e813c..79fcfbc 100644
--- a/src/pulse/scache.h
+++ b/src/pulse/scache.h
@@ -101,7 +101,7 @@ pa_operation* pa_context_play_sample(
         pa_context *c               /**< Context */,
         const char *name            /**< Name of the sample to play */,
         const char *dev             /**< Sink to play this sample on */,
-        pa_volume_t volume          /**< Volume to play this sample with */ ,
+        pa_volume_t volume          /**< Volume to play this sample with. Starting with 0.9.15 you may pass here (pa_volume_t) -1 which will leave the decision about the volume to the server side which is a good idea. */ ,
         pa_context_success_cb_t cb  /**< Call this function after successfully starting playback, or NULL */,
         void *userdata              /**< Userdata to pass to the callback */);
 
@@ -113,7 +113,7 @@ pa_operation* pa_context_play_sample_with_proplist(
         pa_context *c                   /**< Context */,
         const char *name                /**< Name of the sample to play */,
         const char *dev                 /**< Sink to play this sample on */,
-        pa_volume_t volume              /**< Volume to play this sample with */ ,
+        pa_volume_t volume              /**< Volume to play this sample with. Starting with 0.9.15 you may pass here (pa_volume_t) -1 which will leave the decision about the volume to the server side which is a good idea.  */ ,
         pa_proplist *proplist           /**< Property list for this sound. The property list of the cached entry will be merged into this property list */,
         pa_context_play_sample_cb_t cb  /**< Call this function after successfully starting playback, or NULL */,
         void *userdata                  /**< Userdata to pass to the callback */);
diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index e87abe0..c74ca5d 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -588,9 +588,9 @@ char *pa_scache_list_to_string(pa_core *c) {
                 cmn ? cmn : "",
                 (long unsigned)(e->memchunk.memblock ? e->memchunk.length : 0),
                 l,
-                pa_cvolume_snprint(cv, sizeof(cv), &e->volume),
-                pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &e->volume),
-                e->memchunk.memblock ? pa_cvolume_get_balance(&e->volume, &e->channel_map) : 0.0f,
+                e->volume_is_set ? pa_cvolume_snprint(cv, sizeof(cv), &e->volume) : "n/a",
+                e->volume_is_set ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &e->volume) : "n/a",
+                (e->memchunk.memblock && e->volume_is_set) ? pa_cvolume_get_balance(&e->volume, &e->channel_map) : 0.0f,
                 pa_yes_no(e->lazy),
                 e->filename ? e->filename : "n/a");
 
diff --git a/src/pulsecore/core-scache.c b/src/pulsecore/core-scache.c
index 0f34c9d..6d2ae93 100644
--- a/src/pulsecore/core-scache.c
+++ b/src/pulsecore/core-scache.c
@@ -137,6 +137,7 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name) {
     pa_sample_spec_init(&e->sample_spec);
     pa_channel_map_init(&e->channel_map);
     pa_cvolume_init(&e->volume);
+    e->volume_is_set = FALSE;
 
     pa_proplist_sets(e->proplist, PA_PROP_MEDIA_ROLE, "event");
 
@@ -175,6 +176,7 @@ int pa_scache_add_item(
     pa_sample_spec_init(&e->sample_spec);
     pa_channel_map_init(&e->channel_map);
     pa_cvolume_init(&e->volume);
+    e->volume_is_set = FALSE;
 
     if (ss) {
         e->sample_spec = *ss;
@@ -308,6 +310,7 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t
     pa_scache_entry *e;
     pa_cvolume r;
     pa_proplist *merged;
+    pa_bool_t pass_volume;
 
     pa_assert(c);
     pa_assert(name);
@@ -324,10 +327,12 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t
 
         pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index);
 
-        if (pa_cvolume_valid(&e->volume))
-            pa_cvolume_remap(&e->volume, &old_channel_map, &e->channel_map);
-        else
-            pa_cvolume_reset(&e->volume, e->sample_spec.channels);
+        if (e->volume_is_set) {
+            if (pa_cvolume_valid(&e->volume))
+                pa_cvolume_remap(&e->volume, &old_channel_map, &e->channel_map);
+            else
+                pa_cvolume_reset(&e->volume, e->sample_spec.channels);
+        }
     }
 
     if (!e->memchunk.memblock)
@@ -335,19 +340,26 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t
 
     pa_log_debug("Playing sample \"%s\" on \"%s\"", name, sink->name);
 
-    pa_cvolume_set(&r, e->volume.channels, volume);
-    pa_sw_cvolume_multiply(&r, &r, &e->volume);
+    pass_volume = TRUE;
 
-    merged = pa_proplist_new();
+    if (e->volume_is_set && volume != (pa_volume_t) -1) {
+        pa_cvolume_set(&r, e->sample_spec.channels, volume);
+        pa_sw_cvolume_multiply(&r, &r, &e->volume);
+    } else if (e->volume_is_set)
+        r = e->volume;
+    else if (volume != (pa_volume_t) -1)
+        pa_cvolume_set(&r, e->sample_spec.channels, volume);
+    else
+        pass_volume = FALSE;
 
+    merged = pa_proplist_new();
     pa_proplist_setf(merged, PA_PROP_MEDIA_NAME, "Sample %s", name);
-
     pa_proplist_update(merged, PA_UPDATE_REPLACE, e->proplist);
 
     if (p)
         pa_proplist_update(merged, PA_UPDATE_REPLACE, p);
 
-    if (pa_play_memchunk(sink, &e->sample_spec, &e->channel_map, &e->memchunk, &r, merged, sink_input_idx) < 0) {
+    if (pa_play_memchunk(sink, &e->sample_spec, &e->channel_map, &e->memchunk, pass_volume ? &r : NULL, merged, sink_input_idx) < 0) {
         pa_proplist_free(merged);
         return -1;
     }
diff --git a/src/pulsecore/core-scache.h b/src/pulsecore/core-scache.h
index ba65a96..a75f8ac 100644
--- a/src/pulsecore/core-scache.h
+++ b/src/pulsecore/core-scache.h
@@ -36,6 +36,7 @@ typedef struct pa_scache_entry {
     char *name;
 
     pa_cvolume volume;
+    pa_bool_t volume_is_set;
     pa_sample_spec sample_spec;
     pa_channel_map channel_map;
     pa_memchunk memchunk;
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 7ddc010..3896dff 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -2840,6 +2840,7 @@ static void source_output_fill_tagstruct(pa_native_connection *c, pa_tagstruct *
 
 static void scache_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_scache_entry *e) {
     pa_sample_spec fixed_ss;
+    pa_cvolume v;
 
     pa_assert(t);
     pa_assert(e);
@@ -2851,7 +2852,13 @@ static void scache_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_s
 
     pa_tagstruct_putu32(t, e->index);
     pa_tagstruct_puts(t, e->name);
-    pa_tagstruct_put_cvolume(t, &e->volume);
+
+    if (e->volume_is_set)
+        v = e->volume;
+    else
+        pa_cvolume_init(&v);
+
+    pa_tagstruct_put_cvolume(t, &v);
     pa_tagstruct_put_usec(t, e->memchunk.memblock ? pa_bytes_to_usec(e->memchunk.length, &e->sample_spec) : 0);
     pa_tagstruct_put_sample_spec(t, &fixed_ss);
     pa_tagstruct_put_channel_map(t, &e->channel_map);

commit eca32235fb38961cf27aa741219fd7c184d73d1e
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 27 04:29:25 2009 +0100

    get rid of module-flat-volumes since we are moving this into the core

diff --git a/src/Makefile.am b/src/Makefile.am
index f313bb5..00dd53d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -863,7 +863,6 @@ libavahi_wrap_la_LIBADD = $(AM_LIBADD) $(AVAHI_CFLAGS) libpulsecore- at PA_MAJORMIN
 ###################################
 
 modlibexec_LTLIBRARIES += \
-		module-flat-volume.la \
 		module-cli.la \
 		module-cli-protocol-tcp.la \
 		module-simple-protocol-tcp.la \
@@ -1086,8 +1085,7 @@ SYMDEF_FILES = \
 		modules/module-raop-discover-symdef.h \
 		modules/gconf/module-gconf-symdef.h \
 		modules/module-position-event-sounds-symdef.h \
-		modules/module-console-kit-symdef.h \
-		modules/module-flat-volume-symdef.h
+		modules/module-console-kit-symdef.h
 
 EXTRA_DIST += $(SYMDEF_FILES)
 BUILT_SOURCES += $(SYMDEF_FILES)
@@ -1096,12 +1094,6 @@ $(SYMDEF_FILES): modules/module-defs.h.m4
 	$(MKDIR_P) $(dir $@)
 	$(M4) -Dfname="$@" $< > $@
 
-# Flat volume
-
-module_flat_volume_la_SOURCES = modules/module-flat-volume.c
-module_flat_volume_la_LDFLAGS = $(MODULE_LDFLAGS)
-module_flat_volume_la_LIBADD = $(AM_LIBADD) libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
-
 # Simple protocol
 
 module_simple_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c
diff --git a/src/daemon/default.pa.in b/src/daemon/default.pa.in
index d761f5f..5d69926 100755
--- a/src/daemon/default.pa.in
+++ b/src/daemon/default.pa.in
@@ -77,9 +77,6 @@ load-module module-native-protocol-unix
 #load-module module-null-sink sink_name=rtp format=s16be channels=2 rate=44100 description="RTP Multicast Sink"
 #load-module module-rtp-send source=rtp.monitor
 
-### Enable flat volumes where possible
-load-module module-flat-volume
-
 ### Automatically restore the default sink/source when changed by the user during runtime
 load-module module-default-device-restore
 
diff --git a/src/modules/module-flat-volume.c b/src/modules/module-flat-volume.c
deleted file mode 100644
index fe6dc92..0000000
--- a/src/modules/module-flat-volume.c
+++ /dev/null
@@ -1,224 +0,0 @@
-/***
-  This file is part of PulseAudio.
-
-  Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
-  Copyright 2004-2006, 2008 Lennart Poettering
-
-  Contact: Marc-Andre Lureau <marc-andre.lureau at nokia.com>
-
-  PulseAudio is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
-  or (at your option) any later version.
-
-  PulseAudio is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with PulseAudio; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-  USA.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <unistd.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <regex.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <pulse/xmalloc.h>
-
-#include <pulsecore/core-error.h>
-#include <pulsecore/module.h>
-#include <pulsecore/core-util.h>
-#include <pulsecore/log.h>
-#include <pulsecore/sink-input.h>
-#include <pulsecore/core-util.h>
-#include <pulsecore/macro.h>
-
-#include "module-flat-volume-symdef.h"
-
-PA_MODULE_AUTHOR("Marc-Andre Lureau");
-PA_MODULE_DESCRIPTION("Flat volume");
-PA_MODULE_VERSION(PACKAGE_VERSION);
-PA_MODULE_LOAD_ONCE(TRUE);
-PA_MODULE_USAGE("");
-
-struct userdata {
-    pa_subscription *subscription;
-    pa_hook_slot *sink_input_set_volume_hook_slot;
-    pa_hook_slot *sink_input_fixate_hook_slot;
-};
-
-static void process_input_volume_change(
-        pa_cvolume *dest_volume,
-        const pa_cvolume *dest_virtual_volume,
-        pa_channel_map *dest_channel_map,
-        pa_sink_input *this,
-        pa_sink *sink) {
-
-    pa_sink_input *i;
-    uint32_t idx;
-    pa_cvolume max_volume, sink_volume;
-
-    pa_assert(dest_volume);
-    pa_assert(dest_virtual_volume);
-    pa_assert(dest_channel_map);
-    pa_assert(sink);
-
-    if (!(sink->flags & PA_SINK_DECIBEL_VOLUME))
-        return;
-
-    pa_log_debug("Sink input volume changed");
-
-    max_volume = *dest_virtual_volume;
-    pa_cvolume_remap(&max_volume, dest_channel_map, &sink->channel_map);
-
-    for (i = PA_SINK_INPUT(pa_idxset_first(sink->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(sink->inputs, &idx))) {
-        /* skip this sink-input if we are processing a volume change request */
-        if (this && this == i)
-            continue;
-
-        if (pa_cvolume_max(&i->virtual_volume) > pa_cvolume_max(&max_volume)) {
-            max_volume = i->virtual_volume;
-            pa_cvolume_remap(&max_volume, &i->channel_map, &sink->channel_map);
-        }
-    }
-
-    /* Set the master volume, and normalize inputs */
-    if (!pa_cvolume_equal(&max_volume, pa_sink_get_volume(sink, TRUE))) {
-
-        pa_sink_set_volume(sink, &max_volume);
-
-        pa_log_debug("sink = %.2f (changed)", (double)pa_cvolume_avg(pa_sink_get_volume(sink, TRUE))/PA_VOLUME_NORM);
-
-        /* Now, normalize each of the internal volume (client sink-input volume / sink master volume) */
-        for (i = PA_SINK_INPUT(pa_idxset_first(sink->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(sink->inputs, &idx))) {
-            /* skip this sink-input if we are processing a volume change request */
-            if (this && this == i)
-                continue;
-
-            sink_volume = max_volume;
-            pa_cvolume_remap(&sink_volume, &sink->channel_map, &i->channel_map);
-            pa_sw_cvolume_divide(&i->volume, &i->virtual_volume, &sink_volume);
-            pa_log_debug("sink input { id = %d, flat = %.2f, true = %.2f }",
-                         i->index,
-                         (double)pa_cvolume_avg(&i->virtual_volume)/PA_VOLUME_NORM,
-                         (double)pa_cvolume_avg(&i->volume)/PA_VOLUME_NORM);
-            pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_VOLUME, pa_xnewdup(struct pa_cvolume, &i->volume, 1), 0, NULL, pa_xfree);
-        }
-    } else
-        pa_log_debug("sink = %.2f", (double)pa_cvolume_avg(pa_sink_get_volume(sink, TRUE))/PA_VOLUME_NORM);
-
-    /* and this one */
-
-    sink_volume = max_volume;
-    pa_cvolume_remap(&sink_volume, &sink->channel_map, dest_channel_map);
-    pa_sw_cvolume_divide(dest_volume, dest_virtual_volume, &sink_volume);
-    pa_log_debug("caller sink input: { id = %d, flat = %.2f, true = %.2f }",
-                 this ? (int)this->index : -1,
-                 (double)pa_cvolume_avg(dest_virtual_volume)/PA_VOLUME_NORM,
-                 (double)pa_cvolume_avg(dest_volume)/PA_VOLUME_NORM);
-}
-
-static pa_hook_result_t sink_input_set_volume_hook_callback(pa_core *c, pa_sink_input_set_volume_data *this, struct userdata *u) {
-    pa_assert(this);
-    pa_assert(this->sink_input);
-
-    process_input_volume_change(&this->volume, &this->virtual_volume, &this->sink_input->channel_map,
-                                this->sink_input, this->sink_input->sink);
-
-    return PA_HOOK_OK;
-}
-
-static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *core, pa_sink_input_new_data *this, struct userdata *u) {
-    pa_assert(this);
-    pa_assert(this->sink);
-
-    process_input_volume_change(&this->volume, &this->virtual_volume, &this->channel_map,
-                                NULL, this->sink);
-
-    return PA_HOOK_OK;
-}
-
-static void subscribe_callback(pa_core *core, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
-    struct userdata *u = userdata;
-    pa_sink *sink;
-    pa_sink_input *i;
-    uint32_t iidx;
-    pa_cvolume sink_volume;
-
-    pa_assert(core);
-    pa_assert(u);
-
-    if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
-        t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE))
-        return;
-
-    if (!(sink = pa_idxset_get_by_index(core->sinks, idx)))
-        return;
-
-    if (!(sink->flags & PA_SINK_DECIBEL_VOLUME))
-        return;
-
-    pa_log_debug("Sink volume changed");
-    pa_log_debug("sink = %.2f", (double)pa_cvolume_avg(pa_sink_get_volume(sink, TRUE)) / PA_VOLUME_NORM);
-
-    sink_volume = *pa_sink_get_volume(sink, TRUE);
-
-    for (i = PA_SINK_INPUT(pa_idxset_first(sink->inputs, &iidx)); i; i = PA_SINK_INPUT(pa_idxset_next(sink->inputs, &iidx))) {
-        pa_cvolume si_volume;
-
-        si_volume = sink_volume;
-        pa_cvolume_remap(&si_volume, &sink->channel_map, &i->channel_map);
-        pa_sw_cvolume_multiply(&i->virtual_volume, &i->volume, &si_volume);
-        pa_log_debug("sink input = { id = %d, flat = %.2f, true = %.2f }",
-                     i->index,
-                     (double)pa_cvolume_avg(&i->virtual_volume)/PA_VOLUME_NORM,
-                     (double)pa_cvolume_avg(&i->volume)/PA_VOLUME_NORM);
-        pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
-    }
-}
-
-int pa__init(pa_module*m) {
-    struct userdata *u;
-
-    pa_assert(m);
-
-    u = pa_xnew(struct userdata, 1);
-    m->userdata = u;
-
-    u->sink_input_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_fixate_hook_callback, u);
-    u->sink_input_set_volume_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_set_volume_hook_callback, u);
-
-    u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK, subscribe_callback, u);
-
-    return 0;
-}
-
-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_input_set_volume_hook_slot)
-        pa_hook_slot_free(u->sink_input_set_volume_hook_slot);
-    if (u->sink_input_fixate_hook_slot)
-        pa_hook_slot_free(u->sink_input_fixate_hook_slot);
-
-    pa_xfree(u);
-}

commit 4bfa5d7d13350bd0eac439dbe251812ce437ea43
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 27 04:35:11 2009 +0100

    fix size calculation

diff --git a/src/pulsecore/bitset.h b/src/pulsecore/bitset.h
index 21e840a..95f5cfc 100644
--- a/src/pulsecore/bitset.h
+++ b/src/pulsecore/bitset.h
@@ -26,7 +26,7 @@
 #include <pulsecore/macro.h>
 
 #define PA_BITSET_ELEMENTS(n) (((n)+31)/32)
-#define PA_BITSET_SIZE(n) (PA_BITSET_ELEMENTS(n)*32)
+#define PA_BITSET_SIZE(n) (PA_BITSET_ELEMENTS(n)*4)
 
 typedef uint32_t pa_bitset_t;
 

commit d5f46e824e3f8a042e6f67dd4c3fc385545edd74
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 27 04:39:07 2009 +0100

    move flat volume logic into the core. while doing so add n_volume_steps field to sinks/sources

diff --git a/man/pulse-daemon.conf.5.xml.in b/man/pulse-daemon.conf.5.xml.in
index a516ee3..9ddcd6a 100644
--- a/man/pulse-daemon.conf.5.xml.in
+++ b/man/pulse-daemon.conf.5.xml.in
@@ -140,7 +140,6 @@ USA.
       precedence.</p>
     </option>
 
-
     <option>
       <p><opt>system-instance=</opt> Run the daemon as system-wide
       instance, requires root priviliges. Takes a boolean argument,
@@ -148,7 +147,6 @@ USA.
       argument takes precedence.</p>
     </option>
 
-
     <option>
       <p><opt>disable-shm=</opt> Disable data transfer via POSIX
       shared memory. Takes a boolean argument, defaults to
@@ -165,6 +163,13 @@ USA.
       memory overcommit.</p>
     </option>
 
+    <option>
+      <p><opt>flat-volumes=</opt> Enable 'flat' volumes, i.e. where
+      possible let the sink volume equal the maximum of the volumes of
+      the inputs connected to it. Takes a boolean argument, defaults
+      to <opt>yes</opt>.</p>
+    </option>
+
   </section>
 
   <section name="Scheduling">
diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c
index c8c1b6f..279fb7b 100644
--- a/src/daemon/daemon-conf.c
+++ b/src/daemon/daemon-conf.c
@@ -64,6 +64,7 @@ static const pa_daemon_conf default_conf = {
     .realtime_priority = 5,  /* Half of JACK's default rtprio */
     .disallow_module_loading = FALSE,
     .disallow_exit = FALSE,
+    .flat_volumes = TRUE,
     .exit_idle_time = 20,
     .scache_idle_time = 20,
     .auto_log_target = 1,
@@ -418,6 +419,7 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
         { "system-instance",            pa_config_parse_bool,     NULL },
         { "no-cpu-limit",               pa_config_parse_bool,     NULL },
         { "disable-shm",                pa_config_parse_bool,     NULL },
+        { "flat-volumes",               pa_config_parse_bool,     NULL },
         { "exit-idle-time",             pa_config_parse_int,      NULL },
         { "scache-idle-time",           pa_config_parse_int,      NULL },
         { "realtime-priority",          parse_rtprio,             NULL },
@@ -490,6 +492,7 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
     table[i++].data = &c->system_instance;
     table[i++].data = &c->no_cpu_limit;
     table[i++].data = &c->disable_shm;
+    table[i++].data = &c->flat_volumes;
     table[i++].data = &c->exit_idle_time;
     table[i++].data = &c->scache_idle_time;
     table[i++].data = c;
@@ -650,6 +653,7 @@ char *pa_daemon_conf_dump(pa_daemon_conf *c) {
     pa_strbuf_printf(s, "system-instance = %s\n", pa_yes_no(c->system_instance));
     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));
     pa_strbuf_printf(s, "exit-idle-time = %i\n", c->exit_idle_time);
     pa_strbuf_printf(s, "scache-idle-time = %i\n", c->scache_idle_time);
     pa_strbuf_printf(s, "dl-search-path = %s\n", pa_strempty(c->dl_search_path));
diff --git a/src/daemon/daemon-conf.h b/src/daemon/daemon-conf.h
index 3b75b83..aa9d298 100644
--- a/src/daemon/daemon-conf.h
+++ b/src/daemon/daemon-conf.h
@@ -70,7 +70,8 @@ typedef struct pa_daemon_conf {
         load_default_script_file,
         disallow_exit,
         log_meta,
-        log_time;
+        log_time,
+        flat_volumes;
     int exit_idle_time,
         scache_idle_time,
         auto_log_target,
diff --git a/src/daemon/daemon.conf.in b/src/daemon/daemon.conf.in
index 00a9593..69d17f2 100644
--- a/src/daemon/daemon.conf.in
+++ b/src/daemon/daemon.conf.in
@@ -53,6 +53,8 @@
 ; disable-remixing = no
 ; disable-lfe-remixing = yes
 
+; flat-volumes = yes
+
 ; no-cpu-limit = no
 
 ; rlimit-fsize = -1
diff --git a/src/daemon/main.c b/src/daemon/main.c
index 9705f4c..bd8ad1d 100644
--- a/src/daemon/main.c
+++ b/src/daemon/main.c
@@ -917,6 +917,7 @@ int main(int argc, char *argv[]) {
     c->disable_lfe_remixing = !!conf->disable_lfe_remixing;
     c->running_as_daemon = !!conf->daemonize;
     c->disallow_exit = conf->disallow_exit;
+    c->flat_volumes = conf->flat_volumes;
 
     pa_assert_se(pa_signal_init(pa_mainloop_get_api(mainloop)) == 0);
     pa_signal_new(SIGINT, signal_callback, c);
diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index 5020eac..3503c4a 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -757,7 +757,7 @@ static long to_alsa_volume(struct userdata *u, pa_volume_t vol) {
     return PA_CLAMP_UNLIKELY(alsa_vol, u->hw_volume_min, u->hw_volume_max);
 }
 
-static int sink_get_volume_cb(pa_sink *s) {
+static void sink_get_volume_cb(pa_sink *s) {
     struct userdata *u = s->userdata;
     int err;
     unsigned i;
@@ -820,27 +820,24 @@ static int sink_get_volume_cb(pa_sink *s) {
 
     if (!pa_cvolume_equal(&u->hardware_volume, &r)) {
 
-        u->hardware_volume = s->volume = r;
+        s->virtual_volume = u->hardware_volume = r;
 
         if (u->hw_dB_supported) {
             pa_cvolume reset;
 
             /* Hmm, so the hardware volume changed, let's reset our software volume */
-
             pa_cvolume_reset(&reset, s->sample_spec.channels);
             pa_sink_set_soft_volume(s, &reset);
         }
     }
 
-    return 0;
+    return;
 
 fail:
     pa_log_error("Unable to read volume: %s", snd_strerror(err));
-
-    return -1;
 }
 
-static int sink_set_volume_cb(pa_sink *s) {
+static void sink_set_volume_cb(pa_sink *s) {
     struct userdata *u = s->userdata;
     int err;
     unsigned i;
@@ -857,7 +854,7 @@ static int sink_set_volume_cb(pa_sink *s) {
             long alsa_vol;
             pa_volume_t vol;
 
-            vol = s->volume.values[i];
+            vol = s->virtual_volume.values[i];
 
             if (u->hw_dB_supported) {
 
@@ -894,7 +891,7 @@ static int sink_set_volume_cb(pa_sink *s) {
         pa_volume_t vol;
         long alsa_vol;
 
-        vol = pa_cvolume_max(&s->volume);
+        vol = pa_cvolume_max(&s->virtual_volume);
 
         if (u->hw_dB_supported) {
             alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100);
@@ -911,7 +908,7 @@ static int sink_set_volume_cb(pa_sink *s) {
             VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol));
 #endif
 
-            pa_cvolume_set(&r, s->volume.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0));
+            pa_cvolume_set(&r, s->sample_spec.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0));
 
         } else {
             alsa_vol = to_alsa_volume(u, vol);
@@ -932,11 +929,9 @@ static int sink_set_volume_cb(pa_sink *s) {
         char t[PA_CVOLUME_SNPRINT_MAX];
 
         /* Match exactly what the user requested by software */
+        pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &r);
 
-        pa_sw_cvolume_divide(&r, &s->volume, &r);
-        pa_sink_set_soft_volume(s, &r);
-
-        pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->volume));
+        pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->virtual_volume));
         pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &u->hardware_volume));
         pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &r));
 
@@ -945,17 +940,15 @@ static int sink_set_volume_cb(pa_sink *s) {
         /* We can't match exactly what the user requested, hence let's
          * at least tell the user about it */
 
-        s->volume = r;
+        s->virtual_volume = r;
 
-    return 0;
+    return;
 
 fail:
     pa_log_error("Unable to set volume: %s", snd_strerror(err));
-
-    return -1;
 }
 
-static int sink_get_mute_cb(pa_sink *s) {
+static void sink_get_mute_cb(pa_sink *s) {
     struct userdata *u = s->userdata;
     int err, sw;
 
@@ -964,15 +957,13 @@ static int sink_get_mute_cb(pa_sink *s) {
 
     if ((err = snd_mixer_selem_get_playback_switch(u->mixer_elem, 0, &sw)) < 0) {
         pa_log_error("Unable to get switch: %s", snd_strerror(err));
-        return -1;
+        return;
     }
 
     s->muted = !sw;
-
-    return 0;
 }
 
-static int sink_set_mute_cb(pa_sink *s) {
+static void sink_set_mute_cb(pa_sink *s) {
     struct userdata *u = s->userdata;
     int err;
 
@@ -981,10 +972,8 @@ static int sink_set_mute_cb(pa_sink *s) {
 
     if ((err = snd_mixer_selem_set_playback_switch_all(u->mixer_elem, !s->muted)) < 0) {
         pa_log_error("Unable to set switch: %s", snd_strerror(err));
-        return -1;
+        return;
     }
-
-    return 0;
 }
 
 static void sink_update_requested_latency_cb(pa_sink *s) {
@@ -1552,6 +1541,8 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
                 u->sink->flags |= PA_SINK_HW_VOLUME_CTRL | (u->hw_dB_supported ? PA_SINK_DECIBEL_VOLUME : 0);
                 pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->hw_dB_supported ? "supported" : "not supported");
 
+                if (!u->hw_dB_supported)
+                    u->sink->n_volume_steps = u->hw_volume_max - u->hw_volume_min + 1;
             } else
                 pa_log_info("Using software volume control.");
         }
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index 96e0d89..c4d3418 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -703,7 +703,7 @@ static long to_alsa_volume(struct userdata *u, pa_volume_t vol) {
     return PA_CLAMP_UNLIKELY(alsa_vol, u->hw_volume_min, u->hw_volume_max);
 }
 
-static int source_get_volume_cb(pa_source *s) {
+static void source_get_volume_cb(pa_source *s) {
     struct userdata *u = s->userdata;
     int err;
     unsigned i;
@@ -766,27 +766,24 @@ static int source_get_volume_cb(pa_source *s) {
 
     if (!pa_cvolume_equal(&u->hardware_volume, &r)) {
 
-        u->hardware_volume = s->volume = r;
+        s->virtual_volume = u->hardware_volume = r;
 
         if (u->hw_dB_supported) {
             pa_cvolume reset;
 
             /* Hmm, so the hardware volume changed, let's reset our software volume */
-
             pa_cvolume_reset(&reset, s->sample_spec.channels);
             pa_source_set_soft_volume(s, &reset);
         }
     }
 
-    return 0;
+    return;
 
 fail:
     pa_log_error("Unable to read volume: %s", snd_strerror(err));
-
-    return -1;
 }
 
-static int source_set_volume_cb(pa_source *s) {
+static void source_set_volume_cb(pa_source *s) {
     struct userdata *u = s->userdata;
     int err;
     unsigned i;
@@ -803,7 +800,7 @@ static int source_set_volume_cb(pa_source *s) {
             long alsa_vol;
             pa_volume_t vol;
 
-            vol = s->volume.values[i];
+            vol = s->virtual_volume.values[i];
 
             if (u->hw_dB_supported) {
 
@@ -840,7 +837,7 @@ static int source_set_volume_cb(pa_source *s) {
         pa_volume_t vol;
         long alsa_vol;
 
-        vol = pa_cvolume_max(&s->volume);
+        vol = pa_cvolume_max(&s->virtual_volume);
 
         if (u->hw_dB_supported) {
             alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100);
@@ -857,7 +854,7 @@ static int source_set_volume_cb(pa_source *s) {
             VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol));
 #endif
 
-            pa_cvolume_set(&r, s->volume.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0));
+            pa_cvolume_set(&r, s->sample_spec.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0));
 
         } else {
             alsa_vol = to_alsa_volume(u, vol);
@@ -879,10 +876,9 @@ static int source_set_volume_cb(pa_source *s) {
 
         /* Match exactly what the user requested by software */
 
-        pa_sw_cvolume_divide(&r, &s->volume, &r);
-        pa_source_set_soft_volume(s, &r);
+        pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &r);
 
-        pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->volume));
+        pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->virtual_volume));
         pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &u->hardware_volume));
         pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &r));
 
@@ -891,17 +887,15 @@ static int source_set_volume_cb(pa_source *s) {
         /* We can't match exactly what the user requested, hence let's
          * at least tell the user about it */
 
-        s->volume = r;
+        s->virtual_volume = r;
 
-    return 0;
+    return;
 
 fail:
     pa_log_error("Unable to set volume: %s", snd_strerror(err));
-
-    return -1;
 }
 
-static int source_get_mute_cb(pa_source *s) {
+static void source_get_mute_cb(pa_source *s) {
     struct userdata *u = s->userdata;
     int err, sw;
 
@@ -910,15 +904,13 @@ static int source_get_mute_cb(pa_source *s) {
 
     if ((err = snd_mixer_selem_get_capture_switch(u->mixer_elem, 0, &sw)) < 0) {
         pa_log_error("Unable to get switch: %s", snd_strerror(err));
-        return -1;
+        return;
     }
 
     s->muted = !sw;
-
-    return 0;
 }
 
-static int source_set_mute_cb(pa_source *s) {
+static void source_set_mute_cb(pa_source *s) {
     struct userdata *u = s->userdata;
     int err;
 
@@ -927,10 +919,8 @@ static int source_set_mute_cb(pa_source *s) {
 
     if ((err = snd_mixer_selem_set_capture_switch_all(u->mixer_elem, !s->muted)) < 0) {
         pa_log_error("Unable to set switch: %s", snd_strerror(err));
-        return -1;
+        return;
     }
-
-    return 0;
 }
 
 static void source_update_requested_latency_cb(pa_source *s) {
@@ -1372,6 +1362,9 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
                 u->source->set_volume = source_set_volume_cb;
                 u->source->flags |= PA_SOURCE_HW_VOLUME_CTRL | (u->hw_dB_supported ? PA_SOURCE_DECIBEL_VOLUME : 0);
                 pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->hw_dB_supported ? "supported" : "not supported");
+
+                if (!u->hw_dB_supported)
+                    u->source->n_volume_steps = u->hw_volume_max - u->hw_volume_min + 1;
             } else
                 pa_log_info("Using software volume control.");
         }
diff --git a/src/modules/module-lirc.c b/src/modules/module-lirc.c
index bbe4f7c..9a782ca 100644
--- a/src/modules/module-lirc.c
+++ b/src/modules/module-lirc.c
@@ -135,7 +135,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event
                                     cv.values[i] = PA_VOLUME_NORM;
                             }
 
-                            pa_sink_set_volume(s, &cv);
+                            pa_sink_set_volume(s, &cv, TRUE, TRUE);
                             break;
 
                         case DOWN:
@@ -146,7 +146,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event
                                     cv.values[i] = PA_VOLUME_MUTED;
                             }
 
-                            pa_sink_set_volume(s, &cv);
+                            pa_sink_set_volume(s, &cv, TRUE, TRUE);
                             break;
 
                         case MUTE:
diff --git a/src/modules/module-mmkbd-evdev.c b/src/modules/module-mmkbd-evdev.c
index aa6832b..a379923 100644
--- a/src/modules/module-mmkbd-evdev.c
+++ b/src/modules/module-mmkbd-evdev.c
@@ -126,7 +126,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event
                                     cv.values[i] = PA_VOLUME_NORM;
                             }
 
-                            pa_sink_set_volume(s, &cv);
+                            pa_sink_set_volume(s, &cv, TRUE, TRUE);
                             break;
 
                         case DOWN:
@@ -137,7 +137,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event
                                     cv.values[i] = PA_VOLUME_MUTED;
                             }
 
-                            pa_sink_set_volume(s, &cv);
+                            pa_sink_set_volume(s, &cv, TRUE, TRUE);
                             break;
 
                         case MUTE_TOGGLE:
diff --git a/src/modules/module-position-event-sounds.c b/src/modules/module-position-event-sounds.c
index 90e693a..e75b1c5 100644
--- a/src/modules/module-position-event-sounds.c
+++ b/src/modules/module-position-event-sounds.c
@@ -101,23 +101,23 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *core, pa_sink_i
 
     pa_log_debug("Positioning event sound '%s' at %0.2f.", pa_strnull(pa_proplist_gets(data->proplist, PA_PROP_EVENT_ID)), f);
 
-    if (!data->volume_is_set) {
-        pa_cvolume_reset(&data->volume, data->sample_spec.channels);
-        data->volume_is_set = TRUE;
+    if (!data->virtual_volume_is_set) {
+        pa_cvolume_reset(&data->virtual_volume, data->sample_spec.channels);
+        data->virtual_volume_is_set = TRUE;
     }
 
     for (c = 0; c < data->sample_spec.channels; c++) {
 
         if (is_left(data->channel_map.map[c]))
-            data->volume.values[c] =
-                pa_sw_volume_multiply(data->volume.values[c], (pa_volume_t) (PA_VOLUME_NORM * (1.0 - f)));
+            data->virtual_volume.values[c] =
+                pa_sw_volume_multiply(data->virtual_volume.values[c], (pa_volume_t) (PA_VOLUME_NORM * (1.0 - f)));
 
         if (is_right(data->channel_map.map[c]))
-            data->volume.values[c] =
-                pa_sw_volume_multiply(data->volume.values[c], (pa_volume_t) (PA_VOLUME_NORM * f));
+            data->virtual_volume.values[c] =
+                pa_sw_volume_multiply(data->virtual_volume.values[c], (pa_volume_t) (PA_VOLUME_NORM * f));
     }
 
-    pa_log_debug("Final volume %s.", pa_cvolume_snprint(t, sizeof(t), &data->volume));
+    pa_log_debug("Final volume %s.", pa_cvolume_snprint(t, sizeof(t), &data->virtual_volume));
 
     return PA_HOOK_OK;
 }
diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c
index bb93ca8..02ef2aa 100644
--- a/src/modules/module-raop-sink.c
+++ b/src/modules/module-raop-sink.c
@@ -255,20 +255,17 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
     return pa_sink_process_msg(o, code, data, offset, chunk);
 }
 
-static int sink_get_volume_cb(pa_sink *s) {
+static void sink_get_volume_cb(pa_sink *s) {
     struct userdata *u = s->userdata;
     int i;
 
     pa_assert(u);
 
-    for (i = 0; i < s->sample_spec.channels; i++) {
-        s->volume.values[i] = u->volume;
-    }
-
-    return 0;
+    for (i = 0; i < s->sample_spec.channels; i++)
+        s->virtual_volume.values[i] = u->volume;
 }
 
-static int sink_set_volume_cb(pa_sink *s) {
+static void sink_set_volume_cb(pa_sink *s) {
     struct userdata *u = s->userdata;
     int rv;
 
@@ -276,39 +273,34 @@ static int sink_set_volume_cb(pa_sink *s) {
 
     /* If we're muted, we fake it */
     if (u->muted)
-        return 0;
+        return;
 
     pa_assert(s->sample_spec.channels > 0);
 
     /* Avoid pointless volume sets */
-    if (u->volume == s->volume.values[0])
-        return 0;
+    if (u->volume == s->virtual_volume.values[0])
+        return;
 
-    rv = pa_raop_client_set_volume(u->raop, s->volume.values[0]);
+    rv = pa_raop_client_set_volume(u->raop, s->virtual_volume.values[0]);
     if (0 == rv)
-        u->volume = s->volume.values[0];
-
-    return rv;
+        u->volume = s->virtual_volume.values[0];
 }
 
-static int sink_get_mute_cb(pa_sink *s) {
+static void sink_get_mute_cb(pa_sink *s) {
     struct userdata *u = s->userdata;
 
     pa_assert(u);
 
     s->muted = u->muted;
-    return 0;
 }
 
-static int sink_set_mute_cb(pa_sink *s) {
+static void sink_set_mute_cb(pa_sink *s) {
     struct userdata *u = s->userdata;
-    int rv;
 
     pa_assert(u);
 
-    rv = pa_raop_client_set_volume(u->raop, (s->muted ? PA_VOLUME_MUTED : u->volume));
+    pa_raop_client_set_volume(u->raop, (s->muted ? PA_VOLUME_MUTED : u->volume));
     u->muted = s->muted;
-    return rv;
 }
 
 static void thread_func(void *userdata) {
diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c
index 734b2c5..464ff2d 100644
--- a/src/modules/module-stream-restore.c
+++ b/src/modules/module-stream-restore.c
@@ -334,9 +334,9 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu
 
         if (u->restore_volume) {
 
-            if (!new_data->volume_is_set) {
+            if (!new_data->virtual_volume_is_set) {
                 pa_log_info("Restoring volume for sink input %s.", name);
-                pa_sink_input_new_data_set_volume(new_data, pa_cvolume_remap(&e->volume, &e->channel_map, &new_data->channel_map));
+                pa_sink_input_new_data_set_virtual_volume(new_data, pa_cvolume_remap(&e->volume, &e->channel_map, &new_data->channel_map));
             } else
                 pa_log_debug("Not restoring volume for sink input %s, because already set.", name);
         }
diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c
index 61b9516..5c7a6e5 100644
--- a/src/modules/module-tunnel.c
+++ b/src/modules/module-tunnel.c
@@ -1056,10 +1056,10 @@ static void sink_input_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag
     pa_assert(u->sink);
 
     if ((u->version < 11 || !!mute == !!u->sink->muted) &&
-        pa_cvolume_equal(&volume, &u->sink->volume))
+        pa_cvolume_equal(&volume, &u->sink->virtual_volume))
         return;
 
-    memcpy(&u->sink->volume, &volume, sizeof(pa_cvolume));
+    memcpy(&u->sink->virtual_volume, &volume, sizeof(pa_cvolume));
 
     if (u->version >= 11)
         u->sink->muted = !!mute;
@@ -1621,7 +1621,7 @@ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata
 #ifdef TUNNEL_SINK
 
 /* Called from main context */
-static int sink_set_volume(pa_sink *sink) {
+static void sink_set_volume(pa_sink *sink) {
     struct userdata *u;
     pa_tagstruct *t;
     uint32_t tag;
@@ -1634,14 +1634,12 @@ static int sink_set_volume(pa_sink *sink) {
     pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_VOLUME);
     pa_tagstruct_putu32(t, tag = u->ctag++);
     pa_tagstruct_putu32(t, u->device_index);
-    pa_tagstruct_put_cvolume(t, &sink->volume);
+    pa_tagstruct_put_cvolume(t, &sink->virtual_volume);
     pa_pstream_send_tagstruct(u->pstream, t);
-
-    return 0;
 }
 
 /* Called from main context */
-static int sink_set_mute(pa_sink *sink) {
+static void sink_set_mute(pa_sink *sink) {
     struct userdata *u;
     pa_tagstruct *t;
     uint32_t tag;
@@ -1651,7 +1649,7 @@ static int sink_set_mute(pa_sink *sink) {
     pa_assert(u);
 
     if (u->version < 11)
-        return -1;
+        return;
 
     t = pa_tagstruct_new(NULL, 0);
     pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_MUTE);
@@ -1659,8 +1657,6 @@ static int sink_set_mute(pa_sink *sink) {
     pa_tagstruct_putu32(t, u->device_index);
     pa_tagstruct_put_boolean(t, !!sink->muted);
     pa_pstream_send_tagstruct(u->pstream, t);
-
-    return 0;
 }
 
 #endif
diff --git a/src/modules/module-volume-restore.c b/src/modules/module-volume-restore.c
index bdfd381..e32552c 100644
--- a/src/modules/module-volume-restore.c
+++ b/src/modules/module-volume-restore.c
@@ -443,7 +443,7 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu
 
         if (r->volume_is_set && data->sample_spec.channels == r->volume.channels) {
             pa_log_info("Restoring volume for <%s>", r->name);
-            pa_sink_input_new_data_set_volume(data, &r->volume);
+            pa_sink_input_new_data_set_virtual_volume(data, &r->volume);
         }
     }
 
diff --git a/src/modules/oss/module-oss.c b/src/modules/oss/module-oss.c
index 7271a08..eac0c8e 100644
--- a/src/modules/oss/module-oss.c
+++ b/src/modules/oss/module-oss.c
@@ -443,7 +443,6 @@ static pa_usec_t io_sink_get_latency(struct userdata *u) {
     return r;
 }
 
-
 static pa_usec_t io_source_get_latency(struct userdata *u) {
     pa_usec_t r = 0;
 
@@ -527,9 +526,6 @@ static int suspend(struct userdata *u) {
     return 0;
 }
 
-static int sink_get_volume(pa_sink *s);
-static int source_get_volume(pa_source *s);
-
 static int unsuspend(struct userdata *u) {
     int m;
     pa_sample_spec ss, *ss_original;
@@ -620,10 +616,10 @@ static int unsuspend(struct userdata *u) {
 
     build_pollfd(u);
 
-    if (u->sink && u->sink->get_volume)
-        u->sink->get_volume(u->sink);
-    if (u->source && u->source->get_volume)
-        u->source->get_volume(u->source);
+    if (u->sink)
+        pa_sink_get_volume(u->sink, TRUE);
+    if (u->source)
+        pa_source_get_volume(u->source, TRUE);
 
     pa_log_info("Resumed successfully...");
 
@@ -798,84 +794,76 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
     return ret;
 }
 
-static int sink_get_volume(pa_sink *s) {
+static void sink_get_volume(pa_sink *s) {
     struct userdata *u;
-    int r;
 
     pa_assert_se(u = s->userdata);
 
     pa_assert(u->mixer_devmask & (SOUND_MASK_VOLUME|SOUND_MASK_PCM));
 
     if (u->mixer_devmask & SOUND_MASK_VOLUME)
-        if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_VOLUME, &s->sample_spec, &s->volume)) >= 0)
-            return r;
+        if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_VOLUME, &s->sample_spec, &s->virtual_volume) >= 0)
+            return;
 
     if (u->mixer_devmask & SOUND_MASK_PCM)
-        if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_PCM, &s->sample_spec, &s->volume)) >= 0)
-            return r;
+        if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_PCM, &s->sample_spec, &s->virtual_volume) >= 0)
+            return;
 
     pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno));
-    return -1;
 }
 
-static int sink_set_volume(pa_sink *s) {
+static void sink_set_volume(pa_sink *s) {
     struct userdata *u;
-    int r;
 
     pa_assert_se(u = s->userdata);
 
     pa_assert(u->mixer_devmask & (SOUND_MASK_VOLUME|SOUND_MASK_PCM));
 
     if (u->mixer_devmask & SOUND_MASK_VOLUME)
-        if ((r = pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_VOLUME, &s->sample_spec, &s->volume)) >= 0)
-            return r;
+        if (pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_VOLUME, &s->sample_spec, &s->virtual_volume) >= 0)
+            return;
 
     if (u->mixer_devmask & SOUND_MASK_PCM)
-        if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_PCM, &s->sample_spec, &s->volume)) >= 0)
-            return r;
+        if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_PCM, &s->sample_spec, &s->virtual_volume) >= 0)
+            return;
 
     pa_log_info("Device doesn't support writing mixer settings: %s", pa_cstrerror(errno));
-    return -1;
 }
 
-static int source_get_volume(pa_source *s) {
+static void source_get_volume(pa_source *s) {
     struct userdata *u;
-    int r;
 
     pa_assert_se(u = s->userdata);
 
     pa_assert(u->mixer_devmask & (SOUND_MASK_IGAIN|SOUND_MASK_RECLEV));
 
     if (u->mixer_devmask & SOUND_MASK_IGAIN)
-        if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_IGAIN, &s->sample_spec, &s->volume)) >= 0)
-            return r;
+        if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_IGAIN, &s->sample_spec, &s->virtual_volume) >= 0)
+            return;
 
     if (u->mixer_devmask & SOUND_MASK_RECLEV)
-        if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_RECLEV, &s->sample_spec, &s->volume)) >= 0)
-            return r;
+        if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_RECLEV, &s->sample_spec, &s->virtual_volume) >= 0)
+            return;
 
     pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno));
-    return -1;
 }
 
-static int source_set_volume(pa_source *s) {
+static void source_set_volume(pa_source *s) {
     struct userdata *u;
-    int r;
 
     pa_assert_se(u = s->userdata);
 
     pa_assert(u->mixer_devmask & (SOUND_MASK_IGAIN|SOUND_MASK_RECLEV));
 
     if (u->mixer_devmask & SOUND_MASK_IGAIN)
-        if ((r = pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_IGAIN, &s->sample_spec, &s->volume)) >= 0)
-            return r;
+        if (pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_IGAIN, &s->sample_spec, &s->virtual_volume) >= 0)
+            return;
 
     if (u->mixer_devmask & SOUND_MASK_RECLEV)
-        if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_RECLEV, &s->sample_spec, &s->volume)) >= 0)
-            return r;
+        if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_RECLEV, &s->sample_spec, &s->virtual_volume) >= 0)
+            return;
 
     pa_log_info("Device doesn't support writing mixer settings: %s", pa_cstrerror(errno));
-    return -1;
 }
 
 static void thread_func(void *userdata) {
@@ -1417,6 +1405,7 @@ int pa__init(pa_module*m) {
                 u->sink->flags |= PA_SINK_HW_VOLUME_CTRL;
                 u->sink->get_volume = sink_get_volume;
                 u->sink->set_volume = sink_set_volume;
+                u->sink->n_volume_steps = 101;
                 do_close = FALSE;
             }
 
@@ -1425,6 +1414,7 @@ int pa__init(pa_module*m) {
                 u->source->flags |= PA_SOURCE_HW_VOLUME_CTRL;
                 u->source->get_volume = source_get_volume;
                 u->source->set_volume = source_set_volume;
+                u->source->n_volume_steps = 101;
                 do_close = FALSE;
             }
         }
diff --git a/src/pulse/def.h b/src/pulse/def.h
index c2c90e9..7517a7a 100644
--- a/src/pulse/def.h
+++ b/src/pulse/def.h
@@ -600,9 +600,13 @@ typedef enum pa_sink_flags {
     PA_SINK_HW_MUTE_CTRL = 0x0010U,
     /**< Supports hardware mute control \since 0.9.11 */
 
-    PA_SINK_DECIBEL_VOLUME = 0x0020U
+    PA_SINK_DECIBEL_VOLUME = 0x0020U,
     /**< Volume can be translated to dB with pa_sw_volume_to_dB()
      * \since 0.9.11 */
+
+    PA_SINK_FLAT_VOLUME = 0x0040U
+    /**< This sink is in flat volume mode, i.e. always the maximum of
+     * the volume of all connected inputs. \since 0.9.15 */
 } pa_sink_flags_t;
 
 /** \cond fulldocs */
@@ -612,6 +616,7 @@ typedef enum pa_sink_flags {
 #define PA_SINK_NETWORK PA_SINK_NETWORK
 #define PA_SINK_HW_MUTE_CTRL PA_SINK_HW_MUTE_CTRL
 #define PA_SINK_DECIBEL_VOLUME PA_SINK_DECIBEL_VOLUME
+#define PA_SINK_FLAT_VOLUME PA_SINK_FLAT_VOLUME
 /** \endcond */
 
 /** Sink state. \since 0.9.15 */
diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c
index c5c9678..1d50939 100644
--- a/src/pulse/introspect.c
+++ b/src/pulse/introspect.c
@@ -159,6 +159,7 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u
             memset(&i, 0, sizeof(i));
             i.proplist = pa_proplist_new();
             i.base_volume = PA_VOLUME_NORM;
+            i.n_volume_steps = PA_VOLUME_NORM+1;
             mute = FALSE;
             state = PA_SINK_INVALID_STATE;
 
@@ -180,7 +181,8 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u
                   pa_tagstruct_get_usec(t, &i.configured_latency) < 0)) ||
                 (o->context->version >= 15 &&
                  (pa_tagstruct_get_volume(t, &i.base_volume) < 0 ||
-                  pa_tagstruct_getu32(t, &state) < 0))) {
+                  pa_tagstruct_getu32(t, &state) < 0 ||
+                  pa_tagstruct_getu32(t, &i.n_volume_steps) < 0))) {
 
                 pa_context_fail(o->context, PA_ERR_PROTOCOL);
                 pa_proplist_free(i.proplist);
@@ -288,6 +290,7 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,
             memset(&i, 0, sizeof(i));
             i.proplist = pa_proplist_new();
             i.base_volume = PA_VOLUME_NORM;
+            i.n_volume_steps = PA_VOLUME_NORM+1;
             mute = FALSE;
             state = PA_SOURCE_INVALID_STATE;
 
@@ -309,7 +312,8 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,
                   pa_tagstruct_get_usec(t, &i.configured_latency) < 0)) ||
                 (o->context->version >= 15 &&
                  (pa_tagstruct_get_volume(t, &i.base_volume) < 0 ||
-                  pa_tagstruct_getu32(t, &state) < 0))) {
+                  pa_tagstruct_getu32(t, &state) < 0 ||
+                  pa_tagstruct_getu32(t, &i.n_volume_steps) < 0))) {
 
                 pa_context_fail(o->context, PA_ERR_PROTOCOL);
                 pa_proplist_free(i.proplist);
diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h
index d8a28ff..badc787 100644
--- a/src/pulse/introspect.h
+++ b/src/pulse/introspect.h
@@ -214,6 +214,7 @@ typedef struct pa_sink_info {
     pa_usec_t configured_latency;      /**< The latency this device has been configured to. \since 0.9.11 */
     pa_volume_t base_volume;           /**< Some kind of "base" volume that refers to unamplified/unattenuated volume in the context of the output device. \since 0.9.15 */
     pa_sink_state_t state;             /**< State \since 0.9.15 */
+    uint32_t n_volume_steps;           /**< Number of volume steps for sinks which do not support arbitrary volumes. \since 0.9.15 */
 } pa_sink_info;
 
 /** Callback prototype for pa_context_get_sink_info_by_name() and friends */
@@ -269,8 +270,9 @@ typedef struct pa_source_info {
     pa_source_flags_t flags;            /**< Flags */
     pa_proplist *proplist;              /**< Property list \since 0.9.11 */
     pa_usec_t configured_latency;       /**< The latency this device has been configured to. \since 0.9.11 */
-    pa_volume_t base_volume;           /**< Some kind of "base" volume that refers to unamplified/unattenuated volume in the context of the input device. \since 0.9.15 */
-    pa_source_state_t state;             /**< State \since 0.9.15 */
+    pa_volume_t base_volume;            /**< Some kind of "base" volume that refers to unamplified/unattenuated volume in the context of the input device. \since 0.9.15 */
+    pa_source_state_t state;            /**< State \since 0.9.15 */
+    uint32_t n_volume_steps;            /**< Number of volume steps for sources which do not support arbitrary volumes. \since 0.9.15 */
 } pa_source_info;
 
 /** Callback prototype for pa_context_get_source_info_by_name() and friends */
diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c
index da3ef1e..b93f24f 100644
--- a/src/pulsecore/cli-command.c
+++ b/src/pulsecore/cli-command.c
@@ -518,7 +518,7 @@ static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
     }
 
     pa_cvolume_set(&cvolume, sink->sample_spec.channels, volume);
-    pa_sink_set_volume(sink, &cvolume);
+    pa_sink_set_volume(sink, &cvolume, TRUE, TRUE);
     return 0;
 }
 
diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index c74ca5d..647fc1b 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -232,11 +232,12 @@ char *pa_sink_list_to_string(pa_core *c) {
             "  %c index: %u\n"
             "\tname: <%s>\n"
             "\tdriver: <%s>\n"
-            "\tflags: %s%s%s%s%s%s\n"
+            "\tflags: %s%s%s%s%s%s%s\n"
             "\tstate: %s\n"
             "\tvolume: %s%s%s\n"
             "\t        balance %0.2f\n"
             "\tbase volume: %s%s%s\n"
+            "\tvolume steps: %u\n"
             "\tmuted: %s\n"
             "\tcurrent latency: %0.2f ms\n"
             "\tconfigured latency: %0.2f ms; range is %0.2f .. %0.2f ms\n"
@@ -257,6 +258,7 @@ char *pa_sink_list_to_string(pa_core *c) {
             sink->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
             sink->flags & PA_SINK_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
             sink->flags & PA_SINK_LATENCY ? "LATENCY " : "",
+            sink->flags & PA_SINK_FLAT_VOLUME ? "FLAT_VOLUME" : "",
             sink_state_to_string(pa_sink_get_state(sink)),
             pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink, FALSE)),
             sink->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t        " : "",
@@ -265,6 +267,7 @@ char *pa_sink_list_to_string(pa_core *c) {
             pa_volume_snprint(v, sizeof(v), sink->base_volume),
             sink->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t             " : "",
             sink->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_volume_snprint_dB(vdb, sizeof(vdb), sink->base_volume) : "",
+            sink->n_volume_steps,
             pa_yes_no(pa_sink_get_mute(sink, FALSE)),
             (double) pa_sink_get_latency(sink) / (double) PA_USEC_PER_MSEC,
             (double) pa_sink_get_requested_latency(sink) / (double) PA_USEC_PER_MSEC,
@@ -275,7 +278,7 @@ char *pa_sink_list_to_string(pa_core *c) {
             sink->monitor_source ? sink->monitor_source->index : PA_INVALID_INDEX,
             pa_sample_spec_snprint(ss, sizeof(ss), &sink->sample_spec),
             pa_channel_map_snprint(cm, sizeof(cm), &sink->channel_map),
-            cmn ? "\n\t            " : "",
+            cmn ? "\n\t             " : "",
             cmn ? cmn : "",
             pa_sink_used_by(sink),
             pa_sink_linked_by(sink));
@@ -327,6 +330,7 @@ char *pa_source_list_to_string(pa_core *c) {
             "\tvolume: %s%s%s\n"
             "\t        balance %0.2f\n"
             "\tbase volume: %s%s%s\n"
+            "\tvolume steps: %u\n"
             "\tmuted: %s\n"
             "\tcurrent latency: %0.2f ms\n"
             "\tconfigured latency: %0.2f ms; range is %0.2f .. %0.2f ms\n"
@@ -353,6 +357,7 @@ char *pa_source_list_to_string(pa_core *c) {
             pa_volume_snprint(v, sizeof(v), source->base_volume),
             source->flags & PA_SOURCE_DECIBEL_VOLUME ? "\n\t             " : "",
             source->flags & PA_SOURCE_DECIBEL_VOLUME ? pa_sw_volume_snprint_dB(vdb, sizeof(vdb), source->base_volume) : "",
+            source->n_volume_steps,
             pa_yes_no(pa_source_get_mute(source, FALSE)),
             (double) pa_source_get_latency(source) / PA_USEC_PER_MSEC,
             (double) pa_source_get_requested_latency(source) / PA_USEC_PER_MSEC,
@@ -361,7 +366,7 @@ char *pa_source_list_to_string(pa_core *c) {
             (unsigned long) pa_source_get_max_rewind(source) / 1024,
             pa_sample_spec_snprint(ss, sizeof(ss), &source->sample_spec),
             pa_channel_map_snprint(cm, sizeof(cm), &source->channel_map),
-            cmn ? "\n\t            " : "",
+            cmn ? "\n\t             " : "",
             cmn ? cmn : "",
             pa_source_used_by(source),
             pa_source_linked_by(source));
@@ -440,7 +445,7 @@ char *pa_source_output_list_to_string(pa_core *c) {
             clt,
             pa_sample_spec_snprint(ss, sizeof(ss), &o->sample_spec),
             pa_channel_map_snprint(cm, sizeof(cm), &o->channel_map),
-            cmn ? "\n\t            " : "",
+            cmn ? "\n\t             " : "",
             cmn ? cmn : "",
             pa_resample_method_to_string(pa_source_output_get_resample_method(o)));
         if (o->module)
@@ -525,7 +530,7 @@ char *pa_sink_input_list_to_string(pa_core *c) {
             clt,
             pa_sample_spec_snprint(ss, sizeof(ss), &i->sample_spec),
             pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
-            cmn ? "\n\t            " : "",
+            cmn ? "\n\t             " : "",
             cmn ? cmn : "",
             pa_resample_method_to_string(pa_sink_input_get_resample_method(i)));
 
@@ -584,7 +589,7 @@ char *pa_scache_list_to_string(pa_core *c) {
                 e->index,
                 ss,
                 cm,
-                cmn ? "\n\t            " : "",
+                cmn ? "\n\t             " : "",
                 cmn ? cmn : "",
                 (long unsigned)(e->memchunk.memblock ? e->memchunk.length : 0),
                 l,
diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c
index c6e96c1..381a677 100644
--- a/src/pulsecore/core.c
+++ b/src/pulsecore/core.c
@@ -127,6 +127,7 @@ pa_core* pa_core_new(pa_mainloop_api *m, pa_bool_t shared, size_t shm_size) {
 
     c->exit_idle_time = -1;
     c->scache_idle_time = 20;
+    c->flat_volumes = TRUE;
 
     c->resample_method = PA_RESAMPLER_SPEEX_FLOAT_BASE + 3;
 
diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index ffac6d3..e33a245 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -55,7 +55,6 @@ typedef enum pa_core_hook {
     PA_CORE_HOOK_SINK_UNLINK_POST,
     PA_CORE_HOOK_SINK_STATE_CHANGED,
     PA_CORE_HOOK_SINK_PROPLIST_CHANGED,
-    PA_CORE_HOOK_SINK_SET_VOLUME,
     PA_CORE_HOOK_SOURCE_NEW,
     PA_CORE_HOOK_SOURCE_FIXATE,
     PA_CORE_HOOK_SOURCE_PUT,
@@ -129,6 +128,7 @@ struct pa_core {
     pa_silence_cache silence_cache;
 
     int exit_idle_time, scache_idle_time;
+    pa_bool_t flat_volumes;
 
     pa_time_event *exit_event;
 
diff --git a/src/pulsecore/play-memblockq.c b/src/pulsecore/play-memblockq.c
index 758c0de..eac21de 100644
--- a/src/pulsecore/play-memblockq.c
+++ b/src/pulsecore/play-memblockq.c
@@ -197,7 +197,7 @@ pa_sink_input* pa_memblockq_sink_input_new(
     data.driver = __FILE__;
     pa_sink_input_new_data_set_sample_spec(&data, ss);
     pa_sink_input_new_data_set_channel_map(&data, map);
-    pa_sink_input_new_data_set_volume(&data, volume);
+    pa_sink_input_new_data_set_virtual_volume(&data, volume);
     pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p);
 
     u->sink_input = pa_sink_input_new(sink->core, &data, 0);
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 3896dff..80edb20 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -1012,7 +1012,7 @@ static playback_stream* playback_stream_new(
     pa_sink_input_new_data_set_sample_spec(&data, ss);
     pa_sink_input_new_data_set_channel_map(&data, map);
     if (volume)
-        pa_sink_input_new_data_set_volume(&data, volume);
+        pa_sink_input_new_data_set_virtual_volume(&data, volume);
     if (muted_set)
         pa_sink_input_new_data_set_muted(&data, muted);
     data.sync_base = ssync ? ssync->sink_input : NULL;
@@ -2691,6 +2691,7 @@ static void sink_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sin
         if (PA_UNLIKELY(pa_sink_get_state(sink) == PA_SINK_INVALID_STATE))
             pa_log_error("Internal sink state is invalid.");
         pa_tagstruct_putu32(t, pa_sink_get_state(sink));
+        pa_tagstruct_putu32(t, sink->n_volume_steps);
     }
 }
 
@@ -2729,6 +2730,7 @@ static void source_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_s
         if (PA_UNLIKELY(pa_source_get_state(source) == PA_SOURCE_INVALID_STATE))
             pa_log_error("Internal source state is invalid.");
         pa_tagstruct_putu32(t, pa_source_get_state(source));
+        pa_tagstruct_putu32(t, source->n_volume_steps);
     }
 }
 
@@ -3164,7 +3166,7 @@ static void command_set_volume(
     CHECK_VALIDITY(c->pstream, si || sink || source, tag, PA_ERR_NOENTITY);
 
     if (sink)
-        pa_sink_set_volume(sink, &volume);
+        pa_sink_set_volume(sink, &volume, TRUE, TRUE);
     else if (source)
         pa_source_set_volume(source, &volume);
     else if (si)
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index d1ffb0f..8b76df0 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -72,11 +72,18 @@ void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const
         data->channel_map = *map;
 }
 
-void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) {
+void pa_sink_input_new_data_set_soft_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) {
     pa_assert(data);
 
-    if ((data->volume_is_set = !!volume))
-        data->volume = data->virtual_volume = *volume;
+    if ((data->soft_volume_is_set = !!volume))
+        data->soft_volume = *volume;
+}
+
+void pa_sink_input_new_data_set_virtual_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) {
+    pa_assert(data);
+
+    if ((data->virtual_volume_is_set = !!volume))
+        data->virtual_volume = *volume;
 }
 
 void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute) {
@@ -153,17 +160,34 @@ pa_sink_input* pa_sink_input_new(
     pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map));
     pa_return_null_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec));
 
-    if (!data->volume_is_set) {
-        pa_cvolume_reset(&data->volume, data->sample_spec.channels);
-        pa_cvolume_reset(&data->virtual_volume, data->sample_spec.channels);
-    }
+    if (!data->virtual_volume_is_set) {
 
-    pa_return_null_if_fail(pa_cvolume_valid(&data->volume));
-    pa_return_null_if_fail(pa_cvolume_compatible(&data->volume, &data->sample_spec));
+        if (data->sink->flags & PA_SINK_FLAT_VOLUME) {
+            data->virtual_volume = data->sink->virtual_volume;
+            pa_cvolume_remap(&data->virtual_volume, &data->sink->channel_map, &data->channel_map);
+        } else
+            pa_cvolume_reset(&data->virtual_volume, data->sample_spec.channels);
+
+    } else if (!data->virtual_volume_is_absolute) {
+
+        /* When the 'absolute' bool is set then we'll treat the volume
+         * as relative to the sink volume even in flat volume mode */
+        if (data->sink->flags & PA_SINK_FLAT_VOLUME) {
+            pa_cvolume t = data->sink->virtual_volume;
+            pa_cvolume_remap(&t, &data->sink->channel_map, &data->channel_map);
+            pa_sw_cvolume_multiply(&data->virtual_volume, &data->virtual_volume, &t);
+        }
+    }
 
     pa_return_null_if_fail(pa_cvolume_valid(&data->virtual_volume));
     pa_return_null_if_fail(pa_cvolume_compatible(&data->virtual_volume, &data->sample_spec));
 
+    if (!data->soft_volume_is_set)
+        data->soft_volume = data->virtual_volume;
+
+    pa_return_null_if_fail(pa_cvolume_valid(&data->soft_volume));
+    pa_return_null_if_fail(pa_cvolume_compatible(&data->soft_volume, &data->sample_spec));
+
     if (!data->muted_is_set)
         data->muted = FALSE;
 
@@ -184,7 +208,8 @@ pa_sink_input* pa_sink_input_new(
     pa_assert(pa_channel_map_valid(&data->channel_map));
 
     /* Due to the fixing of the sample spec the volume might not match anymore */
-    pa_cvolume_remap(&data->volume, &original_cm, &data->channel_map);
+    pa_cvolume_remap(&data->soft_volume, &original_cm, &data->channel_map);
+    pa_cvolume_remap(&data->virtual_volume, &original_cm, &data->channel_map);
 
     if (data->resample_method == PA_RESAMPLER_INVALID)
         data->resample_method = core->resample_method;
@@ -239,7 +264,7 @@ pa_sink_input* pa_sink_input_new(
     i->channel_map = data->channel_map;
 
     i->virtual_volume = data->virtual_volume;
-    i->volume = data->volume;
+    i->soft_volume = data->soft_volume;
 
     i->muted = data->muted;
 
@@ -263,7 +288,7 @@ pa_sink_input* pa_sink_input_new(
     pa_atomic_store(&i->thread_info.drained, 1);
     i->thread_info.sample_spec = i->sample_spec;
     i->thread_info.resampler = resampler;
-    i->thread_info.volume = i->volume;
+    i->thread_info.soft_volume = i->soft_volume;
     i->thread_info.muted = i->muted;
     i->thread_info.requested_sink_latency = (pa_usec_t) -1;
     i->thread_info.rewrite_nbytes = 0;
@@ -395,9 +420,17 @@ void pa_sink_input_unlink(pa_sink_input *i) {
     update_n_corked(i, PA_SINK_INPUT_UNLINKED);
     i->state = PA_SINK_INPUT_UNLINKED;
 
-    if (linked && i->sink)
+    if (linked && i->sink) {
+        /* We might need to update the sink's volume if we are in flat volume mode. */
+        if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
+            pa_cvolume new_volume;
+            pa_sink_update_flat_volume(i->sink, &new_volume);
+            pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE);
+        }
+
         if (i->sink->asyncmsgq)
             pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_REMOVE_INPUT, i, 0, NULL) == 0);
+    }
 
     reset_callbacks(i);
 
@@ -459,7 +492,7 @@ void pa_sink_input_put(pa_sink_input *i) {
     pa_assert(i->process_rewind);
     pa_assert(i->kill);
 
-    i->thread_info.volume = i->volume;
+    i->thread_info.soft_volume = i->soft_volume;
     i->thread_info.muted = i->muted;
 
     state = i->flags & PA_SINK_INPUT_START_CORKED ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING;
@@ -467,6 +500,13 @@ void pa_sink_input_put(pa_sink_input *i) {
     update_n_corked(i, state);
     i->state = state;
 
+    /* We might need to update the sink's volume if we are in flat volume mode. */
+    if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
+        pa_cvolume new_volume;
+        pa_sink_update_flat_volume(i->sink, &new_volume);
+        pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE);
+    }
+
     pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_ADD_INPUT, i, 0, NULL) == 0);
 
     pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, i->index);
@@ -552,7 +592,7 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa
      * it after and leave it for the sink code */
 
     do_volume_adj_here = !pa_channel_map_equal(&i->channel_map, &i->sink->channel_map);
-    volume_is_norm = pa_cvolume_is_norm(&i->thread_info.volume) && !i->thread_info.muted;
+    volume_is_norm = pa_cvolume_is_norm(&i->thread_info.soft_volume) && !i->thread_info.muted;
 
     while (!pa_memblockq_is_readable(i->thread_info.render_memblockq)) {
         pa_memchunk tchunk;
@@ -598,7 +638,7 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa
                 if (i->thread_info.muted)
                     pa_silence_memchunk(&wchunk, &i->thread_info.sample_spec);
                 else
-                    pa_volume_memchunk(&wchunk, &i->thread_info.sample_spec, &i->thread_info.volume);
+                    pa_volume_memchunk(&wchunk, &i->thread_info.sample_spec, &i->thread_info.soft_volume);
             }
 
             if (!i->thread_info.resampler)
@@ -644,7 +684,7 @@ int pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, pa
         /* We've both the same channel map, so let's have the sink do the adjustment for us*/
         pa_cvolume_mute(volume, i->sink->sample_spec.channels);
     else
-        *volume = i->thread_info.volume;
+        *volume = i->thread_info.soft_volume;
 
     return 0;
 }
@@ -816,34 +856,41 @@ pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i) {
 
 /* Called from main context */
 void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume) {
-    pa_sink_input_set_volume_data data;
-
     pa_sink_input_assert_ref(i);
     pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
     pa_assert(volume);
     pa_assert(pa_cvolume_valid(volume));
     pa_assert(pa_cvolume_compatible(volume, &i->sample_spec));
 
-    data.sink_input = i;
-    data.virtual_volume = *volume;
-    data.volume = *volume;
+    if (pa_cvolume_equal(volume, &i->virtual_volume))
+        return;
 
-    /* If you change something here, consider looking into
-     * module-flat-volume.c as well since it uses very similar
-     * code. */
+    i->virtual_volume = *volume;
 
-    if (pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], &data) < 0)
-        return;
+    if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
+        pa_cvolume new_volume;
 
-    if (!pa_cvolume_equal(&i->volume, &data.volume)) {
-        i->volume = data.volume;
-        pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_VOLUME, &data.volume, 0, NULL) == 0);
-    }
+        /* We are in flat volume mode, so let's update all sink input
+         * volumes and update the flat volume of the sink */
 
-    if (!pa_cvolume_equal(&i->virtual_volume, &data.virtual_volume)) {
-        i->virtual_volume = data.virtual_volume;
-        pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+        pa_sink_update_flat_volume(i->sink, &new_volume);
+        pa_sink_set_volume(i->sink, &new_volume, FALSE, TRUE);
+
+    } else {
+
+        /* OK, we are in normal volume mode. The volume only affects
+         * ourselves */
+
+        i->soft_volume = *volume;
+
+        /* Hooks have the ability to play games with i->soft_volume */
+        pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], i);
+
+        pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME, NULL, 0, NULL) == 0);
     }
+
+    /* The virtual volume changed, let's tell people so */
+    pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
 }
 
 /* Called from main context */
@@ -865,7 +912,7 @@ void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute) {
 
     i->muted = mute;
 
-    pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_MUTE, PA_UINT_TO_PTR(mute), 0, NULL, NULL);
+    pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_MUTE, NULL, 0, NULL) == 0);
     pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
 }
 
@@ -1013,15 +1060,22 @@ int pa_sink_input_start_move(pa_sink_input *i) {
     }
     pa_assert(pa_idxset_isempty(i->direct_outputs));
 
-    pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL) == 0);
+    pa_idxset_remove_by_data(i->sink->inputs, i, NULL);
 
     if (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED)
         pa_assert_se(i->sink->n_corked-- >= 1);
 
-    pa_idxset_remove_by_data(i->sink->inputs, i, NULL);
-    i->sink = NULL;
+    /* We might need to update the sink's volume if we are in flat volume mode. */
+    if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
+        pa_cvolume new_volume;
+        pa_sink_update_flat_volume(i->sink, &new_volume);
+        pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE);
+    }
+
+    pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL) == 0);
 
-    pa_sink_update_status(origin);
+    pa_sink_update_status(i->sink);
+    i->sink = NULL;
 
     return 0;
 }
@@ -1092,6 +1146,14 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest) {
     }
 
     pa_sink_update_status(dest);
+
+    /* We might need to update the sink's volume if we are in flat volume mode. */
+    if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
+        pa_cvolume new_volume;
+        pa_sink_update_flat_volume(i->sink, &new_volume);
+        pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE);
+    }
+
     pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_FINISH_MOVE, i, 0, NULL) == 0);
 
     pa_log_debug("Successfully moved sink input %i to %s.", i->index, dest->name);
@@ -1176,14 +1238,18 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t
 
     switch (code) {
 
-        case PA_SINK_INPUT_MESSAGE_SET_VOLUME:
-            i->thread_info.volume = *((pa_cvolume*) userdata);
-            pa_sink_input_request_rewind(i, 0, TRUE, FALSE, FALSE);
+        case PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME:
+            if (!pa_cvolume_equal(&i->thread_info.soft_volume, &i->soft_volume)) {
+                i->thread_info.soft_volume = i->soft_volume;
+                pa_sink_input_request_rewind(i, 0, TRUE, FALSE, FALSE);
+            }
             return 0;
 
-        case PA_SINK_INPUT_MESSAGE_SET_MUTE:
-            i->thread_info.muted = PA_PTR_TO_UINT(userdata);
-            pa_sink_input_request_rewind(i, 0, TRUE, FALSE, FALSE);
+        case PA_SINK_INPUT_MESSAGE_SET_SOFT_MUTE:
+            if (i->thread_info.muted != i->muted) {
+                i->thread_info.muted = i->muted;
+                pa_sink_input_request_rewind(i, 0, TRUE, FALSE, FALSE);
+            }
             return 0;
 
         case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index 66ec613..5bf38de 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -90,9 +90,7 @@ struct pa_sink_input {
 
     pa_sink_input *sync_prev, *sync_next;
 
-    pa_cvolume virtual_volume;
-
-    pa_cvolume volume;
+    pa_cvolume virtual_volume, soft_volume;
     pa_bool_t muted;
 
     pa_resample_method_t requested_resample_method, actual_resample_method;
@@ -170,7 +168,15 @@ struct pa_sink_input {
         pa_sink_input_state_t state;
         pa_atomic_t drained;
 
-        pa_bool_t attached; /* True only between ->attach() and ->detach() calls */
+        pa_cvolume soft_volume;
+        pa_bool_t muted:1;
+
+        pa_bool_t attached:1; /* True only between ->attach() and ->detach() calls */
+
+        /* 0: rewrite nothing, (size_t) -1: rewrite everything, otherwise how many bytes to rewrite */
+        pa_bool_t rewrite_flush:1, dont_rewind_render:1;
+        size_t rewrite_nbytes;
+        uint64_t underrun_for, playing_for;
 
         pa_sample_spec sample_spec;
 
@@ -179,16 +185,8 @@ struct pa_sink_input {
         /* We maintain a history of resampled audio data here. */
         pa_memblockq *render_memblockq;
 
-        /* 0: rewrite nothing, (size_t) -1: rewrite everything, otherwise how many bytes to rewrite */
-        size_t rewrite_nbytes;
-        pa_bool_t rewrite_flush, dont_rewind_render;
-        uint64_t underrun_for, playing_for;
-
         pa_sink_input *sync_prev, *sync_next;
 
-        pa_cvolume volume;
-        pa_bool_t muted;
-
         /* The requested latency for the sink */
         pa_usec_t requested_sink_latency;
 
@@ -202,8 +200,8 @@ PA_DECLARE_CLASS(pa_sink_input);
 #define PA_SINK_INPUT(o) pa_sink_input_cast(o)
 
 enum {
-    PA_SINK_INPUT_MESSAGE_SET_VOLUME,
-    PA_SINK_INPUT_MESSAGE_SET_MUTE,
+    PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME,
+    PA_SINK_INPUT_MESSAGE_SET_SOFT_MUTE,
     PA_SINK_INPUT_MESSAGE_GET_LATENCY,
     PA_SINK_INPUT_MESSAGE_SET_RATE,
     PA_SINK_INPUT_MESSAGE_SET_STATE,
@@ -228,30 +226,26 @@ typedef struct pa_sink_input_new_data {
     pa_sample_spec sample_spec;
     pa_channel_map channel_map;
 
-    pa_cvolume virtual_volume;
-
-    pa_cvolume volume;
+    pa_cvolume virtual_volume, soft_volume;
     pa_bool_t muted:1;
 
     pa_bool_t sample_spec_is_set:1;
     pa_bool_t channel_map_is_set:1;
-    pa_bool_t volume_is_set:1;
+
+    pa_bool_t virtual_volume_is_set:1, soft_volume_is_set:1;
     pa_bool_t muted_is_set:1;
+
+    pa_bool_t virtual_volume_is_absolute:1;
 } pa_sink_input_new_data;
 
 pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data);
 void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const pa_sample_spec *spec);
 void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const pa_channel_map *map);
-void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume);
+void pa_sink_input_new_data_set_soft_volume(pa_sink_input_new_data *data, const pa_cvolume *volume);
+void pa_sink_input_new_data_set_virtual_volume(pa_sink_input_new_data *data, const pa_cvolume *volume);
 void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute);
 void pa_sink_input_new_data_done(pa_sink_input_new_data *data);
 
-typedef struct pa_sink_set_input_volume_data {
-  pa_sink_input *sink_input;
-  pa_cvolume virtual_volume;
-  pa_cvolume volume;
-} pa_sink_input_set_volume_data;
-
 /* To be called by the implementing module only */
 
 pa_sink_input* pa_sink_input_new(
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 083dbaa..7735d3a 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -195,10 +195,10 @@ pa_sink* pa_sink_new(
     s->inputs = pa_idxset_new(NULL, NULL);
     s->n_corked = 0;
 
-    s->volume = data->volume;
+    s->virtual_volume = data->volume;
+    pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
     s->base_volume = PA_VOLUME_NORM;
-    s->virtual_volume = s->volume;
-
+    s->n_volume_steps = PA_VOLUME_NORM+1;
     s->muted = data->muted;
     s->refresh_volume = s->refresh_muted = FALSE;
 
@@ -216,8 +216,8 @@ pa_sink* pa_sink_new(
             0);
 
     s->thread_info.inputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
-    pa_cvolume_reset(&s->thread_info.soft_volume, s->sample_spec.channels);
-    s->thread_info.soft_muted = FALSE;
+    s->thread_info.soft_volume =  s->soft_volume;
+    s->thread_info.soft_muted = s->muted;
     s->thread_info.state = s->state;
     s->thread_info.rewind_nbytes = 0;
     s->thread_info.rewind_requested = FALSE;
@@ -335,10 +335,17 @@ void pa_sink_put(pa_sink* s) {
     if (!(s->flags & PA_SINK_HW_VOLUME_CTRL)) {
         s->flags |= PA_SINK_DECIBEL_VOLUME;
 
-        s->thread_info.soft_volume = s->volume;
+        s->thread_info.soft_volume = s->soft_volume;
         s->thread_info.soft_muted = s->muted;
     }
 
+    if (s->flags & PA_SINK_DECIBEL_VOLUME)
+        s->n_volume_steps = PA_VOLUME_NORM+1;
+
+    if (s->core->flat_volumes)
+        if (s->flags & PA_SINK_DECIBEL_VOLUME)
+            s->flags |= PA_SINK_FLAT_VOLUME;
+
     pa_assert_se(sink_set_state(s, PA_SINK_IDLE) == 0);
 
     pa_source_put(s->monitor_source);
@@ -911,9 +918,100 @@ pa_usec_t pa_sink_get_latency(pa_sink *s) {
 }
 
 /* Called from main thread */
-void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume) {
-    pa_bool_t changed;
-    pa_sink_set_volume_data data;
+void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume) {
+    pa_sink_input *i;
+    uint32_t idx;
+
+    /* This is called whenever a sink input volume changes and we
+     * might need to fix up the sink volume accordingly. Please note
+     * that we don't actually update the sinks volume here, we only
+     * return how it needs to be updated. The caller should then call
+     * pa_sink_set_flat_volume().*/
+
+    pa_cvolume_mute(new_volume, s->channel_map.channels);
+
+    /* First let's determine the new maximum volume of all inputs
+     * connected to this sink */
+    for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
+        unsigned c;
+        pa_cvolume remapped_volume;
+
+        remapped_volume = i->virtual_volume;
+        pa_cvolume_remap(&remapped_volume, &i->channel_map, &s->channel_map);
+
+        for (c = 0; c < new_volume->channels; c++)
+            if (remapped_volume.values[c] > new_volume->values[c])
+                new_volume->values[c] = remapped_volume.values[c];
+    }
+
+    /* Then, let's update the soft volumes of all inputs connected
+     * to this sink */
+    for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
+        pa_cvolume remapped_new_volume;
+
+        remapped_new_volume = *new_volume;
+        pa_cvolume_remap(&remapped_new_volume, &s->channel_map, &i->channel_map);
+        pa_sw_cvolume_divide(&i->soft_volume, &i->virtual_volume, &remapped_new_volume);
+
+        /* Hooks have the ability to play games with i->soft_volume */
+        pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], i);
+
+        /* We don't issue PA_SINK_INPUT_MESSAGE_SET_VOLUME because
+         * we want the update to have atomically with the sink
+         * volume update, hence we do it within the
+         * pa_sink_set_flat_volume() call below*/
+    }
+}
+
+/* Called from main thread */
+void pa_sink_propagate_flat_volume(pa_sink *s, const pa_cvolume *old_volume) {
+    pa_sink_input *i;
+    uint32_t idx;
+
+    pa_sink_assert_ref(s);
+    pa_assert(PA_SINK_IS_LINKED(s->state));
+    pa_assert(old_volume);
+    pa_assert(s->flags & PA_SINK_FLAT_VOLUME);
+
+    /* This is called whenever the sink volume changes that is not
+     * caused by a sink input volume change. We need to fix up the
+     * sink input volumes accordingly */
+
+    for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
+        pa_cvolume remapped_old_volume, remapped_new_volume, fixed_volume;
+        unsigned c;
+
+        remapped_new_volume = s->virtual_volume;
+        pa_cvolume_remap(&remapped_new_volume, &s->channel_map, &i->channel_map);
+
+        remapped_old_volume = *old_volume;
+        pa_cvolume_remap(&remapped_old_volume, &s->channel_map, &i->channel_map);
+
+        for (c = 0; c < i->sample_spec.channels; c++)
+
+            if (remapped_old_volume.values[c] == PA_VOLUME_MUTED)
+                fixed_volume.values[c] = PA_VOLUME_MUTED;
+            else
+                fixed_volume.values[c] = (pa_volume_t)
+                    ((uint64_t) i->virtual_volume.values[c] *
+                     (uint64_t) remapped_new_volume.values[c] /
+                     (uint64_t) remapped_old_volume.values[c]);
+
+        fixed_volume.channels = i->virtual_volume.channels;
+
+        if (!pa_cvolume_equal(&fixed_volume, &i->virtual_volume)) {
+            i->virtual_volume = fixed_volume;
+
+            /* The virtual volume changed, let's tell people so */
+            pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+        }
+    }
+}
+
+/* Called from main thread */
+void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg) {
+    pa_cvolume old_virtual_volume;
+    pa_bool_t virtual_volume_changed;
 
     pa_sink_assert_ref(s);
     pa_assert(PA_SINK_IS_LINKED(s->state));
@@ -921,39 +1019,45 @@ void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume) {
     pa_assert(pa_cvolume_valid(volume));
     pa_assert(pa_cvolume_compatible(volume, &s->sample_spec));
 
-    data.sink = s;
-    data.virtual_volume = data.volume = *volume;
+    old_virtual_volume = s->virtual_volume;
+    s->virtual_volume = *volume;
+    virtual_volume_changed = !pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume);
 
-    changed = !pa_cvolume_equal(&data.virtual_volume, &s->virtual_volume) ||
-        !pa_cvolume_equal(&data.volume, &s->volume);
+    /* Propagate this volume change back to the inputs */
+    if (virtual_volume_changed)
+        if (propagate && (s->flags & PA_SINK_FLAT_VOLUME))
+            pa_sink_propagate_flat_volume(s, &old_virtual_volume);
 
-    if (changed) {
-        if (pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_SET_VOLUME], &data) < 0)
-            return;
+    if (s->set_volume) {
+        /* If we have a function set_volume(), then we do not apply a
+         * soft volume by default. However, set_volume() is apply one
+         * to s->soft_volume */
 
-        changed = !pa_cvolume_equal(&data.virtual_volume, &s->virtual_volume); /* from client-side view */
-    }
+        pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
+        s->set_volume(s);
 
-    s->volume = data.volume;
-    s->virtual_volume = data.virtual_volume;
+    } else
+        /* If we have no function set_volume(), then the soft volume
+         * becomes the virtual volume */
+        s->soft_volume = s->virtual_volume;
 
-    if (s->set_volume && s->set_volume(s) < 0)
-        s->set_volume = NULL;
+    /* This tells the sink that soft and/or virtual volume changed */
+    if (sendmsg)
+        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0);
 
-    if (!s->set_volume)
-        pa_sink_set_soft_volume(s, &s->volume);
-
-    if (changed)
+    if (virtual_volume_changed)
         pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
 }
 
-/* Called from main thread */
+/* Called from main thread. Only to be called by sink implementor */
 void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume) {
     pa_sink_assert_ref(s);
     pa_assert(volume);
 
+    s->soft_volume = *volume;
+
     if (PA_SINK_IS_LINKED(s->state))
-        pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, volume, 0, NULL);
+        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0);
     else
         s->thread_info.soft_volume = *volume;
 }
@@ -961,21 +1065,22 @@ void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume) {
 /* Called from main thread */
 const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh) {
     pa_sink_assert_ref(s);
-    pa_assert(PA_SINK_IS_LINKED(s->state));
 
     if (s->refresh_volume || force_refresh) {
-        struct pa_cvolume old_volume = s->virtual_volume;
+        struct pa_cvolume old_virtual_volume = s->virtual_volume;
 
-        if (s->get_volume && s->get_volume(s) < 0)
-            s->get_volume = NULL;
+        if (s->get_volume)
+            s->get_volume(s);
 
-        if (!s->get_volume) {
-            pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_VOLUME, &s->volume, 0, NULL);
-            s->virtual_volume = s->volume;
-        }
+        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_VOLUME, NULL, 0, NULL) == 0);
+
+        if (!pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume)) {
+
+            if (s->flags & PA_SINK_FLAT_VOLUME)
+                pa_sink_propagate_flat_volume(s, &old_virtual_volume);
 
-        if (!pa_cvolume_equal(&old_volume, &s->virtual_volume))
             pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+        }
     }
 
     return &s->virtual_volume;
@@ -983,21 +1088,20 @@ const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh) {
 
 /* Called from main thread */
 void pa_sink_set_mute(pa_sink *s, pa_bool_t mute) {
-    pa_bool_t changed;
+    pa_bool_t old_muted;
 
     pa_sink_assert_ref(s);
     pa_assert(PA_SINK_IS_LINKED(s->state));
 
-    changed = s->muted != mute;
+    old_muted = s->muted;
     s->muted = mute;
 
-    if (s->set_mute && s->set_mute(s) < 0)
-        s->set_mute = NULL;
+    if (s->set_mute)
+        s->set_mute(s);
 
-    if (!s->set_mute)
-        pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MUTE, PA_UINT_TO_PTR(mute), 0, NULL, NULL);
+    pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0);
 
-    if (changed)
+    if (old_muted != s->muted)
         pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
 }
 
@@ -1005,16 +1109,14 @@ void pa_sink_set_mute(pa_sink *s, pa_bool_t mute) {
 pa_bool_t pa_sink_get_mute(pa_sink *s, pa_bool_t force_refresh) {
 
     pa_sink_assert_ref(s);
-    pa_assert(PA_SINK_IS_LINKED(s->state));
 
     if (s->refresh_muted || force_refresh) {
         pa_bool_t old_muted = s->muted;
 
-        if (s->get_mute && s->get_mute(s) < 0)
-            s->get_mute = NULL;
+        if (s->get_mute)
+            s->get_mute(s);
 
-        if (!s->get_mute)
-            pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MUTE, &s->muted, 0, NULL);
+        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_MUTE, NULL, 0, NULL) == 0);
 
         if (old_muted != s->muted)
             pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
@@ -1138,6 +1240,22 @@ unsigned pa_sink_check_suspend(pa_sink *s) {
     return ret;
 }
 
+/* Called from the IO thread */
+static void sync_input_volumes_within_thread(pa_sink *s) {
+    pa_sink_input *i;
+    void *state = NULL;
+
+    pa_sink_assert_ref(s);
+
+    while ((i = PA_SINK_INPUT(pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))) {
+        if (pa_cvolume_equal(&i->thread_info.soft_volume, &i->soft_volume))
+            continue;
+
+        i->thread_info.soft_volume = i->soft_volume;
+        pa_sink_input_request_rewind(i, 0, TRUE, FALSE, FALSE);
+    }
+}
+
 /* Called from IO thread, except when it is not */
 int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
     pa_sink *s = PA_SINK(o);
@@ -1192,7 +1310,9 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
              * slow start, i.e. need some time to buffer client
              * samples before beginning streaming. */
 
-            return 0;
+            /* In flat volume mode we need to update the volume as
+             * well */
+            return o->process_msg(o, PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL);
         }
 
         case PA_SINK_MESSAGE_REMOVE_INPUT: {
@@ -1233,7 +1353,9 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
             pa_sink_invalidate_requested_latency(s);
             pa_sink_request_rewind(s, (size_t) -1);
 
-            return 0;
+            /* In flat volume mode we need to update the volume as
+             * well */
+            return o->process_msg(o, PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL);
         }
 
         case PA_SINK_MESSAGE_START_MOVE: {
@@ -1279,7 +1401,9 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
             pa_log_debug("Requesting rewind due to started move");
             pa_sink_request_rewind(s, (size_t) -1);
 
-            return 0;
+            /* In flat volume mode we need to update the volume as
+             * well */
+            return o->process_msg(o, PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL);
         }
 
         case PA_SINK_MESSAGE_FINISH_MOVE: {
@@ -1323,27 +1447,36 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
                 pa_sink_request_rewind(s, nbytes);
             }
 
-            return 0;
+            /* In flat volume mode we need to update the volume as
+             * well */
+            return o->process_msg(o, PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL);
         }
 
         case PA_SINK_MESSAGE_SET_VOLUME:
-            s->thread_info.soft_volume = *((pa_cvolume*) userdata);
 
-            pa_sink_request_rewind(s, (size_t) -1);
-            return 0;
+            if (!pa_cvolume_equal(&s->thread_info.soft_volume, &s->soft_volume)) {
+                s->thread_info.soft_volume = s->soft_volume;
+                pa_sink_request_rewind(s, (size_t) -1);
+            }
 
-        case PA_SINK_MESSAGE_SET_MUTE:
-            s->thread_info.soft_muted = PA_PTR_TO_UINT(userdata);
+            if (s->flags & PA_SINK_FLAT_VOLUME)
+                sync_input_volumes_within_thread(s);
 
-            pa_sink_request_rewind(s, (size_t) -1);
             return 0;
 
         case PA_SINK_MESSAGE_GET_VOLUME:
-            *((pa_cvolume*) userdata) = s->thread_info.soft_volume;
+            return 0;
+
+        case PA_SINK_MESSAGE_SET_MUTE:
+
+            if (!s->thread_info.soft_muted != s->muted) {
+                s->thread_info.soft_muted = s->muted;
+                pa_sink_request_rewind(s, (size_t) -1);
+            }
+
             return 0;
 
         case PA_SINK_MESSAGE_GET_MUTE:
-            *((pa_bool_t*) userdata) = s->thread_info.soft_muted;
             return 0;
 
         case PA_SINK_MESSAGE_SET_STATE:
@@ -1676,6 +1809,7 @@ void pa_sink_update_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t m
     pa_source_update_latency_range(s->monitor_source, min_latency, max_latency);
 }
 
+/* Called from main context */
 size_t pa_sink_get_max_rewind(pa_sink *s) {
     size_t r;
     pa_sink_assert_ref(s);
@@ -1688,6 +1822,7 @@ size_t pa_sink_get_max_rewind(pa_sink *s) {
     return r;
 }
 
+/* Called from main context */
 size_t pa_sink_get_max_request(pa_sink *s) {
     size_t r;
     pa_sink_assert_ref(s);
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index a30245d..b26ca77 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -71,11 +71,11 @@ struct pa_sink {
     unsigned n_corked;
     pa_source *monitor_source;
 
-    pa_cvolume volume;
-    pa_cvolume virtual_volume;
-    pa_bool_t muted;
+    pa_volume_t base_volume; /* shall be constant */
+    unsigned n_volume_steps; /* shall be constant */
 
-    pa_volume_t base_volume;  /* shall be constant */
+    pa_cvolume virtual_volume, soft_volume;
+    pa_bool_t muted:1;
 
     pa_bool_t refresh_volume:1;
     pa_bool_t refresh_muted:1;
@@ -94,23 +94,23 @@ struct pa_sink {
      * context. If this is NULL a PA_SINK_MESSAGE_GET_VOLUME message
      * will be sent to the IO thread instead. If refresh_volume is
      * FALSE neither this function is called nor a message is sent. */
-    int (*get_volume)(pa_sink *s);             /* may be NULL */
+    void (*get_volume)(pa_sink *s);             /* may be NULL */
 
     /* Called when the volume shall be changed. Called from main loop
      * context. If this is NULL a PA_SINK_MESSAGE_SET_VOLUME message
      * will be sent to the IO thread instead. */
-    int (*set_volume)(pa_sink *s);             /* dito */
+    void (*set_volume)(pa_sink *s);             /* dito */
 
     /* Called when the mute setting is queried. Called from main loop
      * context. If this is NULL a PA_SINK_MESSAGE_GET_MUTE message
      * will be sent to the IO thread instead. If refresh_mute is
      * FALSE neither this function is called nor a message is sent.*/
-    int (*get_mute)(pa_sink *s);               /* dito */
+    void (*get_mute)(pa_sink *s);               /* dito */
 
     /* Called when the mute setting shall be changed. Called from main
      * loop context. If this is NULL a PA_SINK_MESSAGE_SET_MUTE
      * message will be sent to the IO thread instead. */
-    int (*set_mute)(pa_sink *s);               /* dito */
+    void (*set_mute)(pa_sink *s);               /* dito */
 
     /* Called when a rewind request is issued. Called from IO thread
      * context. */
@@ -125,6 +125,7 @@ struct pa_sink {
     struct {
         pa_sink_state_t state;
         pa_hashmap *inputs;
+
         pa_cvolume soft_volume;
         pa_bool_t soft_muted:1;
 
@@ -203,13 +204,7 @@ void pa_sink_new_data_set_volume(pa_sink_new_data *data, const pa_cvolume *volum
 void pa_sink_new_data_set_muted(pa_sink_new_data *data, pa_bool_t mute);
 void pa_sink_new_data_done(pa_sink_new_data *data);
 
-typedef struct pa_sink_set_volume_data {
-  pa_sink *sink;
-  pa_cvolume volume;
-  pa_cvolume virtual_volume;
-} pa_sink_set_volume_data;
-
-/* To be called exclusively by the sink driver, from main context */
+/*** To be called exclusively by the sink driver, from main context */
 
 pa_sink* pa_sink_new(
         pa_core *core,
@@ -228,7 +223,9 @@ void pa_sink_set_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_
 void pa_sink_detach(pa_sink *s);
 void pa_sink_attach(pa_sink *s);
 
-/* May be called by everyone, from main context */
+void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume);
+
+/**** May be called by everyone, from main context */
 
 /* The returned value is supposed to be in the time domain of the sound card! */
 pa_usec_t pa_sink_get_latency(pa_sink *s);
@@ -242,11 +239,13 @@ int pa_sink_update_status(pa_sink*s);
 int pa_sink_suspend(pa_sink *s, pa_bool_t suspend);
 int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend);
 
-void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume);
-void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume);
+void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume);
+void pa_sink_propagate_flat_volume(pa_sink *s, const pa_cvolume *old_volume);
+
+void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg);
 const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_bool_t force_refresh);
 void pa_sink_set_mute(pa_sink *sink, pa_bool_t mute);
-pa_bool_t pa_sink_get_mute(pa_sink *sink, pa_bool_t force_refres);
+pa_bool_t pa_sink_get_mute(pa_sink *sink, pa_bool_t force_refresh);
 
 pa_bool_t pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p);
 
@@ -260,7 +259,7 @@ pa_queue *pa_sink_move_all_start(pa_sink *s);
 void pa_sink_move_all_finish(pa_sink *s, pa_queue *q);
 void pa_sink_move_all_fail(pa_queue *q);
 
-/* To be called exclusively by the sink driver, from IO context */
+/*** To be called exclusively by the sink driver, from IO context */
 
 void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result);
 void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result);
@@ -281,7 +280,7 @@ void pa_sink_set_max_request(pa_sink *s, size_t max_request);
 
 void pa_sink_update_latency_range(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency);
 
-/* To be called exclusively by sink input drivers, from IO context */
+/*** To be called exclusively by sink input drivers, from IO context */
 
 void pa_sink_request_rewind(pa_sink*s, size_t nbytes);
 
diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c
index b78afca..1be421f 100644
--- a/src/pulsecore/sound-file-stream.c
+++ b/src/pulsecore/sound-file-stream.c
@@ -322,7 +322,7 @@ int pa_play_file(
     data.sink = sink;
     data.driver = __FILE__;
     pa_sink_input_new_data_set_sample_spec(&data, &ss);
-    pa_sink_input_new_data_set_volume(&data, volume);
+    pa_sink_input_new_data_set_virtual_volume(&data, volume);
     pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, pa_path_get_filename(fname));
     pa_proplist_sets(data.proplist, PA_PROP_MEDIA_FILENAME, fname);
 
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index 1d21ffb..204e06c 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -685,15 +685,15 @@ int pa_source_output_start_move(pa_source_output *o) {
 
     origin = o->source;
 
-    pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL) == 0);
+    pa_idxset_remove_by_data(o->source->outputs, o, NULL);
 
     if (pa_source_output_get_state(o) == PA_SOURCE_OUTPUT_CORKED)
         pa_assert_se(origin->n_corked-- >= 1);
 
-    pa_idxset_remove_by_data(o->source->outputs, o, NULL);
-    o->source = NULL;
+    pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL) == 0);
 
-    pa_source_update_status(origin);
+    pa_source_update_status(o->source);
+    o->source = NULL;
 
     return 0;
 }
@@ -707,6 +707,9 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest) {
     pa_assert(!o->source);
     pa_source_assert_ref(dest);
 
+    if (!pa_source_output_may_move_to(o, dest))
+        return -1;
+
     o->source = dest;
     pa_idxset_put(o->source->outputs, o, NULL);
 
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index 3cddd09..cd6ebc3 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -187,10 +187,12 @@ pa_source* pa_source_new(
     s->n_corked = 0;
     s->monitor_of = NULL;
 
-    s->volume = data->volume;
+    s->virtual_volume = data->volume;
+    pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
+    s->base_volume = PA_VOLUME_NORM;
+    s->n_volume_steps = PA_VOLUME_NORM+1;
     s->muted = data->muted;
     s->refresh_volume = s->refresh_muted = FALSE;
-    s->base_volume = PA_VOLUME_NORM;
 
     reset_callbacks(s);
     s->userdata = NULL;
@@ -206,8 +208,8 @@ pa_source* pa_source_new(
             0);
 
     s->thread_info.outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
-    pa_cvolume_reset(&s->thread_info.soft_volume, s->sample_spec.channels);
-    s->thread_info.soft_muted = FALSE;
+    s->thread_info.soft_volume = s->soft_volume;
+    s->thread_info.soft_muted = s->muted;
     s->thread_info.state = s->state;
     s->thread_info.max_rewind = 0;
     s->thread_info.requested_latency_valid = FALSE;
@@ -293,10 +295,13 @@ void pa_source_put(pa_source *s) {
     if (!(s->flags & PA_SOURCE_HW_VOLUME_CTRL)) {
         s->flags |= PA_SOURCE_DECIBEL_VOLUME;
 
-        s->thread_info.soft_volume = s->volume;
+        s->thread_info.soft_volume = s->soft_volume;
         s->thread_info.soft_muted = s->muted;
     }
 
+    if (s->flags & PA_SOURCE_DECIBEL_VOLUME)
+        s->n_volume_steps = PA_VOLUME_NORM+1;
+
     pa_assert_se(source_set_state(s, PA_SOURCE_IDLE) == 0);
 
     pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_NEW, s->index);
@@ -568,32 +573,38 @@ pa_usec_t pa_source_get_latency(pa_source *s) {
 
 /* Called from main thread */
 void pa_source_set_volume(pa_source *s, const pa_cvolume *volume) {
-    pa_bool_t changed;
+    pa_cvolume old_virtual_volume;
+    pa_bool_t virtual_volume_changed;
 
     pa_source_assert_ref(s);
     pa_assert(PA_SOURCE_IS_LINKED(s->state));
     pa_assert(volume);
+    pa_assert(pa_cvolume_valid(volume));
+    pa_assert(pa_cvolume_compatible(volume, &s->sample_spec));
 
-    changed = !pa_cvolume_equal(volume, &s->volume);
-    s->volume = *volume;
+    old_virtual_volume = s->virtual_volume;
+    s->virtual_volume = *volume;
+    virtual_volume_changed = !pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume);
 
-    if (s->set_volume && s->set_volume(s) < 0)
-        s->set_volume = NULL;
+    if (s->set_volume) {
+        pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
+        s->set_volume(s);
+    } else
+        s->soft_volume = s->virtual_volume;
 
-    if (!s->set_volume)
-        pa_source_set_soft_volume(s, volume);
+    pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0);
 
-    if (changed)
+    if (virtual_volume_changed)
         pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
 }
 
-/* Called from main thread */
+/* Called from main thread. Only to be called by source implementor */
 void pa_source_set_soft_volume(pa_source *s, const pa_cvolume *volume) {
     pa_source_assert_ref(s);
     pa_assert(volume);
 
     if (PA_SOURCE_IS_LINKED(s->state))
-        pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_VOLUME, volume, 0, NULL);
+        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0);
     else
         s->thread_info.soft_volume = *volume;
 }
@@ -604,38 +615,36 @@ const pa_cvolume *pa_source_get_volume(pa_source *s, pa_bool_t force_refresh) {
     pa_assert(PA_SOURCE_IS_LINKED(s->state));
 
     if (s->refresh_volume || force_refresh) {
-        pa_cvolume old_volume = s->volume;
+        pa_cvolume old_virtual_volume = s->virtual_volume;
 
-        if (s->get_volume && s->get_volume(s) < 0)
-            s->get_volume = NULL;
+        if (s->get_volume)
+            s->get_volume(s);
 
-        if (!s->get_volume)
-            pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_VOLUME, &s->volume, 0, NULL);
+        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_VOLUME, NULL, 0, NULL) == 0);
 
-        if (!pa_cvolume_equal(&old_volume, &s->volume))
+        if (!pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume))
             pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
     }
 
-    return &s->volume;
+    return &s->virtual_volume;
 }
 
 /* Called from main thread */
 void pa_source_set_mute(pa_source *s, pa_bool_t mute) {
-    pa_bool_t changed;
+    pa_bool_t old_muted;
 
     pa_source_assert_ref(s);
     pa_assert(PA_SOURCE_IS_LINKED(s->state));
 
-    changed = s->muted != mute;
+    old_muted = s->muted;
     s->muted = mute;
 
-    if (s->set_mute && s->set_mute(s) < 0)
-        s->set_mute = NULL;
+    if (s->set_mute)
+        s->set_mute(s);
 
-    if (!s->set_mute)
-        pa_asyncmsgq_post(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_MUTE, PA_UINT_TO_PTR(mute), 0, NULL, NULL);
+    pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0);
 
-    if (changed)
+    if (old_muted != s->muted)
         pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
 }
 
@@ -648,11 +657,10 @@ pa_bool_t pa_source_get_mute(pa_source *s, pa_bool_t force_refresh) {
     if (s->refresh_muted || force_refresh) {
         pa_bool_t old_muted = s->muted;
 
-        if (s->get_mute && s->get_mute(s) < 0)
-            s->get_mute = NULL;
+        if (s->get_mute)
+            s->get_mute(s);
 
-        if (!s->get_mute)
-            pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_MUTE, &s->muted, 0, NULL);
+        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_MUTE, NULL, 0, NULL) == 0);
 
         if (old_muted != s->muted)
             pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
@@ -661,6 +669,7 @@ pa_bool_t pa_source_get_mute(pa_source *s, pa_bool_t force_refresh) {
     return s->muted;
 }
 
+/* Called from main thread */
 pa_bool_t pa_source_update_proplist(pa_source *s, pa_update_mode_t mode, pa_proplist *p) {
 
     pa_source_assert_ref(s);
@@ -814,19 +823,17 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
         }
 
         case PA_SOURCE_MESSAGE_SET_VOLUME:
-            s->thread_info.soft_volume = *((pa_cvolume*) userdata);
+            s->thread_info.soft_volume = s->soft_volume;
             return 0;
 
-        case PA_SOURCE_MESSAGE_SET_MUTE:
-            s->thread_info.soft_muted = PA_PTR_TO_UINT(userdata);
+        case PA_SOURCE_MESSAGE_GET_VOLUME:
             return 0;
 
-        case PA_SOURCE_MESSAGE_GET_VOLUME:
-            *((pa_cvolume*) userdata) = s->thread_info.soft_volume;
+        case PA_SOURCE_MESSAGE_SET_MUTE:
+            s->thread_info.soft_muted = s->muted;
             return 0;
 
         case PA_SOURCE_MESSAGE_GET_MUTE:
-            *((pa_bool_t*) userdata) = s->thread_info.soft_muted;
             return 0;
 
         case PA_SOURCE_MESSAGE_SET_STATE:
diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
index 479cade..de23977 100644
--- a/src/pulsecore/source.h
+++ b/src/pulsecore/source.h
@@ -73,10 +73,11 @@ struct pa_source {
     unsigned n_corked;
     pa_sink *monitor_of;                     /* may be NULL */
 
-    pa_cvolume volume;
-    pa_bool_t muted;
-
     pa_volume_t base_volume; /* shall be constant */
+    unsigned n_volume_steps; /* shall be constant */
+
+    pa_cvolume virtual_volume, soft_volume;
+    pa_bool_t muted:1;
 
     pa_bool_t refresh_volume:1;
     pa_bool_t refresh_muted:1;
@@ -95,23 +96,23 @@ struct pa_source {
      * context. If this is NULL a PA_SOURCE_MESSAGE_GET_VOLUME message
      * will be sent to the IO thread instead. If refresh_volume is
      * FALSE neither this function is called nor a message is sent. */
-    int (*get_volume)(pa_source *s);         /* dito */
+    void (*get_volume)(pa_source *s);         /* dito */
 
     /* Called when the volume shall be changed. Called from main loop
      * context. If this is NULL a PA_SOURCE_MESSAGE_SET_VOLUME message
      * will be sent to the IO thread instead. */
-    int (*set_volume)(pa_source *s);         /* dito */
+    void (*set_volume)(pa_source *s);         /* dito */
 
     /* Called when the mute setting is queried. Called from main loop
      * context. If this is NULL a PA_SOURCE_MESSAGE_GET_MUTE message
      * will be sent to the IO thread instead. If refresh_mute is
      * FALSE neither this function is called nor a message is sent.*/
-    int (*get_mute)(pa_source *s);           /* dito */
+    void (*get_mute)(pa_source *s);           /* dito */
 
     /* Called when the mute setting shall be changed. Called from main
      * loop context. If this is NULL a PA_SOURCE_MESSAGE_SET_MUTE
      * message will be sent to the IO thread instead. */
-    int (*set_mute)(pa_source *s);           /* dito */
+    void (*set_mute)(pa_source *s);           /* dito */
 
     /* Called when a the requested latency is changed. Called from IO
      * thread context. */
@@ -122,6 +123,7 @@ struct pa_source {
     struct {
         pa_source_state_t state;
         pa_hashmap *outputs;
+
         pa_cvolume soft_volume;
         pa_bool_t soft_muted:1;
 
@@ -189,7 +191,7 @@ void pa_source_new_data_set_volume(pa_source_new_data *data, const pa_cvolume *v
 void pa_source_new_data_set_muted(pa_source_new_data *data, pa_bool_t mute);
 void pa_source_new_data_done(pa_source_new_data *data);
 
-/* To be called exclusively by the source driver, from main context */
+/*** To be called exclusively by the source driver, from main context */
 
 pa_source* pa_source_new(
         pa_core *core,
@@ -208,7 +210,9 @@ void pa_source_set_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t
 void pa_source_detach(pa_source *s);
 void pa_source_attach(pa_source *s);
 
-/* May be called by everyone, from main context */
+void pa_source_set_soft_volume(pa_source *s, const pa_cvolume *volume);
+
+/*** May be called by everyone, from main context */
 
 /* The returned value is supposed to be in the time domain of the sound card! */
 pa_usec_t pa_source_get_latency(pa_source *s);
@@ -222,7 +226,6 @@ int pa_source_suspend(pa_source *s, pa_bool_t suspend);
 int pa_source_suspend_all(pa_core *c, pa_bool_t suspend);
 
 void pa_source_set_volume(pa_source *source, const pa_cvolume *volume);
-void pa_source_set_soft_volume(pa_source *s, const pa_cvolume *volume);
 const pa_cvolume *pa_source_get_volume(pa_source *source, pa_bool_t force_refresh);
 void pa_source_set_mute(pa_source *source, pa_bool_t mute);
 pa_bool_t pa_source_get_mute(pa_source *source, pa_bool_t force_refresh);
@@ -239,7 +242,7 @@ pa_queue *pa_source_move_all_start(pa_source *s);
 void pa_source_move_all_finish(pa_source *s, pa_queue *q);
 void pa_source_move_all_fail(pa_queue *q);
 
-/* To be called exclusively by the source driver, from IO context */
+/*** To be called exclusively by the source driver, from IO context */
 
 void pa_source_post(pa_source*s, const pa_memchunk *chunk);
 void pa_source_post_direct(pa_source*s, pa_source_output *o, const pa_memchunk *chunk);
@@ -255,7 +258,7 @@ pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s);
 void pa_source_set_max_rewind(pa_source *s, size_t max_rewind);
 void pa_source_update_latency_range(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency);
 
-/* To be called exclusively by source output drivers, from IO context */
+/*** To be called exclusively by source output drivers, from IO context */
 
 void pa_source_invalidate_requested_latency(pa_source *s);
 

commit 0ca16caff7658ec0624a17ad43ce48645696534a
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 27 17:55:50 2009 +0100

    add new paramter ignore_dB= to alsa modules

diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index 3503c4a..4328957 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -1236,7 +1236,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
     size_t frame_size;
     snd_pcm_info_t *pcm_info = NULL;
     int err;
-    pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d;
+    pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE;
     pa_usec_t usec;
     pa_sink_new_data data;
 
@@ -1282,6 +1282,11 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
         goto fail;
     }
 
+    if (pa_modargs_get_value_boolean(ma, "ignore_dB", &ignore_dB) < 0) {
+        pa_log("Failed to parse ignore_dB argument.");
+        goto fail;
+    }
+
     if (use_tsched && !pa_rtclock_hrtimer()) {
         pa_log_notice("Disabling timer-based scheduling because high-resolution timers are not available from the kernel.");
         use_tsched = FALSE;
@@ -1503,8 +1508,8 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
             }
 
             if (suitable) {
-                if (snd_mixer_selem_get_playback_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) < 0)
-                    pa_log_info("Mixer doesn't support dB information.");
+                if (ignore_dB || snd_mixer_selem_get_playback_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) < 0)
+                    pa_log_info("Mixer doesn't support dB information or data is ignored.");
                 else {
 #ifdef HAVE_VALGRIND_MEMCHECK_H
                     VALGRIND_MAKE_MEM_DEFINED(&u->hw_dB_min, sizeof(u->hw_dB_min));
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index c4d3418..4204297 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -1072,7 +1072,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
     size_t frame_size;
     snd_pcm_info_t *pcm_info = NULL;
     int err;
-    pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d;
+    pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE;
     pa_source_new_data data;
 
     snd_pcm_info_alloca(&pcm_info);
@@ -1116,6 +1116,11 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
         goto fail;
     }
 
+    if (pa_modargs_get_value_boolean(ma, "ignore_dB", &ignore_dB) < 0) {
+        pa_log("Failed to parse ignore_dB argument.");
+        goto fail;
+    }
+
     if (use_tsched && !pa_rtclock_hrtimer()) {
         pa_log_notice("Disabling timer-based scheduling because high-resolution timers are not available from the kernel.");
         use_tsched = FALSE;
@@ -1324,8 +1329,8 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
             }
 
             if (suitable) {
-                if (snd_mixer_selem_get_capture_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) < 0)
-                    pa_log_info("Mixer doesn't support dB information.");
+                if (ignore_dB || snd_mixer_selem_get_capture_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) < 0)
+                    pa_log_info("Mixer doesn't support dB information or data is ignored.");
                 else {
 #ifdef HAVE_VALGRIND_MEMCHECK_H
                     VALGRIND_MAKE_MEM_DEFINED(&u->hw_dB_min, sizeof(u->hw_dB_min));
diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
index 891195c..8f192c2 100644
--- a/src/modules/alsa/module-alsa-card.c
+++ b/src/modules/alsa/module-alsa-card.c
@@ -51,7 +51,8 @@ PA_MODULE_USAGE(
         "tsched=<enable system timer based scheduling mode?> "
         "tsched_buffer_size=<buffer size when using timer based scheduling> "
         "tsched_buffer_watermark=<lower fill watermark> "
-        "profile=<profile name>");
+        "profile=<profile name> "
+        "ignore_dB=<ignore dB information from the device?>");
 
 static const char* const valid_modargs[] = {
     "name",
@@ -68,6 +69,7 @@ static const char* const valid_modargs[] = {
     "tsched_buffer_size",
     "tsched_buffer_watermark",
     "profile",
+    "ignore_dB",
     NULL
 };
 
diff --git a/src/modules/alsa/module-alsa-sink.c b/src/modules/alsa/module-alsa-sink.c
index 6cf48b5..4f844e0 100644
--- a/src/modules/alsa/module-alsa-sink.c
+++ b/src/modules/alsa/module-alsa-sink.c
@@ -51,7 +51,8 @@ PA_MODULE_USAGE(
         "mmap=<enable memory mapping?> "
         "tsched=<enable system timer based scheduling mode?> "
         "tsched_buffer_size=<buffer size when using timer based scheduling> "
-        "tsched_buffer_watermark=<lower fill watermark>");
+        "tsched_buffer_watermark=<lower fill watermark> "
+        "ignore_dB=<ignore dB information from the device?>");
 
 static const char* const valid_modargs[] = {
     "name",
@@ -68,6 +69,7 @@ static const char* const valid_modargs[] = {
     "tsched",
     "tsched_buffer_size",
     "tsched_buffer_watermark",
+    "ignore_dB",
     NULL
 };
 
diff --git a/src/modules/alsa/module-alsa-source.c b/src/modules/alsa/module-alsa-source.c
index 1e7c387..c35936d 100644
--- a/src/modules/alsa/module-alsa-source.c
+++ b/src/modules/alsa/module-alsa-source.c
@@ -75,7 +75,8 @@ PA_MODULE_USAGE(
         "mmap=<enable memory mapping?> "
         "tsched=<enable system timer based scheduling mode?> "
         "tsched_buffer_size=<buffer size when using timer based scheduling> "
-        "tsched_buffer_watermark=<upper fill watermark>");
+        "tsched_buffer_watermark=<upper fill watermark> "
+        "ignore_dB=<ignore dB information from the device?>");
 
 static const char* const valid_modargs[] = {
     "name",
@@ -92,6 +93,7 @@ static const char* const valid_modargs[] = {
     "tsched",
     "tsched_buffer_size",
     "tsched_buffer_watermark",
+    "ignore_dB",
     NULL
 };
 

commit ee17772cea3471b0e44fd1598f7ababa4100db0c
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 27 22:49:02 2009 +0100

    add missing 'const'

diff --git a/src/pulse/volume.c b/src/pulse/volume.c
index 822fe39..3434cb1 100644
--- a/src/pulse/volume.c
+++ b/src/pulse/volume.c
@@ -341,7 +341,7 @@ static pa_bool_t on_lfe(pa_channel_position_t p) {
         p == PA_CHANNEL_POSITION_LFE;
 }
 
-pa_cvolume *pa_cvolume_remap(pa_cvolume *v, pa_channel_map *from, pa_channel_map *to) {
+pa_cvolume *pa_cvolume_remap(pa_cvolume *v, const pa_channel_map *from, const pa_channel_map *to) {
     int a, b;
     pa_cvolume result;
 
diff --git a/src/pulse/volume.h b/src/pulse/volume.h
index c1967b3..9a883ca 100644
--- a/src/pulse/volume.h
+++ b/src/pulse/volume.h
@@ -228,7 +228,7 @@ double pa_sw_volume_to_linear(pa_volume_t v) PA_GCC_CONST;
 #endif
 
 /** Remap a volume from one channel mapping to a different channel mapping. \since 0.9.12 */
-pa_cvolume *pa_cvolume_remap(pa_cvolume *v, pa_channel_map *from, pa_channel_map *to);
+pa_cvolume *pa_cvolume_remap(pa_cvolume *v, const pa_channel_map *from, const pa_channel_map *to);
 
 /** Return non-zero if the specified volume is compatible with
  * the specified sample spec. \since 0.9.13 */

commit 64b05435889678dcb154c460063395855659485e
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 27 23:35:55 2009 +0100

    when changing volume, store whether it is worth remembering or no

diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
index 8f192c2..e63414e 100644
--- a/src/modules/alsa/module-alsa-card.c
+++ b/src/modules/alsa/module-alsa-card.c
@@ -177,7 +177,7 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
 
             if (inputs) {
                 if (u->sink)
-                    pa_sink_move_all_finish(u->sink, inputs);
+                    pa_sink_move_all_finish(u->sink, inputs, FALSE);
                 else
                     pa_sink_move_all_fail(inputs);
             }
@@ -200,7 +200,7 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
 
             if (outputs) {
                 if (u->source)
-                    pa_source_move_all_finish(u->source, outputs);
+                    pa_source_move_all_finish(u->source, outputs, FALSE);
                 else
                     pa_source_move_all_fail(outputs);
             }
diff --git a/src/modules/module-match.c b/src/modules/module-match.c
index 8c825c4..cbf6268 100644
--- a/src/modules/module-match.c
+++ b/src/modules/module-match.c
@@ -216,7 +216,7 @@ static void callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, v
                 pa_cvolume cv;
                 pa_log_debug("changing volume of sink input '%s' to 0x%03x", n, r->volume);
                 pa_cvolume_set(&cv, si->sample_spec.channels, r->volume);
-                pa_sink_input_set_volume(si, &cv);
+                pa_sink_input_set_volume(si, &cv, TRUE);
             }
         }
     }
diff --git a/src/modules/module-rescue-streams.c b/src/modules/module-rescue-streams.c
index 07a0237..e52e39c 100644
--- a/src/modules/module-rescue-streams.c
+++ b/src/modules/module-rescue-streams.c
@@ -77,7 +77,7 @@ static pa_hook_result_t sink_hook_callback(pa_core *c, pa_sink *sink, void* user
     }
 
     while ((i = pa_idxset_first(sink->inputs, NULL))) {
-        if (pa_sink_input_move_to(i, target) < 0) {
+        if (pa_sink_input_move_to(i, target, FALSE) < 0) {
             pa_log_warn("Failed to move sink input %u \"%s\" to %s.", i->index, pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME), target->name);
             return PA_HOOK_OK;
         }
@@ -121,7 +121,7 @@ static pa_hook_result_t source_hook_callback(pa_core *c, pa_source *source, void
     pa_assert(target != source);
 
     while ((o = pa_idxset_first(source->outputs, NULL))) {
-        if (pa_source_output_move_to(o, target) < 0) {
+        if (pa_source_output_move_to(o, target, FALSE) < 0) {
             pa_log_warn("Failed to move source output %u \"%s\" to %s.", o->index, pa_proplist_gets(o->proplist, PA_PROP_APPLICATION_NAME), target->name);
             return PA_HOOK_OK;
         }
diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c
index 464ff2d..f298231 100644
--- a/src/modules/module-stream-restore.c
+++ b/src/modules/module-stream-restore.c
@@ -438,14 +438,14 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
 
         if (u->restore_muted) {
             pa_log_info("Restoring mute state for sink input %s.", name);
-            pa_sink_input_set_mute(si, e->muted);
+            pa_sink_input_set_mute(si, e->muted, TRUE);
         }
 
         if (u->restore_device &&
             (s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SOURCE))) {
 
             pa_log_info("Restoring device for stream %s.", name);
-            pa_sink_input_move_to(si, s);
+            pa_sink_input_move_to(si, s, TRUE);
         }
     }
 
@@ -465,7 +465,7 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
             (s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SOURCE))) {
 
             pa_log_info("Restoring device for stream %s.", name);
-            pa_source_output_move_to(so, s);
+            pa_source_output_move_to(so, s, TRUE);
         }
     }
 }
diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c
index b93f24f..be5a394 100644
--- a/src/pulsecore/cli-command.c
+++ b/src/pulsecore/cli-command.c
@@ -560,7 +560,7 @@ static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strb
     }
 
     pa_cvolume_set(&cvolume, si->sample_spec.channels, volume);
-    pa_sink_input_set_volume(si, &cvolume);
+    pa_sink_input_set_volume(si, &cvolume, TRUE);
     return 0;
 }
 
@@ -852,7 +852,7 @@ static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf
         return -1;
     }
 
-    pa_sink_input_set_mute(si, mute);
+    pa_sink_input_set_mute(si, mute, TRUE);
     return 0;
 }
 
@@ -1169,7 +1169,7 @@ static int pa_cli_command_move_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf
         return -1;
     }
 
-    if (pa_sink_input_move_to(si, sink) < 0) {
+    if (pa_sink_input_move_to(si, sink, TRUE) < 0) {
         pa_strbuf_puts(buf, "Moved failed.\n");
         return -1;
     }
@@ -1212,7 +1212,7 @@ static int pa_cli_command_move_source_output(pa_core *c, pa_tokenizer *t, pa_str
         return -1;
     }
 
-    if (pa_source_output_move_to(so, source) < 0) {
+    if (pa_source_output_move_to(so, source, TRUE) < 0) {
         pa_strbuf_puts(buf, "Moved failed.\n");
         return -1;
     }
diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c
index b1d3909..1311e67 100644
--- a/src/pulsecore/protocol-esound.c
+++ b/src/pulsecore/protocol-esound.c
@@ -760,7 +760,7 @@ static int esd_proto_stream_pan(connection *c, esd_proto_t request, const void *
         volume.values[0] = (lvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE;
         volume.values[1] = (rvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE;
         volume.channels = 2;
-        pa_sink_input_set_volume(conn->sink_input, &volume);
+        pa_sink_input_set_volume(conn->sink_input, &volume, TRUE);
         ok = 1;
     } else
         ok = 0;
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 80edb20..c33d15e 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -3170,7 +3170,7 @@ static void command_set_volume(
     else if (source)
         pa_source_set_volume(source, &volume);
     else if (si)
-        pa_sink_input_set_volume(si, &volume);
+        pa_sink_input_set_volume(si, &volume, TRUE);
 
     pa_pstream_send_simple_ack(c->pstream, tag);
 }
@@ -3242,7 +3242,7 @@ static void command_set_mute(
     else if (source)
         pa_source_set_mute(source, mute);
     else if (si)
-        pa_sink_input_set_mute(si, mute);
+        pa_sink_input_set_mute(si, mute, TRUE);
 
     pa_pstream_send_simple_ack(c->pstream, tag);
 }
@@ -3852,7 +3852,7 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag
 
         CHECK_VALIDITY(c->pstream, si && sink, tag, PA_ERR_NOENTITY);
 
-        if (pa_sink_input_move_to(si, sink) < 0) {
+        if (pa_sink_input_move_to(si, sink, TRUE) < 0) {
             pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
             return;
         }
@@ -3871,7 +3871,7 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag
 
         CHECK_VALIDITY(c->pstream, so && source, tag, PA_ERR_NOENTITY);
 
-        if (pa_source_output_move_to(so, source) < 0) {
+        if (pa_source_output_move_to(so, source, TRUE) < 0) {
             pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
             return;
         }
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 8b76df0..d4d1119 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -138,8 +138,10 @@ pa_sink_input* pa_sink_input_new(
 
     pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
 
-    if (!data->sink)
+    if (!data->sink) {
         data->sink = pa_namereg_get(core, NULL, PA_NAMEREG_SINK);
+        data->save_sink = FALSE;
+    }
 
     pa_return_null_if_fail(data->sink);
     pa_return_null_if_fail(pa_sink_get_state(data->sink) != PA_SINK_UNLINKED);
@@ -168,6 +170,8 @@ pa_sink_input* pa_sink_input_new(
         } else
             pa_cvolume_reset(&data->virtual_volume, data->sample_spec.channels);
 
+        data->save_volume = FALSE;
+
     } else if (!data->virtual_volume_is_absolute) {
 
         /* When the 'absolute' bool is set then we'll treat the volume
@@ -265,6 +269,9 @@ pa_sink_input* pa_sink_input_new(
 
     i->virtual_volume = data->virtual_volume;
     i->soft_volume = data->soft_volume;
+    i->save_volume = data->save_volume;
+    i->save_sink = data->save_sink;
+    i->save_muted = data->save_muted;
 
     i->muted = data->muted;
 
@@ -855,7 +862,7 @@ pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i) {
 }
 
 /* Called from main context */
-void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume) {
+void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save) {
     pa_sink_input_assert_ref(i);
     pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
     pa_assert(volume);
@@ -866,6 +873,7 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume) {
         return;
 
     i->virtual_volume = *volume;
+    i->save_volume = save;
 
     if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
         pa_cvolume new_volume;
@@ -902,7 +910,7 @@ const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i) {
 }
 
 /* Called from main context */
-void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute) {
+void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute, pa_bool_t save) {
     pa_assert(i);
     pa_sink_input_assert_ref(i);
     pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
@@ -911,6 +919,7 @@ void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute) {
         return;
 
     i->muted = mute;
+    i->save_muted = save;
 
     pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_MUTE, NULL, 0, NULL) == 0);
     pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
@@ -1081,7 +1090,7 @@ int pa_sink_input_start_move(pa_sink_input *i) {
 }
 
 /* Called from main context */
-int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest) {
+int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
     pa_resampler *new_resampler;
 
     pa_sink_input_assert_ref(i);
@@ -1093,6 +1102,7 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest) {
         return -1;
 
     i->sink = dest;
+    i->save_sink = save;
     pa_idxset_put(dest->inputs, i, NULL);
 
     if (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED)
@@ -1169,7 +1179,7 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest) {
 }
 
 /* Called from main context */
-int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest) {
+int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
     pa_sink_input_assert_ref(i);
     pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
     pa_assert(i->sink);
@@ -1184,7 +1194,7 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest) {
     if (pa_sink_input_start_move(i) < 0)
         return -1;
 
-    if (pa_sink_input_finish_move(i, dest) < 0)
+    if (pa_sink_input_finish_move(i, dest, save) < 0)
         return -1;
 
     return 0;
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index 5bf38de..893d869 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -91,7 +91,13 @@ struct pa_sink_input {
     pa_sink_input *sync_prev, *sync_next;
 
     pa_cvolume virtual_volume, soft_volume;
-    pa_bool_t muted;
+    pa_bool_t muted:1;
+
+    /* if TRUE then the source we are connected to and/or the volume
+     * set is worth remembering, i.e. was explicitly chosen by the
+     * user and not automatically. module-stream-restore looks for
+     * this.*/
+    pa_bool_t save_sink:1, save_volume:1, save_muted:1;
 
     pa_resample_method_t requested_resample_method, actual_resample_method;
 
@@ -236,6 +242,8 @@ typedef struct pa_sink_input_new_data {
     pa_bool_t muted_is_set:1;
 
     pa_bool_t virtual_volume_is_absolute:1;
+
+    pa_bool_t save_sink:1, save_volume:1, save_muted:1;
 } pa_sink_input_new_data;
 
 pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data);
@@ -280,15 +288,15 @@ void pa_sink_input_kill(pa_sink_input*i);
 
 pa_usec_t pa_sink_input_get_latency(pa_sink_input *i, pa_usec_t *sink_latency);
 
-void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume);
+void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save);
 const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i);
-void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute);
+void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute, pa_bool_t save);
 pa_bool_t pa_sink_input_get_mute(pa_sink_input *i);
 pa_bool_t pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode, pa_proplist *p);
 
 pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i);
 
-int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest);
+int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t save);
 pa_bool_t pa_sink_input_may_move(pa_sink_input *i); /* may this sink input move at all? */
 pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest); /* may this sink input move to this sink? */
 
@@ -296,7 +304,7 @@ pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest); /* may thi
  * first the detaching from the old sink, then the attaching to the
  * new sink */
 int pa_sink_input_start_move(pa_sink_input *i);
-int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest);
+int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save);
 
 pa_sink_input_state_t pa_sink_input_get_state(pa_sink_input *i);
 
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 7735d3a..3afeadb 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -503,7 +503,7 @@ pa_queue *pa_sink_move_all_start(pa_sink *s) {
 }
 
 /* Called from main context */
-void pa_sink_move_all_finish(pa_sink *s, pa_queue *q) {
+void pa_sink_move_all_finish(pa_sink *s, pa_queue *q, pa_bool_t save) {
     pa_sink_input *i;
 
     pa_sink_assert_ref(s);
@@ -511,7 +511,7 @@ void pa_sink_move_all_finish(pa_sink *s, pa_queue *q) {
     pa_assert(q);
 
     while ((i = PA_SINK_INPUT(pa_queue_pop(q)))) {
-        if (pa_sink_input_finish_move(i, s) < 0)
+        if (pa_sink_input_finish_move(i, s, save) < 0)
             pa_sink_input_unlink(i);
 
         pa_sink_input_unref(i);
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index b26ca77..124b4e1 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -256,7 +256,7 @@ unsigned pa_sink_check_suspend(pa_sink *s); /* Returns how many streams are acti
 
 /* Moves all inputs away, and stores them in pa_queue */
 pa_queue *pa_sink_move_all_start(pa_sink *s);
-void pa_sink_move_all_finish(pa_sink *s, pa_queue *q);
+void pa_sink_move_all_finish(pa_sink *s, pa_queue *q, pa_bool_t save);
 void pa_sink_move_all_fail(pa_queue *q);
 
 /*** To be called exclusively by the sink driver, from IO context */
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index 204e06c..1e1ac4e 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -112,8 +112,10 @@ pa_source_output* pa_source_output_new(
 
     pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
 
-    if (!data->source)
+    if (!data->source) {
         data->source = pa_namereg_get(core, NULL, PA_NAMEREG_SOURCE);
+        data->save_source = FALSE;
+    }
 
     pa_return_null_if_fail(data->source);
     pa_return_null_if_fail(pa_source_get_state(data->source) != PA_SOURCE_UNLINKED);
@@ -196,7 +198,6 @@ pa_source_output* pa_source_output_new(
     o->source = data->source;
     o->client = data->client;
 
-
     o->actual_resample_method = resampler ? pa_resampler_get_method(resampler) : PA_RESAMPLER_INVALID;
     o->requested_resample_method = data->resample_method;
     o->sample_spec = data->sample_spec;
@@ -204,6 +205,8 @@ pa_source_output* pa_source_output_new(
 
     o->direct_on_input = data->direct_on_input;
 
+    o->save_source = data->save_source;
+
     reset_callbacks(o);
     o->userdata = NULL;
 
@@ -699,7 +702,7 @@ int pa_source_output_start_move(pa_source_output *o) {
 }
 
 /* Called from main context */
-int pa_source_output_finish_move(pa_source_output *o, pa_source *dest) {
+int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t save) {
     pa_resampler *new_resampler;
 
     pa_source_output_assert_ref(o);
@@ -711,6 +714,7 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest) {
         return -1;
 
     o->source = dest;
+    o->save_source = save;
     pa_idxset_put(o->source->outputs, o, NULL);
 
     if (pa_source_output_get_state(o) == PA_SOURCE_OUTPUT_CORKED)
@@ -778,7 +782,7 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest) {
 }
 
 /* Called from main context */
-int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
+int pa_source_output_move_to(pa_source_output *o, pa_source *dest, pa_bool_t save) {
 
     pa_source_output_assert_ref(o);
     pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
@@ -794,7 +798,7 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
     if (pa_source_output_start_move(o) < 0)
         return -1;
 
-    if (pa_source_output_finish_move(o, dest) < 0)
+    if (pa_source_output_finish_move(o, dest, save) < 0)
         return -1;
 
     return 0;
diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
index d60e425..671894e 100644
--- a/src/pulsecore/source-output.h
+++ b/src/pulsecore/source-output.h
@@ -79,6 +79,11 @@ struct pa_source_output {
     pa_sample_spec sample_spec;
     pa_channel_map channel_map;
 
+    /* if TRUE then the source we are connected to is worth
+     * remembering, i.e. was explicitly chosen by the user and not
+     * automatically. module-stream-restore looks for this.*/
+    pa_bool_t save_source:1;
+
     pa_resample_method_t requested_resample_method, actual_resample_method;
 
     /* Pushes a new memchunk into the output. Called from IO thread
@@ -187,6 +192,8 @@ typedef struct pa_source_output_new_data {
 
     pa_bool_t sample_spec_is_set:1;
     pa_bool_t channel_map_is_set:1;
+
+    pa_bool_t save_source:1;
 } pa_source_output_new_data;
 
 pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_data *data);
@@ -225,13 +232,13 @@ pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o);
 
 pa_bool_t pa_source_output_may_move(pa_source_output *o);
 pa_bool_t pa_source_output_may_move_to(pa_source_output *o, pa_source *dest);
-int pa_source_output_move_to(pa_source_output *o, pa_source *dest);
+int pa_source_output_move_to(pa_source_output *o, pa_source *dest, pa_bool_t save);
 
 /* The same as pa_source_output_move_to() but in two seperate steps,
  * first the detaching from the old source, then the attaching to the
  * new source */
 int pa_source_output_start_move(pa_source_output *o);
-int pa_source_output_finish_move(pa_source_output *o, pa_source *dest);
+int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t save);
 
 #define pa_source_output_get_state(o) ((o)->state)
 
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index cd6ebc3..0152b08 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -439,7 +439,7 @@ pa_queue *pa_source_move_all_start(pa_source *s) {
 }
 
 /* Called from main context */
-void pa_source_move_all_finish(pa_source *s, pa_queue *q) {
+void pa_source_move_all_finish(pa_source *s, pa_queue *q, pa_bool_t save) {
     pa_source_output *o;
 
     pa_source_assert_ref(s);
@@ -447,7 +447,7 @@ void pa_source_move_all_finish(pa_source *s, pa_queue *q) {
     pa_assert(q);
 
     while ((o = PA_SOURCE_OUTPUT(pa_queue_pop(q)))) {
-        if (pa_source_output_finish_move(o, s) < 0)
+        if (pa_source_output_finish_move(o, s, save) < 0)
             pa_source_output_unlink(o);
 
         pa_source_output_unref(o);
diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
index de23977..8a91016 100644
--- a/src/pulsecore/source.h
+++ b/src/pulsecore/source.h
@@ -239,7 +239,7 @@ unsigned pa_source_check_suspend(pa_source *s); /* Returns how many streams are
 
 /* Moves all inputs away, and stores them in pa_queue */
 pa_queue *pa_source_move_all_start(pa_source *s);
-void pa_source_move_all_finish(pa_source *s, pa_queue *q);
+void pa_source_move_all_finish(pa_source *s, pa_queue *q, pa_bool_t save);
 void pa_source_move_all_fail(pa_queue *q);
 
 /*** To be called exclusively by the source driver, from IO context */

commit d1b754d99867e68dea71d4d6726949d0ce8ba0fa
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 27 23:39:39 2009 +0100

    only store volume/device information that has been flagged for saving, and store both relative and absolute volumes

diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c
index f298231..fe30e29 100644
--- a/src/modules/module-stream-restore.c
+++ b/src/modules/module-stream-restore.c
@@ -87,10 +87,12 @@ struct userdata {
 };
 
 struct entry {
-    char device[PA_NAME_MAX];
     pa_channel_map channel_map;
-    pa_cvolume volume;
+    char device[PA_NAME_MAX];
+    pa_cvolume relative_volume;
+    pa_cvolume absolute_volume;
     pa_bool_t muted:1;
+    pa_bool_t device_valid:1, relative_volume_valid:1, absolute_volume_valid:1, muted_valid:1;
 };
 
 
@@ -153,7 +155,9 @@ static struct entry* read_entry(struct userdata *u, const char *name) {
         goto fail;
 
     if (data.dsize != sizeof(struct entry)) {
-        pa_log_warn("Database contains entry for stream %s of wrong size %lu != %lu", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry));
+        /* This is probably just a database upgrade, hence let's not
+         * consider this more than a debug message */
+        pa_log_debug("Database contains entry for stream %s of wrong size %lu != %lu", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry));
         goto fail;
     }
 
@@ -164,18 +168,19 @@ static struct entry* read_entry(struct userdata *u, const char *name) {
         goto fail;
     }
 
-    if (!(pa_cvolume_valid(&e->volume))) {
-        pa_log_warn("Invalid volume stored in database for stream %s", name);
+    if (!(pa_channel_map_valid(&e->channel_map))) {
+        pa_log_warn("Invalid channel map stored in database for stream %s", name);
         goto fail;
     }
 
-    if (!(pa_channel_map_valid(&e->channel_map))) {
-        pa_log_warn("Invalid channel map stored in database for stream %s", name);
+    if (e->device_valid && !pa_namereg_is_valid_name(e->device)) {
+        pa_log_warn("Invalid device name stored in database for stream %s", name);
         goto fail;
     }
 
-    if (e->volume.channels != e->channel_map.channels) {
-        pa_log_warn("Volume and channel map don't match in database entry for stream %s", name);
+    if ((e->relative_volume_valid && (!pa_cvolume_valid(&e->relative_volume) || e->relative_volume.channels != e->channel_map.channels)) ||
+        (e->absolute_volume_valid && (!pa_cvolume_valid(&e->absolute_volume) || e->absolute_volume.channels != e->channel_map.channels))) {
+        pa_log_warn("Invalid volume stored in database for stream %s", name);
         goto fail;
     }
 
@@ -213,6 +218,33 @@ static void trigger_save(struct userdata *u) {
     u->save_time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, save_time_callback, u);
 }
 
+static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
+    pa_cvolume t;
+
+    pa_assert(a);
+    pa_assert(b);
+
+    if (a->device_valid != b->device_valid ||
+        (a->device_valid && strncmp(a->device, b->device, sizeof(a->device))))
+        return FALSE;
+
+    if (a->muted_valid != b->muted_valid ||
+        (a->muted && (a->muted != b->muted)))
+        return FALSE;
+
+    t = b->relative_volume;
+    if (a->relative_volume_valid != b->relative_volume_valid ||
+        (a->relative_volume_valid && !pa_cvolume_equal(pa_cvolume_remap(&t, &b->channel_map, &a->channel_map), &a->relative_volume)))
+        return FALSE;
+
+    t = b->absolute_volume;
+    if (a->absolute_volume_valid != b->absolute_volume_valid ||
+        (a->absolute_volume_valid && !pa_cvolume_equal(pa_cvolume_remap(&t, &b->channel_map, &a->channel_map), &a->absolute_volume)))
+        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;
@@ -240,9 +272,23 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
             return;
 
         entry.channel_map = sink_input->channel_map;
-        entry.volume = *pa_sink_input_get_volume(sink_input);
+
+        if (sink_input->sink->flags & PA_SINK_FLAT_VOLUME) {
+            entry.absolute_volume = *pa_sink_input_get_volume(sink_input);
+            entry.absolute_volume_valid = sink_input->save_volume;
+
+            pa_sw_cvolume_divide(&entry.relative_volume, &entry.absolute_volume, pa_sink_get_volume(sink_input->sink, FALSE));
+            entry.relative_volume_valid = sink_input->save_volume;
+        } else {
+            entry.relative_volume = *pa_sink_input_get_volume(sink_input);
+            entry.relative_volume_valid = sink_input->save_volume;
+        }
+
         entry.muted = pa_sink_input_get_mute(sink_input);
+        entry.muted_valid = sink_input->save_muted;
+
         pa_strlcpy(entry.device, sink_input->sink->name, sizeof(entry.device));
+        entry.device_valid = sink_input->save_sink;
 
     } else {
         pa_source_output *source_output;
@@ -255,19 +301,15 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
         if (!(name = get_name(source_output->proplist, "source-output")))
             return;
 
-        /* The following fields are filled in to make the entry valid
-         * according to read_entry(). They are otherwise useless */
         entry.channel_map = source_output->channel_map;
-        pa_cvolume_reset(&entry.volume, entry.channel_map.channels);
+
         pa_strlcpy(entry.device, source_output->source->name, sizeof(entry.device));
+        entry.device_valid = source_output->save_source;
     }
 
     if ((old = read_entry(u, name))) {
 
-        if (pa_cvolume_equal(pa_cvolume_remap(&old->volume, &old->channel_map, &entry.channel_map), &entry.volume) &&
-            !old->muted == !entry.muted &&
-            strncmp(old->device, entry.device, sizeof(entry.device)) == 0) {
-
+        if (entries_equal(old, &entry)) {
             pa_xfree(old);
             pa_xfree(name);
             return;
@@ -297,20 +339,25 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
 
     pa_assert(new_data);
 
+    if (!u->restore_device)
+        return PA_HOOK_OK;
+
     if (!(name = get_name(new_data->proplist, "sink-input")))
         return PA_HOOK_OK;
 
     if ((e = read_entry(u, name))) {
         pa_sink *s;
 
-        if (u->restore_device &&
-            (s = pa_namereg_get(c, e->device, PA_NAMEREG_SINK))) {
+        if (e->device_valid) {
 
-            if (!new_data->sink) {
-                pa_log_info("Restoring device for stream %s.", name);
-                new_data->sink = s;
-            } else
-                pa_log_info("Not restore device for stream %s, because already set.", name);
+            if ((s = pa_namereg_get(c, e->device, PA_NAMEREG_SINK))) {
+                if (!new_data->sink) {
+                    pa_log_info("Restoring device for stream %s.", name);
+                    new_data->sink = s;
+                    new_data->save_sink = TRUE;
+                } else
+                    pa_log_info("Not restore device for stream %s, because already set.", name);
+            }
         }
 
         pa_xfree(e);
@@ -327,6 +374,9 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu
 
     pa_assert(new_data);
 
+    if (!u->restore_volume && !u->restore_muted)
+        return PA_HOOK_OK;
+
     if (!(name = get_name(new_data->proplist, "sink-input")))
         return PA_HOOK_OK;
 
@@ -335,16 +385,37 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu
         if (u->restore_volume) {
 
             if (!new_data->virtual_volume_is_set) {
-                pa_log_info("Restoring volume for sink input %s.", name);
-                pa_sink_input_new_data_set_virtual_volume(new_data, pa_cvolume_remap(&e->volume, &e->channel_map, &new_data->channel_map));
+                pa_cvolume v;
+                pa_cvolume_init(&v);
+
+                if (new_data->sink->flags & PA_SINK_FLAT_VOLUME) {
+
+                    if (e->absolute_volume_valid &&
+                        e->device_valid &&
+                        pa_streq(new_data->sink->name, e->device))
+                        v = e->absolute_volume;
+                    else if (e->relative_volume_valid) {
+                        pa_cvolume t = new_data->sink->virtual_volume;
+                        pa_sw_cvolume_multiply(&v, &e->relative_volume, pa_cvolume_remap(&t, &new_data->sink->channel_map, &e->channel_map));
+                    }
+
+                } else if (e->relative_volume_valid)
+                    v = e->relative_volume;
+
+                if (v.channels > 0) {
+                    pa_log_info("Restoring volume for sink input %s.", name);
+                    pa_sink_input_new_data_set_virtual_volume(new_data, pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map));
+                    new_data->save_volume = TRUE;
+                }
             } else
                 pa_log_debug("Not restoring volume for sink input %s, because already set.", name);
         }
 
-        if (u->restore_muted) {
+        if (u->restore_muted && e->muted_valid) {
             if (!new_data->muted_is_set) {
                 pa_log_info("Restoring mute state for sink input %s.", name);
                 pa_sink_input_new_data_set_muted(new_data, e->muted);
+                new_data->save_muted = TRUE;
             } else
                 pa_log_debug("Not restoring mute state for sink input %s, because already set.", name);
         }
@@ -363,21 +434,27 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou
 
     pa_assert(new_data);
 
+    if (!u->restore_device)
+        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 ((e = read_entry(u, name))) {
         pa_source *s;
 
-        if (u->restore_device &&
-            !new_data->direct_on_input &&
-            (s = pa_namereg_get(c, e->device, PA_NAMEREG_SOURCE))) {
-
-            if (!new_data->source) {
-                pa_log_info("Restoring device for stream %s.", name);
-                new_data->source = s;
-            } else
-                pa_log_info("Not restoring device for stream %s, because already set", name);
+        if (e->device_valid) {
+            if ((s = pa_namereg_get(c, e->device, PA_NAMEREG_SOURCE))) {
+                if (!new_data->source) {
+                    pa_log_info("Restoring device for stream %s.", name);
+                    new_data->source = s;
+                    new_data->save_source = TRUE;
+                } else
+                    pa_log_info("Not restoring device for stream %s, because already set", name);
+            }
         }
 
         pa_xfree(e);
@@ -431,18 +508,37 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
         }
 
         if (u->restore_volume) {
-            pa_cvolume v = e->volume;
-            pa_log_info("Restoring volume for sink input %s.", name);
-            pa_sink_input_set_volume(si, pa_cvolume_remap(&v, &e->channel_map, &si->channel_map));
+            pa_cvolume v;
+            pa_cvolume_init(&v);
+
+            if (si->sink->flags & PA_SINK_FLAT_VOLUME) {
+
+                if (e->absolute_volume_valid &&
+                    e->device_valid &&
+                    pa_streq(e->device, si->sink->name))
+                    v = e->absolute_volume;
+                else if (e->relative_volume_valid) {
+                    pa_cvolume t = si->sink->virtual_volume;
+                    pa_sw_cvolume_multiply(&v, &e->relative_volume, pa_cvolume_remap(&t, &si->sink->channel_map, &e->channel_map));
+                }
+            } else if (e->relative_volume_valid)
+                v = e->relative_volume;
+
+            if (v.channels > 0) {
+                pa_log_info("Restoring volume for sink input %s.", name);
+                pa_sink_input_set_volume(si, pa_cvolume_remap(&v, &e->channel_map, &si->channel_map), TRUE);
+            }
         }
 
-        if (u->restore_muted) {
+        if (u->restore_muted &&
+            e->muted_valid) {
             pa_log_info("Restoring mute state for sink input %s.", name);
             pa_sink_input_set_mute(si, e->muted, TRUE);
         }
 
         if (u->restore_device &&
-            (s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SOURCE))) {
+            e->device_valid &&
+            (s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SINK))) {
 
             pa_log_info("Restoring device for stream %s.", name);
             pa_sink_input_move_to(si, s, TRUE);
@@ -462,6 +558,7 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
         }
 
         if (u->restore_device &&
+            e->device_valid &&
             (s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SOURCE))) {
 
             pa_log_info("Restoring device for stream %s.", name);
@@ -548,11 +645,13 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
                 pa_xfree(key.dptr);
 
                 if ((e = read_entry(u, name))) {
+                    pa_cvolume r;
+
                     pa_tagstruct_puts(reply, name);
                     pa_tagstruct_put_channel_map(reply, &e->channel_map);
-                    pa_tagstruct_put_cvolume(reply, &e->volume);
-                    pa_tagstruct_puts(reply, e->device);
-                    pa_tagstruct_put_boolean(reply, e->muted);
+                    pa_tagstruct_put_cvolume(reply, e->relative_volume_valid ? &e->relative_volume : pa_cvolume_init(&r));
+                    pa_tagstruct_puts(reply, e->device_valid ? e->device : NULL);
+                    pa_tagstruct_put_boolean(reply, e->muted_valid ? e->muted : FALSE);
 
                     pa_xfree(e);
                 }
@@ -592,16 +691,28 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
 
                 if (pa_tagstruct_gets(t, &name) < 0 ||
                     pa_tagstruct_get_channel_map(t, &entry.channel_map) ||
-                    pa_tagstruct_get_cvolume(t, &entry.volume) < 0 ||
+                    pa_tagstruct_get_cvolume(t, &entry.relative_volume) < 0 ||
                     pa_tagstruct_gets(t, &device) < 0 ||
                     pa_tagstruct_get_boolean(t, &muted) < 0)
                     goto fail;
 
-                if (entry.channel_map.channels != entry.volume.channels)
+                entry.absolute_volume_valid = FALSE;
+                entry.relative_volume_valid = entry.relative_volume.channels > 0;
+
+                if (entry.relative_volume_valid &&
+                    entry.channel_map.channels != entry.relative_volume.channels)
                     goto fail;
 
                 entry.muted = muted;
-                pa_strlcpy(entry.device, device, sizeof(entry.device));
+                entry.muted_valid = TRUE;
+
+                if (device)
+                    pa_strlcpy(entry.device, device, sizeof(entry.device));
+                entry.device_valid = !!entry.device[0];
+
+                if (entry.device_valid &&
+                    !pa_namereg_is_valid_name(entry.device))
+                    goto fail;
 
                 key.dptr = (void*) name;
                 key.dsize = (int) strlen(name);

commit e439c18785fa0130ecff164e36e0096c4fd92866
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 27 23:52:35 2009 +0100

    make m-p-e-s use pa_cvolume_set_balance()

diff --git a/src/modules/module-position-event-sounds.c b/src/modules/module-position-event-sounds.c
index e75b1c5..8e4f4c3 100644
--- a/src/modules/module-position-event-sounds.c
+++ b/src/modules/module-position-event-sounds.c
@@ -58,30 +58,9 @@ struct userdata {
     pa_hook_slot *sink_input_fixate_hook_slot;
 };
 
-static pa_bool_t is_left(pa_channel_position_t p) {
-    return
-        p == PA_CHANNEL_POSITION_FRONT_LEFT ||
-        p == PA_CHANNEL_POSITION_REAR_LEFT ||
-        p == PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER ||
-        p == PA_CHANNEL_POSITION_SIDE_LEFT ||
-        p == PA_CHANNEL_POSITION_TOP_FRONT_LEFT ||
-        p == PA_CHANNEL_POSITION_TOP_REAR_LEFT;
-}
-
-static pa_bool_t is_right(pa_channel_position_t p) {
-    return
-        p == PA_CHANNEL_POSITION_FRONT_RIGHT ||
-        p == PA_CHANNEL_POSITION_REAR_RIGHT||
-        p == PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER ||
-        p == PA_CHANNEL_POSITION_SIDE_RIGHT ||
-        p == PA_CHANNEL_POSITION_TOP_FRONT_RIGHT ||
-        p == PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
-}
-
 static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *core, pa_sink_input_new_data *data, struct userdata *u) {
     const char *hpos;
     double f;
-    unsigned c;
     char t[PA_CVOLUME_SNPRINT_MAX];
 
     pa_assert(data);
@@ -104,18 +83,11 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *core, pa_sink_i
     if (!data->virtual_volume_is_set) {
         pa_cvolume_reset(&data->virtual_volume, data->sample_spec.channels);
         data->virtual_volume_is_set = TRUE;
+        data->virtual_volume_is_absolute = FALSE;
     }
 
-    for (c = 0; c < data->sample_spec.channels; c++) {
-
-        if (is_left(data->channel_map.map[c]))
-            data->virtual_volume.values[c] =
-                pa_sw_volume_multiply(data->virtual_volume.values[c], (pa_volume_t) (PA_VOLUME_NORM * (1.0 - f)));
-
-        if (is_right(data->channel_map.map[c]))
-            data->virtual_volume.values[c] =
-                pa_sw_volume_multiply(data->virtual_volume.values[c], (pa_volume_t) (PA_VOLUME_NORM * f));
-    }
+    pa_cvolume_set_balance(&data->virtual_volume, &data->channel_map, f*2.0-1.0);
+    data->save_volume = FALSE;
 
     pa_log_debug("Final volume %s.", pa_cvolume_snprint(t, sizeof(t), &data->virtual_volume));
 

commit 0f664b736553f87bce6073b53c19f31046d67dfd
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 27 23:55:22 2009 +0100

    instead of making the volume relative our own, let' pa_sink_input_new() do it for us

diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c
index fe30e29..eb74bc0 100644
--- a/src/modules/module-stream-restore.c
+++ b/src/modules/module-stream-restore.c
@@ -392,15 +392,20 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu
 
                     if (e->absolute_volume_valid &&
                         e->device_valid &&
-                        pa_streq(new_data->sink->name, e->device))
+                        pa_streq(new_data->sink->name, e->device)) {
+
                         v = e->absolute_volume;
-                    else if (e->relative_volume_valid) {
-                        pa_cvolume t = new_data->sink->virtual_volume;
-                        pa_sw_cvolume_multiply(&v, &e->relative_volume, pa_cvolume_remap(&t, &new_data->sink->channel_map, &e->channel_map));
+                        new_data->virtual_volume_is_absolute = TRUE;
+                    } else if (e->relative_volume_valid) {
+
+                        v = e->relative_volume;
+                        new_data->virtual_volume_is_absolute = FALSE;
                     }
 
-                } else if (e->relative_volume_valid)
+                } else if (e->relative_volume_valid) {
                     v = e->relative_volume;
+                    new_data->virtual_volume_is_absolute = FALSE;
+                }
 
                 if (v.channels > 0) {
                     pa_log_info("Restoring volume for sink input %s.", name);

commit 514661e36c03176b08f28d75eed018708cb8094d
Author: Lennart Poettering <lennart at poettering.net>
Date:   Tue Jan 27 23:58:34 2009 +0100

    don't make m-e-s hit an assert when the latency is queried

diff --git a/src/modules/module-esound-sink.c b/src/modules/module-esound-sink.c
index f6b90a4..552cf75 100644
--- a/src/modules/module-esound-sink.c
+++ b/src/modules/module-esound-sink.c
@@ -169,7 +169,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
             w = pa_bytes_to_usec((uint64_t) u->offset + u->memchunk.length, &u->sink->sample_spec);
 
             *((pa_usec_t*) data) = w > r ? w - r : 0;
-            break;
+            return 0;
         }
 
         case SINK_MESSAGE_PASS_SOCKET: {

commit 6e31178fd9cff2377eb1c6943d4c4d7b808b37d6
Author: Colin Guthrie <pulse at colin.guthr.ie>
Date:   Tue Jan 27 22:59:36 2009 +0000

    Fix the message processing for PA_SINK_MESSAGE_GET_LATENCY by returning rather than breaking and falling through.

diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c
index 02ef2aa..74ee612 100644
--- a/src/modules/module-raop-sink.c
+++ b/src/modules/module-raop-sink.c
@@ -208,7 +208,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
             w = pa_bytes_to_usec((u->offset - u->encoding_overhead + (u->encoded_memchunk.length / u->encoding_ratio)), &u->sink->sample_spec);
 
             *((pa_usec_t*) data) = w > r ? w - r : 0;
-            break;
+            return 0;
         }
 
         case SINK_MESSAGE_PASS_SOCKET: {

commit 63157a6662acdd622c14774b827c09a95be96496
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jan 28 00:19:47 2009 +0100

    add missing usage strings

diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c
index a24c0b3..8e0cf92 100644
--- a/src/modules/module-device-restore.c
+++ b/src/modules/module-device-restore.c
@@ -53,6 +53,9 @@ PA_MODULE_AUTHOR("Lennart Poettering");
 PA_MODULE_DESCRIPTION("Automatically restore the volume/mute state of devices");
 PA_MODULE_VERSION(PACKAGE_VERSION);
 PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE(
+        "restore_volume=<Save/restore volumes?> "
+        "restore_muted=<Save/restore muted states?>");
 
 #define SAVE_INTERVAL 10
 
diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c
index eb74bc0..0c9bd4f 100644
--- a/src/modules/module-stream-restore.c
+++ b/src/modules/module-stream-restore.c
@@ -56,6 +56,10 @@ PA_MODULE_AUTHOR("Lennart Poettering");
 PA_MODULE_DESCRIPTION("Automatically restore the volume/mute/device state of streams");
 PA_MODULE_VERSION(PACKAGE_VERSION);
 PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE(
+        "restore_device=<Save/restore sinks/sources?> "
+        "restore_volume=<Save/restore volumes?> "
+        "restore_muted=<Save/restore muted states?>");
 
 #define SAVE_INTERVAL 10
 

commit 3affa7e02deae2d61cacb59dcdd4c2f0846ef9dc
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jan 28 00:22:28 2009 +0100

    make m-v-r a stub that simply load m-s-r

diff --git a/src/modules/module-volume-restore.c b/src/modules/module-volume-restore.c
index e32552c..21c7149 100644
--- a/src/modules/module-volume-restore.c
+++ b/src/modules/module-volume-restore.c
@@ -23,43 +23,19 @@
 #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 <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/core-util.h>
 
 #include "module-volume-restore-symdef.h"
 
 PA_MODULE_AUTHOR("Lennart Poettering");
-PA_MODULE_DESCRIPTION("Automatically restore the volume and the devices of streams");
+PA_MODULE_DESCRIPTION("Compatibility module");
 PA_MODULE_VERSION(PACKAGE_VERSION);
 PA_MODULE_LOAD_ONCE(TRUE);
-PA_MODULE_USAGE(
-        "table=<filename> "
-        "restore_device=<Restore the device for each stream?> "
-        "restore_volume=<Restore the volume for each stream?>"
-);
-
-#define WHITESPACE "\n\r \t"
-#define DEFAULT_VOLUME_TABLE_FILE "volume-restore.table"
-#define SAVE_INTERVAL 10
 
 static const char* const valid_modargs[] = {
     "table",
@@ -68,413 +44,10 @@ static const char* const valid_modargs[] = {
     NULL,
 };
 
-struct rule {
-    char* name;
-    pa_bool_t volume_is_set;
-    pa_cvolume volume;
-    char *sink, *source;
-};
-
-struct userdata {
-    pa_core *core;
-    pa_hashmap *hashmap;
-    pa_subscription *subscription;
-    pa_hook_slot
-        *sink_input_new_hook_slot,
-        *sink_input_fixate_hook_slot,
-        *source_output_new_hook_slot;
-    pa_bool_t modified;
-    char *table_file;
-    pa_time_event *save_time_event;
-};
-
-static pa_cvolume* parse_volume(const char *s, pa_cvolume *v) {
-    char *p;
-    long k;
-    unsigned i;
-
-    pa_assert(s);
-    pa_assert(v);
-
-    if (!isdigit(*s))
-        return NULL;
-
-    k = strtol(s, &p, 0);
-    if (k <= 0 || k > (long) PA_CHANNELS_MAX)
-        return NULL;
-
-    v->channels = (uint8_t) k;
-
-    for (i = 0; i < v->channels; i++) {
-        p += strspn(p, WHITESPACE);
-
-        if (!isdigit(*p))
-            return NULL;
-
-        k = strtol(p, &p, 0);
-
-        if (k < (long) PA_VOLUME_MUTED)
-            return NULL;
-
-        v->values[i] = (pa_volume_t) k;
-    }
-
-    if (*p != 0)
-        return NULL;
-
-    return v;
-}
-
-static int load_rules(struct userdata *u) {
-    FILE *f;
-    int n = 0;
-    int ret = -1;
-    char buf_name[256], buf_volume[256], buf_sink[256], buf_source[256];
-    char *ln = buf_name;
-
-    if (!(f = fopen(u->table_file, "r"))) {
-        if (errno == ENOENT) {
-            pa_log_info("Starting with empty ruleset.");
-            ret = 0;
-        } else
-            pa_log("Failed to open file '%s': %s", u->table_file, pa_cstrerror(errno));
-
-        goto finish;
-    }
-
-    pa_lock_fd(fileno(f), 1);
-
-    while (!feof(f)) {
-        struct rule *rule;
-        pa_cvolume v;
-        pa_bool_t v_is_set;
-
-        if (!fgets(ln, sizeof(buf_name), f))
-            break;
-
-        n++;
-
-        pa_strip_nl(ln);
-
-        if (ln[0] == '#')
-            continue;
-
-        if (ln == buf_name) {
-            ln = buf_volume;
-            continue;
-        }
-
-        if (ln == buf_volume) {
-            ln = buf_sink;
-            continue;
-        }
-
-        if (ln == buf_sink) {
-            ln = buf_source;
-            continue;
-        }
-
-        pa_assert(ln == buf_source);
-
-        if (buf_volume[0]) {
-            if (!parse_volume(buf_volume, &v)) {
-                pa_log("parse failure in %s:%u, stopping parsing", u->table_file, n);
-                goto finish;
-            }
-
-            v_is_set = TRUE;
-        } else
-            v_is_set = FALSE;
-
-        ln = buf_name;
-
-        if (pa_hashmap_get(u->hashmap, buf_name)) {
-            pa_log("double entry in %s:%u, ignoring", u->table_file, n);
-            continue;
-        }
-
-        rule = pa_xnew(struct rule, 1);
-        rule->name = pa_xstrdup(buf_name);
-        if ((rule->volume_is_set = v_is_set))
-            rule->volume = v;
-        rule->sink = buf_sink[0] ? pa_xstrdup(buf_sink) : NULL;
-        rule->source = buf_source[0] ? pa_xstrdup(buf_source) : NULL;
-
-        pa_hashmap_put(u->hashmap, rule->name, rule);
-    }
-
-    if (ln != buf_name) {
-        pa_log("invalid number of lines in %s.", u->table_file);
-        goto finish;
-    }
-
-    ret = 0;
-
-finish:
-    if (f) {
-        pa_lock_fd(fileno(f), 0);
-        fclose(f);
-    }
-
-    return ret;
-}
-
-static int save_rules(struct userdata *u) {
-    FILE *f;
-    int ret = -1;
-    void *state = NULL;
-    struct rule *rule;
-
-    if (!u->modified)
-        return 0;
-
-    pa_log_info("Saving rules...");
-
-    if (!(f = fopen(u->table_file, "w"))) {
-        pa_log("Failed to open file '%s': %s", u->table_file, pa_cstrerror(errno));
-        goto finish;
-    }
-
-    pa_lock_fd(fileno(f), 1);
-
-    while ((rule = pa_hashmap_iterate(u->hashmap, &state, NULL))) {
-        unsigned i;
-
-        fprintf(f, "%s\n", rule->name);
-
-        if (rule->volume_is_set) {
-            fprintf(f, "%u", rule->volume.channels);
-
-            for (i = 0; i < rule->volume.channels; i++)
-                fprintf(f, " %u", rule->volume.values[i]);
-        }
-
-        fprintf(f, "\n%s\n%s\n",
-                rule->sink ? rule->sink : "",
-                rule->source ? rule->source : "");
-    }
-
-    ret = 0;
-    u->modified = FALSE;
-    pa_log_debug("Successfully saved rules...");
-
-finish:
-    if (f) {
-        pa_lock_fd(fileno(f), 0);
-        fclose(f);
-    }
-
-    return ret;
-}
-
-static char* client_name(pa_client *c) {
-    char *t, *e;
-
-    if (!pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME) || !c->driver)
-        return NULL;
-
-    t = pa_sprintf_malloc("%s$%s", c->driver, pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME));
-    t[strcspn(t, "\n\r#")] = 0;
-
-    if (!*t) {
-        pa_xfree(t);
-        return NULL;
-    }
-
-    if ((e = strrchr(t, '('))) {
-        char *k = e + 1 + strspn(e + 1, "0123456789-");
-
-        /* Dirty trick: truncate all trailing parens with numbers in
-         * between, since they are usually used to identify multiple
-         * sessions of the same application, which is something we
-         * explicitly don't want. Besides other stuff this makes xmms
-         * with esound work properly for us. */
-
-        if (*k == ')' && *(k+1) == 0)
-            *e = 0;
-    }
-
-    return t;
-}
-
-static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {
-    struct userdata *u = userdata;
-
-    pa_assert(a);
-    pa_assert(e);
-    pa_assert(tv);
-    pa_assert(u);
-
-    pa_assert(e == u->save_time_event);
-    u->core->mainloop->time_free(u->save_time_event);
-    u->save_time_event = NULL;
-
-    save_rules(u);
-}
-
-static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
-    struct userdata *u =  userdata;
-    pa_sink_input *si = NULL;
-    pa_source_output *so = NULL;
-    struct rule *r;
-    char *name;
-
-    pa_assert(c);
-    pa_assert(u);
-
-    if (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;
-
-    if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
-        if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx)))
-            return;
-
-        if (!si->client || !(name = client_name(si->client)))
-            return;
-    } else {
-        pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT);
-
-        if (!(so = pa_idxset_get_by_index(c->source_outputs, idx)))
-            return;
-
-        if (!so->client || !(name = client_name(so->client)))
-            return;
-    }
-
-    if ((r = pa_hashmap_get(u->hashmap, name))) {
-        pa_xfree(name);
-
-        if (si) {
-
-            if (!r->volume_is_set || !pa_cvolume_equal(pa_sink_input_get_volume(si), &r->volume)) {
-                pa_log_info("Saving volume for <%s>", r->name);
-                r->volume = *pa_sink_input_get_volume(si);
-                r->volume_is_set = TRUE;
-                u->modified = TRUE;
-            }
-
-            if (!r->sink || strcmp(si->sink->name, r->sink) != 0) {
-                pa_log_info("Saving sink for <%s>", r->name);
-                pa_xfree(r->sink);
-                r->sink = pa_xstrdup(si->sink->name);
-                u->modified = TRUE;
-            }
-        } else {
-            pa_assert(so);
-
-            if (!r->source || strcmp(so->source->name, r->source) != 0) {
-                pa_log_info("Saving source for <%s>", r->name);
-                pa_xfree(r->source);
-                r->source = pa_xstrdup(so->source->name);
-                u->modified = TRUE;
-            }
-        }
-
-    } else {
-        pa_log_info("Creating new entry for <%s>", name);
-
-        r = pa_xnew(struct rule, 1);
-        r->name = name;
-
-        if (si) {
-            r->volume = *pa_sink_input_get_volume(si);
-            r->volume_is_set = TRUE;
-            r->sink = pa_xstrdup(si->sink->name);
-            r->source = NULL;
-        } else {
-            pa_assert(so);
-            r->volume_is_set = FALSE;
-            r->sink = NULL;
-            r->source = pa_xstrdup(so->source->name);
-        }
-
-        pa_hashmap_put(u->hashmap, r->name, r);
-        u->modified = TRUE;
-    }
-
-    if (u->modified && !u->save_time_event) {
-        struct timeval tv;
-        pa_gettimeofday(&tv);
-        tv.tv_sec += SAVE_INTERVAL;
-        u->save_time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, save_time_callback, u);
-    }
-}
-
-static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *data, struct userdata *u) {
-    struct rule *r;
-    char *name;
-
-    pa_assert(data);
-
-    /* In the NEW hook we only adjust the device. Adjusting the volume
-     * is left for the FIXATE hook */
-
-    if (!data->client || !(name = client_name(data->client)))
-        return PA_HOOK_OK;
-
-    if ((r = pa_hashmap_get(u->hashmap, name))) {
-        if (!data->sink && r->sink) {
-            if ((data->sink = pa_namereg_get(c, r->sink, PA_NAMEREG_SINK)))
-                pa_log_info("Restoring sink for <%s>", r->name);
-        }
-    }
-
-    pa_xfree(name);
-
-    return PA_HOOK_OK;
-}
-
-static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_input_new_data *data, struct userdata *u) {
-    struct rule *r;
-    char *name;
-
-    pa_assert(data);
-
-    /* In the FIXATE hook we only adjust the volum. Adjusting the device
-     * is left for the NEW hook */
-
-    if (!data->client || !(name = client_name(data->client)))
-        return PA_HOOK_OK;
-
-    if ((r = pa_hashmap_get(u->hashmap, name))) {
-
-        if (r->volume_is_set && data->sample_spec.channels == r->volume.channels) {
-            pa_log_info("Restoring volume for <%s>", r->name);
-            pa_sink_input_new_data_set_virtual_volume(data, &r->volume);
-        }
-    }
-
-    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 *data, struct userdata *u) {
-    struct rule *r;
-    char *name;
-
-    pa_assert(data);
-
-    if (!data->client || !(name = client_name(data->client)))
-        return PA_HOOK_OK;
-
-    if ((r = pa_hashmap_get(u->hashmap, name))) {
-        if (!data->source && r->source) {
-            if ((data->source = pa_namereg_get(c, r->source, PA_NAMEREG_SOURCE)))
-                pa_log_info("Restoring source for <%s>", r->name);
-        }
-    }
-
-    return PA_HOOK_OK;
-}
-
 int pa__init(pa_module*m) {
     pa_modargs *ma = NULL;
-    struct userdata *u;
     pa_bool_t restore_device = TRUE, restore_volume = TRUE;
+    char *t;
 
     pa_assert(m);
 
@@ -483,90 +56,26 @@ int pa__init(pa_module*m) {
         goto fail;
     }
 
-    u = pa_xnew(struct userdata, 1);
-    u->core = m->core;
-    u->hashmap = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
-    u->modified = FALSE;
-    u->subscription = NULL;
-    u->sink_input_new_hook_slot = u->sink_input_fixate_hook_slot = u->source_output_new_hook_slot = NULL;
-    u->save_time_event = NULL;
-
-    m->userdata = u;
-
-    if (!(u->table_file = pa_state_path(pa_modargs_get_value(ma, "table", DEFAULT_VOLUME_TABLE_FILE), TRUE)))
-        goto fail;
-
     if (pa_modargs_get_value_boolean(ma, "restore_device", &restore_device) < 0 ||
         pa_modargs_get_value_boolean(ma, "restore_volume", &restore_volume) < 0) {
         pa_log("restore_volume= and restore_device= expect boolean arguments");
         goto fail;
     }
 
-    if (!(restore_device || restore_volume)) {
-        pa_log("Both restrong the volume and restoring the device are disabled. There's no point in using this module at all then, failing.");
-        goto fail;
-    }
-
-    if (load_rules(u) < 0)
-        goto fail;
+    pa_log_warn("module-volume-restore is obsolete. It has been replaced by module-stream-restore. We will now load the latter but please make sure to remove module-volume-restore from your configuration.");
 
-    u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscribe_callback, u);
-
-    if (restore_device) {
-        u->sink_input_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY, (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, (pa_hook_cb_t) source_output_new_hook_callback, u);
-    }
+    t = pa_sprintf_malloc("restore_volume=%s restore_device=%s", pa_yes_no(restore_volume), pa_yes_no(restore_device));
+    pa_module_load(m->core, "module-stream-restore", t);
+    pa_xfree(t);
 
-    if (restore_volume)
-        u->sink_input_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_fixate_hook_callback, u);
+    pa_module_unload_request(m, TRUE);
 
     pa_modargs_free(ma);
     return 0;
 
 fail:
-    pa__done(m);
     if (ma)
         pa_modargs_free(ma);
 
     return  -1;
 }
-
-static void free_func(void *p, void *userdata) {
-    struct rule *r = p;
-    pa_assert(r);
-
-    pa_xfree(r->name);
-    pa_xfree(r->sink);
-    pa_xfree(r->source);
-    pa_xfree(r);
-}
-
-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_input_new_hook_slot)
-        pa_hook_slot_free(u->sink_input_new_hook_slot);
-    if (u->sink_input_fixate_hook_slot)
-        pa_hook_slot_free(u->sink_input_fixate_hook_slot);
-    if (u->source_output_new_hook_slot)
-        pa_hook_slot_free(u->source_output_new_hook_slot);
-
-    if (u->hashmap) {
-        save_rules(u);
-        pa_hashmap_free(u->hashmap, free_func, NULL);
-    }
-
-    if (u->save_time_event)
-        u->core->mainloop->time_free(u->save_time_event);
-
-    pa_xfree(u->table_file);
-    pa_xfree(u);
-}

commit 611154caca20fc6b3c4240bf3e9c7e78cc403327
Merge: 3affa7e... 6e31178...
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jan 28 00:25:50 2009 +0100

    Merge commit 'coling/master'


commit a6a1b42c871710f8a492f32adc72272cef9d019c
Author: Marc-André Lureau <marcandre.lureau at gmail.com>
Date:   Mon Jan 19 14:53:35 2009 +0200

    bluetooth: hsp volume control

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 804be35..e2f6d01 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -65,7 +65,10 @@ PA_MODULE_LOAD_ONCE(FALSE);
 PA_MODULE_USAGE(
         "sink_name=<name of the device> "
         "address=<address of the device> "
-        "profile=<a2dp|hsp>");
+        "profile=<a2dp|hsp> "
+        "rate=<sample rate> "
+        "channels=<number of channels> "
+        "path=<device object path>");
 
 struct bt_a2dp {
     sbc_capabilities_t sbc_capabilities;
@@ -85,6 +88,7 @@ struct userdata {
     pa_core *core;
     pa_module *module;
     pa_sink *sink;
+    pa_source *source;
 
     pa_thread_mq thread_mq;
     pa_rtpoll *rtpoll;
@@ -109,6 +113,8 @@ struct userdata {
     pa_usec_t latency;
 
     struct bt_a2dp a2dp;
+    char *path;
+    pa_dbus_connection *conn;
 };
 
 static const char* const valid_modargs[] = {
@@ -117,6 +123,7 @@ static const char* const valid_modargs[] = {
     "profile",
     "rate",
     "channels",
+    "path",
     NULL
 };
 
@@ -228,9 +235,9 @@ static int bt_getcaps(struct userdata *u) {
     msg.getcaps_req.h.length = sizeof(msg.getcaps_req);
 
     strncpy(msg.getcaps_req.device, u->addr, 18);
-    if (strcasecmp(u->profile, "a2dp") == 0)
+    if (pa_streq(u->profile, "a2dp"))
         msg.getcaps_req.transport = BT_CAPABILITIES_TRANSPORT_A2DP;
-    else if (strcasecmp(u->profile, "hsp") == 0)
+    else if (pa_streq(u->profile, "hsp"))
         msg.getcaps_req.transport = BT_CAPABILITIES_TRANSPORT_SCO;
     else {
         pa_log_error("Invalid profile argument: %s", u->profile);
@@ -789,6 +796,144 @@ finish:
     pa_log_debug("IO thread shutting down");
 }
 
+static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *msg, void *userdata) {
+    DBusMessageIter arg_i;
+    DBusError err;
+    const char *value;
+    struct userdata *u;
+
+    pa_assert(bus);
+    pa_assert(msg);
+    pa_assert(userdata);
+    u = userdata;
+
+    pa_log_debug("dbus: interface=%s, path=%s, member=%s\n",
+                 dbus_message_get_interface(msg),
+                 dbus_message_get_path(msg),
+                 dbus_message_get_member(msg));
+
+    dbus_error_init(&err);
+
+    if (dbus_message_is_signal(msg, "org.bluez.Headset", "PropertyChanged") ||
+        dbus_message_is_signal(msg, "org.bluez.AudioSink", "PropertyChanged")) {
+
+        struct device *d;
+        const char *profile;
+        DBusMessageIter variant_i;
+        dbus_uint16_t gain;
+
+        if (!dbus_message_iter_init(msg, &arg_i)) {
+            pa_log("dbus: message has no parameters");
+            goto done;
+        }
+
+        if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_STRING) {
+            pa_log("Property name not a string.");
+            goto done;
+        }
+
+        dbus_message_iter_get_basic(&arg_i, &value);
+
+        if (!dbus_message_iter_next(&arg_i)) {
+            pa_log("Property value missing");
+            goto done;
+        }
+
+        if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_VARIANT) {
+            pa_log("Property value not a variant.");
+            goto done;
+        }
+
+        dbus_message_iter_recurse(&arg_i, &variant_i);
+
+        if (dbus_message_iter_get_arg_type(&variant_i) != DBUS_TYPE_UINT16) {
+            dbus_message_iter_get_basic(&variant_i, &gain);
+
+            if (pa_streq(value, "SpeakerGain")) {
+                pa_log("spk gain: %d", gain);
+                pa_cvolume_set(&u->sink->volume, 1, (pa_volume_t) (gain * PA_VOLUME_NORM / 15));
+                u->sink->virtual_volume = u->sink->volume;
+                pa_subscription_post(u->sink->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, u->sink->index);
+            } else {
+                pa_log("mic gain: %d", gain);
+                if (!u->source)
+                    goto done;
+
+                pa_cvolume_set(&u->source->volume, 1, (pa_volume_t) (gain * PA_VOLUME_NORM / 15));
+                pa_subscription_post(u->source->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, u->source->index);
+            }
+        }
+    }
+
+done:
+    dbus_error_free(&err);
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static int sink_get_volume_cb(pa_sink *s) {
+    struct userdata *u = s->userdata;
+    pa_assert(u);
+
+    /* refresh? */
+
+    return 0;
+}
+
+static int source_get_volume_cb(pa_source *s) {
+    struct userdata *u = s->userdata;
+    pa_assert(u);
+
+    /* refresh? */
+
+    return 0;
+}
+
+static int sink_set_volume_cb(pa_sink *s) {
+    DBusError e;
+    DBusMessage *m, *r;
+    DBusMessageIter it, itvar;
+    dbus_uint16_t vol;
+    const char *spkgain = "SpeakerGain";
+    struct userdata *u = s->userdata;
+    pa_assert(u);
+
+    dbus_error_init(&e);
+
+    vol = ((float)pa_cvolume_max(&s->volume) / PA_VOLUME_NORM) * 15;
+    pa_log_debug("set headset volume: %d", vol);
+
+    pa_assert_se(m = dbus_message_new_method_call("org.bluez", u->path, "org.bluez.Headset", "SetProperty"));
+    dbus_message_iter_init_append(m, &it);
+    dbus_message_iter_append_basic(&it, DBUS_TYPE_STRING, &spkgain);
+    dbus_message_iter_open_container(&it, DBUS_TYPE_VARIANT, DBUS_TYPE_UINT16_AS_STRING, &itvar);
+    dbus_message_iter_append_basic(&itvar, DBUS_TYPE_UINT16, &vol);
+    dbus_message_iter_close_container(&it, &itvar);
+
+    r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->conn), m, -1, &e);
+
+finish:
+    if (m)
+        dbus_message_unref(m);
+    if (r)
+        dbus_message_unref(r);
+
+    dbus_error_free(&e);
+
+    return 0;
+}
+
+static int source_set_volume_cb(pa_source *s) {
+    dbus_uint16_t vol;
+    struct userdata *u = s->userdata;
+    pa_assert(u);
+
+    vol = ((float)pa_cvolume_max(&s->volume) / PA_VOLUME_NORM) * 15;
+
+    pa_log_debug("set headset mic volume: %d (not implemented yet)", vol);
+
+    return 0;
+}
+
 int pa__init(pa_module* m) {
     int e;
     pa_modargs *ma;
@@ -796,8 +941,12 @@ int pa__init(pa_module* m) {
     pa_sink_new_data data;
     struct pollfd *pollfd;
     struct userdata *u;
+    DBusError err;
+    char *tmp;
 
     pa_assert(m);
+    dbus_error_init(&err);
+
     m->userdata = u = pa_xnew0(struct userdata, 1);
     u->module = m;
     u->core = m->core;
@@ -833,6 +982,7 @@ int pa__init(pa_module* m) {
         pa_log_error("Failed to get rate from module arguments");
         goto fail;
     }
+    u->path = pa_xstrdup(pa_modargs_get_value(ma, "path", NULL));
 
     channels = u->ss.channels;
     if (pa_modargs_get_value_u32(ma, "channels", &channels) < 0) {
@@ -910,7 +1060,50 @@ int pa__init(pa_module* m) {
         goto fail;
     }
     pa_sink_put(u->sink);
+    if (!u->path)
+        goto end;
+
+    /* connect to the bus */
+    u->conn = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &err);
+    if (dbus_error_is_set(&err) || (u->conn == NULL) ) {
+        pa_log("Failed to get D-Bus connection: %s", err.message);
+        goto fail;
+    }
 
+    /* monitor property changes */
+    if (!dbus_connection_add_filter(pa_dbus_connection_get(u->conn), filter_cb, u, NULL)) {
+        pa_log_error("Failed to add filter function");
+        goto fail;
+    }
+
+    if (pa_streq(u->profile, "hsp")) {
+
+        tmp = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged',path='%s'", u->path);
+        dbus_bus_add_match(pa_dbus_connection_get(u->conn), tmp, &err);
+        pa_xfree(tmp);
+        if (dbus_error_is_set(&err)) {
+            pa_log_error("Unable to subscribe to org.bluez.Headset signals: %s: %s", err.name, err.message);
+            goto fail;
+        }
+    } else {
+
+        tmp = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged',path='%s'", u->path);
+        dbus_bus_add_match(pa_dbus_connection_get(u->conn), tmp, &err);
+        pa_xfree(tmp);
+        if (dbus_error_is_set(&err)) {
+            pa_log_error("Unable to subscribe to org.bluez.AudioSink signals: %s: %s", err.name, err.message);
+            goto fail;
+        }
+    }
+
+    u->sink->get_volume = sink_get_volume_cb;
+    u->sink->set_volume = sink_set_volume_cb;
+    if (u->source) {
+        u->source->get_volume = source_get_volume_cb;
+        u->source->set_volume = source_set_volume_cb;
+    }
+
+end:
     pa_modargs_free(ma);
     return 0;
 
@@ -929,6 +1122,31 @@ void pa__done(pa_module *m) {
     if (!(u = m->userdata))
         return;
 
+    if (u->conn) {
+        DBusError error;
+        char *tmp;
+        dbus_error_init(&error);
+
+        if (pa_streq(u->profile, "hsp")) {
+            tmp = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged',path='%s'", u->path);
+            dbus_bus_remove_match(pa_dbus_connection_get(u->conn), tmp, &error);
+            pa_xfree(tmp);
+            dbus_error_free(&error);
+        } else {
+            tmp = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged',path='%s'", u->path);
+            dbus_bus_remove_match(pa_dbus_connection_get(u->conn), tmp, &error);
+            pa_xfree(tmp);
+            dbus_error_free(&error);
+        }
+
+        dbus_connection_remove_filter(pa_dbus_connection_get(u->conn), filter_cb, u);
+
+        pa_dbus_connection_unref(u->conn);
+    }
+
+    if (u->path)
+        pa_xfree(u->path);
+
     if (u->sink)
         pa_sink_unlink(u->sink);
 
diff --git a/src/modules/bluetooth/module-bluetooth-discover.c b/src/modules/bluetooth/module-bluetooth-discover.c
index 2fe0937..4f83def 100644
--- a/src/modules/bluetooth/module-bluetooth-discover.c
+++ b/src/modules/bluetooth/module-bluetooth-discover.c
@@ -342,7 +342,7 @@ static void load_module_for_device(struct userdata *u, struct device *d, const c
     pa_assert(d);
 
     get_device_properties(u, d);
-    args = pa_sprintf_malloc("sink_name=\"%s\" address=\"%s\" profile=\"%s\"", d->name, d->address, profile);
+    args = pa_sprintf_malloc("sink_name=\"%s\" address=\"%s\" profile=\"%s\" path=\"%s\"", d->name, d->address, profile, d->object_path);
     pa_m = pa_module_load(u->module->core, "module-bluetooth-device", args);
     pa_xfree(args);
 
@@ -468,7 +468,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *msg, void *
 
 done:
     dbus_error_free(&err);
-    return DBUS_HANDLER_RESULT_HANDLED;
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 }
 
 void pa__done(pa_module* m) {

commit 12db687acf3befe485bfff3700111999c95247fa
Author: Marc-André Lureau <marcandre.lureau at gmail.com>
Date:   Mon Jan 19 19:58:41 2009 +0200

    bluetooth: cold hsp/a2dp device detection

diff --git a/src/modules/bluetooth/module-bluetooth-discover.c b/src/modules/bluetooth/module-bluetooth-discover.c
index 4f83def..1bc05c0 100644
--- a/src/modules/bluetooth/module-bluetooth-discover.c
+++ b/src/modules/bluetooth/module-bluetooth-discover.c
@@ -53,6 +53,13 @@ struct uuid {
     PA_LLIST_FIELDS(struct uuid);
 };
 
+struct dbus_pending {
+    char *path;
+    char *profile;
+    DBusPendingCall *pending;
+    PA_LLIST_FIELDS(struct dbus_pending);
+};
+
 struct device {
     char *name;
     char *object_path;
@@ -70,7 +77,9 @@ struct device {
 struct userdata {
     pa_module *module;
     pa_dbus_connection *conn;
+    dbus_int32_t dbus_data_slot;
     PA_LLIST_HEAD(struct device, device_list);
+    PA_LLIST_HEAD(struct dbus_pending, dbus_pending_list);
 };
 
 static struct module *module_new(const char *profile, pa_module *pa_m) {
@@ -118,6 +127,31 @@ static void uuid_free(struct uuid *uuid) {
     pa_xfree(uuid);
 }
 
+static struct dbus_pending *dbus_pending_new(struct userdata *u, DBusPendingCall *pending, const char *path, const char *profile) {
+    struct dbus_pending *node;
+
+    pa_assert(pending);
+
+    node = pa_xnew(struct dbus_pending, 1);
+    node->pending = pending;
+    node->path = pa_xstrdup(path);
+    node->profile = pa_xstrdup(profile);
+    PA_LLIST_INIT(struct dbus_pending, node);
+    dbus_pending_call_set_data(pending, u->dbus_data_slot, node, NULL);
+
+    return node;
+}
+
+static void dbus_pending_free(struct dbus_pending *pending) {
+    pa_assert(pending);
+
+    pa_xfree(pending->path);
+    pa_xfree(pending->profile);
+    dbus_pending_call_cancel(pending->pending);
+    dbus_pending_call_unref(pending->pending);
+    pa_xfree(pending);
+}
+
 static struct device *device_new(const char *object_path) {
     struct device *node;
 
@@ -471,20 +505,266 @@ done:
     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 }
 
+
+
+static void get_properties_reply(DBusPendingCall *pending, void *user_data) {
+    struct userdata *u;
+    DBusMessage *r;
+    dbus_bool_t connected;
+    DBusMessageIter arg_i, element_i;
+    DBusMessageIter variant_i;
+    struct device *d;
+    struct dbus_pending *p;
+
+    pa_assert(u = user_data);
+
+    r = dbus_pending_call_steal_reply(pending);
+    if (!r)
+        goto end;
+
+    if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
+        pa_log("Error from GetProperties reply: %s", dbus_message_get_error_name(r));
+        goto end;
+    }
+
+    if (!dbus_message_iter_init(r, &arg_i)) {
+        pa_log("%s GetProperties reply has no arguments", p->profile);
+        goto end;
+    }
+
+    if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) {
+        pa_log("%s GetProperties argument is not an array", p->profile);
+        goto end;
+    }
+
+    connected = FALSE;
+    dbus_message_iter_recurse(&arg_i, &element_i);
+    while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
+
+        if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
+            DBusMessageIter dict_i;
+            const char *key;
+
+            dbus_message_iter_recurse(&element_i, &dict_i);
+
+            if (dbus_message_iter_get_arg_type(&dict_i) != DBUS_TYPE_STRING) {
+                pa_log("Property name not a string.");
+                goto end;
+            }
+
+            dbus_message_iter_get_basic(&dict_i, &key);
+
+            if (!dbus_message_iter_next(&dict_i))  {
+                pa_log("Property value missing");
+                goto end;
+            }
+
+            if (dbus_message_iter_get_arg_type(&dict_i) != DBUS_TYPE_VARIANT) {
+                pa_log("Property value not a variant.");
+                goto end;
+            }
+
+            dbus_message_iter_recurse(&dict_i, &variant_i);
+
+            switch (dbus_message_iter_get_arg_type(&variant_i)) {
+
+                case DBUS_TYPE_BOOLEAN: {
+
+                    dbus_bool_t value;
+                    dbus_message_iter_get_basic(&variant_i, &value);
+
+                    if (pa_streq(key, "Connected")) {
+                        connected = value;
+                        goto endloop;
+                    }
+
+                    break;
+                }
+            }
+        }
+
+        if (!dbus_message_iter_next(&element_i))
+            break;
+    }
+
+endloop:
+    if (connected) {
+        p = dbus_pending_call_get_data(pending, u->dbus_data_slot);
+        pa_log_debug("%s: %s connected", p->path, p->profile);
+        d = device_find(u, p->path);
+
+        if (!d) {
+            d = device_new(p->path);
+            PA_LLIST_PREPEND(struct device, u->device_list, d);
+        }
+
+        load_module_for_device(u, d, p->profile);
+    }
+
+    dbus_message_unref(r);
+
+end:
+    p = dbus_pending_call_get_data(pending, u->dbus_data_slot);
+    PA_LLIST_REMOVE(struct dbus_pending, u->dbus_pending_list, p);
+    dbus_pending_free(p);
+}
+
+static void list_devices_reply(DBusPendingCall *pending, void *user_data) {
+    DBusMessage *r, *m;
+    DBusPendingCall *call;
+    DBusError e;
+    char **paths = NULL;
+    int i, num = -1;
+    struct dbus_pending *p;
+    struct userdata *u;
+
+    pa_assert(u = user_data);
+    dbus_error_init(&e);
+
+    r = dbus_pending_call_steal_reply(pending);
+    if (!r) {
+        pa_log("Failed to get ListDevices reply");
+        goto end;
+    }
+
+    if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
+        pa_log("Error from ListDevices reply: %s", dbus_message_get_error_name(r));
+        goto end;
+    }
+
+    if (!dbus_message_get_args(r, &e, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &paths, &num, DBUS_TYPE_INVALID)) {
+        pa_log("org.bluez.Adapter.ListDevices returned an error: '%s'\n", e.message);
+        dbus_error_free(&e);
+    } else {
+        for (i = 0; i < num; ++i) {
+            pa_assert_se(m = dbus_message_new_method_call("org.bluez", paths[i], "org.bluez.Headset", "GetProperties"));
+            if (dbus_connection_send_with_reply(pa_dbus_connection_get(u->conn), m, &call, -1)) {
+                p = dbus_pending_new(u, call, paths[i], "hsp");
+                PA_LLIST_PREPEND(struct dbus_pending, u->dbus_pending_list, p);
+                dbus_pending_call_set_notify(call, get_properties_reply, u, NULL);
+            } else {
+                pa_log("Failed to send GetProperties");
+            }
+
+            dbus_message_unref(m);
+
+            pa_assert_se(m = dbus_message_new_method_call("org.bluez", paths[i], "org.bluez.AudioSink", "GetProperties"));
+            if (dbus_connection_send_with_reply(pa_dbus_connection_get(u->conn), m, &call, -1)) {
+                p = dbus_pending_new(u, call, paths[i], "a2dp");
+                PA_LLIST_PREPEND(struct dbus_pending, u->dbus_pending_list, p);
+                dbus_pending_call_set_notify(call, get_properties_reply, u, NULL);
+            } else {
+                pa_log("Failed to send GetProperties");
+            }
+
+            dbus_message_unref(m);
+        }
+    }
+
+    if (paths)
+        dbus_free_string_array (paths);
+    dbus_message_unref(r);
+
+end:
+    p = dbus_pending_call_get_data(pending, u->dbus_data_slot);
+    PA_LLIST_REMOVE(struct dbus_pending, u->dbus_pending_list, p);
+    dbus_pending_free(p);
+}
+
+static void list_adapters_reply(DBusPendingCall *pending, void *user_data) {
+    DBusMessage *r, *m;
+    DBusPendingCall *call;
+    DBusError e;
+    char **paths = NULL;
+    int i, num = -1;
+    struct dbus_pending *p;
+    struct userdata *u;
+
+    pa_assert(u = user_data);
+    dbus_error_init(&e);
+
+    r = dbus_pending_call_steal_reply(pending);
+    if (!r) {
+        pa_log("Failed to get ListAdapters reply");
+        goto end;
+    }
+
+    if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
+        pa_log("Error from ListAdapters reply: %s", dbus_message_get_error_name(r));
+        goto end;
+    }
+
+    if (!dbus_message_get_args(r, &e, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &paths, &num, DBUS_TYPE_INVALID)) {
+        pa_log("org.bluez.Manager.ListAdapters returned an error: '%s'\n", e.message);
+        dbus_error_free(&e);
+    } else {
+        for (i = 0; i < num; ++i) {
+            pa_assert_se(m = dbus_message_new_method_call("org.bluez", paths[i], "org.bluez.Adapter", "ListDevices"));
+            if (dbus_connection_send_with_reply(pa_dbus_connection_get(u->conn), m, &call, -1)) {
+                p = dbus_pending_new(u, call, NULL, NULL);
+                PA_LLIST_PREPEND(struct dbus_pending, u->dbus_pending_list, p);
+                dbus_pending_call_set_notify(call, list_devices_reply, u, NULL);
+            } else {
+                pa_log("Failed to send ListDevices");
+            }
+
+            dbus_message_unref(m);
+        }
+    }
+
+    if (paths)
+        dbus_free_string_array (paths);
+    dbus_message_unref(r);
+
+end:
+    p = dbus_pending_call_get_data(pending, u->dbus_data_slot);
+    PA_LLIST_REMOVE(struct dbus_pending, u->dbus_pending_list, p);
+    dbus_pending_free(p);
+}
+
+static void lookup_devices(struct userdata *u) {
+    DBusMessage *m;
+    DBusPendingCall *call;
+    struct dbus_pending *p;
+
+    pa_assert(u);
+
+    pa_assert_se(m = dbus_message_new_method_call("org.bluez", "/", "org.bluez.Manager", "ListAdapters"));
+    if (dbus_connection_send_with_reply(pa_dbus_connection_get(u->conn), m, &call, -1)) {
+        p = dbus_pending_new(u, call, NULL, NULL);
+        PA_LLIST_PREPEND(struct dbus_pending, u->dbus_pending_list, p);
+        dbus_pending_call_set_notify(call, list_adapters_reply, u, NULL);
+    } else {
+        pa_log("Failed to send ListAdapters");
+    }
+
+    dbus_message_unref(m);
+}
+
 void pa__done(pa_module* m) {
     struct userdata *u;
     struct device *i;
+    struct dbus_pending *p;
 
     pa_assert(m);
 
     if (!(u = m->userdata))
         return;
 
+    while ((p = u->dbus_pending_list)) {
+        PA_LLIST_REMOVE(struct dbus_pending, u->dbus_pending_list, p);
+        dbus_pending_free(p);
+    }
+
     while ((i = u->device_list)) {
         PA_LLIST_REMOVE(struct device, u->device_list, i);
         device_free(i);
     }
 
+    if (u->dbus_data_slot != -1) {
+        dbus_pending_call_free_data_slot(&u->dbus_data_slot);
+    }
+
     if (u->conn) {
         DBusError error;
         dbus_error_init(&error);
@@ -514,8 +794,10 @@ int pa__init(pa_module* m) {
     dbus_error_init(&err);
 
     m->userdata = u = pa_xnew(struct userdata, 1);
+    u->dbus_data_slot = -1;
     u->module = m;
     PA_LLIST_HEAD_INIT(struct device, u->device_list);
+    PA_LLIST_HEAD_INIT(DBusPendingCall, u->dbus_pending_list);
 
     /* connect to the bus */
     u->conn = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &err);
@@ -524,6 +806,9 @@ int pa__init(pa_module* m) {
         goto fail;
     }
 
+    if (!dbus_pending_call_allocate_data_slot(&u->dbus_data_slot))
+        goto fail;
+
     /* dynamic detection of bluetooth audio devices */
     if (!dbus_connection_add_filter(pa_dbus_connection_get(u->conn), filter_cb, u, NULL)) {
         pa_log_error("Failed to add filter function");
@@ -548,6 +833,8 @@ int pa__init(pa_module* m) {
         goto fail;
     }
 
+    lookup_devices(u);
+
     return 0;
 
 fail:

commit 98821c783da026a3f56ac48724806317ce052786
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jan 28 01:31:54 2009 +0100

    print the right software volume

diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index 4328957..b097f22 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -929,11 +929,11 @@ static void sink_set_volume_cb(pa_sink *s) {
         char t[PA_CVOLUME_SNPRINT_MAX];
 
         /* Match exactly what the user requested by software */
-        pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &r);
+        pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &s->hardware_volume);
 
         pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->virtual_volume));
         pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &u->hardware_volume));
-        pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &r));
+        pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->soft_volume));
 
     } else
 
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index 4204297..96bf6a3 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -876,11 +876,11 @@ static void source_set_volume_cb(pa_source *s) {
 
         /* Match exactly what the user requested by software */
 
-        pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &r);
+        pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &s->hardware_volume);
 
         pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->virtual_volume));
         pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &u->hardware_volume));
-        pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &r));
+        pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->soft_volume));
 
     } else
 

commit fc3ff114186020637e1dfbe23ff01d00b0452ccf
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jan 28 01:39:19 2009 +0100

    fix two typos

diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index b097f22..22460bb 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -929,7 +929,7 @@ static void sink_set_volume_cb(pa_sink *s) {
         char t[PA_CVOLUME_SNPRINT_MAX];
 
         /* Match exactly what the user requested by software */
-        pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &s->hardware_volume);
+        pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &u->hardware_volume);
 
         pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->virtual_volume));
         pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &u->hardware_volume));
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index 96bf6a3..0fd9838 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -876,7 +876,7 @@ static void source_set_volume_cb(pa_source *s) {
 
         /* Match exactly what the user requested by software */
 
-        pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &s->hardware_volume);
+        pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &u->hardware_volume);
 
         pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->virtual_volume));
         pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &u->hardware_volume));

commit a5401a50a67ebf1d86e979ee5556961b24a25400
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jan 28 01:46:27 2009 +0100

    store the default sink/source in proper pa_sink*/pa_source* pointers instead of a string

diff --git a/src/daemon/main.c b/src/daemon/main.c
index bd8ad1d..936c214 100644
--- a/src/daemon/main.c
+++ b/src/daemon/main.c
@@ -971,11 +971,6 @@ int main(int argc, char *argv[]) {
         goto finish;
     }
 
-    if (c->default_sink_name && !pa_namereg_get(c, c->default_sink_name, PA_NAMEREG_SINK) && conf->fail) {
-        pa_log_error(_("Default sink name (%s) does not exist in name register."), c->default_sink_name);
-        goto finish;
-    }
-
 #ifdef HAVE_FORK
     if (daemon_pipe[1] >= 0) {
         int ok = 0;
diff --git a/src/modules/module-default-device-restore.c b/src/modules/module-default-device-restore.c
index 97d3fb2..d299f40 100644
--- a/src/modules/module-default-device-restore.c
+++ b/src/modules/module-default-device-restore.c
@@ -57,10 +57,11 @@ static void load(struct userdata *u) {
 
     /* We never overwrite manually configured settings */
 
-    if (u->core->default_sink_name)
+    if (u->core->default_sink)
         pa_log_info("Manually configured default sink, not overwriting.");
     else if ((f = fopen(u->sink_filename, "r"))) {
         char ln[256] = "";
+        pa_sink *s;
 
         fgets(ln, sizeof(ln)-1, f);
         pa_strip_nl(ln);
@@ -68,8 +69,8 @@ static void load(struct userdata *u) {
 
         if (!ln[0])
             pa_log_info("No previous default sink setting, ignoring.");
-        else if (pa_namereg_get(u->core, ln, PA_NAMEREG_SINK)) {
-            pa_namereg_set_default(u->core, ln, PA_NAMEREG_SINK);
+        else if ((s = pa_namereg_get(u->core, ln, PA_NAMEREG_SINK))) {
+            pa_namereg_set_default_sink(u->core, s);
             pa_log_info("Restored default sink '%s'.", ln);
         } else
             pa_log_info("Saved default sink '%s' not existant, not restoring default sink setting.", ln);
@@ -77,10 +78,11 @@ static void load(struct userdata *u) {
     } else if (errno != ENOENT)
         pa_log("Failed to load default sink: %s", pa_cstrerror(errno));
 
-    if (u->core->default_source_name)
+    if (u->core->default_source)
         pa_log_info("Manually configured default source, not overwriting.");
     else if ((f = fopen(u->source_filename, "r"))) {
         char ln[256] = "";
+        pa_source *s;
 
         fgets(ln, sizeof(ln)-1, f);
         pa_strip_nl(ln);
@@ -88,8 +90,8 @@ static void load(struct userdata *u) {
 
         if (!ln[0])
             pa_log_info("No previous default source setting, ignoring.");
-        else if (pa_namereg_get(u->core, ln, PA_NAMEREG_SOURCE)) {
-            pa_namereg_set_default(u->core, ln, PA_NAMEREG_SOURCE);
+        else if ((s = pa_namereg_get(u->core, ln, PA_NAMEREG_SOURCE))) {
+            pa_namereg_set_default_source(u->core, s);
             pa_log_info("Restored default source '%s'.", ln);
         } else
             pa_log_info("Saved default source '%s' not existant, not restoring default source setting.", ln);
@@ -106,8 +108,8 @@ static void save(struct userdata *u) {
 
     if (u->sink_filename) {
         if ((f = fopen(u->sink_filename, "w"))) {
-            const char *n = pa_namereg_get_default_sink_name(u->core);
-            fprintf(f, "%s\n", pa_strempty(n));
+            pa_sink *s = pa_namereg_get_default_sink(u->core);
+            fprintf(f, "%s\n", s ? s->name : "");
             fclose(f);
         } else
             pa_log("Failed to save default sink: %s", pa_cstrerror(errno));
@@ -115,8 +117,8 @@ static void save(struct userdata *u) {
 
     if (u->source_filename) {
         if ((f = fopen(u->source_filename, "w"))) {
-            const char *n = pa_namereg_get_default_source_name(u->core);
-            fprintf(f, "%s\n", pa_strempty(n));
+            pa_source *s = pa_namereg_get_default_source(u->core);
+            fprintf(f, "%s\n", s ? s->name : "");
             fclose(f);
         } else
             pa_log("Failed to save default source: %s", pa_cstrerror(errno));
diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c
index be5a394..1df0bd6 100644
--- a/src/pulsecore/cli-command.c
+++ b/src/pulsecore/cli-command.c
@@ -324,7 +324,8 @@ static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
     char s[256];
     const pa_mempool_stat *stat;
     unsigned k;
-    const char *def_sink, *def_source;
+    pa_sink *def_sink;
+    pa_source *def_source;
 
     static const char* const type_table[PA_MEMBLOCK_TYPE_MAX] = {
         [PA_MEMBLOCK_POOL] = "POOL",
@@ -364,12 +365,12 @@ static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
     pa_strbuf_printf(buf, "Default sample spec: %s\n",
                      pa_sample_spec_snprint(s, sizeof(s), &c->default_sample_spec));
 
-    def_sink = pa_namereg_get_default_sink_name(c);
-    def_source = pa_namereg_get_default_source_name(c);
+    def_sink = pa_namereg_get_default_sink(c);
+    def_source = pa_namereg_get_default_source(c);
     pa_strbuf_printf(buf, "Default sink name: %s\n"
                      "Default source name: %s\n",
-                     def_sink ? def_sink : "none",
-                     def_source ? def_source : "none");
+                     def_sink ? def_sink->name : "none",
+                     def_source ? def_source->name : "none");
 
     for (k = 0; k < PA_MEMBLOCK_TYPE_MAX; k++)
         pa_strbuf_printf(buf,
@@ -858,6 +859,7 @@ static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf
 
 static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
     const char *n;
+    pa_sink *s;
 
     pa_core_assert_ref(c);
     pa_assert(t);
@@ -869,12 +871,17 @@ static int pa_cli_command_sink_default(pa_core *c, pa_tokenizer *t, pa_strbuf *b
         return -1;
     }
 
-    pa_namereg_set_default(c, n, PA_NAMEREG_SINK);
+    if ((s = pa_namereg_get(c, n, PA_NAMEREG_SINK)))
+        pa_namereg_set_default_sink(c, s);
+    else
+        pa_strbuf_printf(buf, "Sink %s does not exist.\n", n);
+
     return 0;
 }
 
 static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
     const char *n;
+    pa_source *s;
 
     pa_core_assert_ref(c);
     pa_assert(t);
@@ -886,7 +893,10 @@ static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf
         return -1;
     }
 
-    pa_namereg_set_default(c, n, PA_NAMEREG_SOURCE);
+    if ((s = pa_namereg_get(c, n, PA_NAMEREG_SOURCE)))
+        pa_namereg_set_default_source(c, s);
+    else
+        pa_strbuf_printf(buf, "Source %s does not exist.\n", n);
     return 0;
 }
 
@@ -1451,7 +1461,6 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
     pa_source *source;
     pa_card *card;
     int nl;
-    const char *p;
     uint32_t idx;
     char txt[256];
     time_t now;
@@ -1518,20 +1527,20 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b
 
     nl = 0;
 
-    if ((p = pa_namereg_get_default_sink_name(c))) {
+    if ((sink = pa_namereg_get_default_sink(c))) {
         if (!nl) {
             pa_strbuf_puts(buf, "\n");
             nl = 1;
         }
-        pa_strbuf_printf(buf, "set-default-sink %s\n", p);
+        pa_strbuf_printf(buf, "set-default-sink %s\n", sink->name);
     }
 
-    if ((p = pa_namereg_get_default_source_name(c))) {
+    if ((source = pa_namereg_get_default_source(c))) {
         if (!nl) {
             pa_strbuf_puts(buf, "\n");
             nl = 1;
         }
-        pa_strbuf_printf(buf, "set-default-source %s\n", p);
+        pa_strbuf_printf(buf, "set-default-source %s\n", source->name);
     }
 
     pa_strbuf_puts(buf, "\n### EOF\n");
diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index 647fc1b..57129d0 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -248,7 +248,7 @@ char *pa_sink_list_to_string(pa_core *c) {
             "\tchannel map: %s%s%s\n"
             "\tused by: %u\n"
             "\tlinked by: %u\n",
-            c->default_sink_name && !strcmp(sink->name, c->default_sink_name) ? '*' : ' ',
+            sink == c->default_sink ? '*' : ' ',
             sink->index,
             sink->name,
             sink->driver,
@@ -339,7 +339,7 @@ char *pa_source_list_to_string(pa_core *c) {
             "\tchannel map: %s%s%s\n"
             "\tused by: %u\n"
             "\tlinked by: %u\n",
-            c->default_source_name && !strcmp(source->name, c->default_source_name) ? '*' : ' ',
+            c->default_source == source ? '*' : ' ',
             source->index,
             source->name,
             source->driver,
diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c
index 381a677..689fc8f 100644
--- a/src/pulsecore/core.c
+++ b/src/pulsecore/core.c
@@ -99,7 +99,8 @@ pa_core* pa_core_new(pa_mainloop_api *m, pa_bool_t shared, size_t shm_size) {
     c->sink_inputs = pa_idxset_new(NULL, NULL);
     c->cards = pa_idxset_new(NULL, NULL);
 
-    c->default_source_name = c->default_sink_name = NULL;
+    c->default_source = NULL;
+    c->default_sink = NULL;
 
     c->modules = NULL;
     c->namereg = NULL;
@@ -191,8 +192,8 @@ static void core_free(pa_object *o) {
     if (c->exit_event)
         c->mainloop->time_free(c->exit_event);
 
-    pa_xfree(c->default_source_name);
-    pa_xfree(c->default_sink_name);
+    pa_assert(!c->default_source);
+    pa_assert(!c->default_sink);
 
     pa_silence_cache_done(&c->silence_cache);
     pa_mempool_free(c->mempool);
diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index e33a245..b349c6f 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -25,6 +25,8 @@
 #include <pulse/mainloop-api.h>
 #include <pulse/sample.h>
 
+typedef struct pa_core pa_core;
+
 #include <pulsecore/idxset.h>
 #include <pulsecore/hashmap.h>
 #include <pulsecore/memblock.h>
@@ -34,9 +36,8 @@
 #include <pulsecore/hook-list.h>
 #include <pulsecore/asyncmsgq.h>
 #include <pulsecore/sample-util.h>
-
-typedef struct pa_core pa_core;
-
+#include <pulsecore/sink.h>
+#include <pulsecore/source.h>
 #include <pulsecore/core-subscribe.h>
 #include <pulsecore/sink-input.h>
 #include <pulsecore/msgobject.h>
@@ -112,7 +113,8 @@ struct pa_core {
     pa_hashmap *namereg, *shared;
 
     /* The name of the default sink/source */
-    char *default_source_name, *default_sink_name;
+    pa_source *default_source;
+    pa_sink *default_sink;
 
     pa_sample_spec default_sample_spec;
     unsigned default_n_fragments, default_fragment_size_msec;
diff --git a/src/pulsecore/namereg.c b/src/pulsecore/namereg.c
index ed652ab..f3d5a8f 100644
--- a/src/pulsecore/namereg.c
+++ b/src/pulsecore/namereg.c
@@ -173,6 +173,11 @@ void pa_namereg_unregister(pa_core *c, const char *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);
+
     pa_xfree(e->name);
     pa_xfree(e);
 }
@@ -182,32 +187,27 @@ void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type) {
     uint32_t idx;
     pa_assert(c);
 
-    if (!name) {
+    if (type == PA_NAMEREG_SOURCE && (!name || pa_streq(name, "@DEFAULT_SOURCE@"))) {
+        pa_source *s;
 
-        if (type == PA_NAMEREG_SOURCE)
-            name = pa_namereg_get_default_source_name(c);
-        else if (type == PA_NAMEREG_SINK)
-            name = pa_namereg_get_default_sink_name(c);
+        if ((s = pa_namereg_get_default_source(c)))
+            return s;
 
-    } else if (strcmp(name, "@DEFAULT_SINK@") == 0) {
-        if (type == PA_NAMEREG_SINK)
-               name = pa_namereg_get_default_sink_name(c);
+    } else if (type == PA_NAMEREG_SINK && (!name || pa_streq(name, "@DEFAULT_SINK@"))) {
+        pa_sink *s;
 
-    } else if (strcmp(name, "@DEFAULT_SOURCE@") == 0) {
-        if (type == PA_NAMEREG_SOURCE)
-            name = pa_namereg_get_default_source_name(c);
+        if ((s = pa_namereg_get_default_sink(c)))
+            return s;
 
-    } else if (strcmp(name, "@DEFAULT_MONITOR@") == 0) {
-        if (type == PA_NAMEREG_SOURCE) {
-            pa_sink *k;
+    } else if (type == PA_NAMEREG_SOURCE && name && pa_streq(name, "@DEFAULT_MONITOR@")) {
+        pa_sink *s;
 
-            if ((k = pa_namereg_get(c, NULL, PA_NAMEREG_SINK)))
-                return k->monitor_source;
-        }
-    } else if (*name == '@')
-        name = NULL;
+        if ((s = pa_namereg_get(c, NULL, PA_NAMEREG_SINK)))
+            return s->monitor_source;
+
+    }
 
-    if (!name)
+    if (*name == '@' || !name || !pa_namereg_is_valid_name(name))
         return NULL;
 
     if (c->namereg && (e = pa_hashmap_get(c->namereg, name)))
@@ -229,62 +229,57 @@ void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type) {
     return NULL;
 }
 
-int pa_namereg_set_default(pa_core*c, const char *name, pa_namereg_type_t type) {
-    char **s;
-
+pa_sink* pa_namereg_set_default_sink(pa_core*c, pa_sink *s) {
     pa_assert(c);
-    pa_assert(type == PA_NAMEREG_SINK || type == PA_NAMEREG_SOURCE);
-
-    s = type == PA_NAMEREG_SINK ? &c->default_sink_name : &c->default_source_name;
 
-    if (!name && !*s)
-        return 0;
+    if (c->default_sink != s) {
+        c->default_sink = s;
+        pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX);
+    }
 
-    if (name && *s && !strcmp(name, *s))
-        return 0;
+    return s;
+}
 
-    if (!pa_namereg_is_valid_name(name))
-        return -1;
+pa_source* pa_namereg_set_default_source(pa_core*c, pa_source *s) {
+    pa_assert(c);
 
-    pa_xfree(*s);
-    *s = pa_xstrdup(name);
-    pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX);
+    if (c->default_source != s) {
+        c->default_source = s;
+        pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX);
+    }
 
-    return 0;
+    return s;
 }
 
-const char *pa_namereg_get_default_sink_name(pa_core *c) {
+pa_sink *pa_namereg_get_default_sink(pa_core *c) {
     pa_sink *s;
 
     pa_assert(c);
 
-    if (c->default_sink_name)
-        return c->default_sink_name;
+    if (c->default_sink)
+        return c->default_sink;
 
     if ((s = pa_idxset_first(c->sinks, NULL)))
-        pa_namereg_set_default(c, s->name, PA_NAMEREG_SINK);
+        return pa_namereg_set_default_sink(c, s);
 
-    return c->default_sink_name;
+    return NULL;
 }
 
-const char *pa_namereg_get_default_source_name(pa_core *c) {
+pa_source *pa_namereg_get_default_source(pa_core *c) {
     pa_source *s;
     uint32_t idx;
 
     pa_assert(c);
 
-    if (c->default_source_name)
-        return c->default_source_name;
+    if (c->default_source)
+        return c->default_source;
 
-    for (s = pa_idxset_first(c->sources, &idx); s; s = pa_idxset_next(c->sources, &idx))
-        if (!s->monitor_of) {
-            pa_namereg_set_default(c, s->name, PA_NAMEREG_SOURCE);
-            break;
-        }
+    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 (!c->default_source_name)
-        if ((s = pa_idxset_first(c->sources, NULL)))
-            pa_namereg_set_default(c, s->name, PA_NAMEREG_SOURCE);
+    if ((s = pa_idxset_first(c->sources, NULL)))
+        return pa_namereg_set_default_source(c, s);
 
-    return c->default_source_name;
+    return NULL;
 }
diff --git a/src/pulsecore/namereg.h b/src/pulsecore/namereg.h
index 4205f2f..b91dd52 100644
--- a/src/pulsecore/namereg.h
+++ b/src/pulsecore/namereg.h
@@ -39,10 +39,12 @@ void pa_namereg_free(pa_core *c);
 const char *pa_namereg_register(pa_core *c, const char *name, pa_namereg_type_t type, void *data, pa_bool_t fail);
 void pa_namereg_unregister(pa_core *c, const char *name);
 void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type);
-int pa_namereg_set_default(pa_core*c, const char *name, pa_namereg_type_t type);
 
-const char *pa_namereg_get_default_sink_name(pa_core *c);
-const char *pa_namereg_get_default_source_name(pa_core *c);
+pa_sink* pa_namereg_set_default_sink(pa_core*c, pa_sink *s);
+pa_source* pa_namereg_set_default_source(pa_core*c, pa_source *s);
+
+pa_sink *pa_namereg_get_default_sink(pa_core *c);
+pa_source *pa_namereg_get_default_source(pa_core *c);
 
 pa_bool_t pa_namereg_is_valid_name(const char *name);
 char* pa_namereg_make_valid_name(const char *name);
diff --git a/src/pulsecore/protocol-http.c b/src/pulsecore/protocol-http.c
index c89d48b..5379a36 100644
--- a/src/pulsecore/protocol-http.c
+++ b/src/pulsecore/protocol-http.c
@@ -156,6 +156,8 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
 
             if (!strcmp(c->url, URL_ROOT)) {
                 char txt[256];
+                pa_sink *def_sink;
+                pa_source *def_source;
                 http_response(c, 200, "OK", "text/html");
 
                 pa_ioline_puts(c->line,
@@ -173,8 +175,12 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
                 PRINTF_FIELD("User Name:", pa_get_user_name(txt, sizeof(txt)));
                 PRINTF_FIELD("Host name:", pa_get_host_name(txt, sizeof(txt)));
                 PRINTF_FIELD("Default Sample Specification:", pa_sample_spec_snprint(txt, sizeof(txt), &c->protocol->core->default_sample_spec));
-                PRINTF_FIELD("Default Sink:", pa_namereg_get_default_sink_name(c->protocol->core));
-                PRINTF_FIELD("Default Source:", pa_namereg_get_default_source_name(c->protocol->core));
+
+                def_sink = pa_namereg_get_default_sink(c->protocol->core);
+                def_source = pa_namereg_get_default_source(c->protocol->core);
+
+                PRINTF_FIELD("Default Sink:", def_sink ? def_sink->name : "n/a");
+                PRINTF_FIELD("Default Source:", def_source ? def_source->name : "n/a");
 
                 pa_ioline_puts(c->line, "</table>");
 
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index c33d15e..c962165 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -3031,7 +3031,8 @@ static void command_get_server_info(pa_pdispatch *pd, uint32_t command, uint32_t
     pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     pa_tagstruct *reply;
     char txt[256];
-    const char *n;
+    pa_sink *def_sink;
+    pa_source *def_source;
     pa_sample_spec fixed_ss;
 
     pa_native_connection_assert_ref(c);
@@ -3053,10 +3054,10 @@ static void command_get_server_info(pa_pdispatch *pd, uint32_t command, uint32_t
     fixup_sample_spec(c, &fixed_ss, &c->protocol->core->default_sample_spec);
     pa_tagstruct_put_sample_spec(reply, &fixed_ss);
 
-    n = pa_namereg_get_default_sink_name(c->protocol->core);
-    pa_tagstruct_puts(reply, n);
-    n = pa_namereg_get_default_source_name(c->protocol->core);
-    pa_tagstruct_puts(reply, n);
+    def_sink = pa_namereg_get_default_sink(c->protocol->core);
+    pa_tagstruct_puts(reply, def_sink ? def_sink->name : NULL);
+    def_source = pa_namereg_get_default_source(c->protocol->core);
+    pa_tagstruct_puts(reply, def_source ? def_source->name : NULL);
 
     pa_tagstruct_putu32(reply, c->protocol->core->cookie);
 
@@ -3671,7 +3672,23 @@ static void command_set_default_sink_or_source(pa_pdispatch *pd, uint32_t comman
     CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
     CHECK_VALIDITY(c->pstream, !s || pa_namereg_is_valid_name(s), tag, PA_ERR_INVALID);
 
-    pa_namereg_set_default(c->protocol->core, s, command == PA_COMMAND_SET_DEFAULT_SOURCE ? PA_NAMEREG_SOURCE : PA_NAMEREG_SINK);
+    if (command == PA_COMMAND_SET_DEFAULT_SOURCE) {
+        pa_source *source;
+
+        source = pa_namereg_get(c->protocol->core, s, PA_NAMEREG_SOURCE);
+        CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
+
+        pa_namereg_set_default_source(c->protocol->core, source);
+    } else {
+        pa_sink *sink;
+        pa_assert(command == PA_COMMAND_SET_DEFAULT_SINK);
+
+        sink = pa_namereg_get(c->protocol->core, s, PA_NAMEREG_SINK);
+        CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
+
+        pa_namereg_set_default_sink(c->protocol->core, sink);
+    }
+
     pa_pstream_send_simple_ack(c->pstream, tag);
 }
 
diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
index 671894e..79b4926 100644
--- a/src/pulsecore/source-output.h
+++ b/src/pulsecore/source-output.h
@@ -32,6 +32,7 @@ typedef struct pa_source_output pa_source_output;
 #include <pulsecore/resampler.h>
 #include <pulsecore/module.h>
 #include <pulsecore/client.h>
+#include <pulsecore/sink-input.h>
 
 typedef enum pa_source_output_state {
     PA_SOURCE_OUTPUT_INIT,

commit b56e0389d1ddf5dea496bf7c0536e1ce1e49b49e
Merge: a5401a5... 12db687...
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jan 28 04:22:51 2009 +0100

    Merge commit '12db687acf3befe485bfff3700111999c95247fa'


commit f7c3ca7e9fb3af2ef0c97ba3964e856128496449
Merge: b56e038... 085ca5f...
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Jan 28 04:25:07 2009 +0100

    Merge commit 'origin/master-tx'


-- 
hooks/post-receive
PulseAudio Sound Server



More information about the pulseaudio-commits mailing list