1 /*
2 ** Surge Synthesizer is Free and Open Source Software
3 **
4 ** Surge is made available under the Gnu General Public License, v3.0
5 ** https://www.gnu.org/licenses/gpl-3.0.en.html
6 **
7 ** Copyright 2004-2021 by various individuals as described by the Git transaction log
8 **
9 ** All source at: https://github.com/surge-synthesizer/surge.git
10 **
11 ** Surge was a commercial product from 2004-2018, with Copyright and ownership
12 ** in that period held by Claes Johanson at Vember Audio. Claes made Surge
13 ** open source in September 2018.
14 */
15
16 #include "globals.h"
17 #include "FastMath.h"
18
19 /*
20 * String oscillator is a self-oscillating delay with various filters and
21 * feedback options.
22 *
23 * The basic circuit is
24 *
25 * At init:
26 * - Excite the delay line with an input. In 'chirp' mode this is only pre-play and
27 * in 'continous' mode it is scaled by the amplitude during play
28 *
29 * At runtime:
30 * - run two delay lines seeded the same and take two taps, tap1 and tap2,
31 * and create an output which is (1-mix) * tap1 + mix * tap2
32 * - create a feedback signal fb* tap + excitation in each line
33 * - run that feedback signal through a tone filter in each line
34 * - drive that feedback signal and run it through a soft clipper in each line
35 * - write that feedback signal to the head of the delay line
36 *
37 */
38
39 #include "StringOscillator.h"
40
stringosc_excitations_count()41 int stringosc_excitations_count() { return 15; }
42
stringosc_excitation_name(int i)43 std::string stringosc_excitation_name(int i)
44 {
45 auto m = (StringOscillator::exciter_modes)i;
46
47 switch (m)
48 {
49 case StringOscillator::burst_noise:
50 return "Burst Noise";
51 case StringOscillator::burst_pink_noise:
52 return "Burst Pink Noise";
53 case StringOscillator::burst_sine:
54 return "Burst Sine";
55 case StringOscillator::burst_ramp:
56 return "Burst Ramp";
57 case StringOscillator::burst_tri:
58 return "Burst Triangle";
59 case StringOscillator::burst_square:
60 return "Burst Square";
61 case StringOscillator::burst_sweep:
62 return "Burst Sweep";
63 case StringOscillator::constant_noise:
64 return "Constant Noise";
65 case StringOscillator::constant_pink_noise:
66 return "Constant Pink Noise";
67 case StringOscillator::constant_sine:
68 return "Constant Sine";
69 case StringOscillator::constant_tri:
70 return "Constant Triangle";
71 case StringOscillator::constant_ramp:
72 return "Constant Ramp";
73 case StringOscillator::constant_square:
74 return "Constant Square";
75 case StringOscillator::constant_sweep:
76 return "Constant Sweep";
77 case StringOscillator::constant_audioin:
78 return "Audio In";
79 }
80 return "Unknown";
81 }
82
init(float pitch,bool is_display,bool nzi)83 void StringOscillator::init(float pitch, bool is_display, bool nzi)
84 {
85 memset((void *)dustBuffer, 0, 2 * (BLOCK_SIZE_OS) * sizeof(float));
86
87 id_exciterlvl = oscdata->p[str_exciter_level].param_id_in_scene;
88 id_str1decay = oscdata->p[str_str1_decay].param_id_in_scene;
89 id_str2decay = oscdata->p[str_str2_decay].param_id_in_scene;
90 id_str2detune = oscdata->p[str_str2_detune].param_id_in_scene;
91 id_strbalance = oscdata->p[str_str_balance].param_id_in_scene;
92 id_stiffness = oscdata->p[str_stiffness].param_id_in_scene;
93
94 if (is_display)
95 {
96 gen = std::minstd_rand(8675309);
97 }
98 else
99 {
100 gen = std::minstd_rand(storage->rand());
101 }
102
103 urd = std::uniform_real_distribution<float>(0.0, 1.0);
104
105 auto pitch_t = std::min(148.f, pitch);
106 auto pitchmult_inv =
107 std::max(1.0, dsamplerate_os * (1 / 8.175798915) * storage->note_to_pitch_inv(pitch_t));
108 auto p2off = oscdata->p[str_str2_detune].get_extended(localcopy[id_str2detune].f);
109 double pitch2_t = 1, pitchmult2_inv = 1;
110
111 if (oscdata->p[str_str2_detune].absolute)
112 {
113 pitch2_t = std::min(148.f, pitch);
114
115 auto frequency = Tunings::MIDI_0_FREQ * storage->note_to_pitch(pitch2_t);
116 auto fac = oscdata->p[str_str2_detune].extend_range ? 12 * 16 : 16;
117 auto detune = localcopy[id_str2detune].f * fac;
118
119 frequency = std::max(10.0, frequency + detune);
120 pitchmult2_inv = std::max(1.0, dsamplerate_os / frequency);
121 }
122 else
123 {
124 pitch2_t = std::min(148.f, pitch + p2off);
125 pitchmult2_inv = std::max(1.0, dsamplerate_os * (1 / 8.175798915) *
126 storage->note_to_pitch_inv(pitch2_t));
127 }
128
129 noiseLp.coeff_LP2B(noiseLp.calc_omega(0) * OSC_OVERSAMPLING, 0.9);
130 for (int i = 0; i < 3; ++i)
131 {
132 fillDustBuffer(pitchmult_inv, pitchmult2_inv);
133 }
134
135 tap[0].startValue(pitchmult_inv);
136 tap[1].startValue(pitchmult2_inv);
137 t2level.startValue(0.5 * limit_range(localcopy[id_strbalance].f, -1.f, 1.f) + 0.5);
138
139 // we need a big prefill to supprot the delay line for FM
140 auto prefill = (int)floor(10 * std::max(pitchmult_inv, pitchmult2_inv));
141
142 for (int i = 0; i < 2; ++i)
143 {
144 delayLine[i].clear();
145 driftLFO[i].init(nzi);
146 }
147
148 auto mode = (exciter_modes)oscdata->p[str_exciter_mode].val.i;
149 phase1 = 0.0, phase2 = 0.0;
150
151 if (!oscdata->retrigger.val.b && !is_display)
152 {
153 phase1 = urd(gen);
154 phase2 = urd(gen);
155 }
156
157 auto r1 = 1.0 / pitchmult_inv;
158 auto r2 = 1.0 / pitchmult2_inv;
159 auto d0 = limit_range(localcopy[id_exciterlvl].f, 0.f, 1.f);
160 d0 *= d0 * d0 * d0;
161
162 int dustrp = BLOCK_SIZE_OS;
163 configureLpAndHpFromTone();
164
165 for (int i = 0; i < prefill; ++i)
166 {
167 float dlv[2] = {0, 0};
168
169 switch (mode)
170 {
171 case burst_sine:
172 d0 = 1;
173 case constant_sine:
174 {
175 dlv[0] = (0.707 * d0 * std::sin(2.0 * M_PI * phase1));
176 dlv[1] = (0.707 * d0 * std::sin(2.0 * M_PI * phase2));
177
178 phase1 += r1;
179 phase2 += r2;
180 }
181 break;
182 case burst_tri:
183 d0 = 1;
184 case constant_tri:
185 {
186 // phase is 0,1 so tri is
187 auto t1 = (phase1 < 0.5) ? (phase1 * 4 - 1) : ((1 - phase1)) * 4 - 1;
188 auto t2 = (phase2 < 0.5) ? (phase2 * 4 - 1) : ((1 - phase2)) * 4 - 1;
189
190 dlv[0] = (0.707 * d0 * t1);
191 dlv[1] = (0.707 * d0 * t2);
192
193 phase1 += r1;
194 phase2 += r2;
195
196 if (phase1 > 1)
197 {
198 phase1 -= 1;
199 }
200
201 if (phase2 > 1)
202 {
203 phase2 -= 1;
204 }
205 }
206 break;
207 case burst_square:
208 d0 = 1;
209 case constant_square:
210 {
211 dlv[0] = (0.707 * d0 * ((phase1 > 0.5) * 2 - 1));
212 dlv[1] = (0.707 * d0 * ((phase2 > 0.5) * 2 - 1));
213
214 phase1 += r1;
215 phase2 += r2;
216
217 if (phase1 > 1)
218 {
219 phase1 -= 1;
220 }
221
222 if (phase2 > 1)
223 {
224 phase2 -= 1;
225 }
226 }
227 break;
228 case burst_ramp:
229 d0 = 1;
230 case constant_ramp:
231 {
232 dlv[0] = (0.707 * d0 * (phase1 * 2 - 1));
233 dlv[1] = (0.707 * d0 * (phase2 * 2 - 1));
234
235 phase1 += r1;
236 phase2 += r2;
237
238 if (phase1 > 1)
239 {
240 phase1 -= 1;
241 }
242
243 if (phase2 > 1)
244 {
245 phase2 -= 1;
246 }
247 }
248 break;
249 case burst_sweep:
250 d0 = 1;
251 case constant_sweep:
252 {
253 if (phase1 == 0)
254 {
255 dlv[0] = (0.707 * d0);
256 }
257 else
258 {
259 dlv[0] = (0.707 * d0 * std::sin(2.0 * M_PI / phase1));
260 }
261
262 if (phase2 == 0)
263 {
264 dlv[1] = (0.707 * d0);
265 }
266 else
267 {
268 dlv[1] = (0.707 * d0 * std::sin(2.0 * M_PI / phase2));
269 }
270
271 phase1 += r1;
272 phase2 += r2;
273
274 if (phase1 > 1)
275 {
276 phase1 -= 1;
277 }
278
279 if (phase2 > 1)
280 {
281 phase2 -= 1;
282 }
283 }
284 break;
285
286 case constant_audioin:
287 dlv[0] = (0);
288 dlv[1] = (0);
289 break;
290
291 case burst_pink_noise:
292 d0 = 1.0;
293 case constant_pink_noise:
294 {
295 if (dustrp >= BLOCK_SIZE_OS)
296 {
297 dustrp = 0;
298 fillDustBuffer(pitchmult_inv, pitchmult2_inv);
299 }
300
301 dlv[0] = (d0 * dustBuffer[0][dustrp]);
302 dlv[1] = (d0 * dustBuffer[1][dustrp]);
303
304 dustrp++;
305 }
306 break;
307 case burst_noise:
308 d0 = 1;
309 case constant_noise:
310 default:
311 dlv[0] = (d0 * (urd(gen) * 2 - 1));
312 dlv[1] = (d0 * (urd(gen) * 2 - 1));
313 break;
314 }
315
316 float lpt[2], hpt[2];
317 lp.process_sample(dlv[0], dlv[1], lpt[0], lpt[1]);
318 hp.process_sample(dlv[0], dlv[1], hpt[0], hpt[1]);
319
320 for (int t = 0; t < 2; ++t)
321 {
322 delayLine[t].write(tone.v < 0 ? lpt[t] : hpt[t]);
323 }
324 }
325 lp.flush_sample_denormal();
326 hp.flush_sample_denormal();
327
328 for (int t = 0; t < 2; ++t)
329 {
330 priorSample[t] = delayLine[t].buffer[(delayLine[t].wp - 1) & delayLine[t].comb_size];
331 }
332
333 charFilt.init(storage->getPatch().character.val.i);
334 }
335
configureLpAndHpFromTone()336 void StringOscillator::configureLpAndHpFromTone()
337 {
338 tone.newValue(limit_range(localcopy[id_stiffness].f, -1.f, 1.f));
339
340 float clo = 10, cmidhi = 60, cmid = 100, chi = -70;
341 float hpCutoff = chi;
342 float lpCutoff = cmid;
343
344 if (tone.v > 0)
345 {
346 // OK so cool scale the HP cutoff
347 auto tv = tone.v;
348 hpCutoff = tv * (cmidhi - chi) + chi;
349 }
350 else
351 {
352 auto tv = -tone.v;
353 lpCutoff = tv * (clo - cmid) + cmid;
354 }
355
356 // Inefficient - copy coefficients later
357 lp.coeff_LP(lp.calc_omega((lpCutoff / 12.0) - 2.f) * OSC_OVERSAMPLING, 0.707);
358 hp.coeff_HP(hp.calc_omega((hpCutoff / 12.0) - 2.f) * OSC_OVERSAMPLING, 0.707);
359 }
360
process_block(float pitch,float drift,bool stereo,bool FM,float fmdepthV)361 void StringOscillator::process_block(float pitch, float drift, bool stereo, bool FM, float fmdepthV)
362 {
363 #define P(m) \
364 case m: \
365 if (FM) \
366 process_block_internal<true, m>(pitch, drift, stereo, fmdepthV); \
367 else \
368 process_block_internal<false, m>(pitch, drift, stereo, fmdepthV); \
369 break;
370
371 auto mode = (exciter_modes)oscdata->p[str_exciter_mode].val.i;
372
373 switch (mode)
374 {
375 P(burst_noise)
376 P(burst_pink_noise)
377 P(burst_sine)
378 P(burst_tri)
379 P(burst_ramp)
380 P(burst_square)
381 P(burst_sweep)
382
383 P(constant_noise)
384 P(constant_pink_noise)
385 P(constant_sine)
386 P(constant_tri)
387 P(constant_ramp)
388 P(constant_square)
389 P(constant_sweep)
390
391 P(constant_audioin)
392 }
393
394 #undef P
395 }
396
397 template <bool FM, StringOscillator::exciter_modes mode>
process_block_internal(float pitch,float drift,bool stereo,float fmdepthV)398 void StringOscillator::process_block_internal(float pitch, float drift, bool stereo, float fmdepthV)
399 {
400 auto lfodetune = drift * driftLFO[0].next();
401 auto pitch_t = std::min(148.f, pitch + lfodetune);
402 auto pitchmult_inv = std::max((FIRipol_N >> 1) + 1.0, dsamplerate_os * (1 / 8.175798915) *
403 storage->note_to_pitch_inv(pitch_t));
404 auto d0 = limit_range(localcopy[id_exciterlvl].f, 0.f, 1.f);
405
406 if (mode >= constant_noise)
407 {
408 examp.newValue(d0 * d0 * d0 * d0);
409 }
410 else
411 {
412 if (d0 < 0.1)
413 {
414 // 5.6234 = powf(0.1, 0.25) * 10 to match it where powf(d0, 0.25) starts below
415 examp.newValue(d0 * 5.6234);
416 }
417 else
418 {
419 examp.newValue(powf(d0, 0.25));
420 }
421 }
422
423 auto dp1 = pitch_to_dphase(pitch_t);
424
425 lfodetune = drift * driftLFO[1].next();
426
427 double dp2;
428 double pitch2_t = 1, pitchmult2_inv = 1;
429 auto p2off = oscdata->p[str_str2_detune].get_extended(localcopy[id_str2detune].f);
430
431 if (oscdata->p[str_str2_detune].absolute)
432 {
433 pitch2_t = std::min(148.f, pitch);
434 auto frequency = Tunings::MIDI_0_FREQ * storage->note_to_pitch(pitch2_t);
435
436 auto fac = oscdata->p[str_str2_detune].extend_range ? 12 * 16 : 16;
437 auto detune = localcopy[id_str2detune].f * fac;
438
439 frequency = std::max(10.0, frequency + detune);
440 pitchmult2_inv = std::max(1.0, dsamplerate_os / frequency);
441 dp2 = frequency * dsamplerate_os_inv;
442 }
443 else
444 {
445 pitch2_t = std::min(148.f, pitch + p2off);
446 pitchmult2_inv = std::max(1.0, dsamplerate_os * (1 / 8.175798915) *
447 storage->note_to_pitch_inv(pitch2_t));
448 dp2 = pitch_to_dphase(pitch2_t);
449 }
450
451 pitchmult_inv = std::min(pitchmult_inv, (delayLine[0].comb_size - 100) * 1.0);
452 pitchmult2_inv = std::min(pitchmult2_inv, (delayLine[0].comb_size - 100) * 1.0);
453
454 tap[0].newValue(pitchmult_inv);
455 tap[1].newValue(pitchmult2_inv);
456
457 t2level.newValue(0.5 * limit_range(localcopy[id_strbalance].f, -1.f, 1.f) + 0.5);
458
459 auto fbp = limit_range(localcopy[id_str1decay].f, 0.f, 1.f);
460
461 if (fbp < 0.2)
462 {
463 // go from 0.85 to 0.95
464 feedback[0].newValue(0.85f + (0.5f * fbp));
465 }
466 else
467 {
468 // go from 0.95 to 1.0
469 feedback[0].newValue(0.9375f + (0.0625f * fbp));
470 }
471
472 auto fbp2 = limit_range(localcopy[id_str2decay].f, 0.f, 1.f);
473
474 if (fbp2 < 0.2)
475 {
476 feedback[1].newValue(0.85f + (0.5f * fbp2));
477 }
478 else
479 {
480 feedback[1].newValue(0.9375f + (0.0625f * fbp2));
481 }
482
483 // fmdepthV basically goes 0 -> 16 so lets push it 0,1 for now
484 double fv = fmdepthV / 16.0;
485
486 fmdepth.newValue(fv);
487
488 configureLpAndHpFromTone();
489
490 float val[2] = {0.f, 0.f}, fbNoOutVal[2] = {0.f, 0.f}, fbv[2] = {0, 0};
491
492 if (mode == constant_pink_noise)
493 {
494 fillDustBuffer(pitchmult_inv, pitchmult2_inv);
495 }
496
497 for (int i = 0; i < BLOCK_SIZE_OS; ++i)
498 {
499 for (int t = 0; t < 2; ++t)
500 {
501 auto v = tap[t].v;
502 float *phs = (t == 0) ? &phase1 : &phase2;
503 float dp = (t == 0) ? dp1 : dp2;
504
505 if (FM)
506 {
507 v *= Surge::DSP::fastexp(limit_range(fmdepth.v * master_osc[i] * 3, -6.f, 4.f));
508 }
509
510 val[t] = delayLine[t].read(v);
511 fbNoOutVal[t] = 0.f;
512
513 // Add continuous excitation
514 switch (mode)
515 {
516 case constant_noise:
517 {
518 val[t] += examp.v * (urd(gen) * 2 - 1);
519 }
520 break;
521 case constant_pink_noise:
522 {
523 auto ds1 = examp.v * dustBuffer[t][i];
524 val[t] += ds1;
525 }
526 break;
527 case constant_ramp:
528 {
529 auto rn = 0.707 * (*phs * 2 - 1);
530
531 val[t] += examp.v * rn;
532 *phs += dp;
533 *phs -= (*phs > 1);
534 }
535 break;
536 case constant_tri:
537 {
538 auto rn = 0.707 * ((*phs < 0.5) ? (*phs * 4 - 1) : ((1 - *phs) * 4 - 1));
539
540 val[t] += examp.v * rn;
541 *phs += dp;
542 *phs -= (*phs > 1);
543 }
544 break;
545 case constant_sweep:
546 {
547 float sv = 1.0;
548
549 if (*phs != 0)
550 {
551 sv = std::sin(2.0 * M_PI / *phs);
552 }
553
554 val[t] += examp.v * 0.707 * sv;
555 *phs += dp;
556 *phs -= (*phs > 1);
557 }
558 break;
559 case constant_sine:
560 {
561 float sv = std::sin(2.0 * M_PI * *phs);
562
563 val[t] += examp.v * 0.707 * sv;
564 *phs += dp;
565 *phs -= (*phs > 1);
566 }
567 break;
568 case constant_square:
569 {
570 auto rn = 0.707 * ((*phs > 0.5) ? 1 : -1);
571
572 val[t] += examp.v * rn;
573 *phs += dp;
574 *phs -= (*phs > 1);
575 }
576 break;
577 case constant_audioin:
578 {
579 fbNoOutVal[t] = examp.v * storage->audio_in[t][i];
580 }
581 break;
582 default:
583 // We should do something else with amplitude here
584 val[t] *= examp.v;
585 break;
586 }
587
588 // precautionary hard clip
589 fbv[t] = limit_range(val[t] + fbNoOutVal[t], -1.f, 1.f);
590 }
591
592 float lpv[2], hpv[2];
593 lp.process_sample(fbv[0], fbv[1], lpv[0], lpv[1]);
594 hp.process_sample(fbv[0], fbv[1], hpv[0], hpv[1]);
595
596 for (int t = 0; t < 2; ++t)
597 {
598 auto filtv = (tone.v > 0) ? hpv[t] : lpv[t];
599
600 if (fabs(filtv) < 1e-16)
601 filtv = 0;
602 delayLine[t].write(filtv * feedback[t].v);
603 }
604
605 float out = val[0] + t2level.v * (val[1] - val[0]);
606
607 // softclip the output
608 out = out * (1.5 - 0.5 * out * out);
609
610 tap[0].process();
611 tap[1].process();
612 t2level.process();
613 feedback[0].process();
614 feedback[1].process();
615 tone.process();
616 examp.process();
617 fmdepth.process();
618
619 output[i] = out;
620 outputR[i] = out;
621 }
622
623 lp.flush_sample_denormal_aggressive();
624 hp.flush_sample_denormal_aggressive();
625
626 if (charFilt.doFilter)
627 {
628 if (stereo)
629 {
630 charFilt.process_block_stereo(output, outputR, BLOCK_SIZE_OS);
631 }
632 else
633 {
634 charFilt.process_block(output, BLOCK_SIZE_OS);
635 }
636 }
637 }
638
init_ctrltypes()639 void StringOscillator::init_ctrltypes()
640 {
641 oscdata->p[str_exciter_mode].set_name("Exciter");
642 oscdata->p[str_exciter_mode].set_type(ct_stringosc_excitation_model);
643
644 oscdata->p[str_exciter_level].set_name("Exciter Level");
645 oscdata->p[str_exciter_level].set_type(ct_percent);
646 oscdata->p[str_exciter_level].val_default.f = 1.f;
647
648 oscdata->p[str_str1_decay].set_name("String 1 Decay");
649 oscdata->p[str_str1_decay].set_type(ct_percent);
650 oscdata->p[str_str1_decay].val_default.f = 0.95;
651
652 oscdata->p[str_str2_decay].set_name("String 2 Decay");
653 oscdata->p[str_str2_decay].set_type(ct_percent);
654 oscdata->p[str_str2_decay].val_default.f = 0.95;
655
656 oscdata->p[str_str2_detune].set_name("String 2 Detune");
657 oscdata->p[str_str2_detune].set_type(ct_oscspread_bipolar);
658
659 oscdata->p[str_str_balance].set_name("String Balance");
660 oscdata->p[str_str_balance].set_type(ct_percent_bipolar_stringbal);
661
662 oscdata->p[str_stiffness].set_name("Stiffness");
663 oscdata->p[str_stiffness].set_type(ct_percent_bipolar);
664 }
665
init_default_values()666 void StringOscillator::init_default_values()
667 {
668 oscdata->p[str_exciter_mode].val.i = 0;
669 oscdata->p[str_exciter_level].val.f = 1.f;
670
671 oscdata->p[str_str1_decay].val.f = 0.95f;
672 oscdata->p[str_str2_decay].val.f = 0.95f;
673
674 oscdata->p[str_str2_detune].val.f = 0.1f;
675 oscdata->p[str_str2_detune].extend_range = false;
676 oscdata->p[str_str_balance].val.f = 0.f;
677
678 oscdata->p[str_stiffness].val.f = 0.f;
679 }
680
fillDustBuffer(float tap0,float tap1)681 void StringOscillator::fillDustBuffer(float tap0, float tap1)
682 {
683 for (int i = 0; i < BLOCK_SIZE_OS; ++i)
684 {
685 auto v0 = urd(gen) * 2 - 1;
686 auto v1 = urd(gen) * 2 - 1;
687 noiseLp.process_sample_nolag(v0, v1);
688 dustBuffer[0][i] = v0 * 1.7;
689 dustBuffer[1][i] = v1 * 1.7;
690 }
691 }
692