pub struct WavWriter<W: Write + Seek> { /* private fields */ }Implementations§
Source§impl<W: Write + Seek> WavWriter<W>
impl<W: Write + Seek> WavWriter<W>
Sourcepub fn new_trimming_tail(sink: W, sample_rate: u32) -> Result<Self>
pub fn new_trimming_tail(sink: W, sample_rate: u32) -> Result<Self>
As WavWriter::new, but drops the model’s end-of-utterance noise burst.
See trailing_noise_samples for what is removed and why it is safe. The cost is that the
last quarter second is held in memory until WavWriter::finish, so this is for file
output only; a raw PCM stream must keep its latency and stays untrimmed.
§Errors
If the provisional header cannot be written.
Examples found in repository?
6fn main() {
7 let path = std::env::args()
8 .nth(1)
9 .expect("usage: tail_writer_probe <wav>");
10 let bytes = std::fs::read(&path).expect("readable wav");
11 let pcm: Vec<f32> = bytes[44..]
12 .as_chunks::<2>()
13 .0
14 .iter()
15 .map(|pair| f32::from(i16::from_le_bytes(*pair)) / 32_767.0)
16 .collect();
17
18 let offline = ftts_core::audio::trailing_noise_samples(&pcm, 24_000);
19 let level = ftts_core::audio::speech_level(&pcm, 24_000);
20 println!("input samples : {}", pcm.len());
21 println!("offline detector : {offline} samples");
22 println!("utterance level : {level:.5}");
23
24 // Exactly the CLI's packetization.
25 let mut writer =
26 ftts_core::audio::WavWriter::new_trimming_tail(Cursor::new(Vec::new()), 24_000)
27 .expect("header");
28 for packet in pcm.chunks(1_920) {
29 writer.write_samples(packet).expect("write");
30 }
31 let out = writer.finish().expect("finish").into_inner();
32 let written = (out.len() - 44) / 2;
33 println!(
34 "writer wrote : {written} samples (trimmed {})",
35 pcm.len() - written
36 );
37
38 // What the detector sees when handed only the tail, which is the writer's situation.
39 let hold = 24_000 * 250 / 1000;
40 let tail = &pcm[pcm.len().saturating_sub(hold)..];
41 println!(
42 "tail-only relative : {} samples (tail len {})",
43 ftts_core::audio::trailing_noise_samples_relative_to(tail, 24_000, level),
44 tail.len()
45 );
46}Sourcepub fn write_samples(&mut self, pcm: &[f32]) -> Result<()>
pub fn write_samples(&mut self, pcm: &[f32]) -> Result<()>
Append one packet of decoded f32 samples.
§Errors
If the sink rejects the write. The count of samples already accepted stays accurate, so a
later WavWriter::finish still describes the file truthfully.
Examples found in repository?
6fn main() {
7 let path = std::env::args()
8 .nth(1)
9 .expect("usage: tail_writer_probe <wav>");
10 let bytes = std::fs::read(&path).expect("readable wav");
11 let pcm: Vec<f32> = bytes[44..]
12 .as_chunks::<2>()
13 .0
14 .iter()
15 .map(|pair| f32::from(i16::from_le_bytes(*pair)) / 32_767.0)
16 .collect();
17
18 let offline = ftts_core::audio::trailing_noise_samples(&pcm, 24_000);
19 let level = ftts_core::audio::speech_level(&pcm, 24_000);
20 println!("input samples : {}", pcm.len());
21 println!("offline detector : {offline} samples");
22 println!("utterance level : {level:.5}");
23
24 // Exactly the CLI's packetization.
25 let mut writer =
26 ftts_core::audio::WavWriter::new_trimming_tail(Cursor::new(Vec::new()), 24_000)
27 .expect("header");
28 for packet in pcm.chunks(1_920) {
29 writer.write_samples(packet).expect("write");
30 }
31 let out = writer.finish().expect("finish").into_inner();
32 let written = (out.len() - 44) / 2;
33 println!(
34 "writer wrote : {written} samples (trimmed {})",
35 pcm.len() - written
36 );
37
38 // What the detector sees when handed only the tail, which is the writer's situation.
39 let hold = 24_000 * 250 / 1000;
40 let tail = &pcm[pcm.len().saturating_sub(hold)..];
41 println!(
42 "tail-only relative : {} samples (tail len {})",
43 ftts_core::audio::trailing_noise_samples_relative_to(tail, 24_000, level),
44 tail.len()
45 );
46}Sourcepub const fn samples_written(&self) -> usize
pub const fn samples_written(&self) -> usize
Samples accepted so far.
Sourcepub const fn duration_millis(&self) -> u64
pub const fn duration_millis(&self) -> u64
Duration of the audio written so far, in milliseconds.
Sourcepub fn finish(self) -> Result<W>
pub fn finish(self) -> Result<W>
Patch the header to the real length and flush.
§Errors
If seeking back to the header, rewriting it, or flushing fails.
Examples found in repository?
6fn main() {
7 let path = std::env::args()
8 .nth(1)
9 .expect("usage: tail_writer_probe <wav>");
10 let bytes = std::fs::read(&path).expect("readable wav");
11 let pcm: Vec<f32> = bytes[44..]
12 .as_chunks::<2>()
13 .0
14 .iter()
15 .map(|pair| f32::from(i16::from_le_bytes(*pair)) / 32_767.0)
16 .collect();
17
18 let offline = ftts_core::audio::trailing_noise_samples(&pcm, 24_000);
19 let level = ftts_core::audio::speech_level(&pcm, 24_000);
20 println!("input samples : {}", pcm.len());
21 println!("offline detector : {offline} samples");
22 println!("utterance level : {level:.5}");
23
24 // Exactly the CLI's packetization.
25 let mut writer =
26 ftts_core::audio::WavWriter::new_trimming_tail(Cursor::new(Vec::new()), 24_000)
27 .expect("header");
28 for packet in pcm.chunks(1_920) {
29 writer.write_samples(packet).expect("write");
30 }
31 let out = writer.finish().expect("finish").into_inner();
32 let written = (out.len() - 44) / 2;
33 println!(
34 "writer wrote : {written} samples (trimmed {})",
35 pcm.len() - written
36 );
37
38 // What the detector sees when handed only the tail, which is the writer's situation.
39 let hold = 24_000 * 250 / 1000;
40 let tail = &pcm[pcm.len().saturating_sub(hold)..];
41 println!(
42 "tail-only relative : {} samples (tail len {})",
43 ftts_core::audio::trailing_noise_samples_relative_to(tail, 24_000, level),
44 tail.len()
45 );
46}Sourcepub fn finish_reporting(self) -> Result<(W, usize)>
pub fn finish_reporting(self) -> Result<(W, usize)>
As WavWriter::finish, also reporting how many samples the file actually contains.
Callers that publish a sample count need this rather than their own tally: tail trimming (and any short write) makes “samples handed to the writer” differ from “samples in the file”, and a reported count that describes audio the file does not hold is a false number in a machine-readable stream.
§Errors
If flushing the held tail, seeking back to the header, rewriting it, or flushing fails.
Trait Implementations§
Source§impl<W: Write + Seek> Drop for WavWriter<W>
impl<W: Write + Seek> Drop for WavWriter<W>
Source§fn drop(&mut self)
fn drop(&mut self)
Best-effort finalisation for a writer dropped without WavWriter::finish.
A dropped writer means an abnormal end — a panic, an early return, a cancelled run. Leaving
the provisional zero-length header would make the file claim it contains no audio while
holding a megabyte of it. The error is deliberately swallowed because Drop cannot report,
which is exactly why finish exists and should be called explicitly.