flac_metadata_read/
flac-metadata-read.rs

1//! Reading the initial STREAMINFO block from a FLAC file,
2//! as documented in its
3//! [specification](https://xiph.org/flac/format.html#stream).
4//!
5
6extern crate bitstream_io;
7
8use bitstream_io::{BigEndian, BitRead, BitReader, FromBitStream};
9use std::num::NonZero;
10
11#[derive(Debug, PartialEq, Eq)]
12struct BlockHeader {
13    last_block: bool, // 1 bit
14    block_type: u8,   // 7 bits
15    block_size: u32,  // 24 bits
16}
17
18impl FromBitStream for BlockHeader {
19    type Error = std::io::Error;
20
21    fn from_reader<R: BitRead + ?Sized>(r: &mut R) -> std::io::Result<Self> {
22        Ok(Self {
23            last_block: r.read_bit()?,
24            block_type: r.read::<7, _>()?,
25            block_size: r.read::<24, _>()?,
26        })
27    }
28}
29
30#[derive(Debug, PartialEq, Eq)]
31struct Streaminfo {
32    minimum_block_size: u16,      // 16 bits
33    maximum_block_size: u16,      // 16 bits
34    minimum_frame_size: u32,      // 24 bits
35    maximum_frame_size: u32,      // 24 bits
36    sample_rate: u32,             // 20 bits
37    channels: NonZero<u8>,        // 3 bits
38    bits_per_sample: NonZero<u8>, // 5 bits
39    total_samples: u64,           // 36 bits
40    md5: [u8; 16],                // 16 bytes
41}
42
43impl FromBitStream for Streaminfo {
44    type Error = std::io::Error;
45
46    fn from_reader<R: BitRead + ?Sized>(r: &mut R) -> std::io::Result<Self> {
47        Ok(Self {
48            minimum_block_size: r.read_to()?,
49            maximum_block_size: r.read_to()?,
50            minimum_frame_size: r.read::<24, _>()?,
51            maximum_frame_size: r.read::<24, _>()?,
52            sample_rate: r.read::<20, _>()?,
53            channels: r.read::<3, _>()?,
54            bits_per_sample: r.read::<5, _>()?,
55            total_samples: r.read::<36, _>()?,
56            md5: r.read_to()?,
57        })
58    }
59}
60
61#[derive(Debug, PartialEq, Eq)]
62struct VorbisComment {
63    vendor: String,
64    comment: Vec<String>,
65}
66
67impl FromBitStream for VorbisComment {
68    type Error = Box<dyn std::error::Error>;
69
70    fn from_reader<R: BitRead + ?Sized>(r: &mut R) -> Result<Self, Self::Error> {
71        use bitstream_io::LE;
72
73        fn read_entry<R: BitRead + ?Sized>(
74            r: &mut R,
75        ) -> Result<String, Box<dyn std::error::Error>> {
76            use std::convert::TryInto;
77            let size = r.read_as_to::<LE, u32>()?.try_into()?;
78            Ok(String::from_utf8(r.read_to_vec(size)?)?)
79        }
80
81        Ok(Self {
82            vendor: read_entry(r)?,
83            comment: (0..r.read_as_to::<LE, u32>()?)
84                .map(|_| read_entry(r))
85                .collect::<Result<Vec<_>, _>>()?,
86        })
87    }
88}
89
90fn main() {
91    // test FLAC file data
92    let flac = include_bytes!("data/metadata-only.flac");
93
94    let mut reader = BitReader::endian(flac.as_slice(), BigEndian);
95
96    // stream marker
97    assert_eq!(&reader.read_to::<[u8; 4]>().unwrap(), b"fLaC");
98
99    // metadata block header
100    assert_eq!(
101        reader.parse::<BlockHeader>().unwrap(),
102        BlockHeader {
103            last_block: false,
104            block_type: 0,
105            block_size: 34
106        }
107    );
108
109    // STREAMINFO block
110    assert_eq!(
111        dbg!(reader.parse::<Streaminfo>().unwrap()),
112        Streaminfo {
113            minimum_block_size: 4096,
114            maximum_block_size: 4096,
115            minimum_frame_size: 1542,
116            maximum_frame_size: 8546,
117            sample_rate: 44100,
118            channels: NonZero::new(2).unwrap(),
119            bits_per_sample: NonZero::new(16).unwrap(),
120            total_samples: 304844,
121            md5: *b"\xFA\xF2\x69\x2F\xFD\xEC\x2D\x5B\x30\x01\x76\xB4\x62\x88\x7D\x92",
122        }
123    );
124
125    // metadata block header
126    assert_eq!(
127        dbg!(reader.parse::<BlockHeader>().unwrap()),
128        BlockHeader {
129            last_block: false,
130            block_type: 4,
131            block_size: 122
132        }
133    );
134
135    // VORBIS_COMMENT block
136    assert_eq!(
137        dbg!(reader.parse::<VorbisComment>().unwrap()),
138        VorbisComment {
139            vendor: "reference libFLAC 1.1.4 20070213".to_string(),
140            comment: vec![
141                "title=2ch 44100  16bit".to_string(),
142                "album=Test Album".to_string(),
143                "artist=Assorted".to_string(),
144                "tracknumber=1".to_string(),
145            ],
146        }
147    );
148}