/***************************************************/
/*! \class Mandolin
    \brief STK mandolin instrument model class.

    This class uses two "twang" models and "commuted
    synthesis" techniques to model a mandolin
    instrument.

    This is a digital waveguide model, making its
    use possibly subject to patents held by Stanford
    University, Yamaha, and others.  Commuted
    Synthesis, in particular, is covered by patents,
    granted, pending, and/or applied-for.  All are
    assigned to the Board of Trustees, Stanford
    University.  For information, contact the Office
    of Technology Licensing, Stanford University.

    Control Change Numbers: 
       - Body Size = 2
       - Pluck Position = 4
       - String Sustain = 11
       - String Detuning = 1
       - Microphone Position = 128

    by Perry R. Cook and Gary P. Scavone, 1995-2011.
*/
/***************************************************/

#include "Mandolin.h"
#include "SKINI.msg"

namespace stk {

Mandolin :: Mandolin( StkFloat lowestFrequency )
{
  if ( lowestFrequency <= 0.0 ) {
    oStream_ << "Mandolin::Mandolin: argument is less than or equal to zero!";
    handleError( StkError::FUNCTION_ARGUMENT );
  }

  // Concatenate the STK rawwave path to the rawwave files
  soundfile_[0].openFile( (Stk::rawwavePath() + "mand1.raw").c_str(), true );
  soundfile_[1].openFile( (Stk::rawwavePath() + "mand2.raw").c_str(), true );
  soundfile_[2].openFile( (Stk::rawwavePath() + "mand3.raw").c_str(), true );
  soundfile_[3].openFile( (Stk::rawwavePath() + "mand4.raw").c_str(), true );
  soundfile_[4].openFile( (Stk::rawwavePath() + "mand5.raw").c_str(), true );
  soundfile_[5].openFile( (Stk::rawwavePath() + "mand6.raw").c_str(), true );
  soundfile_[6].openFile( (Stk::rawwavePath() + "mand7.raw").c_str(), true );
  soundfile_[7].openFile( (Stk::rawwavePath() + "mand8.raw").c_str(), true );
  soundfile_[8].openFile( (Stk::rawwavePath() + "mand9.raw").c_str(), true );
  soundfile_[9].openFile( (Stk::rawwavePath() + "mand10.raw").c_str(), true );
  soundfile_[10].openFile( (Stk::rawwavePath() + "mand11.raw").c_str(), true );
  soundfile_[11].openFile( (Stk::rawwavePath() + "mand12.raw").c_str(), true );

  mic_ = 0;
  detuning_ = 0.995;
  pluckAmplitude_ = 0.5;

  strings_[0].setLowestFrequency( lowestFrequency );
  strings_[1].setLowestFrequency( lowestFrequency );
  this->setFrequency( 220.0 );
  this->setPluckPosition( 0.4 );
}

Mandolin :: ~Mandolin( void )
{
}

void Mandolin :: clear( void )
{
  strings_[0].clear();
  strings_[1].clear();
}

void Mandolin :: setPluckPosition( StkFloat position )
{
  if ( position < 0.0 || position > 1.0 ) {
    std::cerr << "Mandolin::setPluckPosition: position parameter out of range!";
    handleError( StkError::WARNING ); return;
  }

  strings_[0].setPluckPosition( position );
  strings_[1].setPluckPosition( position );
}

void Mandolin :: setDetune( StkFloat detune )
{
  if ( detune <= 0.0 ) {
    oStream_ << "Mandolin::setDeturn: parameter is less than or equal to zero!";
    handleError( StkError::WARNING ); return;
  }

  detuning_ = detune;
  strings_[1].setFrequency( frequency_ * detuning_ );
}

void Mandolin :: setBodySize( StkFloat size )
{
  // Scale the commuted body response by its sample rate (22050).
  StkFloat rate = size * 22050.0 / Stk::sampleRate();
  for ( int i=0; i<12; i++ )
    soundfile_[i].setRate( rate );
}

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

  frequency_ = frequency;
  strings_[0].setFrequency( frequency_ );
  strings_[1].setFrequency( frequency_ * detuning_ );
}

void Mandolin :: pluck( StkFloat amplitude )
{
  if ( amplitude < 0.0 || amplitude > 1.0 ) {
    oStream_ << "Mandolin::pluck: amplitude parameter out of range!";
    handleError( StkError::WARNING ); return;
  }

  soundfile_[mic_].reset();
  pluckAmplitude_ = amplitude;

  //strings_[0].setLoopGain( 0.97 + pluckAmplitude_ * 0.03 );
  //strings_[1].setLoopGain( 0.97 + pluckAmplitude_ * 0.03 );
}

void Mandolin :: pluck( StkFloat amplitude, StkFloat position )
{
  this->setPluckPosition( position );
  this->pluck( amplitude );
}

void Mandolin :: noteOn( StkFloat frequency, StkFloat amplitude )
{
  this->setFrequency( frequency );
  this->pluck( amplitude );
}

void Mandolin :: noteOff( StkFloat amplitude )
{
  if ( amplitude < 0.0 || amplitude > 1.0 ) {
    oStream_ << "Mandolin::noteOff: amplitude is out of range!";
    handleError( StkError::WARNING ); return;
  }

  //strings_[0].setLoopGain( 0.97 + (1 - amplitude) * 0.03 );
  //strings_[1].setLoopGain( 0.97 + (1 - amplitude) * 0.03 );
}

void Mandolin :: controlChange( int number, StkFloat value )
{
#if defined(_STK_DEBUG_)
  if ( Stk::inRange( value, 0.0, 128.0 ) == false ) {
    oStream_ << "Mandolin::controlChange: value (" << value << ") is out of range!";
    handleError( StkError::WARNING ); return;
  }
#endif

  StkFloat normalizedValue = value * ONE_OVER_128;
  if ( number == __SK_BodySize_ ) // 2
    this->setBodySize( normalizedValue * 2.0 );
  else if ( number == __SK_PickPosition_ ) // 4
    this->setPluckPosition( normalizedValue );
  else if ( number == __SK_StringDamping_ ) { // 11
    strings_[0].setLoopGain( 0.97 + (normalizedValue * 0.03) );
    strings_[1].setLoopGain( 0.97 + (normalizedValue * 0.03) );
  }
  else if ( number == __SK_StringDetune_ ) // 1
    this->setDetune( 1.0 - (normalizedValue * 0.1) );
  else if ( number == __SK_AfterTouch_Cont_ ) // 128
    mic_ = (int) (normalizedValue * 11.0);
#if defined(_STK_DEBUG_)
  else {
    oStream_ << "Mandolin::controlChange: undefined control number (" << number << ")!";
    handleError( StkError::WARNING );
  }
#endif
}

} // stk namespace
