flac_metadata_write/
flac-metadata-write.rs

1extern crate bitstream_io;
2
3// Writing the initial STREAMINFO block to a FLAC file,
4// as documented in its
5// [specification](https://xiph.org/flac/format.html#stream).
6
7use bitstream_io::{
8    BigEndian, BitWrite, BitWriter, ByteWrite, ByteWriter, LittleEndian, ToBitStream,
9};
10use std::convert::TryInto;
11use std::num::NonZero;
12
13#[derive(Debug, PartialEq, Eq)]
14struct BlockHeader {
15    last_block: bool, // 1 bit
16    block_type: u8,   // 7 bits
17    block_size: u32,  // 24 bits
18}
19
20impl ToBitStream for BlockHeader {
21    type Error = std::io::Error;
22
23    fn to_writer<W: BitWrite + ?Sized>(&self, w: &mut W) -> std::io::Result<()> {
24        w.write_bit(self.last_block)?;
25        w.write::<7, _>(self.block_type)?;
26        w.write::<24, _>(self.block_size)
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 ToBitStream for Streaminfo {
44    type Error = std::io::Error;
45
46    fn to_writer<W: BitWrite + ?Sized>(&self, w: &mut W) -> std::io::Result<()> {
47        w.write_from(self.minimum_block_size)?;
48        w.write_from(self.maximum_block_size)?;
49        w.write::<24, _>(self.minimum_frame_size)?;
50        w.write::<24, _>(self.maximum_frame_size)?;
51        w.write::<20, _>(self.sample_rate)?;
52        w.write::<3, _>(self.channels)?;
53        w.write::<5, _>(self.bits_per_sample)?;
54        w.write::<36, _>(self.total_samples)?;
55        w.write_bytes(&self.md5)
56    }
57}
58
59#[derive(Debug, PartialEq, Eq)]
60struct VorbisComment {
61    vendor: String,
62    comment: Vec<String>,
63}
64
65impl VorbisComment {
66    fn len(&self) -> usize {
67        4 + self.vendor.len() + 4 + self.comment.iter().map(|c| 4 + c.len()).sum::<usize>()
68    }
69
70    fn write<W: std::io::Write>(&self, w: &mut ByteWriter<W, LittleEndian>) -> std::io::Result<()> {
71        use std::convert::TryInto;
72
73        fn write_entry<W: std::io::Write>(
74            w: &mut ByteWriter<W, LittleEndian>,
75            s: &str,
76        ) -> std::io::Result<()> {
77            w.write::<u32>(s.len().try_into().unwrap())?;
78            w.write_bytes(s.as_bytes())
79        }
80
81        write_entry(w, &self.vendor)?;
82        w.write::<u32>(self.comment.len().try_into().unwrap())?;
83        self.comment.iter().try_for_each(|s| write_entry(w, s))
84    }
85}
86
87fn main() {
88    let mut flac: Vec<u8> = Vec::new();
89
90    let mut writer = BitWriter::endian(&mut flac, BigEndian);
91
92    // stream marker
93    writer.write_bytes(b"fLaC").unwrap();
94
95    // metadata block header
96    writer
97        .build(&BlockHeader {
98            last_block: false,
99            block_type: 0,
100            block_size: 34,
101        })
102        .unwrap();
103
104    // STREAMINFO block
105    writer
106        .build(&Streaminfo {
107            minimum_block_size: 4096,
108            maximum_block_size: 4096,
109            minimum_frame_size: 1542,
110            maximum_frame_size: 8546,
111            sample_rate: 44100,
112            channels: NonZero::new(2).unwrap(),
113            bits_per_sample: NonZero::new(16).unwrap(),
114            total_samples: 304844,
115            md5: *b"\xFA\xF2\x69\x2F\xFD\xEC\x2D\x5B\x30\x01\x76\xB4\x62\x88\x7D\x92",
116        })
117        .unwrap();
118
119    let comment = VorbisComment {
120        vendor: "reference libFLAC 1.1.4 20070213".to_string(),
121        comment: vec![
122            "title=2ch 44100  16bit".to_string(),
123            "album=Test Album".to_string(),
124            "artist=Assorted".to_string(),
125            "tracknumber=1".to_string(),
126        ],
127    };
128
129    // metadata block header
130    writer
131        .build(&BlockHeader {
132            last_block: false,
133            block_type: 4,
134            block_size: comment.len().try_into().unwrap(),
135        })
136        .unwrap();
137
138    // VORBIS_COMMENT block (little endian)
139    comment
140        .write(&mut ByteWriter::new(writer.writer().unwrap()))
141        .unwrap();
142
143    assert_eq!(flac, include_bytes!("data/metadata-only.flac"));
144}