/*
  ZynAddSubFX - a software synthesizer

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

  Modified for rakarrack by Josep Andreu

  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 "Phaser.h"

#ifdef SHR3D_SFX_CORE_RAKARRACK

#define PHASER_LFO_SHAPE 2

Phaser::Phaser()
{
  setpreset(0);
  cleanup();
}

void Phaser::processBlock(const f32* const* inBlock, f32** outBlock, const i32 blockSize)
{
  f32 lfol, lfor;

  lfo.effectlfoout(lfol, lfor);
  f32 lgain = lfol;
  f32 rgain = lfor;
  lgain = (expf(lgain * PHASER_LFO_SHAPE) - 1.0f) / (expf(PHASER_LFO_SHAPE) - 1.0f);
  rgain = (expf(rgain * PHASER_LFO_SHAPE) - 1.0f) / (expf(PHASER_LFO_SHAPE) - 1.0f);

  lgain = 1.0f - phase * (1.0f - depth) - (1.0f - phase) * lgain * depth;
  rgain = 1.0f - phase * (1.0f - depth) - (1.0f - phase) * rgain * depth;

  if (lgain > 1.0)
    lgain = 1.0f;
  else if (lgain < 0.0)
    lgain = 0.0f;
  if (rgain > 1.0)
    rgain = 1.0f;
  else if (rgain < 0.0)
    rgain = 0.0f;

  for (i32 i = 0; i < blockSize; i++)
  {
    const f32 x = (f32)i / fPERIOD;
    const f32 x1 = 1.0f - x;
    const f32 gl = lgain * x + oldlgain * x1;
    const f32 gr = rgain * x + oldrgain * x1;
    f32 inl = inBlock[0][i] * panning + fbl;
    f32 inr = inBlock[1][i] * (1.0f - panning) + fbr;

    //Left channel
    for (i32 j = 0; j < Pstages * 2; j++)
    {			//Phasing routine
      const f32 tmp = oldl[j] + DENORMAL_GUARD;
      oldl[j] = gl * tmp + inl;
      inl = tmp - gl * oldl[j];
    }
    //Right channel
    for (i32 j = 0; j < Pstages * 2; j++)
    {			//Phasing routine
      const f32 tmp = oldr[j] + DENORMAL_GUARD;
      oldr[j] = (gr * tmp) + inr;
      inr = tmp - (gr * oldr[j]);
    }
    //Left/Right crossing
    f32 l = inl;
    f32 r = inr;
    inl = l * (1.0f - lrcross) + r * lrcross;
    inr = r * (1.0f - lrcross) + l * lrcross;

    fbl = inl * fb;
    fbr = inr * fb;
    outBlock[0][i] = inl;
    outBlock[1][i] = inr;
  }

  oldlgain = lgain;
  oldrgain = rgain;

  if (Poutsub != 0)
  {
    for (i32 i = 0; i < blockSize; i++)
    {
      outBlock[0][i] *= -1.0f;
      outBlock[1][i] *= -1.0f;
    }
  }
}

void Phaser::cleanup()
{
  fbl = 0.0;
  fbr = 0.0;
  oldlgain = 0.0;
  oldrgain = 0.0;
  for (i32 i = 0; i < Pstages * 2; i++)
  {
    oldl[i] = 0.0;
    oldr[i] = 0.0;
  }
}

void Phaser::setdepth(i32 Pdepth_)
{
  Pdepth = Pdepth_;
  depth = ((f32)Pdepth_ / 127.0f);
}

void Phaser::setfb(i32 Pfb_)
{
  Pfb = Pfb_;
  fb = ((f32)Pfb_ - 64.0f) / 64.1f;
}

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

void Phaser::setpanning(i32 Ppanning_)
{
  Ppanning = Ppanning_;
  panning = ((f32)Ppanning_ + .5f) / 127.0f;
}

void Phaser::setlrcross(i32 Plrcross_)
{
  Plrcross = Plrcross_;
  lrcross = (f32)Plrcross_ / 127.0f;
}

void Phaser::setstages(i32 Pstages_)
{
  if (Pstages_ >= MAX_PHASER_STAGES)
    Pstages_ = MAX_PHASER_STAGES - 1;
  Pstages = Pstages_;
  cleanup();
}

void Phaser::setphase(i32 Pphase_)
{
  Pphase = Pphase_;
  phase = ((f32)Pphase_ / 127.0f);
}

void Phaser::setpreset(i32 npreset)
{
  const i32 PRESET_SIZE = 12;
  i32 presets[][PRESET_SIZE] = {
    //Phaser1
    {64, 64, 11, 0, 0, 64, 110, 64, 1, 0, 0, 20},
    //Phaser2
    {64, 64, 10, 0, 0, 88, 40, 64, 3, 0, 0, 20},
    //Phaser3
    {64, 64, 8, 0, 0, 66, 68, 107, 2, 0, 0, 20},
    //Phaser4
    {39, 64, 1, 0, 0, 66, 67, 10, 5, 0, 1, 20},
    //Phaser5
    {64, 64, 1, 0, 1, 110, 67, 78, 10, 0, 0, 20},
    //Phaser6
    {64, 64, 31, 100, 0, 58, 37, 78, 3, 0, 0, 20}
  };

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

void Phaser::changepar(i32 npar, i32 value)
{
  switch (npar)
  {
  case 0:
    setvolume(value);
    return;
  case 1:
    setpanning(value);
    return;
  case 2:
    lfo.Pfreq = value;
    lfo.updateparams();
    return;
  case 3:
    lfo.Prandomness = value;
    lfo.updateparams();
    return;
  case 4:
    lfo.PLFOtype = value;
    lfo.updateparams();
    return;
  case 5:
    lfo.Pstereo = value;
    lfo.updateparams();
    return;
  case 6:
    setdepth(value);
    return;
  case 7:
    setfb(value);
    return;
  case 8:
    setstages(value);
    return;
  case 9:
    setlrcross(value);
    return;
  case 10:
    if (value > 1)
      value = 1;
    Poutsub = value;
    return;
  case 11:
    setphase(value);
    return;
  }
  ASSERT(false);
}

i32 Phaser::getpar(i32 npar)
{
  switch (npar)
  {
  case 0:
    return Pvolume;
  case 1:
    return Ppanning;
  case 2:
    return lfo.Pfreq;
  case 3:
    return lfo.Prandomness;
  case 4:
    return lfo.PLFOtype;
  case 5:
    return lfo.Pstereo;
  case 6:
    return Pdepth;
  case 7:
    return Pfb;
  case 8:
    return Pstages;
  case 9:
    return Plrcross;
  case 10:
    return Poutsub;
  case 11:
    return Pphase;
  }
  ASSERT(false);
  return 0;
}

#endif // SHR3D_SFX_CORE_RAKARRACK
