70 if (su < 0.0f) su = 0.0f;
71 if (su > 1.0f) su = 1.0f;
72 float note = kMinNote + su * kNoteRange;
81 if (su < 0.0f) su = 0.0f;
82 if (su > 1.0f) su = 1.0f;
83 set_pulse_width(0.05f + su * 0.9f);
91 if (su < 0.0f) su = 0.0f;
92 if (su > 1.0f) su = 1.0f;
109 const float* fm_mod =
nullptr,
float fm_depth = 0.0f) {
110 if (fm_mod !=
nullptr) {
112 float base_freq = frequency_;
113 for (uint16_t i = 0; i < frames; ++i) {
115 if (freq < 0.0f) freq = 0.0f;
117 render_float(out + i, 1);
119 frequency_ = base_freq;
120 if (amplitude_ != 1.0f) {
121 for (uint16_t i = 0; i < frames; ++i) {
122 out[i] *= amplitude_;
128 while (pos < frames) {
129 uint16_t n = frames - pos;
130 if (n > MAX_BLOCK) n = MAX_BLOCK;
131 render_float(out + pos, n);
132 if (amplitude_ != 1.0f) {
133 for (uint16_t i = 0; i < n; ++i) {
134 out[pos + i] *= amplitude_;
157 return static_cast<uint32_t
>(phase_ * 4294967296.0f);
168 static constexpr float kMinNote = 24.0f;
169 static constexpr float kMaxNote = 120.0f;
170 static constexpr float kNoteRange = kMaxNote - kMinNote;
174 void set_frequency(
float freq_hz) {
178 void set_note(
float midi_note) {
182 void set_pulse_width(
float pw) {
183 pw_ = (pw < 0.05f) ? 0.05f : (pw > 0.95f) ? 0.95f : pw;
186 void set_amplitude(
float amp) { amplitude_ = amp; }
189 static constexpr uint16_t MAX_BLOCK = 48;
192 float frequency_ = 0.0f;
194 float next_sample_ = 0.0f;
197 float amplitude_ = 1.0f;
202 void render_float(
float* out, uint16_t frames) {
214 void render_saw_f(
float* out, uint16_t frames) {
216 float phase = phase_;
217 float next = next_sample_;
218 float freq = frequency_;
219 float gain = hf_gain(freq);
221 for (uint16_t i = 0; i < frames; ++i) {
222 float this_sample = next;
228 float t =
phase / freq;
229 this_sample -= this_blep(t);
230 next -= next_blep(t);
233 this_sample = 2.0f * this_sample - 1.0f;
235 out[i] =
math::clamp(this_sample, -1.0f, 1.0f) * gain;
244 void render_square_f(
float* out, uint16_t frames,
float pw) {
246 float phase = phase_;
247 float next = next_sample_;
248 float freq = frequency_;
250 float gain = hf_gain(freq);
254 for (uint16_t i = 0; i < frames; ++i) {
255 float this_sample = next;
261 if (high &&
phase >= fall_at) {
262 float t = (
phase - fall_at) / freq;
263 this_sample -= this_blep(t);
264 next -= next_blep(t);
270 float t =
phase / freq;
271 this_sample += this_blep(t);
272 next += next_blep(t);
276 next += high ? 1.0f : 0.0f;
277 this_sample = 2.0f * this_sample - 1.0f;
279 out[i] =
math::clamp(this_sample, -1.0f, 1.0f) * gain;
289 void render_triangle_f(
float* out, uint16_t frames) {
291 float phase = phase_;
292 float next = next_sample_;
293 float freq = frequency_;
295 float gain = hf_gain(freq);
297 constexpr float slope_up = 2.0f;
298 constexpr float slope_down = 2.0f;
299 const float discontinuity = (slope_up + slope_down) * freq;
301 for (uint16_t i = 0; i < frames; ++i) {
302 float this_sample = next;
308 if (high ^ (
phase < 0.5f)) {
309 float t = (
phase - 0.5f) / freq;
310 this_sample -= this_integrated_blep(t) * discontinuity;
311 next -= next_integrated_blep(t) * discontinuity;
317 float t =
phase / freq;
318 this_sample += this_integrated_blep(t) * discontinuity;
319 next += next_integrated_blep(t) * discontinuity;
326 : 1.0f - (
phase - 0.5f) * slope_down;
328 out[i] =
math::clamp(2.0f * this_sample - 1.0f, -1.0f, 1.0f) * gain;
348 static float hf_gain(
float freq) {
349 constexpr float FADE_START = 0.40f;
350 constexpr float FADE_END = 0.48f;
351 if (freq <= FADE_START)
return 1.0f;
352 if (freq >= FADE_END)
return 0.0f;
354 constexpr float INV_RANGE = 1.0f / (FADE_END - FADE_START);
355 float t = (freq - FADE_START) * INV_RANGE;
357 return 1.0f - t * t * (3.0f - 2.0f * t);
368 static float this_blep(
float t) {
return 0.5f * t * t; }
371 static float next_blep(
float t) { t = 1.0f - t;
return -0.5f * t * t; }
374 static float next_integrated_blep(
float t) {
375 const float t1 = 0.5f * t;
376 const float t2 = t1 * t1;
377 const float t4 = t2 * t2;
378 return 0.1875f - t1 + 1.5f * t2 - t4;
382 static float this_integrated_blep(
float t) {
383 return next_integrated_blep(1.0f - t);