]> git.sesse.net Git - vlc/blob - modules/audio_filter/stereo_widen.c
avcodec: reuse AVFrames so no need to allocate/free it every time we encode picture...
[vlc] / modules / audio_filter / stereo_widen.c
1 /*****************************************************************************
2  * stereo_widen.c : simple stereo widening effect
3  *****************************************************************************
4  * Copyright (C) 2012 VLC authors and VideoLAN
5  *
6  * Author : Sukrit Sangwan < sukritsangwan at gmail dot com >
7  *
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.
12  *
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.
17  *
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  *****************************************************************************/
22
23 #ifdef HAVE_CONFIG_H
24 # include "config.h"
25 #endif
26
27 #include <vlc_common.h>
28 #include <vlc_aout.h>
29 #include <vlc_filter.h>
30 #include <vlc_plugin.h>
31
32 /*****************************************************************************
33  * Local prototypes
34  *****************************************************************************/
35 static int  Open ( vlc_object_t * );
36 static void Close( vlc_object_t * );
37
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 * );
41
42 struct filter_sys_t
43 {
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            */
48     float f_feedback;
49     float f_crossfeed;
50     float f_dry_mix;
51     bool  b_free_buf;   /* used if callback to delay fails to       *
52                          * allocate buffer, then dont free it twice */
53 };
54
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 "\
61             "and vice versa.")
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.")
72
73 /*****************************************************************************
74  * Module descriptor
75  *****************************************************************************/
76 vlc_module_begin ()
77     set_shortname( N_("Stereo Enhancer") )
78     set_description( N_("Simple stereo widening effect") )
79     set_help( HELP_TEXT )
80     set_category( CAT_AUDIO )
81     set_subcategory( SUBCAT_AUDIO_AFILTER )
82     set_capability( "audio filter", 0 )
83     set_callbacks( Open, Close )
84
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 )
92 vlc_module_end ()
93
94 /*****************************************************************************
95  * Open: Allocate buffer
96  *****************************************************************************/
97 static int Open( vlc_object_t *obj )
98 {
99     filter_t *p_filter  = (filter_t *)obj;
100     filter_sys_t *p_sys;
101
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) )
104         return VLC_EGENERIC;
105
106     if( p_filter->fmt_in.audio.i_channels != 2 )
107     {
108         msg_Err ( p_filter, "stereo enhance requires stereo" );
109         return VLC_EGENERIC;
110     }
111
112     p_sys = p_filter->p_sys = malloc( sizeof(filter_sys_t) );
113     if( unlikely(!p_sys) )
114         return VLC_ENOMEM;
115
116 #define CREATE_VAR( stor, var ) \
117     p_sys->stor = var_CreateGetFloat( obj, var ); \
118     var_AddCallback( p_filter, var, paramCallback, p_sys );
119
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" )
124
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) )
129     {
130         free( p_sys );
131         return VLC_ENOMEM;
132     }
133     p_sys->b_free_buf = true;
134     p_sys->pf_write = p_sys->pf_begin;
135     p_filter->pf_audio_filter = Filter;
136     return VLC_SUCCESS;
137 }
138
139
140 /*****************************************************************************
141  * Filter: process each sample
142  *****************************************************************************/
143 static block_t *Filter( filter_t *p_filter, block_t *p_block )
144 {
145     filter_sys_t *p_sys = p_filter->p_sys;
146     float *p_out = (float *)p_block->p_buffer;
147     float *pf_read;
148
149     for (unsigned i = p_block->i_nb_samples; i > 0; i--)
150     {
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;
155
156         float left  = p_out[0];
157         float right = p_out[1];
158
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;
165
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;
169         else
170             p_sys->pf_write += 2;
171     }
172
173     return p_block;
174 }
175
176 /*****************************************************************************
177  * Close: close the plugin
178  *****************************************************************************/
179 static void Close( vlc_object_t *obj )
180 {
181     filter_t *p_filter  = (filter_t *)obj;
182     filter_sys_t *p_sys = p_filter->p_sys;
183
184 #define DEL_VAR(var) \
185     var_DelCallback( p_filter, var, paramCallback, p_sys ); \
186     var_Destroy( p_filter, var );
187
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 );
194     free( p_sys );
195 }
196
197
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,
203                             void *p_data )
204 {
205     filter_t *p_filter = (filter_t *)p_this;
206     filter_sys_t *p_sys = (filter_sys_t *) p_data;
207
208     VLC_UNUSED(oldval);
209     VLC_UNUSED(p_this);
210
211     if( !strcmp( psz_var, "delay" ) )
212     {
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) )
219         {
220             p_sys->b_free_buf = false;
221             msg_Dbg( p_filter, "Couldnt allocate buffer for delay" );
222             Close( p_this );
223         }
224     }
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;
231
232     return VLC_SUCCESS;
233 }