diff --git a/doc/dox/config/pipewire-props.7.md b/doc/dox/config/pipewire-props.7.md index 8d1cbd70c..e7ee8f848 100644 --- a/doc/dox/config/pipewire-props.7.md +++ b/doc/dox/config/pipewire-props.7.md @@ -778,6 +778,12 @@ Setting this to 0 makes htimestamp never get disabled. Disable timer-based scheduling, and use IRQ for scheduling instead. The "Pro Audio" profile will usually enable this setting, if it is expected it works on the hardware. +@PAR@ node-prop api.alsa.dll-bandwidth-max # double +Sets the maximum bandwidth of the DLL (delay-locked loop) filter used to smooth out rate adjustments. +The default value may be too responsive in some scenarios. +For example, with UAC2 pitch control, the host reacts more slowly compared to local resampling, +so using a lower bandwidth helps avoid oscillations or instability. + @PAR@ node-prop api.alsa.auto-link = false # boolean Link follower PCM devices to the driver PCM device when using IRQ-based scheduling. The "Pro Audio" profile will usually enable this setting, if it is expected it works on the hardware. diff --git a/spa/plugins/alsa/alsa-pcm.c b/spa/plugins/alsa/alsa-pcm.c index 769e7443e..3e5e67b38 100644 --- a/spa/plugins/alsa/alsa-pcm.c +++ b/spa/plugins/alsa/alsa-pcm.c @@ -20,6 +20,7 @@ static struct spa_list cards = SPA_LIST_INIT(&cards); static struct spa_list states = SPA_LIST_INIT(&states); +#define SPA_ALSA_DLL_BW_MIN 0.001 static struct card *find_card(uint32_t index) { @@ -198,6 +199,9 @@ static int alsa_set_param(struct state *state, const char *k, const char *s) state->disable_batch = spa_atob(s); } else if (spa_streq(k, "api.alsa.disable-tsched")) { state->disable_tsched = spa_atob(s); + } else if (spa_streq(k, "api.alsa.dll-bandwidth-max")) { + state->dll_bw_max = SPA_CLAMPD(spa_strtod(s, NULL), + SPA_ALSA_DLL_BW_MIN, SPA_DLL_BW_MAX); } else if (spa_streq(k, "api.alsa.use-chmap")) { state->props.use_chmap = spa_atob(s); } else if (spa_streq(k, "api.alsa.multi-rate")) { @@ -2799,7 +2803,7 @@ static int update_time(struct state *state, uint64_t current_time, snd_pcm_sfram int32_t diff; if (SPA_UNLIKELY(state->dll.bw == 0.0)) { - spa_dll_set_bw(&state->dll, SPA_DLL_BW_MAX, state->threshold, state->rate); + spa_dll_set_bw(&state->dll, state->dll_bw_max, state->threshold, state->rate); state->next_time = current_time; state->base_time = current_time; } @@ -2862,7 +2866,7 @@ static int update_time(struct state *state, uint64_t current_time, snd_pcm_sfram err, state->max_error, state->max_resync, state->err_avg, state->err_var, bw); spa_dll_set_bw(&state->dll, - SPA_CLAMPD(bw, 0.001, SPA_DLL_BW_MAX), + SPA_CLAMPD(bw, SPA_ALSA_DLL_BW_MIN, state->dll_bw_max), state->threshold, state->rate); } diff --git a/spa/plugins/alsa/alsa-pcm.h b/spa/plugins/alsa/alsa-pcm.h index 6fb4a3403..0d23fa2f6 100644 --- a/spa/plugins/alsa/alsa-pcm.h +++ b/spa/plugins/alsa/alsa-pcm.h @@ -244,6 +244,7 @@ struct state { uint64_t underrun; struct spa_dll dll; + double dll_bw_max; double max_error; double max_resync; double err_avg, err_var, err_wdw;