]> git.sesse.net Git - vlc/commitdiff
stereo_widen : filter to enhance stereo
authorSukrit Sangwan <sukritsangwan@gmail.com>
Sun, 11 Mar 2012 01:59:12 +0000 (17:59 -0800)
committerJean-Baptiste Kempf <jb@videolan.org>
Mon, 12 Mar 2012 09:29:51 +0000 (10:29 +0100)
Signed-off-by: Jean-Baptiste Kempf <jb@videolan.org>
NEWS
modules/LIST
modules/audio_filter/Modules.am
modules/audio_filter/stereo_widen.c [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index 338bf576af70bbe166842e96dded1c1627e88eac..0fdaef7bfa7be9f6534f73915b2e9db9f34aad30 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -12,6 +12,7 @@ Encoders:
 
 Audio Filters:
  * new remapping channel filter
+ * new filter to enhance stereo effect by mono suppression and delay effect
 
 Codecs:
  * Support for CDXL, Ut Video, VBLE, Dxtory codecs via libavcodec.
index d6b192d5887b0a05f1c59a9c7f4b11c4c0e0d0cb..09dbad8ad7934036d16011dcde5c83a560c25a66 100644 (file)
@@ -304,6 +304,7 @@ $Id$
  * spudec: RLE DVD subtitles decoder
  * sqlite: manage an SQLite database
  * stats: Stats encoder function
+ * stereo_widen: Enhances stereo effect
  * stl: EBU STL decoder
  * stream_filter_dash: MPEG DASH playback
  * stream_filter_httplive: HTTP Live streaming for playback
index d787dbb1a5a0dd605f0a3c560c7f48704f5daf08..90e7661fa79353b86cf4570c333154a3625b1833 100644 (file)
@@ -6,6 +6,7 @@ SOURCES_audiobargraph_a = audiobargraph_a.c
 SOURCES_param_eq = param_eq.c
 SOURCES_scaletempo = scaletempo.c
 SOURCES_chorus_flanger = chorus_flanger.c
+SOURCES_stereo_widen = stereo_widen.c
 SOURCES_spatializer = \
        spatializer/allpass.cpp spatializer/allpass.hpp \
        spatializer/comb.cpp spatializer/comb.hpp \
@@ -23,7 +24,8 @@ libvlc_LTLIBRARIES += \
        libnormvol_plugin.la \
        libparam_eq_plugin.la \
        libscaletempo_plugin.la \
-       libspatializer_plugin.la
+       libspatializer_plugin.la \
+       libstereo_widen_plugin.la
 
 # Channel mixers
 SOURCES_trivial_channel_mixer = channel_mixer/trivial.c
diff --git a/modules/audio_filter/stereo_widen.c b/modules/audio_filter/stereo_widen.c
new file mode 100644 (file)
index 0000000..fcf93be
--- /dev/null
@@ -0,0 +1,232 @@
+/*****************************************************************************
+ * stereo_widen.c : simple stereo widening effect
+ *****************************************************************************
+ * Copyright (C) 2012 VLC authors and VideoLAN
+ *
+ * Author : Sukrit Sangwan < sukritsangwan at gmail dot com >
+ *
+ * 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.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_aout.h>
+#include <vlc_filter.h>
+#include <vlc_plugin.h>
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+static int  Open ( vlc_object_t * );
+static void Close( vlc_object_t * );
+
+static block_t *Filter ( filter_t *, block_t * );
+static int paramCallback( vlc_object_t *, char const *, vlc_value_t ,
+                            vlc_value_t , void * );
+
+struct filter_sys_t
+{
+    float *pf_begin;    /* circular buffer to store samples */
+    float *pf_write;    /* where to write current sample    */
+    int   i_len;        /* delay in number of samples       */
+    float f_delay;      /* delay in milliseconds            */
+    float f_feedback;
+    float f_crossfeed;
+    float f_dry_mix;
+    bool  b_free_buf;   /* used if callback to delay fails to       *
+                         * allocate buffer, then dont free it twice */
+};
+
+#define DESCRIPTION N_("This filter enhances stereo effect by \
+            suppressing mono,i.e. signal common to both channels, \
+            and by delaying the signal of left into right and vice versa \
+            thereby widening stereo effect")
+#define DELAY_TEXT N_("Delay time")
+#define DELAY_LONGTEXT N_("Time in ms of the delay of left signal into right \
+            and vice versa.")
+#define FEEDBACK_TEXT N_("Feedback Gain")
+#define FEEDBACK_LONGTEXT N_("Amount of gain in delayed left signal into \
+            right and vice versa. Gives a delay effect of left signal in \
+            right output and vice versa which gives widening effect")
+#define CROSSFEED_TEXT N_("Crossfeed")
+#define CROSSFEED_LONGTEXT N_("Cross feed of left into right with inverted \
+            phase. This helps in suppression of mono. If its value is 1 it \
+            cancels all the signal common to both channels")
+#define DRYMIX_TEXT N_("Dry Mix")
+#define DRYMIX_LONGTEXT N_("Level of input signal of original channel")
+
+/*****************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+vlc_module_begin ()
+    set_shortname( N_("stereo_widen") )
+    set_description( DESCRIPTION )
+    set_category( CAT_AUDIO )
+    set_subcategory( SUBCAT_AUDIO_AFILTER )
+    set_capability( "audio filter", 0 )
+    set_callbacks( Open, Close )
+
+    add_float( "delay", 20, DELAY_TEXT, DELAY_LONGTEXT, true )
+    add_float_with_range( "feedback", 0.3, 0.0, 0.9,
+        FEEDBACK_TEXT, FEEDBACK_LONGTEXT, true )
+    add_float_with_range( "crossfeed", 0.3, 0.0, 0.8,
+        CROSSFEED_TEXT, CROSSFEED_LONGTEXT, true )
+    add_float_with_range( "dry-mix", 0.8, 0.0, 1.0,
+        DRYMIX_TEXT, DRYMIX_LONGTEXT, true )
+vlc_module_end ()
+
+/*****************************************************************************
+ * Open: Allocate buffer
+ *****************************************************************************/
+static int Open( vlc_object_t *obj )
+{
+    filter_t *p_filter  = (filter_t *)obj;
+    filter_sys_t *p_sys;
+
+    if( p_filter->fmt_in.audio.i_format != VLC_CODEC_FL32 ||
+     !AOUT_FMTS_IDENTICAL( &p_filter->fmt_in.audio, &p_filter->fmt_out.audio) )
+        return VLC_EGENERIC;
+
+    if( p_filter->fmt_in.audio.i_channels != 2 )
+    {
+        msg_Err ( p_filter, "stereo enhance requires stereo" );
+        return VLC_EGENERIC;
+    }
+
+    p_sys = p_filter->p_sys = malloc( sizeof(filter_sys_t) );
+    if( unlikely(!p_sys) )
+        return VLC_ENOMEM;
+
+#define CREATE_VAR( stor, var ) \
+    p_sys->stor = var_CreateGetFloat( obj, var ); \
+    var_AddCallback( p_filter, var, paramCallback, p_sys );
+
+    CREATE_VAR( f_delay, "delay" )
+    CREATE_VAR( f_feedback, "feedback" )
+    CREATE_VAR( f_crossfeed, "crossfeed" )
+    CREATE_VAR( f_dry_mix, "dry-mix" )
+
+    /* Compute buffer length and allocate space */
+    p_sys->i_len = 2 * p_sys->f_delay * p_filter->fmt_in.audio.i_rate / 1000;
+    p_sys->pf_begin = calloc( p_sys->i_len + 2, sizeof(float) );
+    if( unlikely(!p_sys->pf_begin) )
+    {
+        free( p_sys );
+        return VLC_ENOMEM;
+    }
+    p_sys->b_free_buf = true;
+    p_sys->pf_write = p_sys->pf_begin;
+    p_filter->pf_audio_filter = Filter;
+    return VLC_SUCCESS;
+}
+
+
+/*****************************************************************************
+ * Filter: process each sample
+ *****************************************************************************/
+static block_t *Filter( filter_t *p_filter, block_t *p_block )
+{
+    filter_sys_t *p_sys = p_filter->p_sys;
+    float *p_out = (float *)p_block->p_buffer;
+    float *pf_read;
+
+    for (unsigned i = p_block->i_nb_samples; i > 0; i--)
+    {
+        pf_read = p_sys->pf_write + 2;
+        /* if at end of buffer put read ptr at begin */
+        if( pf_read > p_sys->pf_begin + p_sys->i_len )
+            pf_read = p_sys->pf_begin;
+
+        float left  = p_out[0];
+        float right = p_out[1];
+
+        *(p_out++) = p_sys->f_dry_mix * left  - p_sys->f_crossfeed * right
+                        - p_sys->f_feedback * pf_read[1];
+        *(p_out++) = p_sys->f_dry_mix * right - p_sys->f_crossfeed * left
+                        - p_sys->f_feedback * pf_read[0];
+        p_sys->pf_write[0] = left ;
+        p_sys->pf_write[1] = right;
+
+        /* if at end of buffer place pf_write at begin */
+        if( p_sys->pf_write  == p_sys->pf_begin + p_sys->i_len )
+            p_sys->pf_write  =  p_sys->pf_begin;
+        else
+            p_sys->pf_write += 2;
+    }
+
+    return p_block;
+}
+
+/*****************************************************************************
+ * Close: close the plugin
+ *****************************************************************************/
+static void Close( vlc_object_t *obj )
+{
+    filter_t *p_filter  = (filter_t *)obj;
+    filter_sys_t *p_sys = p_filter->p_sys;
+
+#define DEL_VAR(var) \
+    var_DelCallback( p_filter, var, paramCallback, p_sys ); \
+    var_Destroy( p_filter, var );
+
+    DEL_VAR( "feedback" );
+    DEL_VAR( "crossfeed" );
+    DEL_VAR( "dry-mix" );
+    var_Destroy( p_filter, "delay" );
+    if( p_sys->b_free_buf )
+        free( p_sys->pf_begin );
+    free( p_sys );
+}
+
+
+/**********************************************************************
+ * Callback to update params on the fly
+ **********************************************************************/
+static int paramCallback( vlc_object_t *p_this, char const *psz_var,
+                            vlc_value_t oldval, vlc_value_t newval,
+                            void *p_data )
+{
+    filter_t *p_filter = (filter_t *)p_this;
+    filter_sys_t *p_sys = (filter_sys_t *) p_data;
+
+    VLC_UNUSED(oldval);
+    VLC_UNUSED(p_this);
+
+    if( !strcmp( psz_var, "delay" ) )
+    {
+        p_sys->f_delay = newval.f_float;
+        /* Free previous buffer and allocate new circular buffer */
+        free( p_sys->pf_begin );
+        p_sys->i_len = 2 * p_sys->f_delay * p_filter->fmt_in.audio.i_rate /1000;
+        p_sys->pf_begin = calloc( p_sys->i_len + 2, sizeof(float) );
+        if( unlikely(!p_sys->pf_begin) )
+        {
+            p_sys->b_free_buf = false;
+            msg_Dbg( p_filter, "Couldnt allocate buffer for delay" );
+            Close( p_this );
+        }
+    }
+    else if( !strcmp( psz_var, "feedback" ) )
+        p_sys->f_feedback = newval.f_float;
+    else if( !strcmp( psz_var, "crossfeed" ) )
+        p_sys->f_feedback = newval.f_float;
+    else if( !strcmp( psz_var, "dry-mix" ) )
+        p_sys->f_dry_mix = newval.f_float;
+
+    return VLC_SUCCESS;
+}