flac_codec/metadata/mod.rs
1// Copyright 2025 Brian Langenberger
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! For handling a FLAC file's metadata blocks
10//!
11//! Many items are capitalized simply because they were capitalized
12//! in the original FLAC format documentation.
13//!
14//! # Metadata Blocks
15//!
16//! FLAC supports seven different metadata block types
17//!
18//! | Block Type | Purpose |
19//! |-----------:|---------|
20//! | [STREAMINFO](`Streaminfo`) | stream information such as sample rate, channel count, etc. |
21//! | [PADDING](`Padding`) | empty data which can easily be resized as needed |
22//! | [APPLICATION](`Application`) | application-specific data such as foreign RIFF WAVE chunks |
23//! | [SEEKTABLE](`SeekTable`) | to allow for more efficient seeking within a FLAC file |
24//! | [VORBIS_COMMENT](`VorbisComment`) | textual metadata such as track title, artist name, album name, etc. |
25//! | [CUESHEET](`Cuesheet`) | the original disc's layout, for CD images |
26//! | [PICTURE](`Picture`) | embedded image files such as cover art |
27
28use crate::Error;
29use bitstream_io::{
30 BigEndian, BitRead, BitReader, BitWrite, ByteRead, ByteReader, FromBitStream,
31 FromBitStreamUsing, FromBitStreamWith, LittleEndian, SignedBitCount, ToBitStream,
32 ToBitStreamUsing, write::Overflowed,
33};
34use std::fs::File;
35use std::io::BufReader;
36use std::num::NonZero;
37use std::path::Path;
38
39/// Types related to the CUESHEET metadata block
40pub mod cuesheet;
41
42/// A trait for indicating various pieces of FLAC stream metadata
43///
44/// This metadata may be necessary for decoding a FLAC file
45/// to some other container or an output stream.
46pub trait Metadata {
47 /// Returns channel count
48 ///
49 /// From 1 to 8
50 fn channel_count(&self) -> u8;
51
52 /// Returns channel mask
53 ///
54 /// This uses the channel mask defined
55 /// in the Vorbis comment, if found, or defaults
56 /// to FLAC's default channel assignment if not
57 fn channel_mask(&self) -> ChannelMask {
58 ChannelMask::from_channels(self.channel_count())
59 }
60
61 /// Returns sample rate, in Hz
62 fn sample_rate(&self) -> u32;
63
64 /// Returns decoder's bits-per-sample
65 ///
66 /// From 1 to 32
67 fn bits_per_sample(&self) -> u32;
68
69 /// Returns total number of channel-independent samples, if known
70 fn total_samples(&self) -> Option<u64> {
71 None
72 }
73
74 /// Returns MD5 of entire stream, if known
75 ///
76 /// MD5 is always calculated in terms of little-endian,
77 /// signed, byte-aligned values.
78 fn md5(&self) -> Option<&[u8; 16]> {
79 None
80 }
81
82 /// Returns total length of decoded file, in bytes
83 fn decoded_len(&self) -> Option<u64> {
84 self.total_samples().map(|s| {
85 s * u64::from(self.channel_count()) * u64::from(self.bits_per_sample().div_ceil(8))
86 })
87 }
88
89 /// Returns duration of file
90 fn duration(&self) -> Option<std::time::Duration> {
91 const NANOS_PER_SEC: u64 = 1_000_000_000;
92
93 let sample_rate = u64::from(self.sample_rate());
94
95 self.total_samples().map(|s| {
96 std::time::Duration::new(
97 s / sample_rate,
98 u32::try_from(((s % sample_rate) * NANOS_PER_SEC) / sample_rate)
99 .unwrap_or_default(),
100 )
101 })
102 }
103}
104
105/// A FLAC metadata block header
106///
107/// | Bits | Field | Meaning |
108/// |-----:|------:|---------|
109/// | 1 | `last` | final metadata block in file |
110/// | 7 | `block_type` | type of block |
111/// | 24 | `size` | block size, in bytes |
112///
113/// # Example
114/// ```
115/// use bitstream_io::{BitReader, BitRead, BigEndian};
116/// use flac_codec::metadata::{BlockHeader, BlockType};
117///
118/// let data: &[u8] = &[0b1_0000000, 0x00, 0x00, 0x22];
119/// let mut r = BitReader::endian(data, BigEndian);
120/// assert_eq!(
121/// r.parse::<BlockHeader>().unwrap(),
122/// BlockHeader {
123/// last: true, // 0b1
124/// block_type: BlockType::Streaminfo, // 0b0000000
125/// size: 0x00_00_22u16.into(), // 0x00, 0x00, 0x22
126/// },
127/// );
128/// ```
129#[derive(Debug, Eq, PartialEq)]
130pub struct BlockHeader {
131 /// Whether we are the final block
132 pub last: bool,
133 /// Our block type
134 pub block_type: BlockType,
135 /// Our block size, in bytes
136 pub size: BlockSize,
137}
138
139impl BlockHeader {
140 const SIZE: BlockSize = BlockSize((1 + 7 + 24) / 8);
141}
142
143/// A type of FLAC metadata block
144pub trait MetadataBlock:
145 ToBitStream<Error: Into<Error>> + Into<Block> + TryFrom<Block> + Clone
146{
147 /// The metadata block's type
148 const TYPE: BlockType;
149
150 /// Whether the block can occur multiple times in a file
151 const MULTIPLE: bool;
152
153 /// Size of block, in bytes, not including header
154 fn bytes(&self) -> Option<BlockSize> {
155 self.bits::<BlockBits>().ok().map(|b| b.into())
156 }
157
158 /// Size of block, in bytes, including block header
159 fn total_size(&self) -> Option<BlockSize> {
160 self.bytes().and_then(|s| s.checked_add(BlockHeader::SIZE))
161 }
162}
163
164#[derive(Default)]
165struct BlockBits(u32);
166
167impl BlockBits {
168 const MAX: u32 = BlockSize::MAX * 8;
169}
170
171impl From<u8> for BlockBits {
172 fn from(u: u8) -> Self {
173 Self(u.into())
174 }
175}
176
177impl TryFrom<u32> for BlockBits {
178 type Error = (); // the error will be replaced later
179
180 fn try_from(u: u32) -> Result<Self, Self::Error> {
181 (u <= Self::MAX).then_some(Self(u)).ok_or(())
182 }
183}
184
185impl TryFrom<usize> for BlockBits {
186 type Error = (); // the error will be replaced later
187
188 fn try_from(u: usize) -> Result<Self, Self::Error> {
189 u32::try_from(u)
190 .map_err(|_| ())
191 .and_then(|u| (u <= Self::MAX).then_some(Self(u)).ok_or(()))
192 }
193}
194
195impl bitstream_io::write::Counter for BlockBits {
196 fn checked_add_assign(&mut self, Self(b): Self) -> Result<(), Overflowed> {
197 *self = self
198 .0
199 .checked_add(b)
200 .filter(|b| *b <= Self::MAX)
201 .map(Self)
202 .ok_or(Overflowed)?;
203 Ok(())
204 }
205
206 fn checked_mul(self, Self(b): Self) -> Result<Self, Overflowed> {
207 self.0
208 .checked_mul(b)
209 .filter(|b| *b <= Self::MAX)
210 .map(Self)
211 .ok_or(Overflowed)
212 }
213
214 fn byte_aligned(&self) -> bool {
215 self.0 % 8 == 0
216 }
217}
218
219impl From<BlockBits> for BlockSize {
220 fn from(BlockBits(u): BlockBits) -> Self {
221 assert!(u % 8 == 0);
222 Self(u / 8)
223 }
224}
225
226impl BlockHeader {
227 fn new<M: MetadataBlock>(last: bool, block: &M) -> Result<Self, Error> {
228 fn large_block<E: Into<Error>>(err: E) -> Error {
229 match err.into() {
230 Error::Io(_) => Error::ExcessiveBlockSize,
231 e => e,
232 }
233 }
234
235 Ok(Self {
236 last,
237 block_type: M::TYPE,
238 size: block.bits::<BlockBits>().map_err(large_block)?.into(),
239 })
240 }
241}
242
243impl FromBitStream for BlockHeader {
244 type Error = Error;
245
246 fn from_reader<R: BitRead + ?Sized>(r: &mut R) -> Result<Self, Self::Error> {
247 Ok(Self {
248 last: r.read::<1, _>()?,
249 block_type: r.parse()?,
250 size: r.parse()?,
251 })
252 }
253}
254
255impl ToBitStream for BlockHeader {
256 type Error = Error;
257
258 fn to_writer<W: BitWrite + ?Sized>(&self, w: &mut W) -> Result<(), Self::Error> {
259 w.write::<1, _>(self.last)?;
260 w.build(&self.block_type)?;
261 w.build(&self.size)?;
262 Ok(())
263 }
264}
265
266/// A defined FLAC metadata block type
267#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
268pub enum BlockType {
269 /// The STREAMINFO block
270 Streaminfo = 0,
271 /// The PADDING block
272 Padding = 1,
273 /// The APPLICATION block
274 Application = 2,
275 /// The SEEKTABLE block
276 SeekTable = 3,
277 /// The VORBIS_COMMENT block
278 VorbisComment = 4,
279 /// The CUESHEET block
280 Cuesheet = 5,
281 /// The PICTURE block
282 Picture = 6,
283}
284
285impl std::fmt::Display for BlockType {
286 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
287 match self {
288 Self::Streaminfo => "STREAMINFO".fmt(f),
289 Self::Padding => "PADDING".fmt(f),
290 Self::Application => "APPLICATION".fmt(f),
291 Self::SeekTable => "SEEKTABLE".fmt(f),
292 Self::VorbisComment => "VORBIS_COMMENT".fmt(f),
293 Self::Cuesheet => "CUESHEET".fmt(f),
294 Self::Picture => "PICTURE".fmt(f),
295 }
296 }
297}
298
299impl FromBitStream for BlockType {
300 type Error = Error;
301
302 fn from_reader<R: BitRead + ?Sized>(r: &mut R) -> Result<Self, Self::Error> {
303 match r.read::<7, u8>()? {
304 0 => Ok(Self::Streaminfo),
305 1 => Ok(Self::Padding),
306 2 => Ok(Self::Application),
307 3 => Ok(Self::SeekTable),
308 4 => Ok(Self::VorbisComment),
309 5 => Ok(Self::Cuesheet),
310 6 => Ok(Self::Picture),
311 7..=126 => Err(Error::ReservedMetadataBlock),
312 _ => Err(Error::InvalidMetadataBlock),
313 }
314 }
315}
316
317impl ToBitStream for BlockType {
318 type Error = Error;
319
320 fn to_writer<W: BitWrite + ?Sized>(&self, w: &mut W) -> Result<(), Self::Error> {
321 w.write::<7, u8>(match self {
322 Self::Streaminfo => 0,
323 Self::Padding => 1,
324 Self::Application => 2,
325 Self::SeekTable => 3,
326 Self::VorbisComment => 4,
327 Self::Cuesheet => 5,
328 Self::Picture => 6,
329 })
330 .map_err(Error::Io)
331 }
332}
333
334/// A block type for optional FLAC metadata blocks
335///
336/// This is a subset of [`BlockType`] which contains
337/// no STREAMINFO, which is a required block.
338#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq)]
339pub enum OptionalBlockType {
340 /// The PADDING block
341 Padding = 1,
342 /// The APPLICATION block
343 Application = 2,
344 /// The SEEKTABLE block
345 SeekTable = 3,
346 /// The VORBIS_COMMENT block
347 VorbisComment = 4,
348 /// The CUESHEET block
349 Cuesheet = 5,
350 /// The PICTURE block
351 Picture = 6,
352}
353
354impl From<OptionalBlockType> for BlockType {
355 fn from(block: OptionalBlockType) -> Self {
356 match block {
357 OptionalBlockType::Padding => Self::Padding,
358 OptionalBlockType::Application => Self::Application,
359 OptionalBlockType::SeekTable => Self::SeekTable,
360 OptionalBlockType::VorbisComment => Self::VorbisComment,
361 OptionalBlockType::Cuesheet => Self::Cuesheet,
362 OptionalBlockType::Picture => Self::Picture,
363 }
364 }
365}
366
367/// A 24-bit block size value, with safeguards against overflow
368#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
369pub struct BlockSize(u32);
370
371impl BlockSize {
372 /// A value of 0
373 pub const ZERO: BlockSize = BlockSize(0);
374
375 const MAX: u32 = (1 << 24) - 1;
376
377 /// Our current value as a u32
378 fn get(&self) -> u32 {
379 self.0
380 }
381}
382
383impl BlockSize {
384 /// Conditionally add `BlockSize` to ourself
385 pub fn checked_add(self, rhs: Self) -> Option<Self> {
386 self.0
387 .checked_add(rhs.0)
388 .filter(|s| *s <= Self::MAX)
389 .map(Self)
390 }
391
392 /// Conditionally subtract `BlockSize` from ourself
393 pub fn checked_sub(self, rhs: Self) -> Option<Self> {
394 self.0.checked_sub(rhs.0).map(Self)
395 }
396}
397
398impl std::fmt::Display for BlockSize {
399 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
400 self.0.fmt(f)
401 }
402}
403
404impl FromBitStream for BlockSize {
405 type Error = std::io::Error;
406
407 fn from_reader<R: BitRead + ?Sized>(r: &mut R) -> Result<Self, Self::Error> {
408 r.read::<24, _>().map(Self)
409 }
410}
411
412impl ToBitStream for BlockSize {
413 type Error = std::io::Error;
414
415 fn to_writer<W: BitWrite + ?Sized>(&self, w: &mut W) -> Result<(), Self::Error> {
416 w.write::<24, _>(self.0)
417 }
418}
419
420impl From<u8> for BlockSize {
421 fn from(u: u8) -> Self {
422 Self(u.into())
423 }
424}
425
426impl From<u16> for BlockSize {
427 fn from(u: u16) -> Self {
428 Self(u.into())
429 }
430}
431
432impl TryFrom<usize> for BlockSize {
433 type Error = BlockSizeOverflow;
434
435 fn try_from(u: usize) -> Result<Self, Self::Error> {
436 u32::try_from(u)
437 .map_err(|_| BlockSizeOverflow)
438 .and_then(|s| (s <= Self::MAX).then_some(Self(s)).ok_or(BlockSizeOverflow))
439 }
440}
441
442impl TryFrom<u32> for BlockSize {
443 type Error = BlockSizeOverflow;
444
445 fn try_from(u: u32) -> Result<Self, Self::Error> {
446 (u <= Self::MAX).then_some(Self(u)).ok_or(BlockSizeOverflow)
447 }
448}
449
450impl TryFrom<u64> for BlockSize {
451 type Error = BlockSizeOverflow;
452
453 fn try_from(u: u64) -> Result<Self, Self::Error> {
454 u32::try_from(u)
455 .map_err(|_| BlockSizeOverflow)
456 .and_then(|s| (s <= Self::MAX).then_some(Self(s)).ok_or(BlockSizeOverflow))
457 }
458}
459
460impl From<BlockSize> for u32 {
461 #[inline]
462 fn from(size: BlockSize) -> u32 {
463 size.0
464 }
465}
466
467/// An error that occurs when trying to build an overly large `BlockSize`
468#[derive(Copy, Clone, Debug)]
469pub struct BlockSizeOverflow;
470
471impl std::error::Error for BlockSizeOverflow {}
472
473impl std::fmt::Display for BlockSizeOverflow {
474 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
475 "value too large for BlockSize".fmt(f)
476 }
477}
478
479/// An iterator over FLAC metadata blocks
480pub struct BlockIterator<R: std::io::Read> {
481 reader: R,
482 failed: bool,
483 tag_read: bool,
484 streaminfo_read: bool,
485 seektable_read: bool,
486 vorbiscomment_read: bool,
487 png_read: bool,
488 icon_read: bool,
489 finished: bool,
490}
491
492impl<R: std::io::Read> BlockIterator<R> {
493 /// Creates an iterator over something that implements `Read`.
494 /// Because this may perform many small reads,
495 /// performance is greatly improved by buffering reads
496 /// when reading from a raw `File`.
497 pub fn new(reader: R) -> Self {
498 Self {
499 reader,
500 failed: false,
501 tag_read: false,
502 streaminfo_read: false,
503 seektable_read: false,
504 vorbiscomment_read: false,
505 png_read: false,
506 icon_read: false,
507 finished: false,
508 }
509 }
510
511 fn read_block(&mut self) -> Option<Result<Block, Error>> {
512 // like a slighly easier variant of "Take"
513 struct LimitedReader<R> {
514 reader: R,
515 size: usize,
516 }
517
518 impl<R: std::io::Read> std::io::Read for LimitedReader<R> {
519 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
520 let size = self.size.min(buf.len());
521 self.reader.read(&mut buf[0..size]).inspect(|amt_read| {
522 self.size -= amt_read;
523 })
524 }
525 }
526
527 (!self.finished).then(|| {
528 BitReader::endian(&mut self.reader, BigEndian)
529 .parse()
530 .and_then(|header: BlockHeader| {
531 let mut reader = BitReader::endian(
532 LimitedReader {
533 reader: self.reader.by_ref(),
534 size: header.size.get().try_into().unwrap(),
535 },
536 BigEndian,
537 );
538
539 let block = reader.parse_with(&header)?;
540
541 match reader.into_reader().size {
542 0 => {
543 self.finished = header.last;
544 Ok(block)
545 }
546 _ => Err(Error::InvalidMetadataBlockSize),
547 }
548 })
549 })
550 }
551}
552
553impl<R: std::io::Read> Iterator for BlockIterator<R> {
554 type Item = Result<Block, Error>;
555
556 fn next(&mut self) -> Option<Self::Item> {
557 if self.failed {
558 // once we hit an error, stop any further reads
559 None
560 } else if !self.tag_read {
561 // "fLaC" tag must come before anything else
562 let mut tag = [0; 4];
563 match self.reader.read_exact(&mut tag) {
564 Ok(()) => match &tag {
565 b"fLaC" => {
566 self.tag_read = true;
567 self.next()
568 }
569 _ => {
570 self.failed = true;
571 Some(Err(Error::MissingFlacTag))
572 }
573 },
574 Err(err) => {
575 self.failed = true;
576 Some(Err(Error::Io(err)))
577 }
578 }
579 } else if !self.streaminfo_read {
580 // STREAMINFO block must be first in file
581 match self.read_block() {
582 block @ Some(Ok(Block::Streaminfo(_))) => {
583 self.streaminfo_read = true;
584 block
585 }
586 _ => {
587 self.failed = true;
588 Some(Err(Error::MissingStreaminfo))
589 }
590 }
591 } else {
592 match self.read_block() {
593 Some(Ok(Block::Streaminfo(_))) => Some(Err(Error::MultipleStreaminfo)),
594 seektable @ Some(Ok(Block::SeekTable(_))) => {
595 if !self.seektable_read {
596 self.seektable_read = true;
597 seektable
598 } else {
599 self.failed = true;
600 Some(Err(Error::MultipleSeekTable))
601 }
602 }
603 vorbiscomment @ Some(Ok(Block::VorbisComment(_))) => {
604 if !self.vorbiscomment_read {
605 self.vorbiscomment_read = true;
606 vorbiscomment
607 } else {
608 self.failed = true;
609 Some(Err(Error::MultipleVorbisComment))
610 }
611 }
612 picture @ Some(Ok(Block::Picture(Picture {
613 picture_type: PictureType::Png32x32,
614 ..
615 }))) => {
616 if !self.png_read {
617 self.png_read = true;
618 picture
619 } else {
620 self.failed = true;
621 Some(Err(Error::MultiplePngIcon))
622 }
623 }
624 picture @ Some(Ok(Block::Picture(Picture {
625 picture_type: PictureType::GeneralFileIcon,
626 ..
627 }))) => {
628 if !self.icon_read {
629 self.icon_read = true;
630 picture
631 } else {
632 self.failed = true;
633 Some(Err(Error::MultipleGeneralIcon))
634 }
635 }
636 block @ Some(Err(_)) => {
637 self.failed = true;
638 block
639 }
640 block => block,
641 }
642 }
643 }
644}
645
646/// Returns iterator of blocks from the given reader
647///
648/// The reader should be positioned at the start of the FLAC
649/// file.
650///
651/// Because this may perform many small reads,
652/// using a buffered reader may greatly improve performance
653/// when reading from a raw `File`.
654///
655/// # Example
656///
657/// ```
658/// use flac_codec::{
659/// metadata::{read_blocks, Application, Block},
660/// encode::{FlacSampleWriter, Options},
661/// };
662/// use std::io::{Cursor, Seek};
663///
664/// let mut flac = Cursor::new(vec![]); // a FLAC file in memory
665///
666/// // add some APPLICATION blocks at encode-time
667/// let application_1 = Application {id: 0x1234, data: vec![1, 2, 3, 4]};
668/// let application_2 = Application {id: 0x5678, data: vec![5, 6, 7, 8]};
669///
670/// let options = Options::default()
671/// .application(application_1.clone())
672/// .application(application_2.clone())
673/// .no_padding()
674/// .no_seektable();
675///
676/// let mut writer = FlacSampleWriter::new(
677/// &mut flac, // our wrapped writer
678/// options, // our encoding options
679/// 44100, // sample rate
680/// 16, // bits-per-sample
681/// 1, // channel count
682/// Some(1), // total samples
683/// ).unwrap();
684///
685/// // write a simple FLAC file
686/// writer.write(std::slice::from_ref(&0)).unwrap();
687/// writer.finalize().unwrap();
688///
689/// flac.rewind().unwrap();
690///
691/// // read blocks from encoded file
692/// let blocks = read_blocks(flac)
693/// .skip(1) // skip STREAMINFO block
694/// .collect::<Result<Vec<Block>, _>>()
695/// .unwrap();
696///
697/// // ensure they match our APPLICATION blocks
698/// assert_eq!(blocks, vec![application_1.into(), application_2.into()]);
699/// ```
700pub fn read_blocks<R: std::io::Read>(r: R) -> BlockIterator<R> {
701 BlockIterator::new(r)
702}
703
704/// Returns iterator of blocks from the given path
705///
706/// # Errors
707///
708/// Returns any I/O error from opening the path.
709/// Note that the iterator itself may return any errors
710/// from reading individual blocks.
711pub fn blocks<P: AsRef<Path>>(p: P) -> std::io::Result<BlockIterator<BufReader<File>>> {
712 File::open(p.as_ref()).map(|f| read_blocks(BufReader::new(f)))
713}
714
715/// Returns first instance of the given block from the given reader
716///
717/// The reader should be positioned at the start of the FLAC
718/// file.
719///
720/// Because this may perform many small reads,
721/// using a buffered reader may greatly improve performance
722/// when reading from a raw `File`.
723///
724/// # Example
725///
726/// ```
727/// use flac_codec::{
728/// metadata::{read_block, Streaminfo},
729/// encode::{FlacSampleWriter, Options},
730/// };
731/// use std::io::{Cursor, Seek};
732///
733/// let mut flac: Cursor<Vec<u8>> = Cursor::new(vec![]); // a FLAC file in memory
734///
735/// let mut writer = FlacSampleWriter::new(
736/// &mut flac, // our wrapped writer
737/// Options::default(), // default encoding options
738/// 44100, // sample rate
739/// 16, // bits-per-sample
740/// 1, // channel count
741/// Some(1), // total samples
742/// ).unwrap();
743///
744/// // write a simple FLAC file
745/// writer.write(std::slice::from_ref(&0)).unwrap();
746/// writer.finalize().unwrap();
747///
748/// flac.rewind().unwrap();
749///
750/// // STREAMINFO block must always be present
751/// let streaminfo = match read_block::<_, Streaminfo>(flac) {
752/// Ok(Some(streaminfo)) => streaminfo,
753/// _ => panic!("STREAMINFO not found"),
754/// };
755///
756/// // verify STREAMINFO fields against encoding parameters
757/// assert_eq!(streaminfo.sample_rate, 44100);
758/// assert_eq!(u32::from(streaminfo.bits_per_sample), 16);
759/// assert_eq!(streaminfo.channels.get(), 1);
760/// assert_eq!(streaminfo.total_samples.map(|s| s.get()), Some(1));
761/// ```
762pub fn read_block<R, B>(r: R) -> Result<Option<B>, Error>
763where
764 R: std::io::Read,
765 B: MetadataBlock,
766{
767 read_blocks(r)
768 .find_map(|r| r.map(|b| B::try_from(b).ok()).transpose())
769 .transpose()
770}
771
772/// Returns first instance of the given block from the given path
773///
774/// See the [`read_block`] for additional information.
775///
776/// # Errors
777///
778/// Returns any error from opening the path, or from reading
779/// blocks from the path.
780pub fn block<P, B>(p: P) -> Result<Option<B>, Error>
781where
782 P: AsRef<Path>,
783 B: MetadataBlock,
784{
785 blocks(p).map_err(Error::Io).and_then(|mut blocks| {
786 blocks
787 .find_map(|r| r.map(|b| B::try_from(b).ok()).transpose())
788 .transpose()
789 })
790}
791
792/// Writes iterator of blocks to the given writer.
793///
794/// Because this may perform many small writes,
795/// buffering writes may greatly improve performance
796/// when writing to a raw `File`.
797///
798/// let picture_type = picture.picture_type;
799/// # Errorsprintln!(" type: {} ({})", picture_type as u8, picture_type);
800///
801/// Passes along any I/O errors from the underlying stream.
802/// May also generate an error if any of the blocks are invalid
803/// (e.g. STREAMINFO not being the first block, any block is too large, etc.).
804///
805/// # Example
806///
807/// ```
808/// use flac_codec::metadata::{
809/// write_blocks, read_blocks, Streaminfo, Application, Block,
810/// };
811/// use std::io::{Cursor, Seek};
812///
813/// let mut flac: Cursor<Vec<u8>> = Cursor::new(vec![]); // a FLAC file in memory
814///
815/// // our test blocks
816/// let blocks: Vec<Block> = vec![
817/// Streaminfo {
818/// minimum_block_size: 0,
819/// maximum_block_size: 0,
820/// minimum_frame_size: None,
821/// maximum_frame_size: None,
822/// sample_rate: 44100,
823/// channels: 1u8.try_into().unwrap(),
824/// bits_per_sample: 16u32.try_into().unwrap(),
825/// total_samples: None,
826/// md5: None,
827/// }.into(),
828/// Application {id: 0x1234, data: vec![1, 2, 3, 4]}.into(),
829/// Application {id: 0x5678, data: vec![5, 6, 7, 8]}.into(),
830/// ];
831///
832/// // write our test blocks to a file
833/// write_blocks(&mut flac, blocks.clone()).unwrap();
834///
835/// flac.rewind().unwrap();
836///
837/// // read our blocks back from that file
838/// let read_blocks = read_blocks(flac).collect::<Result<Vec<Block>, _>>().unwrap();
839///
840/// // they should be identical
841/// assert_eq!(blocks, read_blocks);
842/// ```
843pub fn write_blocks<B: AsBlockRef>(
844 mut w: impl std::io::Write,
845 blocks: impl IntoIterator<Item = B>,
846) -> Result<(), Error> {
847 fn iter_last<T>(i: impl Iterator<Item = T>) -> impl Iterator<Item = (bool, T)> {
848 struct LastIterator<I: std::iter::Iterator> {
849 iter: std::iter::Peekable<I>,
850 }
851
852 impl<T, I: std::iter::Iterator<Item = T>> Iterator for LastIterator<I> {
853 type Item = (bool, T);
854
855 fn next(&mut self) -> Option<Self::Item> {
856 let item = self.iter.next()?;
857 Some((self.iter.peek().is_none(), item))
858 }
859 }
860
861 LastIterator { iter: i.peekable() }
862 }
863
864 // "FlaC" tag must come before anything else
865 w.write_all(b"fLaC").map_err(Error::Io)?;
866
867 let mut w = bitstream_io::BitWriter::endian(w, BigEndian);
868 let mut blocks = iter_last(blocks.into_iter());
869
870 // STREAMINFO block must be present and must be first in file
871 let next = blocks.next();
872 match next.as_ref().map(|(last, b)| (last, b.as_block_ref())) {
873 Some((last, streaminfo @ BlockRef::Streaminfo(_))) => w.build_using(&streaminfo, *last)?,
874 _ => return Err(Error::MissingStreaminfo),
875 }
876
877 // certain other blocks in the file must only occur once at most
878 let mut seektable_read = false;
879 let mut vorbiscomment_read = false;
880 let mut png_read = false;
881 let mut icon_read = false;
882
883 blocks.try_for_each(|(last, block)| match block.as_block_ref() {
884 BlockRef::Streaminfo(_) => Err(Error::MultipleStreaminfo),
885 vorbiscomment @ BlockRef::VorbisComment(_) => match vorbiscomment_read {
886 false => {
887 vorbiscomment_read = true;
888 w.build_using(&vorbiscomment.as_block_ref(), last)
889 }
890 true => Err(Error::MultipleVorbisComment),
891 },
892 seektable @ BlockRef::SeekTable(_) => match seektable_read {
893 false => {
894 seektable_read = true;
895 w.build_using(&seektable.as_block_ref(), last)
896 }
897 true => Err(Error::MultipleSeekTable),
898 },
899 picture @ BlockRef::Picture(Picture {
900 picture_type: PictureType::Png32x32,
901 ..
902 }) => {
903 if !png_read {
904 png_read = true;
905 w.build_using(&picture.as_block_ref(), last)
906 } else {
907 Err(Error::MultiplePngIcon)
908 }
909 }
910 picture @ BlockRef::Picture(Picture {
911 picture_type: PictureType::GeneralFileIcon,
912 ..
913 }) => {
914 if !icon_read {
915 icon_read = true;
916 w.build_using(&picture.as_block_ref(), last)
917 } else {
918 Err(Error::MultipleGeneralIcon)
919 }
920 }
921 block => w.build_using(&block.as_block_ref(), last),
922 })
923}
924
925/// Given a Path, attempts to update FLAC metadata blocks
926///
927/// Returns `true` if the file was completely rebuilt,
928/// or `false` if the original was overwritten.
929///
930/// # Errors
931///
932/// Returns error if unable to read metadata blocks,
933/// unable to write blocks, or if the existing or updated
934/// blocks do not conform to the FLAC file specification.
935pub fn update<P, E>(path: P, f: impl FnOnce(&mut BlockList) -> Result<(), E>) -> Result<bool, E>
936where
937 P: AsRef<Path>,
938 E: From<Error>,
939{
940 use std::fs::OpenOptions;
941
942 update_file(
943 OpenOptions::new()
944 .read(true)
945 .write(true)
946 .truncate(false)
947 .create(false)
948 .open(path.as_ref())
949 .map_err(Error::Io)?,
950 || std::fs::File::create(path.as_ref()),
951 f,
952 )
953}
954
955/// Given open file, attempts to update its metadata blocks
956///
957/// The original file should be rewound to the start of the stream.
958///
959/// Applies closure `f` to the blocks and attempts to update them.
960///
961/// If the updated blocks can be made the same size as the
962/// original file by adjusting padding, the file will be
963/// partially overwritten with new contents.
964///
965/// If the new blocks are too large (or small) to fit into
966/// the original file, the original unmodified file is dropped
967/// and the `rebuilt` closure is called to build a new
968/// file. The file's contents are then dumped into the new file.
969///
970/// Returns `true` if the file was completely rebuilt,
971/// or `false` if the original was overwritten.
972///
973/// # Example 1
974///
975/// ```
976/// use flac_codec::{
977/// metadata::{update_file, BlockList, Padding, VorbisComment},
978/// metadata::fields::TITLE,
979/// encode::{FlacSampleWriter, Options},
980/// };
981/// use std::io::{Cursor, Seek};
982///
983/// let mut flac = Cursor::new(vec![]); // a FLAC file in memory
984///
985/// // include a small amount of padding
986/// const PADDING_SIZE: u32 = 100;
987/// let options = Options::default().padding(PADDING_SIZE).unwrap();
988///
989/// let mut writer = FlacSampleWriter::new(
990/// &mut flac, // our wrapped writer
991/// options, // our encoding options
992/// 44100, // sample rate
993/// 16, // bits-per-sample
994/// 1, // channel count
995/// Some(1), // total samples
996/// ).unwrap();
997///
998/// // write a simple FLAC file
999/// writer.write(std::slice::from_ref(&0)).unwrap();
1000/// writer.finalize().unwrap();
1001///
1002/// flac.rewind().unwrap();
1003///
1004/// let mut rebuilt: Vec<u8> = vec![];
1005///
1006/// // update file with new Vorbis Comment
1007/// assert!(matches!(
1008/// update_file::<_, _, flac_codec::Error>(
1009/// // our original file
1010/// &mut flac,
1011/// // a closure to create a new file, if necessary
1012/// || Ok(&mut rebuilt),
1013/// // the closure that performs the metadata update
1014/// |blocklist| {
1015/// blocklist.update::<VorbisComment>(
1016/// // the blocklist itself has a closure
1017/// // that updates a block, creating it if necessary
1018/// // (in this case, we're updating the Vorbis comment)
1019/// |vc| vc.set(TITLE, "Track Title")
1020/// );
1021/// Ok(())
1022/// },
1023/// ),
1024/// Ok(false), // false indicates the original file was updated
1025/// ));
1026///
1027/// flac.rewind().unwrap();
1028///
1029/// // re-read the metadata blocks from the original file
1030/// let blocks = BlockList::read(flac).unwrap();
1031///
1032/// // the original file now has a Vorbis Comment block
1033/// // with the track title that we added
1034/// assert_eq!(
1035/// blocks.get::<VorbisComment>().and_then(|vc| vc.get(TITLE)),
1036/// Some("Track Title"),
1037/// );
1038///
1039/// // the original file's padding block is smaller than before
1040/// // to accomodate our new Vorbis Comment block
1041/// assert!(u32::from(blocks.get::<Padding>().unwrap().size) < PADDING_SIZE);
1042///
1043/// // and the unneeded rebuilt file remains empty
1044/// assert!(rebuilt.is_empty());
1045/// ```
1046///
1047/// # Example 2
1048///
1049/// ```
1050/// use flac_codec::{
1051/// metadata::{update_file, BlockList, VorbisComment},
1052/// metadata::fields::TITLE,
1053/// encode::{FlacSampleWriter, Options},
1054/// };
1055/// use std::io::{Cursor, Seek};
1056///
1057/// let mut flac = Cursor::new(vec![]); // a FLAC file in memory
1058///
1059/// // include no padding in our encoded file
1060/// let options = Options::default().no_padding();
1061///
1062/// let mut writer = FlacSampleWriter::new(
1063/// &mut flac, // our wrapped writer
1064/// options, // our encoding options
1065/// 44100, // sample rate
1066/// 16, // bits-per-sample
1067/// 1, // channel count
1068/// Some(1), // total samples
1069/// ).unwrap();
1070///
1071/// // write a simple FLAC file
1072/// writer.write(std::slice::from_ref(&0)).unwrap();
1073/// writer.finalize().unwrap();
1074///
1075/// flac.rewind().unwrap();
1076///
1077/// let mut rebuilt: Vec<u8> = vec![];
1078///
1079/// // update file with new Vorbis Comment
1080/// assert!(matches!(
1081/// update_file::<_, _, flac_codec::Error>(
1082/// // our original file
1083/// &mut flac,
1084/// // a closure to create a new file, if necessary
1085/// || Ok(&mut rebuilt),
1086/// // the closure that performs the metadata update
1087/// |blocklist| {
1088/// blocklist.update::<VorbisComment>(
1089/// // the blocklist itself has a closure
1090/// // that updates a block, creating it if necessary
1091/// // (in this case, we're updating the Vorbis comment)
1092/// |vc| vc.set(TITLE, "Track Title")
1093/// );
1094/// Ok(())
1095/// },
1096/// ),
1097/// Ok(true), // true indicates the original file was not updated
1098/// ));
1099///
1100/// flac.rewind().unwrap();
1101///
1102/// // re-read the metadata blocks from the original file
1103/// let blocks = BlockList::read(flac).unwrap();
1104///
1105/// // the original file remains unchanged
1106/// // and has no Vorbis Comment block
1107/// assert_eq!(blocks.get::<VorbisComment>(), None);
1108///
1109/// // now read the metadata blocks from the rebuilt file
1110/// let blocks = BlockList::read(rebuilt.as_slice()).unwrap();
1111///
1112/// // the rebuilt file has our Vorbis Comment entry instead
1113/// assert_eq!(
1114/// blocks.get::<VorbisComment>().and_then(|vc| vc.get(TITLE)),
1115/// Some("Track Title"),
1116/// );
1117/// ```
1118pub fn update_file<F, N, E>(
1119 mut original: F,
1120 rebuilt: impl FnOnce() -> std::io::Result<N>,
1121 f: impl FnOnce(&mut BlockList) -> Result<(), E>,
1122) -> Result<bool, E>
1123where
1124 F: std::io::Read + std::io::Seek + std::io::Write,
1125 N: std::io::Write,
1126 E: From<Error>,
1127{
1128 use crate::Counter;
1129 use std::cmp::Ordering;
1130 use std::io::{BufReader, BufWriter, Read, sink};
1131
1132 fn rebuild_file<N, R>(
1133 rebuilt: impl FnOnce() -> std::io::Result<N>,
1134 mut r: R,
1135 blocks: BlockList,
1136 ) -> Result<(), Error>
1137 where
1138 N: std::io::Write,
1139 R: Read,
1140 {
1141 // dump our new blocks and remaining FLAC data to temp file
1142 let mut tmp = Vec::new();
1143 write_blocks(&mut tmp, blocks)?;
1144 std::io::copy(&mut r, &mut tmp).map_err(Error::Io)?;
1145 drop(r);
1146
1147 // fresh original file and rewrite it with tmp file contents
1148 rebuilt()
1149 .and_then(|mut f| f.write_all(tmp.as_slice()))
1150 .map_err(Error::Io)
1151 }
1152
1153 /// Returns Ok if successful
1154 fn grow_padding(blocks: &mut BlockList, more_bytes: u64) -> Result<(), ()> {
1155 // if a block set has more than one PADDING, we'll try the first
1156 // rather than attempt to grow each in turn
1157 //
1158 // this is the most common case
1159 let padding = blocks.get_mut::<Padding>().ok_or(())?;
1160
1161 padding.size = padding
1162 .size
1163 .checked_add(more_bytes.try_into().map_err(|_| ())?)
1164 .ok_or(())?;
1165
1166 Ok(())
1167 }
1168
1169 /// Returns Ok if successful
1170 fn shrink_padding(blocks: &mut BlockList, fewer_bytes: u64) -> Result<(), ()> {
1171 // if a block set has more than one PADDING, we'll try the first
1172 // rather than attempt to grow each in turn
1173 //
1174 // this is the most common case
1175 let padding = blocks.get_mut::<Padding>().ok_or(())?;
1176
1177 padding.size = padding
1178 .size
1179 .checked_sub(fewer_bytes.try_into().map_err(|_| ())?)
1180 .ok_or(())?;
1181
1182 Ok(())
1183 }
1184
1185 // the starting position in the stream we rewind to
1186 let start = std::io::SeekFrom::Start(original.stream_position().map_err(Error::Io)?);
1187
1188 let mut reader = Counter::new(BufReader::new(&mut original));
1189
1190 let mut blocks = BlockList::read(Read::by_ref(&mut reader))?;
1191
1192 let Counter {
1193 stream: reader,
1194 count: old_size,
1195 } = reader;
1196
1197 f(&mut blocks)?;
1198
1199 let new_size = {
1200 let mut new_size = Counter::new(sink());
1201 write_blocks(&mut new_size, blocks.blocks())?;
1202 new_size.count
1203 };
1204
1205 match new_size.cmp(&old_size) {
1206 Ordering::Less => {
1207 // blocks have shrunk in size, so try to expand
1208 // PADDING block to hold additional bytes
1209 match grow_padding(&mut blocks, old_size - new_size) {
1210 Ok(()) => {
1211 original.seek(start).map_err(Error::Io)?;
1212 write_blocks(BufWriter::new(original), blocks)
1213 .map(|()| false)
1214 .map_err(E::from)
1215 }
1216 Err(()) => rebuild_file(rebuilt, reader, blocks)
1217 .map(|()| true)
1218 .map_err(E::from),
1219 }
1220 }
1221 Ordering::Equal => {
1222 // blocks are the same size, so no need to adjust padding
1223 original.seek(start).map_err(Error::Io)?;
1224 write_blocks(BufWriter::new(original), blocks)
1225 .map(|()| false)
1226 .map_err(E::from)
1227 }
1228 Ordering::Greater => {
1229 // blocks have grown in size, so try to shrink
1230 // PADDING block to hold additional bytes
1231 match shrink_padding(&mut blocks, new_size - old_size) {
1232 Ok(()) => {
1233 original.seek(start).map_err(Error::Io)?;
1234 write_blocks(BufWriter::new(original), blocks)
1235 .map(|()| false)
1236 .map_err(E::from)
1237 }
1238 Err(()) => rebuild_file(rebuilt, reader, blocks)
1239 .map(|()| true)
1240 .map_err(E::from),
1241 }
1242 }
1243 }
1244}
1245
1246/// Any possible FLAC metadata block
1247///
1248/// Each block consists of a [`BlockHeader`] followed by the block's contents.
1249///
1250/// ```text
1251/// ┌──────────┬────────┬┄┄┄┄┄┄┄┄┬┄┄┄┬────────┬┄┄┄┄┄┄┄┄┬┄┄┄╮
1252/// │ FLAC Tag │ Block₀ │ Block₁ ┆ … ┆ Frame₀ │ Frame₁ ┆ … ┆ FLAC File
1253/// └──────────┼────────┼┄┄┄┄┄┄┄┄┴┄┄┄┴────────┴┄┄┄┄┄┄┄┄┴┄┄┄╯
1254/// ╭──────────╯ ╰────────────────────────╮
1255/// ├──────────────┬─────────────────────────────┤
1256/// │ Block Header │ Metadata Block Data │ Metadata Block
1257/// └──────────────┴─────────────────────────────┘
1258/// ```
1259#[derive(Debug, Clone, Eq, PartialEq)]
1260pub enum Block {
1261 /// The STREAMINFO block
1262 Streaminfo(Streaminfo),
1263 /// The PADDING block
1264 Padding(Padding),
1265 /// The APPLICATION block
1266 Application(Application),
1267 /// The SEEKTABLE block
1268 SeekTable(SeekTable),
1269 /// The VORBIS_COMMENT block
1270 VorbisComment(VorbisComment),
1271 /// The CUESHEET block
1272 Cuesheet(Cuesheet),
1273 /// The PICTURE block
1274 Picture(Picture),
1275}
1276
1277impl Block {
1278 /// Our block type
1279 pub fn block_type(&self) -> BlockType {
1280 match self {
1281 Self::Streaminfo(_) => BlockType::Streaminfo,
1282 Self::Padding(_) => BlockType::Padding,
1283 Self::Application(_) => BlockType::Application,
1284 Self::SeekTable(_) => BlockType::SeekTable,
1285 Self::VorbisComment(_) => BlockType::VorbisComment,
1286 Self::Cuesheet(_) => BlockType::Cuesheet,
1287 Self::Picture(_) => BlockType::Picture,
1288 }
1289 }
1290}
1291
1292impl AsBlockRef for Block {
1293 fn as_block_ref(&self) -> BlockRef<'_> {
1294 match self {
1295 Self::Streaminfo(s) => BlockRef::Streaminfo(s),
1296 Self::Padding(p) => BlockRef::Padding(p),
1297 Self::Application(a) => BlockRef::Application(a),
1298 Self::SeekTable(s) => BlockRef::SeekTable(s),
1299 Self::VorbisComment(v) => BlockRef::VorbisComment(v),
1300 Self::Cuesheet(v) => BlockRef::Cuesheet(v),
1301 Self::Picture(p) => BlockRef::Picture(p),
1302 }
1303 }
1304}
1305
1306impl FromBitStreamWith<'_> for Block {
1307 type Context = BlockHeader;
1308 type Error = Error;
1309
1310 // parses from reader without header
1311 fn from_reader<R: BitRead + ?Sized>(
1312 r: &mut R,
1313 header: &BlockHeader,
1314 ) -> Result<Self, Self::Error> {
1315 match header.block_type {
1316 BlockType::Streaminfo => Ok(Block::Streaminfo(r.parse()?)),
1317 BlockType::Padding => Ok(Block::Padding(r.parse_using(header.size)?)),
1318 BlockType::Application => Ok(Block::Application(r.parse_using(header.size)?)),
1319 BlockType::SeekTable => Ok(Block::SeekTable(r.parse_using(header.size)?)),
1320 BlockType::VorbisComment => Ok(Block::VorbisComment(r.parse()?)),
1321 BlockType::Cuesheet => Ok(Block::Cuesheet(r.parse()?)),
1322 BlockType::Picture => Ok(Block::Picture(r.parse()?)),
1323 }
1324 }
1325}
1326
1327impl ToBitStreamUsing for Block {
1328 type Context = bool;
1329 type Error = Error;
1330
1331 // builds to writer with header
1332 fn to_writer<W: BitWrite + ?Sized>(&self, w: &mut W, is_last: bool) -> Result<(), Error> {
1333 match self {
1334 Self::Streaminfo(streaminfo) => w
1335 .build(&BlockHeader::new(is_last, streaminfo)?)
1336 .and_then(|()| w.build(streaminfo).map_err(Error::Io)),
1337 Self::Padding(padding) => w
1338 .build(&BlockHeader::new(is_last, padding)?)
1339 .and_then(|()| w.build(padding).map_err(Error::Io)),
1340 Self::Application(application) => w
1341 .build(&BlockHeader::new(is_last, application)?)
1342 .and_then(|()| w.build(application).map_err(Error::Io)),
1343 Self::SeekTable(seektable) => w
1344 .build(&BlockHeader::new(is_last, seektable)?)
1345 .and_then(|()| w.build(seektable)),
1346 Self::VorbisComment(vorbis_comment) => w
1347 .build(&BlockHeader::new(is_last, vorbis_comment)?)
1348 .and_then(|()| w.build(vorbis_comment)),
1349 Self::Cuesheet(cuesheet) => w
1350 .build(&BlockHeader::new(is_last, cuesheet)?)
1351 .and_then(|()| w.build(cuesheet)),
1352 Self::Picture(picture) => w
1353 .build(&BlockHeader::new(is_last, picture)?)
1354 .and_then(|()| w.build(picture)),
1355 }
1356 }
1357}
1358
1359/// A shared reference to a metadata block
1360#[derive(Debug, Copy, Clone, Eq, PartialEq)]
1361pub enum BlockRef<'b> {
1362 /// The STREAMINFO block
1363 Streaminfo(&'b Streaminfo),
1364 /// The PADDING block
1365 Padding(&'b Padding),
1366 /// The APPLICATION block
1367 Application(&'b Application),
1368 /// The SEEKTABLE block
1369 SeekTable(&'b SeekTable),
1370 /// The VORBIS_COMMENT block
1371 VorbisComment(&'b VorbisComment),
1372 /// The CUESHEET block
1373 Cuesheet(&'b Cuesheet),
1374 /// The PICTURE block
1375 Picture(&'b Picture),
1376}
1377
1378impl BlockRef<'_> {
1379 /// Our block type
1380 pub fn block_type(&self) -> BlockType {
1381 match self {
1382 Self::Streaminfo(_) => BlockType::Streaminfo,
1383 Self::Padding(_) => BlockType::Padding,
1384 Self::Application(_) => BlockType::Application,
1385 Self::SeekTable(_) => BlockType::SeekTable,
1386 Self::VorbisComment(_) => BlockType::VorbisComment,
1387 Self::Cuesheet(_) => BlockType::Cuesheet,
1388 Self::Picture(_) => BlockType::Picture,
1389 }
1390 }
1391}
1392
1393impl AsBlockRef for BlockRef<'_> {
1394 fn as_block_ref(&self) -> BlockRef<'_> {
1395 *self
1396 }
1397}
1398
1399/// A trait for items which can make cheap [`BlockRef`] values.
1400pub trait AsBlockRef {
1401 /// Returns fresh reference to ourself.
1402 fn as_block_ref(&self) -> BlockRef<'_>;
1403}
1404
1405impl<T: AsBlockRef> AsBlockRef for &T {
1406 fn as_block_ref(&self) -> BlockRef<'_> {
1407 <T as AsBlockRef>::as_block_ref(*self)
1408 }
1409}
1410
1411impl ToBitStreamUsing for BlockRef<'_> {
1412 type Context = bool;
1413 type Error = Error;
1414
1415 // builds to writer with header
1416 fn to_writer<W: BitWrite + ?Sized>(&self, w: &mut W, is_last: bool) -> Result<(), Error> {
1417 match self {
1418 Self::Streaminfo(streaminfo) => w
1419 .build(&BlockHeader::new(is_last, *streaminfo)?)
1420 .and_then(|()| w.build(*streaminfo).map_err(Error::Io)),
1421 Self::Padding(padding) => w
1422 .build(&BlockHeader::new(is_last, *padding)?)
1423 .and_then(|()| w.build(*padding).map_err(Error::Io)),
1424 Self::Application(application) => w
1425 .build(&BlockHeader::new(is_last, *application)?)
1426 .and_then(|()| w.build(*application).map_err(Error::Io)),
1427 Self::SeekTable(seektable) => w
1428 .build(&BlockHeader::new(is_last, *seektable)?)
1429 .and_then(|()| w.build(*seektable)),
1430 Self::VorbisComment(vorbis_comment) => w
1431 .build(&BlockHeader::new(is_last, *vorbis_comment)?)
1432 .and_then(|()| w.build(*vorbis_comment)),
1433 Self::Cuesheet(cuesheet) => w
1434 .build(&BlockHeader::new(is_last, *cuesheet)?)
1435 .and_then(|()| w.build(*cuesheet)),
1436 Self::Picture(picture) => w
1437 .build(&BlockHeader::new(is_last, *picture)?)
1438 .and_then(|()| w.build(*picture)),
1439 }
1440 }
1441}
1442
1443macro_rules! block {
1444 ($t:ty, $v:ident, $m:literal) => {
1445 impl MetadataBlock for $t {
1446 const TYPE: BlockType = BlockType::$v;
1447 const MULTIPLE: bool = $m;
1448 }
1449
1450 impl From<$t> for Block {
1451 fn from(b: $t) -> Self {
1452 Self::$v(b)
1453 }
1454 }
1455
1456 impl TryFrom<Block> for $t {
1457 type Error = ();
1458
1459 fn try_from(block: Block) -> Result<Self, ()> {
1460 match block {
1461 Block::$v(block) => Ok(block),
1462 _ => Err(()),
1463 }
1464 }
1465 }
1466
1467 impl AsBlockRef for $t {
1468 fn as_block_ref(&self) -> BlockRef<'_> {
1469 BlockRef::$v(self)
1470 }
1471 }
1472 };
1473}
1474
1475macro_rules! optional_block {
1476 ($t:ty, $v:ident) => {
1477 impl OptionalMetadataBlock for $t {
1478 const OPTIONAL_TYPE: OptionalBlockType = OptionalBlockType::$v;
1479 }
1480
1481 impl private::OptionalMetadataBlock for $t {
1482 fn try_from_opt_block(
1483 block: &private::OptionalBlock,
1484 ) -> Result<&Self, &private::OptionalBlock> {
1485 match block {
1486 private::OptionalBlock::$v(block) => Ok(block),
1487 block => Err(block),
1488 }
1489 }
1490
1491 fn try_from_opt_block_mut(
1492 block: &mut private::OptionalBlock,
1493 ) -> Result<&mut Self, &mut private::OptionalBlock> {
1494 match block {
1495 private::OptionalBlock::$v(block) => Ok(block),
1496 block => Err(block),
1497 }
1498 }
1499 }
1500
1501 impl From<$t> for private::OptionalBlock {
1502 fn from(vorbis: $t) -> Self {
1503 private::OptionalBlock::$v(vorbis)
1504 }
1505 }
1506
1507 impl TryFrom<private::OptionalBlock> for $t {
1508 type Error = ();
1509
1510 fn try_from(block: private::OptionalBlock) -> Result<Self, ()> {
1511 match block {
1512 private::OptionalBlock::$v(b) => Ok(b),
1513 _ => Err(()),
1514 }
1515 }
1516 }
1517 };
1518}
1519
1520/// A STREAMINFO metadata block
1521///
1522/// This block contains metadata about the stream's contents.
1523///
1524/// It must *always* be present in a FLAC file,
1525/// must *always* be the first metadata block in the stream,
1526/// and must *not* be present more than once.
1527///
1528/// | Bits | Field | Meaning |
1529/// |-----:|------:|---------|
1530/// | 16 | `minimum_block_size` | minimum block size (in samples) in the stream
1531/// | 16 | `maximum_block_size` | maximum block size (in samples) in the stream
1532/// | 24 | `minimum_frame_size` | minimum frame size (in bytes) in the stream
1533/// | 24 | `maximum_frame_size` | maximum frame size (in bytes) in the stream
1534/// | 20 | `sample_rate` | stream's sample rate, in Hz
1535/// | 3 | `channels` | stream's channel count (+1)
1536/// | 5 | `bits_per_sample` | stream's bits-per-sample (+1)
1537/// | 36 | `total_samples` | stream's total channel-independent samples
1538/// | 16×8 | `md5` | decoded stream's MD5 sum hash
1539///
1540/// # Example
1541/// ```
1542/// use bitstream_io::{BitReader, BitRead, BigEndian, SignedBitCount};
1543/// use flac_codec::metadata::Streaminfo;
1544/// use std::num::NonZero;
1545///
1546/// let data: &[u8] = &[
1547/// 0x10, 0x00,
1548/// 0x10, 0x00,
1549/// 0x00, 0x00, 0x0c,
1550/// 0x00, 0x00, 0x0c,
1551/// 0b00001010, 0b11000100, 0b0100_000_0, 0b1111_0000,
1552/// 0b00000000, 0b00000000, 0b00000000, 0b01010000,
1553/// 0xf5, 0x3f, 0x86, 0x87, 0x6d, 0xcd, 0x77, 0x83,
1554/// 0x22, 0x5c, 0x93, 0xba, 0x8a, 0x93, 0x8c, 0x7d,
1555/// ];
1556///
1557/// let mut r = BitReader::endian(data, BigEndian);
1558/// assert_eq!(
1559/// r.parse::<Streaminfo>().unwrap(),
1560/// Streaminfo {
1561/// minimum_block_size: 0x10_00, // 4096 samples
1562/// maximum_block_size: 0x10_00, // 4096 samples
1563/// minimum_frame_size: NonZero::new(0x00_00_0c), // 12 bytes
1564/// maximum_frame_size: NonZero::new(0x00_00_0c), // 12 bytes
1565/// sample_rate: 0b00001010_11000100_0100, // 44100 Hz
1566/// channels: NonZero::new(0b000 + 1).unwrap(), // 1 channel
1567/// bits_per_sample:
1568/// SignedBitCount::new::<{0b0_1111 + 1}>(), // 16 bps
1569/// total_samples: NonZero::new(
1570/// 0b0000_00000000_00000000_00000000_01010000 // 80 samples
1571/// ),
1572/// md5: Some([
1573/// 0xf5, 0x3f, 0x86, 0x87, 0x6d, 0xcd, 0x77, 0x83,
1574/// 0x22, 0x5c, 0x93, 0xba, 0x8a, 0x93, 0x8c, 0x7d,
1575/// ]),
1576/// },
1577/// );
1578/// ```
1579///
1580/// # Important
1581///
1582/// Changing any of these values to something that differs
1583/// from the values of the file's frame headers will render it
1584/// unplayable, as will moving it anywhere but the first
1585/// metadata block in the file.
1586/// Avoid modifying the position and contents of this block unless you
1587/// know exactly what you are doing.
1588#[derive(Debug, Clone, Eq, PartialEq)]
1589pub struct Streaminfo {
1590 /// The minimum block size (in samples) used in the stream,
1591 /// excluding the last block.
1592 pub minimum_block_size: u16,
1593 /// The maximum block size (in samples) used in the stream,
1594 /// excluding the last block.
1595 pub maximum_block_size: u16,
1596 /// The minimum framesize (in bytes) used in the stream.
1597 ///
1598 /// `None` indicates the value is unknown.
1599 pub minimum_frame_size: Option<NonZero<u32>>,
1600 /// The maximum framesize (in bytes) used in the stream.
1601 ///
1602 /// `None` indicates the value is unknown.
1603 pub maximum_frame_size: Option<NonZero<u32>>,
1604 /// Sample rate in Hz
1605 ///
1606 /// 0 indicates a non-audio stream.
1607 pub sample_rate: u32,
1608 /// Number of channels, from 1 to 8
1609 pub channels: NonZero<u8>,
1610 /// Number of bits-per-sample, from 4 to 32
1611 pub bits_per_sample: SignedBitCount<32>,
1612 /// Total number of interchannel samples in stream.
1613 ///
1614 /// `None` indicates the value is unknown.
1615 pub total_samples: Option<NonZero<u64>>,
1616 /// MD5 hash of unencoded audio data.
1617 ///
1618 /// `None` indicates the value is unknown.
1619 pub md5: Option<[u8; 16]>,
1620}
1621
1622impl Streaminfo {
1623 /// The maximum size of a frame, in bytes (2²⁴ - 1)
1624 pub const MAX_FRAME_SIZE: u32 = (1 << 24) - 1;
1625
1626 /// The maximum sample rate, in Hz (2²⁰ - 1)
1627 pub const MAX_SAMPLE_RATE: u32 = (1 << 20) - 1;
1628
1629 /// The maximum number of channels (8)
1630 pub const MAX_CHANNELS: NonZero<u8> = NonZero::new(8).unwrap();
1631
1632 /// The maximum number of total samples (2³⁶ - 1)
1633 pub const MAX_TOTAL_SAMPLES: NonZero<u64> = NonZero::new((1 << 36) - 1).unwrap();
1634}
1635
1636block!(Streaminfo, Streaminfo, false);
1637
1638impl Metadata for Streaminfo {
1639 fn channel_count(&self) -> u8 {
1640 self.channels.get()
1641 }
1642
1643 fn sample_rate(&self) -> u32 {
1644 self.sample_rate
1645 }
1646
1647 fn bits_per_sample(&self) -> u32 {
1648 self.bits_per_sample.into()
1649 }
1650
1651 fn total_samples(&self) -> Option<u64> {
1652 self.total_samples.map(|s| s.get())
1653 }
1654
1655 fn md5(&self) -> Option<&[u8; 16]> {
1656 self.md5.as_ref()
1657 }
1658}
1659
1660impl FromBitStream for Streaminfo {
1661 type Error = std::io::Error;
1662
1663 fn from_reader<R: BitRead + ?Sized>(r: &mut R) -> Result<Self, Self::Error> {
1664 Ok(Self {
1665 minimum_block_size: r.read_to()?,
1666 maximum_block_size: r.read_to()?,
1667 minimum_frame_size: r.read::<24, _>()?,
1668 maximum_frame_size: r.read::<24, _>()?,
1669 sample_rate: r.read::<20, _>()?,
1670 channels: r.read::<3, _>()?,
1671 bits_per_sample: r
1672 .read_count::<0b11111>()?
1673 .checked_add(1)
1674 .and_then(|c| c.signed_count())
1675 .unwrap(),
1676 total_samples: r.read::<36, _>()?,
1677 md5: r
1678 .read_to()
1679 .map(|md5: [u8; 16]| md5.iter().any(|b| *b != 0).then_some(md5))?,
1680 })
1681 }
1682}
1683
1684impl ToBitStream for Streaminfo {
1685 type Error = std::io::Error;
1686
1687 fn to_writer<W: BitWrite + ?Sized>(&self, w: &mut W) -> Result<(), Self::Error> {
1688 w.write_from(self.minimum_block_size)?;
1689 w.write_from(self.maximum_block_size)?;
1690 w.write::<24, _>(self.minimum_frame_size)?;
1691 w.write::<24, _>(self.maximum_frame_size)?;
1692 w.write::<20, _>(self.sample_rate)?;
1693 w.write::<3, _>(self.channels)?;
1694 w.write_count(
1695 self.bits_per_sample
1696 .checked_sub::<0b11111>(1)
1697 .unwrap()
1698 .count(),
1699 )?;
1700 w.write::<36, _>(self.total_samples)?;
1701 w.write_from(self.md5.unwrap_or([0; 16]))?;
1702 Ok(())
1703 }
1704}
1705
1706/// A PADDING metadata block
1707///
1708/// Padding blocks are empty blocks consisting of all 0 bytes.
1709/// If one wishes to edit the metadata in other blocks,
1710/// adjusting the size of the padding block allows
1711/// us to do so without have to rewrite the entire FLAC file.
1712/// For example, when adding 10 bytes to a comment,
1713/// we can subtract 10 bytes from the padding
1714/// and the total size of all blocks remains unchanged.
1715/// Therefore we can simply overwrite the old comment
1716/// block with the new without affecting the following
1717/// FLAC audio frames.
1718///
1719/// This block may occur multiple times in a FLAC file.
1720///
1721/// # Example
1722///
1723/// ```
1724/// use bitstream_io::{BitReader, BitRead, BigEndian};
1725/// use flac_codec::metadata::{BlockHeader, BlockType, Padding};
1726///
1727/// let data: &[u8] = &[
1728/// 0x81, 0x00, 0x00, 0x0a, // block header
1729/// // padding bytes
1730/// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1731/// ];
1732///
1733/// let mut r = BitReader::endian(data, BigEndian);
1734/// let header = r.parse::<BlockHeader>().unwrap();
1735/// assert_eq!(
1736/// &header,
1737/// &BlockHeader {
1738/// last: true,
1739/// block_type: BlockType::Padding,
1740/// size: 0x0au8.into(),
1741/// },
1742/// );
1743///
1744/// assert_eq!(
1745/// r.parse_using::<Padding>(header.size).unwrap(),
1746/// Padding {
1747/// size: 0x0au8.into(),
1748/// },
1749/// );
1750/// ```
1751#[derive(Debug, Clone, Eq, PartialEq, Default)]
1752pub struct Padding {
1753 /// The size of the padding, in bytes
1754 pub size: BlockSize,
1755}
1756
1757block!(Padding, Padding, true);
1758optional_block!(Padding, Padding);
1759
1760impl FromBitStreamUsing for Padding {
1761 type Context = BlockSize;
1762 type Error = Error;
1763
1764 fn from_reader<R: BitRead + ?Sized>(r: &mut R, size: BlockSize) -> Result<Self, Self::Error> {
1765 r.skip(size.get() * 8)?;
1766 Ok(Self { size })
1767 }
1768}
1769
1770impl ToBitStream for Padding {
1771 type Error = std::io::Error;
1772
1773 fn to_writer<W: BitWrite + ?Sized>(&self, w: &mut W) -> Result<(), Self::Error> {
1774 w.pad(self.size.get() * 8)
1775 }
1776}
1777
1778/// An APPLICATION metadata block
1779///
1780/// This block is for handling application-specific binary metadata,
1781/// such as foreign RIFF WAVE tags.
1782///
1783/// This block may occur multiple times in a FLAC file.
1784///
1785/// | Bits | Field | Meaning |
1786/// |-----:|------:|---------|
1787/// | 32 | `id` | registered application ID
1788/// | rest of block | `data` | application-specific data
1789///
1790#[derive(Debug, Clone, Eq, PartialEq)]
1791pub struct Application {
1792 /// A registered application ID
1793 pub id: u32,
1794 /// Application-specific data
1795 pub data: Vec<u8>,
1796}
1797
1798impl Application {
1799 /// Application ID for RIFF chunk storage
1800 pub const RIFF: u32 = 0x72696666;
1801
1802 /// Application ID for AIFF chunk storage
1803 pub const AIFF: u32 = 0x61696666;
1804}
1805
1806block!(Application, Application, true);
1807optional_block!(Application, Application);
1808
1809impl FromBitStreamUsing for Application {
1810 type Context = BlockSize;
1811 type Error = Error;
1812
1813 fn from_reader<R: BitRead + ?Sized>(r: &mut R, size: BlockSize) -> Result<Self, Self::Error> {
1814 Ok(Self {
1815 id: r.read_to()?,
1816 data: r.read_to_vec(
1817 size.get()
1818 .checked_sub(4)
1819 .ok_or(Error::InsufficientApplicationBlock)?
1820 .try_into()
1821 .unwrap(),
1822 )?,
1823 })
1824 }
1825}
1826
1827impl ToBitStream for Application {
1828 type Error = std::io::Error;
1829
1830 fn to_writer<W: BitWrite + ?Sized>(&self, w: &mut W) -> Result<(), Self::Error> {
1831 w.write_from(self.id)?;
1832 w.write_bytes(&self.data)
1833 }
1834}
1835
1836/// A SEEKTABLE metadata block
1837///
1838/// Because FLAC frames do not store their compressed length,
1839/// a seek table is used for random access within a FLAC file.
1840/// By mapping a sample number to a byte offset,
1841/// one can quickly reach different parts of the file
1842/// without decoding the whole thing.
1843///
1844/// Also note that seek point byte offsets are
1845/// relative to the start of the first FLAC frame,
1846/// and *not* relative to the start of the entire file.
1847/// This allows us to change the size of the set
1848/// of metadata blocks without having to recalculate
1849/// the contents of the seek table.
1850///
1851/// Because the byte and sample offsets are
1852/// file-specific, a seek table generated for one file
1853/// should not be transferred to another FLAC file where the
1854/// frames are different sizes and in different positions.
1855///
1856/// This block may occur only once in a FLAC file.
1857///
1858/// Its seekpoints occupy the entire block.
1859///
1860/// # Example
1861/// ```
1862/// use bitstream_io::{BitReader, BitRead, BigEndian};
1863/// use flac_codec::metadata::{BlockHeader, BlockType, SeekTable, SeekPoint};
1864///
1865/// let data: &[u8] = &[
1866/// 0x83, 0x00, 0x00, 0x48, // block header
1867/// // seekpoint 0
1868/// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1869/// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1870/// 0x00, 0x14,
1871/// // seekpoint 1
1872/// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14,
1873/// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c,
1874/// 0x00, 0x14,
1875/// // seekpoint 2
1876/// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28,
1877/// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22,
1878/// 0x00, 0x14,
1879/// // seekpoint 3
1880/// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
1881/// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
1882/// 0x00, 0x14,
1883/// ];
1884///
1885/// let mut r = BitReader::endian(data, BigEndian);
1886/// let header = r.parse::<BlockHeader>().unwrap();
1887/// assert_eq!(
1888/// &header,
1889/// &BlockHeader {
1890/// last: true,
1891/// block_type: BlockType::SeekTable,
1892/// size: 0x48u8.into(),
1893/// },
1894/// );
1895///
1896/// let seektable = r.parse_using::<SeekTable>(header.size).unwrap();
1897///
1898/// assert_eq!(
1899/// Vec::from(seektable.points),
1900/// vec![
1901/// SeekPoint::Defined {
1902/// sample_offset: 0x00,
1903/// byte_offset: 0x00,
1904/// frame_samples: 0x14,
1905/// },
1906/// SeekPoint::Defined {
1907/// sample_offset: 0x14,
1908/// byte_offset: 0x0c,
1909/// frame_samples: 0x14,
1910/// },
1911/// SeekPoint::Defined {
1912/// sample_offset: 0x28,
1913/// byte_offset: 0x22,
1914/// frame_samples: 0x14,
1915/// },
1916/// SeekPoint::Defined {
1917/// sample_offset: 0x3c,
1918/// byte_offset: 0x3c,
1919/// frame_samples: 0x14,
1920/// },
1921/// ],
1922/// );
1923///
1924/// ```
1925#[derive(Debug, Clone, Eq, PartialEq, Default)]
1926pub struct SeekTable {
1927 /// The seek table's individual seek points
1928 pub points: contiguous::Contiguous<{ Self::MAX_POINTS }, SeekPoint>,
1929}
1930
1931impl SeekTable {
1932 /// The maximum number of seek points that fit into a seek table
1933 pub const MAX_POINTS: usize = (1 << 24) / ((64 + 64 + 16) / 8);
1934}
1935
1936block!(SeekTable, SeekTable, false);
1937optional_block!(SeekTable, SeekTable);
1938
1939impl FromBitStreamUsing for SeekTable {
1940 type Context = BlockSize;
1941 type Error = Error;
1942
1943 fn from_reader<R: BitRead + ?Sized>(r: &mut R, size: BlockSize) -> Result<Self, Self::Error> {
1944 match (size.get() / 18, size.get() % 18) {
1945 (p, 0) => Ok(Self {
1946 points: contiguous::Contiguous::try_collect((0..p).map(|_| r.parse()))
1947 .map_err(|_| Error::InvalidSeekTablePoint)??,
1948 }),
1949 _ => Err(Error::InvalidSeekTableSize),
1950 }
1951 }
1952}
1953
1954impl ToBitStream for SeekTable {
1955 type Error = Error;
1956
1957 fn to_writer<W: BitWrite + ?Sized>(&self, w: &mut W) -> Result<(), Self::Error> {
1958 // ensure non-placeholder seek point offsets increment
1959 let mut last_offset = None;
1960
1961 self.points
1962 .iter()
1963 .try_for_each(|point| match last_offset.as_mut() {
1964 None => {
1965 last_offset = point.sample_offset();
1966 w.build(point).map_err(Error::Io)
1967 }
1968 Some(last_offset) => match point.sample_offset() {
1969 Some(our_offset) => match our_offset > *last_offset {
1970 true => {
1971 *last_offset = our_offset;
1972 w.build(point).map_err(Error::Io)
1973 }
1974 false => Err(Error::InvalidSeekTablePoint),
1975 },
1976 _ => w.build(point).map_err(Error::Io),
1977 },
1978 })
1979 }
1980}
1981
1982/// An individual SEEKTABLE seek point
1983///
1984/// | Bits | Field | Meaning |
1985/// |-----:|------:|---------|
1986/// | 64 | `sample_offset` | sample number of first sample in target frame
1987/// | 64 | `byte_offset` | offset, in bytes, from first frame to target frame's header
1988/// | 16 | `frame_samples` | number of samples in target frame
1989///
1990#[derive(Debug, Clone, Eq, PartialEq)]
1991pub enum SeekPoint {
1992 /// A defined, non-placeholder seek point
1993 Defined {
1994 /// The sample number of the first sample in the target frame
1995 sample_offset: u64,
1996 /// Offset, in bytes, from the first byte of the first frame header
1997 /// to the first byte in the target frame's header
1998 byte_offset: u64,
1999 /// Number of samples in the target frame
2000 frame_samples: u16,
2001 },
2002 /// A placeholder seek point
2003 Placeholder,
2004}
2005
2006impl SeekPoint {
2007 /// Returns our sample offset, if not a placeholder point
2008 pub fn sample_offset(&self) -> Option<u64> {
2009 match self {
2010 Self::Defined { sample_offset, .. } => Some(*sample_offset),
2011 Self::Placeholder => None,
2012 }
2013 }
2014}
2015
2016impl contiguous::Adjacent for SeekPoint {
2017 fn valid_first(&self) -> bool {
2018 true
2019 }
2020
2021 fn is_next(&self, previous: &SeekPoint) -> bool {
2022 // seekpoints must be unique by sample offset
2023 // and sample offsets must be ascending
2024 //
2025 // placeholders can come after non-placeholders or other placeholders,
2026 // but non-placeholders can't come after placeholders
2027 match self {
2028 Self::Defined {
2029 sample_offset: our_offset,
2030 ..
2031 } => match previous {
2032 Self::Defined {
2033 sample_offset: prev_offset,
2034 ..
2035 } => our_offset > prev_offset,
2036 Self::Placeholder => false,
2037 },
2038 Self::Placeholder => true,
2039 }
2040 }
2041}
2042
2043impl FromBitStream for SeekPoint {
2044 type Error = std::io::Error;
2045
2046 fn from_reader<R: BitRead + ?Sized>(r: &mut R) -> Result<Self, Self::Error> {
2047 match r.read_to()? {
2048 u64::MAX => {
2049 let _byte_offset = r.read_to::<u64>()?;
2050 let _frame_samples = r.read_to::<u16>()?;
2051 Ok(Self::Placeholder)
2052 }
2053 sample_offset => Ok(Self::Defined {
2054 sample_offset,
2055 byte_offset: r.read_to()?,
2056 frame_samples: r.read_to()?,
2057 }),
2058 }
2059 }
2060}
2061
2062impl ToBitStream for SeekPoint {
2063 type Error = std::io::Error;
2064
2065 fn to_writer<W: BitWrite + ?Sized>(&self, w: &mut W) -> Result<(), Self::Error> {
2066 match self {
2067 Self::Defined {
2068 sample_offset,
2069 byte_offset,
2070 frame_samples,
2071 } => {
2072 w.write_from(*sample_offset)?;
2073 w.write_from(*byte_offset)?;
2074 w.write_from(*frame_samples)
2075 }
2076 Self::Placeholder => {
2077 w.write_from(u64::MAX)?;
2078 w.write_from::<u64>(0)?;
2079 w.write_from::<u16>(0)
2080 }
2081 }
2082 }
2083}
2084
2085/// A VORBIS_COMMENT metadata block
2086///
2087/// This block contains metadata such as track name,
2088/// artist name, album name, etc. Its contents are
2089/// UTF-8 encoded, `=`-delimited text fields
2090/// with a field name followed by value,
2091/// such as:
2092///
2093/// ```text
2094/// TITLE=Track Title
2095/// ```
2096///
2097/// Field names are case-insensitive and
2098/// may occur multiple times within the same comment
2099/// (a track may have multiple artists and choose to
2100/// store an "ARTIST" field for each one).
2101///
2102/// Commonly-used fields are available in the [`fields`] module.
2103///
2104/// This block may occur only once in a FLAC file.
2105///
2106/// # Byte Order
2107///
2108/// Unlike the rest of a FLAC file, the Vorbis comment's
2109/// length fields are stored in little-endian byte order.
2110///
2111/// | Bits | Field | Meaning |
2112/// |-----:|------:|---------|
2113/// | 32 | vendor string len | length of vendor string, in bytes
2114/// | `vendor string len`×8 | `vendor_string` | vendor string, in UTF-8
2115/// | 32 | field count | number of vendor string fields
2116/// | 32 | field₀ len | length of field₀, in bytes
2117/// | `field₀ len`×8 | `fields₀` | first field value, in UTF-8
2118/// | 32 | field₁ len | length of field₁, in bytes
2119/// | `field₁ len`×8 | `fields₁` | second field value, in UTF-8
2120/// | | | ⋮
2121///
2122/// # Example
2123/// ```
2124/// use bitstream_io::{BitReader, BitRead, LittleEndian};
2125/// use flac_codec::metadata::VorbisComment;
2126/// use flac_codec::metadata::fields::{TITLE, ALBUM, ARTIST};
2127///
2128/// let data: &[u8] = &[
2129/// 0x20, 0x00, 0x00, 0x00, // 32 byte vendor string
2130/// 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63,
2131/// 0x65, 0x20, 0x6c, 0x69, 0x62, 0x46, 0x4c, 0x41,
2132/// 0x43, 0x20, 0x31, 0x2e, 0x34, 0x2e, 0x33, 0x20,
2133/// 0x32, 0x30, 0x32, 0x33, 0x30, 0x36, 0x32, 0x33,
2134/// 0x02, 0x00, 0x00, 0x00, // 2 fields
2135/// 0x0d, 0x00, 0x00, 0x00, // 13 byte field 1
2136/// 0x54, 0x49, 0x54, 0x4c, 0x45, 0x3d, 0x54, 0x65,
2137/// 0x73, 0x74, 0x69, 0x6e, 0x67,
2138/// 0x10, 0x00, 0x00, 0x00, // 16 byte field 2
2139/// 0x41, 0x4c, 0x42, 0x55, 0x4d, 0x3d, 0x54, 0x65,
2140/// 0x73, 0x74, 0x20, 0x41, 0x6c, 0x62, 0x75, 0x6d,
2141/// ];
2142///
2143/// let mut r = BitReader::endian(data, LittleEndian);
2144/// let comment = r.parse::<VorbisComment>().unwrap();
2145///
2146/// assert_eq!(
2147/// &comment,
2148/// &VorbisComment {
2149/// vendor_string: "reference libFLAC 1.4.3 20230623".to_string(),
2150/// fields: vec![
2151/// "TITLE=Testing".to_string(),
2152/// "ALBUM=Test Album".to_string(),
2153/// ],
2154/// },
2155/// );
2156///
2157/// assert_eq!(comment.get(TITLE), Some("Testing"));
2158/// assert_eq!(comment.get(ALBUM), Some("Test Album"));
2159/// assert_eq!(comment.get(ARTIST), None);
2160/// ```
2161#[derive(Debug, Clone, Eq, PartialEq)]
2162pub struct VorbisComment {
2163 /// The vendor string
2164 pub vendor_string: String,
2165 /// The individual metadata comment strings
2166 pub fields: Vec<String>,
2167}
2168
2169impl Default for VorbisComment {
2170 fn default() -> Self {
2171 Self {
2172 vendor_string: concat!(env!("CARGO_PKG_NAME"), " ", env!("CARGO_PKG_VERSION"))
2173 .to_owned(),
2174 fields: vec![],
2175 }
2176 }
2177}
2178
2179impl VorbisComment {
2180 /// Given a field name, returns first matching value, if any
2181 ///
2182 /// Fields are matched case-insensitively
2183 ///
2184 /// # Example
2185 ///
2186 /// ```
2187 /// use flac_codec::metadata::{VorbisComment, fields::{ARTIST, TITLE}};
2188 ///
2189 /// let comment = VorbisComment {
2190 /// fields: vec![
2191 /// "ARTIST=Artist 1".to_owned(),
2192 /// "ARTIST=Artist 2".to_owned(),
2193 /// ],
2194 /// ..VorbisComment::default()
2195 /// };
2196 ///
2197 /// assert_eq!(comment.get(ARTIST), Some("Artist 1"));
2198 /// assert_eq!(comment.get(TITLE), None);
2199 /// ```
2200 pub fn get(&self, field: &str) -> Option<&str> {
2201 self.all(field).next()
2202 }
2203
2204 /// Replaces any instances of the given field with value
2205 ///
2206 /// Fields are matched case-insensitively
2207 ///
2208 /// # Panics
2209 ///
2210 /// Panics if field contains the `=` character.
2211 ///
2212 /// # Example
2213 ///
2214 /// ```
2215 /// use flac_codec::metadata::{VorbisComment, fields::ARTIST};
2216 ///
2217 /// let mut comment = VorbisComment {
2218 /// fields: vec![
2219 /// "ARTIST=Artist 1".to_owned(),
2220 /// "ARTIST=Artist 2".to_owned(),
2221 /// ],
2222 /// ..VorbisComment::default()
2223 /// };
2224 ///
2225 /// comment.set(ARTIST, "Artist 3");
2226 ///
2227 /// assert_eq!(
2228 /// comment.all(ARTIST).collect::<Vec<_>>(),
2229 /// vec!["Artist 3"],
2230 /// );
2231 /// ```
2232 pub fn set<S>(&mut self, field: &str, value: S)
2233 where
2234 S: std::fmt::Display,
2235 {
2236 self.remove(field);
2237 self.insert(field, value);
2238 }
2239
2240 /// Given a field name, iterates over any matching values
2241 ///
2242 /// Fields are matched case-insensitively
2243 ///
2244 /// # Example
2245 ///
2246 /// ```
2247 /// use flac_codec::metadata::{VorbisComment, fields::ARTIST};
2248 ///
2249 /// let comment = VorbisComment {
2250 /// fields: vec![
2251 /// "ARTIST=Artist 1".to_owned(),
2252 /// "ARTIST=Artist 2".to_owned(),
2253 /// ],
2254 /// ..VorbisComment::default()
2255 /// };
2256 ///
2257 /// assert_eq!(
2258 /// comment.all(ARTIST).collect::<Vec<_>>(),
2259 /// vec!["Artist 1", "Artist 2"],
2260 /// );
2261 /// ```
2262 pub fn all(&self, field: &str) -> impl Iterator<Item = &str> {
2263 assert!(!field.contains('='), "field must not contain '='");
2264
2265 self.fields.iter().filter_map(|f| {
2266 f.split_once('=')
2267 .and_then(|(key, value)| key.eq_ignore_ascii_case(field).then_some(value))
2268 })
2269 }
2270
2271 /// Adds new instance of field with the given value
2272 ///
2273 /// # Panics
2274 ///
2275 /// Panics if field contains the `=` character.
2276 ///
2277 /// # Example
2278 ///
2279 /// ```
2280 /// use flac_codec::metadata::{VorbisComment, fields::ARTIST};
2281 ///
2282 /// let mut comment = VorbisComment {
2283 /// fields: vec![
2284 /// "ARTIST=Artist 1".to_owned(),
2285 /// "ARTIST=Artist 2".to_owned(),
2286 /// ],
2287 /// ..VorbisComment::default()
2288 /// };
2289 ///
2290 /// comment.insert(ARTIST, "Artist 3");
2291 ///
2292 /// assert_eq!(
2293 /// comment.all(ARTIST).collect::<Vec<_>>(),
2294 /// vec!["Artist 1", "Artist 2", "Artist 3"],
2295 /// );
2296 /// ```
2297 pub fn insert<S>(&mut self, field: &str, value: S)
2298 where
2299 S: std::fmt::Display,
2300 {
2301 assert!(!field.contains('='), "field must not contain '='");
2302
2303 self.fields.push(format!("{field}={value}"));
2304 }
2305
2306 /// Removes any matching instances of the given field
2307 ///
2308 /// Fields are matched case-insensitively
2309 ///
2310 /// # Panics
2311 ///
2312 /// Panics if field contains the `=` character.
2313 ///
2314 /// # Example
2315 ///
2316 /// ```
2317 /// use flac_codec::metadata::{VorbisComment, fields::ARTIST};
2318 ///
2319 /// let mut comment = VorbisComment {
2320 /// fields: vec![
2321 /// "ARTIST=Artist 1".to_owned(),
2322 /// "ARTIST=Artist 2".to_owned(),
2323 /// ],
2324 /// ..VorbisComment::default()
2325 /// };
2326 ///
2327 /// comment.remove(ARTIST);
2328 ///
2329 /// assert_eq!(comment.get(ARTIST), None);
2330 /// ```
2331 pub fn remove(&mut self, field: &str) {
2332 assert!(!field.contains('='), "field must not contain '='");
2333
2334 self.fields.retain(|f| match f.split_once('=') {
2335 Some((key, _)) => !key.eq_ignore_ascii_case(field),
2336 None => true,
2337 });
2338 }
2339
2340 /// Replaces any instances of the given field with the given values
2341 ///
2342 /// Fields are matched case-insensitively
2343 ///
2344 /// # Panics
2345 ///
2346 /// Panics if field contains the `=` character
2347 ///
2348 /// # Example
2349 ///
2350 /// ```
2351 /// use flac_codec::metadata::{VorbisComment, fields::ARTIST};
2352 ///
2353 /// let mut comment = VorbisComment {
2354 /// fields: vec![
2355 /// "ARTIST=Artist 1".to_owned(),
2356 /// "ARTIST=Artist 2".to_owned(),
2357 /// ],
2358 /// ..VorbisComment::default()
2359 /// };
2360 ///
2361 /// comment.replace(ARTIST, ["Artist 3", "Artist 4"]);
2362 ///
2363 /// assert_eq!(
2364 /// comment.all(ARTIST).collect::<Vec<_>>(),
2365 /// vec!["Artist 3", "Artist 4"],
2366 /// );
2367 ///
2368 /// // reminder that Option also implements IntoIterator
2369 /// comment.replace(ARTIST, Some("Artist 5"));
2370 ///
2371 /// assert_eq!(
2372 /// comment.all(ARTIST).collect::<Vec<_>>(),
2373 /// vec!["Artist 5"],
2374 /// );
2375 /// ```
2376 pub fn replace<S: std::fmt::Display>(
2377 &mut self,
2378 field: &str,
2379 replacements: impl IntoIterator<Item = S>,
2380 ) {
2381 self.remove(field);
2382 self.fields.extend(
2383 replacements
2384 .into_iter()
2385 .map(|value| format!("{field}={value}")),
2386 );
2387 }
2388}
2389
2390block!(VorbisComment, VorbisComment, false);
2391optional_block!(VorbisComment, VorbisComment);
2392
2393impl FromBitStream for VorbisComment {
2394 type Error = Error;
2395
2396 fn from_reader<R: BitRead + ?Sized>(r: &mut R) -> Result<Self, Self::Error> {
2397 fn read_string<R: BitRead + ?Sized>(r: &mut R) -> Result<String, Error> {
2398 let size = r.read_as_to::<LittleEndian, u32>()?.try_into().unwrap();
2399 Ok(String::from_utf8(r.read_to_vec(size)?)?)
2400 }
2401
2402 Ok(Self {
2403 vendor_string: read_string(r)?,
2404 fields: (0..(r.read_as_to::<LittleEndian, u32>()?))
2405 .map(|_| read_string(r))
2406 .collect::<Result<Vec<_>, _>>()?,
2407 })
2408 }
2409}
2410
2411impl ToBitStream for VorbisComment {
2412 type Error = Error;
2413
2414 fn to_writer<W: BitWrite + ?Sized>(&self, w: &mut W) -> Result<(), Self::Error> {
2415 fn write_string<W: BitWrite + ?Sized>(w: &mut W, s: &str) -> Result<(), Error> {
2416 w.write_as_from::<LittleEndian, u32>(
2417 s.len()
2418 .try_into()
2419 .map_err(|_| Error::ExcessiveStringLength)?,
2420 )?;
2421 w.write_bytes(s.as_bytes())?;
2422 Ok(())
2423 }
2424
2425 write_string(w, &self.vendor_string)?;
2426 w.write_as_from::<LittleEndian, u32>(
2427 self.fields
2428 .len()
2429 .try_into()
2430 .map_err(|_| Error::ExcessiveVorbisEntries)?,
2431 )?;
2432 self.fields.iter().try_for_each(|s| write_string(w, s))
2433 }
2434}
2435
2436// As neat as it might be implement IndexMut for VorbisComment,
2437// the trait simply isn't compatible. A "&mut str" is mostly
2438// useless, and I can't return a partial "&mut String"
2439// in order to assing a new string to everything after the
2440// initial "FIELD=" indicator.
2441
2442/// Vorbis comment metadata tag fields
2443///
2444/// Not all of these fields are officially defined in the specification,
2445/// but they are in common use.
2446pub mod fields {
2447 /// Name of current work
2448 pub const TITLE: &str = "TITLE";
2449
2450 /// Name of the artist generally responsible for the current work
2451 pub const ARTIST: &str = "ARTIST";
2452
2453 /// Name of the collection the current work belongs to
2454 pub const ALBUM: &str = "ALBUM";
2455
2456 /// The work's original composer
2457 pub const COMPOSER: &str = "COMPOSER";
2458
2459 /// The performance's conductor
2460 pub const CONDUCTOR: &str = "CONDUCTOR";
2461
2462 /// The current work's performer(s)
2463 pub const PERFORMER: &str = "PERFORMER";
2464
2465 /// The album's publisher
2466 pub const PUBLISHER: &str = "PUBLISHER";
2467
2468 /// The album's catalog number
2469 pub const CATALOG: &str = "CATALOG";
2470
2471 /// Release date of work
2472 pub const DATE: &str = "DATE";
2473
2474 /// Generic comment
2475 pub const COMMENT: &str = "COMMENT";
2476
2477 /// Track number in album
2478 pub const TRACK_NUMBER: &str = "TRACKNUMBER";
2479
2480 /// Total tracks in album
2481 pub const TRACK_TOTAL: &str = "TRACKTOTAL";
2482
2483 /// The channel mask of multi-channel audio streams
2484 pub const CHANNEL_MASK: &str = "WAVEFORMATEXTENSIBLE_CHANNEL_MASK";
2485
2486 /// ReplayGain track gain
2487 pub const RG_TRACK_GAIN: &str = "REPLAYGAIN_TRACK_GAIN";
2488
2489 /// ReplayGain album gain
2490 pub const RG_ALBUM_GAIN: &str = "REPLAYGAIN_ALBUM_GAIN";
2491
2492 /// ReplayGain track peak
2493 pub const RG_TRACK_PEAK: &str = "REPLAYGAIN_TRACK_PEAK";
2494
2495 /// ReplayGain album peak
2496 pub const RG_ALBUM_PEAK: &str = "REPLAYGAIN_ALBUM_PEAK";
2497
2498 /// ReplayGain reference loudness
2499 pub const RG_REFERENCE_LOUDNESS: &str = "REPLAYGAIN_REFERENCE_LOUDNESS";
2500}
2501
2502/// Types for collections which must be contiguous
2503///
2504/// Used by the SEEKTABLE and CUESHEET metadata blocks
2505pub mod contiguous {
2506 /// A trait for types which can be contiguous
2507 pub trait Adjacent {
2508 /// Whether the item is valid as the first in a sequence
2509 fn valid_first(&self) -> bool;
2510
2511 /// Whether the item is immediately following the previous
2512 fn is_next(&self, previous: &Self) -> bool;
2513 }
2514
2515 impl Adjacent for u64 {
2516 fn valid_first(&self) -> bool {
2517 *self == 0
2518 }
2519
2520 fn is_next(&self, previous: &Self) -> bool {
2521 *self > *previous
2522 }
2523 }
2524
2525 impl Adjacent for std::num::NonZero<u8> {
2526 fn valid_first(&self) -> bool {
2527 *self == Self::MIN
2528 }
2529
2530 fn is_next(&self, previous: &Self) -> bool {
2531 previous.checked_add(1).map(|n| n == *self).unwrap_or(false)
2532 }
2533 }
2534
2535 /// A Vec-like type which requires all items to be adjacent
2536 #[derive(Debug, Clone, Eq, PartialEq)]
2537 pub struct Contiguous<const MAX: usize, T: Adjacent> {
2538 items: Vec<T>,
2539 }
2540
2541 impl<const MAX: usize, T: Adjacent> Default for Contiguous<MAX, T> {
2542 fn default() -> Self {
2543 Self { items: Vec::new() }
2544 }
2545 }
2546
2547 impl<const MAX: usize, T: Adjacent> Contiguous<MAX, T> {
2548 /// Constructs new contiguous block with the given capacity
2549 ///
2550 /// See [`Vec::with_capacity`]
2551 pub fn with_capacity(capacity: usize) -> Self {
2552 Self {
2553 items: Vec::with_capacity(capacity.min(MAX)),
2554 }
2555 }
2556
2557 /// Removes all items without adjust total capacity
2558 ///
2559 /// See [`Vec::clear`]
2560 pub fn clear(&mut self) {
2561 self.items.clear()
2562 }
2563
2564 /// Attempts to push item into contiguous set
2565 ///
2566 /// # Errors
2567 ///
2568 /// Returns error if item is not a valid first item
2569 /// in the set or is not contiguous with the
2570 /// existing items.
2571 pub fn try_push(&mut self, item: T) -> Result<(), NonContiguous> {
2572 if self.items.len() < MAX {
2573 if match self.items.last() {
2574 None => item.valid_first(),
2575 Some(last) => item.is_next(last),
2576 } {
2577 self.items.push(item);
2578 Ok(())
2579 } else {
2580 Err(NonContiguous)
2581 }
2582 } else {
2583 Err(NonContiguous)
2584 }
2585 }
2586
2587 /// Attempts to extends set with items from iterator
2588 pub fn try_extend(
2589 &mut self,
2590 iter: impl IntoIterator<Item = T>,
2591 ) -> Result<(), NonContiguous> {
2592 iter.into_iter().try_for_each(|item| self.try_push(item))
2593 }
2594
2595 /// Attempts to collect a contiguous set from a fallible iterator
2596 pub fn try_collect<I, E>(iter: I) -> Result<Result<Self, E>, NonContiguous>
2597 where
2598 I: IntoIterator<Item = Result<T, E>>,
2599 {
2600 let iter = iter.into_iter();
2601 let mut c = Self::with_capacity(iter.size_hint().0);
2602
2603 for item in iter {
2604 match item {
2605 Ok(item) => c.try_push(item)?,
2606 Err(err) => return Ok(Err(err)),
2607 }
2608 }
2609 Ok(Ok(c))
2610 }
2611 }
2612
2613 impl<const MAX: usize, T: Adjacent> std::ops::Deref for Contiguous<MAX, T> {
2614 type Target = [T];
2615
2616 fn deref(&self) -> &[T] {
2617 self.items.as_slice()
2618 }
2619 }
2620
2621 impl<const MAX: usize, T: Adjacent> From<Contiguous<MAX, T>> for Vec<T> {
2622 fn from(contiguous: Contiguous<MAX, T>) -> Self {
2623 contiguous.items
2624 }
2625 }
2626
2627 impl<const MAX: usize, T: Adjacent> From<Contiguous<MAX, T>> for std::collections::VecDeque<T> {
2628 fn from(contiguous: Contiguous<MAX, T>) -> Self {
2629 contiguous.items.into()
2630 }
2631 }
2632
2633 impl<const MAX: usize, T: Adjacent> TryFrom<Vec<T>> for Contiguous<MAX, T> {
2634 type Error = NonContiguous;
2635
2636 fn try_from(items: Vec<T>) -> Result<Self, Self::Error> {
2637 (items.len() <= MAX && is_contiguous(&items))
2638 .then_some(Self { items })
2639 .ok_or(NonContiguous)
2640 }
2641 }
2642
2643 /// Attempted to insert a non-contiguous item into a set
2644 #[derive(Copy, Clone, Debug)]
2645 pub struct NonContiguous;
2646
2647 impl std::error::Error for NonContiguous {}
2648
2649 impl std::fmt::Display for NonContiguous {
2650 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2651 "item is non-contiguous".fmt(f)
2652 }
2653 }
2654
2655 fn is_contiguous<'t, T: Adjacent + 't>(items: impl IntoIterator<Item = &'t T>) -> bool {
2656 and_previous(items).all(|(prev, item)| match prev {
2657 Some(prev) => item.is_next(prev),
2658 None => item.valid_first(),
2659 })
2660 }
2661
2662 fn and_previous<T: Copy>(
2663 iter: impl IntoIterator<Item = T>,
2664 ) -> impl Iterator<Item = (Option<T>, T)> {
2665 let mut previous = None;
2666 iter.into_iter().map(move |i| (previous.replace(i), i))
2667 }
2668}
2669
2670/// A CUESHEET metadata block
2671///
2672/// A cue sheet stores a disc's original layout
2673/// with all its tracks, index points, and disc-specific metadata.
2674///
2675/// This block may occur multiple times in a FLAC file, theoretically.
2676///
2677/// | Bits | Field | Meaning |
2678/// |------:|------:|---------|
2679/// | 128×8 | `catalog_number` | media catalog number, in ASCII
2680/// | 64 | `lead_in_samples` | number of lead-in samples
2681/// | 1 | `is_cdda` | whether cuesheet corresponds to CD-DA
2682/// | 7+258×8 | padding | all 0 bits
2683/// | 8 | track count | number of cuesheet tracks
2684/// | | `tracks` | cuesheet track₀, cuesheet track₁, …
2685///
2686/// Although the structure of this block is not particularly
2687/// complicated, a CUESHEET block must abide by many rules
2688/// in order to be considered valid. Many of these
2689/// rules are encoded into the type system.
2690#[derive(Debug, Clone, Eq, PartialEq)]
2691pub enum Cuesheet {
2692 /// A CD-DA Cuesheet, for audio CDs
2693 CDDA {
2694 /// Media catalog number in ASCII digits
2695 ///
2696 /// For CD-DA, if present, this number must
2697 /// be exactly 13 ASCII digits followed by all
2698 /// 0 bytes.
2699 catalog_number: Option<[cuesheet::Digit; 13]>,
2700
2701 /// Number of lead-in samples
2702 ///
2703 /// For CD-DA, this must be at least 2 seconds
2704 /// (88200 samples), but may be longer.
2705 ///
2706 /// Non-CD-DA cuesheets must always use 0 for
2707 /// lead-in samples, which is why that variant
2708 /// does not have this field.
2709 lead_in_samples: u64,
2710
2711 /// The cue sheet's non-lead-out tracks
2712 ///
2713 /// For CD-DA, 0 ≤ track count ≤ 99
2714 tracks: contiguous::Contiguous<99, cuesheet::TrackCDDA>,
2715
2716 /// The required lead-out-track
2717 ///
2718 /// This has a track number of 170 and
2719 /// indicates the end of the disc.
2720 lead_out: cuesheet::LeadOutCDDA,
2721 },
2722 /// A Non-CD-DA Cuesheet, for non-audio CDs
2723 NonCDDA {
2724 /// Media catalog number in ASCII digits
2725 ///
2726 /// 0 ≤ catalog number digits < 120
2727 catalog_number: Vec<cuesheet::Digit>,
2728
2729 /// The cue sheet's non-lead-out tracks
2730 ///
2731 /// For Non-CD-DA, 0 ≤ track count ≤ 254
2732 tracks: contiguous::Contiguous<254, cuesheet::TrackNonCDDA>,
2733
2734 /// The required lead-out-track
2735 ///
2736 /// This has a track number of 255 and
2737 /// indicates the end of the disc.
2738 lead_out: cuesheet::LeadOutNonCDDA,
2739 },
2740}
2741
2742impl Cuesheet {
2743 /// Default number of lead-in samples
2744 const LEAD_IN: u64 = 44100 * 2;
2745
2746 /// Maximum catalog number length, in digits
2747 const CATALOG_LEN: usize = 128;
2748
2749 /// Media catalog number
2750 pub fn catalog_number(&self) -> impl std::fmt::Display {
2751 use cuesheet::Digit;
2752
2753 enum Digits<'d> {
2754 Digits(&'d [Digit]),
2755 Empty,
2756 }
2757
2758 impl std::fmt::Display for Digits<'_> {
2759 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2760 match self {
2761 Self::Digits(digits) => digits.iter().try_for_each(|d| d.fmt(f)),
2762 Self::Empty => Ok(()),
2763 }
2764 }
2765 }
2766
2767 match self {
2768 Self::CDDA {
2769 catalog_number: Some(catalog_number),
2770 ..
2771 } => Digits::Digits(catalog_number),
2772 Self::CDDA {
2773 catalog_number: None,
2774 ..
2775 } => Digits::Empty,
2776 Self::NonCDDA { catalog_number, .. } => Digits::Digits(catalog_number),
2777 }
2778 }
2779
2780 /// The number of lead-in samples for CD-DA discs
2781 pub fn lead_in_samples(&self) -> Option<u64> {
2782 match self {
2783 Self::CDDA {
2784 lead_in_samples, ..
2785 } => Some(*lead_in_samples),
2786 Self::NonCDDA { .. } => None,
2787 }
2788 }
2789
2790 /// If this is a CD-DA cuesheet
2791 pub fn is_cdda(&self) -> bool {
2792 matches!(self, Self::CDDA { .. })
2793 }
2794
2795 /// Returns total number of tracks in cuesheet
2796 pub fn track_count(&self) -> usize {
2797 match self {
2798 Self::CDDA { tracks, .. } => tracks.len() + 1,
2799 Self::NonCDDA { tracks, .. } => tracks.len() + 1,
2800 }
2801 }
2802
2803 /// Iterates over all tracks in cuesheet
2804 ///
2805 /// Tracks are converted into a unified format suitable for display
2806 pub fn tracks(&self) -> Box<dyn Iterator<Item = cuesheet::TrackGeneric> + '_> {
2807 use cuesheet::{Index, Track};
2808
2809 match self {
2810 Self::CDDA {
2811 tracks, lead_out, ..
2812 } => Box::new(
2813 tracks
2814 .iter()
2815 .map(|track| Track {
2816 offset: track.offset.into(),
2817 number: Some(track.number.get()),
2818 isrc: track.isrc.clone(),
2819 non_audio: track.non_audio,
2820 pre_emphasis: track.pre_emphasis,
2821 index_points: track
2822 .index_points
2823 .iter()
2824 .map(|point| Index {
2825 number: point.number,
2826 offset: point.offset.into(),
2827 })
2828 .collect(),
2829 })
2830 .chain(std::iter::once(Track {
2831 offset: lead_out.offset.into(),
2832 number: None,
2833 isrc: lead_out.isrc.clone(),
2834 non_audio: lead_out.non_audio,
2835 pre_emphasis: lead_out.pre_emphasis,
2836 index_points: vec![],
2837 })),
2838 ),
2839 Self::NonCDDA {
2840 tracks, lead_out, ..
2841 } => Box::new(
2842 tracks
2843 .iter()
2844 .map(|track| Track {
2845 offset: track.offset,
2846 number: Some(track.number.get()),
2847 isrc: track.isrc.clone(),
2848 non_audio: track.non_audio,
2849 pre_emphasis: track.pre_emphasis,
2850 index_points: track
2851 .index_points
2852 .iter()
2853 .map(|point| Index {
2854 number: point.number,
2855 offset: point.offset,
2856 })
2857 .collect(),
2858 })
2859 .chain(std::iter::once(Track {
2860 offset: lead_out.offset,
2861 number: None,
2862 isrc: lead_out.isrc.clone(),
2863 non_audio: lead_out.non_audio,
2864 pre_emphasis: lead_out.pre_emphasis,
2865 index_points: vec![],
2866 })),
2867 ),
2868 }
2869 }
2870
2871 /// Given a filename to use, returns cuesheet data as text
2872 pub fn display(&self, filename: &str) -> impl std::fmt::Display {
2873 struct DisplayCuesheet<'c, 'f> {
2874 cuesheet: &'c Cuesheet,
2875 filename: &'f str,
2876 }
2877
2878 impl std::fmt::Display for DisplayCuesheet<'_, '_> {
2879 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2880 /// A CUESHEET timestamp in MM:SS:FF format
2881 #[derive(Copy, Clone)]
2882 pub struct Timestamp {
2883 minutes: u64,
2884 seconds: u8,
2885 frames: u8,
2886 }
2887
2888 impl Timestamp {
2889 const FRAMES_PER_SECOND: u64 = 75;
2890 const SECONDS_PER_MINUTE: u64 = 60;
2891 const SAMPLES_PER_FRAME: u64 = 44100 / 75;
2892 }
2893
2894 impl From<u64> for Timestamp {
2895 fn from(offset: u64) -> Self {
2896 let total_frames = offset / Self::SAMPLES_PER_FRAME;
2897
2898 Self {
2899 minutes: (total_frames / Self::FRAMES_PER_SECOND)
2900 / Self::SECONDS_PER_MINUTE,
2901 seconds: ((total_frames / Self::FRAMES_PER_SECOND)
2902 % Self::SECONDS_PER_MINUTE)
2903 .try_into()
2904 .unwrap(),
2905 frames: (total_frames % Self::FRAMES_PER_SECOND).try_into().unwrap(),
2906 }
2907 }
2908 }
2909
2910 impl std::fmt::Display for Timestamp {
2911 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2912 write!(
2913 f,
2914 "{:02}:{:02}:{:02}",
2915 self.minutes, self.seconds, self.frames
2916 )
2917 }
2918 }
2919
2920 writeln!(f, "FILE \"{}\" FLAC", self.filename)?;
2921
2922 match self.cuesheet {
2923 Cuesheet::CDDA { tracks, .. } => {
2924 for track in tracks.iter() {
2925 writeln!(
2926 f,
2927 " TRACK {} {}",
2928 track.number,
2929 if track.non_audio {
2930 "NON_AUDIO"
2931 } else {
2932 "AUDIO"
2933 }
2934 )?;
2935 for index in track.index_points.iter() {
2936 writeln!(
2937 f,
2938 " INDEX {:02} {}",
2939 index.number,
2940 Timestamp::from(u64::from(index.offset + track.offset)),
2941 )?;
2942 }
2943 }
2944 }
2945 Cuesheet::NonCDDA { tracks, .. } => {
2946 for track in tracks.iter() {
2947 writeln!(
2948 f,
2949 " TRACK {} {}",
2950 track.number,
2951 if track.non_audio {
2952 "NON_AUDIO"
2953 } else {
2954 "AUDIO"
2955 }
2956 )?;
2957 for index in track.index_points.iter() {
2958 writeln!(
2959 f,
2960 " INDEX {:02} {}",
2961 index.number,
2962 Timestamp::from(index.offset + track.offset),
2963 )?;
2964 }
2965 }
2966 }
2967 }
2968
2969 Ok(())
2970 }
2971 }
2972
2973 DisplayCuesheet {
2974 cuesheet: self,
2975 filename,
2976 }
2977 }
2978
2979 /// Attempts to parse new `Cuesheet` from cue sheet file
2980 ///
2981 /// `total_samples` should be the total number
2982 /// of channel-independent samples, used to
2983 /// calculate the lead-out track
2984 ///
2985 /// `cuesheet` is the entire cuesheet as a string slice
2986 ///
2987 /// This is a simplistic cuesheet parser sufficient
2988 /// for generating FLAC-compatible CUESHEET metadata blocks.
2989 ///
2990 /// # Example File
2991 ///
2992 /// ```text
2993 /// FILE "cdimage.wav" WAVE
2994 /// TRACK 01 AUDIO
2995 /// INDEX 01 00:00:00
2996 /// TRACK 02 AUDIO
2997 /// INDEX 00 02:57:52
2998 /// INDEX 01 03:00:02
2999 /// TRACK 03 AUDIO
3000 /// INDEX 00 04:46:17
3001 /// INDEX 01 04:48:64
3002 /// TRACK 04 AUDIO
3003 /// INDEX 00 07:09:01
3004 /// INDEX 01 07:11:49
3005 /// TRACK 05 AUDIO
3006 /// INDEX 00 09:11:47
3007 /// INDEX 01 09:13:54
3008 /// TRACK 06 AUDIO
3009 /// INDEX 00 11:10:13
3010 /// INDEX 01 11:12:51
3011 /// TRACK 07 AUDIO
3012 /// INDEX 00 13:03:74
3013 /// INDEX 01 13:07:19
3014 /// ```
3015 ///
3016 /// `INDEX` points are in the format:
3017 ///
3018 /// ```text
3019 /// minutes frames
3020 /// ↓↓ ↓↓
3021 /// MM::SS::FF
3022 /// ↑↑
3023 /// seconds
3024 /// ```
3025 ///
3026 /// There are 75 frames per second, and 60 seconds per minute.
3027 /// Since CD audio has 44100 channel-independent samples per second,
3028 /// the number of channel-independent samples per frame is 588
3029 /// (44100 ÷ 75 = 588).
3030 ///
3031 /// Thus, the sample offset of each `INDEX` point can be calculated like:
3032 ///
3033 /// samples = ((MM × 60 × 75) + (SS × 75) + FF) × 588
3034 ///
3035 /// Note that the `INDEX` points are stored in increasing order,
3036 /// as a standard single file cue sheet.
3037 ///
3038 /// # Example
3039 ///
3040 /// ```
3041 /// use flac_codec::metadata::{Cuesheet, cuesheet::{Track, Index, ISRC}};
3042 ///
3043 /// let file = "FILE \"cdimage.wav\" WAVE
3044 /// TRACK 01 AUDIO
3045 /// INDEX 01 00:00:00
3046 /// TRACK 02 AUDIO
3047 /// INDEX 00 02:57:52
3048 /// INDEX 01 03:00:02
3049 /// TRACK 03 AUDIO
3050 /// INDEX 00 04:46:17
3051 /// INDEX 01 04:48:64
3052 /// TRACK 04 AUDIO
3053 /// INDEX 00 07:09:01
3054 /// INDEX 01 07:11:49
3055 /// TRACK 05 AUDIO
3056 /// INDEX 00 09:11:47
3057 /// INDEX 01 09:13:54
3058 /// TRACK 06 AUDIO
3059 /// INDEX 00 11:10:13
3060 /// INDEX 01 11:12:51
3061 /// TRACK 07 AUDIO
3062 /// INDEX 00 13:03:74
3063 /// INDEX 01 13:07:19
3064 /// ";
3065 ///
3066 /// let cuesheet = Cuesheet::parse(39731748, file).unwrap();
3067 /// assert!(cuesheet.is_cdda());
3068 ///
3069 /// let mut tracks = cuesheet.tracks();
3070 ///
3071 /// assert_eq!(
3072 /// tracks.next(),
3073 /// Some(Track {
3074 /// offset: 0,
3075 /// number: Some(01),
3076 /// isrc: ISRC::None,
3077 /// non_audio: false,
3078 /// pre_emphasis: false,
3079 /// index_points: vec![
3080 /// Index { number: 01, offset: 0 },
3081 /// ],
3082 /// }),
3083 /// );
3084 ///
3085 /// assert_eq!(
3086 /// tracks.next(),
3087 /// Some(Track {
3088 /// // track's offset is that of its first index point
3089 /// offset: ((2 * 60 * 75) + (57 * 75) + 52) * 588,
3090 /// number: Some(02),
3091 /// isrc: ISRC::None,
3092 /// non_audio: false,
3093 /// pre_emphasis: false,
3094 /// index_points: vec![
3095 /// // index point offsets are stored relative
3096 /// // to the track's offset
3097 /// Index { number: 00, offset: 0 },
3098 /// Index { number: 01, offset: 175 * 588 }
3099 /// ],
3100 /// }),
3101 /// );
3102 ///
3103 /// assert_eq!(
3104 /// tracks.next(),
3105 /// Some(Track {
3106 /// offset: ((4 * 60 * 75) + (46 * 75) + 17) * 588,
3107 /// number: Some(03),
3108 /// isrc: ISRC::None,
3109 /// non_audio: false,
3110 /// pre_emphasis: false,
3111 /// index_points: vec![
3112 /// Index { number: 00, offset: 0 },
3113 /// Index { number: 01, offset: 197 * 588 }
3114 /// ],
3115 /// }),
3116 /// );
3117 ///
3118 /// // skip over some tracks for brevity
3119 /// assert_eq!(tracks.next().and_then(|track| track.number), Some(04));
3120 /// assert_eq!(tracks.next().and_then(|track| track.number), Some(05));
3121 /// assert_eq!(tracks.next().and_then(|track| track.number), Some(06));
3122 /// assert_eq!(tracks.next().and_then(|track| track.number), Some(07));
3123 ///
3124 /// // the final lead-out track has an offset of the stream's total samples
3125 /// // and no index points
3126 /// assert_eq!(
3127 /// tracks.next(),
3128 /// Some(Track {
3129 /// offset: 39731748,
3130 /// number: None,
3131 /// isrc: ISRC::None,
3132 /// non_audio: false,
3133 /// pre_emphasis: false,
3134 /// index_points: vec![],
3135 /// }),
3136 /// );
3137 ///
3138 /// assert!(tracks.next().is_none());
3139 /// ```
3140 pub fn parse(total_samples: u64, cuesheet: &str) -> Result<Self, CuesheetError> {
3141 use cuesheet::Digit;
3142
3143 fn cdda_catalog(s: &str) -> Result<Option<[Digit; 13]>, CuesheetError> {
3144 s.chars()
3145 .map(Digit::try_from)
3146 .collect::<Result<Vec<_>, _>>()
3147 .and_then(|v| {
3148 <[Digit; 13] as TryFrom<Vec<Digit>>>::try_from(v)
3149 .map_err(|_| CuesheetError::InvalidCatalogNumber)
3150 })
3151 .map(Some)
3152 }
3153
3154 fn non_cdda_catalog(s: &str) -> Result<Vec<Digit>, CuesheetError> {
3155 s.chars()
3156 .map(Digit::try_from)
3157 .collect::<Result<Vec<_>, _>>()
3158 .and_then(|v| {
3159 (v.len() <= Cuesheet::CATALOG_LEN)
3160 .then_some(v)
3161 .ok_or(CuesheetError::InvalidCatalogNumber)
3162 })
3163 }
3164
3165 match total_samples.try_into() {
3166 // if total samples is divisible by 588,
3167 // try to parse a CDDA cuesheet
3168 Ok(lead_out_offset) => ParsedCuesheet::parse(cuesheet, cdda_catalog).and_then(
3169 |ParsedCuesheet {
3170 catalog_number,
3171 tracks,
3172 }| {
3173 Ok(Self::CDDA {
3174 catalog_number,
3175 lead_in_samples: Self::LEAD_IN,
3176 lead_out: cuesheet::LeadOutCDDA::new(tracks.last(), lead_out_offset)?,
3177 tracks,
3178 })
3179 },
3180 ),
3181 // if total samples isn't divisible by 588,
3182 // only try a non-CDDA cuesheet
3183 Err(_) => ParsedCuesheet::parse(cuesheet, non_cdda_catalog).and_then(
3184 |ParsedCuesheet {
3185 catalog_number,
3186 tracks,
3187 }| {
3188 Ok(Self::NonCDDA {
3189 catalog_number,
3190 lead_out: cuesheet::LeadOutNonCDDA::new(tracks.last(), total_samples)?,
3191 tracks,
3192 })
3193 },
3194 ),
3195 }
3196 }
3197
3198 fn track_offsets(&self) -> Box<dyn Iterator<Item = u64> + '_> {
3199 match self {
3200 Self::CDDA {
3201 tracks, lead_out, ..
3202 } => Box::new(
3203 tracks
3204 .iter()
3205 .map(|t| u64::from(t.offset + *t.index_points.start()))
3206 .chain(std::iter::once(u64::from(lead_out.offset))),
3207 ),
3208 Self::NonCDDA {
3209 tracks, lead_out, ..
3210 } => Box::new(
3211 tracks
3212 .iter()
3213 .map(|t| t.offset + t.index_points.start())
3214 .chain(std::iter::once(lead_out.offset)),
3215 ),
3216 }
3217 }
3218
3219 /// Iterates over track ranges in channel-indepedent samples
3220 ///
3221 /// Note that the range of each track is from the track
3222 /// start to the start of the next track, which is indicated
3223 /// by `INDEX 01`.
3224 /// It is *not* from the start of the pre-gaps (`INDEX 00`),
3225 /// which may not be present.
3226 ///
3227 /// ```
3228 /// use flac_codec::metadata::Cuesheet;
3229 ///
3230 /// let file = "FILE \"cdimage.wav\" WAVE
3231 /// TRACK 01 AUDIO
3232 /// INDEX 01 00:00:00
3233 /// TRACK 02 AUDIO
3234 /// INDEX 00 02:57:52
3235 /// INDEX 01 03:00:02
3236 /// TRACK 03 AUDIO
3237 /// INDEX 00 04:46:17
3238 /// INDEX 01 04:48:64
3239 /// TRACK 04 AUDIO
3240 /// INDEX 00 07:09:01
3241 /// INDEX 01 07:11:49
3242 /// TRACK 05 AUDIO
3243 /// INDEX 00 09:11:47
3244 /// INDEX 01 09:13:54
3245 /// TRACK 06 AUDIO
3246 /// INDEX 00 11:10:13
3247 /// INDEX 01 11:12:51
3248 /// TRACK 07 AUDIO
3249 /// INDEX 00 13:03:74
3250 /// INDEX 01 13:07:19
3251 /// ";
3252 ///
3253 /// let cuesheet = Cuesheet::parse(39731748, file).unwrap();
3254 /// let mut track_ranges = cuesheet.track_sample_ranges();
3255 ///
3256 /// // 00:00:00 to 03:00:02
3257 /// assert_eq!(
3258 /// track_ranges.next(),
3259 /// Some(0..((3 * 60 * 75) + (0 * 75) + 2) * 588)
3260 /// );
3261 ///
3262 /// // 03:00:02 to 04:48:64
3263 /// assert_eq!(
3264 /// track_ranges.next(),
3265 /// Some(((3 * 60 * 75) + (0 * 75) + 2) * 588..((4 * 60 * 75) + (48 * 75) + 64) * 588),
3266 /// );
3267 ///
3268 /// // skip a few tracks for brevity
3269 /// assert!(track_ranges.next().is_some()); // to 07:11.49
3270 /// assert!(track_ranges.next().is_some()); // to 09:13:54
3271 /// assert!(track_ranges.next().is_some()); // to 11:12:51
3272 /// assert!(track_ranges.next().is_some()); // to 13:07:19
3273 ///
3274 /// // 13:07:19 to the lead-out
3275 /// assert_eq!(
3276 /// track_ranges.next(),
3277 /// Some(((13 * 60 * 75) + (7 * 75) + 19) * 588..39731748),
3278 /// );
3279 ///
3280 /// assert!(track_ranges.next().is_none());
3281 /// ```
3282 pub fn track_sample_ranges(&self) -> impl Iterator<Item = std::ops::Range<u64>> {
3283 self.track_offsets()
3284 .zip(self.track_offsets().skip(1))
3285 .map(|(s, e)| s..e)
3286 }
3287
3288 /// Iterates over track ranges in bytes
3289 ///
3290 /// Much like [`Cuesheet::track_sample_ranges`], but takes
3291 /// a channel count and bits-per-sample to convert the ranges to bytes.
3292 ///
3293 /// For CD-DA, those values are 2 and 16, respectively.
3294 ///
3295 /// # Panics
3296 ///
3297 /// Panics if either `channel_count` or `bits_per_sample` are 0
3298 pub fn track_byte_ranges(
3299 &self,
3300 channel_count: u8,
3301 bits_per_sample: u32,
3302 ) -> impl Iterator<Item = std::ops::Range<u64>> {
3303 assert!(channel_count > 0, "channel_count must be > 0");
3304 assert!(bits_per_sample > 0, "bits_per_sample > 0");
3305
3306 let multiplier = u64::from(channel_count) * u64::from(bits_per_sample.div_ceil(8));
3307
3308 self.track_sample_ranges()
3309 .map(move |std::ops::Range { start, end }| start * multiplier..end * multiplier)
3310 }
3311}
3312
3313block!(Cuesheet, Cuesheet, true);
3314optional_block!(Cuesheet, Cuesheet);
3315
3316impl FromBitStream for Cuesheet {
3317 type Error = Error;
3318
3319 fn from_reader<R: BitRead + ?Sized>(r: &mut R) -> Result<Self, Self::Error> {
3320 let catalog_number: [u8; Self::CATALOG_LEN] = r.read_to()?;
3321 let lead_in_samples: u64 = r.read_to()?;
3322 let is_cdda = r.read_bit()?;
3323 r.skip(7 + 258 * 8)?;
3324 let track_count: u8 = r.read_to()?;
3325
3326 Ok(if is_cdda {
3327 Self::CDDA {
3328 catalog_number: {
3329 match trim_nulls(&catalog_number) {
3330 [] => None,
3331 number => Some(
3332 number
3333 .iter()
3334 .copied()
3335 .map(cuesheet::Digit::try_from)
3336 .collect::<Result<Vec<_>, u8>>()
3337 // any of the digits aren't valid ASCII
3338 .map_err(|_| Error::from(CuesheetError::InvalidCatalogNumber))?
3339 .try_into()
3340 // the number isn't the correct size
3341 .map_err(|_| Error::from(CuesheetError::InvalidCatalogNumber))?,
3342 ),
3343 }
3344 },
3345 lead_in_samples,
3346 tracks: contiguous::Contiguous::try_collect(
3347 (0..track_count
3348 .checked_sub(1)
3349 .filter(|c| *c <= 99)
3350 .ok_or(Error::from(CuesheetError::NoTracks))?)
3351 .map(|_| r.parse()),
3352 )
3353 .map_err(|_| Error::from(CuesheetError::TracksOutOfSequence))??,
3354 lead_out: r.parse()?,
3355 }
3356 } else {
3357 Self::NonCDDA {
3358 catalog_number: trim_nulls(&catalog_number)
3359 .iter()
3360 .copied()
3361 .map(cuesheet::Digit::try_from)
3362 .collect::<Result<Vec<_>, _>>()
3363 .map_err(|_| Error::from(CuesheetError::InvalidCatalogNumber))?,
3364 tracks: contiguous::Contiguous::try_collect(
3365 (0..track_count
3366 .checked_sub(1)
3367 .ok_or(Error::from(CuesheetError::NoTracks))?)
3368 .map(|_| r.parse()),
3369 )
3370 .map_err(|_| Error::from(CuesheetError::TracksOutOfSequence))??,
3371 lead_out: r.parse()?,
3372 }
3373 })
3374 }
3375}
3376
3377impl ToBitStream for Cuesheet {
3378 type Error = Error;
3379
3380 fn to_writer<W: BitWrite + ?Sized>(&self, w: &mut W) -> Result<(), Self::Error> {
3381 match self {
3382 Self::CDDA {
3383 catalog_number,
3384 lead_in_samples,
3385 tracks,
3386 lead_out,
3387 } => {
3388 w.write_from(match catalog_number {
3389 Some(number) => {
3390 let mut catalog_number = [0; Self::CATALOG_LEN];
3391 catalog_number
3392 .iter_mut()
3393 .zip(number)
3394 .for_each(|(o, i)| *o = (*i).into());
3395 catalog_number
3396 }
3397 None => [0; Self::CATALOG_LEN],
3398 })?;
3399 w.write_from(*lead_in_samples)?;
3400 w.write_bit(true)?; // is CD-DA
3401 w.pad(7 + 258 * 8)?;
3402 w.write::<8, _>(u8::try_from(tracks.len() + 1).unwrap())?;
3403 for track in tracks.iter() {
3404 w.build(track)?;
3405 }
3406 w.build(lead_out)
3407 }
3408 Self::NonCDDA {
3409 catalog_number,
3410 tracks,
3411 lead_out,
3412 } => {
3413 w.write_from({
3414 let mut number = [0; Self::CATALOG_LEN];
3415 number
3416 .iter_mut()
3417 .zip(catalog_number)
3418 .for_each(|(o, i)| *o = (*i).into());
3419 number
3420 })?;
3421 w.write_from::<u64>(0)?; // non-CDDA cuesheets have no lead-in samples
3422 w.write_bit(false)?; // not CD-DA
3423 w.pad(7 + 258 * 8)?;
3424 w.write::<8, _>(u8::try_from(tracks.len() + 1).unwrap())?;
3425 for track in tracks.iter() {
3426 w.build(track)?;
3427 }
3428 w.build(lead_out)
3429 }
3430 }
3431 }
3432}
3433
3434// trims any trailing null bytes
3435fn trim_nulls(mut s: &[u8]) -> &[u8] {
3436 while let [rest @ .., 0] = s {
3437 s = rest;
3438 }
3439 s
3440}
3441
3442type ParsedCuesheetTrack<const INDEX_MAX: usize, O> =
3443 cuesheet::Track<O, NonZero<u8>, cuesheet::IndexVec<INDEX_MAX, O>>;
3444
3445struct ParsedCuesheet<const TRACK_MAX: usize, const INDEX_MAX: usize, C, O: contiguous::Adjacent> {
3446 catalog_number: C,
3447 tracks: contiguous::Contiguous<TRACK_MAX, ParsedCuesheetTrack<INDEX_MAX, O>>,
3448}
3449
3450impl<const TRACK_MAX: usize, const INDEX_MAX: usize, C, O>
3451 ParsedCuesheet<TRACK_MAX, INDEX_MAX, C, O>
3452where
3453 C: Default,
3454 O: contiguous::Adjacent
3455 + std::str::FromStr
3456 + Into<u64>
3457 + std::ops::Sub<Output = O>
3458 + Default
3459 + Copy,
3460{
3461 fn parse(
3462 cuesheet: &str,
3463 parse_catalog: impl Fn(&str) -> Result<C, CuesheetError>,
3464 ) -> Result<Self, CuesheetError> {
3465 type WipTrack<const INDEX_MAX: usize, O> = cuesheet::Track<
3466 Option<O>,
3467 NonZero<u8>,
3468 contiguous::Contiguous<INDEX_MAX, cuesheet::Index<O>>,
3469 >;
3470
3471 impl<const INDEX_MAX: usize, O: contiguous::Adjacent> WipTrack<INDEX_MAX, O> {
3472 fn new(number: NonZero<u8>) -> Self {
3473 Self {
3474 offset: None,
3475 number,
3476 isrc: cuesheet::ISRC::None,
3477 non_audio: false,
3478 pre_emphasis: false,
3479 index_points: contiguous::Contiguous::default(),
3480 }
3481 }
3482 }
3483
3484 impl<const INDEX_MAX: usize, O: contiguous::Adjacent> TryFrom<WipTrack<INDEX_MAX, O>>
3485 for ParsedCuesheetTrack<INDEX_MAX, O>
3486 {
3487 type Error = CuesheetError;
3488
3489 fn try_from(track: WipTrack<INDEX_MAX, O>) -> Result<Self, Self::Error> {
3490 // completed tracks need an offset which
3491 // is set by adding the first index point
3492 Ok(Self {
3493 offset: track.offset.ok_or(CuesheetError::InvalidTrack)?,
3494 number: track.number,
3495 isrc: track.isrc,
3496 non_audio: track.non_audio,
3497 pre_emphasis: track.pre_emphasis,
3498 index_points: track.index_points.try_into()?,
3499 })
3500 }
3501 }
3502
3503 // a bit of a hack, but should be good enough for now
3504 fn unquote(s: &str) -> &str {
3505 if s.len() > 1 && s.starts_with('"') && s.ends_with('"') {
3506 &s[1..s.len() - 1]
3507 } else {
3508 s
3509 }
3510 }
3511
3512 let mut wip_track: Option<WipTrack<INDEX_MAX, O>> = None;
3513
3514 let mut parsed = ParsedCuesheet {
3515 catalog_number: None,
3516 tracks: contiguous::Contiguous::default(),
3517 };
3518
3519 for line in cuesheet.lines() {
3520 let line = line.trim();
3521 match line.split_once(' ').unwrap_or((line, "")) {
3522 ("CATALOG", "") => return Err(CuesheetError::CatalogMissingNumber),
3523 ("CATALOG", number) => match parsed.catalog_number {
3524 Some(_) => return Err(CuesheetError::MultipleCatalogNumber),
3525 ref mut num @ None => {
3526 *num = Some(parse_catalog(unquote(number))?);
3527 }
3528 },
3529 ("TRACK", rest) => {
3530 if let Some(finished) = wip_track.replace(WipTrack::new(
3531 rest.split_once(' ')
3532 .ok_or(CuesheetError::InvalidTrack)?
3533 .0
3534 .parse()
3535 .map_err(|_| CuesheetError::InvalidTrack)?,
3536 )) {
3537 parsed
3538 .tracks
3539 .try_push(finished.try_into()?)
3540 .map_err(|_| CuesheetError::TracksOutOfSequence)?
3541 }
3542 }
3543 ("INDEX", rest) => {
3544 let (number, offset) = rest
3545 .split_once(' ')
3546 .ok_or(CuesheetError::InvalidIndexPoint)?;
3547
3548 let number: u8 = number
3549 .parse()
3550 .map_err(|_| CuesheetError::InvalidIndexPoint)?;
3551
3552 let offset: O = offset
3553 .parse()
3554 .map_err(|_| CuesheetError::InvalidIndexPoint)?;
3555
3556 let wip_track = wip_track.as_mut().ok_or(CuesheetError::PrematureIndex)?;
3557
3558 let index = match &mut wip_track.offset {
3559 // work-in progress track has no offset,
3560 // so we set it from this index point's
3561 // and set the index point's offset to 0
3562 track_offset @ None => {
3563 // the first index of the first track must have an offset of 0
3564 if parsed.tracks.is_empty() && offset.into() != 0 {
3565 return Err(CuesheetError::NonZeroFirstIndex);
3566 }
3567
3568 *track_offset = Some(offset);
3569
3570 cuesheet::Index {
3571 number,
3572 offset: O::default(),
3573 }
3574 }
3575 Some(track_offset) => {
3576 // work-in-progress track has offset,
3577 // so deduct that offset from this index point's
3578
3579 cuesheet::Index {
3580 number,
3581 offset: offset - *track_offset,
3582 }
3583 }
3584 };
3585
3586 wip_track
3587 .index_points
3588 .try_push(index)
3589 .map_err(|_| CuesheetError::IndexPointsOutOfSequence)?;
3590 }
3591 ("ISRC", isrc) => {
3592 use cuesheet::ISRC;
3593
3594 let wip_track = wip_track.as_mut().ok_or(CuesheetError::PrematureISRC)?;
3595
3596 if !wip_track.index_points.is_empty() {
3597 return Err(CuesheetError::LateISRC)?;
3598 }
3599
3600 match &mut wip_track.isrc {
3601 track_isrc @ ISRC::None => {
3602 *track_isrc = ISRC::String(
3603 unquote(isrc)
3604 .parse()
3605 .map_err(|_| CuesheetError::InvalidISRC)?,
3606 );
3607 }
3608 ISRC::String(_) => return Err(CuesheetError::MultipleISRC),
3609 }
3610 }
3611 ("FLAGS", "PRE") => {
3612 let wip_track = wip_track.as_mut().ok_or(CuesheetError::PrematureFlags)?;
3613
3614 if !wip_track.index_points.is_empty() {
3615 return Err(CuesheetError::LateFlags)?;
3616 } else {
3617 wip_track.pre_emphasis = true;
3618 }
3619 }
3620 _ => { /*do nothing for now*/ }
3621 }
3622 }
3623
3624 parsed
3625 .tracks
3626 .try_push(
3627 wip_track
3628 .take()
3629 .ok_or(CuesheetError::NoTracks)?
3630 .try_into()?,
3631 )
3632 .map_err(|_| CuesheetError::TracksOutOfSequence)?;
3633
3634 Ok(ParsedCuesheet {
3635 catalog_number: parsed.catalog_number.unwrap_or_default(),
3636 tracks: parsed.tracks,
3637 })
3638 }
3639}
3640
3641/// An error when trying to parse cue sheet data
3642#[derive(Debug)]
3643#[non_exhaustive]
3644pub enum CuesheetError {
3645 /// CATALOG tag missing catalog number
3646 CatalogMissingNumber,
3647 /// multiple CATALOG numbers found
3648 MultipleCatalogNumber,
3649 /// invalid CATALOG number
3650 InvalidCatalogNumber,
3651 /// Multiple ISRC numbers
3652 MultipleISRC,
3653 /// Invalid ISRC number
3654 InvalidISRC,
3655 /// ISRC number found before TRACK
3656 PrematureISRC,
3657 /// ISRC number after INDEX points
3658 LateISRC,
3659 /// FLAGS seen before TRACK
3660 PrematureFlags,
3661 /// FLAGS seen after INDEX points
3662 LateFlags,
3663 /// ISRC tag missing number
3664 ISRCMissingNumber,
3665 /// Unable to parse TRACK field correctly
3666 InvalidTrack,
3667 /// No tracks in cue sheet
3668 NoTracks,
3669 /// Non-Zero starting INDEX in first TRACK
3670 NonZeroStartingIndex,
3671 /// INDEX point occurs before TRACK
3672 PrematureIndex,
3673 /// Invalid INDEX point in cuesheet
3674 InvalidIndexPoint,
3675 /// No INDEX Points in track
3676 NoIndexPoints,
3677 /// Excessive tracks in cue sheet
3678 ExcessiveTracks,
3679 /// INDEX points in track are out of sequence
3680 IndexPointsOutOfSequence,
3681 /// TRACK points in CUESHEET are out of sequence
3682 TracksOutOfSequence,
3683 /// Lead-out track is not beyond all track indices
3684 ShortLeadOut,
3685 /// INDEX points in lead-out TRACK
3686 IndexPointsInLeadout,
3687 /// Invalid offset for CD-DA CUESHEET
3688 InvalidCDDAOffset,
3689 /// first INDEX of first TRACK doesn't have offset of 0
3690 NonZeroFirstIndex,
3691}
3692
3693impl std::error::Error for CuesheetError {}
3694
3695impl std::fmt::Display for CuesheetError {
3696 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
3697 match self {
3698 Self::CatalogMissingNumber => "CATALOG tag missing number".fmt(f),
3699 Self::MultipleCatalogNumber => "multiple CATALOG numbers found".fmt(f),
3700 Self::InvalidCatalogNumber => "invalid CATALOG number".fmt(f),
3701 Self::MultipleISRC => "multiple ISRC numbers found for track".fmt(f),
3702 Self::InvalidISRC => "invalid ISRC number found".fmt(f),
3703 Self::PrematureISRC => "ISRC number found before TRACK".fmt(f),
3704 Self::LateISRC => "ISRC number found after INDEX points".fmt(f),
3705 Self::PrematureFlags => "FLAGS found before TRACK".fmt(f),
3706 Self::LateFlags => "FLAGS found after INDEX points".fmt(f),
3707 Self::ISRCMissingNumber => "ISRC tag missing number".fmt(f),
3708 Self::InvalidTrack => "invalid TRACK entry".fmt(f),
3709 Self::NoTracks => "no TRACK entries in cue sheet".fmt(f),
3710 Self::NonZeroStartingIndex => {
3711 "first INDEX of first track must have 00:00:00 offset".fmt(f)
3712 }
3713 Self::PrematureIndex => "INDEX found before TRACK".fmt(f),
3714 Self::InvalidIndexPoint => "invalid INDEX entry".fmt(f),
3715 Self::NoIndexPoints => "no INDEX points in track".fmt(f),
3716 Self::ExcessiveTracks => "excessive tracks in CUESHEET".fmt(f),
3717 Self::IndexPointsOutOfSequence => "INDEX points out of sequence".fmt(f),
3718 Self::TracksOutOfSequence => "TRACKS out of sequence".fmt(f),
3719 Self::ShortLeadOut => "lead-out track not beyond final INDEX point".fmt(f),
3720 Self::IndexPointsInLeadout => "INDEX points in lead-out TRACK".fmt(f),
3721 Self::InvalidCDDAOffset => "invalid offset for CD-DA CUESHEET".fmt(f),
3722 Self::NonZeroFirstIndex => "first index of first track has non-zero offset".fmt(f),
3723 }
3724 }
3725}
3726
3727/// A PICTURE metadata block
3728///
3729/// Picture blocks are for embedding artwork
3730/// such as album covers, liner notes, etc.
3731///
3732/// This block may occur multiple times in a FLAC file.
3733///
3734/// | Bits | Field | Meaning |
3735/// |-----:|------:|---------|
3736/// | 32 | `picture_type` | picture type
3737/// | 32 | media type len | media type length, in bytes
3738/// | `media type len`×8 | `media_type` | picture's MIME type
3739/// | 32 | description len | description length, in bytes
3740/// | `description len`×8 | `description` | description of picture, in UTF-8
3741/// | 32 | `width` | width of picture, in pixels
3742/// | 32 | `height`| height of picture, in pixels
3743/// | 32 | `color_depth` | color depth of picture in bits-per-pixel
3744/// | 32 | `colors_used` | for indexed-color pictures, number of colors used
3745/// | 32 | data len | length of picture data, in bytes
3746/// | `data len`×8 | `data` | raw picture data
3747///
3748/// # Example
3749/// ```
3750/// use bitstream_io::{BitReader, BitRead, BigEndian};
3751/// use flac_codec::metadata::{Picture, PictureType};
3752///
3753/// let data: &[u8] = &[
3754/// 0x00, 0x00, 0x00, 0x03, // picture type
3755/// 0x00, 0x00, 0x00, 0x09, // media type len (9 bytes)
3756/// 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67,
3757/// 0x00, 0x00, 0x00, 0x0a, // description len (10 bytes)
3758/// 0x54, 0x65, 0x73, 0x74, 0x20, 0x49, 0x6d, 0x61, 0x67, 0x65,
3759/// 0x00, 0x00, 0x00, 0x10, // width
3760/// 0x00, 0x00, 0x00, 0x09, // height
3761/// 0x00, 0x00, 0x00, 0x18, // color depth
3762/// 0x00, 0x00, 0x00, 0x00, // color count
3763/// 0x00, 0x00, 0x00, 0x5c, // data len (92 bytes)
3764/// 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
3765/// 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52,
3766/// 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x09,
3767/// 0x08, 0x02, 0x00, 0x00, 0x00, 0xb4, 0x48, 0x3b,
3768/// 0x65, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59,
3769/// 0x73, 0x00, 0x00, 0x2e, 0x23, 0x00, 0x00, 0x2e,
3770/// 0x23, 0x01, 0x78, 0xa5, 0x3f, 0x76, 0x00, 0x00,
3771/// 0x00, 0x0e, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3,
3772/// 0x63, 0x60, 0x18, 0x05, 0x43, 0x12, 0x00, 0x00,
3773/// 0x01, 0xb9, 0x00, 0x01, 0xed, 0x78, 0x29, 0x25,
3774/// 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44,
3775/// 0xae, 0x42, 0x60, 0x82,
3776/// ];
3777///
3778/// let mut r = BitReader::endian(data, BigEndian);
3779/// assert_eq!(
3780/// r.parse::<Picture>().unwrap(),
3781/// Picture {
3782/// picture_type: PictureType::FrontCover, // type 3
3783/// media_type: "image/png".to_owned(),
3784/// description: "Test Image".to_owned(),
3785/// width: 0x00_00_00_10, // 16 pixels
3786/// height: 0x00_00_00_09, // 9 pixels
3787/// color_depth: 0x00_00_00_18, // 24 bits-per-pixel
3788/// colors_used: None, // not indexed
3789/// data: vec![
3790/// 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
3791/// 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52,
3792/// 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x09,
3793/// 0x08, 0x02, 0x00, 0x00, 0x00, 0xb4, 0x48, 0x3b,
3794/// 0x65, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59,
3795/// 0x73, 0x00, 0x00, 0x2e, 0x23, 0x00, 0x00, 0x2e,
3796/// 0x23, 0x01, 0x78, 0xa5, 0x3f, 0x76, 0x00, 0x00,
3797/// 0x00, 0x0e, 0x49, 0x44, 0x41, 0x54, 0x18, 0xd3,
3798/// 0x63, 0x60, 0x18, 0x05, 0x43, 0x12, 0x00, 0x00,
3799/// 0x01, 0xb9, 0x00, 0x01, 0xed, 0x78, 0x29, 0x25,
3800/// 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44,
3801/// 0xae, 0x42, 0x60, 0x82,
3802/// ],
3803/// },
3804/// );
3805/// ```
3806#[derive(Debug, Clone, Eq, PartialEq)]
3807pub struct Picture {
3808 /// The picture type
3809 pub picture_type: PictureType,
3810 /// The media type string as specified by RFC2046
3811 pub media_type: String,
3812 /// The description of the picture
3813 pub description: String,
3814 /// The width of the picture in pixels
3815 pub width: u32,
3816 /// The height of the picture in pixels
3817 pub height: u32,
3818 /// The color depth of the picture in bits per pixel
3819 pub color_depth: u32,
3820 /// For indexed-color pictures, the number of colors used
3821 pub colors_used: Option<NonZero<u32>>,
3822 /// The binary picture data
3823 pub data: Vec<u8>,
3824}
3825
3826block!(Picture, Picture, true);
3827optional_block!(Picture, Picture);
3828
3829impl Picture {
3830 /// Attempt to create a new PICTURE block from raw image data
3831 ///
3832 /// Currently supported image types for this method are:
3833 ///
3834 /// - JPEG
3835 /// - PNG
3836 /// - GIF
3837 ///
3838 /// Any type of image data may be placed in a PICTURE block,
3839 /// but the user may have to use external crates
3840 /// to determine their proper image metrics
3841 /// to build a block from.
3842 ///
3843 /// # Errors
3844 ///
3845 /// Returns an error if some problem occurs reading
3846 /// or identifying the file.
3847 pub fn new<S, V>(
3848 picture_type: PictureType,
3849 description: S,
3850 data: V,
3851 ) -> Result<Self, InvalidPicture>
3852 where
3853 S: Into<String>,
3854 V: Into<Vec<u8>> + AsRef<[u8]>,
3855 {
3856 let metrics = PictureMetrics::try_new(data.as_ref())?;
3857 Ok(Self {
3858 picture_type,
3859 description: description.into(),
3860 data: data.into(),
3861 media_type: metrics.media_type.to_owned(),
3862 width: metrics.width,
3863 height: metrics.height,
3864 color_depth: metrics.color_depth,
3865 colors_used: metrics.colors_used,
3866 })
3867 }
3868
3869 /// Attempt to create new PICTURE block from file on disk
3870 pub fn open<S, P>(
3871 picture_type: PictureType,
3872 description: S,
3873 path: P,
3874 ) -> Result<Self, InvalidPicture>
3875 where
3876 S: Into<String>,
3877 P: AsRef<Path>,
3878 {
3879 std::fs::read(path)
3880 .map_err(InvalidPicture::Io)
3881 .and_then(|data| Self::new(picture_type, description, data))
3882 }
3883}
3884
3885impl FromBitStream for Picture {
3886 type Error = Error;
3887
3888 fn from_reader<R: BitRead + ?Sized>(r: &mut R) -> Result<Self, Error> {
3889 fn prefixed_field<R: BitRead + ?Sized>(r: &mut R) -> std::io::Result<Vec<u8>> {
3890 let size = r.read_to::<u32>()?;
3891 r.read_to_vec(size.try_into().unwrap())
3892 }
3893
3894 Ok(Self {
3895 picture_type: r.parse()?,
3896 media_type: String::from_utf8(prefixed_field(r)?)?,
3897 description: String::from_utf8(prefixed_field(r)?)?,
3898 width: r.read_to()?,
3899 height: r.read_to()?,
3900 color_depth: r.read_to()?,
3901 colors_used: r.read::<32, _>()?,
3902 data: prefixed_field(r)?,
3903 })
3904 }
3905}
3906
3907impl ToBitStream for Picture {
3908 type Error = Error;
3909
3910 fn to_writer<W: BitWrite + ?Sized>(&self, w: &mut W) -> Result<(), Error> {
3911 fn prefixed_field<W: BitWrite + ?Sized>(
3912 w: &mut W,
3913 field: &[u8],
3914 error: Error,
3915 ) -> Result<(), Error> {
3916 w.write_from::<u32>(field.len().try_into().map_err(|_| error)?)
3917 .map_err(Error::Io)?;
3918 w.write_bytes(field).map_err(Error::Io)
3919 }
3920
3921 w.build(&self.picture_type)?;
3922 prefixed_field(w, self.media_type.as_bytes(), Error::ExcessiveStringLength)?;
3923 prefixed_field(w, self.description.as_bytes(), Error::ExcessiveStringLength)?;
3924 w.write_from(self.width)?;
3925 w.write_from(self.height)?;
3926 w.write_from(self.color_depth)?;
3927 w.write::<32, _>(self.colors_used)?;
3928 prefixed_field(w, &self.data, Error::ExcessivePictureSize)
3929 }
3930}
3931
3932/// Defined variants of PICTURE type
3933#[derive(Debug, Copy, Clone, Eq, PartialEq)]
3934pub enum PictureType {
3935 /// Other
3936 Other = 0,
3937 /// PNG file icon of 32x32 pixels
3938 Png32x32 = 1,
3939 /// General file icon
3940 GeneralFileIcon = 2,
3941 /// Front cover
3942 FrontCover = 3,
3943 /// Back cover
3944 BackCover = 4,
3945 /// Liner notes page
3946 LinerNotes = 5,
3947 /// Media label (e.g., CD, Vinyl or Cassette label)
3948 MediaLabel = 6,
3949 /// Lead artist, lead performer, or soloist
3950 LeadArtist = 7,
3951 /// Artist or performer
3952 Artist = 8,
3953 /// Conductor
3954 Conductor = 9,
3955 /// Band or orchestra
3956 Band = 10,
3957 /// Composer
3958 Composer = 11,
3959 /// Lyricist or text writer
3960 Lyricist = 12,
3961 /// Recording location
3962 RecordingLocation = 13,
3963 /// During recording
3964 DuringRecording = 14,
3965 /// During performance
3966 DuringPerformance = 15,
3967 /// Movie or video screen capture
3968 ScreenCapture = 16,
3969 /// A bright colored fish
3970 Fish = 17,
3971 /// Illustration
3972 Illustration = 18,
3973 /// Band or artist logotype
3974 BandLogo = 19,
3975 /// Publisher or studio logotype
3976 PublisherLogo = 20,
3977}
3978
3979impl std::fmt::Display for PictureType {
3980 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
3981 match self {
3982 Self::Other => "Other".fmt(f),
3983 Self::Png32x32 => "32×32 PNG Icon".fmt(f),
3984 Self::GeneralFileIcon => "General File Icon".fmt(f),
3985 Self::FrontCover => "Cover (front)".fmt(f),
3986 Self::BackCover => "Cover (back)".fmt(f),
3987 Self::LinerNotes => "Liner Notes".fmt(f),
3988 Self::MediaLabel => "Media Label".fmt(f),
3989 Self::LeadArtist => "Lead Artist".fmt(f),
3990 Self::Artist => "Artist".fmt(f),
3991 Self::Conductor => "Conductor".fmt(f),
3992 Self::Band => "Band or Orchestra".fmt(f),
3993 Self::Composer => "Composer".fmt(f),
3994 Self::Lyricist => "lyricist or Text Writer".fmt(f),
3995 Self::RecordingLocation => "Recording Location".fmt(f),
3996 Self::DuringRecording => "During Recording".fmt(f),
3997 Self::DuringPerformance => "During Performance".fmt(f),
3998 Self::ScreenCapture => "Movie or Video Screen Capture".fmt(f),
3999 Self::Fish => "A Bright Colored Fish".fmt(f),
4000 Self::Illustration => "Illustration".fmt(f),
4001 Self::BandLogo => "Band or Artist Logotype".fmt(f),
4002 Self::PublisherLogo => "Publisher or Studio Logotype".fmt(f),
4003 }
4004 }
4005}
4006
4007impl FromBitStream for PictureType {
4008 type Error = Error;
4009
4010 fn from_reader<R: BitRead + ?Sized>(r: &mut R) -> Result<Self, Error> {
4011 match r.read_to::<u32>()? {
4012 0 => Ok(Self::Other),
4013 1 => Ok(Self::Png32x32),
4014 2 => Ok(Self::GeneralFileIcon),
4015 3 => Ok(Self::FrontCover),
4016 4 => Ok(Self::BackCover),
4017 5 => Ok(Self::LinerNotes),
4018 6 => Ok(Self::MediaLabel),
4019 7 => Ok(Self::LeadArtist),
4020 8 => Ok(Self::Artist),
4021 9 => Ok(Self::Conductor),
4022 10 => Ok(Self::Band),
4023 11 => Ok(Self::Composer),
4024 12 => Ok(Self::Lyricist),
4025 13 => Ok(Self::RecordingLocation),
4026 14 => Ok(Self::DuringRecording),
4027 15 => Ok(Self::DuringPerformance),
4028 16 => Ok(Self::ScreenCapture),
4029 17 => Ok(Self::Fish),
4030 18 => Ok(Self::Illustration),
4031 19 => Ok(Self::BandLogo),
4032 20 => Ok(Self::PublisherLogo),
4033 _ => Err(Error::InvalidPictureType),
4034 }
4035 }
4036}
4037
4038impl ToBitStream for PictureType {
4039 type Error = std::io::Error;
4040
4041 fn to_writer<W: BitWrite + ?Sized>(&self, w: &mut W) -> Result<(), Self::Error> {
4042 w.write_from::<u32>(match self {
4043 Self::Other => 0,
4044 Self::Png32x32 => 1,
4045 Self::GeneralFileIcon => 2,
4046 Self::FrontCover => 3,
4047 Self::BackCover => 4,
4048 Self::LinerNotes => 5,
4049 Self::MediaLabel => 6,
4050 Self::LeadArtist => 7,
4051 Self::Artist => 8,
4052 Self::Conductor => 9,
4053 Self::Band => 10,
4054 Self::Composer => 11,
4055 Self::Lyricist => 12,
4056 Self::RecordingLocation => 13,
4057 Self::DuringRecording => 14,
4058 Self::DuringPerformance => 15,
4059 Self::ScreenCapture => 16,
4060 Self::Fish => 17,
4061 Self::Illustration => 18,
4062 Self::BandLogo => 19,
4063 Self::PublisherLogo => 20,
4064 })
4065 }
4066}
4067
4068/// An error when trying to identify a picture's metrics
4069#[derive(Debug)]
4070#[non_exhaustive]
4071pub enum InvalidPicture {
4072 /// An I/O Error
4073 Io(std::io::Error),
4074 /// Unsupported Image Format
4075 Unsupported,
4076 /// Invalid PNG File
4077 Png(&'static str),
4078 /// Invalid JPEG File
4079 Jpeg(&'static str),
4080 /// Invalid GIF File
4081 Gif(&'static str),
4082}
4083
4084impl From<std::io::Error> for InvalidPicture {
4085 #[inline]
4086 fn from(err: std::io::Error) -> Self {
4087 Self::Io(err)
4088 }
4089}
4090
4091impl std::error::Error for InvalidPicture {}
4092
4093impl std::fmt::Display for InvalidPicture {
4094 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
4095 match self {
4096 Self::Io(err) => err.fmt(f),
4097 Self::Unsupported => "unsupported image format".fmt(f),
4098 Self::Png(s) => write!(f, "PNG parsing error : {s}"),
4099 Self::Jpeg(s) => write!(f, "JPEG parsing error : {s}"),
4100 Self::Gif(s) => write!(f, "GIF parsing error : {s}"),
4101 }
4102 }
4103}
4104
4105struct PictureMetrics {
4106 media_type: &'static str,
4107 width: u32,
4108 height: u32,
4109 color_depth: u32,
4110 colors_used: Option<NonZero<u32>>,
4111}
4112
4113impl PictureMetrics {
4114 fn try_new(data: &[u8]) -> Result<Self, InvalidPicture> {
4115 if data.starts_with(b"\x89\x50\x4E\x47\x0D\x0A\x1A\x0A") {
4116 Self::try_png(data)
4117 } else if data.starts_with(b"\xFF\xD8\xFF") {
4118 Self::try_jpeg(data)
4119 } else if data.starts_with(b"GIF") {
4120 Self::try_gif(data)
4121 } else {
4122 Err(InvalidPicture::Unsupported)
4123 }
4124 }
4125
4126 fn try_png(data: &[u8]) -> Result<Self, InvalidPicture> {
4127 // this is an *extremely* cut-down PNG parser
4128 // that handles just enough of the format to get
4129 // image metadata, but does *not* validate things
4130 // like the block CRC32s
4131
4132 fn plte_colors<R: ByteRead>(mut r: R) -> Result<u32, InvalidPicture> {
4133 loop {
4134 let block_len = r.read::<u32>()?;
4135 match r.read::<[u8; 4]>()?.as_slice() {
4136 b"PLTE" => {
4137 if block_len % 3 == 0 {
4138 break Ok(block_len / 3);
4139 } else {
4140 break Err(InvalidPicture::Png("invalid PLTE length"));
4141 }
4142 }
4143 _ => {
4144 r.skip(block_len)?;
4145 let _crc = r.read::<u32>()?;
4146 }
4147 }
4148 }
4149 }
4150
4151 let mut r = ByteReader::endian(data, BigEndian);
4152 if &r.read::<[u8; 8]>()? != b"\x89\x50\x4E\x47\x0D\x0A\x1A\x0A" {
4153 return Err(InvalidPicture::Png("not a PNG image"));
4154 }
4155
4156 // IHDR chunk must be first
4157 if r.read::<u32>()? != 0x0d {
4158 return Err(InvalidPicture::Png("invalid IHDR length"));
4159 }
4160 if &r.read::<[u8; 4]>()? != b"IHDR" {
4161 return Err(InvalidPicture::Png("IHDR chunk not first"));
4162 }
4163 let width = r.read()?;
4164 let height = r.read()?;
4165 let bit_depth = r.read::<u8>()?;
4166 let color_type = r.read::<u8>()?;
4167 let _compression_method = r.read::<u8>()?;
4168 let _filter_method = r.read::<u8>()?;
4169 let _interlace_method = r.read::<u8>()?;
4170 let _crc = r.read::<u32>()?;
4171
4172 let (color_depth, colors_used) = match color_type {
4173 0 => (bit_depth.into(), None), // grayscale
4174 2 => ((bit_depth * 3).into(), None), // RGB
4175 3 => (0, NonZero::new(plte_colors(r)?)), // palette
4176 4 => ((bit_depth * 2).into(), None), // grayscale + alpha
4177 6 => ((bit_depth * 4).into(), None), // RGB + alpha
4178 _ => return Err(InvalidPicture::Png("invalid color type")),
4179 };
4180
4181 Ok(Self {
4182 media_type: "image/png",
4183 width,
4184 height,
4185 color_depth,
4186 colors_used,
4187 })
4188 }
4189
4190 fn try_jpeg(data: &[u8]) -> Result<Self, InvalidPicture> {
4191 let mut r = ByteReader::endian(data, BigEndian);
4192
4193 if r.read::<u8>()? != 0xFF || r.read::<u8>()? != 0xD8 {
4194 return Err(InvalidPicture::Jpeg("invalid JPEG marker"));
4195 }
4196
4197 loop {
4198 if r.read::<u8>()? != 0xFF {
4199 break Err(InvalidPicture::Jpeg("invalid JPEG marker"));
4200 }
4201 match r.read::<u8>()? {
4202 0xC0 | 0xC1 | 0xC2 | 0xC3 | 0xC5 | 0xC6 | 0xC7 | 0xC9 | 0xCA | 0xCB | 0xCD
4203 | 0xCE | 0xCF => {
4204 let _len = r.read::<u16>()?;
4205 let data_precision = r.read::<u8>()?;
4206 let height = r.read::<u16>()?;
4207 let width = r.read::<u16>()?;
4208 let components = r.read::<u8>()?;
4209 break Ok(Self {
4210 media_type: "image/jpeg",
4211 width: width.into(),
4212 height: height.into(),
4213 color_depth: (data_precision * components).into(),
4214 colors_used: None,
4215 });
4216 }
4217 _ => {
4218 let segment_length = r
4219 .read::<u16>()?
4220 .checked_sub(2)
4221 .ok_or(InvalidPicture::Jpeg("invalid segment length"))?;
4222 r.skip(segment_length.into())?;
4223 }
4224 }
4225 }
4226 }
4227
4228 fn try_gif(data: &[u8]) -> Result<Self, InvalidPicture> {
4229 let mut r = BitReader::endian(data, LittleEndian);
4230
4231 if &r.read_to::<[u8; 3]>()? != b"GIF" {
4232 return Err(InvalidPicture::Gif("invalid GIF signature"));
4233 }
4234
4235 r.skip(3 * 8)?; // ignore version bytes
4236
4237 Ok(Self {
4238 media_type: "image/gif",
4239 width: r.read::<16, _>()?,
4240 height: r.read::<16, _>()?,
4241 colors_used: NonZero::new(1 << (r.read::<3, u32>()? + 1)),
4242 color_depth: 0,
4243 })
4244 }
4245}
4246
4247/// A collection of metadata blocks
4248///
4249/// This collection enforces the restriction that FLAC files
4250/// must always contain a STREAMINFO metadata block
4251/// and that block must always be first in the file.
4252///
4253/// Because it is required, that block may be retrieved
4254/// unconditionally from this collection, while all others
4255/// are optional and may appear in any order.
4256#[derive(Clone, Debug)]
4257pub struct BlockList {
4258 streaminfo: Streaminfo,
4259 blocks: Vec<private::OptionalBlock>,
4260}
4261
4262impl BlockList {
4263 /// Creates `BlockList` from initial STREAMINFO
4264 pub fn new(streaminfo: Streaminfo) -> Self {
4265 Self {
4266 streaminfo,
4267 blocks: Vec::default(),
4268 }
4269 }
4270
4271 /// Reads `BlockList` from the given reader
4272 ///
4273 /// This assumes the reader is rewound to the
4274 /// beginning of the file.
4275 ///
4276 /// Because this may perform many small reads,
4277 /// using a buffered reader is preferred when
4278 /// reading from a raw file.
4279 ///
4280 /// # Errors
4281 ///
4282 /// Returns any error reading or parsing metadata blocks
4283 pub fn read<R: std::io::Read>(r: R) -> Result<Self, Error> {
4284 // TODO - change this to flatten once that stabilizes
4285 read_blocks(r).collect::<Result<Result<Self, _>, _>>()?
4286 }
4287
4288 /// Reads `BlockList` from the given file path
4289 ///
4290 /// # Errors
4291 ///
4292 /// Returns any error reading or parsing metadata blocks
4293 pub fn open<P: AsRef<Path>>(p: P) -> Result<Self, Error> {
4294 File::open(p.as_ref())
4295 .map(BufReader::new)
4296 .map_err(Error::Io)
4297 .and_then(BlockList::read)
4298 }
4299
4300 /// Returns reference to our STREAMINFO metadata block
4301 pub fn streaminfo(&self) -> &Streaminfo {
4302 &self.streaminfo
4303 }
4304
4305 /// Returns exclusive reference to our STREAMINFO metadata block
4306 ///
4307 /// Care must be taken when modifying the STREAMINFO, or
4308 /// one's file could be rendered unplayable.
4309 pub fn streaminfo_mut(&mut self) -> &mut Streaminfo {
4310 &mut self.streaminfo
4311 }
4312
4313 /// Iterates over all the metadata blocks
4314 pub fn blocks(&self) -> impl Iterator<Item = BlockRef<'_>> {
4315 std::iter::once(self.streaminfo.as_block_ref())
4316 .chain(self.blocks.iter().map(|b| b.as_block_ref()))
4317 }
4318
4319 /// Inserts new optional metadata block
4320 ///
4321 /// If the block may only occur once in the stream
4322 /// (such as the SEEKTABLE), any existing block of
4323 /// the same type removed and extracted first.
4324 pub fn insert<B: OptionalMetadataBlock>(&mut self, block: B) -> Option<B> {
4325 if B::MULTIPLE {
4326 self.blocks.push(block.into());
4327 None
4328 } else {
4329 match self
4330 .blocks
4331 .iter_mut()
4332 .find_map(|b| B::try_from_opt_block_mut(b).ok())
4333 {
4334 Some(b) => Some(std::mem::replace(b, block)),
4335 None => {
4336 self.blocks.push(block.into());
4337 None
4338 }
4339 }
4340 }
4341 }
4342
4343 /// Gets reference to metadata block, if present
4344 ///
4345 /// If the block type occurs multiple times,
4346 /// this returns the first instance.
4347 pub fn get<B: OptionalMetadataBlock>(&self) -> Option<&B> {
4348 self.blocks
4349 .iter()
4350 .find_map(|b| B::try_from_opt_block(b).ok())
4351 }
4352
4353 /// Gets mutable reference to metadata block, if present
4354 ///
4355 /// If the block type occurs multiple times,
4356 /// this returns the first instance.
4357 pub fn get_mut<B: OptionalMetadataBlock>(&mut self) -> Option<&mut B> {
4358 self.blocks
4359 .iter_mut()
4360 .find_map(|b| B::try_from_opt_block_mut(b).ok())
4361 }
4362
4363 /// Gets mutable references to a pair of metadata blocks
4364 ///
4365 /// If either block type occurs multiple times,
4366 /// this returns the first instance.
4367 pub fn get_pair_mut<B, C>(&mut self) -> (Option<&mut B>, Option<&mut C>)
4368 where
4369 B: OptionalMetadataBlock,
4370 C: OptionalMetadataBlock,
4371 {
4372 use std::ops::ControlFlow;
4373
4374 match self
4375 .blocks
4376 .iter_mut()
4377 .try_fold((None, None), |acc, block| match acc {
4378 (first @ None, second @ None) => {
4379 ControlFlow::Continue(match B::try_from_opt_block_mut(block) {
4380 Ok(first) => (Some(first), second),
4381 Err(block) => (first, C::try_from_opt_block_mut(block).ok()),
4382 })
4383 }
4384 (first @ Some(_), None) => {
4385 ControlFlow::Continue((first, C::try_from_opt_block_mut(block).ok()))
4386 }
4387 (None, second @ Some(_)) => {
4388 ControlFlow::Continue((B::try_from_opt_block_mut(block).ok(), second))
4389 }
4390 pair @ (Some(_), Some(_)) => ControlFlow::Break(pair),
4391 }) {
4392 ControlFlow::Break(p) | ControlFlow::Continue(p) => p,
4393 }
4394 }
4395
4396 /// Gets references to all metadata blocks of the given type
4397 pub fn get_all<'b, B: OptionalMetadataBlock + 'b>(&'b self) -> impl Iterator<Item = &'b B> {
4398 self.blocks
4399 .iter()
4400 .filter_map(|b| B::try_from_opt_block(b).ok())
4401 }
4402
4403 /// Gets exclusive references to all metadata blocks of the given type
4404 pub fn get_all_mut<'b, B: OptionalMetadataBlock + 'b>(
4405 &'b mut self,
4406 ) -> impl Iterator<Item = &'b mut B> {
4407 self.blocks
4408 .iter_mut()
4409 .filter_map(|b| B::try_from_opt_block_mut(b).ok())
4410 }
4411
4412 /// Returns `true` if block exists in list
4413 pub fn has<B: OptionalMetadataBlock>(&self) -> bool {
4414 self.get::<B>().is_some()
4415 }
4416
4417 /// Removes all instances of the given metadata block type
4418 pub fn remove<B: OptionalMetadataBlock>(&mut self) {
4419 self.blocks.retain(|b| b.block_type() != B::TYPE)
4420 }
4421
4422 /// Updates first instance of the given block, creating it if necessary
4423 ///
4424 /// # Example
4425 ///
4426 /// ```
4427 /// use flac_codec::metadata::{BlockList, Streaminfo, VorbisComment};
4428 /// use flac_codec::metadata::fields::ARTIST;
4429 ///
4430 /// // build a BlockList with a dummy Streaminfo
4431 /// let mut blocklist = BlockList::new(
4432 /// Streaminfo {
4433 /// minimum_block_size: 0,
4434 /// maximum_block_size: 0,
4435 /// minimum_frame_size: None,
4436 /// maximum_frame_size: None,
4437 /// sample_rate: 44100,
4438 /// channels: 1u8.try_into().unwrap(),
4439 /// bits_per_sample: 16u32.try_into().unwrap(),
4440 /// total_samples: None,
4441 /// md5: None,
4442 /// },
4443 /// );
4444 ///
4445 /// // the block starts out with no comment
4446 /// assert!(blocklist.get::<VorbisComment>().is_none());
4447 ///
4448 /// // update Vorbis Comment with artist field,
4449 /// // which adds a new block to the list
4450 /// blocklist.update::<VorbisComment>(
4451 /// |vc| vc.insert(ARTIST, "Artist 1")
4452 /// );
4453 /// assert!(blocklist.get::<VorbisComment>().is_some());
4454 ///
4455 /// // updating Vorbis Comment again reuses that same block
4456 /// blocklist.update::<VorbisComment>(
4457 /// |vc| vc.insert(ARTIST, "Artist 2")
4458 /// );
4459 ///
4460 /// // the block now has two entries
4461 /// assert_eq!(
4462 /// blocklist.get::<VorbisComment>()
4463 /// .unwrap()
4464 /// .all(ARTIST).collect::<Vec<_>>(),
4465 /// vec!["Artist 1", "Artist 2"],
4466 /// );
4467 /// ```
4468 pub fn update<B>(&mut self, f: impl FnOnce(&mut B))
4469 where
4470 B: OptionalMetadataBlock + Default,
4471 {
4472 match self.get_mut() {
4473 Some(block) => f(block),
4474 None => {
4475 let mut b = B::default();
4476 f(&mut b);
4477 self.blocks.push(b.into());
4478 }
4479 }
4480 }
4481
4482 /// Sorts optional metadata blocks by block type
4483 ///
4484 /// The function converts the type to some key which is
4485 /// used for ordering blocks from smallest to largest.
4486 ///
4487 /// The order of blocks of the same type is preserved.
4488 /// This is an important consideration for APPLICATION
4489 /// metadata blocks, which may contain foreign metadata
4490 /// chunks that must be re-applied in the same order.
4491 ///
4492 /// # Example
4493 ///
4494 /// ```
4495 /// use flac_codec::metadata::{
4496 /// BlockList, Streaminfo, Application, Padding, AsBlockRef,
4497 /// OptionalBlockType,
4498 /// };
4499 ///
4500 /// // build a BlockList with a dummy Streaminfo
4501 /// let streaminfo = Streaminfo {
4502 /// minimum_block_size: 0,
4503 /// maximum_block_size: 0,
4504 /// minimum_frame_size: None,
4505 /// maximum_frame_size: None,
4506 /// sample_rate: 44100,
4507 /// channels: 1u8.try_into().unwrap(),
4508 /// bits_per_sample: 16u32.try_into().unwrap(),
4509 /// total_samples: None,
4510 /// md5: None,
4511 /// };
4512 ///
4513 /// let mut blocklist = BlockList::new(streaminfo.clone());
4514 ///
4515 /// // add some blocks
4516 /// let application_1 = Application {
4517 /// id: 0x1234,
4518 /// data: vec![0x01, 0x02, 0x03, 0x04],
4519 /// };
4520 /// blocklist.insert(application_1.clone());
4521 ///
4522 /// let padding = Padding {
4523 /// size: 10u32.try_into().unwrap(),
4524 /// };
4525 /// blocklist.insert(padding.clone());
4526 ///
4527 /// let application_2 = Application {
4528 /// id: 0x6789,
4529 /// data: vec![0x06, 0x07, 0x08, 0x09],
4530 /// };
4531 /// blocklist.insert(application_2.clone());
4532 ///
4533 /// // check their inital order
4534 /// let mut iter = blocklist.blocks();
4535 /// assert_eq!(iter.next(), Some(streaminfo.as_block_ref()));
4536 /// assert_eq!(iter.next(), Some(application_1.as_block_ref()));
4537 /// assert_eq!(iter.next(), Some(padding.as_block_ref()));
4538 /// assert_eq!(iter.next(), Some(application_2.as_block_ref()));
4539 /// assert_eq!(iter.next(), None);
4540 /// drop(iter);
4541 ///
4542 /// // sort the blocks to put padding last
4543 /// blocklist.sort_by(|t| match t {
4544 /// OptionalBlockType::Application => 0,
4545 /// OptionalBlockType::SeekTable => 1,
4546 /// OptionalBlockType::VorbisComment => 2,
4547 /// OptionalBlockType::Cuesheet => 3,
4548 /// OptionalBlockType::Picture => 4,
4549 /// OptionalBlockType::Padding => 5,
4550 /// });
4551 ///
4552 /// // re-check their new order
4553 /// let mut iter = blocklist.blocks();
4554 /// assert_eq!(iter.next(), Some(streaminfo.as_block_ref()));
4555 /// assert_eq!(iter.next(), Some(application_1.as_block_ref()));
4556 /// assert_eq!(iter.next(), Some(application_2.as_block_ref()));
4557 /// assert_eq!(iter.next(), Some(padding.as_block_ref()));
4558 /// assert_eq!(iter.next(), None);
4559 /// ```
4560 pub fn sort_by<O: Ord>(&mut self, f: impl Fn(OptionalBlockType) -> O) {
4561 self.blocks
4562 .sort_by_key(|block| f(block.optional_block_type()));
4563 }
4564}
4565
4566impl Metadata for BlockList {
4567 fn channel_count(&self) -> u8 {
4568 self.streaminfo.channels.get()
4569 }
4570
4571 fn channel_mask(&self) -> ChannelMask {
4572 use fields::CHANNEL_MASK;
4573
4574 self.get::<VorbisComment>()
4575 .and_then(|c| c.get(CHANNEL_MASK).and_then(|m| m.parse().ok()))
4576 .unwrap_or(ChannelMask::from_channels(self.channel_count()))
4577 }
4578
4579 fn sample_rate(&self) -> u32 {
4580 self.streaminfo.sample_rate
4581 }
4582
4583 fn bits_per_sample(&self) -> u32 {
4584 self.streaminfo.bits_per_sample.into()
4585 }
4586
4587 fn total_samples(&self) -> Option<u64> {
4588 self.streaminfo.total_samples.map(|s| s.get())
4589 }
4590
4591 fn md5(&self) -> Option<&[u8; 16]> {
4592 self.streaminfo.md5.as_ref()
4593 }
4594}
4595
4596impl FromIterator<Block> for Result<BlockList, Error> {
4597 fn from_iter<T: IntoIterator<Item = Block>>(iter: T) -> Self {
4598 let mut iter = iter.into_iter();
4599
4600 let mut list = match iter.next() {
4601 Some(Block::Streaminfo(streaminfo)) => BlockList::new(streaminfo),
4602 Some(_) | None => return Err(Error::MissingStreaminfo),
4603 };
4604
4605 for block in iter {
4606 match block {
4607 Block::Streaminfo(_) => return Err(Error::MultipleStreaminfo),
4608 Block::Padding(p) => {
4609 list.insert(p);
4610 }
4611 Block::Application(p) => {
4612 list.insert(p);
4613 }
4614 Block::SeekTable(p) => {
4615 list.insert(p);
4616 }
4617 Block::VorbisComment(p) => {
4618 list.insert(p);
4619 }
4620 Block::Cuesheet(p) => {
4621 list.insert(p);
4622 }
4623 Block::Picture(p) => {
4624 list.insert(p);
4625 }
4626 }
4627 }
4628
4629 Ok(list)
4630 }
4631}
4632
4633impl IntoIterator for BlockList {
4634 type Item = Block;
4635 type IntoIter = Box<dyn Iterator<Item = Block>>;
4636
4637 fn into_iter(self) -> Self::IntoIter {
4638 Box::new(
4639 std::iter::once(self.streaminfo.into())
4640 .chain(self.blocks.into_iter().map(|b| b.into())),
4641 )
4642 }
4643}
4644
4645/// A type of FLAC metadata block which is not required
4646///
4647/// The STREAMINFO block is required. All others are optional.
4648pub trait OptionalMetadataBlock: MetadataBlock + private::OptionalMetadataBlock {
4649 /// Our optional block type
4650 const OPTIONAL_TYPE: OptionalBlockType;
4651}
4652
4653mod private {
4654 use super::{
4655 Application, AsBlockRef, Block, BlockRef, BlockType, Cuesheet, OptionalBlockType, Padding,
4656 Picture, SeekTable, Streaminfo, VorbisComment,
4657 };
4658
4659 #[derive(Clone, Debug)]
4660 pub enum OptionalBlock {
4661 Padding(Padding),
4662 Application(Application),
4663 SeekTable(SeekTable),
4664 VorbisComment(VorbisComment),
4665 Cuesheet(Cuesheet),
4666 Picture(Picture),
4667 }
4668
4669 impl OptionalBlock {
4670 pub fn block_type(&self) -> BlockType {
4671 match self {
4672 Self::Padding(_) => BlockType::Padding,
4673 Self::Application(_) => BlockType::Application,
4674 Self::SeekTable(_) => BlockType::SeekTable,
4675 Self::VorbisComment(_) => BlockType::VorbisComment,
4676 Self::Cuesheet(_) => BlockType::Cuesheet,
4677 Self::Picture(_) => BlockType::Picture,
4678 }
4679 }
4680
4681 pub fn optional_block_type(&self) -> OptionalBlockType {
4682 match self {
4683 Self::Padding(_) => OptionalBlockType::Padding,
4684 Self::Application(_) => OptionalBlockType::Application,
4685 Self::SeekTable(_) => OptionalBlockType::SeekTable,
4686 Self::VorbisComment(_) => OptionalBlockType::VorbisComment,
4687 Self::Cuesheet(_) => OptionalBlockType::Cuesheet,
4688 Self::Picture(_) => OptionalBlockType::Picture,
4689 }
4690 }
4691 }
4692
4693 impl From<OptionalBlock> for Block {
4694 fn from(block: OptionalBlock) -> Block {
4695 match block {
4696 OptionalBlock::Padding(p) => Block::Padding(p),
4697 OptionalBlock::Application(a) => Block::Application(a),
4698 OptionalBlock::SeekTable(s) => Block::SeekTable(s),
4699 OptionalBlock::VorbisComment(v) => Block::VorbisComment(v),
4700 OptionalBlock::Cuesheet(c) => Block::Cuesheet(c),
4701 OptionalBlock::Picture(p) => Block::Picture(p),
4702 }
4703 }
4704 }
4705
4706 impl TryFrom<Block> for OptionalBlock {
4707 type Error = Streaminfo;
4708
4709 fn try_from(block: Block) -> Result<Self, Streaminfo> {
4710 match block {
4711 Block::Streaminfo(s) => Err(s),
4712 Block::Padding(p) => Ok(OptionalBlock::Padding(p)),
4713 Block::Application(a) => Ok(OptionalBlock::Application(a)),
4714 Block::SeekTable(s) => Ok(OptionalBlock::SeekTable(s)),
4715 Block::VorbisComment(v) => Ok(OptionalBlock::VorbisComment(v)),
4716 Block::Cuesheet(c) => Ok(OptionalBlock::Cuesheet(c)),
4717 Block::Picture(p) => Ok(OptionalBlock::Picture(p)),
4718 }
4719 }
4720 }
4721
4722 impl AsBlockRef for OptionalBlock {
4723 fn as_block_ref(&self) -> BlockRef<'_> {
4724 match self {
4725 Self::Padding(p) => BlockRef::Padding(p),
4726 Self::Application(a) => BlockRef::Application(a),
4727 Self::SeekTable(s) => BlockRef::SeekTable(s),
4728 Self::VorbisComment(v) => BlockRef::VorbisComment(v),
4729 Self::Cuesheet(v) => BlockRef::Cuesheet(v),
4730 Self::Picture(p) => BlockRef::Picture(p),
4731 }
4732 }
4733 }
4734
4735 pub trait OptionalMetadataBlock: Into<OptionalBlock> + TryFrom<OptionalBlock> {
4736 fn try_from_opt_block(block: &OptionalBlock) -> Result<&Self, &OptionalBlock>;
4737
4738 fn try_from_opt_block_mut(
4739 block: &mut OptionalBlock,
4740 ) -> Result<&mut Self, &mut OptionalBlock>;
4741 }
4742}
4743
4744/// The channel mask
4745///
4746/// This field is used to communicate that the channels
4747/// in the file differ from FLAC's default channel assignment
4748/// definitions.
4749///
4750/// It is generally used for multi-channel audio
4751/// and stored within the [`VorbisComment`] metadata block
4752/// as the [`fields::CHANNEL_MASK`] field.
4753///
4754/// # Example
4755///
4756/// ```
4757/// use flac_codec::metadata::{ChannelMask, Channel};
4758///
4759/// let mask = "0x003F".parse::<ChannelMask>().unwrap();
4760///
4761/// let mut channels = mask.channels();
4762/// assert_eq!(channels.next(), Some(Channel::FrontLeft));
4763/// assert_eq!(channels.next(), Some(Channel::FrontRight));
4764/// assert_eq!(channels.next(), Some(Channel::FrontCenter));
4765/// assert_eq!(channels.next(), Some(Channel::Lfe));
4766/// assert_eq!(channels.next(), Some(Channel::BackLeft));
4767/// assert_eq!(channels.next(), Some(Channel::BackRight));
4768/// assert_eq!(channels.next(), None);
4769/// ```
4770#[derive(Copy, Clone, Debug, Default)]
4771pub struct ChannelMask {
4772 mask: u32,
4773}
4774
4775impl ChannelMask {
4776 /// Iterates over all the mask's defined channels
4777 pub fn channels(&self) -> impl Iterator<Item = Channel> {
4778 [
4779 Channel::FrontLeft,
4780 Channel::FrontRight,
4781 Channel::FrontCenter,
4782 Channel::Lfe,
4783 Channel::BackLeft,
4784 Channel::BackRight,
4785 Channel::FrontLeftOfCenter,
4786 Channel::FrontRightOfCenter,
4787 Channel::BackCenter,
4788 Channel::SideLeft,
4789 Channel::SideRight,
4790 Channel::TopCenter,
4791 Channel::TopFrontLeft,
4792 Channel::TopFrontCenter,
4793 Channel::TopFrontRight,
4794 Channel::TopRearLeft,
4795 Channel::TopRearCenter,
4796 Channel::TopRearRight,
4797 ]
4798 .into_iter()
4799 .filter(|channel| (*channel as u32 & self.mask) != 0)
4800 }
4801
4802 fn from_channels(channels: u8) -> Self {
4803 match channels {
4804 1 => Self {
4805 mask: Channel::FrontCenter as u32,
4806 },
4807 2 => Self {
4808 mask: Channel::FrontLeft as u32 | Channel::FrontRight as u32,
4809 },
4810 3 => Self {
4811 mask: Channel::FrontLeft as u32
4812 | Channel::FrontRight as u32
4813 | Channel::FrontCenter as u32,
4814 },
4815 4 => Self {
4816 mask: Channel::FrontLeft as u32
4817 | Channel::FrontRight as u32
4818 | Channel::BackLeft as u32
4819 | Channel::BackRight as u32,
4820 },
4821 5 => Self {
4822 mask: Channel::FrontLeft as u32
4823 | Channel::FrontRight as u32
4824 | Channel::FrontCenter as u32
4825 | Channel::SideLeft as u32
4826 | Channel::SideRight as u32,
4827 },
4828 6 => Self {
4829 mask: Channel::FrontLeft as u32
4830 | Channel::FrontRight as u32
4831 | Channel::FrontCenter as u32
4832 | Channel::Lfe as u32
4833 | Channel::SideLeft as u32
4834 | Channel::SideRight as u32,
4835 },
4836 7 => Self {
4837 mask: Channel::FrontLeft as u32
4838 | Channel::FrontRight as u32
4839 | Channel::FrontCenter as u32
4840 | Channel::Lfe as u32
4841 | Channel::BackCenter as u32
4842 | Channel::SideLeft as u32
4843 | Channel::SideRight as u32,
4844 },
4845 8 => Self {
4846 mask: Channel::FrontLeft as u32
4847 | Channel::FrontRight as u32
4848 | Channel::FrontCenter as u32
4849 | Channel::Lfe as u32
4850 | Channel::BackLeft as u32
4851 | Channel::BackRight as u32
4852 | Channel::SideLeft as u32
4853 | Channel::SideRight as u32,
4854 },
4855 // FLAC files are limited to 1-8 channels
4856 _ => panic!("undefined channel count"),
4857 }
4858 }
4859}
4860
4861impl std::str::FromStr for ChannelMask {
4862 type Err = ();
4863
4864 fn from_str(s: &str) -> Result<Self, Self::Err> {
4865 match s.split_once('x').ok_or(())? {
4866 ("0", hex) => u32::from_str_radix(hex, 16)
4867 .map(|mask| ChannelMask { mask })
4868 .map_err(|_| ()),
4869 _ => Err(()),
4870 }
4871 }
4872}
4873
4874impl std::fmt::Display for ChannelMask {
4875 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
4876 write!(f, "0x{:04x}", self.mask)
4877 }
4878}
4879
4880impl From<ChannelMask> for u32 {
4881 fn from(mask: ChannelMask) -> u32 {
4882 mask.mask
4883 }
4884}
4885
4886impl From<u32> for ChannelMask {
4887 fn from(mask: u32) -> ChannelMask {
4888 ChannelMask { mask }
4889 }
4890}
4891
4892/// An individual channel mask channel
4893#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
4894pub enum Channel {
4895 /// Front left channel
4896 FrontLeft = 0b1,
4897
4898 /// Front right channel
4899 FrontRight = 0b10,
4900
4901 /// Front center channel
4902 FrontCenter = 0b100,
4903
4904 /// Low-frequency effects (LFE) channel
4905 Lfe = 0b1000,
4906
4907 /// Back left channel
4908 BackLeft = 0b10000,
4909
4910 /// Back right channel
4911 BackRight = 0b100000,
4912
4913 /// Front left of center channel
4914 FrontLeftOfCenter = 0b1000000,
4915
4916 /// Front right of center channel
4917 FrontRightOfCenter = 0b10000000,
4918
4919 /// Back center channel
4920 BackCenter = 0b100000000,
4921
4922 /// Side left channel
4923 SideLeft = 0b1000000000,
4924
4925 /// Side right channel
4926 SideRight = 0b10000000000,
4927
4928 /// Top center channel
4929 TopCenter = 0b100000000000,
4930
4931 /// Top front left channel
4932 TopFrontLeft = 0b1000000000000,
4933
4934 /// Top front center channel
4935 TopFrontCenter = 0b10000000000000,
4936
4937 /// Top front right channel
4938 TopFrontRight = 0b100000000000000,
4939
4940 /// Top rear left channel
4941 TopRearLeft = 0b1000000000000000,
4942
4943 /// Top rear center channel
4944 TopRearCenter = 0b10000000000000000,
4945
4946 /// Top rear right channel
4947 TopRearRight = 0b100000000000000000,
4948}
4949
4950impl std::fmt::Display for Channel {
4951 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
4952 match self {
4953 Self::FrontLeft => "front left".fmt(f),
4954 Self::FrontRight => "front right".fmt(f),
4955 Self::FrontCenter => "front center".fmt(f),
4956 Self::Lfe => "LFE".fmt(f),
4957 Self::BackLeft => "back left".fmt(f),
4958 Self::BackRight => "back right".fmt(f),
4959 Self::FrontLeftOfCenter => "front left of center".fmt(f),
4960 Self::FrontRightOfCenter => "front right of center".fmt(f),
4961 Self::BackCenter => "back center".fmt(f),
4962 Self::SideLeft => "side left".fmt(f),
4963 Self::SideRight => "side right".fmt(f),
4964 Self::TopCenter => "top center".fmt(f),
4965 Self::TopFrontLeft => "top front left".fmt(f),
4966 Self::TopFrontCenter => "top front center".fmt(f),
4967 Self::TopFrontRight => "top front right".fmt(f),
4968 Self::TopRearLeft => "top rear left".fmt(f),
4969 Self::TopRearCenter => "top rear center".fmt(f),
4970 Self::TopRearRight => "top rear right".fmt(f),
4971 }
4972 }
4973}