Limiter with Auto Release via Peak-Detection

DSP, Plugin and Host development discussion.
Post Reply New Topic
RELATED
PRODUCTS

Post

Hi buddies,

first let me say "sorry" for my bad english, but I will do my best. ;-)

Ok, I try to develop a limitet with an auto-release mode. It should work like the Waves L2 or Izotope Maximizer.
The limiter algo is a very simple one with an attack and release envelope. It has also a look-ahead buffer (creates a little latency) for smooth attacks. The limiter itself works fine.

What do I have to do to create a auto-release funktion? I read about peak-detection and read that transients should create short release times and period waves long release times. So how could i detect that?

Here is the limiter-code. I think that maybe several of You know that algorithm.

Code: Select all

		const float in1 = sampleL;
		const float in2 = sampleR;
		
		const float rect1 = abs( in1 );
		const float rect2 = abs( in2 );

		float keyLink = rect1 > rect2 ? rect1 : rect2;

		if ( keyLink < thresh.current )
			keyLink = thresh.current;

	
		if ( (++peakTimer >= peakHold) || (keyLink > maxPeak) )
		{
			peakTimer = 0;	
			maxPeak = keyLink;
		}

		// attack/release, 
		if ( maxPeak > env )
			att.run( maxPeak, env );
		else
			rel.run( maxPeak, env );

		const float gR = thresh.current / env;

		const unsigned int delayIndex = ( cur - peakHold ) & mask;
		const float delay1 = bufL[ delayIndex ];
		const float delay2 = bufR[ delayIndex ];

		bufL[ cur ] = in1;
		bufR[ cur ] = in2;
		cur++;
		cur = cur & mask;

		// output gain
		outL = delay1 * gR;
		outR = delay2 * gR;

Thanks and Greetz
T.

Post

Have you tried using a peak follower?

Code: Select all

if (fabs(hpl) > fabs(hl))
{ hl = fabs(hpl); }
else
{ hl = (hl * b1); }
b1 is the coef of the filter, I used a derived hpf (hpl is the input).

Post

Yes i tried it and you can see the result in the image (a little simulation app i wrote in AS3. Btw. AS3 is a really nice language to visualize thinks). At the top there is the original input signal. At the bottom the absolute input with the peak detection curve.
Detection 1.png

If i transfer the detected signal to the release of the limiter, there are a lot of variations and there is also a problem with the gain of the input.
the peak detection has also high level if there is e.g. a loud sine bass. so this algo is nice for vu meters but i think this is not a solution for the auto release.

The Detection i need should be like the blackline here:
Detection 2.png
I think it is necessary to detect the transients maybe in frequency spektrum? i don´t know. Any ideas?
You do not have the required permissions to view the files attached to this post.

Post

Oh, now I see what you mean. And yes, an FFT might be "better" at detecting transients, and will fit into the usage of lookahead limiting as well. There's plenty to experiment with in transient detection.

Post

No other ideas here? would be nice if someone could give me a hint.

Post

Relatively infrequent short overs can be released relatively quick because the duration is not long enough for the ear to notice intermodulation distortion allowed by the short release.

As overs get longer, or more frequent, the ear can more easily notice intermodulation distortion caused by a short release.

My code reduces the release time, toward a minimum of maybe 10 or 20 ms, any time the envelope level is below threshold. IOW, the release time never gets below the minimum regardless how long the envelope level stays below the limiting threshold.

Any time that the envelope level is above threshold, the code gradually increases the release time, up to a maximum time constant of about a half second if I recall correctly. Maybe it is some other value, but I think the max is about 500 ms. That is "exponential decay" time constant, IOW it would take a half second for the envelope to decay -8.69 dB, and it decays toward the long-term average signal level, not toward zero, so it is effectively quite a bit longer than a half second at max, depending on the recent history of the envelope.

So if brief overs occur very rarely, then the release time stays around 10 or 20 ms. If brief overs occur more frequently, or if long overs occur, then the release time adapts longer to mask distortion.

If the limiter gets slammed with wall-to-wall many db's above threshold, it basically acts like a long-release auto level device. When the limiter is heavy-slammed for a whole song, it doesn't sound "real compressed" because the release time is relatively long, as in a tape recorder auto-level front-end. You mainly notice the heavy compression when the song ends and the final reverb tail keeps getting cranked up by the limiter recovering with a long release.

But it responds "instant" to peaks, so you don't notice oddities at the beginning of a song if you slam the limiter. It instantly reduces the level, and under a slammed condition the release time builds up quick enough to avoid noticeable distortion.

It was designed to be a two-control (input gain and output attenuation) limiter that won't ever sound bad, a no-brainer limiter with minimal pumping or distortion. But it is not the ideal tool to make a signal "as hot as you can ever get". It limits the dynamic range over the range between no overs to occasional overs. But if you slam it so that there are "continual overs" it is not squashed ruler-flat because the automatic long release preserves a fair amount of short-term dynamics in the music. But that's OK, as I don't think squashed ruler-flat is such a great thing. :)

Post

If a value of a sample is bigger than thresh, than i increase the release time else i decrease the time. OK, i tried it but got a problem. For example i have a input-signal like a finished track and i go slowly down with the thresh, there will be the point where the first samples reaches the thresh value. and if i decrease the thresh more, than i have more samples above the thresh. and if i overact, i have so many samples above the thresh, that the releasetime increases more and more. the behaviour of the release time with a high thresh value is different to the bvehaviour with a low thresh value. that is wrong, i thing.

Post

I make no claim that the variable release method which I described is the only way or the best way to solve the problem. You wanted ideas so I reported my experience.

I use limits as to the shortest and longest release allowed, and use multiple stages of envelope filtering in the attempt to minimize audio frequency ripple in the envelope, before applying the final variable release.

Also a good lookahead limiter should smoothly (but fairly quickly) raise the envelope leading to the "max value of a transient", rather than simply offsetting the envelope in time relative to the gain element. So that the gain reduction on a transient is not overly sudden.

Audio frequency ripple in the envelope causes distortion. Sometimes you might desire distortion as a special effect, but otherwise audible distortion would be considered a defect in a limiter. If you want audible distortion, why not just hard clip the signal? :)

Inappropriate envelope times cause too much pumping and breathing in the audio with heavy limiting. Sometimes you desire pumping as a special effect, but otherwise can be considered a defect in the limiter.

Maybe there is a better way to modulate the release time. Or possibly you would be happier with a shorter max release time.

Yes with long release time, when the limiter is slammed, it acts more like an auto level effect. The slammed dynamic range is fairly narrow with long release, but it allows some amount of short term dynamics to leak thru. But it never allows the max output to exceed the threshold. Some other approach, or a shorter max release time, would squash the output even more flatter. :)

I visualize pumping as "slow intermodulation distortion". For instance if you have sixteenth note hihat and quarter note kick drum, and you slam the limiter with short release time. The hats pump quieter every time the kick hits. Unless you want that sound as a special effect, it seems a defect in the limiter and annoying as hell.

So to avoid pumping and distortion when you slam hell out of the limiter, one solution is a fairly long release time. You can squash flatter by using a shorter release time, at the expense of increased distortion and pumping.

Thats what I dont like about non auto release limiters-- It takes too long tweaking the release trying to judge "acceptable distortion" by ear. Maybe one day the distortion seemed acceptable when I render a file, but then the next day it sounds like an unacceptably big distortion. I think it is better to err on the side of caution and use a long enough release time to minimize excessive short term pumping and transient distortion.

Post

[Duplicate]

Post

themerx, are you aware that your current implementation works in the linear domain rather than decibel (log) based? This means that your timing will be gain reduction dependent (in a very non-musical, i.e. obtrusive manner). Try to run timing filters in the log domain to make them act properly with music signals.

Also, forget this crude approach to peak-hold, it's wrong. Imagine what happens if your signal (over threshold) looks like this:

|
| |
| | |
| | | |
| | | | |
| | | | | |
| | | | | | |
| | | | | | | |
------------------------------------------

This is a very common pattern for decaying music signals (maybe THE most common). Be prepared to handle such signals with grace!
Fabien from Tokyo Dawn Records

Check out my audio processors over at the Tokyo Dawn Labs!

Post

Try something like this. I didn't test it. You will need to add the coef for log2db & db2log.
Subtract your ceiling in dB and add the sample rate(srate)
Good luck!!

Code: Select all

  if ( (++peakTimer >= peakHold) || (keyLink > maxPeak) )
      {
         peakTimer = 0;   
         maxPeak = keyLink;
      }

/* ADD CODE */
   maxPeak = 2.08136898 * log(keylink) * log2db - ceilingdb;

      // attack/release, 
      if ( maxPeak > env )
         att.run( maxPeak, env );
      else
         rel.run( maxPeak, env );

         maxPeak = max(0,env);
   
        if (maxPeak > maxover)
{
        maxover = maxPeak;
        reltime = maxPeak /125;
        relcoef = exp(-1/(reltime * srate));
}
        envmax = maxover + relcoef * (envmax - maxover);
        maxover = envmax;

        gR = exp(overdb * db2log);

     // const float gR = thresh.current / env;

      const unsigned int delayIndex = ( cur - peakHold ) & mask;
      const float delay1 = bufL[ delayIndex ];
      const float delay2 = bufR[ delayIndex ];
SOR8, Free AAX/RTAS/VST/AU
Classic Compressor plugin.
Based on acclaimed 8X compressor.
http://www.cocellproductions.com/Pensad ... lugin.html

Post Reply

Return to “DSP and Plugin Development”