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 { 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> { 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::() .map(|sample| { sample .map(|value| value as f32 / i16::MAX as f32) .map_err(anyhow::Error::from) }) .collect::>>()?; Ok(samples) } }