File: flac-metadata-write.rs

package info (click to toggle)
rust-bitstream-io 4.5.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 580 kB
  • sloc: makefile: 4
file content (144 lines) | stat: -rw-r--r-- 4,237 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
extern crate bitstream_io;

// Writing the initial STREAMINFO block to a FLAC file,
// as documented in its
// [specification](https://xiph.org/flac/format.html#stream).

use bitstream_io::{
    BigEndian, BitWrite, BitWriter, ByteWrite, ByteWriter, LittleEndian, ToBitStream,
};
use std::convert::TryInto;
use std::num::NonZero;

#[derive(Debug, PartialEq, Eq)]
struct BlockHeader {
    last_block: bool, // 1 bit
    block_type: u8,   // 7 bits
    block_size: u32,  // 24 bits
}

impl ToBitStream for BlockHeader {
    type Error = std::io::Error;

    fn to_writer<W: BitWrite + ?Sized>(&self, w: &mut W) -> std::io::Result<()> {
        w.write_bit(self.last_block)?;
        w.write::<7, _>(self.block_type)?;
        w.write::<24, _>(self.block_size)
    }
}

#[derive(Debug, PartialEq, Eq)]
struct Streaminfo {
    minimum_block_size: u16,      // 16 bits
    maximum_block_size: u16,      // 16 bits
    minimum_frame_size: u32,      // 24 bits
    maximum_frame_size: u32,      // 24 bits
    sample_rate: u32,             // 20 bits
    channels: NonZero<u8>,        // 3 bits
    bits_per_sample: NonZero<u8>, // 5 bits
    total_samples: u64,           // 36 bits
    md5: [u8; 16],                // 16 bytes
}

impl ToBitStream for Streaminfo {
    type Error = std::io::Error;

    fn to_writer<W: BitWrite + ?Sized>(&self, w: &mut W) -> std::io::Result<()> {
        w.write_from(self.minimum_block_size)?;
        w.write_from(self.maximum_block_size)?;
        w.write::<24, _>(self.minimum_frame_size)?;
        w.write::<24, _>(self.maximum_frame_size)?;
        w.write::<20, _>(self.sample_rate)?;
        w.write::<3, _>(self.channels)?;
        w.write::<5, _>(self.bits_per_sample)?;
        w.write::<36, _>(self.total_samples)?;
        w.write_bytes(&self.md5)
    }
}

#[derive(Debug, PartialEq, Eq)]
struct VorbisComment {
    vendor: String,
    comment: Vec<String>,
}

impl VorbisComment {
    fn len(&self) -> usize {
        4 + self.vendor.len() + 4 + self.comment.iter().map(|c| 4 + c.len()).sum::<usize>()
    }

    fn write<W: std::io::Write>(&self, w: &mut ByteWriter<W, LittleEndian>) -> std::io::Result<()> {
        use std::convert::TryInto;

        fn write_entry<W: std::io::Write>(
            w: &mut ByteWriter<W, LittleEndian>,
            s: &str,
        ) -> std::io::Result<()> {
            w.write::<u32>(s.len().try_into().unwrap())?;
            w.write_bytes(s.as_bytes())
        }

        write_entry(w, &self.vendor)?;
        w.write::<u32>(self.comment.len().try_into().unwrap())?;
        self.comment.iter().try_for_each(|s| write_entry(w, s))
    }
}

fn main() {
    let mut flac: Vec<u8> = Vec::new();

    let mut writer = BitWriter::endian(&mut flac, BigEndian);

    // stream marker
    writer.write_bytes(b"fLaC").unwrap();

    // metadata block header
    writer
        .build(&BlockHeader {
            last_block: false,
            block_type: 0,
            block_size: 34,
        })
        .unwrap();

    // STREAMINFO block
    writer
        .build(&Streaminfo {
            minimum_block_size: 4096,
            maximum_block_size: 4096,
            minimum_frame_size: 1542,
            maximum_frame_size: 8546,
            sample_rate: 44100,
            channels: NonZero::new(2).unwrap(),
            bits_per_sample: NonZero::new(16).unwrap(),
            total_samples: 304844,
            md5: *b"\xFA\xF2\x69\x2F\xFD\xEC\x2D\x5B\x30\x01\x76\xB4\x62\x88\x7D\x92",
        })
        .unwrap();

    let comment = VorbisComment {
        vendor: "reference libFLAC 1.1.4 20070213".to_string(),
        comment: vec![
            "title=2ch 44100  16bit".to_string(),
            "album=Test Album".to_string(),
            "artist=Assorted".to_string(),
            "tracknumber=1".to_string(),
        ],
    };

    // metadata block header
    writer
        .build(&BlockHeader {
            last_block: false,
            block_type: 4,
            block_size: comment.len().try_into().unwrap(),
        })
        .unwrap();

    // VORBIS_COMMENT block (little endian)
    comment
        .write(&mut ByteWriter::new(writer.writer().unwrap()))
        .unwrap();

    assert_eq!(flac, include_bytes!("data/metadata-only.flac"));
}