/*
  ZynAddSubFX - a software synthesizer

  MuTroMojo.C - "WahWah" effect and others
  Copyright (C) 2002-2005 Nasca Octavian Paul
  Author: Nasca Octavian Paul

  Modified for rakarrack by Ryan Billing

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

#ifdef SHR3D_SFX_CORE_RAKARRACK

MuTroMojo::MuTroMojo()
  : filterl(0, 80.0f, 70.0f, 1)
  , filterr(0, 80.0f, 70.0f, 1)
{
  wahsmooth = 1.0f - expf(-1.0f / (0.02f * fSAMPLE_RATE));  //0.02 seconds

  Fstages = 1;
  Ftype = 1;
  setpreset(0);

  cleanup();
}

void MuTroMojo::processBlock(const f32* const* inBlock, f32** outBlock, const i32 blockSize)
{
  f32 lfol, lfor;
  lfo.effectlfoout(lfol, lfor);
  lfol *= depth * 5.0f;
  lfor *= depth * 5.0f;

  for (i32 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 * ampsmooth + x * (1.0f - ampsmooth) + 1e-10f;

    //oldfbias -= 0.001 * oldfbias2;
    oldfbias = oldfbias * (1.0f - wahsmooth) + fbias * wahsmooth + 1e-10f;  //smooth MIDI control
    oldfbias1 = oldfbias1 * (1.0f - wahsmooth) + oldfbias * wahsmooth + 1e-10f;
    oldfbias2 = oldfbias2 * (1.0f - wahsmooth) + oldfbias1 * wahsmooth + 1e-10f;
  }

  f32 rms = ms1 * ampsns + oldfbias2;

  if (rms > 0.0f) //apply some smooth limiting
    rms = 1.0f - 1.0f / (rms * rms + 1.0f);
  else
    rms = -1.0f + 1.0f / (rms * rms + 1.0f);

  if (variq) q = powf(2.0f, (2.0f * (1.0f - rms) + 1.0f));

  f32 lmod = (lfol + rms);
  f32 rmod = (lfor + rms);

  if (lmod > 1.0f)
    lmod = 1.0f;
  if (lmod < 0.0f)
    lmod = 0.0f;
  if (rmod > 1.0f)
    rmod = 1.0f;
  if (rmod < 0.0f)
    rmod = 0.0f;

  //rms*=rms;    
  f32 frl = minfreq + maxfreq * (powf(base, lmod) - 1.0f) * ibase;
  f32 frr = minfreq + maxfreq * (powf(base, rmod) - 1.0f) * ibase;

  centfreq = frl; //testing variable

  filterl.setfreq_and_q(frl, q);
  filterr.setfreq_and_q(frr, q);

  filterl.filterout(outBlock[0], blockSize);
  filterr.filterout(outBlock[1], blockSize);
}

void MuTroMojo::cleanup()
{
  reinitfilter();
  ms1 = 0.0;
  oldfbias = oldfbias1 = oldfbias2 = 0.0f;
  filterl.cleanup();
  filterr.cleanup();
}

void MuTroMojo::setwidth(i32 Pwidth_)
{
  Pwidth = Pwidth_;
  depth = powf(((f32)Pwidth_ / 127.0f), 2.0f);
}

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

void MuTroMojo::setampsns(i32 Pp)
{
  Pampsns = Pp;
  if (Pampsns > 0)
  {
    ampsns = expf(0.083f * (f32)Pampsns);
  }
  else
  {
    ampsns = -expf(-0.083f * (f32)Pampsns);
  }
  fbias = ((f32)Pampsnsinv) / 127.0f;
  ampsmooth = expf(-1.0f / ((((f32)Pampsmooth) / 127.0f + 0.01f) * fSAMPLE_RATE)); //goes up to 1 second
}

void MuTroMojo::reinitfilter()
{
  //setmix (i32 mix, f32 lpmix, f32 bpmix, f32 hpmix)
  filterl.setmix(1, lpmix, bpmix, hpmix);
  filterr.setmix(1, lpmix, bpmix, hpmix);
}

void MuTroMojo::setpreset(i32 npreset)
{
  const i32 PRESET_SIZE = 18;
  i32 presets[][PRESET_SIZE] = {
    //Wah Pedal
    {16, 10, 60, 0, 0, 64, 0, 0, 10, 7, -16, 40, -3, 1, 2000, 450, 1, 1 },
    //Mutron
    {0, 15, 138, 0, 0, 64, 0, 50, 0, 30, 32, 0, 5, 1, 2000, 60, 0, 1 },
    //Phase Wah
    {0, 50, 60, 0, 0, 64, 30, 10, 10, 30, 32, 0, 10, 2, 2000, 350, 1, 1 },
    //Succulent Phaser
    {64, 8, 35, 10, 0, 64, 50, -10, 53, 35, 28, -16, 32, 4, 2600, 300, 1, 1 },
    //Quacky
    {16, 10, 60, 0, 0, 64, 0, 40, 10, 32, -16, 40, -3, 1, 2000, 400, 1, 1 }
  };

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

  reinitfilter();
}

void MuTroMojo::changepar(i32 npar, i32 value)
{
  switch (npar)
  {
  case 0:
    setvolume(value);
    return;
  case 1:
    Pq = value;
    q = (f32)Pq;
    return;
  case 2:
    lfo.Pfreq = value;
    lfo.updateparams();
    return;
  case 3:
    lfo.Prandomness = 0;//value;
    lfo.updateparams();
    return;
  case 4:
    lfo.PLFOtype = value;
    lfo.updateparams();
    return;
  case 5:
    lfo.Pstereo = value;
    lfo.updateparams();
    return;
  case 6:
    setwidth(value);
    return;
  case 7:
    setampsns(value);
    return;
  case 8:
    Pampsnsinv = value;
    setampsns(Pampsns);
    return;
  case 9:
    Pampsmooth = value;
    setampsns(Pampsns);
    return;
  case 10:
    Plp = value;
    lpmix = ((f32)Plp) / 32.0f;
    reinitfilter();
    return;
  case 11:
    Pbp = value;
    bpmix = ((f32)Pbp) / 32.0f;
    reinitfilter();
    return;
  case 12:
    Php = value;
    hpmix = ((f32)Php) / 32.0f;
    reinitfilter();
    return;
  case 13:
    Pstages = (value - 1);
    filterl.setstages(Pstages);
    filterr.setstages(Pstages);
    cleanup();
    return;
  case 14:
    Prange = value;
    maxfreq = ((f32)Prange);
    return;
  case 15:
    Pminfreq = value;
    minfreq = (f32)value;
    return;
  case 16:
    variq = value;
    return;
  case 17:
    Pqm = value;
    filterl.setmode(Pqm);
    filterr.setmode(Pqm);
    return;
  }
  ASSERT(false);
}

i32 MuTroMojo::getpar(i32 npar)
{
  switch (npar)
  {
  case 0:
    return Pvolume;
  case 1:
    return Pq;
  case 2:
    return lfo.Pfreq;
  case 3:
    return lfo.Prandomness;
  case 4:
    return lfo.PLFOtype;
  case 5:
    return lfo.Pstereo;
  case 6:
    return Pwidth;
  case 7:
    return Pampsns;
  case 8:
    return Pampsnsinv;
  case 9:
    return Pampsmooth;
  case 10:
    return Plp;
  case 11:
    return Pbp;
  case 12:
    return Php;
  case 13:
    return Pstages + 1;
  case 14:
    return Prange;
  case 15:
    return Pminfreq;
  case 16:
    return variq;
  case 17:
    return Pqm;
  }
  ASSERT(false);
  return 0;
}

#endif // SHR3D_SFX_CORE_RAKARRACK
