[vlc-devel] [RFC PATCH] modules: add SoX Resampler audio_filter

Thomas Guillem thomas at gllm.fr
Tue Oct 27 14:19:36 CET 2015



On Tue, Oct 27, 2015, at 13:38, Rémi Denis-Courmont wrote:
> Le 2015-10-27 15:00, Thomas Guillem a écrit :
> > Tested only with FL32/S16/S32 and various sample rates conversions.
> >
> > TODO: compare perfs and quality with speex/src/ugly.
> >
> > ---
> >  NEWS                                  |   3 +
> >  configure.ac                          |  18 +++
> >  modules/MODULES_LIST                  |   1 +
> >  modules/audio_filter/Makefile.am      |   7 +
> >  modules/audio_filter/resampler/soxr.c | 272
> > ++++++++++++++++++++++++++++++++++
> >  5 files changed, 301 insertions(+)
> >  create mode 100644 modules/audio_filter/resampler/soxr.c
> >
> > diff --git a/NEWS b/NEWS
> > index df0354f..bd9d0ef 100644
> > --- a/NEWS
> > +++ b/NEWS
> > @@ -83,6 +83,9 @@ Audio output:
> >     It now supports HDMI/SPDIF passthrough for AC3, 5.1/7.1 and float 
> > output.
> >   * Added Tizen audio module.
> >
> > +Audio filters and output:
> > + * Add SoX Resampler library audio filter module (converter and 
> > resampler)
> > +
> >  Video ouput:
> >   * Linux/BSD default video output is now OpenGL, instead of Xvideo
> >   * Wayland shell surface window provider
> > diff --git a/configure.ac b/configure.ac
> > index 2776a8c..6481d57 100644
> > --- a/configure.ac
> > +++ b/configure.ac
> > @@ -3657,6 +3657,24 @@ dnl
> >  PKG_ENABLE_MODULES_VLC([SAMPLERATE], [], [samplerate], [Resampler
> > with libsamplerate], [auto])
> >
> >  dnl
> > +dnl  soxr module
> > +dnl
> > +AC_ARG_ENABLE(soxr,
> > +  [AS_HELP_STRING([--enable-soxr],
> > +    [use the SoX Resampler library (default auto)])])
> > +have_soxr="no"
> > +AS_IF([test "${enable_soxr}" != "no"], [
> > +  PKG_CHECK_MODULES([SOXR], [soxr >= 0.1], [
> > +    have_soxr="yes"
> > +  ], [
> > +    AS_IF([test "x${enable_soxr}" != "x"], [
> > +      AC_MSG_ERROR([$SOXR_PKG_ERRORS. soxr 0.1 or later required.])
> > +    ])
> > +  ])
> > +])
> > +AM_CONDITIONAL([HAVE_SOXR], [test "${have_soxr}" = "yes"])
> > +
> > +dnl
> >  dnl  OS/2 KAI plugin
> >  dnl
> >  AC_ARG_ENABLE(kai,
> > diff --git a/modules/MODULES_LIST b/modules/MODULES_LIST
> > index 0c4892a..c2faebe 100644
> > --- a/modules/MODULES_LIST
> > +++ b/modules/MODULES_LIST
> > @@ -344,6 +344,7 @@ $Id$
> >   * smf: Standard MIDI file demuxer
> >   * smooth: Microsoft Smooth Streaming input
> >   * sndio: OpenBSD sndio audio output
> > + * soxr: SoX Resampler library audio filter
> >   * spatializer: A spatializer audio filter
> >   * speex: a speex audio decoder/packetizer using the libspeex 
> > library
> >   * speex_resampler: audio resampler using the libspeexdsp library
> > diff --git a/modules/audio_filter/Makefile.am
> > b/modules/audio_filter/Makefile.am
> > index 372d08e..2090133 100644
> > --- a/modules/audio_filter/Makefile.am
> > +++ b/modules/audio_filter/Makefile.am
> > @@ -124,3 +124,10 @@ libspeex_resampler_plugin_la_LIBADD = 
> > $(SPEEXDSP_LIBS)
> >  if HAVE_SPEEXDSP
> >  audio_filter_LTLIBRARIES += libspeex_resampler_plugin.la
> >  endif
> > +
> > +libsoxr_resampler_plugin_la_SOURCES = audio_filter/resampler/soxr.c
> > +libsoxr_resampler_plugin_la_CFLAGS = $(AM_CFLAGS) $(SOXR_CFLAGS)
> > +libsoxr_resampler_plugin_la_LIBADD = $(SOXR_LIBS)
> > +if HAVE_SOXR
> > +audio_filter_LTLIBRARIES += libsoxr_resampler_plugin.la
> > +endif
> > diff --git a/modules/audio_filter/resampler/soxr.c
> > b/modules/audio_filter/resampler/soxr.c
> > new file mode 100644
> > index 0000000..bf7d3a5
> > --- /dev/null
> > +++ b/modules/audio_filter/resampler/soxr.c
> > @@ -0,0 +1,272 @@
> > 
> > +/*****************************************************************************
> > + * soxr.c: resampler/converter using The SoX Resampler library
> > +
> > 
> > *****************************************************************************
> > + * Copyright (C) 2015 VLC authors, VideoLAN and VideoLabs
> > + *
> > + * Authors: Thomas Guillem <thomas at gllm.fr>
> > + *
> > + * This program is free software; you can redistribute it and/or 
> > modify it
> > + * under the terms of the GNU Lesser General Public License as 
> > published by
> > + * the Free Software Foundation; either version 2.1 of the License, 
> > or
> > + * (at your option) any later version.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU Lesser General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU Lesser General Public 
> > License
> > + * along with this program; if not, write to the Free Software 
> > Foundation,
> > + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
> > +
> > 
> > *****************************************************************************/
> > +
> > 
> > +/*****************************************************************************
> > + * Preamble
> > +
> > 
> > *****************************************************************************/
> > +
> > +#ifdef HAVE_CONFIG_H
> > +# include "config.h"
> > +#endif
> > +
> > +#include <vlc_common.h>
> > +#include <vlc_aout.h>
> > +#include <vlc_filter.h>
> > +#include <vlc_plugin.h>
> > +
> > +#include <math.h>
> > +#include <soxr.h>
> > +
> > +#define SOXR_QUALITY_TEXT "Sox Resampling quality"
> > +
> > +const int soxr_resampler_quality_vlclist[] = { 0, 1, 2, 3, 4 };
> 
> static
> 
> > +static const char *const soxr_resampler_quality_vlctext[] =
> > +{
> > +    "Quick cubic interpolation",
> 
> Probably missing N_()

ok

> 
> > +    "Low 16-bit with larger rolloff",
> 
> roll-off

ok

> 
> > +    "Medium 16-bit with medium rolloff",
> > +    "High quality",
> > +    "Very high quality"
> > +};
> > +const soxr_datatype_t soxr_resampler_quality_list[] =
> 
> static

oups... ok

> 
> > +{
> > +    SOXR_QQ,
> > +    SOXR_LQ,
> > +    SOXR_MQ,
> > +    SOXR_HQ,
> > +    SOXR_VHQ
> > +};
> > +#define MAX_SOXR_QUALITY 4
> > +
> > +static int Open( vlc_object_t * );
> > +static int OpenResampler( vlc_object_t * );
> > +static void Close( vlc_object_t * );
> > +
> > +vlc_module_begin ()
> > +    set_shortname( "SoX Resampler" )
> > +    set_category( CAT_AUDIO )
> > +    set_subcategory( SUBCAT_AUDIO_MISC )
> > +    add_integer( "soxr-resampler-quality", 2,
> > +                SOXR_QUALITY_TEXT, NULL, true )
> > +        change_integer_list( soxr_resampler_quality_vlclist,
> > +                             soxr_resampler_quality_vlctext )
> > +    set_capability ( "audio converter", 3 )
> > +    set_callbacks( Open, Close )
> > +
> > +    add_submodule()
> > +    set_capability( "audio resampler", 3 )
> > +    set_callbacks( OpenResampler, Close )
> > +    add_shortcut( "soxr" )
> > +vlc_module_end ()
> > +
> > +struct filter_sys_t
> > +{
> > +    soxr_t soxr;
> > +    block_t *p_last_in;
> > +};
> > +
> > +static block_t *Resample( filter_t *, block_t * );
> > +
> > +static bool
> > +SoXR_GetFormat( vlc_fourcc_t i_format, soxr_datatype_t *p_type )
> > +{
> > +    switch( i_format )
> > +    {
> > +        case VLC_CODEC_FL64:
> > +            *p_type = SOXR_FLOAT64_I;
> > +            return true;
> > +        case VLC_CODEC_FL32:
> > +            *p_type = SOXR_FLOAT32_I;
> > +            return true;
> > +        case VLC_CODEC_S32N:
> > +            *p_type = SOXR_INT32_I;
> > +            return true;
> > +        case VLC_CODEC_S16N:
> > +            *p_type = SOXR_INT16_I;
> > +            return true;
> > +        default:
> > +            return false;
> > +    }
> > +}
> > +
> > +static int
> > +OpenResampler( vlc_object_t *p_obj )
> > +{
> > +    filter_t *p_filter = (filter_t *)p_obj;
> > +
> > +    /* Cannot remix */
> > +    if( p_filter->fmt_in.audio.i_physical_channels
> > +            != p_filter->fmt_out.audio.i_physical_channels
> > +     || p_filter->fmt_in.audio.i_original_channels
> > +            != p_filter->fmt_out.audio.i_original_channels )
> > +        return VLC_EGENERIC;
> 
> i_format != i_format ?

This module can convert format. I didn't tested to convert both format
and sample rate since vlc will spawn a converter and a resampler
separately. Is it worth it to modify the core to try to create a
resampler/converter module that will do both ?

> 
> > +
> > +    /* Get SoXR input/output format */
> > +    soxr_datatype_t i_itype, i_otype;
> > +    if( !SoXR_GetFormat( p_filter->fmt_in.audio.i_format, &i_itype )
> > +     || !SoXR_GetFormat( p_filter->fmt_out.audio.i_format, &i_otype 
> > ) )
> > +        return VLC_EGENERIC;
> > +
> > +    filter_sys_t *p_sys = calloc( 1, sizeof( struct filter_sys_t * ) 
> > );
> > +    if( unlikely( p_sys == NULL ) )
> > +        return VLC_ENOMEM;
> > +
> > +    /* Setup SoXR */
> > +    int64_t i_vlc_q = var_InheritInteger( p_obj, 
> > "soxr-resampler-quality" );
> > +    if( i_vlc_q < 0 )
> > +        i_vlc_q = 0;
> > +    else if( i_vlc_q > MAX_SOXR_QUALITY )
> > +        i_vlc_q = MAX_SOXR_QUALITY;
> > +    const unsigned long i_recipe = 
> > soxr_resampler_quality_list[i_vlc_q];
> > +    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;
> > +    const unsigned long i_flags = f_ratio == 1.f ? 0
> > +                                : SOXR_VR; /* variable rate */
> > +
> > +    /* Create SoXR */
> > +    soxr_error_t error;
> > +    soxr_io_spec_t io_spec = soxr_io_spec( i_itype, i_otype );
> > +    soxr_quality_spec_t q_spec = soxr_quality_spec( i_recipe, 
> > i_flags );
> > +    p_sys->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 ) );
> > +        free( p_sys );
> > +        return VLC_EGENERIC;
> > +    }
> > +    soxr_set_io_ratio( p_sys->soxr, 1 / f_ratio, 0 );
> > +
> > +    msg_Dbg( p_filter, "Using SoX Resampler: %4.4s, %d Hz to %4.4s 
> > %d Hz. "
> > +             "quality: '%s'",
> > +             (const char *)&p_filter->fmt_in.audio.i_format,
> > +             p_filter->fmt_in.audio.i_rate,
> > +             (const char *)&p_filter->fmt_out.audio.i_format,
> > +             p_filter->fmt_out.audio.i_rate,
> > +             soxr_resampler_quality_vlctext[i_vlc_q] );
> > +
> > +    p_filter->p_sys = p_sys;
> > +    p_filter->pf_audio_filter = Resample;
> > +    return VLC_SUCCESS;
> > +}
> > +
> > +static int
> > +Open( vlc_object_t *p_obj )
> > +{
> > +    filter_t *p_filter = (filter_t *)p_obj;
> > +
> > +    /* Will change rate */
> > +    if( p_filter->fmt_in.audio.i_rate == 
> > p_filter->fmt_out.audio.i_rate )
> > +        return VLC_EGENERIC;
> > +    return OpenResampler( p_obj );
> > +}
> > +
> > +static void
> > +Close( vlc_object_t *p_obj )
> > +{
> > +    filter_t *p_filter = (filter_t *)p_obj;
> > +    filter_sys_t *p_sys = p_filter->p_sys;
> > +
> > +    soxr_delete( p_sys->soxr );
> > +
> > +    if( unlikely( p_sys->p_last_in ) )
> > +        block_Release( p_sys->p_last_in );
> > +
> > +    free( p_sys );
> > +}
> > +
> > +static block_t *
> > +Resample( filter_t *p_filter, block_t *p_in )
> > +{
> > +    filter_sys_t *p_sys = p_filter->p_sys;
> > +
> > +    /* Prepend last remaining input buffer to the current one */
> > +    if( unlikely( p_sys->p_last_in ) )
> > +    {
> > +        p_in = block_Realloc( p_in, p_sys->p_last_in->i_buffer,
> > p_in->i_buffer );
> 
> I don't remember if we allow p_in == NULL (we probably should if we 
> don't).

The p_in != NULL check is never done on converters and resampler
modules. So for now, it's not allowed.

> 
> > +        if( unlikely( p_in == NULL ) )
> > +            return NULL;
> > +
> > +        memcpy( p_in->p_buffer, p_sys->p_last_in->p_buffer,
> > +                p_sys->p_last_in->i_buffer );
> > +        p_in->i_nb_samples += p_sys->p_last_in->i_nb_samples;
> > +        block_Release( p_sys->p_last_in );
> > +        p_sys->p_last_in = NULL;
> > +    }
> > +
> > +    const double f_ratio = p_filter->fmt_out.audio.i_rate
> > +                         / (double) p_filter->fmt_in.audio.i_rate;
> > +    const size_t i_ilen = p_in->i_nb_samples;
> > +    const size_t i_olen = ceil( i_ilen * f_ratio );
> > +    const size_t i_oframesize = 
> > p_filter->fmt_out.audio.i_bytes_per_frame;
> > +    size_t i_idone, i_odone;
> > +
> > +    /* 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_set_io_ratio( p_sys->soxr, 1 / f_ratio, i_olen );
> > +    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 );
> > +    if( error )
> > +    {
> > +        msg_Err( p_filter, "soxr_process failed: %s", soxr_strerror(
> > error ) );
> > +        goto error;
> > +    }
> > +
> > +    if( unlikely( i_idone < i_ilen ) )
> > +    {
> > +        msg_Warn( p_filter, "processed input len < input len, "
> > +                 "keeping buffer for next Resample call" );
> > +        const size_t i_done_size = i_idone
> > +                                 *
> > p_filter->fmt_out.audio.i_bytes_per_frame;
> > +
> > +        /* Realloc since p_in can be used as p_out */
> > +        p_sys->p_last_in = block_Alloc( p_in->i_buffer - i_done_size 
> > );
> > +        if( unlikely( p_sys->p_last_in == NULL ) )
> > +            goto error;
> > +        memcpy( p_sys->p_last_in->p_buffer,
> > +                p_in->p_buffer + i_done_size, p_in->i_buffer -
> > i_done_size );
> > +        p_sys->p_last_in->i_nb_samples = p_in->i_nb_samples - 
> > 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 )
> > +        block_Release( p_in );
> > +    return p_out;
> > +
> > +error:
> > +
> > +    if( p_out && p_out != p_in )
> > +        block_Release( p_out );
> > +    block_Release( p_in );
> > +    return NULL;
> > +}


Thanks for the review.

> 
> -- 
> Rémi Denis-Courmont
> http://www.remlab.net/
> _______________________________________________
> vlc-devel mailing list
> To unsubscribe or modify your subscription options:
> https://mailman.videolan.org/listinfo/vlc-devel


More information about the vlc-devel mailing list