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.<br><br>Regards,<br><br>Ronald Wright<br><br><div class="gmail_quote">
On Sun, Jun 27, 2010 at 1:58 AM, Ron Wright <span dir="ltr"><<a href="mailto:logiconcepts819@gmail.com">logiconcepts819@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin: 0pt 0pt 0pt 0.8ex; border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;">
Hello again,<br><br>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.<br>

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