|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn apply_preemphasis(signal: &[f32], coef: f32) -> Vec<f32> { |
|
|
if signal.is_empty() { |
|
|
return vec![]; |
|
|
} |
|
|
|
|
|
let mut output = Vec::with_capacity(signal.len()); |
|
|
output.push(signal[0]); |
|
|
|
|
|
for i in 1..signal.len() { |
|
|
output.push(signal[i] - coef * signal[i - 1]); |
|
|
} |
|
|
|
|
|
output |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn apply_deemphasis(signal: &[f32], coef: f32) -> Vec<f32> { |
|
|
if signal.is_empty() { |
|
|
return vec![]; |
|
|
} |
|
|
|
|
|
let mut output = Vec::with_capacity(signal.len()); |
|
|
output.push(signal[0]); |
|
|
|
|
|
for i in 1..signal.len() { |
|
|
output.push(signal[i] + coef * output[i - 1]); |
|
|
} |
|
|
|
|
|
output |
|
|
} |
|
|
|
|
|
|
|
|
pub fn normalize_audio(signal: &[f32]) -> Vec<f32> { |
|
|
if signal.is_empty() { |
|
|
return vec![]; |
|
|
} |
|
|
|
|
|
let max_abs = signal.iter().map(|x| x.abs()).fold(0.0f32, f32::max); |
|
|
|
|
|
if max_abs < 1e-8 { |
|
|
return signal.to_vec(); |
|
|
} |
|
|
|
|
|
signal.iter().map(|x| x / max_abs).collect() |
|
|
} |
|
|
|
|
|
|
|
|
pub fn normalize_audio_peak(signal: &[f32], peak: f32) -> Vec<f32> { |
|
|
if signal.is_empty() { |
|
|
return vec![]; |
|
|
} |
|
|
|
|
|
let max_abs = signal.iter().map(|x| x.abs()).fold(0.0f32, f32::max); |
|
|
|
|
|
if max_abs < 1e-8 { |
|
|
return signal.to_vec(); |
|
|
} |
|
|
|
|
|
let scale = peak / max_abs; |
|
|
signal.iter().map(|x| x * scale).collect() |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub fn dynamic_range_compression(x: f32) -> f32 { |
|
|
let clip_val = 1e-5; |
|
|
(x.max(clip_val)).ln() |
|
|
} |
|
|
|
|
|
|
|
|
pub fn dynamic_range_compression_array(x: &[f32]) -> Vec<f32> { |
|
|
x.iter().map(|&v| dynamic_range_compression(v)).collect() |
|
|
} |
|
|
|
|
|
|
|
|
pub fn dynamic_range_decompression(x: f32) -> f32 { |
|
|
x.exp() |
|
|
} |
|
|
|
|
|
|
|
|
pub fn dynamic_range_decompression_array(x: &[f32]) -> Vec<f32> { |
|
|
x.iter().map(|&v| dynamic_range_decompression(v)).collect() |
|
|
} |
|
|
|
|
|
|
|
|
pub fn normalize_rms(signal: &[f32], target_rms: f32) -> Vec<f32> { |
|
|
if signal.is_empty() { |
|
|
return vec![]; |
|
|
} |
|
|
|
|
|
let rms = (signal.iter().map(|x| x * x).sum::<f32>() / signal.len() as f32).sqrt(); |
|
|
|
|
|
if rms < 1e-8 { |
|
|
return signal.to_vec(); |
|
|
} |
|
|
|
|
|
let scale = target_rms / rms; |
|
|
signal.iter().map(|x| x * scale).collect() |
|
|
} |
|
|
|
|
|
|
|
|
pub fn soft_clip(signal: &[f32], threshold: f32) -> Vec<f32> { |
|
|
signal |
|
|
.iter() |
|
|
.map(|&x| { |
|
|
if x.abs() <= threshold { |
|
|
x |
|
|
} else { |
|
|
let sign = x.signum(); |
|
|
let excess = x.abs() - threshold; |
|
|
sign * (threshold + (1.0 - (-excess).exp())) |
|
|
} |
|
|
}) |
|
|
.collect() |
|
|
} |
|
|
|
|
|
|
|
|
pub fn pad_audio(signal: &[f32], pad_left: usize, pad_right: usize) -> Vec<f32> { |
|
|
let mut output = vec![0.0; pad_left]; |
|
|
output.extend_from_slice(signal); |
|
|
output.extend(vec![0.0; pad_right]); |
|
|
output |
|
|
} |
|
|
|
|
|
|
|
|
pub fn trim_silence(signal: &[f32], threshold_db: f32) -> Vec<f32> { |
|
|
if signal.is_empty() { |
|
|
return vec![]; |
|
|
} |
|
|
|
|
|
let threshold = 10f32.powf(threshold_db / 20.0); |
|
|
|
|
|
|
|
|
let start = signal |
|
|
.iter() |
|
|
.position(|&x| x.abs() > threshold) |
|
|
.unwrap_or(0); |
|
|
|
|
|
|
|
|
let end = signal |
|
|
.iter() |
|
|
.rposition(|&x| x.abs() > threshold) |
|
|
.unwrap_or(signal.len() - 1); |
|
|
|
|
|
if start >= end { |
|
|
return vec![]; |
|
|
} |
|
|
|
|
|
signal[start..=end].to_vec() |
|
|
} |
|
|
|
|
|
|
|
|
pub fn apply_fade(signal: &[f32], fade_in_samples: usize, fade_out_samples: usize) -> Vec<f32> { |
|
|
if signal.is_empty() { |
|
|
return vec![]; |
|
|
} |
|
|
|
|
|
let mut output = signal.to_vec(); |
|
|
let len = output.len(); |
|
|
|
|
|
|
|
|
for i in 0..fade_in_samples.min(len) { |
|
|
let factor = i as f32 / fade_in_samples as f32; |
|
|
output[i] *= factor; |
|
|
} |
|
|
|
|
|
|
|
|
for i in 0..fade_out_samples.min(len) { |
|
|
let idx = len - 1 - i; |
|
|
let factor = i as f32 / fade_out_samples as f32; |
|
|
output[idx] *= factor; |
|
|
} |
|
|
|
|
|
output |
|
|
} |
|
|
|
|
|
|
|
|
pub fn compute_rms(signal: &[f32]) -> f32 { |
|
|
if signal.is_empty() { |
|
|
return 0.0; |
|
|
} |
|
|
(signal.iter().map(|x| x * x).sum::<f32>() / signal.len() as f32).sqrt() |
|
|
} |
|
|
|
|
|
|
|
|
pub fn compute_peak(signal: &[f32]) -> f32 { |
|
|
signal.iter().map(|x| x.abs()).fold(0.0f32, f32::max) |
|
|
} |
|
|
|
|
|
|
|
|
pub fn compute_crest_factor(signal: &[f32]) -> f32 { |
|
|
let rms = compute_rms(signal); |
|
|
if rms < 1e-8 { |
|
|
return 0.0; |
|
|
} |
|
|
compute_peak(signal) / rms |
|
|
} |
|
|
|