1
0
Fork 0

Make the biquads SIMD capable

This commit is contained in:
Robbert van der Helm 2022-02-15 18:00:01 +01:00
parent 8c30eccb27
commit 935d952d81
3 changed files with 74 additions and 19 deletions

17
Cargo.lock generated
View file

@ -276,6 +276,7 @@ name = "diopser"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"nih_plug", "nih_plug",
"packed_simd_2",
] ]
[[package]] [[package]]
@ -448,6 +449,12 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "libm"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a"
[[package]] [[package]]
name = "lock_api" name = "lock_api"
version = "0.4.6" version = "0.4.6"
@ -619,6 +626,16 @@ dependencies = [
"ttf-parser", "ttf-parser",
] ]
[[package]]
name = "packed_simd_2"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "defdcfef86dcc44ad208f71d9ff4ce28df6537a4e0d6b0e8e845cb8ca10059a6"
dependencies = [
"cfg-if",
"libm",
]
[[package]] [[package]]
name = "parking_lot" name = "parking_lot"
version = "0.12.0" version = "0.12.0"

View file

@ -10,3 +10,5 @@ crate-type = ["cdylib"]
[dependencies] [dependencies]
nih_plug = { path = "../../", features = ["assert_process_allocs"] } nih_plug = { path = "../../", features = ["assert_process_allocs"] }
packed_simd = { version = "0.3.6", package = "packed_simd_2" }

View file

@ -14,43 +14,56 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
use packed_simd::f32x2;
use std::f32::consts; use std::f32::consts;
use std::ops::{Add, Mul, Sub};
/// A simple biquad filter with functions for generating coefficients for an all-pass filter. /// A simple biquad filter with functions for generating coefficients for an all-pass filter.
/// ///
/// Based on <https://en.wikipedia.org/wiki/Digital_biquad_filter#Transposed_direct_forms>. /// Based on <https://en.wikipedia.org/wiki/Digital_biquad_filter#Transposed_direct_forms>.
///
/// The type parameter T should be either an `f32` or a SIMD type.
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct Biquad { pub struct Biquad<T> {
pub coefficients: BiquadCoefficients, pub coefficients: BiquadCoefficients<T>,
s1: f32, s1: T,
s2: f32, s2: T,
} }
/// The coefficients `[b0, b1, b2, a1, a2]` for [Biquad]. These coefficients are all prenormalized, /// The coefficients `[b0, b1, b2, a1, a2]` for [Biquad]. These coefficients are all prenormalized,
/// i.e. they have been divided by `a0`. /// i.e. they have been divided by `a0`.
///
/// The type parameter T should be either an `f32` or a SIMD type.
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct BiquadCoefficients { pub struct BiquadCoefficients<T> {
b0: f32, b0: T,
b1: f32, b1: T,
b2: f32, b2: T,
a1: f32, a1: T,
a2: f32, a2: T,
} }
impl Default for Biquad { /// Either an `f32` or some SIMD vector type of `f32`s that can be used iwth our biquads.
pub trait SimdType:
Mul<Output = Self> + Sub<Output = Self> + Add<Output = Self> + Copy + Sized
{
fn from_f32(value: f32) -> Self;
}
impl<T: SimdType> Default for Biquad<T> {
/// Before setting constants the filter should just act as an identity function. /// Before setting constants the filter should just act as an identity function.
fn default() -> Self { fn default() -> Self {
Self { Self {
coefficients: BiquadCoefficients::identity(), coefficients: BiquadCoefficients::identity(),
s1: 0.0, s1: T::from_f32(0.0),
s2: 0.0, s2: T::from_f32(0.0),
} }
} }
} }
impl Biquad { impl<T: SimdType> Biquad<T> {
/// Process a single sample. /// Process a single sample.
pub fn process(&mut self, sample: f32) -> f32 { pub fn process(&mut self, sample: T) -> T {
let result = self.coefficients.b0 * sample + self.s1; let result = self.coefficients.b0 * sample + self.s1;
self.s1 = self.coefficients.b1 * sample - self.coefficients.a1 * result + self.s2; self.s1 = self.coefficients.b1 * sample - self.coefficients.a1 * result + self.s2;
@ -60,16 +73,27 @@ impl Biquad {
} }
} }
impl BiquadCoefficients { impl<T: SimdType> BiquadCoefficients<T> {
/// Convert scalar coefficients into the correct vector type.
pub fn from_f32s(scalar: BiquadCoefficients<f32>) -> Self {
Self {
b0: T::from_f32(scalar.b0),
b1: T::from_f32(scalar.b1),
b2: T::from_f32(scalar.b2),
a1: T::from_f32(scalar.a1),
a2: T::from_f32(scalar.a2),
}
}
/// Filter coefficients that would cause the sound to be passed through as is. /// Filter coefficients that would cause the sound to be passed through as is.
pub fn identity() -> Self { pub fn identity() -> Self {
Self { Self::from_f32s(BiquadCoefficients {
b0: 1.0, b0: 1.0,
b1: 0.0, b1: 0.0,
b2: 0.0, b2: 0.0,
a1: 0.0, a1: 0.0,
a2: 0.0, a2: 0.0,
} })
} }
/// Compute the coefficients for an all-pass filter. /// Compute the coefficients for an all-pass filter.
@ -93,6 +117,18 @@ impl BiquadCoefficients {
let a1 = (-2.0 * cos_omega0) / a0; let a1 = (-2.0 * cos_omega0) / a0;
let a2 = (1.0 - alpha) / a0; let a2 = (1.0 - alpha) / a0;
Self { b0, b1, b2, a1, a2 } Self::from_f32s(BiquadCoefficients { b0, b1, b2, a1, a2 })
}
}
impl SimdType for f32 {
fn from_f32(value: f32) -> Self {
value
}
}
impl SimdType for f32x2 {
fn from_f32(value: f32) -> Self {
f32x2::splat(value)
} }
} }