use crate::api::{Direction, Flags, Plan};
use crate::kernel::{Complex, Float};
extern crate alloc;
use alloc::vec::Vec;
pub fn resample<T: Float>(signal: &[T], new_len: usize) -> Vec<T> {
if signal.is_empty() || new_len == 0 {
return Vec::new();
}
let n = signal.len();
if new_len == n {
return signal.to_vec();
}
let input: Vec<Complex<T>> = signal.iter().map(|&s| Complex::new(s, T::ZERO)).collect();
let fwd_plan = match Plan::dft_1d(n, Direction::Forward, Flags::ESTIMATE) {
Some(p) => p,
None => return Vec::new(),
};
let mut spectrum = vec![Complex::<T>::zero(); n];
fwd_plan.execute(&input, &mut spectrum);
let mut new_spectrum = vec![Complex::<T>::zero(); new_len];
let amplitude_scale = T::from_usize(new_len) / T::from_usize(n);
if new_len > n {
let n_is_even = n.is_multiple_of(2);
if n_is_even {
let half = n / 2;
new_spectrum[..half].copy_from_slice(&spectrum[..half]);
let neg_count = half - 1; if neg_count > 0 {
new_spectrum[new_len - neg_count..].copy_from_slice(&spectrum[half + 1..]);
}
let half_nyq = Complex::new(spectrum[half].re / T::TWO, spectrum[half].im / T::TWO);
new_spectrum[half] = half_nyq;
new_spectrum[new_len - half] = half_nyq;
} else {
let pos_count = n.div_ceil(2);
let neg_count = n - pos_count;
new_spectrum[..pos_count].copy_from_slice(&spectrum[..pos_count]);
if neg_count > 0 {
new_spectrum[new_len - neg_count..].copy_from_slice(&spectrum[pos_count..]);
}
}
} else {
let new_is_even = new_len.is_multiple_of(2);
if new_is_even {
let new_half = new_len / 2;
let neg_count = new_half - 1;
new_spectrum[..=new_half].copy_from_slice(&spectrum[..=new_half]);
if neg_count > 0 {
new_spectrum[new_len - neg_count..].copy_from_slice(&spectrum[n - neg_count..]);
}
} else {
let pos_count = new_len.div_ceil(2); let neg_count = new_len - pos_count;
new_spectrum[..pos_count].copy_from_slice(&spectrum[..pos_count]);
if neg_count > 0 {
new_spectrum[new_len - neg_count..].copy_from_slice(&spectrum[n - neg_count..]);
}
}
}
for c in &mut new_spectrum {
*c = Complex::new(c.re * amplitude_scale, c.im * amplitude_scale);
}
let inv_plan = match Plan::dft_1d(new_len, Direction::Backward, Flags::ESTIMATE) {
Some(p) => p,
None => return Vec::new(),
};
let mut output = vec![Complex::<T>::zero(); new_len];
inv_plan.execute(&new_spectrum, &mut output);
let ifft_scale = T::ONE / T::from_usize(new_len);
output.iter().map(|c| c.re * ifft_scale).collect()
}
pub fn resample_to<T: Float>(signal: &[T], orig_rate: f64, new_rate: f64) -> Vec<T> {
if signal.is_empty() || orig_rate <= 0.0 || new_rate <= 0.0 {
return Vec::new();
}
let new_len = (signal.len() as f64 * new_rate / orig_rate).round() as usize;
if new_len == 0 {
return Vec::new();
}
resample(signal, new_len)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_resample_identity() {
let n = 64;
let signal: Vec<f64> = (0..n).map(|i| (i as f64 * 0.3).sin()).collect();
let result = resample(&signal, n);
assert_eq!(result.len(), n);
for (a, b) in signal.iter().zip(result.iter()) {
assert!((a - b).abs() < 1e-9, "Identity mismatch: {a} vs {b}");
}
}
#[test]
fn test_resample_double_length() {
let n = 64;
let freq = 5.0f64;
let signal: Vec<f64> = (0..n)
.map(|i| (2.0 * std::f64::consts::PI * freq * i as f64 / n as f64).sin())
.collect();
let resampled = resample(&signal, 2 * n);
assert_eq!(resampled.len(), 2 * n);
for i in 0..n / 4 {
let diff = (resampled[2 * i] - signal[i]).abs();
assert!(
diff < 0.02,
"Sample mismatch at {}: {} vs {}",
i,
resampled[2 * i],
signal[i]
);
}
}
#[test]
fn test_resample_half_length() {
let n = 128;
let signal: Vec<f64> = (0..n).map(|i| (i as f64 * 0.2).sin()).collect();
let resampled = resample(&signal, n / 2);
assert_eq!(resampled.len(), n / 2);
}
#[test]
fn test_resample_empty() {
let empty: Vec<f64> = Vec::new();
assert!(resample(&empty, 64).is_empty());
assert!(resample(&[1.0f64], 0).is_empty());
}
#[test]
fn test_resample_to() {
let signal: Vec<f64> = (0..1000).map(|i| (f64::from(i) * 0.1).sin()).collect();
let resampled = resample_to(&signal, 8000.0, 16000.0);
assert_eq!(resampled.len(), 2000);
}
#[test]
fn test_resample_energy_preservation() {
let n = 128;
let signal: Vec<f64> = (0..n).map(|i| (i as f64 * 0.3).sin()).collect();
let up = resample(&signal, 2 * n);
let orig_energy: f64 = signal.iter().map(|&x| x * x).sum::<f64>();
let up_energy: f64 = up.iter().map(|&x| x * x).sum::<f64>();
let ratio = up_energy / orig_energy;
assert!(
(ratio - 2.0).abs() < 0.1,
"Energy ratio {ratio} should be ~2.0 for 2x upsampling"
);
}
}