use super::AudioResult;
pub fn resample(audio: &[f32], from_rate: u32, to_rate: u32) -> AudioResult<Vec<f32>> {
if from_rate == 0 || to_rate == 0 {
return Err(super::AudioError::InvalidParameters(
"Sample rates must be positive".into(),
));
}
if from_rate == to_rate {
return Ok(audio.to_vec());
}
let ratio = f64::from(to_rate) / f64::from(from_rate);
let output_len = (audio.len() as f64 * ratio).ceil() as usize;
if output_len == 0 {
return Ok(Vec::new());
}
let mut output = Vec::with_capacity(output_len);
for i in 0..output_len {
let src_pos = i as f64 / ratio;
let src_idx = src_pos.floor() as usize;
let frac = (src_pos - src_idx as f64) as f32;
let sample = if src_idx + 1 < audio.len() {
audio[src_idx] * (1.0 - frac) + audio[src_idx + 1] * frac
} else if src_idx < audio.len() {
audio[src_idx]
} else {
0.0
};
output.push(sample);
}
Ok(output)
}
#[derive(Debug, Clone)]
pub struct ResampleConfig {
pub filter_length: usize,
pub cutoff: f32,
}
impl Default for ResampleConfig {
fn default() -> Self {
Self {
filter_length: 64,
cutoff: 0.95,
}
}
}
impl ResampleConfig {
#[must_use]
pub fn high_quality() -> Self {
Self {
filter_length: 128,
cutoff: 0.98,
}
}
#[must_use]
pub fn fast() -> Self {
Self {
filter_length: 16,
cutoff: 0.9,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_resample_same_rate() {
let audio = vec![1.0, 2.0, 3.0, 4.0];
let result = resample(&audio, 16000, 16000).expect("should succeed");
assert_eq!(result, audio);
}
#[test]
fn test_resample_empty() {
let audio: Vec<f32> = vec![];
let result = resample(&audio, 44100, 16000).expect("should succeed");
assert!(result.is_empty());
}
#[test]
fn test_resample_invalid_rate() {
let audio = vec![1.0, 2.0, 3.0];
assert!(resample(&audio, 0, 16000).is_err());
assert!(resample(&audio, 16000, 0).is_err());
}
#[test]
fn test_resample_downsample() {
let audio: Vec<f32> = (0..44100).map(|i| (i as f32 / 44100.0).sin()).collect();
let result = resample(&audio, 44100, 16000).expect("should succeed");
let expected_len = (44100.0_f64 * 16000.0 / 44100.0).ceil() as usize;
assert_eq!(result.len(), expected_len);
}
#[test]
fn test_resample_upsample() {
let audio = vec![0.0, 1.0, 0.0, -1.0];
let result = resample(&audio, 8000, 16000).expect("should succeed");
assert!(result.len() >= 7 && result.len() <= 9);
}
#[test]
fn test_resample_config_default() {
let config = ResampleConfig::default();
assert_eq!(config.filter_length, 64);
assert!((config.cutoff - 0.95).abs() < 0.01);
}
}
#[cfg(test)]
mod tests_falsification_aa_resample {
use super::*;
#[test]
fn test_aa1_resample_no_nan_inf() {
let audio: Vec<f32> = (0..4410)
.map(|i| (2.0 * std::f32::consts::PI * 440.0 * i as f32 / 44100.0).sin())
.collect();
let result = resample(&audio, 44100, 16000).expect("resample should succeed");
let has_nan = result.iter().any(|x| x.is_nan());
assert!(
!has_nan,
"AA1 FALSIFIED: Resample output contains NaN values"
);
let has_inf = result.iter().any(|x| x.is_infinite());
assert!(
!has_inf,
"AA1 FALSIFIED: Resample output contains Inf values"
);
}
#[test]
fn test_aa3_resample_deterministic() {
let audio: Vec<f32> = (0..1000).map(|i| (i as f32 * 0.01).sin()).collect();
let results: Vec<Vec<f32>> = (0..5)
.map(|_| resample(&audio, 44100, 16000).expect("resample"))
.collect();
for (i, result) in results.iter().enumerate().skip(1) {
assert_eq!(
&results[0], result,
"AA3 FALSIFIED: Resample is non-deterministic (run {} differs)",
i
);
}
}
#[test]
fn test_aa4_resample_length_ratio() {
let audio: Vec<f32> = (0..44100).map(|i| (i as f32 * 0.0001).sin()).collect();
let result = resample(&audio, 44100, 16000).expect("resample");
let expected_len = (44100.0_f64 * 16000.0 / 44100.0).ceil() as usize;
assert_eq!(
result.len(),
expected_len,
"AA4 FALSIFIED: Output length {} doesn't match expected {}",
result.len(),
expected_len
);
}
#[test]
fn test_aa5_downsample_reduces_length() {
let audio: Vec<f32> = (0..48000).map(|i| (i as f32 * 0.0001).sin()).collect();
let result = resample(&audio, 48000, 16000).expect("resample");
assert!(
result.len() < audio.len(),
"AA5 FALSIFIED: Downsampling did not reduce length. Input: {}, Output: {}",
audio.len(),
result.len()
);
}
#[test]
fn test_aa6_upsample_increases_length() {
let audio: Vec<f32> = (0..8000).map(|i| (i as f32 * 0.0001).sin()).collect();
let result = resample(&audio, 8000, 16000).expect("resample");
assert!(
result.len() > audio.len(),
"AA6 FALSIFIED: Upsampling did not increase length. Input: {}, Output: {}",
audio.len(),
result.len()
);
}
#[test]
fn test_aa7_same_rate_identity() {
let audio: Vec<f32> = (0..1000).map(|i| (i as f32 * 0.01).sin()).collect();
let result = resample(&audio, 16000, 16000).expect("resample");
assert_eq!(
result, audio,
"AA7 FALSIFIED: Same rate resample modified samples"
);
}
}