#[derive(Debug, Clone)]
pub struct ClippingResult {
pub has_clipping: bool,
pub clipped_samples: usize,
pub clipping_ratio: f32,
pub max_consecutive_clipped: usize,
}
#[must_use]
pub fn detect_clipping(samples: &[f32], threshold: f32) -> ClippingResult {
if samples.is_empty() {
return ClippingResult {
has_clipping: false,
clipped_samples: 0,
clipping_ratio: 0.0,
max_consecutive_clipped: 0,
};
}
let mut clipped_samples = 0;
let mut consecutive = 0;
let mut max_consecutive = 0;
for &sample in samples {
if sample.abs() >= threshold {
clipped_samples += 1;
consecutive += 1;
max_consecutive = max_consecutive.max(consecutive);
} else {
consecutive = 0;
}
}
let clipping_ratio = clipped_samples as f32 / samples.len() as f32;
ClippingResult {
has_clipping: clipped_samples > 0,
clipped_samples,
clipping_ratio,
max_consecutive_clipped: max_consecutive,
}
}
#[must_use]
pub fn detect_clipping_with_hysteresis(
samples: &[f32],
threshold_high: f32,
threshold_low: f32,
) -> ClippingResult {
if samples.is_empty() {
return ClippingResult {
has_clipping: false,
clipped_samples: 0,
clipping_ratio: 0.0,
max_consecutive_clipped: 0,
};
}
let mut clipped_samples = 0;
let mut consecutive = 0;
let mut max_consecutive = 0;
let mut in_clip = false;
for &sample in samples {
let abs_sample = sample.abs();
if !in_clip && abs_sample >= threshold_high {
in_clip = true;
clipped_samples += 1;
consecutive += 1;
} else if in_clip {
clipped_samples += 1;
consecutive += 1;
if abs_sample < threshold_low {
in_clip = false;
max_consecutive = max_consecutive.max(consecutive);
consecutive = 0;
}
}
}
if in_clip {
max_consecutive = max_consecutive.max(consecutive);
}
let clipping_ratio = clipped_samples as f32 / samples.len() as f32;
ClippingResult {
has_clipping: clipped_samples > 0,
clipped_samples,
clipping_ratio,
max_consecutive_clipped: max_consecutive,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_clipping_detection() {
let samples = vec![0.0, 0.5, -0.5, 0.8, -0.8];
let result = detect_clipping(&samples, 0.99);
assert!(!result.has_clipping);
let clipped = vec![0.0, 1.0, 1.0, -1.0, 0.5];
let result = detect_clipping(&clipped, 0.99);
assert!(result.has_clipping);
assert!(result.clipped_samples >= 2); }
#[test]
fn test_clipping_with_hysteresis() {
let samples = vec![0.0, 0.95, 1.0, 0.98, 0.9, 0.5];
let result = detect_clipping_with_hysteresis(&samples, 0.99, 0.9);
assert!(result.has_clipping);
}
}