/*
  Rakarrack Guitar FX

  Sustainer.C - Simple compressor/sustainer effect with easy interface, minimal controls
  Copyright (C) 2010 Ryan Billing
  Author: Ryan Billing

  This program is free software; you can redistribute it and/or modify
  it under the terms of version 3 of the GNU General Public License
  as published by the Free Software Foundation.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License (version 2) for more details.

  You should have received a copy of the GNU General Public License (version 2)
  along with this program; if not, write to the Free Software Foundation,
  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA

*/

#include "Sustainer.h"

#ifdef SHR3D_SFX_CORE_RAKARRACK

Sustainer::Sustainer()
{
  f32 tmp = 0.01f;  //10 ms decay time on peak detectors
  prls = 1.0f - (cSAMPLE_RATE / (cSAMPLE_RATE + tmp));

  tmp = 0.05f; //50 ms att/rel on compressor
  calpha = cSAMPLE_RATE / (cSAMPLE_RATE + tmp);
  cbeta = 1.0f - calpha;
  cthresh = 0.25f;
  cratio = 0.25f;

  timer = 0;
  hold = (i32)(SAMPLE_RATE * 0.0125);  //12.5ms
  cleanup();

  setpreset(0);
}

void Sustainer::cleanup()
{
  compeak = 0.0f;
  compenv = 0.0f;
  oldcompenv = 0.0f;
  cpthresh = cthresh; //dynamic threshold  
}

void Sustainer::processBlock(f32** inOutBlock, const i32 blockSize)
{
  for (i32 i = 0; i < blockSize; i++)    //apply compression to auxresampled
  {
    const f32 auxtempl = input * inOutBlock[0][i];
    const f32 auxtempr = input * inOutBlock[1][i];
    const f32 auxcombi = 0.5f * (auxtempl + auxtempr);

    if (fabs(auxcombi) > compeak)
    {
      compeak = fabs(auxcombi);   //First do peak detection on the signal
      timer = 0;
    }
    if (timer > hold)
    {
      compeak *= prls;
      timer--;
    }
    timer++;
    compenv = cbeta * oldcompenv + calpha * compeak;       //Next average into envelope follower
    oldcompenv = compenv;

    if (compenv > cpthresh)                                //if envelope of signal exceeds thresh, then compress
    {
      compg = cpthresh + cpthresh * (compenv - cpthresh) / compenv;
      cpthresh = cthresh + cratio * (compg - cpthresh);   //cpthresh changes dynamically
      tmpgain = compg / compenv;
    }
    else
    {
      tmpgain = 1.0f;
    }

    if (compenv < cpthresh)
      cpthresh = compenv;
    if (cpthresh < cthresh)
      cpthresh = cthresh;

    inOutBlock[0][i] = auxtempl * tmpgain * level;
    inOutBlock[1][i] = auxtempr * tmpgain * level;
  }
  //End compression
}

void Sustainer::setpreset(i32 npreset)
{
  const i32 PRESET_SIZE = 2;
  i32 presets[][PRESET_SIZE] = {
    //Moderate
    {79, 54},
    //Extreme
    {16, 127},
    //Mild
    {120, 15},
  };

  for (i32 n = 0; n < PRESET_SIZE; n++)
    changepar(n, presets[npreset][n]);
}

void Sustainer::changepar(i32 npar, i32 value)
{
  switch (npar)
  {
  case 0:
    Pvolume = value;
    level = dB2rap(-30.0f * (1.0f - ((f32)Pvolume / 127.0f)));
    return;
  case 1:
    Psustain = value;
    fsustain = (f32)Psustain / 127.0f;
    cratio = 1.25f - fsustain;
    input = dB2rap(42.0f * fsustain - 6.0f);
    cthresh = 0.25f + fsustain;
    return;
  }
  ASSERT(false);
}

i32 Sustainer::getpar(i32 npar)
{
  switch (npar)
  {
  case 0:
    return Pvolume;
  case 1:
    return Psustain;
  }
  ASSERT(false);
  return 0;
};

#endif // SHR3D_SFX_CORE_RAKARRACK
