/*

  CompBand.C - 4 Bands Compressor

  Using Compressor and AnalogFilters by other authors.

  Based on artscompressor.cc by Matthias Kretz <kretz@kde.org>
  Stefan Westerfeld <stefan@space.twc.de>

  Modified by Ryan Billing & Josep Andreu

  ZynAddSubFX - a software synthesizer
  Copyright (C) 2002-2005 Nasca Octavian Paul
  Author: Nasca Octavian Paul

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

#ifdef SHR3D_SFX_CORE_RAKARRACK

#include <string.h>

CompBand::CompBand()
  : 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)
{
  CL.setpreset(5);
  CML.setpreset(5);
  CMH.setpreset(5);
  CH.setpreset(5);

  setpreset(0);
  cleanup();
}

void CompBand::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();
  CL.cleanup();
  CML.cleanup();
  CMH.cleanup();
  CH.cleanup();
}

void CompBand::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);

  f32* low[] = { lowl, lowr };
  f32* midl[] = { midll, midlr };
  f32* midh[] = { midhl, midhr };
  f32* high[] = { highl, highr };

  CL.processBlock(low, blockSize);
  CML.processBlock(midl, blockSize);
  CMH.processBlock(midh, blockSize);
  CH.processBlock(high, blockSize);

  for (i32 i = 0; i < blockSize; i++)
  {
    outBlock[0][i] = (lowl[i] + midll[i] + midhl[i] + highl[i]) * level;
    outBlock[1][i] = (lowr[i] + midlr[i] + midhr[i] + highr[i]) * level;
  }
}

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

void CompBand::setlevel(i32 value)
{
  Plevel = value;
  level = dB2rap(60.0f * (f32)value / 127.0f - 36.0f);
}

void CompBand::setratio(i32 ch, i32 value)
{
  switch (ch)
  {
  case 0:
    CL.changepar(2, value);
    break;
  case 1:
    CML.changepar(2, value);
    break;
  case 2:
    CMH.changepar(2, value);
    break;
  case 3:
    CH.changepar(2, value);
    break;
  }
}

void CompBand::setthres(i32 ch, i32 value)
{
  switch (ch)
  {
  case 0:
    CL.changepar(1, value);
    break;
  case 1:
    CML.changepar(1, value);
    break;
  case 2:
    CMH.changepar(1, value);
    break;
  case 3:
    CH.changepar(1, value);
    break;
  }
}

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

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

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

void CompBand::setpreset(i32 npreset)
{
  const i32 PRESET_SIZE = 13;
  i32 presets[][PRESET_SIZE] = {
    //Good Start
    {0, 16, 16, 16, 16, 0, 0, 0, 0, 1000, 5000, 10000, 48},

    //Loudness
    {0, 16, 2, 2, 4, -16, 24, 24, -8, 140, 1000, 5000, 48},

    //Loudness 2
    {64, 16, 2, 2, 2, -32, 24, 24, 24, 100, 1000, 5000, 48}
  };

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

  cleanup();
}

void CompBand::changepar(i32 npar, i32 value)
{
  switch (npar)
  {
  case 0:
    setvolume(value);
    break;
  case 1:
    PLratio = value;
    setratio(0, value);
    break;
  case 2:
    PMLratio = value;
    setratio(1, value);
    break;
  case 3:
    PMHratio = value;
    setratio(2, value);
    break;
  case 4:
    PHratio = value;
    setratio(3, value);
    break;
  case 5:
    PLthres = value;
    setthres(0, value);
    break;
  case 6:
    PMLthres = value;
    setthres(1, value);
    break;
  case 7:
    PMHthres = value;
    setthres(2, value);
    break;
  case 8:
    PHthres = value;
    setthres(3, value);
    break;
  case 9:
    setCross1(value);
    break;
  case 10:
    setCross2(value);
    break;
  case 11:
    setCross3(value);
    break;
  case 12:
    setlevel(value);
    break;
  }
}

i32 CompBand::getpar(i32 npar)
{
  switch (npar)
  {
  case 0:
    return (Pvolume);
    break;
  case 1:
    return (PLratio);
    break;
  case 2:
    return (PMLratio);
    break;
  case 3:
    return (PMHratio);
    break;
  case 4:
    return (PHratio);
    break;
  case 5:
    return (PLthres);
    break;
  case 6:
    return (PMLthres);
    break;
  case 7:
    return (PMHthres);
    break;
  case 8:
    return (PHthres);
    break;
  case 9:
    return (Cross1);
    break;
  case 10:
    return (Cross2);
    break;
  case 11:
    return (Cross3);
    break;
  case 12:
    return (Plevel);
    break;
  }
  ASSERT(false);
  return 0;
}

#endif // SHR3D_SFX_CORE_RAKARRACK
