1use crate::{Error, Result};
13
14use crate::frame::ChannelMode;
15use crate::tables;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum MpegVersion {
20 V1,
22 V2,
24 V2_5,
26}
27
28impl MpegVersion {
29 pub fn granules(self) -> usize {
31 match self {
32 MpegVersion::V1 => 2,
33 _ => 1,
34 }
35 }
36 pub fn samples_per_frame(self) -> usize {
38 self.granules() * crate::frame::GRANULE_LINES
39 }
40}
41
42#[derive(Debug, Clone)]
44pub struct FrameHeader {
45 pub version: MpegVersion,
46 pub crc_protected: bool,
47 pub bitrate_kbps: u32,
48 pub sample_rate: u32,
49 pub padding: bool,
50 pub channel_mode: ChannelMode,
51 pub copyright: bool,
52 pub original: bool,
53 pub emphasis: u8,
54}
55
56impl FrameHeader {
57 pub fn parse(bytes: [u8; 4]) -> Result<FrameHeader> {
60 let h = u32::from_be_bytes(bytes);
61
62 if (h >> 21) & 0x7FF != 0x7FF {
64 return Err(Error::invalid("mp3 header: bad frame sync"));
65 }
66 let version = match (h >> 19) & 0x3 {
67 0b00 => MpegVersion::V2_5,
68 0b10 => MpegVersion::V2,
69 0b11 => MpegVersion::V1,
70 _ => return Err(Error::invalid("mp3 header: reserved MPEG version")),
71 };
72 if (h >> 17) & 0x3 != 0b01 {
74 return Err(Error::unsupported(
75 "mp3 header: only Layer III is supported",
76 ));
77 }
78 let crc_protected = (h >> 16) & 1 == 0;
79
80 let bitrate_index = ((h >> 12) & 0xF) as usize;
81 if bitrate_index == 0 {
82 return Err(Error::unsupported("mp3 header: free-format bitrate"));
83 }
84 if bitrate_index == 15 {
85 return Err(Error::invalid("mp3 header: reserved bitrate index"));
86 }
87 let bitrate_kbps = match version {
88 MpegVersion::V1 => tables::BITRATE_V1_L3[bitrate_index],
89 _ => tables::BITRATE_V2_L3[bitrate_index],
90 };
91
92 let samplerate_index = ((h >> 10) & 0x3) as usize;
93 if samplerate_index == 3 {
94 return Err(Error::invalid("mp3 header: reserved sample-rate index"));
95 }
96 let base = tables::SAMPLE_RATE[samplerate_index];
98 let sample_rate = match version {
99 MpegVersion::V1 => base,
100 MpegVersion::V2 => base / 2,
101 MpegVersion::V2_5 => base / 4,
102 };
103
104 let padding = (h >> 9) & 1 == 1;
105 let mode_ext = ((h >> 4) & 0x3) as u8;
106 let channel_mode = match (h >> 6) & 0x3 {
107 0b00 => ChannelMode::Stereo,
108 0b01 => ChannelMode::JointStereo {
110 ms_stereo: mode_ext & 0x2 != 0,
111 intensity_stereo: mode_ext & 0x1 != 0,
112 },
113 0b10 => ChannelMode::DualMono,
114 _ => ChannelMode::Mono,
115 };
116
117 Ok(FrameHeader {
118 version,
119 crc_protected,
120 bitrate_kbps,
121 sample_rate,
122 padding,
123 channel_mode,
124 copyright: (h >> 3) & 1 == 1,
125 original: (h >> 2) & 1 == 1,
126 emphasis: (h & 0x3) as u8,
127 })
128 }
129
130 pub fn to_bytes(&self) -> [u8; 4] {
132 let mut h: u32 = 0x7FF << 21; let version = match self.version {
134 MpegVersion::V2_5 => 0b00,
135 MpegVersion::V2 => 0b10,
136 MpegVersion::V1 => 0b11,
137 };
138 h |= version << 19;
139 h |= 0b01 << 17; h |= (!self.crc_protected as u32) << 16;
141
142 let br_table: &[u32; 16] = match self.version {
143 MpegVersion::V1 => &tables::BITRATE_V1_L3,
144 _ => &tables::BITRATE_V2_L3,
145 };
146 let br_idx = br_table
147 .iter()
148 .position(|&b| b == self.bitrate_kbps)
149 .unwrap_or(0) as u32;
150 h |= br_idx << 12;
151
152 let base = match self.version {
153 MpegVersion::V1 => self.sample_rate,
154 MpegVersion::V2 => self.sample_rate * 2,
155 MpegVersion::V2_5 => self.sample_rate * 4,
156 };
157 let sr_idx = tables::SAMPLE_RATE
158 .iter()
159 .position(|&s| s == base)
160 .unwrap_or(0) as u32;
161 h |= sr_idx << 10;
162 h |= (self.padding as u32) << 9;
163
164 let (chan, ext) = match self.channel_mode {
165 ChannelMode::Stereo => (0b00, 0),
166 ChannelMode::JointStereo {
167 ms_stereo,
168 intensity_stereo,
169 } => (0b01, (ms_stereo as u32) << 1 | intensity_stereo as u32),
170 ChannelMode::DualMono => (0b10, 0),
171 ChannelMode::Mono => (0b11, 0),
172 };
173 h |= chan << 6;
174 h |= ext << 4;
175 h |= (self.copyright as u32) << 3;
176 h |= (self.original as u32) << 2;
177 h |= self.emphasis as u32;
178 h.to_be_bytes()
179 }
180
181 pub fn frame_size(&self) -> usize {
185 let spf = self.version.samples_per_frame();
186 let bytes = (spf / 8) * (self.bitrate_kbps as usize * 1000) / self.sample_rate as usize;
187 bytes + self.padding as usize
188 }
189
190 pub fn side_info_len(&self) -> usize {
193 let stereo = self.channel_mode.channels() == 2;
194 match (self.version, stereo) {
195 (MpegVersion::V1, true) => 32,
196 (MpegVersion::V1, false) => 17,
197 (_, true) => 17,
198 (_, false) => 9,
199 }
200 }
201}