1use std::{io::Read, time::Duration};
5
6use crate::{reader::Reader, PositionalError, PositionalResult};
7
8pub(crate) const FRAME_HEADER_SIZE: u8 = 4;
9pub(crate) const XING_HEADER_MIN_SIZE: u8 = 8;
10pub(crate) const XING_VBRI_HEADER_MIN_SIZE: u8 = 22; const ID3V1_FRAME_SIZE: u8 = 128;
14const ID3V2_HEADER_SIZE: u8 = 10;
15const ID3V2_FOOTER_SIZE: u8 = 10;
16const APEV2_HEADER_SIZE: u8 = 32;
17
18const HEADER_WORD_SYNC_MASK: u32 = 0xFFE0_0000;
19
20fn is_header_word_synced(header_word: u32) -> bool {
21 (header_word & HEADER_WORD_SYNC_MASK) == HEADER_WORD_SYNC_MASK
22}
23
24fn maybe_valid_header_word(header_word: u32) -> bool {
25 if version_from_header_word(header_word).is_none()
26 || layer_from_header_word(header_word).is_none()
27 || !is_valid_bitrate_bits(bitrate_bits_from_header_word(header_word))
28 || !is_valid_sample_rate_bits(sample_rate_bits_from_header_word(header_word))
29 {
30 return false;
31 }
32 if header_word & 0b11 == 0b10 {
34 return false;
35 }
36 true
37}
38
39#[derive(Clone, Copy, Debug, PartialEq, Eq)]
41pub enum Version {
42 Mpeg1 = 0,
44
45 Mpeg2 = 1,
47
48 Mpeg25 = 2,
50}
51
52const fn version_index(version: Version) -> usize {
53 version as usize
54}
55
56fn version_from_header_word(header_word: u32) -> Option<Version> {
57 match (header_word >> 19) & 0b11 {
58 0b00 => Some(Version::Mpeg25),
59 0b01 => None,
60 0b10 => Some(Version::Mpeg2),
61 0b11 => Some(Version::Mpeg1),
62 _ => unreachable!("exhaustive match on version bits not recognized by compiler"),
63 }
64}
65
66#[derive(Clone, Copy, Debug, PartialEq, Eq)]
68pub enum Layer {
69 Layer1 = 0,
71
72 Layer2 = 1,
74
75 Layer3 = 2,
77}
78
79const fn layer_index(layer: Layer) -> usize {
80 layer as usize
81}
82
83fn layer_from_header_word(header_word: u32) -> Option<Layer> {
84 match (header_word >> 17) & 0b11 {
85 0b00 => None,
86 0b01 => Some(Layer::Layer3),
87 0b10 => Some(Layer::Layer2),
88 0b11 => Some(Layer::Layer1),
89 _ => unreachable!("exhaustive match on layer bits not recognized by compiler"),
90 }
91}
92
93#[derive(Clone, Copy, Debug, PartialEq, Eq)]
95pub enum Mode {
96 Stereo = 0,
98
99 JointStereo = 1,
101
102 DualChannel = 2,
104
105 Mono = 3,
107}
108
109const fn mode_index(mode: Mode) -> usize {
110 mode as usize
111}
112
113fn mode_from_header_word(header_word: u32) -> Mode {
114 match (header_word >> 6) & 0b11 {
115 0b00 => Mode::Stereo,
116 0b01 => Mode::JointStereo,
117 0b10 => Mode::DualChannel,
118 0b11 => Mode::Mono,
119 _ => unreachable!("exhaustive match on mode bits not recognized by compiler"),
120 }
121}
122
123static BIT_RATES_KBPS: [[[u32; 15]; 3]; 3] = [
124 [
125 [
126 0, 32, 64, 96, 128, 160, 192, 224, 256, 288,
128 320, 352, 384, 416, 448,
129 ],
130 [
131 0, 32, 48, 56, 64, 80, 96, 112, 128, 160,
133 192, 224, 256, 320, 384,
134 ],
135 [
136 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160,
138 192, 224, 256, 320,
139 ],
140 ],
141 [
142 [
143 0, 32, 48, 56, 64, 80, 96, 112, 128, 144,
145 160, 176, 192, 224, 256,
146 ],
147 [
148 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,
150 112, 128, 144, 160,
151 ],
152 [
153 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,
155 112, 128, 144, 160,
156 ],
157 ],
158 [
159 [
160 0, 32, 48, 56, 64, 80, 96, 112, 128, 144,
162 160, 176, 192, 224, 256,
163 ],
164 [
165 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,
167 112, 128, 144, 160,
168 ],
169 [
170 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,
172 112, 128, 144, 160,
173 ],
174 ],
175];
176
177const BITRATE_BITS_MASK: u8 = 0b1111;
178
179fn bitrate_bits_from_header_word(header_word: u32) -> u8 {
180 ((header_word >> 12) & u32::from(BITRATE_BITS_MASK)) as u8
181}
182
183fn is_valid_bitrate_bits(bitrate_bits: u8) -> bool {
184 bitrate_bits & BITRATE_BITS_MASK < BITRATE_BITS_MASK
185}
186
187fn bitrate_bps_from_bits(version: Version, layer: Layer, bitrate_bits: u8) -> u32 {
188 debug_assert!(is_valid_bitrate_bits(bitrate_bits));
189 1000 * BIT_RATES_KBPS[version_index(version)][layer_index(layer)][bitrate_bits as usize]
190}
191
192const SAMPLE_RATES_HZ: [[u16; 3]; 3] = [
193 [44100, 48000, 32000], [22050, 24000, 16000], [11025, 12000, 8000], ];
197
198const SAMPLE_RATE_BITS_MASK: u8 = 0b11;
199
200fn sample_rate_bits_from_header_word(header_word: u32) -> u8 {
201 ((header_word >> 10) & u32::from(SAMPLE_RATE_BITS_MASK)) as u8
202}
203
204fn is_valid_sample_rate_bits(sample_rate_bits: u8) -> bool {
205 sample_rate_bits & SAMPLE_RATE_BITS_MASK < SAMPLE_RATE_BITS_MASK
206}
207
208fn sample_rate_hz_from_bits(version: Version, sample_rate_bits: u8) -> u16 {
209 debug_assert!(is_valid_sample_rate_bits(sample_rate_bits));
210 SAMPLE_RATES_HZ[version_index(version)][sample_rate_bits as usize]
211}
212
213const SAMPLE_COUNT: [[u16; 3]; 3] = [
214 [384, 1152, 1152], [384, 1152, 576], [384, 1152, 576], ];
218
219const fn sample_count(version: Version, layer: Layer) -> u16 {
220 SAMPLE_COUNT[version_index(version)][layer_index(layer)]
221}
222
223const SIDE_INFORMATION_SIZES: [[u16; 4]; 3] = [
224 [32, 32, 32, 17], [17, 17, 17, 9], [17, 17, 17, 9], ];
228
229const fn side_information_size(version: Version, mode: Mode) -> u16 {
230 SIDE_INFORMATION_SIZES[version_index(version)][mode_index(mode)]
231}
232
233#[derive(Debug, Clone)]
234pub(crate) struct FrameHeader {
235 pub(crate) version: Version,
236 pub(crate) layer: Layer,
237 pub(crate) mode: Mode,
238 pub(crate) sample_count: u16,
239 pub(crate) sample_rate_hz: u16,
240 pub(crate) bitrate_bps: Option<u32>,
241 pub(crate) frame_size: Option<u16>,
242}
243
244impl FrameHeader {
245 pub(crate) fn check_payload_size(&self, payload_size: u16) -> bool {
246 if let Some(frame_size) = self.frame_size {
247 payload_size <= frame_size
248 } else {
249 true
252 }
253 }
254}
255
256fn try_read_next_header_word<R: Read>(reader: &mut Reader<'_, R>) -> PositionalResult<Option<u32>> {
257 let mut next_byte_buf = [0u8; 1];
258 let mut initial_byte_offset = reader.position().byte_offset;
259 let mut frame_header_word = 0u32;
260 loop {
261 while !is_header_word_synced(frame_header_word) {
262 if reader.position().byte_offset - initial_byte_offset >= u64::from(FRAME_HEADER_SIZE)
263 && skip_metadata(reader, frame_header_word.to_be_bytes())?
264 {
265 if reader.position().duration == Duration::ZERO {
266 initial_byte_offset = reader.position().byte_offset;
268 frame_header_word = 0u32;
269 continue;
270 }
271 return Ok(None);
273 }
274 if !reader.try_read_exact_until_eof(&mut next_byte_buf)? {
275 return Ok(None);
276 }
277 frame_header_word = (frame_header_word << 8) | u32::from(next_byte_buf[0]);
278 }
279
280 if maybe_valid_header_word(frame_header_word) {
281 break;
282 }
283
284 if !reader.try_read_exact_until_eof(&mut next_byte_buf)? {
286 return Ok(None);
287 }
288 }
289
290 debug_assert!(is_header_word_synced(frame_header_word));
291 debug_assert!(maybe_valid_header_word(frame_header_word));
292 Ok(Some(frame_header_word))
293}
294
295pub(crate) fn skip_metadata<R: Read>(
296 reader: &mut Reader<'_, R>,
297 frame_header_bytes: [u8; FRAME_HEADER_SIZE as usize],
298) -> PositionalResult<bool> {
299 match &frame_header_bytes[..3] {
300 b"ID3" => {
301 let mut id3v2 = [0; (ID3V2_HEADER_SIZE - FRAME_HEADER_SIZE) as usize];
303 if !reader.try_read_exact_until_eof(&mut id3v2)? {
304 return Ok(true);
306 }
307 let flags = id3v2[1];
308 let footer_size = if flags & 0b0001_0000 == 0 {
309 0
310 } else {
311 u32::from(ID3V2_FOOTER_SIZE)
312 };
313 let tag_size = u32::from(id3v2[5])
315 | (u32::from(id3v2[4]) << 7)
316 | (u32::from(id3v2[3]) << 14)
317 | (u32::from(id3v2[2]) << 21);
318 reader.try_skip_exact_until_eof((tag_size + footer_size).into())?;
319 Ok(true)
320 }
321 b"TAG" => {
322 reader.try_skip_exact_until_eof((ID3V1_FRAME_SIZE - FRAME_HEADER_SIZE).into())?;
324 Ok(true)
325 }
326 b"APE" if frame_header_bytes[3] == b'T' => {
327 let mut ape_header = [0; (APEV2_HEADER_SIZE - FRAME_HEADER_SIZE) as usize];
329 if !reader.try_read_exact_until_eof(&mut ape_header)? {
330 return Ok(true);
332 }
333 if &ape_header[..4] == b"AGEX" {
334 let tag_size = u32::from_le_bytes(ape_header[8..12].try_into().expect("4 bytes"));
335 reader.try_skip_exact_until_eof(tag_size.into())?;
336 }
337 Ok(true)
338 }
339 _ => Ok(false),
340 }
341}
342
343pub(crate) type UnrecognizedFrameHeaderError = ([u8; FRAME_HEADER_SIZE as usize], PositionalError);
344
345pub(crate) type TryReadFrameHeaderOutcome =
346 std::result::Result<Option<FrameHeader>, UnrecognizedFrameHeaderError>;
347
348impl FrameHeader {
349 pub(crate) const fn channel_count(&self) -> u8 {
350 match self.mode {
351 Mode::Stereo | Mode::JointStereo | Mode::DualChannel => 2,
352 Mode::Mono => 1,
353 }
354 }
355
356 pub(crate) fn side_information_size(&self) -> u16 {
357 side_information_size(self.version, self.mode)
358 }
359
360 #[allow(clippy::panic_in_result_fn)] pub(crate) fn try_read<R: Read>(
362 reader: &mut Reader<'_, R>,
363 ) -> PositionalResult<TryReadFrameHeaderOutcome> {
364 let Some(header_word) = try_read_next_header_word(reader)? else {
365 return Ok(Ok(None));
366 };
367
368 let version = version_from_header_word(header_word).expect("valid version");
369
370 let sample_rate_hz =
371 sample_rate_hz_from_bits(version, sample_rate_bits_from_header_word(header_word));
372 debug_assert!(sample_rate_hz > 0);
373
374 let layer = layer_from_header_word(header_word).expect("valid layer");
375
376 let bitrate_bps =
377 bitrate_bps_from_bits(version, layer, bitrate_bits_from_header_word(header_word));
378
379 let sample_count = sample_count(version, layer);
380
381 let mode = mode_from_header_word(header_word);
382
383 let padding = (header_word >> 9) & 0b1;
384
385 let frame_size = if layer == Layer::Layer1 {
386 (12 * bitrate_bps / u32::from(sample_rate_hz) + padding) * 4
387 } else {
388 u32::from(sample_count) * (bitrate_bps / 8) / u32::from(sample_rate_hz) + padding
389 };
390 debug_assert!(frame_size <= u16::MAX.into());
391 let frame_size = frame_size as u16;
392
393 Ok(Ok(Some(Self {
394 version,
395 layer,
396 mode,
397 sample_rate_hz,
398 sample_count,
399 bitrate_bps: (bitrate_bps > 0).then_some(bitrate_bps),
400 frame_size: (frame_size > 0).then_some(frame_size),
401 })))
402 }
403}