1use std::fs::File;
16use std::io::{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]
49fn quantize_i16(x: f32) -> i32 {
50 flexaudio_core::quantize_i16(x) as i32
51}
52
53fn enc_err(e: impl std::fmt::Display) -> EncodeError {
55 EncodeError::Encoder(e.to_string())
56}
57
58trait Output: Write + Seek + Send + Sync + std::panic::UnwindSafe + std::panic::RefUnwindSafe {}
59impl<T: Write + Seek + Send + Sync + std::panic::UnwindSafe + std::panic::RefUnwindSafe> Output
60 for T
61{
62}
63
64#[derive(Debug)]
65enum WriterState {
66 Open,
67 Finalized,
68 Failed(FailureCause),
69}
70
71#[derive(Debug)]
74enum FailureCause {
75 Io {
76 code: Option<i32>,
77 kind: std::io::ErrorKind,
78 message: String,
79 },
80 Unsupported(String),
81 Encoder(String),
82}
83
84impl FailureCause {
85 fn capture(error: EncodeError) -> Self {
86 match error {
87 EncodeError::Io(error) => Self::Io {
88 code: error.raw_os_error(),
89 kind: error.kind(),
90 message: error.to_string(),
91 },
92 EncodeError::Unsupported(message) => Self::Unsupported(message),
93 EncodeError::Encoder(message) => Self::Encoder(message),
94 }
95 }
96
97 fn error(&self) -> EncodeError {
98 match self {
99 Self::Io {
100 code,
101 kind,
102 message,
103 } => EncodeError::Io(match code {
104 Some(code) => std::io::Error::from_raw_os_error(*code),
105 None => std::io::Error::new(*kind, message.clone()),
106 }),
107 Self::Unsupported(message) => EncodeError::Unsupported(message.clone()),
108 Self::Encoder(message) => EncodeError::Encoder(message.clone()),
109 }
110 }
111}
112
113pub struct FlacWriter {
123 file: Box<dyn Output>,
125 config: Verified<EncoderConfig>,
126 stream_info: StreamInfo,
128 frame_buf: FrameBuf,
130 context: Context,
132 sink: ByteSink,
134 pending: Vec<i32>,
136 channels: usize,
137 frame_number: usize,
139 state: WriterState,
140}
141
142impl FlacWriter {
143 pub fn create<P: AsRef<Path>>(path: P, sample_rate: u32, channels: u16) -> Result<FlacWriter> {
148 if !(1..=2).contains(&channels) {
149 return Err(EncodeError::Unsupported(format!(
150 "channels must be 1 or 2, got {channels}"
151 )));
152 }
153 if !(1..=MAX_SAMPLE_RATE).contains(&sample_rate) {
154 return Err(EncodeError::Unsupported(format!(
155 "sample rate must be 1..={MAX_SAMPLE_RATE} Hz, got {sample_rate}"
156 )));
157 }
158
159 Self::with_output(sample_rate, channels, Box::new(File::create(path)?))
160 }
161
162 fn with_output(sample_rate: u32, channels: u16, file: Box<dyn Output>) -> Result<Self> {
163 let config = EncoderConfig::default()
164 .into_verified()
165 .map_err(|(_, e)| enc_err(e))?;
166 let mut stream_info =
167 StreamInfo::new(sample_rate as usize, channels as usize, BITS_PER_SAMPLE)
168 .map_err(enc_err)?;
169 stream_info
171 .set_block_sizes(BLOCK_SIZE, BLOCK_SIZE)
172 .map_err(enc_err)?;
173 let frame_buf = FrameBuf::with_size(channels as usize, BLOCK_SIZE).map_err(enc_err)?;
174 let context = Context::new(BITS_PER_SAMPLE, channels as usize);
175
176 let mut writer = FlacWriter {
177 file,
178 config,
179 stream_info,
180 frame_buf,
181 context,
182 sink: ByteSink::new(),
183 pending: Vec::new(),
184 channels: channels as usize,
185 frame_number: 0,
186 state: WriterState::Open,
187 };
188 let header = writer.header_bytes()?;
190 let result = writer.file.write_all(&header).map_err(EncodeError::Io);
191 writer.latch(result)?;
192 Ok(writer)
193 }
194
195 pub fn write_chunk(&mut self, interleaved: &[f32]) -> Result<()> {
202 self.check_open()?;
203 if interleaved.len() % self.channels != 0 {
204 return Err(EncodeError::Unsupported(format!(
205 "chunk length {} is not a multiple of channels {}",
206 interleaved.len(),
207 self.channels
208 )));
209 }
210 self.pending.reserve(interleaved.len());
211 self.pending
212 .extend(interleaved.iter().map(|&x| quantize_i16(x)));
213 let result = self.drain_full_blocks();
214 self.latch(result)
215 }
216
217 pub fn finalize(mut self) -> Result<()> {
223 self.finish()
224 }
225
226 fn check_open(&self) -> Result<()> {
227 match &self.state {
228 WriterState::Open => Ok(()),
229 WriterState::Finalized => Err(EncodeError::Unsupported("writer is finalized".into())),
230 WriterState::Failed(error) => Err(error.error()),
231 }
232 }
233
234 fn latch(&mut self, result: Result<()>) -> Result<()> {
235 if let Err(error) = result {
236 self.state = WriterState::Failed(FailureCause::capture(error));
237 return self.check_open();
238 }
239 Ok(())
240 }
241
242 fn finish(&mut self) -> Result<()> {
243 self.check_open()?;
244 let result = self.finish_inner();
245 self.latch(result)?;
246 self.state = WriterState::Finalized;
247 Ok(())
248 }
249
250 fn drain_full_blocks(&mut self) -> Result<()> {
252 let block_len = BLOCK_SIZE * self.channels;
253 let pending = std::mem::take(&mut self.pending);
256 let mut consumed = 0;
257 let mut result = Ok(());
258 while consumed + block_len <= pending.len() {
259 if let Err(e) = self.encode_block(&pending[consumed..consumed + block_len]) {
260 result = Err(e);
261 break;
262 }
263 consumed += block_len;
264 }
265 self.pending = pending;
266 self.pending.drain(..consumed);
267 result
268 }
269
270 fn encode_block(&mut self, block: &[i32]) -> Result<()> {
273 self.frame_buf.fill_interleaved(block).map_err(enc_err)?;
274 self.context.fill_interleaved(block).map_err(enc_err)?;
276
277 let frame = flacenc::encode_fixed_size_frame(
278 &self.config,
279 &self.frame_buf,
280 self.frame_number,
281 &self.stream_info,
282 )
283 .map_err(enc_err)?;
284 self.stream_info.update_frame_info(&frame);
286
287 self.sink.clear();
288 frame.write(&mut self.sink).map_err(enc_err)?;
289 self.file.write_all(self.sink.as_slice())?;
291 self.frame_number += 1;
292 Ok(())
293 }
294
295 fn finish_inner(&mut self) -> Result<()> {
297 if !self.pending.is_empty() {
299 let tail = std::mem::take(&mut self.pending);
300 self.encode_block(&tail)?;
301 }
302
303 self.stream_info
310 .set_block_sizes(BLOCK_SIZE, BLOCK_SIZE)
311 .map_err(enc_err)?;
312 self.stream_info.set_md5_digest(&self.context.md5_digest());
313 self.stream_info
314 .set_total_samples(self.context.total_samples());
315
316 let header = self.header_bytes()?;
318 self.file.flush()?;
321 self.file.rewind()?;
322 self.file.write_all(b"FAIL")?;
323 self.file.write_all(&header[4..])?;
324 self.file.flush()?;
325 self.file.rewind()?;
326 self.file.write_all(&header[..4])?;
327 Ok(())
328 }
329
330 fn header_bytes(&self) -> Result<Vec<u8>> {
335 let mut info = self.stream_info.clone();
336 if self.frame_number == 0 {
337 info.set_frame_sizes(0, 0).map_err(enc_err)?;
340 }
341 let stream = Stream::with_stream_info(info);
343 let mut sink = ByteSink::new();
344 stream.write(&mut sink).map_err(enc_err)?;
345 let bytes = sink.into_inner();
346 debug_assert_eq!(bytes.len(), HEADER_LEN);
347 Ok(bytes)
348 }
349}
350
351impl Drop for FlacWriter {
352 fn drop(&mut self) {
353 if matches!(self.state, WriterState::Open) {
354 let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| self.finish()));
356 }
357 }
358}
359
360impl std::fmt::Debug for FlacWriter {
362 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
363 f.debug_struct("FlacWriter")
364 .field("sample_rate", &self.stream_info.sample_rate())
365 .field("channels", &self.channels)
366 .field("frame_number", &self.frame_number)
367 .field("pending_samples", &self.pending.len())
368 .field("state", &self.state)
369 .finish_non_exhaustive()
370 }
371}
372
373#[cfg(test)]
374mod tests {
375 use super::*;
376 use std::io::{Cursor, SeekFrom};
377 use std::sync::{Arc, Mutex};
378
379 struct FaultOutput {
380 data: Arc<Mutex<Cursor<Vec<u8>>>>,
381 calls: Arc<Mutex<usize>>,
382 fail_at: usize,
383 partial: bool,
384 fail_flush_at: usize,
385 flushes: usize,
386 }
387
388 impl Write for FaultOutput {
389 fn write(&mut self, bytes: &[u8]) -> std::io::Result<usize> {
390 let mut calls = self.calls.lock().unwrap();
391 *calls += 1;
392 if *calls == self.fail_at {
393 if self.partial {
394 return self.data.lock().unwrap().write(&bytes[..bytes.len() / 2]);
395 }
396 return Err(std::io::Error::other("injected first failure"));
397 }
398 if self.partial && *calls == self.fail_at + 1 {
399 return Err(std::io::Error::other("injected first failure"));
400 }
401 self.data.lock().unwrap().write(bytes)
402 }
403 fn flush(&mut self) -> std::io::Result<()> {
404 self.flushes += 1;
405 if self.flushes == self.fail_flush_at {
406 Err(std::io::Error::other("injected flush failure"))
407 } else {
408 Ok(())
409 }
410 }
411 }
412 impl Seek for FaultOutput {
413 fn seek(&mut self, position: SeekFrom) -> std::io::Result<u64> {
414 self.data.lock().unwrap().seek(position)
415 }
416 }
417
418 #[test]
419 fn failed_frame_is_never_retried_or_finalized() {
420 for partial in [false, true] {
421 let data = Arc::new(Mutex::new(Cursor::new(Vec::new())));
422 let calls = Arc::new(Mutex::new(0));
423 let output = FaultOutput {
424 data: data.clone(),
425 calls: calls.clone(),
426 fail_at: 3,
427 partial,
428 fail_flush_at: usize::MAX,
429 flushes: 0,
430 };
431 let mut writer = FlacWriter::with_output(48_000, 1, Box::new(output)).unwrap();
432 writer.write_chunk(&vec![0.25; BLOCK_SIZE]).unwrap();
433 let first = writer
434 .write_chunk(&vec![0.5; BLOCK_SIZE])
435 .unwrap_err()
436 .to_string();
437 let count = writer.context.total_samples();
438 let digest = writer.context.md5_digest();
439 let bytes = data.lock().unwrap().get_ref().clone();
440 let writes = *calls.lock().unwrap();
441 assert_eq!(writer.write_chunk(&[]).unwrap_err().to_string(), first);
443 assert_eq!(writer.finish().unwrap_err().to_string(), first);
444 assert_eq!(writer.context.total_samples(), count);
445 assert_eq!(writer.context.md5_digest(), digest);
446 assert_eq!(writer.finalize().unwrap_err().to_string(), first);
447 assert_eq!(*calls.lock().unwrap(), writes);
448 assert_eq!(*data.lock().unwrap().get_ref(), bytes);
449 assert!(bytes[22..42].iter().all(|&byte| byte == 0));
451 }
452 }
453
454 #[test]
455 fn failed_drop_and_header_flush_do_not_retry() {
456 let data = Arc::new(Mutex::new(Cursor::new(Vec::new())));
457 let calls = Arc::new(Mutex::new(0));
458 let mut writer = FlacWriter::with_output(
459 48_000,
460 1,
461 Box::new(FaultOutput {
462 data: data.clone(),
463 calls: calls.clone(),
464 fail_at: 2,
465 partial: false,
466 fail_flush_at: usize::MAX,
467 flushes: 0,
468 }),
469 )
470 .unwrap();
471 assert!(writer.write_chunk(&vec![0.25; BLOCK_SIZE]).is_err());
472 let before = *calls.lock().unwrap();
473 drop(writer);
474 assert_eq!(*calls.lock().unwrap(), before);
475
476 let mut writer = FlacWriter::with_output(
477 48_000,
478 1,
479 Box::new(FaultOutput {
480 data: data.clone(),
481 calls: calls.clone(),
482 fail_at: usize::MAX,
483 partial: false,
484 fail_flush_at: 1,
485 flushes: 0,
486 }),
487 )
488 .unwrap();
489 writer.write_chunk(&[0.25]).unwrap();
490 let first = writer.finish().unwrap_err().to_string();
491 assert!(first.contains("flush failure"));
492 let before = *calls.lock().unwrap();
493 assert_eq!(writer.finalize().unwrap_err().to_string(), first);
494 assert_eq!(*calls.lock().unwrap(), before);
495 }
496
497 #[test]
498 fn header_publication_failure_invalidates_magic_and_latches() {
499 for (fail_at, partial, fail_flush_at) in [
501 (4, false, usize::MAX),
502 (4, true, usize::MAX),
503 (usize::MAX, false, 2),
504 (5, false, usize::MAX),
505 (5, true, usize::MAX),
506 ] {
507 let data = Arc::new(Mutex::new(Cursor::new(Vec::new())));
508 let calls = Arc::new(Mutex::new(0));
509 let mut writer = FlacWriter::with_output(
510 48_000,
511 1,
512 Box::new(FaultOutput {
513 data: data.clone(),
514 calls: calls.clone(),
515 fail_at,
516 partial,
517 fail_flush_at,
518 flushes: 0,
519 }),
520 )
521 .unwrap();
522 writer.write_chunk(&[0.25]).unwrap();
523 let first = writer.finish().unwrap_err().to_string();
524 assert_ne!(&data.lock().unwrap().get_ref()[..4], b"fLaC");
525 let before = *calls.lock().unwrap();
526 assert_eq!(writer.finalize().unwrap_err().to_string(), first);
527 assert_eq!(*calls.lock().unwrap(), before);
528 }
529 }
530
531 #[test]
532 fn encoder_failure_and_auto_traits() {
533 fn assert_send_sync<T: Send + Sync + std::panic::UnwindSafe + std::panic::RefUnwindSafe>() {
534 }
535 assert_send_sync::<FlacWriter>();
536 let data = Arc::new(Mutex::new(Cursor::new(Vec::new())));
537 let calls = Arc::new(Mutex::new(0));
538 let mut writer = FlacWriter::with_output(
539 48_000,
540 1,
541 Box::new(FaultOutput {
542 data,
543 calls: calls.clone(),
544 fail_at: usize::MAX,
545 partial: false,
546 fail_flush_at: usize::MAX,
547 flushes: 0,
548 }),
549 )
550 .unwrap();
551 writer.frame_number = usize::MAX;
552 let first = writer.write_chunk(&vec![0.25; BLOCK_SIZE]).unwrap_err();
553 assert!(matches!(first, EncodeError::Encoder(_)));
554 let count = writer.context.total_samples();
555 let digest = writer.context.md5_digest();
556 assert_eq!(
557 writer.write_chunk(&[]).unwrap_err().to_string(),
558 first.to_string()
559 );
560 assert_eq!(writer.context.total_samples(), count);
561 assert_eq!(writer.context.md5_digest(), digest);
562 assert_eq!(
563 writer.finalize().unwrap_err().to_string(),
564 first.to_string()
565 );
566 assert_eq!(*calls.lock().unwrap(), 1);
567 }
568
569 #[test]
570 fn invalid_chunk_does_not_fail_an_open_writer() {
571 let data = Arc::new(Mutex::new(Cursor::new(Vec::new())));
572 let output = FaultOutput {
573 data,
574 calls: Arc::new(Mutex::new(0)),
575 fail_at: usize::MAX,
576 partial: false,
577 fail_flush_at: usize::MAX,
578 flushes: 0,
579 };
580 let mut writer = FlacWriter::with_output(48_000, 2, Box::new(output)).unwrap();
581 assert!(writer.write_chunk(&[0.5]).is_err());
582 writer.write_chunk(&[0.5, 0.5]).unwrap();
583 writer.finalize().unwrap();
584 }
585
586 #[test]
587 fn quantize_reference_points() {
588 assert_eq!(quantize_i16(0.0), 0);
589 assert_eq!(quantize_i16(0.25), 8192);
590 assert_eq!(quantize_i16(-1.0), -32768);
591 assert_eq!(quantize_i16(1.0), 32767);
593 assert_eq!(quantize_i16(2.0), 32767);
595 assert_eq!(quantize_i16(-2.0), -32768);
596 assert_eq!(quantize_i16(f32::NAN), 0);
598 assert_eq!(quantize_i16(f32::INFINITY), 32767);
599 assert_eq!(quantize_i16(f32::NEG_INFINITY), -32768);
600 assert_eq!(quantize_i16(1.5 / 32768.0), 2);
602 assert_eq!(quantize_i16(-1.5 / 32768.0), -2);
603 }
604}