Skip to main content

speech_prep/preprocessing/
artifacts.rs

1//! QA artifact helpers for preprocessing pipeline.
2//! Allows optional writing of before/after audio chunks to disk for manual
3//! review.
4
5use std::fmt;
6use std::fs::{create_dir_all, File};
7use std::io::BufWriter;
8use std::path::Path;
9
10use hound::{SampleFormat, WavSpec, WavWriter};
11
12/// Simple scoped WAV writer (16-bit mono) for QA artifacts.
13pub struct WavArtifactWriter {
14    writer: WavWriter<BufWriter<File>>,
15}
16
17impl fmt::Debug for WavArtifactWriter {
18    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
19        f.debug_struct("WavArtifactWriter").finish()
20    }
21}
22
23impl WavArtifactWriter {
24    /// Create a new WAV artifact writer at the provided path.
25    pub fn create<P: AsRef<Path>>(path: P, sample_rate: u32) -> hound::Result<Self> {
26        if let Some(parent) = path.as_ref().parent() {
27            let _ = create_dir_all(parent);
28        }
29        let spec = WavSpec {
30            channels: 1,
31            sample_rate,
32            bits_per_sample: 16,
33            sample_format: SampleFormat::Int,
34        };
35        let writer = WavWriter::create(path, spec)?;
36        Ok(Self { writer })
37    }
38
39    /// Append samples to the artifact file, clamping to 16-bit PCM range.
40    pub fn write_samples(&mut self, samples: &[f32]) -> hound::Result<()> {
41        for &sample in samples {
42            let clamped = sample.clamp(-1.0, 1.0);
43            let scaled = (clamped * f32::from(i16::MAX)) as i16;
44            self.writer.write_sample(scaled)?;
45        }
46        Ok(())
47    }
48
49    /// Finalize and flush the WAV artifact to disk.
50    pub fn finalize(self) -> hound::Result<()> {
51        self.writer.finalize()
52    }
53}