53 lines
1.9 KiB
Rust
53 lines
1.9 KiB
Rust
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
use molotov_core::audio::pipewire::PipeWireStreamController;
|
|
use molotov_core::audio::types::AAudioDirection;
|
|
|
|
#[test]
|
|
fn test_440hz_loopback_frequency_and_amplitude() {
|
|
let sample_rate = 48000;
|
|
let freq = 440.0f32;
|
|
let duration_samples = 48000 / 2; // 0.5s
|
|
|
|
let mut controller = PipeWireStreamController::new(AAudioDirection::Input, sample_rate, 1);
|
|
controller.request_start().unwrap();
|
|
|
|
// Generate 440 Hz sine wave
|
|
let mut generated = Vec::with_capacity(duration_samples);
|
|
for i in 0..duration_samples {
|
|
let t = i as f32 / sample_rate as f32;
|
|
let sample = (2.0 * std::f32::consts::PI * freq * t).sin();
|
|
generated.push(sample);
|
|
}
|
|
|
|
controller.buffer.write(&generated);
|
|
|
|
let mut captured = vec![0.0f32; duration_samples];
|
|
let read_count = controller.buffer.read(&mut captured);
|
|
assert_eq!(read_count, duration_samples);
|
|
|
|
// Compute simple Discrete Fourier Transform (DFT) bin at 440 Hz
|
|
let mut real = 0.0f32;
|
|
let mut imag = 0.0f32;
|
|
for (i, &sample) in captured.iter().enumerate() {
|
|
let angle = 2.0 * std::f32::consts::PI * freq * (i as f32) / (sample_rate as f32);
|
|
real += sample * angle.cos();
|
|
imag -= sample * angle.sin();
|
|
}
|
|
let magnitude = (real * real + imag * imag).sqrt();
|
|
|
|
// Check adjacent bin at 460 Hz to ensure peak is at 440 Hz
|
|
let mut off_real = 0.0f32;
|
|
let mut off_imag = 0.0f32;
|
|
let off_freq = 460.0f32;
|
|
for (i, &sample) in captured.iter().enumerate() {
|
|
let angle = 2.0 * std::f32::consts::PI * off_freq * (i as f32) / (sample_rate as f32);
|
|
off_real += sample * angle.cos();
|
|
off_imag -= sample * angle.sin();
|
|
}
|
|
let off_magnitude = (off_real * off_real + off_imag * off_imag).sqrt();
|
|
|
|
assert!(
|
|
magnitude > off_magnitude * 10.0,
|
|
"440 Hz peak must be dominant (>10x adjacent frequencies)"
|
|
);
|
|
}
|