From 0db2171cd91c14dcc17ec15b2da0f93efae5052c Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 4 Oct 2024 10:47:47 +0200 Subject: [PATCH] alsa: dynamically adjust the DLL bandwidth Keep a running average and variance of the error. Use this to periodically update the DLL bandwidth. When the variance gets smaller, we update the DLL more slowly to stay closer to the ideal rate. This seems to improve the rate stability. --- spa/plugins/alsa/alsa-pcm.c | 21 ++++++++++++++++----- spa/plugins/alsa/alsa-pcm.h | 1 + 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/spa/plugins/alsa/alsa-pcm.c b/spa/plugins/alsa/alsa-pcm.c index ad5b54bb6..fc4f22550 100644 --- a/spa/plugins/alsa/alsa-pcm.c +++ b/spa/plugins/alsa/alsa-pcm.c @@ -2746,7 +2746,7 @@ static int get_status(struct state *state, uint64_t current_time, snd_pcm_uframe static int update_time(struct state *state, uint64_t current_time, snd_pcm_sframes_t delay, snd_pcm_sframes_t target, bool follower) { - double err, corr; + double err, corr, avg; int32_t diff; if (state->disable_tsched && !follower) { @@ -2784,10 +2784,16 @@ static int update_time(struct state *state, uint64_t current_time, snd_pcm_sfram err = -state->max_error; } - if (!follower || state->matching) + if (!follower || state->matching) { corr = spa_dll_update(&state->dll, err); - else + + avg = (state->err_avg * state->err_wdw + (err - state->err_avg)) / (state->err_wdw + 1.0); + state->err_var = (state->err_var * state->err_wdw + + (err - state->err_avg) * (err - avg)) / (state->err_wdw + 1.0); + state->err_avg = avg; + } else { corr = 1.0; + } if (diff < 0) state->next_time += (uint64_t)(diff / corr * 1e9 / state->rate); @@ -2796,10 +2802,14 @@ static int update_time(struct state *state, uint64_t current_time, snd_pcm_sfram state->base_time = state->next_time; spa_log_debug(state->log, "%s: follower:%d match:%d rate:%f " - "bw:%f thr:%u del:%ld target:%ld err:%f max:%f", + "bw:%f thr:%u del:%ld target:%ld err:%f max:%f var:%f:%f:%f", state->name, follower, state->matching, corr, state->dll.bw, state->threshold, delay, target, - err, state->max_error); + err, state->max_error, state->err_avg, state->err_var, state->err_wdw); + + spa_dll_set_bw(&state->dll, + SPA_CLAMPD(sqrt(state->err_var)/1000.0, 0.001, SPA_DLL_BW_MAX), + state->threshold, state->rate); } if (state->rate_match) { @@ -2904,6 +2914,7 @@ static inline int check_position_config(struct state *state, bool starting) state->threshold = SPA_SCALE32_UP(state->driver_duration, state->rate, state->driver_rate.denom); state->max_error = SPA_MAX(256.0f, state->threshold / 2.0f); state->max_resync = SPA_MIN(state->threshold, state->max_error); + state->err_wdw = (double)state->driver_rate.denom/state->driver_duration; state->resample = !state->pitch_elem && (((uint32_t)state->rate != state->driver_rate.denom) || state->matching); state->alsa_sync = true; diff --git a/spa/plugins/alsa/alsa-pcm.h b/spa/plugins/alsa/alsa-pcm.h index ab4b47c93..2aa377f56 100644 --- a/spa/plugins/alsa/alsa-pcm.h +++ b/spa/plugins/alsa/alsa-pcm.h @@ -244,6 +244,7 @@ struct state { struct spa_dll dll; double max_error; double max_resync; + double err_avg, err_var, err_wdw; struct spa_latency_info latency[2]; struct spa_process_latency_info process_latency;