From b4d80462bf66530ead1e4877f848c63f7693bd58 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 16 Jan 2009 23:33:15 +0100 Subject: add card profile prober --- src/Makefile.am | 9 +- src/modules/alsa/alsa-util.c | 180 ++++++++++++++++++++++++++---------- src/modules/alsa/alsa-util.h | 13 +++ src/modules/alsa/module-alsa-card.c | 56 +++++++++++ 4 files changed, 207 insertions(+), 51 deletions(-) create mode 100644 src/modules/alsa/module-alsa-card.c diff --git a/src/Makefile.am b/src/Makefile.am index 99ed7b2b..839cce09 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-@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 diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c index 5fabf644..37b12dc8 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 51de2856..86b76b7d 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 00000000..64559c4f --- /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 +#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(); +} -- cgit