forge_dictate/audio.rs
1//! The audio a transcription reads from.
2
3/// Sample rate every model in this crate expects, which is the rate
4/// capture opens a device at: one constant, so the two cannot drift.
5pub use forge_capture::SAMPLE_RATE;
6
7/// A buffer at another rate, brought to [`SAMPLE_RATE`] with the capture
8/// path's own filtered resampler: the one way a source declaring another
9/// rate becomes a source this crate accepts.
10pub use forge_capture::resample_mono;
11
12/// A pull-based source of mono audio.
13///
14/// Chunked rather than streamed so a source needs no hardware and no
15/// clock: a buffer already in memory drains through it as fast as the
16/// caller asks, and feeding a twenty second clip takes nowhere near
17/// twenty seconds.
18///
19/// The format accessors exist to be checked. A source whose rate or
20/// channel count does not match is rejected outright, because silently
21/// resampling or de-interleaving it would turn a wrong input into
22/// plausible output rather than an error, which is the failure nobody
23/// notices.
24pub trait AudioSource: Send {
25 /// Next samples, or None once the source is exhausted.
26 ///
27 /// Return None to end the stream; do not return an empty chunk to
28 /// mean "nothing yet". The drain is synchronous and would spin on
29 /// it, and a pull-based source has nothing to wait for.
30 fn next_chunk(&mut self) -> Option<Vec<f32>>;
31
32 /// Samples per second, as the source knows it to be.
33 fn sample_rate(&self) -> u32;
34
35 /// Interleaved channels. Deliberately without a default: a source
36 /// that has not thought about this is exactly the one that would
37 /// get it wrong.
38 fn channels(&self) -> u16;
39}
40
41/// Samples already in memory, the shape a file or a test uses.
42pub struct Samples {
43 samples: std::vec::IntoIter<f32>,
44 sample_rate: u32,
45 channels: u16,
46 chunk: usize,
47}
48
49impl Samples {
50 /// Mono samples at [`SAMPLE_RATE`].
51 pub fn mono(samples: impl Into<Vec<f32>>) -> Self {
52 Self::new(samples, SAMPLE_RATE, 1)
53 }
54
55 /// Samples at a rate and channel count the caller declares. What is
56 /// declared here is what an engine checks against.
57 pub fn new(samples: impl Into<Vec<f32>>, sample_rate: u32, channels: u16) -> Self {
58 Self { samples: samples.into().into_iter(), sample_rate, channels, chunk: 4096 }
59 }
60}
61
62impl AudioSource for Samples {
63 fn next_chunk(&mut self) -> Option<Vec<f32>> {
64 let chunk: Vec<f32> = self.samples.by_ref().take(self.chunk).collect();
65 (!chunk.is_empty()).then_some(chunk)
66 }
67
68 fn sample_rate(&self) -> u32 {
69 self.sample_rate
70 }
71
72 fn channels(&self) -> u16 {
73 self.channels
74 }
75}