/*
  Rakarrack Guitar FX

  Sequence.C - Simple compressor/Sequence effect with easy interface, minimal controls
  Copyright (C) 2010 Ryan Billing
  Author: Ryan Billing & Josep Andreu

  This program is free software; you can redistribute it and/or modify
  it under the terms of version 3 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 "Sequence.h"

#ifdef SHR3D_SFX_CORE_RAKARRACK

#ifdef SHR3D_SFX_CORE_RAKARRACK_LIBSAMPLERATE

#include <string.h>

Sequence::Sequence(i32 Quality, i32 DS, i32 uq, i32 dq)
  : filterl(0, 80.0f, 40.0f, 2)
  , filterr(0, 80.0f, 40.0f, 2)
  , modfilterl(0, 25.0f, 0.15f, 2)
  , modfilterr(0, 25.0f, 0.15f, 2)
  , U_Resample(dq)
  , D_Resample(uq)
{
  hq = Quality;
  adjust(DS, blockSize());

  outi = (f32*)malloc(sizeof(f32) * nPERIOD);
  outo = (f32*)malloc(sizeof(f32) * nPERIOD);

  setpreset(0);

  filterl.setmix(1, 0.33f, -1.0f, 0.25f);
  filterr.setmix(1, 0.33f, -1.0f, 0.25f);

  PS = new PitchShifter(window, hq, nfSAMPLE_RATE);
  PS->ratio = 1.0f;

  cleanup();
}

void Sequence::cleanup()
{
  memset(outi, 0, sizeof(f32) * nPERIOD);
  memset(outo, 0, sizeof(f32) * nPERIOD);
}

void Sequence::processBlock(f32** inBlock, f32** outBlock, const i32 blockSize)
{
  i32 i;
  i32 nextcount, dnextcount;
  i32 hPERIOD;

  f32 ldiff, rdiff, lfol, lfor, ftcount;
  f32 lmod = 0.0f;
  f32 rmod = 0.0f;
  f32 ldbl, ldbr;

  if ((Pmode == 3) || (Pmode == 5) || (Pmode == 6))
    hPERIOD = nPERIOD;
  else
    hPERIOD = blockSize;

  if ((rndflag) && (tcount < hPERIOD + 1))//This is an Easter Egg
  {
    srand(u32(time(NULL)));
    for (i = 0; i < 8; i++)
      fsequence[i] = RND1;
  }

  switch (Pmode)
  {
  case 0:	//Lineal
    nextcount = scount + 1;
    if (nextcount > 7)
      nextcount = 0;
    ldiff = ifperiod * (fsequence[nextcount] - fsequence[scount]);
    lfol = fsequence[scount];

    dscount = (scount + Pstdiff) % 8;
    dnextcount = dscount + 1;
    if (dnextcount > 7)
      dnextcount = 0;
    rdiff = ifperiod * (fsequence[dnextcount] - fsequence[dscount]);
    lfor = fsequence[dscount];

    for (i = 0; i < blockSize; i++)  //Maintain sequenced modulator
    {
      if (++tcount >= intperiod)
      {
        tcount = 0;
        scount++;
        if (scount > 7)
          scount = 0;  //reset to beginning of sequence buffer

        nextcount = scount + 1;
        if (nextcount > 7)
          nextcount = 0;
        ldiff = ifperiod * (fsequence[nextcount] - fsequence[scount]);
        lfol = fsequence[scount];

        dscount = (scount + Pstdiff) % 8;
        dnextcount = dscount + 1;
        if (dnextcount > 7) dnextcount = 0;
        rdiff = ifperiod * (fsequence[dnextcount] - fsequence[dscount]);
        lfor = fsequence[dscount];
      }

      ftcount = (f32)tcount;

      lmod = lfol + ldiff * ftcount;
      rmod = lfor + rdiff * ftcount;

      if (Pamplitude)
      {
        ldbl = lmod * (1.0f - cosf(D_PI * ifperiod * ftcount));
        ldbr = rmod * (1.0f - cosf(D_PI * ifperiod * ftcount));

        outBlock[0][i] = ldbl * inBlock[0][i];
        outBlock[1][i] = ldbr * inBlock[1][i];
      }

      const f32 frl = MINFREQ + MAXFREQ * lmod;
      const f32 frr = MINFREQ + MAXFREQ * rmod;

      if (i % 8 == 0)
      {
        filterl.setfreq_and_q(frl, fq);
        filterr.setfreq_and_q(frr, fq);
      }

      outBlock[0][i] = filterl.filterout_s(outBlock[0][i]);
      outBlock[1][i] = filterr.filterout_s(outBlock[1][i]);
    }
    break;
  case 1:		//Up Down
    for (i = 0; i < blockSize; i++)  //Maintain sequenced modulator
    {
      if (++tcount >= intperiod)
      {
        tcount = 0;
        scount++;
        if (scount > 7)
          scount = 0;  //reset to beginning of sequence buffer
        dscount = (scount + Pstdiff) % 8;
      }

      ftcount = PI_ * ifperiod * (f32)(tcount);

      lmod = sinf(ftcount) * fsequence[scount];
      rmod = sinf(ftcount) * fsequence[dscount];

      if (Pamplitude)
      {
        ldbl = lmod * (1.0f - cosf(2.0f * ftcount));
        ldbr = rmod * (1.0f - cosf(2.0f * ftcount));

        outBlock[0][i] = ldbl * inBlock[0][i];
        outBlock[1][i] = ldbr * inBlock[1][i];
      }

      const f32 frl = MINFREQ + MAXFREQ * lmod;
      const f32 frr = MINFREQ + MAXFREQ * rmod;

      if (i % 8 == 0)
      {
        filterl.setfreq_and_q(frl, fq);
        filterr.setfreq_and_q(frr, fq);
      }

      outBlock[0][i] = filterl.filterout_s(outBlock[0][i]);
      outBlock[1][i] = filterr.filterout_s(outBlock[1][i]);
    }
    break;
  case 2:  //Stepper
    for (i = 0; i < blockSize; i++)  //Maintain sequenced modulator
    {
      if (++tcount >= intperiod)
      {
        tcount = 0;
        scount++;
        if (scount > 7)
          scount = 0;  //reset to beginning of sequence buffer
        dscount = (scount + Pstdiff) % 8;
      }

      lmod = fsequence[scount];
      rmod = fsequence[dscount];

      lmod = modfilterl.filterout_s(lmod);
      rmod = modfilterr.filterout_s(rmod);

      if (Pamplitude)
      {
        ldbl = seqpower * lmod;
        ldbr = seqpower * rmod;

        outBlock[0][i] = ldbl * inBlock[0][i];
        outBlock[1][i] = ldbr * inBlock[1][i];
      }

      const f32 frl = MINFREQ + lmod * MAXFREQ;
      const f32 frr = MINFREQ + rmod * MAXFREQ;

      if (i % 8 == 0)
      {
        filterl.setfreq_and_q(frl, fq);
        filterr.setfreq_and_q(frr, fq);
      }

      outBlock[0][i] = filterl.filterout_s(outBlock[0][i]);
      outBlock[1][i] = filterr.filterout_s(outBlock[1][i]);
    }
    break;
  case 3:  //Shifter
    nextcount = scount + 1;
    if (nextcount > 7)
      nextcount = 0;
    ldiff = ifperiod * (fsequence[nextcount] - fsequence[scount]);
    lfol = fsequence[scount];

    if (DS_state != 0)
    {
      memcpy(templ, inBlock[0], sizeof(f32) * blockSize);
      memcpy(tempr, inBlock[1], sizeof(f32) * blockSize);
      f32* temp[2] = { templ, tempr };
      U_Resample.processBlock(temp, inBlock, blockSize, u_up);
    }

    for (i = 0; i < nPERIOD; i++)  //Maintain sequenced modulator
    {
      if (++tcount >= intperiod)
      {
        tcount = 0;
        scount++;
        if (scount > 7)
          scount = 0;  //reset to beginning of sequence buffer

        nextcount = scount + 1;
        if (nextcount > 7) nextcount = 0;
        ldiff = ifperiod * (fsequence[nextcount] - fsequence[scount]);
        lfol = fsequence[scount];
      }

      ftcount = (f32)tcount;

      lmod = 1.0f + lfol + ldiff * ftcount;

      if (Pamplitude) lmod = 1.0f - (lfol + ldiff * ftcount) * .5f;

      outi[i] = (inBlock[0][i] + inBlock[1][i]) * 0.5f;
      if (outi[i] > 1.0)
        outi[i] = 1.0f;
      if (outi[i] < -1.0)
        outi[i] = -1.0f;
    }

    PS->ratio = lmod;
    PS->smbPitchShift(PS->ratio, nPERIOD, window, hq, nfSAMPLE_RATE, outi, outo);

    memcpy(templ, outo, sizeof(f32) * nPERIOD);
    memcpy(tempr, outo, sizeof(f32) * nPERIOD);

    if (DS_state != 0)
    {
      f32* temp[2] = { templ, tempr };
      D_Resample.processBlock(temp, outBlock, nPERIOD, u_down);
    }
    else
    {
      memcpy(outBlock[0], templ, sizeof(f32) * blockSize);
      memcpy(outBlock[1], tempr, sizeof(f32) * blockSize);
    }
    break;
  case 4:      //Tremor
    nextcount = scount + 1;
    if (nextcount > 7)
      nextcount = 0;
    ldiff = ifperiod * (fsequence[nextcount] - fsequence[scount]);
    lfol = fsequence[scount];

    dscount = (scount + Pstdiff) % 8;
    dnextcount = dscount + 1;
    if (dnextcount > 7)
      dnextcount = 0;
    rdiff = ifperiod * (fsequence[dnextcount] - fsequence[dscount]);
    lfor = fsequence[dscount];

    for (i = 0; i < blockSize; i++)  //Maintain sequenced modulator
    {
      if (++tcount >= intperiod)
      {
        tcount = 0;
        scount++;
        if (scount > 7) scount = 0;  //reset to beginning of sequence buffer

        nextcount = scount + 1;
        if (nextcount > 7) nextcount = 0;
        ldiff = ifperiod * (fsequence[nextcount] - fsequence[scount]);
        lfol = fsequence[scount];

        dscount = (scount + Pstdiff) % 8;
        dnextcount = dscount + 1;
        if (dnextcount > 7) dnextcount = 0;
        rdiff = ifperiod * (fsequence[dnextcount] - fsequence[dscount]);
        lfor = fsequence[dscount];
      }
      //Process Amplitude modulation
      if (Pamplitude)
      {
        ftcount = (f32)tcount;
        lmod = lfol + ldiff * ftcount;
        rmod = lfor + rdiff * ftcount;

        ldbl = seqpower * lmod * (1.0f - cosf(D_PI * ifperiod * ftcount));
        ldbr = seqpower * rmod * (1.0f - cosf(D_PI * ifperiod * ftcount));

        outBlock[0][i] = ldbl * inBlock[0][i];
        outBlock[1][i] = ldbr * inBlock[1][i];
      }
      else
      {
        lmod = seqpower * fsequence[scount];
        rmod = seqpower * fsequence[dscount];
        lmod = modfilterl.filterout_s(lmod);
        rmod = modfilterr.filterout_s(rmod);

        outBlock[0][i] = lmod * inBlock[0][i];
        outBlock[1][i] = rmod * inBlock[1][i];
      }
    }
    break;
  case 5:  //Arpegiator
    lfol = floorf(fsequence[scount] * 12.75f);

    if (DS_state != 0)
    {
      memcpy(templ, inBlock[0], sizeof(f32) * blockSize);
      memcpy(tempr, inBlock[1], sizeof(f32) * blockSize);
      f32* temp[2] = { templ, tempr };
      U_Resample.processBlock(temp, inBlock, blockSize, u_up);
    }

    for (i = 0; i < nPERIOD; i++)  //Maintain sequenced modulator
    {

      if (++tcount >= intperiod)
      {
        tcount = 0;
        scount++;
        if (scount > 7) scount = 0;  //reset to beginning of sequence buffer
        lfol = floorf(fsequence[scount] * 12.75f);
      }

      lmod = powf(2.0f, lfol / 12.0f);

      if (Pamplitude) lmod = powf(2.0f, -lfol / 12.0f);

      outi[i] = (inBlock[0][i] + inBlock[1][i]) * 0.5f;
      if (outi[i] > 1.0)
        outi[i] = 1.0f;
      if (outi[i] < -1.0)
        outi[i] = -1.0f;
    }

    PS->ratio = lmod;
    PS->smbPitchShift(PS->ratio, nPERIOD, window, hq, nfSAMPLE_RATE, outi, outo);

    memcpy(templ, outo, sizeof(f32) * nPERIOD);
    memcpy(tempr, outo, sizeof(f32) * nPERIOD);

    if (DS_state != 0)
    {
      f32* temp[2] = { templ, tempr };
      D_Resample.processBlock(temp, outBlock, nPERIOD, u_down);
    }
    else
    {
      memcpy(outBlock[0], templ, sizeof(f32) * nPERIOD);
      memcpy(outBlock[1], tempr, sizeof(f32) * nPERIOD);
    }
    break;
  case 6:  //Chorus
    nextcount = scount + 1;
    if (nextcount > 7) nextcount = 0;
    ldiff = ifperiod * (fsequence[nextcount] - fsequence[scount]);
    lfol = fsequence[scount];

    if (DS_state != 0)
    {
      memcpy(templ, inBlock[0], sizeof(f32) * blockSize);
      memcpy(tempr, inBlock[1], sizeof(f32) * blockSize);
      memcpy(tempr, inBlock[1], sizeof(f32) * blockSize);
      f32* temp[2] = { templ, tempr };
      U_Resample.processBlock(temp, inBlock, blockSize, u_up);
    }

    for (i = 0; i < nPERIOD; i++)  //Maintain sequenced modulator
    {

      if (++tcount >= intperiod)
      {
        tcount = 0;
        scount++;
        if (scount > 7) scount = 0;  //reset to beginning of sequence buffer

        nextcount = scount + 1;
        if (nextcount > 7) nextcount = 0;
        ldiff = ifperiod * (fsequence[nextcount] - fsequence[scount]);
        lfol = fsequence[scount];
      }

      ftcount = (f32)tcount;

      lmod = 1.0f + (lfol + ldiff * ftcount) * .03f;
      if (Pamplitude) lmod = 1.0f - (lfol + ldiff * ftcount) * .03f;

      outi[i] = (inBlock[0][i] + inBlock[1][i]) * 0.5f;
      if (outi[i] > 1.0)
        outi[i] = 1.0f;
      if (outi[i] < -1.0)
        outi[i] = -1.0f;
    }

    PS->ratio = lmod;
    PS->smbPitchShift(PS->ratio, nPERIOD, window, hq, nfSAMPLE_RATE, outi, outo);

    if (Pstdiff == 1)
    {
      for (i = 0; i < nPERIOD; i++)
      {
        templ[i] = inBlock[0][i] - inBlock[1][i] + outo[i];
        tempr[i] = inBlock[0][i] - inBlock[1][i] + outo[i];
      }
    }
    else
    {
      if (Pstdiff == 2)
      {
        for (i = 0; i < nPERIOD; i++)
        {
          templ[i] = outo[i] * (1.0f - panning);
          tempr[i] = outo[i] * panning;
        }
      }
      else
      {
        memcpy(templ, outo, sizeof(f32) * nPERIOD);
        memcpy(tempr, outo, sizeof(f32) * nPERIOD);
      }
    }

    if (DS_state != 0)
    {
      f32* temp[2] = { templ, tempr };
      D_Resample.processBlock(temp, outBlock, nPERIOD, u_down);
    }
    else
    {
      memcpy(outBlock[0], templ, sizeof(f32) * nPERIOD);
      memcpy(outBlock[1], tempr, sizeof(f32) * nPERIOD);
    }
    break;
    // here case 6:
    //
    // break;
  }
}

void Sequence::setranges(i32 value)
{
  switch (value)
  {
  case 1:              //typical for wahwah pedal
    MINFREQ = 450.0f;
    MAXFREQ = 2500.0f;
    return;
  case 2:
    MINFREQ = 150.0f;
    MAXFREQ = 4000.0f;
    return;
  case 3:
    MINFREQ = 40.0f;
    MAXFREQ = 800.0f;
    return;
  case 4:
    MINFREQ = 100.0f;
    MAXFREQ = 1600.0f;
    return;
  case 5:
    MINFREQ = 80.0f;
    MAXFREQ = 16000.0f;
    return;
  case 6:
    MINFREQ = 60.0f;
    MAXFREQ = 18000.0f;
    return;
  case 7:
    MINFREQ = 40.0f;
    MAXFREQ = 2200.0f;
    return;
  case 8:
    MINFREQ = 20.0f;
    MAXFREQ = 6000.0f;
    return;
  }
  ASSERT(false);
}

void Sequence::adjust(i32 DS, const i32 blockSize)
{
  DS_state = DS;

  switch (DS)
  {
  case 0:
    nPERIOD = blockSize;
    nSAMPLE_RATE = SAMPLE_RATE;
    nfSAMPLE_RATE = fSAMPLE_RATE;
    window = 2048;
    break;
  case 1:
    nPERIOD = lrintf(f32(blockSize) * 96000.0f / fSAMPLE_RATE);
    nSAMPLE_RATE = 96000;
    nfSAMPLE_RATE = 96000.0f;
    window = 2048;
    break;
  case 2:
    nPERIOD = lrintf(f32(blockSize) * 48000.0f / fSAMPLE_RATE);
    nSAMPLE_RATE = 48000;
    nfSAMPLE_RATE = 48000.0f;
    window = 2048;
    break;
  case 3:
    nPERIOD = lrintf(f32(blockSize) * 44100.0f / fSAMPLE_RATE);
    nSAMPLE_RATE = 44100;
    nfSAMPLE_RATE = 44100.0f;
    window = 2048;
    break;
  case 4:
    nPERIOD = lrintf(f32(blockSize) * 32000.0f / fSAMPLE_RATE);
    nSAMPLE_RATE = 32000;
    nfSAMPLE_RATE = 32000.0f;
    window = 2048;
    break;
  case 5:
    nPERIOD = lrintf(f32(blockSize) * 22050.0f / fSAMPLE_RATE);
    nSAMPLE_RATE = 22050;
    nfSAMPLE_RATE = 22050.0f;
    window = 1024;
    break;
  case 6:
    nPERIOD = lrintf(f32(blockSize) * 16000.0f / fSAMPLE_RATE);
    nSAMPLE_RATE = 16000;
    nfSAMPLE_RATE = 16000.0f;
    window = 1024;
    break;
  case 7:
    nPERIOD = lrintf(f32(blockSize) * 12000.0f / fSAMPLE_RATE);
    nSAMPLE_RATE = 12000;
    nfSAMPLE_RATE = 12000.0f;
    window = 512;
    break;
  case 8:
    nPERIOD = lrintf(f32(blockSize) * 8000.0f / fSAMPLE_RATE);
    nSAMPLE_RATE = 8000;
    nfSAMPLE_RATE = 8000.0f;
    window = 512;
    break;
  case 9:
    nPERIOD = lrintf(f32(blockSize) * 4000.0f / fSAMPLE_RATE);
    nSAMPLE_RATE = 4000;
    nfSAMPLE_RATE = 4000.0f;
    window = 256;
    break;
  }
  u_up = (f64)nPERIOD / (f64)blockSize;
  u_down = (f64)blockSize / (f64)nPERIOD;
}

void Sequence::settempo(i32 value)
{
  if ((Pmode == 3) || (Pmode == 5) || (Pmode == 6))
    fperiod = nfSAMPLE_RATE * 60.0f / (subdiv * (f32)value);
  else
    fperiod = fSAMPLE_RATE * 60.0f / (subdiv * (f32)value);  //number of samples before next value

  ifperiod = 1.0f / fperiod;
  intperiod = (i32)fperiod;
}

void Sequence::setpreset(i32 npreset)
{
  const i32 PRESET_SIZE = 15;
  i32 presets[][PRESET_SIZE] = {
    //Jumpy
    {20, 100, 10, 50, 25, 120, 60, 127, 0, 90, 40, 0, 0, 0, 3},
    //Stair Step
    {10, 20, 30, 50, 75, 90, 100, 127, 64, 90, 96, 0, 0, 2, 5},
    //Mild
    {20, 30, 10, 40, 25, 60, 100, 50, 0, 90, 40, 0, 0, 0, 4},
    //WahWah
    {11, 55, 15, 95, 12, 76, 11, 36, 30, 80, 110, 0, 4, 1, 2},
    //Filter Pan
    {28, 59, 94, 127, 120, 80, 50, 24, 64, 180, 107, 0, 3, 0, 8},
    //Stepper
    {30, 127, 30, 50, 80, 40, 110, 80, 0, 240, 95, 1, 1, 2, 2},
    //Shifter
    {0, 0, 127, 127, 0, 0, 127, 127, 64, 114, 64, 1, 0, 3, 3},
    //Tremor
    {30, 127, 30, 50, 80, 40, 110, 80, 0, 240, 95, 1, 1, 4, 2},
    //Boogie 
    {0, 40, 50, 60, 70, 60, 40, 0, 0, 220, 64, 0, 0, 5, 3},
    //Chorus
    {64, 30, 45, 20, 60, 25, 42, 15, 64, 120, 64, 0, 0, 6, 3}
  };

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

void Sequence::changepar(i32 npar, i32 value)
{
  switch (npar)
  {
  case 0:
  case 1:
  case 2:
  case 3:
  case 4:
  case 5:
  case 6:
  case 7:
    Psequence[npar] = value;
    fsequence[npar] = (f32)value / 127.0f;

    seqpower = 0.0f;
    for (i32 i = 0; i < 8; i++)  seqpower += fsequence[i];
    if (seqpower > 0.1f)
    {
      seqpower = 15.0f / seqpower;
      rndflag = 0;
    }

    {
      i32 testegg = 0;
      for (i32 i = 0; i < 8; i++)
        testegg += Psequence[i];
      if (testegg < 4)
      {
        seqpower = 5.0f;  //Easter egg
        rndflag = 1;
      }
    }
    return;
  case 8:
    Pvolume = value;
    outvolume = (f32)Pvolume / 127.0f;
    return;
  case 9:
    Ptempo = value;
    settempo(value);
    return;
  case 10:
    Pq = value;
    panning = ((f32)value + 64.0f) / 128.0f;
    fq = powf(60.0f, ((f32)value - 64.0f) / 64.0f);
    return;
  case 11:
    Pamplitude = value;
    return;
  case 12:
    Pstdiff = value;
    return;
  case 13:
    Pmode = value;
    settempo(Ptempo);
    return;
  case 14:
    Prange = value;
    setranges(Prange);
    return;
  }
  ASSERT(false);
}

i32 Sequence::getpar(i32 npar)
{
  switch (npar)
  {
  case 0:
  case 1:
  case 2:
  case 3:
  case 4:
  case 5:
  case 6:
  case 7:
    return Psequence[npar];
  case 8:
    return Pvolume;
  case 9:
    return Ptempo;
  case 10:
    return Pq;
  case 11:
    return Pamplitude;
  case 12:
    return Pstdiff;
  case 13:
    return Pmode;
  case 14:
    return Prange;
  }
  ASSERT(false);
  return 0;
}

#endif // SHR3D_SFX_CORE_RAKARRACK_LIBSAMPLERATE

#endif // SHR3D_SFX_CORE_RAKARRACK
