1 /*****************************************************************************
2 * stereo_widen.c : simple stereo widening effect
3 *****************************************************************************
4 * Copyright (C) 2012 VLC authors and VideoLAN
6 * Author : Sukrit Sangwan < sukritsangwan at gmail dot com >
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
27 #include <vlc_common.h>
29 #include <vlc_filter.h>
30 #include <vlc_plugin.h>
32 /*****************************************************************************
34 *****************************************************************************/
35 static int Open ( vlc_object_t * );
36 static void Close( vlc_object_t * );
38 static block_t *Filter ( filter_t *, block_t * );
39 static int paramCallback( vlc_object_t *, char const *, vlc_value_t ,
40 vlc_value_t , void * );
44 float *pf_begin; /* circular buffer to store samples */
45 float *pf_write; /* where to write current sample */
46 int i_len; /* delay in number of samples */
47 float f_delay; /* delay in milliseconds */
51 bool b_free_buf; /* used if callback to delay fails to *
52 * allocate buffer, then dont free it twice */
55 #define HELP_TEXT N_("This filter enhances the stereo effect by "\
56 "suppressing mono (signal common to both channels) "\
57 "and by delaying the signal of left into right and vice versa, "\
58 "thereby widening the stereo effect.")
59 #define DELAY_TEXT N_("Delay time")
60 #define DELAY_LONGTEXT N_("Time in ms of the delay of left signal into right "\
62 #define FEEDBACK_TEXT N_("Feedback gain")
63 #define FEEDBACK_LONGTEXT N_("Amount of gain in delayed left signal into "\
64 "right and vice versa. Gives a delay effect of left signal in "\
65 "right output and vice versa which gives widening effect.")
66 #define CROSSFEED_TEXT N_("Crossfeed")
67 #define CROSSFEED_LONGTEXT N_("Cross feed of left into right with inverted "\
68 "phase. This helps in suppressing the mono. If the value is 1 it "\
69 "will cancel all the signal common to both channels.")
70 #define DRYMIX_TEXT N_("Dry mix")
71 #define DRYMIX_LONGTEXT N_("Level of input signal of original channel.")
73 /*****************************************************************************
75 *****************************************************************************/
77 set_shortname( N_("Stereo Enhancer") )
78 set_description( N_("Simple stereo widening effect") )
80 set_category( CAT_AUDIO )
81 set_subcategory( SUBCAT_AUDIO_AFILTER )
82 set_capability( "audio filter", 0 )
83 set_callbacks( Open, Close )
85 add_float( "delay", 20, DELAY_TEXT, DELAY_LONGTEXT, true )
86 add_float_with_range( "feedback", 0.3, 0.0, 0.9,
87 FEEDBACK_TEXT, FEEDBACK_LONGTEXT, true )
88 add_float_with_range( "crossfeed", 0.3, 0.0, 0.8,
89 CROSSFEED_TEXT, CROSSFEED_LONGTEXT, true )
90 add_float_with_range( "dry-mix", 0.8, 0.0, 1.0,
91 DRYMIX_TEXT, DRYMIX_LONGTEXT, true )
94 /*****************************************************************************
95 * Open: Allocate buffer
96 *****************************************************************************/
97 static int Open( vlc_object_t *obj )
99 filter_t *p_filter = (filter_t *)obj;
102 if( p_filter->fmt_in.audio.i_format != VLC_CODEC_FL32 ||
103 !AOUT_FMTS_IDENTICAL( &p_filter->fmt_in.audio, &p_filter->fmt_out.audio) )
106 if( p_filter->fmt_in.audio.i_channels != 2 )
108 msg_Err ( p_filter, "stereo enhance requires stereo" );
112 p_sys = p_filter->p_sys = malloc( sizeof(filter_sys_t) );
113 if( unlikely(!p_sys) )
116 #define CREATE_VAR( stor, var ) \
117 p_sys->stor = var_CreateGetFloat( obj, var ); \
118 var_AddCallback( p_filter, var, paramCallback, p_sys );
120 CREATE_VAR( f_delay, "delay" )
121 CREATE_VAR( f_feedback, "feedback" )
122 CREATE_VAR( f_crossfeed, "crossfeed" )
123 CREATE_VAR( f_dry_mix, "dry-mix" )
125 /* Compute buffer length and allocate space */
126 p_sys->i_len = 2 * p_sys->f_delay * p_filter->fmt_in.audio.i_rate / 1000;
127 p_sys->pf_begin = calloc( p_sys->i_len + 2, sizeof(float) );
128 if( unlikely(!p_sys->pf_begin) )
133 p_sys->b_free_buf = true;
134 p_sys->pf_write = p_sys->pf_begin;
135 p_filter->pf_audio_filter = Filter;
140 /*****************************************************************************
141 * Filter: process each sample
142 *****************************************************************************/
143 static block_t *Filter( filter_t *p_filter, block_t *p_block )
145 filter_sys_t *p_sys = p_filter->p_sys;
146 float *p_out = (float *)p_block->p_buffer;
149 for (unsigned i = p_block->i_nb_samples; i > 0; i--)
151 pf_read = p_sys->pf_write + 2;
152 /* if at end of buffer put read ptr at begin */
153 if( pf_read > p_sys->pf_begin + p_sys->i_len )
154 pf_read = p_sys->pf_begin;
156 float left = p_out[0];
157 float right = p_out[1];
159 *(p_out++) = p_sys->f_dry_mix * left - p_sys->f_crossfeed * right
160 - p_sys->f_feedback * pf_read[1];
161 *(p_out++) = p_sys->f_dry_mix * right - p_sys->f_crossfeed * left
162 - p_sys->f_feedback * pf_read[0];
163 p_sys->pf_write[0] = left ;
164 p_sys->pf_write[1] = right;
166 /* if at end of buffer place pf_write at begin */
167 if( p_sys->pf_write == p_sys->pf_begin + p_sys->i_len )
168 p_sys->pf_write = p_sys->pf_begin;
170 p_sys->pf_write += 2;
176 /*****************************************************************************
177 * Close: close the plugin
178 *****************************************************************************/
179 static void Close( vlc_object_t *obj )
181 filter_t *p_filter = (filter_t *)obj;
182 filter_sys_t *p_sys = p_filter->p_sys;
184 #define DEL_VAR(var) \
185 var_DelCallback( p_filter, var, paramCallback, p_sys ); \
186 var_Destroy( p_filter, var );
188 DEL_VAR( "feedback" );
189 DEL_VAR( "crossfeed" );
190 DEL_VAR( "dry-mix" );
191 var_Destroy( p_filter, "delay" );
192 if( p_sys->b_free_buf )
193 free( p_sys->pf_begin );
198 /**********************************************************************
199 * Callback to update params on the fly
200 **********************************************************************/
201 static int paramCallback( vlc_object_t *p_this, char const *psz_var,
202 vlc_value_t oldval, vlc_value_t newval,
205 filter_t *p_filter = (filter_t *)p_this;
206 filter_sys_t *p_sys = (filter_sys_t *) p_data;
211 if( !strcmp( psz_var, "delay" ) )
213 p_sys->f_delay = newval.f_float;
214 /* Free previous buffer and allocate new circular buffer */
215 free( p_sys->pf_begin );
216 p_sys->i_len = 2 * p_sys->f_delay * p_filter->fmt_in.audio.i_rate /1000;
217 p_sys->pf_begin = calloc( p_sys->i_len + 2, sizeof(float) );
218 if( unlikely(!p_sys->pf_begin) )
220 p_sys->b_free_buf = false;
221 msg_Dbg( p_filter, "Couldnt allocate buffer for delay" );
225 else if( !strcmp( psz_var, "feedback" ) )
226 p_sys->f_feedback = newval.f_float;
227 else if( !strcmp( psz_var, "crossfeed" ) )
228 p_sys->f_feedback = newval.f_float;
229 else if( !strcmp( psz_var, "dry-mix" ) )
230 p_sys->f_dry_mix = newval.f_float;