use crate::{Error, Result};
use crate::frame::ChannelMode;
use crate::tables;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MpegVersion {
V1,
V2,
V2_5,
}
impl MpegVersion {
pub fn granules(self) -> usize {
match self {
MpegVersion::V1 => 2,
_ => 1,
}
}
pub fn samples_per_frame(self) -> usize {
self.granules() * crate::frame::GRANULE_LINES
}
}
#[derive(Debug, Clone)]
pub struct FrameHeader {
pub version: MpegVersion,
pub crc_protected: bool,
pub bitrate_kbps: u32,
pub sample_rate: u32,
pub padding: bool,
pub channel_mode: ChannelMode,
pub copyright: bool,
pub original: bool,
pub emphasis: u8,
}
impl FrameHeader {
pub fn parse(bytes: [u8; 4]) -> Result<FrameHeader> {
let h = u32::from_be_bytes(bytes);
if (h >> 21) & 0x7FF != 0x7FF {
return Err(Error::invalid("mp3 header: bad frame sync"));
}
let version = match (h >> 19) & 0x3 {
0b00 => MpegVersion::V2_5,
0b10 => MpegVersion::V2,
0b11 => MpegVersion::V1,
_ => return Err(Error::invalid("mp3 header: reserved MPEG version")),
};
if (h >> 17) & 0x3 != 0b01 {
return Err(Error::unsupported(
"mp3 header: only Layer III is supported",
));
}
let crc_protected = (h >> 16) & 1 == 0;
let bitrate_index = ((h >> 12) & 0xF) as usize;
if bitrate_index == 0 {
return Err(Error::unsupported("mp3 header: free-format bitrate"));
}
if bitrate_index == 15 {
return Err(Error::invalid("mp3 header: reserved bitrate index"));
}
let bitrate_kbps = match version {
MpegVersion::V1 => tables::BITRATE_V1_L3[bitrate_index],
_ => tables::BITRATE_V2_L3[bitrate_index],
};
let samplerate_index = ((h >> 10) & 0x3) as usize;
if samplerate_index == 3 {
return Err(Error::invalid("mp3 header: reserved sample-rate index"));
}
let base = tables::SAMPLE_RATE[samplerate_index];
let sample_rate = match version {
MpegVersion::V1 => base,
MpegVersion::V2 => base / 2,
MpegVersion::V2_5 => base / 4,
};
let padding = (h >> 9) & 1 == 1;
let mode_ext = ((h >> 4) & 0x3) as u8;
let channel_mode = match (h >> 6) & 0x3 {
0b00 => ChannelMode::Stereo,
0b01 => ChannelMode::JointStereo {
ms_stereo: mode_ext & 0x2 != 0,
intensity_stereo: mode_ext & 0x1 != 0,
},
0b10 => ChannelMode::DualMono,
_ => ChannelMode::Mono,
};
Ok(FrameHeader {
version,
crc_protected,
bitrate_kbps,
sample_rate,
padding,
channel_mode,
copyright: (h >> 3) & 1 == 1,
original: (h >> 2) & 1 == 1,
emphasis: (h & 0x3) as u8,
})
}
pub fn to_bytes(&self) -> [u8; 4] {
let mut h: u32 = 0x7FF << 21; let version = match self.version {
MpegVersion::V2_5 => 0b00,
MpegVersion::V2 => 0b10,
MpegVersion::V1 => 0b11,
};
h |= version << 19;
h |= 0b01 << 17; h |= (!self.crc_protected as u32) << 16;
let br_table: &[u32; 16] = match self.version {
MpegVersion::V1 => &tables::BITRATE_V1_L3,
_ => &tables::BITRATE_V2_L3,
};
let br_idx = br_table
.iter()
.position(|&b| b == self.bitrate_kbps)
.unwrap_or(0) as u32;
h |= br_idx << 12;
let base = match self.version {
MpegVersion::V1 => self.sample_rate,
MpegVersion::V2 => self.sample_rate * 2,
MpegVersion::V2_5 => self.sample_rate * 4,
};
let sr_idx = tables::SAMPLE_RATE
.iter()
.position(|&s| s == base)
.unwrap_or(0) as u32;
h |= sr_idx << 10;
h |= (self.padding as u32) << 9;
let (chan, ext) = match self.channel_mode {
ChannelMode::Stereo => (0b00, 0),
ChannelMode::JointStereo {
ms_stereo,
intensity_stereo,
} => (0b01, (ms_stereo as u32) << 1 | intensity_stereo as u32),
ChannelMode::DualMono => (0b10, 0),
ChannelMode::Mono => (0b11, 0),
};
h |= chan << 6;
h |= ext << 4;
h |= (self.copyright as u32) << 3;
h |= (self.original as u32) << 2;
h |= self.emphasis as u32;
h.to_be_bytes()
}
pub fn frame_size(&self) -> usize {
let spf = self.version.samples_per_frame();
let bytes = (spf / 8) * (self.bitrate_kbps as usize * 1000) / self.sample_rate as usize;
bytes + self.padding as usize
}
pub fn side_info_len(&self) -> usize {
let stereo = self.channel_mode.channels() == 2;
match (self.version, stereo) {
(MpegVersion::V1, true) => 32,
(MpegVersion::V1, false) => 17,
(_, true) => 17,
(_, false) => 9,
}
}
}