pa_cvolume hardware_volume;
+ pa_sample_spec verified_sample_spec;
pa_sample_format_t *supported_formats;
unsigned int *supported_rates;
+ struct {
+ size_t fragment_size;
+ size_t nfrags;
+ size_t tsched_size;
+ size_t tsched_watermark;
+ size_t rewind_safeguard;
+ } initial_info;
size_t
frame_size,
fragment_size,
hwbuf_size,
+ tsched_size,
tsched_watermark,
tsched_watermark_ref,
hwbuf_unused,
(double) u->tsched_watermark_usec / PA_USEC_PER_MSEC);
}
+/* Called from IO Context on unsuspend */
+static void update_size(struct userdata *u, pa_sample_spec *ss) {
+ pa_assert(u);
+ pa_assert(ss);
+
+ u->frame_size = pa_frame_size(ss);
+ u->frames_per_block = pa_mempool_block_size_max(u->core->mempool) / u->frame_size;
+
+ /* use initial values including module arguments */
+ u->fragment_size = u->initial_info.fragment_size;
+ u->hwbuf_size = u->initial_info.nfrags * u->fragment_size;
+ u->tsched_size = u->initial_info.tsched_size;
+ u->tsched_watermark = u->initial_info.tsched_watermark;
+ u->rewind_safeguard = u->initial_info.rewind_safeguard;
+
+ u->tsched_watermark_ref = u->tsched_watermark;
+
+ pa_log_info("Updated frame_size %zu, frames_per_block %lu, fragment_size %zu, hwbuf_size %zu, tsched(size %zu, watermark %zu), rewind_safeguard %zu",
+ u->frame_size, (unsigned long) u->frames_per_block, u->fragment_size, u->hwbuf_size, u->tsched_size, u->tsched_watermark, u->rewind_safeguard);
+}
+
/* Called from IO context */
static int unsuspend(struct userdata *u) {
pa_sample_spec ss;
int err;
bool b, d;
- snd_pcm_uframes_t period_size, buffer_size;
+ snd_pcm_uframes_t period_frames, buffer_frames;
+ snd_pcm_uframes_t tsched_frames = 0;
char *device_name = NULL;
+ bool frame_size_changed = false;
pa_assert(u);
pa_assert(!u->pcm_handle);
goto fail;
}
+ if (pa_frame_size(&u->sink->sample_spec) != u->frame_size) {
+ update_size(u, &u->sink->sample_spec);
+ tsched_frames = u->tsched_size / u->frame_size;
+ frame_size_changed = true;
+ }
+
ss = u->sink->sample_spec;
- period_size = u->fragment_size / u->frame_size;
- buffer_size = u->hwbuf_size / u->frame_size;
+ period_frames = u->fragment_size / u->frame_size;
+ buffer_frames = u->hwbuf_size / u->frame_size;
b = u->use_mmap;
d = u->use_tsched;
- if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &period_size, &buffer_size, 0, &b, &d, true)) < 0) {
+ if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &period_frames, &buffer_frames, tsched_frames, &b, &d, true)) < 0) {
pa_log("Failed to set hardware parameters: %s", pa_alsa_strerror(err));
goto fail;
}
goto fail;
}
- if (period_size*u->frame_size != u->fragment_size ||
- buffer_size*u->frame_size != u->hwbuf_size) {
- pa_log_warn("Resume failed, couldn't restore original fragment settings. (Old: %lu/%lu, New %lu/%lu)",
- (unsigned long) u->hwbuf_size, (unsigned long) u->fragment_size,
- (unsigned long) (buffer_size*u->frame_size), (unsigned long) (period_size*u->frame_size));
+ if (frame_size_changed) {
+ u->fragment_size = (size_t)(period_frames * u->frame_size);
+ u->hwbuf_size = (size_t)(buffer_frames * u->frame_size);
+ pa_proplist_setf(u->sink->proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%zu", u->hwbuf_size);
+ pa_proplist_setf(u->sink->proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%zu", u->fragment_size);
+
+ } else if (period_frames * u->frame_size != u->fragment_size ||
+ buffer_frames * u->frame_size != u->hwbuf_size) {
+ pa_log_warn("Resume failed, couldn't restore original fragment settings. (Old: %zu/%zu, New %lu/%lu)",
+ u->hwbuf_size, u->fragment_size,
+ (unsigned long) buffer_frames * u->frame_size, (unsigned long) period_frames * u->frame_size);
goto fail;
}
return true;
}
-static int sink_reconfigure_cb(pa_sink *s, pa_sample_spec *spec, bool passthrough) {
+static void 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 */
+ bool format_supported = false;
+ bool rate_supported = false;
pa_assert(u);
- for (i = 0; u->supported_rates[i]; i++) {
- if (u->supported_rates[i] == spec->rate) {
- supported = true;
+ for (i = 0; u->supported_formats[i] != PA_SAMPLE_MAX; i++) {
+ if (u->supported_formats[i] == spec->format) {
+ pa_sink_set_sample_format(u->sink, spec->format);
+ format_supported = true;
break;
}
}
- if (!supported) {
- pa_log_info("Sink does not support sample rate of %d Hz", spec->rate);
- return -1;
+ if (!format_supported) {
+ pa_log_info("Sink does not support sample format of %s, set it to a verified value",
+ pa_sample_format_to_string(spec->format));
+ pa_sink_set_sample_format(u->sink, u->verified_sample_spec.format);
}
- if (!PA_SINK_IS_OPENED(s->state)) {
- 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;
+ for (i = 0; u->supported_rates[i]; i++) {
+ if (u->supported_rates[i] == spec->rate) {
+ pa_sink_set_sample_rate(u->sink, spec->rate);
+ rate_supported = true;
+ break;
+ }
}
- /* Passthrough status change is handled during unsuspend */
+ if (!rate_supported) {
+ pa_log_info("Sink does not support sample rate of %u, set it to a verified value", spec->rate);
+ pa_sink_set_sample_rate(u->sink, u->verified_sample_spec.rate);
+ }
- return -1;
+ /* Passthrough status change is handled during unsuspend */
}
static int process_rewind(struct userdata *u) {
u->module = m;
u->use_mmap = use_mmap;
u->use_tsched = use_tsched;
+ u->tsched_size = tsched_size;
+ u->initial_info.nfrags = (size_t) nfrags;
+ u->initial_info.fragment_size = (size_t) frag_size;
+ u->initial_info.tsched_size = (size_t) tsched_size;
+ u->initial_info.tsched_watermark = (size_t) tsched_watermark;
+ u->initial_info.rewind_safeguard = (size_t) rewind_safeguard;
u->deferred_volume = deferred_volume;
u->fixed_latency_range = fixed_latency_range;
u->first = true;
if ((is_iec958(u) || is_hdmi(u)) && ss.channels == 2)
set_formats = true;
+ u->verified_sample_spec = ss;
+
u->supported_formats = pa_alsa_get_supported_formats(u->pcm_handle, ss.format);
if (!u->supported_formats) {
pa_log_error("Failed to find any supported sample formats.");
pa_cvolume hardware_volume;
+ pa_sample_spec verified_sample_spec;
pa_sample_format_t *supported_formats;
unsigned int *supported_rates;
+ struct {
+ size_t fragment_size;
+ size_t nfrags;
+ size_t tsched_size;
+ size_t tsched_watermark;
+ } initial_info;
size_t
frame_size,
fragment_size,
hwbuf_size,
+ tsched_size,
tsched_watermark,
tsched_watermark_ref,
hwbuf_unused,
(double) u->tsched_watermark_usec / PA_USEC_PER_MSEC);
}
+/* Called from IO Context on unsuspend */
+static void update_size(struct userdata *u, pa_sample_spec *ss) {
+ pa_assert(u);
+ pa_assert(ss);
+
+ u->frame_size = pa_frame_size(ss);
+ u->frames_per_block = pa_mempool_block_size_max(u->core->mempool) / u->frame_size;
+
+ /* use initial values including module arguments */
+ u->fragment_size = u->initial_info.fragment_size;
+ u->hwbuf_size = u->initial_info.nfrags * u->fragment_size;
+ u->tsched_size = u->initial_info.tsched_size;
+ u->tsched_watermark = u->initial_info.tsched_watermark;
+
+ u->tsched_watermark_ref = u->tsched_watermark;
+
+ pa_log_info("Updated frame_size %zu, frames_per_block %lu, fragment_size %zu, hwbuf_size %zu, tsched(size %zu, watermark %zu)",
+ u->frame_size, (unsigned long) u->frames_per_block, u->fragment_size, u->hwbuf_size, u->tsched_size, u->tsched_watermark);
+}
+
/* Called from IO context */
static int unsuspend(struct userdata *u) {
pa_sample_spec ss;
int err;
bool b, d;
- snd_pcm_uframes_t period_size, buffer_size;
+ snd_pcm_uframes_t period_frames, buffer_frames;
+ snd_pcm_uframes_t tsched_frames = 0;
+ bool frame_size_changed = false;
pa_assert(u);
pa_assert(!u->pcm_handle);
goto fail;
}
+ if (pa_frame_size(&u->source->sample_spec) != u->frame_size) {
+ update_size(u, &u->source->sample_spec);
+ tsched_frames = u->tsched_size / u->frame_size;
+ frame_size_changed = true;
+ }
+
ss = u->source->sample_spec;
- period_size = u->fragment_size / u->frame_size;
- buffer_size = u->hwbuf_size / u->frame_size;
+ period_frames = u->fragment_size / u->frame_size;
+ buffer_frames = u->hwbuf_size / u->frame_size;
b = u->use_mmap;
d = u->use_tsched;
- if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &period_size, &buffer_size, 0, &b, &d, true)) < 0) {
+ if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &period_frames, &buffer_frames, tsched_frames, &b, &d, true)) < 0) {
pa_log("Failed to set hardware parameters: %s", pa_alsa_strerror(err));
goto fail;
}
goto fail;
}
- if (period_size*u->frame_size != u->fragment_size ||
- buffer_size*u->frame_size != u->hwbuf_size) {
- pa_log_warn("Resume failed, couldn't restore original fragment settings. (Old: %lu/%lu, New %lu/%lu)",
- (unsigned long) u->hwbuf_size, (unsigned long) u->fragment_size,
- (unsigned long) (buffer_size*u->frame_size), (unsigned long) (period_size*u->frame_size));
+ if (frame_size_changed) {
+ u->fragment_size = (size_t)(period_frames * u->frame_size);
+ u->hwbuf_size = (size_t)(buffer_frames * u->frame_size);
+ pa_proplist_setf(u->source->proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%zu", u->hwbuf_size);
+ pa_proplist_setf(u->source->proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%zu", u->fragment_size);
+
+ } else if (period_frames * u->frame_size != u->fragment_size ||
+ buffer_frames * u->frame_size != u->hwbuf_size) {
+ pa_log_warn("Resume failed, couldn't restore original fragment settings. (Old: %zu/%zu, New %lu/%lu)",
+ u->hwbuf_size, u->fragment_size,
+ (unsigned long) buffer_frames * u->frame_size, (unsigned long) period_frames * u->frame_size);
goto fail;
}
update_sw_params(u);
}
-static int source_reconfigure_cb(pa_source *s, pa_sample_spec *spec, bool passthrough) {
+static void 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 */
+ bool format_supported = false;
+ bool rate_supported = false;
pa_assert(u);
- for (i = 0; u->supported_rates[i]; i++) {
- if (u->supported_rates[i] == spec->rate) {
- supported = true;
+ for (i = 0; u->supported_formats[i] != PA_SAMPLE_MAX; i++) {
+ if (u->supported_formats[i] == spec->format) {
+ pa_source_set_sample_format(u->source, spec->format);
+ format_supported = true;
break;
}
}
- if (!supported) {
- pa_log_info("Source does not support sample rate of %d Hz", spec->rate);
- return -1;
+ if (!format_supported) {
+ pa_log_info("Source does not support sample format of %s, set it to a verified value",
+ pa_sample_format_to_string(spec->format));
+ pa_source_set_sample_format(u->source, u->verified_sample_spec.format);
}
- if (!PA_SOURCE_IS_OPENED(s->state)) {
- 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;
+ for (i = 0; u->supported_rates[i]; i++) {
+ if (u->supported_rates[i] == spec->rate) {
+ pa_source_set_sample_rate(u->source, spec->rate);
+ rate_supported = true;
+ break;
+ }
}
- return -1;
+ if (!rate_supported) {
+ pa_log_info("Source does not support sample rate of %u, set it to a verfied value", spec->rate);
+ pa_source_set_sample_rate(u->source, u->verified_sample_spec.rate);
+ }
}
static void thread_func(void *userdata) {
u->module = m;
u->use_mmap = use_mmap;
u->use_tsched = use_tsched;
+ u->tsched_size = tsched_size;
+ u->initial_info.nfrags = (size_t) nfrags;
+ u->initial_info.fragment_size = (size_t) frag_size;
+ u->initial_info.tsched_size = (size_t) tsched_size;
+ u->initial_info.tsched_watermark = (size_t) tsched_watermark;
u->deferred_volume = deferred_volume;
u->fixed_latency_range = fixed_latency_range;
u->first = true;
pa_log_info("Disabling latency range changes on overrun");
}
+ u->verified_sample_spec = ss;
+
u->supported_formats = pa_alsa_get_supported_formats(u->pcm_handle, ss.format);
if (!u->supported_formats) {
pa_log_error("Failed to find any supported sample formats.");
pa_sink_set_max_request_within_thread(s, nbytes);
}
-static int sink_reconfigure_cb(pa_sink *s, pa_sample_spec *spec, bool passthrough) {
+static void sink_reconfigure_cb(pa_sink *s, pa_sample_spec *spec, bool passthrough) {
/* We don't need to do anything */
s->sample_spec = *spec;
-
- return 0;
}
static bool sink_set_formats_cb(pa_sink *s, pa_idxset *formats) {
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
+ /* try to change sink format and 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_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);
+ pa_log_info("Trying to change sample spec");
+ pa_sink_reconfigure(data->sink, &data->sample_spec, pa_sink_input_new_data_is_passthrough(data));
}
if (pa_sink_input_new_data_is_passthrough(data) &&
if (i->state == PA_SINK_INPUT_CORKED && state == PA_SINK_INPUT_RUNNING && pa_sink_used_by(i->sink) == 0 &&
!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 */
+ * to update the sample format and rate to avoid resampling */
pa_sink_reconfigure(i->sink, &i->sample_spec, pa_sink_input_is_passthrough(i));
}
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.
+ /* try to change dest sink format and 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_reconfigure(dest, &i->sample_spec, pa_sink_input_is_passthrough(i)) >= 0)
- pa_log_info("Rate changed to %u Hz", dest->sample_spec.rate);
+ pa_log_info("Trying to change sample spec");
+ pa_sink_reconfigure(dest, &i->sample_spec, pa_sink_input_is_passthrough(i));
}
if (i->moving)
if (i->state == PA_SINK_INPUT_CORKED)
i->sink->n_corked++;
- pa_sink_input_update_rate(i);
+ pa_sink_input_update_resampler(i);
pa_sink_update_status(dest);
/* Called from main context */
/* Updates the sink input's resampler with whatever the current sink requires
- * -- useful when the underlying sink's rate might have changed */
-int pa_sink_input_update_rate(pa_sink_input *i) {
+ * -- useful when the underlying sink's sample spec might have changed */
+int pa_sink_input_update_resampler(pa_sink_input *i) {
pa_resampler *new_resampler;
char *memblockq_name;
void pa_sink_input_cork(pa_sink_input *i, bool b);
int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate);
-int pa_sink_input_update_rate(pa_sink_input *i);
+int pa_sink_input_update_resampler(pa_sink_input *i);
/* This returns the sink's fields converted into out sample type */
size_t pa_sink_input_get_max_rewind(pa_sink_input *i);
}
/* Called from main thread */
-int pa_sink_reconfigure(pa_sink *s, pa_sample_spec *spec, bool passthrough) {
- int ret = -1;
+void pa_sink_reconfigure(pa_sink *s, pa_sample_spec *spec, bool passthrough) {
pa_sample_spec desired_spec;
uint32_t default_rate = s->default_sample_rate;
uint32_t alternate_rate = s->alternate_sample_rate;
bool alternate_rate_is_usable = false;
bool avoid_resampling = s->avoid_resampling;
- /* We currently only try to reconfigure the sample rate */
-
if (pa_sample_spec_equal(spec, &s->sample_spec))
- return 0;
+ return;
if (!s->reconfigure)
- return -1;
+ return;
if (PA_UNLIKELY(default_rate == alternate_rate && !passthrough && !avoid_resampling)) {
pa_log_debug("Default and alternate sample rates are the same, so there is no point in switching.");
- return -1;
+ return;
}
if (PA_SINK_IS_RUNNING(s->state)) {
- pa_log_info("Cannot update rate, SINK_IS_RUNNING, will keep using %u Hz",
- s->sample_spec.rate);
- return -1;
+ pa_log_info("Cannot update sample spec, SINK_IS_RUNNING, will keep using %s and %u Hz",
+ pa_sample_format_to_string(s->sample_spec.format), s->sample_spec.rate);
+ return;
}
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 -1;
+ pa_log_info("Cannot update sample spec, monitor source is RUNNING");
+ return;
}
}
if (PA_UNLIKELY(!pa_sample_spec_valid(spec)))
- return -1;
+ return;
desired_spec = s->sample_spec;
if (passthrough) {
- /* We have to try to use the sink input rate */
+ /* We have to try to use the sink input format and rate */
+ desired_spec.format = spec->format;
desired_spec.rate = spec->rate;
- } else if (avoid_resampling && (spec->rate >= default_rate || spec->rate >= alternate_rate)) {
+ } else if (avoid_resampling) {
/* We just try to set the sink input's sample rate if it's not too low */
- desired_spec.rate = spec->rate;
+ if (spec->rate >= default_rate || spec->rate >= alternate_rate)
+ desired_spec.rate = spec->rate;
+ desired_spec.format = spec->format;
} else if (default_rate == spec->rate || alternate_rate == spec->rate) {
/* We can directly try to use this rate */
desired_spec.rate = spec->rate;
- } else {
+ }
+
+ if (desired_spec.rate != spec->rate) {
/* See if we can pick a rate that results in less resampling effort */
if (default_rate % 11025 == 0 && spec->rate % 11025 == 0)
default_rate_is_usable = true;
}
if (pa_sample_spec_equal(&desired_spec, &s->sample_spec) && passthrough == pa_sink_is_passthrough(s))
- return -1;
+ return;
if (!passthrough && pa_sink_used_by(s) > 0)
- return -1;
+ return;
- pa_log_debug("Suspending sink %s due to changing format, desired rate = %u", s->name, desired_spec.rate);
+ pa_log_debug("Suspending sink %s due to changing format, desired format = %s rate = %u",
+ s->name, pa_sample_format_to_string(desired_spec.format), desired_spec.rate);
pa_sink_suspend(s, true, PA_SUSPEND_INTERNAL);
- if (s->reconfigure(s, &desired_spec, passthrough) >= 0) {
- /* update monitor source as well */
- if (s->monitor_source && !passthrough)
- pa_source_reconfigure(s->monitor_source, &desired_spec, false);
- pa_log_info("Changed format successfully");
+ s->reconfigure(s, &desired_spec, passthrough);
- PA_IDXSET_FOREACH(i, s->inputs, idx) {
- if (i->state == PA_SINK_INPUT_CORKED)
- pa_sink_input_update_rate(i);
- }
+ /* update monitor source as well */
+ if (s->monitor_source && !passthrough)
+ pa_source_reconfigure(s->monitor_source, &s->sample_spec, false);
+ pa_log_info("Reconfigured successfully");
- ret = 0;
+ PA_IDXSET_FOREACH(i, s->inputs, idx) {
+ if (i->state == PA_SINK_INPUT_CORKED)
+ pa_sink_input_update_resampler(i);
}
pa_sink_suspend(s, false, PA_SUSPEND_INTERNAL);
-
- return ret;
}
/* Called from main thread */
return out_formats;
}
+/* Called from the main thread */
+void pa_sink_set_sample_format(pa_sink *s, pa_sample_format_t format) {
+ pa_sample_format_t old_format;
+
+ pa_assert(s);
+ pa_assert(pa_sample_format_valid(format));
+
+ old_format = s->sample_spec.format;
+ if (old_format == format)
+ return;
+
+ pa_log_info("%s: format: %s -> %s",
+ s->name, pa_sample_format_to_string(old_format), pa_sample_format_to_string(format));
+
+ s->sample_spec.format = format;
+
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+}
+
+/* Called from the main thread */
+void pa_sink_set_sample_rate(pa_sink *s, uint32_t rate) {
+ uint32_t old_rate;
+
+ pa_assert(s);
+ pa_assert(pa_sample_rate_valid(rate));
+
+ old_rate = s->sample_spec.rate;
+ if (old_rate == rate)
+ return;
+
+ pa_log_info("%s: rate: %u -> %u", s->name, old_rate, rate);
+
+ s->sample_spec.rate = rate;
+
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+}
+
/* Called from the main thread. */
void pa_sink_set_reference_volume_direct(pa_sink *s, const pa_cvolume *volume) {
pa_cvolume old_volume;
/* Called whenever device parameters need to be changed. Called from
* main thread. */
- int (*reconfigure)(pa_sink *s, pa_sample_spec *spec, bool passthrough);
+ void (*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 */
/**** May be called by everyone, from main context */
-int pa_sink_reconfigure(pa_sink *s, pa_sample_spec *spec, bool passthrough);
+void 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! */
bool pa_sink_check_format(pa_sink *s, pa_format_info *f);
pa_idxset* pa_sink_check_formats(pa_sink *s, pa_idxset *in_formats);
+void pa_sink_set_sample_format(pa_sink *s, pa_sample_format_t format);
+void pa_sink_set_sample_rate(pa_sink *s, uint32_t rate);
+
/*** To be called exclusively by the sink driver, from IO context */
void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result);
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
+ /* try to change source format and 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");
- 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);
+ pa_log_info("Trying to change sample spec");
+ pa_source_reconfigure(data->source, &data->sample_spec, pa_source_output_new_data_is_passthrough(data));
}
if (pa_source_output_new_data_is_passthrough(data) &&
if (o->state == PA_SOURCE_OUTPUT_CORKED && state == PA_SOURCE_OUTPUT_RUNNING && pa_source_used_by(o->source) == 0 &&
!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 */
+ * to update the sample format and rate to avoid resampling */
pa_source_reconfigure(o->source, &o->sample_spec, pa_source_output_is_passthrough(o));
}
if (!(o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) &&
!pa_sample_spec_equal(&o->sample_spec, &dest->sample_spec)) {
- /* try to change dest source rate if possible without glitches.
+ /* try to change dest source format and 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_reconfigure(dest, &o->sample_spec, pa_source_output_is_passthrough(o)) >= 0)
- pa_log_info("Rate changed to %u Hz", dest->sample_spec.rate);
+ pa_log_info("Trying to change sample spec");
+ pa_source_reconfigure(dest, &o->sample_spec, pa_source_output_is_passthrough(o));
}
if (o->moving)
if (o->state == PA_SOURCE_OUTPUT_CORKED)
o->source->n_corked++;
- pa_source_output_update_rate(o);
+ pa_source_output_update_resampler(o);
pa_source_update_status(dest);
/* Called from main context */
/* Updates the source output's resampler with whatever the current source
- * requires -- useful when the underlying source's rate might have changed */
-int pa_source_output_update_rate(pa_source_output *o) {
+ * requires -- useful when the underlying source's sample spec might have changed */
+int pa_source_output_update_resampler(pa_source_output *o) {
pa_resampler *new_resampler;
char *memblockq_name;
void pa_source_output_cork(pa_source_output *o, bool b);
int pa_source_output_set_rate(pa_source_output *o, uint32_t rate);
-int pa_source_output_update_rate(pa_source_output *o);
+int pa_source_output_update_resampler(pa_source_output *o);
size_t pa_source_output_get_max_rewind(pa_source_output *o);
}
/* Called from main thread */
-int pa_source_reconfigure(pa_source *s, pa_sample_spec *spec, bool passthrough) {
- int ret;
+void pa_source_reconfigure(pa_source *s, pa_sample_spec *spec, bool passthrough) {
+ uint32_t idx;
+ pa_source_output *o;
pa_sample_spec desired_spec;
uint32_t default_rate = s->default_sample_rate;
uint32_t alternate_rate = s->alternate_sample_rate;
bool alternate_rate_is_usable = false;
bool avoid_resampling = s->avoid_resampling;
- /* We currently only try to reconfigure the sample rate */
-
if (pa_sample_spec_equal(spec, &s->sample_spec))
- return 0;
+ return;
if (!s->reconfigure && !s->monitor_of)
- return -1;
+ return;
if (PA_UNLIKELY(default_rate == alternate_rate && !passthrough && !avoid_resampling)) {
pa_log_debug("Default and alternate sample rates are the same, so there is no point in switching.");
- return -1;
+ return;
}
if (PA_SOURCE_IS_RUNNING(s->state)) {
- pa_log_info("Cannot update rate, SOURCE_IS_RUNNING, will keep using %u Hz",
- s->sample_spec.rate);
- return -1;
+ pa_log_info("Cannot update sample spec, SOURCE_IS_RUNNING, will keep using %s and %u Hz",
+ pa_sample_format_to_string(s->sample_spec.format), s->sample_spec.rate);
+ return;
}
if (s->monitor_of) {
if (PA_SINK_IS_RUNNING(s->monitor_of->state)) {
- pa_log_info("Cannot update rate, this is a monitor source and the sink is running.");
- return -1;
+ pa_log_info("Cannot update sample spec, this is a monitor source and the sink is running.");
+ return;
}
}
if (PA_UNLIKELY(!pa_sample_spec_valid(spec)))
- return -1;
+ return;
desired_spec = s->sample_spec;
if (passthrough) {
- /* We have to try to use the source output rate */
+ /* We have to try to use the source output format and rate */
+ desired_spec.format = spec->format;
desired_spec.rate = spec->rate;
- } else if (avoid_resampling && (spec->rate >= default_rate || spec->rate >= alternate_rate)) {
+ } else if (avoid_resampling) {
/* We just try to set the source output's sample rate if it's not too low */
- desired_spec.rate = spec->rate;
+ if (spec->rate >= default_rate || spec->rate >= alternate_rate)
+ desired_spec.rate = spec->rate;
+ desired_spec.format = spec->format;
} else if (default_rate == spec->rate || alternate_rate == spec->rate) {
/* We can directly try to use this rate */
desired_spec.rate = spec->rate;
- } else {
+ }
+
+ if (desired_spec.rate != spec->rate) {
/* See if we can pick a rate that results in less resampling effort */
if (default_rate % 11025 == 0 && spec->rate % 11025 == 0)
default_rate_is_usable = true;
}
if (pa_sample_spec_equal(&desired_spec, &s->sample_spec) && passthrough == pa_source_is_passthrough(s))
- return -1;
+ return;
if (!passthrough && pa_source_used_by(s) > 0)
- return -1;
+ return;
- pa_log_debug("Suspending source %s due to changing the sample rate to %u", s->name, desired_spec.rate);
+ pa_log_debug("Suspending source %s due to changing format, desired format = %s rate = %u",
+ s->name, pa_sample_format_to_string(desired_spec.format), desired_spec.rate);
pa_source_suspend(s, true, PA_SUSPEND_INTERNAL);
if (s->reconfigure)
- ret = s->reconfigure(s, &desired_spec, passthrough);
+ s->reconfigure(s, &desired_spec, passthrough);
else {
/* This is a monitor source. */
* have no idea whether the behaviour with passthrough streams is
* sensible. */
if (!passthrough) {
- pa_sample_spec old_spec = s->sample_spec;
-
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_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_reconfigure() makes the rate == s->sample_spec.rate
- * check in the beginning of this function return early, so we
- * avoid looping. */
- s->sample_spec = old_spec;
- }
+ pa_sink_reconfigure(s->monitor_of, &desired_spec, false);
+ s->sample_spec = s->monitor_of->sample_spec;
} else
- ret = -1;
+ goto unsuspend;
}
- if (ret >= 0) {
- uint32_t idx;
- pa_source_output *o;
-
- PA_IDXSET_FOREACH(o, s->outputs, idx) {
- if (o->state == PA_SOURCE_OUTPUT_CORKED)
- pa_source_output_update_rate(o);
- }
-
- pa_log_info("Changed sampling rate successfully");
+ PA_IDXSET_FOREACH(o, s->outputs, idx) {
+ if (o->state == PA_SOURCE_OUTPUT_CORKED)
+ pa_source_output_update_resampler(o);
}
- pa_source_suspend(s, false, PA_SUSPEND_INTERNAL);
+ pa_log_info("Reconfigured successfully");
- return ret;
+unsuspend:
+ pa_source_suspend(s, false, PA_SUSPEND_INTERNAL);
}
/* Called from main thread */
return out_formats;
}
+/* Called from the main thread */
+void pa_source_set_sample_format(pa_source *s, pa_sample_format_t format) {
+ pa_sample_format_t old_format;
+
+ pa_assert(s);
+ pa_assert(pa_sample_format_valid(format));
+
+ old_format = s->sample_spec.format;
+ if (old_format == format)
+ return;
+
+ pa_log_info("%s: format: %s -> %s",
+ s->name, pa_sample_format_to_string(old_format), pa_sample_format_to_string(format));
+
+ s->sample_spec.format = format;
+
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+}
+
+/* Called from the main thread */
+void pa_source_set_sample_rate(pa_source *s, uint32_t rate) {
+ uint32_t old_rate;
+
+ pa_assert(s);
+ pa_assert(pa_sample_rate_valid(rate));
+
+ old_rate = s->sample_spec.rate;
+ if (old_rate == rate)
+ return;
+
+ pa_log_info("%s: rate: %u -> %u", s->name, old_rate, rate);
+
+ s->sample_spec.rate = rate;
+
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+}
+
/* Called from the main thread. */
void pa_source_set_reference_volume_direct(pa_source *s, const pa_cvolume *volume) {
pa_cvolume old_volume;
/* Called whenever device parameters need to be changed. Called from
* main thread. */
- int (*reconfigure)(pa_source *s, pa_sample_spec *spec, bool passthrough);
+ void (*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 */
int pa_source_set_port(pa_source *s, const char *name, bool save);
-int pa_source_reconfigure(pa_source *s, pa_sample_spec *spec, bool passthrough);
+void 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 */
bool pa_source_check_format(pa_source *s, pa_format_info *f);
pa_idxset* pa_source_check_formats(pa_source *s, pa_idxset *in_formats);
+void pa_source_set_sample_format(pa_source *s, pa_sample_format_t format);
+void pa_source_set_sample_rate(pa_source *s, uint32_t rate);
+
/*** To be called exclusively by the source driver, from IO context */
void pa_source_post(pa_source*s, const pa_memchunk *chunk);