From f0ec495938cf0adbee820aec126ece99eb31a1af Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 2 Aug 2011 18:37:28 -0500 Subject: [PATCH] sink,source: support for rate update Avoid resampling or use integer resampling when supported by the sinks/sources Signed-off-by: Pierre-Louis Bossart --- src/pulse/def.h | 10 +++++ src/pulsecore/sink-input.c | 28 +++++++++++++ src/pulsecore/sink.c | 77 +++++++++++++++++++++++++++++++++++ src/pulsecore/sink.h | 11 +++++ src/pulsecore/source-output.c | 24 +++++++++++ src/pulsecore/source.c | 59 +++++++++++++++++++++++++++ src/pulsecore/source.h | 10 +++++ 7 files changed, 219 insertions(+) diff --git a/src/pulse/def.h b/src/pulse/def.h index f43e86468..49e4fdc8b 100644 --- a/src/pulse/def.h +++ b/src/pulse/def.h @@ -826,6 +826,11 @@ static inline int PA_SINK_IS_OPENED(pa_sink_state_t x) { return x == PA_SINK_RUNNING || x == PA_SINK_IDLE; } +/** Returns non-zero if sink is running. \since 1.0 */ +static inline int PA_SINK_IS_RUNNING(pa_sink_state_t x) { + return x == PA_SINK_RUNNING; +} + /** \cond fulldocs */ #define PA_SINK_INVALID_STATE PA_SINK_INVALID_STATE #define PA_SINK_RUNNING PA_SINK_RUNNING @@ -937,6 +942,11 @@ static inline int PA_SOURCE_IS_OPENED(pa_source_state_t x) { return x == PA_SOURCE_RUNNING || x == PA_SOURCE_IDLE; } +/** Returns non-zero if source is running \since 1.0 */ +static inline int PA_SOURCE_IS_RUNNING(pa_source_state_t x) { + return x == PA_SOURCE_RUNNING; +} + /** \cond fulldocs */ #define PA_SOURCE_INVALID_STATE PA_SOURCE_INVALID_STATE #define PA_SOURCE_RUNNING PA_SOURCE_RUNNING diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 5146a9d9e..a64defa44 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -367,6 +367,19 @@ int pa_sink_input_new( pa_assert(pa_sample_spec_valid(&data->sample_spec)); pa_assert(pa_channel_map_valid(&data->channel_map)); + if (!(data->flags & PA_SINK_INPUT_VARIABLE_RATE) && + !pa_sample_spec_equal(&data->sample_spec, &data->sink->sample_spec)) { + /* try to change sink rate. This is done before the FIXATE hook since + module-suspend-on-idle can resume a sink */ + + pa_log_info("Trying to change sample rate"); + if (pa_sink_update_rate(data->sink, data->sample_spec.rate, pa_sink_input_new_data_is_passthrough(data)) == TRUE) + pa_log_info("Rate changed to %u kHz", + data->sink->sample_spec.rate); + else + pa_log_info("Resampling enabled to %u kHz", data->sink->sample_spec.rate); + } + /* Due to the fixing of the sample spec the volume might not match anymore */ pa_cvolume_remap(&data->volume, &original_cm, &data->channel_map); @@ -1605,6 +1618,21 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) { return -PA_ERR_NOTSUPPORTED; } + if (!(i->flags & PA_SINK_INPUT_VARIABLE_RATE) && + !pa_sample_spec_equal(&i->sample_spec, &dest->sample_spec)) { + /* try to change dest sink rate if possible without glitches. + module-suspend-on-idle resumes destination sink with + SINK_INPUT_MOVE_FINISH hook */ + + pa_log_info("Trying to change sample rate"); + if (pa_sink_update_rate(dest, i->sample_spec.rate, pa_sink_input_is_passthrough(i)) == TRUE) + pa_log_info("Rate changed to %u kHz", + dest->sample_spec.rate); + else + pa_log_info("Resampling enabled to %u kHz", + dest->sample_spec.rate); + } + if (i->thread_info.resampler && pa_sample_spec_equal(pa_resampler_output_sample_spec(i->thread_info.resampler), &dest->sample_spec) && pa_channel_map_equal(pa_resampler_output_channel_map(i->thread_info.resampler), &dest->channel_map)) diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 53cab32be..6b5a385f5 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -106,6 +106,13 @@ void pa_sink_new_data_set_channel_map(pa_sink_new_data *data, const pa_channel_m data->channel_map = *map; } +void pa_sink_new_data_set_alternate_sample_rate(pa_sink_new_data *data, const uint32_t alternate_sample_rate) { + pa_assert(data); + + data->alternate_sample_rate_is_set = TRUE; + data->alternate_sample_rate = alternate_sample_rate; +} + void pa_sink_new_data_set_volume(pa_sink_new_data *data, const pa_cvolume *volume) { pa_assert(data); @@ -182,6 +189,7 @@ static void reset_callbacks(pa_sink *s) { s->set_port = NULL; s->get_formats = NULL; s->set_formats = NULL; + s->update_rate = NULL; } /* Called from main context */ @@ -277,6 +285,11 @@ pa_sink* pa_sink_new( s->sample_spec = data->sample_spec; s->channel_map = data->channel_map; + if (data->alternate_sample_rate_is_set) + s->alternate_sample_rate = data->alternate_sample_rate; + else + s->alternate_sample_rate = s->core->alternate_sample_rate; + s->default_sample_rate = s->sample_spec.rate; s->inputs = pa_idxset_new(NULL, NULL); s->n_corked = 0; @@ -364,6 +377,7 @@ pa_sink* pa_sink_new( pa_source_new_data_init(&source_data); pa_source_new_data_set_sample_spec(&source_data, &s->sample_spec); pa_source_new_data_set_channel_map(&source_data, &s->channel_map); + pa_source_new_data_set_alternate_sample_rate(&source_data, s->alternate_sample_rate); source_data.name = pa_sprintf_malloc("%s.monitor", name); source_data.driver = data->driver; source_data.module = data->module; @@ -1312,6 +1326,69 @@ void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) { pa_sink_unref(s); } +/* Called from main thread */ +pa_bool_t pa_sink_update_rate(pa_sink *s, uint32_t rate, pa_bool_t passthrough) +{ + if (s->update_rate) { + uint32_t desired_rate = rate; + uint32_t default_rate = s->default_sample_rate; + uint32_t alternate_rate = s->alternate_sample_rate; + pa_bool_t use_alternate = FALSE; + + if (PA_SINK_IS_RUNNING(s->state)) { + pa_log_info("Cannot update rate, SINK_IS_RUNNING, will keep using %u kHz", + s->sample_spec.rate); + return FALSE; + } + + if (s->monitor_source) { + if (PA_SOURCE_IS_RUNNING(s->monitor_source->state) == TRUE) { + pa_log_info("Cannot update rate, monitor source is RUNNING"); + return FALSE; + } + } + + if (PA_UNLIKELY (desired_rate < 8000 || + desired_rate > PA_RATE_MAX)) + return FALSE; + + if (!passthrough) { + pa_assert(default_rate % 4000 || default_rate % 11025); + pa_assert(alternate_rate % 4000 || alternate_rate % 11025); + + if (default_rate % 4000) { + /* default is a 11025 multiple */ + if ((alternate_rate % 4000 == 0) && (desired_rate % 4000 == 0)) + use_alternate=TRUE; + } else { + /* default is 4000 multiple */ + if ((alternate_rate % 11025 == 0) && (desired_rate % 11025 == 0)) + use_alternate=TRUE; + } + + if (use_alternate) + desired_rate = alternate_rate; + else + desired_rate = default_rate; + } else { + desired_rate = rate; /* use stream sampling rate, discard default/alternate settings */ + } + + if (passthrough || pa_sink_used_by(s) == 0) { + pa_sink_suspend(s, TRUE, PA_SUSPEND_IDLE); /* needed before rate update, will be resumed automatically */ + } + + if (s->update_rate(s, desired_rate) == TRUE) { + /* update monitor source as well */ + if (s->monitor_source) + pa_source_update_rate(s->monitor_source, desired_rate); + pa_log_info("Changed sampling rate successfully"); + return TRUE; + } + } + return FALSE; +} + /* Called from main thread */ pa_usec_t pa_sink_get_latency(pa_sink *s) { pa_usec_t usec = 0; diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h index 7f639e264..a88ac5855 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -88,6 +88,8 @@ struct pa_sink { pa_sample_spec sample_spec; pa_channel_map channel_map; + uint32_t default_sample_rate; + uint32_t alternate_sample_rate; pa_idxset *inputs; unsigned n_corked; @@ -233,6 +235,10 @@ struct pa_sink { * set). Makes a copy of the formats passed in. */ pa_bool_t (*set_formats)(pa_sink *s, pa_idxset *formats); /* may be NULL */ + /* Called whenever the sampling frequency shall be changed. Called from + * main thread. */ + pa_bool_t (*update_rate)(pa_sink *s, uint32_t rate); + /* Contains copies of the above data so that the real-time worker * thread can work without access locking */ struct { @@ -337,11 +343,13 @@ typedef struct pa_sink_new_data { pa_sample_spec sample_spec; pa_channel_map channel_map; + uint32_t alternate_sample_rate; pa_cvolume volume; pa_bool_t muted :1; pa_bool_t sample_spec_is_set:1; pa_bool_t channel_map_is_set:1; + pa_bool_t alternate_sample_rate_is_set:1; pa_bool_t volume_is_set:1; pa_bool_t muted_is_set:1; @@ -356,6 +364,7 @@ pa_sink_new_data* pa_sink_new_data_init(pa_sink_new_data *data); void pa_sink_new_data_set_name(pa_sink_new_data *data, const char *name); void pa_sink_new_data_set_sample_spec(pa_sink_new_data *data, const pa_sample_spec *spec); void pa_sink_new_data_set_channel_map(pa_sink_new_data *data, const pa_channel_map *map); +void pa_sink_new_data_set_alternate_sample_rate(pa_sink_new_data *data, const uint32_t alternate_sample_rate); void pa_sink_new_data_set_volume(pa_sink_new_data *data, const pa_cvolume *volume); void pa_sink_new_data_set_muted(pa_sink_new_data *data, pa_bool_t mute); void pa_sink_new_data_set_port(pa_sink_new_data *data, const char *port); @@ -403,6 +412,8 @@ unsigned pa_device_init_priority(pa_proplist *p); /**** May be called by everyone, from main context */ +pa_bool_t pa_sink_update_rate(pa_sink *s, uint32_t rate, pa_bool_t passthrough); + /* The returned value is supposed to be in the time domain of the sound card! */ pa_usec_t pa_sink_get_latency(pa_sink *s); pa_usec_t pa_sink_get_requested_latency(pa_sink *s); diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index f6ad009a9..46a95a717 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -348,6 +348,15 @@ int pa_source_output_new( /* Due to the fixing of the sample spec the volume might not match anymore */ pa_cvolume_remap(&data->volume, &original_cm, &data->channel_map); + if (!(data->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) && + !pa_sample_spec_equal(&data->sample_spec, &data->source->sample_spec)){ + /* try to change source rate. This is done before the FIXATE hook since + module-suspend-on-idle can resume a source */ + + pa_log_info("Trying to change sample rate"); + pa_source_update_rate(data->source, data->sample_spec.rate); + } + if (data->resample_method == PA_RESAMPLER_INVALID) data->resample_method = core->resample_method; @@ -1385,6 +1394,21 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t return -PA_ERR_NOTSUPPORTED; } + if (!(o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) && + !pa_sample_spec_equal(&o->sample_spec, &dest->sample_spec)){ + /* try to change dest sink rate if possible without glitches. + module-suspend-on-idle resumes destination source with + SOURCE_OUTPUT_MOVE_FINISH hook */ + + pa_log_info("Trying to change sample rate"); + if (pa_source_update_rate(dest, o->sample_spec.rate) == TRUE) + pa_log_info("Rate changed to %u kHz", + dest->sample_spec.rate); + else + pa_log_info("Resampling enabled to %u kHz", + dest->sample_spec.rate); + } + if (o->thread_info.resampler && pa_sample_spec_equal(pa_resampler_input_sample_spec(o->thread_info.resampler), &dest->sample_spec) && pa_channel_map_equal(pa_resampler_input_channel_map(o->thread_info.resampler), &dest->channel_map)) diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index d47280ce8..508fb4a1e 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -98,6 +98,13 @@ void pa_source_new_data_set_channel_map(pa_source_new_data *data, const pa_chann data->channel_map = *map; } +void pa_source_new_data_set_alternate_sample_rate(pa_source_new_data *data, const uint32_t alternate_sample_rate) { + pa_assert(data); + + data->alternate_sample_rate_is_set = TRUE; + data->alternate_sample_rate = alternate_sample_rate; +} + void pa_source_new_data_set_volume(pa_source_new_data *data, const pa_cvolume *volume) { pa_assert(data); @@ -150,6 +157,7 @@ static void reset_callbacks(pa_source *s) { s->update_requested_latency = NULL; s->set_port = NULL; s->get_formats = NULL; + s->update_rate = NULL; } /* Called from main context */ @@ -243,6 +251,11 @@ pa_source* pa_source_new( s->sample_spec = data->sample_spec; s->channel_map = data->channel_map; + if (data->alternate_sample_rate_is_set) + s->alternate_sample_rate = data->alternate_sample_rate; + else + s->alternate_sample_rate = s->core->alternate_sample_rate; + s->default_sample_rate = s->sample_spec.rate; s->outputs = pa_idxset_new(NULL, NULL); s->n_corked = 0; @@ -907,6 +920,52 @@ void pa_source_post_direct(pa_source*s, pa_source_output *o, const pa_memchunk * pa_source_output_push(o, chunk); } +/* Called from main thread */ +pa_bool_t pa_source_update_rate(pa_source *s, uint32_t rate) +{ + + if (s->update_rate) { + uint32_t desired_rate = rate; + uint32_t default_rate = s->default_sample_rate; + uint32_t alternate_rate = s->alternate_sample_rate; + pa_bool_t use_alternate = FALSE; + + if (PA_SOURCE_IS_RUNNING(s->state)) { + pa_log_info("Cannot update rate, SOURCE_IS_RUNNING, will keep using %u kHz", + s->sample_spec.rate); + return FALSE; /* cannot reconfigure a RUNNING source without glitches */ + } + + if (PA_UNLIKELY (desired_rate < 8000 || + desired_rate > PA_RATE_MAX)) + return FALSE; + + pa_assert(default_rate % 4000 || default_rate % 11025); + pa_assert(alternate_rate % 4000 || alternate_rate % 11025); + + if (default_rate % 4000) { + /* default is a 11025 multiple */ + if ((alternate_rate % 4000 == 0) && (desired_rate % 4000 == 0)) + use_alternate=TRUE; + } else { + /* default is 4000 multiple */ + if ((alternate_rate % 11025 == 0) && (desired_rate % 11025 == 0)) + use_alternate=TRUE; + } + + if (use_alternate) + desired_rate = alternate_rate; + else + desired_rate = default_rate; + + if (s->update_rate(s, desired_rate) == TRUE) { + pa_log_info("Changed sampling rate successfully "); + return TRUE; + } + } + return FALSE; +} + /* Called from main thread */ pa_usec_t pa_source_get_latency(pa_source *s) { pa_usec_t usec; diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h index 949ae48b2..8adc5f1bb 100644 --- a/src/pulsecore/source.h +++ b/src/pulsecore/source.h @@ -76,6 +76,8 @@ struct pa_source { pa_sample_spec sample_spec; pa_channel_map channel_map; + uint32_t default_sample_rate; + uint32_t alternate_sample_rate; pa_idxset *outputs; unsigned n_corked; @@ -178,6 +180,10 @@ struct pa_source { * in descending order of preference. */ pa_idxset* (*get_formats)(pa_source *s); /* ditto */ + /* Called whenever the sampling frequency shall be changed. Called from + * main thread. */ + pa_bool_t (*update_rate)(pa_source *s, uint32_t rate); + /* Contains copies of the above data so that the real-time worker * thread can work without access locking */ struct { @@ -262,6 +268,7 @@ typedef struct pa_source_new_data { pa_sample_spec sample_spec; pa_channel_map channel_map; + uint32_t alternate_sample_rate; pa_cvolume volume; pa_bool_t muted:1; @@ -269,6 +276,7 @@ typedef struct pa_source_new_data { pa_bool_t muted_is_set:1; pa_bool_t sample_spec_is_set:1; pa_bool_t channel_map_is_set:1; + pa_bool_t alternate_sample_rate_is_set:1; pa_bool_t namereg_fail:1; @@ -281,6 +289,7 @@ pa_source_new_data* pa_source_new_data_init(pa_source_new_data *data); void pa_source_new_data_set_name(pa_source_new_data *data, const char *name); void pa_source_new_data_set_sample_spec(pa_source_new_data *data, const pa_sample_spec *spec); void pa_source_new_data_set_channel_map(pa_source_new_data *data, const pa_channel_map *map); +void pa_source_new_data_set_alternate_sample_rate(pa_source_new_data *data, const uint32_t alternate_sample_rate); void pa_source_new_data_set_volume(pa_source_new_data *data, const pa_cvolume *volume); void pa_source_new_data_set_muted(pa_source_new_data *data, pa_bool_t mute); void pa_source_new_data_set_port(pa_source_new_data *data, const char *port); @@ -358,6 +367,7 @@ pa_bool_t pa_source_get_mute(pa_source *source, pa_bool_t force_refresh); pa_bool_t pa_source_update_proplist(pa_source *s, pa_update_mode_t mode, pa_proplist *p); int pa_source_set_port(pa_source *s, const char *name, pa_bool_t save); +pa_bool_t pa_source_update_rate(pa_source *s, uint32_t rate); unsigned pa_source_linked_by(pa_source *s); /* Number of connected streams */ unsigned pa_source_used_by(pa_source *s); /* Number of connected streams that are not corked */