1 /*****************************************************************************
2  * spatializer.cpp: sound reverberation
3  *****************************************************************************
4  * Copyright (C) 2004, 2006, 2007 VLC authors and VideoLAN
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 it
13  * under the terms of the GNU Lesser General Public License as published by
14  * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public License
23  * along with this program; if not, write to the Free Software Foundation,
24  * Inc., 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 #include <vlc_filter.h>
44 
45 #include "revmodel.hpp"
46 #define SPAT_AMP 0.3
47 
48 /*****************************************************************************
49  * Module descriptor
50  *****************************************************************************/
51 static int  Open ( vlc_object_t * );
52 static void Close( vlc_object_t * );
53 
54 #define ROOMSIZE_TEXT N_("Room size")
55 #define ROOMSIZE_LONGTEXT N_("Defines the virtual surface of the room" \
56                              " emulated by the filter." )
57 
58 #define WIDTH_TEXT N_("Room width")
59 #define WIDTH_LONGTEXT N_("Width of the virtual room")
60 
61 #define WET_TEXT N_("Wet")
62 #define WET_LONGTEXT NULL
63 
64 #define DRY_TEXT N_("Dry")
65 #define DRY_LONGTEXT NULL
66 
67 #define DAMP_TEXT N_("Damp")
68 #define DAMP_LONGTEXT NULL
69 
70 vlc_module_begin ()
71     set_description( N_("Audio Spatializer") )
72     set_shortname( N_("Spatializer" ) )
73     set_capability( "audio filter", 0 )
74     set_category( CAT_AUDIO )
75     set_subcategory( SUBCAT_AUDIO_AFILTER )
76 
77     set_callbacks( Open, Close )
78     add_shortcut( "spatializer" )
79     add_float_with_range( "spatializer-roomsize", 0.85, 0., 1.1,
80                             ROOMSIZE_TEXT, ROOMSIZE_LONGTEXT, false )
81     add_float_with_range( "spatializer-width", 1,     0.,  1.,
82                             WIDTH_TEXT,WIDTH_LONGTEXT, false )
83     add_float_with_range( "spatializer-wet",   0.4,   0.,  1.,
84                             WET_TEXT,WET_LONGTEXT, false )
85     add_float_with_range( "spatializer-dry",   0.5,   0.,  1.,
86                             DRY_TEXT,DRY_LONGTEXT, false )
87     add_float_with_range( "spatializer-damp",  0.5,   0.,  1.,
88                             DAMP_TEXT,DAMP_LONGTEXT, false )
89 vlc_module_end ()
90 
91 /*****************************************************************************
92  * Local prototypes
93  *****************************************************************************/
94 struct filter_sys_t
95 {
96     vlc_mutex_t lock;
97     revmodel *p_reverbm;
98 };
99 
100 #define DECLARECB(fn) static int fn (vlc_object_t *,char const *, \
101                                      vlc_value_t, vlc_value_t, void *)
102 DECLARECB( RoomCallback  );
103 DECLARECB( WetCallback   );
104 DECLARECB( DryCallback   );
105 DECLARECB( DampCallback  );
106 DECLARECB( WidthCallback );
107 
108 #undef  DECLARECB
109 
110 struct callback_s {
111   const char *psz_name;
112   int (*fp_callback)(vlc_object_t *,const char *,
113                      vlc_value_t,vlc_value_t,void *);
114   void (revmodel::* fp_set)(float);
115 };
116 
117 static const callback_s callbacks[] = {
118     { "spatializer-roomsize", RoomCallback,  &revmodel::setroomsize },
119     { "spatializer-width",    WidthCallback, &revmodel::setwidth },
120     { "spatializer-wet",      WetCallback,   &revmodel::setwet },
121     { "spatializer-dry",      DryCallback,   &revmodel::setdry },
122     { "spatializer-damp",     DampCallback,  &revmodel::setdamp }
123 };
124 enum { num_callbacks=sizeof(callbacks)/sizeof(callback_s) };
125 
126 static block_t *DoWork( filter_t *, block_t * );
127 
128 /*****************************************************************************
129  * Open:
130  *****************************************************************************/
Open(vlc_object_t * p_this)131 static int Open( vlc_object_t *p_this )
132 {
133     filter_t     *p_filter = (filter_t *)p_this;
134     filter_sys_t *p_sys;
135     vlc_object_t *p_aout = p_filter->obj.parent;
136 
137      /* Allocate structure */
138     p_sys = p_filter->p_sys = (filter_sys_t*)malloc( sizeof( *p_sys ) );
139     if( !p_sys )
140         return VLC_ENOMEM;
141 
142     /* Force new to return 0 on failure instead of throwing, since we don't
143        want an exception to leak back to C code. Bad things would happen. */
144     p_sys->p_reverbm = new (nothrow) revmodel;
145     if( !p_sys->p_reverbm )
146     {
147         free( p_sys );
148         return VLC_ENOMEM;
149     }
150 
151     vlc_mutex_init( &p_sys->lock );
152 
153     for(unsigned i=0;i<num_callbacks;++i)
154     {
155         /* NOTE: C++ pointer-to-member function call from table lookup. */
156         (p_sys->p_reverbm->*(callbacks[i].fp_set))
157             (var_CreateGetFloatCommand(p_aout,callbacks[i].psz_name));
158         var_AddCallback( p_aout, callbacks[i].psz_name,
159                          callbacks[i].fp_callback, p_sys );
160     }
161 
162     p_filter->fmt_in.audio.i_format = VLC_CODEC_FL32;
163     aout_FormatPrepare(&p_filter->fmt_in.audio);
164     p_filter->fmt_out.audio = p_filter->fmt_in.audio;
165     p_filter->pf_audio_filter = DoWork;
166     return VLC_SUCCESS;
167 }
168 
169 /*****************************************************************************
170  * Close: close the plugin
171  *****************************************************************************/
Close(vlc_object_t * p_this)172 static void Close( vlc_object_t *p_this )
173 {
174     filter_t     *p_filter = (filter_t *)p_this;
175     filter_sys_t *p_sys = p_filter->p_sys;
176     vlc_object_t *p_aout = p_filter->obj.parent;
177 
178     /* Delete the callbacks */
179     for(unsigned i=0;i<num_callbacks;++i)
180     {
181         var_DelCallback( p_aout, callbacks[i].psz_name,
182                          callbacks[i].fp_callback, p_sys );
183     }
184 
185     delete p_sys->p_reverbm;
186     vlc_mutex_destroy( &p_sys->lock );
187     free( p_sys );
188     msg_Dbg( p_this, "Closing filter spatializer" );
189 }
190 
191 /*****************************************************************************
192  * SpatFilter: process samples buffer
193  * DoWork: call SpatFilter
194  *****************************************************************************/
195 
SpatFilter(filter_t * p_filter,float * out,float * in,unsigned i_samples,unsigned i_channels)196 static void SpatFilter( filter_t *p_filter, float *out, float *in,
197                         unsigned i_samples, unsigned i_channels )
198 {
199     filter_sys_t *p_sys = p_filter->p_sys;
200     vlc_mutex_locker locker( &p_sys->lock );
201 
202     for( unsigned i = 0; i < i_samples; i++ )
203     {
204         for( unsigned ch = 0 ; ch < 2; ch++)
205         {
206             in[ch] = in[ch] * SPAT_AMP;
207         }
208         p_sys->p_reverbm->processreplace( in, out , 1, i_channels);
209         in  += i_channels;
210         out += i_channels;
211     }
212 }
213 
DoWork(filter_t * p_filter,block_t * p_in_buf)214 static block_t *DoWork( filter_t * p_filter, block_t * p_in_buf )
215 {
216     SpatFilter( p_filter, (float*)p_in_buf->p_buffer,
217                (float*)p_in_buf->p_buffer, p_in_buf->i_nb_samples,
218                aout_FormatNbChannels( &p_filter->fmt_in.audio ) );
219     return p_in_buf;
220 }
221 
222 
223 /*****************************************************************************
224  * Variables callbacks
225  *****************************************************************************/
226 
RoomCallback(vlc_object_t * p_this,char const *,vlc_value_t,vlc_value_t newval,void * p_data)227 static int RoomCallback( vlc_object_t *p_this, char const *,
228                          vlc_value_t, vlc_value_t newval, void *p_data )
229 {
230     filter_sys_t *p_sys = (filter_sys_t*)p_data;
231     vlc_mutex_locker locker( &p_sys->lock );
232 
233     p_sys->p_reverbm->setroomsize(newval.f_float);
234     msg_Dbg( p_this, "room size is now %3.1f", newval.f_float );
235     return VLC_SUCCESS;
236 }
237 
WidthCallback(vlc_object_t * p_this,char const *,vlc_value_t,vlc_value_t newval,void * p_data)238 static int WidthCallback( vlc_object_t *p_this, char const *,
239                           vlc_value_t, vlc_value_t newval, void *p_data )
240 {
241     filter_sys_t *p_sys = (filter_sys_t*)p_data;
242     vlc_mutex_locker locker( &p_sys->lock );
243 
244     p_sys->p_reverbm->setwidth(newval.f_float);
245     msg_Dbg( p_this, "width is now %3.1f", newval.f_float );
246     return VLC_SUCCESS;
247 }
248 
WetCallback(vlc_object_t * p_this,char const *,vlc_value_t,vlc_value_t newval,void * p_data)249 static int WetCallback( vlc_object_t *p_this, char const *,
250                         vlc_value_t, vlc_value_t newval, void *p_data )
251 {
252     filter_sys_t *p_sys = (filter_sys_t*)p_data;
253     vlc_mutex_locker locker( &p_sys->lock );
254 
255     p_sys->p_reverbm->setwet(newval.f_float);
256     msg_Dbg( p_this, "'wet' value is now %3.1f", newval.f_float );
257     return VLC_SUCCESS;
258 }
259 
DryCallback(vlc_object_t * p_this,char const *,vlc_value_t,vlc_value_t newval,void * p_data)260 static int DryCallback( vlc_object_t *p_this, char const *,
261                         vlc_value_t, vlc_value_t newval, void *p_data )
262 {
263     filter_sys_t *p_sys = (filter_sys_t*)p_data;
264     vlc_mutex_locker locker( &p_sys->lock );
265 
266     p_sys->p_reverbm->setdry(newval.f_float);
267     msg_Dbg( p_this, "'dry' value is now %3.1f", newval.f_float );
268     return VLC_SUCCESS;
269 }
270 
DampCallback(vlc_object_t * p_this,char const *,vlc_value_t,vlc_value_t newval,void * p_data)271 static int DampCallback( vlc_object_t *p_this, char const *,
272                          vlc_value_t, vlc_value_t newval, void *p_data )
273 {
274     filter_sys_t *p_sys = (filter_sys_t*)p_data;
275     vlc_mutex_locker locker( &p_sys->lock );
276 
277     p_sys->p_reverbm->setdamp(newval.f_float);
278     msg_Dbg( p_this, "'damp' value is now %3.1f", newval.f_float );
279     return VLC_SUCCESS;
280 }
281 
282