f@0: #ifndef STK_SINEWAVE_H f@0: #define STK_SINEWAVE_H f@0: f@0: const unsigned long TABLE_SIZE = 2048; f@0: f@0: #include "Generator.h" f@0: f@0: namespace stk { f@0: f@0: /***************************************************/ f@0: /*! \class SineWave f@0: \brief STK sinusoid oscillator class. f@0: f@0: This class computes and saves a static sine "table" that can be f@0: shared by multiple instances. It has an interface similar to the f@0: WaveLoop class but inherits from the Generator class. Output f@0: values are computed using linear interpolation. f@0: f@0: The "table" length, set in SineWave.h, is 2048 samples by default. f@0: f@0: by Perry R. Cook and Gary P. Scavone, 1995--2014. f@0: */ f@0: /***************************************************/ f@0: f@0: class SineWave : public Generator f@0: { f@0: public: f@0: //! Default constructor. f@0: SineWave( void ); f@0: f@0: //! Class destructor. f@0: ~SineWave( void ); f@0: f@0: //! Clear output and reset time pointer to zero. f@0: void reset( void ); f@0: f@0: //! Set the data read rate in samples. The rate can be negative. f@0: /*! f@0: If the rate value is negative, the data is read in reverse order. f@0: */ f@0: void setRate( StkFloat rate ) { rate_ = rate; }; f@0: f@0: //! Set the data interpolation rate based on a looping frequency. f@0: /*! f@0: This function determines the interpolation rate based on the file f@0: size and the current Stk::sampleRate. The \e frequency value f@0: corresponds to file cycles per second. The frequency can be f@0: negative, in which case the loop is read in reverse order. f@0: */ f@0: void setFrequency( StkFloat frequency ); f@0: f@0: //! Increment the read pointer by \e time in samples, modulo the table size. f@0: void addTime( StkFloat time ); f@0: f@0: //! Increment the read pointer by a normalized \e phase value. f@0: /*! f@0: This function increments the read pointer by a normalized phase f@0: value, such that \e phase = 1.0 corresponds to a 360 degree phase f@0: shift. Positive or negative values are possible. f@0: */ f@0: void addPhase( StkFloat phase ); f@0: f@0: //! Add a normalized phase offset to the read pointer. f@0: /*! f@0: A \e phaseOffset = 1.0 corresponds to a 360 degree phase f@0: offset. Positive or negative values are possible. f@0: */ f@0: void addPhaseOffset( StkFloat phaseOffset ); f@0: f@0: //! Return the last computed output value. f@0: StkFloat lastOut( void ) const { return lastFrame_[0]; }; f@0: f@0: //! Compute and return one output sample. f@0: StkFloat tick( void ); f@0: f@0: //! Fill a channel of the StkFrames object with computed outputs. f@0: /*! f@0: The \c channel argument must be less than the number of f@0: channels in the StkFrames argument (the first channel is specified f@0: by 0). However, range checking is only performed if _STK_DEBUG_ f@0: is defined during compilation, in which case an out-of-range value f@0: will trigger an StkError exception. f@0: */ f@0: StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); f@0: f@0: protected: f@0: f@0: void sampleRateChanged( StkFloat newRate, StkFloat oldRate ); f@0: f@0: static StkFrames table_; f@0: StkFloat time_; f@0: StkFloat rate_; f@0: StkFloat phaseOffset_; f@0: unsigned int iIndex_; f@0: StkFloat alpha_; f@0: f@0: }; f@0: f@0: inline StkFloat SineWave :: tick( void ) f@0: { f@0: // Check limits of time address ... if necessary, recalculate modulo f@0: // TABLE_SIZE. f@0: while ( time_ < 0.0 ) f@0: time_ += TABLE_SIZE; f@0: while ( time_ >= TABLE_SIZE ) f@0: time_ -= TABLE_SIZE; f@0: f@0: iIndex_ = (unsigned int) time_; f@0: alpha_ = time_ - iIndex_; f@0: StkFloat tmp = table_[ iIndex_ ]; f@0: tmp += ( alpha_ * ( table_[ iIndex_ + 1 ] - tmp ) ); f@0: f@0: // Increment time, which can be negative. f@0: time_ += rate_; f@0: f@0: lastFrame_[0] = tmp; f@0: return lastFrame_[0]; f@0: } f@0: f@0: inline StkFrames& SineWave :: tick( StkFrames& frames, unsigned int channel ) f@0: { f@0: #if defined(_STK_DEBUG_) f@0: if ( channel >= frames.channels() ) { f@0: oStream_ << "SineWave::tick(): channel and StkFrames arguments are incompatible!"; f@0: handleError( StkError::FUNCTION_ARGUMENT ); f@0: } f@0: #endif f@0: f@0: StkFloat *samples = &frames[channel]; f@0: StkFloat tmp = 0.0; f@0: f@0: unsigned int hop = frames.channels(); f@0: for ( unsigned int i=0; i= TABLE_SIZE ) f@0: time_ -= TABLE_SIZE; f@0: f@0: iIndex_ = (unsigned int) time_; f@0: alpha_ = time_ - iIndex_; f@0: tmp = table_[ iIndex_ ]; f@0: tmp += ( alpha_ * ( table_[ iIndex_ + 1 ] - tmp ) ); f@0: *samples = tmp; f@0: f@0: // Increment time, which can be negative. f@0: time_ += rate_; f@0: } f@0: f@0: lastFrame_[0] = tmp; f@0: return frames; f@0: } f@0: f@0: } // stk namespace f@0: f@0: #endif f@0: