[music-dsp] 2nd order biquad for envelope detector?

Alex Raider flashinc at mail.ru
Fri May 21 04:17:07 EDT 2004


Hi.

> I am exploring the possibility of using a 2nd order biquad for an envelope
> detector. After some testing, comparing results with poplular products, I'm
> just not getting the attack shape that I want from 1-pole low-pass filters.
> I've tried all sorts of stuff in the coefficient calculation, but nothing
> seems to work. So I've been toying with the idea of using a 2nd order biquad
> for the job, but haven't had time to implement it yet.
> Is this ever done in dynamics processors? I seem to remember a thread from
> the archives (which, of course, I can't find now) that mentioned an AES
> paper on using higher order filters (with low Q values, presumably) for
> smoother envelopes. Anyone remember this?
> Also, does anyone have any advice/opinion on the subject?
> == chunk


You on a wrong way, because you not need frequency domain filter.
Increasing filter order just gives you stronger rolloff (better
performance in frequency domain).   "Biquad" just the name for
technique for analog IIR filter design.

To do envelope extracting, you need time domain processing.
Averaging filter is most suitable for that.
averager is perfect "smoother" - thats we looking for.
And this kind of processing is opposite to frequency domain processing.

The first order lowpass does perfect job.
You can, however try to averaging N points of input signal:

       N/2
1/N * SUMM( x[n] )
       -N/2



For those who looking for compressor...

Below is a hardknee compressor prototype,
with windowing RMS look-ahead envelope calculation,
adjustable attack/decay times.

DSP algo modelled in matlab.

RMS is a true way to estimate _musical_ signal energy,
our ears behaves in a same way.

I ripped off some pre-processing and post-processing stuff,
but envelope calculation and hard knee`d compressor
is correct and should work.


void compress
    (
        float*  wav_in,     // signal
        int     n,          // N samples
        double  threshold,  // threshold (percents)
        double  slope,      // slope angle (percents)
        int     sr,         // sample rate (smp/sec)
        double  tla,        // lookahead  (ms)
        double  twnd,       // window time (ms)
        double  tatt,       // attack time  (ms)
        double  trel        // release time (ms)
    )
{
    typedef float   stereodata[2];
    stereodata*     wav = (stereodata*) wav_in; // our stereo signal
    threshold *= 0.01;          // threshold to unity (0...1)
    slope *= 0.01;              // slope to unity
    tla *= 1e-3;                // lookahead time to seconds
    twnd *= 1e-3;               // window time to seconds
    tatt *= 1e-3;               // attack time to seconds
    trel *= 1e-3;               // release time to seconds

    // attack and release "per sample decay"
    double  att = (tatt == 0.0) ? (0.0) : exp (-1.0 / (sr * tatt));
    double  rel = (trel == 0.0) ? (0.0) : exp (-1.0 / (sr * trel));

    // envelope
    double  env = 0.0;

    // sample offset to lookahead wnd start
    int     lhsmp = (int) (sr * tla);

    // samples count in lookahead window
    int     nrms = (int) (sr * twnd);

    // for each sample...
    for (int i = 0; i < n; ++i)
    {
        // now compute RMS
        double  summ = 0;

        // for each sample in window
        for (int j = 0; j < nrms; ++j)
        {
            int     lki = i + j + lhsmp;
            double  smp;

            // if we in bounds of signal?
            // if so, convert to mono
            if (lki < n)
                smp = 0.5 * wav[lki][0] + 0.5 * wav[lki][1];
            else
                smp = 0.0;      // if we out of bounds we just get zero in smp

            summ += smp * smp;  // square em..
        }

        double  rms = sqrt (summ / nrms);   // root-mean-square

        // dynamic selection: attack or release?
        double  theta = rms > env ? att : rel;

        // smoothing with capacitor, envelope extraction...
        // here be aware of pIV denormal numbers glitch
        env = (1.0 - theta) * rms + theta * env;

        // the very easy hard knee 1:N compressor
        double  gain = 1.0;
        if (env > threshold)
            gain = gain - (env - threshold) * slope;

        // result - two hard kneed compressed channels...
        float  leftchannel = wav[i][0] * gain;
        float  rightchannel = wav[i][1] * gain;
    }
}



to making all it work,
try for first time:

threshold = 50%
slope = 50%
RMS window width = 1 ms
lookahead = 3 ms
attack time = 0.1 ms
release time = 300 ms

This code can be significantly improved in speed by
changing RMS calculation loop to 'running summ'
(keeping the summ in 'window' -
adding next newest sample and subtracting oldest on each step)



Alex Raider / Flash inc.




More information about the music-dsp mailing list