1 /*****************************************************************************
2  * equalizer.c:
3  *****************************************************************************
4  * Copyright (C) 2004-2012 VLC authors and VideoLAN
5  * $Id: f83d4599caba5d237aac0627c89afc9f6f4c7649 $
6  *
7  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23 
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31 
32 #include <math.h>
33 
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
36 #include <vlc_charset.h>
37 
38 #include <vlc_aout.h>
39 #include <vlc_filter.h>
40 
41 #include "equalizer_presets.h"
42 
43 /* TODO:
44  *  - optimize a bit (you can hardly do slower ;)
45  *  - add tables for more bands (15 and 32 would be cool), maybe with auto coeffs
46  *    computation (not too hard once the Q is found).
47  *  - support for external preset
48  *  - callback to handle preset changes on the fly
49  *  - ...
50  */
51 
52 /*****************************************************************************
53  * Module descriptor
54  *****************************************************************************/
55 static int  Open ( vlc_object_t * );
56 static void Close( vlc_object_t * );
57 
58 #define PRESET_TEXT N_( "Equalizer preset" )
59 #define PRESET_LONGTEXT N_("Preset to use for the equalizer." )
60 
61 #define BANDS_TEXT N_( "Bands gain")
62 #define BANDS_LONGTEXT N_( \
63          "Don't use presets, but manually specified bands. You need to " \
64          "provide 10 values between -20dB and 20dB, separated by spaces, " \
65          "e.g. \"0 2 4 2 0 -2 -4 -2 0 2\"." )
66 
67 #define VLC_BANDS_TEXT N_( "Use VLC frequency bands" )
68 #define VLC_BANDS_LONGTEXT N_( \
69          "Use the VLC frequency bands. Otherwise, use the ISO Standard " \
70          "frequency bands." )
71 
72 #define TWOPASS_TEXT N_( "Two pass" )
73 #define TWOPASS_LONGTEXT N_( "Filter the audio twice. This provides a more "  \
74          "intense effect.")
75 
76 #define PREAMP_TEXT N_("Global gain" )
77 #define PREAMP_LONGTEXT N_("Set the global gain in dB (-20 ... 20)." )
78 
79 vlc_module_begin ()
80     set_description( N_("Equalizer with 10 bands") )
81     set_shortname( N_("Equalizer" ) )
82     set_capability( "audio filter", 0 )
83     set_category( CAT_AUDIO )
84     set_subcategory( SUBCAT_AUDIO_AFILTER )
85 
86     add_string( "equalizer-preset", "flat", PRESET_TEXT,
87                 PRESET_LONGTEXT, false )
88         change_string_list( preset_list, preset_list_text )
89     add_string( "equalizer-bands", NULL, BANDS_TEXT,
90                 BANDS_LONGTEXT, true )
91     add_bool( "equalizer-2pass", false, TWOPASS_TEXT,
92               TWOPASS_LONGTEXT, true )
93     add_bool( "equalizer-vlcfreqs", true, VLC_BANDS_TEXT,
94               VLC_BANDS_LONGTEXT, true )
95     add_float( "equalizer-preamp", 12.0f, PREAMP_TEXT,
96                PREAMP_LONGTEXT, true )
97     set_callbacks( Open, Close )
98     add_shortcut( "equalizer" )
99 vlc_module_end ()
100 
101 /*****************************************************************************
102  * Local prototypes
103  *****************************************************************************/
104 struct filter_sys_t
105 {
106     /* Filter static config */
107     int i_band;
108     float *f_alpha;
109     float *f_beta;
110     float *f_gamma;
111 
112     /* Filter dyn config */
113     float *f_amp;   /* Per band amp */
114     float f_gamp;   /* Global preamp */
115     bool b_2eqz;
116 
117     /* Filter state */
118     float x[32][2];
119     float y[32][128][2];
120 
121     /* Second filter state */
122     float x2[32][2];
123     float y2[32][128][2];
124 
125     vlc_mutex_t lock;
126 };
127 
128 static block_t *DoWork( filter_t *, block_t * );
129 
130 #define EQZ_IN_FACTOR (0.25f)
131 static int  EqzInit( filter_t *, int );
132 static void EqzFilter( filter_t *, float *, float *, int, int );
133 static void EqzClean( filter_t * );
134 
135 static int PresetCallback ( vlc_object_t *, char const *, vlc_value_t,
136                             vlc_value_t, void * );
137 static int PreampCallback ( vlc_object_t *, char const *, vlc_value_t,
138                             vlc_value_t, void * );
139 static int BandsCallback  ( vlc_object_t *, char const *, vlc_value_t,
140                             vlc_value_t, void * );
141 static int TwoPassCallback( vlc_object_t *, char const *, vlc_value_t,
142                             vlc_value_t, void * );
143 
144 
145 
146 /*****************************************************************************
147  * Open:
148  *****************************************************************************/
Open(vlc_object_t * p_this)149 static int Open( vlc_object_t *p_this )
150 {
151     filter_t     *p_filter = (filter_t *)p_this;
152 
153     /* Allocate structure */
154     filter_sys_t *p_sys = p_filter->p_sys = malloc( sizeof( *p_sys ) );
155     if( !p_sys )
156         return VLC_ENOMEM;
157 
158     vlc_mutex_init( &p_sys->lock );
159     if( EqzInit( p_filter, p_filter->fmt_in.audio.i_rate ) != VLC_SUCCESS )
160     {
161         vlc_mutex_destroy( &p_sys->lock );
162         free( p_sys );
163         return VLC_EGENERIC;
164     }
165 
166     p_filter->fmt_in.audio.i_format = VLC_CODEC_FL32;
167     aout_FormatPrepare(&p_filter->fmt_in.audio);
168     p_filter->fmt_out.audio = p_filter->fmt_in.audio;
169     p_filter->pf_audio_filter = DoWork;
170 
171     return VLC_SUCCESS;
172 }
173 
174 /*****************************************************************************
175  * Close: close the plugin
176  *****************************************************************************/
Close(vlc_object_t * p_this)177 static void Close( vlc_object_t *p_this )
178 {
179     filter_t     *p_filter = (filter_t *)p_this;
180     filter_sys_t *p_sys = p_filter->p_sys;
181 
182     EqzClean( p_filter );
183     vlc_mutex_destroy( &p_sys->lock );
184     free( p_sys );
185 }
186 
187 /*****************************************************************************
188  * DoWork: process samples buffer
189  *****************************************************************************
190  *
191  *****************************************************************************/
DoWork(filter_t * p_filter,block_t * p_in_buf)192 static block_t * DoWork( filter_t * p_filter, block_t * p_in_buf )
193 {
194     EqzFilter( p_filter, (float*)p_in_buf->p_buffer,
195                (float*)p_in_buf->p_buffer, p_in_buf->i_nb_samples,
196                aout_FormatNbChannels( &p_filter->fmt_in.audio ) );
197     return p_in_buf;
198 }
199 
200 /*****************************************************************************
201  * Equalizer stuff
202  *****************************************************************************/
203 typedef struct
204 {
205     int   i_band;
206 
207     struct
208     {
209         float f_frequency;
210         float f_alpha;
211         float f_beta;
212         float f_gamma;
213     } band[EQZ_BANDS_MAX];
214 
215 } eqz_config_t;
216 
217 /* Equalizer coefficient calculation function based on equ-xmms */
EqzCoeffs(int i_rate,float f_octave_percent,bool b_use_vlc_freqs,eqz_config_t * p_eqz_config)218 static void EqzCoeffs( int i_rate, float f_octave_percent,
219                        bool b_use_vlc_freqs,
220                        eqz_config_t *p_eqz_config )
221 {
222     const float *f_freq_table_10b = b_use_vlc_freqs
223                                   ? f_vlc_frequency_table_10b
224                                   : f_iso_frequency_table_10b;
225     float f_rate = (float) i_rate;
226     float f_nyquist_freq = 0.5f * f_rate;
227     float f_octave_factor = powf( 2.0f, 0.5f * f_octave_percent );
228     float f_octave_factor_1 = 0.5f * ( f_octave_factor + 1.0f );
229     float f_octave_factor_2 = 0.5f * ( f_octave_factor - 1.0f );
230 
231     p_eqz_config->i_band = EQZ_BANDS_MAX;
232 
233     for( int i = 0; i < EQZ_BANDS_MAX; i++ )
234     {
235         float f_freq = f_freq_table_10b[i];
236 
237         p_eqz_config->band[i].f_frequency = f_freq;
238 
239         if( f_freq <= f_nyquist_freq )
240         {
241             float f_theta_1 = ( 2.0f * (float) M_PI * f_freq ) / f_rate;
242             float f_theta_2 = f_theta_1 / f_octave_factor;
243             float f_sin     = sinf( f_theta_2 );
244             float f_sin_prd = sinf( f_theta_2 * f_octave_factor_1 )
245                             * sinf( f_theta_2 * f_octave_factor_2 );
246             float f_sin_hlf = f_sin * 0.5f;
247             float f_den     = f_sin_hlf + f_sin_prd;
248 
249             p_eqz_config->band[i].f_alpha = f_sin_prd / f_den;
250             p_eqz_config->band[i].f_beta  = ( f_sin_hlf - f_sin_prd ) / f_den;
251             p_eqz_config->band[i].f_gamma = f_sin * cosf( f_theta_1 ) / f_den;
252         }
253         else
254         {
255             /* Any frequency beyond the Nyquist frequency is no good... */
256             p_eqz_config->band[i].f_alpha =
257             p_eqz_config->band[i].f_beta  =
258             p_eqz_config->band[i].f_gamma = 0.0f;
259         }
260     }
261 }
262 
EqzConvertdB(float db)263 static inline float EqzConvertdB( float db )
264 {
265     /* Map it to gain,
266      * (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)
267      * db = 20*log( out / in ) with out = in + amp*iir(i/EQZ_IN_FACTOR)
268      * or iir(i) == i for the center freq so
269      * db = 20*log( 1 + amp/EQZ_IN_FACTOR )
270      * -> amp = EQZ_IN_FACTOR*(10^(db/20) - 1)
271      **/
272 
273     if( db < -20.0f )
274         db = -20.0f;
275     else if(  db > 20.0f )
276         db = 20.0f;
277     return EQZ_IN_FACTOR * ( powf( 10.0f, db / 20.0f ) - 1.0f );
278 }
279 
EqzInit(filter_t * p_filter,int i_rate)280 static int EqzInit( filter_t *p_filter, int i_rate )
281 {
282     filter_sys_t *p_sys = p_filter->p_sys;
283     eqz_config_t cfg;
284     int i, ch;
285     vlc_value_t val1, val2, val3;
286     vlc_object_t *p_aout = p_filter->obj.parent;
287     int i_ret = VLC_ENOMEM;
288 
289     bool b_vlcFreqs = var_InheritBool( p_aout, "equalizer-vlcfreqs" );
290     EqzCoeffs( i_rate, 1.0f, b_vlcFreqs, &cfg );
291 
292     /* Create the static filter config */
293     p_sys->i_band = cfg.i_band;
294     p_sys->f_alpha = vlc_alloc( p_sys->i_band, sizeof(float) );
295     p_sys->f_beta  = vlc_alloc( p_sys->i_band, sizeof(float) );
296     p_sys->f_gamma = vlc_alloc( p_sys->i_band, sizeof(float) );
297     if( !p_sys->f_alpha || !p_sys->f_beta || !p_sys->f_gamma )
298         goto error;
299 
300     for( i = 0; i < p_sys->i_band; i++ )
301     {
302         p_sys->f_alpha[i] = cfg.band[i].f_alpha;
303         p_sys->f_beta[i]  = cfg.band[i].f_beta;
304         p_sys->f_gamma[i] = cfg.band[i].f_gamma;
305     }
306 
307     /* Filter dyn config */
308     p_sys->b_2eqz = false;
309     p_sys->f_gamp = 1.0f;
310     p_sys->f_amp  = vlc_alloc( p_sys->i_band, sizeof(float) );
311     if( !p_sys->f_amp )
312         goto error;
313 
314     for( i = 0; i < p_sys->i_band; i++ )
315     {
316         p_sys->f_amp[i] = 0.0f;
317     }
318 
319     /* Filter state */
320     for( ch = 0; ch < 32; ch++ )
321     {
322         p_sys->x[ch][0]  =
323         p_sys->x[ch][1]  =
324         p_sys->x2[ch][0] =
325         p_sys->x2[ch][1] = 0.0f;
326 
327         for( i = 0; i < p_sys->i_band; i++ )
328         {
329             p_sys->y[ch][i][0]  =
330             p_sys->y[ch][i][1]  =
331             p_sys->y2[ch][i][0] =
332             p_sys->y2[ch][i][1] = 0.0f;
333         }
334     }
335 
336     var_Create( p_aout, "equalizer-bands", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
337     var_Create( p_aout, "equalizer-preset", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
338 
339     p_sys->b_2eqz = var_CreateGetBool( p_aout, "equalizer-2pass" );
340 
341     var_Create( p_aout, "equalizer-preamp", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT );
342 
343     /* Get initial values */
344     var_Get( p_aout, "equalizer-preset", &val1 );
345     var_Get( p_aout, "equalizer-bands", &val2 );
346     var_Get( p_aout, "equalizer-preamp", &val3 );
347 
348     /* Load the preset only if equalizer-bands is not set. */
349     if ( val2.psz_string == NULL || *val2.psz_string == '\0' )
350         PresetCallback( VLC_OBJECT( p_aout ), NULL, val1, val1, p_sys );
351     free( val1.psz_string );
352     BandsCallback(  VLC_OBJECT( p_aout ), NULL, val2, val2, p_sys );
353     PreampCallback( VLC_OBJECT( p_aout ), NULL, val3, val3, p_sys );
354 
355     /* Exit if we have no preset and no bands value */
356     if (!val2.psz_string || !*val2.psz_string)
357     {
358         msg_Err(p_filter, "No preset selected");
359         free( val2.psz_string );
360         free( p_sys->f_amp );
361         i_ret = VLC_EGENERIC;
362         goto error;
363     }
364     free( val2.psz_string );
365 
366     /* Add our own callbacks */
367     var_AddCallback( p_aout, "equalizer-preset", PresetCallback, p_sys );
368     var_AddCallback( p_aout, "equalizer-bands", BandsCallback, p_sys );
369     var_AddCallback( p_aout, "equalizer-preamp", PreampCallback, p_sys );
370     var_AddCallback( p_aout, "equalizer-2pass", TwoPassCallback, p_sys );
371 
372     msg_Dbg( p_filter, "equalizer loaded for %d Hz with %d bands %d pass",
373                         i_rate, p_sys->i_band, p_sys->b_2eqz ? 2 : 1 );
374     for( i = 0; i < p_sys->i_band; i++ )
375     {
376         msg_Dbg( p_filter, "   %.2f Hz -> factor:%f alpha:%f beta:%f gamma:%f",
377                  cfg.band[i].f_frequency, p_sys->f_amp[i],
378                  p_sys->f_alpha[i], p_sys->f_beta[i], p_sys->f_gamma[i]);
379     }
380     return VLC_SUCCESS;
381 
382 error:
383     free( p_sys->f_alpha );
384     free( p_sys->f_beta );
385     free( p_sys->f_gamma );
386     return i_ret;
387 }
388 
EqzFilter(filter_t * p_filter,float * out,float * in,int i_samples,int i_channels)389 static void EqzFilter( filter_t *p_filter, float *out, float *in,
390                        int i_samples, int i_channels )
391 {
392     filter_sys_t *p_sys = p_filter->p_sys;
393     int i, ch, j;
394 
395     vlc_mutex_lock( &p_sys->lock );
396     for( i = 0; i < i_samples; i++ )
397     {
398         for( ch = 0; ch < i_channels; ch++ )
399         {
400             const float x = in[ch];
401             float o = 0.0f;
402 
403             for( j = 0; j < p_sys->i_band; j++ )
404             {
405                 float y = p_sys->f_alpha[j] * ( x - p_sys->x[ch][1] ) +
406                           p_sys->f_gamma[j] * p_sys->y[ch][j][0] -
407                           p_sys->f_beta[j]  * p_sys->y[ch][j][1];
408 
409                 p_sys->y[ch][j][1] = p_sys->y[ch][j][0];
410                 p_sys->y[ch][j][0] = y;
411 
412                 o += y * p_sys->f_amp[j];
413             }
414             p_sys->x[ch][1] = p_sys->x[ch][0];
415             p_sys->x[ch][0] = x;
416 
417             /* Second filter */
418             if( p_sys->b_2eqz )
419             {
420                 const float x2 = EQZ_IN_FACTOR * x + o;
421                 o = 0.0f;
422                 for( j = 0; j < p_sys->i_band; j++ )
423                 {
424                     float y = p_sys->f_alpha[j] * ( x2 - p_sys->x2[ch][1] ) +
425                               p_sys->f_gamma[j] * p_sys->y2[ch][j][0] -
426                               p_sys->f_beta[j]  * p_sys->y2[ch][j][1];
427 
428                     p_sys->y2[ch][j][1] = p_sys->y2[ch][j][0];
429                     p_sys->y2[ch][j][0] = y;
430 
431                     o += y * p_sys->f_amp[j];
432                 }
433                 p_sys->x2[ch][1] = p_sys->x2[ch][0];
434                 p_sys->x2[ch][0] = x2;
435 
436                 /* We add source PCM + filtered PCM */
437                 out[ch] = p_sys->f_gamp * p_sys->f_gamp *( EQZ_IN_FACTOR * x2 + o );
438             }
439             else
440             {
441                 /* We add source PCM + filtered PCM */
442                 out[ch] = p_sys->f_gamp *( EQZ_IN_FACTOR * x + o );
443             }
444         }
445 
446         in  += i_channels;
447         out += i_channels;
448     }
449     vlc_mutex_unlock( &p_sys->lock );
450 }
451 
EqzClean(filter_t * p_filter)452 static void EqzClean( filter_t *p_filter )
453 {
454     filter_sys_t *p_sys = p_filter->p_sys;
455     vlc_object_t *p_aout = p_filter->obj.parent;
456 
457     var_DelCallback( p_aout, "equalizer-bands", BandsCallback, p_sys );
458     var_DelCallback( p_aout, "equalizer-preset", PresetCallback, p_sys );
459     var_DelCallback( p_aout, "equalizer-preamp", PreampCallback, p_sys );
460     var_DelCallback( p_aout, "equalizer-2pass", TwoPassCallback, p_sys );
461 
462     free( p_sys->f_alpha );
463     free( p_sys->f_beta );
464     free( p_sys->f_gamma );
465 
466     free( p_sys->f_amp );
467 }
468 
469 
PresetCallback(vlc_object_t * p_aout,char const * psz_cmd,vlc_value_t oldval,vlc_value_t newval,void * p_data)470 static int PresetCallback( vlc_object_t *p_aout, char const *psz_cmd,
471                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
472 {
473     const eqz_preset_t *preset = NULL;
474     const char *psz_preset = newval.psz_string;
475 
476     for( unsigned i = 0; i < NB_PRESETS; i++ )
477         if( !strcasecmp( eqz_preset_10b[i].psz_name, psz_preset ) )
478         {
479             preset = eqz_preset_10b + i;
480             break;
481         }
482 
483     if( preset == NULL )
484     {
485         msg_Err( p_aout, "equalizer preset '%s' not found", psz_preset );
486         msg_Info( p_aout, "full list:" );
487         for( unsigned i = 0; i < NB_PRESETS; i++ )
488              msg_Info( p_aout, "  - '%s'", eqz_preset_10b[i].psz_name );
489         return VLC_EGENERIC;
490     }
491 
492     char *bands = NULL;
493 
494     for( unsigned i = 0; i < EQZ_BANDS_MAX; i++ )
495     {
496         char *psz;
497 
498         lldiv_t d = lldiv( lroundf(preset->f_amp[i] * 10000000.f), 10000000 );
499 
500         if( asprintf( &psz, "%s %lld.%07llu", i ? bands : "",
501                       d.quot, d.rem ) == -1 )
502             psz = NULL;
503 
504         free( bands );
505         if( unlikely(psz == NULL) )
506             return VLC_ENOMEM;
507         bands = psz;
508     }
509 
510     var_SetFloat( p_aout, "equalizer-preamp", preset->f_preamp );
511     var_SetString( p_aout, "equalizer-bands", bands );
512     free( bands );
513     (void) psz_cmd; (void) oldval; (void) p_data;
514     return VLC_SUCCESS;
515 }
516 
PreampCallback(vlc_object_t * p_this,char const * psz_cmd,vlc_value_t oldval,vlc_value_t newval,void * p_data)517 static int PreampCallback( vlc_object_t *p_this, char const *psz_cmd,
518                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
519 {
520     VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
521     filter_sys_t *p_sys = p_data;
522     float preamp;
523 
524     if( newval.f_float < -20.f )
525         preamp = .1f;
526     else if( newval.f_float < 20.f )
527         preamp = powf( 10.f, newval.f_float / 20.f );
528     else
529         preamp = 10.f;
530 
531     vlc_mutex_lock( &p_sys->lock );
532     p_sys->f_gamp = preamp;
533     vlc_mutex_unlock( &p_sys->lock );
534     return VLC_SUCCESS;
535 }
536 
BandsCallback(vlc_object_t * p_this,char const * psz_cmd,vlc_value_t oldval,vlc_value_t newval,void * p_data)537 static int BandsCallback( vlc_object_t *p_this, char const *psz_cmd,
538                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
539 {
540     VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
541     filter_sys_t *p_sys = p_data;
542     const char *p = newval.psz_string;
543     int i = 0;
544 
545     /* Same thing for bands */
546     vlc_mutex_lock( &p_sys->lock );
547     while( i < p_sys->i_band )
548     {
549         char *next;
550         /* Read dB -20/20 */
551         float f = us_strtof( p, &next );
552         if( next == p || isnan( f ) )
553             break; /* no conversion */
554 
555         p_sys->f_amp[i++] = EqzConvertdB( f );
556 
557         if( *next == '\0' )
558             break; /* end of line */
559         p = &next[1];
560     }
561     while( i < p_sys->i_band )
562         p_sys->f_amp[i++] = EqzConvertdB( 0.f );
563     vlc_mutex_unlock( &p_sys->lock );
564     return VLC_SUCCESS;
565 }
TwoPassCallback(vlc_object_t * p_this,char const * psz_cmd,vlc_value_t oldval,vlc_value_t newval,void * p_data)566 static int TwoPassCallback( vlc_object_t *p_this, char const *psz_cmd,
567                             vlc_value_t oldval, vlc_value_t newval, void *p_data )
568 {
569     VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
570     filter_sys_t *p_sys = p_data;
571 
572     vlc_mutex_lock( &p_sys->lock );
573     p_sys->b_2eqz = newval.b_bool;
574     vlc_mutex_unlock( &p_sys->lock );
575     return VLC_SUCCESS;
576 }
577 
578