1 /*****************************************************************************
2 * compressor.c: dynamic range compressor, ported from plugins from LADSPA SWH
3 *****************************************************************************
4 * Copyright (C) 2010 Ronald Wright
7 * Author: Ronald Wright <logiconcepts819@gmail.com>
8 * Original author: Steve Harris <steve@plugin.org.uk>
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.
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.
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 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
36 #include <vlc_common.h>
37 #include <vlc_plugin.h>
40 #include <vlc_filter.h>
42 /*****************************************************************************
44 *****************************************************************************/
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)
58 #define LIN_INTERP(f,a,b) ((a) + (f) * ( (b) - (a) ))
59 #define LIMIT(v,l,u) (v < l ? l : ( v > u ? u : v ))
63 float pf_buf[RMS_BUF_SIZE];
74 float pf_vals[AOUT_CHAN_MAX];
77 } p_buf[LOOKAHEAD_SIZE];
97 float pf_db_data[DB_TABLE_SIZE];
98 float pf_lin_data[LIN_TABLE_SIZE];
118 static int Open ( vlc_object_t * );
119 static void Close ( vlc_object_t * );
120 static block_t *DoWork ( filter_t *, block_t * );
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 );
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 * );
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 * );
151 /*****************************************************************************
153 *****************************************************************************/
155 #define RMS_PEAK_TEXT N_( "RMS/peak" )
156 #define RMS_PEAK_LONGTEXT N_( "Set the RMS/peak (0 ... 1)." )
158 #define ATTACK_TEXT N_( "Attack time" )
159 #define ATTACK_LONGTEXT N_( \
160 "Set the attack time in milliseconds (1.5 ... 400)." )
162 #define RELEASE_TEXT N_( "Release time" )
163 #define RELEASE_LONGTEXT N_( \
164 "Set the release time in milliseconds (2 ... 800)." )
166 #define THRESHOLD_TEXT N_( "Threshold level" )
167 #define THRESHOLD_LONGTEXT N_( "Set the threshold level in dB (-30 ... 0)." )
169 #define RATIO_TEXT N_( "Ratio" )
170 #define RATIO_LONGTEXT N_( "Set the ratio (n:1) (1 ... 20)." )
172 #define KNEE_TEXT N_( "Knee radius" )
173 #define KNEE_LONGTEXT N_( "Set the knee radius in dB (1 ... 10)." )
175 #define MAKEUP_GAIN_TEXT N_( "Makeup gain" )
176 #define MAKEUP_GAIN_LONGTEXT N_( "Set the makeup gain in dB (0 ... 24)." )
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 )
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" )
203 /*****************************************************************************
204 * Open: initialize interface
205 *****************************************************************************/
207 static int Open( vlc_object_t *p_this )
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;
215 if( p_filter->fmt_in.audio.i_format != VLC_CODEC_FL32 ||
216 p_filter->fmt_out.audio.i_format != VLC_CODEC_FL32 )
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" );
223 if( !AOUT_FMTS_SIMILAR( &p_filter->fmt_in.audio,
224 &p_filter->fmt_out.audio ) )
226 p_filter->fmt_out.audio = p_filter->fmt_in.audio;
227 msg_Warn( p_filter, "input and output formats are not similar" );
231 /* Initialize the filter parameter structure */
232 p_sys = p_filter->p_sys = calloc( 1, sizeof(*p_sys) );
238 /* Initialize the attack lookup table */
239 p_sys->pf_as[0] = 1.0f;
240 for( int i = 1; i < A_TBL; i++ )
242 p_sys->pf_as[i] = expf( -1.0f / ( f_sample_rate * i / A_TBL ) );
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 ) );
250 /* Initialize decibel lookup tables */
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" );
263 /* Initialize the mutex */
264 vlc_mutex_init( &p_sys->lock );
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 );
275 /* Set the filter function */
276 p_filter->pf_audio_filter = DoWork;
278 /* At this stage, we are ready! */
279 msg_Dbg( p_filter, "compressor successfully initialized" );
283 /*****************************************************************************
284 * Close: destroy interface
285 *****************************************************************************/
287 static void Close( vlc_object_t *p_this )
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;
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 );
302 /* Destroy the mutex */
303 vlc_mutex_destroy( &p_sys->lock );
305 /* Destroy the filter parameter structure */
309 /*****************************************************************************
310 * DoWork: process samples buffer
311 *****************************************************************************/
313 static block_t * DoWork( filter_t * p_filter, block_t * p_in_buf )
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;
319 /* Current parameters */
320 filter_sys_t *p_sys = p_filter->p_sys;
322 /* Fetch the configurable parameters */
323 vlc_mutex_lock( &p_sys->lock );
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) */
333 vlc_mutex_unlock( &p_sys->lock );
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;
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;
358 /* Process the current buffer */
359 for( int i = 0; i < i_samples; i++ )
361 float f_lev_in_old, f_lev_in_new;
363 /* Now, compress the pre-equalized audio (ported from sc4_1882
364 * plugin with a few modifications) */
366 /* Fetch the old delayed buffer value */
367 f_lev_in_old = p_la->p_buf[p_la->i_pos].f_lev_in;
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++ )
374 f_lev_in_new = Max( f_lev_in_new, fabs( pf_buf[i_chan] ) );
376 p_la->p_buf[p_la->i_pos].f_lev_in = f_lev_in_new;
378 /* Add the square of the peak value to a running sum */
379 f_sum += f_lev_in_new * f_lev_in_new;
381 /* Update the RMS envelope */
382 if( f_amp > f_env_rms )
384 f_env_rms = f_env_rms * f_ga + f_amp * ( 1.0f - f_ga );
388 f_env_rms = f_env_rms * f_gr + f_amp * ( 1.0f - f_gr );
390 RoundToZero( &f_env_rms );
392 /* Update the peak envelope */
393 if( f_lev_in_old > f_env_peak )
395 f_env_peak = f_env_peak * f_ga + f_lev_in_old * ( 1.0f - f_ga );
399 f_env_peak = f_env_peak * f_gr + f_lev_in_old * ( 1.0f - f_gr );
401 RoundToZero( &f_env_peak );
403 /* Process the RMS value and update the output gain every 4 samples */
404 if( ( p_sys->i_count++ & 3 ) == 3 )
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 );
410 if( isnan( f_env_rms ) )
412 /* This can happen sometimes, but I don't know why. */
416 /* Find the superposition of the RMS and peak envelopes */
417 f_env = LIN_INTERP( f_rms_peak, f_env_rms, f_env_peak );
419 /* Update the output gain */
420 if( f_env <= f_knee_min )
422 /* Gain below the knee (and below the threshold) */
425 else if( f_env < f_knee_max )
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,
435 /* Gain above the knee (and above the threshold) */
436 f_gain_out = Db2Lin( ( f_threshold - Lin2Db( f_env, p_sys ) )
441 /* Find the total gain */
442 f_gain = f_gain * f_ef_a + f_gain_out * f_ef_ai;
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;
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;
461 /*****************************************************************************
462 * Helper functions for compressor
463 *****************************************************************************/
465 static void DbInit( filter_sys_t * p_sys )
467 float *pf_lin_data = p_sys->pf_lin_data;
468 float *pf_db_data = p_sys->pf_db_data;
470 /* Fill linear lookup table */
471 for( int i = 0; i < LIN_TABLE_SIZE; i++ )
473 pf_lin_data[i] = powf( 10.0f, ( ( DB_MAX - DB_MIN ) *
474 (float)i / LIN_TABLE_SIZE + DB_MIN ) / 20.0f );
477 /* Fill logarithmic lookup table */
478 for( int i = 0; i < DB_TABLE_SIZE; i++ )
480 pf_db_data[i] = 20.0f * log10f( ( LIN_MAX - LIN_MIN ) *
481 (float)i / DB_TABLE_SIZE + LIN_MIN );
485 static float Db2Lin( float f_db, filter_sys_t * p_sys )
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;
496 else if( i_base > LIN_TABLE_SIZE - 3 )
498 return pf_lin_data[LIN_TABLE_SIZE - 2];
501 #ifdef DB_DEFAULT_CUBE
502 return CubeInterp( f_ofs, pf_lin_data[i_base - 1],
504 pf_lin_data[i_base + 1],
505 pf_lin_data[i_base + 2] );
507 return ( 1.0f - f_ofs ) * pf_lin_data[i_base]
508 + f_ofs * pf_lin_data[i_base + 1];
512 static float Lin2Db( float f_lin, filter_sys_t * p_sys )
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;
521 return pf_db_data[2] * f_scale * 0.5f - 23.0f * ( 2.0f - f_scale );
523 else if( i_base > DB_TABLE_SIZE - 3 )
525 return pf_db_data[DB_TABLE_SIZE - 2];
528 #ifdef DB_DEFAULT_CUBE
529 return CubeInterp( f_ofs, pf_db_data[i_base - 1],
531 pf_db_data[i_base + 1],
532 pf_db_data[i_base + 2] );
534 return ( 1.0f - f_ofs ) * pf_db_data[i_base]
535 + f_ofs * pf_db_data[i_base + 1];
539 #ifdef DB_DEFAULT_CUBE
540 /* Cubic interpolation function */
541 static float CubeInterp( const float f_fr, const float f_inm1,
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 ) ) );
552 /* Zero out denormals by adding and subtracting a small number, from Laurent
554 static void RoundToZero( float *pf_x )
556 static const float f_anti_denormal = 1e-18;
558 *pf_x += f_anti_denormal;
559 *pf_x -= f_anti_denormal;
562 /* A set of branchless clipping operations from Laurent de Soras */
564 static float Max( float f_x, float f_a )
574 static float Clamp( float f_x, float f_a, float f_b )
576 const float f_x1 = fabs( f_x - f_a );
577 const float f_x2 = fabs( f_x - f_b );
579 f_x = f_x1 + f_a + f_b;
586 /* Round float to int using IEEE int* hack */
587 static int Round( float f_x )
594 return p.i - 0x4b400000;
597 /* Calculate current level from root-mean-squared of circular buffer ("RMS") */
598 static float RmsEnvProcess( rms_env * p_r, const float f_x )
600 /* Remove the old term from the sum */
601 p_r->f_sum -= p_r->pf_buf[p_r->i_pos];
603 /* Add the new term to the sum */
606 /* If the sum is small enough, make it zero */
607 if( p_r->f_sum < 1.0e-6 )
612 /* Replace the old term in the array with the new one */
613 p_r->pf_buf[p_r->i_pos] = f_x;
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 );
618 /* Return the RMS value */
619 return sqrt( p_r->f_sum / p_r->i_count );
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
625 static void BufferProcess( float * pf_buf, int i_channels, float f_gain,
626 float f_mug, lookahead * p_la )
628 /* Loop through every channel */
629 for( int i_chan = 0; i_chan < i_channels; i_chan++ )
631 float f_x = pf_buf[i_chan]; /* Current buffer value */
633 /* Output the compressed delayed buffer value */
634 pf_buf[i_chan] = p_la->p_buf[p_la->i_pos].pf_vals[i_chan]
637 /* Update the delayed buffer value */
638 p_la->p_buf[p_la->i_pos].pf_vals[i_chan] = f_x;
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 );
645 /*****************************************************************************
647 *****************************************************************************/
648 static int RMSPeakCallback( vlc_object_t *p_this, char const *psz_cmd,
649 vlc_value_t oldval, vlc_value_t newval,
652 VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
653 filter_sys_t *p_sys = p_data;
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 );
662 static int AttackCallback( vlc_object_t *p_this, char const *psz_cmd,
663 vlc_value_t oldval, vlc_value_t newval,
666 VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
667 filter_sys_t *p_sys = p_data;
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 );
676 static int ReleaseCallback( vlc_object_t *p_this, char const *psz_cmd,
677 vlc_value_t oldval, vlc_value_t newval,
680 VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
681 filter_sys_t *p_sys = p_data;
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 );
690 static int ThresholdCallback( vlc_object_t *p_this, char const *psz_cmd,
691 vlc_value_t oldval, vlc_value_t newval,
694 VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
695 filter_sys_t *p_sys = p_data;
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 );
704 static int RatioCallback( vlc_object_t *p_this, char const *psz_cmd,
705 vlc_value_t oldval, vlc_value_t newval,
708 VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
709 filter_sys_t *p_sys = p_data;
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 );
718 static int KneeCallback( vlc_object_t *p_this, char const *psz_cmd,
719 vlc_value_t oldval, vlc_value_t newval,
722 VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
723 filter_sys_t *p_sys = p_data;
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 );
732 static int MakeupGainCallback( vlc_object_t *p_this, char const *psz_cmd,
733 vlc_value_t oldval, vlc_value_t newval,
736 VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
737 filter_sys_t *p_sys = p_data;
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 );