diff options
Diffstat (limited to 'src/modules/alsa/alsa-util.c')
-rw-r--r-- | src/modules/alsa/alsa-util.c | 261 |
1 files changed, 192 insertions, 69 deletions
diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c index 18d6880e..c03866cc 100644 --- a/src/modules/alsa/alsa-util.c +++ b/src/modules/alsa/alsa-util.c @@ -189,15 +189,6 @@ struct pa_alsa_fdlist *pa_alsa_fdlist_new(void) { 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; } @@ -281,6 +272,11 @@ 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) @@ -307,6 +303,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)); + try_auto: for (i = 0; try_order[i] != PA_SAMPLE_INVALID; i++) { @@ -314,6 +314,10 @@ try_auto: 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; @@ -334,7 +338,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; @@ -348,11 +351,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) { @@ -360,14 +367,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; @@ -375,54 +386,70 @@ 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() failed: %s", 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() failed: %s", 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) { - _period_size = tsched_size; - _periods = 1; + snd_pcm_uframes_t buffer_size; 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); + + _period_size = tsched_size; + _periods = 1; } - 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) @@ -528,7 +555,7 @@ 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", + "hw", NULL, N_("Analog Mono"), "analog-mono", 1, @@ -536,7 +563,7 @@ static const struct pa_alsa_profile_info device_table[] = { "Capture", "Mic" }, {{ 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT }}, - "front", + "front", "hw", N_("Analog Stereo"), "analog-stereo", 10, @@ -544,7 +571,7 @@ static const struct pa_alsa_profile_info device_table[] = { "Capture", "Mic" }, {{ 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT }}, - "iec958", + "iec958", NULL, N_("Digital Stereo (IEC958)"), "iec958-stereo", 5, @@ -552,7 +579,7 @@ static const struct pa_alsa_profile_info device_table[] = { "IEC958 In", NULL }, {{ 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT }}, - "hdmi", + "hdmi", NULL, N_("Digital Stereo (HDMI)"), "hdmi-stereo", 4, @@ -561,7 +588,7 @@ static const struct pa_alsa_profile_info device_table[] = { {{ 4, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT }}, - "surround40", + "surround40", NULL, N_("Analog Surround 4.0"), "analog-surround-40", 7, @@ -570,7 +597,7 @@ static const struct pa_alsa_profile_info device_table[] = { {{ 4, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT }}, - "a52", + "a52", NULL, N_("Digital Surround 4.0 (IEC958/AC3)"), "iec958-ac3-surround-40", 2, @@ -580,7 +607,7 @@ static const struct pa_alsa_profile_info device_table[] = { {{ 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", + "surround41", NULL, N_("Analog Surround 4.1"), "analog-surround-41", 7, @@ -590,7 +617,7 @@ static const struct pa_alsa_profile_info device_table[] = { {{ 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", + "surround50", NULL, N_("Analog Surround 5.0"), "analog-surround-50", 7, @@ -600,7 +627,7 @@ static const struct pa_alsa_profile_info device_table[] = { {{ 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", + "surround51", NULL, N_("Analog Surround 5.1"), "analog-surround-51", 8, @@ -610,7 +637,7 @@ static const struct pa_alsa_profile_info device_table[] = { {{ 6, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT, PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT, PA_CHANNEL_POSITION_FRONT_CENTER, PA_CHANNEL_POSITION_LFE}}, - "a52", + "a52", NULL, N_("Digital Surround 5.1 (IEC958/AC3)"), "iec958-ac3-surround-51", 3, @@ -621,16 +648,72 @@ 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, PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT }}, - "surround71", + "surround71", NULL, N_("Analog Surround 7.1"), "analog-surround-71", 7, "Master", "PCM", "Capture", "Mic" }, - {{ 0, { 0 }}, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL } + {{ 0, { 0 }}, NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL } }; +static snd_pcm_t *open_by_device_string_with_fallback( + const char *prefix, + const char *prefix_fallback, + const char *dev_id, + 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) { + + snd_pcm_t *pcm_handle; + char *d; + + d = pa_sprintf_malloc("%s:%s", prefix, 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 && prefix_fallback) { + + d = pa_sprintf_malloc("%s:%s", prefix_fallback, 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); + } + + return pcm_handle; +} + snd_pcm_t *pa_alsa_open_by_device_id_auto( const char *dev_id, char **dev, @@ -671,14 +754,14 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto( 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, + pcm_handle = open_by_device_string_with_fallback( + device_table[i].alsa_name, + device_table[i].alsa_name_fallback, + dev_id, dev, &try_ss, map, @@ -690,8 +773,6 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto( use_tsched, TRUE); - pa_xfree(d); - if (pcm_handle) { *ss = try_ss; @@ -703,6 +784,7 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto( return pcm_handle; } + } if (direction > 0) { @@ -775,7 +857,6 @@ snd_pcm_t *pa_alsa_open_by_device_id_profile( pa_bool_t *use_tsched, const pa_alsa_profile_info *profile) { - char *d; snd_pcm_t *pcm_handle; pa_sample_spec try_ss; @@ -787,14 +868,14 @@ snd_pcm_t *pa_alsa_open_by_device_id_profile( pa_assert(period_size); pa_assert(profile); - d = pa_sprintf_malloc("%s:%s", profile->alsa_name, dev_id); - try_ss.channels = profile->map.channels; try_ss.rate = ss->rate; try_ss.format = ss->format; - pcm_handle = pa_alsa_open_by_device_string( - d, + pcm_handle = open_by_device_string_with_fallback( + profile->alsa_name, + profile->alsa_name_fallback, + dev_id, dev, &try_ss, map, @@ -806,8 +887,6 @@ snd_pcm_t *pa_alsa_open_by_device_id_profile( use_tsched, TRUE); - pa_xfree(d); - if (!pcm_handle) return NULL; @@ -860,6 +939,8 @@ snd_pcm_t *pa_alsa_open_by_device_string( 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) { @@ -928,26 +1009,25 @@ int pa_alsa_probe_profiles( 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, + pcm_i = open_by_device_string_with_fallback( + i->alsa_name, + i->alsa_name_fallback, + dev_id, + NULL, &try_ss, &try_map, SND_PCM_STREAM_PLAYBACK, NULL, NULL, 0, NULL, NULL, TRUE); - pa_xfree(id); - if (!pcm_i) continue; } @@ -956,26 +1036,25 @@ int pa_alsa_probe_profiles( 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, + pcm_j = open_by_device_string_with_fallback( + j->alsa_name, + j->alsa_name_fallback, + dev_id, + NULL, &try_ss, &try_map, SND_PCM_STREAM_CAPTURE, NULL, NULL, 0, NULL, NULL, TRUE); - pa_xfree(jd); - if (!pcm_j) continue; } @@ -1117,6 +1196,7 @@ success: int pa_alsa_find_mixer_and_elem( snd_pcm_t *pcm, + char **ctl_device, snd_mixer_t **_m, snd_mixer_elem_t **_e, const char *control_name, @@ -1144,9 +1224,13 @@ int pa_alsa_find_mixer_and_elem( /* First, try by name */ if ((dev = snd_pcm_name(pcm))) - if (pa_alsa_prepare_mixer(m, dev) >= 0) + if (pa_alsa_prepare_mixer(m, dev) >= 0) { found = TRUE; + if (ctl_device) + *ctl_device = pa_xstrdup(dev); + } + /* Then, try by card index */ if (!found) { snd_pcm_info_t* info; @@ -1161,9 +1245,15 @@ int pa_alsa_find_mixer_and_elem( md = pa_sprintf_malloc("hw:%i", card_idx); if (!dev || !pa_streq(dev, md)) - if (pa_alsa_prepare_mixer(m, md) >= 0) + if (pa_alsa_prepare_mixer(m, md) >= 0) { found = TRUE; + if (ctl_device) { + *ctl_device = md; + md = NULL; + } + } + pa_xfree(md); } } @@ -1199,6 +1289,9 @@ int pa_alsa_find_mixer_and_elem( } if (!e) { + if (ctl_device) + pa_xfree(*ctl_device); + snd_mixer_close(m); return -1; } @@ -1542,6 +1635,36 @@ void pa_alsa_init_proplist_pcm(pa_core *c, pa_proplist *p, snd_pcm_t *pcm, snd_m 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))) + pa_proplist_sets(p, "alsa.mixer_name", t); + + if ((t = snd_ctl_card_info_get_components(info))) + 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; |