1 module stk.sinewave; 2 3 const uint TABLE_SIZE = 2048; 4 5 import stk.generator; 6 7 /***************************************************/ 8 /*! \class SineWave 9 \brief STK sinusoid oscillator class. 10 11 This class computes and saves a static sine "table" that can be 12 shared by multiple instances. It has an interface similar to the 13 WaveLoop class but inherits from the Generator class. Output 14 values are computed using linear interpolation. 15 16 The "table" length, set in SineWave.h, is 2048 samples by default. 17 18 by Perry R. Cook and Gary P. Scavone, 1995--2016. 19 */ 20 /***************************************************/ 21 22 class SineWave : Generator 23 { 24 public: 25 //! Default constructor. 26 this() { 27 if ( table_.empty() ) { 28 table_.resize( TABLE_SIZE + 1, 1 ); 29 StkFloat temp = 1.0 / TABLE_SIZE; 30 for ( uint i=0; i<=TABLE_SIZE; i++ ) 31 table_[i] = sin( TWO_PI * i * temp ); 32 } 33 Stk.addSampleRateAlert( this ); 34 } 35 36 //! Class destructor. 37 ~this() { 38 Stk.removeSampleRateAlert( this ); 39 } 40 41 //! Clear output and reset time pointer to zero. 42 void reset() { 43 time_ = 0.0; 44 lastFrame_[0] = 0; 45 } 46 47 //! Set the data read rate in samples. The rate can be negative. 48 /*! 49 If the rate value is negative, the data is read in reverse order. 50 */ 51 void setRate( StkFloat rate ) { rate_ = rate; }; 52 53 //! Set the data interpolation rate based on a looping frequency. 54 /*! 55 This function determines the interpolation rate based on the file 56 size and the current Stk::sampleRate. The \e frequency value 57 corresponds to file cycles per second. The frequency can be 58 negative, in which case the loop is read in reverse order. 59 */ 60 void setFrequency( StkFloat frequency ) { 61 this.setRate( TABLE_SIZE * frequency / Stk.sampleRate() ); 62 } 63 64 //! Increment the read pointer by \e time in samples, modulo the table size. 65 void addTime( StkFloat time ) { 66 // Add an absolute time in samples. 67 time_ += time; 68 } 69 70 //! Increment the read pointer by a normalized \e phase value. 71 /*! 72 This function increments the read pointer by a normalized phase 73 value, such that \e phase = 1.0 corresponds to a 360 degree phase 74 shift. Positive or negative values are possible. 75 */ 76 void addPhase( StkFloat phase ) { 77 // Add a time in cycles (one cycle = TABLE_SIZE). 78 time_ += TABLE_SIZE * phase; 79 } 80 81 //! Add a normalized phase offset to the read pointer. 82 /*! 83 A \e phaseOffset = 1.0 corresponds to a 360 degree phase 84 offset. Positive or negative values are possible. 85 */ 86 void addPhaseOffset( StkFloat phaseOffset ) { 87 // Add a phase offset relative to any previous offset value. 88 time_ += ( phaseOffset - phaseOffset_ ) * TABLE_SIZE; 89 phaseOffset_ = phaseOffset; 90 } 91 92 //! Return the last computed output value. 93 StkFloat lastOut() const { return lastFrame_[0]; }; 94 95 //! Compute and return one output sample. 96 StkFloat tick() { 97 // Check limits of time address ... if necessary, recalculate modulo 98 // TABLE_SIZE. 99 while ( time_ < 0.0 ) 100 time_ += TABLE_SIZE; 101 while ( time_ >= TABLE_SIZE ) 102 time_ -= TABLE_SIZE; 103 104 iIndex_ = cast (uint) time_; 105 alpha_ = time_ - iIndex_; 106 StkFloat tmp = table_[ iIndex_ ]; 107 tmp += ( alpha_ * ( table_[ iIndex_ + 1 ] - tmp ) ); 108 109 // Increment time, which can be negative. 110 time_ += rate_; 111 112 lastFrame_[0] = tmp; 113 return lastFrame_[0]; 114 } 115 116 //! Fill a channel of the StkFrames object with computed outputs. 117 /*! 118 The \c channel argument must be less than the number of 119 channels in the StkFrames argument (the first channel is specified 120 by 0). However, range checking is only performed if _STK_DEBUG_ 121 is defined during compilation, in which case an out-of-range value 122 will trigger an StkError exception. 123 */ 124 ref StkFrames tick( ref StkFrames frames, uint channel = 0 ) { 125 StkFloat *samples = &frames[channel]; 126 StkFloat tmp = 0.0; 127 128 uint hop = frames.channels(); 129 for ( uint i=0; i < frames.frames(); i++, samples += hop ) { 130 131 // Check limits of time address ... if necessary, recalculate modulo 132 // TABLE_SIZE. 133 while ( time_ < 0.0 ) 134 time_ += TABLE_SIZE; 135 while ( time_ >= TABLE_SIZE ) 136 time_ -= TABLE_SIZE; 137 138 iIndex_ = cast(uint) time_; 139 alpha_ = time_ - iIndex_; 140 tmp = table_[ iIndex_ ]; 141 tmp += ( alpha_ * ( table_[ iIndex_ + 1 ] - tmp ) ); 142 *samples = tmp; 143 144 // Increment time, which can be negative. 145 time_ += rate_; 146 } 147 148 lastFrame_[0] = tmp; 149 return frames; 150 } 151 152 protected: 153 154 override void sampleRateChanged( StkFloat newRate, StkFloat oldRate ) { 155 if ( !ignoreSampleRateChange_ ) 156 this.setRate( oldRate * rate_ / newRate ); 157 } 158 159 static __gshared StkFrames table_; 160 StkFloat time_ = 0; 161 StkFloat rate_ = 1; 162 StkFloat phaseOffset_ = 0; 163 uint iIndex_; 164 StkFloat alpha_; 165 166 }