1 /*****************************************************************************
2 * compressor.c: dynamic range compressor, ported from plugins from LADSPA SWH
3 *****************************************************************************
4 * Copyright (C) 2010 Ronald Wright
5 * $Id: 6a7f726f793c40511f1d1574334e56b47baa559a $
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 it
11 * under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * 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." )
157
158 #define ATTACK_TEXT N_( "Attack time" )
159 #define ATTACK_LONGTEXT N_( "Set the attack time in milliseconds." )
160
161 #define RELEASE_TEXT N_( "Release time" )
162 #define RELEASE_LONGTEXT N_( "Set the release time in milliseconds." )
163
164 #define THRESHOLD_TEXT N_( "Threshold level" )
165 #define THRESHOLD_LONGTEXT N_( "Set the threshold level in dB." )
166
167 #define RATIO_TEXT N_( "Ratio" )
168 #define RATIO_LONGTEXT N_( "Set the ratio (n:1)." )
169
170 #define KNEE_TEXT N_( "Knee radius" )
171 #define KNEE_LONGTEXT N_( "Set the knee radius in dB." )
172
173 #define MAKEUP_GAIN_TEXT N_( "Makeup gain" )
174 #define MAKEUP_GAIN_LONGTEXT N_( "Set the makeup gain in dB (0 ... 24)." )
175
176 vlc_module_begin()
177 set_shortname( N_("Compressor") )
178 set_description( N_("Dynamic range compressor") )
179 set_capability( "audio filter", 0 )
set_category(CAT_AUDIO)180 set_category( CAT_AUDIO )
181 set_subcategory( SUBCAT_AUDIO_AFILTER )
182
183 add_float_with_range( "compressor-rms-peak", 0.2, 0.0, 1.0,
184 RMS_PEAK_TEXT, RMS_PEAK_LONGTEXT, false )
185 add_float_with_range( "compressor-attack", 25.0, 1.5, 400.0,
186 ATTACK_TEXT, ATTACK_LONGTEXT, false )
187 add_float_with_range( "compressor-release", 100.0, 2.0, 800.0,
188 RELEASE_TEXT, RELEASE_LONGTEXT, false )
189 add_float_with_range( "compressor-threshold", -11.0, -30.0, 0.0,
190 THRESHOLD_TEXT, THRESHOLD_LONGTEXT, false )
191 add_float_with_range( "compressor-ratio", 4.0, 1.0, 20.0,
192 RATIO_TEXT, RATIO_LONGTEXT, false )
193 add_float_with_range( "compressor-knee", 5.0, 1.0, 10.0,
194 KNEE_TEXT, KNEE_LONGTEXT, false )
195 add_float_with_range( "compressor-makeup-gain", 7.0, 0.0, 24.0,
196 MAKEUP_GAIN_TEXT, MAKEUP_GAIN_LONGTEXT, false )
197 set_callbacks( Open, Close )
198 add_shortcut( "compressor" )
199 vlc_module_end ()
200
201 /*****************************************************************************
202 * Open: initialize interface
203 *****************************************************************************/
204
205 static int Open( vlc_object_t *p_this )
206 {
207 filter_t *p_filter = (filter_t*)p_this;
208 vlc_object_t *p_aout = p_filter->obj.parent;
209 float f_sample_rate = p_filter->fmt_in.audio.i_rate;
210 float f_num;
211
212 /* Initialize the filter parameter structure */
213 filter_sys_t *p_sys = p_filter->p_sys = calloc( 1, sizeof(*p_sys) );
214 if( !p_sys )
215 {
216 return VLC_ENOMEM;
217 }
218
219 /* Initialize the attack lookup table */
220 p_sys->pf_as[0] = 1.0f;
221 for( int i = 1; i < A_TBL; i++ )
222 {
223 p_sys->pf_as[i] = expf( -1.0f / ( f_sample_rate * i / A_TBL ) );
224 }
225
226 /* Calculate the RMS and lookahead sizes from the sample rate */
227 f_num = 0.01f * f_sample_rate;
228 p_sys->rms.i_count = Round( Clamp( 0.5f * f_num, 1.0f, RMS_BUF_SIZE ) );
229 p_sys->la.i_count = Round( Clamp( f_num, 1.0f, LOOKAHEAD_SIZE ) );
230
231 /* Initialize decibel lookup tables */
232 DbInit( p_sys );
233
234 /* Restore the last saved settings */
235 p_sys->f_rms_peak = var_CreateGetFloat( p_aout, "compressor-rms-peak" );
236 p_sys->f_attack = var_CreateGetFloat( p_aout, "compressor-attack" );
237 p_sys->f_release = var_CreateGetFloat( p_aout, "compressor-release" );
238 p_sys->f_threshold = var_CreateGetFloat( p_aout, "compressor-threshold" );
239 p_sys->f_ratio = var_CreateGetFloat( p_aout, "compressor-ratio" );
240 p_sys->f_knee = var_CreateGetFloat( p_aout, "compressor-knee" );
241 p_sys->f_makeup_gain =
242 var_CreateGetFloat( p_aout, "compressor-makeup-gain" );
243
244 /* Initialize the mutex */
245 vlc_mutex_init( &p_sys->lock );
246
247 /* Add our own callbacks */
248 var_AddCallback( p_aout, "compressor-rms-peak", RMSPeakCallback, p_sys );
249 var_AddCallback( p_aout, "compressor-attack", AttackCallback, p_sys );
250 var_AddCallback( p_aout, "compressor-release", ReleaseCallback, p_sys );
251 var_AddCallback( p_aout, "compressor-threshold", ThresholdCallback, p_sys );
252 var_AddCallback( p_aout, "compressor-ratio", RatioCallback, p_sys );
253 var_AddCallback( p_aout, "compressor-knee", KneeCallback, p_sys );
254 var_AddCallback( p_aout, "compressor-makeup-gain", MakeupGainCallback, p_sys );
255
256 /* Set the filter function */
257 p_filter->fmt_in.audio.i_format = VLC_CODEC_FL32;
258 aout_FormatPrepare(&p_filter->fmt_in.audio);
259 p_filter->fmt_out.audio = p_filter->fmt_in.audio;
260 p_filter->pf_audio_filter = DoWork;
261
262 /* At this stage, we are ready! */
263 msg_Dbg( p_filter, "compressor successfully initialized" );
264 return VLC_SUCCESS;
265 }
266
267 /*****************************************************************************
268 * Close: destroy interface
269 *****************************************************************************/
270
Close(vlc_object_t * p_this)271 static void Close( vlc_object_t *p_this )
272 {
273 filter_t *p_filter = (filter_t*)p_this;
274 vlc_object_t *p_aout = p_filter->obj.parent;
275 filter_sys_t *p_sys = p_filter->p_sys;
276
277 /* Remove our callbacks */
278 var_DelCallback( p_aout, "compressor-rms-peak", RMSPeakCallback, p_sys );
279 var_DelCallback( p_aout, "compressor-attack", AttackCallback, p_sys );
280 var_DelCallback( p_aout, "compressor-release", ReleaseCallback, p_sys );
281 var_DelCallback( p_aout, "compressor-threshold", ThresholdCallback, p_sys );
282 var_DelCallback( p_aout, "compressor-ratio", RatioCallback, p_sys );
283 var_DelCallback( p_aout, "compressor-knee", KneeCallback, p_sys );
284 var_DelCallback( p_aout, "compressor-makeup-gain", MakeupGainCallback, p_sys );
285
286 /* Destroy the mutex */
287 vlc_mutex_destroy( &p_sys->lock );
288
289 /* Destroy the filter parameter structure */
290 free( p_sys );
291 }
292
293 /*****************************************************************************
294 * DoWork: process samples buffer
295 *****************************************************************************/
296
DoWork(filter_t * p_filter,block_t * p_in_buf)297 static block_t * DoWork( filter_t * p_filter, block_t * p_in_buf )
298 {
299 int i_samples = p_in_buf->i_nb_samples;
300 int i_channels = aout_FormatNbChannels( &p_filter->fmt_in.audio );
301 float *pf_buf = (float*)p_in_buf->p_buffer;
302
303 /* Current parameters */
304 filter_sys_t *p_sys = p_filter->p_sys;
305
306 /* Fetch the configurable parameters */
307 vlc_mutex_lock( &p_sys->lock );
308
309 float f_rms_peak = p_sys->f_rms_peak; /* RMS/peak */
310 float f_attack = p_sys->f_attack; /* Attack time (ms) */
311 float f_release = p_sys->f_release; /* Release time (ms) */
312 float f_threshold = p_sys->f_threshold; /* Threshold level (dB) */
313 float f_ratio = p_sys->f_ratio; /* Ratio (n:1) */
314 float f_knee = p_sys->f_knee; /* Knee radius (dB) */
315 float f_makeup_gain = p_sys->f_makeup_gain; /* Makeup gain (dB) */
316
317 vlc_mutex_unlock( &p_sys->lock );
318
319 /* Fetch the internal parameters */
320 float f_amp = p_sys->f_amp;
321 float *pf_as = p_sys->pf_as;
322 float f_env = p_sys->f_env;
323 float f_env_peak = p_sys->f_env_peak;
324 float f_env_rms = p_sys->f_env_rms;
325 float f_gain = p_sys->f_gain;
326 float f_gain_out = p_sys->f_gain_out;
327 rms_env *p_rms = &p_sys->rms;
328 float f_sum = p_sys->f_sum;
329 lookahead *p_la = &p_sys->la;
330
331 /* Prepare other compressor parameters */
332 float f_ga = f_attack < 2.0f ? 0.0f :
333 pf_as[Round( f_attack * 0.001f * ( A_TBL - 1 ) )];
334 float f_gr = pf_as[Round( f_release * 0.001f * ( A_TBL - 1 ) )];
335 float f_rs = ( f_ratio - 1.0f ) / f_ratio;
336 float f_mug = Db2Lin( f_makeup_gain, p_sys );
337 float f_knee_min = Db2Lin( f_threshold - f_knee, p_sys );
338 float f_knee_max = Db2Lin( f_threshold + f_knee, p_sys );
339 float f_ef_a = f_ga * 0.25f;
340 float f_ef_ai = 1.0f - f_ef_a;
341
342 /* Process the current buffer */
343 for( int i = 0; i < i_samples; i++ )
344 {
345 float f_lev_in_old, f_lev_in_new;
346
347 /* Now, compress the pre-equalized audio (ported from sc4_1882
348 * plugin with a few modifications) */
349
350 /* Fetch the old delayed buffer value */
351 f_lev_in_old = p_la->p_buf[p_la->i_pos].f_lev_in;
352
353 /* Find the peak value of current sample. This becomes the new delayed
354 * buffer value that replaces the old one in the lookahead array */
355 f_lev_in_new = fabs( pf_buf[0] );
356 for( int i_chan = 1; i_chan < i_channels; i_chan++ )
357 {
358 f_lev_in_new = Max( f_lev_in_new, fabs( pf_buf[i_chan] ) );
359 }
360 p_la->p_buf[p_la->i_pos].f_lev_in = f_lev_in_new;
361
362 /* Add the square of the peak value to a running sum */
363 f_sum += f_lev_in_new * f_lev_in_new;
364
365 /* Update the RMS envelope */
366 if( f_amp > f_env_rms )
367 {
368 f_env_rms = f_env_rms * f_ga + f_amp * ( 1.0f - f_ga );
369 }
370 else
371 {
372 f_env_rms = f_env_rms * f_gr + f_amp * ( 1.0f - f_gr );
373 }
374 RoundToZero( &f_env_rms );
375
376 /* Update the peak envelope */
377 if( f_lev_in_old > f_env_peak )
378 {
379 f_env_peak = f_env_peak * f_ga + f_lev_in_old * ( 1.0f - f_ga );
380 }
381 else
382 {
383 f_env_peak = f_env_peak * f_gr + f_lev_in_old * ( 1.0f - f_gr );
384 }
385 RoundToZero( &f_env_peak );
386
387 /* Process the RMS value and update the output gain every 4 samples */
388 if( ( p_sys->i_count++ & 3 ) == 3 )
389 {
390 /* Process the RMS value by placing in the mean square value, and
391 * reset the running sum */
392 f_amp = RmsEnvProcess( p_rms, f_sum * 0.25f );
393 f_sum = 0.0f;
394 if( isnan( f_env_rms ) )
395 {
396 /* This can happen sometimes, but I don't know why. */
397 f_env_rms = 0.0f;
398 }
399
400 /* Find the superposition of the RMS and peak envelopes */
401 f_env = LIN_INTERP( f_rms_peak, f_env_rms, f_env_peak );
402
403 /* Update the output gain */
404 if( f_env <= f_knee_min )
405 {
406 /* Gain below the knee (and below the threshold) */
407 f_gain_out = 1.0f;
408 }
409 else if( f_env < f_knee_max )
410 {
411 /* Gain within the knee */
412 const float f_x = -( f_threshold
413 - f_knee - Lin2Db( f_env, p_sys ) ) / f_knee;
414 f_gain_out = Db2Lin( -f_knee * f_rs * f_x * f_x * 0.25f,
415 p_sys );
416 }
417 else
418 {
419 /* Gain above the knee (and above the threshold) */
420 f_gain_out = Db2Lin( ( f_threshold - Lin2Db( f_env, p_sys ) )
421 * f_rs, p_sys );
422 }
423 }
424
425 /* Find the total gain */
426 f_gain = f_gain * f_ef_a + f_gain_out * f_ef_ai;
427
428 /* Write the resulting buffer to the output */
429 BufferProcess( pf_buf, i_channels, f_gain, f_mug, p_la );
430 pf_buf += i_channels;
431 }
432
433 /* Update the internal parameters */
434 p_sys->f_sum = f_sum;
435 p_sys->f_amp = f_amp;
436 p_sys->f_gain = f_gain;
437 p_sys->f_gain_out = f_gain_out;
438 p_sys->f_env = f_env;
439 p_sys->f_env_rms = f_env_rms;
440 p_sys->f_env_peak = f_env_peak;
441
442 return p_in_buf;
443 }
444
445 /*****************************************************************************
446 * Helper functions for compressor
447 *****************************************************************************/
448
DbInit(filter_sys_t * p_sys)449 static void DbInit( filter_sys_t * p_sys )
450 {
451 float *pf_lin_data = p_sys->pf_lin_data;
452 float *pf_db_data = p_sys->pf_db_data;
453
454 /* Fill linear lookup table */
455 for( int i = 0; i < LIN_TABLE_SIZE; i++ )
456 {
457 pf_lin_data[i] = powf( 10.0f, ( ( DB_MAX - DB_MIN ) *
458 (float)i / LIN_TABLE_SIZE + DB_MIN ) / 20.0f );
459 }
460
461 /* Fill logarithmic lookup table */
462 for( int i = 0; i < DB_TABLE_SIZE; i++ )
463 {
464 pf_db_data[i] = 20.0f * log10f( ( LIN_MAX - LIN_MIN ) *
465 (float)i / DB_TABLE_SIZE + LIN_MIN );
466 }
467 }
468
Db2Lin(float f_db,filter_sys_t * p_sys)469 static float Db2Lin( float f_db, filter_sys_t * p_sys )
470 {
471 float f_scale = ( f_db - DB_MIN ) * LIN_TABLE_SIZE / ( DB_MAX - DB_MIN );
472 int i_base = Round( f_scale - 0.5f );
473 float f_ofs = f_scale - i_base;
474 float *pf_lin_data = p_sys->pf_lin_data;
475
476 if( i_base < 1 )
477 {
478 return 0.0f;
479 }
480 else if( i_base > LIN_TABLE_SIZE - 3 )
481 {
482 return pf_lin_data[LIN_TABLE_SIZE - 2];
483 }
484
485 #ifdef DB_DEFAULT_CUBE
486 return CubeInterp( f_ofs, pf_lin_data[i_base - 1],
487 pf_lin_data[i_base],
488 pf_lin_data[i_base + 1],
489 pf_lin_data[i_base + 2] );
490 #else
491 return ( 1.0f - f_ofs ) * pf_lin_data[i_base]
492 + f_ofs * pf_lin_data[i_base + 1];
493 #endif
494 }
495
Lin2Db(float f_lin,filter_sys_t * p_sys)496 static float Lin2Db( float f_lin, filter_sys_t * p_sys )
497 {
498 float f_scale = ( f_lin - LIN_MIN ) * DB_TABLE_SIZE / ( LIN_MAX - LIN_MIN );
499 int i_base = Round( f_scale - 0.5f );
500 float f_ofs = f_scale - i_base;
501 float *pf_db_data = p_sys->pf_db_data;
502
503 if( i_base < 2 )
504 {
505 return pf_db_data[2] * f_scale * 0.5f - 23.0f * ( 2.0f - f_scale );
506 }
507 else if( i_base > DB_TABLE_SIZE - 3 )
508 {
509 return pf_db_data[DB_TABLE_SIZE - 2];
510 }
511
512 #ifdef DB_DEFAULT_CUBE
513 return CubeInterp( f_ofs, pf_db_data[i_base - 1],
514 pf_db_data[i_base],
515 pf_db_data[i_base + 1],
516 pf_db_data[i_base + 2] );
517 #else
518 return ( 1.0f - f_ofs ) * pf_db_data[i_base]
519 + f_ofs * pf_db_data[i_base + 1];
520 #endif
521 }
522
523 #ifdef DB_DEFAULT_CUBE
524 /* Cubic interpolation function */
CubeInterp(const float f_fr,const float f_inm1,const float f_in,const float f_inp1,const float f_inp2)525 static float CubeInterp( const float f_fr, const float f_inm1,
526 const float f_in,
527 const float f_inp1,
528 const float f_inp2 )
529 {
530 return f_in + 0.5f * f_fr * ( f_inp1 - f_inm1 +
531 f_fr * ( 4.0f * f_inp1 + 2.0f * f_inm1 - 5.0f * f_in - f_inp2 +
532 f_fr * ( 3.0f * ( f_in - f_inp1 ) - f_inm1 + f_inp2 ) ) );
533 }
534 #endif
535
536 /* Zero out denormals by adding and subtracting a small number, from Laurent
537 * de Soras */
RoundToZero(float * pf_x)538 static void RoundToZero( float *pf_x )
539 {
540 static const float f_anti_denormal = 1e-18;
541
542 *pf_x += f_anti_denormal;
543 *pf_x -= f_anti_denormal;
544 }
545
546 /* A set of branchless clipping operations from Laurent de Soras */
547
Max(float f_x,float f_a)548 static float Max( float f_x, float f_a )
549 {
550 f_x -= f_a;
551 f_x += fabsf( f_x );
552 f_x *= 0.5f;
553 f_x += f_a;
554
555 return f_x;
556 }
557
Clamp(float f_x,float f_a,float f_b)558 static float Clamp( float f_x, float f_a, float f_b )
559 {
560 const float f_x1 = fabsf( f_x - f_a );
561 const float f_x2 = fabsf( f_x - f_b );
562
563 f_x = f_x1 + f_a + f_b;
564 f_x -= f_x2;
565 f_x *= 0.5f;
566
567 return f_x;
568 }
569
570 /* Round float to int using IEEE int* hack */
Round(float f_x)571 static int Round( float f_x )
572 {
573 ls_pcast32 p;
574
575 p.f = f_x;
576 p.f += ( 3 << 22 );
577
578 return p.i - 0x4b400000;
579 }
580
581 /* Calculate current level from root-mean-squared of circular buffer ("RMS") */
RmsEnvProcess(rms_env * p_r,const float f_x)582 static float RmsEnvProcess( rms_env * p_r, const float f_x )
583 {
584 /* Remove the old term from the sum */
585 p_r->f_sum -= p_r->pf_buf[p_r->i_pos];
586
587 /* Add the new term to the sum */
588 p_r->f_sum += f_x;
589
590 /* If the sum is small enough, make it zero */
591 if( p_r->f_sum < 1.0e-6f )
592 {
593 p_r->f_sum = 0.0f;
594 }
595
596 /* Replace the old term in the array with the new one */
597 p_r->pf_buf[p_r->i_pos] = f_x;
598
599 /* Go to the next position for the next RMS calculation */
600 p_r->i_pos = ( p_r->i_pos + 1 ) % ( p_r->i_count );
601
602 /* Return the RMS value */
603 return sqrt( p_r->f_sum / p_r->i_count );
604 }
605
606 /* Output the compressed delayed buffer and store the current buffer. Uses a
607 * circular array, just like the one used in calculating the RMS of the buffer
608 */
BufferProcess(float * pf_buf,int i_channels,float f_gain,float f_mug,lookahead * p_la)609 static void BufferProcess( float * pf_buf, int i_channels, float f_gain,
610 float f_mug, lookahead * p_la )
611 {
612 /* Loop through every channel */
613 for( int i_chan = 0; i_chan < i_channels; i_chan++ )
614 {
615 float f_x = pf_buf[i_chan]; /* Current buffer value */
616
617 /* Output the compressed delayed buffer value */
618 pf_buf[i_chan] = p_la->p_buf[p_la->i_pos].pf_vals[i_chan]
619 * f_gain * f_mug;
620
621 /* Update the delayed buffer value */
622 p_la->p_buf[p_la->i_pos].pf_vals[i_chan] = f_x;
623 }
624
625 /* Go to the next delayed buffer value for the next run */
626 p_la->i_pos = ( p_la->i_pos + 1 ) % ( p_la->i_count );
627 }
628
629 /*****************************************************************************
630 * Callback functions
631 *****************************************************************************/
RMSPeakCallback(vlc_object_t * p_this,char const * psz_cmd,vlc_value_t oldval,vlc_value_t newval,void * p_data)632 static int RMSPeakCallback( vlc_object_t *p_this, char const *psz_cmd,
633 vlc_value_t oldval, vlc_value_t newval,
634 void * p_data )
635 {
636 VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
637 filter_sys_t *p_sys = p_data;
638
639 vlc_mutex_lock( &p_sys->lock );
640 p_sys->f_rms_peak = Clamp( newval.f_float, 0.0f, 1.0f );
641 vlc_mutex_unlock( &p_sys->lock );
642
643 return VLC_SUCCESS;
644 }
645
AttackCallback(vlc_object_t * p_this,char const * psz_cmd,vlc_value_t oldval,vlc_value_t newval,void * p_data)646 static int AttackCallback( vlc_object_t *p_this, char const *psz_cmd,
647 vlc_value_t oldval, vlc_value_t newval,
648 void * p_data )
649 {
650 VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
651 filter_sys_t *p_sys = p_data;
652
653 vlc_mutex_lock( &p_sys->lock );
654 p_sys->f_attack = Clamp( newval.f_float, 1.5f, 400.0f );
655 vlc_mutex_unlock( &p_sys->lock );
656
657 return VLC_SUCCESS;
658 }
659
ReleaseCallback(vlc_object_t * p_this,char const * psz_cmd,vlc_value_t oldval,vlc_value_t newval,void * p_data)660 static int ReleaseCallback( vlc_object_t *p_this, char const *psz_cmd,
661 vlc_value_t oldval, vlc_value_t newval,
662 void * p_data )
663 {
664 VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
665 filter_sys_t *p_sys = p_data;
666
667 vlc_mutex_lock( &p_sys->lock );
668 p_sys->f_release = Clamp( newval.f_float, 2.0f, 800.0f );
669 vlc_mutex_unlock( &p_sys->lock );
670
671 return VLC_SUCCESS;
672 }
673
ThresholdCallback(vlc_object_t * p_this,char const * psz_cmd,vlc_value_t oldval,vlc_value_t newval,void * p_data)674 static int ThresholdCallback( vlc_object_t *p_this, char const *psz_cmd,
675 vlc_value_t oldval, vlc_value_t newval,
676 void * p_data )
677 {
678 VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
679 filter_sys_t *p_sys = p_data;
680
681 vlc_mutex_lock( &p_sys->lock );
682 p_sys->f_threshold = Clamp( newval.f_float, -30.0f, 0.0f );
683 vlc_mutex_unlock( &p_sys->lock );
684
685 return VLC_SUCCESS;
686 }
687
RatioCallback(vlc_object_t * p_this,char const * psz_cmd,vlc_value_t oldval,vlc_value_t newval,void * p_data)688 static int RatioCallback( vlc_object_t *p_this, char const *psz_cmd,
689 vlc_value_t oldval, vlc_value_t newval,
690 void * p_data )
691 {
692 VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
693 filter_sys_t *p_sys = p_data;
694
695 vlc_mutex_lock( &p_sys->lock );
696 p_sys->f_ratio = Clamp( newval.f_float, 1.0f, 20.0f );
697 vlc_mutex_unlock( &p_sys->lock );
698
699 return VLC_SUCCESS;
700 }
701
KneeCallback(vlc_object_t * p_this,char const * psz_cmd,vlc_value_t oldval,vlc_value_t newval,void * p_data)702 static int KneeCallback( vlc_object_t *p_this, char const *psz_cmd,
703 vlc_value_t oldval, vlc_value_t newval,
704 void * p_data )
705 {
706 VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
707 filter_sys_t *p_sys = p_data;
708
709 vlc_mutex_lock( &p_sys->lock );
710 p_sys->f_knee = Clamp( newval.f_float, 1.0f, 10.0f );
711 vlc_mutex_unlock( &p_sys->lock );
712
713 return VLC_SUCCESS;
714 }
715
MakeupGainCallback(vlc_object_t * p_this,char const * psz_cmd,vlc_value_t oldval,vlc_value_t newval,void * p_data)716 static int MakeupGainCallback( vlc_object_t *p_this, char const *psz_cmd,
717 vlc_value_t oldval, vlc_value_t newval,
718 void * p_data )
719 {
720 VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
721 filter_sys_t *p_sys = p_data;
722
723 vlc_mutex_lock( &p_sys->lock );
724 p_sys->f_makeup_gain = Clamp( newval.f_float, 0.0f, 24.0f );
725 vlc_mutex_unlock( &p_sys->lock );
726
727 return VLC_SUCCESS;
728 }
729