/*
  VaryBand.C - Vary Band Volumen effect

  ZynAddSubFX - a software synthesizer
  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 "VaryBand.h"

#ifdef SHR3D_SFX_CORE_RAKARRACK

#include "../common/AnalogFilter.h"

#include <string.h>

/*
 * Waveshape (this is called by OscilGen::waveshape and Distorsion::process)
 */

VaryBand::VaryBand()
  : lpf1l(2, 500.0f, .7071f, 0)
  , lpf1r(2, 500.0f, .7071f, 0)
  , hpf1l(3, 500.0f, .7071f, 0)
  , hpf1r(3, 500.0f, .7071f, 0)
  , lpf2l(2, 2500.0f, .7071f, 0)
  , lpf2r(2, 2500.0f, .7071f, 0)
  , hpf2l(3, 2500.0f, .7071f, 0)
  , hpf2r(3, 2500.0f, .7071f, 0)
  , lpf3l(2, 5000.0f, .7071f, 0)
  , lpf3r(2, 5000.0f, .7071f, 0)
  , hpf3l(3, 5000.0f, .7071f, 0)
  , hpf3r(3, 5000.0f, .7071f, 0)
{
  //default values
  coeff = 1.0f / (f32)blockSize();

  setpreset(0);
  cleanup();
}

void VaryBand::cleanup()
{
  lpf1l.cleanup();
  hpf1l.cleanup();
  lpf1r.cleanup();
  hpf1r.cleanup();
  lpf2l.cleanup();
  hpf2l.cleanup();
  lpf2r.cleanup();
  hpf2r.cleanup();
  lpf3l.cleanup();
  hpf3l.cleanup();
  lpf3r.cleanup();
  hpf3r.cleanup();
}

void VaryBand::processBlock(const f32* const* inBlock, f32** outBlock, const i32 blockSize)
{
  memcpy(lowl, inBlock[0], sizeof(f32) * blockSize);
  memcpy(midll, inBlock[0], sizeof(f32) * blockSize);
  memcpy(midhl, inBlock[0], sizeof(f32) * blockSize);
  memcpy(highl, inBlock[0], sizeof(f32) * blockSize);

  lpf1l.filterout(lowl, blockSize);
  hpf1l.filterout(midll, blockSize);
  lpf2l.filterout(midll, blockSize);
  hpf2l.filterout(midhl, blockSize);
  lpf3l.filterout(midhl, blockSize);
  hpf3l.filterout(highl, blockSize);

  memcpy(lowr, inBlock[1], sizeof(f32) * blockSize);
  memcpy(midlr, inBlock[1], sizeof(f32) * blockSize);
  memcpy(midhr, inBlock[1], sizeof(f32) * blockSize);
  memcpy(highr, inBlock[1], sizeof(f32) * blockSize);

  lpf1r.filterout(lowr, blockSize);
  hpf1r.filterout(midlr, blockSize);
  lpf2r.filterout(midlr, blockSize);
  hpf2r.filterout(midhr, blockSize);
  lpf3r.filterout(midhr, blockSize);
  hpf3r.filterout(highr, blockSize);

  lfo1.effectlfoout(lfo1l, lfo1r);
  lfo2.effectlfoout(lfo2l, lfo2r);

  d1 = (lfo1l - v1l) * coeff;
  d2 = (lfo1r - v1r) * coeff;
  d3 = (lfo2l - v2l) * coeff;
  d4 = (lfo2r - v2r) * coeff;

  for (i32 i = 0; i < blockSize; i++)
  {
    setCombi(Pcombi);

    outBlock[0][i] = lowl[i] * volL + midll[i] * volML + midhl[i] * volMH + highl[i] * volH;
    outBlock[1][i] = lowr[i] * volLr + midlr[i] * volMLr + midhr[i] * volMHr + highr[i] * volHr;
  }
}

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

void VaryBand::setCombi(i32 value)
{
  v1l += d1;
  v1r += d2;
  v2l += d3;
  v2r += d4;

  switch (value)
  {
  case 0:
    volL = v1l;
    volLr = v1r;
    volML = v1l;
    volMLr = v1r;
    volMH = v2l;
    volMHr = v2r;
    volH = v2l;
    volHr = v2r;
    return;
  case 1:
    volL = v1l;
    volLr = v1r;
    volML = v2l;
    volMLr = v2r;
    volMH = v2l;
    volMHr = v2r;
    volH = v1l;
    volHr = v1r;
    return;
  case 2:
    volL = v1l;
    volLr = v1r;
    volML = v2l;
    volMLr = v2r;
    volMH = v1l;
    volMHr = v1r;
    volH = v2l;
    volHr = v2r;
    return;
  case 3:
    volL = 1.0f;
    volLr = 1.0f;
    volML = v1l;
    volMLr = v1r;
    volMH = v1l;
    volMHr = v1r;
    volH = 1.0f;
    volHr = 1.0f;
    return;
  case 4:
    volL = 1.0f;
    volLr = 1.0f;
    volML = v1l;
    volMLr = v1r;
    volMH = v2l;
    volMHr = v2r;
    volH = 1.0f;
    volHr = 1.0f;
    return;
  case 5:
    volL = 0.0f;
    volLr = 0.0f;
    volML = v1l;
    volMLr = v1r;
    volMH = v1l;
    volMHr = v1r;
    volH = 0.0f;
    volHr = 0.0f;
    return;
  case 6:
    volL = 0.0f;
    volLr = 0.0f;
    volML = v1l;
    volMLr = v1r;
    volMH = v2l;
    volMHr = v2r;
    volH = 0.0f;
    volHr = 0.0f;
    return;
  case 7:
    volL = v1l;
    volLr = v1r;
    volML = 1.0f;
    volMLr = 1.0f;
    volMH = 1.0f;
    volMHr = 1.0f;
    volH = v1l;
    volHr = v1r;
    return;
  case 8:
    volL = v1l;
    volLr = v1r;
    volML = 1.0f;
    volMLr = 1.0f;
    volMH = 1.0f;
    volMHr = 1.0f;
    volH = v2l;
    volHr = v2r;
    return;
  case 9:
    volL = v1l;
    volLr = v1r;
    volML = 0.0f;
    volMLr = 0.0f;
    volMH = 0.0f;
    volMHr = 0.0f;
    volH = v1l;
    volHr = v1r;
    return;
  case 10:
    volL = v1l;
    volLr = v1r;
    volML = 0.0f;
    volMLr = 0.0f;
    volMH = 0.0f;
    volMHr = 0.0f;
    volH = v2l;
    volHr = v2r;
    return;
  }
  ASSERT(false);
}

void VaryBand::setCross1(i32 value)
{
  Cross1 = value;
  lpf1l.setfreq((f32)value);
  lpf1r.setfreq((f32)value);
  hpf1l.setfreq((f32)value);
  hpf1r.setfreq((f32)value);
}

void VaryBand::setCross2(i32 value)
{
  Cross2 = value;
  hpf2l.setfreq((f32)value);
  hpf2r.setfreq((f32)value);
  lpf2l.setfreq((f32)value);
  lpf2r.setfreq((f32)value);
}

void VaryBand::setCross3(i32 value)
{
  Cross3 = value;
  hpf3l.setfreq((f32)value);
  hpf3r.setfreq((f32)value);
  lpf3l.setfreq((f32)value);
  lpf3r.setfreq((f32)value);
}

void VaryBand::setpreset(i32 npreset)
{
  const i32 PRESET_SIZE = 11;
  i32 presets[][PRESET_SIZE] = {
    //Vary1
    {0, 40, 0, 64, 80, 0, 0, 500, 2500, 5000, 0},
    //Vary2
    {0, 80, 0, 64, 40, 0, 0, 120, 600, 2300, 1},
    //Vary3
    {0, 120, 0, 64, 40, 0, 0, 800, 2300, 5200, 2}
  };

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

  cleanup();
}

void VaryBand::changepar(i32 npar, i32 value)
{
  switch (npar)
  {
  case 0:
    setvolume(value);
    return;
  case 1:
    lfo1.Pfreq = value;
    lfo1.updateparams();
    return;
  case 2:
    lfo1.PLFOtype = value;
    lfo1.updateparams();
    return;
  case 3:
    lfo1.Pstereo = value;
    lfo1.updateparams();
    return;
  case 4:
    lfo2.Pfreq = value;
    lfo2.updateparams();
    return;
  case 5:
    lfo2.PLFOtype = value;
    lfo2.updateparams();
    return;
  case 6:
    lfo2.Pstereo = value;
    lfo2.updateparams();
    return;
  case 7:
    setCross1(value);
    return;
  case 8:
    setCross2(value);
    return;
  case 9:
    setCross3(value);
    return;
  case 10:
    Pcombi = value;
    return;
  }
  ASSERT(false);
}

i32 VaryBand::getpar(i32 npar)
{
  switch (npar)
  {
  case 0:
    return Pvolume;
  case 1:
    return lfo1.Pfreq;
  case 2:
    return lfo1.PLFOtype;
  case 3:
    return lfo1.Pstereo;
  case 4:
    return lfo2.Pfreq;
  case 5:
    return lfo2.PLFOtype;
  case 6:
    return lfo2.Pstereo;
  case 7:
    return Cross1;
  case 8:
    return Cross2;
  case 9:
    return Cross3;
  case 10:
    return Pcombi;
  }
  ASSERT(false);
  return 0;
}

#endif // SHR3D_SFX_CORE_RAKARRACK
