/*
  ZynAddSubFX - a software synthesizer

  DynamicFilter.C - "WahWah" effect and others
  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 "DynamicFilter.h"

#ifdef SHR3D_SFX_CORE_RAKARRACK

DynamicFilter::DynamicFilter()
  : filterpars(0, 64, 64)
{
  setpreset(0);
  cleanup();
}

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

  if (filterpars.changed)
  {
    filterpars.changed = false;
    cleanup();
  }

  lfo.effectlfoout(lfol, lfor);
  lfol *= depth * 5.0f;
  lfor *= depth * 5.0f;
  f32 freq = filterpars.getfreq();
  f32 q = filterpars.getq();

  for (i = 0; i < blockSize; i++)
  {
    outBlock[0][i] = inBlock[0][i];
    outBlock[1][i] = inBlock[1][i];

    f32 x = (fabsf(inBlock[0][i]) + fabsf(inBlock[1][i])) * 0.5f;
    ms1 = ms1 * (1.0f - ampsmooth) + x * ampsmooth + 1e-10f;
  }

  f32 ampsmooth2 = powf(ampsmooth, 0.2f) * 0.3f;
  ms2 = ms2 * (1.0f - ampsmooth2) + ms1 * ampsmooth2;
  ms3 = ms3 * (1.0f - ampsmooth2) + ms2 * ampsmooth2;
  ms4 = ms4 * (1.0f - ampsmooth2) + ms3 * ampsmooth2;
  f32 rms = (sqrtf(ms4)) * ampsns;

  f32 frl = filterl->getrealfreq(freq + lfol + rms);
  f32 frr = filterr->getrealfreq(freq + lfor + rms);

  filterl->setfreq_and_q(frl, q);
  filterr->setfreq_and_q(frr, q);

  filterl->filterout(outBlock[0], blockSize);
  filterr->filterout(outBlock[1], blockSize);

  //panning    
  for (i = 0; i < blockSize; i++)
  {
    outBlock[0][i] *= panning;
    outBlock[1][i] *= (1.0f - panning);
  }
}

void DynamicFilter::cleanup()
{
  reinitfilter();
  ms1 = 0.0f;
  ms2 = 0.0f;
  ms3 = 0.0f;
  ms4 = 0.0f;
}

void DynamicFilter::setdepth(i32 Pdepth_)
{
  Pdepth = Pdepth_;
  depth = powf(((f32)Pdepth_ / 127.0f), 2.0f);
}

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

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

void DynamicFilter::setampsns(i32 Pampsns_)
{
  ampsns = powf((f32)Pampsns_ / 127.0f, 2.5f) * 10.0f;
  if (Pampsnsinv != 0)
    ampsns = -ampsns;
  ampsmooth = expf((f32)-Pampsmooth / 127.0f * 10.0f) * 0.99f;
  Pampsns = Pampsns_;
}

void DynamicFilter::reinitfilter()
{
  if (filterl != NULL)
    delete (filterl);
  if (filterr != NULL)
    delete (filterr);
  filterl = new Filter(filterpars);
  filterr = new Filter(filterpars);
}

void DynamicFilter::setpreset(i32 npreset)
{
  const i32 PRESET_SIZE = 10;
  i32 presets[][PRESET_SIZE] = {
    //WahWah
    {64, 64, 80, 0, 0, 64, 70, 90, 0, 60},
    //AutoWah
    {64, 64, 70, 0, 0, 80, 70, 0, 0, 60},
    //Sweep
    {64, 64, 30, 0, 0, 50, 80, 0, 0, 60},
    //VocalMorph1
    {64, 64, 80, 0, 0, 64, 70, 64, 0, 60},
    //VocalMorph1
    {64, 64, 50, 0, 0, 96, 64, 0, 0, 60}
  };

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

  filterpars.defaults();
  switch (npreset)
  {
  case 0:
    filterpars.Pcategory = 0;
    filterpars.Ptype = 2;
    filterpars.Pfreq = 45;
    filterpars.Pq = 64;
    filterpars.Pstages = 1;
    filterpars.Pgain = 64;
    break;
  case 1:
    filterpars.Pcategory = 2;
    filterpars.Ptype = 0;
    filterpars.Pfreq = 72;
    filterpars.Pq = 64;
    filterpars.Pstages = 0;
    filterpars.Pgain = 64;
    break;
  case 2:
    filterpars.Pcategory = 0;
    filterpars.Ptype = 4;
    filterpars.Pfreq = 64;
    filterpars.Pq = 64;
    filterpars.Pstages = 2;
    filterpars.Pgain = 64;
    break;
  case 3:
    filterpars.Pcategory = 1;
    filterpars.Ptype = 0;
    filterpars.Pfreq = 50;
    filterpars.Pq = 70;
    filterpars.Pstages = 1;
    filterpars.Pgain = 64;

    filterpars.Psequencesize = 2;
    // "I"
    filterpars.Pvowels[0].formants[0].freq = 34;
    filterpars.Pvowels[0].formants[0].amp = 127;
    filterpars.Pvowels[0].formants[0].q = 64;
    filterpars.Pvowels[0].formants[1].freq = 99;
    filterpars.Pvowels[0].formants[1].amp = 122;
    filterpars.Pvowels[0].formants[1].q = 64;
    filterpars.Pvowels[0].formants[2].freq = 108;
    filterpars.Pvowels[0].formants[2].amp = 112;
    filterpars.Pvowels[0].formants[2].q = 64;
    // "A"
    filterpars.Pvowels[1].formants[0].freq = 61;
    filterpars.Pvowels[1].formants[0].amp = 127;
    filterpars.Pvowels[1].formants[0].q = 64;
    filterpars.Pvowels[1].formants[1].freq = 71;
    filterpars.Pvowels[1].formants[1].amp = 121;
    filterpars.Pvowels[1].formants[1].q = 64;
    filterpars.Pvowels[1].formants[2].freq = 99;
    filterpars.Pvowels[1].formants[2].amp = 117;
    filterpars.Pvowels[1].formants[2].q = 64;
    break;
  case 4:
    filterpars.Pcategory = 1;
    filterpars.Ptype = 0;
    filterpars.Pfreq = 64;
    filterpars.Pq = 70;
    filterpars.Pstages = 1;
    filterpars.Pgain = 64;

    filterpars.Psequencesize = 2;
    filterpars.Pnumformants = 2;
    filterpars.Pvowelclearness = 0;

    filterpars.Pvowels[0].formants[0].freq = 70;
    filterpars.Pvowels[0].formants[0].amp = 127;
    filterpars.Pvowels[0].formants[0].q = 64;
    filterpars.Pvowels[0].formants[1].freq = 80;
    filterpars.Pvowels[0].formants[1].amp = 122;
    filterpars.Pvowels[0].formants[1].q = 64;

    filterpars.Pvowels[1].formants[0].freq = 20;
    filterpars.Pvowels[1].formants[0].amp = 127;
    filterpars.Pvowels[1].formants[0].q = 64;
    filterpars.Pvowels[1].formants[1].freq = 100;
    filterpars.Pvowels[1].formants[1].amp = 121;
    filterpars.Pvowels[1].formants[1].q = 64;
    break;
  };

  //          for (i32 i=0;i<5;i++){
  //              printf("freq=%d  amp=%d  q=%d\n",filterpars.Pvowels[0].formants[i].freq,filterpars.Pvowels[0].formants[i].amp,filterpars.Pvowels[0].formants[i].q);
  //          };

  reinitfilter();
}

void DynamicFilter::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:
    setampsns(value);
    return;
  case 8:
    Pampsnsinv = value;
    setampsns(Pampsns);
    return;
  case 9:
    Pampsmooth = value;
    setampsns(Pampsns);
    return;
  }
  ASSERT(false);
}

i32 DynamicFilter::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 Pampsns;
  case 8:
    return Pampsnsinv;
  case 9:
    return Pampsmooth;
  }
  ASSERT(false);
  return 0;
}

#endif // SHR3D_SFX_CORE_RAKARRACK
