Peak follower with independent Attack, Release and Level

Hello there,

I’m currently working on a compressor and I have some troubles getting the peak follower working properly. I followed this paper Digital Dynamic Range Compressor Design from Giannoulis, Massberg and Reiss and implemented the Smoothed Branching Peak Detector (eq 16, same as juce::BallisticsFilter) and the Smoothed Decoupled Peak Detector (eq 17).

The issue I’m facing is that I can’t get independent control over attack, release and keep a level constant when changing those when the release time get smaller or in the same range as the attack time.

With the Smoothed Decoupled Peak Detector, the attack envelope is always impressed upon the release envelope (as indicated in the paper), i.e. the actual measurable release time is the sum of the attack and release times.

With the Smoothed Branching Peak Detector, when the release time gets shorter or in the same range as the attack time, the output level gets strongly affected when changing either the attack or release. In the paper, they mention that the attack and release times are often added to set the actual release time. The authors say

it guarantees that the release time can never be shorter than the attack time.

However I’d like to have the possibility to set a release time shorter than the attack time and have proper trajectories and level. I tried several commercial compressor and they manage to do it properly, so it must be possible :slight_smile: .

Does anyone have any clues on what I should try to achieve the intended behavior?

Thanks for your help!

this is a simple compressor with independent attack and decay.


float cmp_attackFactor=0.01:
float cmp_decayFactor=0.0001:
float cmp_env=0;
float cmp_treshold= .5;
float cmp_reduction=.5;    // 0..1 => fractional reduction over the treshold level
float compress(float sample)
{
   float tryEnv = fabs(sample);
   
   if (tryEnv > cmp_env)  
   {  // attack
      cmp_env +=   (tryEnv-cmp_env) * cmp_attackFactor;
   }else{
     //decay
     cmp_env +=   (tryEnv-cmp_env) * cmp_decayFactor;
   }

   if( cmp_env >cmp_ treshold)
   {
       //   the .00001 is just a trcik to avoiud division by zero
       float factor = cmp_treshold / (real_env+.00001); // you can smooth the factor also if needed
       factor = 1. + cmp_reduction * (factor-1.);
       return(factor * sample);
  )else{
       return(sample);
  }

}





1 Like

if you want to smooth the compressionfactor, you use a global cmp_factor with a surffactor

float  cmp_factor=1;
float cmp_factorSmoothing=0.01;

// rest of code here..

if( cmp_env >cmp_ treshold)
   {
       //   the .00001 is just a trcik to avoiud division by zero
       float factor = cmp_treshold / (real_env+.00001); // you can smooth the factor also if needed
       factor = 1. + cmp_reduction * (factor-1.);
       cmp_factor +=  (factor - cmp_factor) * cmp_factorSmoothing
  )else{
        cmp_factor +=  (1. - cmp_factor) * cmp_factorSmoothing
  }

  return cmp_factor * sample;

Yes, your code corresponds to the Smoothed Branching Peak Detector I was mentioning. I have implemented it and the issue is that the output level moves a lot when changing the attack or release, as if I was moving the threshold as well.

if you move the treshold, the output volume will change. that is when you need “makeup”
The max makeup you need is 1./cmp_treshold
this will work fine for low to medium hard compression

cmp_makeup = 1./cmp_treshold; // treshold can not be 0!
return cmp_factor * cmp_makeup * sample;

My issue is that I have the output volume changing with the attack or release changing (when the release is shorter or in the same range as the attack). I don’t move the threshold when this happens.

Of course if I moved the threshold it would be normal to observe a volume change.

this would not happen in the code above. the att/decay logic does not influence the volume here.
Such things can happen when you reset the cmp_env or cmp_factorSmooth when dialing
Or when separate attack and decay envs are created in parallel: my example uses a signle “stitched” env