#![forbid(unsafe_code)]
#![allow(clippy::excessive_precision)]
#![allow(clippy::cast_lossless)]
use std::f64::consts::PI;
#[derive(Clone, Debug)]
pub struct KWeightFilter {
sample_rate: f64,
pre_filter: BiquadFilter,
rlb_filter: BiquadFilter,
}
impl KWeightFilter {
#[must_use]
pub fn new(sample_rate: f64) -> Self {
let pre_filter = Self::calculate_pre_filter(sample_rate);
let rlb_filter = Self::calculate_rlb_filter(sample_rate);
Self {
sample_rate,
pre_filter,
rlb_filter,
}
}
pub fn process(&mut self, input: f64) -> f64 {
let stage1 = self.pre_filter.process(input);
self.rlb_filter.process(stage1)
}
pub fn process_samples(&mut self, samples: &mut [f64]) {
for sample in samples {
*sample = self.process(*sample);
}
}
pub fn reset(&mut self) {
self.pre_filter.reset();
self.rlb_filter.reset();
}
fn calculate_pre_filter(sample_rate: f64) -> BiquadFilter {
let f0 = 1681.974450955533;
let g = 3.999843853973347;
let q = 0.7071752369554196;
let k = (PI * f0 / sample_rate).tan();
let k_squared = k * k;
let norm = 1.0 / (1.0 + k / q + k_squared);
let b0 = norm;
let b1 = -2.0 * norm;
let b2 = norm;
let a1 = 2.0 * (k_squared - 1.0) * norm;
let a2 = (1.0 - k / q + k_squared) * norm;
BiquadFilter::new(b0 * g, b1 * g, b2 * g, a1, a2)
}
fn calculate_rlb_filter(sample_rate: f64) -> BiquadFilter {
let f0 = 38.13547087602444;
let q = 0.5003270373238773;
let g = 1.0;
let k = (PI * f0 / sample_rate).tan();
let k_squared = k * k;
let vh = 10.0_f64.powf(g / 20.0);
let vb = vh.powf(0.4996667741545416);
let norm = 1.0 / (1.0 + k / q + k_squared);
let b0 = (vh + vb * k / q + k_squared) * norm;
let b1 = 2.0 * (k_squared - vh) * norm;
let b2 = (vh - vb * k / q + k_squared) * norm;
let a1 = 2.0 * (k_squared - 1.0) * norm;
let a2 = (1.0 - k / q + k_squared) * norm;
BiquadFilter::new(b0, b1, b2, a1, a2)
}
#[must_use]
pub fn sample_rate(&self) -> f64 {
self.sample_rate
}
}
#[derive(Clone, Debug)]
struct BiquadFilter {
b0: f64,
b1: f64,
b2: f64,
a1: f64,
a2: f64,
x1: f64,
x2: f64,
y1: f64,
y2: f64,
}
impl BiquadFilter {
fn new(b0: f64, b1: f64, b2: f64, a1: f64, a2: f64) -> Self {
Self {
b0,
b1,
b2,
a1,
a2,
x1: 0.0,
x2: 0.0,
y1: 0.0,
y2: 0.0,
}
}
fn process(&mut self, input: f64) -> f64 {
let output = self.b0 * input + self.b1 * self.x1 + self.b2 * self.x2
- self.a1 * self.y1
- self.a2 * self.y2;
self.x2 = self.x1;
self.x1 = input;
self.y2 = self.y1;
self.y1 = output;
output
}
fn reset(&mut self) {
self.x1 = 0.0;
self.x2 = 0.0;
self.y1 = 0.0;
self.y2 = 0.0;
}
}
#[derive(Clone, Debug)]
pub struct KWeightFilterBank {
filters: Vec<KWeightFilter>,
sample_rate: f64,
}
impl KWeightFilterBank {
#[must_use]
pub fn new(channels: usize, sample_rate: f64) -> Self {
let filters = (0..channels)
.map(|_| KWeightFilter::new(sample_rate))
.collect();
Self {
filters,
sample_rate,
}
}
pub fn process_interleaved(
&mut self,
samples: &[f64],
channels: usize,
output: &mut [f64],
) -> usize {
if channels == 0 || channels != self.filters.len() {
return 0;
}
let frames = samples.len() / channels;
if output.len() < samples.len() {
return 0;
}
for frame in 0..frames {
for ch in 0..channels {
let idx = frame * channels + ch;
output[idx] = self.filters[ch].process(samples[idx]);
}
}
frames
}
pub fn process_planar(&mut self, channels: &mut [Vec<f64>]) {
for (ch, samples) in channels.iter_mut().enumerate() {
if ch < self.filters.len() {
self.filters[ch].process_samples(samples);
}
}
}
pub fn reset(&mut self) {
for filter in &mut self.filters {
filter.reset();
}
}
#[must_use]
pub fn channels(&self) -> usize {
self.filters.len()
}
#[must_use]
pub fn sample_rate(&self) -> f64 {
self.sample_rate
}
}