| |
|
|
|
|
| |
| |
| |
| |
| |
| |
| |
| 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 |
| } |
|
|