diff options
author | Georg Chini <georg@chini.tk> | 2021-01-01 00:27:24 +0100 |
---|---|---|
committer | PulseAudio Marge Bot <pulseaudio-maintainers@lists.freedesktop.org> | 2021-11-03 18:37:31 +0000 |
commit | d55fde2fed663e46409dd2e3b4af279d97190f73 (patch) | |
tree | 39d7c910c5707c36e98f2e7d8c2a60af0afaeb2f | |
parent | a275a0b8111071ac1937f0c0f3c1703e3831beeb (diff) |
source-output: Fix rewinding
If the output implements a process_rewind() callback, the resampler delay is
not taken into account. This leads to glitches during volume changes when
source and source output rates differ.
This patch fixes the problem.
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/120>
-rw-r--r-- | src/pulsecore/source-output.c | 59 |
1 files changed, 47 insertions, 12 deletions
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index cf16c400b..2e2b7a274 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -29,6 +29,7 @@ #include <pulse/xmalloc.h> #include <pulse/util.h> #include <pulse/internal.h> +#include <pulse/timeval.h> #include <pulsecore/core-format.h> #include <pulsecore/mix.h> @@ -237,6 +238,7 @@ int pa_source_output_new( pa_channel_map volume_map; int r; char *pt; + size_t resampler_history; pa_assert(_o); pa_assert(core); @@ -499,6 +501,11 @@ int pa_source_output_new( 0, &o->source->silence); + resampler_history = (uint64_t) PA_RESAMPLER_MAX_DELAY_USEC * o->source->sample_spec.rate / PA_USEC_PER_SEC; + resampler_history *= pa_frame_size(&o->source->sample_spec); + + pa_memblockq_set_maxrewind(o->thread_info.delay_memblockq, resampler_history + pa_source_get_max_rewind(o->source)); + pa_assert_se(pa_idxset_put(core->source_outputs, o, &o->index) == 0); pa_assert_se(pa_idxset_put(o->source->outputs, pa_source_output_ref(o), NULL) == 0); @@ -865,21 +872,36 @@ void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes /* in so return; if (o->process_rewind) { - pa_assert(pa_memblockq_get_length(o->thread_info.delay_memblockq) == 0); + size_t source_output_nbytes; + size_t length; - if (o->thread_info.resampler) - nbytes = pa_resampler_result(o->thread_info.resampler, nbytes); + /* The length of the memblockq may be non-zero if pa_source_output_rewind() is called twice + * without pa_source_output_push() called in between. In that case, the resampler has already + * been reset and we can skip that part. */ + length = pa_memblockq_get_length(o->thread_info.delay_memblockq); - pa_log_debug("Have to rewind %lu bytes on implementor.", (unsigned long) nbytes); + pa_memblockq_rewind(o->thread_info.delay_memblockq, nbytes); - if (nbytes > 0) - o->process_rewind(o, nbytes); + source_output_nbytes = pa_resampler_result(o->thread_info.resampler, nbytes); - if (o->thread_info.resampler) - pa_resampler_rewind(o->thread_info.resampler, nbytes, NULL, 0); + pa_log_debug("Have to rewind %lu bytes on implementor.", (unsigned long) source_output_nbytes); - } else - pa_memblockq_seek(o->thread_info.delay_memblockq, - ((int64_t) nbytes), PA_SEEK_RELATIVE, true); + if (source_output_nbytes > 0) + o->process_rewind(o, source_output_nbytes); + + if (o->thread_info.resampler && length == 0) { + size_t resampler_bytes; + + /* Round down to full frames */ + resampler_bytes = (size_t) pa_resampler_get_delay(o->thread_info.resampler, false) * pa_frame_size(&o->source->sample_spec); + if (resampler_bytes > 0) + pa_memblockq_rewind(o->thread_info.delay_memblockq, resampler_bytes); + + pa_resampler_rewind(o->thread_info.resampler, source_output_nbytes, NULL, 0); + } + } + + pa_memblockq_seek(o->thread_info.delay_memblockq, - ((int64_t) nbytes), PA_SEEK_RELATIVE, true); } /* Called from thread context */ @@ -887,18 +909,25 @@ size_t pa_source_output_get_max_rewind(pa_source_output *o) { pa_source_output_assert_ref(o); pa_source_output_assert_io_context(o); - return o->thread_info.resampler ? pa_resampler_request(o->thread_info.resampler, o->source->thread_info.max_rewind) : o->source->thread_info.max_rewind; + return pa_resampler_result(o->thread_info.resampler, o->source->thread_info.max_rewind); } /* Called from thread context */ void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes /* in the source's sample spec */) { + size_t resampler_history; + pa_source_output_assert_ref(o); pa_source_output_assert_io_context(o); pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state)); pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec)); + resampler_history = (uint64_t) PA_RESAMPLER_MAX_DELAY_USEC * o->source->sample_spec.rate / PA_USEC_PER_SEC; + resampler_history *= pa_frame_size(&o->source->sample_spec); + + pa_memblockq_set_maxrewind(o->thread_info.delay_memblockq, resampler_history + nbytes); + if (o->update_max_rewind) - o->update_max_rewind(o, o->thread_info.resampler ? pa_resampler_result(o->thread_info.resampler, nbytes) : nbytes); + o->update_max_rewind(o, pa_resampler_result(o->thread_info.resampler, nbytes)); } /* Called from thread context */ @@ -1785,6 +1814,7 @@ finish: int pa_source_output_update_resampler(pa_source_output *o) { pa_resampler *new_resampler; char *memblockq_name; + size_t resampler_history; pa_source_output_assert_ref(o); pa_assert_ctl_context(); @@ -1842,6 +1872,11 @@ int pa_source_output_update_resampler(pa_source_output *o) { &o->source->silence); pa_xfree(memblockq_name); + resampler_history = (uint64_t) PA_RESAMPLER_MAX_DELAY_USEC * o->source->sample_spec.rate / PA_USEC_PER_SEC; + resampler_history *= pa_frame_size(&o->source->sample_spec); + + pa_memblockq_set_maxrewind(o->thread_info.delay_memblockq, resampler_history + pa_source_get_max_rewind(o->source)); + o->actual_resample_method = new_resampler ? pa_resampler_get_method(new_resampler) : PA_RESAMPLER_INVALID; pa_log_debug("Updated resampler for source output %d", o->index); |