mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-11-03 09:01:50 -05:00
alsa: Probe sink/source sample rates
This probes sink and source sample rates and uses this information to validate rate changes and check incoming passthrough formats.
This commit is contained in:
parent
3555634e6e
commit
e67440e220
4 changed files with 107 additions and 5 deletions
|
|
@ -109,6 +109,8 @@ struct userdata {
|
||||||
|
|
||||||
pa_cvolume hardware_volume;
|
pa_cvolume hardware_volume;
|
||||||
|
|
||||||
|
unsigned int *rates;
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
frame_size,
|
frame_size,
|
||||||
fragment_size,
|
fragment_size,
|
||||||
|
|
@ -1509,8 +1511,8 @@ static pa_idxset* sink_get_formats(pa_sink *s) {
|
||||||
|
|
||||||
static pa_bool_t sink_set_formats(pa_sink *s, pa_idxset *formats) {
|
static pa_bool_t sink_set_formats(pa_sink *s, pa_idxset *formats) {
|
||||||
struct userdata *u = s->userdata;
|
struct userdata *u = s->userdata;
|
||||||
pa_format_info *f;
|
pa_format_info *f, *g;
|
||||||
uint32_t idx;
|
uint32_t idx, n;
|
||||||
|
|
||||||
pa_assert(u);
|
pa_assert(u);
|
||||||
|
|
||||||
|
|
@ -1528,16 +1530,26 @@ static pa_bool_t sink_set_formats(pa_sink *s, pa_idxset *formats) {
|
||||||
* This is fine for now since we don't support that via the passthrough
|
* This is fine for now since we don't support that via the passthrough
|
||||||
* framework, but this must be changed if we do. */
|
* framework, but this must be changed if we do. */
|
||||||
|
|
||||||
|
/* Count how many sample rates we support */
|
||||||
|
for (idx = 0, n = 0; u->rates[idx]; idx++)
|
||||||
|
n++;
|
||||||
|
|
||||||
/* First insert non-PCM formats since we prefer those. */
|
/* First insert non-PCM formats since we prefer those. */
|
||||||
PA_IDXSET_FOREACH(f, formats, idx) {
|
PA_IDXSET_FOREACH(f, formats, idx) {
|
||||||
if (!pa_format_info_is_pcm(f))
|
if (!pa_format_info_is_pcm(f)) {
|
||||||
pa_idxset_put(u->formats, pa_format_info_copy(f), NULL);
|
g = pa_format_info_copy(f);
|
||||||
|
pa_format_info_set_prop_int_array(g, PA_PROP_FORMAT_RATE, (int *) u->rates, n);
|
||||||
|
pa_idxset_put(u->formats, g, NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now add any PCM formats */
|
/* Now add any PCM formats */
|
||||||
PA_IDXSET_FOREACH(f, formats, idx) {
|
PA_IDXSET_FOREACH(f, formats, idx) {
|
||||||
if (pa_format_info_is_pcm(f))
|
if (pa_format_info_is_pcm(f)) {
|
||||||
|
/* We don't set rates here since we'll just tack on a resampler for
|
||||||
|
* unsupported rates */
|
||||||
pa_idxset_put(u->formats, pa_format_info_copy(f), NULL);
|
pa_idxset_put(u->formats, pa_format_info_copy(f), NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
@ -1546,13 +1558,29 @@ static pa_bool_t sink_set_formats(pa_sink *s, pa_idxset *formats) {
|
||||||
static pa_bool_t sink_update_rate_cb(pa_sink *s, uint32_t rate)
|
static pa_bool_t sink_update_rate_cb(pa_sink *s, uint32_t rate)
|
||||||
{
|
{
|
||||||
struct userdata *u = s->userdata;
|
struct userdata *u = s->userdata;
|
||||||
|
int i;
|
||||||
|
pa_bool_t supported = FALSE;
|
||||||
|
|
||||||
pa_assert(u);
|
pa_assert(u);
|
||||||
|
|
||||||
|
for (i = 0; u->rates[i]; i++) {
|
||||||
|
if (u->rates[i] == rate) {
|
||||||
|
supported = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!supported) {
|
||||||
|
pa_log_info("Sink does not support sample rate of %d Hz", rate);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
if (!PA_SINK_IS_OPENED(s->state)) {
|
if (!PA_SINK_IS_OPENED(s->state)) {
|
||||||
pa_log_info("Updating rate for device %s, new rate is %d",u->device_name, rate);
|
pa_log_info("Updating rate for device %s, new rate is %d",u->device_name, rate);
|
||||||
u->sink->sample_spec.rate = rate;
|
u->sink->sample_spec.rate = rate;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2121,6 +2149,12 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
|
||||||
if (is_iec958(u) || is_hdmi(u))
|
if (is_iec958(u) || is_hdmi(u))
|
||||||
set_formats = TRUE;
|
set_formats = TRUE;
|
||||||
|
|
||||||
|
u->rates = pa_alsa_get_supported_rates(u->pcm_handle);
|
||||||
|
if (!u->rates) {
|
||||||
|
pa_log_error("Failed to find any supported sample rates.");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
/* ALSA might tweak the sample spec, so recalculate the frame size */
|
/* ALSA might tweak the sample spec, so recalculate the frame size */
|
||||||
frame_size = pa_frame_size(&ss);
|
frame_size = pa_frame_size(&ss);
|
||||||
|
|
||||||
|
|
@ -2350,6 +2384,9 @@ static void userdata_free(struct userdata *u) {
|
||||||
if (u->formats)
|
if (u->formats)
|
||||||
pa_idxset_free(u->formats, (pa_free2_cb_t) pa_format_info_free2, NULL);
|
pa_idxset_free(u->formats, (pa_free2_cb_t) pa_format_info_free2, NULL);
|
||||||
|
|
||||||
|
if (u->rates)
|
||||||
|
pa_xfree(u->rates);
|
||||||
|
|
||||||
reserve_done(u);
|
reserve_done(u);
|
||||||
monitor_done(u);
|
monitor_done(u);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,8 @@ struct userdata {
|
||||||
|
|
||||||
pa_cvolume hardware_volume;
|
pa_cvolume hardware_volume;
|
||||||
|
|
||||||
|
unsigned int *rates;
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
frame_size,
|
frame_size,
|
||||||
fragment_size,
|
fragment_size,
|
||||||
|
|
@ -1385,13 +1387,29 @@ static void source_update_requested_latency_cb(pa_source *s) {
|
||||||
static pa_bool_t source_update_rate_cb(pa_source *s, uint32_t rate)
|
static pa_bool_t source_update_rate_cb(pa_source *s, uint32_t rate)
|
||||||
{
|
{
|
||||||
struct userdata *u = s->userdata;
|
struct userdata *u = s->userdata;
|
||||||
|
int i;
|
||||||
|
pa_bool_t supported = FALSE;
|
||||||
|
|
||||||
pa_assert(u);
|
pa_assert(u);
|
||||||
|
|
||||||
|
for (i = 0; u->rates[i]; i++) {
|
||||||
|
if (u->rates[i] == rate) {
|
||||||
|
supported = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!supported) {
|
||||||
|
pa_log_info("Sink does not support sample rate of %d Hz", rate);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
if (!PA_SOURCE_IS_OPENED(s->state)) {
|
if (!PA_SOURCE_IS_OPENED(s->state)) {
|
||||||
pa_log_info("Updating rate for device %s, new rate is %d", u->device_name, rate);
|
pa_log_info("Updating rate for device %s, new rate is %d", u->device_name, rate);
|
||||||
u->source->sample_spec.rate = rate;
|
u->source->sample_spec.rate = rate;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1862,6 +1880,12 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
|
||||||
if (u->use_tsched)
|
if (u->use_tsched)
|
||||||
pa_log_info("Successfully enabled timer-based scheduling mode.");
|
pa_log_info("Successfully enabled timer-based scheduling mode.");
|
||||||
|
|
||||||
|
u->rates = pa_alsa_get_supported_rates(u->pcm_handle);
|
||||||
|
if (!u->rates) {
|
||||||
|
pa_log_error("Failed to find any supported sample rates.");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
/* ALSA might tweak the sample spec, so recalculate the frame size */
|
/* ALSA might tweak the sample spec, so recalculate the frame size */
|
||||||
frame_size = pa_frame_size(&ss);
|
frame_size = pa_frame_size(&ss);
|
||||||
|
|
||||||
|
|
@ -2062,6 +2086,9 @@ static void userdata_free(struct userdata *u) {
|
||||||
if (u->smoother)
|
if (u->smoother)
|
||||||
pa_smoother_free(u->smoother);
|
pa_smoother_free(u->smoother);
|
||||||
|
|
||||||
|
if (u->rates)
|
||||||
|
pa_xfree(u->rates);
|
||||||
|
|
||||||
reserve_done(u);
|
reserve_done(u);
|
||||||
monitor_done(u);
|
monitor_done(u);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1327,6 +1327,42 @@ char *pa_alsa_get_reserve_name(const char *device) {
|
||||||
return pa_sprintf_malloc("Audio%i", i);
|
return pa_sprintf_malloc("Audio%i", i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned int *pa_alsa_get_supported_rates(snd_pcm_t *pcm) {
|
||||||
|
static unsigned int all_rates[] = { 8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000, 384000 };
|
||||||
|
pa_bool_t supported[PA_ELEMENTSOF(all_rates)] = { FALSE, };
|
||||||
|
snd_pcm_hw_params_t *hwparams;
|
||||||
|
unsigned int i, j, n, *rates = NULL;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
snd_pcm_hw_params_alloca(&hwparams);
|
||||||
|
|
||||||
|
if ((ret = snd_pcm_hw_params_any(pcm, hwparams)) < 0) {
|
||||||
|
pa_log_debug("snd_pcm_hw_params_any() failed: %s", pa_alsa_strerror(ret));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0, n = 0; i < PA_ELEMENTSOF(all_rates); i++) {
|
||||||
|
if (snd_pcm_hw_params_test_rate(pcm, hwparams, all_rates[i], 0) == 0) {
|
||||||
|
supported[i] = TRUE;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n == 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
rates = pa_xnew(unsigned int, n + 1);
|
||||||
|
|
||||||
|
for (i = 0, j = 0; i < PA_ELEMENTSOF(all_rates); i++) {
|
||||||
|
if (supported[i])
|
||||||
|
rates[j++] = all_rates[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
rates[j] = 0;
|
||||||
|
|
||||||
|
return rates;
|
||||||
|
}
|
||||||
|
|
||||||
pa_bool_t pa_alsa_pcm_is_hw(snd_pcm_t *pcm) {
|
pa_bool_t pa_alsa_pcm_is_hw(snd_pcm_t *pcm) {
|
||||||
snd_pcm_info_t* info;
|
snd_pcm_info_t* info;
|
||||||
snd_pcm_info_alloca(&info);
|
snd_pcm_info_alloca(&info);
|
||||||
|
|
|
||||||
|
|
@ -133,6 +133,8 @@ char *pa_alsa_get_driver_name_by_pcm(snd_pcm_t *pcm);
|
||||||
|
|
||||||
char *pa_alsa_get_reserve_name(const char *device);
|
char *pa_alsa_get_reserve_name(const char *device);
|
||||||
|
|
||||||
|
unsigned int *pa_alsa_get_supported_rates(snd_pcm_t *pcm);
|
||||||
|
|
||||||
pa_bool_t pa_alsa_pcm_is_hw(snd_pcm_t *pcm);
|
pa_bool_t pa_alsa_pcm_is_hw(snd_pcm_t *pcm);
|
||||||
pa_bool_t pa_alsa_pcm_is_modem(snd_pcm_t *pcm);
|
pa_bool_t pa_alsa_pcm_is_modem(snd_pcm_t *pcm);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue