1 // Copyright 2014 Olivier Gillet.
2 //
3 // Author: Olivier Gillet (ol.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 // Oscillator.
28 
29 #include "warps/dsp/oscillator.h"
30 
31 #include "stmlib/dsp/parameter_interpolator.h"
32 #include "stmlib/utils/random.h"
33 
34 namespace warps {
35 
36 using namespace stmlib;
37 
38 const float kToFloat = 1.0f / 4294967296.0f;
39 const float kToUint32 = 4294967296.0f;
40 
41 
Init(float sample_rate)42 void Oscillator::Init(float sample_rate) {
43   one_hertz_ = 1.0f / sample_rate;
44 
45   next_sample_ = 0.0f;
46   phase_ = 0.0f;
47   phase_increment_ = 100.0f * one_hertz_;
48   hp_state_ = 0.0f;
49   lp_state_ = 0.0f;
50 
51   high_ = false;
52 
53   external_input_level_ = 0.0f;
54 
55   filter_.Init();
56 }
57 
Render(OscillatorShape shape,float note,float * modulation,float * out,size_t size)58 float Oscillator::Render(
59     OscillatorShape shape,
60     float note,
61     float* modulation,
62     float* out,
63     size_t size) {
64   return (this->*fn_table_[shape])(note, modulation, out, size);
65 }
66 
RenderSine(float note,float * modulation,float * out,size_t size)67 float Oscillator::RenderSine(
68     float note,
69     float* modulation,
70     float* out,
71     size_t size) {
72   float phase = phase_;
73   ParameterInterpolator phase_increment(
74       &phase_increment_,
75       midi_to_increment(note),
76       size);
77   while (size--) {
78     phase += phase_increment.Next();
79     if (phase >= 1.0f) {
80       phase -= 1.0f;
81     }
82     uint32_t modulated_phase = static_cast<uint32_t>(phase * kToUint32);
83     modulated_phase += static_cast<int32_t>(*modulation++ * kToUint32) * 4;
84     uint32_t integral = modulated_phase >> 22;
85     float fractional = static_cast<float>(modulated_phase << 10) * kToFloat;
86     float a = lut_sin[integral];
87     float b = lut_sin[integral + 1];
88     *out++ = a + (b - a) * fractional;
89   }
90   phase_ = phase;
91   return 1.0f;
92 }
93 
94 template<OscillatorShape shape>
RenderPolyblep(float note,float * modulation,float * out,size_t size)95 float Oscillator::RenderPolyblep(
96     float note,
97     float* modulation,
98     float* out,
99     size_t size) {
100   float phase = phase_;
101   ParameterInterpolator phase_increment(
102       &phase_increment_,
103       midi_to_increment(note),
104       size);
105 
106   float next_sample = next_sample_;
107   bool high = high_;
108   float lp_state = lp_state_;
109   float hp_state = hp_state_;
110 
111   while (size--) {
112     float this_sample = next_sample;
113     next_sample = 0.0f;
114 
115     float modulated_increment = phase_increment.Next() * (1.0f + *modulation++);
116 
117     if (modulated_increment <= 0.0f) {
118       modulated_increment = 1.0e-7;
119     }
120     phase += modulated_increment;
121 
122     if (shape == OSCILLATOR_SHAPE_TRIANGLE) {
123       if (!high && phase >= 0.5f) {
124         float t = (phase - 0.5f) / modulated_increment;
125         this_sample += ThisBlepSample(t);
126         next_sample += NextBlepSample(t);
127         high = true;
128       }
129       if (phase >= 1.0f) {
130         phase -= 1.0f;
131         float t = phase / modulated_increment;
132         this_sample -= ThisBlepSample(t);
133         next_sample -= NextBlepSample(t);
134         high = false;
135       }
136       const float integrator_coefficient = modulated_increment * 0.0625f;
137       next_sample += phase < 0.5f ? 0.0f : 1.0f;
138       this_sample = 128.0f * (this_sample - 0.5f);
139       lp_state += integrator_coefficient * (this_sample - lp_state);
140       *out++ = lp_state;
141     } else {
142       if (phase >= 1.0f) {
143         phase -= 1.0f;
144         float t = phase / modulated_increment;
145         this_sample -= ThisBlepSample(t);
146         next_sample -= NextBlepSample(t);
147       }
148       next_sample += phase;
149 
150       if (shape == OSCILLATOR_SHAPE_SAW) {
151         this_sample = this_sample * 2.0f - 1.0f;
152         // Slight roll-off of high frequencies - prevent high components near
153         // 48kHz that are not eliminated by the upsampling filter.
154         lp_state += 0.3f * (this_sample - lp_state);
155         *out++ = lp_state;
156       } else {
157         lp_state += 0.25f * ((hp_state - this_sample) - lp_state);
158         *out++ = 4.0f * lp_state;
159         hp_state = this_sample;
160       }
161     }
162   }
163 
164   high_ = high;
165   phase_ = phase;
166   next_sample_ = next_sample;
167   lp_state_ = lp_state;
168   hp_state_ = hp_state;
169 
170   return shape == OSCILLATOR_SHAPE_PULSE
171       ?  0.025f / (0.0002f + phase_increment_)
172       : 1.0f;
173 }
174 
Duck(const float * internal,const float * external,float * destination,size_t size)175 float Oscillator::Duck(
176     const float* internal,
177     const float* external,
178     float* destination, size_t size) {
179   float level = external_input_level_;
180   for (size_t i = 0; i < size; ++i) {
181     float error = external[i] * external[i] - level;
182     level += ((error > 0.0f) ? 0.01f : 0.0001f) * error;
183     float internal_gain = 1.0f - 32.0f * level;
184     if (internal_gain <= 0.0f) {
185       internal_gain = 0.0f;
186     }
187     destination[i] = external[i] + internal_gain * (internal[i] - external[i]);
188   }
189   external_input_level_ = level;
190   return level;
191 }
192 
RenderNoise(float note,float * modulation,float * out,size_t size)193 float Oscillator::RenderNoise(
194     float note,
195     float* modulation,
196     float* out,
197     size_t size) {
198   for (size_t i = 0; i < size; ++i) {
199     float noise = static_cast<float>(stmlib::Random::GetWord()) * kToFloat;
200     out[i] = 2.0f * noise - 1.0f;
201   }
202   Duck(out, modulation, out, size);
203   filter_.set_f_q<FREQUENCY_ACCURATE>(midi_to_increment(note) * 4.0f, 1.0f);
204   filter_.Process<FILTER_MODE_LOW_PASS>(out, out, size);
205   return 1.0f;
206 }
207 
208 /* static */
209 Oscillator::RenderFn Oscillator::fn_table_[] = {
210   &Oscillator::RenderSine,
211   &Oscillator::RenderPolyblep<OSCILLATOR_SHAPE_TRIANGLE>,
212   &Oscillator::RenderPolyblep<OSCILLATOR_SHAPE_SAW>,
213   &Oscillator::RenderPolyblep<OSCILLATOR_SHAPE_PULSE>,
214   &Oscillator::RenderNoise,
215 };
216 
217 }  // namespace warps
218