From 6d14b44c2a3681df616c9a6ab1e405b9a697efc7 Mon Sep 17 00:00:00 2001 From: Nathan Graule Date: Fri, 16 Feb 2024 22:43:53 +0100 Subject: [PATCH 01/21] refactor: move diodeclipper/svfmixer to nih-plug integration --- Cargo.lock | 3 + Cargo.toml | 5 +- examples/diodeclipper/Cargo.toml | 6 +- examples/diodeclipper/src/dsp.rs | 234 ++++++++++++++++++++++++++ examples/diodeclipper/src/lib.rs | 275 +++++++------------------------ examples/svfmixer/Cargo.toml | 2 +- examples/svfmixer/src/dsp.rs | 2 +- examples/svfmixer/src/lib.rs | 127 +++----------- src/contrib/mod.rs | 2 + src/contrib/nih_plug.rs | 172 +++++++++++++++++++ src/dsp/parameter.rs | 27 ++- src/oversample/mod.rs | 44 +++-- src/saturators/clippers/mod.rs | 6 + 13 files changed, 567 insertions(+), 338 deletions(-) create mode 100644 examples/diodeclipper/src/dsp.rs create mode 100644 src/contrib/nih_plug.rs diff --git a/Cargo.lock b/Cargo.lock index f81af25..7abf129 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1211,7 +1211,9 @@ dependencies = [ name = "diodeclipper" version = "0.1.0" dependencies = [ + "enum-map", "nih_plug", + "num-traits", "valib", ] @@ -4276,6 +4278,7 @@ dependencies = [ "fundsp", "insta", "nalgebra", + "nih_plug", "num-traits", "numeric-array", "numeric_literals", diff --git a/Cargo.toml b/Cargo.toml index 0d71317..dff9696 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ keywords = ["virtual-analog", "audio", "plugin", "va-modeling", "dsp"] enum-map = "2.7.3" nih_plug = { git = "https://github.com/robbert-vdh/nih-plug.git" } nih_plug_vizia = { git = "https://github.com/robbert-vdh/nih-plug.git" } +num-traits = "0.2.18" nalgebra = "0.32.3" @@ -29,7 +30,8 @@ az = "1.2.1" enum-map.workspace = true fundsp = { version = "0.16.0", optional = true } nalgebra.workspace = true -num-traits = "0.2.18" +nih_plug = { workspace = true, optional = true } +num-traits.workspace = true numeric-array = { version = "0.5.2", optional = true } numeric_literals = "0.2.0" portable-atomic = { version = "1.6.0", features = ["float"] } @@ -43,6 +45,7 @@ serde = "*" [features] fundsp = ["dep:fundsp", "dep:numeric-array", "dep:typenum"] +nih-plug = ["dep:nih_plug"] [profile.dev] opt-level = 1 diff --git a/examples/diodeclipper/Cargo.toml b/examples/diodeclipper/Cargo.toml index ae2bd13..5014876 100644 --- a/examples/diodeclipper/Cargo.toml +++ b/examples/diodeclipper/Cargo.toml @@ -13,5 +13,7 @@ keywords.workspace = true crate-type = ["cdylib"] [dependencies] -nih_plug = { workspace = true } -valib = { path = "../.." } \ No newline at end of file +enum-map.workspace = true +nih_plug.workspace = true +num-traits.workspace = true +valib = { path = "../..", features = ["nih-plug"] } \ No newline at end of file diff --git a/examples/diodeclipper/src/dsp.rs b/examples/diodeclipper/src/dsp.rs new file mode 100644 index 0000000..48595de --- /dev/null +++ b/examples/diodeclipper/src/dsp.rs @@ -0,0 +1,234 @@ +use enum_map::Enum; +use num_traits::Zero; + +use valib::dsp::parameter::{HasParameters, Parameter, SmoothedParam}; +use valib::dsp::{PerSampleBlockAdapter, DSP}; +use valib::filters::biquad::Biquad; +use valib::oversample::{Oversample, Oversampled}; +use valib::saturators::clippers::{DiodeClipper, DiodeClipperModel}; +use valib::saturators::Linear; +use valib::simd::{AutoF32x2, AutoF64x2, SimdComplexField}; +use valib::{Scalar, SimdCast}; + +struct DcBlocker(Biquad); + +impl DcBlocker { + const CUTOFF_HZ: f32 = 5.0; + const Q: f32 = 0.707; + fn new(samplerate: f32) -> Self + where + T: Scalar, + { + Self(Biquad::highpass( + T::from_f64((Self::CUTOFF_HZ / samplerate) as f64), + T::from_f64(Self::Q as f64), + )) + } +} + +impl DSP<1, 1> for DcBlocker { + type Sample = T; + + fn process(&mut self, x: [Self::Sample; 1]) -> [Self::Sample; 1] { + self.0.process(x) + } + + fn reset(&mut self) { + self.0.reset() + } + + fn latency(&self) -> usize { + self.0.latency() + } + + fn set_samplerate(&mut self, samplerate: f32) { + self.0.set_samplerate(samplerate); + self.0.update_coefficients(&Biquad::highpass( + T::from_f64((Self::CUTOFF_HZ / samplerate) as f64), + T::from_f64(Self::Q as f64), + )); + } +} + +type Sample = AutoF32x2; +type Sample64 = AutoF64x2; + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Enum)] +pub enum DiodeType { + Silicon, + Germanium, + Led, +} + +impl DiodeType { + pub fn name(&self) -> String { + match self { + Self::Silicon => "Silicon", + Self::Germanium => "Germanium", + Self::Led => "LED", + } + .to_string() + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Enum)] +pub enum DspParams { + Drive, + ModelSwitch, + DiodeType, + NumForward, + NumBackward, + ForceReset, +} + +pub struct DspInner { + drive: SmoothedParam, + model_switch: Parameter, + num_forward: Parameter, + num_backward: Parameter, + diode_type: Parameter, + force_reset: Parameter, + nr_model: DiodeClipperModel, + nr_nr: DiodeClipper, +} + +impl DspInner { + fn new(samplerate: f32) -> Self { + Self { + drive: Parameter::new(1.0).smoothed_exponential(samplerate, 10.0), + model_switch: Parameter::new(0.0), + num_forward: Parameter::new(1.0), + num_backward: Parameter::new(1.0), + diode_type: Parameter::new(0.0), + force_reset: Parameter::new(0.0), + nr_model: DiodeClipperModel::new_silicon(1, 1), + nr_nr: DiodeClipper::new_silicon(1, 1, Sample64::zero()), + } + } + + fn update_from_params(&mut self) { + if self.num_forward.has_changed() + || self.num_backward.has_changed() + || self.diode_type.has_changed() + { + let num_fwd = self.num_forward.get_value() as _; + let num_bck = self.num_backward.get_value() as _; + self.nr_model = match self.diode_type.get_enum::() { + DiodeType::Silicon => DiodeClipperModel::new_silicon(num_fwd, num_bck), + DiodeType::Germanium => DiodeClipperModel::new_germanium(num_fwd, num_bck), + DiodeType::Led => DiodeClipperModel::new_led(num_fwd, num_bck), + }; + let last_vout = self.nr_nr.last_output(); + self.nr_nr = match self.diode_type.get_enum::() { + DiodeType::Silicon => { + DiodeClipper::new_silicon(num_fwd as usize, num_bck as usize, last_vout) + } + DiodeType::Germanium => { + DiodeClipper::new_germanium(num_fwd as usize, num_bck as usize, last_vout) + } + DiodeType::Led => { + DiodeClipper::new_led(num_fwd as usize, num_bck as usize, last_vout) + } + }; + } + } +} + +impl HasParameters for DspInner { + type Enum = DspParams; + + fn get_parameter(&self, param: Self::Enum) -> &Parameter { + match param { + DspParams::Drive => &self.drive.param, + DspParams::ModelSwitch => &self.model_switch, + DspParams::DiodeType => &self.diode_type, + DspParams::NumForward => &self.num_forward, + DspParams::NumBackward => &self.num_backward, + DspParams::ForceReset => &self.force_reset, + } + } +} + +impl DSP<1, 1> for DspInner { + type Sample = Sample; + + fn process(&mut self, x: [Self::Sample; 1]) -> [Self::Sample; 1] { + if self.force_reset.has_changed() { + DSP::reset(self) + } + self.update_from_params(); + + let drive = Sample64::from_f64(self.drive.next_sample() as _); + let x64 = x.map(|x| x.cast() * drive); + if self.model_switch.get_bool() { + self.nr_model.process(x64) + } else { + self.nr_nr.process(x64) + } + .map(|x| x / drive.simd_asinh()) + .map(|x| x.cast()) + } + + fn set_samplerate(&mut self, samplerate: f32) { + DSP::set_samplerate(&mut self.nr_model, samplerate); + DSP::set_samplerate(&mut self.nr_nr, samplerate); + } + + fn latency(&self) -> usize { + if self.model_switch.get_bool() { + DSP::latency(&self.nr_model) + } else { + DSP::latency(&self.nr_nr) + } + } + + fn reset(&mut self) { + DSP::reset(&mut self.nr_model); + DSP::reset(&mut self.nr_nr); + } +} + +pub struct Dsp { + inner: PerSampleBlockAdapter, 1, 1>, + dc_blocker: DcBlocker, +} + +impl DSP<1, 1> for Dsp { + type Sample = Sample; + + fn process(&mut self, x: [Self::Sample; 1]) -> [Self::Sample; 1] { + self.dc_blocker.process(self.inner.process(x)) + } + + fn set_samplerate(&mut self, samplerate: f32) { + DSP::set_samplerate(&mut self.inner, samplerate); + DSP::set_samplerate(&mut self.dc_blocker, samplerate); + } + + fn latency(&self) -> usize { + DSP::latency(&self.inner) + DSP::latency(&self.dc_blocker) + } + + fn reset(&mut self) { + DSP::reset(&mut self.inner); + DSP::reset(&mut self.dc_blocker); + } +} + +impl HasParameters for Dsp { + type Enum = DspParams; + + fn get_parameter(&self, param: Self::Enum) -> &Parameter { + self.inner.get_parameter(param) + } +} + +pub fn create_dsp(samplerate: f32, oversample: usize, max_block_size: usize) -> Dsp { + let inner = PerSampleBlockAdapter::new( + Oversample::new(oversample, max_block_size).with_dsp(DspInner::new(samplerate)), + ); + Dsp { + inner, + dc_blocker: DcBlocker::new(samplerate), + } +} diff --git a/examples/diodeclipper/src/lib.rs b/examples/diodeclipper/src/lib.rs index 6fc5ea6..5486b30 100644 --- a/examples/diodeclipper/src/lib.rs +++ b/examples/diodeclipper/src/lib.rs @@ -1,20 +1,16 @@ -use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; -use nih_plug::{buffer::Block, prelude::*}; +use enum_map::Enum; +use nih_plug::prelude::*; +use nih_plug::util::db_to_gain; -use valib::dsp::DSP; -use valib::math::interpolation::{Cubic, Interpolate}; -use valib::oversample::Oversample; -use valib::{ - filters::biquad::Biquad, - saturators::{ - clippers::{DiodeClipper, DiodeClipperModel}, - Linear, - }, - simd::{AutoF32x2, AutoF64x2, AutoSimd, SimdComplexField, SimdValue}, - Scalar, -}; +use dsp::Dsp; +use valib::contrib::nih_plug::{process_buffer_simd, NihParamsController}; +use valib::dsp::DSPBlock; + +use crate::dsp::{create_dsp, DiodeType, DspParams}; + +mod dsp; #[cfg(debug_assertions)] const OVERSAMPLE: usize = 4; @@ -23,146 +19,63 @@ const OVERSAMPLE: usize = 16; const MAX_BLOCK_SIZE: usize = 512; -#[derive(Debug, Enum, Eq, PartialEq)] -enum DiodeType { - Silicon, - Germanium, - Led, -} - -impl DiodeType { - #[inline] - fn get_clipper_model(&self, nf: usize, nb: usize) -> DiodeClipper { - match self { - Self::Silicon => DiodeClipper::new_silicon(nf, nb, T::from_f64(0.)), - Self::Germanium => DiodeClipper::new_germanium(nf, nb, T::from_f64(0.)), - Self::Led => DiodeClipper::new_led(nf, nb, T::from_f64(0.)), - } - } - #[inline] - fn get_model_params(&self, nf: u8, nb: u8) -> DiodeClipperModel { - match self { - Self::Silicon => DiodeClipperModel::new_silicon(nf, nb), - Self::Germanium => DiodeClipperModel::new_germanium(nf, nb), - Self::Led => DiodeClipperModel::new_led(nf, nb), - } - } -} - -#[derive(Debug, Params)] -struct ClipperParams { - #[id = "type"] - dtype: EnumParam, - #[id = "nf"] - nf: IntParam, - #[id = "nb"] - nb: IntParam, - #[id = "drive"] - drive: FloatParam, - #[id = "model"] - model: BoolParam, - #[id = "qlty"] - quality: IntParam, - #[id = "reset"] - reset: BoolParam, +struct ClipperPlugin { + dsp: Dsp, + params: Arc>, } -impl ClipperParams { - fn new(reset_atomic: Arc) -> Self { - Self { - dtype: EnumParam::new("Diode", DiodeType::Silicon), - nf: IntParam::new("# Forward", 1, IntRange::Linear { min: 1, max: 5 }), - nb: IntParam::new("# Backward", 1, IntRange::Linear { min: 1, max: 5 }), - drive: FloatParam::new( +impl Default for ClipperPlugin { + fn default() -> Self { + let dsp = create_dsp(44100.0, OVERSAMPLE, MAX_BLOCK_SIZE); + let params = NihParamsController::new(&dsp, |k, _| match k { + DspParams::Drive => FloatParam::new( "Drive", - 100., + 0.0, FloatRange::Skewed { - min: 1., - max: 300., - factor: FloatRange::skew_factor(-2.5), + min: db_to_gain(-12.0), + max: db_to_gain(40.0), + factor: FloatRange::gain_skew_factor(-12.0, 40.0), }, ) - .with_smoother(SmoothingStyle::Exponential(50.)) - .with_string_to_value(formatters::s2v_f32_gain_to_db()) + .with_unit(" dB") .with_value_to_string(formatters::v2s_f32_gain_to_db(2)) - .with_unit(" dB"), - model: BoolParam::new("Use Model", true), - quality: IntParam::new("Sim Quality", 50, IntRange::Linear { min: 10, max: 100 }), - reset: BoolParam::new("Reset", false).with_callback(Arc::new(move |_| { - reset_atomic.store(true, Ordering::Release) - })), - } - } -} - -type Sample = AutoF32x2; -type Sample64 = AutoF64x2; - -struct ClipperPlugin { - params: Arc, - clipper_nr: DiodeClipper, - clipper_model: DiodeClipperModel, - dc_couple_in: Biquad, - dc_couple_out: Biquad, - oversample: Oversample, - force_reset: Arc, -} - -impl Default for ClipperPlugin { - fn default() -> Self { - // let samplerate = 44.1e3 * OVERSAMPLE as f32; - let force_reset = Arc::new(AtomicBool::new(false)); + .with_string_to_value(formatters::s2v_f32_gain_to_db()), + DspParams::ModelSwitch => { + FloatParam::new("Model", 0.0, FloatRange::Linear { min: 0.0, max: 1.0 }) + .with_step_size(1.0) + .with_value_to_string(Arc::new(|x| { + if x > 0.5 { + "Analytic".to_string() + } else { + "Newton-Rhapson".to_string() + } + })) + } + DspParams::DiodeType => { + FloatParam::new("Diode type", 0.0, FloatRange::Linear { min: 0.0, max: 2.0 }) + .with_step_size(1.0) + .with_value_to_string(Arc::new(|x| { + DiodeType::from_usize(nih_dbg!(x as _)).name() + })) + } + DspParams::NumForward => { + FloatParam::new("# Forward", 1.0, FloatRange::Linear { min: 1.0, max: 5.0 }) + .with_step_size(1.0) + } + DspParams::NumBackward => { + FloatParam::new("# Backward", 1.0, FloatRange::Linear { min: 1.0, max: 5.0 }) + .with_step_size(1.0) + } + DspParams::ForceReset => FloatParam::new( + "Force reset", + 0.0, + FloatRange::Linear { min: 0.0, max: 1.0 }, + ) + .with_step_size(1.0), + }); Self { - params: Arc::new(ClipperParams::new(force_reset.clone())), - clipper_nr: DiodeClipper::new_silicon(1, 1, Sample64::from_f64(0.0)), - clipper_model: DiodeClipperModel::new_silicon(1, 1), - dc_couple_in: Biquad::highpass(Sample::from_f64(20. / 44.1e3), Sample::from_f64(1.0)), - dc_couple_out: Biquad::highpass(Sample::from_f64(20. / 44.1e3), Sample::from_f64(1.0)), - oversample: Oversample::new(OVERSAMPLE, MAX_BLOCK_SIZE), - force_reset, - } - } -} - -fn block_to_simd_array( - block: &mut Block, - output: &mut [AutoSimd<[T; N]>], - cast: impl Fn(f32) -> T, -) -> usize { - let mut i = 0; - for samples in block.iter_samples().take(output.len()) { - let mut it = samples.into_iter(); - output[i] = AutoSimd(std::array::from_fn(|_| cast(it.next().copied().unwrap()))); - i += 1; - } - i -} - -fn simd_array_to_block( - input: &[AutoSimd<[T; N]>], - block: &mut Block, - uncast: impl Fn(&T) -> f32, -) { - for (inp, mut out_samples) in input.iter().zip(block.iter_samples()) { - for i in 0..N { - *out_samples.get_mut(i).unwrap() = uncast(&inp.0[i]); - } - } -} - -fn apply>, const N: usize>( - buffer: &mut Buffer, - dsp: &mut P, -) where - AutoSimd<[f32; N]>: SimdValue, - ::Element: Copy, -{ - for mut samples in buffer.iter_samples() { - let mut it = samples.iter_mut(); - let input = AutoSimd(std::array::from_fn(|_| it.next().copied().unwrap())); - let [output] = dsp.process([input]); - for (out, inp) in samples.into_iter().zip(output.0.into_iter()) { - *out = inp; + dsp, + params: Arc::new(params), } } } @@ -199,19 +112,12 @@ impl Plugin for ClipperPlugin { buffer_config: &BufferConfig, _context: &mut impl InitContext, ) -> bool { - let samplerate = buffer_config.sample_rate as f64; - self.dc_couple_in = - Biquad::highpass(Sample::from_f64(20. / samplerate), Sample::from_f64(1.0)); - self.dc_couple_out = - Biquad::highpass(Sample::from_f64(20. / samplerate), Sample::from_f64(1.0)); + self.dsp.set_samplerate(buffer_config.sample_rate); true } fn reset(&mut self) { - self.dc_couple_in.reset(); - self.dc_couple_out.reset(); - self.oversample.reset(); - self.clipper_nr.reset(); + self.dsp.reset(); } fn process( @@ -220,63 +126,8 @@ impl Plugin for ClipperPlugin { _aux: &mut AuxiliaryBuffers, _context: &mut impl ProcessContext, ) -> ProcessStatus { - let clipper_latency = match self.params.model.value() { - true => self.clipper_model.latency(), - false => self.clipper_nr.latency(), - }; - let latency = self.dc_couple_in.latency() - + self.dc_couple_out.latency() - + self.oversample.latency() - + clipper_latency; - _context.set_latency_samples(latency as _); - if self.force_reset.load(Ordering::Acquire) { - self.force_reset.store(false, Ordering::Release); - self.reset(); - } - self.clipper_nr = self - .params - .dtype - .value() - .get_clipper_model(self.params.nf.value() as _, self.params.nb.value() as _); - self.clipper_nr.max_iter = self.params.quality.value() as _; - self.clipper_model = self - .params - .dtype - .value() - .get_model_params(self.params.nf.value() as _, self.params.nb.value() as _); - - apply(buffer, &mut self.dc_couple_in); - - for (_, mut block) in buffer.iter_blocks(MAX_BLOCK_SIZE) { - let mut drive = [0.; MAX_BLOCK_SIZE]; - let mut drive_os = [0.; OVERSAMPLE * MAX_BLOCK_SIZE]; - let len = block.samples(); - self.params - .drive - .smoothed - .next_block_exact(&mut drive[..len]); - Cubic::interpolate_slice(&mut drive_os[..OVERSAMPLE * len], &drive[..len]); - - let mut simd_buffer = [Sample64::from_f64(0.0); MAX_BLOCK_SIZE]; - let actual_len = block_to_simd_array(&mut block, &mut simd_buffer, |f| f as f64); - let simd_buffer = &mut simd_buffer[..actual_len]; - let mut os_buffer = self.oversample.oversample(simd_buffer); - for (i, s) in os_buffer.iter_mut().enumerate() { - let drive = Sample64::from_f64(drive_os[i] as _); - let input = *s * drive; - let [output] = if self.params.model.value() { - self.clipper_model.process([input]) - } else { - self.clipper_nr.process([input]) - }; - *s = output / drive.simd_asinh(); - } - os_buffer.finish(simd_buffer); - simd_array_to_block(simd_buffer, &mut block, |f| *f as f32); - } - - apply(buffer, &mut self.dc_couple_out); - + _context.set_latency_samples(self.dsp.latency() as _); + process_buffer_simd::<_, _, MAX_BLOCK_SIZE>(&mut self.dsp, buffer); ProcessStatus::Normal } } diff --git a/examples/svfmixer/Cargo.toml b/examples/svfmixer/Cargo.toml index e284256..e1ed2ed 100644 --- a/examples/svfmixer/Cargo.toml +++ b/examples/svfmixer/Cargo.toml @@ -16,4 +16,4 @@ crate-type = ["cdylib"] enum-map.workspace = true nalgebra.workspace = true nih_plug.workspace = true -valib = { path = "../.." } \ No newline at end of file +valib = { path = "../..", features = ["nih-plug"] } \ No newline at end of file diff --git a/examples/svfmixer/src/dsp.rs b/examples/svfmixer/src/dsp.rs index 85ab786..c301095 100644 --- a/examples/svfmixer/src/dsp.rs +++ b/examples/svfmixer/src/dsp.rs @@ -10,7 +10,7 @@ use valib::saturators::{Clipper, Saturator, Slew}; use valib::simd::{AutoSimd, SimdValue}; use valib::Scalar; -type Sample = AutoSimd<[f32; 2]>; +pub(crate) type Sample = AutoSimd<[f32; 2]>; pub type Dsp = Oversampled; diff --git a/examples/svfmixer/src/lib.rs b/examples/svfmixer/src/lib.rs index 3030ab9..77344f2 100644 --- a/examples/svfmixer/src/lib.rs +++ b/examples/svfmixer/src/lib.rs @@ -2,59 +2,28 @@ use std::sync::Arc; use nih_plug::prelude::*; -use extend::FloatParamExt; -use valib::dsp::parameter::HasParameters; -use valib::dsp::utils::{slice_to_mono_block, slice_to_mono_block_mut}; +use valib::contrib::nih_plug::{process_buffer_simd, NihParamsController}; use valib::dsp::DSPBlock; use valib::oversample::Oversample; -use valib::Scalar; use crate::dsp::{Dsp, DspInner, DspParam}; mod dsp; -mod extend { - use std::sync::Arc; - - use nih_plug::params::FloatParam; - - use valib::dsp::parameter::Parameter; - - pub trait FloatParamExt { - fn bind_to_parameter(self, param: &Parameter) -> Self; - } - - impl FloatParamExt for FloatParam { - fn bind_to_parameter(self, param: &Parameter) -> Self { - let param = param.clone(); - self.with_callback(Arc::new(move |value| param.set_value(value))) - } - } -} - const MAX_BUFFER_SIZE: usize = 512; const OVERSAMPLE: usize = 2; -#[derive(Debug, Params)] -struct PluginParams { - #[id = "drive"] - drive: FloatParam, - #[id = "fc"] - fc: FloatParam, - #[id = "q"] - q: FloatParam, - #[id = "lp"] - lp_gain: FloatParam, - #[id = "bp"] - bp_gain: FloatParam, - #[id = "hp"] - hp_gain: FloatParam, +struct SvfMixerPlugin { + params: Arc>, + dsp: Dsp, } -impl PluginParams { - fn new(dsp: &Dsp) -> Self { - Self { - drive: FloatParam::new( +impl Default for SvfMixerPlugin { + fn default() -> Self { + let dsp_inner = DspInner::new(44100.0); + let dsp = Oversample::new(OVERSAMPLE, MAX_BUFFER_SIZE).with_dsp(dsp_inner); + let params_controller = NihParamsController::new(&dsp, |param, _| match param { + DspParam::Drive => FloatParam::new( "Drive", 1.0, FloatRange::Skewed { @@ -65,9 +34,8 @@ impl PluginParams { ) .with_value_to_string(formatters::v2s_f32_gain_to_db(2)) .with_string_to_value(formatters::s2v_f32_gain_to_db()) - .with_unit(" dB") - .bind_to_parameter(dsp.get_parameter(DspParam::Drive)), - fc: FloatParam::new( + .with_unit(" dB"), + DspParam::Cutoff => FloatParam::new( "Frequency", 300., FloatRange::Skewed { @@ -77,11 +45,11 @@ impl PluginParams { }, ) .with_value_to_string(formatters::v2s_f32_hz_then_khz_with_note_name(2, false)) - .with_string_to_value(formatters::s2v_f32_hz_then_khz()) - .bind_to_parameter(dsp.get_parameter(DspParam::Cutoff)), - q: FloatParam::new("Q", 0.5, FloatRange::Linear { min: 0., max: 1.25 }) - .bind_to_parameter(dsp.get_parameter(DspParam::Resonance)), - lp_gain: FloatParam::new( + .with_string_to_value(formatters::s2v_f32_hz_then_khz()), + DspParam::Resonance => { + FloatParam::new("Q", 0.5, FloatRange::Linear { min: 0., max: 1.25 }) + } + DspParam::LpGain => FloatParam::new( "LP Gain", 0., FloatRange::SymmetricalSkewed { @@ -93,9 +61,8 @@ impl PluginParams { ) .with_value_to_string(formatters::v2s_f32_gain_to_db(2)) .with_string_to_value(formatters::s2v_f32_gain_to_db()) - .with_unit(" dB") - .bind_to_parameter(dsp.get_parameter(DspParam::LpGain)), - bp_gain: FloatParam::new( + .with_unit(" dB"), + DspParam::BpGain => FloatParam::new( "BP Gain", 0., FloatRange::SymmetricalSkewed { @@ -107,9 +74,8 @@ impl PluginParams { ) .with_value_to_string(formatters::v2s_f32_gain_to_db(2)) .with_string_to_value(formatters::s2v_f32_gain_to_db()) - .with_unit(" dB") - .bind_to_parameter(dsp.get_parameter(DspParam::BpGain)), - hp_gain: FloatParam::new( + .with_unit(" dB"), + DspParam::HpGain => FloatParam::new( "HP Gain", 0., FloatRange::SymmetricalSkewed { @@ -121,24 +87,10 @@ impl PluginParams { ) .with_value_to_string(formatters::v2s_f32_gain_to_db(2)) .with_string_to_value(formatters::s2v_f32_gain_to_db()) - .with_unit(" dB") - .bind_to_parameter(dsp.get_parameter(DspParam::HpGain)), - } - } -} - -struct SvfMixerPlugin { - params: Arc, - dsp: Dsp, -} - -impl Default for SvfMixerPlugin { - fn default() -> Self { - let dsp_inner = DspInner::new(44100.0); - let dsp = Oversample::new(OVERSAMPLE, MAX_BUFFER_SIZE).with_dsp(dsp_inner); - let params = PluginParams::new(&dsp); + .with_unit(" dB"), + }); Self { - params: Arc::new(params), + params: Arc::new(params_controller), dsp, } } @@ -163,8 +115,8 @@ impl nih_plug::prelude::Plugin for SvfMixerPlugin { aux_outputs: &[], }, }]; - type BackgroundTask = (); type SysExMessage = (); + type BackgroundTask = (); fn params(&self) -> Arc { self.params.clone() @@ -197,35 +149,6 @@ impl nih_plug::prelude::Plugin for SvfMixerPlugin { } } -fn process_buffer_simd< - T: Scalar + From<[f32; 2]>, - Dsp: DSPBlock<1, 1, Sample = T>, - const BUFSIZE: usize, ->( - dsp: &mut Dsp, - buffer: &mut Buffer, -) { - let mut input = [T::from_f64(0.0); BUFSIZE]; - let mut output = input; - for (_, mut block) in buffer.iter_blocks(BUFSIZE) { - for (i, mut c) in block.iter_samples().enumerate() { - input[i] = T::from(std::array::from_fn(|i| c.get_mut(i).copied().unwrap())); - output[i] = input[i]; - } - - let input = &input[..block.samples()]; - let output = &mut output[..block.samples()]; - - dsp.process_block(slice_to_mono_block(input), slice_to_mono_block_mut(output)); - - for (i, mut c) in block.iter_samples().enumerate() { - for (ch, s) in c.iter_mut().enumerate() { - *s = output[i].extract(ch); - } - } - } -} - impl ClapPlugin for SvfMixerPlugin { const CLAP_ID: &'static str = "com.github.SolarLiner.valib.SVFMixer"; const CLAP_DESCRIPTION: Option<&'static str> = None; diff --git a/src/contrib/mod.rs b/src/contrib/mod.rs index 8fb7f31..ad009f4 100644 --- a/src/contrib/mod.rs +++ b/src/contrib/mod.rs @@ -1,3 +1,5 @@ #![doc = include_str!("./README.md")] #[cfg(feature = "fundsp")] pub mod fundsp; +#[cfg(feature = "nih-plug")] +pub mod nih_plug; diff --git a/src/contrib/nih_plug.rs b/src/contrib/nih_plug.rs new file mode 100644 index 0000000..29ede21 --- /dev/null +++ b/src/contrib/nih_plug.rs @@ -0,0 +1,172 @@ +#![cfg(feature = "nih-plug")] + +use std::sync::Arc; + +use enum_map::{Enum, EnumArray, EnumMap}; +use nih_plug::nih_debug_assert; +use nih_plug::params::FloatParam; +use nih_plug::prelude::*; +use nih_plug::{buffer::Buffer, params::Param}; + +use crate::dsp::parameter::{HasParameters, Parameter}; +use crate::dsp::utils::{slice_to_mono_block, slice_to_mono_block_mut}; +use crate::dsp::DSPBlock; +use crate::Scalar; + +/// Bind a [`valib`] [`Parameter`] to a [`nig_plug`] parameter.. +pub trait BindToParameter { + /// Bind a [`Parameter`] to a nih-plug [`FloatParam`]. + fn bind_to_parameter(self, param: &Parameter) -> Self; +} + +impl BindToParameter for FloatParam { + fn bind_to_parameter(self, param: &Parameter) -> Self { + let param = param.clone(); + self.with_callback(Arc::new(move |value| param.set_value(value))) + } +} + +impl BindToParameter for BoolParam { + fn bind_to_parameter(self, param: &Parameter) -> Self { + let param = param.clone(); + self.with_callback(Arc::new(move |b| param.set_bool(b))) + } +} + +impl BindToParameter + for EnumParam +{ + fn bind_to_parameter(self, param: &Parameter) -> Self { + let param = param.clone(); + self.with_callback(Arc::new(move |e| param.set_enum(e))) + } +} + +/// Adapter struct for processors with parameters +#[derive(Debug)] +pub struct NihParamsController +where + P::Enum: EnumArray + EnumArray, +{ + nih_map: EnumMap, +} + +impl NihParamsController

+where + P::Enum: EnumArray + EnumArray, +{ + pub fn new(inner: &P, param_map: impl Fn(P::Enum, String) -> FloatParam) -> Self { + let nih_map = EnumMap::from_fn(|k| { + param_map(k, inner.full_name(k)).bind_to_parameter(inner.get_parameter(k)) + }); + Self { nih_map } + } +} + +unsafe impl Params for NihParamsController

+where + P::Enum: EnumArray + EnumArray, + <

::Enum as enum_map::EnumArray>::Array: Send + Sync, + <

::Enum as enum_map::EnumArray>::Array: Send + Sync, +{ + fn param_map(&self) -> Vec<(String, ParamPtr, String)> { + self.nih_map + .iter() + .map(|(k, p)| (k.into_usize().to_string(), p.as_ptr(), p.name().to_string())) + .collect() + } +} + +/// Processes a [`nih-plug`] buffer in its entirety with a [`DSPBlock`] instance, where inputs in +/// the dsp instance correspond to channels in the buffer. +/// +/// # Arguments +/// +/// * `dsp`: [`DSPBlock`] instance to process the buffer with +/// * `buffer`: Buffer to process +/// +/// panics if the scalar type has more channels than the buffer holds. +pub fn process_buffer< + T: Scalar, + Dsp, + const CHANNELS: usize, + const MAX_BUF_SIZE: usize, +>( + dsp: &mut Dsp, + buffer: &mut Buffer, +) where + Dsp: DSPBlock, +{ + assert!(CHANNELS <= buffer.channels()); + let mut input = [[T::zero(); CHANNELS]; MAX_BUF_SIZE]; + let mut output = input; + let max_buffer_size = dsp + .max_block_size() + .map(|mbf| mbf.min(MAX_BUF_SIZE)) + .unwrap_or(MAX_BUF_SIZE); + + for (_, mut block) in buffer.iter_blocks(max_buffer_size) { + let input = &mut input[..block.samples()]; + let output = &mut output[..block.samples()]; + for (i, mut s) in block.iter_samples().enumerate() { + for (ch, s) in s.iter_mut().map(|s| *s).enumerate() { + input[i][ch] = T::splat(s); + } + } + + dsp.process_block(input, output); + + for (i, mut s) in block.iter_samples().enumerate() { + for (ch, s) in s.iter_mut().enumerate() { + *s = output[i][ch].extract(0); + } + } + } +} + +/// Processes a [`nih-plug`] buffer in its entirety with a [`DSPBlock`] instance, mapping channels +/// to lanes in the scalar type. +/// +/// This function automatically respects the value reported by [`DSPBlock::max_buffer_size`]. Up to +/// [`MAX_BUF_SIZE`] samples will be processed at once. +/// +/// # Arguments +/// +/// * `dsp`: [`DSPBlock`] instance to process the buffer with +/// * `buffer`: Buffer to process +/// +/// panics if the scalar type has more channels than the buffer holds. +pub fn process_buffer_simd< + T: Scalar, + Dsp: DSPBlock<1, 1, Sample = T>, + const MAX_BUF_SIZE: usize, +>( + dsp: &mut Dsp, + buffer: &mut Buffer, +) { + let channels = buffer.channels(); + assert!(T::lanes() <= channels); + let mut input = [T::from_f64(0.0); MAX_BUF_SIZE]; + let mut output = input; + let max_buffer_size = dsp.max_block_size().unwrap_or(MAX_BUF_SIZE); + nih_debug_assert!(max_buffer_size <= MAX_BUF_SIZE); + for (_, mut block) in buffer.iter_blocks(max_buffer_size) { + for (i, mut c) in block.iter_samples().enumerate() { + for ch in 0..channels { + input[i].replace(ch, c.get_mut(ch).copied().unwrap()); + } + output[i] = input[i]; + } + + let input = &input[..block.samples()]; + let output = &mut output[..block.samples()]; + + dsp.process_block(slice_to_mono_block(input), slice_to_mono_block_mut(output)); + + for (i, mut c) in block.iter_samples().enumerate() { + for (ch, s) in c.iter_mut().enumerate() { + *s = output[i].extract(ch); + } + } + } +} diff --git a/src/dsp/parameter.rs b/src/dsp/parameter.rs index 0937529..5d87fdf 100644 --- a/src/dsp/parameter.rs +++ b/src/dsp/parameter.rs @@ -12,7 +12,6 @@ //! [`Parallel`]: crate::dsp::blocks::Parallel use core::fmt; use std::fmt::Formatter; - use std::ops::Deref; use std::sync::atomic::Ordering; use std::sync::Arc; @@ -79,6 +78,25 @@ impl Parameter { self.0.changed.swap(false, Ordering::AcqRel) } + pub fn get_bool(&self) -> bool { + self.get_value() > 0.5 + } + + pub fn set_bool(&self, value: bool) { + self.set_value(if value { 1.0 } else { 0.0 }) + } + + pub fn get_enum(&self) -> E { + let index = self.get_value().min(E::LENGTH as f32 - 1.0); + eprintln!("get_enum index {index}"); + return E::from_usize(index.floor() as _); + } + + pub fn set_enum(&self, value: E) { + let step = f32::recip(E::LENGTH as f32); + self.set_value(value.into_usize() as f32 * step); + } + pub fn filtered

(&self, dsp: P) -> FilteredParam

{ FilteredParam { param: self.clone(), @@ -224,6 +242,13 @@ pub trait HasParameters { .map(Self::Enum::from_usize) .map(|p| (p, self.get_parameter(p))) } + + fn full_name(&self, param: Self::Enum) -> String { + self.get_parameter(param) + .name() + .map(|s| s.to_string()) + .unwrap_or_else(|| format!("Param {}", param.into_usize() + 1)) + } } /// Separate controller for controlling parameters of a [`DSP`] instance from another place. diff --git a/src/oversample/mod.rs b/src/oversample/mod.rs index b156704..9aaefbe 100644 --- a/src/oversample/mod.rs +++ b/src/oversample/mod.rs @@ -12,21 +12,25 @@ use crate::{ filters::biquad::Biquad, }; +const CASCADE: usize = 8; + #[derive(Debug, Clone)] pub struct Oversample { os_factor: usize, os_buffer: Box<[T]>, - pre_filter: Series<[Biquad; 8]>, - post_filter: Series<[Biquad; 8]>, + pre_filter: Series<[Biquad; CASCADE]>, + post_filter: Series<[Biquad; CASCADE]>, } impl Oversample { pub fn new(os_factor: usize, max_block_size: usize) -> Self { assert!(os_factor > 1); let os_buffer = vec![T::zero(); max_block_size * os_factor].into_boxed_slice(); + let cascade_adjustment = f64::sqrt(2f64.powf(1.0 / CASCADE as f64) - 1.0); + println!("cascade adjustment {cascade_adjustment}"); let filters = std::array::from_fn(|_| { Biquad::lowpass( - T::from_f64(2.0 * os_factor as f64).simd_recip(), + T::from_f64(2.0 * os_factor as f64).simd_recip() * T::from_f64(cascade_adjustment), T::from_f64(0.707), ) }); @@ -167,31 +171,35 @@ where { type Sample = T; - fn latency(&self) -> usize { - self.oversampling.latency() + self.inner.latency() - } - - fn max_block_size(&self) -> Option { - Some(self.oversampling.max_block_size()) - } - - fn reset(&mut self) { - self.oversampling.reset(); - self.inner.reset(); - } - fn process_block(&mut self, inputs: &[[Self::Sample; 1]], outputs: &mut [[Self::Sample; 1]]) { let inputs = mono_block_to_slice(inputs); let mut os_block = self.oversampling.oversample(inputs); let inner_outputs = slice_to_mono_block_mut(&mut os_block); self.staging_buffer[..inner_outputs.len()].copy_from_slice(inner_outputs); self.inner - .process_block(&self.staging_buffer, inner_outputs); + .process_block(&self.staging_buffer[..inner_outputs.len()], inner_outputs); os_block.finish(mono_block_to_slice_mut(outputs)); } fn set_samplerate(&mut self, samplerate: f32) { - self.inner.set_samplerate(self.oversampling.os_factor as f32 * samplerate); + self.inner + .set_samplerate(self.oversampling.os_factor as f32 * samplerate); + } + + fn max_block_size(&self) -> Option { + Some(match self.inner.max_block_size() { + Some(size) => size.min(self.oversampling.max_block_size() / self.os_factor()), + None => self.oversampling.max_block_size(), + }) + } + + fn latency(&self) -> usize { + self.oversampling.latency() + self.inner.latency() + } + + fn reset(&mut self) { + self.oversampling.reset(); + self.inner.reset(); } } diff --git a/src/saturators/clippers/mod.rs b/src/saturators/clippers/mod.rs index 5f0bee5..bd64c6e 100644 --- a/src/saturators/clippers/mod.rs +++ b/src/saturators/clippers/mod.rs @@ -23,6 +23,12 @@ pub struct DiodeClipper { last_vout: T, } +impl DiodeClipper { + pub fn last_output(&self) -> T { + self.last_vout + } +} + impl DiodeClipper { pub fn reset(&mut self) { self.last_vout = self.vin; From f77e6f36556249cd9d35ea9efba538dc7c4196ba Mon Sep 17 00:00:00 2001 From: Nathan Graule Date: Fri, 16 Feb 2024 23:01:00 +0100 Subject: [PATCH 02/21] fix: cascade lowpass cutoff compensation --- src/oversample/mod.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/oversample/mod.rs b/src/oversample/mod.rs index 9aaefbe..ef9a37d 100644 --- a/src/oversample/mod.rs +++ b/src/oversample/mod.rs @@ -26,14 +26,12 @@ impl Oversample { pub fn new(os_factor: usize, max_block_size: usize) -> Self { assert!(os_factor > 1); let os_buffer = vec![T::zero(); max_block_size * os_factor].into_boxed_slice(); - let cascade_adjustment = f64::sqrt(2f64.powf(1.0 / CASCADE as f64) - 1.0); - println!("cascade adjustment {cascade_adjustment}"); - let filters = std::array::from_fn(|_| { - Biquad::lowpass( - T::from_f64(2.0 * os_factor as f64).simd_recip() * T::from_f64(cascade_adjustment), - T::from_f64(0.707), - ) - }); + let fc_raw = f64::recip(2.1 * os_factor as f64); + let cascade_adjustment = f64::sqrt(2f64.powf(1.0 / CASCADE as f64) - 1.0).recip(); + let fc_corr = fc_raw * cascade_adjustment; + println!("cascade adjustment {cascade_adjustment}: {fc_raw} -> {fc_corr}"); + let filters = + std::array::from_fn(|_| Biquad::lowpass(T::from_f64(fc_corr), T::from_f64(0.707))); Self { os_factor, os_buffer, From aa1a5e50461194d4e1757292549b7ff0516a159b Mon Sep 17 00:00:00 2001 From: Nathan Graule Date: Fri, 16 Feb 2024 23:30:35 +0100 Subject: [PATCH 03/21] fix: oversampling? --- src/oversample/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/oversample/mod.rs b/src/oversample/mod.rs index ef9a37d..25249fc 100644 --- a/src/oversample/mod.rs +++ b/src/oversample/mod.rs @@ -5,21 +5,21 @@ use crate::dsp::{ utils::{mono_block_to_slice, mono_block_to_slice_mut, slice_to_mono_block_mut}, DSP, }; -use crate::saturators::Linear; +use crate::saturators::{Clipper, Linear}; use crate::Scalar; use crate::{ dsp::{blocks::Series, DSPBlock}, filters::biquad::Biquad, }; -const CASCADE: usize = 8; +const CASCADE: usize = 4; #[derive(Debug, Clone)] pub struct Oversample { os_factor: usize, os_buffer: Box<[T]>, - pre_filter: Series<[Biquad; CASCADE]>, - post_filter: Series<[Biquad; CASCADE]>, + pre_filter: Series<[Biquad; CASCADE]>, + post_filter: Series<[Biquad; CASCADE]>, } impl Oversample { From 262bcf82ace6b6eab38cac16be7be805e7ec9b91 Mon Sep 17 00:00:00 2001 From: Nathan Graule Date: Sat, 17 Feb 2024 15:26:34 +0100 Subject: [PATCH 04/21] refactor: interpolation module --- examples/dirty-biquad/src/lib.rs | 6 +- examples/ladder/src/lib.rs | 6 +- src/math/interpolation.rs | 190 ++++++++++-------- src/math/lut.rs | 10 +- ...ate_valib__math__interpolation__Cubic.snap | 16 ++ ...e_valib__math__interpolation__Hermite.snap | 16 ++ ...te_valib__math__interpolation__Linear.snap | 16 ++ ...e_interpolation__f64____{{closure}}__.snap | 16 ++ ...e_valib__math__interpolation__Nearest.snap | 16 ++ ..._valib__math__interpolation__ZeroHold.snap | 16 ++ src/oscillators/wavetable.rs | 39 ++-- src/util.rs | 4 +- 12 files changed, 233 insertions(+), 118 deletions(-) create mode 100644 src/math/snapshots/valib__math__interpolation__tests__test_interpolate_valib__math__interpolation__Cubic.snap create mode 100644 src/math/snapshots/valib__math__interpolation__tests__test_interpolate_valib__math__interpolation__Hermite.snap create mode 100644 src/math/snapshots/valib__math__interpolation__tests__test_interpolate_valib__math__interpolation__Linear.snap create mode 100644 src/math/snapshots/valib__math__interpolation__tests__test_interpolate_valib__math__interpolation__MappedLinear__valib__math__interpolation__sine_interpolation__f64____{{closure}}__.snap create mode 100644 src/math/snapshots/valib__math__interpolation__tests__test_interpolate_valib__math__interpolation__Nearest.snap create mode 100644 src/math/snapshots/valib__math__interpolation__tests__test_interpolate_valib__math__interpolation__ZeroHold.snap diff --git a/examples/dirty-biquad/src/lib.rs b/examples/dirty-biquad/src/lib.rs index c0f4e3a..a6e0310 100644 --- a/examples/dirty-biquad/src/lib.rs +++ b/examples/dirty-biquad/src/lib.rs @@ -256,9 +256,9 @@ impl nih_plug::prelude::Plugin for Plugin { .drive .smoothed .next_block_exact(&mut drive[..len]); - Cubic::interpolate_slice(&mut os_fc[..os_len], &fc[..len]); - Cubic::interpolate_slice(&mut os_q[..os_len], &q[..len]); - Cubic::interpolate_slice(&mut os_drive[..os_len], &drive[..len]); + Cubic.interpolate_slice(&mut os_fc[..os_len], &fc[..len]); + Cubic.interpolate_slice(&mut os_q[..os_len], &q[..len]); + Cubic.interpolate_slice(&mut os_drive[..os_len], &drive[..len]); let mut simd_block = [Sample::from_f64(0.0); MAX_BLOCK_SIZE]; let actual_len = diff --git a/examples/ladder/src/lib.rs b/examples/ladder/src/lib.rs index 8cb5b7f..c48c28d 100644 --- a/examples/ladder/src/lib.rs +++ b/examples/ladder/src/lib.rs @@ -243,9 +243,9 @@ impl Plugin for LadderFilterPlugin { let mut os_fc = [0.; OVERSAMPLE * MAX_BUFFER_SIZE]; let mut os_q = [0.; OVERSAMPLE * MAX_BUFFER_SIZE]; - Cubic::interpolate_slice(&mut os_drive[..os_len], &drive[..len]); - Cubic::interpolate_slice(&mut os_fc[..os_len], &fc[..len]); - Cubic::interpolate_slice(&mut os_q[..os_len], &q[..len]); + Cubic.interpolate_slice(&mut os_drive[..os_len], &drive[..len]); + Cubic.interpolate_slice(&mut os_fc[..os_len], &fc[..len]); + Cubic.interpolate_slice(&mut os_q[..os_len], &q[..len]); let buffer = &mut simd_slice[..len]; let mut os_buffer = self.oversample.oversample(buffer); diff --git a/src/math/interpolation.rs b/src/math/interpolation.rs index 9420726..0076058 100644 --- a/src/math/interpolation.rs +++ b/src/math/interpolation.rs @@ -1,66 +1,91 @@ -use crate::{util::simd_index_simd, SimdCast, SimdValue}; - use nalgebra::SimdPartialOrd; -use num_traits::{FromPrimitive, Num}; +use num_traits::{AsPrimitive, FromPrimitive, NumAssignOps, NumOps}; use numeric_literals::replace_float_literals; +use simba::simd::{SimdSigned, SimdValue}; use crate::Scalar; +use crate::{util::simd_index_simd, SimdCast}; + +pub trait SimdIndex: + Copy + NumAssignOps + NumOps + SimdPartialOrd + SimdValue +{ +} + +impl> SimdIndex + for T +{ +} + +pub trait SimdInterpolatable: SimdCast +where + >::Output: SimdIndex, +{ + fn index_from_usize(value: usize) -> Self::Output { + Self::Output::splat(value) + } +} + +impl> SimdInterpolatable for T where >::Output: SimdIndex {} /// Interpolation trait. Interpolators implement function that connect discrete points into a continuous function. /// Functions can have any number of taps, both in the forward and bacward directions. It's up to the called to provide /// either actual existing points or extrapolated values when the indices would be out of bounds. pub trait Interpolate { - /// Provide the relative indices needed to compute the interpolation - fn rel_indices() -> [isize; N]; + /// Provide the indices needed for interpolating around the input index, where the index is the element that corresponds to t = 0. + fn indices(index: usize) -> [usize; N]; - /// Interpolate a single point from the given taps (in the same order as the indices defined in [`Self::rel_indices`]). + /// Interpolate a single point from the given taps (in the same order as the indices defined in [`Interpolate::indices`]). /// The t parameter is assumed to be in the 0..=1 range, and it's up to the caller to provide values in the valid range. - fn interpolate(t: T, taps: [T; N]) -> T; + fn interpolate(&self, t: T, taps: [T; N]) -> T; /// Interpolate a value from an entire slice, where the t parameter is the "floating index" into the slice /// (meaning 3.5 is halfway between index 3 and 4 on the given slice). - fn interpolate_on_slice(t: T, values: &[T]) -> T + fn interpolate_on_slice(&self, t: T, values: &[T]) -> T where - T: Scalar + SimdCast, - >::Output: Copy + Num + SimdPartialOrd, + T: Scalar + SimdInterpolatable, + >::Output: SimdIndex, { - let ix_max = >::Output::splat(values.len() as isize - 1); - // let rate = input.len() as f64 / output.len() as f64; - let taps_ix = Self::rel_indices(); - - let zero = >::Output::splat(0); let input_frac = t.simd_fract(); let input_index = t.simd_floor().cast(); - let taps = taps_ix - .map(|tap| >::Output::splat(tap) + input_index) - .map(|tap| simd_index_simd(values, tap.simd_clamp(zero, ix_max))); - Self::interpolate(input_frac, taps) + let taps_ix: [_; N] = std::array::from_fn(|i| { + let mut output = input_index; + for j in 0..>::Output::lanes() { + output.replace(j, Self::indices(output.extract(j))[i]); + } + output + }); + + let zero = T::index_from_usize(0); + let ix_max = T::index_from_usize(values.len() - 1); + let taps = taps_ix.map(|tap| simd_index_simd(values, tap.simd_clamp(zero, ix_max))); + self.interpolate(input_frac, taps) } /// Interpolate one slice into another, where the output slice ends up containing the same "range" of values as /// the input slice, but also automatically performs interpolation using this instance. - fn interpolate_slice(output: &mut [T], input: &[T]) + fn interpolate_slice(&self, output: &mut [T], input: &[T]) where - T: Scalar + SimdCast, - >::Output: Copy + Num + SimdPartialOrd, + T: Scalar + SimdInterpolatable, + >::Output: SimdIndex, { let rate = input.len() as f64 / output.len() as f64; for (i, o) in output.iter_mut().enumerate() { let t = T::from_f64(rate * i as f64); - *o = Self::interpolate_on_slice(t, input); + *o = self.interpolate_on_slice(t, input); } } } +/// Zero-hold interpolation, where the output is the input without additional computation. pub struct ZeroHold; impl Interpolate for ZeroHold { - fn rel_indices() -> [isize; 1] { - [0] + fn indices(index: usize) -> [usize; 1] { + [index] } - fn interpolate(_: T, [x]: [T; 1]) -> T { + fn interpolate(&self, _: T, [x]: [T; 1]) -> T { x } } @@ -69,11 +94,11 @@ impl Interpolate for ZeroHold { pub struct Nearest; impl Interpolate for Nearest { - fn rel_indices() -> [isize; 2] { - [0, 1] + fn indices(index: usize) -> [usize; 2] { + [index, index + 1] } - fn interpolate(t: T, [a, b]: [T; 2]) -> T { + fn interpolate(&self, t: T, [a, b]: [T; 2]) -> T { b.select(t.simd_gt(T::from_f64(0.5).unwrap()), a) } } @@ -82,25 +107,47 @@ impl Interpolate for Nearest { pub struct Linear; impl Interpolate for Linear { - fn rel_indices() -> [isize; 2] { - [0, 1] + fn indices(index: usize) -> [usize; 2] { + [index, index + 1] } - fn interpolate(t: T, [a, b]: [T; 2]) -> T { + fn interpolate(&self, t: T, [a, b]: [T; 2]) -> T { a + (b - a) * t } } +/// Sine interpolation, which is linear interpolation where the control input is first modulated by +/// a cosine function. Produes smoother results than bare linear interpolation because the interpolation +/// is smooth at the limits, as all derivatives are 0 there. +pub struct MappedLinear(pub F); + +pub fn sine_interpolation() -> MappedLinear T> { + MappedLinear(|t| T::simd_cos(t * T::simd_pi())) +} + +impl T> Interpolate for MappedLinear +where + Linear: Interpolate, +{ + fn indices(index: usize) -> [usize; 2] { + Linear::indices(index) + } + + fn interpolate(&self, t: T, taps: [T; 2]) -> T { + Linear.interpolate(self.0(t), taps) + } +} + /// Cubic algorithm, smoother than [`Linear`], but needs 4 taps instead of 2. pub struct Cubic; impl Interpolate for Cubic { - fn rel_indices() -> [isize; 4] { - [-1, 0, 1, 2] + fn indices(index: usize) -> [usize; 4] { + [index.saturating_sub(1), index, index + 1, index + 2] } #[replace_float_literals(T::from_f64(literal))] - fn interpolate(t: T, taps: [T; 4]) -> T { + fn interpolate(&self, t: T, taps: [T; 4]) -> T { taps[1] + 0.5 * t @@ -114,12 +161,12 @@ impl Interpolate for Cubic { pub struct Hermite; impl Interpolate for Hermite { - fn rel_indices() -> [isize; 4] { - [-1, 0, 1, 2] + fn indices(index: usize) -> [usize; 4] { + [index.saturating_sub(1), index, index + 1, index + 2] } #[replace_float_literals(T::from_f64(literal))] - fn interpolate(t: T, taps: [T; 4]) -> T { + fn interpolate(&self, t: T, taps: [T; 4]) -> T { let c0 = taps[1]; let c1 = 0.5 * (taps[2] - taps[0]); let c2 = taps[0] - 2.5 * taps[1] + 2.0 * taps[2] - 0.5 * taps[3]; @@ -128,62 +175,27 @@ impl Interpolate for Hermite { } } -#[derive(Debug, Clone)] -pub struct Sine; - -impl Interpolate for Sine { - fn rel_indices() -> [isize; 2] { - [0, 1] - } - - #[replace_float_literals(T::from_f64(literal))] - fn interpolate(t: T, taps: [T; 2]) -> T { - let fac = T::simd_cos(t * T::simd_pi()) * 0.5 + 0.5; - Linear::interpolate(fac, taps) - } -} - #[cfg(test)] mod tests { - use super::*; - - #[test] - fn test_interpolate_nearest() { - let a = [0., 1., 1.]; - let mut actual = [0.; 12]; - let expected = [0., 0., 0., 1., 1., 1., 1., 1., 1., 1., 1., 1.]; - Nearest::interpolate_slice(&mut actual, &a); - assert_eq!(actual, expected); - } - - #[test] - fn test_interpolate_linear() { - let a = [0., 1., 1.]; - let mut actual = [0.; 12]; - let expected = [0., 0.25, 0.5, 0.75, 1., 1., 1., 1., 1., 1., 1., 1.]; - Linear::interpolate_slice(&mut actual, &a); - assert_eq!(actual, expected); - } + use rstest::rstest; - #[test] - fn test_interpolate_cubic() { - let a = [0., 1., 1.]; - let mut actual = [0.; 12]; - let expected = [ - 0.0, 0.203125, 0.5, 0.796875, 1.0, 1.0703125, 1.0625, 1.0234375, 1.0, 1.0, 1.0, 1.0, - ]; - Cubic::interpolate_slice(&mut actual, &a); - assert_eq!(actual, expected); - } + use super::*; - #[test] - fn test_interpolate_hermite() { + #[rstest] + fn test_interpolate( + #[values(ZeroHold, Nearest, Linear, Cubic, Hermite, sine_interpolation())] interp: Interp, + ) where + Interp: Interpolate, + { let a = [0., 1., 1.]; let mut actual = [0.; 12]; - let expected = [ - 0.0, 0.203125, 0.5, 0.796875, 1.0, 1.0703125, 1.0625, 1.0234375, 1.0, 1.0, 1.0, 1.0, - ]; - Hermite::interpolate_slice(&mut actual, &a); - assert_eq!(actual, expected); + interp.interpolate_slice(&mut actual, &a); + let name = format!( + "test_interpolate_{}", + std::any::type_name::() + .replace(['<', '>'], "__") + .replace("::", "__") + ); + insta::assert_csv_snapshot!(name, &actual as &[_], { "[]" => insta::rounded_redaction(6) }); } } diff --git a/src/math/lut.rs b/src/math/lut.rs index d781dc7..e006a2e 100644 --- a/src/math/lut.rs +++ b/src/math/lut.rs @@ -5,7 +5,7 @@ use numeric_literals::replace_float_literals; use crate::{simd::SimdPartialOrd, Scalar, SimdCast}; -use super::interpolation::Interpolate; +use super::interpolation::{Interpolate, SimdIndex, SimdInterpolatable}; #[derive(Debug, Clone)] pub struct Lut { @@ -18,15 +18,15 @@ impl Lut { Self { array, range } } - pub fn get(&self, index: T) -> T + pub fn get(&self, interp: &Interp, index: T) -> T where - T: Scalar + SimdCast, + T: Scalar + SimdInterpolatable, + >::Output: SimdIndex, Interp: Interpolate, - >::Output: Copy + Num + SimdPartialOrd, { let normalized = (index - self.range.start) / (self.range.end - self.range.start); let array_index = normalized * T::from_f64(N as f64); - Interp::interpolate_on_slice(array_index, &self.array) + interp.interpolate_on_slice(array_index, &self.array) } } diff --git a/src/math/snapshots/valib__math__interpolation__tests__test_interpolate_valib__math__interpolation__Cubic.snap b/src/math/snapshots/valib__math__interpolation__tests__test_interpolate_valib__math__interpolation__Cubic.snap new file mode 100644 index 0000000..78a1050 --- /dev/null +++ b/src/math/snapshots/valib__math__interpolation__tests__test_interpolate_valib__math__interpolation__Cubic.snap @@ -0,0 +1,16 @@ +--- +source: src/math/interpolation.rs +expression: "&actual as &[_]" +--- +0.0 +0.203125 +0.5 +0.796875 +1.0 +1.070313 +1.0625 +1.023438 +1.0 +1.0 +1.0 +1.0 diff --git a/src/math/snapshots/valib__math__interpolation__tests__test_interpolate_valib__math__interpolation__Hermite.snap b/src/math/snapshots/valib__math__interpolation__tests__test_interpolate_valib__math__interpolation__Hermite.snap new file mode 100644 index 0000000..78a1050 --- /dev/null +++ b/src/math/snapshots/valib__math__interpolation__tests__test_interpolate_valib__math__interpolation__Hermite.snap @@ -0,0 +1,16 @@ +--- +source: src/math/interpolation.rs +expression: "&actual as &[_]" +--- +0.0 +0.203125 +0.5 +0.796875 +1.0 +1.070313 +1.0625 +1.023438 +1.0 +1.0 +1.0 +1.0 diff --git a/src/math/snapshots/valib__math__interpolation__tests__test_interpolate_valib__math__interpolation__Linear.snap b/src/math/snapshots/valib__math__interpolation__tests__test_interpolate_valib__math__interpolation__Linear.snap new file mode 100644 index 0000000..db2b6e8 --- /dev/null +++ b/src/math/snapshots/valib__math__interpolation__tests__test_interpolate_valib__math__interpolation__Linear.snap @@ -0,0 +1,16 @@ +--- +source: src/math/interpolation.rs +expression: "&actual as &[_]" +--- +0.0 +0.25 +0.5 +0.75 +1.0 +1.0 +1.0 +1.0 +1.0 +1.0 +1.0 +1.0 diff --git a/src/math/snapshots/valib__math__interpolation__tests__test_interpolate_valib__math__interpolation__MappedLinear__valib__math__interpolation__sine_interpolation__f64____{{closure}}__.snap b/src/math/snapshots/valib__math__interpolation__tests__test_interpolate_valib__math__interpolation__MappedLinear__valib__math__interpolation__sine_interpolation__f64____{{closure}}__.snap new file mode 100644 index 0000000..76a32eb --- /dev/null +++ b/src/math/snapshots/valib__math__interpolation__tests__test_interpolate_valib__math__interpolation__MappedLinear__valib__math__interpolation__sine_interpolation__f64____{{closure}}__.snap @@ -0,0 +1,16 @@ +--- +source: src/math/interpolation.rs +expression: "&actual as &[_]" +--- +1.0 +0.707107 +0.0 +-0.707107 +1.0 +1.0 +1.0 +1.0 +1.0 +1.0 +1.0 +1.0 diff --git a/src/math/snapshots/valib__math__interpolation__tests__test_interpolate_valib__math__interpolation__Nearest.snap b/src/math/snapshots/valib__math__interpolation__tests__test_interpolate_valib__math__interpolation__Nearest.snap new file mode 100644 index 0000000..c91757e --- /dev/null +++ b/src/math/snapshots/valib__math__interpolation__tests__test_interpolate_valib__math__interpolation__Nearest.snap @@ -0,0 +1,16 @@ +--- +source: src/math/interpolation.rs +expression: "&actual as &[_]" +--- +0.0 +0.0 +0.0 +1.0 +1.0 +1.0 +1.0 +1.0 +1.0 +1.0 +1.0 +1.0 diff --git a/src/math/snapshots/valib__math__interpolation__tests__test_interpolate_valib__math__interpolation__ZeroHold.snap b/src/math/snapshots/valib__math__interpolation__tests__test_interpolate_valib__math__interpolation__ZeroHold.snap new file mode 100644 index 0000000..652b1ee --- /dev/null +++ b/src/math/snapshots/valib__math__interpolation__tests__test_interpolate_valib__math__interpolation__ZeroHold.snap @@ -0,0 +1,16 @@ +--- +source: src/math/interpolation.rs +expression: "&actual as &[_]" +--- +0.0 +0.0 +0.0 +0.0 +1.0 +1.0 +1.0 +1.0 +1.0 +1.0 +1.0 +1.0 diff --git a/src/oscillators/wavetable.rs b/src/oscillators/wavetable.rs index 1f80dde..d7a4d24 100644 --- a/src/oscillators/wavetable.rs +++ b/src/oscillators/wavetable.rs @@ -2,6 +2,7 @@ use std::{marker::PhantomData, ops::Range}; use num_traits::Num; +use crate::math::interpolation::{SimdIndex, SimdInterpolatable}; use crate::{ dsp::DSP, math::interpolation::{Interpolate, Linear}, @@ -13,49 +14,55 @@ use crate::{ /// its DSP implementation expects a phasor signal as its first input pub struct Wavetable { array: [T; N], - __interp: PhantomData, + interpolation: Interp, } impl, const N: usize, const I: usize, Interp: Interpolate> DSP<1, 1> for Wavetable where - >::Output: Copy + Num + SimdPartialOrd, + T: Scalar + SimdInterpolatable, + >::Output: SimdIndex, { type Sample = T; fn process(&mut self, [phase]: [Self::Sample; 1]) -> [Self::Sample; 1] { - let y = Interp::interpolate_on_slice(phase.simd_fract(), &self.array); + let y = self + .interpolation + .interpolate_on_slice(phase.simd_fract(), &self.array); [y] } } impl Wavetable { - pub const fn new(array: [T; N]) -> Self { + pub const fn new(interpolation: Interp, array: [T; N]) -> Self { Self { array, - __interp: PhantomData, + interpolation, } } } impl Wavetable { - pub fn from_fn(range: Range, f: impl Fn(T) -> T) -> Self { + pub fn from_fn(interpolation: Interp, range: Range, f: impl Fn(T) -> T) -> Self { let r = range.end - range.start; let step = T::from_f64(N as f64) / r; - Self::new(std::array::from_fn(|i| { - let x = T::from_f64(i as f64) * step; - f(x) - })) + Self::new( + interpolation, + std::array::from_fn(|i| { + let x = T::from_f64(i as f64) * step; + f(x) + }), + ) } - pub fn sin() -> Self { - Self::from_fn(T::zero()..T::simd_two_pi(), |x| x.simd_sin()) + pub fn sin(interpolation: Interp) -> Self { + Self::from_fn(interpolation, T::zero()..T::simd_two_pi(), |x| x.simd_sin()) } - pub fn cos() -> Self { - Self::from_fn(T::zero()..T::simd_two_pi(), |x| x.simd_cos()) + pub fn cos(interpolation: Interp) -> Self { + Self::from_fn(interpolation, T::zero()..T::simd_two_pi(), |x| x.simd_cos()) } - pub fn tan() -> Self { - Self::from_fn(T::zero()..T::simd_two_pi(), |x| x.simd_tan()) + pub fn tan(interpolation: Interp) -> Self { + Self::from_fn(interpolation, T::zero()..T::simd_two_pi(), |x| x.simd_tan()) } } diff --git a/src/util.rs b/src/util.rs index 190b603..d180110 100644 --- a/src/util.rs +++ b/src/util.rs @@ -56,7 +56,7 @@ where pub fn lerp(t: T, a: T, b: T) -> T { use crate::math::interpolation::{Interpolate, Linear}; - Linear::interpolate(t, [a, b]) + Linear.interpolate(t, [a, b]) } #[replace_float_literals(T::from_f64(literal))] @@ -78,7 +78,7 @@ mod tests { let a = [0., 1., 1.]; let mut actual = [0.; 12]; let expected = [0., 0.25, 0.5, 0.75, 1., 1., 1., 1., 1., 1., 1., 1.]; - Linear::interpolate_slice(&mut actual, &a); + Linear.interpolate_slice(&mut actual, &a); assert_eq!(actual, expected); } } From 89aa042b02a1a03f8f697a471ad86e5f262a97f3 Mon Sep 17 00:00:00 2001 From: Nathan Graule Date: Sat, 17 Feb 2024 15:27:27 +0100 Subject: [PATCH 05/21] refactor: oversample module to use interpolation primitives instead of IIR filters --- src/oversample/mod.rs | 83 +- .../valib__oversample__tests__os block.snap | 3934 ++++++++--------- ...rsample__tests__oversampled_dsp_block.snap | 88 +- .../valib__oversample__tests__post os.snap | 990 ++--- 4 files changed, 2531 insertions(+), 2564 deletions(-) diff --git a/src/oversample/mod.rs b/src/oversample/mod.rs index 25249fc..c5bfddb 100644 --- a/src/oversample/mod.rs +++ b/src/oversample/mod.rs @@ -1,16 +1,14 @@ +use num_traits::Num; use std::ops::{Deref, DerefMut}; use crate::dsp::parameter::{HasParameters, Parameter}; +use crate::dsp::DSPBlock; use crate::dsp::{ utils::{mono_block_to_slice, mono_block_to_slice_mut, slice_to_mono_block_mut}, DSP, }; -use crate::saturators::{Clipper, Linear}; -use crate::Scalar; -use crate::{ - dsp::{blocks::Series, DSPBlock}, - filters::biquad::Biquad, -}; +use crate::math::interpolation::{Cubic, Interpolate, SimdIndex, SimdInterpolatable}; +use crate::{Scalar, SimdCast}; const CASCADE: usize = 4; @@ -18,41 +16,36 @@ const CASCADE: usize = 4; pub struct Oversample { os_factor: usize, os_buffer: Box<[T]>, - pre_filter: Series<[Biquad; CASCADE]>, - post_filter: Series<[Biquad; CASCADE]>, } impl Oversample { pub fn new(os_factor: usize, max_block_size: usize) -> Self { assert!(os_factor > 1); let os_buffer = vec![T::zero(); max_block_size * os_factor].into_boxed_slice(); - let fc_raw = f64::recip(2.1 * os_factor as f64); - let cascade_adjustment = f64::sqrt(2f64.powf(1.0 / CASCADE as f64) - 1.0).recip(); - let fc_corr = fc_raw * cascade_adjustment; - println!("cascade adjustment {cascade_adjustment}: {fc_raw} -> {fc_corr}"); - let filters = - std::array::from_fn(|_| Biquad::lowpass(T::from_f64(fc_corr), T::from_f64(0.707))); Self { os_factor, os_buffer, - pre_filter: Series(filters), - post_filter: Series(filters), } } pub fn latency(&self) -> usize { - 2 * self.os_factor + DSP::latency(&self.pre_filter) + DSP::latency(&self.post_filter) + 2 * self.os_factor } pub fn max_block_size(&self) -> usize { self.os_buffer.len() / self.os_factor } - pub fn oversample(&mut self, buffer: &[T]) -> OversampleBlock { - let os_len = self.zero_stuff(buffer); - for s in &mut self.os_buffer[..os_len] { - *s = self.pre_filter.process([*s])[0]; - } + pub fn oversample(&mut self, buffer: &[T]) -> OversampleBlock + where + Cubic: Interpolate, + T: SimdInterpolatable, + >::Output: SimdIndex, + { + let os_len = buffer.len() * self.os_factor; + let output = &mut self.os_buffer[..os_len]; + Cubic.interpolate_slice(output, buffer); + OversampleBlock { filter: self, os_len, @@ -61,8 +54,6 @@ impl Oversample { pub fn reset(&mut self) { self.os_buffer.fill(T::zero()); - DSP::reset(&mut self.pre_filter); - DSP::reset(&mut self.post_filter); } pub fn with_dsp>(self, dsp: P) -> Oversampled { @@ -76,33 +67,6 @@ impl Oversample { inner: dsp, } } - - fn zero_stuff(&mut self, inp: &[T]) -> usize { - let os_len = inp.len() * self.os_factor; - assert!(self.os_buffer.len() >= os_len); - - self.os_buffer[..os_len].fill(T::zero()); - for (i, s) in inp.iter().copied().enumerate() { - self.os_buffer[self.os_factor * i] = s * T::from_f64(self.os_factor as f64); - } - os_len - } - - fn decimate(&mut self, out: &mut [T]) { - let os_len = out.len() * self.os_factor; - assert!(os_len <= self.os_buffer.len()); - - for (i, s) in self - .os_buffer - .iter() - .step_by(self.os_factor) - .copied() - .enumerate() - .take(out.len()) - { - out[i] = s; - } - } } pub struct OversampleBlock<'a, T> { @@ -124,13 +88,15 @@ impl<'a, T> DerefMut for OversampleBlock<'a, T> { } } -impl<'a, T: Scalar> OversampleBlock<'a, T> { +impl<'a, T: Scalar + SimdInterpolatable> OversampleBlock<'a, T> +where + Cubic: Interpolate, + >::Output: SimdIndex, +{ pub fn finish(self, out: &mut [T]) { - let filter = self.filter; - for s in &mut filter.os_buffer[..self.os_len] { - *s = filter.post_filter.process([*s])[0]; - } - filter.decimate(out); + let inlen = out.len() * self.filter.os_factor; + let input = &self.filter.os_buffer[..inlen]; + Cubic.interpolate_slice(out, input); } } @@ -164,7 +130,8 @@ where impl DSPBlock<1, 1> for Oversampled where - T: Scalar, + T: Scalar + SimdInterpolatable, + >::Output: SimdIndex, P: DSPBlock<1, 1, Sample = T>, { type Sample = T; diff --git a/src/oversample/snapshots/valib__oversample__tests__os block.snap b/src/oversample/snapshots/valib__oversample__tests__os block.snap index fc61de0..bec55b8 100644 --- a/src/oversample/snapshots/valib__oversample__tests__os block.snap +++ b/src/oversample/snapshots/valib__oversample__tests__os block.snap @@ -3,2050 +3,2050 @@ source: src/oversample/mod.rs expression: "&*osblock" --- 0.0 -0.0 -0.0 -0.0 -0.0 -0.0 -0.0 -0.0 -0.0 -0.0 -0.001 -0.002 -0.004 -0.008 -0.016 -0.028 -0.045 -0.067 -0.092 -0.12 -0.149 -0.177 -0.204 -0.229 -0.254 -0.277 -0.299 -0.322 -0.345 -0.367 -0.39 -0.412 -0.435 -0.457 -0.479 -0.5 -0.521 -0.542 -0.562 -0.583 -0.602 -0.622 -0.641 -0.659 -0.678 -0.695 -0.713 -0.73 -0.746 -0.763 -0.778 -0.793 -0.808 -0.822 -0.836 -0.849 -0.862 -0.874 -0.886 -0.897 -0.907 -0.918 -0.927 -0.936 -0.944 -0.952 -0.959 -0.966 -0.972 -0.977 -0.982 -0.987 -0.99 -0.993 -0.996 -0.998 +0.018 +0.043 +0.071 +0.098 +0.122 +0.147 +0.171 +0.195 +0.219 +0.243 +0.267 +0.29 +0.314 +0.337 +0.36 +0.383 +0.405 +0.428 +0.45 +0.471 +0.493 +0.514 +0.535 +0.556 +0.576 +0.596 +0.615 +0.634 +0.653 +0.672 +0.69 +0.707 +0.724 +0.741 +0.757 +0.773 +0.788 +0.803 +0.818 +0.831 +0.845 +0.858 +0.87 +0.882 +0.893 +0.904 +0.914 +0.924 +0.933 +0.942 +0.95 +0.957 +0.964 +0.97 +0.976 +0.981 +0.985 +0.989 +0.992 +0.995 +0.997 0.999 1.0 1.0 +1.0 0.999 -0.998 0.997 -0.994 -0.991 -0.988 -0.984 -0.979 -0.974 -0.968 -0.962 -0.955 -0.947 -0.939 -0.93 -0.921 -0.911 -0.9 -0.889 -0.878 -0.866 -0.853 -0.84 -0.827 -0.813 -0.798 -0.783 -0.768 -0.752 -0.735 -0.719 -0.701 -0.684 -0.665 -0.647 -0.628 -0.609 -0.589 -0.569 -0.549 -0.528 -0.507 -0.486 -0.464 -0.442 -0.42 -0.398 -0.375 -0.352 -0.329 -0.306 -0.282 -0.259 -0.235 -0.211 -0.187 -0.163 -0.139 -0.114 -0.09 -0.065 -0.041 -0.016 --0.008 --0.033 --0.057 --0.082 --0.106 --0.131 --0.155 --0.179 --0.203 --0.227 --0.251 --0.275 --0.298 --0.322 --0.345 --0.368 --0.39 --0.413 --0.435 --0.457 --0.479 --0.5 --0.521 --0.542 --0.562 --0.583 --0.602 --0.622 --0.641 --0.659 --0.678 --0.695 --0.713 --0.73 --0.746 --0.763 --0.778 --0.793 --0.808 --0.822 --0.836 --0.849 --0.862 --0.874 --0.886 --0.897 --0.907 --0.918 --0.927 --0.936 --0.944 --0.952 --0.959 --0.966 --0.972 --0.977 --0.982 --0.987 --0.99 --0.993 --0.996 --0.998 +0.995 +0.992 +0.989 +0.985 +0.981 +0.976 +0.97 +0.964 +0.957 +0.95 +0.942 +0.933 +0.924 +0.914 +0.904 +0.893 +0.882 +0.87 +0.858 +0.845 +0.831 +0.818 +0.803 +0.788 +0.773 +0.757 +0.741 +0.724 +0.707 +0.69 +0.672 +0.653 +0.634 +0.615 +0.596 +0.576 +0.556 +0.535 +0.514 +0.493 +0.471 +0.45 +0.428 +0.405 +0.383 +0.36 +0.337 +0.314 +0.29 +0.267 +0.243 +0.219 +0.195 +0.171 +0.147 +0.122 +0.098 +0.074 +0.049 +0.025 +-0.0 +-0.025 +-0.049 +-0.074 +-0.098 +-0.122 +-0.147 +-0.171 +-0.195 +-0.219 +-0.243 +-0.267 +-0.29 +-0.314 +-0.337 +-0.36 +-0.383 +-0.405 +-0.428 +-0.45 +-0.471 +-0.493 +-0.514 +-0.535 +-0.556 +-0.576 +-0.596 +-0.615 +-0.634 +-0.653 +-0.672 +-0.69 +-0.707 +-0.724 +-0.741 +-0.757 +-0.773 +-0.788 +-0.803 +-0.818 +-0.831 +-0.845 +-0.858 +-0.87 +-0.882 +-0.893 +-0.904 +-0.914 +-0.924 +-0.933 +-0.942 +-0.95 +-0.957 +-0.964 +-0.97 +-0.976 +-0.981 +-0.985 +-0.989 +-0.992 +-0.995 +-0.997 -0.999 -1.0 -1.0 +-1.0 -0.999 --0.998 -0.997 --0.994 --0.991 --0.988 --0.984 --0.979 --0.974 --0.968 --0.962 --0.955 --0.947 --0.939 --0.93 --0.921 --0.911 --0.9 --0.889 --0.878 --0.866 --0.853 --0.84 --0.827 --0.813 --0.798 --0.783 --0.768 --0.752 --0.735 --0.719 --0.701 --0.684 --0.665 --0.647 --0.628 --0.609 --0.589 --0.569 --0.549 --0.528 --0.507 --0.486 --0.464 --0.442 --0.42 --0.398 --0.375 --0.352 --0.329 --0.306 --0.282 --0.259 --0.235 --0.211 --0.187 --0.163 --0.139 --0.114 --0.09 --0.065 --0.041 --0.016 -0.008 -0.033 -0.057 -0.082 -0.106 -0.131 -0.155 -0.179 -0.203 -0.227 -0.251 -0.275 -0.298 -0.322 -0.345 -0.368 -0.39 -0.413 -0.435 -0.457 -0.479 -0.5 -0.521 -0.542 -0.562 -0.583 -0.602 -0.622 -0.641 -0.659 -0.678 -0.695 -0.713 -0.73 -0.746 -0.763 -0.778 -0.793 -0.808 -0.822 -0.836 -0.849 -0.862 -0.874 -0.886 -0.897 -0.907 -0.918 -0.927 -0.936 -0.944 -0.952 -0.959 -0.966 -0.972 -0.977 -0.982 -0.987 -0.99 -0.993 -0.996 -0.998 +-0.995 +-0.992 +-0.989 +-0.985 +-0.981 +-0.976 +-0.97 +-0.964 +-0.957 +-0.95 +-0.942 +-0.933 +-0.924 +-0.914 +-0.904 +-0.893 +-0.882 +-0.87 +-0.858 +-0.845 +-0.831 +-0.818 +-0.803 +-0.788 +-0.773 +-0.757 +-0.741 +-0.724 +-0.707 +-0.69 +-0.672 +-0.653 +-0.634 +-0.615 +-0.596 +-0.576 +-0.556 +-0.535 +-0.514 +-0.493 +-0.471 +-0.45 +-0.428 +-0.405 +-0.383 +-0.36 +-0.337 +-0.314 +-0.29 +-0.267 +-0.243 +-0.219 +-0.195 +-0.171 +-0.147 +-0.122 +-0.098 +-0.074 +-0.049 +-0.025 +0.0 +0.025 +0.049 +0.074 +0.098 +0.122 +0.147 +0.171 +0.195 +0.219 +0.243 +0.267 +0.29 +0.314 +0.337 +0.36 +0.383 +0.405 +0.428 +0.45 +0.471 +0.493 +0.514 +0.535 +0.556 +0.576 +0.596 +0.615 +0.634 +0.653 +0.672 +0.69 +0.707 +0.724 +0.741 +0.757 +0.773 +0.788 +0.803 +0.818 +0.831 +0.845 +0.858 +0.87 +0.882 +0.893 +0.904 +0.914 +0.924 +0.933 +0.942 +0.95 +0.957 +0.964 +0.97 +0.976 +0.981 +0.985 +0.989 +0.992 +0.995 +0.997 0.999 1.0 1.0 +1.0 0.999 -0.998 0.997 -0.994 -0.991 -0.988 -0.984 -0.979 -0.974 -0.968 -0.962 -0.955 -0.947 -0.939 -0.93 -0.921 -0.911 -0.9 -0.889 -0.878 -0.866 -0.853 -0.84 -0.827 -0.813 -0.798 -0.783 -0.768 -0.752 -0.735 -0.719 -0.701 -0.684 -0.665 -0.647 -0.628 -0.609 -0.589 -0.569 -0.549 -0.528 -0.507 -0.486 -0.464 -0.442 -0.42 -0.398 -0.375 -0.352 -0.329 -0.306 -0.282 -0.259 -0.235 -0.211 -0.187 -0.163 -0.139 -0.114 -0.09 -0.065 -0.041 -0.016 --0.008 --0.033 --0.057 --0.082 --0.106 --0.131 --0.155 --0.179 --0.203 --0.227 --0.251 --0.275 --0.298 --0.322 --0.345 --0.368 --0.39 --0.413 --0.435 --0.457 --0.479 --0.5 --0.521 --0.542 --0.562 --0.583 --0.602 --0.622 --0.641 --0.659 --0.678 --0.695 --0.713 --0.73 --0.746 --0.763 --0.778 --0.793 --0.808 --0.822 --0.836 --0.849 --0.862 --0.874 --0.886 --0.897 --0.907 --0.918 --0.927 --0.936 --0.944 --0.952 --0.959 --0.966 --0.972 --0.977 --0.982 --0.987 --0.99 --0.993 --0.996 --0.998 +0.995 +0.992 +0.989 +0.985 +0.981 +0.976 +0.97 +0.964 +0.957 +0.95 +0.942 +0.933 +0.924 +0.914 +0.904 +0.893 +0.882 +0.87 +0.858 +0.845 +0.831 +0.818 +0.803 +0.788 +0.773 +0.757 +0.741 +0.724 +0.707 +0.69 +0.672 +0.653 +0.634 +0.615 +0.596 +0.576 +0.556 +0.535 +0.514 +0.493 +0.471 +0.45 +0.428 +0.405 +0.383 +0.36 +0.337 +0.314 +0.29 +0.267 +0.243 +0.219 +0.195 +0.171 +0.147 +0.122 +0.098 +0.074 +0.049 +0.025 +-0.0 +-0.025 +-0.049 +-0.074 +-0.098 +-0.122 +-0.147 +-0.171 +-0.195 +-0.219 +-0.243 +-0.267 +-0.29 +-0.314 +-0.337 +-0.36 +-0.383 +-0.405 +-0.428 +-0.45 +-0.471 +-0.493 +-0.514 +-0.535 +-0.556 +-0.576 +-0.596 +-0.615 +-0.634 +-0.653 +-0.672 +-0.69 +-0.707 +-0.724 +-0.741 +-0.757 +-0.773 +-0.788 +-0.803 +-0.818 +-0.831 +-0.845 +-0.858 +-0.87 +-0.882 +-0.893 +-0.904 +-0.914 +-0.924 +-0.933 +-0.942 +-0.95 +-0.957 +-0.964 +-0.97 +-0.976 +-0.981 +-0.985 +-0.989 +-0.992 +-0.995 +-0.997 -0.999 -1.0 -1.0 +-1.0 -0.999 --0.998 -0.997 --0.994 --0.991 --0.988 --0.984 --0.979 --0.974 --0.968 --0.962 --0.955 --0.947 --0.939 --0.93 --0.921 --0.911 --0.9 --0.889 --0.878 --0.866 --0.853 --0.84 --0.827 --0.813 --0.798 --0.783 --0.768 --0.752 --0.735 --0.719 --0.701 --0.684 --0.665 --0.647 --0.628 --0.609 --0.589 --0.569 --0.549 --0.528 --0.507 --0.486 --0.464 --0.442 --0.42 --0.398 --0.375 --0.352 --0.329 --0.306 --0.282 --0.259 --0.235 --0.211 --0.187 --0.163 --0.139 --0.114 --0.09 --0.065 --0.041 --0.016 -0.008 -0.033 -0.057 -0.082 -0.106 -0.131 -0.155 -0.179 -0.203 -0.227 -0.251 -0.275 -0.298 -0.322 -0.345 -0.368 -0.39 -0.413 -0.435 -0.457 -0.479 -0.5 -0.521 -0.542 -0.562 -0.583 -0.602 -0.622 -0.641 -0.659 -0.678 -0.695 -0.713 -0.73 -0.746 -0.763 -0.778 -0.793 -0.808 -0.822 -0.836 -0.849 -0.862 -0.874 -0.886 -0.897 -0.907 -0.918 -0.927 -0.936 -0.944 -0.952 -0.959 -0.966 -0.972 -0.977 -0.982 -0.987 -0.99 -0.993 -0.996 -0.998 +-0.995 +-0.992 +-0.989 +-0.985 +-0.981 +-0.976 +-0.97 +-0.964 +-0.957 +-0.95 +-0.942 +-0.933 +-0.924 +-0.914 +-0.904 +-0.893 +-0.882 +-0.87 +-0.858 +-0.845 +-0.831 +-0.818 +-0.803 +-0.788 +-0.773 +-0.757 +-0.741 +-0.724 +-0.707 +-0.69 +-0.672 +-0.653 +-0.634 +-0.615 +-0.596 +-0.576 +-0.556 +-0.535 +-0.514 +-0.493 +-0.471 +-0.45 +-0.428 +-0.405 +-0.383 +-0.36 +-0.337 +-0.314 +-0.29 +-0.267 +-0.243 +-0.219 +-0.195 +-0.171 +-0.147 +-0.122 +-0.098 +-0.074 +-0.049 +-0.025 +0.0 +0.025 +0.049 +0.074 +0.098 +0.122 +0.147 +0.171 +0.195 +0.219 +0.243 +0.267 +0.29 +0.314 +0.337 +0.36 +0.383 +0.405 +0.428 +0.45 +0.471 +0.493 +0.514 +0.535 +0.556 +0.576 +0.596 +0.615 +0.634 +0.653 +0.672 +0.69 +0.707 +0.724 +0.741 +0.757 +0.773 +0.788 +0.803 +0.818 +0.831 +0.845 +0.858 +0.87 +0.882 +0.893 +0.904 +0.914 +0.924 +0.933 +0.942 +0.95 +0.957 +0.964 +0.97 +0.976 +0.981 +0.985 +0.989 +0.992 +0.995 +0.997 0.999 1.0 1.0 +1.0 0.999 -0.998 0.997 -0.994 -0.991 -0.988 -0.984 -0.979 -0.974 -0.968 -0.962 -0.955 -0.947 -0.939 -0.93 -0.921 -0.911 -0.9 -0.889 -0.878 -0.866 -0.853 -0.84 -0.827 -0.813 -0.798 -0.783 -0.768 -0.752 -0.735 -0.719 -0.701 -0.684 -0.665 -0.647 -0.628 -0.609 -0.589 -0.569 -0.549 -0.528 -0.507 -0.486 -0.464 -0.442 -0.42 -0.398 -0.375 -0.352 -0.329 -0.306 -0.282 -0.259 -0.235 -0.211 -0.187 -0.163 -0.139 -0.114 -0.09 -0.065 -0.041 -0.016 --0.008 --0.033 --0.057 --0.082 --0.106 --0.131 --0.155 --0.179 --0.203 --0.227 --0.251 --0.275 --0.298 --0.322 --0.345 --0.368 --0.39 --0.413 --0.435 --0.457 --0.479 --0.5 --0.521 --0.542 --0.562 --0.583 --0.602 --0.622 --0.641 --0.659 --0.678 --0.695 --0.713 --0.73 --0.746 --0.763 --0.778 --0.793 --0.808 --0.822 --0.836 --0.849 --0.862 --0.874 --0.886 --0.897 --0.907 --0.918 --0.927 --0.936 --0.944 --0.952 --0.959 --0.966 --0.972 --0.977 --0.982 --0.987 --0.99 --0.993 --0.996 --0.998 +0.995 +0.992 +0.989 +0.985 +0.981 +0.976 +0.97 +0.964 +0.957 +0.95 +0.942 +0.933 +0.924 +0.914 +0.904 +0.893 +0.882 +0.87 +0.858 +0.845 +0.831 +0.818 +0.803 +0.788 +0.773 +0.757 +0.741 +0.724 +0.707 +0.69 +0.672 +0.653 +0.634 +0.615 +0.596 +0.576 +0.556 +0.535 +0.514 +0.493 +0.471 +0.45 +0.428 +0.405 +0.383 +0.36 +0.337 +0.314 +0.29 +0.267 +0.243 +0.219 +0.195 +0.171 +0.147 +0.122 +0.098 +0.074 +0.049 +0.025 +-0.0 +-0.025 +-0.049 +-0.074 +-0.098 +-0.122 +-0.147 +-0.171 +-0.195 +-0.219 +-0.243 +-0.267 +-0.29 +-0.314 +-0.337 +-0.36 +-0.383 +-0.405 +-0.428 +-0.45 +-0.471 +-0.493 +-0.514 +-0.535 +-0.556 +-0.576 +-0.596 +-0.615 +-0.634 +-0.653 +-0.672 +-0.69 +-0.707 +-0.724 +-0.741 +-0.757 +-0.773 +-0.788 +-0.803 +-0.818 +-0.831 +-0.845 +-0.858 +-0.87 +-0.882 +-0.893 +-0.904 +-0.914 +-0.924 +-0.933 +-0.942 +-0.95 +-0.957 +-0.964 +-0.97 +-0.976 +-0.981 +-0.985 +-0.989 +-0.992 +-0.995 +-0.997 -0.999 -1.0 -1.0 +-1.0 -0.999 --0.998 -0.997 --0.994 --0.991 --0.988 --0.984 --0.979 --0.974 --0.968 --0.962 --0.955 --0.947 --0.939 --0.93 --0.921 --0.911 --0.9 --0.889 --0.878 --0.866 --0.853 --0.84 --0.827 --0.813 --0.798 --0.783 --0.768 --0.752 --0.735 --0.719 --0.701 --0.684 --0.665 --0.647 --0.628 --0.609 --0.589 --0.569 --0.549 --0.528 --0.507 --0.486 --0.464 --0.442 --0.42 --0.398 --0.375 --0.352 --0.329 --0.306 --0.282 --0.259 --0.235 --0.211 --0.187 --0.163 --0.139 --0.114 --0.09 --0.065 --0.041 --0.016 -0.008 -0.033 -0.057 -0.082 -0.106 -0.131 -0.155 -0.179 -0.203 -0.227 -0.251 -0.275 -0.298 -0.322 -0.345 -0.368 -0.39 -0.413 -0.435 -0.457 -0.479 -0.5 -0.521 -0.542 -0.562 -0.583 -0.602 -0.622 -0.641 -0.659 -0.678 -0.695 -0.713 -0.73 -0.746 -0.763 -0.778 -0.793 -0.808 -0.822 -0.836 -0.849 -0.862 -0.874 -0.886 -0.897 -0.907 -0.918 -0.927 -0.936 -0.944 -0.952 -0.959 -0.966 -0.972 -0.977 -0.982 -0.987 -0.99 -0.993 -0.996 -0.998 +-0.995 +-0.992 +-0.989 +-0.985 +-0.981 +-0.976 +-0.97 +-0.964 +-0.957 +-0.95 +-0.942 +-0.933 +-0.924 +-0.914 +-0.904 +-0.893 +-0.882 +-0.87 +-0.858 +-0.845 +-0.831 +-0.818 +-0.803 +-0.788 +-0.773 +-0.757 +-0.741 +-0.724 +-0.707 +-0.69 +-0.672 +-0.653 +-0.634 +-0.615 +-0.596 +-0.576 +-0.556 +-0.535 +-0.514 +-0.493 +-0.471 +-0.45 +-0.428 +-0.405 +-0.383 +-0.36 +-0.337 +-0.314 +-0.29 +-0.267 +-0.243 +-0.219 +-0.195 +-0.171 +-0.147 +-0.122 +-0.098 +-0.074 +-0.049 +-0.025 +0.0 +0.025 +0.049 +0.074 +0.098 +0.122 +0.147 +0.171 +0.195 +0.219 +0.243 +0.267 +0.29 +0.314 +0.337 +0.36 +0.383 +0.405 +0.428 +0.45 +0.471 +0.493 +0.514 +0.535 +0.556 +0.576 +0.596 +0.615 +0.634 +0.653 +0.672 +0.69 +0.707 +0.724 +0.741 +0.757 +0.773 +0.788 +0.803 +0.818 +0.831 +0.845 +0.858 +0.87 +0.882 +0.893 +0.904 +0.914 +0.924 +0.933 +0.942 +0.95 +0.957 +0.964 +0.97 +0.976 +0.981 +0.985 +0.989 +0.992 +0.995 +0.997 0.999 1.0 1.0 +1.0 0.999 -0.998 0.997 -0.994 -0.991 -0.988 -0.984 -0.979 -0.974 -0.968 -0.962 -0.955 -0.947 -0.939 -0.93 -0.921 -0.911 -0.9 -0.889 -0.878 -0.866 -0.853 -0.84 -0.827 -0.813 -0.798 -0.783 -0.768 -0.752 -0.735 -0.719 -0.701 -0.684 -0.665 -0.647 -0.628 -0.609 -0.589 -0.569 -0.549 -0.528 -0.507 -0.486 -0.464 -0.442 -0.42 -0.398 -0.375 -0.352 -0.329 -0.306 -0.282 -0.259 -0.235 -0.211 -0.187 -0.163 -0.139 -0.114 -0.09 -0.065 -0.041 -0.016 --0.008 --0.033 --0.057 --0.082 --0.106 --0.131 --0.155 --0.179 --0.203 --0.227 --0.251 --0.275 --0.298 --0.322 --0.345 --0.368 --0.39 --0.413 --0.435 --0.457 --0.479 --0.5 --0.521 --0.542 --0.562 --0.583 --0.602 --0.622 --0.641 --0.659 --0.678 --0.695 --0.713 --0.73 --0.746 --0.763 --0.778 --0.793 --0.808 --0.822 --0.836 --0.849 --0.862 --0.874 --0.886 --0.897 --0.907 --0.918 --0.927 --0.936 --0.944 --0.952 --0.959 --0.966 --0.972 --0.977 --0.982 --0.987 --0.99 --0.993 --0.996 --0.998 +0.995 +0.992 +0.989 +0.985 +0.981 +0.976 +0.97 +0.964 +0.957 +0.95 +0.942 +0.933 +0.924 +0.914 +0.904 +0.893 +0.882 +0.87 +0.858 +0.845 +0.831 +0.818 +0.803 +0.788 +0.773 +0.757 +0.741 +0.724 +0.707 +0.69 +0.672 +0.653 +0.634 +0.615 +0.596 +0.576 +0.556 +0.535 +0.514 +0.493 +0.471 +0.45 +0.428 +0.405 +0.383 +0.36 +0.337 +0.314 +0.29 +0.267 +0.243 +0.219 +0.195 +0.171 +0.147 +0.122 +0.098 +0.074 +0.049 +0.025 +-0.0 +-0.025 +-0.049 +-0.074 +-0.098 +-0.122 +-0.147 +-0.171 +-0.195 +-0.219 +-0.243 +-0.267 +-0.29 +-0.314 +-0.337 +-0.36 +-0.383 +-0.405 +-0.428 +-0.45 +-0.471 +-0.493 +-0.514 +-0.535 +-0.556 +-0.576 +-0.596 +-0.615 +-0.634 +-0.653 +-0.672 +-0.69 +-0.707 +-0.724 +-0.741 +-0.757 +-0.773 +-0.788 +-0.803 +-0.818 +-0.831 +-0.845 +-0.858 +-0.87 +-0.882 +-0.893 +-0.904 +-0.914 +-0.924 +-0.933 +-0.942 +-0.95 +-0.957 +-0.964 +-0.97 +-0.976 +-0.981 +-0.985 +-0.989 +-0.992 +-0.995 +-0.997 -0.999 -1.0 -1.0 +-1.0 -0.999 --0.998 -0.997 --0.994 --0.991 --0.988 --0.984 --0.979 --0.974 --0.968 --0.962 --0.955 --0.947 --0.939 --0.93 --0.921 --0.911 --0.9 --0.889 --0.878 --0.866 --0.853 --0.84 --0.827 --0.813 --0.798 --0.783 --0.768 --0.752 --0.735 --0.719 --0.701 --0.684 --0.665 --0.647 --0.628 --0.609 --0.589 --0.569 --0.549 --0.528 --0.507 --0.486 --0.464 --0.442 --0.42 --0.398 --0.375 --0.352 --0.329 --0.306 --0.282 --0.259 --0.235 --0.211 --0.187 --0.163 --0.139 --0.114 --0.09 --0.065 --0.041 --0.016 -0.008 -0.033 -0.057 -0.082 -0.106 -0.131 -0.155 -0.179 -0.203 -0.227 -0.251 -0.275 -0.298 -0.322 -0.345 -0.368 -0.39 -0.413 -0.435 -0.457 -0.479 -0.5 -0.521 -0.542 -0.562 -0.583 -0.602 -0.622 -0.641 -0.659 -0.678 -0.695 -0.713 -0.73 -0.746 -0.763 -0.778 -0.793 -0.808 -0.822 -0.836 -0.849 -0.862 -0.874 -0.886 -0.897 -0.907 -0.918 -0.927 -0.936 -0.944 -0.952 -0.959 -0.966 -0.972 -0.977 -0.982 -0.987 -0.99 -0.993 -0.996 -0.998 +-0.995 +-0.992 +-0.989 +-0.985 +-0.981 +-0.976 +-0.97 +-0.964 +-0.957 +-0.95 +-0.942 +-0.933 +-0.924 +-0.914 +-0.904 +-0.893 +-0.882 +-0.87 +-0.858 +-0.845 +-0.831 +-0.818 +-0.803 +-0.788 +-0.773 +-0.757 +-0.741 +-0.724 +-0.707 +-0.69 +-0.672 +-0.653 +-0.634 +-0.615 +-0.596 +-0.576 +-0.556 +-0.535 +-0.514 +-0.493 +-0.471 +-0.45 +-0.428 +-0.405 +-0.383 +-0.36 +-0.337 +-0.314 +-0.29 +-0.267 +-0.243 +-0.219 +-0.195 +-0.171 +-0.147 +-0.122 +-0.098 +-0.074 +-0.049 +-0.025 +0.0 +0.025 +0.049 +0.074 +0.098 +0.122 +0.147 +0.171 +0.195 +0.219 +0.243 +0.267 +0.29 +0.314 +0.337 +0.36 +0.383 +0.405 +0.428 +0.45 +0.471 +0.493 +0.514 +0.535 +0.556 +0.576 +0.596 +0.615 +0.634 +0.653 +0.672 +0.69 +0.707 +0.724 +0.741 +0.757 +0.773 +0.788 +0.803 +0.818 +0.831 +0.845 +0.858 +0.87 +0.882 +0.893 +0.904 +0.914 +0.924 +0.933 +0.942 +0.95 +0.957 +0.964 +0.97 +0.976 +0.981 +0.985 +0.989 +0.992 +0.995 +0.997 0.999 1.0 1.0 +1.0 0.999 -0.998 0.997 -0.994 -0.991 -0.988 -0.984 -0.979 -0.974 -0.968 -0.962 -0.955 -0.947 -0.939 -0.93 -0.921 -0.911 -0.9 -0.889 -0.878 -0.866 -0.853 -0.84 -0.827 -0.813 -0.798 -0.783 -0.768 -0.752 -0.735 -0.719 -0.701 -0.684 -0.665 -0.647 -0.628 -0.609 -0.589 -0.569 -0.549 -0.528 -0.507 -0.486 -0.464 -0.442 -0.42 -0.398 -0.375 -0.352 -0.329 -0.306 -0.282 -0.259 -0.235 -0.211 -0.187 -0.163 -0.139 -0.114 -0.09 -0.065 -0.041 -0.016 --0.008 --0.033 --0.057 --0.082 --0.106 --0.131 --0.155 --0.179 --0.203 --0.227 --0.251 --0.275 --0.298 --0.322 --0.345 --0.368 --0.39 --0.413 --0.435 --0.457 --0.479 --0.5 --0.521 --0.542 --0.562 --0.583 --0.602 --0.622 --0.641 --0.659 --0.678 --0.695 --0.713 --0.73 --0.746 --0.763 --0.778 --0.793 --0.808 --0.822 --0.836 --0.849 --0.862 --0.874 --0.886 --0.897 --0.907 --0.918 --0.927 --0.936 --0.944 --0.952 --0.959 --0.966 --0.972 --0.977 --0.982 --0.987 --0.99 --0.993 --0.996 --0.998 +0.995 +0.992 +0.989 +0.985 +0.981 +0.976 +0.97 +0.964 +0.957 +0.95 +0.942 +0.933 +0.924 +0.914 +0.904 +0.893 +0.882 +0.87 +0.858 +0.845 +0.831 +0.818 +0.803 +0.788 +0.773 +0.757 +0.741 +0.724 +0.707 +0.69 +0.672 +0.653 +0.634 +0.615 +0.596 +0.576 +0.556 +0.535 +0.514 +0.493 +0.471 +0.45 +0.428 +0.405 +0.383 +0.36 +0.337 +0.314 +0.29 +0.267 +0.243 +0.219 +0.195 +0.171 +0.147 +0.122 +0.098 +0.074 +0.049 +0.025 +-0.0 +-0.025 +-0.049 +-0.074 +-0.098 +-0.122 +-0.147 +-0.171 +-0.195 +-0.219 +-0.243 +-0.267 +-0.29 +-0.314 +-0.337 +-0.36 +-0.383 +-0.405 +-0.428 +-0.45 +-0.471 +-0.493 +-0.514 +-0.535 +-0.556 +-0.576 +-0.596 +-0.615 +-0.634 +-0.653 +-0.672 +-0.69 +-0.707 +-0.724 +-0.741 +-0.757 +-0.773 +-0.788 +-0.803 +-0.818 +-0.831 +-0.845 +-0.858 +-0.87 +-0.882 +-0.893 +-0.904 +-0.914 +-0.924 +-0.933 +-0.942 +-0.95 +-0.957 +-0.964 +-0.97 +-0.976 +-0.981 +-0.985 +-0.989 +-0.992 +-0.995 +-0.997 -0.999 -1.0 -1.0 +-1.0 -0.999 --0.998 -0.997 --0.994 --0.991 --0.988 --0.984 --0.979 --0.974 --0.968 --0.962 --0.955 --0.947 --0.939 --0.93 --0.921 --0.911 --0.9 --0.889 --0.878 --0.866 --0.853 --0.84 --0.827 --0.813 --0.798 --0.783 --0.768 --0.752 --0.735 --0.719 --0.701 --0.684 --0.665 --0.647 --0.628 --0.609 --0.589 --0.569 --0.549 --0.528 --0.507 --0.486 --0.464 --0.442 --0.42 --0.398 --0.375 --0.352 --0.329 --0.306 --0.282 --0.259 --0.235 --0.211 --0.187 --0.163 --0.139 --0.114 --0.09 --0.065 --0.041 --0.016 -0.008 -0.033 -0.057 -0.082 -0.106 -0.131 -0.155 -0.179 -0.203 -0.227 -0.251 -0.275 -0.298 -0.322 -0.345 -0.368 -0.39 -0.413 -0.435 -0.457 -0.479 -0.5 -0.521 -0.542 -0.562 -0.583 -0.602 -0.622 -0.641 -0.659 -0.678 -0.695 -0.713 -0.73 -0.746 -0.763 -0.778 -0.793 -0.808 -0.822 -0.836 -0.849 -0.862 -0.874 -0.886 -0.897 -0.907 -0.918 -0.927 -0.936 -0.944 -0.952 -0.959 -0.966 -0.972 -0.977 -0.982 -0.987 -0.99 -0.993 -0.996 -0.998 +-0.995 +-0.992 +-0.989 +-0.985 +-0.981 +-0.976 +-0.97 +-0.964 +-0.957 +-0.95 +-0.942 +-0.933 +-0.924 +-0.914 +-0.904 +-0.893 +-0.882 +-0.87 +-0.858 +-0.845 +-0.831 +-0.818 +-0.803 +-0.788 +-0.773 +-0.757 +-0.741 +-0.724 +-0.707 +-0.69 +-0.672 +-0.653 +-0.634 +-0.615 +-0.596 +-0.576 +-0.556 +-0.535 +-0.514 +-0.493 +-0.471 +-0.45 +-0.428 +-0.405 +-0.383 +-0.36 +-0.337 +-0.314 +-0.29 +-0.267 +-0.243 +-0.219 +-0.195 +-0.171 +-0.147 +-0.122 +-0.098 +-0.074 +-0.049 +-0.025 +0.0 +0.025 +0.049 +0.074 +0.098 +0.122 +0.147 +0.171 +0.195 +0.219 +0.243 +0.267 +0.29 +0.314 +0.337 +0.36 +0.383 +0.405 +0.428 +0.45 +0.471 +0.493 +0.514 +0.535 +0.556 +0.576 +0.596 +0.615 +0.634 +0.653 +0.672 +0.69 +0.707 +0.724 +0.741 +0.757 +0.773 +0.788 +0.803 +0.818 +0.831 +0.845 +0.858 +0.87 +0.882 +0.893 +0.904 +0.914 +0.924 +0.933 +0.942 +0.95 +0.957 +0.964 +0.97 +0.976 +0.981 +0.985 +0.989 +0.992 +0.995 +0.997 0.999 1.0 1.0 +1.0 0.999 -0.998 0.997 -0.994 -0.991 -0.988 -0.984 -0.979 -0.974 -0.968 -0.962 -0.955 -0.947 -0.939 -0.93 -0.921 -0.911 -0.9 -0.889 -0.878 -0.866 -0.853 -0.84 -0.827 -0.813 -0.798 -0.783 -0.768 -0.752 -0.735 -0.719 -0.701 -0.684 -0.665 -0.647 -0.628 -0.609 -0.589 -0.569 -0.549 -0.528 -0.507 -0.486 -0.464 -0.442 -0.42 -0.398 -0.375 -0.352 -0.329 -0.306 -0.282 -0.259 -0.235 -0.211 -0.187 -0.163 -0.139 -0.114 -0.09 -0.065 -0.041 -0.016 --0.008 --0.033 --0.057 --0.082 --0.106 --0.131 --0.155 --0.179 --0.203 --0.227 --0.251 --0.275 --0.298 --0.322 --0.345 --0.368 --0.39 --0.413 --0.435 --0.457 --0.479 --0.5 --0.521 --0.542 --0.562 --0.583 --0.602 --0.622 --0.641 --0.659 --0.678 --0.695 --0.713 --0.73 --0.746 --0.763 --0.778 --0.793 --0.808 --0.822 --0.836 --0.849 --0.862 --0.874 --0.886 --0.897 --0.907 --0.918 --0.927 --0.936 --0.944 --0.952 --0.959 --0.966 --0.972 --0.977 --0.982 --0.987 --0.99 --0.993 --0.996 --0.998 +0.995 +0.992 +0.989 +0.985 +0.981 +0.976 +0.97 +0.964 +0.957 +0.95 +0.942 +0.933 +0.924 +0.914 +0.904 +0.893 +0.882 +0.87 +0.858 +0.845 +0.831 +0.818 +0.803 +0.788 +0.773 +0.757 +0.741 +0.724 +0.707 +0.69 +0.672 +0.653 +0.634 +0.615 +0.596 +0.576 +0.556 +0.535 +0.514 +0.493 +0.471 +0.45 +0.428 +0.405 +0.383 +0.36 +0.337 +0.314 +0.29 +0.267 +0.243 +0.219 +0.195 +0.171 +0.147 +0.122 +0.098 +0.074 +0.049 +0.025 +-0.0 +-0.025 +-0.049 +-0.074 +-0.098 +-0.122 +-0.147 +-0.171 +-0.195 +-0.219 +-0.243 +-0.267 +-0.29 +-0.314 +-0.337 +-0.36 +-0.383 +-0.405 +-0.428 +-0.45 +-0.471 +-0.493 +-0.514 +-0.535 +-0.556 +-0.576 +-0.596 +-0.615 +-0.634 +-0.653 +-0.672 +-0.69 +-0.707 +-0.724 +-0.741 +-0.757 +-0.773 +-0.788 +-0.803 +-0.818 +-0.831 +-0.845 +-0.858 +-0.87 +-0.882 +-0.893 +-0.904 +-0.914 +-0.924 +-0.933 +-0.942 +-0.95 +-0.957 +-0.964 +-0.97 +-0.976 +-0.981 +-0.985 +-0.989 +-0.992 +-0.995 +-0.997 -0.999 -1.0 -1.0 +-1.0 -0.999 --0.998 -0.997 --0.994 --0.991 --0.988 --0.984 --0.979 --0.974 --0.968 --0.962 --0.955 --0.947 --0.939 --0.93 --0.921 --0.911 --0.9 --0.889 --0.878 --0.866 --0.853 --0.84 --0.827 --0.813 --0.798 --0.783 --0.768 --0.752 --0.735 --0.719 --0.701 --0.684 --0.665 --0.647 --0.628 --0.609 --0.589 --0.569 --0.549 --0.528 --0.507 --0.486 --0.464 --0.442 --0.42 --0.398 --0.375 --0.352 --0.329 --0.306 --0.282 --0.259 --0.235 --0.211 --0.187 --0.163 --0.139 --0.114 --0.09 --0.065 --0.041 --0.016 -0.008 -0.033 -0.057 -0.082 -0.106 -0.131 -0.155 -0.179 -0.203 -0.227 -0.251 -0.275 -0.298 -0.322 -0.345 -0.368 -0.39 -0.413 -0.435 -0.457 -0.479 -0.5 -0.521 -0.542 -0.562 -0.583 -0.602 -0.622 -0.641 -0.659 -0.678 -0.695 -0.713 -0.73 -0.746 -0.763 -0.778 -0.793 -0.808 -0.822 -0.836 -0.849 -0.862 -0.874 -0.886 -0.897 -0.907 -0.918 -0.927 -0.936 -0.944 -0.952 -0.959 -0.966 -0.972 -0.977 -0.982 -0.987 -0.99 -0.993 -0.996 -0.998 +-0.995 +-0.992 +-0.989 +-0.985 +-0.981 +-0.976 +-0.97 +-0.964 +-0.957 +-0.95 +-0.942 +-0.933 +-0.924 +-0.914 +-0.904 +-0.893 +-0.882 +-0.87 +-0.858 +-0.845 +-0.831 +-0.818 +-0.803 +-0.788 +-0.773 +-0.757 +-0.741 +-0.724 +-0.707 +-0.69 +-0.672 +-0.653 +-0.634 +-0.615 +-0.596 +-0.576 +-0.556 +-0.535 +-0.514 +-0.493 +-0.471 +-0.45 +-0.428 +-0.405 +-0.383 +-0.36 +-0.337 +-0.314 +-0.29 +-0.267 +-0.243 +-0.219 +-0.195 +-0.171 +-0.147 +-0.122 +-0.098 +-0.074 +-0.049 +-0.025 +0.0 +0.025 +0.049 +0.074 +0.098 +0.122 +0.147 +0.171 +0.195 +0.219 +0.243 +0.267 +0.29 +0.314 +0.337 +0.36 +0.383 +0.405 +0.428 +0.45 +0.471 +0.493 +0.514 +0.535 +0.556 +0.576 +0.596 +0.615 +0.634 +0.653 +0.672 +0.69 +0.707 +0.724 +0.741 +0.757 +0.773 +0.788 +0.803 +0.818 +0.831 +0.845 +0.858 +0.87 +0.882 +0.893 +0.904 +0.914 +0.924 +0.933 +0.942 +0.95 +0.957 +0.964 +0.97 +0.976 +0.981 +0.985 +0.989 +0.992 +0.995 +0.997 0.999 1.0 1.0 +1.0 0.999 -0.998 0.997 -0.994 -0.991 -0.988 -0.984 -0.979 -0.974 -0.968 -0.962 -0.955 -0.947 -0.939 -0.93 -0.921 -0.911 -0.9 -0.889 -0.878 -0.866 -0.853 -0.84 -0.827 -0.813 -0.798 -0.783 -0.768 -0.752 -0.735 -0.719 -0.701 -0.684 -0.665 -0.647 -0.628 -0.609 -0.589 -0.569 -0.549 -0.528 -0.507 -0.486 -0.464 -0.442 -0.42 -0.398 -0.375 -0.352 -0.329 -0.306 -0.282 -0.259 -0.235 -0.211 -0.187 -0.163 -0.139 -0.114 -0.09 -0.065 -0.041 -0.016 --0.008 --0.033 --0.057 --0.082 --0.106 --0.131 --0.155 --0.179 --0.203 --0.227 --0.251 --0.275 --0.298 --0.322 --0.345 --0.368 --0.39 --0.413 --0.435 --0.457 --0.479 --0.5 --0.521 --0.542 --0.562 --0.583 --0.602 --0.622 --0.641 --0.659 --0.678 --0.695 --0.713 --0.73 --0.746 --0.763 --0.778 --0.793 --0.808 --0.822 --0.836 --0.849 --0.862 --0.874 --0.886 --0.897 --0.907 --0.918 --0.927 --0.936 --0.944 --0.952 --0.959 --0.966 --0.972 --0.977 --0.982 --0.987 --0.99 --0.993 --0.996 --0.998 +0.995 +0.992 +0.989 +0.985 +0.981 +0.976 +0.97 +0.964 +0.957 +0.95 +0.942 +0.933 +0.924 +0.914 +0.904 +0.893 +0.882 +0.87 +0.858 +0.845 +0.831 +0.818 +0.803 +0.788 +0.773 +0.757 +0.741 +0.724 +0.707 +0.69 +0.672 +0.653 +0.634 +0.615 +0.596 +0.576 +0.556 +0.535 +0.514 +0.493 +0.471 +0.45 +0.428 +0.405 +0.383 +0.36 +0.337 +0.314 +0.29 +0.267 +0.243 +0.219 +0.195 +0.171 +0.147 +0.122 +0.098 +0.074 +0.049 +0.025 +-0.0 +-0.025 +-0.049 +-0.074 +-0.098 +-0.122 +-0.147 +-0.171 +-0.195 +-0.219 +-0.243 +-0.267 +-0.29 +-0.314 +-0.337 +-0.36 +-0.383 +-0.405 +-0.428 +-0.45 +-0.471 +-0.493 +-0.514 +-0.535 +-0.556 +-0.576 +-0.596 +-0.615 +-0.634 +-0.653 +-0.672 +-0.69 +-0.707 +-0.724 +-0.741 +-0.757 +-0.773 +-0.788 +-0.803 +-0.818 +-0.831 +-0.845 +-0.858 +-0.87 +-0.882 +-0.893 +-0.904 +-0.914 +-0.924 +-0.933 +-0.942 +-0.95 +-0.957 +-0.964 +-0.97 +-0.976 +-0.981 +-0.985 +-0.989 +-0.992 +-0.995 +-0.997 -0.999 -1.0 -1.0 +-1.0 -0.999 --0.998 -0.997 --0.994 --0.991 --0.988 --0.984 --0.979 --0.974 --0.968 --0.962 --0.955 --0.947 --0.939 --0.93 --0.921 --0.911 --0.9 --0.889 --0.878 --0.866 --0.853 --0.84 --0.827 --0.813 --0.798 --0.783 --0.768 --0.752 --0.735 --0.719 --0.701 --0.684 --0.665 --0.647 --0.628 --0.609 --0.589 --0.569 --0.549 --0.528 --0.507 --0.486 --0.464 --0.442 --0.42 --0.398 --0.375 --0.352 --0.329 --0.306 --0.282 --0.259 --0.235 --0.211 --0.187 --0.163 --0.139 --0.114 --0.09 --0.065 --0.041 --0.016 -0.008 -0.033 -0.057 -0.082 -0.106 -0.131 -0.155 -0.179 -0.203 -0.227 -0.251 -0.275 -0.298 -0.322 -0.345 -0.368 -0.39 -0.413 -0.435 -0.457 -0.479 -0.5 -0.521 -0.542 -0.562 -0.583 -0.602 -0.622 -0.641 -0.659 -0.678 -0.695 -0.713 -0.73 -0.746 -0.763 -0.778 -0.793 -0.808 -0.822 -0.836 -0.849 -0.862 -0.874 -0.886 -0.897 -0.907 -0.918 -0.927 -0.936 -0.944 -0.952 -0.959 -0.966 -0.972 -0.977 -0.982 -0.987 -0.99 -0.993 -0.996 -0.998 +-0.995 +-0.992 +-0.989 +-0.985 +-0.981 +-0.976 +-0.97 +-0.964 +-0.957 +-0.95 +-0.942 +-0.933 +-0.924 +-0.914 +-0.904 +-0.893 +-0.882 +-0.87 +-0.858 +-0.845 +-0.831 +-0.818 +-0.803 +-0.788 +-0.773 +-0.757 +-0.741 +-0.724 +-0.707 +-0.69 +-0.672 +-0.653 +-0.634 +-0.615 +-0.596 +-0.576 +-0.556 +-0.535 +-0.514 +-0.493 +-0.471 +-0.45 +-0.428 +-0.405 +-0.383 +-0.36 +-0.337 +-0.314 +-0.29 +-0.267 +-0.243 +-0.219 +-0.195 +-0.171 +-0.147 +-0.122 +-0.098 +-0.074 +-0.049 +-0.025 +0.0 +0.025 +0.049 +0.074 +0.098 +0.122 +0.147 +0.171 +0.195 +0.219 +0.243 +0.267 +0.29 +0.314 +0.337 +0.36 +0.383 +0.405 +0.428 +0.45 +0.471 +0.493 +0.514 +0.535 +0.556 +0.576 +0.596 +0.615 +0.634 +0.653 +0.672 +0.69 +0.707 +0.724 +0.741 +0.757 +0.773 +0.788 +0.803 +0.818 +0.831 +0.845 +0.858 +0.87 +0.882 +0.893 +0.904 +0.914 +0.924 +0.933 +0.942 +0.95 +0.957 +0.964 +0.97 +0.976 +0.981 +0.985 +0.989 +0.992 +0.995 +0.997 0.999 1.0 1.0 +1.0 0.999 -0.998 0.997 -0.994 -0.991 -0.988 -0.984 -0.979 -0.974 -0.968 -0.962 -0.955 -0.947 -0.939 -0.93 -0.921 -0.911 -0.9 -0.889 -0.878 -0.866 -0.853 -0.84 -0.827 -0.813 -0.798 -0.783 -0.768 -0.752 -0.735 -0.719 -0.701 -0.684 -0.665 -0.647 -0.628 -0.609 -0.589 -0.569 -0.549 -0.528 -0.507 -0.486 -0.464 -0.442 -0.42 -0.398 -0.375 -0.352 -0.329 -0.306 -0.282 -0.259 -0.235 -0.211 -0.187 -0.163 -0.139 -0.114 -0.09 -0.065 -0.041 -0.016 --0.008 --0.033 --0.057 --0.082 --0.106 --0.131 --0.155 --0.179 --0.203 --0.227 --0.251 --0.275 --0.298 --0.322 --0.345 --0.368 --0.39 --0.413 --0.435 --0.457 --0.479 --0.5 --0.521 --0.542 --0.562 --0.583 --0.602 --0.622 --0.641 --0.659 --0.678 --0.695 --0.713 --0.73 --0.746 --0.763 --0.778 --0.793 --0.808 --0.822 --0.836 --0.849 --0.862 --0.874 --0.886 --0.897 --0.907 --0.918 --0.927 --0.936 --0.944 --0.952 --0.959 --0.966 --0.972 --0.977 --0.982 --0.987 --0.99 --0.993 --0.996 --0.998 +0.995 +0.992 +0.989 +0.985 +0.981 +0.976 +0.97 +0.964 +0.957 +0.95 +0.942 +0.933 +0.924 +0.914 +0.904 +0.893 +0.882 +0.87 +0.858 +0.845 +0.831 +0.818 +0.803 +0.788 +0.773 +0.757 +0.741 +0.724 +0.707 +0.69 +0.672 +0.653 +0.634 +0.615 +0.596 +0.576 +0.556 +0.535 +0.514 +0.493 +0.471 +0.45 +0.428 +0.405 +0.383 +0.36 +0.337 +0.314 +0.29 +0.267 +0.243 +0.219 +0.195 +0.171 +0.147 +0.122 +0.098 +0.074 +0.049 +0.025 +-0.0 +-0.025 +-0.049 +-0.074 +-0.098 +-0.122 +-0.147 +-0.171 +-0.195 +-0.219 +-0.243 +-0.267 +-0.29 +-0.314 +-0.337 +-0.36 +-0.383 +-0.405 +-0.428 +-0.45 +-0.471 +-0.493 +-0.514 +-0.535 +-0.556 +-0.576 +-0.596 +-0.615 +-0.634 +-0.653 +-0.672 +-0.69 +-0.707 +-0.724 +-0.741 +-0.757 +-0.773 +-0.788 +-0.803 +-0.818 +-0.831 +-0.845 +-0.858 +-0.87 +-0.882 +-0.893 +-0.904 +-0.914 +-0.924 +-0.933 +-0.942 +-0.95 +-0.957 +-0.964 +-0.97 +-0.976 +-0.981 +-0.985 +-0.989 +-0.992 +-0.995 +-0.997 -0.999 -1.0 -1.0 +-1.0 -0.999 --0.998 -0.997 --0.994 --0.991 --0.988 --0.984 --0.979 --0.974 --0.968 --0.962 --0.955 --0.947 --0.939 --0.93 --0.921 --0.911 --0.9 --0.889 --0.878 --0.866 --0.853 --0.84 --0.827 --0.813 --0.798 --0.783 --0.768 --0.752 --0.735 --0.719 --0.701 --0.684 --0.665 --0.647 --0.628 --0.609 --0.589 --0.569 --0.549 --0.528 --0.507 --0.486 --0.464 --0.442 --0.42 --0.398 --0.375 --0.352 +-0.995 +-0.992 +-0.989 +-0.985 +-0.981 +-0.976 +-0.97 +-0.964 +-0.957 +-0.95 +-0.942 +-0.933 +-0.924 +-0.914 +-0.904 +-0.893 +-0.882 +-0.87 +-0.858 +-0.845 +-0.831 +-0.818 +-0.803 +-0.788 +-0.773 +-0.757 +-0.741 +-0.724 +-0.707 +-0.69 +-0.672 +-0.653 +-0.634 +-0.615 +-0.596 +-0.576 +-0.556 +-0.535 +-0.514 +-0.493 +-0.471 +-0.45 +-0.428 +-0.405 +-0.383 +-0.36 +-0.337 +-0.314 +-0.29 +-0.267 +-0.243 +-0.219 +-0.195 +-0.169 +-0.141 +-0.116 +-0.098 +-0.091 +-0.092 +-0.096 diff --git a/src/oversample/snapshots/valib__oversample__tests__oversampled_dsp_block.snap b/src/oversample/snapshots/valib__oversample__tests__oversampled_dsp_block.snap index 0789ec1..289453a 100644 --- a/src/oversample/snapshots/valib__oversample__tests__oversampled_dsp_block.snap +++ b/src/oversample/snapshots/valib__oversample__tests__oversampled_dsp_block.snap @@ -2,67 +2,67 @@ source: src/oversample/mod.rs expression: "&output as &[_]" --- --0.0 --0.0 --0.015 --0.235 --0.845 --1.124 -1.0 --0.988 --1.003 -1.0 -1.0 -1.0 -1.0 -1.0 --0.999 --0.929 --0.271 -0.954 -1.204 -0.967 -0.99 -1.005 -0.999 +-1.0 +-1.0 +-1.0 +-1.0 +-1.0 +-1.0 +-1.0 +1.0 +1.0 +1.0 +1.0 1.0 1.0 1.0 1.0 -0.989 -0.722 --0.371 --1.234 --1.057 --0.963 --1.005 --1.001 --0.999 +1.0 +1.0 +1.0 +1.0 +1.0 +-1.0 +-1.0 +-1.0 +-1.0 +-1.0 +-1.0 -1.0 -1.0 -1.0 --0.999 --0.929 --0.271 -0.954 -1.204 -0.967 -0.99 -1.005 -0.999 +-1.0 +-1.0 +-1.0 +1.0 +1.0 +1.0 +1.0 +1.0 +1.0 +1.0 +1.0 1.0 1.0 1.0 1.0 -0.989 -0.722 --0.371 --1.234 --1.057 --0.963 --1.005 --1.001 --0.999 +1.0 +-1.0 +-1.0 +-1.0 +-1.0 +-1.0 -1.0 -1.0 -1.0 +-1.0 +-1.0 +-1.0 +-1.0 +1.0 diff --git a/src/oversample/snapshots/valib__oversample__tests__post os.snap b/src/oversample/snapshots/valib__oversample__tests__post os.snap index e67db05..00f2b5f 100644 --- a/src/oversample/snapshots/valib__oversample__tests__post os.snap +++ b/src/oversample/snapshots/valib__oversample__tests__post os.snap @@ -3,514 +3,514 @@ source: src/oversample/mod.rs expression: "&out as &[_]" --- 0.0 -0.0 -0.0 -0.0 -0.0 -0.0 -0.003 -0.026 -0.096 -0.207 -0.31 -0.398 -0.485 -0.569 -0.647 -0.719 -0.783 -0.841 -0.89 -0.93 -0.962 -0.984 -0.997 +0.098 +0.195 +0.29 +0.383 +0.471 +0.556 +0.634 +0.707 +0.773 +0.831 +0.882 +0.924 +0.957 +0.981 +0.995 1.0 -0.993 -0.977 -0.952 -0.917 -0.874 -0.822 -0.762 -0.695 -0.622 -0.542 -0.457 -0.367 -0.274 -0.179 -0.082 --0.017 --0.114 --0.211 --0.306 --0.398 --0.486 --0.569 --0.647 --0.719 --0.783 --0.841 --0.89 --0.93 --0.962 --0.984 --0.997 +0.995 +0.981 +0.957 +0.924 +0.882 +0.831 +0.773 +0.707 +0.634 +0.556 +0.471 +0.383 +0.29 +0.195 +0.098 +-0.0 +-0.098 +-0.195 +-0.29 +-0.383 +-0.471 +-0.556 +-0.634 +-0.707 +-0.773 +-0.831 +-0.882 +-0.924 +-0.957 +-0.981 +-0.995 -1.0 --0.993 --0.977 --0.952 --0.917 --0.874 --0.822 --0.762 --0.695 --0.622 --0.542 --0.457 --0.367 --0.274 --0.179 --0.082 -0.017 -0.114 -0.211 -0.306 -0.398 -0.486 -0.569 -0.647 -0.719 -0.783 -0.841 -0.89 -0.93 -0.962 -0.984 -0.997 +-0.995 +-0.981 +-0.957 +-0.924 +-0.882 +-0.831 +-0.773 +-0.707 +-0.634 +-0.556 +-0.471 +-0.383 +-0.29 +-0.195 +-0.098 +0.0 +0.098 +0.195 +0.29 +0.383 +0.471 +0.556 +0.634 +0.707 +0.773 +0.831 +0.882 +0.924 +0.957 +0.981 +0.995 1.0 -0.993 -0.977 -0.952 -0.917 -0.874 -0.822 -0.762 -0.695 -0.622 -0.542 -0.457 -0.367 -0.274 -0.179 -0.082 --0.017 --0.114 --0.211 --0.306 --0.398 --0.486 --0.569 --0.647 --0.719 --0.783 --0.841 --0.89 --0.93 --0.962 --0.984 --0.997 +0.995 +0.981 +0.957 +0.924 +0.882 +0.831 +0.773 +0.707 +0.634 +0.556 +0.471 +0.383 +0.29 +0.195 +0.098 +-0.0 +-0.098 +-0.195 +-0.29 +-0.383 +-0.471 +-0.556 +-0.634 +-0.707 +-0.773 +-0.831 +-0.882 +-0.924 +-0.957 +-0.981 +-0.995 -1.0 --0.993 --0.977 --0.952 --0.917 --0.874 --0.822 --0.762 --0.695 --0.622 --0.542 --0.457 --0.367 --0.274 --0.179 --0.082 -0.017 -0.114 -0.211 -0.306 -0.398 -0.486 -0.569 -0.647 -0.719 -0.783 -0.841 -0.89 -0.93 -0.962 -0.984 -0.997 +-0.995 +-0.981 +-0.957 +-0.924 +-0.882 +-0.831 +-0.773 +-0.707 +-0.634 +-0.556 +-0.471 +-0.383 +-0.29 +-0.195 +-0.098 +0.0 +0.098 +0.195 +0.29 +0.383 +0.471 +0.556 +0.634 +0.707 +0.773 +0.831 +0.882 +0.924 +0.957 +0.981 +0.995 1.0 -0.993 -0.977 -0.952 -0.917 -0.874 -0.822 -0.762 -0.695 -0.622 -0.542 -0.457 -0.367 -0.274 -0.179 -0.082 --0.017 --0.114 --0.211 --0.306 --0.398 --0.486 --0.569 --0.647 --0.719 --0.783 --0.841 --0.89 --0.93 --0.962 --0.984 --0.997 +0.995 +0.981 +0.957 +0.924 +0.882 +0.831 +0.773 +0.707 +0.634 +0.556 +0.471 +0.383 +0.29 +0.195 +0.098 +-0.0 +-0.098 +-0.195 +-0.29 +-0.383 +-0.471 +-0.556 +-0.634 +-0.707 +-0.773 +-0.831 +-0.882 +-0.924 +-0.957 +-0.981 +-0.995 -1.0 --0.993 --0.977 --0.952 --0.917 --0.874 --0.822 --0.762 --0.695 --0.622 --0.542 --0.457 --0.367 --0.274 --0.179 --0.082 -0.017 -0.114 -0.211 -0.306 -0.398 -0.486 -0.569 -0.647 -0.719 -0.783 -0.841 -0.89 -0.93 -0.962 -0.984 -0.997 +-0.995 +-0.981 +-0.957 +-0.924 +-0.882 +-0.831 +-0.773 +-0.707 +-0.634 +-0.556 +-0.471 +-0.383 +-0.29 +-0.195 +-0.098 +0.0 +0.098 +0.195 +0.29 +0.383 +0.471 +0.556 +0.634 +0.707 +0.773 +0.831 +0.882 +0.924 +0.957 +0.981 +0.995 1.0 -0.993 -0.977 -0.952 -0.917 -0.874 -0.822 -0.762 -0.695 -0.622 -0.542 -0.457 -0.367 -0.274 -0.179 -0.082 --0.017 --0.114 --0.211 --0.306 --0.398 --0.486 --0.569 --0.647 --0.719 --0.783 --0.841 --0.89 --0.93 --0.962 --0.984 --0.997 +0.995 +0.981 +0.957 +0.924 +0.882 +0.831 +0.773 +0.707 +0.634 +0.556 +0.471 +0.383 +0.29 +0.195 +0.098 +-0.0 +-0.098 +-0.195 +-0.29 +-0.383 +-0.471 +-0.556 +-0.634 +-0.707 +-0.773 +-0.831 +-0.882 +-0.924 +-0.957 +-0.981 +-0.995 -1.0 --0.993 --0.977 --0.952 --0.917 --0.874 --0.822 --0.762 --0.695 --0.622 --0.542 --0.457 --0.367 --0.274 --0.179 --0.082 -0.017 -0.114 -0.211 -0.306 -0.398 -0.486 -0.569 -0.647 -0.719 -0.783 -0.841 -0.89 -0.93 -0.962 -0.984 -0.997 +-0.995 +-0.981 +-0.957 +-0.924 +-0.882 +-0.831 +-0.773 +-0.707 +-0.634 +-0.556 +-0.471 +-0.383 +-0.29 +-0.195 +-0.098 +0.0 +0.098 +0.195 +0.29 +0.383 +0.471 +0.556 +0.634 +0.707 +0.773 +0.831 +0.882 +0.924 +0.957 +0.981 +0.995 1.0 -0.993 -0.977 -0.952 -0.917 -0.874 -0.822 -0.762 -0.695 -0.622 -0.542 -0.457 -0.367 -0.274 -0.179 -0.082 --0.017 --0.114 --0.211 --0.306 --0.398 --0.486 --0.569 --0.647 --0.719 --0.783 --0.841 --0.89 --0.93 --0.962 --0.984 --0.997 +0.995 +0.981 +0.957 +0.924 +0.882 +0.831 +0.773 +0.707 +0.634 +0.556 +0.471 +0.383 +0.29 +0.195 +0.098 +-0.0 +-0.098 +-0.195 +-0.29 +-0.383 +-0.471 +-0.556 +-0.634 +-0.707 +-0.773 +-0.831 +-0.882 +-0.924 +-0.957 +-0.981 +-0.995 -1.0 --0.993 --0.977 --0.952 --0.917 --0.874 --0.822 --0.762 --0.695 --0.622 --0.542 --0.457 --0.367 --0.274 --0.179 --0.082 -0.017 -0.114 -0.211 -0.306 -0.398 -0.486 -0.569 -0.647 -0.719 -0.783 -0.841 -0.89 -0.93 -0.962 -0.984 -0.997 +-0.995 +-0.981 +-0.957 +-0.924 +-0.882 +-0.831 +-0.773 +-0.707 +-0.634 +-0.556 +-0.471 +-0.383 +-0.29 +-0.195 +-0.098 +0.0 +0.098 +0.195 +0.29 +0.383 +0.471 +0.556 +0.634 +0.707 +0.773 +0.831 +0.882 +0.924 +0.957 +0.981 +0.995 1.0 -0.993 -0.977 -0.952 -0.917 -0.874 -0.822 -0.762 -0.695 -0.622 -0.542 -0.457 -0.367 -0.274 -0.179 -0.082 --0.017 --0.114 --0.211 --0.306 --0.398 --0.486 --0.569 --0.647 --0.719 --0.783 --0.841 --0.89 --0.93 --0.962 --0.984 --0.997 +0.995 +0.981 +0.957 +0.924 +0.882 +0.831 +0.773 +0.707 +0.634 +0.556 +0.471 +0.383 +0.29 +0.195 +0.098 +-0.0 +-0.098 +-0.195 +-0.29 +-0.383 +-0.471 +-0.556 +-0.634 +-0.707 +-0.773 +-0.831 +-0.882 +-0.924 +-0.957 +-0.981 +-0.995 -1.0 --0.993 --0.977 --0.952 --0.917 --0.874 --0.822 --0.762 --0.695 --0.622 --0.542 --0.457 --0.367 --0.274 --0.179 --0.082 -0.017 -0.114 -0.211 -0.306 -0.398 -0.486 -0.569 -0.647 -0.719 -0.783 -0.841 -0.89 -0.93 -0.962 -0.984 -0.997 +-0.995 +-0.981 +-0.957 +-0.924 +-0.882 +-0.831 +-0.773 +-0.707 +-0.634 +-0.556 +-0.471 +-0.383 +-0.29 +-0.195 +-0.098 +0.0 +0.098 +0.195 +0.29 +0.383 +0.471 +0.556 +0.634 +0.707 +0.773 +0.831 +0.882 +0.924 +0.957 +0.981 +0.995 1.0 -0.993 -0.977 -0.952 -0.917 -0.874 -0.822 -0.762 -0.695 -0.622 -0.542 -0.457 -0.367 -0.274 -0.179 -0.082 --0.017 --0.114 --0.211 --0.306 --0.398 --0.486 --0.569 --0.647 --0.719 --0.783 --0.841 --0.89 --0.93 --0.962 --0.984 --0.997 +0.995 +0.981 +0.957 +0.924 +0.882 +0.831 +0.773 +0.707 +0.634 +0.556 +0.471 +0.383 +0.29 +0.195 +0.098 +-0.0 +-0.098 +-0.195 +-0.29 +-0.383 +-0.471 +-0.556 +-0.634 +-0.707 +-0.773 +-0.831 +-0.882 +-0.924 +-0.957 +-0.981 +-0.995 -1.0 --0.993 --0.977 --0.952 --0.917 --0.874 --0.822 --0.762 --0.695 --0.622 --0.542 --0.457 --0.367 --0.274 --0.179 --0.082 -0.017 -0.114 -0.211 -0.306 -0.398 -0.486 -0.569 -0.647 -0.719 -0.783 -0.841 -0.89 -0.93 -0.962 -0.984 -0.997 +-0.995 +-0.981 +-0.957 +-0.924 +-0.882 +-0.831 +-0.773 +-0.707 +-0.634 +-0.556 +-0.471 +-0.383 +-0.29 +-0.195 +-0.098 +0.0 +0.098 +0.195 +0.29 +0.383 +0.471 +0.556 +0.634 +0.707 +0.773 +0.831 +0.882 +0.924 +0.957 +0.981 +0.995 1.0 -0.993 -0.977 -0.952 -0.917 -0.874 -0.822 -0.762 -0.695 -0.622 -0.542 -0.457 -0.367 -0.274 -0.179 -0.082 --0.017 --0.114 --0.211 --0.306 --0.398 --0.486 --0.569 --0.647 --0.719 --0.783 --0.841 --0.89 --0.93 --0.962 --0.984 --0.997 +0.995 +0.981 +0.957 +0.924 +0.882 +0.831 +0.773 +0.707 +0.634 +0.556 +0.471 +0.383 +0.29 +0.195 +0.098 +-0.0 +-0.098 +-0.195 +-0.29 +-0.383 +-0.471 +-0.556 +-0.634 +-0.707 +-0.773 +-0.831 +-0.882 +-0.924 +-0.957 +-0.981 +-0.995 -1.0 --0.993 --0.977 --0.952 --0.917 --0.874 --0.822 --0.762 --0.695 +-0.995 +-0.981 +-0.957 +-0.924 +-0.882 +-0.831 +-0.773 +-0.707 +-0.634 +-0.556 +-0.471 +-0.383 +-0.29 +-0.195 +-0.098 From d142288a2ad4ad934ad654e12d0fa10c5611f41c Mon Sep 17 00:00:00 2001 From: Nathan Graule Date: Sat, 17 Feb 2024 17:00:50 +0100 Subject: [PATCH 06/21] feat: lanczos interpolator --- src/math/interpolation.rs | 52 ++++++++++++++++++- ...e_valib__math__interpolation__Lanczos.snap | 16 ++++++ 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 src/math/snapshots/valib__math__interpolation__tests__test_interpolate_valib__math__interpolation__Lanczos.snap diff --git a/src/math/interpolation.rs b/src/math/interpolation.rs index 0076058..5449e4c 100644 --- a/src/math/interpolation.rs +++ b/src/math/interpolation.rs @@ -78,6 +78,7 @@ pub trait Interpolate { } /// Zero-hold interpolation, where the output is the input without additional computation. +#[derive(Debug, Copy, Clone)] pub struct ZeroHold; impl Interpolate for ZeroHold { @@ -91,6 +92,7 @@ impl Interpolate for ZeroHold { } /// Nearest-neighbor interpolation, where the neighbor is chosen based on how close it is to the floating index. +#[derive(Debug, Copy, Clone)] pub struct Nearest; impl Interpolate for Nearest { @@ -104,6 +106,7 @@ impl Interpolate for Nearest { } /// Standard-issue linear interpolation algorithm. +#[derive(Debug, Copy, Clone)] pub struct Linear; impl Interpolate for Linear { @@ -119,6 +122,7 @@ impl Interpolate for Linear { /// Sine interpolation, which is linear interpolation where the control input is first modulated by /// a cosine function. Produes smoother results than bare linear interpolation because the interpolation /// is smooth at the limits, as all derivatives are 0 there. +#[derive(Debug, Copy, Clone)] pub struct MappedLinear(pub F); pub fn sine_interpolation() -> MappedLinear T> { @@ -139,6 +143,7 @@ where } /// Cubic algorithm, smoother than [`Linear`], but needs 4 taps instead of 2. +#[derive(Debug, Copy, Clone)] pub struct Cubic; impl Interpolate for Cubic { @@ -158,6 +163,7 @@ impl Interpolate for Cubic { } /// 4-tap cubic Hermite spline interpolation +#[derive(Debug, Copy, Clone)] pub struct Hermite; impl Interpolate for Hermite { @@ -175,6 +181,41 @@ impl Interpolate for Hermite { } } +#[derive(Debug, Copy, Clone)] +pub struct Lanczos; + +impl Interpolate for Lanczos { + fn indices(index: usize) -> [usize; 7] { + std::array::from_fn(|i| (index + i).saturating_sub(3)) + } + + fn interpolate(&self, t: T, taps: [T; 7]) -> T { + let a = T::from_f64(3.0); + let x = T::from_f64(4.0) + t; + taps.iter() + .copied() + .enumerate() + .map(|(i, s)| { + let i = x.simd_floor() - a + T::one() + T::from_f64(i as _); + s * Self::window::(x - i) + }) + .reduce(|a, b| a + b) + .unwrap_or_else(T::zero) + } +} + +impl Lanczos { + fn window(t: T) -> T { + fn sinc(x: T) -> T { + let nx = x * T::simd_pi(); + let y = nx.simd_sin() / nx; + T::one().select(x.simd_eq(T::zero()), y) + } + + sinc(t) * sinc(t / T::from_f64(3.0)) + } +} + #[cfg(test)] mod tests { use rstest::rstest; @@ -183,7 +224,16 @@ mod tests { #[rstest] fn test_interpolate( - #[values(ZeroHold, Nearest, Linear, Cubic, Hermite, sine_interpolation())] interp: Interp, + #[values( + ZeroHold, + Nearest, + Linear, + Cubic, + Hermite, + sine_interpolation(), + Lanczos + )] + interp: Interp, ) where Interp: Interpolate, { diff --git a/src/math/snapshots/valib__math__interpolation__tests__test_interpolate_valib__math__interpolation__Lanczos.snap b/src/math/snapshots/valib__math__interpolation__tests__test_interpolate_valib__math__interpolation__Lanczos.snap new file mode 100644 index 0000000..04a2484 --- /dev/null +++ b/src/math/snapshots/valib__math__interpolation__tests__test_interpolate_valib__math__interpolation__Lanczos.snap @@ -0,0 +1,16 @@ +--- +source: src/math/interpolation.rs +expression: "&actual as &[_]" +--- +-0.0 +-0.049628 +-0.098371 +-0.097583 +0.0 +0.220562 +0.509556 +0.792484 +1.0 +1.110629 +1.117483 +1.062674 From acc333cdb6218665680ebd0c4484c1cbcce8a081 Mon Sep 17 00:00:00 2001 From: Nathan Graule Date: Sat, 17 Feb 2024 17:02:01 +0100 Subject: [PATCH 07/21] Revert "refactor: oversample module to use interpolation primitives instead of IIR filters" This reverts commit cbec640c9300dab7016bf5c437284e16357134d9. --- src/oversample/mod.rs | 83 +- .../valib__oversample__tests__os block.snap | 3934 ++++++++--------- ...rsample__tests__oversampled_dsp_block.snap | 88 +- .../valib__oversample__tests__post os.snap | 990 ++--- 4 files changed, 2564 insertions(+), 2531 deletions(-) diff --git a/src/oversample/mod.rs b/src/oversample/mod.rs index c5bfddb..25249fc 100644 --- a/src/oversample/mod.rs +++ b/src/oversample/mod.rs @@ -1,14 +1,16 @@ -use num_traits::Num; use std::ops::{Deref, DerefMut}; use crate::dsp::parameter::{HasParameters, Parameter}; -use crate::dsp::DSPBlock; use crate::dsp::{ utils::{mono_block_to_slice, mono_block_to_slice_mut, slice_to_mono_block_mut}, DSP, }; -use crate::math::interpolation::{Cubic, Interpolate, SimdIndex, SimdInterpolatable}; -use crate::{Scalar, SimdCast}; +use crate::saturators::{Clipper, Linear}; +use crate::Scalar; +use crate::{ + dsp::{blocks::Series, DSPBlock}, + filters::biquad::Biquad, +}; const CASCADE: usize = 4; @@ -16,36 +18,41 @@ const CASCADE: usize = 4; pub struct Oversample { os_factor: usize, os_buffer: Box<[T]>, + pre_filter: Series<[Biquad; CASCADE]>, + post_filter: Series<[Biquad; CASCADE]>, } impl Oversample { pub fn new(os_factor: usize, max_block_size: usize) -> Self { assert!(os_factor > 1); let os_buffer = vec![T::zero(); max_block_size * os_factor].into_boxed_slice(); + let fc_raw = f64::recip(2.1 * os_factor as f64); + let cascade_adjustment = f64::sqrt(2f64.powf(1.0 / CASCADE as f64) - 1.0).recip(); + let fc_corr = fc_raw * cascade_adjustment; + println!("cascade adjustment {cascade_adjustment}: {fc_raw} -> {fc_corr}"); + let filters = + std::array::from_fn(|_| Biquad::lowpass(T::from_f64(fc_corr), T::from_f64(0.707))); Self { os_factor, os_buffer, + pre_filter: Series(filters), + post_filter: Series(filters), } } pub fn latency(&self) -> usize { - 2 * self.os_factor + 2 * self.os_factor + DSP::latency(&self.pre_filter) + DSP::latency(&self.post_filter) } pub fn max_block_size(&self) -> usize { self.os_buffer.len() / self.os_factor } - pub fn oversample(&mut self, buffer: &[T]) -> OversampleBlock - where - Cubic: Interpolate, - T: SimdInterpolatable, - >::Output: SimdIndex, - { - let os_len = buffer.len() * self.os_factor; - let output = &mut self.os_buffer[..os_len]; - Cubic.interpolate_slice(output, buffer); - + pub fn oversample(&mut self, buffer: &[T]) -> OversampleBlock { + let os_len = self.zero_stuff(buffer); + for s in &mut self.os_buffer[..os_len] { + *s = self.pre_filter.process([*s])[0]; + } OversampleBlock { filter: self, os_len, @@ -54,6 +61,8 @@ impl Oversample { pub fn reset(&mut self) { self.os_buffer.fill(T::zero()); + DSP::reset(&mut self.pre_filter); + DSP::reset(&mut self.post_filter); } pub fn with_dsp>(self, dsp: P) -> Oversampled { @@ -67,6 +76,33 @@ impl Oversample { inner: dsp, } } + + fn zero_stuff(&mut self, inp: &[T]) -> usize { + let os_len = inp.len() * self.os_factor; + assert!(self.os_buffer.len() >= os_len); + + self.os_buffer[..os_len].fill(T::zero()); + for (i, s) in inp.iter().copied().enumerate() { + self.os_buffer[self.os_factor * i] = s * T::from_f64(self.os_factor as f64); + } + os_len + } + + fn decimate(&mut self, out: &mut [T]) { + let os_len = out.len() * self.os_factor; + assert!(os_len <= self.os_buffer.len()); + + for (i, s) in self + .os_buffer + .iter() + .step_by(self.os_factor) + .copied() + .enumerate() + .take(out.len()) + { + out[i] = s; + } + } } pub struct OversampleBlock<'a, T> { @@ -88,15 +124,13 @@ impl<'a, T> DerefMut for OversampleBlock<'a, T> { } } -impl<'a, T: Scalar + SimdInterpolatable> OversampleBlock<'a, T> -where - Cubic: Interpolate, - >::Output: SimdIndex, -{ +impl<'a, T: Scalar> OversampleBlock<'a, T> { pub fn finish(self, out: &mut [T]) { - let inlen = out.len() * self.filter.os_factor; - let input = &self.filter.os_buffer[..inlen]; - Cubic.interpolate_slice(out, input); + let filter = self.filter; + for s in &mut filter.os_buffer[..self.os_len] { + *s = filter.post_filter.process([*s])[0]; + } + filter.decimate(out); } } @@ -130,8 +164,7 @@ where impl DSPBlock<1, 1> for Oversampled where - T: Scalar + SimdInterpolatable, - >::Output: SimdIndex, + T: Scalar, P: DSPBlock<1, 1, Sample = T>, { type Sample = T; diff --git a/src/oversample/snapshots/valib__oversample__tests__os block.snap b/src/oversample/snapshots/valib__oversample__tests__os block.snap index bec55b8..fc61de0 100644 --- a/src/oversample/snapshots/valib__oversample__tests__os block.snap +++ b/src/oversample/snapshots/valib__oversample__tests__os block.snap @@ -3,2050 +3,2050 @@ source: src/oversample/mod.rs expression: "&*osblock" --- 0.0 -0.018 -0.043 -0.071 -0.098 -0.122 -0.147 -0.171 -0.195 -0.219 -0.243 -0.267 -0.29 -0.314 -0.337 -0.36 -0.383 -0.405 -0.428 -0.45 -0.471 -0.493 -0.514 -0.535 -0.556 -0.576 -0.596 -0.615 -0.634 -0.653 -0.672 -0.69 -0.707 -0.724 -0.741 -0.757 -0.773 -0.788 -0.803 -0.818 -0.831 -0.845 -0.858 -0.87 -0.882 -0.893 -0.904 -0.914 -0.924 -0.933 -0.942 -0.95 -0.957 -0.964 -0.97 -0.976 -0.981 -0.985 -0.989 -0.992 -0.995 -0.997 +0.0 +0.0 +0.0 +0.0 +0.0 +0.0 +0.0 +0.0 +0.0 +0.001 +0.002 +0.004 +0.008 +0.016 +0.028 +0.045 +0.067 +0.092 +0.12 +0.149 +0.177 +0.204 +0.229 +0.254 +0.277 +0.299 +0.322 +0.345 +0.367 +0.39 +0.412 +0.435 +0.457 +0.479 +0.5 +0.521 +0.542 +0.562 +0.583 +0.602 +0.622 +0.641 +0.659 +0.678 +0.695 +0.713 +0.73 +0.746 +0.763 +0.778 +0.793 +0.808 +0.822 +0.836 +0.849 +0.862 +0.874 +0.886 +0.897 +0.907 +0.918 +0.927 +0.936 +0.944 +0.952 +0.959 +0.966 +0.972 +0.977 +0.982 +0.987 +0.99 +0.993 +0.996 +0.998 0.999 1.0 1.0 -1.0 0.999 +0.998 0.997 -0.995 -0.992 -0.989 -0.985 -0.981 -0.976 -0.97 -0.964 -0.957 -0.95 -0.942 -0.933 -0.924 -0.914 -0.904 -0.893 -0.882 -0.87 -0.858 -0.845 -0.831 -0.818 -0.803 -0.788 -0.773 -0.757 -0.741 -0.724 -0.707 -0.69 -0.672 -0.653 -0.634 -0.615 -0.596 -0.576 -0.556 -0.535 -0.514 -0.493 -0.471 -0.45 -0.428 -0.405 -0.383 -0.36 -0.337 -0.314 -0.29 -0.267 -0.243 -0.219 -0.195 -0.171 -0.147 -0.122 -0.098 -0.074 -0.049 -0.025 --0.0 --0.025 --0.049 --0.074 --0.098 --0.122 --0.147 --0.171 --0.195 --0.219 --0.243 --0.267 --0.29 --0.314 --0.337 --0.36 --0.383 --0.405 --0.428 --0.45 --0.471 --0.493 --0.514 --0.535 --0.556 --0.576 --0.596 --0.615 --0.634 --0.653 --0.672 --0.69 --0.707 --0.724 --0.741 --0.757 --0.773 --0.788 --0.803 --0.818 --0.831 --0.845 --0.858 --0.87 --0.882 --0.893 --0.904 --0.914 --0.924 --0.933 --0.942 --0.95 --0.957 --0.964 --0.97 --0.976 --0.981 --0.985 --0.989 --0.992 --0.995 --0.997 +0.994 +0.991 +0.988 +0.984 +0.979 +0.974 +0.968 +0.962 +0.955 +0.947 +0.939 +0.93 +0.921 +0.911 +0.9 +0.889 +0.878 +0.866 +0.853 +0.84 +0.827 +0.813 +0.798 +0.783 +0.768 +0.752 +0.735 +0.719 +0.701 +0.684 +0.665 +0.647 +0.628 +0.609 +0.589 +0.569 +0.549 +0.528 +0.507 +0.486 +0.464 +0.442 +0.42 +0.398 +0.375 +0.352 +0.329 +0.306 +0.282 +0.259 +0.235 +0.211 +0.187 +0.163 +0.139 +0.114 +0.09 +0.065 +0.041 +0.016 +-0.008 +-0.033 +-0.057 +-0.082 +-0.106 +-0.131 +-0.155 +-0.179 +-0.203 +-0.227 +-0.251 +-0.275 +-0.298 +-0.322 +-0.345 +-0.368 +-0.39 +-0.413 +-0.435 +-0.457 +-0.479 +-0.5 +-0.521 +-0.542 +-0.562 +-0.583 +-0.602 +-0.622 +-0.641 +-0.659 +-0.678 +-0.695 +-0.713 +-0.73 +-0.746 +-0.763 +-0.778 +-0.793 +-0.808 +-0.822 +-0.836 +-0.849 +-0.862 +-0.874 +-0.886 +-0.897 +-0.907 +-0.918 +-0.927 +-0.936 +-0.944 +-0.952 +-0.959 +-0.966 +-0.972 +-0.977 +-0.982 +-0.987 +-0.99 +-0.993 +-0.996 +-0.998 -0.999 -1.0 -1.0 --1.0 -0.999 +-0.998 -0.997 --0.995 --0.992 --0.989 --0.985 --0.981 --0.976 --0.97 --0.964 --0.957 --0.95 --0.942 --0.933 --0.924 --0.914 --0.904 --0.893 --0.882 --0.87 --0.858 --0.845 --0.831 --0.818 --0.803 --0.788 --0.773 --0.757 --0.741 --0.724 --0.707 --0.69 --0.672 --0.653 --0.634 --0.615 --0.596 --0.576 --0.556 --0.535 --0.514 --0.493 --0.471 --0.45 --0.428 --0.405 --0.383 --0.36 --0.337 --0.314 --0.29 --0.267 --0.243 --0.219 --0.195 --0.171 --0.147 --0.122 --0.098 --0.074 --0.049 --0.025 -0.0 -0.025 -0.049 -0.074 -0.098 -0.122 -0.147 -0.171 -0.195 -0.219 -0.243 -0.267 -0.29 -0.314 -0.337 -0.36 -0.383 -0.405 -0.428 -0.45 -0.471 -0.493 -0.514 -0.535 -0.556 -0.576 -0.596 -0.615 -0.634 -0.653 -0.672 -0.69 -0.707 -0.724 -0.741 -0.757 -0.773 -0.788 -0.803 -0.818 -0.831 -0.845 -0.858 -0.87 -0.882 -0.893 -0.904 -0.914 -0.924 -0.933 -0.942 -0.95 -0.957 -0.964 -0.97 -0.976 -0.981 -0.985 -0.989 -0.992 -0.995 -0.997 +-0.994 +-0.991 +-0.988 +-0.984 +-0.979 +-0.974 +-0.968 +-0.962 +-0.955 +-0.947 +-0.939 +-0.93 +-0.921 +-0.911 +-0.9 +-0.889 +-0.878 +-0.866 +-0.853 +-0.84 +-0.827 +-0.813 +-0.798 +-0.783 +-0.768 +-0.752 +-0.735 +-0.719 +-0.701 +-0.684 +-0.665 +-0.647 +-0.628 +-0.609 +-0.589 +-0.569 +-0.549 +-0.528 +-0.507 +-0.486 +-0.464 +-0.442 +-0.42 +-0.398 +-0.375 +-0.352 +-0.329 +-0.306 +-0.282 +-0.259 +-0.235 +-0.211 +-0.187 +-0.163 +-0.139 +-0.114 +-0.09 +-0.065 +-0.041 +-0.016 +0.008 +0.033 +0.057 +0.082 +0.106 +0.131 +0.155 +0.179 +0.203 +0.227 +0.251 +0.275 +0.298 +0.322 +0.345 +0.368 +0.39 +0.413 +0.435 +0.457 +0.479 +0.5 +0.521 +0.542 +0.562 +0.583 +0.602 +0.622 +0.641 +0.659 +0.678 +0.695 +0.713 +0.73 +0.746 +0.763 +0.778 +0.793 +0.808 +0.822 +0.836 +0.849 +0.862 +0.874 +0.886 +0.897 +0.907 +0.918 +0.927 +0.936 +0.944 +0.952 +0.959 +0.966 +0.972 +0.977 +0.982 +0.987 +0.99 +0.993 +0.996 +0.998 0.999 1.0 1.0 -1.0 0.999 +0.998 0.997 -0.995 -0.992 -0.989 -0.985 -0.981 -0.976 -0.97 -0.964 -0.957 -0.95 -0.942 -0.933 -0.924 -0.914 -0.904 -0.893 -0.882 -0.87 -0.858 -0.845 -0.831 -0.818 -0.803 -0.788 -0.773 -0.757 -0.741 -0.724 -0.707 -0.69 -0.672 -0.653 -0.634 -0.615 -0.596 -0.576 -0.556 -0.535 -0.514 -0.493 -0.471 -0.45 -0.428 -0.405 -0.383 -0.36 -0.337 -0.314 -0.29 -0.267 -0.243 -0.219 -0.195 -0.171 -0.147 -0.122 -0.098 -0.074 -0.049 -0.025 --0.0 --0.025 --0.049 --0.074 --0.098 --0.122 --0.147 --0.171 --0.195 --0.219 --0.243 --0.267 --0.29 --0.314 --0.337 --0.36 --0.383 --0.405 --0.428 --0.45 --0.471 --0.493 --0.514 --0.535 --0.556 --0.576 --0.596 --0.615 --0.634 --0.653 --0.672 --0.69 --0.707 --0.724 --0.741 --0.757 --0.773 --0.788 --0.803 --0.818 --0.831 --0.845 --0.858 --0.87 --0.882 --0.893 --0.904 --0.914 --0.924 --0.933 --0.942 --0.95 --0.957 --0.964 --0.97 --0.976 --0.981 --0.985 --0.989 --0.992 --0.995 --0.997 +0.994 +0.991 +0.988 +0.984 +0.979 +0.974 +0.968 +0.962 +0.955 +0.947 +0.939 +0.93 +0.921 +0.911 +0.9 +0.889 +0.878 +0.866 +0.853 +0.84 +0.827 +0.813 +0.798 +0.783 +0.768 +0.752 +0.735 +0.719 +0.701 +0.684 +0.665 +0.647 +0.628 +0.609 +0.589 +0.569 +0.549 +0.528 +0.507 +0.486 +0.464 +0.442 +0.42 +0.398 +0.375 +0.352 +0.329 +0.306 +0.282 +0.259 +0.235 +0.211 +0.187 +0.163 +0.139 +0.114 +0.09 +0.065 +0.041 +0.016 +-0.008 +-0.033 +-0.057 +-0.082 +-0.106 +-0.131 +-0.155 +-0.179 +-0.203 +-0.227 +-0.251 +-0.275 +-0.298 +-0.322 +-0.345 +-0.368 +-0.39 +-0.413 +-0.435 +-0.457 +-0.479 +-0.5 +-0.521 +-0.542 +-0.562 +-0.583 +-0.602 +-0.622 +-0.641 +-0.659 +-0.678 +-0.695 +-0.713 +-0.73 +-0.746 +-0.763 +-0.778 +-0.793 +-0.808 +-0.822 +-0.836 +-0.849 +-0.862 +-0.874 +-0.886 +-0.897 +-0.907 +-0.918 +-0.927 +-0.936 +-0.944 +-0.952 +-0.959 +-0.966 +-0.972 +-0.977 +-0.982 +-0.987 +-0.99 +-0.993 +-0.996 +-0.998 -0.999 -1.0 -1.0 --1.0 -0.999 +-0.998 -0.997 --0.995 --0.992 --0.989 --0.985 --0.981 --0.976 --0.97 --0.964 --0.957 --0.95 --0.942 --0.933 --0.924 --0.914 --0.904 --0.893 --0.882 --0.87 --0.858 --0.845 --0.831 --0.818 --0.803 --0.788 --0.773 --0.757 --0.741 --0.724 --0.707 --0.69 --0.672 --0.653 --0.634 --0.615 --0.596 --0.576 --0.556 --0.535 --0.514 --0.493 --0.471 --0.45 --0.428 --0.405 --0.383 --0.36 --0.337 --0.314 --0.29 --0.267 --0.243 --0.219 --0.195 --0.171 --0.147 --0.122 --0.098 --0.074 --0.049 --0.025 -0.0 -0.025 -0.049 -0.074 -0.098 -0.122 -0.147 -0.171 -0.195 -0.219 -0.243 -0.267 -0.29 -0.314 -0.337 -0.36 -0.383 -0.405 -0.428 -0.45 -0.471 -0.493 -0.514 -0.535 -0.556 -0.576 -0.596 -0.615 -0.634 -0.653 -0.672 -0.69 -0.707 -0.724 -0.741 -0.757 -0.773 -0.788 -0.803 -0.818 -0.831 -0.845 -0.858 -0.87 -0.882 -0.893 -0.904 -0.914 -0.924 -0.933 -0.942 -0.95 -0.957 -0.964 -0.97 -0.976 -0.981 -0.985 -0.989 -0.992 -0.995 -0.997 +-0.994 +-0.991 +-0.988 +-0.984 +-0.979 +-0.974 +-0.968 +-0.962 +-0.955 +-0.947 +-0.939 +-0.93 +-0.921 +-0.911 +-0.9 +-0.889 +-0.878 +-0.866 +-0.853 +-0.84 +-0.827 +-0.813 +-0.798 +-0.783 +-0.768 +-0.752 +-0.735 +-0.719 +-0.701 +-0.684 +-0.665 +-0.647 +-0.628 +-0.609 +-0.589 +-0.569 +-0.549 +-0.528 +-0.507 +-0.486 +-0.464 +-0.442 +-0.42 +-0.398 +-0.375 +-0.352 +-0.329 +-0.306 +-0.282 +-0.259 +-0.235 +-0.211 +-0.187 +-0.163 +-0.139 +-0.114 +-0.09 +-0.065 +-0.041 +-0.016 +0.008 +0.033 +0.057 +0.082 +0.106 +0.131 +0.155 +0.179 +0.203 +0.227 +0.251 +0.275 +0.298 +0.322 +0.345 +0.368 +0.39 +0.413 +0.435 +0.457 +0.479 +0.5 +0.521 +0.542 +0.562 +0.583 +0.602 +0.622 +0.641 +0.659 +0.678 +0.695 +0.713 +0.73 +0.746 +0.763 +0.778 +0.793 +0.808 +0.822 +0.836 +0.849 +0.862 +0.874 +0.886 +0.897 +0.907 +0.918 +0.927 +0.936 +0.944 +0.952 +0.959 +0.966 +0.972 +0.977 +0.982 +0.987 +0.99 +0.993 +0.996 +0.998 0.999 1.0 1.0 -1.0 0.999 +0.998 0.997 -0.995 -0.992 -0.989 -0.985 -0.981 -0.976 -0.97 -0.964 -0.957 -0.95 -0.942 -0.933 -0.924 -0.914 -0.904 -0.893 -0.882 -0.87 -0.858 -0.845 -0.831 -0.818 -0.803 -0.788 -0.773 -0.757 -0.741 -0.724 -0.707 -0.69 -0.672 -0.653 -0.634 -0.615 -0.596 -0.576 -0.556 -0.535 -0.514 -0.493 -0.471 -0.45 -0.428 -0.405 -0.383 -0.36 -0.337 -0.314 -0.29 -0.267 -0.243 -0.219 -0.195 -0.171 -0.147 -0.122 -0.098 -0.074 -0.049 -0.025 --0.0 --0.025 --0.049 --0.074 --0.098 --0.122 --0.147 --0.171 --0.195 --0.219 --0.243 --0.267 --0.29 --0.314 --0.337 --0.36 --0.383 --0.405 --0.428 --0.45 --0.471 --0.493 --0.514 --0.535 --0.556 --0.576 --0.596 --0.615 --0.634 --0.653 --0.672 --0.69 --0.707 --0.724 --0.741 --0.757 --0.773 --0.788 --0.803 --0.818 --0.831 --0.845 --0.858 --0.87 --0.882 --0.893 --0.904 --0.914 --0.924 --0.933 --0.942 --0.95 --0.957 --0.964 --0.97 --0.976 --0.981 --0.985 --0.989 --0.992 --0.995 --0.997 +0.994 +0.991 +0.988 +0.984 +0.979 +0.974 +0.968 +0.962 +0.955 +0.947 +0.939 +0.93 +0.921 +0.911 +0.9 +0.889 +0.878 +0.866 +0.853 +0.84 +0.827 +0.813 +0.798 +0.783 +0.768 +0.752 +0.735 +0.719 +0.701 +0.684 +0.665 +0.647 +0.628 +0.609 +0.589 +0.569 +0.549 +0.528 +0.507 +0.486 +0.464 +0.442 +0.42 +0.398 +0.375 +0.352 +0.329 +0.306 +0.282 +0.259 +0.235 +0.211 +0.187 +0.163 +0.139 +0.114 +0.09 +0.065 +0.041 +0.016 +-0.008 +-0.033 +-0.057 +-0.082 +-0.106 +-0.131 +-0.155 +-0.179 +-0.203 +-0.227 +-0.251 +-0.275 +-0.298 +-0.322 +-0.345 +-0.368 +-0.39 +-0.413 +-0.435 +-0.457 +-0.479 +-0.5 +-0.521 +-0.542 +-0.562 +-0.583 +-0.602 +-0.622 +-0.641 +-0.659 +-0.678 +-0.695 +-0.713 +-0.73 +-0.746 +-0.763 +-0.778 +-0.793 +-0.808 +-0.822 +-0.836 +-0.849 +-0.862 +-0.874 +-0.886 +-0.897 +-0.907 +-0.918 +-0.927 +-0.936 +-0.944 +-0.952 +-0.959 +-0.966 +-0.972 +-0.977 +-0.982 +-0.987 +-0.99 +-0.993 +-0.996 +-0.998 -0.999 -1.0 -1.0 --1.0 -0.999 +-0.998 -0.997 --0.995 --0.992 --0.989 --0.985 --0.981 --0.976 --0.97 --0.964 --0.957 --0.95 --0.942 --0.933 --0.924 --0.914 --0.904 --0.893 --0.882 --0.87 --0.858 --0.845 --0.831 --0.818 --0.803 --0.788 --0.773 --0.757 --0.741 --0.724 --0.707 --0.69 --0.672 --0.653 --0.634 --0.615 --0.596 --0.576 --0.556 --0.535 --0.514 --0.493 --0.471 --0.45 --0.428 --0.405 --0.383 --0.36 --0.337 --0.314 --0.29 --0.267 --0.243 --0.219 --0.195 --0.171 --0.147 --0.122 --0.098 --0.074 --0.049 --0.025 -0.0 -0.025 -0.049 -0.074 -0.098 -0.122 -0.147 -0.171 -0.195 -0.219 -0.243 -0.267 -0.29 -0.314 -0.337 -0.36 -0.383 -0.405 -0.428 -0.45 -0.471 -0.493 -0.514 -0.535 -0.556 -0.576 -0.596 -0.615 -0.634 -0.653 -0.672 -0.69 -0.707 -0.724 -0.741 -0.757 -0.773 -0.788 -0.803 -0.818 -0.831 -0.845 -0.858 -0.87 -0.882 -0.893 -0.904 -0.914 -0.924 -0.933 -0.942 -0.95 -0.957 -0.964 -0.97 -0.976 -0.981 -0.985 -0.989 -0.992 -0.995 -0.997 +-0.994 +-0.991 +-0.988 +-0.984 +-0.979 +-0.974 +-0.968 +-0.962 +-0.955 +-0.947 +-0.939 +-0.93 +-0.921 +-0.911 +-0.9 +-0.889 +-0.878 +-0.866 +-0.853 +-0.84 +-0.827 +-0.813 +-0.798 +-0.783 +-0.768 +-0.752 +-0.735 +-0.719 +-0.701 +-0.684 +-0.665 +-0.647 +-0.628 +-0.609 +-0.589 +-0.569 +-0.549 +-0.528 +-0.507 +-0.486 +-0.464 +-0.442 +-0.42 +-0.398 +-0.375 +-0.352 +-0.329 +-0.306 +-0.282 +-0.259 +-0.235 +-0.211 +-0.187 +-0.163 +-0.139 +-0.114 +-0.09 +-0.065 +-0.041 +-0.016 +0.008 +0.033 +0.057 +0.082 +0.106 +0.131 +0.155 +0.179 +0.203 +0.227 +0.251 +0.275 +0.298 +0.322 +0.345 +0.368 +0.39 +0.413 +0.435 +0.457 +0.479 +0.5 +0.521 +0.542 +0.562 +0.583 +0.602 +0.622 +0.641 +0.659 +0.678 +0.695 +0.713 +0.73 +0.746 +0.763 +0.778 +0.793 +0.808 +0.822 +0.836 +0.849 +0.862 +0.874 +0.886 +0.897 +0.907 +0.918 +0.927 +0.936 +0.944 +0.952 +0.959 +0.966 +0.972 +0.977 +0.982 +0.987 +0.99 +0.993 +0.996 +0.998 0.999 1.0 1.0 -1.0 0.999 +0.998 0.997 -0.995 -0.992 -0.989 -0.985 -0.981 -0.976 -0.97 -0.964 -0.957 -0.95 -0.942 -0.933 -0.924 -0.914 -0.904 -0.893 -0.882 -0.87 -0.858 -0.845 -0.831 -0.818 -0.803 -0.788 -0.773 -0.757 -0.741 -0.724 -0.707 -0.69 -0.672 -0.653 -0.634 -0.615 -0.596 -0.576 -0.556 -0.535 -0.514 -0.493 -0.471 -0.45 -0.428 -0.405 -0.383 -0.36 -0.337 -0.314 -0.29 -0.267 -0.243 -0.219 -0.195 -0.171 -0.147 -0.122 -0.098 -0.074 -0.049 -0.025 --0.0 --0.025 --0.049 --0.074 --0.098 --0.122 --0.147 --0.171 --0.195 --0.219 --0.243 --0.267 --0.29 --0.314 --0.337 --0.36 --0.383 --0.405 --0.428 --0.45 --0.471 --0.493 --0.514 --0.535 --0.556 --0.576 --0.596 --0.615 --0.634 --0.653 --0.672 --0.69 --0.707 --0.724 --0.741 --0.757 --0.773 --0.788 --0.803 --0.818 --0.831 --0.845 --0.858 --0.87 --0.882 --0.893 --0.904 --0.914 --0.924 --0.933 --0.942 --0.95 --0.957 --0.964 --0.97 --0.976 --0.981 --0.985 --0.989 --0.992 --0.995 --0.997 +0.994 +0.991 +0.988 +0.984 +0.979 +0.974 +0.968 +0.962 +0.955 +0.947 +0.939 +0.93 +0.921 +0.911 +0.9 +0.889 +0.878 +0.866 +0.853 +0.84 +0.827 +0.813 +0.798 +0.783 +0.768 +0.752 +0.735 +0.719 +0.701 +0.684 +0.665 +0.647 +0.628 +0.609 +0.589 +0.569 +0.549 +0.528 +0.507 +0.486 +0.464 +0.442 +0.42 +0.398 +0.375 +0.352 +0.329 +0.306 +0.282 +0.259 +0.235 +0.211 +0.187 +0.163 +0.139 +0.114 +0.09 +0.065 +0.041 +0.016 +-0.008 +-0.033 +-0.057 +-0.082 +-0.106 +-0.131 +-0.155 +-0.179 +-0.203 +-0.227 +-0.251 +-0.275 +-0.298 +-0.322 +-0.345 +-0.368 +-0.39 +-0.413 +-0.435 +-0.457 +-0.479 +-0.5 +-0.521 +-0.542 +-0.562 +-0.583 +-0.602 +-0.622 +-0.641 +-0.659 +-0.678 +-0.695 +-0.713 +-0.73 +-0.746 +-0.763 +-0.778 +-0.793 +-0.808 +-0.822 +-0.836 +-0.849 +-0.862 +-0.874 +-0.886 +-0.897 +-0.907 +-0.918 +-0.927 +-0.936 +-0.944 +-0.952 +-0.959 +-0.966 +-0.972 +-0.977 +-0.982 +-0.987 +-0.99 +-0.993 +-0.996 +-0.998 -0.999 -1.0 -1.0 --1.0 -0.999 +-0.998 -0.997 --0.995 --0.992 --0.989 --0.985 --0.981 --0.976 --0.97 --0.964 --0.957 --0.95 --0.942 --0.933 --0.924 --0.914 --0.904 --0.893 --0.882 --0.87 --0.858 --0.845 --0.831 --0.818 --0.803 --0.788 --0.773 --0.757 --0.741 --0.724 --0.707 --0.69 --0.672 --0.653 --0.634 --0.615 --0.596 --0.576 --0.556 --0.535 --0.514 --0.493 --0.471 --0.45 --0.428 --0.405 --0.383 --0.36 --0.337 --0.314 --0.29 --0.267 --0.243 --0.219 --0.195 --0.171 --0.147 --0.122 --0.098 --0.074 --0.049 --0.025 -0.0 -0.025 -0.049 -0.074 -0.098 -0.122 -0.147 -0.171 -0.195 -0.219 -0.243 -0.267 -0.29 -0.314 -0.337 -0.36 -0.383 -0.405 -0.428 -0.45 -0.471 -0.493 -0.514 -0.535 -0.556 -0.576 -0.596 -0.615 -0.634 -0.653 -0.672 -0.69 -0.707 -0.724 -0.741 -0.757 -0.773 -0.788 -0.803 -0.818 -0.831 -0.845 -0.858 -0.87 -0.882 -0.893 -0.904 -0.914 -0.924 -0.933 -0.942 -0.95 -0.957 -0.964 -0.97 -0.976 -0.981 -0.985 -0.989 -0.992 -0.995 -0.997 +-0.994 +-0.991 +-0.988 +-0.984 +-0.979 +-0.974 +-0.968 +-0.962 +-0.955 +-0.947 +-0.939 +-0.93 +-0.921 +-0.911 +-0.9 +-0.889 +-0.878 +-0.866 +-0.853 +-0.84 +-0.827 +-0.813 +-0.798 +-0.783 +-0.768 +-0.752 +-0.735 +-0.719 +-0.701 +-0.684 +-0.665 +-0.647 +-0.628 +-0.609 +-0.589 +-0.569 +-0.549 +-0.528 +-0.507 +-0.486 +-0.464 +-0.442 +-0.42 +-0.398 +-0.375 +-0.352 +-0.329 +-0.306 +-0.282 +-0.259 +-0.235 +-0.211 +-0.187 +-0.163 +-0.139 +-0.114 +-0.09 +-0.065 +-0.041 +-0.016 +0.008 +0.033 +0.057 +0.082 +0.106 +0.131 +0.155 +0.179 +0.203 +0.227 +0.251 +0.275 +0.298 +0.322 +0.345 +0.368 +0.39 +0.413 +0.435 +0.457 +0.479 +0.5 +0.521 +0.542 +0.562 +0.583 +0.602 +0.622 +0.641 +0.659 +0.678 +0.695 +0.713 +0.73 +0.746 +0.763 +0.778 +0.793 +0.808 +0.822 +0.836 +0.849 +0.862 +0.874 +0.886 +0.897 +0.907 +0.918 +0.927 +0.936 +0.944 +0.952 +0.959 +0.966 +0.972 +0.977 +0.982 +0.987 +0.99 +0.993 +0.996 +0.998 0.999 1.0 1.0 -1.0 0.999 +0.998 0.997 -0.995 -0.992 -0.989 -0.985 -0.981 -0.976 -0.97 -0.964 -0.957 -0.95 -0.942 -0.933 -0.924 -0.914 -0.904 -0.893 -0.882 -0.87 -0.858 -0.845 -0.831 -0.818 -0.803 -0.788 -0.773 -0.757 -0.741 -0.724 -0.707 -0.69 -0.672 -0.653 -0.634 -0.615 -0.596 -0.576 -0.556 -0.535 -0.514 -0.493 -0.471 -0.45 -0.428 -0.405 -0.383 -0.36 -0.337 -0.314 -0.29 -0.267 -0.243 -0.219 -0.195 -0.171 -0.147 -0.122 -0.098 -0.074 -0.049 -0.025 --0.0 --0.025 --0.049 --0.074 --0.098 --0.122 --0.147 --0.171 --0.195 --0.219 --0.243 --0.267 --0.29 --0.314 --0.337 --0.36 --0.383 --0.405 --0.428 --0.45 --0.471 --0.493 --0.514 --0.535 --0.556 --0.576 --0.596 --0.615 --0.634 --0.653 --0.672 --0.69 --0.707 --0.724 --0.741 --0.757 --0.773 --0.788 --0.803 --0.818 --0.831 --0.845 --0.858 --0.87 --0.882 --0.893 --0.904 --0.914 --0.924 --0.933 --0.942 --0.95 --0.957 --0.964 --0.97 --0.976 --0.981 --0.985 --0.989 --0.992 --0.995 --0.997 +0.994 +0.991 +0.988 +0.984 +0.979 +0.974 +0.968 +0.962 +0.955 +0.947 +0.939 +0.93 +0.921 +0.911 +0.9 +0.889 +0.878 +0.866 +0.853 +0.84 +0.827 +0.813 +0.798 +0.783 +0.768 +0.752 +0.735 +0.719 +0.701 +0.684 +0.665 +0.647 +0.628 +0.609 +0.589 +0.569 +0.549 +0.528 +0.507 +0.486 +0.464 +0.442 +0.42 +0.398 +0.375 +0.352 +0.329 +0.306 +0.282 +0.259 +0.235 +0.211 +0.187 +0.163 +0.139 +0.114 +0.09 +0.065 +0.041 +0.016 +-0.008 +-0.033 +-0.057 +-0.082 +-0.106 +-0.131 +-0.155 +-0.179 +-0.203 +-0.227 +-0.251 +-0.275 +-0.298 +-0.322 +-0.345 +-0.368 +-0.39 +-0.413 +-0.435 +-0.457 +-0.479 +-0.5 +-0.521 +-0.542 +-0.562 +-0.583 +-0.602 +-0.622 +-0.641 +-0.659 +-0.678 +-0.695 +-0.713 +-0.73 +-0.746 +-0.763 +-0.778 +-0.793 +-0.808 +-0.822 +-0.836 +-0.849 +-0.862 +-0.874 +-0.886 +-0.897 +-0.907 +-0.918 +-0.927 +-0.936 +-0.944 +-0.952 +-0.959 +-0.966 +-0.972 +-0.977 +-0.982 +-0.987 +-0.99 +-0.993 +-0.996 +-0.998 -0.999 -1.0 -1.0 --1.0 -0.999 +-0.998 -0.997 --0.995 --0.992 --0.989 --0.985 --0.981 --0.976 --0.97 --0.964 --0.957 --0.95 --0.942 --0.933 --0.924 --0.914 --0.904 --0.893 --0.882 --0.87 --0.858 --0.845 --0.831 --0.818 --0.803 --0.788 --0.773 --0.757 --0.741 --0.724 --0.707 --0.69 --0.672 --0.653 --0.634 --0.615 --0.596 --0.576 --0.556 --0.535 --0.514 --0.493 --0.471 --0.45 --0.428 --0.405 --0.383 --0.36 --0.337 --0.314 --0.29 --0.267 --0.243 --0.219 --0.195 --0.171 --0.147 --0.122 --0.098 --0.074 --0.049 --0.025 -0.0 -0.025 -0.049 -0.074 -0.098 -0.122 -0.147 -0.171 -0.195 -0.219 -0.243 -0.267 -0.29 -0.314 -0.337 -0.36 -0.383 -0.405 -0.428 -0.45 -0.471 -0.493 -0.514 -0.535 -0.556 -0.576 -0.596 -0.615 -0.634 -0.653 -0.672 -0.69 -0.707 -0.724 -0.741 -0.757 -0.773 -0.788 -0.803 -0.818 -0.831 -0.845 -0.858 -0.87 -0.882 -0.893 -0.904 -0.914 -0.924 -0.933 -0.942 -0.95 -0.957 -0.964 -0.97 -0.976 -0.981 -0.985 -0.989 -0.992 -0.995 -0.997 +-0.994 +-0.991 +-0.988 +-0.984 +-0.979 +-0.974 +-0.968 +-0.962 +-0.955 +-0.947 +-0.939 +-0.93 +-0.921 +-0.911 +-0.9 +-0.889 +-0.878 +-0.866 +-0.853 +-0.84 +-0.827 +-0.813 +-0.798 +-0.783 +-0.768 +-0.752 +-0.735 +-0.719 +-0.701 +-0.684 +-0.665 +-0.647 +-0.628 +-0.609 +-0.589 +-0.569 +-0.549 +-0.528 +-0.507 +-0.486 +-0.464 +-0.442 +-0.42 +-0.398 +-0.375 +-0.352 +-0.329 +-0.306 +-0.282 +-0.259 +-0.235 +-0.211 +-0.187 +-0.163 +-0.139 +-0.114 +-0.09 +-0.065 +-0.041 +-0.016 +0.008 +0.033 +0.057 +0.082 +0.106 +0.131 +0.155 +0.179 +0.203 +0.227 +0.251 +0.275 +0.298 +0.322 +0.345 +0.368 +0.39 +0.413 +0.435 +0.457 +0.479 +0.5 +0.521 +0.542 +0.562 +0.583 +0.602 +0.622 +0.641 +0.659 +0.678 +0.695 +0.713 +0.73 +0.746 +0.763 +0.778 +0.793 +0.808 +0.822 +0.836 +0.849 +0.862 +0.874 +0.886 +0.897 +0.907 +0.918 +0.927 +0.936 +0.944 +0.952 +0.959 +0.966 +0.972 +0.977 +0.982 +0.987 +0.99 +0.993 +0.996 +0.998 0.999 1.0 1.0 -1.0 0.999 +0.998 0.997 -0.995 -0.992 -0.989 -0.985 -0.981 -0.976 -0.97 -0.964 -0.957 -0.95 -0.942 -0.933 -0.924 -0.914 -0.904 -0.893 -0.882 -0.87 -0.858 -0.845 -0.831 -0.818 -0.803 -0.788 -0.773 -0.757 -0.741 -0.724 -0.707 -0.69 -0.672 -0.653 -0.634 -0.615 -0.596 -0.576 -0.556 -0.535 -0.514 -0.493 -0.471 -0.45 -0.428 -0.405 -0.383 -0.36 -0.337 -0.314 -0.29 -0.267 -0.243 -0.219 -0.195 -0.171 -0.147 -0.122 -0.098 -0.074 -0.049 -0.025 --0.0 --0.025 --0.049 --0.074 --0.098 --0.122 --0.147 --0.171 --0.195 --0.219 --0.243 --0.267 --0.29 --0.314 --0.337 --0.36 --0.383 --0.405 --0.428 --0.45 --0.471 --0.493 --0.514 --0.535 --0.556 --0.576 --0.596 --0.615 --0.634 --0.653 --0.672 --0.69 --0.707 --0.724 --0.741 --0.757 --0.773 --0.788 --0.803 --0.818 --0.831 --0.845 --0.858 --0.87 --0.882 --0.893 --0.904 --0.914 --0.924 --0.933 --0.942 --0.95 --0.957 --0.964 --0.97 --0.976 --0.981 --0.985 --0.989 --0.992 --0.995 --0.997 +0.994 +0.991 +0.988 +0.984 +0.979 +0.974 +0.968 +0.962 +0.955 +0.947 +0.939 +0.93 +0.921 +0.911 +0.9 +0.889 +0.878 +0.866 +0.853 +0.84 +0.827 +0.813 +0.798 +0.783 +0.768 +0.752 +0.735 +0.719 +0.701 +0.684 +0.665 +0.647 +0.628 +0.609 +0.589 +0.569 +0.549 +0.528 +0.507 +0.486 +0.464 +0.442 +0.42 +0.398 +0.375 +0.352 +0.329 +0.306 +0.282 +0.259 +0.235 +0.211 +0.187 +0.163 +0.139 +0.114 +0.09 +0.065 +0.041 +0.016 +-0.008 +-0.033 +-0.057 +-0.082 +-0.106 +-0.131 +-0.155 +-0.179 +-0.203 +-0.227 +-0.251 +-0.275 +-0.298 +-0.322 +-0.345 +-0.368 +-0.39 +-0.413 +-0.435 +-0.457 +-0.479 +-0.5 +-0.521 +-0.542 +-0.562 +-0.583 +-0.602 +-0.622 +-0.641 +-0.659 +-0.678 +-0.695 +-0.713 +-0.73 +-0.746 +-0.763 +-0.778 +-0.793 +-0.808 +-0.822 +-0.836 +-0.849 +-0.862 +-0.874 +-0.886 +-0.897 +-0.907 +-0.918 +-0.927 +-0.936 +-0.944 +-0.952 +-0.959 +-0.966 +-0.972 +-0.977 +-0.982 +-0.987 +-0.99 +-0.993 +-0.996 +-0.998 -0.999 -1.0 -1.0 --1.0 -0.999 +-0.998 -0.997 --0.995 --0.992 --0.989 --0.985 --0.981 --0.976 --0.97 --0.964 --0.957 --0.95 --0.942 --0.933 --0.924 --0.914 --0.904 --0.893 --0.882 --0.87 --0.858 --0.845 --0.831 --0.818 --0.803 --0.788 --0.773 --0.757 --0.741 --0.724 --0.707 --0.69 --0.672 --0.653 --0.634 --0.615 --0.596 --0.576 --0.556 --0.535 --0.514 --0.493 --0.471 --0.45 --0.428 --0.405 --0.383 --0.36 --0.337 --0.314 --0.29 --0.267 --0.243 --0.219 --0.195 --0.171 --0.147 --0.122 --0.098 --0.074 --0.049 --0.025 -0.0 -0.025 -0.049 -0.074 -0.098 -0.122 -0.147 -0.171 -0.195 -0.219 -0.243 -0.267 -0.29 -0.314 -0.337 -0.36 -0.383 -0.405 -0.428 -0.45 -0.471 -0.493 -0.514 -0.535 -0.556 -0.576 -0.596 -0.615 -0.634 -0.653 -0.672 -0.69 -0.707 -0.724 -0.741 -0.757 -0.773 -0.788 -0.803 -0.818 -0.831 -0.845 -0.858 -0.87 -0.882 -0.893 -0.904 -0.914 -0.924 -0.933 -0.942 -0.95 -0.957 -0.964 -0.97 -0.976 -0.981 -0.985 -0.989 -0.992 -0.995 -0.997 +-0.994 +-0.991 +-0.988 +-0.984 +-0.979 +-0.974 +-0.968 +-0.962 +-0.955 +-0.947 +-0.939 +-0.93 +-0.921 +-0.911 +-0.9 +-0.889 +-0.878 +-0.866 +-0.853 +-0.84 +-0.827 +-0.813 +-0.798 +-0.783 +-0.768 +-0.752 +-0.735 +-0.719 +-0.701 +-0.684 +-0.665 +-0.647 +-0.628 +-0.609 +-0.589 +-0.569 +-0.549 +-0.528 +-0.507 +-0.486 +-0.464 +-0.442 +-0.42 +-0.398 +-0.375 +-0.352 +-0.329 +-0.306 +-0.282 +-0.259 +-0.235 +-0.211 +-0.187 +-0.163 +-0.139 +-0.114 +-0.09 +-0.065 +-0.041 +-0.016 +0.008 +0.033 +0.057 +0.082 +0.106 +0.131 +0.155 +0.179 +0.203 +0.227 +0.251 +0.275 +0.298 +0.322 +0.345 +0.368 +0.39 +0.413 +0.435 +0.457 +0.479 +0.5 +0.521 +0.542 +0.562 +0.583 +0.602 +0.622 +0.641 +0.659 +0.678 +0.695 +0.713 +0.73 +0.746 +0.763 +0.778 +0.793 +0.808 +0.822 +0.836 +0.849 +0.862 +0.874 +0.886 +0.897 +0.907 +0.918 +0.927 +0.936 +0.944 +0.952 +0.959 +0.966 +0.972 +0.977 +0.982 +0.987 +0.99 +0.993 +0.996 +0.998 0.999 1.0 1.0 -1.0 0.999 +0.998 0.997 -0.995 -0.992 -0.989 -0.985 -0.981 -0.976 -0.97 -0.964 -0.957 -0.95 -0.942 -0.933 -0.924 -0.914 -0.904 -0.893 -0.882 -0.87 -0.858 -0.845 -0.831 -0.818 -0.803 -0.788 -0.773 -0.757 -0.741 -0.724 -0.707 -0.69 -0.672 -0.653 -0.634 -0.615 -0.596 -0.576 -0.556 -0.535 -0.514 -0.493 -0.471 -0.45 -0.428 -0.405 -0.383 -0.36 -0.337 -0.314 -0.29 -0.267 -0.243 -0.219 -0.195 -0.171 -0.147 -0.122 -0.098 -0.074 -0.049 -0.025 --0.0 --0.025 --0.049 --0.074 --0.098 --0.122 --0.147 --0.171 --0.195 --0.219 --0.243 --0.267 --0.29 --0.314 --0.337 --0.36 --0.383 --0.405 --0.428 --0.45 --0.471 --0.493 --0.514 --0.535 --0.556 --0.576 --0.596 --0.615 --0.634 --0.653 --0.672 --0.69 --0.707 --0.724 --0.741 --0.757 --0.773 --0.788 --0.803 --0.818 --0.831 --0.845 --0.858 --0.87 --0.882 --0.893 --0.904 --0.914 --0.924 --0.933 --0.942 --0.95 --0.957 --0.964 --0.97 --0.976 --0.981 --0.985 --0.989 --0.992 --0.995 --0.997 +0.994 +0.991 +0.988 +0.984 +0.979 +0.974 +0.968 +0.962 +0.955 +0.947 +0.939 +0.93 +0.921 +0.911 +0.9 +0.889 +0.878 +0.866 +0.853 +0.84 +0.827 +0.813 +0.798 +0.783 +0.768 +0.752 +0.735 +0.719 +0.701 +0.684 +0.665 +0.647 +0.628 +0.609 +0.589 +0.569 +0.549 +0.528 +0.507 +0.486 +0.464 +0.442 +0.42 +0.398 +0.375 +0.352 +0.329 +0.306 +0.282 +0.259 +0.235 +0.211 +0.187 +0.163 +0.139 +0.114 +0.09 +0.065 +0.041 +0.016 +-0.008 +-0.033 +-0.057 +-0.082 +-0.106 +-0.131 +-0.155 +-0.179 +-0.203 +-0.227 +-0.251 +-0.275 +-0.298 +-0.322 +-0.345 +-0.368 +-0.39 +-0.413 +-0.435 +-0.457 +-0.479 +-0.5 +-0.521 +-0.542 +-0.562 +-0.583 +-0.602 +-0.622 +-0.641 +-0.659 +-0.678 +-0.695 +-0.713 +-0.73 +-0.746 +-0.763 +-0.778 +-0.793 +-0.808 +-0.822 +-0.836 +-0.849 +-0.862 +-0.874 +-0.886 +-0.897 +-0.907 +-0.918 +-0.927 +-0.936 +-0.944 +-0.952 +-0.959 +-0.966 +-0.972 +-0.977 +-0.982 +-0.987 +-0.99 +-0.993 +-0.996 +-0.998 -0.999 -1.0 -1.0 --1.0 -0.999 +-0.998 -0.997 --0.995 --0.992 --0.989 --0.985 --0.981 --0.976 --0.97 --0.964 --0.957 --0.95 --0.942 --0.933 --0.924 --0.914 --0.904 --0.893 --0.882 --0.87 --0.858 --0.845 --0.831 --0.818 --0.803 --0.788 --0.773 --0.757 --0.741 --0.724 --0.707 --0.69 --0.672 --0.653 --0.634 --0.615 --0.596 --0.576 --0.556 --0.535 --0.514 --0.493 --0.471 --0.45 --0.428 --0.405 --0.383 --0.36 --0.337 --0.314 --0.29 --0.267 --0.243 --0.219 --0.195 --0.171 --0.147 --0.122 --0.098 --0.074 --0.049 --0.025 -0.0 -0.025 -0.049 -0.074 -0.098 -0.122 -0.147 -0.171 -0.195 -0.219 -0.243 -0.267 -0.29 -0.314 -0.337 -0.36 -0.383 -0.405 -0.428 -0.45 -0.471 -0.493 -0.514 -0.535 -0.556 -0.576 -0.596 -0.615 -0.634 -0.653 -0.672 -0.69 -0.707 -0.724 -0.741 -0.757 -0.773 -0.788 -0.803 -0.818 -0.831 -0.845 -0.858 -0.87 -0.882 -0.893 -0.904 -0.914 -0.924 -0.933 -0.942 -0.95 -0.957 -0.964 -0.97 -0.976 -0.981 -0.985 -0.989 -0.992 -0.995 -0.997 +-0.994 +-0.991 +-0.988 +-0.984 +-0.979 +-0.974 +-0.968 +-0.962 +-0.955 +-0.947 +-0.939 +-0.93 +-0.921 +-0.911 +-0.9 +-0.889 +-0.878 +-0.866 +-0.853 +-0.84 +-0.827 +-0.813 +-0.798 +-0.783 +-0.768 +-0.752 +-0.735 +-0.719 +-0.701 +-0.684 +-0.665 +-0.647 +-0.628 +-0.609 +-0.589 +-0.569 +-0.549 +-0.528 +-0.507 +-0.486 +-0.464 +-0.442 +-0.42 +-0.398 +-0.375 +-0.352 +-0.329 +-0.306 +-0.282 +-0.259 +-0.235 +-0.211 +-0.187 +-0.163 +-0.139 +-0.114 +-0.09 +-0.065 +-0.041 +-0.016 +0.008 +0.033 +0.057 +0.082 +0.106 +0.131 +0.155 +0.179 +0.203 +0.227 +0.251 +0.275 +0.298 +0.322 +0.345 +0.368 +0.39 +0.413 +0.435 +0.457 +0.479 +0.5 +0.521 +0.542 +0.562 +0.583 +0.602 +0.622 +0.641 +0.659 +0.678 +0.695 +0.713 +0.73 +0.746 +0.763 +0.778 +0.793 +0.808 +0.822 +0.836 +0.849 +0.862 +0.874 +0.886 +0.897 +0.907 +0.918 +0.927 +0.936 +0.944 +0.952 +0.959 +0.966 +0.972 +0.977 +0.982 +0.987 +0.99 +0.993 +0.996 +0.998 0.999 1.0 1.0 -1.0 0.999 +0.998 0.997 -0.995 -0.992 -0.989 -0.985 -0.981 -0.976 -0.97 -0.964 -0.957 -0.95 -0.942 -0.933 -0.924 -0.914 -0.904 -0.893 -0.882 -0.87 -0.858 -0.845 -0.831 -0.818 -0.803 -0.788 -0.773 -0.757 -0.741 -0.724 -0.707 -0.69 -0.672 -0.653 -0.634 -0.615 -0.596 -0.576 -0.556 -0.535 -0.514 -0.493 -0.471 -0.45 -0.428 -0.405 -0.383 -0.36 -0.337 -0.314 -0.29 -0.267 -0.243 -0.219 -0.195 -0.171 -0.147 -0.122 -0.098 -0.074 -0.049 -0.025 --0.0 --0.025 --0.049 --0.074 --0.098 --0.122 --0.147 --0.171 --0.195 --0.219 --0.243 --0.267 --0.29 --0.314 --0.337 --0.36 --0.383 --0.405 --0.428 --0.45 --0.471 --0.493 --0.514 --0.535 --0.556 --0.576 --0.596 --0.615 --0.634 --0.653 --0.672 --0.69 --0.707 --0.724 --0.741 --0.757 --0.773 --0.788 --0.803 --0.818 --0.831 --0.845 --0.858 --0.87 --0.882 --0.893 --0.904 --0.914 --0.924 --0.933 --0.942 --0.95 --0.957 --0.964 --0.97 --0.976 --0.981 --0.985 --0.989 --0.992 --0.995 --0.997 +0.994 +0.991 +0.988 +0.984 +0.979 +0.974 +0.968 +0.962 +0.955 +0.947 +0.939 +0.93 +0.921 +0.911 +0.9 +0.889 +0.878 +0.866 +0.853 +0.84 +0.827 +0.813 +0.798 +0.783 +0.768 +0.752 +0.735 +0.719 +0.701 +0.684 +0.665 +0.647 +0.628 +0.609 +0.589 +0.569 +0.549 +0.528 +0.507 +0.486 +0.464 +0.442 +0.42 +0.398 +0.375 +0.352 +0.329 +0.306 +0.282 +0.259 +0.235 +0.211 +0.187 +0.163 +0.139 +0.114 +0.09 +0.065 +0.041 +0.016 +-0.008 +-0.033 +-0.057 +-0.082 +-0.106 +-0.131 +-0.155 +-0.179 +-0.203 +-0.227 +-0.251 +-0.275 +-0.298 +-0.322 +-0.345 +-0.368 +-0.39 +-0.413 +-0.435 +-0.457 +-0.479 +-0.5 +-0.521 +-0.542 +-0.562 +-0.583 +-0.602 +-0.622 +-0.641 +-0.659 +-0.678 +-0.695 +-0.713 +-0.73 +-0.746 +-0.763 +-0.778 +-0.793 +-0.808 +-0.822 +-0.836 +-0.849 +-0.862 +-0.874 +-0.886 +-0.897 +-0.907 +-0.918 +-0.927 +-0.936 +-0.944 +-0.952 +-0.959 +-0.966 +-0.972 +-0.977 +-0.982 +-0.987 +-0.99 +-0.993 +-0.996 +-0.998 -0.999 -1.0 -1.0 --1.0 -0.999 +-0.998 -0.997 --0.995 --0.992 --0.989 --0.985 --0.981 --0.976 --0.97 --0.964 --0.957 --0.95 --0.942 --0.933 --0.924 --0.914 --0.904 --0.893 --0.882 --0.87 --0.858 --0.845 --0.831 --0.818 --0.803 --0.788 --0.773 --0.757 --0.741 --0.724 --0.707 --0.69 --0.672 --0.653 --0.634 --0.615 --0.596 --0.576 --0.556 --0.535 --0.514 --0.493 --0.471 --0.45 --0.428 --0.405 --0.383 --0.36 --0.337 --0.314 --0.29 --0.267 --0.243 --0.219 --0.195 --0.169 --0.141 --0.116 --0.098 --0.091 --0.092 --0.096 +-0.994 +-0.991 +-0.988 +-0.984 +-0.979 +-0.974 +-0.968 +-0.962 +-0.955 +-0.947 +-0.939 +-0.93 +-0.921 +-0.911 +-0.9 +-0.889 +-0.878 +-0.866 +-0.853 +-0.84 +-0.827 +-0.813 +-0.798 +-0.783 +-0.768 +-0.752 +-0.735 +-0.719 +-0.701 +-0.684 +-0.665 +-0.647 +-0.628 +-0.609 +-0.589 +-0.569 +-0.549 +-0.528 +-0.507 +-0.486 +-0.464 +-0.442 +-0.42 +-0.398 +-0.375 +-0.352 diff --git a/src/oversample/snapshots/valib__oversample__tests__oversampled_dsp_block.snap b/src/oversample/snapshots/valib__oversample__tests__oversampled_dsp_block.snap index 289453a..0789ec1 100644 --- a/src/oversample/snapshots/valib__oversample__tests__oversampled_dsp_block.snap +++ b/src/oversample/snapshots/valib__oversample__tests__oversampled_dsp_block.snap @@ -2,67 +2,67 @@ source: src/oversample/mod.rs expression: "&output as &[_]" --- +-0.0 +-0.0 +-0.015 +-0.235 +-0.845 +-1.124 -1.0 +-0.988 +-1.003 -1.0 -1.0 -1.0 -1.0 -1.0 --1.0 --1.0 --1.0 --1.0 --1.0 --1.0 --1.0 -1.0 -1.0 -1.0 -1.0 +-0.999 +-0.929 +-0.271 +0.954 +1.204 +0.967 +0.99 +1.005 +0.999 1.0 1.0 1.0 1.0 -1.0 -1.0 -1.0 -1.0 -1.0 --1.0 --1.0 --1.0 --1.0 --1.0 --1.0 +0.989 +0.722 +-0.371 +-1.234 +-1.057 +-0.963 +-1.005 +-1.001 +-0.999 -1.0 -1.0 -1.0 --1.0 --1.0 --1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 +-0.999 +-0.929 +-0.271 +0.954 +1.204 +0.967 +0.99 +1.005 +0.999 1.0 1.0 1.0 1.0 -1.0 --1.0 --1.0 --1.0 --1.0 --1.0 +0.989 +0.722 +-0.371 +-1.234 +-1.057 +-0.963 +-1.005 +-1.001 +-0.999 -1.0 -1.0 -1.0 --1.0 --1.0 --1.0 --1.0 -1.0 diff --git a/src/oversample/snapshots/valib__oversample__tests__post os.snap b/src/oversample/snapshots/valib__oversample__tests__post os.snap index 00f2b5f..e67db05 100644 --- a/src/oversample/snapshots/valib__oversample__tests__post os.snap +++ b/src/oversample/snapshots/valib__oversample__tests__post os.snap @@ -3,514 +3,514 @@ source: src/oversample/mod.rs expression: "&out as &[_]" --- 0.0 -0.098 -0.195 -0.29 -0.383 -0.471 -0.556 -0.634 -0.707 -0.773 -0.831 -0.882 -0.924 -0.957 -0.981 -0.995 +0.0 +0.0 +0.0 +0.0 +0.0 +0.003 +0.026 +0.096 +0.207 +0.31 +0.398 +0.485 +0.569 +0.647 +0.719 +0.783 +0.841 +0.89 +0.93 +0.962 +0.984 +0.997 1.0 -0.995 -0.981 -0.957 -0.924 -0.882 -0.831 -0.773 -0.707 -0.634 -0.556 -0.471 -0.383 -0.29 -0.195 -0.098 --0.0 --0.098 --0.195 --0.29 --0.383 --0.471 --0.556 --0.634 --0.707 --0.773 --0.831 --0.882 --0.924 --0.957 --0.981 --0.995 +0.993 +0.977 +0.952 +0.917 +0.874 +0.822 +0.762 +0.695 +0.622 +0.542 +0.457 +0.367 +0.274 +0.179 +0.082 +-0.017 +-0.114 +-0.211 +-0.306 +-0.398 +-0.486 +-0.569 +-0.647 +-0.719 +-0.783 +-0.841 +-0.89 +-0.93 +-0.962 +-0.984 +-0.997 -1.0 --0.995 --0.981 --0.957 --0.924 --0.882 --0.831 --0.773 --0.707 --0.634 --0.556 --0.471 --0.383 --0.29 --0.195 --0.098 -0.0 -0.098 -0.195 -0.29 -0.383 -0.471 -0.556 -0.634 -0.707 -0.773 -0.831 -0.882 -0.924 -0.957 -0.981 -0.995 +-0.993 +-0.977 +-0.952 +-0.917 +-0.874 +-0.822 +-0.762 +-0.695 +-0.622 +-0.542 +-0.457 +-0.367 +-0.274 +-0.179 +-0.082 +0.017 +0.114 +0.211 +0.306 +0.398 +0.486 +0.569 +0.647 +0.719 +0.783 +0.841 +0.89 +0.93 +0.962 +0.984 +0.997 1.0 -0.995 -0.981 -0.957 -0.924 -0.882 -0.831 -0.773 -0.707 -0.634 -0.556 -0.471 -0.383 -0.29 -0.195 -0.098 --0.0 --0.098 --0.195 --0.29 --0.383 --0.471 --0.556 --0.634 --0.707 --0.773 --0.831 --0.882 --0.924 --0.957 --0.981 --0.995 +0.993 +0.977 +0.952 +0.917 +0.874 +0.822 +0.762 +0.695 +0.622 +0.542 +0.457 +0.367 +0.274 +0.179 +0.082 +-0.017 +-0.114 +-0.211 +-0.306 +-0.398 +-0.486 +-0.569 +-0.647 +-0.719 +-0.783 +-0.841 +-0.89 +-0.93 +-0.962 +-0.984 +-0.997 -1.0 --0.995 --0.981 --0.957 --0.924 --0.882 --0.831 --0.773 --0.707 --0.634 --0.556 --0.471 --0.383 --0.29 --0.195 --0.098 -0.0 -0.098 -0.195 -0.29 -0.383 -0.471 -0.556 -0.634 -0.707 -0.773 -0.831 -0.882 -0.924 -0.957 -0.981 -0.995 +-0.993 +-0.977 +-0.952 +-0.917 +-0.874 +-0.822 +-0.762 +-0.695 +-0.622 +-0.542 +-0.457 +-0.367 +-0.274 +-0.179 +-0.082 +0.017 +0.114 +0.211 +0.306 +0.398 +0.486 +0.569 +0.647 +0.719 +0.783 +0.841 +0.89 +0.93 +0.962 +0.984 +0.997 1.0 -0.995 -0.981 -0.957 -0.924 -0.882 -0.831 -0.773 -0.707 -0.634 -0.556 -0.471 -0.383 -0.29 -0.195 -0.098 --0.0 --0.098 --0.195 --0.29 --0.383 --0.471 --0.556 --0.634 --0.707 --0.773 --0.831 --0.882 --0.924 --0.957 --0.981 --0.995 +0.993 +0.977 +0.952 +0.917 +0.874 +0.822 +0.762 +0.695 +0.622 +0.542 +0.457 +0.367 +0.274 +0.179 +0.082 +-0.017 +-0.114 +-0.211 +-0.306 +-0.398 +-0.486 +-0.569 +-0.647 +-0.719 +-0.783 +-0.841 +-0.89 +-0.93 +-0.962 +-0.984 +-0.997 -1.0 --0.995 --0.981 --0.957 --0.924 --0.882 --0.831 --0.773 --0.707 --0.634 --0.556 --0.471 --0.383 --0.29 --0.195 --0.098 -0.0 -0.098 -0.195 -0.29 -0.383 -0.471 -0.556 -0.634 -0.707 -0.773 -0.831 -0.882 -0.924 -0.957 -0.981 -0.995 +-0.993 +-0.977 +-0.952 +-0.917 +-0.874 +-0.822 +-0.762 +-0.695 +-0.622 +-0.542 +-0.457 +-0.367 +-0.274 +-0.179 +-0.082 +0.017 +0.114 +0.211 +0.306 +0.398 +0.486 +0.569 +0.647 +0.719 +0.783 +0.841 +0.89 +0.93 +0.962 +0.984 +0.997 1.0 -0.995 -0.981 -0.957 -0.924 -0.882 -0.831 -0.773 -0.707 -0.634 -0.556 -0.471 -0.383 -0.29 -0.195 -0.098 --0.0 --0.098 --0.195 --0.29 --0.383 --0.471 --0.556 --0.634 --0.707 --0.773 --0.831 --0.882 --0.924 --0.957 --0.981 --0.995 +0.993 +0.977 +0.952 +0.917 +0.874 +0.822 +0.762 +0.695 +0.622 +0.542 +0.457 +0.367 +0.274 +0.179 +0.082 +-0.017 +-0.114 +-0.211 +-0.306 +-0.398 +-0.486 +-0.569 +-0.647 +-0.719 +-0.783 +-0.841 +-0.89 +-0.93 +-0.962 +-0.984 +-0.997 -1.0 --0.995 --0.981 --0.957 --0.924 --0.882 --0.831 --0.773 --0.707 --0.634 --0.556 --0.471 --0.383 --0.29 --0.195 --0.098 -0.0 -0.098 -0.195 -0.29 -0.383 -0.471 -0.556 -0.634 -0.707 -0.773 -0.831 -0.882 -0.924 -0.957 -0.981 -0.995 +-0.993 +-0.977 +-0.952 +-0.917 +-0.874 +-0.822 +-0.762 +-0.695 +-0.622 +-0.542 +-0.457 +-0.367 +-0.274 +-0.179 +-0.082 +0.017 +0.114 +0.211 +0.306 +0.398 +0.486 +0.569 +0.647 +0.719 +0.783 +0.841 +0.89 +0.93 +0.962 +0.984 +0.997 1.0 -0.995 -0.981 -0.957 -0.924 -0.882 -0.831 -0.773 -0.707 -0.634 -0.556 -0.471 -0.383 -0.29 -0.195 -0.098 --0.0 --0.098 --0.195 --0.29 --0.383 --0.471 --0.556 --0.634 --0.707 --0.773 --0.831 --0.882 --0.924 --0.957 --0.981 --0.995 +0.993 +0.977 +0.952 +0.917 +0.874 +0.822 +0.762 +0.695 +0.622 +0.542 +0.457 +0.367 +0.274 +0.179 +0.082 +-0.017 +-0.114 +-0.211 +-0.306 +-0.398 +-0.486 +-0.569 +-0.647 +-0.719 +-0.783 +-0.841 +-0.89 +-0.93 +-0.962 +-0.984 +-0.997 -1.0 --0.995 --0.981 --0.957 --0.924 --0.882 --0.831 --0.773 --0.707 --0.634 --0.556 --0.471 --0.383 --0.29 --0.195 --0.098 -0.0 -0.098 -0.195 -0.29 -0.383 -0.471 -0.556 -0.634 -0.707 -0.773 -0.831 -0.882 -0.924 -0.957 -0.981 -0.995 +-0.993 +-0.977 +-0.952 +-0.917 +-0.874 +-0.822 +-0.762 +-0.695 +-0.622 +-0.542 +-0.457 +-0.367 +-0.274 +-0.179 +-0.082 +0.017 +0.114 +0.211 +0.306 +0.398 +0.486 +0.569 +0.647 +0.719 +0.783 +0.841 +0.89 +0.93 +0.962 +0.984 +0.997 1.0 -0.995 -0.981 -0.957 -0.924 -0.882 -0.831 -0.773 -0.707 -0.634 -0.556 -0.471 -0.383 -0.29 -0.195 -0.098 --0.0 --0.098 --0.195 --0.29 --0.383 --0.471 --0.556 --0.634 --0.707 --0.773 --0.831 --0.882 --0.924 --0.957 --0.981 --0.995 +0.993 +0.977 +0.952 +0.917 +0.874 +0.822 +0.762 +0.695 +0.622 +0.542 +0.457 +0.367 +0.274 +0.179 +0.082 +-0.017 +-0.114 +-0.211 +-0.306 +-0.398 +-0.486 +-0.569 +-0.647 +-0.719 +-0.783 +-0.841 +-0.89 +-0.93 +-0.962 +-0.984 +-0.997 -1.0 --0.995 --0.981 --0.957 --0.924 --0.882 --0.831 --0.773 --0.707 --0.634 --0.556 --0.471 --0.383 --0.29 --0.195 --0.098 -0.0 -0.098 -0.195 -0.29 -0.383 -0.471 -0.556 -0.634 -0.707 -0.773 -0.831 -0.882 -0.924 -0.957 -0.981 -0.995 +-0.993 +-0.977 +-0.952 +-0.917 +-0.874 +-0.822 +-0.762 +-0.695 +-0.622 +-0.542 +-0.457 +-0.367 +-0.274 +-0.179 +-0.082 +0.017 +0.114 +0.211 +0.306 +0.398 +0.486 +0.569 +0.647 +0.719 +0.783 +0.841 +0.89 +0.93 +0.962 +0.984 +0.997 1.0 -0.995 -0.981 -0.957 -0.924 -0.882 -0.831 -0.773 -0.707 -0.634 -0.556 -0.471 -0.383 -0.29 -0.195 -0.098 --0.0 --0.098 --0.195 --0.29 --0.383 --0.471 --0.556 --0.634 --0.707 --0.773 --0.831 --0.882 --0.924 --0.957 --0.981 --0.995 +0.993 +0.977 +0.952 +0.917 +0.874 +0.822 +0.762 +0.695 +0.622 +0.542 +0.457 +0.367 +0.274 +0.179 +0.082 +-0.017 +-0.114 +-0.211 +-0.306 +-0.398 +-0.486 +-0.569 +-0.647 +-0.719 +-0.783 +-0.841 +-0.89 +-0.93 +-0.962 +-0.984 +-0.997 -1.0 --0.995 --0.981 --0.957 --0.924 --0.882 --0.831 --0.773 --0.707 --0.634 --0.556 --0.471 --0.383 --0.29 --0.195 --0.098 -0.0 -0.098 -0.195 -0.29 -0.383 -0.471 -0.556 -0.634 -0.707 -0.773 -0.831 -0.882 -0.924 -0.957 -0.981 -0.995 +-0.993 +-0.977 +-0.952 +-0.917 +-0.874 +-0.822 +-0.762 +-0.695 +-0.622 +-0.542 +-0.457 +-0.367 +-0.274 +-0.179 +-0.082 +0.017 +0.114 +0.211 +0.306 +0.398 +0.486 +0.569 +0.647 +0.719 +0.783 +0.841 +0.89 +0.93 +0.962 +0.984 +0.997 1.0 -0.995 -0.981 -0.957 -0.924 -0.882 -0.831 -0.773 -0.707 -0.634 -0.556 -0.471 -0.383 -0.29 -0.195 -0.098 --0.0 --0.098 --0.195 --0.29 --0.383 --0.471 --0.556 --0.634 --0.707 --0.773 --0.831 --0.882 --0.924 --0.957 --0.981 --0.995 +0.993 +0.977 +0.952 +0.917 +0.874 +0.822 +0.762 +0.695 +0.622 +0.542 +0.457 +0.367 +0.274 +0.179 +0.082 +-0.017 +-0.114 +-0.211 +-0.306 +-0.398 +-0.486 +-0.569 +-0.647 +-0.719 +-0.783 +-0.841 +-0.89 +-0.93 +-0.962 +-0.984 +-0.997 -1.0 --0.995 --0.981 --0.957 --0.924 --0.882 --0.831 --0.773 --0.707 --0.634 --0.556 --0.471 --0.383 --0.29 --0.195 --0.098 +-0.993 +-0.977 +-0.952 +-0.917 +-0.874 +-0.822 +-0.762 +-0.695 From 9b139cc9c108b10d1b85e3c941b415781269f488 Mon Sep 17 00:00:00 2001 From: Nathan Graule Date: Sun, 18 Feb 2024 11:24:01 +0100 Subject: [PATCH 08/21] wip: biquad design mod with polynom struct --- examples/diodeclipper/src/dsp.rs | 54 +- examples/svfmixer/src/lib.rs | 5 +- src/filters/biquad.rs | 2 + src/filters/biquad/design.rs | 165 + src/oversample/mod.rs | 40 +- .../valib__oversample__tests__os block.snap | 3964 ++++++++--------- ...rsample__tests__oversampled_dsp_block.snap | 84 +- .../valib__oversample__tests__post os.snap | 988 ++-- 8 files changed, 2744 insertions(+), 2558 deletions(-) create mode 100644 src/filters/biquad/design.rs diff --git a/examples/diodeclipper/src/dsp.rs b/examples/diodeclipper/src/dsp.rs index 48595de..d95a903 100644 --- a/examples/diodeclipper/src/dsp.rs +++ b/examples/diodeclipper/src/dsp.rs @@ -1,8 +1,9 @@ use enum_map::Enum; +use nih_plug::util::gain_to_db_fast; use num_traits::Zero; use valib::dsp::parameter::{HasParameters, Parameter, SmoothedParam}; -use valib::dsp::{PerSampleBlockAdapter, DSP}; +use valib::dsp::{DSPBlock, DSP}; use valib::filters::biquad::Biquad; use valib::oversample::{Oversample, Oversampled}; use valib::saturators::clippers::{DiodeClipper, DiodeClipperModel}; @@ -33,21 +34,21 @@ impl DSP<1, 1> for DcBlocker { self.0.process(x) } - fn reset(&mut self) { - self.0.reset() - } - - fn latency(&self) -> usize { - self.0.latency() - } - fn set_samplerate(&mut self, samplerate: f32) { - self.0.set_samplerate(samplerate); + DSP::set_samplerate(&mut self.0, samplerate); self.0.update_coefficients(&Biquad::highpass( T::from_f64((Self::CUTOFF_HZ / samplerate) as f64), T::from_f64(Self::Q as f64), )); } + + fn latency(&self) -> usize { + DSP::latency(&self.0) + } + + fn reset(&mut self) { + self.0.reset() + } } type Sample = AutoF32x2; @@ -189,29 +190,38 @@ impl DSP<1, 1> for DspInner { } pub struct Dsp { - inner: PerSampleBlockAdapter, 1, 1>, + inner: Oversampled, + max_oversampling: usize, dc_blocker: DcBlocker, } -impl DSP<1, 1> for Dsp { +impl DSPBlock<1, 1> for Dsp { type Sample = Sample; - fn process(&mut self, x: [Self::Sample; 1]) -> [Self::Sample; 1] { - self.dc_blocker.process(self.inner.process(x)) + fn process_block(&mut self, inputs: &[[Self::Sample; 1]], outputs: &mut [[Self::Sample; 1]]) { + self.inner.process_block(inputs, outputs); + + for o in outputs { + *o = self.dc_blocker.process(*o); + } } fn set_samplerate(&mut self, samplerate: f32) { - DSP::set_samplerate(&mut self.inner, samplerate); - DSP::set_samplerate(&mut self.dc_blocker, samplerate); + DSPBlock::set_samplerate(&mut self.inner, samplerate); + DSPBlock::set_samplerate(&mut self.dc_blocker, samplerate); + } + + fn max_block_size(&self) -> Option { + DSPBlock::max_block_size(&self.inner) } fn latency(&self) -> usize { - DSP::latency(&self.inner) + DSP::latency(&self.dc_blocker) + DSPBlock::latency(&self.inner) + DSPBlock::latency(&self.dc_blocker) } fn reset(&mut self) { - DSP::reset(&mut self.inner); - DSP::reset(&mut self.dc_blocker); + DSPBlock::reset(&mut self.inner); + DSPBlock::reset(&mut self.dc_blocker); } } @@ -224,11 +234,11 @@ impl HasParameters for Dsp { } pub fn create_dsp(samplerate: f32, oversample: usize, max_block_size: usize) -> Dsp { - let inner = PerSampleBlockAdapter::new( - Oversample::new(oversample, max_block_size).with_dsp(DspInner::new(samplerate)), - ); + let mut inner = + Oversample::new(oversample, max_block_size).with_dsp(samplerate, DspInner::new(samplerate)); Dsp { inner, + max_oversampling: oversample, dc_blocker: DcBlocker::new(samplerate), } } diff --git a/examples/svfmixer/src/lib.rs b/examples/svfmixer/src/lib.rs index 77344f2..4a41b9a 100644 --- a/examples/svfmixer/src/lib.rs +++ b/examples/svfmixer/src/lib.rs @@ -20,8 +20,9 @@ struct SvfMixerPlugin { impl Default for SvfMixerPlugin { fn default() -> Self { - let dsp_inner = DspInner::new(44100.0); - let dsp = Oversample::new(OVERSAMPLE, MAX_BUFFER_SIZE).with_dsp(dsp_inner); + let samplerate = 44100.0; + let dsp_inner = DspInner::new(samplerate); + let dsp = Oversample::new(OVERSAMPLE, MAX_BUFFER_SIZE).with_dsp(samplerate, dsp_inner); let params_controller = NihParamsController::new(&dsp, |param, _| match param { DspParam::Drive => FloatParam::new( "Drive", diff --git a/src/filters/biquad.rs b/src/filters/biquad.rs index 7ea71e1..ba5b29e 100644 --- a/src/filters/biquad.rs +++ b/src/filters/biquad.rs @@ -22,6 +22,8 @@ use crate::{ Scalar, }; +pub mod design; + /// Biquad struct in Transposed Direct Form II. Optionally, a [`Saturator`] instance can be used /// to apply waveshaping to the internal states. #[derive(Debug, Copy, Clone)] diff --git a/src/filters/biquad/design.rs b/src/filters/biquad/design.rs new file mode 100644 index 0000000..d78a821 --- /dev/null +++ b/src/filters/biquad/design.rs @@ -0,0 +1,165 @@ +use std::{iter, ops}; + +use num_traits::{Num, One, Zero}; +use num_traits::real::Real; + +#[derive(Debug, Clone)] +pub struct Polynom(pub Vec); + +impl Polynom { + pub fn polyline(offset: T, scale: T) -> Self { + Self(vec![offset, scale]) + } +} + +impl PartialEq for Polynom { + fn eq(&self, other: &Self) -> bool { + self.degree() == other.degree() && &self.0[..self.degree()] == &other.0[..other.degree()] + } +} + +impl Eq for Polynom {} + +impl Polynom { + pub fn degree(&self) -> usize { + self.0.iter().rposition(|s| !s.is_zero()).unwrap_or(0) + } + + pub fn canonicalize_in_place(&mut self) { + for _ in self.0.drain(self.degree() + 1..) {} + } + + pub fn canonicalize(mut self) -> Self { + self.canonicalize_in_place(); + self + } +} + +impl Polynom { + pub fn eval(&self, x: T) -> T { + self.0.iter().copied().enumerate().fold(T::zero(), |acc, (i, s)| acc + s * x.powi(i as _)) + } +} + +impl ops::Index for Polynom { + type Output = T; + + fn index(&self, index: usize) -> &Self::Output { + &self.0[index] + } +} + +impl ops::IndexMut for Polynom { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + &mut self.0[index] + } +} + +impl ops::Neg for Polynom { + type Output = Polynom; + + fn neg(mut self) -> Self::Output { + Polynom(self.0.into_iter().map(T::neg).collect()) + } +} + +impl> ops::Add for Polynom { + type Output = Polynom; + + fn add(self, rhs: Self) -> Self::Output { + Polynom(self.0.iter().copied().zip(rhs.0.iter().copied()).map(|(a, b)| a + b).collect()) + } +} + +impl> ops::Sub for Polynom { + type Output = Polynom; + + fn sub(self, rhs: Self) -> Self::Output { + Polynom(self.0.iter().copied().zip(rhs.0.iter().copied()).map(|(a, b)| a - b).collect()) + } +} + +impl> iter::Sum> for Polynom { + fn sum>>(iter: I) -> Self { + iter.fold(Self::zero(), ops::Add::add) + } +} + +impl + ops::Mul> ops::Mul for Polynom where Self: One { + type Output = Self; + + fn mul(self, rhs: Self) -> Self::Output { + if self.is_zero() || rhs.is_zero() { + return Self::zero(); + } + if self.is_one() { + return rhs; + } + if rhs.is_one() { + return self; + } + + let mut out = vec![T::zero(); self.degree() + rhs.degree() - 1]; + convolve(&mut out, &self.0[..self.degree()], &rhs.0[..rhs.degree()]); + Self(out) + } +} + +impl Zero for Polynom where Self: ops::Add { + fn zero() -> Self { + Self(vec![]) + } + + fn is_zero(&self) -> bool { + self.0.iter().all(|s| s.is_zero()) + } +} + +impl One for Polynom where Self: ops::Mul { + fn one() -> Self { + Self(vec![T::one()]) + } +} + +impl> Polynom where Self: One + ops::Mul { + pub fn from_roots(roots: &[T]) -> Self { + roots.iter().copied().map(|r| Self::polyline(-r, T::one())).fold(Self::one(), |acc, p| acc * p) + } +} + +// Naive convolution algorithm, suitable to the relatively small arrays ofr polynomial coefficients here +// See the Karatsuba convolution algorithm for a non-FFT algorithm that is better than O(n^2) (but worse than linear time) +fn convolve + ops::Mul>(output: &mut [T], a: &[T], b: &[T]) { + let (a, b) = if a.len() < b.len() { (a, b) } else { (b, a) }; + + output.fill(T::zero()); + for k in 0..a.len() { + for i in 0..b.len() { + output[k+i] += a[k] * b[i]; + } + } +} + +#[cfg(test)] +mod tests { + use rstest::rstest; + use super::*; + + #[rstest] + #[case(&[3.0, 8.0, 14.0, 8.0, 3.0], &[1.0, 2.0, 3.0], &[3.0, 2.0, 1.0])] + fn test_convolution(#[case] expected: &[f32], #[case] a: &[f32], #[case] b: &[f32]) { + let mut actual = expected.to_vec(); + actual.fill(0.0); + + convolve(&mut actual, a, b); + + assert_eq!(expected, &*actual); + } + + #[rstest] + #[case(Polynom(vec![1.0, 2.0, 1.0]), vec![-1.0, -1.0])] + fn test_polynom_from_roots(#[case] expected: Polynom, #[case] roots: Vec) { + let actual = Polynom::from_roots(&roots); + assert_eq!(expected, actual); + } +} \ No newline at end of file diff --git a/src/oversample/mod.rs b/src/oversample/mod.rs index 25249fc..8c83dd7 100644 --- a/src/oversample/mod.rs +++ b/src/oversample/mod.rs @@ -5,33 +5,33 @@ use crate::dsp::{ utils::{mono_block_to_slice, mono_block_to_slice_mut, slice_to_mono_block_mut}, DSP, }; -use crate::saturators::{Clipper, Linear}; +use crate::saturators::Linear; use crate::Scalar; use crate::{ dsp::{blocks::Series, DSPBlock}, filters::biquad::Biquad, }; -const CASCADE: usize = 4; +const CASCADE: usize = 5; #[derive(Debug, Clone)] pub struct Oversample { os_factor: usize, os_buffer: Box<[T]>, - pre_filter: Series<[Biquad; CASCADE]>, - post_filter: Series<[Biquad; CASCADE]>, + pre_filter: Series<[Biquad; CASCADE]>, + post_filter: Series<[Biquad; CASCADE]>, } impl Oversample { pub fn new(os_factor: usize, max_block_size: usize) -> Self { assert!(os_factor > 1); let os_buffer = vec![T::zero(); max_block_size * os_factor].into_boxed_slice(); - let fc_raw = f64::recip(2.1 * os_factor as f64); + let fc_raw = f64::recip(2.0 * os_factor as f64); let cascade_adjustment = f64::sqrt(2f64.powf(1.0 / CASCADE as f64) - 1.0).recip(); let fc_corr = fc_raw * cascade_adjustment; println!("cascade adjustment {cascade_adjustment}: {fc_raw} -> {fc_corr}"); let filters = - std::array::from_fn(|_| Biquad::lowpass(T::from_f64(fc_corr), T::from_f64(0.707))); + std::array::from_fn(|_| Biquad::lowpass(T::from_f64(fc_raw), T::from_f64(0.707))); Self { os_factor, os_buffer, @@ -65,15 +65,17 @@ impl Oversample { DSP::reset(&mut self.post_filter); } - pub fn with_dsp>(self, dsp: P) -> Oversampled { + pub fn with_dsp>(self, samplerate: f32, mut dsp: P) -> Oversampled { let max_block_size = dsp.max_block_size().unwrap_or(self.os_buffer.len()); // Verify that we satisfy the inner DSPBlock instance's requirement on maximum block size assert!(self.os_buffer.len() <= max_block_size); let staging_buffer = vec![[T::zero(); 1]; max_block_size].into_boxed_slice(); + dsp.set_samplerate(samplerate * self.os_factor as f32); Oversampled { oversampling: self, staging_buffer, inner: dsp, + samplerate, } } @@ -138,6 +140,7 @@ pub struct Oversampled { oversampling: Oversample, staging_buffer: Box<[[T; 1]]>, pub inner: P, + samplerate: f32, } impl Oversampled { @@ -150,18 +153,23 @@ impl Oversampled where T: Scalar, { - #[deprecated = "Use Oversample::with_dsp"] - pub fn new(oversampling: Oversample, inner: P) -> Self - where - P: DSP<1, 1, Sample = T>, - { - oversampling.with_dsp(inner) - } pub fn into_inner(self) -> P { self.inner } } +impl Oversampled +where + T: Scalar, + P: DSPBlock<1, 1, Sample = T>, +{ + pub fn set_oversampling_amount(&mut self, amt: usize) { + assert!(amt > 1); + self.oversampling.os_factor = amt; + self.set_samplerate(self.samplerate); + } +} + impl DSPBlock<1, 1> for Oversampled where T: Scalar, @@ -187,7 +195,7 @@ where fn max_block_size(&self) -> Option { Some(match self.inner.max_block_size() { Some(size) => size.min(self.oversampling.max_block_size() / self.os_factor()), - None => self.oversampling.max_block_size(), + None => self.oversampling.max_block_size() / self.os_factor(), }) } @@ -260,7 +268,7 @@ mod tests { frequency: freq, phase: 0.0, }; - let mut os = Oversample::::new(4, 64).with_dsp(dsp); + let mut os = Oversample::::new(4, 64).with_dsp(samplerate, dsp); let input = [[0.0]; 64]; let mut output = [[0.0]; 64]; diff --git a/src/oversample/snapshots/valib__oversample__tests__os block.snap b/src/oversample/snapshots/valib__oversample__tests__os block.snap index fc61de0..1c96d64 100644 --- a/src/oversample/snapshots/valib__oversample__tests__os block.snap +++ b/src/oversample/snapshots/valib__oversample__tests__os block.snap @@ -6,2047 +6,2047 @@ expression: "&*osblock" 0.0 0.0 0.0 -0.0 -0.0 -0.0 -0.0 -0.0 -0.0 -0.001 -0.002 -0.004 -0.008 -0.016 -0.028 -0.045 -0.067 -0.092 -0.12 -0.149 -0.177 -0.204 -0.229 -0.254 -0.277 +0.005 +0.036 +0.114 +0.182 +0.135 +0.048 +0.154 +0.365 0.299 -0.322 -0.345 -0.367 -0.39 -0.412 -0.435 -0.457 -0.479 -0.5 -0.521 -0.542 -0.562 -0.583 -0.602 -0.622 -0.641 -0.659 -0.678 -0.695 -0.713 -0.73 -0.746 -0.763 -0.778 -0.793 -0.808 -0.822 -0.836 -0.849 -0.862 -0.874 -0.886 -0.897 -0.907 -0.918 -0.927 -0.936 -0.944 -0.952 -0.959 -0.966 -0.972 -0.977 -0.982 -0.987 -0.99 -0.993 -0.996 -0.998 -0.999 -1.0 -1.0 -0.999 -0.998 -0.997 -0.994 -0.991 -0.988 -0.984 -0.979 -0.974 -0.968 -0.962 -0.955 -0.947 -0.939 -0.93 -0.921 -0.911 -0.9 -0.889 -0.878 -0.866 -0.853 -0.84 -0.827 -0.813 -0.798 -0.783 -0.768 -0.752 -0.735 -0.719 -0.701 -0.684 -0.665 -0.647 -0.628 -0.609 -0.589 -0.569 -0.549 -0.528 -0.507 -0.486 +0.055 +0.179 +0.552 0.464 -0.442 -0.42 -0.398 -0.375 -0.352 -0.329 -0.306 +0.056 +0.201 +0.735 +0.625 +0.057 +0.222 +0.911 +0.779 +0.057 +0.24 +1.078 +0.926 +0.057 +0.256 +1.234 +1.064 +0.056 +0.27 +1.379 +1.192 +0.054 0.282 -0.259 -0.235 -0.211 -0.187 -0.163 -0.139 -0.114 -0.09 -0.065 -0.041 -0.016 --0.008 +1.511 +1.308 +0.052 +0.29 +1.627 +1.411 +0.05 +0.296 +1.729 +1.501 +0.047 +0.299 +1.813 +1.577 +0.043 +0.299 +1.881 +1.637 +0.039 +0.296 +1.93 +1.682 +0.035 +0.29 +1.96 +1.71 +0.031 +0.281 +1.972 +1.722 +0.026 +0.27 +1.964 +1.718 +0.021 +0.256 +1.938 +1.696 +0.015 +0.24 +1.893 +1.659 +0.01 +0.221 +1.83 +1.605 +0.004 +0.201 +1.749 +1.536 +-0.001 +0.178 +1.651 +1.453 +-0.007 +0.154 +1.538 +1.355 +-0.012 +0.128 +1.409 +1.244 +-0.018 +0.101 +1.267 +1.121 +-0.023 +0.073 +1.113 +0.988 +-0.028 +0.044 +0.948 +0.845 -0.033 +0.015 +0.774 +0.693 +-0.037 +-0.015 +0.593 +0.536 +-0.041 +-0.044 +0.406 +0.372 +-0.045 +-0.073 +0.214 +0.206 +-0.048 +-0.101 +0.021 +0.037 +-0.051 +-0.128 +-0.172 +-0.132 +-0.053 +-0.154 +-0.364 +-0.3 +-0.055 +-0.178 +-0.552 +-0.464 +-0.056 +-0.201 +-0.735 +-0.625 -0.057 --0.082 --0.106 --0.131 --0.155 --0.179 --0.203 --0.227 --0.251 --0.275 --0.298 --0.322 --0.345 --0.368 --0.39 --0.413 --0.435 --0.457 --0.479 --0.5 --0.521 --0.542 --0.562 --0.583 --0.602 --0.622 --0.641 --0.659 --0.678 --0.695 --0.713 --0.73 --0.746 --0.763 --0.778 --0.793 --0.808 --0.822 --0.836 --0.849 --0.862 --0.874 --0.886 --0.897 --0.907 --0.918 --0.927 --0.936 --0.944 --0.952 --0.959 --0.966 --0.972 --0.977 --0.982 --0.987 --0.99 --0.993 --0.996 --0.998 --0.999 --1.0 --1.0 --0.999 --0.998 --0.997 --0.994 --0.991 --0.988 --0.984 --0.979 --0.974 --0.968 --0.962 --0.955 --0.947 --0.939 --0.93 --0.921 +-0.222 -0.911 --0.9 --0.889 --0.878 --0.866 --0.853 --0.84 --0.827 --0.813 --0.798 --0.783 --0.768 --0.752 --0.735 --0.719 --0.701 --0.684 --0.665 --0.647 --0.628 --0.609 --0.589 --0.569 --0.549 --0.528 --0.507 --0.486 --0.464 --0.442 --0.42 --0.398 --0.375 --0.352 --0.329 --0.306 +-0.779 +-0.057 +-0.24 +-1.078 +-0.926 +-0.057 +-0.256 +-1.234 +-1.064 +-0.056 +-0.27 +-1.379 +-1.192 +-0.054 -0.282 --0.259 --0.235 --0.211 --0.187 --0.163 --0.139 --0.114 --0.09 --0.065 --0.041 --0.016 -0.008 +-1.511 +-1.308 +-0.052 +-0.29 +-1.627 +-1.411 +-0.05 +-0.296 +-1.729 +-1.501 +-0.047 +-0.299 +-1.813 +-1.577 +-0.043 +-0.299 +-1.881 +-1.637 +-0.039 +-0.296 +-1.93 +-1.682 +-0.035 +-0.29 +-1.96 +-1.71 +-0.031 +-0.281 +-1.972 +-1.722 +-0.026 +-0.27 +-1.964 +-1.718 +-0.021 +-0.256 +-1.938 +-1.696 +-0.015 +-0.24 +-1.893 +-1.659 +-0.01 +-0.221 +-1.83 +-1.605 +-0.004 +-0.201 +-1.749 +-1.536 +0.001 +-0.178 +-1.651 +-1.453 +0.007 +-0.154 +-1.538 +-1.355 +0.012 +-0.128 +-1.409 +-1.244 +0.018 +-0.101 +-1.267 +-1.121 +0.023 +-0.073 +-1.113 +-0.988 +0.028 +-0.044 +-0.948 +-0.845 0.033 +-0.015 +-0.774 +-0.693 +0.037 +0.015 +-0.593 +-0.536 +0.041 +0.044 +-0.406 +-0.372 +0.045 +0.073 +-0.214 +-0.206 +0.048 +0.101 +-0.021 +-0.037 +0.051 +0.128 +0.172 +0.132 +0.053 +0.154 +0.364 +0.3 +0.055 +0.178 +0.552 +0.464 +0.056 +0.201 +0.735 +0.625 0.057 -0.082 -0.106 -0.131 -0.155 -0.179 -0.203 -0.227 -0.251 -0.275 -0.298 -0.322 -0.345 -0.368 -0.39 -0.413 -0.435 -0.457 -0.479 -0.5 -0.521 -0.542 -0.562 -0.583 -0.602 -0.622 -0.641 -0.659 -0.678 -0.695 -0.713 -0.73 -0.746 -0.763 -0.778 -0.793 -0.808 -0.822 -0.836 -0.849 -0.862 -0.874 -0.886 -0.897 -0.907 -0.918 -0.927 -0.936 -0.944 -0.952 -0.959 -0.966 -0.972 -0.977 -0.982 -0.987 -0.99 -0.993 -0.996 -0.998 -0.999 -1.0 -1.0 -0.999 -0.998 -0.997 -0.994 -0.991 -0.988 -0.984 -0.979 -0.974 -0.968 -0.962 -0.955 -0.947 -0.939 -0.93 -0.921 +0.222 0.911 -0.9 -0.889 -0.878 -0.866 -0.853 -0.84 -0.827 -0.813 -0.798 -0.783 -0.768 -0.752 -0.735 -0.719 -0.701 -0.684 -0.665 -0.647 -0.628 -0.609 -0.589 -0.569 -0.549 -0.528 -0.507 -0.486 -0.464 -0.442 -0.42 -0.398 -0.375 -0.352 -0.329 -0.306 +0.779 +0.057 +0.24 +1.078 +0.926 +0.057 +0.256 +1.234 +1.064 +0.056 +0.27 +1.379 +1.192 +0.054 0.282 -0.259 -0.235 -0.211 -0.187 -0.163 -0.139 -0.114 -0.09 -0.065 -0.041 -0.016 --0.008 +1.511 +1.308 +0.052 +0.29 +1.627 +1.411 +0.05 +0.296 +1.729 +1.501 +0.047 +0.299 +1.813 +1.577 +0.043 +0.299 +1.881 +1.637 +0.039 +0.296 +1.93 +1.682 +0.035 +0.29 +1.96 +1.71 +0.031 +0.281 +1.972 +1.722 +0.026 +0.27 +1.964 +1.718 +0.021 +0.256 +1.938 +1.696 +0.015 +0.24 +1.893 +1.659 +0.01 +0.221 +1.83 +1.605 +0.004 +0.201 +1.749 +1.536 +-0.001 +0.178 +1.651 +1.453 +-0.007 +0.154 +1.538 +1.355 +-0.012 +0.128 +1.409 +1.244 +-0.018 +0.101 +1.267 +1.121 +-0.023 +0.073 +1.113 +0.988 +-0.028 +0.044 +0.948 +0.845 -0.033 +0.015 +0.774 +0.693 +-0.037 +-0.015 +0.593 +0.536 +-0.041 +-0.044 +0.406 +0.372 +-0.045 +-0.073 +0.214 +0.206 +-0.048 +-0.101 +0.021 +0.037 +-0.051 +-0.128 +-0.172 +-0.132 +-0.053 +-0.154 +-0.364 +-0.3 +-0.055 +-0.178 +-0.552 +-0.464 +-0.056 +-0.201 +-0.735 +-0.625 -0.057 --0.082 --0.106 --0.131 --0.155 --0.179 --0.203 --0.227 --0.251 --0.275 --0.298 --0.322 --0.345 --0.368 --0.39 --0.413 --0.435 --0.457 --0.479 --0.5 --0.521 --0.542 --0.562 --0.583 --0.602 --0.622 --0.641 --0.659 --0.678 --0.695 --0.713 --0.73 --0.746 --0.763 --0.778 --0.793 --0.808 --0.822 --0.836 --0.849 --0.862 --0.874 --0.886 --0.897 --0.907 --0.918 --0.927 --0.936 --0.944 --0.952 --0.959 --0.966 --0.972 --0.977 --0.982 --0.987 --0.99 --0.993 --0.996 --0.998 --0.999 --1.0 --1.0 --0.999 --0.998 --0.997 --0.994 --0.991 --0.988 --0.984 --0.979 --0.974 --0.968 --0.962 --0.955 --0.947 --0.939 --0.93 --0.921 +-0.222 -0.911 --0.9 --0.889 --0.878 --0.866 --0.853 --0.84 --0.827 --0.813 --0.798 --0.783 --0.768 --0.752 --0.735 --0.719 --0.701 --0.684 --0.665 --0.647 --0.628 --0.609 --0.589 --0.569 --0.549 --0.528 --0.507 --0.486 --0.464 --0.442 --0.42 --0.398 --0.375 --0.352 --0.329 --0.306 +-0.779 +-0.057 +-0.24 +-1.078 +-0.926 +-0.057 +-0.256 +-1.234 +-1.064 +-0.056 +-0.27 +-1.379 +-1.192 +-0.054 -0.282 --0.259 --0.235 --0.211 --0.187 --0.163 --0.139 --0.114 --0.09 --0.065 --0.041 --0.016 -0.008 +-1.511 +-1.308 +-0.052 +-0.29 +-1.627 +-1.411 +-0.05 +-0.296 +-1.729 +-1.501 +-0.047 +-0.299 +-1.813 +-1.577 +-0.043 +-0.299 +-1.881 +-1.637 +-0.039 +-0.296 +-1.93 +-1.682 +-0.035 +-0.29 +-1.96 +-1.71 +-0.031 +-0.281 +-1.972 +-1.722 +-0.026 +-0.27 +-1.964 +-1.718 +-0.021 +-0.256 +-1.938 +-1.696 +-0.015 +-0.24 +-1.893 +-1.659 +-0.01 +-0.221 +-1.83 +-1.605 +-0.004 +-0.201 +-1.749 +-1.536 +0.001 +-0.178 +-1.651 +-1.453 +0.007 +-0.154 +-1.538 +-1.355 +0.012 +-0.128 +-1.409 +-1.244 +0.018 +-0.101 +-1.267 +-1.121 +0.023 +-0.073 +-1.113 +-0.988 +0.028 +-0.044 +-0.948 +-0.845 0.033 +-0.015 +-0.774 +-0.693 +0.037 +0.015 +-0.593 +-0.536 +0.041 +0.044 +-0.406 +-0.372 +0.045 +0.073 +-0.214 +-0.206 +0.048 +0.101 +-0.021 +-0.037 +0.051 +0.128 +0.172 +0.132 +0.053 +0.154 +0.364 +0.3 +0.055 +0.178 +0.552 +0.464 +0.056 +0.201 +0.735 +0.625 0.057 -0.082 -0.106 -0.131 -0.155 -0.179 -0.203 -0.227 -0.251 -0.275 -0.298 -0.322 -0.345 -0.368 -0.39 -0.413 -0.435 -0.457 -0.479 -0.5 -0.521 -0.542 -0.562 -0.583 -0.602 -0.622 -0.641 -0.659 -0.678 -0.695 -0.713 -0.73 -0.746 -0.763 -0.778 -0.793 -0.808 -0.822 -0.836 -0.849 -0.862 -0.874 -0.886 -0.897 -0.907 -0.918 -0.927 -0.936 -0.944 -0.952 -0.959 -0.966 -0.972 -0.977 -0.982 -0.987 -0.99 -0.993 -0.996 -0.998 -0.999 -1.0 -1.0 -0.999 -0.998 -0.997 -0.994 -0.991 -0.988 -0.984 -0.979 -0.974 -0.968 -0.962 -0.955 -0.947 -0.939 -0.93 -0.921 +0.222 0.911 -0.9 -0.889 -0.878 -0.866 -0.853 -0.84 -0.827 -0.813 -0.798 -0.783 -0.768 -0.752 -0.735 -0.719 -0.701 -0.684 -0.665 -0.647 -0.628 -0.609 -0.589 -0.569 -0.549 -0.528 -0.507 -0.486 -0.464 -0.442 -0.42 -0.398 -0.375 -0.352 -0.329 -0.306 +0.779 +0.057 +0.24 +1.078 +0.926 +0.057 +0.256 +1.234 +1.064 +0.056 +0.27 +1.379 +1.192 +0.054 0.282 -0.259 -0.235 -0.211 -0.187 -0.163 -0.139 -0.114 -0.09 -0.065 -0.041 -0.016 --0.008 +1.511 +1.308 +0.052 +0.29 +1.627 +1.411 +0.05 +0.296 +1.729 +1.501 +0.047 +0.299 +1.813 +1.577 +0.043 +0.299 +1.881 +1.637 +0.039 +0.296 +1.93 +1.682 +0.035 +0.29 +1.96 +1.71 +0.031 +0.281 +1.972 +1.722 +0.026 +0.27 +1.964 +1.718 +0.021 +0.256 +1.938 +1.696 +0.015 +0.24 +1.893 +1.659 +0.01 +0.221 +1.83 +1.605 +0.004 +0.201 +1.749 +1.536 +-0.001 +0.178 +1.651 +1.453 +-0.007 +0.154 +1.538 +1.355 +-0.012 +0.128 +1.409 +1.244 +-0.018 +0.101 +1.267 +1.121 +-0.023 +0.073 +1.113 +0.988 +-0.028 +0.044 +0.948 +0.845 -0.033 +0.015 +0.774 +0.693 +-0.037 +-0.015 +0.593 +0.536 +-0.041 +-0.044 +0.406 +0.372 +-0.045 +-0.073 +0.214 +0.206 +-0.048 +-0.101 +0.021 +0.037 +-0.051 +-0.128 +-0.172 +-0.132 +-0.053 +-0.154 +-0.364 +-0.3 +-0.055 +-0.178 +-0.552 +-0.464 +-0.056 +-0.201 +-0.735 +-0.625 -0.057 --0.082 --0.106 --0.131 --0.155 --0.179 --0.203 --0.227 --0.251 --0.275 --0.298 --0.322 --0.345 --0.368 --0.39 --0.413 --0.435 --0.457 --0.479 --0.5 --0.521 --0.542 --0.562 --0.583 --0.602 --0.622 --0.641 --0.659 --0.678 --0.695 --0.713 --0.73 --0.746 --0.763 --0.778 --0.793 --0.808 --0.822 --0.836 --0.849 --0.862 --0.874 --0.886 --0.897 --0.907 --0.918 --0.927 --0.936 --0.944 --0.952 --0.959 --0.966 --0.972 --0.977 --0.982 --0.987 --0.99 --0.993 --0.996 --0.998 --0.999 --1.0 --1.0 --0.999 --0.998 --0.997 --0.994 --0.991 --0.988 --0.984 --0.979 --0.974 --0.968 --0.962 --0.955 --0.947 --0.939 --0.93 --0.921 +-0.222 -0.911 --0.9 --0.889 --0.878 --0.866 --0.853 --0.84 --0.827 --0.813 --0.798 --0.783 --0.768 --0.752 --0.735 --0.719 --0.701 --0.684 --0.665 --0.647 --0.628 --0.609 --0.589 --0.569 --0.549 --0.528 --0.507 --0.486 --0.464 --0.442 --0.42 --0.398 --0.375 --0.352 --0.329 --0.306 +-0.779 +-0.057 +-0.24 +-1.078 +-0.926 +-0.057 +-0.256 +-1.234 +-1.064 +-0.056 +-0.27 +-1.379 +-1.192 +-0.054 -0.282 --0.259 --0.235 --0.211 --0.187 --0.163 --0.139 --0.114 --0.09 --0.065 --0.041 --0.016 -0.008 +-1.511 +-1.308 +-0.052 +-0.29 +-1.627 +-1.411 +-0.05 +-0.296 +-1.729 +-1.501 +-0.047 +-0.299 +-1.813 +-1.577 +-0.043 +-0.299 +-1.881 +-1.637 +-0.039 +-0.296 +-1.93 +-1.682 +-0.035 +-0.29 +-1.96 +-1.71 +-0.031 +-0.281 +-1.972 +-1.722 +-0.026 +-0.27 +-1.964 +-1.718 +-0.021 +-0.256 +-1.938 +-1.696 +-0.015 +-0.24 +-1.893 +-1.659 +-0.01 +-0.221 +-1.83 +-1.605 +-0.004 +-0.201 +-1.749 +-1.536 +0.001 +-0.178 +-1.651 +-1.453 +0.007 +-0.154 +-1.538 +-1.355 +0.012 +-0.128 +-1.409 +-1.244 +0.018 +-0.101 +-1.267 +-1.121 +0.023 +-0.073 +-1.113 +-0.988 +0.028 +-0.044 +-0.948 +-0.845 0.033 +-0.015 +-0.774 +-0.693 +0.037 +0.015 +-0.593 +-0.536 +0.041 +0.044 +-0.406 +-0.372 +0.045 +0.073 +-0.214 +-0.206 +0.048 +0.101 +-0.021 +-0.037 +0.051 +0.128 +0.172 +0.132 +0.053 +0.154 +0.364 +0.3 +0.055 +0.178 +0.552 +0.464 +0.056 +0.201 +0.735 +0.625 0.057 -0.082 -0.106 -0.131 -0.155 -0.179 -0.203 -0.227 -0.251 -0.275 -0.298 -0.322 -0.345 -0.368 -0.39 -0.413 -0.435 -0.457 -0.479 -0.5 -0.521 -0.542 -0.562 -0.583 -0.602 -0.622 -0.641 -0.659 -0.678 -0.695 -0.713 -0.73 -0.746 -0.763 -0.778 -0.793 -0.808 -0.822 -0.836 -0.849 -0.862 -0.874 -0.886 -0.897 -0.907 -0.918 -0.927 -0.936 -0.944 -0.952 -0.959 -0.966 -0.972 -0.977 -0.982 -0.987 -0.99 -0.993 -0.996 -0.998 -0.999 -1.0 -1.0 -0.999 -0.998 -0.997 -0.994 -0.991 -0.988 -0.984 -0.979 -0.974 -0.968 -0.962 -0.955 -0.947 -0.939 -0.93 -0.921 +0.222 0.911 -0.9 -0.889 -0.878 -0.866 -0.853 -0.84 -0.827 -0.813 -0.798 -0.783 -0.768 -0.752 -0.735 -0.719 -0.701 -0.684 -0.665 -0.647 -0.628 -0.609 -0.589 -0.569 -0.549 -0.528 -0.507 -0.486 -0.464 -0.442 -0.42 -0.398 -0.375 -0.352 -0.329 -0.306 +0.779 +0.057 +0.24 +1.078 +0.926 +0.057 +0.256 +1.234 +1.064 +0.056 +0.27 +1.379 +1.192 +0.054 0.282 -0.259 -0.235 -0.211 -0.187 -0.163 -0.139 -0.114 -0.09 -0.065 -0.041 -0.016 --0.008 +1.511 +1.308 +0.052 +0.29 +1.627 +1.411 +0.05 +0.296 +1.729 +1.501 +0.047 +0.299 +1.813 +1.577 +0.043 +0.299 +1.881 +1.637 +0.039 +0.296 +1.93 +1.682 +0.035 +0.29 +1.96 +1.71 +0.031 +0.281 +1.972 +1.722 +0.026 +0.27 +1.964 +1.718 +0.021 +0.256 +1.938 +1.696 +0.015 +0.24 +1.893 +1.659 +0.01 +0.221 +1.83 +1.605 +0.004 +0.201 +1.749 +1.536 +-0.001 +0.178 +1.651 +1.453 +-0.007 +0.154 +1.538 +1.355 +-0.012 +0.128 +1.409 +1.244 +-0.018 +0.101 +1.267 +1.121 +-0.023 +0.073 +1.113 +0.988 +-0.028 +0.044 +0.948 +0.845 -0.033 +0.015 +0.774 +0.693 +-0.037 +-0.015 +0.593 +0.536 +-0.041 +-0.044 +0.406 +0.372 +-0.045 +-0.073 +0.214 +0.206 +-0.048 +-0.101 +0.021 +0.037 +-0.051 +-0.128 +-0.172 +-0.132 +-0.053 +-0.154 +-0.364 +-0.3 +-0.055 +-0.178 +-0.552 +-0.464 +-0.056 +-0.201 +-0.735 +-0.625 -0.057 --0.082 --0.106 --0.131 --0.155 --0.179 --0.203 --0.227 --0.251 --0.275 --0.298 --0.322 --0.345 --0.368 --0.39 --0.413 --0.435 --0.457 --0.479 --0.5 --0.521 --0.542 --0.562 --0.583 --0.602 --0.622 --0.641 --0.659 --0.678 --0.695 --0.713 --0.73 --0.746 --0.763 --0.778 --0.793 --0.808 --0.822 --0.836 --0.849 --0.862 --0.874 --0.886 --0.897 --0.907 --0.918 --0.927 --0.936 --0.944 --0.952 --0.959 --0.966 --0.972 --0.977 --0.982 --0.987 --0.99 --0.993 --0.996 --0.998 --0.999 --1.0 --1.0 --0.999 --0.998 --0.997 --0.994 --0.991 --0.988 --0.984 --0.979 --0.974 --0.968 --0.962 --0.955 --0.947 --0.939 --0.93 --0.921 +-0.222 -0.911 --0.9 --0.889 --0.878 --0.866 --0.853 --0.84 --0.827 --0.813 --0.798 --0.783 --0.768 --0.752 --0.735 --0.719 --0.701 --0.684 --0.665 --0.647 --0.628 --0.609 --0.589 --0.569 --0.549 --0.528 --0.507 --0.486 --0.464 --0.442 --0.42 --0.398 --0.375 --0.352 --0.329 --0.306 +-0.779 +-0.057 +-0.24 +-1.078 +-0.926 +-0.057 +-0.256 +-1.234 +-1.064 +-0.056 +-0.27 +-1.379 +-1.192 +-0.054 -0.282 --0.259 --0.235 --0.211 --0.187 --0.163 --0.139 --0.114 --0.09 --0.065 --0.041 --0.016 -0.008 +-1.511 +-1.308 +-0.052 +-0.29 +-1.627 +-1.411 +-0.05 +-0.296 +-1.729 +-1.501 +-0.047 +-0.299 +-1.813 +-1.577 +-0.043 +-0.299 +-1.881 +-1.637 +-0.039 +-0.296 +-1.93 +-1.682 +-0.035 +-0.29 +-1.96 +-1.71 +-0.031 +-0.281 +-1.972 +-1.722 +-0.026 +-0.27 +-1.964 +-1.718 +-0.021 +-0.256 +-1.938 +-1.696 +-0.015 +-0.24 +-1.893 +-1.659 +-0.01 +-0.221 +-1.83 +-1.605 +-0.004 +-0.201 +-1.749 +-1.536 +0.001 +-0.178 +-1.651 +-1.453 +0.007 +-0.154 +-1.538 +-1.355 +0.012 +-0.128 +-1.409 +-1.244 +0.018 +-0.101 +-1.267 +-1.121 +0.023 +-0.073 +-1.113 +-0.988 +0.028 +-0.044 +-0.948 +-0.845 0.033 +-0.015 +-0.774 +-0.693 +0.037 +0.015 +-0.593 +-0.536 +0.041 +0.044 +-0.406 +-0.372 +0.045 +0.073 +-0.214 +-0.206 +0.048 +0.101 +-0.021 +-0.037 +0.051 +0.128 +0.172 +0.132 +0.053 +0.154 +0.364 +0.3 +0.055 +0.178 +0.552 +0.464 +0.056 +0.201 +0.735 +0.625 0.057 -0.082 -0.106 -0.131 -0.155 -0.179 -0.203 -0.227 -0.251 -0.275 -0.298 -0.322 -0.345 -0.368 -0.39 -0.413 -0.435 -0.457 -0.479 -0.5 -0.521 -0.542 -0.562 -0.583 -0.602 -0.622 -0.641 -0.659 -0.678 -0.695 -0.713 -0.73 -0.746 -0.763 -0.778 -0.793 -0.808 -0.822 -0.836 -0.849 -0.862 -0.874 -0.886 -0.897 -0.907 -0.918 -0.927 -0.936 -0.944 -0.952 -0.959 -0.966 -0.972 -0.977 -0.982 -0.987 -0.99 -0.993 -0.996 -0.998 -0.999 -1.0 -1.0 -0.999 -0.998 -0.997 -0.994 -0.991 -0.988 -0.984 -0.979 -0.974 -0.968 -0.962 -0.955 -0.947 -0.939 -0.93 -0.921 +0.222 0.911 -0.9 -0.889 -0.878 -0.866 -0.853 -0.84 -0.827 -0.813 -0.798 -0.783 -0.768 -0.752 -0.735 -0.719 -0.701 -0.684 -0.665 -0.647 -0.628 -0.609 -0.589 -0.569 -0.549 -0.528 -0.507 -0.486 -0.464 -0.442 -0.42 -0.398 -0.375 -0.352 -0.329 -0.306 +0.779 +0.057 +0.24 +1.078 +0.926 +0.057 +0.256 +1.234 +1.064 +0.056 +0.27 +1.379 +1.192 +0.054 0.282 -0.259 -0.235 -0.211 -0.187 -0.163 -0.139 -0.114 -0.09 -0.065 -0.041 -0.016 --0.008 +1.511 +1.308 +0.052 +0.29 +1.627 +1.411 +0.05 +0.296 +1.729 +1.501 +0.047 +0.299 +1.813 +1.577 +0.043 +0.299 +1.881 +1.637 +0.039 +0.296 +1.93 +1.682 +0.035 +0.29 +1.96 +1.71 +0.031 +0.281 +1.972 +1.722 +0.026 +0.27 +1.964 +1.718 +0.021 +0.256 +1.938 +1.696 +0.015 +0.24 +1.893 +1.659 +0.01 +0.221 +1.83 +1.605 +0.004 +0.201 +1.749 +1.536 +-0.001 +0.178 +1.651 +1.453 +-0.007 +0.154 +1.538 +1.355 +-0.012 +0.128 +1.409 +1.244 +-0.018 +0.101 +1.267 +1.121 +-0.023 +0.073 +1.113 +0.988 +-0.028 +0.044 +0.948 +0.845 -0.033 +0.015 +0.774 +0.693 +-0.037 +-0.015 +0.593 +0.536 +-0.041 +-0.044 +0.406 +0.372 +-0.045 +-0.073 +0.214 +0.206 +-0.048 +-0.101 +0.021 +0.037 +-0.051 +-0.128 +-0.172 +-0.132 +-0.053 +-0.154 +-0.364 +-0.3 +-0.055 +-0.178 +-0.552 +-0.464 +-0.056 +-0.201 +-0.735 +-0.625 -0.057 --0.082 --0.106 --0.131 --0.155 --0.179 --0.203 --0.227 --0.251 --0.275 --0.298 --0.322 --0.345 --0.368 --0.39 --0.413 --0.435 --0.457 --0.479 --0.5 --0.521 --0.542 --0.562 --0.583 --0.602 --0.622 --0.641 --0.659 --0.678 --0.695 --0.713 --0.73 --0.746 --0.763 --0.778 --0.793 --0.808 --0.822 --0.836 --0.849 --0.862 --0.874 --0.886 --0.897 --0.907 --0.918 --0.927 --0.936 --0.944 --0.952 --0.959 --0.966 --0.972 --0.977 --0.982 --0.987 --0.99 --0.993 --0.996 --0.998 --0.999 --1.0 --1.0 --0.999 --0.998 --0.997 --0.994 --0.991 --0.988 --0.984 --0.979 --0.974 --0.968 --0.962 --0.955 --0.947 --0.939 --0.93 --0.921 +-0.222 -0.911 --0.9 --0.889 --0.878 --0.866 --0.853 --0.84 --0.827 --0.813 --0.798 --0.783 --0.768 --0.752 --0.735 --0.719 --0.701 --0.684 --0.665 --0.647 --0.628 --0.609 --0.589 --0.569 --0.549 --0.528 --0.507 --0.486 --0.464 --0.442 --0.42 --0.398 --0.375 --0.352 --0.329 --0.306 +-0.779 +-0.057 +-0.24 +-1.078 +-0.926 +-0.057 +-0.256 +-1.234 +-1.064 +-0.056 +-0.27 +-1.379 +-1.192 +-0.054 -0.282 --0.259 --0.235 --0.211 --0.187 --0.163 --0.139 --0.114 --0.09 --0.065 --0.041 --0.016 -0.008 +-1.511 +-1.308 +-0.052 +-0.29 +-1.627 +-1.411 +-0.05 +-0.296 +-1.729 +-1.501 +-0.047 +-0.299 +-1.813 +-1.577 +-0.043 +-0.299 +-1.881 +-1.637 +-0.039 +-0.296 +-1.93 +-1.682 +-0.035 +-0.29 +-1.96 +-1.71 +-0.031 +-0.281 +-1.972 +-1.722 +-0.026 +-0.27 +-1.964 +-1.718 +-0.021 +-0.256 +-1.938 +-1.696 +-0.015 +-0.24 +-1.893 +-1.659 +-0.01 +-0.221 +-1.83 +-1.605 +-0.004 +-0.201 +-1.749 +-1.536 +0.001 +-0.178 +-1.651 +-1.453 +0.007 +-0.154 +-1.538 +-1.355 +0.012 +-0.128 +-1.409 +-1.244 +0.018 +-0.101 +-1.267 +-1.121 +0.023 +-0.073 +-1.113 +-0.988 +0.028 +-0.044 +-0.948 +-0.845 0.033 +-0.015 +-0.774 +-0.693 +0.037 +0.015 +-0.593 +-0.536 +0.041 +0.044 +-0.406 +-0.372 +0.045 +0.073 +-0.214 +-0.206 +0.048 +0.101 +-0.021 +-0.037 +0.051 +0.128 +0.172 +0.132 +0.053 +0.154 +0.364 +0.3 +0.055 +0.178 +0.552 +0.464 +0.056 +0.201 +0.735 +0.625 0.057 -0.082 -0.106 -0.131 -0.155 -0.179 -0.203 -0.227 -0.251 -0.275 -0.298 -0.322 -0.345 -0.368 -0.39 -0.413 -0.435 -0.457 -0.479 -0.5 -0.521 -0.542 -0.562 -0.583 -0.602 -0.622 -0.641 -0.659 -0.678 -0.695 -0.713 -0.73 -0.746 -0.763 -0.778 -0.793 -0.808 -0.822 -0.836 -0.849 -0.862 -0.874 -0.886 -0.897 -0.907 -0.918 -0.927 -0.936 -0.944 -0.952 -0.959 -0.966 -0.972 -0.977 -0.982 -0.987 -0.99 -0.993 -0.996 -0.998 -0.999 -1.0 -1.0 -0.999 -0.998 -0.997 -0.994 -0.991 -0.988 -0.984 -0.979 -0.974 -0.968 -0.962 -0.955 -0.947 -0.939 -0.93 -0.921 +0.222 0.911 -0.9 -0.889 -0.878 -0.866 -0.853 -0.84 -0.827 -0.813 -0.798 -0.783 -0.768 -0.752 -0.735 -0.719 -0.701 -0.684 -0.665 -0.647 -0.628 -0.609 -0.589 -0.569 -0.549 -0.528 -0.507 -0.486 -0.464 -0.442 -0.42 -0.398 -0.375 -0.352 -0.329 -0.306 +0.779 +0.057 +0.24 +1.078 +0.926 +0.057 +0.256 +1.234 +1.064 +0.056 +0.27 +1.379 +1.192 +0.054 0.282 -0.259 -0.235 -0.211 -0.187 -0.163 -0.139 -0.114 -0.09 -0.065 -0.041 -0.016 --0.008 +1.511 +1.308 +0.052 +0.29 +1.627 +1.411 +0.05 +0.296 +1.729 +1.501 +0.047 +0.299 +1.813 +1.577 +0.043 +0.299 +1.881 +1.637 +0.039 +0.296 +1.93 +1.682 +0.035 +0.29 +1.96 +1.71 +0.031 +0.281 +1.972 +1.722 +0.026 +0.27 +1.964 +1.718 +0.021 +0.256 +1.938 +1.696 +0.015 +0.24 +1.893 +1.659 +0.01 +0.221 +1.83 +1.605 +0.004 +0.201 +1.749 +1.536 +-0.001 +0.178 +1.651 +1.453 +-0.007 +0.154 +1.538 +1.355 +-0.012 +0.128 +1.409 +1.244 +-0.018 +0.101 +1.267 +1.121 +-0.023 +0.073 +1.113 +0.988 +-0.028 +0.044 +0.948 +0.845 -0.033 +0.015 +0.774 +0.693 +-0.037 +-0.015 +0.593 +0.536 +-0.041 +-0.044 +0.406 +0.372 +-0.045 +-0.073 +0.214 +0.206 +-0.048 +-0.101 +0.021 +0.037 +-0.051 +-0.128 +-0.172 +-0.132 +-0.053 +-0.154 +-0.364 +-0.3 +-0.055 +-0.178 +-0.552 +-0.464 +-0.056 +-0.201 +-0.735 +-0.625 -0.057 --0.082 --0.106 --0.131 --0.155 --0.179 --0.203 --0.227 --0.251 --0.275 --0.298 --0.322 --0.345 --0.368 --0.39 --0.413 --0.435 --0.457 --0.479 --0.5 --0.521 --0.542 --0.562 --0.583 --0.602 --0.622 --0.641 --0.659 --0.678 --0.695 --0.713 --0.73 --0.746 --0.763 --0.778 --0.793 --0.808 --0.822 --0.836 --0.849 --0.862 --0.874 --0.886 --0.897 --0.907 --0.918 --0.927 --0.936 --0.944 --0.952 --0.959 --0.966 --0.972 --0.977 --0.982 --0.987 --0.99 --0.993 --0.996 --0.998 --0.999 --1.0 --1.0 --0.999 --0.998 --0.997 --0.994 --0.991 --0.988 --0.984 --0.979 --0.974 --0.968 --0.962 --0.955 --0.947 --0.939 --0.93 --0.921 +-0.222 -0.911 --0.9 --0.889 --0.878 --0.866 --0.853 --0.84 --0.827 --0.813 --0.798 --0.783 --0.768 --0.752 --0.735 --0.719 --0.701 --0.684 --0.665 --0.647 --0.628 --0.609 --0.589 --0.569 --0.549 --0.528 --0.507 --0.486 --0.464 --0.442 --0.42 --0.398 --0.375 --0.352 --0.329 --0.306 +-0.779 +-0.057 +-0.24 +-1.078 +-0.926 +-0.057 +-0.256 +-1.234 +-1.064 +-0.056 +-0.27 +-1.379 +-1.192 +-0.054 -0.282 --0.259 --0.235 --0.211 --0.187 --0.163 --0.139 --0.114 --0.09 --0.065 --0.041 --0.016 -0.008 +-1.511 +-1.308 +-0.052 +-0.29 +-1.627 +-1.411 +-0.05 +-0.296 +-1.729 +-1.501 +-0.047 +-0.299 +-1.813 +-1.577 +-0.043 +-0.299 +-1.881 +-1.637 +-0.039 +-0.296 +-1.93 +-1.682 +-0.035 +-0.29 +-1.96 +-1.71 +-0.031 +-0.281 +-1.972 +-1.722 +-0.026 +-0.27 +-1.964 +-1.718 +-0.021 +-0.256 +-1.938 +-1.696 +-0.015 +-0.24 +-1.893 +-1.659 +-0.01 +-0.221 +-1.83 +-1.605 +-0.004 +-0.201 +-1.749 +-1.536 +0.001 +-0.178 +-1.651 +-1.453 +0.007 +-0.154 +-1.538 +-1.355 +0.012 +-0.128 +-1.409 +-1.244 +0.018 +-0.101 +-1.267 +-1.121 +0.023 +-0.073 +-1.113 +-0.988 +0.028 +-0.044 +-0.948 +-0.845 0.033 +-0.015 +-0.774 +-0.693 +0.037 +0.015 +-0.593 +-0.536 +0.041 +0.044 +-0.406 +-0.372 +0.045 +0.073 +-0.214 +-0.206 +0.048 +0.101 +-0.021 +-0.037 +0.051 +0.128 +0.172 +0.132 +0.053 +0.154 +0.364 +0.3 +0.055 +0.178 +0.552 +0.464 +0.056 +0.201 +0.735 +0.625 0.057 -0.082 -0.106 -0.131 -0.155 -0.179 -0.203 -0.227 -0.251 -0.275 -0.298 -0.322 -0.345 -0.368 -0.39 -0.413 -0.435 -0.457 -0.479 -0.5 -0.521 -0.542 -0.562 -0.583 -0.602 -0.622 -0.641 -0.659 -0.678 -0.695 -0.713 -0.73 -0.746 -0.763 -0.778 -0.793 -0.808 -0.822 -0.836 -0.849 -0.862 -0.874 -0.886 -0.897 -0.907 -0.918 -0.927 -0.936 -0.944 -0.952 -0.959 -0.966 -0.972 -0.977 -0.982 -0.987 -0.99 -0.993 -0.996 -0.998 -0.999 -1.0 -1.0 -0.999 -0.998 -0.997 -0.994 -0.991 -0.988 -0.984 -0.979 -0.974 -0.968 -0.962 -0.955 -0.947 -0.939 -0.93 -0.921 +0.222 0.911 -0.9 -0.889 -0.878 -0.866 -0.853 -0.84 -0.827 -0.813 -0.798 -0.783 -0.768 -0.752 -0.735 -0.719 -0.701 -0.684 -0.665 -0.647 -0.628 -0.609 -0.589 -0.569 -0.549 -0.528 -0.507 -0.486 -0.464 -0.442 -0.42 -0.398 -0.375 -0.352 -0.329 -0.306 +0.779 +0.057 +0.24 +1.078 +0.926 +0.057 +0.256 +1.234 +1.064 +0.056 +0.27 +1.379 +1.192 +0.054 0.282 -0.259 -0.235 -0.211 -0.187 -0.163 -0.139 -0.114 -0.09 -0.065 -0.041 -0.016 --0.008 +1.511 +1.308 +0.052 +0.29 +1.627 +1.411 +0.05 +0.296 +1.729 +1.501 +0.047 +0.299 +1.813 +1.577 +0.043 +0.299 +1.881 +1.637 +0.039 +0.296 +1.93 +1.682 +0.035 +0.29 +1.96 +1.71 +0.031 +0.281 +1.972 +1.722 +0.026 +0.27 +1.964 +1.718 +0.021 +0.256 +1.938 +1.696 +0.015 +0.24 +1.893 +1.659 +0.01 +0.221 +1.83 +1.605 +0.004 +0.201 +1.749 +1.536 +-0.001 +0.178 +1.651 +1.453 +-0.007 +0.154 +1.538 +1.355 +-0.012 +0.128 +1.409 +1.244 +-0.018 +0.101 +1.267 +1.121 +-0.023 +0.073 +1.113 +0.988 +-0.028 +0.044 +0.948 +0.845 -0.033 +0.015 +0.774 +0.693 +-0.037 +-0.015 +0.593 +0.536 +-0.041 +-0.044 +0.406 +0.372 +-0.045 +-0.073 +0.214 +0.206 +-0.048 +-0.101 +0.021 +0.037 +-0.051 +-0.128 +-0.172 +-0.132 +-0.053 +-0.154 +-0.364 +-0.3 +-0.055 +-0.178 +-0.552 +-0.464 +-0.056 +-0.201 +-0.735 +-0.625 -0.057 --0.082 --0.106 --0.131 --0.155 --0.179 --0.203 --0.227 --0.251 --0.275 --0.298 --0.322 --0.345 --0.368 --0.39 --0.413 --0.435 --0.457 --0.479 --0.5 --0.521 --0.542 --0.562 --0.583 --0.602 --0.622 --0.641 --0.659 --0.678 --0.695 --0.713 --0.73 --0.746 --0.763 --0.778 --0.793 --0.808 --0.822 --0.836 --0.849 --0.862 --0.874 --0.886 --0.897 --0.907 --0.918 --0.927 --0.936 --0.944 --0.952 --0.959 --0.966 --0.972 --0.977 --0.982 --0.987 --0.99 --0.993 --0.996 --0.998 --0.999 --1.0 --1.0 --0.999 --0.998 --0.997 --0.994 --0.991 --0.988 --0.984 --0.979 --0.974 --0.968 --0.962 --0.955 --0.947 --0.939 --0.93 --0.921 +-0.222 -0.911 --0.9 --0.889 --0.878 --0.866 --0.853 --0.84 --0.827 --0.813 --0.798 --0.783 --0.768 --0.752 --0.735 --0.719 --0.701 --0.684 --0.665 --0.647 --0.628 --0.609 --0.589 --0.569 --0.549 --0.528 --0.507 --0.486 --0.464 --0.442 --0.42 --0.398 --0.375 --0.352 --0.329 --0.306 +-0.779 +-0.057 +-0.24 +-1.078 +-0.926 +-0.057 +-0.256 +-1.234 +-1.064 +-0.056 +-0.27 +-1.379 +-1.192 +-0.054 -0.282 --0.259 --0.235 --0.211 --0.187 --0.163 --0.139 --0.114 --0.09 --0.065 --0.041 --0.016 -0.008 +-1.511 +-1.308 +-0.052 +-0.29 +-1.627 +-1.411 +-0.05 +-0.296 +-1.729 +-1.501 +-0.047 +-0.299 +-1.813 +-1.577 +-0.043 +-0.299 +-1.881 +-1.637 +-0.039 +-0.296 +-1.93 +-1.682 +-0.035 +-0.29 +-1.96 +-1.71 +-0.031 +-0.281 +-1.972 +-1.722 +-0.026 +-0.27 +-1.964 +-1.718 +-0.021 +-0.256 +-1.938 +-1.696 +-0.015 +-0.24 +-1.893 +-1.659 +-0.01 +-0.221 +-1.83 +-1.605 +-0.004 +-0.201 +-1.749 +-1.536 +0.001 +-0.178 +-1.651 +-1.453 +0.007 +-0.154 +-1.538 +-1.355 +0.012 +-0.128 +-1.409 +-1.244 +0.018 +-0.101 +-1.267 +-1.121 +0.023 +-0.073 +-1.113 +-0.988 +0.028 +-0.044 +-0.948 +-0.845 0.033 +-0.015 +-0.774 +-0.693 +0.037 +0.015 +-0.593 +-0.536 +0.041 +0.044 +-0.406 +-0.372 +0.045 +0.073 +-0.214 +-0.206 +0.048 +0.101 +-0.021 +-0.037 +0.051 +0.128 +0.172 +0.132 +0.053 +0.154 +0.364 +0.3 +0.055 +0.178 +0.552 +0.464 +0.056 +0.201 +0.735 +0.625 0.057 -0.082 -0.106 -0.131 -0.155 -0.179 -0.203 -0.227 -0.251 -0.275 -0.298 -0.322 -0.345 -0.368 -0.39 -0.413 -0.435 -0.457 -0.479 -0.5 -0.521 -0.542 -0.562 -0.583 -0.602 -0.622 -0.641 -0.659 -0.678 -0.695 -0.713 -0.73 -0.746 -0.763 -0.778 -0.793 -0.808 -0.822 -0.836 -0.849 -0.862 -0.874 -0.886 -0.897 -0.907 -0.918 -0.927 -0.936 -0.944 -0.952 -0.959 -0.966 -0.972 -0.977 -0.982 -0.987 -0.99 -0.993 -0.996 -0.998 -0.999 -1.0 -1.0 -0.999 -0.998 -0.997 -0.994 -0.991 -0.988 -0.984 -0.979 -0.974 -0.968 -0.962 -0.955 -0.947 -0.939 -0.93 -0.921 +0.222 0.911 -0.9 -0.889 -0.878 -0.866 -0.853 -0.84 -0.827 -0.813 -0.798 -0.783 -0.768 -0.752 -0.735 -0.719 -0.701 -0.684 -0.665 -0.647 -0.628 -0.609 -0.589 -0.569 -0.549 -0.528 -0.507 -0.486 -0.464 -0.442 -0.42 -0.398 -0.375 -0.352 -0.329 -0.306 +0.779 +0.057 +0.24 +1.078 +0.926 +0.057 +0.256 +1.234 +1.064 +0.056 +0.27 +1.379 +1.192 +0.054 0.282 -0.259 -0.235 -0.211 -0.187 -0.163 -0.139 -0.114 -0.09 -0.065 -0.041 -0.016 --0.008 +1.511 +1.308 +0.052 +0.29 +1.627 +1.411 +0.05 +0.296 +1.729 +1.501 +0.047 +0.299 +1.813 +1.577 +0.043 +0.299 +1.881 +1.637 +0.039 +0.296 +1.93 +1.682 +0.035 +0.29 +1.96 +1.71 +0.031 +0.281 +1.972 +1.722 +0.026 +0.27 +1.964 +1.718 +0.021 +0.256 +1.938 +1.696 +0.015 +0.24 +1.893 +1.659 +0.01 +0.221 +1.83 +1.605 +0.004 +0.201 +1.749 +1.536 +-0.001 +0.178 +1.651 +1.453 +-0.007 +0.154 +1.538 +1.355 +-0.012 +0.128 +1.409 +1.244 +-0.018 +0.101 +1.267 +1.121 +-0.023 +0.073 +1.113 +0.988 +-0.028 +0.044 +0.948 +0.845 -0.033 +0.015 +0.774 +0.693 +-0.037 +-0.015 +0.593 +0.536 +-0.041 +-0.044 +0.406 +0.372 +-0.045 +-0.073 +0.214 +0.206 +-0.048 +-0.101 +0.021 +0.037 +-0.051 +-0.128 +-0.172 +-0.132 +-0.053 +-0.154 +-0.364 +-0.3 +-0.055 +-0.178 +-0.552 +-0.464 +-0.056 +-0.201 +-0.735 +-0.625 -0.057 --0.082 --0.106 --0.131 --0.155 --0.179 --0.203 --0.227 --0.251 --0.275 --0.298 --0.322 --0.345 --0.368 --0.39 --0.413 --0.435 --0.457 --0.479 --0.5 --0.521 --0.542 --0.562 --0.583 --0.602 --0.622 --0.641 --0.659 --0.678 --0.695 --0.713 --0.73 --0.746 --0.763 --0.778 --0.793 --0.808 --0.822 --0.836 --0.849 --0.862 --0.874 --0.886 --0.897 --0.907 --0.918 --0.927 --0.936 --0.944 --0.952 --0.959 --0.966 --0.972 --0.977 --0.982 --0.987 --0.99 --0.993 --0.996 --0.998 --0.999 --1.0 --1.0 --0.999 --0.998 --0.997 --0.994 --0.991 --0.988 --0.984 --0.979 --0.974 --0.968 --0.962 --0.955 --0.947 --0.939 --0.93 --0.921 +-0.222 -0.911 --0.9 --0.889 --0.878 --0.866 --0.853 --0.84 --0.827 --0.813 --0.798 --0.783 --0.768 --0.752 --0.735 --0.719 --0.701 --0.684 --0.665 --0.647 --0.628 --0.609 --0.589 --0.569 --0.549 --0.528 --0.507 --0.486 --0.464 --0.442 --0.42 --0.398 --0.375 --0.352 +-0.779 +-0.057 +-0.24 +-1.078 +-0.926 +-0.057 +-0.256 +-1.234 +-1.064 +-0.056 +-0.27 +-1.379 +-1.192 +-0.054 +-0.282 +-1.511 +-1.308 +-0.052 +-0.29 +-1.627 +-1.411 +-0.05 +-0.296 +-1.729 +-1.501 +-0.047 +-0.299 +-1.813 +-1.577 +-0.043 +-0.299 +-1.881 +-1.637 +-0.039 +-0.296 +-1.93 +-1.682 +-0.035 +-0.29 +-1.96 +-1.71 +-0.031 +-0.281 +-1.972 +-1.722 +-0.026 +-0.27 +-1.964 +-1.718 +-0.021 +-0.256 +-1.938 +-1.696 +-0.015 +-0.24 +-1.893 +-1.659 +-0.01 +-0.221 +-1.83 +-1.605 +-0.004 +-0.201 +-1.749 +-1.536 +0.001 +-0.178 +-1.651 +-1.453 +0.007 +-0.154 +-1.538 +-1.355 +0.012 +-0.128 +-1.409 +-1.244 +0.018 +-0.101 +-1.267 +-1.121 +0.023 +-0.073 +-1.113 +-0.988 +0.028 +-0.044 +-0.948 +-0.845 +0.033 +-0.015 +-0.774 +-0.693 +0.037 +0.015 +-0.593 +-0.536 +0.041 +0.044 +-0.406 +-0.372 +0.045 +0.073 +-0.214 diff --git a/src/oversample/snapshots/valib__oversample__tests__oversampled_dsp_block.snap b/src/oversample/snapshots/valib__oversample__tests__oversampled_dsp_block.snap index 0789ec1..1d4cacb 100644 --- a/src/oversample/snapshots/valib__oversample__tests__oversampled_dsp_block.snap +++ b/src/oversample/snapshots/valib__oversample__tests__oversampled_dsp_block.snap @@ -2,67 +2,67 @@ source: src/oversample/mod.rs expression: "&output as &[_]" --- --0.0 --0.0 --0.015 --0.235 --0.845 --1.124 +-0.012 +-1.177 +-1.034 +-1.009 -1.0 --0.988 --1.003 -1.0 -1.0 -1.0 -1.0 -1.0 --0.999 --0.929 --0.271 -0.954 -1.204 -0.967 -0.99 -1.005 +-1.0 +-1.0 +-1.0 +-0.796 +1.239 +1.038 +0.995 0.999 1.0 1.0 1.0 1.0 -0.989 -0.722 --0.371 --1.234 --1.057 --0.963 --1.005 --1.001 --0.999 +1.0 +1.0 +1.0 +1.0 +-0.713 +-0.888 +-0.995 +-1.004 +-1.0 +-1.0 +-1.0 +-1.0 -1.0 -1.0 -1.0 --0.999 --0.929 --0.271 -0.954 -1.204 -0.967 -0.99 -1.005 +-1.0 +-0.796 +1.239 +1.038 +0.995 0.999 1.0 1.0 1.0 1.0 -0.989 -0.722 --0.371 --1.234 --1.057 --0.963 --1.005 --1.001 --0.999 +1.0 +1.0 +1.0 +1.0 +-0.713 +-0.888 +-0.995 +-1.004 +-1.0 +-1.0 +-1.0 +-1.0 +-1.0 -1.0 -1.0 -1.0 +-0.796 diff --git a/src/oversample/snapshots/valib__oversample__tests__post os.snap b/src/oversample/snapshots/valib__oversample__tests__post os.snap index e67db05..876668e 100644 --- a/src/oversample/snapshots/valib__oversample__tests__post os.snap +++ b/src/oversample/snapshots/valib__oversample__tests__post os.snap @@ -4,513 +4,513 @@ expression: "&out as &[_]" --- 0.0 0.0 -0.0 -0.0 -0.0 -0.0 -0.003 -0.026 -0.096 -0.207 -0.31 -0.398 -0.485 -0.569 -0.647 -0.719 +0.069 +0.116 +0.185 +0.259 +0.331 +0.399 +0.463 +0.523 +0.577 +0.627 +0.67 +0.706 +0.736 +0.759 +0.775 +0.783 0.783 -0.841 -0.89 -0.93 -0.962 -0.984 -0.997 -1.0 -0.993 -0.977 -0.952 -0.917 -0.874 -0.822 -0.762 -0.695 -0.622 -0.542 -0.457 -0.367 -0.274 -0.179 -0.082 --0.017 --0.114 --0.211 --0.306 --0.398 --0.486 --0.569 --0.647 --0.719 +0.776 +0.761 +0.739 +0.71 +0.675 +0.632 +0.584 +0.53 +0.471 +0.407 +0.339 +0.269 +0.195 +0.12 +0.043 +-0.034 +-0.11 +-0.186 +-0.26 +-0.331 +-0.399 +-0.463 +-0.523 +-0.577 +-0.627 +-0.67 +-0.706 +-0.736 +-0.759 +-0.775 -0.783 --0.841 --0.89 --0.93 --0.962 --0.984 --0.997 --1.0 --0.993 --0.977 --0.952 --0.917 --0.874 --0.822 --0.762 --0.695 --0.622 --0.542 --0.457 --0.367 --0.274 --0.179 --0.082 -0.017 -0.114 -0.211 -0.306 -0.398 -0.486 -0.569 -0.647 -0.719 +-0.783 +-0.776 +-0.761 +-0.739 +-0.71 +-0.675 +-0.632 +-0.584 +-0.53 +-0.471 +-0.407 +-0.339 +-0.269 +-0.195 +-0.12 +-0.043 +0.034 +0.11 +0.186 +0.26 +0.331 +0.399 +0.463 +0.523 +0.577 +0.627 +0.67 +0.706 +0.736 +0.759 +0.775 +0.783 0.783 -0.841 -0.89 -0.93 -0.962 -0.984 -0.997 -1.0 -0.993 -0.977 -0.952 -0.917 -0.874 -0.822 -0.762 -0.695 -0.622 -0.542 -0.457 -0.367 -0.274 -0.179 -0.082 --0.017 --0.114 --0.211 --0.306 --0.398 --0.486 --0.569 --0.647 --0.719 +0.776 +0.761 +0.739 +0.71 +0.675 +0.632 +0.584 +0.53 +0.471 +0.407 +0.339 +0.269 +0.195 +0.12 +0.043 +-0.034 +-0.11 +-0.186 +-0.26 +-0.331 +-0.399 +-0.463 +-0.523 +-0.577 +-0.627 +-0.67 +-0.706 +-0.736 +-0.759 +-0.775 +-0.783 -0.783 --0.841 --0.89 --0.93 --0.962 --0.984 --0.997 --1.0 --0.993 --0.977 --0.952 --0.917 --0.874 --0.822 --0.762 --0.695 --0.622 --0.542 --0.457 --0.367 --0.274 --0.179 --0.082 -0.017 -0.114 -0.211 -0.306 -0.398 -0.486 -0.569 -0.647 -0.719 +-0.776 +-0.761 +-0.739 +-0.71 +-0.675 +-0.632 +-0.584 +-0.53 +-0.471 +-0.407 +-0.339 +-0.269 +-0.195 +-0.12 +-0.043 +0.034 +0.11 +0.186 +0.26 +0.331 +0.399 +0.463 +0.523 +0.577 +0.627 +0.67 +0.706 +0.736 +0.759 +0.775 0.783 -0.841 -0.89 -0.93 -0.962 -0.984 -0.997 -1.0 -0.993 -0.977 -0.952 -0.917 -0.874 -0.822 -0.762 -0.695 -0.622 -0.542 -0.457 -0.367 -0.274 -0.179 -0.082 --0.017 --0.114 --0.211 --0.306 --0.398 --0.486 --0.569 --0.647 --0.719 +0.783 +0.776 +0.761 +0.739 +0.71 +0.675 +0.632 +0.584 +0.53 +0.471 +0.407 +0.339 +0.269 +0.195 +0.12 +0.043 +-0.034 +-0.11 +-0.186 +-0.26 +-0.331 +-0.399 +-0.463 +-0.523 +-0.577 +-0.627 +-0.67 +-0.706 +-0.736 +-0.759 +-0.775 +-0.783 -0.783 --0.841 --0.89 --0.93 --0.962 --0.984 --0.997 --1.0 --0.993 --0.977 --0.952 --0.917 --0.874 --0.822 --0.762 --0.695 --0.622 --0.542 --0.457 --0.367 --0.274 --0.179 --0.082 -0.017 -0.114 -0.211 -0.306 -0.398 -0.486 -0.569 -0.647 -0.719 +-0.776 +-0.761 +-0.739 +-0.71 +-0.675 +-0.632 +-0.584 +-0.53 +-0.471 +-0.407 +-0.339 +-0.269 +-0.195 +-0.12 +-0.043 +0.034 +0.11 +0.186 +0.26 +0.331 +0.399 +0.463 +0.523 +0.577 +0.627 +0.67 +0.706 +0.736 +0.759 +0.775 0.783 -0.841 -0.89 -0.93 -0.962 -0.984 -0.997 -1.0 -0.993 -0.977 -0.952 -0.917 -0.874 -0.822 -0.762 -0.695 -0.622 -0.542 -0.457 -0.367 -0.274 -0.179 -0.082 --0.017 --0.114 --0.211 --0.306 --0.398 --0.486 --0.569 --0.647 --0.719 +0.783 +0.776 +0.761 +0.739 +0.71 +0.675 +0.632 +0.584 +0.53 +0.471 +0.407 +0.339 +0.269 +0.195 +0.12 +0.043 +-0.034 +-0.11 +-0.186 +-0.26 +-0.331 +-0.399 +-0.463 +-0.523 +-0.577 +-0.627 +-0.67 +-0.706 +-0.736 +-0.759 +-0.775 +-0.783 -0.783 --0.841 --0.89 --0.93 --0.962 --0.984 --0.997 --1.0 --0.993 --0.977 --0.952 --0.917 --0.874 --0.822 --0.762 --0.695 --0.622 --0.542 --0.457 --0.367 --0.274 --0.179 --0.082 -0.017 -0.114 -0.211 -0.306 -0.398 -0.486 -0.569 -0.647 -0.719 +-0.776 +-0.761 +-0.739 +-0.71 +-0.675 +-0.632 +-0.584 +-0.53 +-0.471 +-0.407 +-0.339 +-0.269 +-0.195 +-0.12 +-0.043 +0.034 +0.11 +0.186 +0.26 +0.331 +0.399 +0.463 +0.523 +0.577 +0.627 +0.67 +0.706 +0.736 +0.759 +0.775 +0.783 0.783 -0.841 -0.89 -0.93 -0.962 -0.984 -0.997 -1.0 -0.993 -0.977 -0.952 -0.917 -0.874 -0.822 -0.762 -0.695 -0.622 -0.542 -0.457 -0.367 -0.274 -0.179 -0.082 --0.017 --0.114 --0.211 --0.306 --0.398 --0.486 --0.569 --0.647 --0.719 +0.776 +0.761 +0.739 +0.71 +0.675 +0.632 +0.584 +0.53 +0.471 +0.407 +0.339 +0.269 +0.195 +0.12 +0.043 +-0.034 +-0.11 +-0.186 +-0.26 +-0.331 +-0.399 +-0.463 +-0.523 +-0.577 +-0.627 +-0.67 +-0.706 +-0.736 +-0.759 +-0.775 -0.783 --0.841 --0.89 --0.93 --0.962 --0.984 --0.997 --1.0 --0.993 --0.977 --0.952 --0.917 --0.874 --0.822 --0.762 --0.695 --0.622 --0.542 --0.457 --0.367 --0.274 --0.179 --0.082 -0.017 -0.114 -0.211 -0.306 -0.398 -0.486 -0.569 -0.647 -0.719 +-0.783 +-0.776 +-0.761 +-0.739 +-0.71 +-0.675 +-0.632 +-0.584 +-0.53 +-0.471 +-0.407 +-0.339 +-0.269 +-0.195 +-0.12 +-0.043 +0.034 +0.11 +0.186 +0.26 +0.331 +0.399 +0.463 +0.523 +0.577 +0.627 +0.67 +0.706 +0.736 +0.759 +0.775 +0.783 0.783 -0.841 -0.89 -0.93 -0.962 -0.984 -0.997 -1.0 -0.993 -0.977 -0.952 -0.917 -0.874 -0.822 -0.762 -0.695 -0.622 -0.542 -0.457 -0.367 -0.274 -0.179 -0.082 --0.017 --0.114 --0.211 --0.306 --0.398 --0.486 --0.569 --0.647 --0.719 +0.776 +0.761 +0.739 +0.71 +0.675 +0.632 +0.584 +0.53 +0.471 +0.407 +0.339 +0.269 +0.195 +0.12 +0.043 +-0.034 +-0.11 +-0.186 +-0.26 +-0.331 +-0.399 +-0.463 +-0.523 +-0.577 +-0.627 +-0.67 +-0.706 +-0.736 +-0.759 +-0.775 -0.783 --0.841 --0.89 --0.93 --0.962 --0.984 --0.997 --1.0 --0.993 --0.977 --0.952 --0.917 --0.874 --0.822 --0.762 --0.695 --0.622 --0.542 --0.457 --0.367 --0.274 --0.179 --0.082 -0.017 -0.114 -0.211 -0.306 -0.398 -0.486 -0.569 -0.647 -0.719 +-0.783 +-0.776 +-0.761 +-0.739 +-0.71 +-0.675 +-0.632 +-0.584 +-0.53 +-0.471 +-0.407 +-0.339 +-0.269 +-0.195 +-0.12 +-0.043 +0.034 +0.11 +0.186 +0.26 +0.331 +0.399 +0.463 +0.523 +0.577 +0.627 +0.67 +0.706 +0.736 +0.759 +0.775 +0.783 0.783 -0.841 -0.89 -0.93 -0.962 -0.984 -0.997 -1.0 -0.993 -0.977 -0.952 -0.917 -0.874 -0.822 -0.762 -0.695 -0.622 -0.542 -0.457 -0.367 -0.274 -0.179 -0.082 --0.017 --0.114 --0.211 --0.306 --0.398 --0.486 --0.569 --0.647 --0.719 +0.776 +0.761 +0.739 +0.71 +0.675 +0.632 +0.584 +0.53 +0.471 +0.407 +0.339 +0.269 +0.195 +0.12 +0.043 +-0.034 +-0.11 +-0.186 +-0.26 +-0.331 +-0.399 +-0.463 +-0.523 +-0.577 +-0.627 +-0.67 +-0.706 +-0.736 +-0.759 +-0.775 +-0.783 -0.783 --0.841 --0.89 --0.93 --0.962 --0.984 --0.997 --1.0 --0.993 --0.977 --0.952 --0.917 --0.874 --0.822 --0.762 --0.695 --0.622 --0.542 --0.457 --0.367 --0.274 --0.179 --0.082 -0.017 -0.114 -0.211 -0.306 -0.398 -0.486 -0.569 -0.647 -0.719 +-0.776 +-0.761 +-0.739 +-0.71 +-0.675 +-0.632 +-0.584 +-0.53 +-0.471 +-0.407 +-0.339 +-0.269 +-0.195 +-0.12 +-0.043 +0.034 +0.11 +0.186 +0.26 +0.331 +0.399 +0.463 +0.523 +0.577 +0.627 +0.67 +0.706 +0.736 +0.759 +0.775 0.783 -0.841 -0.89 -0.93 -0.962 -0.984 -0.997 -1.0 -0.993 -0.977 -0.952 -0.917 -0.874 -0.822 -0.762 -0.695 -0.622 -0.542 -0.457 -0.367 -0.274 -0.179 -0.082 --0.017 --0.114 --0.211 --0.306 --0.398 --0.486 --0.569 --0.647 --0.719 +0.783 +0.776 +0.761 +0.739 +0.71 +0.675 +0.632 +0.584 +0.53 +0.471 +0.407 +0.339 +0.268 +0.195 +0.12 +0.043 +-0.034 +-0.11 +-0.186 +-0.26 +-0.331 +-0.399 +-0.463 +-0.523 +-0.577 +-0.627 +-0.67 +-0.706 +-0.736 +-0.759 +-0.775 +-0.783 -0.783 --0.841 --0.89 --0.93 --0.962 --0.984 --0.997 --1.0 --0.993 --0.977 --0.952 --0.917 --0.874 --0.822 --0.762 --0.695 +-0.776 +-0.761 +-0.739 +-0.71 +-0.675 +-0.632 +-0.584 +-0.53 +-0.471 +-0.407 +-0.339 +-0.269 +-0.195 From bc677f60797391c1ca88a35a6d84c373c4ec6ae3 Mon Sep 17 00:00:00 2001 From: Nathan Graule Date: Sun, 18 Feb 2024 21:50:28 +0100 Subject: [PATCH 09/21] wip: butterworth + bilinear transform in design module --- Cargo.toml | 4 + src/dsp/blocks.rs | 41 +++++ src/filters/biquad/design.rs | 305 +++++++++++++++++++++-------------- src/lib.rs | 2 + src/math/mod.rs | 2 + src/math/polynom.rs | 245 ++++++++++++++++++++++++++++ src/oversample/mod.rs | 26 +-- 7 files changed, 492 insertions(+), 133 deletions(-) create mode 100644 src/math/polynom.rs diff --git a/Cargo.toml b/Cargo.toml index dff9696..e363e40 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,6 +44,10 @@ rstest = "0.18.2" serde = "*" [features] +default = ["biquad-design", "oversample"] +biquad-design = ["math-polynom"] +math-polynom = [] +oversample = ["biquad-design"] fundsp = ["dep:fundsp", "dep:numeric-array", "dep:typenum"] nih-plug = ["dep:nih_plug"] diff --git a/src/dsp/blocks.rs b/src/dsp/blocks.rs index 0c39e4c..22cb881 100644 --- a/src/dsp/blocks.rs +++ b/src/dsp/blocks.rs @@ -1,5 +1,7 @@ //! Small [`DSP`] building blocks for reusability. use std::marker::PhantomData; +use std::ops; +use std::ops::DerefMut; use nalgebra::{Complex, ComplexField, SMatrix, SVector}; use num_traits::{One, Zero}; @@ -283,6 +285,33 @@ impl, const N: usize, const C: usize> DSP for Series<[P; C]> } } +impl DSP for Series> +where + P: DSP, +{ + type Sample = P::Sample; + + fn process(&mut self, x: [Self::Sample; N]) -> [Self::Sample; N] { + self.0.iter_mut().fold(x, |x, dsp| dsp.process(x)) + } + + fn set_samplerate(&mut self, samplerate: f32) { + for s in &mut self.0 { + s.set_samplerate(samplerate); + } + } + + fn latency(&self) -> usize { + self.0.iter().map(|dsp| dsp.latency()).sum() + } + + fn reset(&mut self) { + for dsp in self.0.iter_mut() { + dsp.reset(); + } + } +} + impl DspAnalysis for Series<[P; C]> where P: DspAnalysis, @@ -295,6 +324,18 @@ where } } +impl DspAnalysis for Series> +where + P: DspAnalysis, +{ + fn h_z(&self, z: Complex) -> [[Complex; N]; N] { + self.0.iter().fold([[Complex::one(); N]; N], |acc, f| { + let ret = f.h_z(z); + std::array::from_fn(|i| std::array::from_fn(|j| acc[i][j] * ret[i][j])) + }) + } +} + /// Process inner DSP blocks in parallel. Input is fanned out to all inner blocks, then summed back out. #[derive(Debug, Copy, Clone)] pub struct Parallel(pub T); diff --git a/src/filters/biquad/design.rs b/src/filters/biquad/design.rs index d78a821..603a747 100644 --- a/src/filters/biquad/design.rs +++ b/src/filters/biquad/design.rs @@ -1,165 +1,230 @@ -use std::{iter, ops}; +use std::f64::consts::PI; +use std::{fmt, ops}; -use num_traits::{Num, One, Zero}; -use num_traits::real::Real; +use nalgebra::Complex; +use num_traits::{NumOps, One, Zero}; +use simba::simd::{SimdComplexField, SimdValue}; -#[derive(Debug, Clone)] -pub struct Polynom(pub Vec); +use crate::dsp::blocks::Series; +use crate::math::polynom::Polynom; +use crate::saturators::Linear; +use crate::Scalar; -impl Polynom { - pub fn polyline(offset: T, scale: T) -> Self { - Self(vec![offset, scale]) - } -} - -impl PartialEq for Polynom { - fn eq(&self, other: &Self) -> bool { - self.degree() == other.degree() && &self.0[..self.degree()] == &other.0[..other.degree()] - } -} +use super::Biquad; -impl Eq for Polynom {} - -impl Polynom { - pub fn degree(&self) -> usize { - self.0.iter().rposition(|s| !s.is_zero()).unwrap_or(0) - } - - pub fn canonicalize_in_place(&mut self) { - for _ in self.0.drain(self.degree() + 1..) {} - } +/// Structure holding the numerator and denominator of a fraction as separate numbers. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct Rational(pub T, pub T); - pub fn canonicalize(mut self) -> Self { - self.canonicalize_in_place(); - self +impl Rational { + /// Map the numerator and denominator of this rational + pub fn map(self, mut map: impl FnMut(T) -> U) -> Rational { + Rational(map(self.0), map(self.1)) } } -impl Polynom { - pub fn eval(&self, x: T) -> T { - self.0.iter().copied().enumerate().fold(T::zero(), |acc, (i, s)| acc + s * x.powi(i as _)) +impl> Rational { + /// Evaluate the fraction and return a single output. + pub fn eval(self) -> T::Output { + self.0 / self.1 } } -impl ops::Index for Polynom { - type Output = T; - - fn index(&self, index: usize) -> &Self::Output { - &self.0[index] - } +/// Factored transfer function rational, stored as poles and zeros. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct TransferFunction { + zeros: Vec, + poles: Vec, } -impl ops::IndexMut for Polynom { - fn index_mut(&mut self, index: usize) -> &mut Self::Output { - &mut self.0[index] +impl TransferFunction { + /// Map the zeros and poles of the transfer function into another value (possibly of another type) + pub fn map(self, mut map: impl FnMut(T) -> U) -> TransferFunction { + TransferFunction { + zeros: self.zeros.into_iter().map(&mut map).collect(), + poles: self.poles.into_iter().map(map).collect(), + } } } -impl ops::Neg for Polynom { - type Output = Polynom; - - fn neg(mut self) -> Self::Output { - Polynom(self.0.into_iter().map(T::neg).collect()) +impl TransferFunction { + pub fn eval(&self, x: T) -> T { + let num = self + .zeros + .iter() + .copied() + .map(|z| x - z) + .fold(T::one(), ops::Mul::mul); + let den = self + .poles + .iter() + .copied() + .map(|p| x - p) + .fold(T::one(), ops::Mul::mul); + num / den } } -impl> ops::Add for Polynom { - type Output = Polynom; - - fn add(self, rhs: Self) -> Self::Output { - Polynom(self.0.iter().copied().zip(rhs.0.iter().copied()).map(|(a, b)| a + b).collect()) +impl + ops::Neg> + TransferFunction +{ + /// Expand the transfer function into a fraction of two polynomials + pub fn as_polynom_rational(&self) -> Rational> { + Rational( + Polynom::from_roots(self.zeros.iter().copied()), + Polynom::from_roots(self.poles.iter().copied()), + ) } } -impl> ops::Sub for Polynom { - type Output = Polynom; - - fn sub(self, rhs: Self) -> Self::Output { - Polynom(self.0.iter().copied().zip(rhs.0.iter().copied()).map(|(a, b)| a - b).collect()) +impl TransferFunction> +where + T::SimdBool: SimdValue, +{ + /// Perform a bilinear transform from analog to digital, using Tustin's method. + pub fn bilinear_transform(self, samplerate: T) -> Self { + let mut res = self.map(|x| bilinear_transform(samplerate, x)); + let final_degree = res.poles.len().max(res.zeros.len()); + let to_add = final_degree - res.zeros.len(); + res.zeros + .extend(std::iter::repeat(-Complex::one()).take(to_add)); + res + } + + /// Checks whether the transfer function is BIBO-stable, assuming this instance is an analog transfer function + pub fn is_analog_stable(&self) -> T::SimdBool { + self.poles + .iter() + .map(|p| p.im.is_simd_negative()) + .fold(T::SimdBool::splat(true), ops::BitAnd::bitand) + } + + /// Checks whether the transfer function is BIBO-stable, assuming this instance is a digital transfer function. + pub fn is_digital_stable(&self) -> T::SimdBool + where + T::SimdBool: fmt::Debug, + { + self.poles + .iter() + .map(|p| dbg!(p.norm_sqr()).simd_le(T::one())) + .fold(T::SimdBool::splat(true), ops::BitAnd::bitand) } } -impl> iter::Sum> for Polynom { - fn sum>>(iter: I) -> Self { - iter.fold(Self::zero(), ops::Add::add) - } +/// Creates a [`Biquad`] instance matching the given expanded digital transfer function. +/// Only biquadratic transfer functions (degree <= 2) can be passed into this function. Use the +/// [`cascaded_biquad_sections`] function to create a series of cascaded biquads instead. +pub fn biquad(transfer_function: Rational>) -> Biquad { + assert!(transfer_function.0.degree() <= 2); + assert!(transfer_function.1.degree() <= 2); + + let a0 = transfer_function.1.get(2); + Biquad::new( + std::array::from_fn(|i| transfer_function.0.get(2 - i)), + std::array::from_fn(|i| transfer_function.1.get(1 - i) / a0), + ) } -impl + ops::Mul> ops::Mul for Polynom where Self: One { - type Output = Self; - - fn mul(self, rhs: Self) -> Self::Output { - if self.is_zero() || rhs.is_zero() { - return Self::zero(); - } - if self.is_one() { - return rhs; - } - if rhs.is_one() { - return self; - } - - let mut out = vec![T::zero(); self.degree() + rhs.degree() - 1]; - convolve(&mut out, &self.0[..self.degree()], &rhs.0[..rhs.degree()]); - Self(out) - } +/// Perform the bilinear transform over a single complex number, using Tustin's method. +pub fn bilinear_transform(samplerate: T, s: Complex) -> Complex { + let samplerate = Complex::from(samplerate); + let num = Complex::::one() + s / samplerate; + let den = Complex::::one() - s / samplerate; + num / den } -impl Zero for Polynom where Self: ops::Add { - fn zero() -> Self { - Self(vec![]) - } - - fn is_zero(&self) -> bool { - self.0.iter().all(|s| s.is_zero()) +/// Compute the transfer function of Nth order Butterworth filter. +pub fn butterworth(order: usize, fc: T) -> TransferFunction> +where + Complex: SimdComplexField, +{ + let from_theta = |theta: T| Complex::simd_exp(-Complex::i() * theta.into()); + let poles = Vec::from_iter( + (0..order) + .map(|k| (2.0 * k as f64 + 1.0) * PI / (2.0 * order as f64)) + .map(|theta| from_theta(T::from_f64(theta))) + .map(|pak| pak * T::simd_two_pi().into() * fc.into()), + ); + + TransferFunction { + zeros: vec![], + poles, } } -impl One for Polynom where Self: ops::Mul { - fn one() -> Self { - Self(vec![T::one()]) - } +/// Transform a factored transfer function into a set of biquadratic transfer functions recreating +/// the original one when applied in series. +/// The order of the input transfer function can be either even or odd; if it is odd, the last biquad +/// will only be a 1-st order filter. +pub fn into_biquadratic(transfer_function: TransferFunction) -> Vec> { + let mut zeros2 = transfer_function.zeros.into_iter().array_chunks::<2>(); + let mut poles2 = transfer_function.poles.into_iter().array_chunks::<2>(); + let mut res = + Vec::from_iter( + zeros2 + .by_ref() + .zip(poles2.by_ref()) + .map(|(z, p)| TransferFunction { + zeros: z.into(), + poles: p.into(), + }), + ); + + match (zeros2.into_remainder(), poles2.into_remainder()) { + (None, None) => {} + (Some(z), None) => res.push(TransferFunction { + zeros: z.collect(), + poles: vec![], + }), + (None, Some(p)) => res.push(TransferFunction { + zeros: vec![], + poles: p.collect(), + }), + (Some(z), Some(p)) => res.push(TransferFunction { + zeros: z.collect(), + poles: p.collect(), + }), + } + res } -impl> Polynom where Self: One + ops::Mul { - pub fn from_roots(roots: &[T]) -> Self { - roots.iter().copied().map(|r| Self::polyline(-r, T::one())).fold(Self::one(), |acc, p| acc * p) - } +/// Instanciates a set of biquads in series which implement the Nth order transfer function given as argument. +pub fn cascaded_biquad_sections( + transfer_function: TransferFunction>, +) -> Series>> { + let v = into_biquadratic(transfer_function) + .into_iter() + .map(|tf| tf.as_polynom_rational().map(|p| p.map(|x| x.re))) + .map(biquad) + .collect(); + Series(v) } -// Naive convolution algorithm, suitable to the relatively small arrays ofr polynomial coefficients here -// See the Karatsuba convolution algorithm for a non-FFT algorithm that is better than O(n^2) (but worse than linear time) -fn convolve + ops::Mul>(output: &mut [T], a: &[T], b: &[T]) { - let (a, b) = if a.len() < b.len() { (a, b) } else { (b, a) }; - - output.fill(T::zero()); - for k in 0..a.len() { - for i in 0..b.len() { - output[k+i] += a[k] * b[i]; - } - } +/// Computes a filter that implements the given Nth order Butterworth filter as a series of cascaded +/// Biquad filters. +pub fn biquad_butterworth(order: usize, fc: T) -> Series>> +where + Complex: SimdComplexField, +{ + cascaded_biquad_sections(butterworth(order, fc)) } #[cfg(test)] mod tests { - use rstest::rstest; use super::*; + use std::f64::consts::TAU; - #[rstest] - #[case(&[3.0, 8.0, 14.0, 8.0, 3.0], &[1.0, 2.0, 3.0], &[3.0, 2.0, 1.0])] - fn test_convolution(#[case] expected: &[f32], #[case] a: &[f32], #[case] b: &[f32]) { - let mut actual = expected.to_vec(); - actual.fill(0.0); - - convolve(&mut actual, a, b); - - assert_eq!(expected, &*actual); + #[test] + fn test_butterworth_analog() { + let butter = butterworth(2, 0.25f64); + assert!(butter.is_analog_stable()); + insta::assert_debug_snapshot!(butter); } - #[rstest] - #[case(Polynom(vec![1.0, 2.0, 1.0]), vec![-1.0, -1.0])] - fn test_polynom_from_roots(#[case] expected: Polynom, #[case] roots: Vec) { - let actual = Polynom::from_roots(&roots); - assert_eq!(expected, actual); + #[test] + fn test_butterworth_digital() { + let butter = dbg!(dbg!(butterworth(2, 0.25f64)).bilinear_transform(TAU)); + assert!(butter.is_digital_stable()); + insta::assert_debug_snapshot!(butter); } -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index 3bf504f..66dfc15 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +#![cfg_attr(feature = "biquad-design", feature(iter_array_chunks))] #![cfg_attr(feature = "fundsp", feature(generic_const_exprs))] #![doc = include_str!("./README.md")] use az::CastFrom; @@ -14,6 +15,7 @@ pub mod filters; pub mod fir; pub mod math; pub mod oscillators; +#[cfg(feature = "oversample")] pub mod oversample; pub mod saturators; pub mod util; diff --git a/src/math/mod.rs b/src/math/mod.rs index f1a4bdc..b68a160 100644 --- a/src/math/mod.rs +++ b/src/math/mod.rs @@ -9,6 +9,8 @@ use crate::Scalar; pub mod interpolation; pub mod lut; +#[cfg(feature = "math-polynom")] +pub mod polynom; /// Trait desciring a multivariate root equation. Root equations are solved with numerical methods such as /// Newton-Rhapson, when linear algebra cannot be used (e.g. in the case of nonlinear systems). diff --git a/src/math/polynom.rs b/src/math/polynom.rs new file mode 100644 index 0000000..5f3d9d6 --- /dev/null +++ b/src/math/polynom.rs @@ -0,0 +1,245 @@ +use std::{iter, ops}; + +use num_traits::real::Real; +use num_traits::{Num, One, Zero}; + +#[derive(Debug, Clone)] +pub struct Polynom(pub Vec); + +impl FromIterator for Polynom { + fn from_iter>(iter: It) -> Self { + Self(Vec::from_iter(iter)) + } +} + +impl Polynom { + pub fn polyline(offset: T, scale: T) -> Self { + Self(vec![offset, scale]) + } + + pub fn map(self, map: impl FnMut(T) -> U) -> Polynom { + Polynom(self.0.into_iter().map(map).collect()) + } +} + +impl PartialEq for Polynom { + fn eq(&self, other: &Self) -> bool { + self.degree() == other.degree() && self.0[..self.degree()] == other.0[..other.degree()] + } +} + +impl Eq for Polynom {} + +impl Polynom { + pub fn degree(&self) -> usize { + self.0.iter().rposition(|s| !s.is_zero()).unwrap_or(0) + } + + pub fn canonicalize_in_place(&mut self) { + for _ in self.0.drain(self.degree() + 1..) {} + } + + pub fn canonicalize(mut self) -> Self { + self.canonicalize_in_place(); + self + } + + pub fn get(&self, rank: usize) -> T + where + T: Copy, + { + if rank <= self.degree() { + self.0[rank] + } else { + T::zero() + } + } +} + +impl Polynom { + pub fn eval(&self, x: T) -> T { + self.0 + .iter() + .copied() + .enumerate() + .fold(T::zero(), |acc, (i, s)| acc + s * x.powi(i as _)) + } +} + +impl ops::Index for Polynom { + type Output = T; + + fn index(&self, index: usize) -> &Self::Output { + &self.0[index] + } +} + +impl ops::IndexMut for Polynom { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + &mut self.0[index] + } +} + +impl ops::Neg for Polynom { + type Output = Polynom; + + fn neg(self) -> Self::Output { + Polynom(self.0.into_iter().map(T::neg).collect()) + } +} + +impl> ops::Add for Polynom { + type Output = Polynom; + + fn add(self, rhs: Self) -> Self::Output { + Polynom( + self.0 + .iter() + .copied() + .zip(rhs.0.iter().copied()) + .map(|(a, b)| a + b) + .collect(), + ) + } +} + +impl> ops::Sub for Polynom { + type Output = Polynom; + + fn sub(self, rhs: Self) -> Self::Output { + Polynom( + self.0 + .iter() + .copied() + .zip(rhs.0.iter().copied()) + .map(|(a, b)| a - b) + .collect(), + ) + } +} + +impl> iter::Sum> for Polynom { + fn sum>>(iter: I) -> Self { + iter.fold(Self::zero(), ops::Add::add) + } +} + +impl + ops::Mul> ops::Mul + for Polynom +{ + type Output = Self; + + fn mul(self, rhs: Self) -> Self::Output { + if self.is_zero() || rhs.is_zero() { + return Self::zero(); + } + if self.is_one() { + return rhs; + } + if rhs.is_one() { + return self; + } + + let a = &self.0[..=self.degree()]; + let b = &rhs.0[..=rhs.degree()]; + let mut out = vec![T::zero(); a.len() + b.len() - 1]; + convolve(&mut out, a, b); + Self(out) + } +} + +impl Zero for Polynom +where + Self: ops::Add, +{ + fn zero() -> Self { + Self(vec![]) + } + + fn is_zero(&self) -> bool { + self.0.iter().all(|s| s.is_zero()) + } +} + +impl One for Polynom +where + Self: ops::Mul, +{ + fn one() -> Self { + Self(vec![T::one()]) + } +} + +impl> Polynom +where + Self: One, +{ + pub fn from_roots(roots: impl IntoIterator) -> Self { + roots + .into_iter() + .map(|r| Self::polyline(-r, T::one())) + .fold(Self::one(), ops::Mul::mul) + } +} + +// Naive convolution algorithm, suitable to the relatively small arrays ofr polynomial coefficients here +// See the Karatsuba convolution algorithm for a non-FFT algorithm that is better than O(n^2) (but worse than linear time) +fn convolve + ops::Mul>( + output: &mut [T], + a: &[T], + b: &[T], +) { + let (a, b) = if a.len() < b.len() { (a, b) } else { (b, a) }; + + output.fill(T::zero()); + for k in 0..a.len() { + for i in 0..b.len() { + output[k + i] += a[k] * b[i]; + } + } +} + +#[cfg(test)] +mod tests { + use num_traits::{One, Zero}; + use rstest::rstest; + + use crate::math::polynom::{convolve, Polynom}; + + #[rstest] + #[case(0, Polynom::zero())] + #[case(0, Polynom::one())] + #[case(2, Polynom(vec ! [0.0, 1.0, 2.0, 0.0]))] + fn test_degree(#[case] expected: usize, #[case] p: Polynom) { + let actual = p.degree(); + assert_eq!(expected, actual); + } + + #[rstest] + #[case(& [3.0, 8.0, 14.0, 8.0, 3.0], & [1.0, 2.0, 3.0], & [3.0, 2.0, 1.0])] + fn test_convolution(#[case] expected: &[f32], #[case] a: &[f32], #[case] b: &[f32]) { + let mut actual = expected.to_vec(); + actual.fill(0.0); + + convolve(&mut actual, a, b); + + assert_eq!(expected, &*actual); + } + + #[rstest] + #[case(Polynom::zero(), Polynom::one(), Polynom::zero())] + #[case(Polynom(vec ! [1.0, 2.0]), Polynom(vec ! [1.0, 2.0]), Polynom::one())] + #[case(Polynom(vec ! [1.0, 2.0]), Polynom::one(), Polynom(vec ! [1.0, 2.0]))] + #[case(Polynom(vec ! [- 1.0, 1.0, 2.0]), Polynom(vec ! [1.0, 1.0]), Polynom(vec ! [- 1.0, 2.0]))] + fn test_mul(#[case] expected: Polynom, #[case] a: Polynom, #[case] b: Polynom) { + let actual = a * b; + assert_eq!(expected, actual); + } + + #[rstest] + #[case(Polynom(vec ! [1.0, 2.0, 1.0]), vec ! [- 1.0, - 1.0])] + fn test_polynom_from_roots(#[case] expected: Polynom, #[case] roots: Vec) { + let actual = Polynom::from_roots(roots); + assert_eq!(expected, actual); + } +} diff --git a/src/oversample/mod.rs b/src/oversample/mod.rs index 8c83dd7..edda47c 100644 --- a/src/oversample/mod.rs +++ b/src/oversample/mod.rs @@ -1,3 +1,5 @@ +use nalgebra::Complex; +use simba::simd::SimdComplexField; use std::ops::{Deref, DerefMut}; use crate::dsp::parameter::{HasParameters, Parameter}; @@ -5,6 +7,7 @@ use crate::dsp::{ utils::{mono_block_to_slice, mono_block_to_slice_mut, slice_to_mono_block_mut}, DSP, }; +use crate::filters::biquad::design::biquad_butterworth; use crate::saturators::Linear; use crate::Scalar; use crate::{ @@ -12,31 +15,28 @@ use crate::{ filters::biquad::Biquad, }; -const CASCADE: usize = 5; - #[derive(Debug, Clone)] pub struct Oversample { os_factor: usize, os_buffer: Box<[T]>, - pre_filter: Series<[Biquad; CASCADE]>, - post_filter: Series<[Biquad; CASCADE]>, + pre_filter: Series>>, + post_filter: Series>>, } impl Oversample { - pub fn new(os_factor: usize, max_block_size: usize) -> Self { + pub fn new(os_factor: usize, max_block_size: usize) -> Self + where + Complex: SimdComplexField, + { assert!(os_factor > 1); let os_buffer = vec![T::zero(); max_block_size * os_factor].into_boxed_slice(); - let fc_raw = f64::recip(2.0 * os_factor as f64); - let cascade_adjustment = f64::sqrt(2f64.powf(1.0 / CASCADE as f64) - 1.0).recip(); - let fc_corr = fc_raw * cascade_adjustment; - println!("cascade adjustment {cascade_adjustment}: {fc_raw} -> {fc_corr}"); - let filters = - std::array::from_fn(|_| Biquad::lowpass(T::from_f64(fc_raw), T::from_f64(0.707))); + let fc = f64::recip(2.0 * os_factor as f64); + let filters = biquad_butterworth(6, T::from_f64(fc)); Self { os_factor, os_buffer, - pre_filter: Series(filters), - post_filter: Series(filters), + pre_filter: filters.clone(), + post_filter: filters, } } From ac2b01a592876fb7b2f4fddbea78510201315290 Mon Sep 17 00:00:00 2001 From: Nathan Graule Date: Thu, 22 Feb 2024 09:29:48 +0100 Subject: [PATCH 10/21] fix(filters): readd biquad/mod.rs that went missing --- src/filters/biquad/mod.rs | 271 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 271 insertions(+) create mode 100644 src/filters/biquad/mod.rs diff --git a/src/filters/biquad/mod.rs b/src/filters/biquad/mod.rs new file mode 100644 index 0000000..813de29 --- /dev/null +++ b/src/filters/biquad/mod.rs @@ -0,0 +1,271 @@ +//! Transposed Direct Form II Biquad implementation - nonlinearities based on https://jatinchowdhury18.medium.com/complex-nonlinearities-episode-5-nonlinear-feedback-filters-115e65fc0402 + +use nalgebra::Complex; +use numeric_literals::replace_float_literals; + +use crate::dsp::analysis::DspAnalysis; +use crate::dsp::DSP; +use crate::{ + saturators::{Dynamic, Saturator}, + Scalar, +}; + +pub mod design; + +/// Biquad struct in Transposed Direct Form II. Optionally, a [`Saturator`](crate::saturators::Saturator) instance can be used +/// to apply waveshaping to the internal states. +#[derive(Debug, Copy, Clone)] +pub struct Biquad { + na: [T; 2], + b: [T; 3], + s: [T; 2], + sats: [S; 2], +} + +impl Biquad> { + /// Apply these new saturators to this Biquad instance, returning a new instance of it. + pub fn with_saturators(mut self, a: Dynamic, b: Dynamic) -> Biquad> { + self.set_saturators(a, b); + self + } + + /// Replace the saturators in this Biquad instance with the provided values. + pub fn set_saturators(&mut self, a: Dynamic, b: Dynamic) { + self.sats = [a, b]; + } +} + +impl Biquad { + pub fn update_coefficients(&mut self, other: &Self) { + self.na = other.na; + self.b = other.b; + } +} + +impl Biquad { + /// Create a new instance of a Biquad with the provided poles and zeros coefficients. + pub fn new(b: [T; 3], a: [T; 2]) -> Self { + Self { + na: a.map(T::neg), + b, + s: [T::zero(); 2], + sats: Default::default(), + } + } + + /// Create a lowpass with the provided frequency cutoff coefficient (normalized where 1 == samplerate) and resonance factor. + #[replace_float_literals(T::from_f64(literal))] + pub fn lowpass(fc: T, q: T) -> Self { + let w0 = T::simd_two_pi() * fc; + let (sw0, cw0) = w0.simd_sin_cos(); + let b1 = 1. - cw0; + let b0 = b1 / 2.; + let b2 = b0; + + let alpha = sw0 / (2. * q); + let a0 = 1. + alpha; + let a1 = -2. * cw0; + let a2 = 1. - alpha; + + Self::new([b0, b1, b2].map(|b| b / a0), [a1, a2].map(|a| a / a0)) + } + + /// Create a highpass with the provided frequency cutoff coefficient (normalized where 1 == samplerate) and resonance factor. + #[replace_float_literals(T::from_f64(literal))] + pub fn highpass(fc: T, q: T) -> Self { + let w0 = T::simd_two_pi() * fc; + let (sw0, cw0) = w0.simd_sin_cos(); + let b1 = -(1. + cw0); + let b0 = -b1 / 2.; + let b2 = b0; + + let alpha = sw0 / (2. * q); + let a0 = 1. + alpha; + let a1 = -2. * cw0; + let a2 = 1. - alpha; + + Self::new([b0, b1, b2].map(|b| b / a0), [a1, a2].map(|a| a / a0)) + } + + /// Create a bandpass with the provided frequency cutoff coefficient (normalized where 1 == samplerate) and resonance factor. + /// The resulting bandpass is normalized so that the maximum of the transfer function sits at 0 dB, making it + /// appear as having a sharper slope than it actually does. + #[replace_float_literals(T::from_f64(literal))] + pub fn bandpass_peak0(fc: T, q: T) -> Self { + let w0 = T::simd_two_pi() * fc; + let (sw0, cw0) = w0.simd_sin_cos(); + let alpha = sw0 / (2. * q); + + let b0 = alpha; + let b1 = 0.; + let b2 = -alpha; + + let a0 = 1. + alpha; + let a1 = -2. * cw0; + let a2 = 1. - alpha; + + Self::new([b0, b1, b2].map(|b| b / a0), [a1, a2].map(|a| a / a0)) + } + + /// Create a notch with the provided frequency cutoff coefficient (normalized where 1 == samplerate) and resonance factor. + #[replace_float_literals(T::from_f64(literal))] + pub fn notch(fc: T, q: T) -> Self { + let w0 = T::simd_two_pi() * fc; + let (sw0, cw0) = w0.simd_sin_cos(); + let alpha = sw0 / (2. * q); + + let b0 = 1.; + let b1 = -2. * cw0; + let b2 = 1.; + + let a0 = 1. + alpha; + let a1 = -2. * cw0; + let a2 = 1. - alpha; + + Self::new([b0, b1, b2].map(|b| b / a0), [a1, a2].map(|a| a / a0)) + } + + /// Create an allpass with the provided frequency cutoff coefficient (normalized where 1 == samplerate) and resonance factor. + #[replace_float_literals(T::from_f64(literal))] + pub fn allpass(fc: T, q: T) -> Self { + let w0 = T::simd_two_pi() * fc; + let (sw0, cw0) = w0.simd_sin_cos(); + let alpha = sw0 / (2. * q); + + let b0 = 1. - alpha; + let b1 = -2. * cw0; + let b2 = 1. + alpha; + + let a0 = b2; + let a1 = b1; + let a2 = b0; + + Self::new([b0, b1, b2].map(|b| b / a0), [a1, a2].map(|a| a / a0)) + } + + /// Create a peaking filter with the provided frequency cutoff coefficient (normalized where 1 == samplerate) and resonance factor. + #[replace_float_literals(T::from_f64(literal))] + pub fn peaking(fc: T, q: T, amp: T) -> Self { + let w0 = T::simd_two_pi() * fc; + let (sw0, cw0) = w0.simd_sin_cos(); + let alpha = sw0 / (2. * q); + + let b0 = 1. + alpha * amp; + let b1 = -2. * cw0; + let b2 = 1. - alpha * amp; + + let a0 = 1. + alpha / amp; + let a1 = b1; + let a2 = 1. - alpha / amp; + + Self::new([b0, b1, b2].map(|b| b / a0), [a1, a2].map(|a| a / a0)) + } + + /// Create a low shelf with the provided frequency cutoff coefficient (normalized where 1 == samplerate) and resonance factor. + #[replace_float_literals(T::from_f64(literal))] + pub fn lowshelf(fc: T, q: T, amp: T) -> Self { + let w0 = T::simd_two_pi() * fc; + let (sw0, cw0) = w0.simd_sin_cos(); + let alpha = sw0 / (2. * q); + + let t = (amp + 1.) - (amp - 1.) * cw0; + let tp = (amp - 1.) - (amp + 1.) * cw0; + let u = 2. * amp.simd_sqrt() * alpha; + + let b0 = amp * (t + u); + let b1 = 2. * amp * (tp); + let b2 = amp * (t - u); + + let t = (amp + 1.) + (amp - 1.) * cw0; + let a0 = t + u; + let a1 = -2. * ((amp - 1.) + (amp + 1.) * cw0); + let a2 = t - u; + + Self::new([b0, b1, b2].map(|b| b / a0), [a1, a2].map(|a| a / a0)) + } + + /// Create a high shelf with the provided frequency cutoff coefficient (normalized where 1 == samplerate) and resonance factor. + #[replace_float_literals(T::from_f64(literal))] + pub fn highshelf(fc: T, q: T, amp: T) -> Self { + let w0 = T::simd_two_pi() * fc; + let (sw0, cw0) = w0.simd_sin_cos(); + let alpha = sw0 / (2. * q); + + let b0 = amp * ((amp + 1.) + (amp - 1.) * cw0 + 2. * amp.simd_sqrt() * alpha); + let b1 = -2. * amp * ((amp + 1.) + (amp - 1.) * cw0); + let b2 = amp * ((amp + 1.) + (amp - 1.) * cw0 - (2. * amp.simd_sqrt() * alpha)); + + let a0 = (amp + 1.) - (amp - 1.) * cw0 + 2. * amp.simd_sqrt() * alpha; + let a1 = 2. * ((amp - 1.) - (amp + 1.) * cw0); + let a2 = ((amp + 1.) - (amp - 1.) * cw0) - 2. * amp.simd_sqrt() * alpha; + + Self::new([b0, b1, b2].map(|b| b / a0), [a1, a2].map(|a| a / a0)) + } + + pub fn reset(&mut self) { + self.s.fill(T::zero()); + } +} + +impl> DSP<1, 1> for Biquad { + type Sample = T; + + #[inline] + #[replace_float_literals(T::from_f64(literal))] + fn process(&mut self, x: [Self::Sample; 1]) -> [Self::Sample; 1] { + let x = x[0]; + let in0 = x * self.b[0] + self.s[0]; + let s_out: [_; 2] = std::array::from_fn(|i| self.sats[i].saturate(in0 / 10.)); + let in1 = x * self.b[1] + self.s[1] + self.sats[0].saturate(in0 / 10.) * 10. * self.na[0]; + let in2 = x * self.b[2] + self.sats[1].saturate(in0 / 10.) * 10. * self.na[1]; + self.s = [in1, in2]; + + for (s, y) in self.sats.iter_mut().zip(s_out.into_iter()) { + s.update_state(in0 / 10., y); + } + [in0] + } +} + +impl DspAnalysis<1, 1> for Biquad +where + Self: DSP<1, 1, Sample = T>, +{ + fn h_z(&self, z: Complex) -> [[Complex; 1]; 1] { + let num = z.powi(-1).scale(self.b[1]) + z.powi(-2).scale(self.b[2]) + self.b[0]; + let den = z.powi(-1).scale(-self.na[0]) + z.powi(-2).scale(-self.na[1]) + T::one(); + [[num / den]] + } +} + +#[cfg(test)] +mod tests { + use crate::{ + dsp::{ + utils::{slice_to_mono_block, slice_to_mono_block_mut}, + DSPBlock, + }, + saturators::clippers::DiodeClipperModel, + }; + + use super::*; + + #[test] + fn test_lp_diode_clipper() { + let samplerate = 1000.0; + let sat = DiodeClipperModel::new_led(2, 3); + let mut biquad = Biquad::lowpass(10.0 / samplerate, 20.0) + .with_saturators(Dynamic::DiodeClipper(sat), Dynamic::DiodeClipper(sat)); + + let input: [_; 512] = + std::array::from_fn(|i| i as f64 / samplerate).map(|t| (10.0 * t).fract() * 2.0 - 1.0); + let mut output = [0.0; 512]; + biquad.process_block( + slice_to_mono_block(&input), + slice_to_mono_block_mut(&mut output), + ); + + insta::assert_csv_snapshot!(&output as &[_], { "[]" => insta::rounded_redaction(4) }); + } +} + From 8c6c8e69345f61357859ebbd246729b2a8e63081 Mon Sep 17 00:00:00 2001 From: Nathan Graule Date: Thu, 22 Feb 2024 10:17:13 +0100 Subject: [PATCH 11/21] test(filters): move existing biquad snapshots to new folder + add analog design snapshot --- ...uad__design__tests__butterworth_analog.snap | 18 ++++++++++++++++++ ...lters__biquad__tests__lp_diode_clipper.snap | 0 2 files changed, 18 insertions(+) create mode 100644 src/filters/biquad/snapshots/valib__filters__biquad__design__tests__butterworth_analog.snap rename src/filters/{ => biquad}/snapshots/valib__filters__biquad__tests__lp_diode_clipper.snap (100%) diff --git a/src/filters/biquad/snapshots/valib__filters__biquad__design__tests__butterworth_analog.snap b/src/filters/biquad/snapshots/valib__filters__biquad__design__tests__butterworth_analog.snap new file mode 100644 index 0000000..979136a --- /dev/null +++ b/src/filters/biquad/snapshots/valib__filters__biquad__design__tests__butterworth_analog.snap @@ -0,0 +1,18 @@ +--- +source: src/filters/biquad/design.rs +assertion_line: 221 +expression: butter +--- +TransferFunction { + zeros: [], + poles: [ + Complex { + re: 1.1107207345395915, + im: -1.1107207345395915, + }, + Complex { + re: -1.1107207345395915, + im: -1.1107207345395915, + }, + ], +} diff --git a/src/filters/snapshots/valib__filters__biquad__tests__lp_diode_clipper.snap b/src/filters/biquad/snapshots/valib__filters__biquad__tests__lp_diode_clipper.snap similarity index 100% rename from src/filters/snapshots/valib__filters__biquad__tests__lp_diode_clipper.snap rename to src/filters/biquad/snapshots/valib__filters__biquad__tests__lp_diode_clipper.snap From c82dcbbfe3c20ee594506e1274f0c961b0d4af19 Mon Sep 17 00:00:00 2001 From: Nathan Graule Date: Fri, 23 Feb 2024 18:06:42 +0100 Subject: [PATCH 12/21] Revert using oversampling with butterworth filters --- src/oversample/mod.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/oversample/mod.rs b/src/oversample/mod.rs index edda47c..4ab6571 100644 --- a/src/oversample/mod.rs +++ b/src/oversample/mod.rs @@ -1,13 +1,13 @@ +use std::ops::{Deref, DerefMut}; + use nalgebra::Complex; use simba::simd::SimdComplexField; -use std::ops::{Deref, DerefMut}; use crate::dsp::parameter::{HasParameters, Parameter}; use crate::dsp::{ utils::{mono_block_to_slice, mono_block_to_slice_mut, slice_to_mono_block_mut}, DSP, }; -use crate::filters::biquad::design::biquad_butterworth; use crate::saturators::Linear; use crate::Scalar; use crate::{ @@ -15,12 +15,14 @@ use crate::{ filters::biquad::Biquad, }; +const CASCADE: usize = 16; + #[derive(Debug, Clone)] pub struct Oversample { os_factor: usize, os_buffer: Box<[T]>, - pre_filter: Series>>, - post_filter: Series>>, + pre_filter: Series<[Biquad; CASCADE]>, + post_filter: Series<[Biquad; CASCADE]>, } impl Oversample { @@ -31,7 +33,11 @@ impl Oversample { assert!(os_factor > 1); let os_buffer = vec![T::zero(); max_block_size * os_factor].into_boxed_slice(); let fc = f64::recip(2.0 * os_factor as f64); - let filters = biquad_butterworth(6, T::from_f64(fc)); + let filter = Biquad::lowpass( + T::from_f64(fc), + T::from_f64(std::f64::consts::FRAC_1_SQRT_2), + ); + let filters = Series([filter; CASCADE]); Self { os_factor, os_buffer, From bfc99f47bfd7214da82544c52b3345a670ec3fea Mon Sep 17 00:00:00 2001 From: Nathan Graule Date: Fri, 23 Feb 2024 18:07:41 +0100 Subject: [PATCH 13/21] feat(nih-plug): provide AnyParam to allow bool/int/float param types to be used --- src/contrib/nih_plug.rs | 85 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 78 insertions(+), 7 deletions(-) diff --git a/src/contrib/nih_plug.rs b/src/contrib/nih_plug.rs index 29ede21..f49fbc3 100644 --- a/src/contrib/nih_plug.rs +++ b/src/contrib/nih_plug.rs @@ -1,5 +1,7 @@ #![cfg(feature = "nih-plug")] +use std::fmt; +use std::fmt::Formatter; use std::sync::Arc; use enum_map::{Enum, EnumArray, EnumMap}; @@ -26,6 +28,13 @@ impl BindToParameter for FloatParam { } } +impl BindToParameter for IntParam { + fn bind_to_parameter(self, param: &Parameter) -> Self { + let param = param.clone(); + self.with_callback(Arc::new(move |x| param.set_value(x as _))) + } +} + impl BindToParameter for BoolParam { fn bind_to_parameter(self, param: &Parameter) -> Self { let param = param.clone(); @@ -42,20 +51,83 @@ impl BindToParame } } +#[derive(Debug)] +pub enum AnyParam { + FloatParam(FloatParam), + IntParam(IntParam), + BoolParam(BoolParam), +} + +impl fmt::Display for AnyParam { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + Self::FloatParam(fp) => write!(f, "{}", fp), + Self::IntParam(fp) => write!(f, "{}", fp), + Self::BoolParam(fp) => write!(f, "{}", fp), + } + } +} + +impl From for AnyParam { + fn from(value: FloatParam) -> Self { + Self::FloatParam(value) + } +} + +impl From for AnyParam { + fn from(value: IntParam) -> Self { + Self::IntParam(value) + } +} + +impl From for AnyParam { + fn from(value: BoolParam) -> Self { + Self::BoolParam(value) + } +} + +impl BindToParameter for AnyParam { + fn bind_to_parameter(self, param: &Parameter) -> Self { + match self { + Self::FloatParam(fp) => Self::FloatParam(fp.bind_to_parameter(param)), + Self::IntParam(ip) => Self::IntParam(ip.bind_to_parameter(param)), + Self::BoolParam(bp) => Self::BoolParam(bp.bind_to_parameter(param)), + } + } +} + +impl AnyParam { + pub fn as_ptr(&self) -> ParamPtr { + match self { + Self::FloatParam(fp) => fp.as_ptr(), + Self::IntParam(fp) => fp.as_ptr(), + Self::BoolParam(fp) => fp.as_ptr(), + } + } + + pub fn name(&self) -> &str { + match self { + Self::FloatParam(fp) => fp.name(), + Self::IntParam(ip) => ip.name(), + Self::BoolParam(bp) => bp.name(), + } + } +} + /// Adapter struct for processors with parameters #[derive(Debug)] pub struct NihParamsController where - P::Enum: EnumArray + EnumArray, + P::Enum: EnumArray, { - nih_map: EnumMap, + nih_map: EnumMap, } impl NihParamsController

where - P::Enum: EnumArray + EnumArray, + P::Enum: EnumArray, { - pub fn new(inner: &P, param_map: impl Fn(P::Enum, String) -> FloatParam) -> Self { + pub fn new(inner: &P, param_map: impl Fn(P::Enum, String) -> AnyParam) -> Self { let nih_map = EnumMap::from_fn(|k| { param_map(k, inner.full_name(k)).bind_to_parameter(inner.get_parameter(k)) }); @@ -65,9 +137,8 @@ where unsafe impl Params for NihParamsController

where - P::Enum: EnumArray + EnumArray, - <

::Enum as enum_map::EnumArray>::Array: Send + Sync, - <

::Enum as enum_map::EnumArray>::Array: Send + Sync, + P::Enum: 'static + Send + Sync + EnumArray, + >::Array: Send + Sync, { fn param_map(&self) -> Vec<(String, ParamPtr, String)> { self.nih_map From 530fba23fbe5286140ccf947be7e8faa24b93e51 Mon Sep 17 00:00:00 2001 From: Nathan Graule Date: Fri, 23 Feb 2024 18:07:58 +0100 Subject: [PATCH 14/21] feat(nih-plug): use AnyParam in diode clipper example --- examples/diodeclipper/src/lib.rs | 33 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/examples/diodeclipper/src/lib.rs b/examples/diodeclipper/src/lib.rs index 5486b30..d95bf59 100644 --- a/examples/diodeclipper/src/lib.rs +++ b/examples/diodeclipper/src/lib.rs @@ -39,39 +39,34 @@ impl Default for ClipperPlugin { ) .with_unit(" dB") .with_value_to_string(formatters::v2s_f32_gain_to_db(2)) - .with_string_to_value(formatters::s2v_f32_gain_to_db()), + .with_string_to_value(formatters::s2v_f32_gain_to_db()) + .into(), DspParams::ModelSwitch => { - FloatParam::new("Model", 0.0, FloatRange::Linear { min: 0.0, max: 1.0 }) - .with_step_size(1.0) + IntParam::new("Model", 0, IntRange::Linear { min: 0, max: 1 }) .with_value_to_string(Arc::new(|x| { - if x > 0.5 { - "Analytic".to_string() - } else { - "Newton-Rhapson".to_string() + match x { + 0 => "Newton-Rhapson", + 1 => "Model", + _ => "INVALID", } + .to_string() })) + .into() } DspParams::DiodeType => { - FloatParam::new("Diode type", 0.0, FloatRange::Linear { min: 0.0, max: 2.0 }) - .with_step_size(1.0) + IntParam::new("Diode type", 0, IntRange::Linear { min: 0, max: 2 }) .with_value_to_string(Arc::new(|x| { DiodeType::from_usize(nih_dbg!(x as _)).name() })) + .into() } DspParams::NumForward => { - FloatParam::new("# Forward", 1.0, FloatRange::Linear { min: 1.0, max: 5.0 }) - .with_step_size(1.0) + IntParam::new("# Forward", 1, IntRange::Linear { min: 1, max: 5 }).into() } DspParams::NumBackward => { - FloatParam::new("# Backward", 1.0, FloatRange::Linear { min: 1.0, max: 5.0 }) - .with_step_size(1.0) + IntParam::new("# Backward", 1, IntRange::Linear { min: 1, max: 5 }).into() } - DspParams::ForceReset => FloatParam::new( - "Force reset", - 0.0, - FloatRange::Linear { min: 0.0, max: 1.0 }, - ) - .with_step_size(1.0), + DspParams::ForceReset => BoolParam::new("Force reset", false).into(), }); Self { dsp, From 99fc3ff5c1c60d6a3b296cb66b9134552003c2cd Mon Sep 17 00:00:00 2001 From: Nathan Graule Date: Fri, 23 Feb 2024 21:00:39 +0100 Subject: [PATCH 15/21] feat(examples): use new nih-plug integration for dirty-biquad --- Cargo.lock | 1 + examples/dirty-biquad/Cargo.toml | 3 +- examples/dirty-biquad/src/dsp.rs | 169 +++++++++++++++++++ examples/dirty-biquad/src/lib.rs | 267 ++++++------------------------- src/contrib/nih_plug.rs | 12 ++ src/dsp/blocks.rs | 5 + src/dsp/parameter.rs | 9 +- src/saturators/mod.rs | 5 + 8 files changed, 252 insertions(+), 219 deletions(-) create mode 100644 examples/dirty-biquad/src/dsp.rs diff --git a/Cargo.lock b/Cargo.lock index 7abf129..7661567 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1221,6 +1221,7 @@ dependencies = [ name = "dirty-biquad" version = "0.1.0" dependencies = [ + "enum-map", "nih_plug", "valib", ] diff --git a/examples/dirty-biquad/Cargo.toml b/examples/dirty-biquad/Cargo.toml index 8cdf071..1654e43 100644 --- a/examples/dirty-biquad/Cargo.toml +++ b/examples/dirty-biquad/Cargo.toml @@ -13,5 +13,6 @@ keywords.workspace = true crate-type = ["cdylib"] [dependencies] +enum-map.workspace = true nih_plug = { workspace = true } -valib = { path = "../.." } \ No newline at end of file +valib = { path = "../..", features = ["nih-plug"] } \ No newline at end of file diff --git a/examples/dirty-biquad/src/dsp.rs b/examples/dirty-biquad/src/dsp.rs new file mode 100644 index 0000000..0212bfd --- /dev/null +++ b/examples/dirty-biquad/src/dsp.rs @@ -0,0 +1,169 @@ +use enum_map::Enum; +use std::fmt; +use std::fmt::Formatter; + +use crate::{MAX_BLOCK_SIZE, OVERSAMPLE}; +use valib::dsp::parameter::{HasParameters, Parameter, SmoothedParam}; +use valib::dsp::DSP; +use valib::filters::biquad::Biquad; +use valib::oversample::{Oversample, Oversampled}; +use valib::saturators::clippers::DiodeClipperModel; +use valib::saturators::Dynamic; +use valib::simd::{AutoF32x2, SimdComplexField, SimdValue}; + +pub type Sample = AutoF32x2; + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Enum)] +pub enum FilterType { + Lowpass, + Bandpass, + Highpass, + Notch, + Allpass, +} + +impl fmt::Display for FilterType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + Self::Lowpass => write!(f, "Low pass"), + Self::Bandpass => write!(f, "Band pass"), + Self::Highpass => write!(f, "High pass"), + Self::Notch => write!(f, "Notch"), + Self::Allpass => write!(f, "Allpass"), + } + } +} + +impl FilterType { + pub fn as_biquad(&self, fc: Sample, res: Sample) -> Biquad> { + match self { + Self::Lowpass => Biquad::lowpass(fc, res), + Self::Bandpass => Biquad::bandpass_peak0(fc, res), + Self::Highpass => Biquad::highpass(fc, res), + Self::Notch => Biquad::notch(fc, res), + Self::Allpass => Biquad::allpass(fc, res), + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Enum)] +pub enum SaturatorType { + Linear, + Tanh, + DiodeSym, + DiodeAssym, +} + +impl fmt::Display for SaturatorType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + Self::Linear => write!(f, "Linear"), + Self::Tanh => write!(f, "Tanh"), + Self::DiodeSym => write!(f, "Diode (symmetric)"), + Self::DiodeAssym => write!(f, "Diode (asymmetric)"), + } + } +} + +impl SaturatorType { + pub fn saturator(&self) -> Dynamic { + match self { + Self::Linear => Dynamic::Linear, + Self::Tanh => Dynamic::Tanh, + Self::DiodeSym => Dynamic::DiodeClipper(DiodeClipperModel::new_silicon(1, 1)), + Self::DiodeAssym => Dynamic::DiodeClipper(DiodeClipperModel::new_germanium(1, 2)), + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Enum)] +pub enum DspParameters { + Drive, + Cutoff, + Resonance, + FilterType, + SaturatorType, +} + +pub struct DspInner { + samplerate: f32, + drive: SmoothedParam, + fc: SmoothedParam, + resonance: SmoothedParam, + ftype: Parameter, + saturator: Parameter, + biquad: Biquad>, +} + +impl DspInner { + fn update_params(&mut self) { + let fc = Sample::splat(self.fc.next_sample() / self.samplerate); + let res = Sample::splat(self.resonance.next_sample()); + let biquad = self.ftype.get_enum::().as_biquad(fc, res); + self.biquad.update_coefficients(&biquad); + if self.saturator.has_changed() { + let nl = self.saturator.get_enum::().saturator(); + self.biquad.set_saturators(nl, nl); + } + } +} + +impl DSP<1, 1> for DspInner { + type Sample = Sample; + + fn process(&mut self, x: [Self::Sample; 1]) -> [Self::Sample; 1] { + self.update_params(); + let drive = Sample::splat(self.drive.next_sample()); + println!("Drive {}", drive.extract(0)); + let x = x.map(|x| x * drive); + self.biquad.process(x).map(|x| x / drive.simd_asinh()) + } + + fn set_samplerate(&mut self, samplerate: f32) { + self.samplerate = samplerate; + self.drive.set_samplerate(samplerate); + self.fc.set_samplerate(samplerate); + self.resonance.set_samplerate(samplerate); + self.biquad.set_samplerate(samplerate); + } + + fn latency(&self) -> usize { + self.biquad.latency() + } + + fn reset(&mut self) { + self.drive.reset(); + self.fc.reset(); + self.resonance.reset(); + self.biquad.reset(); + } +} + +impl HasParameters for DspInner { + type Enum = DspParameters; + + fn get_parameter(&self, param: Self::Enum) -> &Parameter { + match param { + DspParameters::Drive => &self.drive.param, + DspParameters::Cutoff => &self.fc.param, + DspParameters::Resonance => &self.resonance.param, + DspParameters::FilterType => &self.ftype, + DspParameters::SaturatorType => &self.saturator, + } + } +} + +pub type Dsp = Oversampled; + +pub fn create(samplerate: f32) -> Dsp { + let dsp = DspInner { + samplerate, + drive: Parameter::new(1.0).smoothed_exponential(samplerate, 50.0), + fc: Parameter::new(3000.0).smoothed_exponential(samplerate, 50.0), + resonance: Parameter::new(0.5).smoothed_linear(samplerate, 50.0), + ftype: Parameter::new(0.0), + saturator: Parameter::new(0.0), + biquad: Biquad::lowpass(Sample::splat(3000.0 / samplerate), Sample::splat(0.5)), + }; + Oversample::new(OVERSAMPLE, MAX_BLOCK_SIZE).with_dsp(samplerate, dsp) +} diff --git a/examples/dirty-biquad/src/lib.rs b/examples/dirty-biquad/src/lib.rs index a6e0310..20e38d8 100644 --- a/examples/dirty-biquad/src/lib.rs +++ b/examples/dirty-biquad/src/lib.rs @@ -1,193 +1,74 @@ use std::sync::Arc; use nih_plug::prelude::*; +use nih_plug::util::db_to_gain; -use valib::dsp::DSP; -use valib::filters::biquad::Biquad; -use valib::math::interpolation::{Cubic, Interpolate}; -use valib::oversample::Oversample; -use valib::saturators::clippers::DiodeClipperModel; -use valib::saturators::Dynamic; -use valib::simd::{AutoF32x2, AutoSimd, SimdValue}; -use valib::Scalar; +use valib::contrib::nih_plug::{enum_int_param, process_buffer_simd, NihParamsController}; +use valib::dsp::DSPBlock; +use valib::simd::AutoF32x2; + +use crate::dsp::{DspParameters, FilterType, SaturatorType}; + +mod dsp; const OVERSAMPLE: usize = 2; const MAX_BLOCK_SIZE: usize = 512; type Sample = AutoF32x2; -#[derive(Debug, Enum, Eq, PartialEq)] -enum FilterType { - Lowpass, - Bandpass, - Highpass, -} - -#[derive(Debug, Enum, Eq, PartialEq)] -enum NLType { - Linear, - Clipped, - Tanh, - Diode, -} - -impl NLType { - pub fn as_dynamic_saturator(&self) -> Dynamic { - match self { - Self::Linear => Dynamic::Linear, - Self::Clipped => Dynamic::HardClipper, - Self::Tanh => Dynamic::Tanh, - Self::Diode => Dynamic::DiodeClipper(DiodeClipperModel::new_germanium(2, 3)), - } - } -} - -#[derive(Debug, Params)] -struct PluginParams { - #[id = "fc"] - fc: FloatParam, - #[id = "q"] - q: FloatParam, - #[id = "drive"] - drive: FloatParam, - #[id = "type"] - filter_type: EnumParam, - #[id = "nl"] - nonlinearity: EnumParam, +struct Plugin { + params: Arc>, + dsp: dsp::Dsp, } -impl Default for PluginParams { +impl Default for Plugin { fn default() -> Self { - Self { - fc: FloatParam::new( - "Frequency", - 300., + let dsp = dsp::create(44100.0); + let params = NihParamsController::new(&dsp, |param, name| match param { + DspParameters::Drive => FloatParam::new( + "Drive", + 1.0, FloatRange::Skewed { - min: 20., + min: 1.0, + max: db_to_gain(40.0), + factor: FloatRange::gain_skew_factor(0.0, 40.0), + }, + ) + .with_unit(" dB") + .with_string_to_value(formatters::s2v_f32_gain_to_db()) + .with_value_to_string(formatters::v2s_f32_gain_to_db(2)) + .into(), + DspParameters::Cutoff => FloatParam::new( + "Cutoff", + 3000.0, + FloatRange::Skewed { + min: 20.0, max: 20e3, factor: FloatRange::skew_factor(-2.0), }, ) - .with_value_to_string(formatters::v2s_f32_hz_then_khz_with_note_name(2, false)) .with_string_to_value(formatters::s2v_f32_hz_then_khz()) - .with_smoother(SmoothingStyle::Exponential(10.)), - q: FloatParam::new( - "Q", + .with_value_to_string(formatters::v2s_f32_hz_then_khz_with_note_name(2, true)) + .into(), + DspParameters::Resonance => FloatParam::new( + "Resonance", 0.5, - FloatRange::Linear { - min: 1e-2, - max: 30., - }, - ) - .with_smoother(SmoothingStyle::Exponential(50.)), - drive: FloatParam::new( - "Drive", - 1., - FloatRange::SymmetricalSkewed { - min: util::db_to_gain(-36.), - max: util::db_to_gain(36.), - center: 1., - factor: FloatRange::skew_factor(-2.), + FloatRange::Skewed { + min: 0.0, + max: 30.0, + factor: FloatRange::skew_factor(-2.0), }, ) - .with_unit(" dB") - .with_string_to_value(formatters::s2v_f32_gain_to_db()) - .with_value_to_string(formatters::v2s_f32_gain_to_db(2)) - .with_smoother(SmoothingStyle::Exponential(100.)), - filter_type: EnumParam::new("Filter Type", FilterType::Lowpass), - nonlinearity: EnumParam::new("Nonlinearity", NLType::Linear), - } - } -} - -#[derive(Debug)] -struct Plugin { - params: Arc, - biquad: Biquad>, - oversample: Oversample, -} - -fn get_biquad( - samplerate: f32, - fc: f32, - q: f32, - filter_type: FilterType, -) -> Biquad> { - let fc = fc / samplerate; - let fc = Sample::splat(fc); - let q = Sample::splat(q); - match filter_type { - FilterType::Lowpass => Biquad::lowpass(fc, q), - FilterType::Bandpass => Biquad::bandpass_peak0(fc, q), - FilterType::Highpass => Biquad::highpass(fc, q), - } -} - -impl Plugin { - fn set_filters(&mut self, samplerate: f32) { - let biquad = get_biquad( - samplerate, - self.params.fc.value(), - self.params.q.value(), - self.params.filter_type.value(), - ); - let nltype = self.params.nonlinearity.value().as_dynamic_saturator(); - self.biquad.update_coefficients(&biquad); - self.biquad.set_saturators(nltype, nltype); - } -} - -impl Default for Plugin { - fn default() -> Self { - let params = Arc::new(PluginParams::default()); + .with_value_to_string(formatters::v2s_f32_rounded(2)) + .into(), + DspParameters::FilterType => enum_int_param::("Filter type").into(), + DspParameters::SaturatorType => { + enum_int_param::("Saturator type").into() + } + }); Self { - params, - biquad: Biquad::new([Sample::from([0.0; 2]); 3], [Sample::from([0.0; 2]); 2]), - oversample: Oversample::new(OVERSAMPLE, MAX_BLOCK_SIZE), - } - } -} - -fn block_to_simd_array( - block: &mut nih_plug::buffer::Block, - output: &mut [AutoSimd<[T; N]>], - cast: impl Fn(f32) -> T, -) -> usize { - let mut i = 0; - for samples in block.iter_samples().take(output.len()) { - let mut it = samples.into_iter(); - output[i] = AutoSimd(std::array::from_fn(|_| cast(it.next().copied().unwrap()))); - i += 1; - } - i -} - -fn simd_array_to_block( - input: &[AutoSimd<[T; N]>], - block: &mut nih_plug::buffer::Block, - uncast: impl Fn(&T) -> f32, -) { - for (inp, mut out_samples) in input.iter().zip(block.iter_samples()) { - for i in 0..N { - *out_samples.get_mut(i).unwrap() = uncast(&inp.0[i]); - } - } -} - -#[cfg(never)] // Not used but nice to keep around -fn apply>, const N: usize>( - buffer: &mut Buffer, - dsp: &mut P, -) where - AutoSimd<[f32; N]>: SimdValue, - ::Element: Copy, -{ - for mut samples in buffer.iter_samples() { - let mut it = samples.iter_mut(); - let input = AutoSimd(std::array::from_fn(|_| it.next().copied().unwrap())); - let [output] = dsp.process([input]); - for (out, inp) in samples.into_iter().zip(output.0.into_iter()) { - *out = inp; + params: Arc::new(params), + dsp, } } } @@ -211,8 +92,8 @@ impl nih_plug::prelude::Plugin for Plugin { aux_outputs: &[], }, }]; - type BackgroundTask = (); type SysExMessage = (); + type BackgroundTask = (); fn params(&self) -> Arc { self.params.clone() @@ -224,13 +105,12 @@ impl nih_plug::prelude::Plugin for Plugin { buffer_config: &BufferConfig, _context: &mut impl InitContext, ) -> bool { - self.set_filters(buffer_config.sample_rate); + self.dsp.set_samplerate(buffer_config.sample_rate); true } fn reset(&mut self) { - self.biquad.reset(); - self.oversample.reset(); + self.dsp.reset(); } fn process( @@ -239,49 +119,8 @@ impl nih_plug::prelude::Plugin for Plugin { _aux: &mut AuxiliaryBuffers, ctx: &mut impl ProcessContext, ) -> ProcessStatus { - let params = self.params.clone(); - let os_samplerate = ctx.transport().sample_rate * OVERSAMPLE as f32; - let mut fc = [0.; MAX_BLOCK_SIZE]; - let mut q = [0.; MAX_BLOCK_SIZE]; - let mut drive = [0.; MAX_BLOCK_SIZE]; - let mut os_fc = [0.; OVERSAMPLE * MAX_BLOCK_SIZE]; - let mut os_q = [0.; OVERSAMPLE * MAX_BLOCK_SIZE]; - let mut os_drive = [0.; OVERSAMPLE * MAX_BLOCK_SIZE]; - for (_, mut block) in buffer.iter_blocks(MAX_BLOCK_SIZE) { - let len = block.samples(); - let os_len = OVERSAMPLE * len; - self.params.fc.smoothed.next_block_exact(&mut fc[..len]); - self.params.q.smoothed.next_block_exact(&mut q[..len]); - self.params - .drive - .smoothed - .next_block_exact(&mut drive[..len]); - Cubic.interpolate_slice(&mut os_fc[..os_len], &fc[..len]); - Cubic.interpolate_slice(&mut os_q[..os_len], &q[..len]); - Cubic.interpolate_slice(&mut os_drive[..os_len], &drive[..len]); - - let mut simd_block = [Sample::from_f64(0.0); MAX_BLOCK_SIZE]; - let actual_len = - block_to_simd_array(&mut block, &mut simd_block, std::convert::identity); - let simd_block = &mut simd_block[..actual_len]; - let mut os_buffer = self.oversample.oversample(simd_block); - for (i, s) in os_buffer.iter_mut().enumerate() { - let nltype = params.nonlinearity.value().as_dynamic_saturator(); - self.biquad.update_coefficients(&get_biquad( - os_samplerate, - os_fc[i], - os_q[i], - self.params.filter_type.value(), - )); - self.biquad.set_saturators(nltype, nltype); - let drive = Sample::splat(os_drive[i]); - *s = self.biquad.process([*s * drive])[0] / drive; - } - - os_buffer.finish(simd_block); - simd_array_to_block(simd_block, &mut block, |&v| v); - } - + ctx.set_latency_samples(self.dsp.latency() as _); + process_buffer_simd::<_, _, MAX_BLOCK_SIZE>(&mut self.dsp, buffer); ProcessStatus::Normal } } diff --git a/src/contrib/nih_plug.rs b/src/contrib/nih_plug.rs index f49fbc3..ddc8343 100644 --- a/src/contrib/nih_plug.rs +++ b/src/contrib/nih_plug.rs @@ -15,6 +15,18 @@ use crate::dsp::utils::{slice_to_mono_block, slice_to_mono_block_mut}; use crate::dsp::DSPBlock; use crate::Scalar; +pub fn enum_int_param(param_name: impl Into) -> IntParam { + IntParam::new( + param_name, + 0, + IntRange::Linear { + min: 0, + max: (E::LENGTH - 1) as i32, + }, + ) + .with_value_to_string(Arc::new(|x| E::from_usize(x as _).to_string())) +} + /// Bind a [`valib`] [`Parameter`] to a [`nig_plug`] parameter.. pub trait BindToParameter { /// Bind a [`Parameter`] to a nih-plug [`FloatParam`]. diff --git a/src/dsp/blocks.rs b/src/dsp/blocks.rs index 22cb881..aaea38a 100644 --- a/src/dsp/blocks.rs +++ b/src/dsp/blocks.rs @@ -107,6 +107,11 @@ impl P1 { } } + pub fn with_state(mut self, state: T) -> Self { + self.s = state; + self + } + pub fn set_fc(&mut self, fc: T) { self.fc = fc; } diff --git a/src/dsp/parameter.rs b/src/dsp/parameter.rs index 5d87fdf..0969bf0 100644 --- a/src/dsp/parameter.rs +++ b/src/dsp/parameter.rs @@ -88,8 +88,7 @@ impl Parameter { pub fn get_enum(&self) -> E { let index = self.get_value().min(E::LENGTH as f32 - 1.0); - eprintln!("get_enum index {index}"); - return E::from_usize(index.floor() as _); + E::from_usize(index.floor() as _) } pub fn set_enum(&self, value: E) { @@ -193,10 +192,11 @@ impl SmoothedParam { /// * `duration_ms`: Maximum duration of a sweep, that is the duration it would take to go from one extreme to the other. pub fn linear(param: Parameter, samplerate: f32, duration_ms: f32) -> Self { let max_diff = 1000.0 / duration_ms; + let initial_state = param.get_value(); Self { param, smoothing: Smoothing::Linear { - slew: Slew::new(samplerate, max_diff), + slew: Slew::new(samplerate, max_diff).with_state(initial_state), max_diff, }, } @@ -211,10 +211,11 @@ impl SmoothedParam { /// * `t60_ms`: "Time to decay by 60 dB" -- the time it takes for the output to be within 0.1% of the target value. pub fn exponential(param: Parameter, samplerate: f32, t60_ms: f32) -> Self { let tau = 6.91 * t60_ms / 1e3; + let initial_value = param.get_value(); Self { param, smoothing: Smoothing::Exponential(Series2::new( - P1::new(samplerate, tau.recip()), + P1::new(samplerate, tau.recip()).with_state(initial_value), ModMatrix { weights: SMatrix::<_, 1, 3>::new(1.0, 0.0, 0.0), ..ModMatrix::default() diff --git a/src/saturators/mod.rs b/src/saturators/mod.rs index 3e34130..0def44e 100644 --- a/src/saturators/mod.rs +++ b/src/saturators/mod.rs @@ -184,6 +184,11 @@ impl Slew { } } + pub fn with_state(mut self, state: T) -> Self { + self.last_out = state; + self + } + pub fn set_max_diff(&mut self, max: T, samplerate: T) { self.max_diff = max / samplerate; } From 8fda9ca9707a2bbd916e412a558e7ca2f35e017e Mon Sep 17 00:00:00 2001 From: Nathan Graule Date: Fri, 23 Feb 2024 21:05:10 +0100 Subject: [PATCH 16/21] feat(nih-plug): add parameter to add default enum value --- examples/diodeclipper/src/dsp.rs | 13 +++++++------ examples/diodeclipper/src/lib.rs | 29 +++++++++++------------------ examples/dirty-biquad/src/lib.rs | 8 +++++--- src/contrib/nih_plug.rs | 9 ++++++--- 4 files changed, 29 insertions(+), 30 deletions(-) diff --git a/examples/diodeclipper/src/dsp.rs b/examples/diodeclipper/src/dsp.rs index d95a903..4d515af 100644 --- a/examples/diodeclipper/src/dsp.rs +++ b/examples/diodeclipper/src/dsp.rs @@ -1,6 +1,8 @@ use enum_map::Enum; use nih_plug::util::gain_to_db_fast; use num_traits::Zero; +use std::fmt; +use std::fmt::Formatter; use valib::dsp::parameter::{HasParameters, Parameter, SmoothedParam}; use valib::dsp::{DSPBlock, DSP}; @@ -61,14 +63,13 @@ pub enum DiodeType { Led, } -impl DiodeType { - pub fn name(&self) -> String { +impl fmt::Display for DiodeType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { - Self::Silicon => "Silicon", - Self::Germanium => "Germanium", - Self::Led => "LED", + Self::Silicon => write!(f, "Silicon"), + Self::Germanium => write!(f, "Germanium"), + Self::Led => write!(f, "LED"), } - .to_string() } } diff --git a/examples/diodeclipper/src/lib.rs b/examples/diodeclipper/src/lib.rs index d95bf59..1cd2278 100644 --- a/examples/diodeclipper/src/lib.rs +++ b/examples/diodeclipper/src/lib.rs @@ -5,7 +5,7 @@ use nih_plug::prelude::*; use nih_plug::util::db_to_gain; use dsp::Dsp; -use valib::contrib::nih_plug::{process_buffer_simd, NihParamsController}; +use valib::contrib::nih_plug::{enum_int_param, process_buffer_simd, NihParamsController}; use valib::dsp::DSPBlock; use crate::dsp::{create_dsp, DiodeType, DspParams}; @@ -41,24 +41,17 @@ impl Default for ClipperPlugin { .with_value_to_string(formatters::v2s_f32_gain_to_db(2)) .with_string_to_value(formatters::s2v_f32_gain_to_db()) .into(), - DspParams::ModelSwitch => { - IntParam::new("Model", 0, IntRange::Linear { min: 0, max: 1 }) - .with_value_to_string(Arc::new(|x| { - match x { - 0 => "Newton-Rhapson", - 1 => "Model", - _ => "INVALID", - } - .to_string() - })) - .into() - } + DspParams::ModelSwitch => BoolParam::new("Model", false) + .with_value_to_string(Arc::new(|x| { + match x { + false => "Newton-Rhapson", + true => "Model", + } + .to_string() + })) + .into(), DspParams::DiodeType => { - IntParam::new("Diode type", 0, IntRange::Linear { min: 0, max: 2 }) - .with_value_to_string(Arc::new(|x| { - DiodeType::from_usize(nih_dbg!(x as _)).name() - })) - .into() + enum_int_param::("Diode type", DiodeType::Silicon).into() } DspParams::NumForward => { IntParam::new("# Forward", 1, IntRange::Linear { min: 1, max: 5 }).into() diff --git a/examples/dirty-biquad/src/lib.rs b/examples/dirty-biquad/src/lib.rs index 20e38d8..3f66845 100644 --- a/examples/dirty-biquad/src/lib.rs +++ b/examples/dirty-biquad/src/lib.rs @@ -54,16 +54,18 @@ impl Default for Plugin { "Resonance", 0.5, FloatRange::Skewed { - min: 0.0, + min: 0.02, max: 30.0, factor: FloatRange::skew_factor(-2.0), }, ) .with_value_to_string(formatters::v2s_f32_rounded(2)) .into(), - DspParameters::FilterType => enum_int_param::("Filter type").into(), + DspParameters::FilterType => { + enum_int_param::("Filter type", FilterType::Lowpass).into() + } DspParameters::SaturatorType => { - enum_int_param::("Saturator type").into() + enum_int_param::("Saturator type", SaturatorType::Linear).into() } }); Self { diff --git a/src/contrib/nih_plug.rs b/src/contrib/nih_plug.rs index ddc8343..7d5b973 100644 --- a/src/contrib/nih_plug.rs +++ b/src/contrib/nih_plug.rs @@ -15,13 +15,16 @@ use crate::dsp::utils::{slice_to_mono_block, slice_to_mono_block_mut}; use crate::dsp::DSPBlock; use crate::Scalar; -pub fn enum_int_param(param_name: impl Into) -> IntParam { +pub fn enum_int_param( + param_name: impl Into, + default_value: E, +) -> IntParam { IntParam::new( param_name, - 0, + default_value.into_usize() as _, IntRange::Linear { min: 0, - max: (E::LENGTH - 1) as i32, + max: (E::LENGTH - 1) as _, }, ) .with_value_to_string(Arc::new(|x| E::from_usize(x as _).to_string())) From 1ce7076a8a76bb08a12ebcc4d942d6c99aab1c77 Mon Sep 17 00:00:00 2001 From: Nathan Graule Date: Fri, 23 Feb 2024 22:11:04 +0100 Subject: [PATCH 17/21] feat(examples): make ladder example use nih-plug integration --- Cargo.lock | 1 + examples/ladder/Cargo.toml | 3 +- examples/ladder/src/dsp.rs | 207 ++++++++++++++++++++++++++++++++++ examples/ladder/src/lib.rs | 221 ++++++------------------------------- 4 files changed, 243 insertions(+), 189 deletions(-) create mode 100644 examples/ladder/src/dsp.rs diff --git a/Cargo.lock b/Cargo.lock index 7661567..eebf0ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2112,6 +2112,7 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" name = "ladder" version = "0.1.0" dependencies = [ + "enum-map", "nih_plug", "valib", ] diff --git a/examples/ladder/Cargo.toml b/examples/ladder/Cargo.toml index 112576c..926e0df 100644 --- a/examples/ladder/Cargo.toml +++ b/examples/ladder/Cargo.toml @@ -13,5 +13,6 @@ keywords.workspace = true crate-type = ["cdylib"] [dependencies] +enum-map.workspace = true nih_plug = { workspace = true } -valib = { path = "../.." } \ No newline at end of file +valib = { path = "../..", features = ["nih-plug"] } \ No newline at end of file diff --git a/examples/ladder/src/dsp.rs b/examples/ladder/src/dsp.rs new file mode 100644 index 0000000..a1cd111 --- /dev/null +++ b/examples/ladder/src/dsp.rs @@ -0,0 +1,207 @@ +use crate::{MAX_BUFFER_SIZE, OVERSAMPLE}; +use enum_map::Enum; +use std::fmt; +use std::fmt::Formatter; +use valib::dsp::parameter::{HasParameters, Parameter, SmoothedParam}; +use valib::dsp::DSP; +use valib::filters::ladder::{Ideal, Ladder, Transistor, OTA}; +use valib::oversample::{Oversample, Oversampled}; +use valib::saturators::clippers::DiodeClipperModel; +use valib::saturators::Tanh; +use valib::simd::{AutoF32x2, SimdComplexField, SimdValue}; + +pub type Sample = AutoF32x2; + +#[allow(clippy::large_enum_variant)] +enum DspLadder { + Ideal(Ladder), + Transistor(Ladder>>), + OTA(Ladder>), +} + +impl DSP<1, 1> for DspLadder { + type Sample = Sample; + + fn process(&mut self, x: [Self::Sample; 1]) -> [Self::Sample; 1] { + match self { + Self::Ideal(ladder) => ladder.process(x), + Self::Transistor(ladder) => ladder.process(x), + Self::OTA(ladder) => ladder.process(x), + } + } + + fn set_samplerate(&mut self, samplerate: f32) { + match self { + Self::Ideal(ladder) => ladder.set_samplerate(samplerate), + Self::Transistor(ladder) => ladder.set_samplerate(samplerate), + Self::OTA(ladder) => ladder.set_samplerate(samplerate), + } + } + + fn latency(&self) -> usize { + 4 // Inlined from the ladder implementation + } + + fn reset(&mut self) { + match self { + Self::Ideal(ladder) => ladder.reset(), + Self::Transistor(ladder) => ladder.reset(), + Self::OTA(ladder) => ladder.reset(), + } + } +} + +impl DspLadder { + fn set_cutoff(&mut self, fc: Sample) { + match self { + Self::Ideal(ladder) => ladder.set_cutoff(fc), + Self::Transistor(ladder) => ladder.set_cutoff(fc), + Self::OTA(ladder) => ladder.set_cutoff(fc), + } + } + + fn set_resonance(&mut self, res: Sample) { + match self { + Self::Ideal(ladder) => ladder.set_resonance(res), + Self::Transistor(ladder) => ladder.set_resonance(res), + Self::OTA(ladder) => ladder.set_resonance(res), + } + } + + fn set_compensated(&mut self, compensated: bool) { + match self { + Self::Ideal(ladder) => ladder.compensated = compensated, + Self::Transistor(ladder) => ladder.compensated = compensated, + Self::OTA(ladder) => ladder.compensated = compensated, + } + } +} + +#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Enum)] +pub enum LadderType { + #[default] + Ideal, + Transistor, + OTA, +} + +impl fmt::Display for LadderType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + Self::Ideal => write!(f, "Ideal"), + Self::Transistor => write!(f, "Transistor"), + Self::OTA => write!(f, "OTA"), + } + } +} + +impl LadderType { + fn as_ladder(&self, samplerate: f32, fc: Sample, res: Sample) -> DspLadder { + match self { + Self::Ideal => DspLadder::Ideal(Ladder::new(samplerate, fc, res)), + Self::Transistor => DspLadder::Transistor(Ladder::new(samplerate, fc, res)), + Self::OTA => DspLadder::OTA(Ladder::new(samplerate, fc, res)), + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Enum)] +pub enum DspParameters { + LadderType, + Drive, + Cutoff, + Resonance, + Compensated, +} + +pub struct DspInner { + ladder_type: Parameter, + drive: SmoothedParam, + cutoff: SmoothedParam, + resonance: SmoothedParam, + compensated: Parameter, + ladder: DspLadder, + samplerate: f32, +} + +impl DspInner { + fn update_from_parameters(&mut self) { + let fc = Sample::splat(self.cutoff.next_sample()); + let res = Sample::splat({ + let k = 4.0 * self.resonance.next_sample(); + if self.ladder_type.get_enum::() == LadderType::Ideal { + k.min(3.95) + } else { + k + } + }); + if self.ladder_type.has_changed() { + self.ladder = + self.ladder_type + .get_enum::() + .as_ladder(self.samplerate, fc, res); + } + + self.ladder.set_cutoff(fc); + self.ladder.set_resonance(res); + self.ladder.set_compensated(self.compensated.get_bool()); + } +} + +impl DSP<1, 1> for DspInner { + type Sample = Sample; + + fn process(&mut self, x: [Self::Sample; 1]) -> [Self::Sample; 1] { + self.update_from_parameters(); + let drive = Sample::splat(self.drive.next_sample() / 4.0); + self.ladder.process(x.map(|x| x * drive)).map(|x| x / drive) + } + + fn set_samplerate(&mut self, samplerate: f32) { + self.samplerate = samplerate; + self.drive.set_samplerate(samplerate); + self.cutoff.set_samplerate(samplerate); + self.resonance.set_samplerate(samplerate); + self.ladder.set_samplerate(samplerate); + } + + fn latency(&self) -> usize { + self.ladder.latency() + } + + fn reset(&mut self) { + self.drive.reset(); + self.cutoff.reset(); + self.resonance.reset(); + self.ladder.reset(); + } +} + +impl HasParameters for DspInner { + type Enum = DspParameters; + + fn get_parameter(&self, param: Self::Enum) -> &Parameter { + match param { + DspParameters::LadderType => &self.ladder_type, + DspParameters::Drive => &self.drive.param, + DspParameters::Cutoff => &self.cutoff.param, + DspParameters::Resonance => &self.resonance.param, + DspParameters::Compensated => &self.compensated, + } + } +} + +pub type Dsp = Oversampled; + +pub fn create(samplerate: f32) -> Dsp { + let dsp = DspInner { + ladder_type: Parameter::new(0.0), + drive: Parameter::new(1.0).smoothed_exponential(samplerate, 50.0), + cutoff: Parameter::new(300.0).smoothed_exponential(samplerate, 10.0), + resonance: Parameter::new(0.5).smoothed_exponential(samplerate, 50.0), + ladder: LadderType::Ideal.as_ladder(samplerate, Sample::splat(300.0), Sample::splat(0.5)), + compensated: Parameter::new(0.0), + samplerate, + }; + Oversample::new(OVERSAMPLE, MAX_BUFFER_SIZE).with_dsp(samplerate, dsp) +} diff --git a/examples/ladder/src/lib.rs b/examples/ladder/src/lib.rs index c48c28d..1e31f23 100644 --- a/examples/ladder/src/lib.rs +++ b/examples/ladder/src/lib.rs @@ -1,45 +1,27 @@ -use std::sync::{atomic::AtomicBool, Arc}; - +use crate::dsp::{DspParameters, LadderType}; use nih_plug::prelude::*; +use std::sync::{atomic::AtomicBool, Arc}; +use valib::contrib::nih_plug::{enum_int_param, process_buffer_simd, NihParamsController}; +use valib::dsp::DSPBlock; -use valib::math::interpolation::{Cubic, Interpolate}; -use valib::oversample::Oversample; -use valib::{dsp::DSP, Scalar}; -use valib::{ - filters::ladder::{Ideal, Ladder, Transistor, OTA}, - saturators::{clippers::DiodeClipperModel, Tanh}, - simd::{AutoF32x2, SimdValue}, -}; +mod dsp; const MAX_BUFFER_SIZE: usize = 512; -const OVERSAMPLE: usize = 2; +const OVERSAMPLE: usize = 16; -#[derive(Debug, Default, Enum, Clone, Copy, PartialEq, Eq)] -enum LadderTopology { - #[default] - Ideal, - Ota, - Transistor, -} - -#[derive(Debug, Params)] -struct PluginParams { - #[id = "drive"] - drive: FloatParam, - #[id = "fc"] - fc: FloatParam, - #[id = "q"] - q: FloatParam, - #[id = "topo"] - topology: EnumParam, - #[id = "comp"] - compensated: BoolParam, +struct LadderFilterPlugin { + params: Arc>, + dsp: dsp::Dsp, } -impl PluginParams { - fn new(topology_changed: Arc) -> Self { - Self { - drive: FloatParam::new( +impl Default for LadderFilterPlugin { + fn default() -> Self { + let dsp = dsp::create(44100.0); + let params = NihParamsController::new(&dsp, |param, _name| match param { + DspParameters::LadderType => { + enum_int_param::("Topology", LadderType::default()).into() + } + DspParameters::Drive => FloatParam::new( "Drive", 1.0, FloatRange::Skewed { @@ -50,8 +32,8 @@ impl PluginParams { ) .with_value_to_string(formatters::v2s_f32_gain_to_db(2)) .with_string_to_value(formatters::s2v_f32_gain_to_db()) - .with_smoother(SmoothingStyle::Exponential(10.)), - fc: FloatParam::new( + .into(), + DspParameters::Cutoff => FloatParam::new( "Frequency", 300., FloatRange::Skewed { @@ -62,109 +44,19 @@ impl PluginParams { ) .with_value_to_string(formatters::v2s_f32_hz_then_khz_with_note_name(2, false)) .with_string_to_value(formatters::s2v_f32_hz_then_khz()) - .with_smoother(SmoothingStyle::Exponential(10.)), - q: FloatParam::new("Q", 0.5, FloatRange::Linear { min: 0., max: 1.25 }) - .with_smoother(SmoothingStyle::Exponential(50.)), - topology: EnumParam::new("Topology", LadderTopology::default()).with_callback( - Arc::new(move |_| { - topology_changed.store(true, std::sync::atomic::Ordering::SeqCst) - }), - ), - compensated: BoolParam::new("Compensated", true), - } - } -} - -type Sample = AutoF32x2; -type DspT = Ladder; -type DspIdeal = DspT; -type DspOta = DspT>; -type DspTransistor = DspT>>; - -#[derive(Debug, Clone, Copy)] -#[allow(clippy::large_enum_variant)] -enum Dsp { - Ideal(DspIdeal), - Ota(DspOta), - Transistor(DspTransistor), -} -impl Dsp { - fn set_params(&mut self, freq: Sample, q: Sample) { - match self { - Self::Ideal(f) => { - f.set_cutoff(freq); - f.set_resonance(q); - } - Self::Ota(f) => { - f.set_cutoff(freq); - f.set_resonance(q); - } - Self::Transistor(f) => { - f.set_cutoff(freq); - f.set_resonance(q); + .into(), + DspParameters::Resonance => { + FloatParam::new("Q", 0.5, FloatRange::Linear { min: 0., max: 1.25 }) + .with_unit(" %") + .with_value_to_string(formatters::v2s_f32_percentage(2)) + .with_string_to_value(formatters::s2v_f32_percentage()) + .into() } - } - } -} - -impl DSP<1, 1> for Dsp { - type Sample = Sample; - - fn process(&mut self, x: [Self::Sample; 1]) -> [Self::Sample; 1] { - match self { - Self::Ideal(f) => f.process(x), - Self::Ota(f) => f.process(x), - Self::Transistor(f) => f.process(x), - } - } -} - -#[derive(Debug)] -struct LadderFilterPlugin { - topology_changed: Arc, - params: Arc, - oversample: Oversample, - dsp: Dsp, -} -impl LadderFilterPlugin { - fn setup_filter(&mut self, samplerate: impl Copy + Into) { - if self - .topology_changed - .load(std::sync::atomic::Ordering::SeqCst) - { - let fc = Sample::splat(self.params.fc.value()); - let q = Sample::splat(self.params.q.value()); - self.dsp = match self.params.topology.value() { - LadderTopology::Ideal => Dsp::Ideal(DspIdeal::new(samplerate, fc, q)), - LadderTopology::Ota => Dsp::Ota(DspOta::new(samplerate, fc, q)), - LadderTopology::Transistor => { - Dsp::Transistor(DspTransistor::new(samplerate, fc, q)) - } - }; - self.topology_changed - .store(false, std::sync::atomic::Ordering::SeqCst); - } - let compensated = match &mut self.dsp { - Dsp::Ideal(f) => &mut f.compensated, - Dsp::Ota(f) => &mut f.compensated, - Dsp::Transistor(f) => &mut f.compensated, - }; - *compensated = self.params.compensated.value(); - } -} - -impl Default for LadderFilterPlugin { - fn default() -> Self { - let topology_changed = Arc::new(AtomicBool::new(false)); - let params = Arc::new(PluginParams::new(topology_changed.clone())); - let fc = Sample::splat(params.fc.default_plain_value()); - let q = Sample::splat(params.q.default_plain_value()); - let dsp = DspIdeal::new(44100.0, fc, q); + DspParameters::Compensated => BoolParam::new("Compensated", false).into(), + }); Self { - topology_changed, - params, - dsp: Dsp::Ideal(dsp), - oversample: Oversample::new(OVERSAMPLE, MAX_BUFFER_SIZE), + params: Arc::new(params), + dsp, } } } @@ -201,7 +93,7 @@ impl Plugin for LadderFilterPlugin { buffer_config: &BufferConfig, _context: &mut impl InitContext, ) -> bool { - self.setup_filter(buffer_config.sample_rate * OVERSAMPLE as f32); + self.dsp.set_samplerate(buffer_config.sample_rate); true } @@ -215,55 +107,8 @@ impl Plugin for LadderFilterPlugin { _aux: &mut AuxiliaryBuffers, context: &mut impl ProcessContext, ) -> ProcessStatus { - self.setup_filter(context.transport().sample_rate * OVERSAMPLE as f32); - - let mut drive = [0.; MAX_BUFFER_SIZE]; - let mut fc = [0.; MAX_BUFFER_SIZE]; - let mut q = [0.; MAX_BUFFER_SIZE]; - let mut simd_slice = [Sample::from_f64(0.0); MAX_BUFFER_SIZE]; - - for (_, mut block) in buffer.iter_blocks(MAX_BUFFER_SIZE) { - for (i, mut sample) in block.iter_samples().enumerate() { - simd_slice[i] = Sample::new( - sample.get_mut(0).copied().unwrap(), - sample.get_mut(1).copied().unwrap(), - ); - } - let len = block.samples(); - let os_len = OVERSAMPLE * len; - - self.params - .drive - .smoothed - .next_block_exact(&mut drive[..len]); - self.params.fc.smoothed.next_block_exact(&mut fc[..len]); - self.params.q.smoothed.next_block_exact(&mut q[..len]); - - let mut os_drive = [0.; OVERSAMPLE * MAX_BUFFER_SIZE]; - let mut os_fc = [0.; OVERSAMPLE * MAX_BUFFER_SIZE]; - let mut os_q = [0.; OVERSAMPLE * MAX_BUFFER_SIZE]; - - Cubic.interpolate_slice(&mut os_drive[..os_len], &drive[..len]); - Cubic.interpolate_slice(&mut os_fc[..os_len], &fc[..len]); - Cubic.interpolate_slice(&mut os_q[..os_len], &q[..len]); - - let buffer = &mut simd_slice[..len]; - let mut os_buffer = self.oversample.oversample(buffer); - for (i, s) in os_buffer.iter_mut().enumerate() { - let fc = Sample::splat(os_fc[i]); - let q = Sample::splat(4.0 * os_q[i]); - self.dsp.set_params(fc, q); - let drive = Sample::splat(os_drive[i]); - *s = self.dsp.process([*s * drive])[0] / drive; - } - os_buffer.finish(buffer); - - for (i, mut s) in block.iter_samples().enumerate() { - *s.get_mut(0).unwrap() = buffer[i].extract(0); - *s.get_mut(1).unwrap() = buffer[i].extract(1); - } - } - + context.set_latency_samples(self.dsp.latency() as _); + process_buffer_simd::<_, _, MAX_BUFFER_SIZE>(&mut self.dsp, buffer); ProcessStatus::Normal } } From 480322aebf3081036e04afd6b912cd4de3dcdc21 Mon Sep 17 00:00:00 2001 From: Nathan Graule Date: Fri, 23 Feb 2024 22:16:13 +0100 Subject: [PATCH 18/21] feat(examples): update svfmixer example --- examples/svfmixer/src/lib.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/examples/svfmixer/src/lib.rs b/examples/svfmixer/src/lib.rs index 4a41b9a..2e52c3d 100644 --- a/examples/svfmixer/src/lib.rs +++ b/examples/svfmixer/src/lib.rs @@ -35,7 +35,8 @@ impl Default for SvfMixerPlugin { ) .with_value_to_string(formatters::v2s_f32_gain_to_db(2)) .with_string_to_value(formatters::s2v_f32_gain_to_db()) - .with_unit(" dB"), + .with_unit(" dB") + .into(), DspParam::Cutoff => FloatParam::new( "Frequency", 300., @@ -46,9 +47,10 @@ impl Default for SvfMixerPlugin { }, ) .with_value_to_string(formatters::v2s_f32_hz_then_khz_with_note_name(2, false)) - .with_string_to_value(formatters::s2v_f32_hz_then_khz()), + .with_string_to_value(formatters::s2v_f32_hz_then_khz()) + .into(), DspParam::Resonance => { - FloatParam::new("Q", 0.5, FloatRange::Linear { min: 0., max: 1.25 }) + FloatParam::new("Q", 0.5, FloatRange::Linear { min: 0., max: 1.25 }).into() } DspParam::LpGain => FloatParam::new( "LP Gain", @@ -62,7 +64,8 @@ impl Default for SvfMixerPlugin { ) .with_value_to_string(formatters::v2s_f32_gain_to_db(2)) .with_string_to_value(formatters::s2v_f32_gain_to_db()) - .with_unit(" dB"), + .with_unit(" dB") + .into(), DspParam::BpGain => FloatParam::new( "BP Gain", 0., @@ -75,7 +78,8 @@ impl Default for SvfMixerPlugin { ) .with_value_to_string(formatters::v2s_f32_gain_to_db(2)) .with_string_to_value(formatters::s2v_f32_gain_to_db()) - .with_unit(" dB"), + .with_unit(" dB") + .into(), DspParam::HpGain => FloatParam::new( "HP Gain", 0., @@ -88,7 +92,8 @@ impl Default for SvfMixerPlugin { ) .with_value_to_string(formatters::v2s_f32_gain_to_db(2)) .with_string_to_value(formatters::s2v_f32_gain_to_db()) - .with_unit(" dB"), + .with_unit(" dB") + .into(), }); Self { params: Arc::new(params_controller), From d173466c248a0251ca05a660f8280e72322e8bca Mon Sep 17 00:00:00 2001 From: Nathan Graule Date: Fri, 23 Feb 2024 22:45:55 +0100 Subject: [PATCH 19/21] fix(filters): deactivate designer module --- src/filters/biquad/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/filters/biquad/mod.rs b/src/filters/biquad/mod.rs index 813de29..6fbd6d2 100644 --- a/src/filters/biquad/mod.rs +++ b/src/filters/biquad/mod.rs @@ -10,6 +10,7 @@ use crate::{ Scalar, }; +#[cfg(never)] pub mod design; /// Biquad struct in Transposed Direct Form II. Optionally, a [`Saturator`](crate::saturators::Saturator) instance can be used @@ -268,4 +269,3 @@ mod tests { insta::assert_csv_snapshot!(&output as &[_], { "[]" => insta::rounded_redaction(4) }); } } - From 3709c3544b8d205c3ddd6221b478e9cae86ae1fe Mon Sep 17 00:00:00 2001 From: Nathan Graule Date: Fri, 23 Feb 2024 22:46:19 +0100 Subject: [PATCH 20/21] test(oversample): fix snapshots after changing to 16 cascades of biquad filters --- .../valib__oversample__tests__os block.snap | 3962 ++++++++--------- ...rsample__tests__oversampled_dsp_block.snap | 112 +- .../valib__oversample__tests__post os.snap | 1020 ++--- 3 files changed, 2547 insertions(+), 2547 deletions(-) diff --git a/src/oversample/snapshots/valib__oversample__tests__os block.snap b/src/oversample/snapshots/valib__oversample__tests__os block.snap index 1c96d64..44c7bd9 100644 --- a/src/oversample/snapshots/valib__oversample__tests__os block.snap +++ b/src/oversample/snapshots/valib__oversample__tests__os block.snap @@ -6,2047 +6,2047 @@ expression: "&*osblock" 0.0 0.0 0.0 -0.005 -0.036 -0.114 -0.182 -0.135 -0.048 -0.154 -0.365 -0.299 -0.055 -0.179 -0.552 -0.464 -0.056 -0.201 -0.735 -0.625 -0.057 -0.222 -0.911 -0.779 -0.057 -0.24 -1.078 -0.926 -0.057 -0.256 -1.234 -1.064 -0.056 -0.27 -1.379 -1.192 -0.054 -0.282 -1.511 -1.308 -0.052 -0.29 -1.627 -1.411 -0.05 -0.296 -1.729 -1.501 -0.047 -0.299 -1.813 -1.577 -0.043 -0.299 -1.881 -1.637 -0.039 -0.296 -1.93 -1.682 -0.035 -0.29 -1.96 -1.71 -0.031 -0.281 -1.972 -1.722 -0.026 -0.27 -1.964 -1.718 -0.021 -0.256 -1.938 -1.696 -0.015 -0.24 -1.893 -1.659 +0.0 +0.0 +0.0 +0.0 +0.0 +0.0 +0.0 +0.0 +0.0 +0.0 +0.0 +0.0 +0.0 +0.0 +0.0 +0.0 +0.0 +0.0 +0.001 +0.002 +0.003 +0.006 0.01 -0.221 -1.83 -1.605 -0.004 -0.201 -1.749 -1.536 --0.001 -0.178 -1.651 -1.453 --0.007 -0.154 -1.538 -1.355 --0.012 -0.128 -1.409 -1.244 --0.018 -0.101 -1.267 -1.121 --0.023 +0.017 +0.026 +0.038 +0.054 0.073 -1.113 +0.096 +0.122 +0.15 +0.178 +0.207 +0.234 +0.261 +0.286 +0.31 +0.333 +0.355 +0.377 +0.398 +0.42 +0.442 +0.464 +0.485 +0.507 +0.528 +0.549 +0.569 +0.589 +0.609 +0.628 +0.647 +0.666 +0.684 +0.701 +0.719 +0.736 +0.752 +0.768 +0.783 +0.798 +0.813 +0.827 +0.841 +0.854 +0.866 +0.878 +0.89 +0.901 +0.911 +0.921 +0.93 +0.939 +0.947 +0.955 +0.962 +0.968 +0.974 +0.979 +0.984 0.988 --0.028 -0.044 -0.948 -0.845 --0.033 -0.015 -0.774 -0.693 --0.037 --0.015 -0.593 -0.536 +0.991 +0.994 +0.997 +0.998 +0.999 +1.0 +1.0 +0.999 +0.998 +0.996 +0.993 +0.99 +0.987 +0.982 +0.977 +0.972 +0.966 +0.959 +0.952 +0.944 +0.936 +0.927 +0.917 +0.907 +0.897 +0.886 +0.874 +0.862 +0.849 +0.836 +0.822 +0.808 +0.793 +0.778 +0.762 +0.746 +0.73 +0.713 +0.695 +0.677 +0.659 +0.64 +0.621 +0.602 +0.582 +0.562 +0.542 +0.521 +0.5 +0.478 +0.457 +0.435 +0.412 +0.39 +0.367 +0.344 +0.321 +0.298 +0.274 +0.251 +0.227 +0.203 +0.179 +0.155 +0.13 +0.106 +0.081 +0.057 +0.032 +0.008 +-0.017 -0.041 --0.044 -0.406 -0.372 --0.045 --0.073 -0.214 -0.206 --0.048 --0.101 -0.021 -0.037 --0.051 --0.128 --0.172 --0.132 --0.053 --0.154 --0.364 --0.3 --0.055 --0.178 --0.552 +-0.066 +-0.09 +-0.115 +-0.139 +-0.163 +-0.187 +-0.211 +-0.235 +-0.259 +-0.283 +-0.306 +-0.329 +-0.352 +-0.375 +-0.398 +-0.42 +-0.443 -0.464 --0.056 --0.201 --0.735 --0.625 --0.057 --0.222 +-0.486 +-0.507 +-0.528 +-0.549 +-0.569 +-0.589 +-0.609 +-0.628 +-0.647 +-0.666 +-0.684 +-0.701 +-0.719 +-0.736 +-0.752 +-0.768 +-0.783 +-0.798 +-0.813 +-0.827 +-0.841 +-0.854 +-0.866 +-0.878 +-0.89 +-0.901 -0.911 --0.779 --0.057 --0.24 --1.078 --0.926 --0.057 --0.256 --1.234 --1.064 --0.056 --0.27 --1.379 --1.192 --0.054 --0.282 --1.511 --1.308 --0.052 --0.29 --1.627 --1.411 --0.05 --0.296 --1.729 --1.501 --0.047 --0.299 --1.813 --1.577 --0.043 --0.299 --1.881 --1.637 --0.039 --0.296 --1.93 --1.682 --0.035 --0.29 --1.96 --1.71 --0.031 --0.281 --1.972 --1.722 --0.026 --0.27 --1.964 --1.718 --0.021 --0.256 --1.938 --1.696 --0.015 --0.24 --1.893 --1.659 --0.01 --0.221 --1.83 --1.605 --0.004 --0.201 --1.749 --1.536 -0.001 --0.178 --1.651 --1.453 -0.007 --0.154 --1.538 --1.355 -0.012 --0.128 --1.409 --1.244 -0.018 --0.101 --1.267 --1.121 -0.023 --0.073 --1.113 +-0.921 +-0.93 +-0.939 +-0.947 +-0.955 +-0.962 +-0.968 +-0.974 +-0.979 +-0.984 -0.988 -0.028 --0.044 --0.948 --0.845 -0.033 --0.015 --0.774 --0.693 -0.037 -0.015 --0.593 --0.536 +-0.991 +-0.994 +-0.997 +-0.998 +-0.999 +-1.0 +-1.0 +-0.999 +-0.998 +-0.996 +-0.993 +-0.99 +-0.987 +-0.982 +-0.977 +-0.972 +-0.966 +-0.959 +-0.952 +-0.944 +-0.936 +-0.927 +-0.917 +-0.907 +-0.897 +-0.886 +-0.874 +-0.862 +-0.849 +-0.836 +-0.822 +-0.808 +-0.793 +-0.778 +-0.762 +-0.746 +-0.73 +-0.713 +-0.695 +-0.677 +-0.659 +-0.64 +-0.621 +-0.602 +-0.582 +-0.562 +-0.542 +-0.521 +-0.5 +-0.478 +-0.457 +-0.435 +-0.412 +-0.39 +-0.367 +-0.344 +-0.321 +-0.298 +-0.274 +-0.251 +-0.227 +-0.203 +-0.179 +-0.155 +-0.13 +-0.106 +-0.081 +-0.057 +-0.032 +-0.008 +0.017 0.041 -0.044 --0.406 --0.372 -0.045 -0.073 --0.214 --0.206 -0.048 -0.101 --0.021 --0.037 -0.051 -0.128 -0.172 -0.132 -0.053 -0.154 -0.364 -0.3 -0.055 -0.178 -0.552 +0.066 +0.09 +0.115 +0.139 +0.163 +0.187 +0.211 +0.235 +0.259 +0.283 +0.306 +0.329 +0.352 +0.375 +0.398 +0.42 +0.443 0.464 -0.056 -0.201 -0.735 -0.625 -0.057 -0.222 +0.486 +0.507 +0.528 +0.549 +0.569 +0.589 +0.609 +0.628 +0.647 +0.666 +0.684 +0.701 +0.719 +0.736 +0.752 +0.768 +0.783 +0.798 +0.813 +0.827 +0.841 +0.854 +0.866 +0.878 +0.89 +0.901 0.911 -0.779 -0.057 -0.24 -1.078 -0.926 -0.057 -0.256 -1.234 -1.064 -0.056 -0.27 -1.379 -1.192 -0.054 -0.282 -1.511 -1.308 -0.052 -0.29 -1.627 -1.411 -0.05 -0.296 -1.729 -1.501 -0.047 -0.299 -1.813 -1.577 -0.043 -0.299 -1.881 -1.637 -0.039 -0.296 -1.93 -1.682 -0.035 -0.29 -1.96 -1.71 -0.031 -0.281 -1.972 -1.722 -0.026 -0.27 -1.964 -1.718 -0.021 -0.256 -1.938 -1.696 -0.015 -0.24 -1.893 -1.659 -0.01 -0.221 -1.83 -1.605 -0.004 -0.201 -1.749 -1.536 --0.001 -0.178 -1.651 -1.453 --0.007 -0.154 -1.538 -1.355 --0.012 -0.128 -1.409 -1.244 --0.018 -0.101 -1.267 -1.121 --0.023 -0.073 -1.113 +0.921 +0.93 +0.939 +0.947 +0.955 +0.962 +0.968 +0.974 +0.979 +0.984 0.988 --0.028 -0.044 -0.948 -0.845 --0.033 -0.015 -0.774 -0.693 --0.037 --0.015 -0.593 -0.536 +0.991 +0.994 +0.997 +0.998 +0.999 +1.0 +1.0 +0.999 +0.998 +0.996 +0.993 +0.99 +0.987 +0.982 +0.977 +0.972 +0.966 +0.959 +0.952 +0.944 +0.936 +0.927 +0.917 +0.907 +0.897 +0.886 +0.874 +0.862 +0.849 +0.836 +0.822 +0.808 +0.793 +0.778 +0.762 +0.746 +0.73 +0.713 +0.695 +0.677 +0.659 +0.64 +0.621 +0.602 +0.582 +0.562 +0.542 +0.521 +0.5 +0.478 +0.457 +0.435 +0.412 +0.39 +0.367 +0.344 +0.321 +0.298 +0.274 +0.251 +0.227 +0.203 +0.179 +0.155 +0.13 +0.106 +0.081 +0.057 +0.032 +0.008 +-0.017 -0.041 --0.044 -0.406 -0.372 --0.045 --0.073 -0.214 -0.206 --0.048 --0.101 -0.021 -0.037 --0.051 --0.128 --0.172 --0.132 --0.053 --0.154 --0.364 --0.3 --0.055 --0.178 --0.552 +-0.066 +-0.09 +-0.115 +-0.139 +-0.163 +-0.187 +-0.211 +-0.235 +-0.259 +-0.283 +-0.306 +-0.329 +-0.352 +-0.375 +-0.398 +-0.42 +-0.443 -0.464 --0.056 --0.201 --0.735 --0.625 --0.057 --0.222 +-0.486 +-0.507 +-0.528 +-0.549 +-0.569 +-0.589 +-0.609 +-0.628 +-0.647 +-0.666 +-0.684 +-0.701 +-0.719 +-0.736 +-0.752 +-0.768 +-0.783 +-0.798 +-0.813 +-0.827 +-0.841 +-0.854 +-0.866 +-0.878 +-0.89 +-0.901 -0.911 --0.779 --0.057 --0.24 --1.078 --0.926 --0.057 --0.256 --1.234 --1.064 --0.056 --0.27 --1.379 --1.192 --0.054 --0.282 --1.511 --1.308 --0.052 --0.29 --1.627 --1.411 --0.05 --0.296 --1.729 --1.501 --0.047 --0.299 --1.813 --1.577 --0.043 --0.299 --1.881 --1.637 --0.039 --0.296 --1.93 --1.682 --0.035 --0.29 --1.96 --1.71 --0.031 --0.281 --1.972 --1.722 --0.026 --0.27 --1.964 --1.718 --0.021 --0.256 --1.938 --1.696 --0.015 --0.24 --1.893 --1.659 --0.01 --0.221 --1.83 --1.605 --0.004 --0.201 --1.749 --1.536 -0.001 --0.178 --1.651 --1.453 -0.007 --0.154 --1.538 --1.355 -0.012 --0.128 --1.409 --1.244 -0.018 --0.101 --1.267 --1.121 -0.023 --0.073 --1.113 +-0.921 +-0.93 +-0.939 +-0.947 +-0.955 +-0.962 +-0.968 +-0.974 +-0.979 +-0.984 -0.988 -0.028 --0.044 --0.948 --0.845 -0.033 --0.015 --0.774 --0.693 -0.037 -0.015 --0.593 --0.536 +-0.991 +-0.994 +-0.997 +-0.998 +-0.999 +-1.0 +-1.0 +-0.999 +-0.998 +-0.996 +-0.993 +-0.99 +-0.987 +-0.982 +-0.977 +-0.972 +-0.966 +-0.959 +-0.952 +-0.944 +-0.936 +-0.927 +-0.917 +-0.907 +-0.897 +-0.886 +-0.874 +-0.862 +-0.849 +-0.836 +-0.822 +-0.808 +-0.793 +-0.778 +-0.762 +-0.746 +-0.73 +-0.713 +-0.695 +-0.677 +-0.659 +-0.64 +-0.621 +-0.602 +-0.582 +-0.562 +-0.542 +-0.521 +-0.5 +-0.478 +-0.457 +-0.435 +-0.412 +-0.39 +-0.367 +-0.344 +-0.321 +-0.298 +-0.274 +-0.251 +-0.227 +-0.203 +-0.179 +-0.155 +-0.13 +-0.106 +-0.081 +-0.057 +-0.032 +-0.008 +0.017 0.041 -0.044 --0.406 --0.372 -0.045 -0.073 --0.214 --0.206 -0.048 -0.101 --0.021 --0.037 -0.051 -0.128 -0.172 -0.132 -0.053 -0.154 -0.364 -0.3 -0.055 -0.178 -0.552 +0.066 +0.09 +0.115 +0.139 +0.163 +0.187 +0.211 +0.235 +0.259 +0.283 +0.306 +0.329 +0.352 +0.375 +0.398 +0.42 +0.443 0.464 -0.056 -0.201 -0.735 -0.625 -0.057 -0.222 +0.486 +0.507 +0.528 +0.549 +0.569 +0.589 +0.609 +0.628 +0.647 +0.666 +0.684 +0.701 +0.719 +0.736 +0.752 +0.768 +0.783 +0.798 +0.813 +0.827 +0.841 +0.854 +0.866 +0.878 +0.89 +0.901 0.911 -0.779 -0.057 -0.24 -1.078 -0.926 -0.057 -0.256 -1.234 -1.064 -0.056 -0.27 -1.379 -1.192 -0.054 -0.282 -1.511 -1.308 -0.052 -0.29 -1.627 -1.411 -0.05 -0.296 -1.729 -1.501 -0.047 -0.299 -1.813 -1.577 -0.043 -0.299 -1.881 -1.637 -0.039 -0.296 -1.93 -1.682 -0.035 -0.29 -1.96 -1.71 -0.031 -0.281 -1.972 -1.722 -0.026 -0.27 -1.964 -1.718 -0.021 -0.256 -1.938 -1.696 -0.015 -0.24 -1.893 -1.659 -0.01 -0.221 -1.83 -1.605 -0.004 -0.201 -1.749 -1.536 --0.001 -0.178 -1.651 -1.453 --0.007 -0.154 -1.538 -1.355 --0.012 -0.128 -1.409 -1.244 --0.018 -0.101 -1.267 -1.121 --0.023 -0.073 -1.113 +0.921 +0.93 +0.939 +0.947 +0.955 +0.962 +0.968 +0.974 +0.979 +0.984 0.988 --0.028 -0.044 -0.948 -0.845 --0.033 -0.015 -0.774 -0.693 --0.037 --0.015 -0.593 -0.536 +0.991 +0.994 +0.997 +0.998 +0.999 +1.0 +1.0 +0.999 +0.998 +0.996 +0.993 +0.99 +0.987 +0.982 +0.977 +0.972 +0.966 +0.959 +0.952 +0.944 +0.936 +0.927 +0.917 +0.907 +0.897 +0.886 +0.874 +0.862 +0.849 +0.836 +0.822 +0.808 +0.793 +0.778 +0.762 +0.746 +0.73 +0.713 +0.695 +0.677 +0.659 +0.64 +0.621 +0.602 +0.582 +0.562 +0.542 +0.521 +0.5 +0.478 +0.457 +0.435 +0.412 +0.39 +0.367 +0.344 +0.321 +0.298 +0.274 +0.251 +0.227 +0.203 +0.179 +0.155 +0.13 +0.106 +0.081 +0.057 +0.032 +0.008 +-0.017 -0.041 --0.044 -0.406 -0.372 --0.045 --0.073 -0.214 -0.206 --0.048 --0.101 -0.021 -0.037 --0.051 --0.128 --0.172 --0.132 --0.053 --0.154 --0.364 --0.3 --0.055 --0.178 --0.552 +-0.066 +-0.09 +-0.115 +-0.139 +-0.163 +-0.187 +-0.211 +-0.235 +-0.259 +-0.283 +-0.306 +-0.329 +-0.352 +-0.375 +-0.398 +-0.42 +-0.443 -0.464 --0.056 --0.201 --0.735 --0.625 --0.057 --0.222 +-0.486 +-0.507 +-0.528 +-0.549 +-0.569 +-0.589 +-0.609 +-0.628 +-0.647 +-0.666 +-0.684 +-0.701 +-0.719 +-0.736 +-0.752 +-0.768 +-0.783 +-0.798 +-0.813 +-0.827 +-0.841 +-0.854 +-0.866 +-0.878 +-0.89 +-0.901 -0.911 --0.779 --0.057 --0.24 --1.078 --0.926 --0.057 --0.256 --1.234 --1.064 --0.056 --0.27 --1.379 --1.192 --0.054 --0.282 --1.511 --1.308 --0.052 --0.29 --1.627 --1.411 --0.05 --0.296 --1.729 --1.501 --0.047 --0.299 --1.813 --1.577 --0.043 --0.299 --1.881 --1.637 --0.039 --0.296 --1.93 --1.682 --0.035 --0.29 --1.96 --1.71 --0.031 --0.281 --1.972 --1.722 --0.026 --0.27 --1.964 --1.718 --0.021 --0.256 --1.938 --1.696 --0.015 --0.24 --1.893 --1.659 --0.01 --0.221 --1.83 --1.605 --0.004 --0.201 --1.749 --1.536 -0.001 --0.178 --1.651 --1.453 -0.007 --0.154 --1.538 --1.355 -0.012 --0.128 --1.409 --1.244 -0.018 --0.101 --1.267 --1.121 -0.023 --0.073 --1.113 +-0.921 +-0.93 +-0.939 +-0.947 +-0.955 +-0.962 +-0.968 +-0.974 +-0.979 +-0.984 -0.988 -0.028 --0.044 --0.948 --0.845 -0.033 --0.015 --0.774 --0.693 -0.037 -0.015 --0.593 --0.536 +-0.991 +-0.994 +-0.997 +-0.998 +-0.999 +-1.0 +-1.0 +-0.999 +-0.998 +-0.996 +-0.993 +-0.99 +-0.987 +-0.982 +-0.977 +-0.972 +-0.966 +-0.959 +-0.952 +-0.944 +-0.936 +-0.927 +-0.917 +-0.907 +-0.897 +-0.886 +-0.874 +-0.862 +-0.849 +-0.836 +-0.822 +-0.808 +-0.793 +-0.778 +-0.762 +-0.746 +-0.73 +-0.713 +-0.695 +-0.677 +-0.659 +-0.64 +-0.621 +-0.602 +-0.582 +-0.562 +-0.542 +-0.521 +-0.5 +-0.478 +-0.457 +-0.435 +-0.412 +-0.39 +-0.367 +-0.344 +-0.321 +-0.298 +-0.274 +-0.251 +-0.227 +-0.203 +-0.179 +-0.155 +-0.13 +-0.106 +-0.081 +-0.057 +-0.032 +-0.008 +0.017 0.041 -0.044 --0.406 --0.372 -0.045 -0.073 --0.214 --0.206 -0.048 -0.101 --0.021 --0.037 -0.051 -0.128 -0.172 -0.132 -0.053 -0.154 -0.364 -0.3 -0.055 -0.178 -0.552 +0.066 +0.09 +0.115 +0.139 +0.163 +0.187 +0.211 +0.235 +0.259 +0.283 +0.306 +0.329 +0.352 +0.375 +0.398 +0.42 +0.443 0.464 -0.056 -0.201 -0.735 -0.625 -0.057 -0.222 +0.486 +0.507 +0.528 +0.549 +0.569 +0.589 +0.609 +0.628 +0.647 +0.666 +0.684 +0.701 +0.719 +0.736 +0.752 +0.768 +0.783 +0.798 +0.813 +0.827 +0.841 +0.854 +0.866 +0.878 +0.89 +0.901 0.911 -0.779 -0.057 -0.24 -1.078 -0.926 -0.057 -0.256 -1.234 -1.064 -0.056 -0.27 -1.379 -1.192 -0.054 -0.282 -1.511 -1.308 -0.052 -0.29 -1.627 -1.411 -0.05 -0.296 -1.729 -1.501 -0.047 -0.299 -1.813 -1.577 -0.043 -0.299 -1.881 -1.637 -0.039 -0.296 -1.93 -1.682 -0.035 -0.29 -1.96 -1.71 -0.031 -0.281 -1.972 -1.722 -0.026 -0.27 -1.964 -1.718 -0.021 -0.256 -1.938 -1.696 -0.015 -0.24 -1.893 -1.659 -0.01 -0.221 -1.83 -1.605 -0.004 -0.201 -1.749 -1.536 --0.001 -0.178 -1.651 -1.453 --0.007 -0.154 -1.538 -1.355 --0.012 -0.128 -1.409 -1.244 --0.018 -0.101 -1.267 -1.121 --0.023 -0.073 -1.113 +0.921 +0.93 +0.939 +0.947 +0.955 +0.962 +0.968 +0.974 +0.979 +0.984 0.988 --0.028 -0.044 -0.948 -0.845 --0.033 -0.015 -0.774 -0.693 --0.037 --0.015 -0.593 -0.536 +0.991 +0.994 +0.997 +0.998 +0.999 +1.0 +1.0 +0.999 +0.998 +0.996 +0.993 +0.99 +0.987 +0.982 +0.977 +0.972 +0.966 +0.959 +0.952 +0.944 +0.936 +0.927 +0.917 +0.907 +0.897 +0.886 +0.874 +0.862 +0.849 +0.836 +0.822 +0.808 +0.793 +0.778 +0.762 +0.746 +0.73 +0.713 +0.695 +0.677 +0.659 +0.64 +0.621 +0.602 +0.582 +0.562 +0.542 +0.521 +0.5 +0.478 +0.457 +0.435 +0.412 +0.39 +0.367 +0.344 +0.321 +0.298 +0.274 +0.251 +0.227 +0.203 +0.179 +0.155 +0.13 +0.106 +0.081 +0.057 +0.032 +0.008 +-0.017 -0.041 --0.044 -0.406 -0.372 --0.045 --0.073 -0.214 -0.206 --0.048 --0.101 -0.021 -0.037 --0.051 --0.128 --0.172 --0.132 --0.053 --0.154 --0.364 --0.3 --0.055 --0.178 --0.552 +-0.066 +-0.09 +-0.115 +-0.139 +-0.163 +-0.187 +-0.211 +-0.235 +-0.259 +-0.283 +-0.306 +-0.329 +-0.352 +-0.375 +-0.398 +-0.42 +-0.443 -0.464 --0.056 --0.201 --0.735 --0.625 --0.057 --0.222 +-0.486 +-0.507 +-0.528 +-0.549 +-0.569 +-0.589 +-0.609 +-0.628 +-0.647 +-0.666 +-0.684 +-0.701 +-0.719 +-0.736 +-0.752 +-0.768 +-0.783 +-0.798 +-0.813 +-0.827 +-0.841 +-0.854 +-0.866 +-0.878 +-0.89 +-0.901 -0.911 --0.779 --0.057 --0.24 --1.078 --0.926 --0.057 --0.256 --1.234 --1.064 --0.056 --0.27 --1.379 --1.192 --0.054 --0.282 --1.511 --1.308 --0.052 --0.29 --1.627 --1.411 --0.05 --0.296 --1.729 --1.501 --0.047 --0.299 --1.813 --1.577 --0.043 --0.299 --1.881 --1.637 --0.039 --0.296 --1.93 --1.682 --0.035 --0.29 --1.96 --1.71 --0.031 --0.281 --1.972 --1.722 --0.026 --0.27 --1.964 --1.718 --0.021 --0.256 --1.938 --1.696 --0.015 --0.24 --1.893 --1.659 --0.01 --0.221 --1.83 --1.605 --0.004 --0.201 --1.749 --1.536 -0.001 --0.178 --1.651 --1.453 -0.007 --0.154 --1.538 --1.355 -0.012 --0.128 --1.409 --1.244 -0.018 --0.101 --1.267 --1.121 -0.023 --0.073 --1.113 +-0.921 +-0.93 +-0.939 +-0.947 +-0.955 +-0.962 +-0.968 +-0.974 +-0.979 +-0.984 -0.988 -0.028 --0.044 --0.948 --0.845 -0.033 --0.015 --0.774 --0.693 -0.037 -0.015 --0.593 --0.536 +-0.991 +-0.994 +-0.997 +-0.998 +-0.999 +-1.0 +-1.0 +-0.999 +-0.998 +-0.996 +-0.993 +-0.99 +-0.987 +-0.982 +-0.977 +-0.972 +-0.966 +-0.959 +-0.952 +-0.944 +-0.936 +-0.927 +-0.917 +-0.907 +-0.897 +-0.886 +-0.874 +-0.862 +-0.849 +-0.836 +-0.822 +-0.808 +-0.793 +-0.778 +-0.762 +-0.746 +-0.73 +-0.713 +-0.695 +-0.677 +-0.659 +-0.64 +-0.621 +-0.602 +-0.582 +-0.562 +-0.542 +-0.521 +-0.5 +-0.478 +-0.457 +-0.435 +-0.412 +-0.39 +-0.367 +-0.344 +-0.321 +-0.298 +-0.274 +-0.251 +-0.227 +-0.203 +-0.179 +-0.155 +-0.13 +-0.106 +-0.081 +-0.057 +-0.032 +-0.008 +0.017 0.041 -0.044 --0.406 --0.372 -0.045 -0.073 --0.214 --0.206 -0.048 -0.101 --0.021 --0.037 -0.051 -0.128 -0.172 -0.132 -0.053 -0.154 -0.364 -0.3 -0.055 -0.178 -0.552 +0.066 +0.09 +0.115 +0.139 +0.163 +0.187 +0.211 +0.235 +0.259 +0.283 +0.306 +0.329 +0.352 +0.375 +0.398 +0.42 +0.443 0.464 -0.056 -0.201 -0.735 -0.625 -0.057 -0.222 +0.486 +0.507 +0.528 +0.549 +0.569 +0.589 +0.609 +0.628 +0.647 +0.666 +0.684 +0.701 +0.719 +0.736 +0.752 +0.768 +0.783 +0.798 +0.813 +0.827 +0.841 +0.854 +0.866 +0.878 +0.89 +0.901 0.911 -0.779 -0.057 -0.24 -1.078 -0.926 -0.057 -0.256 -1.234 -1.064 -0.056 -0.27 -1.379 -1.192 -0.054 -0.282 -1.511 -1.308 -0.052 -0.29 -1.627 -1.411 -0.05 -0.296 -1.729 -1.501 -0.047 -0.299 -1.813 -1.577 -0.043 -0.299 -1.881 -1.637 -0.039 -0.296 -1.93 -1.682 -0.035 -0.29 -1.96 -1.71 -0.031 -0.281 -1.972 -1.722 -0.026 -0.27 -1.964 -1.718 -0.021 -0.256 -1.938 -1.696 -0.015 -0.24 -1.893 -1.659 -0.01 -0.221 -1.83 -1.605 -0.004 -0.201 -1.749 -1.536 --0.001 -0.178 -1.651 -1.453 --0.007 -0.154 -1.538 -1.355 --0.012 -0.128 -1.409 -1.244 --0.018 -0.101 -1.267 -1.121 --0.023 -0.073 -1.113 +0.921 +0.93 +0.939 +0.947 +0.955 +0.962 +0.968 +0.974 +0.979 +0.984 0.988 --0.028 -0.044 -0.948 -0.845 --0.033 -0.015 -0.774 -0.693 --0.037 --0.015 -0.593 -0.536 +0.991 +0.994 +0.997 +0.998 +0.999 +1.0 +1.0 +0.999 +0.998 +0.996 +0.993 +0.99 +0.987 +0.982 +0.977 +0.972 +0.966 +0.959 +0.952 +0.944 +0.936 +0.927 +0.917 +0.907 +0.897 +0.886 +0.874 +0.862 +0.849 +0.836 +0.822 +0.808 +0.793 +0.778 +0.762 +0.746 +0.73 +0.713 +0.695 +0.677 +0.659 +0.64 +0.621 +0.602 +0.582 +0.562 +0.542 +0.521 +0.5 +0.478 +0.457 +0.435 +0.412 +0.39 +0.367 +0.344 +0.321 +0.298 +0.274 +0.251 +0.227 +0.203 +0.179 +0.155 +0.13 +0.106 +0.081 +0.057 +0.032 +0.008 +-0.017 -0.041 --0.044 -0.406 -0.372 --0.045 --0.073 -0.214 -0.206 --0.048 --0.101 -0.021 -0.037 --0.051 --0.128 --0.172 --0.132 --0.053 --0.154 --0.364 --0.3 --0.055 --0.178 --0.552 +-0.066 +-0.09 +-0.115 +-0.139 +-0.163 +-0.187 +-0.211 +-0.235 +-0.259 +-0.283 +-0.306 +-0.329 +-0.352 +-0.375 +-0.398 +-0.42 +-0.443 -0.464 --0.056 --0.201 --0.735 --0.625 --0.057 --0.222 +-0.486 +-0.507 +-0.528 +-0.549 +-0.569 +-0.589 +-0.609 +-0.628 +-0.647 +-0.666 +-0.684 +-0.701 +-0.719 +-0.736 +-0.752 +-0.768 +-0.783 +-0.798 +-0.813 +-0.827 +-0.841 +-0.854 +-0.866 +-0.878 +-0.89 +-0.901 -0.911 --0.779 --0.057 --0.24 --1.078 --0.926 --0.057 --0.256 --1.234 --1.064 --0.056 --0.27 --1.379 --1.192 --0.054 --0.282 --1.511 --1.308 --0.052 --0.29 --1.627 --1.411 --0.05 --0.296 --1.729 --1.501 --0.047 --0.299 --1.813 --1.577 --0.043 --0.299 --1.881 --1.637 --0.039 --0.296 --1.93 --1.682 --0.035 --0.29 --1.96 --1.71 --0.031 --0.281 --1.972 --1.722 --0.026 --0.27 --1.964 --1.718 --0.021 --0.256 --1.938 --1.696 --0.015 --0.24 --1.893 --1.659 --0.01 --0.221 --1.83 --1.605 --0.004 --0.201 --1.749 --1.536 -0.001 --0.178 --1.651 --1.453 -0.007 --0.154 --1.538 --1.355 -0.012 --0.128 --1.409 --1.244 -0.018 --0.101 --1.267 --1.121 -0.023 --0.073 --1.113 +-0.921 +-0.93 +-0.939 +-0.947 +-0.955 +-0.962 +-0.968 +-0.974 +-0.979 +-0.984 -0.988 -0.028 --0.044 --0.948 --0.845 -0.033 --0.015 --0.774 --0.693 -0.037 -0.015 --0.593 --0.536 +-0.991 +-0.994 +-0.997 +-0.998 +-0.999 +-1.0 +-1.0 +-0.999 +-0.998 +-0.996 +-0.993 +-0.99 +-0.987 +-0.982 +-0.977 +-0.972 +-0.966 +-0.959 +-0.952 +-0.944 +-0.936 +-0.927 +-0.917 +-0.907 +-0.897 +-0.886 +-0.874 +-0.862 +-0.849 +-0.836 +-0.822 +-0.808 +-0.793 +-0.778 +-0.762 +-0.746 +-0.73 +-0.713 +-0.695 +-0.677 +-0.659 +-0.64 +-0.621 +-0.602 +-0.582 +-0.562 +-0.542 +-0.521 +-0.5 +-0.478 +-0.457 +-0.435 +-0.412 +-0.39 +-0.367 +-0.344 +-0.321 +-0.298 +-0.274 +-0.251 +-0.227 +-0.203 +-0.179 +-0.155 +-0.13 +-0.106 +-0.081 +-0.057 +-0.032 +-0.008 +0.017 0.041 -0.044 --0.406 --0.372 -0.045 -0.073 --0.214 --0.206 -0.048 -0.101 --0.021 --0.037 -0.051 -0.128 -0.172 -0.132 -0.053 -0.154 -0.364 -0.3 -0.055 -0.178 -0.552 +0.066 +0.09 +0.115 +0.139 +0.163 +0.187 +0.211 +0.235 +0.259 +0.283 +0.306 +0.329 +0.352 +0.375 +0.398 +0.42 +0.443 0.464 -0.056 -0.201 -0.735 -0.625 -0.057 -0.222 +0.486 +0.507 +0.528 +0.549 +0.569 +0.589 +0.609 +0.628 +0.647 +0.666 +0.684 +0.701 +0.719 +0.736 +0.752 +0.768 +0.783 +0.798 +0.813 +0.827 +0.841 +0.854 +0.866 +0.878 +0.89 +0.901 0.911 -0.779 -0.057 -0.24 -1.078 -0.926 -0.057 -0.256 -1.234 -1.064 -0.056 -0.27 -1.379 -1.192 -0.054 -0.282 -1.511 -1.308 -0.052 -0.29 -1.627 -1.411 -0.05 -0.296 -1.729 -1.501 -0.047 -0.299 -1.813 -1.577 -0.043 -0.299 -1.881 -1.637 -0.039 -0.296 -1.93 -1.682 -0.035 -0.29 -1.96 -1.71 -0.031 -0.281 -1.972 -1.722 -0.026 -0.27 -1.964 -1.718 -0.021 -0.256 -1.938 -1.696 -0.015 -0.24 -1.893 -1.659 -0.01 -0.221 -1.83 -1.605 -0.004 -0.201 -1.749 -1.536 --0.001 -0.178 -1.651 -1.453 --0.007 -0.154 -1.538 -1.355 --0.012 -0.128 -1.409 -1.244 --0.018 -0.101 -1.267 -1.121 --0.023 -0.073 -1.113 +0.921 +0.93 +0.939 +0.947 +0.955 +0.962 +0.968 +0.974 +0.979 +0.984 0.988 --0.028 -0.044 -0.948 -0.845 --0.033 -0.015 -0.774 -0.693 --0.037 --0.015 -0.593 -0.536 +0.991 +0.994 +0.997 +0.998 +0.999 +1.0 +1.0 +0.999 +0.998 +0.996 +0.993 +0.99 +0.987 +0.982 +0.977 +0.972 +0.966 +0.959 +0.952 +0.944 +0.936 +0.927 +0.917 +0.907 +0.897 +0.886 +0.874 +0.862 +0.849 +0.836 +0.822 +0.808 +0.793 +0.778 +0.762 +0.746 +0.73 +0.713 +0.695 +0.677 +0.659 +0.64 +0.621 +0.602 +0.582 +0.562 +0.542 +0.521 +0.5 +0.478 +0.457 +0.435 +0.412 +0.39 +0.367 +0.344 +0.321 +0.298 +0.274 +0.251 +0.227 +0.203 +0.179 +0.155 +0.13 +0.106 +0.081 +0.057 +0.032 +0.008 +-0.017 -0.041 --0.044 -0.406 -0.372 --0.045 --0.073 -0.214 -0.206 --0.048 --0.101 -0.021 -0.037 --0.051 --0.128 --0.172 --0.132 --0.053 --0.154 --0.364 --0.3 --0.055 --0.178 --0.552 +-0.066 +-0.09 +-0.115 +-0.139 +-0.163 +-0.187 +-0.211 +-0.235 +-0.259 +-0.283 +-0.306 +-0.329 +-0.352 +-0.375 +-0.398 +-0.42 +-0.443 -0.464 --0.056 --0.201 --0.735 --0.625 --0.057 --0.222 +-0.486 +-0.507 +-0.528 +-0.549 +-0.569 +-0.589 +-0.609 +-0.628 +-0.647 +-0.666 +-0.684 +-0.701 +-0.719 +-0.736 +-0.752 +-0.768 +-0.783 +-0.798 +-0.813 +-0.827 +-0.841 +-0.854 +-0.866 +-0.878 +-0.89 +-0.901 -0.911 --0.779 --0.057 --0.24 --1.078 --0.926 --0.057 --0.256 --1.234 --1.064 --0.056 --0.27 --1.379 --1.192 --0.054 --0.282 --1.511 --1.308 --0.052 --0.29 --1.627 --1.411 --0.05 --0.296 --1.729 --1.501 --0.047 --0.299 --1.813 --1.577 --0.043 --0.299 --1.881 --1.637 --0.039 --0.296 --1.93 --1.682 --0.035 --0.29 --1.96 --1.71 --0.031 --0.281 --1.972 --1.722 --0.026 --0.27 --1.964 --1.718 --0.021 --0.256 --1.938 --1.696 --0.015 --0.24 --1.893 --1.659 --0.01 --0.221 --1.83 --1.605 --0.004 --0.201 --1.749 --1.536 -0.001 --0.178 --1.651 --1.453 -0.007 --0.154 --1.538 --1.355 -0.012 --0.128 --1.409 --1.244 -0.018 --0.101 --1.267 --1.121 -0.023 --0.073 --1.113 +-0.921 +-0.93 +-0.939 +-0.947 +-0.955 +-0.962 +-0.968 +-0.974 +-0.979 +-0.984 -0.988 -0.028 --0.044 --0.948 --0.845 -0.033 --0.015 --0.774 --0.693 -0.037 -0.015 --0.593 --0.536 +-0.991 +-0.994 +-0.997 +-0.998 +-0.999 +-1.0 +-1.0 +-0.999 +-0.998 +-0.996 +-0.993 +-0.99 +-0.987 +-0.982 +-0.977 +-0.972 +-0.966 +-0.959 +-0.952 +-0.944 +-0.936 +-0.927 +-0.917 +-0.907 +-0.897 +-0.886 +-0.874 +-0.862 +-0.849 +-0.836 +-0.822 +-0.808 +-0.793 +-0.778 +-0.762 +-0.746 +-0.73 +-0.713 +-0.695 +-0.677 +-0.659 +-0.64 +-0.621 +-0.602 +-0.582 +-0.562 +-0.542 +-0.521 +-0.5 +-0.478 +-0.457 +-0.435 +-0.412 +-0.39 +-0.367 +-0.344 +-0.321 +-0.298 +-0.274 +-0.251 +-0.227 +-0.203 +-0.179 +-0.155 +-0.13 +-0.106 +-0.081 +-0.057 +-0.032 +-0.008 +0.017 0.041 -0.044 --0.406 --0.372 -0.045 -0.073 --0.214 --0.206 -0.048 -0.101 --0.021 --0.037 -0.051 -0.128 -0.172 -0.132 -0.053 -0.154 -0.364 -0.3 -0.055 -0.178 -0.552 +0.066 +0.09 +0.115 +0.139 +0.163 +0.187 +0.211 +0.235 +0.259 +0.283 +0.306 +0.329 +0.352 +0.375 +0.398 +0.42 +0.443 0.464 -0.056 -0.201 -0.735 -0.625 -0.057 -0.222 +0.486 +0.507 +0.528 +0.549 +0.569 +0.589 +0.609 +0.628 +0.647 +0.666 +0.684 +0.701 +0.719 +0.736 +0.752 +0.768 +0.783 +0.798 +0.813 +0.827 +0.841 +0.854 +0.866 +0.878 +0.89 +0.901 0.911 -0.779 -0.057 -0.24 -1.078 -0.926 -0.057 -0.256 -1.234 -1.064 -0.056 -0.27 -1.379 -1.192 -0.054 -0.282 -1.511 -1.308 -0.052 -0.29 -1.627 -1.411 -0.05 -0.296 -1.729 -1.501 -0.047 -0.299 -1.813 -1.577 -0.043 -0.299 -1.881 -1.637 -0.039 -0.296 -1.93 -1.682 -0.035 -0.29 -1.96 -1.71 -0.031 -0.281 -1.972 -1.722 -0.026 -0.27 -1.964 -1.718 -0.021 -0.256 -1.938 -1.696 -0.015 -0.24 -1.893 -1.659 -0.01 -0.221 -1.83 -1.605 -0.004 -0.201 -1.749 -1.536 --0.001 -0.178 -1.651 -1.453 --0.007 -0.154 -1.538 -1.355 --0.012 -0.128 -1.409 -1.244 --0.018 -0.101 -1.267 -1.121 --0.023 -0.073 -1.113 +0.921 +0.93 +0.939 +0.947 +0.955 +0.962 +0.968 +0.974 +0.979 +0.984 0.988 --0.028 -0.044 -0.948 -0.845 --0.033 -0.015 -0.774 -0.693 --0.037 --0.015 -0.593 -0.536 +0.991 +0.994 +0.997 +0.998 +0.999 +1.0 +1.0 +0.999 +0.998 +0.996 +0.993 +0.99 +0.987 +0.982 +0.977 +0.972 +0.966 +0.959 +0.952 +0.944 +0.936 +0.927 +0.917 +0.907 +0.897 +0.886 +0.874 +0.862 +0.849 +0.836 +0.822 +0.808 +0.793 +0.778 +0.762 +0.746 +0.73 +0.713 +0.695 +0.677 +0.659 +0.64 +0.621 +0.602 +0.582 +0.562 +0.542 +0.521 +0.5 +0.478 +0.457 +0.435 +0.412 +0.39 +0.367 +0.344 +0.321 +0.298 +0.274 +0.251 +0.227 +0.203 +0.179 +0.155 +0.13 +0.106 +0.081 +0.057 +0.032 +0.008 +-0.017 -0.041 --0.044 -0.406 -0.372 --0.045 --0.073 -0.214 -0.206 --0.048 --0.101 -0.021 -0.037 --0.051 --0.128 --0.172 --0.132 --0.053 --0.154 --0.364 --0.3 --0.055 --0.178 --0.552 +-0.066 +-0.09 +-0.115 +-0.139 +-0.163 +-0.187 +-0.211 +-0.235 +-0.259 +-0.283 +-0.306 +-0.329 +-0.352 +-0.375 +-0.398 +-0.42 +-0.443 -0.464 --0.056 --0.201 --0.735 --0.625 --0.057 --0.222 +-0.486 +-0.507 +-0.528 +-0.549 +-0.569 +-0.589 +-0.609 +-0.628 +-0.647 +-0.666 +-0.684 +-0.701 +-0.719 +-0.736 +-0.752 +-0.768 +-0.783 +-0.798 +-0.813 +-0.827 +-0.841 +-0.854 +-0.866 +-0.878 +-0.89 +-0.901 -0.911 --0.779 --0.057 --0.24 --1.078 --0.926 --0.057 --0.256 --1.234 --1.064 --0.056 --0.27 --1.379 --1.192 --0.054 --0.282 --1.511 --1.308 --0.052 --0.29 --1.627 --1.411 --0.05 --0.296 --1.729 --1.501 --0.047 --0.299 --1.813 --1.577 --0.043 --0.299 --1.881 --1.637 --0.039 --0.296 --1.93 --1.682 --0.035 --0.29 --1.96 --1.71 --0.031 --0.281 --1.972 --1.722 --0.026 --0.27 --1.964 --1.718 --0.021 --0.256 --1.938 --1.696 --0.015 --0.24 --1.893 --1.659 --0.01 --0.221 --1.83 --1.605 --0.004 --0.201 --1.749 --1.536 -0.001 --0.178 --1.651 --1.453 -0.007 --0.154 --1.538 --1.355 -0.012 --0.128 --1.409 --1.244 -0.018 --0.101 --1.267 --1.121 -0.023 --0.073 --1.113 +-0.921 +-0.93 +-0.939 +-0.947 +-0.955 +-0.962 +-0.968 +-0.974 +-0.979 +-0.984 -0.988 -0.028 --0.044 --0.948 --0.845 -0.033 --0.015 --0.774 --0.693 -0.037 -0.015 --0.593 --0.536 +-0.991 +-0.994 +-0.997 +-0.998 +-0.999 +-1.0 +-1.0 +-0.999 +-0.998 +-0.996 +-0.993 +-0.99 +-0.987 +-0.982 +-0.977 +-0.972 +-0.966 +-0.959 +-0.952 +-0.944 +-0.936 +-0.927 +-0.917 +-0.907 +-0.897 +-0.886 +-0.874 +-0.862 +-0.849 +-0.836 +-0.822 +-0.808 +-0.793 +-0.778 +-0.762 +-0.746 +-0.73 +-0.713 +-0.695 +-0.677 +-0.659 +-0.64 +-0.621 +-0.602 +-0.582 +-0.562 +-0.542 +-0.521 +-0.5 +-0.478 +-0.457 +-0.435 +-0.412 +-0.39 +-0.367 +-0.344 +-0.321 +-0.298 +-0.274 +-0.251 +-0.227 +-0.203 +-0.179 +-0.155 +-0.13 +-0.106 +-0.081 +-0.057 +-0.032 +-0.008 +0.017 0.041 -0.044 --0.406 --0.372 -0.045 -0.073 --0.214 --0.206 -0.048 -0.101 --0.021 --0.037 -0.051 -0.128 -0.172 -0.132 -0.053 -0.154 -0.364 -0.3 -0.055 -0.178 -0.552 +0.066 +0.09 +0.115 +0.139 +0.163 +0.187 +0.211 +0.235 +0.259 +0.283 +0.306 +0.329 +0.352 +0.375 +0.398 +0.42 +0.443 0.464 -0.056 -0.201 -0.735 -0.625 -0.057 -0.222 +0.486 +0.507 +0.528 +0.549 +0.569 +0.589 +0.609 +0.628 +0.647 +0.666 +0.684 +0.701 +0.719 +0.736 +0.752 +0.768 +0.783 +0.798 +0.813 +0.827 +0.841 +0.854 +0.866 +0.878 +0.89 +0.901 0.911 -0.779 -0.057 -0.24 -1.078 -0.926 -0.057 -0.256 -1.234 -1.064 -0.056 -0.27 -1.379 -1.192 -0.054 -0.282 -1.511 -1.308 -0.052 -0.29 -1.627 -1.411 -0.05 -0.296 -1.729 -1.501 -0.047 -0.299 -1.813 -1.577 -0.043 -0.299 -1.881 -1.637 -0.039 -0.296 -1.93 -1.682 -0.035 -0.29 -1.96 -1.71 -0.031 -0.281 -1.972 -1.722 -0.026 -0.27 -1.964 -1.718 -0.021 -0.256 -1.938 -1.696 -0.015 -0.24 -1.893 -1.659 -0.01 -0.221 -1.83 -1.605 -0.004 -0.201 -1.749 -1.536 --0.001 -0.178 -1.651 -1.453 --0.007 -0.154 -1.538 -1.355 --0.012 -0.128 -1.409 -1.244 --0.018 -0.101 -1.267 -1.121 --0.023 -0.073 -1.113 +0.921 +0.93 +0.939 +0.947 +0.955 +0.962 +0.968 +0.974 +0.979 +0.984 0.988 --0.028 -0.044 -0.948 -0.845 --0.033 -0.015 -0.774 -0.693 --0.037 --0.015 -0.593 -0.536 +0.991 +0.994 +0.997 +0.998 +0.999 +1.0 +1.0 +0.999 +0.998 +0.996 +0.993 +0.99 +0.987 +0.982 +0.977 +0.972 +0.966 +0.959 +0.952 +0.944 +0.936 +0.927 +0.917 +0.907 +0.897 +0.886 +0.874 +0.862 +0.849 +0.836 +0.822 +0.808 +0.793 +0.778 +0.762 +0.746 +0.73 +0.713 +0.695 +0.677 +0.659 +0.64 +0.621 +0.602 +0.582 +0.562 +0.542 +0.521 +0.5 +0.478 +0.457 +0.435 +0.412 +0.39 +0.367 +0.344 +0.321 +0.298 +0.274 +0.251 +0.227 +0.203 +0.179 +0.155 +0.13 +0.106 +0.081 +0.057 +0.032 +0.008 +-0.017 -0.041 --0.044 -0.406 -0.372 --0.045 --0.073 -0.214 -0.206 --0.048 --0.101 -0.021 -0.037 --0.051 --0.128 --0.172 --0.132 --0.053 --0.154 --0.364 --0.3 --0.055 --0.178 --0.552 +-0.066 +-0.09 +-0.115 +-0.139 +-0.163 +-0.187 +-0.211 +-0.235 +-0.259 +-0.283 +-0.306 +-0.329 +-0.352 +-0.375 +-0.398 +-0.42 +-0.443 -0.464 --0.056 --0.201 --0.735 --0.625 --0.057 --0.222 +-0.486 +-0.507 +-0.528 +-0.549 +-0.569 +-0.589 +-0.609 +-0.628 +-0.647 +-0.666 +-0.684 +-0.701 +-0.719 +-0.736 +-0.752 +-0.768 +-0.783 +-0.798 +-0.813 +-0.827 +-0.841 +-0.854 +-0.866 +-0.878 +-0.89 +-0.901 -0.911 --0.779 --0.057 --0.24 --1.078 --0.926 --0.057 --0.256 --1.234 --1.064 --0.056 --0.27 --1.379 --1.192 --0.054 --0.282 --1.511 --1.308 --0.052 --0.29 --1.627 --1.411 --0.05 --0.296 --1.729 --1.501 --0.047 --0.299 --1.813 --1.577 --0.043 --0.299 --1.881 --1.637 --0.039 --0.296 --1.93 --1.682 --0.035 --0.29 --1.96 --1.71 --0.031 --0.281 --1.972 --1.722 --0.026 --0.27 --1.964 --1.718 --0.021 --0.256 --1.938 --1.696 --0.015 --0.24 --1.893 --1.659 --0.01 --0.221 --1.83 --1.605 --0.004 --0.201 --1.749 --1.536 -0.001 --0.178 --1.651 --1.453 -0.007 --0.154 --1.538 --1.355 -0.012 --0.128 --1.409 --1.244 -0.018 --0.101 --1.267 --1.121 -0.023 --0.073 --1.113 +-0.921 +-0.93 +-0.939 +-0.947 +-0.955 +-0.962 +-0.968 +-0.974 +-0.979 +-0.984 -0.988 -0.028 --0.044 --0.948 --0.845 -0.033 --0.015 --0.774 --0.693 -0.037 -0.015 --0.593 --0.536 -0.041 -0.044 --0.406 --0.372 -0.045 -0.073 --0.214 +-0.991 +-0.994 +-0.997 +-0.998 +-0.999 +-1.0 +-1.0 +-0.999 +-0.998 +-0.996 +-0.993 +-0.99 +-0.987 +-0.982 +-0.977 +-0.972 +-0.966 +-0.959 +-0.952 +-0.944 +-0.936 +-0.927 +-0.917 +-0.907 +-0.897 +-0.886 +-0.874 +-0.862 +-0.849 +-0.836 +-0.822 +-0.808 +-0.793 +-0.778 +-0.762 +-0.746 +-0.73 +-0.713 +-0.695 +-0.677 +-0.659 +-0.64 diff --git a/src/oversample/snapshots/valib__oversample__tests__oversampled_dsp_block.snap b/src/oversample/snapshots/valib__oversample__tests__oversampled_dsp_block.snap index 1d4cacb..05ade91 100644 --- a/src/oversample/snapshots/valib__oversample__tests__oversampled_dsp_block.snap +++ b/src/oversample/snapshots/valib__oversample__tests__oversampled_dsp_block.snap @@ -2,67 +2,67 @@ source: src/oversample/mod.rs expression: "&output as &[_]" --- --0.012 --1.177 --1.034 --1.009 --1.0 --1.0 --1.0 --1.0 --1.0 --1.0 --1.0 --1.0 --1.0 --0.796 -1.239 -1.038 -0.995 -0.999 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 +-0.0 +-0.0 +-0.0 +-0.0 +-0.001 +-0.015 +-0.133 +-0.52 +-1.014 +-1.135 +-0.996 +-0.976 +-1.006 +-1.002 +-0.999 +-1.0 +-1.0 +-0.997 +-0.943 +-0.599 +0.306 +1.18 +1.209 +0.951 +0.971 +1.014 1.0 +0.999 1.0 --0.713 --0.888 --0.995 +0.999 +0.984 +0.832 +0.208 +-0.821 +-1.296 +-1.054 +-0.936 -1.004 +-1.007 +-0.997 -1.0 -1.0 --1.0 --1.0 --1.0 --1.0 --1.0 --1.0 --0.796 -1.239 -1.038 -0.995 -0.999 -1.0 -1.0 -1.0 -1.0 -1.0 -1.0 +-0.997 +-0.943 +-0.599 +0.306 +1.18 +1.209 +0.951 +0.971 +1.014 1.0 +0.999 1.0 --0.713 --0.888 --0.995 +0.999 +0.984 +0.832 +0.208 +-0.821 +-1.296 +-1.054 +-0.936 -1.004 --1.0 --1.0 --1.0 --1.0 --1.0 --1.0 --1.0 --1.0 --0.796 +-1.007 diff --git a/src/oversample/snapshots/valib__oversample__tests__post os.snap b/src/oversample/snapshots/valib__oversample__tests__post os.snap index 876668e..d7ff072 100644 --- a/src/oversample/snapshots/valib__oversample__tests__post os.snap +++ b/src/oversample/snapshots/valib__oversample__tests__post os.snap @@ -4,513 +4,513 @@ expression: "&out as &[_]" --- 0.0 0.0 -0.069 -0.116 -0.185 -0.259 -0.331 -0.399 -0.463 -0.523 -0.577 -0.627 -0.67 -0.706 -0.736 -0.759 -0.775 -0.783 -0.783 -0.776 -0.761 -0.739 -0.71 -0.675 -0.632 -0.584 -0.53 -0.471 -0.407 -0.339 -0.269 -0.195 -0.12 -0.043 --0.034 --0.11 --0.186 --0.26 --0.331 --0.399 --0.463 --0.523 --0.577 --0.627 --0.67 --0.706 --0.736 --0.759 --0.775 --0.783 --0.783 --0.776 --0.761 --0.739 --0.71 --0.675 --0.632 --0.584 --0.53 --0.471 --0.407 --0.339 --0.269 --0.195 --0.12 --0.043 -0.034 -0.11 -0.186 -0.26 -0.331 -0.399 -0.463 -0.523 -0.577 -0.627 -0.67 -0.706 -0.736 -0.759 -0.775 -0.783 -0.783 -0.776 -0.761 -0.739 -0.71 -0.675 -0.632 -0.584 -0.53 -0.471 -0.407 -0.339 -0.269 -0.195 -0.12 -0.043 --0.034 --0.11 --0.186 --0.26 --0.331 --0.399 --0.463 --0.523 --0.577 --0.627 --0.67 --0.706 --0.736 --0.759 --0.775 --0.783 --0.783 --0.776 --0.761 --0.739 --0.71 --0.675 --0.632 --0.584 --0.53 --0.471 --0.407 --0.339 --0.269 --0.195 --0.12 --0.043 -0.034 -0.11 -0.186 -0.26 -0.331 -0.399 -0.463 -0.523 -0.577 -0.627 -0.67 -0.706 -0.736 -0.759 -0.775 -0.783 -0.783 -0.776 -0.761 -0.739 -0.71 -0.675 -0.632 -0.584 -0.53 -0.471 -0.407 -0.339 -0.269 -0.195 -0.12 -0.043 --0.034 --0.11 --0.186 --0.26 --0.331 --0.399 --0.463 --0.523 --0.577 --0.627 --0.67 --0.706 --0.736 --0.759 --0.775 --0.783 --0.783 --0.776 --0.761 --0.739 --0.71 --0.675 --0.632 --0.584 --0.53 --0.471 --0.407 --0.339 --0.269 --0.195 --0.12 --0.043 -0.034 -0.11 -0.186 -0.26 -0.331 -0.399 -0.463 -0.523 -0.577 -0.627 -0.67 -0.706 -0.736 -0.759 -0.775 -0.783 -0.783 -0.776 -0.761 -0.739 -0.71 -0.675 -0.632 -0.584 -0.53 -0.471 -0.407 -0.339 -0.269 -0.195 -0.12 -0.043 --0.034 --0.11 --0.186 --0.26 --0.331 --0.399 --0.463 --0.523 --0.577 --0.627 --0.67 --0.706 --0.736 --0.759 --0.775 --0.783 --0.783 --0.776 --0.761 --0.739 --0.71 --0.675 --0.632 --0.584 --0.53 --0.471 --0.407 --0.339 --0.269 --0.195 --0.12 --0.043 -0.034 -0.11 -0.186 -0.26 -0.331 -0.399 -0.463 -0.523 -0.577 -0.627 -0.67 -0.706 -0.736 -0.759 -0.775 -0.783 -0.783 -0.776 -0.761 -0.739 -0.71 -0.675 -0.632 -0.584 -0.53 -0.471 -0.407 -0.339 -0.269 -0.195 -0.12 -0.043 --0.034 --0.11 --0.186 --0.26 --0.331 --0.399 --0.463 --0.523 --0.577 --0.627 --0.67 --0.706 --0.736 --0.759 --0.775 --0.783 --0.783 --0.776 --0.761 --0.739 --0.71 --0.675 --0.632 --0.584 --0.53 --0.471 --0.407 --0.339 --0.269 --0.195 --0.12 --0.043 -0.034 -0.11 -0.186 -0.26 -0.331 -0.399 -0.463 -0.523 -0.577 -0.627 -0.67 -0.706 -0.736 -0.759 -0.775 -0.783 -0.783 -0.776 -0.761 -0.739 -0.71 -0.675 -0.632 -0.584 -0.53 -0.471 -0.407 -0.339 -0.269 -0.195 -0.12 -0.043 --0.034 --0.11 --0.186 --0.26 --0.331 --0.399 --0.463 --0.523 --0.577 --0.627 --0.67 --0.706 --0.736 --0.759 --0.775 --0.783 --0.783 --0.776 --0.761 --0.739 --0.71 --0.675 --0.632 --0.584 --0.53 --0.471 --0.407 --0.339 --0.269 --0.195 --0.12 --0.043 -0.034 -0.11 -0.186 -0.26 -0.331 -0.399 -0.463 -0.523 -0.577 -0.627 -0.67 -0.706 -0.736 -0.759 -0.775 -0.783 -0.783 -0.776 -0.761 -0.739 -0.71 -0.675 -0.632 -0.584 -0.53 -0.471 -0.407 -0.339 -0.269 -0.195 -0.12 -0.043 --0.034 --0.11 --0.186 --0.26 --0.331 --0.399 --0.463 --0.523 --0.577 --0.627 --0.67 --0.706 --0.736 --0.759 --0.775 --0.783 --0.783 --0.776 --0.761 --0.739 --0.71 --0.675 --0.632 --0.584 --0.53 --0.471 --0.407 --0.339 --0.269 --0.195 --0.12 --0.043 -0.034 -0.11 -0.186 -0.26 -0.331 -0.399 -0.463 -0.523 -0.577 -0.627 -0.67 -0.706 -0.736 -0.759 -0.775 -0.783 -0.783 -0.776 -0.761 -0.739 -0.71 -0.675 -0.632 -0.584 -0.53 -0.471 -0.407 -0.339 -0.268 -0.195 -0.12 -0.043 --0.034 --0.11 --0.186 --0.26 --0.331 --0.399 --0.463 --0.523 --0.577 --0.627 --0.67 --0.706 --0.736 --0.759 --0.775 --0.783 --0.783 --0.776 --0.761 --0.739 --0.71 --0.675 --0.632 --0.584 --0.53 --0.471 --0.407 --0.339 --0.269 --0.195 +0.0 +0.0 +0.0 +0.0 +0.0 +0.0 +0.0 +0.0 +0.0 +0.0 +0.002 +0.01 +0.04 +0.109 +0.214 +0.325 +0.418 +0.5 +0.582 +0.66 +0.73 +0.794 +0.849 +0.897 +0.936 +0.966 +0.987 +0.998 +0.999 +0.991 +0.974 +0.947 +0.911 +0.866 +0.813 +0.751 +0.683 +0.608 +0.528 +0.442 +0.352 +0.258 +0.162 +0.065 +-0.033 +-0.131 +-0.228 +-0.322 +-0.413 +-0.5 +-0.583 +-0.66 +-0.73 +-0.794 +-0.849 +-0.897 +-0.936 +-0.966 +-0.987 +-0.998 +-0.999 +-0.991 +-0.974 +-0.947 +-0.911 +-0.866 +-0.813 +-0.751 +-0.683 +-0.608 +-0.528 +-0.442 +-0.352 +-0.258 +-0.162 +-0.065 +0.033 +0.131 +0.228 +0.322 +0.413 +0.5 +0.583 +0.66 +0.73 +0.794 +0.849 +0.897 +0.936 +0.966 +0.987 +0.998 +0.999 +0.991 +0.974 +0.947 +0.911 +0.866 +0.813 +0.751 +0.683 +0.608 +0.528 +0.442 +0.352 +0.258 +0.162 +0.065 +-0.033 +-0.131 +-0.228 +-0.322 +-0.413 +-0.5 +-0.583 +-0.66 +-0.73 +-0.794 +-0.849 +-0.897 +-0.936 +-0.966 +-0.987 +-0.998 +-0.999 +-0.991 +-0.974 +-0.947 +-0.911 +-0.866 +-0.813 +-0.751 +-0.683 +-0.608 +-0.528 +-0.442 +-0.352 +-0.258 +-0.162 +-0.065 +0.033 +0.131 +0.228 +0.322 +0.413 +0.5 +0.583 +0.66 +0.73 +0.794 +0.849 +0.897 +0.936 +0.966 +0.987 +0.998 +0.999 +0.991 +0.974 +0.947 +0.911 +0.866 +0.813 +0.751 +0.683 +0.608 +0.528 +0.442 +0.352 +0.258 +0.162 +0.065 +-0.033 +-0.131 +-0.228 +-0.322 +-0.413 +-0.5 +-0.583 +-0.66 +-0.73 +-0.794 +-0.849 +-0.897 +-0.936 +-0.966 +-0.987 +-0.998 +-0.999 +-0.991 +-0.974 +-0.947 +-0.911 +-0.866 +-0.813 +-0.751 +-0.683 +-0.608 +-0.528 +-0.442 +-0.352 +-0.258 +-0.162 +-0.065 +0.033 +0.131 +0.228 +0.322 +0.413 +0.5 +0.583 +0.66 +0.73 +0.794 +0.849 +0.897 +0.936 +0.966 +0.987 +0.998 +0.999 +0.991 +0.974 +0.947 +0.911 +0.866 +0.813 +0.751 +0.683 +0.608 +0.528 +0.442 +0.352 +0.258 +0.162 +0.065 +-0.033 +-0.131 +-0.228 +-0.322 +-0.413 +-0.5 +-0.583 +-0.66 +-0.73 +-0.794 +-0.849 +-0.897 +-0.936 +-0.966 +-0.987 +-0.998 +-0.999 +-0.991 +-0.974 +-0.947 +-0.911 +-0.866 +-0.813 +-0.751 +-0.683 +-0.608 +-0.528 +-0.442 +-0.352 +-0.258 +-0.162 +-0.065 +0.033 +0.131 +0.228 +0.322 +0.413 +0.5 +0.583 +0.66 +0.73 +0.794 +0.849 +0.897 +0.936 +0.966 +0.987 +0.998 +0.999 +0.991 +0.974 +0.947 +0.911 +0.866 +0.813 +0.751 +0.683 +0.608 +0.528 +0.442 +0.352 +0.258 +0.162 +0.065 +-0.033 +-0.131 +-0.228 +-0.322 +-0.413 +-0.5 +-0.583 +-0.66 +-0.73 +-0.794 +-0.849 +-0.897 +-0.936 +-0.966 +-0.987 +-0.998 +-0.999 +-0.991 +-0.974 +-0.947 +-0.911 +-0.866 +-0.813 +-0.751 +-0.683 +-0.608 +-0.528 +-0.442 +-0.352 +-0.258 +-0.162 +-0.065 +0.033 +0.131 +0.228 +0.322 +0.413 +0.5 +0.583 +0.66 +0.73 +0.794 +0.849 +0.897 +0.936 +0.966 +0.987 +0.998 +0.999 +0.991 +0.974 +0.947 +0.911 +0.866 +0.813 +0.751 +0.683 +0.608 +0.528 +0.442 +0.352 +0.258 +0.162 +0.065 +-0.033 +-0.131 +-0.228 +-0.322 +-0.413 +-0.5 +-0.583 +-0.66 +-0.73 +-0.794 +-0.849 +-0.897 +-0.936 +-0.966 +-0.987 +-0.998 +-0.999 +-0.991 +-0.974 +-0.947 +-0.911 +-0.866 +-0.813 +-0.751 +-0.683 +-0.608 +-0.528 +-0.442 +-0.352 +-0.258 +-0.162 +-0.065 +0.033 +0.131 +0.228 +0.322 +0.413 +0.5 +0.583 +0.66 +0.73 +0.794 +0.849 +0.897 +0.936 +0.966 +0.987 +0.998 +0.999 +0.991 +0.974 +0.947 +0.911 +0.866 +0.813 +0.751 +0.683 +0.608 +0.528 +0.442 +0.352 +0.258 +0.162 +0.065 +-0.033 +-0.131 +-0.228 +-0.322 +-0.413 +-0.5 +-0.583 +-0.66 +-0.73 +-0.794 +-0.849 +-0.897 +-0.936 +-0.966 +-0.987 +-0.998 +-0.999 +-0.991 +-0.974 +-0.947 +-0.911 +-0.866 +-0.813 +-0.751 +-0.683 +-0.608 +-0.528 +-0.442 +-0.352 +-0.258 +-0.162 +-0.065 +0.033 +0.131 +0.228 +0.322 +0.413 +0.5 +0.583 +0.66 +0.73 +0.794 +0.849 +0.897 +0.936 +0.966 +0.987 +0.998 +0.999 +0.991 +0.974 +0.947 +0.911 +0.866 +0.813 +0.751 +0.683 +0.608 +0.528 +0.442 +0.352 +0.258 +0.162 +0.065 +-0.033 +-0.131 +-0.228 +-0.322 +-0.413 +-0.5 +-0.583 +-0.66 +-0.73 +-0.794 +-0.849 +-0.897 +-0.936 +-0.966 +-0.987 +-0.998 +-0.999 +-0.991 From 0d1fe6c52ebd1c0712e9cb1fa276046d52c4ec8d Mon Sep 17 00:00:00 2001 From: Nathan Graule Date: Fri, 22 Mar 2024 20:58:36 +0100 Subject: [PATCH 21/21] fix(biquad): merge conflicting biquad modules --- src/filters/biquad.rs | 284 -------------------------------------- src/filters/biquad/mod.rs | 18 ++- 2 files changed, 16 insertions(+), 286 deletions(-) delete mode 100644 src/filters/biquad.rs diff --git a/src/filters/biquad.rs b/src/filters/biquad.rs deleted file mode 100644 index ba5b29e..0000000 --- a/src/filters/biquad.rs +++ /dev/null @@ -1,284 +0,0 @@ -//! Transposed Direct Form II Biquad implementation -//! -//! Nonlinearities based on -//! -//! # Usage -//! -//! ```rust -//! use valib::dsp::DSP; -//! use valib::filters::biquad::Biquad; -//! use valib::saturators::Tanh; -//! let mut lowpass = Biquad::::lowpass(0.25 /* normalized frequency */, 0.707 /* Q */); -//! let output = lowpass.process([0.0]); -//! ``` - -use nalgebra::Complex; -use numeric_literals::replace_float_literals; - -use crate::dsp::analysis::DspAnalysis; -use crate::dsp::DSP; -use crate::{ - saturators::{Dynamic, Saturator}, - Scalar, -}; - -pub mod design; - -/// Biquad struct in Transposed Direct Form II. Optionally, a [`Saturator`] instance can be used -/// to apply waveshaping to the internal states. -#[derive(Debug, Copy, Clone)] -pub struct Biquad { - na: [T; 2], - b: [T; 3], - s: [T; 2], - sats: [S; 2], -} - -// TODO: No need to restrict this on dynamic saturators only -impl Biquad> { - /// Apply these new saturators to this Biquad instance, returning a new instance of it. - pub fn with_saturators(mut self, a: Dynamic, b: Dynamic) -> Biquad> { - self.set_saturators(a, b); - self - } - - /// Replace the saturators in this Biquad instance with the provided values. - pub fn set_saturators(&mut self, a: Dynamic, b: Dynamic) { - self.sats = [a, b]; - } -} - -impl Biquad { - pub fn update_coefficients(&mut self, other: &Self) { - self.na = other.na; - self.b = other.b; - } -} - -// TODO: Make those return linear biquads, where the user can then pass their saturators directly through `with_saturators` -impl Biquad { - /// Create a new instance of a Biquad with the provided poles and zeros coefficients. - pub fn new(b: [T; 3], a: [T; 2]) -> Self { - Self { - na: a.map(T::neg), - b, - s: [T::zero(); 2], - sats: Default::default(), - } - } - - /// Create a lowpass with the provided frequency cutoff coefficient (normalized where 1 == samplerate) and resonance factor. - #[replace_float_literals(T::from_f64(literal))] - pub fn lowpass(fc: T, q: T) -> Self { - let w0 = T::simd_two_pi() * fc; - let (sw0, cw0) = w0.simd_sin_cos(); - let b1 = 1. - cw0; - let b0 = b1 / 2.; - let b2 = b0; - - let alpha = sw0 / (2. * q); - let a0 = 1. + alpha; - let a1 = -2. * cw0; - let a2 = 1. - alpha; - - Self::new([b0, b1, b2].map(|b| b / a0), [a1, a2].map(|a| a / a0)) - } - - /// Create a highpass with the provided frequency cutoff coefficient (normalized where 1 == samplerate) and resonance factor. - #[replace_float_literals(T::from_f64(literal))] - pub fn highpass(fc: T, q: T) -> Self { - let w0 = T::simd_two_pi() * fc; - let (sw0, cw0) = w0.simd_sin_cos(); - let b1 = -(1. + cw0); - let b0 = -b1 / 2.; - let b2 = b0; - - let alpha = sw0 / (2. * q); - let a0 = 1. + alpha; - let a1 = -2. * cw0; - let a2 = 1. - alpha; - - Self::new([b0, b1, b2].map(|b| b / a0), [a1, a2].map(|a| a / a0)) - } - - /// Create a bandpass with the provided frequency cutoff coefficient (normalized where 1 == samplerate) and resonance factor. - /// The resulting bandpass is normalized so that the maximum of the transfer function sits at 0 dB, making it - /// appear as having a sharper slope than it actually does. - #[replace_float_literals(T::from_f64(literal))] - pub fn bandpass_peak0(fc: T, q: T) -> Self { - let w0 = T::simd_two_pi() * fc; - let (sw0, cw0) = w0.simd_sin_cos(); - let alpha = sw0 / (2. * q); - - let b0 = alpha; - let b1 = 0.; - let b2 = -alpha; - - let a0 = 1. + alpha; - let a1 = -2. * cw0; - let a2 = 1. - alpha; - - Self::new([b0, b1, b2].map(|b| b / a0), [a1, a2].map(|a| a / a0)) - } - - /// Create a notch with the provided frequency cutoff coefficient (normalized where 1 == samplerate) and resonance factor. - #[replace_float_literals(T::from_f64(literal))] - pub fn notch(fc: T, q: T) -> Self { - let w0 = T::simd_two_pi() * fc; - let (sw0, cw0) = w0.simd_sin_cos(); - let alpha = sw0 / (2. * q); - - let b0 = 1.; - let b1 = -2. * cw0; - let b2 = 1.; - - let a0 = 1. + alpha; - let a1 = -2. * cw0; - let a2 = 1. - alpha; - - Self::new([b0, b1, b2].map(|b| b / a0), [a1, a2].map(|a| a / a0)) - } - - /// Create an allpass with the provided frequency cutoff coefficient (normalized where 1 == samplerate) and resonance factor. - #[replace_float_literals(T::from_f64(literal))] - pub fn allpass(fc: T, q: T) -> Self { - let w0 = T::simd_two_pi() * fc; - let (sw0, cw0) = w0.simd_sin_cos(); - let alpha = sw0 / (2. * q); - - let b0 = 1. - alpha; - let b1 = -2. * cw0; - let b2 = 1. + alpha; - - let a0 = b2; - let a1 = b1; - let a2 = b0; - - Self::new([b0, b1, b2].map(|b| b / a0), [a1, a2].map(|a| a / a0)) - } - - /// Create a peaking filter with the provided frequency cutoff coefficient (normalized where 1 == samplerate) and resonance factor. - #[replace_float_literals(T::from_f64(literal))] - pub fn peaking(fc: T, q: T, amp: T) -> Self { - let w0 = T::simd_two_pi() * fc; - let (sw0, cw0) = w0.simd_sin_cos(); - let alpha = sw0 / (2. * q); - - let b0 = 1. + alpha * amp; - let b1 = -2. * cw0; - let b2 = 1. - alpha * amp; - - let a0 = 1. + alpha / amp; - let a1 = b1; - let a2 = 1. - alpha / amp; - - Self::new([b0, b1, b2].map(|b| b / a0), [a1, a2].map(|a| a / a0)) - } - - /// Create a low shelf with the provided frequency cutoff coefficient (normalized where 1 == samplerate) and resonance factor. - #[replace_float_literals(T::from_f64(literal))] - pub fn lowshelf(fc: T, q: T, amp: T) -> Self { - let w0 = T::simd_two_pi() * fc; - let (sw0, cw0) = w0.simd_sin_cos(); - let alpha = sw0 / (2. * q); - - let t = (amp + 1.) - (amp - 1.) * cw0; - let tp = (amp - 1.) - (amp + 1.) * cw0; - let u = 2. * amp.simd_sqrt() * alpha; - - let b0 = amp * (t + u); - let b1 = 2. * amp * (tp); - let b2 = amp * (t - u); - - let t = (amp + 1.) + (amp - 1.) * cw0; - let a0 = t + u; - let a1 = -2. * ((amp - 1.) + (amp + 1.) * cw0); - let a2 = t - u; - - Self::new([b0, b1, b2].map(|b| b / a0), [a1, a2].map(|a| a / a0)) - } - - /// Create a high shelf with the provided frequency cutoff coefficient (normalized where 1 == samplerate) and resonance factor. - #[replace_float_literals(T::from_f64(literal))] - pub fn highshelf(fc: T, q: T, amp: T) -> Self { - let w0 = T::simd_two_pi() * fc; - let (sw0, cw0) = w0.simd_sin_cos(); - let alpha = sw0 / (2. * q); - - let b0 = amp * ((amp + 1.) + (amp - 1.) * cw0 + 2. * amp.simd_sqrt() * alpha); - let b1 = -2. * amp * ((amp + 1.) + (amp - 1.) * cw0); - let b2 = amp * ((amp + 1.) + (amp - 1.) * cw0 - (2. * amp.simd_sqrt() * alpha)); - - let a0 = (amp + 1.) - (amp - 1.) * cw0 + 2. * amp.simd_sqrt() * alpha; - let a1 = 2. * ((amp - 1.) - (amp + 1.) * cw0); - let a2 = ((amp + 1.) - (amp - 1.) * cw0) - 2. * amp.simd_sqrt() * alpha; - - Self::new([b0, b1, b2].map(|b| b / a0), [a1, a2].map(|a| a / a0)) - } - - pub fn reset(&mut self) { - self.s.fill(T::zero()); - } -} - -impl> DSP<1, 1> for Biquad { - type Sample = T; - - #[inline] - #[replace_float_literals(T::from_f64(literal))] - fn process(&mut self, x: [Self::Sample; 1]) -> [Self::Sample; 1] { - let x = x[0]; - let in0 = x * self.b[0] + self.s[0]; - let s_out: [_; 2] = std::array::from_fn(|i| self.sats[i].saturate(in0 / 10.)); - let in1 = x * self.b[1] + self.s[1] + self.sats[0].saturate(in0 / 10.) * 10. * self.na[0]; - let in2 = x * self.b[2] + self.sats[1].saturate(in0 / 10.) * 10. * self.na[1]; - self.s = [in1, in2]; - - for (s, y) in self.sats.iter_mut().zip(s_out.into_iter()) { - s.update_state(in0 / 10., y); - } - [in0] - } -} - -impl DspAnalysis<1, 1> for Biquad -where - Self: DSP<1, 1, Sample = T>, -{ - fn h_z(&self, z: Complex) -> [[Complex; 1]; 1] { - let num = z.powi(-1).scale(self.b[1]) + z.powi(-2).scale(self.b[2]) + self.b[0]; - let den = z.powi(-1).scale(-self.na[0]) + z.powi(-2).scale(-self.na[1]) + T::one(); - [[num / den]] - } -} - -#[cfg(test)] -mod tests { - use crate::{ - dsp::{ - utils::{slice_to_mono_block, slice_to_mono_block_mut}, - DSPBlock, - }, - saturators::clippers::DiodeClipperModel, - }; - - use super::*; - - #[test] - fn test_lp_diode_clipper() { - let samplerate = 1000.0; - let sat = DiodeClipperModel::new_led(2, 3); - let mut biquad = Biquad::lowpass(10.0 / samplerate, 20.0) - .with_saturators(Dynamic::DiodeClipper(sat), Dynamic::DiodeClipper(sat)); - - let input: [_; 512] = - std::array::from_fn(|i| i as f64 / samplerate).map(|t| (10.0 * t).fract() * 2.0 - 1.0); - let mut output = [0.0; 512]; - biquad.process_block( - slice_to_mono_block(&input), - slice_to_mono_block_mut(&mut output), - ); - - insta::assert_csv_snapshot!(&output as &[_], { "[]" => insta::rounded_redaction(4) }); - } -} diff --git a/src/filters/biquad/mod.rs b/src/filters/biquad/mod.rs index 6fbd6d2..1a244a0 100644 --- a/src/filters/biquad/mod.rs +++ b/src/filters/biquad/mod.rs @@ -1,4 +1,16 @@ -//! Transposed Direct Form II Biquad implementation - nonlinearities based on https://jatinchowdhury18.medium.com/complex-nonlinearities-episode-5-nonlinear-feedback-filters-115e65fc0402 +//! Transposed Direct Form II Biquad implementation +//! +//! Nonlinearities based on +//! +//! # Usage +//! +//! ```rust +//! use valib::dsp::DSP; +//! use valib::filters::biquad::Biquad; +//! use valib::saturators::Tanh; +//! let mut lowpass = Biquad::::lowpass(0.25 /* normalized frequency */, 0.707 /* Q */); +//! let output = lowpass.process([0.0]); +//! ``` use nalgebra::Complex; use numeric_literals::replace_float_literals; @@ -13,7 +25,7 @@ use crate::{ #[cfg(never)] pub mod design; -/// Biquad struct in Transposed Direct Form II. Optionally, a [`Saturator`](crate::saturators::Saturator) instance can be used +/// Biquad struct in Transposed Direct Form II. Optionally, a [`Saturator`] instance can be used /// to apply waveshaping to the internal states. #[derive(Debug, Copy, Clone)] pub struct Biquad { @@ -23,6 +35,7 @@ pub struct Biquad { sats: [S; 2], } +// TODO: No need to restrict this on dynamic saturators only impl Biquad> { /// Apply these new saturators to this Biquad instance, returning a new instance of it. pub fn with_saturators(mut self, a: Dynamic, b: Dynamic) -> Biquad> { @@ -43,6 +56,7 @@ impl Biquad { } } +// TODO: Make those return linear biquads, where the user can then pass their saturators directly through `with_saturators` impl Biquad { /// Create a new instance of a Biquad with the provided poles and zeros coefficients. pub fn new(b: [T; 3], a: [T; 2]) -> Self {