[vlc-devel] [PATCH 3/3] soxr: use a fixed and a variable-rate SoxR instance

Thomas Guillem thomas at gllm.fr
Mon Nov 2 17:06:11 CET 2015


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).
---
 modules/audio_filter/resampler/soxr.c | 176 ++++++++++++++++++++++++----------
 1 file changed, 124 insertions(+), 52 deletions(-)

diff --git a/modules/audio_filter/resampler/soxr.c b/modules/audio_filter/resampler/soxr.c
index 5c7a9a7..e11ffc4 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,10 +223,43 @@ 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 block_t *
+SoXR_Resample( filter_t *p_filter, soxr_t soxr, block_t *p_in,
+               mtime_t i_pts, size_t i_ilen, size_t i_olen )
+{
+    size_t i_idone, i_odone;
+    const size_t i_oframesize = p_filter->fmt_out.audio.i_bytes_per_frame;
+
+    block_t *p_out = p_in && i_ilen >= i_olen ? p_in
+                   : block_Alloc( i_olen * i_oframesize );
+
+    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;
+    }
+    p_out->i_buffer = i_odone * i_oframesize;
+    p_out->i_nb_samples = i_odone;
+    p_out->i_pts = i_pts;
+    p_out->i_length = i_odone * CLOCK_FREQ / p_filter->fmt_out.audio.i_rate;
+
+error:
+    if( p_in &&  p_out != p_in )
+        block_Release( p_in );
+
+    return p_out;
+}
+
 static size_t
 SoXR_GetOutLen( size_t i_ilen, double f_ratio )
 {
@@ -221,59 +273,79 @@ Resample( filter_t *p_filter, block_t *p_in )
 {
     filter_sys_t *p_sys = p_filter->p_sys;
 
-    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 )
+    if( p_sys->vr_soxr )
     {
-        /* "audio resampler" with variable ratio */
+        /* "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;
-        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 );
+        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_in->i_pts, 0,
+                                           p_sys->i_last_olen );
+            soxr_clear( p_sys->last_soxr );
+            p_sys->i_last_olen = 0;
+            p_sys->last_soxr = NULL;
+            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, p_in->i_pts,
+                                   p_in->i_nb_samples, i_olen );
+            if( !p_out )
+                return NULL;
+            p_sys->i_last_olen = i_olen;
+            p_sys->last_soxr = soxr;
+        }
+
+        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 );
+            p_out->i_nb_samples = i_nb_samples;
+        }
+        return p_out;
     }
     else
-        i_olen = SoXR_GetOutLen( i_ilen, p_sys->f_fixed_ratio );
-
-    /* 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 );
-    if( error )
     {
-        msg_Err( p_filter, "soxr_process failed: %s", soxr_strerror( error ) );
-        goto error;
-    }
+        /* "audio converter" with fixed ratio */
 
-    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 )
-        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;
+        const size_t i_olen = SoXR_GetOutLen( p_in->i_nb_samples,
+                                              p_sys->f_fixed_ratio );
+        return SoXR_Resample( p_filter, p_sys->soxr, p_in, p_in->i_pts,
+                              p_in->i_nb_samples, i_olen );
+    }
 }
-- 
2.1.4



More information about the vlc-devel mailing list