/*
  ZynAddSubFX - a software synthesizer

  Alienwah.C - "AlienWah" 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 "Alienwah.h"

#ifdef SHR3D_SFX_CORE_RAKARRACK

Alienwah::Alienwah()
{
  setpreset(0);
  cleanup();
  oldclfol.a = fb;
  oldclfol.b = 0.0;
  oldclfor.a = fb;
  oldclfor.b = 0.0;
}


/*
 * Apply the effect
 */
void Alienwah::processBlock(const f32* const* inBlock, f32** outBlock, const i32 blockSize)
{
  i32 i;
  f32 lfol, lfor;
  COMPLEXTYPE clfol, clfor, out, tmp;

  lfo.effectlfoout(lfol, lfor);
  lfol *= depth * D_PI;
  lfor *= depth * D_PI;
  clfol.a = cosf(lfol + phase) * fb;
  clfol.b = sinf(lfol + phase) * fb;
  clfor.a = cosf(lfor + phase) * fb;
  clfor.b = sinf(lfor + phase) * fb;

  for (i = 0; i < blockSize; i++)
  {
    f32 x = (f32)i / f32(blockSize);
    f32 x1 = 1.0f - x;
    //left  
    tmp.a = clfol.a * x + oldclfol.a * x1;
    tmp.b = clfol.b * x + oldclfol.b * x1;

    out.a = tmp.a * oldl[oldk].a - tmp.b * oldl[oldk].b
      + (1.0f - fabsf(fb)) * inBlock[0][i] * panning;
    out.b = tmp.a * oldl[oldk].b + tmp.b * oldl[oldk].a;
    oldl[oldk].a = out.a;
    oldl[oldk].b = out.b;
    f32 l = out.a * 10.0f * (fb + 0.1f);

    //right
    tmp.a = clfor.a * x + oldclfor.a * x1;
    tmp.b = clfor.b * x + oldclfor.b * x1;

    out.a = tmp.a * oldr[oldk].a - tmp.b * oldr[oldk].b
      + (1.0f - fabsf(fb)) * inBlock[1][i] * (1.0f - panning);
    out.b = tmp.a * oldr[oldk].b + tmp.b * oldr[oldk].a;
    oldr[oldk].a = out.a;
    oldr[oldk].b = out.b;
    f32 r = out.a * 10.0f * (fb + 0.1f);


    if (++oldk >= Pdelay)
      oldk = 0;
    //LRcross
    outBlock[0][i] = l * (1.0f - lrcross) + r * lrcross;
    outBlock[1][i] = r * (1.0f - lrcross) + l * lrcross;
  }

  oldclfol.a = clfol.a;
  oldclfol.b = clfol.b;
  oldclfor.a = clfor.a;
  oldclfor.b = clfor.b;
}

/*
 * Cleanup the effect
 */
void Alienwah::cleanup()
{
  for (i32 i = oldpdelay; i < MAX_ALIENWAH_DELAY; i++)
  {
    oldl[i].a = 0.0f;
    oldl[i].b = 0.0f;
    oldr[i].a = 0.0f;
    oldr[i].b = 0.0f;

  }
  oldk = 0;
}


/*
 * Parameter control
 */

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

void Alienwah::setfb(i32 Pfb_)
{
  Pfb = Pfb_;
  fb = fabsf(((f32)Pfb_ - 64.0f) / 64.1f);
  fb = sqrtf(fb);
  if (fb < 0.4f)
    fb = 0.4f;
  if (Pfb_ < 64)
    fb = -fb;
}

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

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

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

void Alienwah::setphase(i32 Pphase_)
{
  Pphase = Pphase_;
  phase = ((f32)Pphase_ - 64.0f) / 64.0f * PI_;
}

void Alienwah::setdelay(i32 Pdelay_)
{
  if (Pdelay_ > MAX_ALIENWAH_DELAY)
    Pdelay_ = MAX_ALIENWAH_DELAY;
  Pdelay = Pdelay_;
  if (Pdelay_ > oldpdelay)
    cleanup();
  oldpdelay = Pdelay_;
}

void Alienwah::setpreset(i32 npreset)
{
  const i32 PRESET_SIZE = 11;
  i32 presets[][PRESET_SIZE] = {
    //AlienWah1
    {64, 64, 80, 0, 0, 62, 60, 105, 25, 0, 64},
    //AlienWah2
    {64, 64, 95, 106, 0, 101, 60, 105, 17, 0, 64},
    //AlienWah3
    {64, 64, 55, 0, 1, 100, 112, 105, 31, 0, 42},
    //AlienWah4
    {64, 64, 1, 0, 1, 66, 101, 11, 47, 0, 86}
  };

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

void Alienwah::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:
    setdelay(value);
    return;
  case 9:
    setlrcross(value);
    return;
  case 10:
    setphase(value);
    return;
  }
  ASSERT(false);
}

i32 Alienwah::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 Pdelay;
  case 9:
    return Plrcross;
  case 10:
    return Pphase;
  }
  ASSERT(false);
  return 0;
}

#endif // SHR3D_SFX_CORE_RAKARRACK
