use scirs2_core::ndarray::{Array1, ArrayView1};
use sklears_core::{error::Result as SklResult, prelude::SklearsError};
use std::f64::consts::PI;
#[derive(Debug, Clone)]
pub struct Resampler {
target_rate: f64,
original_rate: f64,
method: String,
}
impl Resampler {
pub fn new() -> Self {
Self {
target_rate: 22050.0,
original_rate: 44100.0,
method: "linear".to_string(),
}
}
pub fn target_rate(mut self, target_rate: f64) -> Self {
self.target_rate = target_rate;
self
}
pub fn original_rate(mut self, original_rate: f64) -> Self {
self.original_rate = original_rate;
self
}
pub fn method(mut self, method: String) -> Self {
self.method = method;
self
}
pub fn resample(&self, signal: &ArrayView1<f64>) -> SklResult<Array1<f64>> {
if signal.is_empty() {
return Err(SklearsError::InvalidInput("Empty signal".to_string()));
}
let ratio = self.target_rate / self.original_rate;
let new_length = (signal.len() as f64 * ratio) as usize;
if new_length == 0 {
return Err(SklearsError::InvalidInput(
"Invalid resampling ratio".to_string(),
));
}
let mut resampled = Array1::zeros(new_length);
match self.method.as_str() {
"linear" => {
for i in 0..new_length {
let orig_index = i as f64 / ratio;
let left_idx = orig_index.floor() as usize;
let right_idx = (left_idx + 1).min(signal.len() - 1);
let frac = orig_index - orig_index.floor();
if left_idx < signal.len() {
resampled[i] = signal[left_idx] * (1.0 - frac) + signal[right_idx] * frac;
}
}
}
"nearest" => {
for i in 0..new_length {
let orig_index = ((i as f64 / ratio).round() as usize).min(signal.len() - 1);
resampled[i] = signal[orig_index];
}
}
"cubic" => {
for i in 0..new_length {
let orig_pos = i as f64 / ratio;
let idx = orig_pos.floor() as isize;
let t = orig_pos - idx as f64;
let p0 = Self::sample_with_clamp(signal, idx - 1);
let p1 = Self::sample_with_clamp(signal, idx);
let p2 = Self::sample_with_clamp(signal, idx + 1);
let p3 = Self::sample_with_clamp(signal, idx + 2);
resampled[i] = Self::catmull_rom(p0, p1, p2, p3, t);
}
}
_ => {
return Err(SklearsError::InvalidInput(format!(
"Unknown resampling method: {}",
self.method
)));
}
}
Ok(resampled)
}
fn sample_with_clamp(signal: &ArrayView1<f64>, index: isize) -> f64 {
let clamped = index.clamp(0, signal.len() as isize - 1) as usize;
signal[clamped]
}
fn catmull_rom(p0: f64, p1: f64, p2: f64, p3: f64, t: f64) -> f64 {
let t2 = t * t;
let t3 = t2 * t;
0.5 * ((2.0 * p1)
+ (-p0 + p2) * t
+ (2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3) * t2
+ (-p0 + 3.0 * p1 - 3.0 * p2 + p3) * t3)
}
pub fn extract_features(&self, signal: &ArrayView1<f64>) -> SklResult<Array1<f64>> {
let resampled = self.resample(signal)?;
let mut features = Vec::new();
features.push(resampled.len() as f64 / signal.len() as f64);
let orig_energy = signal.iter().map(|x| x * x).sum::<f64>() / signal.len() as f64;
let resamp_energy = resampled.iter().map(|x| x * x).sum::<f64>() / resampled.len() as f64;
features.push(resamp_energy / orig_energy.max(1e-10));
Ok(Array1::from_vec(features))
}
pub fn ratio(&self) -> f64 {
self.target_rate / self.original_rate
}
pub fn is_upsampling(&self) -> bool {
self.target_rate > self.original_rate
}
pub fn is_downsampling(&self) -> bool {
self.target_rate < self.original_rate
}
pub fn estimate_new_length(&self, original_length: usize) -> usize {
(original_length as f64 * self.ratio()) as usize
}
}
impl Default for Resampler {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct EnvelopeDetector {
method: String,
n_fft: usize,
window: String,
}
impl EnvelopeDetector {
pub fn new() -> Self {
Self {
method: "hilbert".to_string(),
n_fft: 1024,
window: "hanning".to_string(),
}
}
pub fn method(mut self, method: String) -> Self {
self.method = method;
self
}
pub fn n_fft(mut self, n_fft: usize) -> Self {
self.n_fft = n_fft;
self
}
pub fn window(mut self, window: String) -> Self {
self.window = window;
self
}
pub fn extract_features(&self, signal: &ArrayView1<f64>) -> SklResult<Array1<f64>> {
if signal.is_empty() {
return Err(SklearsError::InvalidInput("Empty signal".to_string()));
}
let envelope = match self.method.as_str() {
"hilbert" => self.hilbert_envelope(signal)?,
"rectify" => self.rectified_envelope(signal)?,
_ => {
return Err(SklearsError::InvalidInput(format!(
"Unknown envelope method: {}",
self.method
)))
}
};
Ok(envelope)
}
fn hilbert_envelope(&self, signal: &ArrayView1<f64>) -> SklResult<Array1<f64>> {
let n = signal.len();
let mut envelope = Array1::zeros(n);
for i in 0..n {
let real_part = signal[i];
let mut imag_part = 0.0;
for k in 0..n {
if k != i {
let kernel = 1.0 / (PI * (i as f64 - k as f64));
imag_part += signal[k] * kernel;
}
}
envelope[i] = (real_part * real_part + imag_part * imag_part).sqrt();
}
Ok(envelope)
}
fn rectified_envelope(&self, signal: &ArrayView1<f64>) -> SklResult<Array1<f64>> {
let rectified: Array1<f64> = signal.mapv(|x| x.abs());
let window_size = 20;
let mut smoothed = Array1::zeros(rectified.len());
for i in window_size..rectified.len() - window_size {
let mut sum = 0.0;
for j in i - window_size..i + window_size {
sum += rectified[j];
}
smoothed[i] = sum / (2 * window_size) as f64;
}
Ok(smoothed)
}
pub fn envelope_statistics(&self, signal: &ArrayView1<f64>) -> SklResult<Array1<f64>> {
let envelope = self.extract_features(signal)?;
let mut stats = Vec::new();
let mean = envelope.mean().unwrap_or(0.0);
stats.push(mean);
let std = envelope.std(0.0);
stats.push(std);
let max_val = envelope.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
stats.push(max_val);
let min_val = envelope.iter().cloned().fold(f64::INFINITY, f64::min);
let dynamic_range = max_val - min_val;
stats.push(dynamic_range);
Ok(Array1::from_vec(stats))
}
pub fn detect_peaks(&self, signal: &ArrayView1<f64>, min_height: f64) -> SklResult<Vec<usize>> {
let envelope = self.extract_features(signal)?;
let mut peaks = Vec::new();
for i in 1..envelope.len() - 1 {
if envelope[i] > envelope[i - 1]
&& envelope[i] > envelope[i + 1]
&& envelope[i] > min_height
{
peaks.push(i);
}
}
Ok(peaks)
}
pub fn extract(&self, signal: &ArrayView1<f64>) -> SklResult<Array1<f64>> {
self.extract_features(signal)
}
}
impl Default for EnvelopeDetector {
fn default() -> Self {
Self::new()
}
}