diff options
Diffstat (limited to 'src/modules/alsa-util.c')
-rw-r--r-- | src/modules/alsa-util.c | 178 |
1 files changed, 164 insertions, 14 deletions
diff --git a/src/modules/alsa-util.c b/src/modules/alsa-util.c index ffe7795e..ff3af19d 100644 --- a/src/modules/alsa-util.c +++ b/src/modules/alsa-util.c @@ -30,6 +30,7 @@ #include <pulse/sample.h> #include <pulse/xmalloc.h> +#include <pulse/timeval.h> #include <pulsecore/log.h> #include <pulsecore/macro.h> @@ -555,6 +556,7 @@ snd_pcm_t *pa_alsa_open_by_device_id( for (i = 0;; i += direction) { pa_sample_spec try_ss; + pa_bool_t reformat; if (i < 0) { pa_assert(direction == -1); @@ -579,8 +581,9 @@ snd_pcm_t *pa_alsa_open_by_device_id( d = pa_sprintf_malloc("%s:%s", device_table[i].name, dev_id); + reformat = FALSE; for (;;) { - pa_log_debug("Trying %s...", d); + 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 @@ -592,7 +595,7 @@ snd_pcm_t *pa_alsa_open_by_device_id( /* SND_PCM_NONBLOCK| */ SND_PCM_NO_AUTO_RESAMPLE| SND_PCM_NO_AUTO_CHANNELS| - SND_PCM_NO_AUTO_FORMAT)) < 0) { + (reformat ? 0 : SND_PCM_NO_AUTO_FORMAT))) < 0) { pa_log_info("Couldn't open PCM device %s: %s", d, snd_strerror(err)); break; } @@ -603,6 +606,12 @@ snd_pcm_t *pa_alsa_open_by_device_id( 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; @@ -610,6 +619,8 @@ snd_pcm_t *pa_alsa_open_by_device_id( pa_xfree(d); d = t; + reformat = FALSE; + snd_pcm_close(pcm_handle); continue; } @@ -654,6 +665,7 @@ snd_pcm_t *pa_alsa_open_by_device_string( int err; char *d; snd_pcm_t *pcm_handle; + pa_bool_t reformat = FALSE; pa_assert(device); pa_assert(dev); @@ -665,7 +677,7 @@ snd_pcm_t *pa_alsa_open_by_device_string( d = pa_xstrdup(device); for (;;) { - pa_log_debug("Trying %s...", d); + 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 @@ -677,7 +689,7 @@ snd_pcm_t *pa_alsa_open_by_device_string( /*SND_PCM_NONBLOCK|*/ SND_PCM_NO_AUTO_RESAMPLE| SND_PCM_NO_AUTO_CHANNELS| - SND_PCM_NO_AUTO_FORMAT)) < 0) { + (reformat ? 0 : SND_PCM_NO_AUTO_FORMAT))) < 0) { pa_log("Error opening PCM device %s: %s", d, snd_strerror(err)); pa_xfree(d); return NULL; @@ -685,6 +697,13 @@ snd_pcm_t *pa_alsa_open_by_device_string( if ((err = pa_alsa_set_hw_params(pcm_handle, ss, nfrags, period_size, tsched_size, use_mmap, use_tsched, FALSE)) < 0) { + if (!reformat) { + reformat = TRUE; + + snd_pcm_close(pcm_handle); + continue; + } + /* 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:")) { @@ -694,6 +713,8 @@ snd_pcm_t *pa_alsa_open_by_device_string( pa_xfree(d); d = t; + reformat = FALSE; + snd_pcm_close(pcm_handle); continue; } @@ -739,8 +760,32 @@ int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev) { return 0; } -snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const char *fallback) { - snd_mixer_elem_t *elem; +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); @@ -750,17 +795,57 @@ snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const snd_mixer_selem_id_set_name(sid, name); - if (!(elem = snd_mixer_find_selem(mixer, sid))) { - pa_log_info("Cannot find mixer control \"%s\".", snd_mixer_selem_id_get_name(sid)); + if ((elem = snd_mixer_find_selem(mixer, sid))) { + + if (elem_has_volume(elem, playback) && + elem_has_switch(elem, playback)) + goto success; - if (fallback) { - snd_mixer_selem_id_set_name(sid, fallback); + 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); + + if ((fallback_elem = snd_mixer_find_selem(mixer, sid))) { - if (!(elem = snd_mixer_find_selem(mixer, sid))) - pa_log_warn("Cannot find fallback mixer control \"%s\".", snd_mixer_selem_id_get_name(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)); @@ -1045,14 +1130,20 @@ int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents) { pa_assert(pcm); if (revents & POLLERR) - pa_log_warn("Got POLLERR from ALSA"); + pa_log_debug("Got POLLERR from ALSA"); if (revents & POLLNVAL) pa_log_warn("Got POLLNVAL from ALSA"); if (revents & POLLHUP) pa_log_warn("Got POLLHUP from ALSA"); + if (revents & POLLPRI) + pa_log_warn("Got POLLPRI from ALSA"); + if (revents & POLLIN) + pa_log_debug("Got POLLIN from ALSA"); + if (revents & POLLOUT) + pa_log_debug("Got POLLOUT from ALSA"); state = snd_pcm_state(pcm); - pa_log_warn("PCM state is %s", snd_pcm_state_name(state)); + pa_log_debug("PCM state is %s", snd_pcm_state_name(state)); /* Try to recover from this error */ @@ -1109,3 +1200,62 @@ pa_rtpoll_item* pa_alsa_build_pollfd(snd_pcm_t *pcm, pa_rtpoll *rtpoll) { return item; } + +snd_pcm_sframes_t pa_alsa_safe_avail_update(snd_pcm_t *pcm, size_t hwbuf_size, const pa_sample_spec *ss) { + snd_pcm_sframes_t n; + size_t k; + + pa_assert(pcm); + pa_assert(hwbuf_size > 0); + pa_assert(ss); + + /* Some ALSA driver expose weird bugs, let's inform the user about + * what is going on */ + + n = snd_pcm_avail_update(pcm); + + if (n <= 0) + return n; + + k = (size_t) n * pa_frame_size(ss); + + if (k >= hwbuf_size * 3 || + k >= pa_bytes_per_second(ss)*10) + pa_log("snd_pcm_avail_update() returned a value that is exceptionally large: %lu bytes (%lu ms) " + "Most likely this is an ALSA driver bug. Please report this issue to the PulseAudio developers.", + (unsigned long) k, (unsigned long) (pa_bytes_to_usec(k, ss) / PA_USEC_PER_MSEC)); + + return n; +} + +int pa_alsa_safe_mmap_begin(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas, snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames, size_t hwbuf_size, const pa_sample_spec *ss) { + int r; + snd_pcm_uframes_t before; + size_t k; + + pa_assert(pcm); + pa_assert(areas); + pa_assert(offset); + pa_assert(frames); + pa_assert(hwbuf_size > 0); + pa_assert(ss); + + before = *frames; + + r = snd_pcm_mmap_begin(pcm, areas, offset, frames); + + if (r < 0) + return r; + + k = (size_t) *frames * pa_frame_size(ss); + + if (*frames > before || + k >= hwbuf_size * 3 || + k >= pa_bytes_per_second(ss)*10) + + pa_log("snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes (%lu ms) " + "Most likely this is an ALSA driver bug. Please report this issue to the PulseAudio developers.", + (unsigned long) k, (unsigned long) (pa_bytes_to_usec(k, ss) / PA_USEC_PER_MSEC)); + + return r; +} |