Compressor Code

2009-08-31
2013-05-28
  • Transmogrifox
    Transmogrifox
    2009-08-31

    I have not been entirely happy with the way the compressor behaves...just seems something "not right" happens when the signal gets above the threshold. 

    I took a look through Compressor.C (and Compressor.h, global.h) and now see something that doesn't settle well in my bones regarding the code. I am an electrical engineer, so I know mathematically should be happening, but  I'm not an experienced programmer, so somebody with knowledge please explain  if I am reading this wrong.  See in-line comments of mine:

      for (i = 0; i < PERIOD; i++)
        {

          float delta = fmaxf (fabsf (efxoutl[i]), fabsf (efxoutr[i])) - volume;

          if (delta > 0.0)
        volume += att * delta;
          else
        volume += rel * delta;

          if (volume <= knee_min)
        {
          gain = output;
             // This settles well with me,  "output" appears to be the un-compressed gain.

        }
          else if (volume < knee_max)
        {
    /*Here's something that does not look right.  We should be multiplying "output" by a number less than 1 that depends on thresh, ratio, and volume.  The only way this could seem ok is if compress() returns a value less than 0, so that gain is decreased.  Even that would take more complexity than what compress() seems to be doing.  Playing around with FreeMat, this type of algorithm returned undesirable results.*/
          gain =
            output +
            ((volume - knee_min) * compress () / (knee_max - knee_min));
        }
          else
        {
            //Again, this does something strange.  We should be multiplying compress() * output.
          gain = compress () + output;
        }

    //Apart from the apparent error, this block of code is very elegant.
          float gain_t = .4 * gain + .6 * gain_old;

          efxoutl[i] *= gain_t;
          efxoutr[i] *= gain_t;
          gain_old = gain;

        }

    }

    float
    Compressor::compress ()
    {

      volume_db = rap2dB (volume);

    /*The more the volume deviates above the thresh, the larger the magnitude of the output.  This behaves more like an expander than a compressor, unless the sign of the return value is negative.  Some test values with FreeMat seem to support my fear. */
      return (dB2rap ((volume_db - tthreshold) * ratio + tthreshold) / volume);

    }

    I will post a second reply with my proposed solution.  I may also try it on my Ubuntu Studio machine and recompile when I have time to see if it yields the expected behavior.

    Has anybody ever plugged in a modulated signal source to observe attack, release, and compression behavior?  I may also put something together on my bread board.  My fears in the code will either be confirmed or negated by watching its behavior on a test signal.

     
    • Transmogrifox
      Transmogrifox
      2009-08-31

      Ok, see below snippet of code for a start to a solution, just for the sake of consideration.  I will try this and see whether this produces the desired result:

      float eratio;
          //this needs to be defined somewhere.  I'm guessing this should be within the Compressor object definition.

      void
      Compressor::out (float *efxoutl, float *efxoutr)
      {

        int i;

        for (i = 0; i < PERIOD; i++)
          {

            float delta = fmaxf (fabsf (efxoutl[i]), fabsf (efxoutr[i])) - volume;

            if (delta > 0.0)
          volume += att * delta;
            else
          volume += rel * delta;

            if (volume <= knee_min)
          {
            gain = output;

          }
            else if (volume < knee_max)
          {
          //We want a dynamic ratio here that depends on volume.  As can be seen, ratio starts
          //at something negligibly larger than 1 once volume exceeds knee_min, and increases toward selected
          // ratio by the time it has reached knee_max.
          eratio = 1+ ((ratio-1)*(volume - knee_min) )/(knee_max - knee_min);
              //Now the return value of compress() is doubly dependent on volume.  This should
              //create a gentle transition from a ratio of 1 to the selected ratio within the
              // bounds of knee_min and knee_max.
            gain =  output * compress ();

          }
            else
          {
            eratio = ratio;
            gain = output * compress ();
          }

            float gain_t = .4 * gain + .6 * gain_old;

            efxoutl[i] *= gain_t;
            efxoutr[i] *= gain_t;
            gain_old = gain;

          }

      }

      float
      Compressor::compress ()
      {

        volume_db = rap2dB (volume);

        return (volume/(dB2rap ((volume_db - tthreshold) * ratio + tthreshold) ));
          //now the gain is reduced by an amount that reduces volume toward tthreshold.

      }

       
      • Josep Andreu
        Josep Andreu
        2009-09-06

        Hi, sorry for the reply delay ..... the code is very interesting ...  please send me the files to my email holborn@telefonica.net.

        The history of the rakarrack compressor code is really long and any contribution is welcome

        Josep

         
    • Transmogrifox
      Transmogrifox
      2009-08-31

      Oops, a subtle oversight, but very important:

      float
      Compressor::compress ()
      {

      volume_db = rap2dB (volume);

      return (volume/(dB2rap ((volume_db - tthreshold) * eratio + tthreshold) ));
      //now the gain is reduced by an amount that reduces volume toward tthreshold.
      // Note the previous instance of "ratio" has been replaced with "eratio"

      }

       
    • Transmogrifox
      Transmogrifox
      2009-09-01

      Yet another "booboo".  Here's the correction of the above:
      float 
      Compressor::compress () 

      volume_db = rap2dB (volume); 

      return ( (dB2rap ((volume_db - tthreshold) / eratio + tthreshold) )/volume ); 
      //now the gain is reduced by an amount that reduces volume toward tthreshold. 
      // Note the previous instance of "ratio" has been replaced with "eratio"
      // We should be dividing the deviance of input by compression ratio.  We then
      // divide by the volume to come up with a factor to reduce signal power "output"
      // to the appropriate level to correspond to the given compression ratio.
      // This whole scene now appears to behave nicely in a FreeMat routine.

      }

       
    • Transmogrifox
      Transmogrifox
      2009-09-02

      I made the modifications to Compress.C and Compress.h as indicated from my ponderings above and recompiled rakarrack 0.3.0 on my Debian Lenny PowerPC (ibook G4).  The behavior is what I desire.  I used it with Hydrogen drum kit since I didn't have a guitar input and played with the symbols.  When the compressor release time was long, you could hear the fading symbol  sustained like you would expect to hear when processing symbols with a rackmount compressor at some venue.  Adjusting threshold and ratio behaved as I would expect a compressor to behave.

      I will try to figure out how to submit the patch and see if y'all will accept it.  Thank you guys for making such an awesome DSP program for Guitar.  I love Rakarrack.

       
    • Josep Andreu
      Josep Andreu
      2009-09-06

      I will change the code in the CVS for sure, please send me your name email, country city .... I like to put in the AUTHORS file all the contributions.

      Josep

       
    • Transmogrifox
      Transmogrifox
      2009-09-07

      I have sent the goods to your email address provided above.  Please let me know if you don't get that in your inbox, and I'll gzip it and try again.  I didn't zip because they're pretty small files.

      Thanks

       
  • Transmogrifox
    Transmogrifox
    2009-09-17

    I have been doing more math, and FreeMat (Matlab) modeling on my algorithm.  My treatment of the knee is still incorrect.  Fortunately the result is not bad, so I encourage any readers of this post to try it out with the CVS version.

    I will be making mods to cause this to behave more like an &quot;academically correct&quot; compressor knee.  I'm thinking the change will result in only subtle improvement, but some musicians are sensitive to subtle nuances.

    I noticed the artscompressor was morphed into the Beast project, and now goes by bse-artscompressor.  The code in that project did away with the knee, and the algorithm is identical to what Josep committed to CVS recently.

    Josep, thanks again for committing the patch to CVS.  I have done some testing with a square-wave-modulated sine wave test signal while recording the output in audacity.  The classic compressor attack &amp; release shapes are there, so this knee issue is quite subtle.

    I, for one, am becoming more excited about Rakarrack because I have not found another guitar FX processor for linux that is as well developed and as well suited to the job of mulit-FX processing.  Guitarix, for example, is focused on one thing.  Ayemux looks awesome, but it looks like it was a Google summer of Code project that pretty much died at the end of summer 2008, and doesn't seem to have met with any ambitious developers since (Ayemux is basically a fancy GUI for CAPS and LADSPA plugins).  I'm guessing it's quite alpha.  Therefore, my opinion is Rakarrack currently has the most promise of becoming a studio distribution standard for real-time guitar processing.