/***************************************************/
/*! \class BandedWG
    \brief Banded waveguide modeling class.

    This class uses banded waveguide techniques to
    model a variety of sounds, including bowed
    bars, glasses, and bowls.  For more
    information, see Essl, G. and Cook, P. "Banded
    Waveguides: Towards Physical Modelling of Bar
    Percussion Instruments", Proceedings of the
    1999 International Computer Music Conference.

    Control Change Numbers: 
       - Bow Pressure = 2
       - Bow Motion = 4
       - Strike Position = 8 (not implemented)
       - Vibrato Frequency = 11
       - Gain = 1
       - Bow Velocity = 128
       - Set Striking = 64
       - Instrument Presets = 16
         - Uniform Bar = 0
         - Tuned Bar = 1
         - Glass Harmonica = 2
         - Tibetan Bowl = 3

    by Georg Essl, 1999--2004.
    Modified for STK 4.0 by Gary Scavone.
*/
/***************************************************/

#include "BandedWG.h"
#include "SKINImsg.h"
#include <cmath>

namespace stk {

BandedWG :: BandedWG( void )
{
  doPluck_ = true;

  bowTable_.setSlope( 3.0 );
  adsr_.setAllTimes( 0.02, 0.005, 0.9, 0.01 );

  frequency_ = 220.0;
  this->setPreset(0);

  bowPosition_ = 0;
  baseGain_ = 0.999;
  
  integrationConstant_ = 0.0;
  trackVelocity_ = false;

  bowVelocity_ = 0.0;
  bowTarget_ = 0.0;

  strikeAmp_ = 0.0;
}

BandedWG :: ~BandedWG( void )
{
}

void BandedWG :: clear( void )
{
  for ( int i=0; i<nModes_; i++ ) {
    delay_[i].clear();
    bandpass_[i].clear();
  }
}

void BandedWG :: setPreset( int preset )
{
  int i;
  switch ( preset ) {

  case 1: // Tuned Bar
    presetModes_ = 4;
    modes_[0] = (StkFloat) 1.0;
    modes_[1] = (StkFloat) 4.0198391420;
    modes_[2] = (StkFloat) 10.7184986595;
    modes_[3] = (StkFloat) 18.0697050938;

    for (i=0; i<presetModes_; i++) {
      basegains_[i] = (StkFloat) pow(0.999,(double) i+1);
      excitation_[i] = 1.0;
    }

    break;

  case 2: // Glass Harmonica
    presetModes_ = 5;
    modes_[0] = (StkFloat) 1.0;
    modes_[1] = (StkFloat) 2.32;
    modes_[2] = (StkFloat) 4.25;
    modes_[3] = (StkFloat) 6.63;
    modes_[4] = (StkFloat) 9.38;
    // modes_[5] = (StkFloat) 12.22;

    for (i=0; i<presetModes_; i++) {
      basegains_[i] = (StkFloat) pow(0.999,(double) i+1);
      excitation_[i] = 1.0;
    }
    /*
      baseGain_ = (StkFloat) 0.99999;
      for (i=0; i<presetModes_; i++) 
      gains_[i]= (StkFloat) pow(baseGain_, delay_[i].getDelay()+i);
    */

    break;
   
  case 3: // Tibetan Prayer Bowl (ICMC'02)
    presetModes_ = 12;
    modes_[0]=0.996108344;
    basegains_[0]=0.999925960128219;
    excitation_[0]=11.900357/10.0;
    modes_[1]=1.0038916562;
    basegains_[1]=0.999925960128219;
    excitation_[1]=11.900357/10.;
    modes_[2]=2.979178;
    basegains_[2]=0.999982774366897;
    excitation_[2]=10.914886/10.;
    modes_[3]=2.99329767;
    basegains_[3]=0.999982774366897;
    excitation_[3]=10.914886/10.;
    modes_[4]=5.704452;
    basegains_[4]=1.0; //0.999999999999999999987356406352;
    excitation_[4]=42.995041/10.;
    modes_[5]=5.704452;
    basegains_[5]=1.0; //0.999999999999999999987356406352;
    excitation_[5]=42.995041/10.;
    modes_[6]=8.9982;
    basegains_[6]=1.0; //0.999999999999999999996995497558225;
    excitation_[6]=40.063034/10.;
    modes_[7]=9.01549726;
    basegains_[7]=1.0; //0.999999999999999999996995497558225;
    excitation_[7]=40.063034/10.;
    modes_[8]=12.83303;
    basegains_[8]=0.999965497558225;
    excitation_[8]=7.063034/10.;
    modes_[9]=12.807382;
    basegains_[9]=0.999965497558225;
    excitation_[9]=7.063034/10.;
    modes_[10]=17.2808219;
    basegains_[10]=0.9999999999999999999965497558225;
    excitation_[10]=57.063034/10.;
    modes_[11]=21.97602739726;
    basegains_[11]=0.999999999999999965497558225;
    excitation_[11]=57.063034/10.;

    break;	

  default: // Uniform Bar
    presetModes_ = 4;
    modes_[0] = (StkFloat) 1.0;
    modes_[1] = (StkFloat) 2.756;
    modes_[2] = (StkFloat) 5.404;
    modes_[3] = (StkFloat) 8.933;

    for (i=0; i<presetModes_; i++) {
      basegains_[i] = (StkFloat) pow(0.9,(double) i+1);
      excitation_[i] = 1.0;
    }

    break;
  }

  nModes_ = presetModes_;
  setFrequency( frequency_ );
}

void BandedWG :: setFrequency( StkFloat frequency )
{
#if defined(_STK_DEBUG_)
  if ( frequency <= 0.0 ) {
    oStream_ << "BandedWG::setFrequency: parameter is less than or equal to zero!";
    handleError( StkError::WARNING ); return;
  }
#endif

  if (frequency > 1568.0) frequency = 1568.0;

  StkFloat radius;
  StkFloat base = Stk::sampleRate() / frequency;
  StkFloat length;
  for (int i=0; i<presetModes_; i++) {
    // Calculate the delay line lengths for each mode.
    length = (int)(base / modes_[i]);
    if ( length > 2.0) {
      delay_[i].setDelay( length );
      gains_[i]=basegains_[i];
      //	  gains_[i]=(StkFloat) pow(basegains_[i], 1/((StkFloat)delay_[i].getDelay()));
      //	  std::cerr << gains_[i];
    }
    else	{
      nModes_ = i;
      break;
    }
    //	std::cerr << std::endl;

    // Set the bandpass filter resonances
    radius = 1.0 - PI * 32 / Stk::sampleRate(); //frequency_ * modes_[i] / Stk::sampleRate()/32;
    if ( radius < 0.0 ) radius = 0.0;
    bandpass_[i].setResonance(frequency * modes_[i], radius, true);

    delay_[i].clear();
    bandpass_[i].clear();
  }

  //int olen = (int)(delay_[0].getDelay());
  //strikePosition_ = (int)(strikePosition_*(length/modes_[0])/olen);
}

void BandedWG :: setStrikePosition( StkFloat position )
{
  strikePosition_ = (int)(delay_[0].getDelay() * position / 2.0);
}

void BandedWG :: startBowing( StkFloat amplitude, StkFloat rate )
{
  adsr_.setAttackRate(rate);
  adsr_.keyOn();
  maxVelocity_ = 0.03 + (0.1 * amplitude); 
}

void BandedWG :: stopBowing( StkFloat rate )
{
  adsr_.setReleaseRate(rate);
  adsr_.keyOff();
}

void BandedWG :: pluck( StkFloat amplitude )
{
  int j;
  StkFloat min_len = delay_[nModes_-1].getDelay();
  for (int i=0; i<nModes_; i++)
    for(j=0; j<(int)(delay_[i].getDelay()/min_len); j++)
      delay_[i].tick( excitation_[i]*amplitude / nModes_ );

  //	strikeAmp_ += amplitude;
}

void BandedWG :: noteOn( StkFloat frequency, StkFloat amplitude )
{
  this->setFrequency( frequency );

  if ( doPluck_ )
    this->pluck( amplitude );
  else
    this->startBowing( amplitude, amplitude * 0.001 );
}

void BandedWG :: noteOff( StkFloat amplitude )
{
  if ( !doPluck_ )
    this->stopBowing( (1.0 - amplitude) * 0.005 );
}

StkFloat BandedWG :: tick( unsigned int )
{
  int k;

  StkFloat input = 0.0;
  if ( doPluck_ ) {
    input = 0.0;
    //  input = strikeAmp_/nModes_;
    //  strikeAmp_ = 0.0;
  }
  else {
    if ( integrationConstant_ == 0.0 )
      velocityInput_ = 0.0;
    else
      velocityInput_ = integrationConstant_ * velocityInput_;

    for ( k=0; k<nModes_; k++ )
      velocityInput_ += baseGain_ * delay_[k].lastOut();
      
    if ( trackVelocity_ )  {
      bowVelocity_ *= 0.9995;
      bowVelocity_ += bowTarget_;
      bowTarget_ *= 0.995;
    }
    else
      bowVelocity_ = adsr_.tick() * maxVelocity_;

    input = bowVelocity_ - velocityInput_;
    input = input * bowTable_.tick(input);
    input = input/(StkFloat)nModes_;
  }

  StkFloat data = 0.0;  
  for ( k=0; k<nModes_; k++ ) {
    bandpass_[k].tick(input + gains_[k] * delay_[k].lastOut());
    delay_[k].tick(bandpass_[k].lastOut());
    data += bandpass_[k].lastOut();
  }
  
  //lastFrame_[0] = data * nModes_;
  lastFrame_[0] = data * 4;
  return lastFrame_[0];
}

void BandedWG :: controlChange( int number, StkFloat value )
{
#if defined(_STK_DEBUG_)
  if ( value < 0 || ( number != 101 && value > 128.0 ) ) {
    oStream_ << "BandedWG::controlChange: value (" << value << ") is out of range!";
    handleError( StkError::WARNING ); return;
  }
#endif

  StkFloat normalizedValue = value * ONE_OVER_128;
  if (number == __SK_BowPressure_) { // 2
    if ( normalizedValue == 0.0 )
      doPluck_ = true;
    else {
      doPluck_ = false;
      bowTable_.setSlope( 10.0 - (9.0 * normalizedValue));
    }
  }
  else if (number == 4)	{ // 4
    if ( !trackVelocity_ ) trackVelocity_ = true;
    bowTarget_ += 0.005 * (normalizedValue - bowPosition_);
    bowPosition_ = normalizedValue;
    //adsr_.setTarget(bowPosition_);
  }
  else if (number == 8) // 8
    this->setStrikePosition( normalizedValue );
  else if (number == __SK_AfterTouch_Cont_) { // 128
    //bowTarget_ += 0.02 * (normalizedValue - bowPosition_);
    //bowPosition_ = normalizedValue;
    if ( trackVelocity_ ) trackVelocity_ = false;
    maxVelocity_ = 0.13 * normalizedValue; 
    adsr_.setTarget(normalizedValue);
  }      
  else if (number == __SK_ModWheel_) { // 1
    //    baseGain_ = 0.9989999999 + (0.001 * normalizedValue );
	  baseGain_ = 0.8999999999999999 + (0.1 * normalizedValue);
    //	std::cerr << "Yuck!" << std::endl;
    for (int i=0; i<nModes_; i++)
      gains_[i]=(StkFloat) basegains_[i]*baseGain_;
    //      gains_[i]=(StkFloat) pow(baseGain_, (int)((StkFloat)delay_[i].getDelay()+i));
  }
  else if (number == __SK_ModFrequency_) // 11
    integrationConstant_ = normalizedValue;
  else if (number == __SK_Sustain_)	{ // 64
    if (value < 65) doPluck_ = true;
    else doPluck_ = false;
  }
  else if (number == __SK_Portamento_)	{ // 65
    if (value < 65) trackVelocity_ = false;
    else trackVelocity_ = true;
  }
  else if (number == __SK_ProphesyRibbon_) // 16
    this->setPreset((int) value);
#if defined(_STK_DEBUG_)
  else {
    oStream_ << "BandedWG::controlChange: undefined control number (" << number << ")!";
    handleError( StkError::WARNING );
  }
#endif
}

} // stk namespace
