diff options
Diffstat (limited to 'src/modules/alsa/alsa-util.c')
| -rw-r--r-- | src/modules/alsa/alsa-util.c | 185 | 
1 files changed, 157 insertions, 28 deletions
| diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c index 5236d02f..d00a80f1 100644 --- a/src/modules/alsa/alsa-util.c +++ b/src/modules/alsa/alsa-util.c @@ -1,7 +1,7 @@  /***    This file is part of PulseAudio. -  Copyright 2004-2006 Lennart Poettering +  Copyright 2004-2009 Lennart Poettering    Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB    PulseAudio is free software; you can redistribute it and/or modify @@ -32,11 +32,13 @@  #include <pulse/xmalloc.h>  #include <pulse/timeval.h>  #include <pulse/util.h> +#include <pulse/i18n.h>  #include <pulsecore/log.h>  #include <pulsecore/macro.h>  #include <pulsecore/core-util.h>  #include <pulsecore/atomic.h> +#include <pulsecore/core-error.h>  #include "alsa-util.h" @@ -112,7 +114,7 @@ static void io_cb(pa_mainloop_api*a, pa_io_event* e, int fd, pa_io_event_flags_t  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; +    int err, n;      struct pollfd *temp;      pa_assert(a); @@ -121,7 +123,11 @@ static void defer_cb(pa_mainloop_api*a, pa_defer_event* e, void *userdata) {      a->defer_enable(fdl->defer, 0); -    num_fds = (unsigned) snd_mixer_poll_descriptors_count(fdl->mixer); +    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) @@ -342,7 +348,8 @@ int pa_alsa_set_hw_params(          goto finish;      if (_use_mmap) { -        if ((ret = snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED)) < 0) { + +        if (snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0) {              /* mmap() didn't work, fall back to interleaved */ @@ -462,6 +469,7 @@ finish:  int pa_alsa_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min) {      snd_pcm_sw_params_t *swparams; +    snd_pcm_uframes_t boundary;      int err;      pa_assert(pcm); @@ -473,7 +481,22 @@ int pa_alsa_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min) {          return err;      } -    if ((err = snd_pcm_sw_params_set_stop_threshold(pcm, swparams, (snd_pcm_uframes_t) -1)) < 0) { +    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)); +        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)); +        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)); +        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));          return err;      } @@ -499,39 +522,39 @@ int pa_alsa_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min) {  static const struct pa_alsa_profile_info device_table[] = {      {{ 1, { PA_CHANNEL_POSITION_MONO }},       "hw", -     "Analog Mono", +     N_("Analog Mono"),       "analog-mono",       1 },      {{ 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT }},       "front", -     "Analog Stereo", +     N_("Analog Stereo"),       "analog-stereo",       10 },      {{ 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT }},       "iec958", -     "IEC958 Digital Stereo", +     N_("Digital Stereo (IEC958)"),       "iec958-stereo",       5 },      {{ 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT }},       "hdmi", -     "HDMI Digital Stereo", +     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", -     "Analog Surround 4.0", +     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", -     "IEC958/AC3 Digital Surround 4.0", +     N_("Digital Surround 4.0 (IEC958/AC3)"),       "iec958-ac3-surround-40",       2 }, @@ -539,7 +562,7 @@ static const struct pa_alsa_profile_info device_table[] = {              PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,              PA_CHANNEL_POSITION_LFE }},       "surround41", -     "Analog Surround 4.1", +     N_("Analog Surround 4.1"),       "analog-surround-41",       7 }, @@ -547,7 +570,7 @@ static const struct pa_alsa_profile_info device_table[] = {              PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,              PA_CHANNEL_POSITION_CENTER }},       "surround50", -     "Analog Surround 5.0", +     N_("Analog Surround 5.0"),       "analog-surround-50",       7 }, @@ -555,7 +578,7 @@ static const struct pa_alsa_profile_info device_table[] = {              PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,              PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_LFE }},       "surround51", -     "Analog Surround 5.1", +     N_("Analog Surround 5.1"),       "analog-surround-51",       8 }, @@ -563,7 +586,7 @@ static const struct pa_alsa_profile_info device_table[] = {              PA_CHANNEL_POSITION_FRONT_RIGHT, PA_CHANNEL_POSITION_REAR_LEFT,              PA_CHANNEL_POSITION_REAR_RIGHT, PA_CHANNEL_POSITION_LFE}},       "a52", -     "IEC958/AC3 Digital Surround 5.1", +     N_("Digital Surround 5.1 (IEC958/AC3)"),       "iec958-ac3-surround-51",       3 }, @@ -572,7 +595,7 @@ static const struct pa_alsa_profile_info device_table[] = {              PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_LFE,              PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT }},       "surround71", -     "Analog Surround 7.1", +     N_("Analog Surround 7.1"),       "analog-surround-71",       7 }, @@ -805,8 +828,7 @@ snd_pcm_t *pa_alsa_open_by_device_string(                                  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_xfree(d); -            return NULL; +            goto fail;          }          if ((err = pa_alsa_set_hw_params(pcm_handle, ss, nfrags, period_size, tsched_size, use_mmap, use_tsched, require_exact_channel_number)) < 0) { @@ -834,9 +856,9 @@ snd_pcm_t *pa_alsa_open_by_device_string(              }              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; + +            goto fail;          }          if (dev) @@ -849,6 +871,11 @@ snd_pcm_t *pa_alsa_open_by_device_string(          return pcm_handle;      } + +fail: +    pa_xfree(d); + +    return NULL;  }  int pa_alsa_probe_profiles( @@ -1057,6 +1084,86 @@ success:      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! */ @@ -1281,7 +1388,7 @@ void pa_alsa_init_proplist_card(pa_core *c, pa_proplist *p, int card) {  #endif  } -void pa_alsa_init_proplist_pcm(pa_core *c, pa_proplist *p, snd_pcm_info_t *pcm_info) { +void pa_alsa_init_proplist_pcm_info(pa_core *c, 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", @@ -1302,7 +1409,7 @@ void pa_alsa_init_proplist_pcm(pa_core *c, pa_proplist *p, snd_pcm_info_t *pcm_i      snd_pcm_class_t class;      snd_pcm_subclass_t subclass; -    const char *n, *id, *sdn, *cn; +    const char *n, *id, *sdn, *cn = NULL;      int card;      pa_assert(p); @@ -1347,6 +1454,28 @@ void pa_alsa_init_proplist_pcm(pa_core *c, pa_proplist *p, snd_pcm_info_t *pcm_i          pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, n);  } +void pa_alsa_init_proplist_pcm(pa_core *c, pa_proplist *p, snd_pcm_t *pcm) { +    snd_pcm_hw_params_t *hwparams; +    snd_pcm_info_t *info; +    int bits, err; + +    snd_pcm_hw_params_alloca(&hwparams); +    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)); +    else { + +        if ((bits = snd_pcm_hw_params_get_sbits(hwparams)) >= 0) +            pa_proplist_setf(p, "alsa.resolution_bits", "%i", bits); +    } + +    if ((err = snd_pcm_info(pcm, info)) < 0) +        pa_log_warn("Error fetching PCM info: %s", snd_strerror(err)); +    else +        pa_alsa_init_proplist_pcm_info(c, p, info); +} +  int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents) {      snd_pcm_state_t state;      int err; @@ -1425,7 +1554,7 @@ 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 pa_alsa_safe_avail(snd_pcm_t *pcm, size_t hwbuf_size, const pa_sample_spec *ss) {      snd_pcm_sframes_t n;      size_t k; @@ -1436,7 +1565,7 @@ snd_pcm_sframes_t pa_alsa_safe_avail_update(snd_pcm_t *pcm, size_t hwbuf_size, c      /* Some ALSA driver expose weird bugs, let's inform the user about       * what is going on */ -    n = snd_pcm_avail_update(pcm); +    n = snd_pcm_avail(pcm);      if (n <= 0)          return n; @@ -1445,8 +1574,8 @@ snd_pcm_sframes_t pa_alsa_safe_avail_update(snd_pcm_t *pcm, size_t hwbuf_size, c      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.", +        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 ALSA developers."),                 (unsigned long) k, (unsigned long) (pa_bytes_to_usec(k, ss) / PA_USEC_PER_MSEC));      return n; @@ -1477,8 +1606,8 @@ int pa_alsa_safe_mmap_begin(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas          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.", +        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 ALSA developers."),                 (unsigned long) k, (unsigned long) (pa_bytes_to_usec(k, ss) / PA_USEC_PER_MSEC));      return r; | 
