]> git.sesse.net Git - vlc/blob - modules/audio_filter/compressor.c
Use var_Inherit* instead of var_CreateGet*.
[vlc] / modules / audio_filter / compressor.c
1 /*****************************************************************************
2  * compressor.c: dynamic range compressor, ported from plugins from LADSPA SWH
3  *****************************************************************************
4  * Copyright (C) 2010 Ronald Wright
5  * $Id$
6  *
7  * Author: Ronald Wright <logiconcepts819@gmail.com>
8  * Original author: Steve Harris <steve@plugin.org.uk>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <math.h>
34 #include <stdint.h>
35
36 #include <vlc_common.h>
37 #include <vlc_plugin.h>
38
39 #include <vlc_aout.h>
40 #include <vlc_filter.h>
41
42 /*****************************************************************************
43 * Local prototypes.
44 *****************************************************************************/
45
46 #define A_TBL (256)
47
48 #define DB_TABLE_SIZE   (1024)
49 #define DB_MIN          (-60.0f)
50 #define DB_MAX          (24.0f)
51 #define LIN_TABLE_SIZE  (1024)
52 #define LIN_MIN         (0.0000000002f)
53 #define LIN_MAX         (9.0f)
54 #define DB_DEFAULT_CUBE
55 #define RMS_BUF_SIZE    (960)
56 #define LOOKAHEAD_SIZE  ((RMS_BUF_SIZE)<<1)
57
58 #define LIN_INTERP(f,a,b) ((a) + (f) * ( (b) - (a) ))
59 #define LIMIT(v,l,u)      (v < l ? l : ( v > u ? u : v ))
60
61 typedef struct
62 {
63     float        pf_buf[RMS_BUF_SIZE];
64     unsigned int i_pos;
65     unsigned int i_count;
66     float        f_sum;
67
68 } rms_env;
69
70 typedef struct
71 {
72     struct
73     {
74         float pf_vals[AOUT_CHAN_MAX];
75         float f_lev_in;
76
77     } p_buf[LOOKAHEAD_SIZE];
78     unsigned int i_pos;
79     unsigned int i_count;
80
81 } lookahead;
82
83 struct filter_sys_t
84 {
85     float f_amp;
86     float pf_as[A_TBL];
87     unsigned int i_count;
88     float f_env;
89     float f_env_peak;
90     float f_env_rms;
91     float f_gain;
92     float f_gain_out;
93     rms_env rms;
94     float f_sum;
95     lookahead la;
96
97     float pf_db_data[DB_TABLE_SIZE];
98     float pf_lin_data[LIN_TABLE_SIZE];
99
100     vlc_mutex_t lock;
101
102     float f_rms_peak;
103     float f_attack;
104     float f_release;
105     float f_threshold;
106     float f_ratio;
107     float f_knee;
108     float f_makeup_gain;
109 };
110
111 typedef union
112 {
113     float f;
114     int32_t i;
115
116 } ls_pcast32;
117
118 static int      Open            ( vlc_object_t * );
119 static void     Close           ( vlc_object_t * );
120 static block_t *DoWork          ( filter_t *, block_t * );
121
122 static void     DbInit          ( filter_sys_t * );
123 static float    Db2Lin          ( float, filter_sys_t * );
124 static float    Lin2Db          ( float, filter_sys_t * );
125 #ifdef DB_DEFAULT_CUBE
126 static float    CubeInterp      ( const float, const float, const float,
127                                   const float, const float );
128 #endif
129 static void     RoundToZero     ( float * );
130 static float    Max             ( float, float );
131 static float    Clamp           ( float, float, float );
132 static int      Round           ( float );
133 static float    RmsEnvProcess   ( rms_env *, const float );
134 static void     BufferProcess   ( float *, int, float, float, lookahead * );
135
136 static int RMSPeakCallback      ( vlc_object_t *, char const *, vlc_value_t,
137                                   vlc_value_t, void * );
138 static int AttackCallback       ( vlc_object_t *, char const *, vlc_value_t,
139                                   vlc_value_t, void * );
140 static int ReleaseCallback      ( vlc_object_t *, char const *, vlc_value_t,
141                                   vlc_value_t, void * );
142 static int ThresholdCallback    ( vlc_object_t *, char const *, vlc_value_t,
143                                   vlc_value_t, void * );
144 static int RatioCallback        ( vlc_object_t *, char const *, vlc_value_t,
145                                   vlc_value_t, void * );
146 static int KneeCallback         ( vlc_object_t *, char const *, vlc_value_t,
147                                   vlc_value_t, void * );
148 static int MakeupGainCallback   ( vlc_object_t *, char const *, vlc_value_t,
149                                   vlc_value_t, void * );
150
151 /*****************************************************************************
152  * Module descriptor
153  *****************************************************************************/
154
155 #define RMS_PEAK_TEXT N_( "RMS/peak" )
156 #define RMS_PEAK_LONGTEXT N_( "Set the RMS/peak (0 ... 1)." )
157
158 #define ATTACK_TEXT N_( "Attack time" )
159 #define ATTACK_LONGTEXT N_( \
160         "Set the attack time in milliseconds (1.5 ... 400)." )
161
162 #define RELEASE_TEXT N_( "Release time" )
163 #define RELEASE_LONGTEXT N_( \
164         "Set the release time in milliseconds (2 ... 800)." )
165
166 #define THRESHOLD_TEXT N_( "Threshold level" )
167 #define THRESHOLD_LONGTEXT N_( "Set the threshold level in dB (-30 ... 0)." )
168
169 #define RATIO_TEXT N_( "Ratio" )
170 #define RATIO_LONGTEXT N_( "Set the ratio (n:1) (1 ... 20)." )
171
172 #define KNEE_TEXT N_( "Knee radius" )
173 #define KNEE_LONGTEXT N_( "Set the knee radius in dB (1 ... 10)." )
174
175 #define MAKEUP_GAIN_TEXT N_( "Makeup gain" )
176 #define MAKEUP_GAIN_LONGTEXT N_( "Set the makeup gain in dB (0 ... 24)." )
177
178 vlc_module_begin()
179     set_shortname( _("Compressor") )
180     set_description( _("Dynamic range compressor") )
181     set_capability( "audio filter", 0 )
182     set_category( CAT_AUDIO )
183     set_subcategory( SUBCAT_AUDIO_AFILTER )
184
185     add_float( "compressor-rms-peak", 0.0, NULL, RMS_PEAK_TEXT,
186                RMS_PEAK_LONGTEXT, false )
187     add_float( "compressor-attack", 25.0, NULL, ATTACK_TEXT,
188                ATTACK_LONGTEXT, false )
189     add_float( "compressor-release", 100.0, NULL, RELEASE_TEXT,
190                RELEASE_LONGTEXT, false )
191     add_float( "compressor-threshold", -11.0, NULL, THRESHOLD_TEXT,
192                THRESHOLD_LONGTEXT, false )
193     add_float( "compressor-ratio", 8.0, NULL, RATIO_TEXT,
194                RATIO_LONGTEXT, false )
195     add_float( "compressor-knee", 2.5, NULL, KNEE_TEXT,
196                KNEE_LONGTEXT, false )
197     add_float( "compressor-makeup-gain", 7.0, NULL, MAKEUP_GAIN_TEXT,
198                MAKEUP_GAIN_LONGTEXT, false )
199     set_callbacks( Open, Close )
200     add_shortcut( "compressor" )
201 vlc_module_end ()
202
203 /*****************************************************************************
204  * Open: initialize interface
205  *****************************************************************************/
206
207 static int Open( vlc_object_t *p_this )
208 {
209     filter_t *p_filter = (filter_t*)p_this;
210     vlc_object_t *p_aout = p_filter->p_parent;
211     float f_sample_rate = p_filter->fmt_in.audio.i_rate;
212     filter_sys_t *p_sys;
213     float f_num;
214
215     if( p_filter->fmt_in.audio.i_format != VLC_CODEC_FL32 ||
216         p_filter->fmt_out.audio.i_format != VLC_CODEC_FL32 )
217     {
218         p_filter->fmt_in.audio.i_format = VLC_CODEC_FL32;
219         p_filter->fmt_out.audio.i_format = VLC_CODEC_FL32;
220         msg_Warn( p_filter, "bad input or output format" );
221         return VLC_EGENERIC;
222     }
223     if( !AOUT_FMTS_SIMILAR( &p_filter->fmt_in.audio,
224                             &p_filter->fmt_out.audio ) )
225     {
226         p_filter->fmt_out.audio = p_filter->fmt_in.audio;
227         msg_Warn( p_filter, "input and output formats are not similar" );
228         return VLC_EGENERIC;
229     }
230
231     /* Initialize the filter parameter structure */
232     p_sys = p_filter->p_sys = calloc( 1, sizeof(*p_sys) );
233     if( !p_sys )
234     {
235         return VLC_ENOMEM;
236     }
237
238     /* Initialize the attack lookup table */
239     p_sys->pf_as[0] = 1.0f;
240     for( int i = 1; i < A_TBL; i++ )
241     {
242         p_sys->pf_as[i] = expf( -1.0f / ( f_sample_rate * i / A_TBL ) );
243     }
244
245     /* Calculate the RMS and lookahead sizes from the sample rate */
246     f_num = 0.01f * f_sample_rate;
247     p_sys->rms.i_count = Round( Clamp( 0.5f * f_num, 1.0f, RMS_BUF_SIZE ) );
248     p_sys->la.i_count = Round( Clamp( f_num, 1.0f, LOOKAHEAD_SIZE ) );
249
250     /* Initialize decibel lookup tables */
251     DbInit( p_sys );
252
253     /* Restore the last saved settings */
254     p_sys->f_rms_peak    = var_CreateGetFloat( p_aout, "compressor-rms-peak" );
255     p_sys->f_attack      = var_CreateGetFloat( p_aout, "compressor-attack" );
256     p_sys->f_release     = var_CreateGetFloat( p_aout, "compressor-release" );
257     p_sys->f_threshold   = var_CreateGetFloat( p_aout, "compressor-threshold" );
258     p_sys->f_ratio       = var_CreateGetFloat( p_aout, "compressor-ratio" );
259     p_sys->f_knee        = var_CreateGetFloat( p_aout, "compressor-knee" );
260     p_sys->f_makeup_gain =
261            var_CreateGetFloat( p_aout, "compressor-makeup-gain" );
262
263     /* Initialize the mutex */
264     vlc_mutex_init( &p_sys->lock );
265
266     /* Add our own callbacks */
267     var_AddCallback( p_aout, "compressor-rms-peak", RMSPeakCallback, p_sys );
268     var_AddCallback( p_aout, "compressor-attack", AttackCallback, p_sys );
269     var_AddCallback( p_aout, "compressor-release", ReleaseCallback, p_sys );
270     var_AddCallback( p_aout, "compressor-threshold", ThresholdCallback, p_sys );
271     var_AddCallback( p_aout, "compressor-ratio", RatioCallback, p_sys );
272     var_AddCallback( p_aout, "compressor-knee", KneeCallback, p_sys );
273     var_AddCallback( p_aout, "compressor-makeup-gain", MakeupGainCallback, p_sys );
274
275     /* Set the filter function */
276     p_filter->pf_audio_filter = DoWork;
277
278     /* At this stage, we are ready! */
279     msg_Dbg( p_filter, "compressor successfully initialized" );
280     return VLC_SUCCESS;
281 }
282
283 /*****************************************************************************
284  * Close: destroy interface
285  *****************************************************************************/
286
287 static void Close( vlc_object_t *p_this )
288 {
289     filter_t *p_filter = (filter_t*)p_this;
290     vlc_object_t *p_aout = p_filter->p_parent;
291     filter_sys_t *p_sys = p_filter->p_sys;
292
293     /* Remove our callbacks */
294     var_DelCallback( p_aout, "compressor-rms-peak", RMSPeakCallback, p_sys );
295     var_DelCallback( p_aout, "compressor-attack", AttackCallback, p_sys );
296     var_DelCallback( p_aout, "compressor-release", ReleaseCallback, p_sys );
297     var_DelCallback( p_aout, "compressor-threshold", ThresholdCallback, p_sys );
298     var_DelCallback( p_aout, "compressor-ratio", RatioCallback, p_sys );
299     var_DelCallback( p_aout, "compressor-knee", KneeCallback, p_sys );
300     var_DelCallback( p_aout, "compressor-makeup-gain", MakeupGainCallback, p_sys );
301
302     /* Destroy the mutex */
303     vlc_mutex_destroy( &p_sys->lock );
304
305     /* Destroy the filter parameter structure */
306     free( p_sys );
307 }
308
309 /*****************************************************************************
310  * DoWork: process samples buffer
311  *****************************************************************************/
312
313 static block_t * DoWork( filter_t * p_filter, block_t * p_in_buf )
314 {
315     int i_samples = p_in_buf->i_nb_samples;
316     int i_channels = aout_FormatNbChannels( &p_filter->fmt_in.audio );
317     float *pf_buf = (float*)p_in_buf->p_buffer;
318
319     /* Current parameters */
320     filter_sys_t *p_sys = p_filter->p_sys;
321
322     /* Fetch the configurable parameters */
323     vlc_mutex_lock( &p_sys->lock );
324
325     float f_rms_peak    = p_sys->f_rms_peak;     /* RMS/peak */
326     float f_attack      = p_sys->f_attack;       /* Attack time (ms) */
327     float f_release     = p_sys->f_release;      /* Release time (ms) */
328     float f_threshold   = p_sys->f_threshold;    /* Threshold level (dB) */
329     float f_ratio       = p_sys->f_ratio;        /* Ratio (n:1) */
330     float f_knee        = p_sys->f_knee;         /* Knee radius (dB) */
331     float f_makeup_gain = p_sys->f_makeup_gain;  /* Makeup gain (dB) */
332
333     vlc_mutex_unlock( &p_sys->lock );
334
335     /* Fetch the internal parameters */
336     float f_amp      =  p_sys->f_amp;
337     float *pf_as     =  p_sys->pf_as;
338     float f_env      =  p_sys->f_env;
339     float f_env_peak =  p_sys->f_env_peak;
340     float f_env_rms  =  p_sys->f_env_rms;
341     float f_gain     =  p_sys->f_gain;
342     float f_gain_out =  p_sys->f_gain_out;
343     rms_env *p_rms   = &p_sys->rms;
344     float f_sum      =  p_sys->f_sum;
345     lookahead *p_la  = &p_sys->la;
346
347     /* Prepare other compressor parameters */
348     float f_ga       = f_attack < 2.0f ? 0.0f :
349                        pf_as[Round( f_attack  * 0.001f * ( A_TBL - 1 ) )];
350     float f_gr       = pf_as[Round( f_release * 0.001f * ( A_TBL - 1 ) )];
351     float f_rs       = ( f_ratio - 1.0f ) / f_ratio;
352     float f_mug      = Db2Lin( f_makeup_gain, p_sys );
353     float f_knee_min = Db2Lin( f_threshold - f_knee, p_sys );
354     float f_knee_max = Db2Lin( f_threshold + f_knee, p_sys );
355     float f_ef_a     = f_ga * 0.25f;
356     float f_ef_ai    = 1.0f - f_ef_a;
357
358     /* Process the current buffer */
359     for( int i = 0; i < i_samples; i++ )
360     {
361         float f_lev_in_old, f_lev_in_new;
362
363         /* Now, compress the pre-equalized audio (ported from sc4_1882
364          * plugin with a few modifications) */
365
366         /* Fetch the old delayed buffer value */
367         f_lev_in_old = p_la->p_buf[p_la->i_pos].f_lev_in;
368
369         /* Find the peak value of current sample.  This becomes the new delayed
370          * buffer value that replaces the old one in the lookahead array */
371         f_lev_in_new = fabs( pf_buf[0] );
372         for( int i_chan = 1; i_chan < i_channels; i_chan++ )
373         {
374             f_lev_in_new = Max( f_lev_in_new, fabs( pf_buf[i_chan] ) );
375         }
376         p_la->p_buf[p_la->i_pos].f_lev_in = f_lev_in_new;
377
378         /* Add the square of the peak value to a running sum */
379         f_sum += f_lev_in_new * f_lev_in_new;
380
381         /* Update the RMS envelope */
382         if( f_amp > f_env_rms )
383         {
384             f_env_rms = f_env_rms * f_ga + f_amp * ( 1.0f - f_ga );
385         }
386         else
387         {
388             f_env_rms = f_env_rms * f_gr + f_amp * ( 1.0f - f_gr );
389         }
390         RoundToZero( &f_env_rms );
391
392         /* Update the peak envelope */
393         if( f_lev_in_old > f_env_peak )
394         {
395             f_env_peak = f_env_peak * f_ga + f_lev_in_old * ( 1.0f - f_ga );
396         }
397         else
398         {
399             f_env_peak = f_env_peak * f_gr + f_lev_in_old * ( 1.0f - f_gr );
400         }
401         RoundToZero( &f_env_peak );
402
403         /* Process the RMS value and update the output gain every 4 samples */
404         if( ( p_sys->i_count++ & 3 ) == 3 )
405         {
406             /* Process the RMS value by placing in the mean square value, and
407              * reset the running sum */
408             f_amp = RmsEnvProcess( p_rms, f_sum * 0.25f );
409             f_sum = 0.0f;
410             if( isnan( f_env_rms ) )
411             {
412                 /* This can happen sometimes, but I don't know why. */
413                 f_env_rms = 0.0f;
414             }
415
416             /* Find the superposition of the RMS and peak envelopes */
417             f_env = LIN_INTERP( f_rms_peak, f_env_rms, f_env_peak );
418
419             /* Update the output gain */
420             if( f_env <= f_knee_min )
421             {
422                 /* Gain below the knee (and below the threshold) */
423                 f_gain_out = 1.0f;
424             }
425             else if( f_env < f_knee_max )
426             {
427                 /* Gain within the knee */
428                 const float f_x = -( f_threshold
429                                    - f_knee - Lin2Db( f_env, p_sys ) ) / f_knee;
430                 f_gain_out = Db2Lin( -f_knee * f_rs * f_x * f_x * 0.25f,
431                                       p_sys );
432             }
433             else
434             {
435                 /* Gain above the knee (and above the threshold) */
436                 f_gain_out = Db2Lin( ( f_threshold - Lin2Db( f_env, p_sys ) )
437                                      * f_rs, p_sys );
438             }
439         }
440
441         /* Find the total gain */
442         f_gain = f_gain * f_ef_a + f_gain_out * f_ef_ai;
443
444         /* Write the resulting buffer to the output */
445         BufferProcess( pf_buf, i_channels, f_gain, f_mug, p_la );
446         pf_buf += i_channels;
447     }
448
449     /* Update the internal parameters */
450     p_sys->f_sum      = f_sum;
451     p_sys->f_amp      = f_amp;
452     p_sys->f_gain     = f_gain;
453     p_sys->f_gain_out = f_gain_out;
454     p_sys->f_env      = f_env;
455     p_sys->f_env_rms  = f_env_rms;
456     p_sys->f_env_peak = f_env_peak;
457
458     return p_in_buf;
459 }
460
461 /*****************************************************************************
462  * Helper functions for compressor
463  *****************************************************************************/
464
465 static void DbInit( filter_sys_t * p_sys )
466 {
467     float *pf_lin_data = p_sys->pf_lin_data;
468     float *pf_db_data = p_sys->pf_db_data;
469
470     /* Fill linear lookup table */
471     for( int i = 0; i < LIN_TABLE_SIZE; i++ )
472     {
473         pf_lin_data[i] = powf( 10.0f, ( ( DB_MAX - DB_MIN ) *
474                    (float)i / LIN_TABLE_SIZE + DB_MIN ) / 20.0f );
475     }
476
477     /* Fill logarithmic lookup table */
478     for( int i = 0; i < DB_TABLE_SIZE; i++ )
479     {
480         pf_db_data[i] = 20.0f * log10f( ( LIN_MAX - LIN_MIN ) *
481                    (float)i / DB_TABLE_SIZE + LIN_MIN );
482     }
483 }
484
485 static float Db2Lin( float f_db, filter_sys_t * p_sys )
486 {
487     float f_scale = ( f_db - DB_MIN ) * LIN_TABLE_SIZE / ( DB_MAX - DB_MIN );
488     int i_base = Round( f_scale - 0.5f );
489     float f_ofs = f_scale - i_base;
490     float *pf_lin_data = p_sys->pf_lin_data;
491
492     if( i_base < 1 )
493     {
494         return 0.0f;
495     }
496     else if( i_base > LIN_TABLE_SIZE - 3 )
497     {
498         return pf_lin_data[LIN_TABLE_SIZE - 2];
499     }
500
501 #ifdef DB_DEFAULT_CUBE
502     return CubeInterp( f_ofs, pf_lin_data[i_base - 1],
503                               pf_lin_data[i_base],
504                               pf_lin_data[i_base + 1],
505                               pf_lin_data[i_base + 2] );
506 #else
507     return ( 1.0f - f_ofs ) * pf_lin_data[i_base]
508                   + f_ofs   * pf_lin_data[i_base + 1];
509 #endif
510 }
511
512 static float Lin2Db( float f_lin, filter_sys_t * p_sys )
513 {
514     float f_scale = ( f_lin - LIN_MIN ) * DB_TABLE_SIZE / ( LIN_MAX - LIN_MIN );
515     int i_base = Round( f_scale - 0.5f );
516     float f_ofs = f_scale - i_base;
517     float *pf_db_data = p_sys->pf_db_data;
518
519     if( i_base < 2 )
520     {
521         return pf_db_data[2] * f_scale * 0.5f - 23.0f * ( 2.0f - f_scale );
522     }
523     else if( i_base > DB_TABLE_SIZE - 3 )
524     {
525         return pf_db_data[DB_TABLE_SIZE - 2];
526     }
527
528 #ifdef DB_DEFAULT_CUBE
529     return CubeInterp( f_ofs, pf_db_data[i_base - 1],
530                               pf_db_data[i_base],
531                               pf_db_data[i_base + 1],
532                               pf_db_data[i_base + 2] );
533 #else
534     return ( 1.0f - f_ofs ) * pf_db_data[i_base]
535                   + f_ofs   * pf_db_data[i_base + 1];
536 #endif
537 }
538
539 #ifdef DB_DEFAULT_CUBE
540 /* Cubic interpolation function */
541 static float CubeInterp( const float f_fr, const float f_inm1,
542                                            const float f_in,
543                                            const float f_inp1,
544                                            const float f_inp2 )
545 {
546     return f_in + 0.5f * f_fr * ( f_inp1 - f_inm1 +
547          f_fr * ( 4.0f * f_inp1 + 2.0f * f_inm1 - 5.0f * f_in - f_inp2 +
548          f_fr * ( 3.0f * ( f_in - f_inp1 ) - f_inm1 + f_inp2 ) ) );
549 }
550 #endif
551
552 /* Zero out denormals by adding and subtracting a small number, from Laurent
553  * de Soras */
554 static void RoundToZero( float *pf_x )
555 {
556     static const float f_anti_denormal = 1e-18;
557
558     *pf_x += f_anti_denormal;
559     *pf_x -= f_anti_denormal;
560 }
561
562 /* A set of branchless clipping operations from Laurent de Soras */
563
564 static float Max( float f_x, float f_a )
565 {
566     f_x -= f_a;
567     f_x += fabs( f_x );
568     f_x *= 0.5;
569     f_x += f_a;
570
571     return f_x;
572 }
573
574 static float Clamp( float f_x, float f_a, float f_b )
575 {
576     const float f_x1 = fabs( f_x - f_a );
577     const float f_x2 = fabs( f_x - f_b );
578
579     f_x = f_x1 + f_a + f_b;
580     f_x -= f_x2;
581     f_x *= 0.5;
582
583     return f_x;
584 }
585
586 /* Round float to int using IEEE int* hack */
587 static int Round( float f_x )
588 {
589     ls_pcast32 p;
590
591     p.f = f_x;
592     p.f += ( 3 << 22 );
593
594     return p.i - 0x4b400000;
595 }
596
597 /* Calculate current level from root-mean-squared of circular buffer ("RMS") */
598 static float RmsEnvProcess( rms_env * p_r, const float f_x )
599 {
600     /* Remove the old term from the sum */
601     p_r->f_sum -= p_r->pf_buf[p_r->i_pos];
602
603     /* Add the new term to the sum */
604     p_r->f_sum += f_x;
605
606     /* If the sum is small enough, make it zero */
607     if( p_r->f_sum < 1.0e-6 )
608     {
609         p_r->f_sum = 0.0f;
610     }
611
612     /* Replace the old term in the array with the new one */
613     p_r->pf_buf[p_r->i_pos] = f_x;
614
615     /* Go to the next position for the next RMS calculation */
616     p_r->i_pos = ( p_r->i_pos + 1 ) % ( p_r->i_count );
617
618     /* Return the RMS value */
619     return sqrt( p_r->f_sum / p_r->i_count );
620 }
621
622 /* Output the compressed delayed buffer and store the current buffer.  Uses a
623  * circular array, just like the one used in calculating the RMS of the buffer
624  */
625 static void BufferProcess( float * pf_buf, int i_channels, float f_gain,
626                            float f_mug, lookahead * p_la )
627 {
628     /* Loop through every channel */
629     for( int i_chan = 0; i_chan < i_channels; i_chan++ )
630     {
631         float f_x = pf_buf[i_chan]; /* Current buffer value */
632
633         /* Output the compressed delayed buffer value */
634         pf_buf[i_chan] = p_la->p_buf[p_la->i_pos].pf_vals[i_chan]
635                        * f_gain * f_mug;
636
637         /* Update the delayed buffer value */
638         p_la->p_buf[p_la->i_pos].pf_vals[i_chan] = f_x;
639     }
640
641     /* Go to the next delayed buffer value for the next run */
642     p_la->i_pos = ( p_la->i_pos + 1 ) % ( p_la->i_count );
643 }
644
645 /*****************************************************************************
646  * Callback functions
647  *****************************************************************************/
648 static int RMSPeakCallback( vlc_object_t *p_this, char const *psz_cmd,
649                             vlc_value_t oldval, vlc_value_t newval,
650                             void * p_data )
651 {
652     VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
653     filter_sys_t *p_sys = p_data;
654
655     vlc_mutex_lock( &p_sys->lock );
656     p_sys->f_rms_peak = Clamp( newval.f_float, 0.0f, 1.0f );
657     vlc_mutex_unlock( &p_sys->lock );
658
659     return VLC_SUCCESS;
660 }
661
662 static int AttackCallback( vlc_object_t *p_this, char const *psz_cmd,
663                            vlc_value_t oldval, vlc_value_t newval,
664                            void * p_data )
665 {
666     VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
667     filter_sys_t *p_sys = p_data;
668
669     vlc_mutex_lock( &p_sys->lock );
670     p_sys->f_attack = Clamp( newval.f_float, 1.5f, 400.0f );
671     vlc_mutex_unlock( &p_sys->lock );
672
673     return VLC_SUCCESS;
674 }
675
676 static int ReleaseCallback( vlc_object_t *p_this, char const *psz_cmd,
677                             vlc_value_t oldval, vlc_value_t newval,
678                             void * p_data )
679 {
680     VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
681     filter_sys_t *p_sys = p_data;
682
683     vlc_mutex_lock( &p_sys->lock );
684     p_sys->f_release = Clamp( newval.f_float, 2.0f, 800.0f );
685     vlc_mutex_unlock( &p_sys->lock );
686
687     return VLC_SUCCESS;
688 }
689
690 static int ThresholdCallback( vlc_object_t *p_this, char const *psz_cmd,
691                               vlc_value_t oldval, vlc_value_t newval,
692                               void * p_data )
693 {
694     VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
695     filter_sys_t *p_sys = p_data;
696
697     vlc_mutex_lock( &p_sys->lock );
698     p_sys->f_threshold = Clamp( newval.f_float, -30.0f, 0.0f );
699     vlc_mutex_unlock( &p_sys->lock );
700
701     return VLC_SUCCESS;
702 }
703
704 static int RatioCallback( vlc_object_t *p_this, char const *psz_cmd,
705                           vlc_value_t oldval, vlc_value_t newval,
706                           void * p_data )
707 {
708     VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
709     filter_sys_t *p_sys = p_data;
710
711     vlc_mutex_lock( &p_sys->lock );
712     p_sys->f_ratio = Clamp( newval.f_float, 1.0f, 20.0f );
713     vlc_mutex_unlock( &p_sys->lock );
714
715     return VLC_SUCCESS;
716 }
717
718 static int KneeCallback( vlc_object_t *p_this, char const *psz_cmd,
719                          vlc_value_t oldval, vlc_value_t newval,
720                          void * p_data )
721 {
722     VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
723     filter_sys_t *p_sys = p_data;
724
725     vlc_mutex_lock( &p_sys->lock );
726     p_sys->f_knee = Clamp( newval.f_float, 1.0f, 10.0f );
727     vlc_mutex_unlock( &p_sys->lock );
728
729     return VLC_SUCCESS;
730 }
731
732 static int MakeupGainCallback( vlc_object_t *p_this, char const *psz_cmd,
733                                vlc_value_t oldval, vlc_value_t newval,
734                                void * p_data )
735 {
736     VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
737     filter_sys_t *p_sys = p_data;
738
739     vlc_mutex_lock( &p_sys->lock );
740     p_sys->f_makeup_gain = Clamp( newval.f_float, 0.0f, 24.0f );
741     vlc_mutex_unlock( &p_sys->lock );
742
743     return VLC_SUCCESS;
744 }