63 lines
1.7 KiB
Rust
63 lines
1.7 KiB
Rust
use std::{
|
|
fs,
|
|
path::{Path, PathBuf},
|
|
process::Command,
|
|
};
|
|
|
|
use anyhow::{anyhow, Context, Result};
|
|
|
|
pub struct AudioPipeline;
|
|
|
|
impl AudioPipeline {
|
|
pub fn extract_to_wav(input_path: &str, workspace: &Path) -> Result<PathBuf> {
|
|
fs::create_dir_all(workspace)
|
|
.with_context(|| format!("failed to create workspace: {}", workspace.display()))?;
|
|
|
|
let output_path = workspace.join("normalized.wav");
|
|
let status = Command::new("ffmpeg")
|
|
.arg("-y")
|
|
.arg("-i")
|
|
.arg(input_path)
|
|
.arg("-ac")
|
|
.arg("1")
|
|
.arg("-ar")
|
|
.arg("16000")
|
|
.arg("-f")
|
|
.arg("wav")
|
|
.arg(&output_path)
|
|
.status()
|
|
.context("failed to launch ffmpeg, please install ffmpeg and ensure it is in PATH")?;
|
|
|
|
if !status.success() {
|
|
return Err(anyhow!("ffmpeg exited with status: {status}"));
|
|
}
|
|
|
|
Ok(output_path)
|
|
}
|
|
|
|
pub fn load_wav_f32(path: &Path) -> Result<Vec<f32>> {
|
|
let mut reader =
|
|
hound::WavReader::open(path).with_context(|| format!("failed to open {}", path.display()))?;
|
|
let spec = reader.spec();
|
|
|
|
if spec.channels != 1 {
|
|
return Err(anyhow!(
|
|
"expected mono wav, found {} channels in {}",
|
|
spec.channels,
|
|
path.display()
|
|
));
|
|
}
|
|
|
|
let samples = reader
|
|
.samples::<i16>()
|
|
.map(|sample| {
|
|
sample
|
|
.map(|value| value as f32 / i16::MAX as f32)
|
|
.map_err(anyhow::Error::from)
|
|
})
|
|
.collect::<Result<Vec<_>>>()?;
|
|
|
|
Ok(samples)
|
|
}
|
|
}
|