[vlc-devel] Dynamic range compressor patch

Ron Wright logiconcepts819 at gmail.com
Sun Jun 27 17:24:25 CEST 2010


I didn't like the delay in the attack and release settings for peak
compression, so I have attached the updated compressor core patch that
resolves the problem.

Regards,

Ronald Wright

On Sun, Jun 27, 2010 at 1:58 AM, Ron Wright <logiconcepts819 at gmail.com>wrote:

> Hello again,
>
> I have updated the patches again as usual, and added a third patch that
> includes the compressor module to be linked against the math library.
> Moreover, I have modified the compressor code so as to add latency
> compensation for RMS compression, also known as "lookahead."  However, this
> comes at a cost.  The cost is a delay at the output that is inversely
> proportional to the sample rate.  If any of you folks can think of a better
> way of implementing this lookahead mechanism, let me know.
>
> Regards,
>
> Ronald Wright
>
>
> On Sat, Jun 26, 2010 at 2:51 PM, Laurent Aimar <fenrir at elivagar.org>wrote:
>
>> On Fri, Jun 25, 2010 at 03:39:30PM -0500, Ron Wright wrote:
>> > Hello,
>> >
>> > I have made many drastic changes in the compressor.c file and some minor
>> > changes in the UI code.  Here are some of the changes:
>> >
>> > 1. Added VLC copyright and $id$ tag to the banner.
>> > 2. Made the filter structure, RMS envelope structure, and attack lookup
>> table
>> > contiguous in memory so that only one memory allocation and one memory
>> > deallocation is needed.
>> > 3. Reorganized the functions so that they don't appear out of place.
>> > 4. Removed two wasteful members (amplitude and gain reduction) from the
>> filter
>> > structure (this is probably used to implement output meters in certain
>> LADSPA
>> > frontends).
>> > 5. The release of the mutex lock in the DoWork function now occurs
>> immediately
>> > after retrieving the shared values.
>> > 6. Replaced the logic in the callback functions with clipping functions.
>> > 7. Removed useless casts.
>> > 8. Changed default values.
>> >
>> > Here are some notes to take into consideration:
>> >
>> > 1. The f_max and f_clamp functions work as expected.  Proof:
>> >
>> > f_max (with arguments x and a):
>> >
>> > If x < a, then 0.5 * ((x - a) + fabs(x - a)) + a = 0.5 * ((x - a) - (x -
>> a)) +
>> > a = a
>> > If x >= a, then 0.5 * ((x - a) + fabs(x - a)) + a = 0.5 * ((x - a) + (x
>> - a)) +
>> > a = 0.5*2*(x - a) + a = x
>> >
>> > f_clamp (with arguments x, a, and b):
>> >
>> > If x < a, then 0.5 * (fabs(x - a) + a + b - fabs(x - b)) = 0.5 * (-(x -
>> a) + a
>> > + b + (x - b)) = 0.5 * (2*a - x + b + x - b) = a
>> > If a <= x < b, then 0.5 * (fabs(x - a) + a + b - fabs(x - b)) = 0.5 *
>> ((x - a)
>> > + a + b + (x - b)) = 0.5 * (x + b + x - b) = x
>> > If x >= b, then 0.5 * (fabs(x - a) + a + b - fabs(x - b)) = 0.5 * ((x -
>> a) + a
>> > + b - (x - b)) = 0.5 * (x + b - x + b) = b
>> >
>> > These are branchless clipping functions from Laurent de Soras.
>> >
>> > 2. A buffer overflow will not occur when either attack or release
>> converted to
>> > seconds is multiplied by (A_TBL - 1), with the resulting value used as
>> an index
>> > to retrieve the attack (or release) value from the lookup table.  The
>> maximum
>> > of the maximum values of attack and release is 800, and A_TBL is 256, so
>> 800 *
>> > 0.001 * 255 = 204.  This means that the index will neither go beyond 255
>> nor
>> > even reach 255.  The minimum of the minimum values of attack and release
>> is
>> > 1.5.  However, the real minimum value used in the code is 2.
>> Nevertheless,
>> > there will also be no buffer underflow, since this value is positive.
>>  Thanks,
>>
>>
>> > diff --git a/modules/audio_filter/Modules.am
>> b/modules/audio_filter/Modules.am
>> > index eae73fc..7767cbe 100644
>> > --- a/modules/audio_filter/Modules.am
>> > +++ b/modules/audio_filter/Modules.am
>> > @@ -1,5 +1,6 @@
>> >  SUBDIRS = channel_mixer converter resampler spatializer
>> >  SOURCES_equalizer = equalizer.c equalizer_presets.h
>> > +SOURCES_compressor = compressor.c
>> >  SOURCES_normvol = normvol.c
>> >  SOURCES_audiobargraph_a = audiobargraph_a.c
>> >  SOURCES_param_eq = param_eq.c
>> > @@ -9,6 +10,7 @@ SOURCES_chorus_flanger = chorus_flanger.c
>> >  libvlc_LTLIBRARIES += \
>> >       libaudiobargraph_a_plugin.la \
>> >       libchorus_flanger_plugin.la \
>> > +     libcompressor_plugin.la \
>> >       libequalizer_plugin.la \
>> >       libnormvol_plugin.la \
>> >       libparam_eq_plugin.la \
>>  I am not sure, but seeing that you use mathematics functions, I think you
>> also
>> need to modify configure.ac to link with -lm.
>>
>> > diff --git a/modules/audio_filter/compressor.c
>> b/modules/audio_filter/compressor.c
>> > new file mode 100644
>> > index 0000000..daa260a
>> > --- /dev/null
>> > +++ b/modules/audio_filter/compressor.c
>> > @@ -0,0 +1,683 @@
>> >
>> +/*****************************************************************************
>> > + * compressor.c: dynamic range compressor, ported from SC4 plugin
>> > +
>> *****************************************************************************
>> > + * Copyright (C) 2010 the VideoLAN team
>>  Please use your name instead of 'the VideoLAN team' (as the code is
>> ported, dunno
>> if the original copyright shouldn't be used too).
>>
>> > +#define A_TBL (256)
>> > +
>> > +#define DB_TABLE_SIZE   (1024)
>> > +#define DB_MIN          (-60.0f)
>> > +#define DB_MAX          (24.0f)
>> > +#define LIN_TABLE_SIZE  (1024)
>> > +#define LIN_MIN         (0.0000000002f)
>> > +#define LIN_MAX         (9.0f)
>> > +#define DB_DEFAULT_CUBE
>> > +#define RMS_BUF_SIZE    (64)
>> > +
>> > +#define LIN_INTERP(f,a,b) ((a) + (f) * ( (b) - (a) ))
>> > +
>> > +typedef struct
>> > +{
>> > +    float        buffer[RMS_BUF_SIZE];
>> > +    unsigned int pos;
>> > +    float        sum;
>> > +
>> > +} rms_env;
>> > +
>> > +struct filter_sys_t
>> > +{
>> > +    float rms_peak;
>> > +    float attack;
>> > +    float release;
>> > +    float threshold;
>> > +    float ratio;
>> > +    float knee;
>> > +    float makeup_gain;
>> > +
>> > +    float amp;
>> > +    float *as;
>> > +    unsigned int count;
>> > +    float env;
>> > +    float env_peak;
>> > +    float env_rms;
>> > +    float gain;
>> > +    float gain_thres;
>> > +    rms_env* rms;
>> > +    float sum;
>> > +
>> > +    float db_data[DB_TABLE_SIZE];
>> > +    float lin_data[LIN_TABLE_SIZE];
>> > +
>> > +    vlc_mutex_t lock;
>> > +};
>>  I meant to use directly
>>
>> rms_env rms;
>> float   as[A_TBL].
>>
>> to simplify the allocation.
>>
>>  Also, if you could move the variables protected by the lock below the
>> declaration of the lock, it would make it easier to maintain.
>>
>> >
>> +/*****************************************************************************
>> > + * Open: initialize interface
>> > +
>> *****************************************************************************/
>> > +
>> > +static int Open( vlc_object_t *p_this )
>> > +{
>> > +    filter_t *p_filter = (filter_t*)p_this;
>> > +    vlc_object_t *p_aout = p_filter->p_parent;
>> > +    float sample_rate = p_filter->fmt_in.audio.i_rate;
>> > +    struct filter_sys_t *p_sys;
>> > +    unsigned int i;
>> > +
>> > +    if( p_filter->fmt_in.audio.i_format != VLC_CODEC_FL32 ||
>> > +        p_filter->fmt_out.audio.i_format != VLC_CODEC_FL32 )
>> > +    {
>> > +        p_filter->fmt_in.audio.i_format = VLC_CODEC_FL32;
>> > +        p_filter->fmt_out.audio.i_format = VLC_CODEC_FL32;
>> > +        msg_Warn( p_filter, "bad input or output format" );
>> > +        return VLC_EGENERIC;
>> > +    }
>> > +    if( !AOUT_FMTS_SIMILAR( &p_filter->fmt_in.audio,
>> > +                            &p_filter->fmt_out.audio ) )
>> > +    {
>> > +        memcpy( &p_filter->fmt_out.audio, &p_filter->fmt_in.audio,
>> > +                sizeof(audio_sample_format_t) );
>> p_filter->fmt_out.audio = p_filter->fmt_in.audio;
>> is simpler.
>>
>> > +        msg_Warn( p_filter, "input and output formats are not similar"
>> );
>> > +        return VLC_EGENERIC;
>> > +    }
>> > +
>> > +    p_sys = p_filter->p_sys = calloc( 1, sizeof(struct filter_sys_t)
>> > +                                       + sizeof(rms_env)
>> > +                                       + A_TBL * sizeof(float) );
>> > +    if( !p_sys )
>> > +    {
>> > +        return VLC_ENOMEM;
>> > +    }
>> > +    p_sys->rms = (rms_env*)( p_sys + 1 );
>> > +    p_sys->as  = (float  *)( p_sys->rms + 1 );
>>  See remarks above at filter_sys_t.
>>
>> >
>> +/*****************************************************************************
>> > + * Close: destroy interface
>> > +
>> *****************************************************************************/
>> > +
>> > +static void Close( vlc_object_t *p_this )
>> > +{
>> > +    filter_t *p_filter = (filter_t*)p_this;
>> > +    vlc_object_t *p_aout = p_filter->p_parent;
>> > +    struct filter_sys_t *p_sys = p_filter->p_sys;
>> > +
>> > +    var_DelCallback( p_aout, "compressor-rms-peak", RMSPeakCallback,
>> p_sys );
>> > +    var_DelCallback( p_aout, "compressor-attack", AttackCallback, p_sys
>> );
>> > +    var_DelCallback( p_aout, "compressor-release", ReleaseCallback,
>> p_sys );
>> > +    var_DelCallback( p_aout, "compressor-threshold", ThresholdCallback,
>> p_sys );
>> > +    var_DelCallback( p_aout, "compressor-ratio", RatioCallback, p_sys
>> );
>> > +    var_DelCallback( p_aout, "compressor-knee", KneeCallback, p_sys );
>> > +    var_DelCallback( p_aout, "compressor-makeup-gain",
>> MakeupGainCallback,
>> > +                     p_sys );
>> > +
>> > +    vlc_mutex_destroy( &p_sys->lock );
>> > +
>> > +    free( p_sys );
>> > +}
>> > +
>> >
>> +/*****************************************************************************
>> > + * DoWork: process samples buffer
>> > +
>> *****************************************************************************/
>> > +
>> > +static block_t * DoWork( filter_t * p_filter, block_t * p_in_buf )
>> > +{
>> > +    int i_samples = p_in_buf->i_nb_samples;
>> > +    int i_channels = aout_FormatNbChannels( &p_filter->fmt_in.audio );
>> > +    float *p_buf = (float*)p_in_buf->p_buffer;
>> > +
>> > +    float rms_peak, attack, release, threshold, ratio, knee,
>> makeup_gain;
>> > +    float amp, *as, env, env_peak, env_rms, gain, gain_thres, sum;
>> > +    unsigned int count;
>> > +    rms_env *rms;
>> > +
>> > +    float ga, gr, rs, mug, knee_min, knee_max, ef_a, ef_ai;
>> > +
>> > +    int pos, pos_chan;
>> > +
>> > +    /* Current configuration */
>> > +    struct filter_sys_t *p_sys = p_filter->p_sys;
>> > +
>> > +    vlc_mutex_lock( &p_sys->lock );
>> > +
>> > +    /* RMS/peak (float value) */
>> > +    rms_peak = p_sys->rms_peak;
>> > +
>> > +    /* Attack time (ms) (float value) */
>> > +    attack = p_sys->attack;
>> > +
>> > +    /* Release time (ms) (float value) */
>> > +    release = p_sys->release;
>> > +
>> > +    /* Threshold level (dB) (float value) */
>> > +    threshold = p_sys->threshold;
>> > +
>> > +    /* Ratio (n:1) (float value) */
>> > +    ratio = p_sys->ratio;
>> > +
>> > +    /* Knee radius (dB) (float value) */
>> > +    knee = p_sys->knee;
>> > +
>> > +    /* Makeup gain (dB) (float value) */
>> > +    makeup_gain = p_sys->makeup_gain;
>> > +
>> > +    vlc_mutex_unlock( &p_sys->lock );
>> > +
>> > +    amp = p_sys->amp;
>> > +    as = p_sys->as;
>> > +    count = p_sys->count;
>> > +    env = p_sys->env;
>> > +    env_peak = p_sys->env_peak;
>> > +    env_rms = p_sys->env_rms;
>> > +    gain = p_sys->gain;
>> > +    gain_thres = p_sys->gain_thres;
>> > +    rms = p_sys->rms;
>> > +    sum = p_sys->sum;
>> > +
>> > +    ga = attack < 2.0f ? 0.0f
>> > +                       : as[f_round( attack * 0.001f * ( A_TBL - 1 )
>> )];
>> > +    gr = as[f_round( release * 0.001f * ( A_TBL - 1 ) )];
>> > +    rs = ( ratio - 1.0f ) / ratio;
>> > +    mug = db2lin( makeup_gain, p_sys );
>> > +    knee_min = db2lin( threshold - knee, p_sys );
>> > +    knee_max = db2lin( threshold + knee, p_sys );
>> > +    ef_a = ga * 0.25f;
>> > +    ef_ai = 1.0f - ef_a;
>> > +    for( pos = 0; pos < i_samples; pos++ )
>>  If you want, you can also declare a variable inside code (we accept c99)
>> or in for()
>> statement.
>>
>> Regards,
>>
>> --
>> fenrir
>>
>> _______________________________________________
>> vlc-devel mailing list
>> To unsubscribe or modify your subscription options:
>> http://mailman.videolan.org/listinfo/vlc-devel
>>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mailman.videolan.org/pipermail/vlc-devel/attachments/20100627/cfb726ae/attachment.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: vlc-compressor-core.patch
Type: text/x-patch
Size: 25071 bytes
Desc: not available
URL: <http://mailman.videolan.org/pipermail/vlc-devel/attachments/20100627/cfb726ae/attachment.bin>


More information about the vlc-devel mailing list