/*
  rakarrack - a guitar effects software

  Shifter.C  -  Shifter
  Copyright (C) 2008-2010 Josep Andreu
  Author: Josep Andreu

  Using Stephan M. Bernsee smbPtichShifter engine.

 This program is free software; you can redistribute it and/or modify
 it under the terms of version 2 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
 (version2)  along with this program; if not, write to the Free Software
 Foundation,
 Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA

*/


#include "Shifter.h"

#ifdef SHR3D_SFX_CORE_RAKARRACK

#ifdef SHR3D_SFX_CORE_RAKARRACK_LIBSAMPLERATE

#include <string.h>

Shifter::Shifter(i32 Quality, i32 DS, i32 uq, i32 dq)
  : U_Resample(dq)
  , D_Resample(uq)
  , hq(Quality)
{
  adjust(DS, blockSize());

  outi = (f32*)malloc(sizeof(f32) * nPERIOD);
  outo = (f32*)malloc(sizeof(f32) * nPERIOD);

  PS = new PitchShifter(window, hq, nfSAMPLE_RATE);
  PS->ratio = 1.0f;

  state = IDLE;
  env = 0.0f;
  tune = 0.0f;
  Pupdown = 0;
  Pinterval = 0;
  setpreset(0);
  cleanup();
}

void Shifter::cleanup()
{
  state = IDLE;
  memset(outi, 0, sizeof(f32) * nPERIOD);
  memset(outo, 0, sizeof(f32) * nPERIOD);
}

void Shifter::adjust(i32 DS, const i32 blockSize)
{
  DS_state = DS;

  switch (DS)
  {
  case 0:
    nPERIOD = blockSize;
    nSAMPLE_RATE = SAMPLE_RATE;
    nfSAMPLE_RATE = fSAMPLE_RATE;
    window = 2048;
    break;
  case 1:
    nPERIOD = lrintf(f32(blockSize) * 96000.0f / fSAMPLE_RATE);
    nSAMPLE_RATE = 96000;
    nfSAMPLE_RATE = 96000.0f;
    window = 2048;
    break;
  case 2:
    nPERIOD = lrintf(f32(blockSize) * 48000.0f / fSAMPLE_RATE);
    nSAMPLE_RATE = 48000;
    nfSAMPLE_RATE = 48000.0f;
    window = 2048;
    break;
  case 3:
    nPERIOD = lrintf(f32(blockSize) * 44100.0f / fSAMPLE_RATE);
    nSAMPLE_RATE = 44100;
    nfSAMPLE_RATE = 44100.0f;
    window = 2048;
    break;
  case 4:
    nPERIOD = lrintf(f32(blockSize) * 32000.0f / fSAMPLE_RATE);
    nSAMPLE_RATE = 32000;
    nfSAMPLE_RATE = 32000.0f;
    window = 2048;
    break;
  case 5:
    nPERIOD = lrintf(f32(blockSize) * 22050.0f / fSAMPLE_RATE);
    nSAMPLE_RATE = 22050;
    nfSAMPLE_RATE = 22050.0f;
    window = 1024;
    break;
  case 6:
    nPERIOD = lrintf(f32(blockSize) * 16000.0f / fSAMPLE_RATE);
    nSAMPLE_RATE = 16000;
    nfSAMPLE_RATE = 16000.0f;
    window = 1024;
    break;
  case 7:
    nPERIOD = lrintf(f32(blockSize) * 12000.0f / fSAMPLE_RATE);
    nSAMPLE_RATE = 12000;
    nfSAMPLE_RATE = 12000.0f;
    window = 512;
    break;
  case 8:
    nPERIOD = lrintf(f32(blockSize) * 8000.0f / fSAMPLE_RATE);
    nSAMPLE_RATE = 8000;
    nfSAMPLE_RATE = 8000.0f;
    window = 512;
    break;
  case 9:
    nPERIOD = lrintf(f32(blockSize) * 4000.0f / fSAMPLE_RATE);
    nSAMPLE_RATE = 4000;
    nfSAMPLE_RATE = 4000.0f;
    window = 256;
    break;
  }
  u_up = (f64)nPERIOD / (f64)blockSize;
  u_down = (f64)blockSize / (f64)nPERIOD;
}

void Shifter::processBlock(f32** inBlock, f32** outBlock, const i32 blockSize)
{
  if (DS_state != 0)
  {
    memcpy(templ, inBlock[0], sizeof(f32) * blockSize);
    memcpy(tempr, inBlock[1], sizeof(f32) * blockSize);
    f32* temp[2] = { templ, tempr };
    U_Resample.processBlock(temp, inBlock, blockSize, u_up);
  }

  for (i32 i = 0; i < nPERIOD; i++)
  {
    if (Pmode == 0)
    {
      const f32 sum = fabsf(inBlock[0][i]) + fabsf(inBlock[1][i]);
      if (sum > env) env = sum;  else env = sum * ENV_TR + env * (1.0f - ENV_TR);

      if (env <= tz_level)
      {
        state = IDLE;
        tune = 0.0;
      }

      if ((state == IDLE) && (env >= t_level)) state = UP;

      if (state == UP)
      {
        tune += a_rate;
        if (tune >= 1.0f) state = WAIT;
      }

      if (state == WAIT)
      {
        if (env < td_level)
          state = DOWN;
      }

      if (state == DOWN)
      {
        tune -= d_rate;
        if (tune <= 0.0)
        {
          tune = 0.0;
          state = IDLE;
        }
      }
    }
    outi[i] = (inBlock[0][i] + inBlock[1][i]) * 0.5f;
    if (outi[i] > 1.0)
      outi[i] = 1.0f;
    if (outi[i] < -1.0)
      outi[i] = -1.0f;
  }

  f32 use;
  if (Pmode == 1)
    use = whammy;
  else
    use = tune;

  if ((Pmode == 0) && (Pinterval == 0))
    use = tune * whammy;

  if (Pupdown)
    PS->ratio = 1.0f - (1.0f - range) * use;
  else
    PS->ratio = 1.0f + ((range - 1.0f) * use);

  PS->smbPitchShift(PS->ratio, nPERIOD, window, hq, nfSAMPLE_RATE, outi, outo);

  for (i32 i = 0; i < nPERIOD; i++)
  {
    templ[i] = outo[i] * gain * panning;
    tempr[i] = outo[i] * gain * (1.0f - panning);
  }

  if (DS_state != 0)
  {
    f32* temp[2] = { templ, tempr };
    D_Resample.processBlock(temp, outBlock, nPERIOD, u_down);

  }
  else
  {
    memcpy(outBlock[0], templ, sizeof(f32) * blockSize);
    memcpy(outBlock[1], tempr, sizeof(f32) * blockSize);
  }
}

void Shifter::setvolume(i32 value)
{
  this->Pvolume = value;
  outvolume = (f32)Pvolume / 127.0f;
}

void Shifter::setpanning(i32 value)
{
  this->Ppan = value;
  panning = (f32)Ppan / 127.0f;
}

void Shifter::setgain(i32 value)
{
  this->Pgain = value;
  gain = (f32)Pgain / 127.0f;
  gain *= 2.0f;
}

void Shifter::setinterval(i32 value)
{
  interval = (f32)value;
  if ((Pmode == 0) && (Pinterval == 0))
    interval = 1.0f;
  if (Pupdown)
    interval *= -1.0f;
  range = powf(2.0f, interval / 12.0f);
}

void Shifter::setpreset(i32 npreset)
{
  const i32 PRESET_SIZE = 10;
  i32 presets[][PRESET_SIZE] = {
    //Fast
    {0, 64, 64, 200, 200, -20, 2, 0, 0, 0},
    //Slowup
    {0, 64, 64, 900, 200, -20, 2, 0, 0, 0},
    //Slowdown
    {0, 64, 64, 900, 200, -20, 3, 1, 0, 0},
    //Chorus
    {64, 64, 64, 0, 0, -20, 1, 0, 1, 22},
    //Trig Chorus
    {64, 64, 64, 250, 100, -10, 0, 0, 0, 25}
  };

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

void Shifter::changepar(i32 npar, i32 value)
{
  switch (npar)
  {
  case 0:
    setvolume(value);
    return;
  case 1:
    setpanning(value);
    return;
  case 2:
    setgain(value);
    return;
  case 3:
    Pattack = value;
    a_rate = 1000.0f / ((f32)Pattack * nfSAMPLE_RATE);
    return;
  case 4:
    Pdecay = value;
    d_rate = 1000.0f / ((f32)Pdecay * nfSAMPLE_RATE);
    return;
  case 5:
    Pthreshold = value;
    t_level = dB2rap((f32)Pthreshold);
    td_level = t_level * .75f;
    tz_level = t_level * .5f;
    return;
  case 6:
    Pinterval = value;
    setinterval(Pinterval);
    return;
  case 7:
    Pupdown = value;
    setinterval(Pinterval);
    return;
  case 8:
    Pmode = value;
    return;
  case 9:
    Pwhammy = value;
    whammy = (f32)value / 127.0f;
    return;
  }
  ASSERT(false);
}

i32 Shifter::getpar(i32 npar)
{
  switch (npar)
  {
  case 0:
    return Pvolume;
  case 1:
    return Ppan;
  case 2:
    return Pgain;
  case 3:
    return Pattack;
  case 4:
    return Pdecay;
  case 5:
    return Pthreshold;
  case 6:
    return Pinterval;
  case 7:
    return Pupdown;
  case 8:
    return Pmode;
  case 9:
    return Pwhammy;
  }
  ASSERT(false);
  return 0;
}

#endif // SHR3D_SFX_CORE_RAKARRACK_LIBSAMPLERATE

#endif // SHR3D_SFX_CORE_RAKARRACK
