Skip to content

Commit c99cf67

Browse files
committed
fix(examples): polysynth: NaNs with biquad and svf filters
1 parent d5c1652 commit c99cf67

File tree

1 file changed

+36
-21
lines changed

1 file changed

+36
-21
lines changed

examples/polysynth/src/dsp.rs

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -388,16 +388,26 @@ impl<T: Scalar> Saturator<T> for Sinh {
388388
}
389389
}
390390

391+
fn svf_clipper<T: Scalar>() -> bjt::CommonCollector<T> {
392+
bjt::CommonCollector {
393+
vee: T::from_f64(-1.),
394+
vcc: T::from_f64(1.),
395+
xbias: T::from_f64(-0.1),
396+
ybias: T::from_f64(0.1),
397+
}
398+
}
399+
391400
#[derive(Debug, Copy, Clone)]
392401
enum FilterImpl<T> {
393402
Transistor(Ladder<T, Transistor<Tanh>>),
394403
Ota(Ladder<T, OTA<Tanh>>),
395-
Svf(Svf<T, Sinh>),
404+
Svf(bjt::CommonCollector<T>, Svf<T, Sinh>),
396405
Biquad(Biquad<T, Clipper<T>>),
397406
}
398407

399408
impl<T: Scalar> FilterImpl<T> {
400409
fn from_type(samplerate: T, ftype: FilterType, cutoff: T, resonance: T) -> FilterImpl<T> {
410+
let cutoff = cutoff.simd_clamp(T::zero(), samplerate / T::from_f64(12.));
401411
match ftype {
402412
FilterType::TransistorLadder => {
403413
let mut ladder = Ladder::new(samplerate, cutoff, T::from_f64(4.) * resonance);
@@ -409,13 +419,13 @@ impl<T: Scalar> FilterImpl<T> {
409419
ladder.compensated = true;
410420
Self::Ota(ladder)
411421
}
412-
FilterType::Svf => Self::Svf(Svf::new(samplerate, cutoff, T::one() - resonance)),
422+
FilterType::Svf => Self::Svf(
423+
svf_clipper(),
424+
Svf::new(samplerate, cutoff, T::one() - resonance),
425+
),
413426
FilterType::Digital => Self::Biquad(
414-
Biquad::lowpass(
415-
cutoff / samplerate,
416-
(T::from_f64(3.) * resonance).simd_exp(),
417-
)
418-
.with_saturators(Default::default(), Default::default()),
427+
Biquad::lowpass(cutoff / samplerate, T::one())
428+
.with_saturators(Default::default(), Default::default()),
419429
),
420430
}
421431
}
@@ -430,14 +440,14 @@ impl<T: Scalar> FilterImpl<T> {
430440
p.set_cutoff(cutoff);
431441
p.set_resonance(T::from_f64(4.) * resonance);
432442
}
433-
Self::Svf(p) => {
443+
Self::Svf(_, p) => {
434444
p.set_cutoff(cutoff);
435-
p.set_r(T::one() - resonance);
445+
p.set_r(T::one() - resonance.simd_sqrt());
436446
}
437447
Self::Biquad(p) => {
438448
p.update_coefficients(&Biquad::lowpass(
439449
cutoff / samplerate,
440-
(T::from_f64(3.) * resonance).simd_exp(),
450+
T::from_f64(4.7) * (T::from_f64(2.) * resonance - T::one()).simd_exp(),
441451
));
442452
}
443453
}
@@ -448,7 +458,7 @@ impl<T: Scalar> FilterImpl<T> {
448458
Self::Transistor(..) => T::from_f64(0.5),
449459
Self::Ota(..) => T::from_f64(db_to_gain(9.) as _),
450460
Self::Svf(..) => T::from_f64(db_to_gain(9.) as _),
451-
Self::Biquad(..) => T::from_f64(db_to_gain(6.) as _),
461+
Self::Biquad(..) => T::from_f64(db_to_gain(12.) as _),
452462
}
453463
}
454464
}
@@ -460,7 +470,7 @@ impl<T: Scalar> DSPMeta for FilterImpl<T> {
460470
match self {
461471
FilterImpl::Transistor(p) => p.set_samplerate(samplerate),
462472
FilterImpl::Ota(p) => p.set_samplerate(samplerate),
463-
FilterImpl::Svf(p) => p.set_samplerate(samplerate),
473+
FilterImpl::Svf(_, p) => p.set_samplerate(samplerate),
464474
FilterImpl::Biquad(p) => p.set_samplerate(samplerate),
465475
}
466476
}
@@ -469,7 +479,7 @@ impl<T: Scalar> DSPMeta for FilterImpl<T> {
469479
match self {
470480
FilterImpl::Transistor(p) => p.latency(),
471481
FilterImpl::Ota(p) => p.latency(),
472-
FilterImpl::Svf(p) => p.latency(),
482+
FilterImpl::Svf(_, p) => p.latency(),
473483
FilterImpl::Biquad(p) => p.latency(),
474484
}
475485
}
@@ -478,7 +488,7 @@ impl<T: Scalar> DSPMeta for FilterImpl<T> {
478488
match self {
479489
FilterImpl::Transistor(p) => p.reset(),
480490
FilterImpl::Ota(p) => p.reset(),
481-
FilterImpl::Svf(p) => p.reset(),
491+
FilterImpl::Svf(_, p) => p.reset(),
482492
FilterImpl::Biquad(p) => p.reset(),
483493
}
484494
}
@@ -492,7 +502,7 @@ impl<T: Scalar> DSPProcess<1, 1> for FilterImpl<T> {
492502
let y = match self {
493503
FilterImpl::Transistor(p) => p.process(x),
494504
FilterImpl::Ota(p) => p.process(x),
495-
FilterImpl::Svf(p) => [p.process(x)[0]],
505+
FilterImpl::Svf(bjt, p) => [p.process(bjt.process(x))[0]],
496506
FilterImpl::Biquad(p) => p.process(x),
497507
};
498508
y.map(|x| drive_out * x)
@@ -522,6 +532,7 @@ impl<T: Scalar> Filter<T> {
522532
fn update_filter(&mut self, modulation_st: T) {
523533
let cutoff =
524534
semitone_to_ratio(modulation_st) * T::from_f64(self.params.cutoff.smoothed.next() as _);
535+
let cutoff = cutoff.simd_clamp(T::zero(), self.samplerate / T::from_f64(12.));
525536
let resonance = T::from_f64(self.params.resonance.smoothed.next() as _);
526537
self.fimpl = match self.params.filter_type.value() {
527538
FilterType::TransistorLadder if !matches!(self.fimpl, FilterImpl::Transistor(..)) => {
@@ -534,10 +545,13 @@ impl<T: Scalar> Filter<T> {
534545
ladder.compensated = true;
535546
FilterImpl::Ota(ladder)
536547
}
537-
FilterType::Svf if !matches!(self.fimpl, FilterImpl::Svf(..)) => {
538-
FilterImpl::Svf(Svf::new(self.samplerate, cutoff, T::one() - resonance))
539-
}
548+
FilterType::Svf if !matches!(self.fimpl, FilterImpl::Svf(..)) => FilterImpl::Svf(
549+
svf_clipper(),
550+
Svf::new(self.samplerate, cutoff, T::one() - resonance),
551+
),
540552
FilterType::Digital if !matches!(self.fimpl, FilterImpl::Biquad(..)) => {
553+
let resonance =
554+
T::from_f64(4.7) * (T::from_f64(2.) * resonance - T::one()).simd_exp();
541555
FilterImpl::Biquad(
542556
Biquad::lowpass(cutoff / self.samplerate, resonance)
543557
.with_saturators(Default::default(), Default::default()),
@@ -838,7 +852,8 @@ impl<T: Scalar> DSPMeta for Effects<T> {
838852

839853
impl<T: Scalar> DSPProcess<1, 1> for Effects<T> {
840854
fn process(&mut self, x: [Self::Sample; 1]) -> [Self::Sample; 1] {
841-
self.bjt.process(self.dc_blocker.process(x))
855+
let y = self.bjt.process(self.dc_blocker.process(x));
856+
y.map(|x| T::from_f64(0.5) * x)
842857
}
843858
}
844859

@@ -847,8 +862,8 @@ impl<T: Scalar> Effects<T> {
847862
Self {
848863
dc_blocker: DcBlocker::new(samplerate),
849864
bjt: bjt::CommonCollector {
850-
vee: T::from_f64(-2.5),
851-
vcc: T::from_f64(2.5),
865+
vee: T::from_f64(-2.),
866+
vcc: T::from_f64(2.),
852867
xbias: T::from_f64(0.1),
853868
ybias: T::from_f64(-0.1),
854869
},

0 commit comments

Comments
 (0)