/*
  ZynAddSubFX - a software synthesizer

  Echoverse.C - Echo effect
  Copyright (C) 2002-2005 Nasca Octavian Paul
  Author: Nasca Octavian Paul

  Modified for rakarrack by Josep Andreu
  Reverse Echo effect by Transmogrifox

  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 (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 "Echoverse.h"

#ifdef SHR3D_SFX_CORE_RAKARRACK

Echoverse::Echoverse()
{
  //default values
  Srate_Attack_Coeff = 1.0f / (fSAMPLE_RATE * ATTACK);
  maxx_delay = 1 + SAMPLE_RATE * MAX_DELAY;

  ldelay = new f32[maxx_delay];
  rdelay = new f32[maxx_delay];

  setpreset(0);
  cleanup();
}

void Echoverse::cleanup()
{
  for (i32 i = 0; i < maxx_delay; i++)
    ldelay[i] = 0.0;
  for (i32 i = 0; i < maxx_delay; i++)
    rdelay[i] = 0.0;
  oldl = 0.0;
  oldr = 0.0;
}

void Echoverse::initdelays()
{
  kl = 0;
  kr = 0;

  if (Plrdelay > 0)
  {
    rkl = lrdelay;
    rkr = lrdelay / 2;
  }
  else
  {
    rkr = lrdelay;
    rkl = lrdelay / 2;
  }
  if (rkl > delay)
    rkl = delay;
  if (rkr < 1)
    rkr = 1;
  if (rkr > delay)
    rkr = delay;
  if (rkl < 1)
    rkl = 1;

  rvkl = delay;
  rvkr = delay;
  Srate_Attack_Coeff = 15.0f * cSAMPLE_RATE;   // Set swell time to 66ms 

  for (i32 i = delay; i < maxx_delay; ++i)
  {
    ldelay[i] = 0.0f;
    rdelay[i] = 0.0f;
  }

  oldl = 0.0;
  oldr = 0.0;
}

void Echoverse::processBlock(const f32* const* inBlock, f32** outBlock, const i32 blockSize)
{
  for (i32 i = 0; i < blockSize; i++)
  {
    //LowPass Filter
    const f32 ldl = lfeedback * hidamp + oldl * (1.0f - hidamp);
    const f32 rdl = rfeedback * hidamp + oldr * (1.0f - hidamp);
    oldl = ldl + DENORMAL_GUARD;
    oldr = rdl + DENORMAL_GUARD;

    ldelay[kl] = ldl + inBlock[0][i];
    rdelay[kr] = ldl + inBlock[1][i];

    if (++kl > delay)
      kl = 0;
    if (++kr > delay)
      kr = 0;
    rvkl = delay - kl;
    rvkr = delay - kr;

    if (++rkl > delay)
      rkl = 0;
    if (++rkr > delay)
      rkr = 0;

    if (reverse > 0.0f)
    {
      f32 lswell = ((f32)(kl - rvkl)) * Srate_Attack_Coeff;
      lswell = 1.0f - 1.0f / (lswell * lswell + 1.0f);
      outBlock[0][i] = reverse * (ldelay[rvkl] * lswell) + (ldelay[kl] * (1.0f - reverse));

      f32 rswell = ((f32)(kr - rvkr)) * Srate_Attack_Coeff;
      rswell = 1.0f - 1.0f / (rswell * rswell + 1.0f);
      outBlock[1][i] = reverse * (rdelay[rvkr] * rswell) + (rdelay[kr] * (1.0f - reverse));  //Volume ducking near zero crossing.
    }
    else
    {
      outBlock[0][i] = ldelay[kl];
      outBlock[1][i] = rdelay[kr];
    }

    lfeedback = lpanning * fb * outBlock[0][i];
    rfeedback = rpanning * fb * outBlock[1][i];

    if (Pes)
    {
      outBlock[0][i] *= cosf(lrcross);
      outBlock[1][i] *= sinf(lrcross);

      const f32 avg = (outBlock[0][i] + outBlock[1][i]) * 0.5f;
      const f32 ldiff = outBlock[0][i] - avg;
      const f32 rdiff = outBlock[1][i] - avg;

      f32 tmp = avg + ldiff * pes;
      outBlock[0][i] = 0.5f * tmp;

      tmp = avg + rdiff * pes;
      outBlock[1][i] = 0.5f * tmp;
    }
    outBlock[0][i] = (outBlock[0][i] + pingpong * rdelay[rkl]) * lpanning;
    outBlock[1][i] = (outBlock[1][i] + pingpong * ldelay[rkr]) * rpanning;
  }
}

void Echoverse::setvolume(i32 Pvolume_)
{
  Pvolume = Pvolume_;
  outvolume = (f32)Pvolume_ / 127.0f;
}

void Echoverse::setpanning(i32 Ppanning_)
{
  Ppanning = Ppanning_;
  lpanning = ((f32)Ppanning_) / 64.0f;
  rpanning = 2.0f - lpanning;
  lpanning = 10.0f * powf(lpanning, 4);
  rpanning = 10.0f * powf(rpanning, 4);
  lpanning = 1.0f - 1.0f / (lpanning + 1.0f);
  rpanning = 1.0f - 1.0f / (rpanning + 1.0f);
  lpanning *= 1.1f;
  rpanning *= 1.1f;
}

void Echoverse::setreverse(i32 Preverse_)
{
  Preverse = Preverse_;
  reverse = (f32)Preverse_ / 127.0f;
}

void Echoverse::setdelay(i32 Pdelay_)
{
  Pdelay = Pdelay_;
  fdelay = 60.0f / ((f32)Pdelay_);
  if (fdelay < 0.01f) fdelay = 0.01f;
  if (fdelay > (f32) MAX_DELAY) fdelay = (f32)MAX_DELAY;  //Constrains 10ms ... MAX_DELAY
  delay = 1 + lrintf(subdiv * fdelay * fSAMPLE_RATE);
  initdelays();
}

void Echoverse::setlrdelay(i32 Plrdelay_)
{
  Plrdelay = Plrdelay_;
  f32 tmp =
    ((f32)delay) * fabs(((f32)Plrdelay_ - 64.0f) / 65.0f);
  lrdelay = lrintf(tmp);

  tmp = fabs(((f32)Plrdelay_ - 64.0f) / 32.0f);
  pingpong = 1.0f - 1.0f / (5.0f * tmp * tmp + 1.0f);
  pingpong *= 1.2f;
  initdelays();
}

void Echoverse::setlrcross(i32 Plrcross_)
{
  Plrcross = Plrcross_;
  lrcross = D_PI * (f32)Plrcross_ / 128.0f;
}

void Echoverse::setfb(i32 Pfb_)
{
  Pfb = Pfb_;
  fb = (f32)Pfb_ / 128.0f;
}

void Echoverse::sethidamp(i32 Phidamp_)
{
  Phidamp = Phidamp_;
  hidamp = expf(-D_PI * 500.0f * ((f32)Phidamp_) / fSAMPLE_RATE);
}

void Echoverse::setpreset(i32 npreset)
{
  const i32 PRESET_SIZE = 10;
  i32 presets[][PRESET_SIZE] = {
    //Echo 1
    {64, 64, 90, 64, 64, 64, 64, 0, 1, 96},
    //Echo 2
    {64, 64, 90, 64, 64, 64, 64, 0, 2 ,96},
    //Echo 3
    {64, 64, 90, 64, 64, 64, 64, 0, 3 ,96}
  };

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

void Echoverse::changepar(i32 npar, i32 value)
{
  switch (npar)
  {
  case 0:
    setvolume(value);
    return;
  case 1:
    setpanning(value);
    return;
  case 2:
    setdelay(value);
    return;
  case 3:
    setlrdelay(value);
    return;
  case 4:
    setlrcross(value);
    return;
  case 5:
    setfb(value);
    return;
  case 6:
    sethidamp(value);
    return;
  case 7:
    setreverse(value);
    return;
  case 8:
    Psubdiv = value;
    subdiv = 1.0f / ((f32)(value + 1));
    delay = 1 + lrintf(subdiv * fdelay * fSAMPLE_RATE);
    initdelays();
    return;
  case 9:
    Pes = value;
    pes = 8.0f * (f32)Pes / 127.0f;
    return;
  }
  ASSERT(false);
}

i32 Echoverse::getpar(i32 npar)
{
  switch (npar)
  {
  case 0:
    return Pvolume;
  case 1:
    return Ppanning;
  case 2:
    return Pdelay;
  case 3:
    return Plrdelay;
  case 4:
    return Plrcross;
  case 5:
    return Pfb;
  case 6:
    return Phidamp;
  case 7:
    return Preverse;
  case 8:
    return Psubdiv;
  case 9:
    return Pes;
  }
  ASSERT(false);
  return (0);
}

#endif // SHR3D_SFX_CORE_RAKARRACK
