diff --git a/CHANGES.md b/CHANGES.md index 2056546..96c0e59 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,6 +10,8 @@ - `AdaptiveTanh` is now generic `Adaptive` distortion with an inner shape. To migrate, try `Adaptive::new(timescale, Tanh(hardness))`. - `Clip` shape now has a hardness parameter. `Clip(1.0)` to migrate. +- `SvfCoeffs` is now `SvfCoefs`. +- Implemented denormal prevention for `x86`. ### Version 0.18.2 diff --git a/examples/keys.rs b/examples/keys.rs index 4d4c4d7..8b3308c 100644 --- a/examples/keys.rs +++ b/examples/keys.rs @@ -137,6 +137,8 @@ fn run(device: &cpal::Device, config: &cpal::StreamConfig) -> Result<(), anyh where T: SizedSample + FromSample, { + fundsp::denormal::prevent_denormals(); + let sample_rate = config.sample_rate.0 as f64; let channels = config.channels as usize; @@ -312,17 +314,17 @@ impl eframe::App for State { self.net.crossfade( self.phaser_id, Fade::Smooth, - 0.1, + 0.2, Box::new( - phaser(0.9, |t| sin_hz(0.08, t) * 0.5 + 0.5) - | phaser(0.9, |t| sin_hz(0.08, t + 0.1) * 0.5 + 0.5), + phaser(0.8, |t| sin_hz(0.08, t) * 0.5 + 0.5) + | phaser(0.8, |t| sin_hz(0.08, t + 0.1) * 0.5 + 0.5), ), ); } else { self.net.crossfade( self.phaser_id, Fade::Smooth, - 0.1, + 0.2, Box::new(multipass::()), ); } @@ -335,12 +337,12 @@ impl eframe::App for State { self.net.crossfade( self.flanger_id, Fade::Smooth, - 0.1, + 0.2, Box::new( - flanger(0.9, 0.005, 0.015, |t| { - lerp11(0.005, 0.015, sin_hz(0.06, t + 0.1)) - }) | flanger(0.9, 0.005, 0.015, |t| { - lerp11(0.005, 0.015, sin_hz(0.06, t)) + flanger(0.8, 0.005, 0.015, |t| { + lerp11(0.0025, 0.015, sin_hz(0.06, t + 0.1)) + }) | flanger(0.8, 0.005, 0.015, |t| { + lerp11(0.0025, 0.015, sin_hz(0.06, t)) }), ), ); @@ -348,7 +350,7 @@ impl eframe::App for State { self.net.crossfade( self.flanger_id, Fade::Smooth, - 0.1, + 0.2, Box::new(multipass::()), ); } @@ -452,14 +454,14 @@ impl eframe::App for State { }); let waveform = match self.waveform { Waveform::Sine => Net::wrap(Box::new(pitch * 2.0 >> sine() * 0.1)), - Waveform::Saw => Net::wrap(Box::new(pitch >> saw() * 0.5)), - Waveform::Square => Net::wrap(Box::new(pitch >> square() * 0.5)), - Waveform::Triangle => Net::wrap(Box::new(pitch >> triangle() * 0.5)), - Waveform::Organ => Net::wrap(Box::new(pitch >> organ() * 0.5)), - Waveform::Hammond => Net::wrap(Box::new(pitch >> hammond() * 0.5)), + Waveform::Saw => Net::wrap(Box::new(pitch >> saw() * 0.25)), + Waveform::Square => Net::wrap(Box::new(pitch >> square() * 0.25)), + Waveform::Triangle => Net::wrap(Box::new(pitch >> triangle() * 0.25)), + Waveform::Organ => Net::wrap(Box::new(pitch >> organ() * 0.25)), + Waveform::Hammond => Net::wrap(Box::new(pitch >> hammond() * 0.25)), Waveform::Pulse => Net::wrap(Box::new( (pitch | lfo(move |t| lerp11(0.01, 0.99, sin_hz(0.1, t)))) - >> pulse() * 0.5, + >> pulse() * 0.25, )), Waveform::Pluck => { Net::wrap(Box::new(zero() >> pluck(pitch_hz as f32, 0.5, 0.5) * 0.5)) @@ -469,14 +471,14 @@ impl eframe::App for State { | pitch * 4.0 | lfo(move |t| { funutd::math::lerp( - 100.0, - 50.0 + 0.03 * pitch_hz, - clamp01(t * 5.0), + 200.0, + 50.0 + 0.05 * pitch_hz, + clamp01(t * 3.0), ) })) >> !resonator() >> resonator() - >> shape(Adaptive::new(0.01, Tanh(0.1))), + >> shape(Adaptive::new(0.1, Atan(0.05))) * 0.5, )), }; let filter = match self.filter { @@ -486,7 +488,7 @@ impl eframe::App for State { >> moog(), )), Filter::Butterworth => Net::wrap(Box::new( - (pass() | lfo(move |t| max(400.0, 10000.0 * exp(-t * 5.0)))) + (pass() | lfo(move |t| max(400.0, 20000.0 * exp(-t * 5.0)))) >> butterpass(), )), Filter::Bandpass => Net::wrap(Box::new( diff --git a/src/biquad.rs b/src/biquad.rs new file mode 100644 index 0000000..f7e41d9 --- /dev/null +++ b/src/biquad.rs @@ -0,0 +1,387 @@ +//! Biquad filters with nonlinearities. + +use super::audionode::*; +use super::math::*; +use super::setting::*; +use super::shape::*; +use super::signal::*; +use super::*; +use core::marker::PhantomData; + +#[derive(Copy, Clone, Debug, Default)] +pub struct BiquadCoefs { + pub a1: F, + pub a2: F, + pub b0: F, + pub b1: F, + pub b2: F, +} + +impl BiquadCoefs { + /// Returns settings for a Butterworth lowpass filter. + /// Cutoff is the -3 dB point of the filter in Hz. + pub fn butter_lowpass(sample_rate: F, cutoff: F) -> Self { + let c = F::from_f64; + let f: F = tan(cutoff * F::PI / sample_rate); + let a0r: F = c(1.0) / (c(1.0) + F::SQRT_2 * f + f * f); + let a1: F = (c(2.0) * f * f - c(2.0)) * a0r; + let a2: F = (c(1.0) - F::SQRT_2 * f + f * f) * a0r; + let b0: F = f * f * a0r; + let b1: F = c(2.0) * b0; + let b2: F = b0; + Self { a1, a2, b0, b1, b2 } + } + + /// Returns settings for a constant-gain bandpass resonator. + /// The center frequency is given in Hz. + /// Bandwidth is the difference in Hz between -3 dB points of the filter response. + /// The overall gain of the filter is independent of bandwidth. + pub fn resonator(sample_rate: F, center: F, bandwidth: F) -> Self { + let c = F::from_f64; + let r: F = exp(-F::PI * bandwidth / sample_rate); + let a1: F = c(-2.0) * r * cos(F::TAU * center / sample_rate); + let a2: F = r * r; + let b0: F = sqrt(c(1.0) - r * r) * c(0.5); + let b1: F = c(0.0); + let b2: F = -b0; + Self { a1, a2, b0, b1, b2 } + } + + /// Arbitrary biquad. + pub fn arbitrary(a1: F, a2: F, b0: F, b1: F, b2: F) -> Self { + Self { a1, a2, b0, b1, b2 } + } + + /// Frequency response at frequency `omega` expressed as fraction of sampling rate. + pub fn response(&self, omega: f64) -> Complex64 { + let z1 = Complex64::from_polar(1.0, -f64::TAU * omega); + let z2 = z1 * z1; + /// Complex64 with real component `x` and imaginary component zero. + fn re(x: T) -> Complex64 { + Complex64::new(x.to_f64(), 0.0) + } + (re(self.b0) + re(self.b1) * z1 + re(self.b2) * z2) + / (re(1.0) + re(self.a1) * z1 + re(self.a2) * z2) + } +} + +/// 2nd order IIR filter implemented in normalized Direct Form I. +/// - Setting: coefficients as tuple Parameter::Biquad(a1, a2, b0, b1, b2). +/// - Input 0: input signal. +/// - Output 0: filtered signal. +#[derive(Default, Clone)] +pub struct Biquad { + coefs: BiquadCoefs, + x1: F, + x2: F, + y1: F, + y2: F, + sample_rate: f64, +} + +impl Biquad { + pub fn new() -> Self { + Self { + sample_rate: DEFAULT_SR, + ..Default::default() + } + } + pub fn with_coefs(coefs: BiquadCoefs) -> Self { + Self { + coefs, + sample_rate: DEFAULT_SR, + ..Default::default() + } + } + pub fn coefs(&self) -> &BiquadCoefs { + &self.coefs + } + pub fn set_coefs(&mut self, coefs: BiquadCoefs) { + self.coefs = coefs; + } +} + +impl AudioNode for Biquad { + const ID: u64 = 15; + type Inputs = typenum::U1; + type Outputs = typenum::U1; + + fn reset(&mut self) { + self.x1 = F::zero(); + self.x2 = F::zero(); + self.y1 = F::zero(); + self.y2 = F::zero(); + } + + fn set_sample_rate(&mut self, sample_rate: f64) { + self.sample_rate = sample_rate; + } + + #[inline] + fn tick(&mut self, input: &Frame) -> Frame { + let x0 = convert(input[0]); + let y0 = self.coefs.b0 * x0 + self.coefs.b1 * self.x1 + self.coefs.b2 * self.x2 + - self.coefs.a1 * self.y1 + - self.coefs.a2 * self.y2; + self.x2 = self.x1; + self.x1 = x0; + self.y2 = self.y1; + self.y1 = y0; + [convert(y0)].into() + } + + fn set(&mut self, setting: Setting) { + if let Parameter::Biquad(a1, a2, b0, b1, b2) = setting.parameter() { + self.set_coefs(BiquadCoefs::arbitrary( + F::from_f32(*a1), + F::from_f32(*a2), + F::from_f32(*b0), + F::from_f32(*b1), + F::from_f32(*b2), + )); + } + } + + fn route(&mut self, input: &SignalFrame, frequency: f64) -> SignalFrame { + let mut output = SignalFrame::new(self.outputs()); + output.set( + 0, + input.at(0).filter(0.0, |r| { + r * self.coefs().response(frequency / self.sample_rate) + }), + ); + output + } +} + +/// Butterworth lowpass filter. +/// Setting: cutoff. +/// Number of inputs is `N`, either `U1` or `U2`. +/// - Input 0: input signal +/// - Input 1 (optional): cutoff frequency (Hz) +/// - Output 0: filtered signal +#[derive(Clone)] +pub struct ButterLowpass> { + _marker: PhantomData, + biquad: Biquad, + sample_rate: F, + cutoff: F, +} + +impl> ButterLowpass { + /// Create new Butterworth lowpass filter with initial `cutoff` frequency in Hz. + pub fn new(cutoff: F) -> Self { + let mut node = ButterLowpass { + _marker: PhantomData, + biquad: Biquad::new(), + sample_rate: F::from_f64(DEFAULT_SR), + cutoff: F::zero(), + }; + node.biquad.reset(); + node.set_cutoff(cutoff); + node + } + pub fn set_cutoff(&mut self, cutoff: F) { + self.biquad + .set_coefs(BiquadCoefs::butter_lowpass(self.sample_rate, cutoff)); + self.cutoff = cutoff; + } +} + +impl> AudioNode for ButterLowpass { + const ID: u64 = 16; + type Inputs = N; + type Outputs = typenum::U1; + + fn reset(&mut self) { + self.biquad.reset(); + } + + fn set_sample_rate(&mut self, sample_rate: f64) { + self.sample_rate = convert(sample_rate); + self.biquad.set_sample_rate(sample_rate); + self.set_cutoff(self.cutoff); + } + + #[inline] + fn tick(&mut self, input: &Frame) -> Frame { + if N::USIZE > 1 { + let cutoff: F = convert(input[1]); + if cutoff != self.cutoff { + self.set_cutoff(cutoff); + } + } + self.biquad.tick(&[input[0]].into()) + } + + fn set(&mut self, setting: Setting) { + if let Parameter::Center(cutoff) = setting.parameter() { + self.set_cutoff(F::from_f32(*cutoff)); + } + } + + fn route(&mut self, input: &SignalFrame, frequency: f64) -> SignalFrame { + let mut output = SignalFrame::new(self.outputs()); + output.set( + 0, + input.at(0).filter(0.0, |r| { + r * self + .biquad + .coefs() + .response(frequency / self.sample_rate.to_f64()) + }), + ); + output + } +} + +/// Constant-gain bandpass filter (resonator). +/// Filter gain is (nearly) independent of bandwidth. +/// Setting: (center, bandwidth). +/// Number of inputs is `N`, either `U1` or `U3`. +/// - Input 0: input signal +/// - Input 1 (optional): filter center frequency (peak) (Hz) +/// - Input 2 (optional): filter bandwidth (distance) between -3 dB points (Hz) +/// - Output 0: filtered signal +#[derive(Clone)] +pub struct Resonator> { + _marker: PhantomData, + biquad: Biquad, + sample_rate: F, + center: F, + bandwidth: F, +} + +impl> Resonator { + /// Create new resonator bandpass. Initial `center` frequency and `bandwidth` are specified in Hz. + pub fn new(center: F, bandwidth: F) -> Self { + let mut node = Resonator { + _marker: PhantomData, + biquad: Biquad::new(), + sample_rate: F::from_f64(DEFAULT_SR), + center, + bandwidth, + }; + node.biquad.reset(); + node.set_center_bandwidth(center, bandwidth); + node + } + pub fn set_center_bandwidth(&mut self, center: F, bandwidth: F) { + self.biquad + .set_coefs(BiquadCoefs::resonator(self.sample_rate, center, bandwidth)); + self.center = center; + self.bandwidth = bandwidth; + } +} + +impl> AudioNode for Resonator { + const ID: u64 = 17; + type Inputs = N; + type Outputs = typenum::U1; + + fn reset(&mut self) { + self.biquad.reset(); + } + + fn set_sample_rate(&mut self, sample_rate: f64) { + self.sample_rate = convert(sample_rate); + self.set_center_bandwidth(self.center, self.bandwidth); + } + + #[inline] + fn tick(&mut self, input: &Frame) -> Frame { + if N::USIZE >= 3 { + let center: F = convert(input[1]); + let bandwidth: F = convert(input[2]); + if center != self.center || bandwidth != self.bandwidth { + self.biquad + .set_coefs(BiquadCoefs::resonator(self.sample_rate, center, bandwidth)); + self.center = center; + self.bandwidth = bandwidth; + } + } + self.biquad.tick(&[input[0]].into()) + } + + fn set(&mut self, setting: Setting) { + match setting.parameter() { + Parameter::Center(center) => { + self.set_center_bandwidth(F::from_f32(*center), self.bandwidth) + } + Parameter::CenterBandwidth(center, bandwidth) => { + self.set_center_bandwidth(F::from_f32(*center), F::from_f32(*bandwidth)) + } + _ => (), + } + } + + fn route(&mut self, input: &SignalFrame, frequency: f64) -> SignalFrame { + let mut output = SignalFrame::new(self.outputs()); + output.set( + 0, + input.at(0).filter(0.0, |r| { + r * self + .biquad + .coefs() + .response(frequency / self.sample_rate.to_f64()) + }), + ); + output + } +} + +#[derive(Clone)] +/// Biquad in transposed direct form II with nonlinear feedback. +pub struct BiquadFb { + shape1: S, + shape2: S, + coefs: BiquadCoefs, + s1: F, + s2: F, +} + +// Transposed Direct Form II would be: +// y0 = b0 * x0 + s1 +// s1 = s2 + b1 * x0 - a1 * y0 +// s2 = b2 * x0 - a2 * y0 + +impl BiquadFb { + /* + inline float process (float x) override + { + // process input sample, direct form II transposed + float y = z[1] + x*b[0]; + z[1] = z[2] + x*b[1] - saturator (y)*a[1]; + z[2] = x*b[2] - saturator (y)*a[2]; + + return y; + } + */ +} + +impl AudioNode for BiquadFb { + const ID: u64 = 88; + /// Input arity. + type Inputs = typenum::U1; + /// Output arity. + type Outputs = typenum::U1; + + fn tick(&mut self, input: &Frame) -> Frame { + let x0 = F::from_f32(input[0]); + let y0 = self.coefs.b0 * x0 + self.s1; + self.s1 = self.s2 + self.coefs.b1 * x0 + - F::from_f32(self.shape1.shape(y0.to_f32())) * self.coefs.a1; + self.s2 = self.coefs.b2 * x0 - F::from_f32(self.shape2.shape(y0.to_f32())) * self.coefs.a2; + [y0.to_f32()].into() + } + + /* + fn tick(&mut self, input: &Frame) -> Frame { + let x0 = F::from_f32(input[0]); + let y0 = self.coefs.b0 * x0 + self.s1; + self.s1 = F::from_f32(self.shape1.shape((self.s2 + self.coefs.b1 * x0 - y0 * self.coefs.a1).to_f32())); + self.s2 = F::from_f32(self.shape2.shape((self.coefs.b2 * x0 - y0 * self.coefs.a2).to_f32())); + [y0.to_f32()].into() + } + */ +} diff --git a/src/denormal.rs b/src/denormal.rs new file mode 100644 index 0000000..2feebb0 --- /dev/null +++ b/src/denormal.rs @@ -0,0 +1,21 @@ +//! Denormal prevention. + +/// Attempt to set processor flags to prevent denormals. +#[inline] +pub fn prevent_denormals() { + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + { + #[cfg(all(target_arch = "x86_64", target_feature = "sse"))] + #[allow(deprecated)] + use core::arch::x86_64::_mm_setcsr; + + #[cfg(all(target_arch = "x86", target_feature = "sse"))] + use core::arch::x86::_mm_setcsr; + + // Treat denormals as zero while enabling all interrupt masks. + #[allow(deprecated)] + unsafe { + _mm_setcsr(0x9fc0) + }; + } +} diff --git a/src/feedback.rs b/src/feedback.rs index d79d06a..f24ad25 100644 --- a/src/feedback.rs +++ b/src/feedback.rs @@ -126,12 +126,14 @@ where #[inline] fn tick(&mut self, input: &Frame) -> Frame { + super::denormal::prevent_denormals(); let output = self.x.tick(&(input + self.value.clone())); self.value = self.feedback.frame(&output); output } fn process(&mut self, size: usize, input: &BufferRef, output: &mut BufferMut) { + super::denormal::prevent_denormals(); for i in 0..size { let input_frame = Frame::generate(|channel| input.at_f32(channel, i) + self.value[channel]); @@ -236,12 +238,14 @@ where #[inline] fn tick(&mut self, input: &Frame) -> Frame { + super::denormal::prevent_denormals(); let output = self.x.tick(&(input + self.value.clone())); self.value = self.feedback.frame(&self.y.tick(&output)); output } fn process(&mut self, size: usize, input: &BufferRef, output: &mut BufferMut) { + super::denormal::prevent_denormals(); for i in 0..size { let input_frame = Frame::generate(|channel| input.at_f32(channel, i) + self.value[channel]); @@ -350,6 +354,7 @@ impl AudioUnit for FeedbackUnit { } fn tick(&mut self, input: &[f32], output: &mut [f32]) { + super::denormal::prevent_denormals(); let read_i = self.read_index(self.samples); for (channel, (tick, i)) in self.tick_buffer.iter_mut().zip(input.iter()).enumerate() { *tick = *i + self.feedback[channel][read_i]; @@ -362,6 +367,7 @@ impl AudioUnit for FeedbackUnit { } fn process(&mut self, size: usize, input: &BufferRef, output: &mut BufferMut) { + super::denormal::prevent_denormals(); if size <= self.samples { // We have enough feedback samples to process the whole block at once. for channel in 0..self.channels { diff --git a/src/filter.rs b/src/filter.rs index f54c83b..9ece611 100644 --- a/src/filter.rs +++ b/src/filter.rs @@ -6,337 +6,9 @@ use super::setting::*; use super::signal::*; use super::*; use core::marker::PhantomData; -use num_complex::Complex64; use numeric_array::typenum::*; use numeric_array::*; -#[derive(Copy, Clone, Debug, Default)] -pub struct BiquadCoefs { - pub a1: F, - pub a2: F, - pub b0: F, - pub b1: F, - pub b2: F, -} - -impl BiquadCoefs { - /// Returns settings for a Butterworth lowpass filter. - /// Cutoff is the -3 dB point of the filter in Hz. - pub fn butter_lowpass(sample_rate: F, cutoff: F) -> Self { - let c = F::from_f64; - let f: F = tan(cutoff * F::PI / sample_rate); - let a0r: F = c(1.0) / (c(1.0) + F::SQRT_2 * f + f * f); - let a1: F = (c(2.0) * f * f - c(2.0)) * a0r; - let a2: F = (c(1.0) - F::SQRT_2 * f + f * f) * a0r; - let b0: F = f * f * a0r; - let b1: F = c(2.0) * b0; - let b2: F = b0; - Self { a1, a2, b0, b1, b2 } - } - - /// Returns settings for a constant-gain bandpass resonator. - /// The center frequency is given in Hz. - /// Bandwidth is the difference in Hz between -3 dB points of the filter response. - /// The overall gain of the filter is independent of bandwidth. - pub fn resonator(sample_rate: F, center: F, bandwidth: F) -> Self { - let c = F::from_f64; - let r: F = exp(-F::PI * bandwidth / sample_rate); - let a1: F = c(-2.0) * r * cos(F::TAU * center / sample_rate); - let a2: F = r * r; - let b0: F = sqrt(c(1.0) - r * r) * c(0.5); - let b1: F = c(0.0); - let b2: F = -b0; - Self { a1, a2, b0, b1, b2 } - } - - /// Arbitrary biquad. - pub fn arbitrary(a1: F, a2: F, b0: F, b1: F, b2: F) -> Self { - Self { a1, a2, b0, b1, b2 } - } - - /// Frequency response at frequency `omega` expressed as fraction of sampling rate. - pub fn response(&self, omega: f64) -> Complex64 { - let z1 = Complex64::from_polar(1.0, -f64::TAU * omega); - let z2 = z1 * z1; - /// Complex64 with real component `x` and imaginary component zero. - fn re(x: T) -> Complex64 { - Complex64::new(x.to_f64(), 0.0) - } - (re(self.b0) + re(self.b1) * z1 + re(self.b2) * z2) - / (re(1.0) + re(self.a1) * z1 + re(self.a2) * z2) - } -} - -/// 2nd order IIR filter implemented in normalized Direct Form I. -/// - Setting: coefficients as tuple Parameter::Biquad(a1, a2, b0, b1, b2). -/// - Input 0: input signal. -/// - Output 0: filtered signal. -#[derive(Default, Clone)] -pub struct Biquad { - coefs: BiquadCoefs, - x1: F, - x2: F, - y1: F, - y2: F, - sample_rate: f64, -} - -impl Biquad { - pub fn new() -> Self { - Self { - sample_rate: DEFAULT_SR, - ..Default::default() - } - } - pub fn with_coefs(coefs: BiquadCoefs) -> Self { - Self { - coefs, - sample_rate: DEFAULT_SR, - ..Default::default() - } - } - pub fn coefs(&self) -> &BiquadCoefs { - &self.coefs - } - pub fn set_coefs(&mut self, coefs: BiquadCoefs) { - self.coefs = coefs; - } -} - -impl AudioNode for Biquad { - const ID: u64 = 15; - type Inputs = typenum::U1; - type Outputs = typenum::U1; - - fn reset(&mut self) { - self.x1 = F::zero(); - self.x2 = F::zero(); - self.y1 = F::zero(); - self.y2 = F::zero(); - } - - fn set_sample_rate(&mut self, sample_rate: f64) { - self.sample_rate = sample_rate; - } - - #[inline] - fn tick(&mut self, input: &Frame) -> Frame { - let x0 = convert(input[0]); - let y0 = self.coefs.b0 * x0 + self.coefs.b1 * self.x1 + self.coefs.b2 * self.x2 - - self.coefs.a1 * self.y1 - - self.coefs.a2 * self.y2; - self.x2 = self.x1; - self.x1 = x0; - self.y2 = self.y1; - self.y1 = y0; - [convert(y0)].into() - - // Transposed Direct Form II would be: - // y0 = b0 * x0 + s1 - // s1 = s2 + b1 * x0 - a1 * y0 - // s2 = b2 * x0 - a2 * y0 - } - - fn set(&mut self, setting: Setting) { - if let Parameter::Biquad(a1, a2, b0, b1, b2) = setting.parameter() { - self.set_coefs(BiquadCoefs::arbitrary( - F::from_f32(*a1), - F::from_f32(*a2), - F::from_f32(*b0), - F::from_f32(*b1), - F::from_f32(*b2), - )); - } - } - - fn route(&mut self, input: &SignalFrame, frequency: f64) -> SignalFrame { - let mut output = SignalFrame::new(self.outputs()); - output.set( - 0, - input.at(0).filter(0.0, |r| { - r * self.coefs().response(frequency / self.sample_rate) - }), - ); - output - } -} - -/// Butterworth lowpass filter. -/// Setting: cutoff. -/// Number of inputs is `N`, either `U1` or `U2`. -/// - Input 0: input signal -/// - Input 1 (optional): cutoff frequency (Hz) -/// - Output 0: filtered signal -#[derive(Clone)] -pub struct ButterLowpass> { - _marker: PhantomData, - biquad: Biquad, - sample_rate: F, - cutoff: F, -} - -impl> ButterLowpass { - /// Create new Butterworth lowpass filter with initial `cutoff` frequency in Hz. - pub fn new(cutoff: F) -> Self { - let mut node = ButterLowpass { - _marker: PhantomData, - biquad: Biquad::new(), - sample_rate: F::from_f64(DEFAULT_SR), - cutoff: F::zero(), - }; - node.biquad.reset(); - node.set_cutoff(cutoff); - node - } - pub fn set_cutoff(&mut self, cutoff: F) { - self.biquad - .set_coefs(BiquadCoefs::butter_lowpass(self.sample_rate, cutoff)); - self.cutoff = cutoff; - } -} - -impl> AudioNode for ButterLowpass { - const ID: u64 = 16; - type Inputs = N; - type Outputs = typenum::U1; - - fn reset(&mut self) { - self.biquad.reset(); - } - - fn set_sample_rate(&mut self, sample_rate: f64) { - self.sample_rate = convert(sample_rate); - self.biquad.set_sample_rate(sample_rate); - self.set_cutoff(self.cutoff); - } - - #[inline] - fn tick(&mut self, input: &Frame) -> Frame { - if N::USIZE > 1 { - let cutoff: F = convert(input[1]); - if cutoff != self.cutoff { - self.set_cutoff(cutoff); - } - } - self.biquad.tick(&[input[0]].into()) - } - - fn set(&mut self, setting: Setting) { - if let Parameter::Center(cutoff) = setting.parameter() { - self.set_cutoff(F::from_f32(*cutoff)); - } - } - - fn route(&mut self, input: &SignalFrame, frequency: f64) -> SignalFrame { - let mut output = SignalFrame::new(self.outputs()); - output.set( - 0, - input.at(0).filter(0.0, |r| { - r * self - .biquad - .coefs() - .response(frequency / self.sample_rate.to_f64()) - }), - ); - output - } -} - -/// Constant-gain bandpass filter (resonator). -/// Filter gain is (nearly) independent of bandwidth. -/// Setting: (center, bandwidth). -/// Number of inputs is `N`, either `U1` or `U3`. -/// - Input 0: input signal -/// - Input 1 (optional): filter center frequency (peak) (Hz) -/// - Input 2 (optional): filter bandwidth (distance) between -3 dB points (Hz) -/// - Output 0: filtered signal -#[derive(Clone)] -pub struct Resonator> { - _marker: PhantomData, - biquad: Biquad, - sample_rate: F, - center: F, - bandwidth: F, -} - -impl> Resonator { - /// Create new resonator bandpass. Initial `center` frequency and `bandwidth` are specified in Hz. - pub fn new(center: F, bandwidth: F) -> Self { - let mut node = Resonator { - _marker: PhantomData, - biquad: Biquad::new(), - sample_rate: F::from_f64(DEFAULT_SR), - center, - bandwidth, - }; - node.biquad.reset(); - node.set_center_bandwidth(center, bandwidth); - node - } - pub fn set_center_bandwidth(&mut self, center: F, bandwidth: F) { - self.biquad - .set_coefs(BiquadCoefs::resonator(self.sample_rate, center, bandwidth)); - self.center = center; - self.bandwidth = bandwidth; - } -} - -impl> AudioNode for Resonator { - const ID: u64 = 17; - type Inputs = N; - type Outputs = typenum::U1; - - fn reset(&mut self) { - self.biquad.reset(); - } - - fn set_sample_rate(&mut self, sample_rate: f64) { - self.sample_rate = convert(sample_rate); - self.set_center_bandwidth(self.center, self.bandwidth); - } - - #[inline] - fn tick(&mut self, input: &Frame) -> Frame { - if N::USIZE >= 3 { - let center: F = convert(input[1]); - let bandwidth: F = convert(input[2]); - if center != self.center || bandwidth != self.bandwidth { - self.biquad - .set_coefs(BiquadCoefs::resonator(self.sample_rate, center, bandwidth)); - self.center = center; - self.bandwidth = bandwidth; - } - } - self.biquad.tick(&[input[0]].into()) - } - - fn set(&mut self, setting: Setting) { - match setting.parameter() { - Parameter::Center(center) => { - self.set_center_bandwidth(F::from_f32(*center), self.bandwidth) - } - Parameter::CenterBandwidth(center, bandwidth) => { - self.set_center_bandwidth(F::from_f32(*center), F::from_f32(*bandwidth)) - } - _ => (), - } - } - - fn route(&mut self, input: &SignalFrame, frequency: f64) -> SignalFrame { - let mut output = SignalFrame::new(self.outputs()); - output.set( - 0, - input.at(0).filter(0.0, |r| { - r * self - .biquad - .coefs() - .response(frequency / self.sample_rate.to_f64()) - }), - ); - output - } -} - /// One-pole lowpass filter. /// Setting: cutoff. /// The number of inputs is `N`, either `U1` or `U2`. diff --git a/src/hacker.rs b/src/hacker.rs index 762976b..617feba 100644 --- a/src/hacker.rs +++ b/src/hacker.rs @@ -6,6 +6,7 @@ use alloc::sync::Arc; pub use super::audionode::*; pub use super::audiounit::*; +pub use super::biquad::*; pub use super::buffer::*; pub use super::combinator::*; pub use super::delay::*; diff --git a/src/hacker32.rs b/src/hacker32.rs index b11df0b..d824d85 100644 --- a/src/hacker32.rs +++ b/src/hacker32.rs @@ -6,6 +6,7 @@ use alloc::sync::Arc; pub use super::audionode::*; pub use super::audiounit::*; +pub use super::biquad::*; pub use super::buffer::*; pub use super::combinator::*; pub use super::delay::*; diff --git a/src/lib.rs b/src/lib.rs index 21eb34a..df53bd1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -518,9 +518,11 @@ impl Real for f64 { pub mod adsr; pub mod audionode; pub mod audiounit; +pub mod biquad; pub mod buffer; pub mod combinator; pub mod delay; +pub mod denormal; pub mod dynamics; pub mod envelope; pub mod feedback; diff --git a/src/prelude.rs b/src/prelude.rs index e1e4888..1818343 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -6,6 +6,7 @@ use alloc::sync::Arc; pub use super::audionode::*; pub use super::audiounit::*; +pub use super::biquad::*; pub use super::buffer::*; pub use super::combinator::*; pub use super::delay::*; @@ -2717,7 +2718,7 @@ pub fn phaser f32 + Clone + Send + Sync>( ) -> An> { pass() & feedback( - (pass() | lfo(move |t| lerp(1.0, 10.0, phase_f(t)))) + (pass() | lfo(move |t| lerp(2.0, 20.0, clamp01(phase_f(t))))) >> pipei::(|_i| (pass() | add(0.05)) >> !allpole::()) >> (mul(feedback_amount) | sink()), ) diff --git a/src/shape.rs b/src/shape.rs index 6cc8f83..6a349c4 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -43,7 +43,7 @@ impl f32 + Clone + Sync + Send> Shape for ShapeFn { } } -/// Clip signal to -1...1. +/// Clamp signal multiplied by the hardness parameter to -1...1. #[derive(Clone)] pub struct Clip(pub f32); @@ -58,7 +58,7 @@ impl Shape for Clip { } } -/// Clip signal between the two arguments (minimum and maximum). +/// Clamp signal between the two arguments (minimum and maximum). #[derive(Clone)] pub struct ClipTo(pub f32, pub f32); @@ -155,17 +155,20 @@ impl Shape for SoftCrush { } } -/// Adaptive normalizing distortion with smoothing timescale and hardness as parameters. +/// Adaptive normalizing distortion with smoothing timescale and inner shape as parameters. /// Smoothing timescale is specified in seconds. /// It is the time it takes for level estimation to move halfway to a new level. -/// The argument to `tanh` is divided by the RMS level of the signal and multiplied by hardness. -/// Minimum estimated signal level for adaptive distortion is approximately -60 dB. +/// Minimum estimated signal level for adaptive distortion is -60 dB. +/// The argument to the inner shape is divided by the RMS level of the signal. #[derive(Clone)] pub struct Adaptive { + /// Inner shape. inner: S, + /// Smoothing timescale in seconds. timescale: f32, /// Per-sample smoothing factor. smoothing: f32, + /// Current level estimate. state: f32, } @@ -190,7 +193,7 @@ impl Shape for Adaptive { self.inner.shape(input / sqrt(self.state)) } fn reset(&mut self) { - self.state = 1.0e-6; + self.state = 1.0e-3; self.inner.reset(); } fn set_sample_rate(&mut self, sample_rate: f64) { diff --git a/src/svf.rs b/src/svf.rs index b6f2484..8a3399a 100644 --- a/src/svf.rs +++ b/src/svf.rs @@ -14,7 +14,7 @@ use numeric_array::typenum::*; /// State variable filter coefficients, generic formulation. #[derive(Clone, Default)] -pub struct SvfCoeffs { +pub struct SvfCoefs { pub a1: F, pub a2: F, pub a3: F, @@ -23,7 +23,7 @@ pub struct SvfCoeffs { pub m2: F, } -impl SvfCoeffs { +impl SvfCoefs { /// Calculate coefficients for a lowpass filter. pub fn lowpass(sample_rate: F, cutoff: F, q: F) -> Self { let g = tan(F::from_f64(f64::PI) * cutoff / sample_rate); @@ -35,7 +35,7 @@ impl SvfCoeffs { let m1 = F::zero(); let m2 = F::one(); - SvfCoeffs { + SvfCoefs { a1, a2, a3, @@ -56,7 +56,7 @@ impl SvfCoeffs { let m1 = -k; let m2 = -F::one(); - SvfCoeffs { + SvfCoefs { a1, a2, a3, @@ -77,7 +77,7 @@ impl SvfCoeffs { let m1 = F::one(); let m2 = F::zero(); - SvfCoeffs { + SvfCoefs { a1, a2, a3, @@ -98,7 +98,7 @@ impl SvfCoeffs { let m1 = -k; let m2 = F::zero(); - SvfCoeffs { + SvfCoefs { a1, a2, a3, @@ -119,7 +119,7 @@ impl SvfCoeffs { let m1 = -k; let m2 = F::new(-2); - SvfCoeffs { + SvfCoefs { a1, a2, a3, @@ -140,7 +140,7 @@ impl SvfCoeffs { let m1 = F::new(-2) * k; let m2 = F::zero(); - SvfCoeffs { + SvfCoefs { a1, a2, a3, @@ -163,7 +163,7 @@ impl SvfCoeffs { let m1 = k * (a * a - F::one()); let m2 = F::zero(); - SvfCoeffs { + SvfCoefs { a1, a2, a3, @@ -186,7 +186,7 @@ impl SvfCoeffs { let m1 = k * (a - F::one()); let m2 = a * a - F::one(); - SvfCoeffs { + SvfCoefs { a1, a2, a3, @@ -209,7 +209,7 @@ impl SvfCoeffs { let m1 = k * (F::one() - a) * a; let m2 = F::one() - a * a; - SvfCoeffs { + SvfCoefs { a1, a2, a3, @@ -231,30 +231,30 @@ pub trait SvfMode: Clone + Default + Sync + Send { type Setting: Sync + Send + Clone + Default; /// Update coefficients and parameters from settings. - fn set(&mut self, setting: Self::Setting, params: &mut SvfParams, coeffs: &mut SvfCoeffs); + fn set(&mut self, setting: Self::Setting, params: &mut SvfParams, coefs: &mut SvfCoefs); /// Update coefficients and state from the full set of parameters. - fn update(&mut self, params: &SvfParams, coeffs: &mut SvfCoeffs); + fn update(&mut self, params: &SvfParams, coefs: &mut SvfCoefs); /// Update coefficients and state from sample rate and/or cutoff. /// Other parameters are untouched. - fn update_frequency(&mut self, params: &SvfParams, coeffs: &mut SvfCoeffs) { + fn update_frequency(&mut self, params: &SvfParams, coefs: &mut SvfCoefs) { // Do a bulk update by default. - self.update(params, coeffs); + self.update(params, coefs); } /// Update coefficients and state from Q. /// Other parameters are untouched. - fn update_q(&mut self, params: &SvfParams, coeffs: &mut SvfCoeffs) { + fn update_q(&mut self, params: &SvfParams, coefs: &mut SvfCoefs) { // Do a bulk update by default. - self.update(params, coeffs); + self.update(params, coefs); } /// Update coefficients and state from gain. Gain is given as amplitude. /// Other parameters are untouched. /// Only equalizing modes (bell and shelf) support gain. It is ignored by default. #[allow(unused_variables)] - fn update_gain(&mut self, params: &SvfParams, coeffs: &mut SvfCoeffs) {} + fn update_gain(&mut self, params: &SvfParams, coefs: &mut SvfCoefs) {} /// Update coefficients, parameters and state from input. #[allow(unused_variables)] @@ -262,7 +262,7 @@ pub trait SvfMode: Clone + Default + Sync + Send { &mut self, input: &Frame, params: &mut SvfParams, - coeffs: &mut SvfCoeffs, + coefs: &mut SvfCoefs, ) { } @@ -302,28 +302,28 @@ impl SvfMode for LowpassMode { &mut self, (cutoff, q): Self::Setting, params: &mut SvfParams, - coeffs: &mut SvfCoeffs, + coefs: &mut SvfCoefs, ) { params.cutoff = cutoff; params.q = q; - self.update(params, coeffs); + self.update(params, coefs); } - fn update(&mut self, params: &SvfParams, coeffs: &mut SvfCoeffs) { - *coeffs = SvfCoeffs::lowpass(params.sample_rate, params.cutoff, params.q); + fn update(&mut self, params: &SvfParams, coefs: &mut SvfCoefs) { + *coefs = SvfCoefs::lowpass(params.sample_rate, params.cutoff, params.q); } #[inline] fn update_inputs( &mut self, input: &Frame, params: &mut SvfParams, - coeffs: &mut SvfCoeffs, + coefs: &mut SvfCoefs, ) { let cutoff = input[1]; let q = input[2]; if cutoff != params.cutoff || q != params.q { params.cutoff = cutoff; params.q = q; - self.update(params, coeffs); + self.update(params, coefs); } } @@ -361,28 +361,28 @@ impl SvfMode for HighpassMode { &mut self, (cutoff, q): Self::Setting, params: &mut SvfParams, - coeffs: &mut SvfCoeffs, + coefs: &mut SvfCoefs, ) { params.cutoff = cutoff; params.q = q; - self.update(params, coeffs); + self.update(params, coefs); } - fn update(&mut self, params: &SvfParams, coeffs: &mut SvfCoeffs) { - *coeffs = SvfCoeffs::highpass(params.sample_rate, params.cutoff, params.q); + fn update(&mut self, params: &SvfParams, coefs: &mut SvfCoefs) { + *coefs = SvfCoefs::highpass(params.sample_rate, params.cutoff, params.q); } #[inline] fn update_inputs( &mut self, input: &Frame, params: &mut SvfParams, - coeffs: &mut SvfCoeffs, + coefs: &mut SvfCoefs, ) { let cutoff = input[1]; let q = input[2]; if cutoff != params.cutoff || q != params.q { params.cutoff = cutoff; params.q = q; - self.update(params, coeffs); + self.update(params, coefs); } } @@ -420,28 +420,28 @@ impl SvfMode for BandpassMode { &mut self, (cutoff, q): Self::Setting, params: &mut SvfParams, - coeffs: &mut SvfCoeffs, + coefs: &mut SvfCoefs, ) { params.cutoff = cutoff; params.q = q; - self.update(params, coeffs); + self.update(params, coefs); } - fn update(&mut self, params: &SvfParams, coeffs: &mut SvfCoeffs) { - *coeffs = SvfCoeffs::bandpass(params.sample_rate, params.cutoff, params.q); + fn update(&mut self, params: &SvfParams, coefs: &mut SvfCoefs) { + *coefs = SvfCoefs::bandpass(params.sample_rate, params.cutoff, params.q); } #[inline] fn update_inputs( &mut self, input: &Frame, params: &mut SvfParams, - coeffs: &mut SvfCoeffs, + coefs: &mut SvfCoefs, ) { let cutoff = input[1]; let q = input[2]; if cutoff != params.cutoff || q != params.q { params.cutoff = cutoff; params.q = q; - self.update(params, coeffs); + self.update(params, coefs); } } @@ -479,28 +479,28 @@ impl SvfMode for NotchMode { &mut self, (cutoff, q): Self::Setting, params: &mut SvfParams, - coeffs: &mut SvfCoeffs, + coefs: &mut SvfCoefs, ) { params.cutoff = cutoff; params.q = q; - self.update(params, coeffs); + self.update(params, coefs); } - fn update(&mut self, params: &SvfParams, coeffs: &mut SvfCoeffs) { - *coeffs = SvfCoeffs::notch(params.sample_rate, params.cutoff, params.q); + fn update(&mut self, params: &SvfParams, coefs: &mut SvfCoefs) { + *coefs = SvfCoefs::notch(params.sample_rate, params.cutoff, params.q); } #[inline] fn update_inputs( &mut self, input: &Frame, params: &mut SvfParams, - coeffs: &mut SvfCoeffs, + coefs: &mut SvfCoefs, ) { let cutoff = input[1]; let q = input[2]; if cutoff != params.cutoff || q != params.q { params.cutoff = cutoff; params.q = q; - self.update(params, coeffs); + self.update(params, coefs); } } @@ -538,28 +538,28 @@ impl SvfMode for PeakMode { &mut self, (cutoff, q): Self::Setting, params: &mut SvfParams, - coeffs: &mut SvfCoeffs, + coefs: &mut SvfCoefs, ) { params.cutoff = cutoff; params.q = q; - self.update(params, coeffs); + self.update(params, coefs); } - fn update(&mut self, params: &SvfParams, coeffs: &mut SvfCoeffs) { - *coeffs = SvfCoeffs::peak(params.sample_rate, params.cutoff, params.q); + fn update(&mut self, params: &SvfParams, coefs: &mut SvfCoefs) { + *coefs = SvfCoefs::peak(params.sample_rate, params.cutoff, params.q); } #[inline] fn update_inputs( &mut self, input: &Frame, params: &mut SvfParams, - coeffs: &mut SvfCoeffs, + coefs: &mut SvfCoefs, ) { let cutoff = input[1]; let q = input[2]; if cutoff != params.cutoff || q != params.q { params.cutoff = cutoff; params.q = q; - self.update(params, coeffs); + self.update(params, coefs); } } @@ -598,28 +598,28 @@ impl SvfMode for AllpassMode { &mut self, (cutoff, q): Self::Setting, params: &mut SvfParams, - coeffs: &mut SvfCoeffs, + coefs: &mut SvfCoefs, ) { params.cutoff = cutoff; params.q = q; - self.update(params, coeffs); + self.update(params, coefs); } - fn update(&mut self, params: &SvfParams, coeffs: &mut SvfCoeffs) { - *coeffs = SvfCoeffs::allpass(params.sample_rate, params.cutoff, params.q); + fn update(&mut self, params: &SvfParams, coefs: &mut SvfCoefs) { + *coefs = SvfCoefs::allpass(params.sample_rate, params.cutoff, params.q); } #[inline] fn update_inputs( &mut self, input: &Frame, params: &mut SvfParams, - coeffs: &mut SvfCoeffs, + coefs: &mut SvfCoefs, ) { let cutoff = input[1]; let q = input[2]; if cutoff != params.cutoff || q != params.q { params.cutoff = cutoff; params.q = q; - self.update(params, coeffs); + self.update(params, coefs); } } @@ -658,22 +658,22 @@ impl SvfMode for BellMode { &mut self, (cutoff, q, gain): Self::Setting, params: &mut SvfParams, - coeffs: &mut SvfCoeffs, + coefs: &mut SvfCoefs, ) { params.cutoff = cutoff; params.q = q; params.gain = gain; - self.update(params, coeffs); + self.update(params, coefs); } - fn update(&mut self, params: &SvfParams, coeffs: &mut SvfCoeffs) { - *coeffs = SvfCoeffs::bell(params.sample_rate, params.cutoff, params.q, params.gain); + fn update(&mut self, params: &SvfParams, coefs: &mut SvfCoefs) { + *coefs = SvfCoefs::bell(params.sample_rate, params.cutoff, params.q, params.gain); } #[inline] fn update_inputs( &mut self, input: &Frame, params: &mut SvfParams, - coeffs: &mut SvfCoeffs, + coefs: &mut SvfCoefs, ) { let cutoff = input[1]; let q = input[2]; @@ -682,7 +682,7 @@ impl SvfMode for BellMode { params.cutoff = cutoff; params.q = q; params.gain = gain; - self.update(params, coeffs); + self.update(params, coefs); } } @@ -723,22 +723,22 @@ impl SvfMode for LowshelfMode { &mut self, (cutoff, q, gain): Self::Setting, params: &mut SvfParams, - coeffs: &mut SvfCoeffs, + coefs: &mut SvfCoefs, ) { params.cutoff = cutoff; params.q = q; params.gain = gain; - self.update(params, coeffs); + self.update(params, coefs); } - fn update(&mut self, params: &SvfParams, coeffs: &mut SvfCoeffs) { - *coeffs = SvfCoeffs::lowshelf(params.sample_rate, params.cutoff, params.q, params.gain); + fn update(&mut self, params: &SvfParams, coefs: &mut SvfCoefs) { + *coefs = SvfCoefs::lowshelf(params.sample_rate, params.cutoff, params.q, params.gain); } #[inline] fn update_inputs( &mut self, input: &Frame, params: &mut SvfParams, - coeffs: &mut SvfCoeffs, + coefs: &mut SvfCoefs, ) { let cutoff = input[1]; let q = input[2]; @@ -747,7 +747,7 @@ impl SvfMode for LowshelfMode { params.cutoff = cutoff; params.q = q; params.gain = gain; - self.update(params, coeffs); + self.update(params, coefs); } } @@ -791,22 +791,22 @@ impl SvfMode for HighshelfMode { &mut self, (cutoff, q, gain): Self::Setting, params: &mut SvfParams, - coeffs: &mut SvfCoeffs, + coefs: &mut SvfCoefs, ) { params.cutoff = cutoff; params.q = q; params.gain = gain; - self.update(params, coeffs); + self.update(params, coefs); } - fn update(&mut self, params: &SvfParams, coeffs: &mut SvfCoeffs) { - *coeffs = SvfCoeffs::highshelf(params.sample_rate, params.cutoff, params.q, params.gain); + fn update(&mut self, params: &SvfParams, coefs: &mut SvfCoefs) { + *coefs = SvfCoefs::highshelf(params.sample_rate, params.cutoff, params.q, params.gain); } #[inline] fn update_inputs( &mut self, input: &Frame, params: &mut SvfParams, - coeffs: &mut SvfCoeffs, + coefs: &mut SvfCoefs, ) { let cutoff = input[1]; let q = input[2]; @@ -815,7 +815,7 @@ impl SvfMode for HighshelfMode { params.cutoff = cutoff; params.q = q; params.gain = gain; - self.update(params, coeffs); + self.update(params, coefs); } } @@ -851,7 +851,7 @@ where { mode: M, params: SvfParams, - coeffs: SvfCoeffs, + coefs: SvfCoefs, ic1eq: F, ic2eq: F, } @@ -864,13 +864,13 @@ where { pub fn new(mode: M, params: &SvfParams) -> Self { let params = params.clone(); - let mut coeffs = SvfCoeffs::default(); + let mut coefs = SvfCoefs::default(); let mut mode = mode; - mode.update(¶ms, &mut coeffs); + mode.update(¶ms, &mut coefs); Svf { mode, params, - coeffs, + coefs, ic1eq: F::zero(), ic2eq: F::zero(), } @@ -920,7 +920,7 @@ where fn set_sample_rate(&mut self, sample_rate: f64) { self.params.sample_rate = convert(sample_rate); - self.mode.update_frequency(&self.params, &mut self.coeffs); + self.mode.update_frequency(&self.params, &mut self.coefs); } #[inline] @@ -928,15 +928,15 @@ where // Update parameters from input. let input = Frame::generate(|i| convert(input[i])); self.mode - .update_inputs(&input, &mut self.params, &mut self.coeffs); + .update_inputs(&input, &mut self.params, &mut self.coefs); let v0 = input[0]; let v3 = v0 - self.ic2eq; - let v1 = self.coeffs.a1 * self.ic1eq + self.coeffs.a2 * v3; - let v2 = self.ic2eq + self.coeffs.a2 * self.ic1eq + self.coeffs.a3 * v3; + let v1 = self.coefs.a1 * self.ic1eq + self.coefs.a2 * v3; + let v2 = self.ic2eq + self.coefs.a2 * self.ic1eq + self.coefs.a3 * v3; self.ic1eq = F::new(2) * v1 - self.ic1eq; self.ic2eq = F::new(2) * v2 - self.ic2eq; [convert( - self.coeffs.m0 * v0 + self.coeffs.m1 * v1 + self.coeffs.m2 * v2, + self.coefs.m0 * v0 + self.coefs.m1 * v1 + self.coefs.m2 * v2, )] .into() } @@ -965,7 +965,7 @@ where { mode: M, params: SvfParams, - coeffs: SvfCoeffs, + coefs: SvfCoefs, ic1eq: F, ic2eq: F, } @@ -977,13 +977,13 @@ where { pub fn new(mode: M, params: &SvfParams) -> Self { let params = params.clone(); - let mut coeffs = SvfCoeffs::default(); + let mut coefs = SvfCoefs::default(); let mut mode = mode; - mode.update(¶ms, &mut coeffs); + mode.update(¶ms, &mut coefs); FixedSvf { mode, params, - coeffs, + coefs, ic1eq: F::zero(), ic2eq: F::zero(), } @@ -1019,7 +1019,7 @@ where #[inline] pub fn set_cutoff(&mut self, cutoff: F) { self.params.cutoff = cutoff; - self.mode.update_frequency(&self.params, &mut self.coeffs); + self.mode.update_frequency(&self.params, &mut self.coefs); } /// Set filter center in Hz. Synonymous with `set_cutoff`. @@ -1033,7 +1033,7 @@ where pub fn set_cutoff_q(&mut self, cutoff: F, q: F) { self.params.cutoff = cutoff; self.params.q = q; - self.mode.update_frequency(&self.params, &mut self.coeffs); + self.mode.update_frequency(&self.params, &mut self.coefs); } /// Set filter center in Hz and Q. Synonymous with `set_cutoff_q`. @@ -1046,14 +1046,14 @@ where #[inline] pub fn set_q(&mut self, q: F) { self.params.q = q; - self.mode.update_q(&self.params, &mut self.coeffs); + self.mode.update_q(&self.params, &mut self.coefs); } /// Set filter gain. Only equalizing modes support gain. Other modes ignore it. #[inline] pub fn set_gain(&mut self, gain: F) { self.params.gain = gain; - self.mode.update_gain(&self.params, &mut self.coeffs); + self.mode.update_gain(&self.params, &mut self.coefs); } /// Set filter cutoff in Hz, Q and gain. Synonymous with `set_center_q_gain`. @@ -1062,7 +1062,7 @@ where self.params.cutoff = cutoff; self.params.q = q; self.params.gain = gain; - self.mode.update(&self.params, &mut self.coeffs); + self.mode.update(&self.params, &mut self.coefs); } /// Set filter center in Hz, Q and gain. Synonymous with `set_cutoff_q_gain`. @@ -1088,19 +1088,19 @@ where fn set_sample_rate(&mut self, sample_rate: f64) { self.params.sample_rate = convert(sample_rate); - self.mode.update_frequency(&self.params, &mut self.coeffs); + self.mode.update_frequency(&self.params, &mut self.coefs); } #[inline] fn tick(&mut self, input: &Frame) -> Frame { let v0 = convert(input[0]); let v3 = v0 - self.ic2eq; - let v1 = self.coeffs.a1 * self.ic1eq + self.coeffs.a2 * v3; - let v2 = self.ic2eq + self.coeffs.a2 * self.ic1eq + self.coeffs.a3 * v3; + let v1 = self.coefs.a1 * self.ic1eq + self.coefs.a2 * v3; + let v2 = self.ic2eq + self.coefs.a2 * self.ic1eq + self.coefs.a3 * v3; self.ic1eq = F::new(2) * v1 - self.ic1eq; self.ic2eq = F::new(2) * v2 - self.ic2eq; [convert( - self.coeffs.m0 * v0 + self.coeffs.m1 * v1 + self.coeffs.m2 * v2, + self.coefs.m0 * v0 + self.coefs.m1 * v1 + self.coefs.m2 * v2, )] .into() }