#[allow(unused_imports)]
use embedded_dsp::FloatMath;
use embedded_dsp::{
BiquadCascadeInstanceF32, LmsInstanceF32, apply_window_f32, biquad_cascade_df1_f32,
blackman_f32, flattop_f32, hamming_f32, hanning_f32, lms_f32, mean_f32, power_f32, rfft_f32,
rms_f32, var_f32,
};
#[derive(Clone)]
pub struct BiquadAudioFilter {
coeffs: [f32; 5], state: [f32; 4], }
impl BiquadAudioFilter {
pub fn new(b0: f32, b1: f32, b2: f32, a1: f32, a2: f32) -> Self {
Self {
coeffs: [b0, b1, b2, a1, a2],
state: [0.0; 4],
}
}
pub fn lowpass(cutoff_hz: f32, sample_rate_hz: f32, q: f32) -> Self {
let omega = 2.0 * core::f32::consts::PI * cutoff_hz / sample_rate_hz;
let alpha = omega.sin() / (2.0 * q);
let cos_w = omega.cos();
let b0 = (1.0 - cos_w) / 2.0;
let b1 = 1.0 - cos_w;
let b2 = (1.0 - cos_w) / 2.0;
let a0 = 1.0 + alpha;
let a1 = -2.0 * cos_w;
let a2 = 1.0 - alpha;
Self::new(b0 / a0, b1 / a0, b2 / a0, -a1 / a0, -a2 / a0)
}
pub fn highpass(cutoff_hz: f32, sample_rate_hz: f32, q: f32) -> Self {
let omega = 2.0 * core::f32::consts::PI * cutoff_hz / sample_rate_hz;
let alpha = omega.sin() / (2.0 * q);
let cos_w = omega.cos();
let b0 = (1.0 + cos_w) / 2.0;
let b1 = -(1.0 + cos_w);
let b2 = (1.0 + cos_w) / 2.0;
let a0 = 1.0 + alpha;
let a1 = -2.0 * cos_w;
let a2 = 1.0 - alpha;
Self::new(b0 / a0, b1 / a0, b2 / a0, -a1 / a0, -a2 / a0)
}
pub fn bandpass(cutoff_hz: f32, sample_rate_hz: f32, q: f32) -> Self {
let omega = 2.0 * core::f32::consts::PI * cutoff_hz / sample_rate_hz;
let alpha = omega.sin() / (2.0 * q);
let cos_w = omega.cos();
let b0 = alpha;
let b1 = 0.0;
let b2 = -alpha;
let a0 = 1.0 + alpha;
let a1 = -2.0 * cos_w;
let a2 = 1.0 - alpha;
Self::new(b0 / a0, b1 / a0, b2 / a0, -a1 / a0, -a2 / a0)
}
pub fn notch(cutoff_hz: f32, sample_rate_hz: f32, q: f32) -> Self {
let omega = 2.0 * core::f32::consts::PI * cutoff_hz / sample_rate_hz;
let alpha = omega.sin() / (2.0 * q);
let cos_w = omega.cos();
let b0 = 1.0;
let b1 = -2.0 * cos_w;
let b2 = 1.0;
let a0 = 1.0 + alpha;
let a1 = -2.0 * cos_w;
let a2 = 1.0 - alpha;
Self::new(b0 / a0, b1 / a0, b2 / a0, -a1 / a0, -a2 / a0)
}
pub fn reset(&mut self) {
self.state.fill(0.0);
}
pub fn process_sample(&mut self, input: f32) -> f32 {
let mut inst = BiquadCascadeInstanceF32 {
num_stages: 1,
coeffs: &self.coeffs,
state: &mut self.state,
};
let src = [input];
let mut dst = [0.0];
biquad_cascade_df1_f32(&mut inst, &src, &mut dst);
dst[0]
}
pub fn process_pcm8(&mut self, input: i8) -> i8 {
let in_f32 = input as f32 / 128.0;
let out_f32 = self.process_sample(in_f32);
(out_f32 * 127.0).clamp(-128.0, 127.0) as i8
}
pub fn process_buffer(&mut self, samples: &mut [f32]) {
let mut inst = BiquadCascadeInstanceF32 {
num_stages: 1,
coeffs: &self.coeffs,
state: &mut self.state,
};
for sample in samples.iter_mut() {
let src = [*sample];
let mut dst = [0.0];
biquad_cascade_df1_f32(&mut inst, &src, &mut dst);
*sample = dst[0];
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WindowType {
Rectangular,
Hanning,
Hamming,
Blackman,
FlatTop,
}
pub struct AudioSpectrumAnalyzer;
impl AudioSpectrumAnalyzer {
pub fn analyze_spectrum(src: &[f32], window_type: WindowType, dst_mag: &mut [f32]) {
let n = src.len();
if n < 2 || (n & (n - 1)) != 0 || dst_mag.len() < n / 2 {
return;
}
let mut win_buf = [0.0f32; 1024];
let mut sample_buf = [0.0f32; 1024];
if n > 1024 {
return;
}
sample_buf[..n].copy_from_slice(&src[..n]);
match window_type {
WindowType::Rectangular => {}
WindowType::Hanning => {
hanning_f32(&mut win_buf[..n]);
apply_window_f32(&mut sample_buf[..n], &win_buf[..n]);
}
WindowType::Hamming => {
hamming_f32(&mut win_buf[..n]);
apply_window_f32(&mut sample_buf[..n], &win_buf[..n]);
}
WindowType::Blackman => {
blackman_f32(&mut win_buf[..n]);
apply_window_f32(&mut sample_buf[..n], &win_buf[..n]);
}
WindowType::FlatTop => {
flattop_f32(&mut win_buf[..n]);
apply_window_f32(&mut sample_buf[..n], &win_buf[..n]);
}
}
let mut fft_out = [0.0f32; 2048];
rfft_f32(&sample_buf[..n], &mut fft_out[..2 * n], n, 0);
for k in 0..(n / 2) {
let re = fft_out[2 * k];
let im = fft_out[2 * k + 1];
dst_mag[k] = (re * re + im * im).sqrt();
}
}
pub fn find_peak_frequency(
src: &[f32],
sample_rate_hz: f32,
window_type: WindowType,
) -> (f32, f32) {
let n = src.len();
if n < 4 || (n & (n - 1)) != 0 {
return (0.0, 0.0);
}
let num_bins = n / 2;
let mut mag_buf = [0.0f32; 512];
if num_bins > mag_buf.len() {
return (0.0, 0.0);
}
Self::analyze_spectrum(src, window_type, &mut mag_buf[..num_bins]);
let mut max_mag = 0.0f32;
let mut max_bin = 0;
for (k, &mag) in mag_buf[..num_bins].iter().enumerate().skip(1) {
if mag > max_mag {
max_mag = mag;
max_bin = k;
}
}
let bin_width = sample_rate_hz / (n as f32);
let freq = (max_bin as f32) * bin_width;
(freq, max_mag)
}
}
#[derive(Debug, Clone, Copy)]
pub struct AudioStats {
pub rms: f32,
pub peak: f32,
pub mean: f32,
pub power: f32,
pub variance: f32,
}
pub struct AudioMeter;
impl AudioMeter {
pub fn measure(samples: &[f32]) -> AudioStats {
if samples.is_empty() {
return AudioStats {
rms: 0.0,
peak: 0.0,
mean: 0.0,
power: 0.0,
variance: 0.0,
};
}
let mut mean = 0.0f32;
let mut rms = 0.0f32;
let mut power = 0.0f32;
let mut variance = 0.0f32;
let _ = mean_f32(samples, &mut mean);
let _ = rms_f32(samples, &mut rms);
let _ = power_f32(samples, &mut power);
let _ = var_f32(samples, &mut variance);
let mut peak = 0.0f32;
for &s in samples {
let abs_s = s.abs();
if abs_s > peak {
peak = abs_s;
}
}
AudioStats {
rms,
peak,
mean,
power,
variance,
}
}
}
pub struct AudioLmsFilter<'a> {
inst: LmsInstanceF32<'a>,
}
impl<'a> AudioLmsFilter<'a> {
pub fn new(num_taps: u16, coeffs: &'a mut [f32], state: &'a mut [f32], mu: f32) -> Self {
let inst = LmsInstanceF32::init(num_taps, coeffs, state, mu);
Self { inst }
}
pub fn process(&mut self, src: &[f32], ref_signal: &[f32], out: &mut [f32], err: &mut [f32]) {
lms_f32(&mut self.inst, src, ref_signal, out, err);
}
}
pub struct GoertzelDetector {
coeff: f32,
s_prev: f32,
s_prev2: f32,
}
impl GoertzelDetector {
pub fn new(target_freq: f32, sample_rate: f32) -> Self {
let omega = 2.0 * core::f32::consts::PI * target_freq / sample_rate;
let coeff = 2.0 * omega.cos();
Self {
coeff,
s_prev: 0.0,
s_prev2: 0.0,
}
}
pub fn reset(&mut self) {
self.s_prev = 0.0;
self.s_prev2 = 0.0;
}
pub fn update(&mut self, sample: f32) {
let s = sample + self.coeff * self.s_prev - self.s_prev2;
self.s_prev2 = self.s_prev;
self.s_prev = s;
}
pub fn magnitude(&self) -> f32 {
(self.s_prev * self.s_prev + self.s_prev2 * self.s_prev2
- self.coeff * self.s_prev * self.s_prev2)
.sqrt()
}
}
pub struct EnvelopeFollower {
attack_coeff: f32,
release_coeff: f32,
envelope: f32,
}
impl EnvelopeFollower {
pub fn new(attack_time_sec: f32, release_time_sec: f32, sample_rate: f32) -> Self {
let attack_coeff = (-1.0 / (attack_time_sec * sample_rate)).exp();
let release_coeff = (-1.0 / (release_time_sec * sample_rate)).exp();
Self {
attack_coeff,
release_coeff,
envelope: 0.0,
}
}
pub fn update(&mut self, sample: f32) -> f32 {
let input_mag = sample.abs();
if input_mag > self.envelope {
self.envelope =
self.attack_coeff * self.envelope + (1.0 - self.attack_coeff) * input_mag;
} else {
self.envelope =
self.release_coeff * self.envelope + (1.0 - self.release_coeff) * input_mag;
}
self.envelope
}
pub fn reset(&mut self) {
self.envelope = 0.0;
}
}
#[derive(Debug, Clone)]
pub struct BiquadAudioFilterQ15 {
b0: i16,
b1: i16,
b2: i16,
a1: i16,
a2: i16,
x1: i16,
x2: i16,
y1: i16,
y2: i16,
}
impl BiquadAudioFilterQ15 {
pub const fn new(b0: i16, b1: i16, b2: i16, a1: i16, a2: i16) -> Self {
Self {
b0,
b1,
b2,
a1,
a2,
x1: 0,
x2: 0,
y1: 0,
y2: 0,
}
}
pub fn reset(&mut self) {
self.x1 = 0;
self.x2 = 0;
self.y1 = 0;
self.y2 = 0;
}
pub fn process_sample_i16(&mut self, x: i16) -> i16 {
let acc = (self.b0 as i32 * x as i32)
+ (self.b1 as i32 * self.x1 as i32)
+ (self.b2 as i32 * self.x2 as i32)
- (self.a1 as i32 * self.y1 as i32)
- (self.a2 as i32 * self.y2 as i32);
let y = (acc >> 14).clamp(-32768, 32767) as i16;
self.x2 = self.x1;
self.x1 = x;
self.y2 = self.y1;
self.y1 = y;
y
}
pub fn process_sample_i8(&mut self, x: i8) -> i8 {
let x16 = (x as i16) << 8;
let y16 = self.process_sample_i16(x16);
(y16 >> 8) as i8
}
}