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 }