1 // Copyright 2016 Emilie Gillet.
2 //
3 // Author: Emilie Gillet (emilie.o.gillet@gmail.com)
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 // THE SOFTWARE.
22 //
23 // See http://creativecommons.org/licenses/MIT/ for more information.
24 //
25 // -----------------------------------------------------------------------------
26 //
27 // Single waveform oscillator. Can optionally do audio-rate linear FM, with
28 // through-zero capabilities (negative frequencies).
29 
30 #ifndef PLAITS_DSP_OSCILLATOR_OSCILLATOR_H_
31 #define PLAITS_DSP_OSCILLATOR_OSCILLATOR_H_
32 
33 #include "stmlib/dsp/dsp.h"
34 #include "stmlib/dsp/parameter_interpolator.h"
35 #include "stmlib/dsp/polyblep.h"
36 
37 namespace plaits {
38 
39 enum OscillatorShape {
40   OSCILLATOR_SHAPE_IMPULSE_TRAIN,
41   OSCILLATOR_SHAPE_SAW,
42   OSCILLATOR_SHAPE_TRIANGLE,
43   OSCILLATOR_SHAPE_SLOPE,
44   OSCILLATOR_SHAPE_SQUARE,
45   OSCILLATOR_SHAPE_SQUARE_BRIGHT,
46   OSCILLATOR_SHAPE_SQUARE_DARK,
47   OSCILLATOR_SHAPE_SQUARE_TRIANGLE
48 };
49 
50 const float kMaxFrequency = 0.25f;
51 const float kMinFrequency = 0.000001f;
52 
53 class Oscillator {
54  public:
Oscillator()55   Oscillator() { }
~Oscillator()56   ~Oscillator() { }
57 
Init()58   void Init() {
59     phase_ = 0.5f;
60     next_sample_ = 0.0f;
61     lp_state_ = 1.0f;
62     hp_state_ = 0.0f;
63     high_ = true;
64 
65     frequency_ = 0.001f;
66     pw_ = 0.5f;
67   }
68 
69   template<OscillatorShape shape>
Render(float frequency,float pw,float * out,size_t size)70   void Render(float frequency, float pw, float* out, size_t size) {
71     Render<shape, false, false>(frequency, pw, NULL, out, size);
72   }
73 
74   template<OscillatorShape shape>
Render(float frequency,float pw,const float * fm,float * out,size_t size)75   void Render(
76       float frequency,
77       float pw,
78       const float* fm,
79       float* out,
80       size_t size) {
81     if (!fm) {
82       Render<shape, false, false>(frequency, pw, NULL, out, size);
83     } else {
84       Render<shape, true, true>(frequency, pw, fm, out, size);
85     }
86   }
87 
88   template<OscillatorShape shape, bool has_external_fm, bool through_zero_fm>
Render(float frequency,float pw,const float * external_fm,float * out,size_t size)89   void Render(
90       float frequency,
91       float pw,
92       const float* external_fm,
93       float* out,
94       size_t size) {
95 
96     if (!has_external_fm) {
97       if (!through_zero_fm) {
98         CONSTRAIN(frequency, kMinFrequency, kMaxFrequency);
99       } else {
100         CONSTRAIN(frequency, -kMaxFrequency, kMaxFrequency);
101       }
102       CONSTRAIN(pw, fabsf(frequency) * 2.0f, 1.0f - 2.0f * fabsf(frequency))
103     }
104 
105     stmlib::ParameterInterpolator fm(&frequency_, frequency, size);
106     stmlib::ParameterInterpolator pwm(&pw_, pw, size);
107 
108     float next_sample = next_sample_;
109 
110     while (size--) {
111       float this_sample = next_sample;
112       next_sample = 0.0f;
113 
114       float frequency = fm.Next();
115       if (has_external_fm) {
116         frequency *= (1.0f + *external_fm++);
117         if (!through_zero_fm) {
118           CONSTRAIN(frequency, kMinFrequency, kMaxFrequency);
119         } else {
120           CONSTRAIN(frequency, -kMaxFrequency, kMaxFrequency);
121         }
122       }
123       float pw = (shape == OSCILLATOR_SHAPE_SQUARE_TRIANGLE ||
124                   shape == OSCILLATOR_SHAPE_TRIANGLE) ? 0.5f : pwm.Next();
125       if (has_external_fm) {
126         CONSTRAIN(pw, fabsf(frequency) * 2.0f, 1.0f - 2.0f * fabsf(frequency))
127       }
128       phase_ += frequency;
129 
130       if (shape <= OSCILLATOR_SHAPE_SAW) {
131         if (phase_ >= 1.0f) {
132           phase_ -= 1.0f;
133           float t = phase_ / frequency;
134           this_sample -= stmlib::ThisBlepSample(t);
135           next_sample -= stmlib::NextBlepSample(t);
136         } else if (through_zero_fm && phase_ < 0.0f) {
137           float t = phase_ / frequency;
138           phase_ += 1.0f;
139           this_sample += stmlib::ThisBlepSample(t);
140           next_sample += stmlib::NextBlepSample(t);
141         }
142         next_sample += phase_;
143 
144         if (shape == OSCILLATOR_SHAPE_SAW) {
145           *out++ = 2.0f * this_sample - 1.0f;
146         } else {
147           lp_state_ += 0.25f * ((hp_state_ - this_sample) - lp_state_);
148           *out++ = 4.0f * lp_state_;
149           hp_state_ = this_sample;
150         }
151       } else if (shape <= OSCILLATOR_SHAPE_SLOPE) {
152         float slope_up = 2.0f;
153         float slope_down = 2.0f;
154         if (shape == OSCILLATOR_SHAPE_SLOPE) {
155           slope_up = 1.0f / (pw);
156           slope_down = 1.0f / (1.0f - pw);
157         }
158         if (high_ ^ (phase_ < pw)) {
159           float t = (phase_ - pw) / frequency;
160           float discontinuity = (slope_up + slope_down) * frequency;
161           if (through_zero_fm && frequency < 0.0f) {
162             discontinuity = -discontinuity;
163           }
164           this_sample -= stmlib::ThisIntegratedBlepSample(t) * discontinuity;
165           next_sample -= stmlib::NextIntegratedBlepSample(t) * discontinuity;
166           high_ = phase_ < pw;
167         }
168         if (phase_ >= 1.0f) {
169           phase_ -= 1.0f;
170           float t = phase_ / frequency;
171           float discontinuity = (slope_up + slope_down) * frequency;
172           this_sample += stmlib::ThisIntegratedBlepSample(t) * discontinuity;
173           next_sample += stmlib::NextIntegratedBlepSample(t) * discontinuity;
174           high_ = true;
175         } else if (through_zero_fm && phase_ < 0.0f) {
176           float t = phase_ / frequency;
177           phase_ += 1.0f;
178           float discontinuity = (slope_up + slope_down) * frequency;
179           this_sample -= stmlib::ThisIntegratedBlepSample(t) * discontinuity;
180           next_sample -= stmlib::NextIntegratedBlepSample(t) * discontinuity;
181           high_ = false;
182         }
183         next_sample += high_
184           ? phase_ * slope_up
185           : 1.0f - (phase_ - pw) * slope_down;
186         *out++ = 2.0f * this_sample - 1.0f;
187       } else {
188         if (high_ ^ (phase_ >= pw)) {
189           float t = (phase_ - pw) / frequency;
190           float discontinuity = 1.0f;
191           if (through_zero_fm && frequency < 0.0f) {
192             discontinuity = -discontinuity;
193           }
194           this_sample += stmlib::ThisBlepSample(t) * discontinuity;
195           next_sample += stmlib::NextBlepSample(t) * discontinuity;
196           high_ = phase_ >= pw;
197         }
198         if (phase_ >= 1.0f) {
199           phase_ -= 1.0f;
200           float t = phase_ / frequency;
201           this_sample -= stmlib::ThisBlepSample(t);
202           next_sample -= stmlib::NextBlepSample(t);
203           high_ = false;
204         } else if (through_zero_fm && phase_ < 0.0f) {
205           float t = phase_ / frequency;
206           phase_ += 1.0f;
207           this_sample += stmlib::ThisBlepSample(t);
208           next_sample += stmlib::NextBlepSample(t);
209           high_ = true;
210         }
211         next_sample += phase_ < pw ? 0.0f : 1.0f;
212 
213         if (shape == OSCILLATOR_SHAPE_SQUARE_TRIANGLE) {
214           const float integrator_coefficient = frequency * 0.0625f;
215           this_sample = 128.0f * (this_sample - 0.5f);
216           lp_state_ += integrator_coefficient * (this_sample - lp_state_);
217           *out++ = lp_state_;
218         } else if (shape == OSCILLATOR_SHAPE_SQUARE_DARK) {
219           const float integrator_coefficient = frequency * 2.0f;
220           this_sample = 4.0f * (this_sample - 0.5f);
221           lp_state_ += integrator_coefficient * (this_sample - lp_state_);
222           *out++ = lp_state_;
223         } else if (shape == OSCILLATOR_SHAPE_SQUARE_BRIGHT) {
224           const float integrator_coefficient = frequency * 2.0f;
225           this_sample = 2.0f * this_sample - 1.0f;
226           lp_state_ += integrator_coefficient * (this_sample - lp_state_);
227           *out++ = (this_sample - lp_state_) * 0.5f;
228         } else {
229           this_sample = 2.0f * this_sample - 1.0f;
230           *out++ = this_sample;
231         }
232       }
233     }
234     next_sample_ = next_sample;
235   }
236 
237  private:
238   // Oscillator state.
239   float phase_;
240   float next_sample_;
241   float lp_state_;
242   float hp_state_;
243   bool high_;
244 
245   // For interpolation of parameters.
246   float frequency_;
247   float pw_;
248 
249   DISALLOW_COPY_AND_ASSIGN(Oscillator);
250 };
251 
252 }  // namespace plaits
253 
254 #endif  // PLAITS_DSP_OSCILLATOR_OSCILLATOR_H_