[pulseaudio-commits] [SCM] PulseAudio Sound Server branch, master, updated. v0.9.13-288-gb987e5e

Lennart Poettering gitmailer-noreply at 0pointer.de
Mon Jan 19 13:02:58 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 branch has been updated
      from  5793f93350dd8f29b7bc97eb1ad38873e4ecebde (commit)

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

Summary of changes:
 PROTOCOL                              |    8 +-
 src/Makefile.am                       |    9 +-
 src/map-file                          |    3 +
 src/modules/alsa/alsa-util.c          |  297 ++++++++++++++++++++-------------
 src/modules/alsa/alsa-util.h          |   19 ++-
 src/modules/alsa/module-alsa-card.c   |  196 ++++++++++++++++++++++
 src/modules/alsa/module-alsa-sink.c   |   16 +-
 src/modules/alsa/module-alsa-source.c |   14 +-
 src/pulse/introspect.c                |    7 +-
 src/pulse/introspect.h                |    1 +
 src/pulse/proplist.c                  |   21 ++-
 src/pulse/proplist.h                  |   17 ++-
 src/pulse/volume.c                    |   91 ++++++++++
 src/pulse/volume.h                    |   14 ++
 src/pulsecore/card.c                  |   13 +-
 src/pulsecore/card.h                  |   12 +-
 src/pulsecore/cli-command.c           |    1 +
 src/pulsecore/cli-text.c              |   61 +++++--
 src/pulsecore/module.c                |   30 +++-
 src/pulsecore/module.h                |    7 +-
 src/pulsecore/protocol-native.c       |   14 +-
 src/pulsecore/strbuf.c                |    6 +
 src/pulsecore/strbuf.h                |    3 +
 src/tests/voltest.c                   |   29 ++++
 24 files changed, 704 insertions(+), 185 deletions(-)
 create mode 100644 src/modules/alsa/module-alsa-card.c

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

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 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) {

-- 
hooks/post-receive
PulseAudio Sound Server



More information about the pulseaudio-commits mailing list