mirror of
				https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
				synced 2025-11-03 09:01:50 -05:00 
			
		
		
		
	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>
This commit is contained in:
		
							parent
							
								
									a275a0b811
								
							
						
					
					
						commit
						d55fde2fed
					
				
					 1 changed files with 47 additions and 12 deletions
				
			
		| 
						 | 
					@ -29,6 +29,7 @@
 | 
				
			||||||
#include <pulse/xmalloc.h>
 | 
					#include <pulse/xmalloc.h>
 | 
				
			||||||
#include <pulse/util.h>
 | 
					#include <pulse/util.h>
 | 
				
			||||||
#include <pulse/internal.h>
 | 
					#include <pulse/internal.h>
 | 
				
			||||||
 | 
					#include <pulse/timeval.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <pulsecore/core-format.h>
 | 
					#include <pulsecore/core-format.h>
 | 
				
			||||||
#include <pulsecore/mix.h>
 | 
					#include <pulsecore/mix.h>
 | 
				
			||||||
| 
						 | 
					@ -237,6 +238,7 @@ int pa_source_output_new(
 | 
				
			||||||
    pa_channel_map volume_map;
 | 
					    pa_channel_map volume_map;
 | 
				
			||||||
    int r;
 | 
					    int r;
 | 
				
			||||||
    char *pt;
 | 
					    char *pt;
 | 
				
			||||||
 | 
					    size_t resampler_history;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pa_assert(_o);
 | 
					    pa_assert(_o);
 | 
				
			||||||
    pa_assert(core);
 | 
					    pa_assert(core);
 | 
				
			||||||
| 
						 | 
					@ -499,6 +501,11 @@ int pa_source_output_new(
 | 
				
			||||||
            0,
 | 
					            0,
 | 
				
			||||||
            &o->source->silence);
 | 
					            &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(core->source_outputs, o, &o->index) == 0);
 | 
				
			||||||
    pa_assert_se(pa_idxset_put(o->source->outputs, pa_source_output_ref(o), NULL) == 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;
 | 
					        return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (o->process_rewind) {
 | 
					    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)
 | 
					        /* The length of the memblockq may be non-zero if pa_source_output_rewind() is called twice
 | 
				
			||||||
            nbytes = pa_resampler_result(o->thread_info.resampler, nbytes);
 | 
					         * 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)
 | 
					        source_output_nbytes = pa_resampler_result(o->thread_info.resampler, nbytes);
 | 
				
			||||||
            o->process_rewind(o, nbytes);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (o->thread_info.resampler)
 | 
					        pa_log_debug("Have to rewind %lu bytes on implementor.", (unsigned long) source_output_nbytes);
 | 
				
			||||||
            pa_resampler_rewind(o->thread_info.resampler, nbytes, NULL, 0);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    } else
 | 
					        if (source_output_nbytes > 0)
 | 
				
			||||||
        pa_memblockq_seek(o->thread_info.delay_memblockq, - ((int64_t) nbytes), PA_SEEK_RELATIVE, true);
 | 
					            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 */
 | 
					/* 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_ref(o);
 | 
				
			||||||
    pa_source_output_assert_io_context(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 */
 | 
					/* Called from thread context */
 | 
				
			||||||
void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes  /* in the source's sample spec */) {
 | 
					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_ref(o);
 | 
				
			||||||
    pa_source_output_assert_io_context(o);
 | 
					    pa_source_output_assert_io_context(o);
 | 
				
			||||||
    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state));
 | 
					    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state));
 | 
				
			||||||
    pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec));
 | 
					    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)
 | 
					    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 */
 | 
					/* Called from thread context */
 | 
				
			||||||
| 
						 | 
					@ -1785,6 +1814,7 @@ finish:
 | 
				
			||||||
int pa_source_output_update_resampler(pa_source_output *o) {
 | 
					int pa_source_output_update_resampler(pa_source_output *o) {
 | 
				
			||||||
    pa_resampler *new_resampler;
 | 
					    pa_resampler *new_resampler;
 | 
				
			||||||
    char *memblockq_name;
 | 
					    char *memblockq_name;
 | 
				
			||||||
 | 
					    size_t resampler_history;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pa_source_output_assert_ref(o);
 | 
					    pa_source_output_assert_ref(o);
 | 
				
			||||||
    pa_assert_ctl_context();
 | 
					    pa_assert_ctl_context();
 | 
				
			||||||
| 
						 | 
					@ -1842,6 +1872,11 @@ int pa_source_output_update_resampler(pa_source_output *o) {
 | 
				
			||||||
            &o->source->silence);
 | 
					            &o->source->silence);
 | 
				
			||||||
    pa_xfree(memblockq_name);
 | 
					    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;
 | 
					    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);
 | 
					    pa_log_debug("Updated resampler for source output %d", o->index);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue