File size: 4,217 Bytes
2bbfbb7 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
//! Audio I/O operations
use crate::{Error, Result};
use hound::{SampleFormat, WavReader, WavSpec, WavWriter};
use std::path::Path;
/// Audio data container
#[derive(Debug, Clone)]
pub struct AudioData {
/// Audio samples (mono, normalized to [-1, 1])
pub samples: Vec<f32>,
/// Sample rate in Hz
pub sample_rate: u32,
}
impl AudioData {
/// Create new audio data
pub fn new(samples: Vec<f32>, sample_rate: u32) -> Self {
Self {
samples,
sample_rate,
}
}
/// Get duration in seconds
pub fn duration(&self) -> f32 {
self.samples.len() as f32 / self.sample_rate as f32
}
/// Get number of samples
pub fn len(&self) -> usize {
self.samples.len()
}
/// Check if empty
pub fn is_empty(&self) -> bool {
self.samples.is_empty()
}
}
/// Load audio from WAV file
///
/// # Arguments
/// * `path` - Path to WAV file
/// * `target_sr` - Optional target sample rate (will resample if different)
///
/// # Returns
/// Audio data with samples normalized to [-1, 1]
pub fn load_audio<P: AsRef<Path>>(path: P, target_sr: Option<u32>) -> Result<AudioData> {
let path = path.as_ref();
if !path.exists() {
return Err(Error::FileNotFound(path.display().to_string()));
}
let reader = WavReader::open(path).map_err(|e| Error::Audio(format!("Failed to open WAV: {}", e)))?;
let spec = reader.spec();
let sample_rate = spec.sample_rate;
let channels = spec.channels as usize;
// Read samples based on format
let samples: Vec<f32> = match spec.sample_format {
SampleFormat::Float => {
let samples: Vec<f32> = reader
.into_samples::<f32>()
.collect::<std::result::Result<Vec<_>, _>>()
.map_err(|e| Error::Audio(format!("Failed to read samples: {}", e)))?;
samples
}
SampleFormat::Int => {
let bits = spec.bits_per_sample;
let samples: Vec<i32> = reader
.into_samples::<i32>()
.collect::<std::result::Result<Vec<_>, _>>()
.map_err(|e| Error::Audio(format!("Failed to read samples: {}", e)))?;
// Normalize to [-1, 1]
let max_val = (1 << (bits - 1)) as f32;
samples.iter().map(|&s| s as f32 / max_val).collect()
}
};
// Convert to mono if stereo
let mono_samples = if channels > 1 {
samples
.chunks(channels)
.map(|chunk| chunk.iter().sum::<f32>() / channels as f32)
.collect()
} else {
samples
};
let mut audio = AudioData::new(mono_samples, sample_rate);
// Resample if needed
if let Some(target) = target_sr {
if target != sample_rate {
audio = super::resample::resample(&audio, target)?;
}
}
Ok(audio)
}
/// Save audio to WAV file
///
/// # Arguments
/// * `path` - Output path
/// * `audio` - Audio data to save
pub fn save_audio<P: AsRef<Path>>(path: P, audio: &AudioData) -> Result<()> {
let spec = WavSpec {
channels: 1,
sample_rate: audio.sample_rate,
bits_per_sample: 32,
sample_format: SampleFormat::Float,
};
let mut writer = WavWriter::create(path, spec)
.map_err(|e| Error::Audio(format!("Failed to create WAV writer: {}", e)))?;
for &sample in &audio.samples {
writer
.write_sample(sample)
.map_err(|e| Error::Audio(format!("Failed to write sample: {}", e)))?;
}
writer
.finalize()
.map_err(|e| Error::Audio(format!("Failed to finalize WAV: {}", e)))?;
Ok(())
}
/// Save audio samples with specified sample rate
pub fn save_samples<P: AsRef<Path>>(path: P, samples: &[f32], sample_rate: u32) -> Result<()> {
let audio = AudioData::new(samples.to_vec(), sample_rate);
save_audio(path, &audio)
}
/// Load multiple audio files in parallel
pub fn load_audio_batch<P: AsRef<Path> + Sync>(
paths: &[P],
target_sr: Option<u32>,
) -> Result<Vec<AudioData>> {
use rayon::prelude::*;
paths
.par_iter()
.map(|p| load_audio(p, target_sr))
.collect()
}
|