]> git.sesse.net Git - vlc/blob - modules/audio_filter/equalizer.c
*.nib: continuous sliders
[vlc] / modules / audio_filter / equalizer.c
1 /*****************************************************************************
2  * equalizer.c:
3  *****************************************************************************
4  * Copyright (C) 2004 VideoLAN
5  * $Id$
6  *
7  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>                                      /* malloc(), free() */
28 #include <math.h>
29
30 #include <vlc/vlc.h>
31
32 #include <vlc/aout.h>
33 #include "aout_internal.h"
34
35 #include "equalizer_presets.h"
36 /* TODO:
37  *  - add tables for other rates ( 22500, 11250, ...)
38  *  - optimize a bit (you can hardly do slower ;)
39  *  - add tables for more bands (15 and 32 would be cool), maybe with auto coeffs
40  *  computation (not too hard once the Q is found).
41  *  - support for external preset
42  *  - callback to handle preset changes on the fly
43  *  - ...
44  */
45
46 /*****************************************************************************
47  * Module descriptor
48  *****************************************************************************/
49 static int  Open ( vlc_object_t * );
50 static void Close( vlc_object_t * );
51
52 #define PRESET_TEXT N_( "Equalizer preset" )
53 #define PRESET_LONGTEXT PRESET_TEXT
54
55 #define BANDS_TEXT N_( "Bands gain")
56 #define BANDS_LONGTEXT N_( "Override preset bands gain in dB (-20 ... 20)" )
57
58 #define TWOPASS_TEXT N_( "Two pass" )
59 #define TWOPASS_LONGTEXT N_( "Filter twice the audio" )
60
61 #define PREAMP_TEXT N_("Global gain" )
62 #define PREAMP_LONGTEXT N_("Set the global gain in dB (-20 ... 20)" )
63
64 vlc_module_begin();
65     set_description( _("Equalizer 10 bands") );
66     set_capability( "audio filter", 0 );
67     add_string( "equalizer-preset", "flat", NULL, PRESET_TEXT,
68                 PRESET_LONGTEXT, VLC_TRUE );
69         change_string_list( preset_list, preset_list_text, 0 );
70     add_string( "equalizer-bands", NULL, NULL, BANDS_TEXT,
71                 BANDS_LONGTEXT, VLC_TRUE );
72     add_bool( "equalizer-2pass", 0, NULL, TWOPASS_TEXT,
73               TWOPASS_LONGTEXT, VLC_TRUE );
74     add_float( "equalizer-preamp", 0.0, NULL, PREAMP_TEXT,
75                PREAMP_LONGTEXT, VLC_TRUE );
76     set_callbacks( Open, Close );
77     add_shortcut( "equalizer" );
78 vlc_module_end();
79
80 /*****************************************************************************
81  * Local prototypes
82  *****************************************************************************/
83 typedef struct aout_filter_sys_t
84 {
85     /* Filter static config */
86     int i_band;
87     float *f_alpha;
88     float *f_beta;
89     float *f_gamma;
90
91     float f_newpreamp;
92     char *psz_newbands;
93     vlc_bool_t b_first;
94
95     /* Filter dyn config */
96     float *f_amp;   /* Per band amp */
97     float f_gamp;   /* Global preamp */
98     vlc_bool_t b_2eqz;
99
100     /* Filter state */
101     float x[32][2];
102     float y[32][128][2];
103
104     /* Second filter state */
105     float x2[32][2];
106     float y2[32][128][2];
107
108 } aout_filter_sys_t;
109
110 static void DoWork( aout_instance_t *, aout_filter_t *,
111                     aout_buffer_t *, aout_buffer_t * );
112
113 #define EQZ_IN_FACTOR (0.25)
114 static int  EqzInit( aout_filter_t *, int );
115 static void EqzFilter( aout_instance_t *,aout_filter_t *, float *, float *,
116                         int, int );
117 static void EqzClean( aout_filter_t * );
118
119 static int PresetCallback( vlc_object_t *, char const *,
120                                            vlc_value_t, vlc_value_t, void * );
121 static int PreampCallback( vlc_object_t *, char const *,
122                                            vlc_value_t, vlc_value_t, void * );
123 static int BandsCallback ( vlc_object_t *, char const *,
124                                            vlc_value_t, vlc_value_t, void * );
125
126
127
128 /*****************************************************************************
129  * Open:
130  *****************************************************************************/
131 static int Open( vlc_object_t *p_this )
132 {
133     aout_filter_t     *p_filter = (aout_filter_t *)p_this;
134     aout_filter_sys_t *p_sys;
135
136     if( p_filter->input.i_format != VLC_FOURCC('f','l','3','2' ) ||
137         p_filter->output.i_format != VLC_FOURCC('f','l','3','2') )
138     {
139         msg_Warn( p_filter, "Bad input or output format" );
140         return VLC_EGENERIC;
141     }
142     if ( !AOUT_FMTS_SIMILAR( &p_filter->input, &p_filter->output ) )
143     {
144         msg_Warn( p_filter, "input and output formats are not similar" );
145         return VLC_EGENERIC;
146     }
147
148     p_filter->pf_do_work = DoWork;
149     p_filter->b_in_place = VLC_TRUE;
150
151     /* Allocate structure */
152     p_sys = p_filter->p_sys = malloc( sizeof( aout_filter_sys_t ) );
153
154     if( EqzInit( p_filter, p_filter->input.i_rate ) )
155         return VLC_EGENERIC;
156
157     return VLC_SUCCESS;
158 }
159
160 /*****************************************************************************
161  * Close: close the plugin
162  *****************************************************************************/
163 static void Close( vlc_object_t *p_this )
164 {
165     aout_filter_t     *p_filter = (aout_filter_t *)p_this;
166     aout_filter_sys_t *p_sys = p_filter->p_sys;
167
168     EqzClean( p_filter );
169     free( p_sys );
170 }
171
172 /*****************************************************************************
173  * DoWork: process samples buffer
174  *****************************************************************************
175  *
176  *****************************************************************************/
177 static void DoWork( aout_instance_t * p_aout, aout_filter_t * p_filter,
178                     aout_buffer_t * p_in_buf, aout_buffer_t * p_out_buf )
179 {
180     p_out_buf->i_nb_samples = p_in_buf->i_nb_samples;
181     p_out_buf->i_nb_bytes = p_in_buf->i_nb_bytes;
182
183     EqzFilter( p_aout, p_filter, (float*)p_out_buf->p_buffer,
184                (float*)p_in_buf->p_buffer, p_in_buf->i_nb_samples,
185                aout_FormatNbChannels( &p_filter->input ) );
186 }
187
188 /*****************************************************************************
189  * Equalizer stuff
190  *****************************************************************************/
191 typedef struct
192 {
193     int   i_band;
194
195     struct
196     {
197         float f_frequency;
198         float f_alpha;
199         float f_beta;
200         float f_gamma;
201     } band[EQZ_BANDS_MAX];
202
203 } eqz_config_t;
204
205 /* Value from equ-xmms */
206 static const eqz_config_t eqz_config_44100_10b =
207 {
208     10,
209     {
210         {    60, 0.003013, 0.993973, 1.993901 },
211         {   170, 0.008490, 0.983019, 1.982437 },
212         {   310, 0.015374, 0.969252, 1.967331 },
213         {   600, 0.029328, 0.941343, 1.934254 },
214         {  1000, 0.047918, 0.904163, 1.884869 },
215         {  3000, 0.130408, 0.739184, 1.582718 },
216         {  6000, 0.226555, 0.546889, 1.015267 },
217         { 12000, 0.344937, 0.310127, -0.181410 },
218         { 14000, 0.366438, 0.267123, -0.521151 },
219         { 16000, 0.379009, 0.241981, -0.808451 },
220     }
221 };
222 static const eqz_config_t eqz_config_48000_10b =
223 {
224     10,
225     {
226         {    60, 0.002769, 0.994462, 1.994400 },
227         {   170, 0.007806, 0.984388, 1.983897 },
228         {   310, 0.014143, 0.971714, 1.970091 },
229         {   600, 0.027011, 0.945978, 1.939979 },
230         {  1000, 0.044203, 0.911595, 1.895241 },
231         {  3000, 0.121223, 0.757553, 1.623767 },
232         {  6000, 0.212888, 0.574224, 1.113145 },
233         { 12000, 0.331347, 0.337307, 0.000000 },
234         { 14000, 0.355263, 0.289473, -0.333740 },
235         { 16000, 0.371900, 0.256201, -0.628100 }
236     }
237 };
238
239 static inline float EqzConvertdB( float db )
240 {
241     /* Map it to gain,
242      * (we do as if the input of iir is /EQZ_IN_FACTOR, but in fact it's the non iir data that is *EQZ_IN_FACTOR)
243      * db = 20*log( out / in ) with out = in + amp*iir(i/EQZ_IN_FACTOR)
244      * or iir(i) == i for the center freq so
245      * db = 20*log( 1 + amp/EQZ_IN_FACTOR )
246      * -> amp = EQZ_IN_FACTOR*(10^(db/20) - 1)
247      **/
248
249     if( db < -20.0 )
250         db = -20.0;
251     else if(  db > 20.0 )
252         db = 20.0;
253     return EQZ_IN_FACTOR * ( pow( 10, db / 20.0 ) - 1.0 );
254 }
255
256 static int EqzInit( aout_filter_t *p_filter, int i_rate )
257 {
258     aout_filter_sys_t *p_sys = p_filter->p_sys;
259     const eqz_config_t *p_cfg;
260     int i, ch;
261     vlc_value_t val1, val2, val3;
262     aout_instance_t *p_aout = (aout_instance_t *)p_filter->p_parent;
263
264     /* Select the config */
265     if( i_rate == 48000 )
266     {
267         p_cfg = &eqz_config_48000_10b;
268     }
269     else if( i_rate == 44100 )
270     {
271         p_cfg = &eqz_config_44100_10b;
272     }
273     else
274     {
275         /* TODO compute the coeffs on the fly */
276         msg_Err( p_filter, "unsupported rate" );
277         return VLC_EGENERIC;
278     }
279
280     /* Create the static filter config */
281     p_sys->i_band = p_cfg->i_band;
282     p_sys->f_alpha = malloc( p_sys->i_band * sizeof(float) );
283     p_sys->f_beta  = malloc( p_sys->i_band * sizeof(float) );
284     p_sys->f_gamma = malloc( p_sys->i_band * sizeof(float) );
285     for( i = 0; i < p_sys->i_band; i++ )
286     {
287         p_sys->f_alpha[i] = p_cfg->band[i].f_alpha;
288         p_sys->f_beta[i]  = p_cfg->band[i].f_beta;
289         p_sys->f_gamma[i] = p_cfg->band[i].f_gamma;
290     }
291
292     /* Filter dyn config */
293     p_sys->b_2eqz = VLC_FALSE;
294     p_sys->f_gamp = 1.0;
295     p_sys->f_amp   = malloc( p_sys->i_band * sizeof(float) );
296     for( i = 0; i < p_sys->i_band; i++ )
297     {
298         p_sys->f_amp[i] = 0.0;
299     }
300
301     /* Filter state */
302     for( ch = 0; ch < 32; ch++ )
303     {
304         p_sys->x[ch][0]  =
305         p_sys->x[ch][1]  =
306         p_sys->x2[ch][0] =
307         p_sys->x2[ch][1] = 0.0;
308
309         for( i = 0; i < p_sys->i_band; i++ )
310         {
311             p_sys->y[ch][i][0]  =
312             p_sys->y[ch][i][1]  =
313             p_sys->y2[ch][i][0] =
314             p_sys->y2[ch][i][1] = 0.0;
315         }
316     }
317
318     var_CreateGetString( p_aout,"equalizer-bands" );
319     var_CreateGetString( p_aout, "equalizer-preset" );
320
321     p_sys->b_2eqz = var_CreateGetBool( p_aout, "equalizer-2pass" );
322
323     var_CreateGetFloat( p_aout, "equalizer-preamp" );
324
325     /* Get initial values */
326     var_Get( p_aout, "equalizer-preset", &val1 );
327     var_Get( p_aout, "equalizer-bands", &val2 );
328     var_Get( p_aout, "equalizer-preamp", &val3 );
329
330     p_sys->b_first = VLC_TRUE;
331     PresetCallback( VLC_OBJECT( p_aout ), NULL, val1, val1, p_sys );
332     BandsCallback(  VLC_OBJECT( p_aout ), NULL, val2, val2, p_sys );
333     PreampCallback( VLC_OBJECT( p_aout ), NULL, val3, val3, p_sys );
334     p_sys->b_first = VLC_FALSE;
335
336     /* Register preset bands (for intf) if : */
337     /* We have no bands info --> the preset info must be given to the intf */
338     /* or The bands info matches the preset */
339     if (p_sys->psz_newbands == NULL)
340     {
341         msg_Err(p_filter, "No preset selected");
342         return (VLC_EGENERIC);
343     }
344     if( ( *(val2.psz_string) &&
345         strstr( p_sys->psz_newbands, val2.psz_string ) ) || !*val2.psz_string )
346     {
347         var_SetString( p_aout, "equalizer-bands", p_sys->psz_newbands );
348         var_SetFloat( p_aout, "equalizer-preamp", p_sys->f_newpreamp );
349     }
350
351     /* Add our own callbacks */
352     var_AddCallback( p_aout, "equalizer-preset", PresetCallback, p_sys );
353     var_AddCallback( p_aout, "equalizer-bands", BandsCallback, p_sys );
354     var_AddCallback( p_aout, "equalizer-preamp", PreampCallback, p_sys );
355
356     msg_Dbg( p_filter, "equalizer loaded for %d Hz with %d bands %d pass",
357                         i_rate, p_sys->i_band, p_sys->b_2eqz ? 2 : 1 );
358     for( i = 0; i < p_sys->i_band; i++ )
359     {
360         msg_Dbg( p_filter, "   %d Hz -> factor:%f alpha:%f beta:%f gamma:%f",
361                  (int)p_cfg->band[i].f_frequency, p_sys->f_amp[i],
362                  p_sys->f_alpha[i], p_sys->f_beta[i], p_sys->f_gamma[i]);
363     }
364     return VLC_SUCCESS;
365 }
366
367 static void EqzFilter( aout_instance_t *p_aout,
368                        aout_filter_t *p_filter, float *out, float *in,
369                        int i_samples, int i_channels )
370 {
371     aout_filter_sys_t *p_sys = p_filter->p_sys;
372     int i, ch, j;
373
374     for( i = 0; i < i_samples; i++ )
375     {
376         for( ch = 0; ch < i_channels; ch++ )
377         {
378             const float x = in[ch];
379             float o = 0.0;
380
381             for( j = 0; j < p_sys->i_band; j++ )
382             {
383                 float y = p_sys->f_alpha[j] * ( x - p_sys->x[ch][1] ) +
384                           p_sys->f_gamma[j] * p_sys->y[ch][j][0] -
385                           p_sys->f_beta[j]  * p_sys->y[ch][j][1];
386
387                 p_sys->y[ch][j][1] = p_sys->y[ch][j][0];
388                 p_sys->y[ch][j][0] = y;
389
390                 o += y * p_sys->f_amp[j];
391             }
392             p_sys->x[ch][1] = p_sys->x[ch][0];
393             p_sys->x[ch][0] = x;
394
395             /* Second filter */
396             if( p_sys->b_2eqz )
397             {
398                 const float x2 = EQZ_IN_FACTOR * x + o;
399                 o = 0.0;
400                 for( j = 0; j < p_sys->i_band; j++ )
401                 {
402                     float y = p_sys->f_alpha[j] * ( x2 - p_sys->x2[ch][1] ) +
403                               p_sys->f_gamma[j] * p_sys->y2[ch][j][0] -
404                               p_sys->f_beta[j]  * p_sys->y2[ch][j][1];
405
406                     p_sys->y2[ch][j][1] = p_sys->y2[ch][j][0];
407                     p_sys->y2[ch][j][0] = y;
408
409                     o += y * p_sys->f_amp[j];
410                 }
411                 p_sys->x2[ch][1] = p_sys->x2[ch][0];
412                 p_sys->x2[ch][0] = x2;
413
414                 /* We add source PCM + filtered PCM */
415                 out[ch] = p_sys->f_gamp *( EQZ_IN_FACTOR * x2 + o );
416             }
417             else
418             {
419                 /* We add source PCM + filtered PCM */
420                 out[ch] = p_sys->f_gamp *( EQZ_IN_FACTOR * x + o );
421             }
422         }
423
424         in  += i_channels;
425         out += i_channels;
426     }
427 }
428
429 static void EqzClean( aout_filter_t *p_filter )
430 {
431     aout_filter_sys_t *p_sys = p_filter->p_sys;
432
433     var_DelCallback( (aout_instance_t *)p_filter->p_parent,
434                         "equalizer-bands", BandsCallback, p_sys );
435     var_DelCallback( (aout_instance_t *)p_filter->p_parent,
436                         "equalizer-preset", PresetCallback, p_sys );
437     var_DelCallback( (aout_instance_t *)p_filter->p_parent,
438                         "equalizer-preamp", PreampCallback, p_sys );
439
440     free( p_sys->f_alpha );
441     free( p_sys->f_beta );
442     free( p_sys->f_gamma );
443
444     free( p_sys->f_amp );
445 }
446
447
448 static int PresetCallback( vlc_object_t *p_this, char const *psz_cmd,
449                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
450 {
451     aout_filter_sys_t *p_sys = (aout_filter_sys_t *)p_data;
452     aout_instance_t *p_aout = (aout_instance_t *)p_this;
453
454     char *psz_preset = newval.psz_string;
455     char psz_newbands[120];
456
457     memset( psz_newbands, 0, 120 );
458
459     if( *psz_preset && p_sys->i_band == 10 )
460     {
461         int i;
462         /* */
463         for( i = 0; eqz_preset_10b[i] != NULL; i++ )
464         {
465             if( !strcasecmp( eqz_preset_10b[i]->psz_name, psz_preset ) )
466             {
467                 int j;
468                 p_sys->f_gamp *= pow( 10, eqz_preset_10b[i]->f_preamp / 20.0 );
469                 for( j = 0; j < p_sys->i_band; j++ )
470                 {
471                     p_sys->f_amp[j] = EqzConvertdB(
472                                         eqz_preset_10b[i]->f_amp[j] );
473                     sprintf( psz_newbands, "%s %f", psz_newbands,
474                                          eqz_preset_10b[i]->f_amp[j] );
475                 }
476                 if( p_sys->b_first == VLC_FALSE )
477                 {
478                     var_SetString( p_aout, "equalizer-bands", psz_newbands );
479                     var_SetFloat( p_aout, "equalizer-preamp",
480                                     eqz_preset_10b[i]->f_preamp );
481                 }
482                 else
483                 {
484                     p_sys->psz_newbands = strdup( psz_newbands );
485                     p_sys->f_newpreamp = eqz_preset_10b[i]->f_preamp;
486                 }
487                 break;
488             }
489         }
490         if( eqz_preset_10b[i] == NULL )
491         {
492             msg_Err( p_aout, "equalizer preset '%s' not found", psz_preset );
493             msg_Dbg( p_aout, "full list:" );
494             for( i = 0; eqz_preset_10b[i] != NULL; i++ )
495                 msg_Dbg( p_aout, "  - '%s'", eqz_preset_10b[i]->psz_name );
496         }
497     }
498     return VLC_SUCCESS;
499 }
500
501 static int PreampCallback( vlc_object_t *p_this, char const *psz_cmd,
502                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
503 {
504     aout_filter_sys_t *p_sys = (aout_filter_sys_t *)p_data;
505
506     if( newval.f_float < -20.0 )
507         newval.f_float = -20.0;
508     else if( newval.f_float > 20.0 )
509         newval.f_float = 20.0;
510     p_sys->f_gamp = pow( 10, newval.f_float /20.0);
511
512     return VLC_SUCCESS;
513 }
514
515 static int BandsCallback( vlc_object_t *p_this, char const *psz_cmd,
516                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
517 {
518     aout_filter_sys_t *p_sys = (aout_filter_sys_t *)p_data;
519     char *psz_bands = newval.psz_string;
520
521     /* Same thing for bands */
522     if( *psz_bands )
523     {
524         char *p = psz_bands, *p_next;
525         int i;
526
527         for( i = 0; i < p_sys->i_band; i++ )
528         {
529             /* Read dB -20/20 */
530 #ifdef HAVE_STRTOF
531             float f = strtof( p, &p_next );
532 #else
533             float f = (float) strtod( p, &p_next );
534 #endif
535             if( !p_next || p_next == p ) break; /* strtof() failed */
536
537             p_sys->f_amp[i] = EqzConvertdB( f );
538
539             if( !*p ) break; /* end of line */
540             p=p_next+1;
541         }
542     }
543
544     return VLC_SUCCESS;
545 }