/*
  Arpie.C - Arpeggiated Echo effect
  Copyright (C) 2002-2005 Nasca Octavian Paul
  Author: Nasca Octavian Paul

  Modified for rakarrack by Josep Andreu
  Arpeggio Echo effect by Transmogrifox

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

#ifdef SHR3D_SFX_CORE_RAKARRACK

#include <string.h>

Arpie::Arpie()
{
  Srate_Attack_Coeff = 1.0f / (fSAMPLE_RATE * ATTACK);
  invattack = SAMPLE_RATE / 15;
  envattack = 1.0f / (f32)invattack;
  fade = SAMPLE_RATE / 10;    //200ms fade time available

  setpreset(0);
  setpattern(0);
  cleanup();
}

/*
 * Cleanup the effect
 */
void Arpie::cleanup()
{
  memset(ldelay, 0, sizeof(f32) * maxx_delay);
  memset(rdelay, 0, sizeof(f32) * maxx_delay);
  oldl = 0.0;
  oldr = 0.0;
  rvkl = 0;
  rvkr = 0;
  rvfl = 0;
  rvfr = 0;
  kl = 0;
  kr = 0;
  harmonic = 1;
}


/*
 * Initialize the delays
 */
void Arpie::initdelays()
{
  kl = 0;
  kr = 0;

  dl = delay - lrdelay;
  if (dl < 1)
    dl = 1;
  dr = delay + lrdelay;
  if (dr < 1)
    dr = 1;

  rvkl = 0;
  rvkr = 0;
  Srate_Attack_Coeff = 15.0f / (dl + dr);   // Set swell time to 1/10th of average delay time 
  fade = (dl + dr) / 5;

  for (i32 i = dl; i < maxx_delay; i++)
    ldelay[i] = 0.0;
  for (i32 i = dr; i < maxx_delay; i++)
    rdelay[i] = 0.0;
  oldl = 0.0;
  oldr = 0.0;
}

/*
 * Effect output
 */
void Arpie::processBlock(const f32* const* inBlock, f32** outBlock, const i32 blockSize)
{
  f32 l, r, ldl, rdl, rswell, lswell;

  for (i32 i = 0; i < blockSize; i++)
  {
    ldl = ldelay[kl];
    rdl = rdelay[kr];
    l = ldl * (1.0f - lrcross) + rdl * lrcross;
    r = rdl * (1.0f - lrcross) + ldl * lrcross;
    ldl = l;
    rdl = r;

    ldl = inBlock[0][i] * panning - ldl * fb;
    rdl = inBlock[1][i] * (1.0f - panning) - rdl * fb;

    if (reverse > 0.0)
    {
      lswell = (f32)(abs(kl - rvkl)) * Srate_Attack_Coeff;
      envswell = (1.0f - cosf(PI_ * envcnt * envattack));
      if (envswell > 1.0f)
        envswell = 1.0f;
      if (lswell <= PI_)
      {
        lswell = 0.5f * (1.0f - cosf(lswell));  //Clickless transition
        outBlock[0][i] = envswell * (reverse * (ldelay[rvkl] * lswell + ldelay[rvfl] * (1.0f - lswell)) + (ldl * (1 - reverse)));   //Volume ducking near zero crossing.     
      }
      else
      {
        outBlock[0][i] = ((ldelay[rvkl] * reverse) + (ldl * (1 - reverse))) * envswell;
      }

      rswell = (f32)(abs(kr - rvkr)) * Srate_Attack_Coeff;
      if (rswell <= PI_)
      {
        rswell = 0.5f * (1.0f - cosf(rswell));   //Clickless transition 
        outBlock[1][i] = envswell * (reverse * (rdelay[rvkr] * rswell + rdelay[rvfr] * (1.0f - rswell)) + (rdl * (1 - reverse)));  //Volume ducking near zero crossing.
      }
      else
      {
        outBlock[1][i] = envswell * ((rdelay[rvkr] * reverse) + (rdl * (1 - reverse)));
      }
    }
    else
    {
      outBlock[0][i] = ldl;
      outBlock[1][i] = rdl;
    }

    //LowPass Filter
    ldelay[kl] = ldl = ldl * hidamp + oldl * (1.0f - hidamp);
    rdelay[kr] = rdl = rdl * hidamp + oldr * (1.0f - hidamp);
    oldl = ldl + DENORMAL_GUARD;
    oldr = rdl + DENORMAL_GUARD;

    if (++envcnt >= invattack)
      envcnt = invattack;
    if (kl > (dl - invattack))
      envcnt -= 2;
    if (envcnt < 0)
      envcnt = 0;

    if (++kl >= dl)
    {
      kl = 0;
      envcnt = 0;
      if (++harmonic >= Pharms)
        harmonic = 0;
    }
    if (++kr >= dr)
      kr = 0;
    rvkl += pattern[harmonic];
    if (rvkl >= (dl))
      rvkl = rvkl % (dl);
    rvkr += pattern[harmonic];
    if (rvkr >= (dr))
      rvkr = rvkr % (dr);

    rvfl = rvkl + fade;
    if (rvfl >= (dl))
      rvfl = rvfl % (dl);
    rvfr = rvkr + fade;
    if (rvfr >= (dr))
      rvfr = rvfr % (dr);
  }
}


/*
 * Parameter control
 */
void Arpie::setvolume(i32 Pvolume_)
{
  Pvolume = Pvolume_;
  outvolume = (f32)Pvolume_ / 127.0f;
  if (Pvolume_ == 0)
    cleanup();
}

void Arpie::setpanning(i32 Ppanning_)
{
  Ppanning = Ppanning_;
  panning = ((f32)Ppanning_ + 0.5f) / 127.0f;
}

void Arpie::setreverse(i32 Preverse_)
{
  Preverse = Preverse_;
  reverse = (f32)Preverse_ / 127.0f;
}

void Arpie::setdelay(i32 Pdelay_)
{
  Pdelay = Pdelay_;
  if (Pdelay_ < 2)
    Pdelay_ = 2;
  if (Pdelay_ > 600)
    Pdelay_ = 600;	//100ms .. 2 sec constraint
  delay = 1 + lrintf((60.0f / ((f32)(subdiv * Pdelay_))) * fSAMPLE_RATE);	//quarter notes
  initdelays();
}

void Arpie::setlrdelay(i32 Plrdelay_)
{
  Plrdelay = Plrdelay_;
  f32 tmp =
    (powf(2.0, fabsf((f32)Plrdelay_ - 64.0f) / 64.0f * 9.0f) -
      1.0f) / 1000.0f * fSAMPLE_RATE;
  if (Plrdelay_ < 64.0)
    tmp = -tmp;
  lrdelay = lrintf(tmp);
  initdelays();
}

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

void Arpie::setfb(i32 Pfb_)
{
  Pfb = Pfb_;
  fb = (f32)Pfb_ / 128.0f;
}

void Arpie::sethidamp(i32 Phidamp_)
{
  Phidamp = Phidamp_;
  hidamp = 0.5f - (f32)Phidamp_ / 254.0f;
}

void Arpie::setpattern(i32 Ppattern_)
{
  Ppattern = Ppattern_;

  const i32 PATTERN_SIZE = MAXHARMS;
  const i32 NUM_PATTERNS = 7;
  i32 setpatterns[NUM_PATTERNS][PATTERN_SIZE] =
  {
    {2, 3, 4, 5, 6, 7, 8, 9},
    {9, 8, 7, 6, 5, 4, 3, 2},
    {2, 4, 3, 5, 4, 6, 5, 7},
    {2, 2, 4, 3, 6, 2, 5, 3},
    {3, 2, 4, 3, 5, 4, 6, 5},
    {4, 3, 2, 7, 5, 3, 4, 2},
    {2, 3, 4, 5, 6, 7, 8, 9}
  };

  if (Ppattern_ >= PATTERN_SIZE)
    Ppattern_ = PATTERN_SIZE - 1;
  for (i32 i = 0; i < PATTERN_SIZE; i++)
    pattern[i] = setpatterns[Ppattern_][i];
}

void
Arpie::setpreset(i32 npreset)
{
  const i32 PRESET_SIZE = 9;
  i32 presets[][PRESET_SIZE] = {
    //Arpie 1
    {67, 64, 35, 64, 30, 59, 0, 127, 4},
    //Arpie 2
    {67, 64, 21, 64, 30, 59, 0, 64, 4},
    //Arpie 3
    {67, 75, 60, 64, 30, 59, 10, 0, 4},
    //Simple Arpie
    {67, 60, 44, 64, 30, 0, 0, 0, 4},
    //Canyon
    {67, 60, 102, 50, 30, 82, 48, 0, 4},
    //Panning Arpie 1
    {67, 64, 44, 17, 0, 82, 24, 0, 4},
    //Panning Arpie 2
    {81, 60, 46, 118, 100, 68, 18, 0, 4},
    //Panning Arpie 3
    {81, 60, 26, 100, 127, 67, 36, 0, 4},
    //Feedback Arpie
    {62, 64, 28, 64, 100, 90, 55, 0, 4}
  };

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

void Arpie::changepar(i32 npar, i32 value)
{
  switch (npar)
  {
  case 0:
    setvolume(value);
    return;
  case 1:
    setpanning(value);
    return;
  case 2:
    setdelay(value);
    return;
  case 3:
    setlrdelay(value);
    return;
  case 4:
    setlrcross(value);
    return;
  case 5:
    setfb(value);
    return;
  case 6:
    sethidamp(value);
    return;
  case 7:
    setreverse(value);
    return;
  case 8:
    Pharms = value;
    if ((Pharms < 2) && (Pharms >= MAXHARMS))
    {
      Pharms = 2;
    }
    return;
  case 9:
    setpattern(value);
    return;
  case 10:
    Psubdiv = value;
    subdiv = Psubdiv + 1;
    setdelay(Pdelay);
    return;
  }
  ASSERT(false);
}

i32 Arpie::getpar(i32 npar)
{
  switch (npar)
  {
  case 0:
    return Pvolume;
  case 1:
    return Ppanning;
  case 2:
    return Pdelay;
  case 3:
    return Plrdelay;
  case 4:
    return Plrcross;
  case 5:
    return Pfb;
  case 6:
    return Phidamp;
  case 7:
    return Preverse;
  case 8:
    return Pharms;
  case 9:
    return Ppattern;
  case 10:
    return Psubdiv;
  }
  ASSERT(false);
  return 0;
}

#endif // SHR3D_SFX_CORE_RAKARRACK
