#![cfg(feature = "dsp")]
use embedded_audio::dsp::{
AudioLmsFilter, AudioMeter, AudioSpectrumAnalyzer, BiquadAudioFilter, EnvelopeFollower,
GoertzelDetector, WindowType,
};
use embedded_audio::prelude::*;
use embedded_audio::synth::Waveform;
#[test]
fn test_biquad_lowpass_filtering() {
let mut filter = BiquadAudioFilter::lowpass(1000.0, 16000.0, 0.707);
let mut dc = [1.0f32; 100];
filter.process_buffer(&mut dc);
assert!((dc[99] - 1.0).abs() < 0.05);
filter.reset();
let mut high_freq = [0.0f32; 100];
for i in 0..100 {
let t = i as f32 / 16000.0;
high_freq[i] = (2.0 * core::f32::consts::PI * 7000.0 * t).sin();
}
filter.process_buffer(&mut high_freq);
assert!(high_freq[99].abs() < 0.2);
}
#[test]
fn test_biquad_highpass_filtering() {
let mut filter = BiquadAudioFilter::highpass(3000.0, 16000.0, 0.707);
let mut low_freq = [0.0f32; 100];
for i in 0..100 {
let t = i as f32 / 16000.0;
low_freq[i] = (2.0 * core::f32::consts::PI * 100.0 * t).sin();
}
filter.process_buffer(&mut low_freq);
assert!(low_freq[99].abs() < 0.15);
filter.reset();
let mut high_freq = [0.0f32; 100];
for i in 0..100 {
let t = i as f32 / 16000.0;
high_freq[i] = (2.0 * core::f32::consts::PI * 6000.0 * t).sin();
}
filter.process_buffer(&mut high_freq);
assert!(high_freq[99].abs() > 0.7);
}
#[test]
fn test_biquad_bandpass_filtering() {
let mut filter = BiquadAudioFilter::bandpass(1000.0, 16000.0, 1.0);
let mut center_signal = [0.0f32; 128];
for i in 0..128 {
let t = i as f32 / 16000.0;
center_signal[i] = (2.0 * core::f32::consts::PI * 1000.0 * t).sin();
}
filter.process_buffer(&mut center_signal);
let center_peak = center_signal[64..]
.iter()
.map(|s| s.abs())
.fold(0.0f32, f32::max);
assert!(center_peak > 0.3);
filter.reset();
let mut off_center_signal = [0.0f32; 128];
for i in 0..128 {
let t = i as f32 / 16000.0;
off_center_signal[i] = (2.0 * core::f32::consts::PI * 4000.0 * t).sin();
}
filter.process_buffer(&mut off_center_signal);
let off_center_peak = off_center_signal[64..]
.iter()
.map(|s| s.abs())
.fold(0.0f32, f32::max);
assert!(off_center_peak < 0.25);
}
#[test]
fn test_biquad_notch_filtering() {
let mut filter = BiquadAudioFilter::notch(60.0, 1000.0, 5.0);
let mut hum_signal = [0.0f32; 200];
for i in 0..200 {
let t = i as f32 / 1000.0;
hum_signal[i] = (2.0 * core::f32::consts::PI * 60.0 * t).sin();
}
filter.process_buffer(&mut hum_signal);
assert!(hum_signal[199].abs() < 0.15);
}
#[test]
fn test_biquad_pcm8_filtering() {
let mut filter = BiquadAudioFilter::lowpass(1000.0, 16000.0, 0.707);
let sample_in: i8 = 100;
let mut sample_out = 0i8;
for _ in 0..50 {
sample_out = filter.process_pcm8(sample_in);
}
assert!((sample_out - 100).abs() < 10);
}
#[test]
fn test_audio_engine_dsp_ticking_and_metering() {
let mut engine = AudioEngine::from_sample_rate(16000, 255, DutyMode::Linear);
engine.play_tone(440, 100, Waveform::Sine);
let mut buf = [0.0f32; 256];
let written = engine.fill_pcm_f32_buffer(&mut buf);
assert_eq!(written, 256);
let stats = AudioMeter::measure(&buf);
assert!(stats.rms > 0.0);
assert!(stats.peak > 0.0);
assert!(stats.power > 0.0);
}
#[test]
fn test_audio_meter_known_sine_wave() {
let mut sine_buf = [0.0f32; 1000];
for i in 0..1000 {
let t = i as f32 / 1000.0;
sine_buf[i] = (2.0 * core::f32::consts::PI * 10.0 * t).sin();
}
let stats = AudioMeter::measure(&sine_buf);
assert!((stats.peak - 1.0).abs() < 0.01);
assert!((stats.rms - 0.7071).abs() < 0.02);
assert!(stats.mean.abs() < 0.01);
}
#[test]
fn test_spectrum_analyzer_pitch_detection() {
let mut engine = AudioEngine::from_sample_rate(16000, 255, DutyMode::Linear);
engine.play_tone(1000, 100, Waveform::Sine);
let mut buf = [0.0f32; 256];
engine.fill_pcm_f32_buffer(&mut buf);
let (peak_freq, peak_mag) =
AudioSpectrumAnalyzer::find_peak_frequency(&buf, 16000.0, WindowType::Hanning);
assert!(peak_mag > 0.0);
assert!((peak_freq - 1000.0).abs() <= 62.5);
}
#[test]
fn test_spectrum_analyzer_windowing_types() {
let mut engine = AudioEngine::from_sample_rate(16000, 255, DutyMode::Linear);
engine.play_tone(800, 100, Waveform::Sine);
let mut buf = [0.0f32; 128];
engine.fill_pcm_f32_buffer(&mut buf);
let mut mag_hanning = [0.0f32; 64];
let mut mag_hamming = [0.0f32; 64];
let mut mag_blackman = [0.0f32; 64];
AudioSpectrumAnalyzer::analyze_spectrum(&buf, WindowType::Hanning, &mut mag_hanning);
AudioSpectrumAnalyzer::analyze_spectrum(&buf, WindowType::Hamming, &mut mag_hamming);
AudioSpectrumAnalyzer::analyze_spectrum(&buf, WindowType::Blackman, &mut mag_blackman);
assert!(mag_hanning[6] > 0.0);
assert!(mag_hamming[6] > 0.0);
assert!(mag_blackman[6] > 0.0);
}
#[test]
fn test_lms_adaptive_filter() {
let mut coeffs = [0.0f32; 8];
let mut state = [0.0f32; 8];
let mut filter = AudioLmsFilter::new(8, &mut coeffs, &mut state, 0.01);
let src = [0.5f32; 16];
let ref_sig = [0.5f32; 16];
let mut out = [0.0f32; 16];
let mut err = [0.0f32; 16];
filter.process(&src, &ref_sig, &mut out, &mut err);
assert!(err[15].abs() < err[0].abs());
}
#[test]
fn test_end_to_end_dsp_audio_pipeline() {
let sample_rate = 16000;
let mut engine = AudioEngine::from_sample_rate(sample_rate, 255, DutyMode::Linear);
engine.play_tone(2000, 200, Waveform::Sine);
let mut pcm_buffer = [0.0f32; 256];
engine.fill_pcm_f32_buffer(&mut pcm_buffer);
let mut filter = BiquadAudioFilter::highpass(1000.0, sample_rate as f32, 0.707);
filter.process_buffer(&mut pcm_buffer);
let stats = AudioMeter::measure(&pcm_buffer);
assert!(stats.rms > 0.0);
let (peak_freq, peak_mag) = AudioSpectrumAnalyzer::find_peak_frequency(
&pcm_buffer,
sample_rate as f32,
WindowType::Hanning,
);
assert!(peak_mag > 0.0);
assert!((peak_freq - 2000.0).abs() <= 62.5);
}
#[test]
fn test_goertzel_detector() {
let mut detector = GoertzelDetector::new(1000.0, 8000.0);
for i in 0..80 {
let t = i as f32 / 8000.0;
let s = (2.0 * core::f32::consts::PI * 1000.0 * t).sin();
detector.update(s);
}
assert!(detector.magnitude() > 1.0);
}
#[test]
fn test_envelope_follower() {
let mut env = EnvelopeFollower::new(0.01, 0.1, 44100.0);
let val1 = env.update(1.0);
let val2 = env.update(0.0);
assert!(val1 > 0.0);
assert!(val2 < val1);
}