[vlc-devel] [PATCH] Add Scaletempo audio filter
Laurent Aimar
fenrir at via.ecp.fr
Wed Jun 18 23:26:54 CEST 2008
Hi,
On Mon, Jun 16, 2008, Rov Juvano wrote:
> Scaletempo maintains the audio pitch when playback rate != 1.0 (i.e.
> no chipmunk effect). This fixes the pitch scaling caused by using the
> resampler to handle playback rate.
> Ported from GStreamer. Inspired by SoundTouch library by Olli Parviainen.
> +++ b/modules/audio_filter/scaletempo.c
> @@ -0,0 +1,567 @@
> +/*****************************************************************************
> + * scaletempo.c: Scale audio tempo while maintaining pitch
> + *****************************************************************************
> + * Copyright © 2008 the VideoLAN team
> + * $Id$
> + *
> + * Authors: Rov Juvano <rovjuvano at users.sourceforge.net>
As asked by Remi, are you the only author of all the code of scaletempo.c ? (as
you have said it is "Ported from GStreamer"). If not you should add back the original
authors.
> +static int StrideCallback( vlc_object_t *p_this, char const *psz_cmd,
> + vlc_value_t oldval, vlc_value_t newval, void *p_data )
> +{
> + VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
> + aout_filter_sys_t *p = (aout_filter_sys_t *)p_data;
> + uint new_value = newval.i_int;
> + if( p->ms_stride != new_value ) {
> + p->ms_stride = new_value;
> + p->reinit_buffers = true;
> + }
> + return VLC_SUCCESS;
> +}
> +
> +static int OverlapCallback( vlc_object_t *p_this, char const *psz_cmd,
> + vlc_value_t oldval, vlc_value_t newval, void *p_data )
> +{
> + VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
> + aout_filter_sys_t *p = (aout_filter_sys_t *)p_data;
> + float new_value = newval.f_float;
> + if( p->percent_overlap != new_value ) {
> + p->percent_overlap = new_value;
> + p->reinit_buffers = true;
> + }
> + return VLC_SUCCESS;
> +}
> +
> +static int SearchCallback( vlc_object_t *p_this, char const *psz_cmd,
> + vlc_value_t oldval, vlc_value_t newval, void *p_data )
> +{
> + VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
> + aout_filter_sys_t *p = (aout_filter_sys_t *)p_data;
> + uint new_value = newval.i_int;
> + if( p->ms_search != new_value ) {
> + p->ms_search = new_value;
> + p->reinit_buffers = true;
> + }
> + return VLC_SUCCESS;
> +}
Those callbacks are not thread safe. They can be called while you are in
DoWork(). You need to add a proper locking scheme. Reading your code, it
seems you just need to lock inside these callbacks and around
if( p->reinit_buffers ) { reinit_buffers() }
in DoWork.
You probably want to check thoses value against sane bounds (here or in
reinit_buffers). What happens if ms_stride is 0 for example ?
> + if( p_filter->input.i_format != VLC_FOURCC('f','l','3','2') )
> + {
> + b_fit = false;
> + p_filter->input.i_format = p_filter->output.i_format = VLC_FOURCC('f','l','3','2');
> + msg_Warn( p_filter, "bad input or output format" );
> + }
> + if( ! AOUT_FMTS_SIMILAR( &p_filter->input, &p_filter->output ) )
> + {
> + b_fit = false;
> + memcpy( &p_filter->output, &p_filter->input, sizeof(audio_sample_format_t) );
> + msg_Warn( p_filter, "input and output formats are not similar" );
> + }
> +
> + if( ! b_fit )
> + {
> + return VLC_EGENERIC;
> + }
I am not sure you can change p_filter->input/p_filter->output if you
return VLC_EGENERIC.
> + p_sys = p_filter->p_sys = malloc( sizeof(aout_filter_sys_t) );
> + if( !p_sys )
> + {
> + msg_Err( p_this, "Out of memory" );
> + return VLC_ENOMEM;
> + }
No need to print a message when out of memory. (msg_Err needs memory too ;)
> + aout_instance_t *p_aout = (aout_instance_t *)p_filter->p_parent;
> + p_sys->ms_stride = var_CreateGetInteger( p_aout, "scaletempo-stride" );
> + p_sys->percent_overlap = var_CreateGetFloat( p_aout, "scaletempo-overlap" );
> + p_sys->ms_search = var_CreateGetInteger( p_aout, "scaletempo-search" );
I find it weird to create those variables in aout but I don't know if it is
the current policy.
> + size_t i_outsize = calculate_output_buffer_size ( p_filter, p_in_buf->i_nb_bytes );
> + if( i_outsize > p_out_buf->i_size ) {
> + p->p_buffers[ p->i_buf ] = realloc( p->p_buffers[ p->i_buf ], i_outsize );
> + if( ! p->p_buffers[ p->i_buf ] )
> + {
> + msg_Err( VLC_OBJECT(p_filter), "Out of memory" );
> + return;
> + }
No need for "out of mem" message.
You also have a memory leak when realloc fails (the old buffer is not
released).
> diff --git a/src/audio_output/input.c b/src/audio_output/input.c
> index 184e8f7..fa62463 100644
> --- a/src/audio_output/input.c
> +++ b/src/audio_output/input.c
> @@ -401,6 +401,18 @@ int aout_InputNew( aout_instance_t * p_aout, aout_input_t * p_input )
> }
> p_input->i_resampling_type = AOUT_RESAMPLING_NONE;
>
> + p_input->p_playback_rate_filter = NULL;
> + for (int i=0; i < p_input->i_nb_filters; i++) {
> + aout_filter_t * p_filter = p_input->pp_filters[i];
> + if (strcmp ("scaletempo", p_filter->psz_object_name) == 0) {
> + p_input->p_playback_rate_filter = p_filter;
> + break;
> + }
> + }
> + if (!p_input->p_playback_rate_filter && p_input->i_nb_resamplers > 0) {
> + p_input->p_playback_rate_filter = p_input->pp_resamplers[0];
> + }
Inside src/audio_output I would prefer you to keep current VLC code format
style.
I think it would be more userfriendly if scaletempo was selected by
default (instead of specifying by audio-filter). The code loading filters
(audio-filter) could be made a function and then reused. Of course, an option
should be added to disable this behavior.
> - if( p_input->pp_resamplers[0]->input.i_rate == 1000 * p_input->input.i_rate / i_input_rate )
> + uint i_nominal_rate =
Use unsigned int instead.
> +/*
> + * Scaletempo works by producing audio in constant sized chunks (a "stride") but
> + * consuming chunks proportional to the playback rate.
> + *
> + * Scaletempo then smooths the output by blending the end of one stride with
> + * the next ("overlap").
> + *
> + * Scaletempo smooths the overlap further by searching within the input buffer
> + * for the best overlap position. Scaletempo uses a statistical cross correlation
> + * (roughly a dot-product). Scaletempo consumes most of its CPU cycles here.
> + *
> + * NOTE:
> + * sample: a single audio sample for one channel
> + * frame: a single set of samples, one for each channel
> + * VLC uses these terms differently
> + */
About the algo, I did find the quality a bit low (especially at high speed).
I have played with audio time-stretching before and I had a better quality if
I did not use "constant sized chunks".
For that I estimate the period using autocorellation and then I duplicated or
overlapped the audio by chunk equal to the local period. The duplication or
overlapping amount was controled by the scale factor I wanted. It is a bit
more complicated though.
Anyway the quality should not stop the integration of scaletempo, it is a
nice feature :)
Thank you for your work.
Regards,
--
fenrir
More information about the vlc-devel
mailing list