1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2004 VideoLAN
5 * $Id: goom.c 8019 2004-06-22 19:35:01Z fenrir $
7 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
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.
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.
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 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
27 #include <stdlib.h> /* malloc(), free() */
33 #include "aout_internal.h"
36 * - add tables for other rates ( 22500, 11250, ...)
37 * - optimize a bit (you can hardly do slower ;)
38 * - add tables for more bands (15 and 32 would be cool), maybe with auto coeffs
39 * computation (not too hard once the Q is found).
40 * - support for external preset
41 * - callback to handle preset changes on the fly
45 /*****************************************************************************
47 *****************************************************************************/
48 static int Open ( vlc_object_t * );
49 static void Close( vlc_object_t * );
51 #define PRESET_TEXT N_( "Equalizer preset" )
52 #define PRESET_LONGTEXT PRESET_TEXT
54 #define BANDS_TEXT N_( "Bands gain")
55 #define BANDS_LONGTEXT N_( "Override preset bands gain in dB (-20 ... 20)" )
57 #define TWOPASS_TEXT N_( "Two pass" )
58 #define TWOPASS_LONGTEXT N_( "Filter twice the audio" )
60 #define PREAMP_TEXT N_("Global gain" )
61 #define PREAMP_LONGTEXT N_("Set the global gain in dB (-20 ... 20)" )
63 static char *preset_list[] = {
64 "flat", "classical", "club", "dance", "fullbass", "fullbasstreeble",
65 "fulltreeble", "headphones","largehall", "live", "party", "pop", "reggae",
66 "rock", "ska", "soft", "softrock", "techno"
68 static char *preset_list_text[] = {
69 N_("Flat"), N_("Classical"), N_("Club"), N_("Dance"), N_("Full bass"),
70 N_("Full bass and treeble"), N_("Full treeble"), N_("Laptop speakers and Headphones"),
71 N_("Large Hall"), N_("Live"), N_("Party"), N_("Pop"), N_("Reggae"),
72 N_("Rock"), N_("Ska"), N_("Soft"), N_("Soft rock"), N_("Techno"),
76 set_description( _("Equalizer 10 bands") );
77 set_capability( "audio filter", 0 );
78 add_string( "equalizer-preset", "flat", NULL, PRESET_TEXT, PRESET_LONGTEXT, VLC_TRUE );
79 change_string_list( preset_list, preset_list_text, 0 );
80 add_string( "equalizer-bands", NULL, NULL, BANDS_TEXT, BANDS_LONGTEXT, VLC_TRUE );
81 add_bool( "equalizer-2pass", 0, NULL, TWOPASS_TEXT, TWOPASS_LONGTEXT, VLC_TRUE );
82 add_float( "equalizer-preamp", 0.0, NULL, PREAMP_TEXT, PREAMP_LONGTEXT, VLC_TRUE );
83 set_callbacks( Open, Close );
84 add_shortcut( "equalizer" );
87 /*****************************************************************************
89 *****************************************************************************/
91 typedef struct aout_filter_sys_t
93 /* Filter static config */
99 /* Filter dyn config */
100 float *f_amp; /* Per band amp */
101 float f_gamp; /* Global preamp */
108 /* Second filter state */
110 float y2[32][128][2];
114 static void DoWork( aout_instance_t *, aout_filter_t *,
115 aout_buffer_t *, aout_buffer_t * );
117 #define EQZ_IN_FACTOR (0.25)
118 static int EqzInit( aout_filter_t *, int i_rate );
119 static void EqzFilter( aout_filter_t *, float *out, float *in, int i_samples, int i_channels );
120 static void EqzClean( aout_filter_t * );
122 /*****************************************************************************
124 *****************************************************************************/
125 static int Open( vlc_object_t *p_this )
127 aout_filter_t *p_filter = (aout_filter_t *)p_this;
128 aout_filter_sys_t *p_sys;
130 if( p_filter->input.i_format != VLC_FOURCC('f','l','3','2' ) ||
131 p_filter->output.i_format != VLC_FOURCC('f','l','3','2') )
133 msg_Warn( p_filter, "Bad input or output format" );
136 if ( !AOUT_FMTS_SIMILAR( &p_filter->input, &p_filter->output ) )
138 msg_Warn( p_filter, "input and output formats are not similar" );
142 p_filter->pf_do_work = DoWork;
143 p_filter->b_in_place = VLC_TRUE;
145 /* Allocate structure */
146 p_sys = p_filter->p_sys = malloc( sizeof( aout_filter_sys_t ) );
148 if( EqzInit( p_filter, p_filter->input.i_rate ) )
154 /*****************************************************************************
155 * Close: close the plugin
156 *****************************************************************************/
157 static void Close( vlc_object_t *p_this )
159 aout_filter_t *p_filter = (aout_filter_t *)p_this;
160 aout_filter_sys_t *p_sys = p_filter->p_sys;
162 EqzClean( p_filter );
166 /*****************************************************************************
167 * DoWork: process samples buffer
168 *****************************************************************************
170 *****************************************************************************/
171 static void DoWork( aout_instance_t * p_aout, aout_filter_t * p_filter,
172 aout_buffer_t * p_in_buf, aout_buffer_t * p_out_buf )
174 p_out_buf->i_nb_samples = p_in_buf->i_nb_samples;
175 p_out_buf->i_nb_bytes = p_in_buf->i_nb_bytes;
177 EqzFilter( p_filter, (float*)p_out_buf->p_buffer, (float*)p_in_buf->p_buffer,
178 p_in_buf->i_nb_samples, aout_FormatNbChannels( &p_filter->input ) );
181 /*****************************************************************************
183 *****************************************************************************/
197 /* Value from equ-xmms */
198 static const eqz_config_t eqz_config_44100_10b =
202 { 60, 0.003013, 0.993973, 1.993901 },
203 { 170, 0.008490, 0.983019, 1.982437 },
204 { 310, 0.015374, 0.969252, 1.967331 },
205 { 600, 0.029328, 0.941343, 1.934254 },
206 { 1000, 0.047918, 0.904163, 1.884869 },
207 { 3000, 0.130408, 0.739184, 1.582718 },
208 { 6000, 0.226555, 0.546889, 1.015267 },
209 { 12000, 0.344937, 0.310127, -0.181410 },
210 { 14000, 0.366438, 0.267123, -0.521151 },
211 { 16000, 0.379009, 0.241981, -0.808451 },
214 static const eqz_config_t eqz_config_48000_10b =
218 { 60, 0.002769, 0.994462, 1.994400 },
219 { 170, 0.007806, 0.984388, 1.983897 },
220 { 310, 0.014143, 0.971714, 1.970091 },
221 { 600, 0.027011, 0.945978, 1.939979 },
222 { 1000, 0.044203, 0.911595, 1.895241 },
223 { 3000, 0.121223, 0.757553, 1.623767 },
224 { 6000, 0.212888, 0.574224, 1.113145 },
225 { 12000, 0.331347, 0.337307, 0.000000 },
226 { 14000, 0.355263, 0.289473, -0.333740 },
227 { 16000, 0.371900, 0.256201, -0.628100 }
238 static const eqz_preset_t eqz_preset_flat_10b=
241 { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 },
243 static const eqz_preset_t eqz_preset_classical_10b=
246 { -1.11022e-15, -1.11022e-15, -1.11022e-15, -1.11022e-15, -1.11022e-15, -1.11022e-15, -7.2, -7.2, -7.2, -9.6 }
248 static const eqz_preset_t eqz_preset_club_10b=
251 { -1.11022e-15, -1.11022e-15, 8, 5.6, 5.6, 5.6, 3.2, -1.11022e-15, -1.11022e-15, -1.11022e-15 }
253 static const eqz_preset_t eqz_preset_dance_10b=
256 { 9.6, 7.2, 2.4, -1.11022e-15, -1.11022e-15, -5.6, -7.2, -7.2, -1.11022e-15, -1.11022e-15 }
258 static const eqz_preset_t eqz_preset_fullbass_10b=
261 { -8, 9.6, 9.6, 5.6, 1.6, -4, -8, -10.4, -11.2, -11.2 }
263 static const eqz_preset_t eqz_preset_fullbasstreeble_10b=
265 "fullbasstreeble", 10,
266 { 7.2, 5.6, -1.11022e-15, -7.2, -4.8, 1.6, 8, 11.2, 12, 12 }
269 static const eqz_preset_t eqz_preset_fulltreeble_10b=
272 { -9.6, -9.6, -9.6, -4, 2.4, 11.2, 16, 16, 16, 16.8 }
274 static const eqz_preset_t eqz_preset_headphones_10b=
277 { 4.8, 11.2, 5.6, -3.2, -2.4, 1.6, 4.8, 9.6, 12.8, 14.4 }
279 static const eqz_preset_t eqz_preset_largehall_10b=
282 { 10.4, 10.4, 5.6, 5.6, -1.11022e-15, -4.8, -4.8, -4.8, -1.11022e-15, -1.11022e-15 }
284 static const eqz_preset_t eqz_preset_live_10b=
287 { -4.8, -1.11022e-15, 4, 5.6, 5.6, 5.6, 4, 2.4, 2.4, 2.4 }
289 static const eqz_preset_t eqz_preset_party_10b=
292 { 7.2, 7.2, -1.11022e-15, -1.11022e-15, -1.11022e-15, -1.11022e-15, -1.11022e-15, -1.11022e-15, 7.2, 7.2 }
294 static const eqz_preset_t eqz_preset_pop_10b=
297 { -1.6, 4.8, 7.2, 8, 5.6, -1.11022e-15, -2.4, -2.4, -1.6, -1.6 }
299 static const eqz_preset_t eqz_preset_reggae_10b=
302 { -1.11022e-15, -1.11022e-15, -1.11022e-15, -5.6, -1.11022e-15, 6.4, 6.4, -1.11022e-15, -1.11022e-15, -1.11022e-15 }
304 static const eqz_preset_t eqz_preset_rock_10b=
307 { 8, 4.8, -5.6, -8, -3.2, 4, 8.8, 11.2, 11.2, 11.2 }
309 static const eqz_preset_t eqz_preset_ska_10b=
312 { -2.4, -4.8, -4, -1.11022e-15, 4, 5.6, 8.8, 9.6, 11.2, 9.6 }
314 static const eqz_preset_t eqz_preset_soft_10b=
317 { 4.8, 1.6, -1.11022e-15, -2.4, -1.11022e-15, 4, 8, 9.6, 11.2, 12 }
319 static const eqz_preset_t eqz_preset_softrock_10b=
322 { 4, 4, 2.4, -1.11022e-15, -4, -5.6, -3.2, -1.11022e-15, 2.4, 8.8 }
324 static const eqz_preset_t eqz_preset_techno_10b=
327 { 8, 5.6, -1.11022e-15, -5.6, -4.8, -1.11022e-15, 8, 9.6, 9.6, 8.8 }
330 static const eqz_preset_t *eqz_preset_10b[] =
332 &eqz_preset_flat_10b,
333 &eqz_preset_classical_10b,
334 &eqz_preset_club_10b,
335 &eqz_preset_dance_10b,
336 &eqz_preset_fullbass_10b,
337 &eqz_preset_fullbasstreeble_10b,
338 &eqz_preset_fulltreeble_10b,
339 &eqz_preset_headphones_10b,
340 &eqz_preset_largehall_10b,
341 &eqz_preset_live_10b,
342 &eqz_preset_party_10b,
344 &eqz_preset_reggae_10b,
345 &eqz_preset_rock_10b,
347 &eqz_preset_soft_10b,
348 &eqz_preset_softrock_10b,
349 &eqz_preset_techno_10b,
354 static inline float EqzConvertdB( float db )
357 * (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)
358 * db = 20*log( out / in ) with out = in + amp*iir(i/EQZ_IN_FACTOR)
359 * or iir(i) == i for the center freq so
360 * db = 20*log( 1 + amp/EQZ_IN_FACTOR )
361 * -> amp = EQZ_IN_FACTOR*(10^(db/20) - 1)
368 return EQZ_IN_FACTOR * ( pow( 10, db / 20.0 ) - 1.0 );
371 static int EqzInit( aout_filter_t *p_filter, int i_rate )
373 aout_filter_sys_t *p_sys = p_filter->p_sys;
374 const eqz_config_t *p_cfg;
379 /* Select the config */
380 if( i_rate == 48000 )
382 p_cfg = &eqz_config_48000_10b;
384 else if( i_rate == 44100 )
386 p_cfg = &eqz_config_44100_10b;
390 /* TODO compute the coeffs on the fly */
391 msg_Err( p_filter, "unsupported rate" );
395 /* Create the static filter config */
396 p_sys->i_band = p_cfg->i_band;
397 p_sys->f_alpha = malloc( p_sys->i_band * sizeof(float) );
398 p_sys->f_beta = malloc( p_sys->i_band * sizeof(float) );
399 p_sys->f_gamma = malloc( p_sys->i_band * sizeof(float) );
400 for( i = 0; i < p_sys->i_band; i++ )
402 p_sys->f_alpha[i] = p_cfg->band[i].f_alpha;
403 p_sys->f_beta[i] = p_cfg->band[i].f_beta;
404 p_sys->f_gamma[i] = p_cfg->band[i].f_gamma;
407 /* Filter dyn config */
408 p_sys->b_2eqz = VLC_FALSE;
410 p_sys->f_amp = malloc( p_sys->i_band * sizeof(float) );
411 for( i = 0; i < p_sys->i_band; i++ )
413 p_sys->f_amp[i] = 0.0;
417 for( ch = 0; ch < 32; ch++ )
422 p_sys->x2[ch][1] = 0.0;
424 for( i = 0; i < p_sys->i_band; i++ )
428 p_sys->y2[ch][i][0] =
429 p_sys->y2[ch][i][1] = 0.0;
433 /* Now parse config */
434 p_sys->b_2eqz = var_CreateGetBool( p_filter, "equalizer-2pass" );
435 f_float = var_CreateGetFloat( p_filter, "equalizer-preamp" );
436 if( f_float < -20.0 )
438 else if( f_float > 20.0 )
440 p_sys->f_gamp = pow( 10, f_float /20.0);
442 psz = var_CreateGetString( p_filter, "equalizer-preset" );
443 if( *psz && p_sys->i_band == 10 )
447 for( i = 0; eqz_preset_10b[i] != NULL; i++ )
449 if( !strcasecmp( eqz_preset_10b[i]->psz_name, psz ) )
452 for( j = 0; j < p_sys->i_band; j++ )
453 p_sys->f_amp[j] = EqzConvertdB( eqz_preset_10b[i]->f_amp[j] );
457 if( eqz_preset_10b[i] == NULL )
459 msg_Err( p_filter, "equalizer preset '%s' not found", psz );
460 msg_Dbg( p_filter, "full list:" );
461 for( i = 0; eqz_preset_10b[i] != NULL; i++ )
462 msg_Dbg( p_filter, " - '%s'", eqz_preset_10b[i]->psz_name );
467 psz = var_CreateGetString( p_filter, "equalizer-bands" );
472 for( i = 0; i < p_sys->i_band; i++ )
479 p_sys->f_amp[i] = EqzConvertdB( f );
490 msg_Dbg( p_filter, "equalizer loaded for %d Hz with %d bands %d pass",
491 i_rate, p_sys->i_band, p_sys->b_2eqz ? 2 : 1 );
492 for( i = 0; i < p_sys->i_band; i++ )
494 msg_Dbg( p_filter, " %d Hz -> factor:%f alpha:%f beta:%f gamma:%f",
495 (int)p_cfg->band[i].f_frequency, p_sys->f_amp[i],
496 p_sys->f_alpha[i], p_sys->f_beta[i], p_sys->f_gamma[i]);
501 static void EqzFilter( aout_filter_t *p_filter, float *out, float *in, int i_samples, int i_channels )
503 aout_filter_sys_t *p_sys = p_filter->p_sys;
506 for( i = 0; i < i_samples; i++ )
508 for( ch = 0; ch < i_channels; ch++ )
510 const float x = in[ch];
513 for( j = 0; j < p_sys->i_band; j++ )
515 float y = p_sys->f_alpha[j] * ( x - p_sys->x[ch][1] ) +
516 p_sys->f_gamma[j] * p_sys->y[ch][j][0] -
517 p_sys->f_beta[j] * p_sys->y[ch][j][1];
519 p_sys->y[ch][j][1] = p_sys->y[ch][j][0];
520 p_sys->y[ch][j][0] = y;
522 o += y * p_sys->f_amp[j];
524 p_sys->x[ch][1] = p_sys->x[ch][0];
530 const float x2 = EQZ_IN_FACTOR * x + o;
532 for( j = 0; j < p_sys->i_band; j++ )
534 float y = p_sys->f_alpha[j] * ( x2 - p_sys->x2[ch][1] ) +
535 p_sys->f_gamma[j] * p_sys->y2[ch][j][0] -
536 p_sys->f_beta[j] * p_sys->y2[ch][j][1];
538 p_sys->y2[ch][j][1] = p_sys->y2[ch][j][0];
539 p_sys->y2[ch][j][0] = y;
541 o += y * p_sys->f_amp[j];
543 p_sys->x2[ch][1] = p_sys->x2[ch][0];
544 p_sys->x2[ch][0] = x2;
546 /* We add source PCM + filtered PCM */
547 out[ch] = p_sys->f_gamp *( EQZ_IN_FACTOR * x2 + o );
551 /* We add source PCM + filtered PCM */
552 out[ch] = p_sys->f_gamp *( EQZ_IN_FACTOR * x + o );
561 static void EqzClean( aout_filter_t *p_filter )
563 aout_filter_sys_t *p_sys = p_filter->p_sys;
565 free( p_sys->f_alpha );
566 free( p_sys->f_beta );
567 free( p_sys->f_gamma );
569 free( p_sys->f_amp );