diff options
Diffstat (limited to 'src/modules/alsa')
-rw-r--r-- | src/modules/alsa/alsa-sink.c | 90 | ||||
-rw-r--r-- | src/modules/alsa/alsa-source.c | 90 | ||||
-rw-r--r-- | src/modules/alsa/alsa-util.c | 261 | ||||
-rw-r--r-- | src/modules/alsa/alsa-util.h | 5 | ||||
-rw-r--r-- | src/modules/alsa/module-alsa-card.c | 20 | ||||
-rw-r--r-- | src/modules/alsa/module-alsa-sink.c | 2 | ||||
-rw-r--r-- | src/modules/alsa/module-alsa-source.c | 2 |
7 files changed, 378 insertions, 92 deletions
diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 41e8b477..59a5ca75 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -116,6 +116,8 @@ struct userdata { pa_reserve_wrapper *reserve; pa_hook_slot *reserve_slot; + pa_reserve_monitor_wrapper *monitor; + pa_hook_slot *monitor_slot; }; static void userdata_free(struct userdata *u); @@ -124,7 +126,7 @@ static pa_hook_result_t reserve_cb(pa_reserve_wrapper *r, void *forced, struct u pa_assert(r); pa_assert(u); - if (pa_sink_suspend(u->sink, TRUE) < 0) + if (pa_sink_suspend(u->sink, TRUE, PA_SUSPEND_APPLICATION) < 0) return PA_HOOK_CANCEL; return PA_HOOK_OK; @@ -185,6 +187,57 @@ static int reserve_init(struct userdata *u, const char *dname) { return 0; } +static pa_hook_result_t monitor_cb(pa_reserve_monitor_wrapper *w, void* busy, struct userdata *u) { + pa_bool_t b; + + pa_assert(w); + pa_assert(u); + + b = PA_PTR_TO_UINT(busy) && !u->reserve; + + pa_sink_suspend(u->sink, b, PA_SUSPEND_APPLICATION); + return PA_HOOK_OK; +} + +static void monitor_done(struct userdata *u) { + pa_assert(u); + + if (u->monitor_slot) { + pa_hook_slot_free(u->monitor_slot); + u->monitor_slot = NULL; + } + + if (u->monitor) { + pa_reserve_monitor_wrapper_unref(u->monitor); + u->monitor = NULL; + } +} + +static int reserve_monitor_init(struct userdata *u, const char *dname) { + char *rname; + + pa_assert(u); + pa_assert(dname); + + if (pa_in_system_mode()) + return 0; + + /* We are resuming, try to lock the device */ + if (!(rname = pa_alsa_get_reserve_name(dname))) + return 0; + + u->monitor = pa_reserve_monitor_wrapper_get(u->core, rname); + pa_xfree(rname); + + if (!(u->monitor)) + return -1; + + pa_assert(!u->monitor_slot); + u->monitor_slot = pa_hook_connect(pa_reserve_monitor_wrapper_hook(u->monitor), PA_HOOK_NORMAL, (pa_hook_cb_t) monitor_cb, u); + + return 0; +} + static void fix_min_sleep_wakeup(struct userdata *u) { size_t max_use, max_use_2; @@ -473,7 +526,7 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle u->since_start += frames * u->frame_size; #ifdef DEBUG_TIMING - pa_log_debug("Wrote %lu bytes", (unsigned long) (frames * u->frame_size)); + pa_log_debug("Wrote %lu bytes (of possible %lu bytes)", (unsigned long) (frames * u->frame_size), (unsigned long) n_bytes); #endif if ((size_t) frames * u->frame_size >= n_bytes) @@ -1146,7 +1199,7 @@ fail: static void sink_get_mute_cb(pa_sink *s) { struct userdata *u = s->userdata; - int err, sw; + int err, sw = 0; pa_assert(u); pa_assert(u->mixer_elem); @@ -1508,6 +1561,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca size_t frame_size; pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE; pa_sink_new_data data; + char *control_device = NULL; pa_assert(m); pa_assert(ma); @@ -1579,9 +1633,14 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca pa_rtclock_usec(), TRUE); - if (reserve_init(u, pa_modargs_get_value( - ma, "device_id", - pa_modargs_get_value(ma, "device", DEFAULT_DEVICE))) < 0) + dev_id = pa_modargs_get_value( + ma, "device_id", + pa_modargs_get_value(ma, "device", DEFAULT_DEVICE)); + + if (reserve_init(u, dev_id) < 0) + goto fail; + + if (reserve_monitor_init(u, dev_id) < 0) goto fail; b = use_mmap; @@ -1664,7 +1723,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca /* ALSA might tweak the sample spec, so recalculate the frame size */ frame_size = pa_frame_size(&ss); - pa_alsa_find_mixer_and_elem(u->pcm_handle, &u->mixer_handle, &u->mixer_elem, pa_modargs_get_value(ma, "control", NULL), profile); + pa_alsa_find_mixer_and_elem(u->pcm_handle, &control_device, &u->mixer_handle, &u->mixer_elem, pa_modargs_get_value(ma, "control", NULL), profile); pa_sink_new_data_init(&data); data.driver = driver; @@ -1687,6 +1746,17 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca pa_alsa_init_description(data.proplist); + if (control_device) { + pa_alsa_init_proplist_ctl(data.proplist, control_device); + pa_xfree(control_device); + } + + if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) { + pa_log("Invalid properties"); + pa_sink_new_data_done(&data); + goto fail; + } + u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY|(u->use_tsched ? PA_SINK_DYNAMIC_LATENCY : 0)); pa_sink_new_data_done(&data); @@ -1730,7 +1800,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca pa_log_info("Time scheduling watermark is %0.2fms", (double) pa_bytes_to_usec(u->tsched_watermark, &ss) / PA_USEC_PER_MSEC); } else - u->sink->fixed_latency = pa_bytes_to_usec(u->hwbuf_size, &ss); + pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(u->hwbuf_size, &ss)); reserve_update(u); @@ -1770,7 +1840,8 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca fail: - userdata_free(u); + if (u) + userdata_free(u); return NULL; } @@ -1815,6 +1886,7 @@ static void userdata_free(struct userdata *u) { pa_smoother_free(u->smoother); reserve_done(u); + monitor_done(u); pa_xfree(u->device_name); pa_xfree(u); diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 843f70bb..c176309e 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -114,6 +114,8 @@ struct userdata { pa_reserve_wrapper *reserve; pa_hook_slot *reserve_slot; + pa_reserve_monitor_wrapper *monitor; + pa_hook_slot *monitor_slot; }; static void userdata_free(struct userdata *u); @@ -122,7 +124,7 @@ static pa_hook_result_t reserve_cb(pa_reserve_wrapper *r, void *forced, struct u pa_assert(r); pa_assert(u); - if (pa_source_suspend(u->source, TRUE) < 0) + if (pa_source_suspend(u->source, TRUE, PA_SUSPEND_APPLICATION) < 0) return PA_HOOK_CANCEL; return PA_HOOK_OK; @@ -183,6 +185,57 @@ static int reserve_init(struct userdata *u, const char *dname) { return 0; } +static pa_hook_result_t monitor_cb(pa_reserve_monitor_wrapper *w, void* busy, struct userdata *u) { + pa_bool_t b; + + pa_assert(w); + pa_assert(u); + + b = PA_PTR_TO_UINT(busy) && !u->reserve; + + pa_source_suspend(u->source, b, PA_SUSPEND_APPLICATION); + return PA_HOOK_OK; +} + +static void monitor_done(struct userdata *u) { + pa_assert(u); + + if (u->monitor_slot) { + pa_hook_slot_free(u->monitor_slot); + u->monitor_slot = NULL; + } + + if (u->monitor) { + pa_reserve_monitor_wrapper_unref(u->monitor); + u->monitor = NULL; + } +} + +static int reserve_monitor_init(struct userdata *u, const char *dname) { + char *rname; + + pa_assert(u); + pa_assert(dname); + + if (pa_in_system_mode()) + return 0; + + /* We are resuming, try to lock the device */ + if (!(rname = pa_alsa_get_reserve_name(dname))) + return 0; + + u->monitor = pa_reserve_monitor_wrapper_get(u->core, rname); + pa_xfree(rname); + + if (!(u->monitor)) + return -1; + + pa_assert(!u->monitor_slot); + u->monitor_slot = pa_hook_connect(pa_reserve_monitor_wrapper_hook(u->monitor), PA_HOOK_NORMAL, (pa_hook_cb_t) monitor_cb, u); + + return 0; +} + static void fix_min_sleep_wakeup(struct userdata *u) { size_t max_use, max_use_2; pa_assert(u); @@ -455,7 +508,7 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled u->read_count += frames * u->frame_size; #ifdef DEBUG_TIMING - pa_log_debug("Read %lu bytes", (unsigned long) (frames * u->frame_size)); + pa_log_debug("Read %lu bytes (of possible %lu bytes)", (unsigned long) (frames * u->frame_size), (unsigned long) n_bytes); #endif if ((size_t) frames * u->frame_size >= n_bytes) @@ -1105,7 +1158,7 @@ fail: static void source_get_mute_cb(pa_source *s) { struct userdata *u = s->userdata; - int err, sw; + int err, sw = 0; pa_assert(u); pa_assert(u->mixer_elem); @@ -1366,6 +1419,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p size_t frame_size; pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE; pa_source_new_data data; + char *control_device = NULL; pa_assert(m); pa_assert(ma); @@ -1437,9 +1491,14 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p pa_rtclock_usec(), FALSE); - if (reserve_init(u, pa_modargs_get_value( - ma, "device_id", - pa_modargs_get_value(ma, "device", DEFAULT_DEVICE))) < 0) + dev_id = pa_modargs_get_value( + ma, "device_id", + pa_modargs_get_value(ma, "device", DEFAULT_DEVICE)); + + if (reserve_init(u, dev_id) < 0) + goto fail; + + if (reserve_monitor_init(u, dev_id) < 0) goto fail; b = use_mmap; @@ -1519,7 +1578,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p /* ALSA might tweak the sample spec, so recalculate the frame size */ frame_size = pa_frame_size(&ss); - pa_alsa_find_mixer_and_elem(u->pcm_handle, &u->mixer_handle, &u->mixer_elem, pa_modargs_get_value(ma, "control", NULL), profile); + pa_alsa_find_mixer_and_elem(u->pcm_handle, &control_device, &u->mixer_handle, &u->mixer_elem, pa_modargs_get_value(ma, "control", NULL), profile); pa_source_new_data_init(&data); data.driver = driver; @@ -1542,6 +1601,17 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p pa_alsa_init_description(data.proplist); + if (control_device) { + pa_alsa_init_proplist_ctl(data.proplist, control_device); + pa_xfree(control_device); + } + + if (pa_modargs_get_proplist(ma, "source_properties", data.proplist, PA_UPDATE_REPLACE) < 0) { + pa_log("Invalid properties"); + pa_source_new_data_done(&data); + goto fail; + } + u->source = pa_source_new(m->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY|(u->use_tsched ? PA_SOURCE_DYNAMIC_LATENCY : 0)); pa_source_new_data_done(&data); @@ -1582,7 +1652,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p pa_log_info("Time scheduling watermark is %0.2fms", (double) pa_bytes_to_usec(u->tsched_watermark, &ss) / PA_USEC_PER_MSEC); } else - u->source->fixed_latency = pa_bytes_to_usec(u->hwbuf_size, &ss); + pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(u->hwbuf_size, &ss)); reserve_update(u); @@ -1621,7 +1691,8 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p fail: - userdata_free(u); + if (u) + userdata_free(u); return NULL; } @@ -1663,6 +1734,7 @@ static void userdata_free(struct userdata *u) { pa_smoother_free(u->smoother); reserve_done(u); + monitor_done(u); pa_xfree(u->device_name); pa_xfree(u); 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; diff --git a/src/modules/alsa/alsa-util.h b/src/modules/alsa/alsa-util.h index 77ac8a7f..27f43712 100644 --- a/src/modules/alsa/alsa-util.h +++ b/src/modules/alsa/alsa-util.h @@ -56,6 +56,7 @@ int pa_alsa_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min); typedef struct pa_alsa_profile_info { pa_channel_map map; const char *alsa_name; + const char *alsa_name_fallback; const char *description; /* internationalized */ const char *name; unsigned priority; @@ -65,7 +66,9 @@ typedef struct pa_alsa_profile_info { int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev); snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const char *fallback, pa_bool_t playback); -int pa_alsa_find_mixer_and_elem(snd_pcm_t *pcm, snd_mixer_t **_m, snd_mixer_elem_t **_e, const char *control_name, const pa_alsa_profile_info*profile); +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, const pa_alsa_profile_info*profile); + +void pa_alsa_init_proplist_ctl(pa_proplist *p, const char *name); /* Picks a working profile based on the specified ss/map */ snd_pcm_t *pa_alsa_open_by_device_id_auto( diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c index 51d466e1..ad52f5e3 100644 --- a/src/modules/alsa/module-alsa-card.c +++ b/src/modules/alsa/module-alsa-card.c @@ -43,9 +43,12 @@ PA_MODULE_VERSION(PACKAGE_VERSION); PA_MODULE_LOAD_ONCE(FALSE); PA_MODULE_USAGE( "name=<name for the card/sink/source, to be prefixed> " - "card_name=<name for card> " - "sink_name=<name for sink> " - "source_name=<name for source> " + "card_name=<name for the card> " + "card_properties=<properties for the card> " + "sink_name=<name for the sink> " + "sink_properties=<properties for the sink> " + "source_name=<name for the source> " + "source_properties=<properties for the source> " "device_id=<ALSA card index> " "format=<sample format> " "rate=<sample rate> " @@ -61,8 +64,11 @@ PA_MODULE_USAGE( static const char* const valid_modargs[] = { "name", "card_name", + "card_properties", "sink_name", + "sink_properties", "source_name", + "source_properties", "device_id", "format", "rate", @@ -135,7 +141,7 @@ static void enumerate_cb( bonus += 20000; } - pa_log_info("Found output profile '%s'", t); + pa_log_info("Found profile '%s'", t); p = pa_card_profile_new(n, t, sizeof(struct profile_data)); @@ -340,6 +346,12 @@ int pa__init(pa_module *m) { add_disabled_profile(data.profiles); + if (pa_modargs_get_proplist(ma, "card_properties", data.proplist, PA_UPDATE_REPLACE) < 0) { + pa_log("Invalid properties"); + pa_card_new_data_done(&data); + goto fail; + } + u->card = pa_card_new(m->core, &data); pa_card_new_data_done(&data); diff --git a/src/modules/alsa/module-alsa-sink.c b/src/modules/alsa/module-alsa-sink.c index 8e600ab8..058ea205 100644 --- a/src/modules/alsa/module-alsa-sink.c +++ b/src/modules/alsa/module-alsa-sink.c @@ -40,6 +40,7 @@ PA_MODULE_LOAD_ONCE(FALSE); PA_MODULE_USAGE( "name=<name of the sink, to be prefixed> " "sink_name=<name for the sink> " + "sink_properities=<properties for the sink> " "device=<ALSA device> " "device_id=<ALSA card index> " "format=<sample format> " @@ -58,6 +59,7 @@ PA_MODULE_USAGE( static const char* const valid_modargs[] = { "name", "sink_name", + "sink_properties", "device", "device_id", "format", diff --git a/src/modules/alsa/module-alsa-source.c b/src/modules/alsa/module-alsa-source.c index e6b27b3d..3bd1b451 100644 --- a/src/modules/alsa/module-alsa-source.c +++ b/src/modules/alsa/module-alsa-source.c @@ -64,6 +64,7 @@ PA_MODULE_LOAD_ONCE(FALSE); PA_MODULE_USAGE( "name=<name for the source, to be prefixed> " "source_name=<name for the source> " + "source_properties=<properties for the source> " "device=<ALSA device> " "device_id=<ALSA card index> " "format=<sample format> " @@ -82,6 +83,7 @@ PA_MODULE_USAGE( static const char* const valid_modargs[] = { "name", "source_name", + "source_properties", "device", "device_id", "format", |