diff options
Diffstat (limited to 'src/modules/alsa/alsa-util.c')
-rw-r--r-- | src/modules/alsa/alsa-util.c | 1191 |
1 files changed, 332 insertions, 859 deletions
diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c index 454cfd4e..1f3e5dcd 100644 --- a/src/modules/alsa/alsa-util.c +++ b/src/modules/alsa/alsa-util.c @@ -33,6 +33,7 @@ #include <pulse/timeval.h> #include <pulse/util.h> #include <pulse/i18n.h> +#include <pulse/utf8.h> #include <pulsecore/log.h> #include <pulsecore/macro.h> @@ -40,8 +41,11 @@ #include <pulsecore/atomic.h> #include <pulsecore/core-error.h> #include <pulsecore/once.h> +#include <pulsecore/thread.h> +#include <pulsecore/conf-parser.h> #include "alsa-util.h" +#include "alsa-mixer.h" #ifdef HAVE_HAL #include "hal-util.h" @@ -51,191 +55,6 @@ #include "udev-util.h" #endif -struct pa_alsa_fdlist { - unsigned num_fds; - struct pollfd *fds; - /* This is a temporary buffer used to avoid lots of mallocs */ - struct pollfd *work_fds; - - snd_mixer_t *mixer; - - pa_mainloop_api *m; - pa_defer_event *defer; - pa_io_event **ios; - - pa_bool_t polled; - - void (*cb)(void *userdata); - void *userdata; -}; - -static void io_cb(pa_mainloop_api*a, pa_io_event* e, int fd, pa_io_event_flags_t events, void *userdata) { - - struct pa_alsa_fdlist *fdl = userdata; - int err; - unsigned i; - unsigned short revents; - - pa_assert(a); - pa_assert(fdl); - pa_assert(fdl->mixer); - pa_assert(fdl->fds); - pa_assert(fdl->work_fds); - - if (fdl->polled) - return; - - fdl->polled = TRUE; - - memcpy(fdl->work_fds, fdl->fds, sizeof(struct pollfd) * fdl->num_fds); - - for (i = 0; i < fdl->num_fds; i++) { - if (e == fdl->ios[i]) { - if (events & PA_IO_EVENT_INPUT) - fdl->work_fds[i].revents |= POLLIN; - if (events & PA_IO_EVENT_OUTPUT) - fdl->work_fds[i].revents |= POLLOUT; - if (events & PA_IO_EVENT_ERROR) - fdl->work_fds[i].revents |= POLLERR; - if (events & PA_IO_EVENT_HANGUP) - fdl->work_fds[i].revents |= POLLHUP; - break; - } - } - - pa_assert(i != fdl->num_fds); - - if ((err = snd_mixer_poll_descriptors_revents(fdl->mixer, fdl->work_fds, fdl->num_fds, &revents)) < 0) { - pa_log_error("Unable to get poll revent: %s", snd_strerror(err)); - return; - } - - a->defer_enable(fdl->defer, 1); - - if (revents) - snd_mixer_handle_events(fdl->mixer); -} - -static void defer_cb(pa_mainloop_api*a, pa_defer_event* e, void *userdata) { - struct pa_alsa_fdlist *fdl = userdata; - unsigned num_fds, i; - int err, n; - struct pollfd *temp; - - pa_assert(a); - pa_assert(fdl); - pa_assert(fdl->mixer); - - a->defer_enable(fdl->defer, 0); - - if ((n = snd_mixer_poll_descriptors_count(fdl->mixer)) < 0) { - pa_log("snd_mixer_poll_descriptors_count() failed: %s", snd_strerror(n)); - return; - } - num_fds = (unsigned) n; - - if (num_fds != fdl->num_fds) { - if (fdl->fds) - pa_xfree(fdl->fds); - if (fdl->work_fds) - pa_xfree(fdl->work_fds); - fdl->fds = pa_xnew0(struct pollfd, num_fds); - fdl->work_fds = pa_xnew(struct pollfd, num_fds); - } - - memset(fdl->work_fds, 0, sizeof(struct pollfd) * num_fds); - - if ((err = snd_mixer_poll_descriptors(fdl->mixer, fdl->work_fds, num_fds)) < 0) { - pa_log_error("Unable to get poll descriptors: %s", snd_strerror(err)); - return; - } - - fdl->polled = FALSE; - - if (memcmp(fdl->fds, fdl->work_fds, sizeof(struct pollfd) * num_fds) == 0) - return; - - if (fdl->ios) { - for (i = 0; i < fdl->num_fds; i++) - a->io_free(fdl->ios[i]); - - if (num_fds != fdl->num_fds) { - pa_xfree(fdl->ios); - fdl->ios = NULL; - } - } - - if (!fdl->ios) - fdl->ios = pa_xnew(pa_io_event*, num_fds); - - /* Swap pointers */ - temp = fdl->work_fds; - fdl->work_fds = fdl->fds; - fdl->fds = temp; - - fdl->num_fds = num_fds; - - for (i = 0;i < num_fds;i++) - fdl->ios[i] = a->io_new(a, fdl->fds[i].fd, - ((fdl->fds[i].events & POLLIN) ? PA_IO_EVENT_INPUT : 0) | - ((fdl->fds[i].events & POLLOUT) ? PA_IO_EVENT_OUTPUT : 0), - io_cb, fdl); -} - -struct pa_alsa_fdlist *pa_alsa_fdlist_new(void) { - struct pa_alsa_fdlist *fdl; - - fdl = pa_xnew0(struct pa_alsa_fdlist, 1); - - fdl->num_fds = 0; - fdl->fds = NULL; - fdl->work_fds = NULL; - fdl->mixer = NULL; - fdl->m = NULL; - fdl->defer = NULL; - fdl->ios = NULL; - fdl->polled = FALSE; - - return fdl; -} - -void pa_alsa_fdlist_free(struct pa_alsa_fdlist *fdl) { - pa_assert(fdl); - - if (fdl->defer) { - pa_assert(fdl->m); - fdl->m->defer_free(fdl->defer); - } - - if (fdl->ios) { - unsigned i; - pa_assert(fdl->m); - for (i = 0; i < fdl->num_fds; i++) - fdl->m->io_free(fdl->ios[i]); - pa_xfree(fdl->ios); - } - - if (fdl->fds) - pa_xfree(fdl->fds); - if (fdl->work_fds) - pa_xfree(fdl->work_fds); - - pa_xfree(fdl); -} - -int pa_alsa_fdlist_set_mixer(struct pa_alsa_fdlist *fdl, snd_mixer_t *mixer_handle, pa_mainloop_api* m) { - pa_assert(fdl); - pa_assert(mixer_handle); - pa_assert(m); - pa_assert(!fdl->m); - - fdl->mixer = mixer_handle; - fdl->m = m; - fdl->defer = m->defer_new(m, defer_cb, fdl); - - return 0; -} - static int set_format(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, pa_sample_format_t *f) { static const snd_pcm_format_t format_trans[] = { @@ -267,11 +86,11 @@ static int set_format(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, pa_s PA_SAMPLE_S16RE, PA_SAMPLE_ALAW, PA_SAMPLE_ULAW, - PA_SAMPLE_U8, - PA_SAMPLE_INVALID + PA_SAMPLE_U8 }; - int i, ret; + unsigned i; + int ret; pa_assert(pcm_handle); pa_assert(f); @@ -279,6 +98,10 @@ static int set_format(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, pa_s if ((ret = snd_pcm_hw_params_set_format(pcm_handle, hwparams, format_trans[*f])) >= 0) return ret; + pa_log_debug("snd_pcm_hw_params_set_format(%s) failed: %s", + snd_pcm_format_description(format_trans[*f]), + pa_alsa_strerror(ret)); + if (*f == PA_SAMPLE_FLOAT32BE) *f = PA_SAMPLE_FLOAT32LE; else if (*f == PA_SAMPLE_FLOAT32LE) @@ -305,13 +128,21 @@ static int set_format(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, pa_s if ((ret = snd_pcm_hw_params_set_format(pcm_handle, hwparams, format_trans[*f])) >= 0) return ret; + pa_log_debug("snd_pcm_hw_params_set_format(%s) failed: %s", + snd_pcm_format_description(format_trans[*f]), + pa_alsa_strerror(ret)); + try_auto: - for (i = 0; try_order[i] != PA_SAMPLE_INVALID; i++) { + for (i = 0; i < PA_ELEMENTSOF(try_order); i++) { *f = try_order[i]; if ((ret = snd_pcm_hw_params_set_format(pcm_handle, hwparams, format_trans[*f])) >= 0) return ret; + + pa_log_debug("snd_pcm_hw_params_set_format(%s) failed: %s", + snd_pcm_format_description(format_trans[*f]), + pa_alsa_strerror(ret)); } return -1; @@ -332,7 +163,6 @@ int pa_alsa_set_hw_params( int ret = -1; 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; pa_sample_format_t f = ss->format; @@ -346,11 +176,15 @@ int pa_alsa_set_hw_params( snd_pcm_hw_params_alloca(&hwparams); - if ((ret = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0) + if ((ret = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0) { + pa_log_debug("snd_pcm_hw_params_any() failed: %s", pa_alsa_strerror(ret)); goto finish; + } - if ((ret = snd_pcm_hw_params_set_rate_resample(pcm_handle, hwparams, 0)) < 0) + if ((ret = snd_pcm_hw_params_set_rate_resample(pcm_handle, hwparams, 0)) < 0) { + pa_log_debug("snd_pcm_hw_params_set_rate_resample() failed: %s", pa_alsa_strerror(ret)); goto finish; + } if (_use_mmap) { @@ -358,14 +192,18 @@ int pa_alsa_set_hw_params( /* mmap() didn't work, fall back to interleaved */ - if ((ret = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) + if ((ret = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { + pa_log_debug("snd_pcm_hw_params_set_access() failed: %s", pa_alsa_strerror(ret)); goto finish; + } _use_mmap = FALSE; } - } else if ((ret = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) + } else if ((ret = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { + pa_log_debug("snd_pcm_hw_params_set_access() failed: %s", pa_alsa_strerror(ret)); goto finish; + } if (!_use_mmap) _use_tsched = FALSE; @@ -373,54 +211,72 @@ int pa_alsa_set_hw_params( if ((ret = set_format(pcm_handle, hwparams, &f)) < 0) goto finish; - if ((ret = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &r, NULL)) < 0) + if ((ret = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &r, NULL)) < 0) { + pa_log_debug("snd_pcm_hw_params_set_rate_near() failed: %s", pa_alsa_strerror(ret)); goto finish; + } if (require_exact_channel_number) { - if ((ret = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, c)) < 0) + if ((ret = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, c)) < 0) { + pa_log_debug("snd_pcm_hw_params_set_channels(%u) failed: %s", c, pa_alsa_strerror(ret)); goto finish; + } } else { - if ((ret = snd_pcm_hw_params_set_channels_near(pcm_handle, hwparams, &c)) < 0) + if ((ret = snd_pcm_hw_params_set_channels_near(pcm_handle, hwparams, &c)) < 0) { + pa_log_debug("snd_pcm_hw_params_set_channels_near(%u) failed: %s", c, pa_alsa_strerror(ret)); goto finish; + } } - if ((ret = snd_pcm_hw_params_set_periods_integer(pcm_handle, hwparams)) < 0) + if ((ret = snd_pcm_hw_params_set_periods_integer(pcm_handle, hwparams)) < 0) { + pa_log_debug("snd_pcm_hw_params_set_periods_integer() failed: %s", pa_alsa_strerror(ret)); goto finish; + } 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); if (_use_tsched) { + snd_pcm_uframes_t buffer_size = 0; + + if ((ret = snd_pcm_hw_params_get_buffer_size_max(hwparams, &buffer_size)) < 0) + pa_log_warn("snd_pcm_hw_params_get_buffer_size_max() failed: %s", pa_alsa_strerror(ret)); + else + pa_log_debug("Maximum hw buffer size is %u ms", (unsigned) buffer_size * 1000 / r); + _period_size = tsched_size; _periods = 1; - - 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 (_period_size > 0 && _periods > 0) { + snd_pcm_uframes_t buffer_size; + + buffer_size = _periods * _period_size; + + if ((ret = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &buffer_size)) < 0) + pa_log_info("snd_pcm_hw_params_set_buffer_size_near() failed: %s", pa_alsa_strerror(ret)); + } 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. All in all this is mostly a hint to ALSA, so + * we don't care if this fails. */ dir = 0; - if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir)) < 0) { + if (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) { + if (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; + pa_log_info("snd_pcm_hw_params_set_periods_near() failed: %s", pa_alsa_strerror(ret)); } } } - - 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) @@ -435,12 +291,16 @@ int pa_alsa_set_hw_params( if (ss->format != 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) + if ((ret = snd_pcm_prepare(pcm_handle)) < 0) { + pa_log_info("snd_pcm_prepare() failed: %s", pa_alsa_strerror(ret)); goto finish; + } if ((ret = snd_pcm_hw_params_get_period_size(hwparams, &_period_size, &dir)) < 0 || - (ret = snd_pcm_hw_params_get_periods(hwparams, &_periods, &dir)) < 0) + (ret = snd_pcm_hw_params_get_periods(hwparams, &_periods, &dir)) < 0) { + pa_log_info("snd_pcm_hw_params_get_period{s|_size}() failed: %s", pa_alsa_strerror(ret)); goto finish; + } /* If the sample rate deviates too much, we need to resample */ if (r < ss->rate*.95 || r > ss->rate*1.05) @@ -482,131 +342,48 @@ int pa_alsa_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min) { snd_pcm_sw_params_alloca(&swparams); if ((err = snd_pcm_sw_params_current(pcm, swparams) < 0)) { - pa_log_warn("Unable to determine current swparams: %s\n", snd_strerror(err)); + pa_log_warn("Unable to determine current swparams: %s\n", pa_alsa_strerror(err)); return err; } if ((err = snd_pcm_sw_params_set_period_event(pcm, swparams, 0)) < 0) { - pa_log_warn("Unable to disable period event: %s\n", snd_strerror(err)); + pa_log_warn("Unable to disable period event: %s\n", pa_alsa_strerror(err)); return err; } if ((err = snd_pcm_sw_params_set_tstamp_mode(pcm, swparams, SND_PCM_TSTAMP_ENABLE)) < 0) { - pa_log_warn("Unable to enable time stamping: %s\n", snd_strerror(err)); + pa_log_warn("Unable to enable time stamping: %s\n", pa_alsa_strerror(err)); return err; } if ((err = snd_pcm_sw_params_get_boundary(swparams, &boundary)) < 0) { - pa_log_warn("Unable to get boundary: %s\n", snd_strerror(err)); + pa_log_warn("Unable to get boundary: %s\n", pa_alsa_strerror(err)); return err; } if ((err = snd_pcm_sw_params_set_stop_threshold(pcm, swparams, boundary)) < 0) { - pa_log_warn("Unable to set stop threshold: %s\n", snd_strerror(err)); + pa_log_warn("Unable to set stop threshold: %s\n", pa_alsa_strerror(err)); return err; } if ((err = snd_pcm_sw_params_set_start_threshold(pcm, swparams, (snd_pcm_uframes_t) -1)) < 0) { - pa_log_warn("Unable to set start threshold: %s\n", snd_strerror(err)); + pa_log_warn("Unable to set start threshold: %s\n", pa_alsa_strerror(err)); return err; } if ((err = snd_pcm_sw_params_set_avail_min(pcm, swparams, avail_min)) < 0) { - pa_log_error("snd_pcm_sw_params_set_avail_min() failed: %s", snd_strerror(err)); + pa_log_error("snd_pcm_sw_params_set_avail_min() failed: %s", pa_alsa_strerror(err)); return err; } if ((err = snd_pcm_sw_params(pcm, swparams)) < 0) { - pa_log_warn("Unable to set sw params: %s\n", snd_strerror(err)); + pa_log_warn("Unable to set sw params: %s\n", pa_alsa_strerror(err)); return err; } return 0; } -static const struct pa_alsa_profile_info device_table[] = { - {{ 1, { PA_CHANNEL_POSITION_MONO }}, - "hw", - N_("Analog Mono"), - "analog-mono", - 1 }, - - {{ 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT }}, - "front", - N_("Analog Stereo"), - "analog-stereo", - 10 }, - - {{ 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT }}, - "iec958", - N_("Digital Stereo (IEC958)"), - "iec958-stereo", - 5 }, - - {{ 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT }}, - "hdmi", - N_("Digital Stereo (HDMI)"), - "hdmi-stereo", - 4 }, - - {{ 4, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, - PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT }}, - "surround40", - N_("Analog Surround 4.0"), - "analog-surround-40", - 7 }, - - {{ 4, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, - PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT }}, - "a52", - N_("Digital Surround 4.0 (IEC958/AC3)"), - "iec958-ac3-surround-40", - 2 }, - - {{ 5, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, - PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, - PA_CHANNEL_POSITION_LFE }}, - "surround41", - N_("Analog Surround 4.1"), - "analog-surround-41", - 7 }, - - {{ 5, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, - PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, - PA_CHANNEL_POSITION_CENTER }}, - "surround50", - N_("Analog Surround 5.0"), - "analog-surround-50", - 7 }, - - {{ 6, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, - PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, - PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_LFE }}, - "surround51", - N_("Analog Surround 5.1"), - "analog-surround-51", - 8 }, - - {{ 6, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_CENTER, - PA_CHANNEL_POSITION_FRONT_RIGHT, PA_CHANNEL_POSITION_REAR_LEFT, - PA_CHANNEL_POSITION_REAR_RIGHT, PA_CHANNEL_POSITION_LFE}}, - "a52", - N_("Digital Surround 5.1 (IEC958/AC3)"), - "iec958-ac3-surround-51", - 3 }, - - {{ 8, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, - PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, - PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_LFE, - PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT }}, - "surround71", - N_("Analog Surround 7.1"), - "analog-surround-71", - 7 }, - - {{ 0, { 0 }}, NULL, NULL, NULL, 0 } -}; - snd_pcm_t *pa_alsa_open_by_device_id_auto( const char *dev_id, char **dev, @@ -618,12 +395,13 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto( snd_pcm_uframes_t tsched_size, pa_bool_t *use_mmap, pa_bool_t *use_tsched, - const pa_alsa_profile_info **profile) { + pa_alsa_profile_set *ps, + pa_alsa_mapping **mapping) { - int i; - int direction = 1; char *d; snd_pcm_t *pcm_handle; + void *state; + pa_alsa_mapping *m; pa_assert(dev_id); pa_assert(dev); @@ -631,114 +409,82 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto( pa_assert(map); pa_assert(nfrags); pa_assert(period_size); + pa_assert(ps); /* First we try to find a device string with a superset of the - * requested channel map and open it without the plug: prefix. We - * iterate through our device table from top to bottom and take - * the first that matches. If we didn't find a working device that - * way, we iterate backwards, and check all devices that do not - * provide a superset of the requested channel map.*/ - - i = 0; - for (;;) { - - if ((direction > 0) == pa_channel_map_superset(&device_table[i].map, map)) { - pa_sample_spec try_ss; - - pa_log_debug("Checking for %s (%s)", device_table[i].name, device_table[i].alsa_name); - - d = pa_sprintf_malloc("%s:%s", device_table[i].alsa_name, dev_id); - - 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); - - pa_xfree(d); - - if (pcm_handle) { - - *ss = try_ss; - *map = device_table[i].map; - pa_assert(map->channels == ss->channels); - - if (profile) - *profile = &device_table[i]; - - return pcm_handle; - } - } - - if (direction > 0) { - if (!device_table[i+1].alsa_name) { - /* OK, so we are at the end of our list. Let's turn - * back. */ - direction = -1; - } else { - /* We are not at the end of the list, so let's simply - * try the next entry */ - i++; - } + * requested channel map. We iterate through our device table from + * top to bottom and take the first that matches. If we didn't + * find a working device that way, we iterate backwards, and check + * all devices that do not provide a superset of the requested + * channel map.*/ + + PA_HASHMAP_FOREACH(m, ps->mappings, state) { + if (!pa_channel_map_superset(&m->channel_map, map)) + continue; + + pa_log_debug("Checking for superset %s (%s)", m->name, m->device_strings[0]); + + pcm_handle = pa_alsa_open_by_device_id_mapping( + dev_id, + dev, + ss, + map, + mode, + nfrags, + period_size, + tsched_size, + use_mmap, + use_tsched, + m); + + if (pcm_handle) { + if (mapping) + *mapping = m; + + return pcm_handle; } + } - if (direction < 0) { - - if (device_table[i+1].alsa_name && - device_table[i].map.channels == device_table[i+1].map.channels) { - - /* OK, the next entry has the same number of channels, - * let's try it */ - i++; - - } else { - /* Hmm, so the next entry does not have the same - * number of channels, so let's go backwards until we - * find the next entry with a differnt number of - * channels */ - - for (i--; i >= 0; i--) - if (device_table[i].map.channels != device_table[i+1].map.channels) - break; - - /* Hmm, there is no entry with a different number of - * entries, then we're done */ - if (i < 0) - break; - - /* OK, now lets find go back as long as we have the same number of channels */ - for (; i > 0; i--) - if (device_table[i].map.channels != device_table[i-1].map.channels) - break; - } + PA_HASHMAP_FOREACH_BACKWARDS(m, ps->mappings, state) { + if (pa_channel_map_superset(&m->channel_map, map)) + continue; + + pa_log_debug("Checking for subset %s (%s)", m->name, m->device_strings[0]); + + pcm_handle = pa_alsa_open_by_device_id_mapping( + dev_id, + dev, + ss, + map, + mode, + nfrags, + period_size, + tsched_size, + use_mmap, + use_tsched, + m); + + if (pcm_handle) { + if (mapping) + *mapping = m; + + return pcm_handle; } } - /* OK, we didn't find any good device, so let's try the raw plughw: stuff */ - + /* OK, we didn't find any good device, so let's try the raw hw: stuff */ 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, FALSE); pa_xfree(d); - if (pcm_handle && profile) - *profile = NULL; + if (pcm_handle && mapping) + *mapping = NULL; return pcm_handle; } -snd_pcm_t *pa_alsa_open_by_device_id_profile( +snd_pcm_t *pa_alsa_open_by_device_id_mapping( const char *dev_id, char **dev, pa_sample_spec *ss, @@ -749,11 +495,11 @@ snd_pcm_t *pa_alsa_open_by_device_id_profile( snd_pcm_uframes_t tsched_size, pa_bool_t *use_mmap, pa_bool_t *use_tsched, - const pa_alsa_profile_info *profile) { + pa_alsa_mapping *m) { - char *d; snd_pcm_t *pcm_handle; pa_sample_spec try_ss; + pa_channel_map try_map; pa_assert(dev_id); pa_assert(dev); @@ -761,19 +507,19 @@ snd_pcm_t *pa_alsa_open_by_device_id_profile( pa_assert(map); pa_assert(nfrags); pa_assert(period_size); - pa_assert(profile); - - d = pa_sprintf_malloc("%s:%s", profile->alsa_name, dev_id); + pa_assert(m); - try_ss.channels = profile->map.channels; + try_ss.channels = m->channel_map.channels; try_ss.rate = ss->rate; try_ss.format = ss->format; + try_map = m->channel_map; - pcm_handle = pa_alsa_open_by_device_string( - d, + pcm_handle = pa_alsa_open_by_template( + m->device_strings, + dev_id, dev, &try_ss, - map, + &try_map, mode, nfrags, period_size, @@ -782,13 +528,11 @@ snd_pcm_t *pa_alsa_open_by_device_id_profile( use_tsched, TRUE); - pa_xfree(d); - if (!pcm_handle) return NULL; *ss = try_ss; - *map = profile->map; + *map = try_map; pa_assert(map->channels == ss->channels); return pcm_handle; @@ -821,21 +565,17 @@ snd_pcm_t *pa_alsa_open_by_device_string( 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_NONBLOCK| SND_PCM_NO_AUTO_RESAMPLE| SND_PCM_NO_AUTO_CHANNELS| (reformat ? 0 : SND_PCM_NO_AUTO_FORMAT))) < 0) { - pa_log_info("Error opening PCM device %s: %s", d, snd_strerror(err)); + pa_log_info("Error opening PCM device %s: %s", d, pa_alsa_strerror(err)); goto fail; } + pa_log_debug("Managed to open %s", d); + 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) { @@ -846,7 +586,6 @@ snd_pcm_t *pa_alsa_open_by_device_string( } /* Hmm, some hw is very exotic, so we retry with plug, if without it didn't work */ - if (!pa_startswith(d, "plug:") && !pa_startswith(d, "plughw:")) { char *t; @@ -860,7 +599,7 @@ snd_pcm_t *pa_alsa_open_by_device_string( continue; } - pa_log_info("Failed to set hardware parameters on %s: %s", d, snd_strerror(err)); + pa_log_info("Failed to set hardware parameters on %s: %s", d, pa_alsa_strerror(err)); snd_pcm_close(pcm_handle); goto fail; @@ -883,414 +622,51 @@ fail: return NULL; } -int pa_alsa_probe_profiles( +snd_pcm_t *pa_alsa_open_by_template( + char **template, 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); - - if (i->alsa_name || j->alsa_name) - 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; - - pa_assert(mixer); - pa_assert(dev); - - if ((err = snd_mixer_attach(mixer, dev)) < 0) { - pa_log_info("Unable to attach to mixer %s: %s", dev, snd_strerror(err)); - return -1; - } - - if ((err = snd_mixer_selem_register(mixer, NULL, NULL)) < 0) { - pa_log_warn("Unable to register mixer: %s", snd_strerror(err)); - return -1; - } - - if ((err = snd_mixer_load(mixer)) < 0) { - pa_log_warn("Unable to load mixer: %s", snd_strerror(err)); - return -1; - } - - pa_log_info("Successfully attached to mixer '%s'", dev); - - return 0; -} - -static pa_bool_t elem_has_volume(snd_mixer_elem_t *elem, pa_bool_t playback) { - pa_assert(elem); - - if (playback && snd_mixer_selem_has_playback_volume(elem)) - return TRUE; - - if (!playback && snd_mixer_selem_has_capture_volume(elem)) - return TRUE; - - return FALSE; -} - -static pa_bool_t elem_has_switch(snd_mixer_elem_t *elem, pa_bool_t playback) { - pa_assert(elem); - - if (playback && snd_mixer_selem_has_playback_switch(elem)) - return TRUE; - - if (!playback && snd_mixer_selem_has_capture_switch(elem)) - return TRUE; - - return FALSE; -} - -snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const char *fallback, pa_bool_t playback) { - snd_mixer_elem_t *elem = NULL, *fallback_elem = NULL; - snd_mixer_selem_id_t *sid = NULL; - - snd_mixer_selem_id_alloca(&sid); - - pa_assert(mixer); - pa_assert(name); - - snd_mixer_selem_id_set_name(sid, name); - snd_mixer_selem_id_set_index(sid, 0); - - if ((elem = snd_mixer_find_selem(mixer, sid))) { - - if (elem_has_volume(elem, playback) && - elem_has_switch(elem, playback)) - goto success; - - if (!elem_has_volume(elem, playback) && - !elem_has_switch(elem, playback)) - elem = NULL; - } - - pa_log_info("Cannot find mixer control \"%s\" or mixer control is no combination of switch/volume.", snd_mixer_selem_id_get_name(sid)); - - if (fallback) { - snd_mixer_selem_id_set_name(sid, fallback); - snd_mixer_selem_id_set_index(sid, 0); - - if ((fallback_elem = snd_mixer_find_selem(mixer, sid))) { - - if (elem_has_volume(fallback_elem, playback) && - elem_has_switch(fallback_elem, playback)) { - elem = fallback_elem; - goto success; - } - - if (!elem_has_volume(fallback_elem, playback) && - !elem_has_switch(fallback_elem, playback)) - fallback_elem = NULL; - } - - pa_log_warn("Cannot find fallback mixer control \"%s\" or mixer control is no combination of switch/volume.", snd_mixer_selem_id_get_name(sid)); - } - - if (elem && fallback_elem) { - - /* Hmm, so we have both elements, but neither has both mute - * and volume. Let's prefer the one with the volume */ - - if (elem_has_volume(elem, playback)) - goto success; - - if (elem_has_volume(fallback_elem, playback)) { - elem = fallback_elem; - goto success; - } - } - - if (!elem && fallback_elem) - elem = fallback_elem; - -success: - - if (elem) - pa_log_info("Using mixer control \"%s\".", snd_mixer_selem_id_get_name(sid)); - - return elem; -} - -int pa_alsa_find_mixer_and_elem( - snd_pcm_t *pcm, - snd_mixer_t **_m, - snd_mixer_elem_t **_e) { - - int err; - snd_mixer_t *m; - snd_mixer_elem_t *e; - pa_bool_t found = FALSE; - const char *dev; - - pa_assert(pcm); - pa_assert(_m); - pa_assert(_e); - - if ((err = snd_mixer_open(&m, 0)) < 0) { - pa_log("Error opening mixer: %s", snd_strerror(err)); - return -1; - } - - /* First, try by name */ - if ((dev = snd_pcm_name(pcm))) - if (pa_alsa_prepare_mixer(m, dev) >= 0) - found = TRUE; - - /* Then, try by card index */ - if (!found) { - snd_pcm_info_t* info; - snd_pcm_info_alloca(&info); - - if (snd_pcm_info(pcm, info) >= 0) { - char *md; - int card_idx; - - if ((card_idx = snd_pcm_info_get_card(info)) >= 0) { - - md = pa_sprintf_malloc("hw:%i", card_idx); - - if (!dev || !pa_streq(dev, md)) - if (pa_alsa_prepare_mixer(m, md) >= 0) - found = TRUE; - - pa_xfree(md); - } - } - } - - if (!found) { - snd_mixer_close(m); - return -1; - } - - switch (snd_pcm_stream(pcm)) { - - case SND_PCM_STREAM_PLAYBACK: - e = pa_alsa_find_elem(m, "Master", "PCM", TRUE); - break; - - case SND_PCM_STREAM_CAPTURE: - e = pa_alsa_find_elem(m, "Capture", "Mic", FALSE); - break; - - default: - pa_assert_not_reached(); - } - - if (!e) { - snd_mixer_close(m); - return -1; - } - - pa_assert(e && m); - - *_m = m; - *_e = e; - - return 0; -} - -static const snd_mixer_selem_channel_id_t alsa_channel_ids[PA_CHANNEL_POSITION_MAX] = { - [PA_CHANNEL_POSITION_MONO] = SND_MIXER_SCHN_MONO, /* The ALSA name is just an alias! */ - - [PA_CHANNEL_POSITION_FRONT_CENTER] = SND_MIXER_SCHN_FRONT_CENTER, - [PA_CHANNEL_POSITION_FRONT_LEFT] = SND_MIXER_SCHN_FRONT_LEFT, - [PA_CHANNEL_POSITION_FRONT_RIGHT] = SND_MIXER_SCHN_FRONT_RIGHT, - - [PA_CHANNEL_POSITION_REAR_CENTER] = SND_MIXER_SCHN_REAR_CENTER, - [PA_CHANNEL_POSITION_REAR_LEFT] = SND_MIXER_SCHN_REAR_LEFT, - [PA_CHANNEL_POSITION_REAR_RIGHT] = SND_MIXER_SCHN_REAR_RIGHT, - - [PA_CHANNEL_POSITION_LFE] = SND_MIXER_SCHN_WOOFER, - - [PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = SND_MIXER_SCHN_UNKNOWN, - - [PA_CHANNEL_POSITION_SIDE_LEFT] = SND_MIXER_SCHN_SIDE_LEFT, - [PA_CHANNEL_POSITION_SIDE_RIGHT] = SND_MIXER_SCHN_SIDE_RIGHT, - - [PA_CHANNEL_POSITION_AUX0] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX1] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX2] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX3] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX4] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX5] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX6] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX7] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX8] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX9] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX10] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX11] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX12] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX13] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX14] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX15] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX16] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX17] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX18] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX19] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX20] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX21] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX22] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX23] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX24] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX25] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX26] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX27] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX28] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX29] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX30] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_AUX31] = SND_MIXER_SCHN_UNKNOWN, - - [PA_CHANNEL_POSITION_TOP_CENTER] = SND_MIXER_SCHN_UNKNOWN, - - [PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = SND_MIXER_SCHN_UNKNOWN, - - [PA_CHANNEL_POSITION_TOP_REAR_CENTER] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_TOP_REAR_LEFT] = SND_MIXER_SCHN_UNKNOWN, - [PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = SND_MIXER_SCHN_UNKNOWN -}; - - -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) { - unsigned i; - pa_bool_t alsa_channel_used[SND_MIXER_SCHN_LAST]; - pa_bool_t mono_used = FALSE; - - pa_assert(elem); - pa_assert(channel_map); - pa_assert(mixer_map); - - memset(&alsa_channel_used, 0, sizeof(alsa_channel_used)); - - if (channel_map->channels > 1 && - ((playback && snd_mixer_selem_has_playback_volume_joined(elem)) || - (!playback && snd_mixer_selem_has_capture_volume_joined(elem)))) { - pa_log_info("ALSA device lacks independant volume controls for each channel."); - return -1; - } - - for (i = 0; i < channel_map->channels; i++) { - snd_mixer_selem_channel_id_t id; - pa_bool_t is_mono; - - is_mono = channel_map->map[i] == PA_CHANNEL_POSITION_MONO; - id = alsa_channel_ids[channel_map->map[i]]; - - if (!is_mono && id == SND_MIXER_SCHN_UNKNOWN) { - pa_log_info("Configured channel map contains channel '%s' that is unknown to the ALSA mixer.", pa_channel_position_to_string(channel_map->map[i])); - return -1; - } - - if ((is_mono && mono_used) || (!is_mono && alsa_channel_used[id])) { - pa_log_info("Channel map has duplicate channel '%s', falling back to software volume control.", pa_channel_position_to_string(channel_map->map[i])); - return -1; - } - - if ((playback && (!snd_mixer_selem_has_playback_channel(elem, id) || (is_mono && !snd_mixer_selem_is_playback_mono(elem)))) || - (!playback && (!snd_mixer_selem_has_capture_channel(elem, id) || (is_mono && !snd_mixer_selem_is_capture_mono(elem))))) { - - pa_log_info("ALSA device lacks separate volumes control for channel '%s'", pa_channel_position_to_string(channel_map->map[i])); - return -1; - } + char **dev, + pa_sample_spec *ss, + pa_channel_map* map, + int mode, + uint32_t *nfrags, + snd_pcm_uframes_t *period_size, + snd_pcm_uframes_t tsched_size, + pa_bool_t *use_mmap, + pa_bool_t *use_tsched, + pa_bool_t require_exact_channel_number) { - if (is_mono) { - mixer_map[i] = SND_MIXER_SCHN_MONO; - mono_used = TRUE; - } else { - mixer_map[i] = id; - alsa_channel_used[id] = TRUE; - } + snd_pcm_t *pcm_handle; + char **i; + + for (i = template; *i; i++) { + char *d; + + d = pa_replace(*i, "%f", dev_id); + + pcm_handle = pa_alsa_open_by_device_string( + d, + dev, + ss, + map, + mode, + nfrags, + period_size, + tsched_size, + use_mmap, + use_tsched, + require_exact_channel_number); + + pa_xfree(d); + + if (pcm_handle) + return pcm_handle; } - pa_log_info("All %u channels can be mapped to mixer channels.", channel_map->channels); - - return 0; + return NULL; } -void pa_alsa_dump(snd_pcm_t *pcm) { +void pa_alsa_dump(pa_log_level_t level, snd_pcm_t *pcm) { int err; snd_output_t *out; @@ -1299,11 +675,11 @@ void pa_alsa_dump(snd_pcm_t *pcm) { pa_assert_se(snd_output_buffer_open(&out) == 0); if ((err = snd_pcm_dump(pcm, out)) < 0) - pa_log_debug("snd_pcm_dump(): %s", snd_strerror(err)); + pa_logl(level, "snd_pcm_dump(): %s", pa_alsa_strerror(err)); else { char *s = NULL; snd_output_buffer_string(out, &s); - pa_log_debug("snd_pcm_dump():\n%s", pa_strnull(s)); + pa_logl(level, "snd_pcm_dump():\n%s", pa_strnull(s)); } pa_assert_se(snd_output_close(out) == 0); @@ -1313,24 +689,33 @@ void pa_alsa_dump_status(snd_pcm_t *pcm) { int err; snd_output_t *out; snd_pcm_status_t *status; + char *s = NULL; pa_assert(pcm); snd_pcm_status_alloca(&status); - pa_assert_se(snd_output_buffer_open(&out) == 0); + if ((err = snd_output_buffer_open(&out)) < 0) { + pa_log_debug("snd_output_buffer_open() failed: %s", pa_cstrerror(err)); + return; + } - pa_assert_se(snd_pcm_status(pcm, status) == 0); + if ((err = snd_pcm_status(pcm, status)) < 0) { + pa_log_debug("snd_pcm_status() failed: %s", pa_cstrerror(err)); + goto finish; + } - if ((err = snd_pcm_status_dump(status, out)) < 0) - pa_log_debug("snd_pcm_dump(): %s", snd_strerror(err)); - else { - char *s = NULL; - snd_output_buffer_string(out, &s); - pa_log_debug("snd_pcm_dump():\n%s", pa_strnull(s)); + if ((err = snd_pcm_status_dump(status, out)) < 0) { + pa_log_debug("snd_pcm_dump(): %s", pa_alsa_strerror(err)); + goto finish; } - pa_assert_se(snd_output_close(out) == 0); + snd_output_buffer_string(out, &s); + pa_log_debug("snd_pcm_dump():\n%s", pa_strnull(s)); + +finish: + + snd_output_close(out); } static void alsa_error_handler(const char *file, int line, const char *function, int err, const char *fmt,...) { @@ -1350,38 +735,43 @@ static void alsa_error_handler(const char *file, int line, const char *function, static pa_atomic_t n_error_handler_installed = PA_ATOMIC_INIT(0); -void pa_alsa_redirect_errors_inc(void) { +void pa_alsa_refcnt_inc(void) { /* This is not really thread safe, but we do our best */ if (pa_atomic_inc(&n_error_handler_installed) == 0) snd_lib_error_set_handler(alsa_error_handler); } -void pa_alsa_redirect_errors_dec(void) { +void pa_alsa_refcnt_dec(void) { int r; pa_assert_se((r = pa_atomic_dec(&n_error_handler_installed)) >= 1); - if (r == 1) + if (r == 1) { snd_lib_error_set_handler(NULL); + snd_config_update_free_global(); + } } pa_bool_t pa_alsa_init_description(pa_proplist *p) { - const char *s; + const char *d, *k; pa_assert(p); if (pa_device_init_description(p)) return TRUE; - if ((s = pa_proplist_gets(p, "alsa.card_name"))) { - pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, s); - return TRUE; - } + if (!(d = pa_proplist_gets(p, "alsa.card_name"))) + d = pa_proplist_gets(p, "alsa.name"); - if ((s = pa_proplist_gets(p, "alsa.name"))) { - pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, s); - return TRUE; - } + if (!d) + return FALSE; + + k = pa_proplist_gets(p, PA_PROP_DEVICE_PROFILE_DESCRIPTION); + + if (d && k) + pa_proplist_setf(p, PA_PROP_DEVICE_DESCRIPTION, _("%s %s"), d, k); + else if (d) + pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, d); return FALSE; } @@ -1410,7 +800,7 @@ void pa_alsa_init_proplist_card(pa_core *c, pa_proplist *p, int card) { } #ifdef HAVE_UDEV - pa_udev_get_info(c, p, card); + pa_udev_get_info(card, p); #endif #ifdef HAVE_HAL @@ -1447,16 +837,14 @@ void pa_alsa_init_proplist_pcm_info(pa_core *c, pa_proplist *p, snd_pcm_info_t * pa_proplist_sets(p, PA_PROP_DEVICE_API, "alsa"); - class = snd_pcm_info_get_class(pcm_info); - if (class <= SND_PCM_CLASS_LAST) { + if ((class = snd_pcm_info_get_class(pcm_info)) <= SND_PCM_CLASS_LAST) { if (class_table[class]) pa_proplist_sets(p, PA_PROP_DEVICE_CLASS, class_table[class]); if (alsa_class_table[class]) pa_proplist_sets(p, "alsa.class", alsa_class_table[class]); } - subclass = snd_pcm_info_get_subclass(pcm_info); - if (subclass <= SND_PCM_SUBCLASS_LAST) + if ((subclass = snd_pcm_info_get_subclass(pcm_info)) <= SND_PCM_SUBCLASS_LAST) if (alsa_subclass_table[subclass]) pa_proplist_sets(p, "alsa.subclass", alsa_subclass_table[subclass]); @@ -1485,7 +873,7 @@ void pa_alsa_init_proplist_pcm(pa_core *c, pa_proplist *p, snd_pcm_t *pcm) { snd_pcm_info_alloca(&info); if ((err = snd_pcm_hw_params_current(pcm, hwparams)) < 0) - pa_log_warn("Error fetching hardware parameter info: %s", snd_strerror(err)); + pa_log_warn("Error fetching hardware parameter info: %s", pa_alsa_strerror(err)); else { if ((bits = snd_pcm_hw_params_get_sbits(hwparams)) >= 0) @@ -1493,11 +881,41 @@ void pa_alsa_init_proplist_pcm(pa_core *c, pa_proplist *p, snd_pcm_t *pcm) { } if ((err = snd_pcm_info(pcm, info)) < 0) - pa_log_warn("Error fetching PCM info: %s", snd_strerror(err)); + pa_log_warn("Error fetching PCM info: %s", pa_alsa_strerror(err)); else pa_alsa_init_proplist_pcm_info(c, p, info); } +void pa_alsa_init_proplist_ctl(pa_proplist *p, const char *name) { + int err; + snd_ctl_t *ctl; + snd_ctl_card_info_t *info; + const char *t; + + pa_assert(p); + + snd_ctl_card_info_alloca(&info); + + if ((err = snd_ctl_open(&ctl, name, 0)) < 0) { + pa_log_warn("Error opening low-level control device '%s'", name); + return; + } + + if ((err = snd_ctl_card_info(ctl, info)) < 0) { + pa_log_warn("Control device %s card info: %s", name, snd_strerror(err)); + snd_ctl_close(ctl); + return; + } + + if ((t = snd_ctl_card_info_get_mixername(info)) && *t) + pa_proplist_sets(p, "alsa.mixer_name", t); + + if ((t = snd_ctl_card_info_get_components(info)) && *t) + pa_proplist_sets(p, "alsa.components", t); + + snd_ctl_close(ctl); +} + int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents) { snd_pcm_state_t state; int err; @@ -1526,14 +944,14 @@ int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents) { case SND_PCM_STATE_XRUN: if ((err = snd_pcm_recover(pcm, -EPIPE, 1)) != 0) { - pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP and XRUN: %s", snd_strerror(err)); + pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP and XRUN: %s", pa_alsa_strerror(err)); return -1; } break; case SND_PCM_STATE_SUSPENDED: if ((err = snd_pcm_recover(pcm, -ESTRPIPE, 1)) != 0) { - pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP and SUSPENDED: %s", snd_strerror(err)); + pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP and SUSPENDED: %s", pa_alsa_strerror(err)); return -1; } break; @@ -1543,7 +961,7 @@ int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents) { snd_pcm_drop(pcm); if ((err = snd_pcm_prepare(pcm)) < 0) { - pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP with snd_pcm_prepare(): %s", snd_strerror(err)); + pa_log_warn("Could not recover from POLLERR|POLLNVAL|POLLHUP with snd_pcm_prepare(): %s", pa_alsa_strerror(err)); return -1; } break; @@ -1560,7 +978,7 @@ pa_rtpoll_item* pa_alsa_build_pollfd(snd_pcm_t *pcm, pa_rtpoll *rtpoll) { pa_assert(pcm); if ((n = snd_pcm_poll_descriptors_count(pcm)) < 0) { - pa_log("snd_pcm_poll_descriptors_count() failed: %s", snd_strerror(n)); + pa_log("snd_pcm_poll_descriptors_count() failed: %s", pa_alsa_strerror(n)); return NULL; } @@ -1568,7 +986,7 @@ pa_rtpoll_item* pa_alsa_build_pollfd(snd_pcm_t *pcm, pa_rtpoll *rtpoll) { pollfd = pa_rtpoll_item_get_pollfd(item, NULL); if ((err = snd_pcm_poll_descriptors(pcm, pollfd, (unsigned) n)) < 0) { - pa_log("snd_pcm_poll_descriptors() failed: %s", snd_strerror(err)); + pa_log("snd_pcm_poll_descriptors() failed: %s", pa_alsa_strerror(err)); pa_rtpoll_item_free(item); return NULL; } @@ -1605,6 +1023,7 @@ snd_pcm_sframes_t pa_alsa_safe_avail(snd_pcm_t *pcm, size_t hwbuf_size, const pa (unsigned long) (pa_bytes_to_usec(k, ss) / PA_USEC_PER_MSEC), pa_strnull(dn)); pa_xfree(dn); + pa_alsa_dump(PA_LOG_ERROR, pcm); } PA_ONCE_END; /* Mhmm, let's try not to fail completely */ @@ -1646,6 +1065,7 @@ int pa_alsa_safe_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delay, size_t hwbuf_si (unsigned long) (pa_bytes_to_usec(abs_k, ss) / PA_USEC_PER_MSEC), pa_strnull(dn)); pa_xfree(dn); + pa_alsa_dump(PA_LOG_ERROR, pcm); } PA_ONCE_END; /* Mhmm, let's try not to fail completely */ @@ -1691,6 +1111,7 @@ int pa_alsa_safe_mmap_begin(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas (unsigned long) (pa_bytes_to_usec(k, ss) / PA_USEC_PER_MSEC), pa_strnull(dn)); pa_xfree(dn); + pa_alsa_dump(PA_LOG_ERROR, pcm); } PA_ONCE_END; return r; @@ -1716,10 +1137,11 @@ char *pa_alsa_get_driver_name(int card) { char *pa_alsa_get_driver_name_by_pcm(snd_pcm_t *pcm) { int card; - snd_pcm_info_t* info; snd_pcm_info_alloca(&info); + pa_assert(pcm); + if (snd_pcm_info(pcm, info) < 0) return NULL; @@ -1749,3 +1171,54 @@ char *pa_alsa_get_reserve_name(const char *device) { return pa_sprintf_malloc("Audio%i", i); } + +pa_bool_t pa_alsa_pcm_is_hw(snd_pcm_t *pcm) { + snd_pcm_info_t* info; + snd_pcm_info_alloca(&info); + + pa_assert(pcm); + + if (snd_pcm_info(pcm, info) < 0) + return FALSE; + + return snd_pcm_info_get_card(info) >= 0; +} + +pa_bool_t pa_alsa_pcm_is_modem(snd_pcm_t *pcm) { + snd_pcm_info_t* info; + snd_pcm_info_alloca(&info); + + pa_assert(pcm); + + if (snd_pcm_info(pcm, info) < 0) + return FALSE; + + return snd_pcm_info_get_class(info) == SND_PCM_CLASS_MODEM; +} + +PA_STATIC_TLS_DECLARE(cstrerror, pa_xfree); + +const char* pa_alsa_strerror(int errnum) { + const char *original = NULL; + char *translated, *t; + char errbuf[128]; + + if ((t = PA_STATIC_TLS_GET(cstrerror))) + pa_xfree(t); + + original = snd_strerror(errnum); + + if (!original) { + pa_snprintf(errbuf, sizeof(errbuf), "Unknown error %i", errnum); + original = errbuf; + } + + if (!(translated = pa_locale_to_utf8(original))) { + pa_log_warn("Unable to convert error string to locale, filtering."); + translated = pa_utf8_filter(original); + } + + PA_STATIC_TLS_SET(cstrerror, translated); + + return translated; +} |