/*
  ZynAddSubFX - a software synthesizer

  Echo.C - Echo 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 "MusicalDelay.h"

#ifdef SHR3D_SFX_CORE_RAKARRACK

MusicalDelay::MusicalDelay()
{
  maxx_delay = SAMPLE_RATE * MAX_DELAY;
  ldelay1 = new f32[maxx_delay];
  rdelay1 = new f32[maxx_delay];
  ldelay2 = new f32[maxx_delay];
  rdelay2 = new f32[maxx_delay];

  dl1 = maxx_delay - 1;
  dl2 = maxx_delay - 1;
  dr1 = maxx_delay - 1;
  dr2 = maxx_delay - 1;

  setpreset(0);
  cleanup();
}

void MusicalDelay::cleanup()
{
  for (i32 i = 0; i < dl1; i++)
    ldelay1[i] = 0.0;
  for (i32 i = 0; i < dr1; i++)
    rdelay1[i] = 0.0;
  for (i32 i = 0; i < dl2; i++)
    ldelay2[i] = 0.0;
  for (i32 i = 0; i < dr2; i++)
    rdelay2[i] = 0.0;

  oldl1 = 0.0;
  oldr1 = 0.0;
  oldl2 = 0.0;
  oldr2 = 0.0;
}

void MusicalDelay::initdelays()
{
  kl1 = 0;
  kr1 = 0;

  if (delay1 >= maxx_delay)
    delay1 = maxx_delay - 1;
  if (delay2 >= maxx_delay)
    delay2 = maxx_delay - 1;

  dl1 = delay1;
  if (dl1 < 1)
    dl1 = 1;
  dr1 = delay1;
  if (dr1 < 1)
    dr1 = 1;
  kl2 = 0;
  kr2 = 0;
  dl2 = delay2 + lrdelay;
  if (dl2 < 1)
    dl2 = 1;
  dr2 = delay2 + lrdelay;
  if (dr2 < 1)
    dr2 = 1;

  for (i32 i = dl1; i < maxx_delay; i++)
    ldelay1[i] = 0.0;
  for (i32 i = dl2; i < maxx_delay; i++)
    ldelay2[i] = 0.0;

  for (i32 i = dr1; i < maxx_delay; i++)
    rdelay1[i] = 0.0;
  for (i32 i = dr2; i < maxx_delay; i++)
    rdelay2[i] = 0.0;

  cleanup();
}

void MusicalDelay::processBlock(const f32* const* inBlock, f32** outBlock, const i32 blockSize)
{
  f32 l1, r1, ldl1, rdl1, l2, r2, ldl2, rdl2;

  for (i32 i = 0; i < blockSize; i++)
  {
    ldl1 = ldelay1[kl1];
    rdl1 = rdelay1[kr1];
    l1 = ldl1 * (1.0f - lrcross) + rdl1 * lrcross;
    r1 = rdl1 * (1.0f - lrcross) + ldl1 * lrcross;
    ldl1 = l1;
    rdl1 = r1;

    ldl2 = ldelay2[kl2];
    rdl2 = rdelay2[kr2];
    l2 = ldl2 * (1.0f - lrcross) + rdl2 * lrcross;
    r2 = rdl2 * (1.0f - lrcross) + ldl2 * lrcross;
    ldl2 = l2;
    rdl2 = r2;

    ldl1 = inBlock[0][i] * gain1 * panning1 - ldl1 * fb1;
    rdl1 = inBlock[1][i] * gain1 * (1.0f - panning1) - rdl1 * fb1;

    ldl2 = inBlock[0][i] * gain2 * panning2 - ldl2 * fb2;
    rdl2 = inBlock[1][i] * gain2 * (1.0f - panning2) - rdl2 * fb2;

    outBlock[0][i] = (ldl1 + ldl2) * 2.0f;
    outBlock[1][i] = (rdl1 + rdl2) * 2.0f;

    //LowPass Filter
    ldelay1[kl1] = ldl1 = ldl1 * hidamp + oldl1 * (1.0f - hidamp);
    rdelay1[kr1] = rdl1 = rdl1 * hidamp + oldr1 * (1.0f - hidamp);
    oldl1 = ldl1;
    oldr1 = rdl1;

    ldelay2[kl2] = ldl2 = ldl2 * hidamp + oldl2 * (1.0f - hidamp);
    rdelay2[kr2] = rdl2 = rdl2 * hidamp + oldr2 * (1.0f - hidamp);
    oldl2 = ldl2;
    oldr2 = rdl2;

    if (++kl1 >= dl1)
      kl1 = 0;
    if (++kr1 >= dr1)
      kr1 = 0;

    if (++kl2 >= dl2)
      kl2 = 0;
    if (++kr2 >= dr2)
      kr2 = 0;
  }
}

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

void MusicalDelay::setpanning(i32 num, i32 Ppanning)
{
  switch (num)
  {
  case 1:
    this->Ppanning1 = Ppanning;
    panning1 = ((f32)Ppanning1 + 0.5f) / 127.0f;
    break;
  case 2:
    this->Ppanning2 = Ppanning;
    panning2 = ((f32)Ppanning2 + 0.5f) / 127.0f;
    break;
  }
}

void MusicalDelay::setdelay(i32 num, i32 Pdelay)
{
  const f32 ntem = 60.0f / (f32)Ptempo;
  f32 coef;
  switch (num)
  {
  case 1:
    this->Pdelay1 = Pdelay;
    break;
  case 2:
    this->Pdelay2 = Pdelay;
    break;
  case 3:
    this->Plrdelay = Pdelay;
    break;
  }

  delay1 = lrintf((ntem / (f32)Pdelay1) * fSAMPLE_RATE);

  if (Plrdelay != 0)
    coef = ntem / (f32)Plrdelay;
  else
    coef = 0;

  delay2 = lrintf((coef + (ntem / (f32)Pdelay2)) * fSAMPLE_RATE);

  initdelays();
}

void MusicalDelay::setgain(i32 num, i32 PGain)
{
  switch (num)
  {
  case 1:
    this->Pgain1 = PGain;
    gain1 = (f32)Pgain1 / 127.0f;
    break;
  case 2:
    this->Pgain2 = PGain;
    gain2 = (f32)Pgain2 / 127.0f;
    break;
  }
}

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

void MusicalDelay::setfb(i32 num, i32 Pfb)
{
  switch (num)
  {
  case 1:
    this->Pfb1 = Pfb;
    fb1 = (f32)Pfb1 / 127.0f;
    break;
  case 2:
    this->Pfb2 = Pfb;
    fb2 = (f32)Pfb2 / 127.0f;
    break;
  }
}

void MusicalDelay::sethidamp(i32 Phidamp_)
{
  Phidamp = Phidamp_;
  hidamp = 1.0f - (f32)Phidamp_ / 127.0f;
}

void MusicalDelay::settempo(i32 Ptempo_)
{
  f32 coef = 0.0;

  Ptempo = Ptempo_;
  const f32 ntem = 60.0f / (f32)Ptempo_;

  delay1 = lrintf((ntem / (f32)Pdelay1) * fSAMPLE_RATE);
  if (Plrdelay != 0)
    coef = ntem / (f32)Plrdelay;
  else
    coef = 0;
  delay2 = lrintf((coef + (ntem / (f32)Pdelay2)) * fSAMPLE_RATE);

  initdelays();
}

void MusicalDelay::setpreset(i32 npreset)
{
  const i32 PRESET_SIZE = 13;
  i32 presets[][PRESET_SIZE] = {
    //Echo 1
    {64, 0, 2, 7, 0, 59, 0, 127, 4, 59, 106, 75, 75},
    //Echo 2
    {67, 0, 3, 7, 0, 59, 0, 127, 6, 69, 60, 127, 127}
  };

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

void MusicalDelay::changepar(i32 npar, i32 value)
{
  switch (npar)
  {
  case 0:
    setvolume(value);
    return;
  case 1:
    setpanning(1, value);
    return;
  case 2:
    setdelay(1, value);
    return;
  case 3:
    setdelay(3, value);
    return;
  case 4:
    setlrcross(value);
    return;
  case 5:
    setfb(1, value);
    return;
  case 6:
    sethidamp(value);
    return;
  case 7:
    setpanning(2, value);
    return;
  case 8:
    setdelay(2, value);
    return;
  case 9:
    setfb(2, value);
    return;
  case 10:
    settempo(value);
    return;
  case 11:
    setgain(1, value);
    return;
  case 12:
    setgain(2, value);
    return;
  }
  ASSERT(false);
}

i32 MusicalDelay::getpar(i32 npar)
{
  switch (npar)
  {
  case 0:
    return Pvolume;
  case 1:
    return Ppanning1;
  case 2:
    return Pdelay1;
  case 3:
    return Plrdelay;
  case 4:
    return Plrcross;
  case 5:
    return Pfb1;
  case 6:
    return Phidamp;
  case 7:
    return Ppanning2;
  case 8:
    return Pdelay2;
  case 9:
    return Pfb2;
  case 10:
    return Ptempo;
  case 11:
    return Pgain1;
  case 12:
    return Pgain2;
  }
  ASSERT(false);
  return 0;
}

#endif // SHR3D_SFX_CORE_RAKARRACK
