use crate::AudioData;
pub fn trim(
signal: &[f32],
sample_rate: u32,
start_sec: f32,
duration_sec: f32,
) -> Option<Vec<f32>> {
let n = signal.len();
let start = ((start_sec * sample_rate as f32) as usize).min(n);
let end = (start + (duration_sec * sample_rate as f32) as usize).min(n);
if end <= start {
return None;
}
Some(signal[start..end].to_vec())
}
pub fn pad(signal: &[f32], sample_rate: u32, start_sec: f32, end_sec: f32) -> Vec<f32> {
let prepend = (start_sec * sample_rate as f32) as usize;
let append = (end_sec * sample_rate as f32) as usize;
let mut out = Vec::with_capacity(prepend + signal.len() + append);
out.resize(prepend, 0.0);
out.extend_from_slice(signal);
out.resize(prepend + signal.len() + append, 0.0);
out
}
pub fn fade(signal: &[f32], sample_rate: u32, in_sec: f32, out_sec: f32) -> Vec<f32> {
let n = signal.len();
let mut out = signal.to_vec();
let fi = ((in_sec * sample_rate as f32) as usize).min(n);
if fi > 0 {
for i in 0..fi {
let gain = i as f32 / fi as f32;
out[i] *= gain;
}
}
let fo = ((out_sec * sample_rate as f32) as usize).min(n);
if fo > 0 {
let start = n - fo;
for i in start..n {
let gain = (n - 1 - i) as f32 / fo as f32;
out[i] *= gain;
}
}
out
}
pub fn silence_strip(
signal: &[f32],
sample_rate: u32,
threshold_amplitude: f32,
min_duration_sec: f32,
) -> Vec<f32> {
let n = signal.len();
if n == 0 {
return vec![];
}
let min_silent = ((min_duration_sec * sample_rate as f32) as usize).max(1);
let mut start = 0usize;
for (i, &s) in signal.iter().enumerate() {
if s.abs() > threshold_amplitude {
start = i.saturating_sub(min_silent);
break;
}
start = n; }
if start >= n {
return vec![0.0f32; signal.len()];
}
let mut end = n;
for (i, &s) in signal.iter().enumerate().rev() {
if s.abs() > threshold_amplitude {
end = (i + min_silent + 1).min(n);
break;
}
end = 0; }
if end <= start {
end = n;
start = 0;
}
signal[start..end].to_vec()
}
pub fn vad(
signal: &[f32],
sample_rate: u32,
threshold_amplitude: f32,
min_duration_sec: f32,
) -> Option<(f32, f32)> {
let min_samples = ((min_duration_sec * sample_rate as f32) as usize).max(1);
let mut run_start = None;
for (i, &s) in signal.iter().enumerate() {
if s.abs() > threshold_amplitude {
if run_start.is_none() {
run_start = Some(i);
}
if let Some(rs) = run_start {
if i - rs >= min_samples {
let start = rs as f32 / sample_rate as f32;
let mut end = i;
for (j, &s) in signal.iter().enumerate().skip(i) {
if s.abs() <= threshold_amplitude {
break;
}
end = j;
}
return Some((start, end as f32 / sample_rate as f32));
}
}
} else {
run_start = None;
}
}
None
}
pub fn gain_db(signal: &[f32], db: f32) -> Vec<f32> {
let factor = 10.0f32.powf(db / 20.0);
signal.iter().map(|s| s * factor).collect()
}
pub fn remix(channels: &[Vec<f32>], spec: &[Vec<(usize, f32)>]) -> Vec<Vec<f32>> {
let n = channels.first().map_or(0, |c| c.len());
spec.iter()
.map(|out_spec| {
let mut out = vec![0.0f32; n];
for &(idx, gain) in out_spec {
if let Some(ch) = channels.get(idx) {
for (i, &s) in ch.iter().enumerate() {
out[i] += s * gain;
}
}
}
out
})
.collect()
}
pub fn select_channels(channels: &[Vec<f32>], indices: &[usize]) -> Vec<Vec<f32>> {
indices.iter().filter_map(|&i| channels.get(i).cloned()).collect()
}
pub fn reverse(signal: &[f32]) -> Vec<f32> {
let mut out = signal.to_vec();
out.reverse();
out
}
pub fn dither(signal: &[f32], bits: u32) -> Vec<f32> {
let step = 2.0f32.powi(1 - bits as i32); let mut rng: u64 = 0xDEADBEEF;
let next_f32 = |rng: &mut u64| -> f32 {
*rng ^= *rng << 13;
*rng ^= *rng >> 7;
*rng ^= *rng << 17;
(*rng as f32) / (u64::MAX as f32) - 0.5 };
signal
.iter()
.map(|&s| {
let d1 = next_f32(&mut rng) * step;
let d2 = next_f32(&mut rng) * step;
s + d1 + d2
})
.collect()
}
impl AudioData {
pub fn trim(&self, start_sec: f32, duration_sec: f32) -> AudioData {
let ch: Vec<Vec<f32>> = self
.channels
.iter()
.filter_map(|c| trim(c, self.sample_rate, start_sec, duration_sec))
.collect();
AudioData { sample_rate: self.sample_rate, channels: ch }
}
pub fn pad_extend(&self, start_sec: f32, end_sec: f32) -> AudioData {
let ch: Vec<Vec<f32>> =
self.channels.iter().map(|c| pad(c, self.sample_rate, start_sec, end_sec)).collect();
AudioData { sample_rate: self.sample_rate, channels: ch }
}
pub fn fade(&self, in_sec: f32, out_sec: f32) -> AudioData {
let ch: Vec<Vec<f32>> =
self.channels.iter().map(|c| fade(c, self.sample_rate, in_sec, out_sec)).collect();
AudioData { sample_rate: self.sample_rate, channels: ch }
}
pub fn silence_strip(&self, threshold_amplitude: f32, min_duration_sec: f32) -> AudioData {
let ch: Vec<Vec<f32>> = self
.channels
.iter()
.map(|c| silence_strip(c, self.sample_rate, threshold_amplitude, min_duration_sec))
.collect();
AudioData { sample_rate: self.sample_rate, channels: ch }
}
pub fn gain_db(&self, db: f32) -> AudioData {
let ch: Vec<Vec<f32>> = self.channels.iter().map(|c| gain_db(c, db)).collect();
AudioData { sample_rate: self.sample_rate, channels: ch }
}
pub fn remix(&self, spec: &[Vec<(usize, f32)>]) -> AudioData {
let ch = remix(&self.channels, spec);
AudioData { sample_rate: self.sample_rate, channels: ch }
}
pub fn reverse(&self) -> AudioData {
let ch: Vec<Vec<f32>> = self.channels.iter().map(|c| reverse(c)).collect();
AudioData { sample_rate: self.sample_rate, channels: ch }
}
pub fn dither(&self, bits: u32) -> AudioData {
let ch: Vec<Vec<f32>> = self.channels.iter().map(|c| dither(c, bits)).collect();
AudioData { sample_rate: self.sample_rate, channels: ch }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn trim_cuts_range() {
let sig: Vec<f32> = (0..1000).map(|i| i as f32).collect();
let out = trim(&sig, 1000, 0.1, 0.2).unwrap();
assert_eq!(out.len(), 200);
assert_eq!(out[0], 100.0);
}
#[test]
fn pad_adds_silence() {
let sig = vec![1.0f32; 100];
let out = pad(&sig, 1000, 0.05, 0.1);
assert_eq!(out.len(), 250);
assert_eq!(out[0..50].iter().sum::<f32>(), 0.0);
assert_eq!(out[150..250].iter().sum::<f32>(), 0.0);
}
#[test]
fn fade_ramps() {
let sig = vec![1.0f32; 1000];
let out = fade(&sig, 1000, 0.1, 0.2);
assert!(out[0] < 0.05); assert!(out[50] > 0.4 && out[50] < 0.6); assert!(out[99] > 0.95); assert!(out[999] < 0.05); assert!(out[500] > 0.99); }
#[test]
fn silence_strip_strips_ends() {
let mut sig = vec![0.001f32; 1000];
sig[200..300].copy_from_slice(&vec![1.0f32; 100]);
let out = silence_strip(&sig, 1000, 0.01, 0.05);
assert!(out.len() > 100 && out.len() < 400, "got {}", out.len());
}
#[test]
fn vad_finds_speech() {
let mut sig = vec![0.001f32; 1000];
sig[300..500].copy_from_slice(&vec![0.5f32; 200]);
let result = vad(&sig, 1000, 0.1, 0.05);
assert!(result.is_some());
let (start, end) = result.unwrap();
assert!((0.29..=0.31).contains(&start));
assert!((0.49..=0.51).contains(&end));
}
#[test]
fn gain_db_multiplies() {
let sig = vec![1.0f32; 100];
let out = gain_db(&sig, -6.0);
let expected = 10.0f32.powf(-6.0 / 20.0);
assert!((out[0] - expected).abs() < 0.001);
}
#[test]
fn remix_to_mono() {
let l = vec![1.0f32; 100];
let r = vec![2.0f32; 100];
let out = remix(&[l, r], &[vec![(0, 1.0), (1, 1.0)]]);
assert_eq!(out.len(), 1);
assert_eq!(out[0][0], 3.0);
}
#[test]
fn reverse_flips() {
let sig: Vec<f32> = (0..100).map(|i| i as f32).collect();
let out = reverse(&sig);
assert_eq!(out[0], 99.0);
assert_eq!(out[99], 0.0);
}
#[test]
fn dither_adds_noise() {
let sig = vec![1.0f32; 1000];
let out = dither(&sig, 16);
let diff_count = sig.iter().zip(&out).filter(|(a, b)| (**a - **b).abs() > 1e-6).count();
assert!(diff_count > 900);
}
#[test]
fn audio_trim_trait() {
let audio = crate::generate_wave(44_100, 440.0, 1.0, 0.0);
let trimmed = audio.trim(0.1, 0.3);
assert_eq!(trimmed.channels[0].len(), (0.3 * 44_100.0) as usize);
}
#[test]
fn audio_gain_trait() {
let audio = crate::generate_wave(44_100, 440.0, 1.0, 0.0);
let boosted = audio.gain_db(6.0);
assert!(boosted.channels[0][100].abs() > audio.channels[0][100].abs());
}
#[test]
fn audio_reverse_trait() {
let audio = crate::generate_wave(44_100, 440.0, 1.0, 0.0);
let rev = audio.reverse();
assert_eq!(rev.channels[0][0], audio.channels[0][audio.channels[0].len() - 1]);
}
}