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