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_