From a103e82029d15dff4515803b9a3b1cc59fab991e Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 7 Oct 2011 18:12:32 -0500 Subject: [PATCH] alsa: reset watermark to initial values on resume Watermark level and latency values are not restored when resuming, the values used prior to suspending are reused. This leads to side effects when underruns happen and buffer sizes are updated, PulseAudio can never meet lower latency requirements. Solution: keep track of watermark and latency values on sink or source creation, and reapply them on resume to start with a clean slate. Signed-off-by: Pierre-Louis Bossart --- src/modules/alsa/alsa-sink.c | 63 ++++++++++++++++++++++++++++------------ src/modules/alsa/alsa-source.c | 66 +++++++++++++++++++++++++++++------------- 2 files changed, 90 insertions(+), 39 deletions(-) diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 80bd6ba..c868453 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -115,6 +115,7 @@ struct userdata { fragment_size, hwbuf_size, tsched_watermark, + tsched_watermark_ref, hwbuf_unused, min_sleep, min_wakeup, @@ -125,6 +126,7 @@ struct userdata { rewind_safeguard; pa_usec_t watermark_dec_not_before; + pa_usec_t min_latency_ref; pa_memchunk memchunk; @@ -984,6 +986,41 @@ static int update_sw_params(struct userdata *u) { return 0; } +/* Called from IO Context on unsuspend or from main thread when creating sink */ +static void reset_watermark(struct userdata *u, size_t tsched_watermark, pa_sample_spec *ss, + pa_bool_t in_thread) +{ + u->tsched_watermark = pa_usec_to_bytes_round_up(pa_bytes_to_usec_round_up(tsched_watermark, ss), + &u->sink->sample_spec); + + u->watermark_inc_step = pa_usec_to_bytes(TSCHED_WATERMARK_INC_STEP_USEC, &u->sink->sample_spec); + u->watermark_dec_step = pa_usec_to_bytes(TSCHED_WATERMARK_DEC_STEP_USEC, &u->sink->sample_spec); + + u->watermark_inc_threshold = pa_usec_to_bytes_round_up(TSCHED_WATERMARK_INC_THRESHOLD_USEC, &u->sink->sample_spec); + u->watermark_dec_threshold = pa_usec_to_bytes_round_up(TSCHED_WATERMARK_DEC_THRESHOLD_USEC, &u->sink->sample_spec); + + fix_min_sleep_wakeup(u); + fix_tsched_watermark(u); + + if (in_thread) + pa_sink_set_latency_range_within_thread(u->sink, + u->min_latency_ref, + pa_bytes_to_usec(u->hwbuf_size, ss)); + else { + pa_sink_set_latency_range(u->sink, + 0, + pa_bytes_to_usec(u->hwbuf_size, ss)); + + /* work-around assert in pa_sink_set_latency_within_thead, + keep track of min_latency and reuse it when + this routine is called from IO context */ + u->min_latency_ref = u->sink->thread_info.min_latency; + } + + pa_log_info("Time scheduling watermark is %0.2fms", + (double) pa_bytes_to_usec(u->tsched_watermark, ss) / PA_USEC_PER_MSEC); +} + /* Called from IO context */ static int unsuspend(struct userdata *u) { pa_sample_spec ss; @@ -1057,6 +1094,10 @@ static int unsuspend(struct userdata *u) { u->first = TRUE; u->since_start = 0; + /* reset the watermark to the value defined when sink was created */ + if (u->use_tsched) + reset_watermark(u, u->tsched_watermark_ref, &u->sink->sample_spec, TRUE); + pa_log_info("Resumed successfully..."); pa_xfree(device_name); @@ -1928,7 +1969,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca struct userdata *u = NULL; const char *dev_id = NULL; - pa_sample_spec ss, requested_ss; + pa_sample_spec ss; pa_channel_map map; uint32_t nfrags, frag_size, buffer_size, tsched_size, tsched_watermark, rewind_safeguard; snd_pcm_uframes_t period_frames, buffer_frames, tsched_frames; @@ -1947,7 +1988,6 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca goto fail; } - requested_ss = ss; frame_size = pa_frame_size(&ss); nfrags = m->core->default_n_fragments; @@ -2209,23 +2249,8 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca } if (u->use_tsched) { - u->tsched_watermark = pa_usec_to_bytes_round_up(pa_bytes_to_usec_round_up(tsched_watermark, &requested_ss), &u->sink->sample_spec); - - u->watermark_inc_step = pa_usec_to_bytes(TSCHED_WATERMARK_INC_STEP_USEC, &u->sink->sample_spec); - u->watermark_dec_step = pa_usec_to_bytes(TSCHED_WATERMARK_DEC_STEP_USEC, &u->sink->sample_spec); - - u->watermark_inc_threshold = pa_usec_to_bytes_round_up(TSCHED_WATERMARK_INC_THRESHOLD_USEC, &u->sink->sample_spec); - u->watermark_dec_threshold = pa_usec_to_bytes_round_up(TSCHED_WATERMARK_DEC_THRESHOLD_USEC, &u->sink->sample_spec); - - fix_min_sleep_wakeup(u); - fix_tsched_watermark(u); - - pa_sink_set_latency_range(u->sink, - 0, - pa_bytes_to_usec(u->hwbuf_size, &ss)); - - pa_log_info("Time scheduling watermark is %0.2fms", - (double) pa_bytes_to_usec(u->tsched_watermark, &ss) / PA_USEC_PER_MSEC); + u->tsched_watermark_ref = tsched_watermark; + reset_watermark(u, u->tsched_watermark_ref, &ss, FALSE); } else pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(u->hwbuf_size, &ss)); diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 4b3c8b7..d544828 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -103,6 +103,7 @@ struct userdata { fragment_size, hwbuf_size, tsched_watermark, + tsched_watermark_ref, hwbuf_unused, min_sleep, min_wakeup, @@ -112,6 +113,7 @@ struct userdata { watermark_dec_threshold; pa_usec_t watermark_dec_not_before; + pa_usec_t min_latency_ref; char *device_name; /* name of the PCM device */ char *control_device; /* name of the control device */ @@ -896,6 +898,41 @@ static int update_sw_params(struct userdata *u) { return 0; } +/* Called from IO Context on unsuspend or from main thread when creating source */ +static void reset_watermark(struct userdata *u, size_t tsched_watermark, pa_sample_spec *ss, + pa_bool_t in_thread) +{ + u->tsched_watermark = pa_usec_to_bytes_round_up(pa_bytes_to_usec_round_up(tsched_watermark, ss), + &u->source->sample_spec); + + u->watermark_inc_step = pa_usec_to_bytes(TSCHED_WATERMARK_INC_STEP_USEC, &u->source->sample_spec); + u->watermark_dec_step = pa_usec_to_bytes(TSCHED_WATERMARK_DEC_STEP_USEC, &u->source->sample_spec); + + u->watermark_inc_threshold = pa_usec_to_bytes_round_up(TSCHED_WATERMARK_INC_THRESHOLD_USEC, &u->source->sample_spec); + u->watermark_dec_threshold = pa_usec_to_bytes_round_up(TSCHED_WATERMARK_DEC_THRESHOLD_USEC, &u->source->sample_spec); + + fix_min_sleep_wakeup(u); + fix_tsched_watermark(u); + + if (in_thread) + pa_source_set_latency_range_within_thread(u->source, + u->min_latency_ref, + pa_bytes_to_usec(u->hwbuf_size, ss)); + else { + pa_source_set_latency_range(u->source, + 0, + pa_bytes_to_usec(u->hwbuf_size, ss)); + + /* work-around assert in pa_source_set_latency_within_thead, + keep track of min_latency and reuse it when + this routine is called from IO context */ + u->min_latency_ref = u->source->thread_info.min_latency; + } + + pa_log_info("Time scheduling watermark is %0.2fms", + (double) pa_bytes_to_usec(u->tsched_watermark, ss) / PA_USEC_PER_MSEC); +} + /* Called from IO context */ static int unsuspend(struct userdata *u) { pa_sample_spec ss; @@ -961,6 +998,10 @@ static int unsuspend(struct userdata *u) { u->first = TRUE; + /* reset the watermark to the value defined when source was created */ + if (u->use_tsched) + reset_watermark(u, u->tsched_watermark_ref, &u->source->sample_spec, TRUE); + pa_log_info("Resumed successfully..."); return 0; @@ -1627,7 +1668,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p struct userdata *u = NULL; const char *dev_id = NULL; - pa_sample_spec ss, requested_ss; + pa_sample_spec ss; pa_channel_map map; uint32_t nfrags, frag_size, buffer_size, tsched_size, tsched_watermark; snd_pcm_uframes_t period_frames, buffer_frames, tsched_frames; @@ -1646,7 +1687,6 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p goto fail; } - requested_ss = ss; frame_size = pa_frame_size(&ss); nfrags = m->core->default_n_fragments; @@ -1889,24 +1929,10 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p (double) pa_bytes_to_usec(u->hwbuf_size, &ss) / PA_USEC_PER_MSEC); if (u->use_tsched) { - u->tsched_watermark = pa_usec_to_bytes_round_up(pa_bytes_to_usec_round_up(tsched_watermark, &requested_ss), &u->source->sample_spec); - - u->watermark_inc_step = pa_usec_to_bytes(TSCHED_WATERMARK_INC_STEP_USEC, &u->source->sample_spec); - u->watermark_dec_step = pa_usec_to_bytes(TSCHED_WATERMARK_DEC_STEP_USEC, &u->source->sample_spec); - - u->watermark_inc_threshold = pa_usec_to_bytes_round_up(TSCHED_WATERMARK_INC_THRESHOLD_USEC, &u->source->sample_spec); - u->watermark_dec_threshold = pa_usec_to_bytes_round_up(TSCHED_WATERMARK_DEC_THRESHOLD_USEC, &u->source->sample_spec); - - fix_min_sleep_wakeup(u); - fix_tsched_watermark(u); - - pa_source_set_latency_range(u->source, - 0, - pa_bytes_to_usec(u->hwbuf_size, &ss)); - - pa_log_info("Time scheduling watermark is %0.2fms", - (double) pa_bytes_to_usec(u->tsched_watermark, &ss) / PA_USEC_PER_MSEC); - } else + u->tsched_watermark_ref = tsched_watermark; + reset_watermark(u, u->tsched_watermark_ref, &ss, FALSE); + } + else pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(u->hwbuf_size, &ss)); reserve_update(u); -- 2.7.4