flexaudio_encode/
writer.rs1use std::fs::File;
16use std::io::{BufWriter, Seek, Write};
17use std::path::Path;
18
19use flacenc::bitsink::ByteSink;
20use flacenc::component::{BitRepr, Stream, StreamInfo};
21use flacenc::config::Encoder as EncoderConfig;
22use flacenc::error::{Verified, Verify};
23use flacenc::source::{Context, Fill, FrameBuf};
24
25use crate::error::{EncodeError, Result};
26
27const BLOCK_SIZE: usize = 4096;
29
30const BITS_PER_SAMPLE: usize = 16;
34
35const MAX_SAMPLE_RATE: u32 = 96_000;
38
39const HEADER_LEN: usize = 42;
41
42#[inline]
48fn quantize_i16(x: f32) -> i32 {
49 (x * 32768.0).round().clamp(-32768.0, 32767.0) as i32
50}
51
52fn enc_err(e: impl std::fmt::Display) -> EncodeError {
54 EncodeError::Encoder(e.to_string())
55}
56
57pub struct FlacWriter {
68 file: BufWriter<File>,
69 config: Verified<EncoderConfig>,
70 stream_info: StreamInfo,
72 frame_buf: FrameBuf,
74 context: Context,
76 sink: ByteSink,
78 pending: Vec<i32>,
80 channels: usize,
81 frame_number: usize,
83 finalized: bool,
85}
86
87impl FlacWriter {
88 pub fn create<P: AsRef<Path>>(path: P, sample_rate: u32, channels: u16) -> Result<FlacWriter> {
93 if !(1..=2).contains(&channels) {
94 return Err(EncodeError::Unsupported(format!(
95 "channels must be 1 or 2, got {channels}"
96 )));
97 }
98 if !(1..=MAX_SAMPLE_RATE).contains(&sample_rate) {
99 return Err(EncodeError::Unsupported(format!(
100 "sample rate must be 1..={MAX_SAMPLE_RATE} Hz, got {sample_rate}"
101 )));
102 }
103
104 let config = EncoderConfig::default()
105 .into_verified()
106 .map_err(|(_, e)| enc_err(e))?;
107 let mut stream_info =
108 StreamInfo::new(sample_rate as usize, channels as usize, BITS_PER_SAMPLE)
109 .map_err(enc_err)?;
110 stream_info
112 .set_block_sizes(BLOCK_SIZE, BLOCK_SIZE)
113 .map_err(enc_err)?;
114 let frame_buf = FrameBuf::with_size(channels as usize, BLOCK_SIZE).map_err(enc_err)?;
115 let context = Context::new(BITS_PER_SAMPLE, channels as usize);
116
117 let mut writer = FlacWriter {
118 file: BufWriter::new(File::create(path)?),
119 config,
120 stream_info,
121 frame_buf,
122 context,
123 sink: ByteSink::new(),
124 pending: Vec::new(),
125 channels: channels as usize,
126 frame_number: 0,
127 finalized: false,
128 };
129 let header = writer.header_bytes()?;
131 writer.file.write_all(&header)?;
132 Ok(writer)
133 }
134
135 pub fn write_chunk(&mut self, interleaved: &[f32]) -> Result<()> {
142 if interleaved.len() % self.channels != 0 {
143 return Err(EncodeError::Unsupported(format!(
144 "chunk length {} is not a multiple of channels {}",
145 interleaved.len(),
146 self.channels
147 )));
148 }
149 self.pending.reserve(interleaved.len());
150 self.pending
151 .extend(interleaved.iter().map(|&x| quantize_i16(x)));
152 self.drain_full_blocks()
153 }
154
155 pub fn finalize(mut self) -> Result<()> {
161 let result = self.finish_inner();
162 self.finalized = true;
164 result
165 }
166
167 fn drain_full_blocks(&mut self) -> Result<()> {
169 let block_len = BLOCK_SIZE * self.channels;
170 let pending = std::mem::take(&mut self.pending);
173 let mut consumed = 0;
174 let mut result = Ok(());
175 while consumed + block_len <= pending.len() {
176 if let Err(e) = self.encode_block(&pending[consumed..consumed + block_len]) {
177 result = Err(e);
178 break;
179 }
180 consumed += block_len;
181 }
182 self.pending = pending;
183 self.pending.drain(..consumed);
184 result
185 }
186
187 fn encode_block(&mut self, block: &[i32]) -> Result<()> {
190 self.frame_buf.fill_interleaved(block).map_err(enc_err)?;
191 self.context.fill_interleaved(block).map_err(enc_err)?;
193
194 let frame = flacenc::encode_fixed_size_frame(
195 &self.config,
196 &self.frame_buf,
197 self.frame_number,
198 &self.stream_info,
199 )
200 .map_err(enc_err)?;
201 self.stream_info.update_frame_info(&frame);
203
204 self.sink.clear();
205 frame.write(&mut self.sink).map_err(enc_err)?;
206 self.file.write_all(self.sink.as_slice())?;
208 self.frame_number += 1;
209 Ok(())
210 }
211
212 fn finish_inner(&mut self) -> Result<()> {
214 if !self.pending.is_empty() {
216 let tail = std::mem::take(&mut self.pending);
217 self.encode_block(&tail)?;
218 }
219
220 self.stream_info
227 .set_block_sizes(BLOCK_SIZE, BLOCK_SIZE)
228 .map_err(enc_err)?;
229 self.stream_info.set_md5_digest(&self.context.md5_digest());
230 self.stream_info
231 .set_total_samples(self.context.total_samples());
232
233 let header = self.header_bytes()?;
235 self.file.rewind()?;
236 self.file.write_all(&header)?;
237 self.file.flush()?;
238 Ok(())
239 }
240
241 fn header_bytes(&self) -> Result<Vec<u8>> {
246 let mut info = self.stream_info.clone();
247 if self.frame_number == 0 {
248 info.set_frame_sizes(0, 0).map_err(enc_err)?;
251 }
252 let stream = Stream::with_stream_info(info);
254 let mut sink = ByteSink::new();
255 stream.write(&mut sink).map_err(enc_err)?;
256 let bytes = sink.into_inner();
257 debug_assert_eq!(bytes.len(), HEADER_LEN);
258 Ok(bytes)
259 }
260}
261
262impl Drop for FlacWriter {
263 fn drop(&mut self) {
264 if !self.finalized {
265 let _ = self.finish_inner();
267 }
268 }
269}
270
271impl std::fmt::Debug for FlacWriter {
273 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
274 f.debug_struct("FlacWriter")
275 .field("sample_rate", &self.stream_info.sample_rate())
276 .field("channels", &self.channels)
277 .field("frame_number", &self.frame_number)
278 .field("pending_samples", &self.pending.len())
279 .field("finalized", &self.finalized)
280 .finish_non_exhaustive()
281 }
282}
283
284#[cfg(test)]
285mod tests {
286 use super::quantize_i16;
287
288 #[test]
289 fn quantize_reference_points() {
290 assert_eq!(quantize_i16(0.0), 0);
291 assert_eq!(quantize_i16(0.25), 8192);
292 assert_eq!(quantize_i16(-1.0), -32768);
293 assert_eq!(quantize_i16(1.0), 32767);
295 assert_eq!(quantize_i16(2.0), 32767);
297 assert_eq!(quantize_i16(-2.0), -32768);
298 assert_eq!(quantize_i16(f32::NAN), 0);
300 assert_eq!(quantize_i16(f32::INFINITY), 32767);
301 assert_eq!(quantize_i16(f32::NEG_INFINITY), -32768);
302 assert_eq!(quantize_i16(1.5 / 32768.0), 2);
304 assert_eq!(quantize_i16(-1.5 / 32768.0), -2);
305 }
306}