/*
  Rakarrack Audio FX
  Ring DSP Code based on "(author)" LADSPA plugin(swh-plugins).
  ZynAddSubFX effect structure - Copyright (C) 2002-2005 Nasca Octavian Paul
  Modified and adapted for rakarrack by Josep Andreu

  Ring.C - Ring Modulator effect

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

#ifdef SHR3D_SFX_CORE_RAKARRACK

#include <string.h>

Ring::Ring()
{
  sin_tbl = (f32*)malloc(sizeof(f32) * SAMPLE_RATE);
  tri_tbl = (f32*)malloc(sizeof(f32) * SAMPLE_RATE);
  squ_tbl = (f32*)malloc(sizeof(f32) * SAMPLE_RATE);
  saw_tbl = (f32*)malloc(sizeof(f32) * SAMPLE_RATE);

  Create_Tables();

  offset = 0;

  //default values
  Pvolume = 50;
  Plrcross = 40;
  Plevel = 64;
  Pstereo = 0;

  scale = 1.0f;
  sin = 0.0f;
  tri = 0.0f;
  saw = 0.0f;
  squ = 0.0f;

  setpreset(0);
}

void Ring::Create_Tables()
{
  for (u32 i = 0; i < SAMPLE_RATE; i++)
    sin_tbl[i] = sinf((f32)i * D_PI / fSAMPLE_RATE);
  for (u32 i = 0; i < SAMPLE_RATE; i++)
    tri_tbl[i] = acosf(cosf((f32)i * D_PI / fSAMPLE_RATE)) / D_PI - 1.0f;
  for (u32 i = 0; i < SAMPLE_RATE; i++)
    squ_tbl[i] = (i < SAMPLE_RATE / 2) ? 1.0f : -1.0f;
  for (u32 i = 0; i < SAMPLE_RATE; i++)
    saw_tbl[i] = ((2.0f * i) - fSAMPLE_RATE) / fSAMPLE_RATE;
}

void Ring::processBlock(const f32* const* inBlock, f32** outBlock, const i32 blockSize)
{
  const f32 inputvol = (f32)Pinput / 127.0f;

  if (Pstereo != 0)
  {				//Stereo
    for (i32 i = 0; i < blockSize; i++)
    {
      outBlock[0][i] = inBlock[0][i] * inputvol;
      outBlock[1][i] = inBlock[1][i] * inputvol;
      if (inputvol == 0.0)
        outBlock[0][i] = 1.0; outBlock[1][i] = 1.0;
    }
  }
  else
  {
    for (i32 i = 0; i < blockSize; i++)
    {
      outBlock[0][i] = (inBlock[0][i] + inBlock[1][i]) * inputvol;
      if (inputvol == 0.0)
        outBlock[0][i] = 1.0;
    }
  }

  for (i32 i = 0; i < blockSize; i++)
  {
    const f32 tmpfactor = depth * (scale * (sin * sin_tbl[offset] + tri * tri_tbl[offset] + saw * saw_tbl[offset] + squ * squ_tbl[offset]) + idepth);    //This is now mathematically equivalent, but less computation
    outBlock[0][i] *= tmpfactor;

    if (Pstereo != 0)
    {
      outBlock[1][i] *= tmpfactor;
    }
    offset += Pfreq;
    if (offset > SAMPLE_RATE) offset -= SAMPLE_RATE;
  }

  if (Pstereo == 0)
    memcpy(outBlock[1], outBlock[0], blockSize * sizeof(f32));

  f32 level = dB2rap(60.0f * (f32)Plevel / 127.0f - 40.0f);

  for (i32 i = 0; i < blockSize; i++)
  {
    f32 lout = outBlock[0][i];
    f32 rout = outBlock[1][i];

    const f32 l = lout * (1.0f - lrcross) + rout * lrcross;
    const f32 r = rout * (1.0f - lrcross) + lout * lrcross;

    lout = l;
    rout = r;

    outBlock[0][i] = lout * level * panning;
    outBlock[1][i] = rout * level * (1.0f - panning);
  }
}

void Ring::setpanning(i32 Ppan)
{
  Ppanning = Ppan;
  panning = (f32)(Ppanning + 64) / 128.0f;
  // is Ok ... 
}

void Ring::setlrcross(i32 Plrc)
{
  Plrcross = Plrc;
  lrcross = (f32)(Plrcross + 64) / 128.0f;
}

void Ring::setscale()
{
  scale = sin + tri + saw + squ;
  if (scale == 0.0)
    scale = 1.0;
  scale = 1.0f / scale;
}

void Ring::setpreset(i32 npreset)
{
  const i32 PRESET_SIZE = 13;
  i32 presets[][PRESET_SIZE] = {
    //Saw-Sin
    {-64, 0, -64, 64, 35, 1, 0, 20, 0, 40, 0, 64, 1},
    //E string
    {0, 0, 0, 64, 100, 82, 0, 100, 0, 0, 0, 64, 0},
    //A string
    {0, 0, 0, 64, 100, 110, 0, 0, 100, 50, 0, 64, 0},
    //dissonance
    {0, 0, 0, 64, 100, 817, 0, 20, 0, 100, 0, 64, 1},
    //Fast Beat
    {0, 0, 0, 64, 100, 15, 0, 20, 0, 100, 0, 64, 1},
    //Ring Amp
    {0, 0, 0, 64, 100, 1, 0, 20, 0, 100, 0, 64, 0},
  };

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

void Ring::changepar(i32 npar, i32 value)
{
  switch (npar)
  {
  case 0:
    Pvolume = value;
    outvolume = (f32)(64 + value) / 128.0f;
    return;
  case 1:
    setpanning(value);
    return;
  case 2:
    setlrcross(value);
    return;
  case 3:
    Plevel = value;
    return;
  case 4:
    Pdepthp = value;
    depth = (f32)Pdepthp / 100.0f;
    idepth = 1.0f - depth;
    return;
  case 5:
    if (value > 20000)		//Make sure bad inputs can't cause buffer overflow
      Pfreq = 20000;
    else if (value < 1)
      Pfreq = 1;
    else
      Pfreq = value;
    return;
  case 6:
    if (value > 1)
      value = 1;
    Pstereo = value;
    return;
  case 7:
    Psin = value;
    sin = (f32)Psin / 100.0f;
    setscale();
    return;
  case 8:
    Ptri = value;
    tri = (f32)Ptri / 100.0f;
    setscale();
    return;
  case 9:
    Psaw = value;
    saw = (f32)Psaw / 100.0f;
    setscale();
    return;
  case 10:
    Psqu = value;
    squ = (f32)Psqu / 100.0f;
    setscale();
    return;
  case 11:
    Pinput = value;
    return;
  case 12:
    Pafreq = value;
    return;
  }
  ASSERT(false);
}

i32 Ring::getpar(i32 npar)
{
  switch (npar)
  {
  case 0:
    return Pvolume;
  case 1:
    return Ppanning;
  case 2:
    return Plrcross;
  case 3:
    return Plevel;
  case 4:
    return Pdepthp;
  case 5:
    return Pfreq;
  case 6:
    return Pstereo;
  case 7:
    return Psin;
  case 8:
    return Ptri;
  case 9:
    return Psaw;
  case 10:
    return Psqu;
  case 11:
    return Pinput;
  case 12:
    return Pafreq;
  }
  ASSERT(false);
  return 0;
}

#endif // SHR3D_SFX_CORE_RAKARRACK
