/*
  rakarrack - a guitar effects software

  Harmonizer.C  -  Harmonizer
  Copyright (C) 2008 Josep Andreu
  Author:  Josep Andreu

  Using Stephan M. Bernsee smbPitchShifter engine.

 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
 (version2)  along with this program; if not, write to the Free Software
 Foundation,
 Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA

*/


#include "Harmonizer.h"

#ifdef SHR3D_SFX_CORE_RAKARRACK

#ifdef SHR3D_SFX_CORE_RAKARRACK_LIBSAMPLERATE

#if 0

#include <string.h>

inline f32 r__ratio[3];

Harmonizer::Harmonizer(i32 Quality, i32 DS, i32 uq, i32 dq)
  : pl(6, 22000, 1, 0)
  , U_Resample(dq)
  , D_Resample(uq)
  , hq(Quality)
{
  adjust(DS, blockSize());

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

  memset(outi, 0, sizeof(f32) * nPERIOD);
  memset(outo, 0, sizeof(f32) * nPERIOD);

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

  PMIDI = 0;
  mira = 0;
  setpreset(0);

  cleanup();
}

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

void Harmonizer::applyfilters(f32* outBlock, const i32 blockSize)
{
  pl.filterout(outBlock, blockSize);
}

void Harmonizer::processBlock(f32** inBlock, f32** outBlock, const i32 blockSize)
{
  if ((DS_state != 0) && (Pinterval != 12))
  {
    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 (i32 i = 0; i < nPERIOD; i++)
  {
    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;
  }

  if ((PMIDI) || (PSELECT))
    PS->ratio = r__ratio[0];

  if (Pinterval != 12)
  {
    PS->smbPitchShift(PS->ratio, nPERIOD, window, hq, nfSAMPLE_RATE, outi, outo);

    if ((DS_state != 0) && (Pinterval != 12))
    {
      D_Resample.mono_out(outo, templ, nPERIOD, u_down, blockSize);
    }
    else
    {
      memcpy(templ, outo, sizeof(f32) * blockSize);
    }

    applyfilters(templ, blockSize);

    for (i32 i = 0; i < blockSize; i++)
    {
      outBlock[0][i] = templ[i] * gain * panning;
      outBlock[1][i] = templ[i] * gain * (1.0f - panning);
    }
  }
}

void Harmonizer::setvolume(i32 value)
{
  this->Pvolume = value;
  outvolume = (f32)Pvolume / 127.0f;
}

void Harmonizer::setpanning(i32 value)
{
  this->Ppan = value;
  panning = (f32)Ppan / 127.0f;
}

void Harmonizer::setgain(i32 value)
{
  this->Pgain = value;
  gain = (f32)Pgain / 127.0f;
  gain *= 2.0;
}

void Harmonizer::setinterval(i32 value)
{
  this->Pinterval = value;
  interval = (f32)Pinterval - 12.0f;
  PS->ratio = powf(2.0f, interval / 12.0f);
  if (Pinterval % 12 == 0)
    mira = 0;
  else
    mira = 1;
}

void Harmonizer::fsetfreq(i32 value)
{
  fPfreq = value;
  f32 tmp = (f32)value;
  pl.setfreq(tmp);
}

void Harmonizer::fsetgain(i32 value)
{
  this->fPgain = value;
  const f32 tmp = 30.0f * ((f32)value - 64.0f) / 64.0f;
  pl.setgain(tmp);
}

void Harmonizer::fsetq(i32 value)
{
  this->fPq = value;
  const f32 tmp = powf(30.0f, ((f32)value - 64.0f) / 64.0f);
  pl.setq(tmp);
}

void Harmonizer::setMIDI(i32 value)
{
  this->PMIDI = value;
}

void Harmonizer::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 Harmonizer::setpreset(i32 npreset)
{
  const i32 PRESET_SIZE = 11;
  i32 presets[][PRESET_SIZE] = {
    //Plain
    {64, 64, 64, 12, 6000, 0, 0, 0, 64, 64, 0},
    //Octavador
    {64, 64, 64, 0, 6000, 0, 0, 0, 64, 64, 0},
    //3mdown
    {64, 64, 64, 9, 6000, 0, 0, 0, 64, 64, 0}
  };

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

void Harmonizer::changepar(i32 npar, i32 value)
{
  switch (npar)
  {
  case 0:
    setvolume(value);
    return;
  case 1:
    setpanning(value);
    return;
  case 2:
    setgain(value);
    return;
  case 3:
    setinterval(value);
    return;
  case 4:
    fsetfreq(value);
    return;
  case 5:
    PSELECT = value;
    return;
  case 6:
    Pnote = value;
    return;
  case 7:
    Ptype = value;
    return;
  case 8:
    fsetgain(value);
    return;
  case 9:
    fsetq(value);
    return;
  case 10:
    setMIDI(value);
    return;
  }
  ASSERT(false);
}

i32 Harmonizer::getpar(i32 npar)
{
  switch (npar)
  {
  case 0:
    return Pvolume;
  case 1:
    return Ppan;
  case 2:
    return Pgain;
  case 3:
    return Pinterval;
  case 4:
    return fPfreq;
  case 5:
    return PSELECT;
  case 6:
    return Pnote;
  case 7:
    return Ptype;
  case 8:
    return fPgain;
  case 9:
    return fPq;
  case 10:
    return PMIDI;
  }
  ASSERT(false);
  return 0;
}

#endif

#endif // SHR3D_SFX_CORE_RAKARRACK_LIBSAMPLERATE

#endif // SHR3D_SFX_CORE_RAKARRACK
