]> code.delx.au - pulseaudio/commitdiff
add card profile prober
authorLennart Poettering <lennart@poettering.net>
Fri, 16 Jan 2009 22:33:15 +0000 (23:33 +0100)
committerLennart Poettering <lennart@poettering.net>
Fri, 16 Jan 2009 22:33:15 +0000 (23:33 +0100)
src/Makefile.am
src/modules/alsa/alsa-util.c
src/modules/alsa/alsa-util.h
src/modules/alsa/module-alsa-card.c [new file with mode: 0644]

index 99ed7b2bff3445c2afd1ec0d659a711ee2f3d610..839cce0970f91ff1bd3092f64dbccd9643efb33e 100644 (file)
@@ -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-@PA_MAJORMINORMICRO@.la libpulsecommon-@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-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
+module_alsa_card_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
+
 # Solaris
 
 module_solaris_la_SOURCES = modules/module-solaris.c
index 5fabf6449a56be5550ad2cbe4bc68b07da6ff894..37b12dc81ba6e63b229be7e00a9195b9c178795b 100644 (file)
@@ -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;
 
index 51de28563ed04fc822635b2c838270492215066e..86b76b7d657003f6ed83b8e6aeca199f96c9f4c9 100644 (file)
@@ -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 (file)
index 0000000..64559c4
--- /dev/null
@@ -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();
+}