]> git.sesse.net Git - vlc/blob - modules/audio_filter/spatializer/spatializer.cpp
Fix memleak when out of memory.
[vlc] / modules / audio_filter / spatializer / spatializer.cpp
1 /*****************************************************************************
2  * spatializer.cpp: sound reverberation
3  *****************************************************************************
4  * Copyright (C) 2004, 2006, 2007 the VideoLAN team
5  *
6  * Google Summer of Code 2007
7  *
8  * Authors: Biodun Osunkunle <biodun@videolan.org>
9  *
10  * Mentor : Jean-Baptiste Kempf <jb@videolan.org>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License along
23  * with this program; if not, write to the Free Software Foundation, Inc.,
24  * 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25  *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #include <stdlib.h>                                      /* malloc(), free() */
35 #include <math.h>
36
37 #include <new>
38 using std::nothrow;
39
40 #include <vlc_common.h>
41 #include <vlc_plugin.h>
42 #include <vlc_aout.h>
43
44 #include "revmodel.hpp"
45 #define SPAT_AMP 0.3
46
47 /*****************************************************************************
48  * Module descriptor
49  *****************************************************************************/
50 static int  Open ( vlc_object_t * );
51 static void Close( vlc_object_t * );
52
53 #define ROOMSIZE_TEXT N_("Room size")
54 #define ROOMSIZE_LONGTEXT N_("Defines the virtual surface of the room" \
55                              " emulated by the filter." )
56
57 #define WIDTH_TEXT N_("Room width")
58 #define WIDTH_LONGTEXT N_("Width of the virtual room")
59
60 #define WET_TEXT N_("Wet")
61 #define WET_LONGTEXT NULL
62
63 #define DRY_TEXT N_("Dry")
64 #define DRY_LONGTEXT NULL
65
66 #define DAMP_TEXT N_("Damp")
67 #define DAMP_LONGTEXT NULL
68
69 vlc_module_begin ()
70     set_description( N_("Audio Spatializer") )
71     set_shortname( N_("Spatializer" ) )
72     set_capability( "audio filter", 0 )
73     set_category( CAT_AUDIO )
74     set_subcategory( SUBCAT_AUDIO_AFILTER )
75
76     set_callbacks( Open, Close )
77     add_shortcut( "spatializer" )
78     add_float( "spatializer-roomsize", 1.05, NULL, ROOMSIZE_TEXT,
79                ROOMSIZE_LONGTEXT, true )
80     add_float( "spatializer-width", 10., NULL, WIDTH_TEXT,WIDTH_LONGTEXT, true )
81     add_float( "spatializer-wet", 3., NULL, WET_TEXT,WET_LONGTEXT, true )
82     add_float( "spatializer-dry", 2., NULL, DRY_TEXT,DRY_LONGTEXT, true )
83     add_float( "spatializer-damp", 1., NULL, DAMP_TEXT,DAMP_LONGTEXT, true )
84 vlc_module_end ()
85
86 /*****************************************************************************
87  * Local prototypes
88  *****************************************************************************/
89 struct aout_filter_sys_t
90 {
91     vlc_mutex_t lock;
92     revmodel *p_reverbm;
93 };
94
95 class CLocker
96 {
97 public:
98     CLocker( vlc_mutex_t *p_lock_to_manage ) : p_lock(p_lock_to_manage)
99     {
100         vlc_mutex_lock( p_lock );
101     }
102     ~CLocker()
103     {
104         vlc_mutex_unlock( p_lock );
105     }
106 private:
107     vlc_mutex_t *p_lock;
108 };
109
110 #define DECLARECB(fn) static int fn (vlc_object_t *,char const *, \
111                                      vlc_value_t, vlc_value_t, void *)
112 DECLARECB( RoomCallback  );
113 DECLARECB( WetCallback   );
114 DECLARECB( DryCallback   );
115 DECLARECB( DampCallback  );
116 DECLARECB( WidthCallback );
117
118 #undef  DECLARECB
119
120 struct callback_s {
121   const char *psz_name;
122   int (*fp_callback)(vlc_object_t *,const char *,
123                      vlc_value_t,vlc_value_t,void *);
124   void (revmodel::* fp_set)(float);
125 };
126
127 static const callback_s callbacks[] = {
128     { "spatializer-roomsize", RoomCallback,  &revmodel::setroomsize },
129     { "spatializer-width",    WidthCallback, &revmodel::setwidth },
130     { "spatializer-wet",      WetCallback,   &revmodel::setwet },
131     { "spatializer-dry",      DryCallback,   &revmodel::setdry },
132     { "spatializer-damp",     DampCallback,  &revmodel::setdamp }
133 };
134 enum { num_callbacks=sizeof(callbacks)/sizeof(callback_s) };
135
136 static void DoWork( aout_instance_t *, aout_filter_t *,
137                     aout_buffer_t *, aout_buffer_t * );
138
139 /*****************************************************************************
140  * Open:
141  *****************************************************************************/
142 static int Open( vlc_object_t *p_this )
143 {
144     aout_filter_t     *p_filter = (aout_filter_t *)p_this;
145     aout_filter_sys_t *p_sys;
146     aout_instance_t   *p_aout = (aout_instance_t *)p_filter->p_parent;
147     bool               b_fit = true;
148     msg_Dbg( p_this, "Opening filter spatializer" );
149
150     if( p_filter->input.i_format != VLC_CODEC_FL32 ||
151         p_filter->output.i_format != VLC_CODEC_FL32 )
152     {
153         b_fit = false;
154         p_filter->input.i_format = VLC_CODEC_FL32;
155         p_filter->output.i_format = VLC_CODEC_FL32;
156         msg_Warn( p_filter, "bad input or output format" );
157     }
158     if ( !AOUT_FMTS_SIMILAR( &p_filter->input, &p_filter->output ) )
159     {
160         b_fit = false;
161         memcpy( &p_filter->output, &p_filter->input,
162                 sizeof(audio_sample_format_t) );
163         msg_Warn( p_filter, "input and output formats are not similar" );
164     }
165
166     if ( ! b_fit )
167     {
168         return VLC_EGENERIC;
169     }
170
171     p_filter->pf_do_work = DoWork;
172     p_filter->b_in_place = true;
173
174      /* Allocate structure */
175     p_sys = p_filter->p_sys = (aout_filter_sys_t*)malloc( sizeof( aout_filter_sys_t ) );
176     if( !p_sys )
177         return VLC_ENOMEM;
178
179     /* Force new to return 0 on failure instead of throwing, since we don't
180        want an exception to leak back to C code. Bad things would happen. */
181     p_sys->p_reverbm = new (nothrow) revmodel;
182     if( !p_sys->p_reverbm )
183     {
184         free( p_sys );
185         return VLC_ENOMEM;
186     }
187
188     vlc_mutex_init( &p_sys->lock );
189
190     for(unsigned i=0;i<num_callbacks;++i)
191     {
192         /* NOTE: C++ pointer-to-member function call from table lookup. */
193         (p_sys->p_reverbm->*(callbacks[i].fp_set))
194             (var_CreateGetFloatCommand(p_aout,callbacks[i].psz_name));
195         var_AddCallback( p_aout, callbacks[i].psz_name,
196                          callbacks[i].fp_callback, p_sys );
197     }
198
199     return VLC_SUCCESS;
200 }
201
202 /*****************************************************************************
203  * Close: close the plugin
204  *****************************************************************************/
205 static void Close( vlc_object_t *p_this )
206 {
207     aout_filter_t     *p_filter = (aout_filter_t *)p_this;
208     aout_filter_sys_t *p_sys = p_filter->p_sys;
209     aout_instance_t *p_aout = (aout_instance_t *)p_filter->p_parent;
210
211     /* Delete the callbacks */
212     for(unsigned i=0;i<num_callbacks;++i)
213     {
214         var_DelCallback( p_aout, callbacks[i].psz_name,
215                          callbacks[i].fp_callback, p_sys );
216     }
217
218     delete p_sys->p_reverbm;
219     vlc_mutex_destroy( &p_sys->lock );
220     free( p_sys );
221     msg_Dbg( p_this, "Closing filter spatializer" );
222 }
223
224 /*****************************************************************************
225  * SpatFilter: process samples buffer
226  * DoWork: call SpatFilter
227  *****************************************************************************/
228
229 static inline
230 void SpatFilter( aout_instance_t *p_aout, aout_filter_t *p_filter,
231                  float *out, float *in, int i_samples, int i_channels )
232 {
233     aout_filter_sys_t *p_sys = p_filter->p_sys;
234     CLocker locker( &p_sys->lock );
235
236     int i, ch;
237     for( i = 0; i < i_samples; i++ )
238     {
239         for( ch = 0 ; ch < 2; ch++)
240         {
241             in[ch] = in[ch] * SPAT_AMP;
242         }
243         p_sys->p_reverbm->processreplace( in, out , 1, i_channels);
244         in  += i_channels;
245         out += i_channels;
246     }
247 }
248
249 static void DoWork( aout_instance_t * p_aout, aout_filter_t * p_filter,
250                     aout_buffer_t * p_in_buf, aout_buffer_t * p_out_buf )
251 {
252     p_out_buf->i_nb_samples = p_in_buf->i_nb_samples;
253     p_out_buf->i_nb_bytes = p_in_buf->i_nb_bytes;
254
255     SpatFilter( p_aout, p_filter, (float*)p_out_buf->p_buffer,
256                (float*)p_in_buf->p_buffer, p_in_buf->i_nb_samples,
257                aout_FormatNbChannels( &p_filter->input ) );
258 }
259
260
261 /*****************************************************************************
262  * Variables callbacks
263  *****************************************************************************/
264
265 static int RoomCallback( vlc_object_t *p_this, char const *psz_cmd,
266                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
267 {
268     (void)psz_cmd;    (void)oldval;
269     aout_filter_sys_t *p_sys = (aout_filter_sys_t*)p_data;
270     CLocker locker( &p_sys->lock );
271
272     p_sys->p_reverbm->setroomsize(newval.f_float);
273     msg_Dbg( p_this, "room size is now %3.1f", newval.f_float );
274     return VLC_SUCCESS;
275 }
276
277 static int WidthCallback( vlc_object_t *p_this, char const *psz_cmd,
278                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
279 {
280     (void)psz_cmd;    (void)oldval;
281     aout_filter_sys_t *p_sys = (aout_filter_sys_t*)p_data;
282     CLocker locker( &p_sys->lock );
283
284     p_sys->p_reverbm->setwidth(newval.f_float);
285     msg_Dbg( p_this, "width is now %3.1f", newval.f_float );
286     return VLC_SUCCESS;
287 }
288 static int WetCallback( vlc_object_t *p_this, char const *psz_cmd,
289                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
290 {
291     (void)psz_cmd;    (void)oldval;
292     aout_filter_sys_t *p_sys = (aout_filter_sys_t*)p_data;
293     CLocker locker( &p_sys->lock );
294
295     p_sys->p_reverbm->setwet(newval.f_float);
296     msg_Dbg( p_this, "'wet' value is now %3.1f", newval.f_float );
297     return VLC_SUCCESS;
298 }
299 static int DryCallback( vlc_object_t *p_this, char const *psz_cmd,
300                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
301 {
302     (void)psz_cmd;    (void)oldval;
303     aout_filter_sys_t *p_sys = (aout_filter_sys_t*)p_data;
304     CLocker locker( &p_sys->lock );
305
306     p_sys->p_reverbm->setdry(newval.f_float);
307     msg_Dbg( p_this, "'dry' value is now %3.1f", newval.f_float );
308     return VLC_SUCCESS;
309 }
310 static int DampCallback( vlc_object_t *p_this, char const *psz_cmd,
311                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
312 {
313     (void)psz_cmd;    (void)oldval;
314     aout_filter_sys_t *p_sys = (aout_filter_sys_t*)p_data;
315     CLocker locker( &p_sys->lock );
316
317     p_sys->p_reverbm->setdamp(newval.f_float);
318     msg_Dbg( p_this, "'damp' value is now %3.1f", newval.f_float );
319     return VLC_SUCCESS;
320 }
321