vlc | branch: master | Thomas Guillem <[email protected]> | Tue Nov 3 15:13:34 2015 +0100| [004078f2fe55b616e029e5f64b23bb03c773f135] | committer: Thomas Guillem
soxr: use a fixed and a variable-rate SoxR instance The SoXR variable-rate engine is slower than the fixed one, so use it only when it's needed (when the input ratio is changing to catch up a delay). > http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=004078f2fe55b616e029e5f64b23bb03c773f135 --- modules/audio_filter/resampler/soxr.c | 183 ++++++++++++++++++++++++--------- 1 file changed, 135 insertions(+), 48 deletions(-) diff --git a/modules/audio_filter/resampler/soxr.c b/modules/audio_filter/resampler/soxr.c index 5c7a9a7..5bbd9a1 100644 --- a/modules/audio_filter/resampler/soxr.c +++ b/modules/audio_filter/resampler/soxr.c @@ -33,6 +33,7 @@ #include <vlc_filter.h> #include <vlc_plugin.h> +#include <assert.h> #include <math.h> #include <soxr.h> @@ -81,7 +82,11 @@ vlc_module_end () struct filter_sys_t { soxr_t soxr; + soxr_t vr_soxr; + soxr_t last_soxr; double f_fixed_ratio; + size_t i_last_olen; + mtime_t i_last_pts; }; static block_t *Resample( filter_t *, block_t * ); @@ -140,15 +145,13 @@ Open( vlc_object_t *p_obj, bool b_change_ratio ) const unsigned i_channels = aout_FormatNbChannels( &p_filter->fmt_in.audio ); const double f_ratio = p_filter->fmt_out.audio.i_rate / (double) p_filter->fmt_in.audio.i_rate; - /* XXX: Performances are worse with Variable-Rate */ - const unsigned long i_flags = b_change_ratio ? SOXR_VR : 0; - p_sys->f_fixed_ratio = b_change_ratio ? 0.0f : f_ratio; + p_sys->f_fixed_ratio = f_ratio; soxr_error_t error; /* IO spec */ soxr_io_spec_t io_spec = soxr_io_spec( i_itype, i_otype ); /* Quality spec */ - soxr_quality_spec_t q_spec = soxr_quality_spec( i_recipe, i_flags ); + soxr_quality_spec_t q_spec = soxr_quality_spec( i_recipe, 0 ); /* Create SoXR */ p_sys->soxr = soxr_create( 1, f_ratio, i_channels, &error, &io_spec, &q_spec, NULL ); @@ -158,8 +161,24 @@ Open( vlc_object_t *p_obj, bool b_change_ratio ) free( p_sys ); return VLC_EGENERIC; } + + /* Create a 'variable-rate' SoXR if needed: it is slower than the fixed + * one, but it will be only used when the input rate is changing (to catch + * up a delay). */ if( b_change_ratio ) - soxr_set_io_ratio( p_sys->soxr, 1 / f_ratio, 0 ); + { + soxr_quality_spec_t q_spec = soxr_quality_spec( SOXR_LQ, SOXR_VR ); + p_sys->vr_soxr = soxr_create( 1, f_ratio, i_channels, + &error, &io_spec, &q_spec, NULL ); + if( error ) + { + msg_Err( p_filter, "soxr_create failed: %s", soxr_strerror( error ) ); + soxr_delete( p_sys->soxr ); + free( p_sys ); + return VLC_EGENERIC; + } + soxr_set_io_ratio( p_sys->vr_soxr, 1 / f_ratio, 0 ); + } msg_Dbg( p_filter, "Using SoX Resampler with '%s' engine and '%s' quality " "to convert %4.4s/%dHz to %4.4s/%dHz.", @@ -204,76 +223,144 @@ Close( vlc_object_t *p_obj ) filter_sys_t *p_sys = p_filter->p_sys; soxr_delete( p_sys->soxr ); + if( p_sys->vr_soxr ) + soxr_delete( p_sys->vr_soxr ); free( p_sys ); } -static size_t -SoXR_GetOutLen( size_t i_ilen, double f_ratio ) -{ - /* Processed output len might be a little bigger than expected. Indeed SoXR - * can hold a few samples to meet the need of the resampling filter. */ - return lrint( ( i_ilen + 2 ) * f_ratio * 11. / 10. ); -} - static block_t * -Resample( filter_t *p_filter, block_t *p_in ) +SoXR_Resample( filter_t *p_filter, soxr_t soxr, block_t *p_in, size_t i_olen ) { filter_sys_t *p_sys = p_filter->p_sys; - + size_t i_idone, i_odone; const size_t i_oframesize = p_filter->fmt_out.audio.i_bytes_per_frame; - const size_t i_ilen = p_in->i_nb_samples; - size_t i_olen, i_idone, i_odone; - - if( p_sys->f_fixed_ratio == 0.0f ) - { - /* "audio resampler" with variable ratio */ - - const double f_ratio = p_filter->fmt_out.audio.i_rate - / (double) p_filter->fmt_in.audio.i_rate; - if( f_ratio == 1.0f ) - return p_in; - - i_olen = SoXR_GetOutLen( i_ilen, f_ratio ); - - soxr_set_io_ratio( p_sys->soxr, 1 / f_ratio, i_olen ); - } - else - i_olen = SoXR_GetOutLen( i_ilen, p_sys->f_fixed_ratio ); + const size_t i_ilen = p_in ? p_in->i_nb_samples : 0; - /* Use input buffer as output if there is enough room */ block_t *p_out = i_ilen >= i_olen ? p_in : block_Alloc( i_olen * i_oframesize ); - if( unlikely( p_out == NULL ) ) - goto error; - /* Process SoXR */ - soxr_error_t error = soxr_process( p_sys->soxr, - p_in->p_buffer, i_ilen, &i_idone, - p_out->p_buffer, i_olen, &i_odone ); + soxr_error_t error = soxr_process( soxr, p_in ? p_in->p_buffer : NULL, + i_ilen, &i_idone, p_out->p_buffer, + i_olen, &i_odone ); if( error ) { msg_Err( p_filter, "soxr_process failed: %s", soxr_strerror( error ) ); + block_Release( p_out ); goto error; } - if( unlikely( i_idone < i_ilen ) ) msg_Err( p_filter, "lost %zd of %zd input frames", i_ilen - i_idone, i_idone); p_out->i_buffer = i_odone * i_oframesize; p_out->i_nb_samples = i_odone; - p_out->i_pts = p_in->i_pts; p_out->i_length = i_odone * CLOCK_FREQ / p_filter->fmt_out.audio.i_rate; - if( p_out != p_in ) + if( p_in ) + { + p_sys->i_last_olen = i_olen; + p_sys->last_soxr = soxr; + } + else + { + soxr_clear( soxr ); + p_sys->i_last_olen = 0; + p_sys->last_soxr = NULL; + } + +error: + if( p_in && p_out != p_in ) block_Release( p_in ); + return p_out; +} -error: +static size_t +SoXR_GetOutLen( size_t i_ilen, double f_ratio ) +{ + /* Processed output len might be a little bigger than expected. Indeed SoXR + * can hold a few samples to meet the need of the resampling filter. */ + return lrint( ( i_ilen + 2 ) * f_ratio * 11. / 10. ); +} - if( p_out && p_out != p_in ) - block_Release( p_out ); - block_Release( p_in ); - return NULL; +static block_t * +Resample( filter_t *p_filter, block_t *p_in ) +{ + filter_sys_t *p_sys = p_filter->p_sys; + const mtime_t i_pts = p_in->i_pts; + + if( p_sys->vr_soxr ) + { + /* "audio resampler" with variable ratio: use the fixed resampler when + * the ratio is the same than the fixed one, otherwise use the variable + * resampler. */ + + soxr_t soxr; + block_t *p_flushed_out = NULL, *p_out = NULL; + const double f_ratio = p_filter->fmt_out.audio.i_rate + / (double) p_filter->fmt_in.audio.i_rate; + const size_t i_olen = SoXR_GetOutLen( p_in->i_nb_samples, f_ratio ); + + if( f_ratio != p_sys->f_fixed_ratio ) + { + /* using variable resampler */ + soxr_set_io_ratio( p_sys->vr_soxr, 1 / f_ratio, i_olen ); + soxr = p_sys->vr_soxr; + } + else if( f_ratio == 1.0f ) + { + /* not using any resampler */ + soxr = NULL; + p_out = p_in; + } + else + { + /* using fixed resampler */ + soxr = p_sys->soxr; + } + + /* If the new soxr is different than the last one, flush it */ + if( p_sys->last_soxr && soxr != p_sys->last_soxr && p_sys->i_last_olen ) + { + p_flushed_out = SoXR_Resample( p_filter, p_sys->last_soxr, + NULL, p_sys->i_last_olen ); + if( soxr ) + msg_Dbg( p_filter, "Using '%s' engine", soxr_engine( soxr ) ); + } + + if( soxr ) + { + assert( !p_out ); + p_out = SoXR_Resample( p_filter, soxr, p_in, i_olen ); + if( !p_out ) + return NULL; + } + + if( p_flushed_out ) + { + /* Prepend the flushed output data to p_out */ + const unsigned i_nb_samples = p_flushed_out->i_nb_samples + + p_out->i_nb_samples; + + block_ChainAppend( &p_flushed_out, p_out ); + p_out = block_ChainGather( p_flushed_out ); + if( !p_out ) + return NULL; + p_out->i_nb_samples = i_nb_samples; + } + p_out->i_pts = i_pts; + return p_out; + } + else + { + /* "audio converter" with fixed ratio */ + + const size_t i_olen = SoXR_GetOutLen( p_in->i_nb_samples, + p_sys->f_fixed_ratio ); + block_t *p_out = SoXR_Resample( p_filter, p_sys->soxr, p_in, i_olen ); + if( p_out ) + p_out->i_pts = i_pts; + return p_out; + } } _______________________________________________ vlc-commits mailing list [email protected] https://mailman.videolan.org/listinfo/vlc-commits
