This rejigs the update_rate() logic to encompass changes to the sample spec as a whole, as well as passthrough status. As a result, sinks/sources provide a reconfigure() method which allows reconfiguration as required. The behaviour itself is currently unchanged -- alsa-sink/-source do not actually implement anything other than rate updates for now (nor are they ever requested to). This can be modified in the future, to allow, for example 24-bit output when incoming media supports it, as well as channel count changes for passthrough sinks. Another related change is that passthrough status is now part of sink/source reconfiguration, and we can stop doing a suspend/unsuspend when entering/leaving passthrough state. So that part is now divided in two -- pa_sink_reconfigure() sets the sink in passthrough mode if required, and pa_sink_enter_passthrough() sets up everything else (this currently means only volumes, but could disable other processing) for passthrough mode. --- src/modules/alsa/alsa-sink.c | 16 ++++++---- src/modules/alsa/alsa-source.c | 14 +++++---- src/pulsecore/sink-input.c | 6 ++-- src/pulsecore/sink.c | 71 +++++++++++++++++------------------------- src/pulsecore/sink.h | 7 ++--- src/pulsecore/source-output.c | 6 ++-- src/pulsecore/source.c | 58 ++++++++++++++++++---------------- src/pulsecore/source.h | 6 ++-- 8 files changed, 90 insertions(+), 94 deletions(-) diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 827a65081..96fd02aec 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -1596,31 +1596,35 @@ static bool sink_set_formats(pa_sink *s, pa_idxset *formats) { return true; } -static int sink_update_rate_cb(pa_sink *s, uint32_t rate) { +static int sink_reconfigure_cb(pa_sink *s, pa_sample_spec *spec, bool passthrough) { struct userdata *u = s->userdata; int i; bool supported = false; + /* FIXME: we only update rate for now */ + pa_assert(u); for (i = 0; u->rates[i]; i++) { - if (u->rates[i] == rate) { + if (u->rates[i] == spec->rate) { supported = true; break; } } if (!supported) { - pa_log_info("Sink does not support sample rate of %d Hz", rate); + pa_log_info("Sink does not support sample rate of %d Hz", spec->rate); return -1; } if (!PA_SINK_IS_OPENED(s->state)) { - pa_log_info("Updating rate for device %s, new rate is %d",u->device_name, rate); - u->sink->sample_spec.rate = rate; + pa_log_info("Updating rate for device %s, new rate is %d", u->device_name, spec->rate); + u->sink->sample_spec.rate = spec->rate; return 0; } + /* Passthrough status change is handled during unsuspend */ + return -1; } @@ -2357,7 +2361,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca else u->sink->set_port = sink_set_port_cb; if (u->sink->alternate_sample_rate) - u->sink->update_rate = sink_update_rate_cb; + u->sink->reconfigure = sink_reconfigure_cb; u->sink->userdata = u; pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 6bec188ea..3f87a4792 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -1398,28 +1398,30 @@ static void source_update_requested_latency_cb(pa_source *s) { update_sw_params(u); } -static int source_update_rate_cb(pa_source *s, uint32_t rate) { +static int source_reconfigure_cb(pa_source *s, pa_sample_spec *spec, bool passthrough) { struct userdata *u = s->userdata; int i; bool supported = false; + /* FIXME: we only update rate for now */ + pa_assert(u); for (i = 0; u->rates[i]; i++) { - if (u->rates[i] == rate) { + if (u->rates[i] == spec->rate) { supported = true; break; } } if (!supported) { - pa_log_info("Source does not support sample rate of %d Hz", rate); + pa_log_info("Source does not support sample rate of %d Hz", spec->rate); return -1; } if (!PA_SOURCE_IS_OPENED(s->state)) { - pa_log_info("Updating rate for device %s, new rate is %d", u->device_name, rate); - u->source->sample_spec.rate = rate; + pa_log_info("Updating rate for device %s, new rate is %d", u->device_name, spec->rate); + u->source->sample_spec.rate = spec->rate; return 0; } @@ -2041,7 +2043,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p else u->source->set_port = source_set_port_cb; if (u->source->alternate_sample_rate) - u->source->update_rate = source_update_rate_cb; + u->source->reconfigure = source_reconfigure_cb; u->source->userdata = u; pa_source_set_asyncmsgq(u->source, u->thread_mq.inq); diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index d1e16ee27..05fe2c026 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -410,7 +410,7 @@ int pa_sink_input_new( 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)) >= 0) + if (pa_sink_reconfigure(data->sink, &data->sample_spec, pa_sink_input_new_data_is_passthrough(data)) >= 0) pa_log_info("Rate changed to %u Hz", data->sink->sample_spec.rate); } @@ -609,7 +609,7 @@ static void sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state) !pa_sample_spec_equal(&i->sample_spec, &i->sink->sample_spec)) { /* We were uncorked and the sink was not playing anything -- let's try * to update the sample rate to avoid resampling */ - pa_sink_update_rate(i->sink, i->sample_spec.rate, pa_sink_input_is_passthrough(i)); + pa_sink_reconfigure(i->sink, &i->sample_spec, pa_sink_input_is_passthrough(i)); } pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) == 0); @@ -1900,7 +1900,7 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, bool save) { 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)) >= 0) + if (pa_sink_reconfigure(dest, &i->sample_spec, pa_sink_input_is_passthrough(i)) >= 0) pa_log_info("Rate changed to %u Hz", dest->sample_spec.rate); } diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 94f41b927..017b95392 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -161,7 +161,7 @@ static void reset_callbacks(pa_sink *s) { s->set_port = NULL; s->get_formats = NULL; s->set_formats = NULL; - s->update_rate = NULL; + s->reconfigure = NULL; } /* Called from main context */ @@ -1411,9 +1411,9 @@ void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) { } /* Called from main thread */ -int pa_sink_update_rate(pa_sink *s, uint32_t rate, bool passthrough) { +int pa_sink_reconfigure(pa_sink *s, pa_sample_spec *spec, bool passthrough) { int ret = -1; - uint32_t desired_rate; + pa_sample_spec desired_spec; uint32_t default_rate = s->default_sample_rate; uint32_t alternate_rate = s->alternate_sample_rate; uint32_t idx; @@ -1422,10 +1422,12 @@ int pa_sink_update_rate(pa_sink *s, uint32_t rate, bool passthrough) { bool alternate_rate_is_usable = false; bool avoid_resampling = s->core->avoid_resampling; - if (rate == s->sample_spec.rate) + /* We currently only try to reconfigure the sample rate */ + + if (pa_sample_spec_equal(spec, &s->sample_spec)) return 0; - if (!s->update_rate) + if (!s->reconfigure) return -1; if (PA_UNLIKELY(default_rate == alternate_rate && !passthrough && !avoid_resampling)) { @@ -1446,52 +1448,54 @@ int pa_sink_update_rate(pa_sink *s, uint32_t rate, bool passthrough) { } } - if (PA_UNLIKELY(!pa_sample_rate_valid(rate))) + if (PA_UNLIKELY(!pa_sample_spec_valid(spec))) return -1; + desired_spec = s->sample_spec; + if (passthrough) { /* We have to try to use the sink input rate */ - desired_rate = rate; + desired_spec.rate = spec->rate; - } else if (avoid_resampling && (rate >= default_rate || rate >= alternate_rate)) { + } else if (avoid_resampling && (spec->rate >= default_rate || spec->rate >= alternate_rate)) { /* We just try to set the sink input's sample rate if it's not too low */ - desired_rate = rate; + desired_spec.rate = spec->rate; - } else if (default_rate == rate || alternate_rate == rate) { + } else if (default_rate == spec->rate || alternate_rate == spec->rate) { /* We can directly try to use this rate */ - desired_rate = rate; + desired_spec.rate = spec->rate; } else { /* See if we can pick a rate that results in less resampling effort */ - if (default_rate % 11025 == 0 && rate % 11025 == 0) + if (default_rate % 11025 == 0 && spec->rate % 11025 == 0) default_rate_is_usable = true; - if (default_rate % 4000 == 0 && rate % 4000 == 0) + if (default_rate % 4000 == 0 && spec->rate % 4000 == 0) default_rate_is_usable = true; - if (alternate_rate && alternate_rate % 11025 == 0 && rate % 11025 == 0) + if (alternate_rate && alternate_rate % 11025 == 0 && spec->rate % 11025 == 0) alternate_rate_is_usable = true; - if (alternate_rate && alternate_rate % 4000 == 0 && rate % 4000 == 0) + if (alternate_rate && alternate_rate % 4000 == 0 && spec->rate % 4000 == 0) alternate_rate_is_usable = true; if (alternate_rate_is_usable && !default_rate_is_usable) - desired_rate = alternate_rate; + desired_spec.rate = alternate_rate; else - desired_rate = default_rate; + desired_spec.rate = default_rate; } - if (desired_rate == s->sample_spec.rate) + if (pa_sample_spec_equal(&desired_spec, &s->sample_spec) && passthrough == pa_sink_is_passthrough(s)) return -1; if (!passthrough && pa_sink_used_by(s) > 0) return -1; - pa_log_debug("Suspending sink %s due to changing the sample rate.", s->name); + pa_log_debug("Suspending sink %s due to changing format.", s->name); pa_sink_suspend(s, true, PA_SUSPEND_INTERNAL); - if (s->update_rate(s, desired_rate) >= 0) { + if (s->reconfigure(s, &desired_spec, passthrough) >= 0) { /* update monitor source as well */ if (s->monitor_source && !passthrough) - pa_source_update_rate(s->monitor_source, desired_rate, false); - pa_log_info("Changed sampling rate successfully"); + pa_source_reconfigure(s->monitor_source, &desired_spec, false); + pa_log_info("Changed format successfully"); PA_IDXSET_FOREACH(i, s->inputs, idx) { if (i->state == PA_SINK_INPUT_CORKED) @@ -1626,10 +1630,9 @@ bool pa_sink_is_passthrough(pa_sink *s) { void pa_sink_enter_passthrough(pa_sink *s) { pa_cvolume volume; - if (s->is_passthrough_set) { - pa_log_debug("Sink %s is already in passthrough mode, nothing to do", s->name); - return; - } + /* The sink implementation is reconfigured for passthrough in + * pa_sink_reconfigure(). This function sets the PA core objects to + * passthrough mode. */ /* disable the monitor in passthrough mode */ if (s->monitor_source) { @@ -1645,21 +1648,10 @@ void pa_sink_enter_passthrough(pa_sink *s) { pa_sink_set_volume(s, &volume, true, false); pa_log_debug("Suspending/Restarting sink %s to enter passthrough mode", s->name); - - /* force sink to be resumed in passthrough mode */ - pa_sink_suspend(s, true, PA_SUSPEND_INTERNAL); - s->is_passthrough_set = true; - pa_sink_suspend(s, false, PA_SUSPEND_INTERNAL); } /* Called from main context */ void pa_sink_leave_passthrough(pa_sink *s) { - - if (!s->is_passthrough_set) { - pa_log_debug("Sink %s is not in passthrough mode, nothing to do", s->name); - return; - } - /* Unsuspend monitor */ if (s->monitor_source) { pa_log_debug("Resuming monitor source %s, because the sink is leaving the passthrough mode.", s->monitor_source->name); @@ -1672,11 +1664,6 @@ void pa_sink_leave_passthrough(pa_sink *s) { pa_cvolume_init(&s->saved_volume); s->saved_save_volume = false; - /* force sink to be resumed in non-passthrough mode */ - pa_sink_suspend(s, true, PA_SUSPEND_INTERNAL); - s->is_passthrough_set = false; - pa_sink_suspend(s, false, PA_SUSPEND_INTERNAL); - } /* Called from main context. */ diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h index 7deafdd85..cae5e517b 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -108,7 +108,6 @@ struct pa_sink { /* Saved volume state while we're in passthrough mode */ pa_cvolume saved_volume; bool saved_save_volume:1; - bool is_passthrough_set:1; pa_asyncmsgq *asyncmsgq; @@ -245,9 +244,9 @@ struct pa_sink { * set). Makes a copy of the formats passed in. */ bool (*set_formats)(pa_sink *s, pa_idxset *formats); /* may be NULL */ - /* Called whenever the sampling frequency shall be changed. Called from + /* Called whenever device parameters need to be changed. Called from * main thread. */ - int (*update_rate)(pa_sink *s, uint32_t rate); + int (*reconfigure)(pa_sink *s, pa_sample_spec *spec, bool passthrough); /* Contains copies of the above data so that the real-time worker * thread can work without access locking */ @@ -423,7 +422,7 @@ unsigned pa_device_init_priority(pa_proplist *p); /**** May be called by everyone, from main context */ -int pa_sink_update_rate(pa_sink *s, uint32_t rate, bool passthrough); +int pa_sink_reconfigure(pa_sink *s, pa_sample_spec *spec, bool passthrough); void pa_sink_set_port_latency_offset(pa_sink *s, int64_t offset); /* The returned value is supposed to be in the time domain of the sound card! */ diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index 687b2b567..f8a421aa8 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -356,7 +356,7 @@ int pa_source_output_new( module-suspend-on-idle can resume a source */ pa_log_info("Trying to change sample rate"); - if (pa_source_update_rate(data->source, data->sample_spec.rate, pa_source_output_new_data_is_passthrough(data)) >= 0) + if (pa_source_reconfigure(data->source, &data->sample_spec, pa_source_output_new_data_is_passthrough(data)) >= 0) pa_log_info("Rate changed to %u Hz", data->source->sample_spec.rate); } @@ -529,7 +529,7 @@ static void source_output_set_state(pa_source_output *o, pa_source_output_state_ !pa_sample_spec_equal(&o->sample_spec, &o->source->sample_spec)) { /* We were uncorked and the source was not playing anything -- let's try * to update the sample rate to avoid resampling */ - pa_source_update_rate(o->source, o->sample_spec.rate, pa_source_output_is_passthrough(o)); + pa_source_reconfigure(o->source, &o->sample_spec, pa_source_output_is_passthrough(o)); } pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) == 0); @@ -1523,7 +1523,7 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, bool save SOURCE_OUTPUT_MOVE_FINISH hook */ pa_log_info("Trying to change sample rate"); - if (pa_source_update_rate(dest, o->sample_spec.rate, pa_source_output_is_passthrough(o)) >= 0) + if (pa_source_reconfigure(dest, &o->sample_spec, pa_source_output_is_passthrough(o)) >= 0) pa_log_info("Rate changed to %u Hz", dest->sample_spec.rate); } diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index e0d32af4e..d579c3574 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -150,7 +150,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; + s->reconfigure = NULL; } /* Called from main context */ @@ -981,19 +981,21 @@ void pa_source_post_direct(pa_source*s, pa_source_output *o, const pa_memchunk * } /* Called from main thread */ -int pa_source_update_rate(pa_source *s, uint32_t rate, bool passthrough) { +int pa_source_reconfigure(pa_source *s, pa_sample_spec *spec, bool passthrough) { int ret; - uint32_t desired_rate; + pa_sample_spec desired_spec; uint32_t default_rate = s->default_sample_rate; uint32_t alternate_rate = s->alternate_sample_rate; bool default_rate_is_usable = false; bool alternate_rate_is_usable = false; bool avoid_resampling = s->core->avoid_resampling; - if (rate == s->sample_spec.rate) + /* We currently only try to reconfigure the sample rate */ + + if (pa_sample_spec_equal(spec, &s->sample_spec)) return 0; - if (!s->update_rate && !s->monitor_of) + if (!s->reconfigure && !s->monitor_of) return -1; if (PA_UNLIKELY(default_rate == alternate_rate && !passthrough && !avoid_resampling)) { @@ -1014,39 +1016,41 @@ int pa_source_update_rate(pa_source *s, uint32_t rate, bool passthrough) { } } - if (PA_UNLIKELY(!pa_sample_rate_valid(rate))) + if (PA_UNLIKELY(!pa_sample_spec_valid(spec))) return -1; + desired_spec = s->sample_spec; + if (passthrough) { /* We have to try to use the source output rate */ - desired_rate = rate; + desired_spec.rate = spec->rate; - } else if (avoid_resampling && (rate >= default_rate || rate >= alternate_rate)) { + } else if (avoid_resampling && (spec->rate >= default_rate || spec->rate >= alternate_rate)) { /* We just try to set the source output's sample rate if it's not too low */ - desired_rate = rate; + desired_spec.rate = spec->rate; - } else if (default_rate == rate || alternate_rate == rate) { + } else if (default_rate == spec->rate || alternate_rate == spec->rate) { /* We can directly try to use this rate */ - desired_rate = rate; + desired_spec.rate = spec->rate; } else { /* See if we can pick a rate that results in less resampling effort */ - if (default_rate % 11025 == 0 && rate % 11025 == 0) + if (default_rate % 11025 == 0 && spec->rate % 11025 == 0) default_rate_is_usable = true; - if (default_rate % 4000 == 0 && rate % 4000 == 0) + if (default_rate % 4000 == 0 && spec->rate % 4000 == 0) default_rate_is_usable = true; - if (alternate_rate && alternate_rate % 11025 == 0 && rate % 11025 == 0) + if (alternate_rate && alternate_rate % 11025 == 0 && spec->rate % 11025 == 0) alternate_rate_is_usable = true; - if (alternate_rate && alternate_rate % 4000 == 0 && rate % 4000 == 0) + if (alternate_rate && alternate_rate % 4000 == 0 && spec->rate % 4000 == 0) alternate_rate_is_usable = true; if (alternate_rate_is_usable && !default_rate_is_usable) - desired_rate = alternate_rate; + desired_spec.rate = alternate_rate; else - desired_rate = default_rate; + desired_spec.rate = default_rate; } - if (desired_rate == s->sample_spec.rate) + if (pa_sample_spec_equal(&desired_spec, &s->sample_spec) && passthrough == pa_source_is_passthrough(s)) return -1; if (!passthrough && pa_source_used_by(s) > 0) @@ -1055,8 +1059,8 @@ int pa_source_update_rate(pa_source *s, uint32_t rate, bool passthrough) { pa_log_debug("Suspending source %s due to changing the sample rate.", s->name); pa_source_suspend(s, true, PA_SUSPEND_INTERNAL); - if (s->update_rate) - ret = s->update_rate(s, desired_rate); + if (s->reconfigure) + ret = s->reconfigure(s, &desired_spec, passthrough); else { /* This is a monitor source. */ @@ -1064,23 +1068,23 @@ int pa_source_update_rate(pa_source *s, uint32_t rate, bool passthrough) { * have no idea whether the behaviour with passthrough streams is * sensible. */ if (!passthrough) { - uint32_t old_rate = s->sample_spec.rate; + pa_sample_spec old_spec = s->sample_spec; - s->sample_spec.rate = desired_rate; - ret = pa_sink_update_rate(s->monitor_of, desired_rate, false); + s->sample_spec = desired_spec; + ret = pa_sink_reconfigure(s->monitor_of, &desired_spec, false); if (ret < 0) { /* Changing the sink rate failed, roll back the old rate for * the monitor source. Why did we set the source rate before - * calling pa_sink_update_rate(), you may ask. The reason is - * that pa_sink_update_rate() tries to update the monitor + * calling pa_sink_reconfigure(), you may ask. The reason is + * that pa_sink_reconfigure() tries to update the monitor * source rate, but we are already in the process of updating * the monitor source rate, so there's a risk of entering an * infinite loop. Setting the source rate before calling - * pa_sink_update_rate() makes the rate == s->sample_spec.rate + * pa_sink_reconfigure() makes the rate == s->sample_spec.rate * check in the beginning of this function return early, so we * avoid looping. */ - s->sample_spec.rate = old_rate; + s->sample_spec = old_spec; } } else ret = -1; diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h index 1e33cde2d..7fb4a79ea 100644 --- a/src/pulsecore/source.h +++ b/src/pulsecore/source.h @@ -202,9 +202,9 @@ 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 + /* Called whenever device parameters need to be changed. Called from * main thread. */ - int (*update_rate)(pa_source *s, uint32_t rate); + int (*reconfigure)(pa_source *s, pa_sample_spec *spec, bool passthrough); /* Contains copies of the above data so that the real-time worker * thread can work without access locking */ @@ -396,7 +396,7 @@ bool pa_source_update_proplist(pa_source *s, pa_update_mode_t mode, pa_proplist int pa_source_set_port(pa_source *s, const char *name, bool save); void pa_source_set_mixer_dirty(pa_source *s, bool is_dirty); -int pa_source_update_rate(pa_source *s, uint32_t rate, bool passthrough); +int pa_source_reconfigure(pa_source *s, pa_sample_spec *spec, bool passthrough); 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 */ -- 2.13.6