crosssubtitle-ai/src-tauri/src/audio.rs
2026-03-18 15:36:08 +08:00

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