use std::{
io::{BufRead, BufReader, Cursor, ErrorKind, Read},
time::Duration,
};
use crate::mp3::bitrate::Bitrate;
mod bitrate;
/// Destroy an MP3, ripping it's frames apart. Also removes any ID3v2 tags
/// because who needs metadata?
pub struct Breaker {
pub frames: Vec,
}
impl Breaker {
pub fn new() -> Self {
Self { frames: vec![] }
}
pub fn split(&mut self, data: Vec) -> Result<(), std::io::Error> {
let cursor = Cursor::new(data);
let mut reader = BufReader::new(cursor);
let mut consumed = 0;
loop {
print!("[{consumed:06X}] reading... ");
let mut three = [0x00, 0x00, 0x00];
if let Err(e) = reader.read_exact(&mut three) {
if e.kind() == ErrorKind::UnexpectedEof {
println!("out of bytes!");
break;
} else {
println!("failed!");
return Err(e);
}
}
consumed += 3;
if &three == b"ID3" {
println!("found ID3v2!");
Self::skip_id3v2(&mut reader, &mut consumed)?
} else if three[0] == 0xFF && three[1] & 0b1110_0000 == 0b1110_0000 {
print!("Have header - ");
let mut one_more = [0x00];
reader.read_exact(&mut one_more)?;
consumed += 1;
let header =
Header::from_bytes([three[0], three[1], three[2], one_more[0]]).unwrap();
let dat_len = header.data_length();
let mut data = vec![0; dat_len];
reader.read_exact(&mut data)?;
consumed += dat_len;
let frame = Frame { header, data };
println!(
"{}kbps {}kHz {:<4}bytes [{}ms]",
frame.header.bitrate.kbps().unwrap(),
frame.header.samplerate.freq() / 1000,
frame.header.length(),
frame.duration().as_millis()
);
self.frames.push(frame);
} else {
println!("unsynced!");
panic!()
}
}
Ok(())
}
/// Assumes the ident "TAG" was already consumed
fn skip_id3v2(reader: &mut R, consumed: &mut usize) -> Result<(), std::io::Error> {
// We don't actually want this, but want to get rid of it.
let mut version_and_flags = [0x00, 0x00, 0x00];
reader.read_exact(&mut version_and_flags)?;
*consumed += 3;
println!(
"Version {} Revision {}",
version_and_flags[0], version_and_flags[1]
);
let mut syncsafe_size = [0x00, 0x00, 0x00, 0x00];
reader.read_exact(&mut syncsafe_size)?;
*consumed += 4;
// Size is MSB
let mut size = syncsafe_size[3] as u32;
// Shift right eight, but back one because most significant bit is 0 due to syncsafe
size |= (syncsafe_size[2] as u32) << 7;
size |= (syncsafe_size[1] as u32) << 14;
size |= (syncsafe_size[0] as u32) << 21;
let human = if size > 1024 * 1024 {
format!("{:.2}MiB", size as f32 / (1024.0 * 1024.0))
} else if size > 1024 {
format!("{:.2}KiB", size as f32 / 1024.0)
} else {
format!("{size}B")
};
println!("ID3v2 size is {human} bytes");
// Make a vec size big. We're not here to be efficient, sorry if this dissapoint you.
let mut skip = vec![0x00; size as usize];
reader.read_exact(&mut skip)?;
*consumed += size as usize;
Ok(())
}
}
pub struct Frame {
pub header: Header,
pub data: Vec,
}
impl Frame {
/// The number of moments-in-time this frame represents. This is constant
/// and related to the [Layer]
pub fn sample_count(&self) -> usize {
// http://www.datavoyage.com/mpgscript/mpeghdr.htm
// > Frame size is the number of samples contained in a frame. It is
// > constant and always 384 samples for Layer I and 1152 samples for
// > Layer II and Layer III.
match self.header.layer {
Layer::Reserved => panic!(),
Layer::Layer1 => 384,
Layer::Layer2 | Layer::Layer3 => 1152,
}
}
/// Compute the duration of this audio frame
pub fn duration(&self) -> Duration {
let millis = (self.sample_count() * 1000) / self.header.samplerate.freq();
Duration::from_millis(millis as u64)
}
}
pub struct Header {
// I only want to parse what i need, but we need this for writing out, so
pub raw: [u8; 4],
pub version: Version,
pub layer: Layer,
pub crc: bool,
pub bitrate: Bitrate,
pub samplerate: SampleRate,
pub pad: bool,
}
impl Header {
pub fn from_bytes(raw: [u8; 4]) -> Result {
if raw[0] != 0xFF || raw[1] & 0b1110_0000 != 0b1110_0000 {
return Err(Error::HeaderUnsync);
}
//TODO: gen- yell if the version and layer aren't V1 L3?
let version = Version::from_packed(raw[1]);
let layer = Layer::from_packed(raw[1]);
// CRC is 2bytes and directly follows the frame header
let crc = raw[1] & 1 == 0;
let bitrate = Bitrate::resolve(raw[2], version, layer)?;
let samplerate = SampleRate::from_packed(raw[2]);
if let SampleRate::Reserved = samplerate {
return Err(Error::SampleRateReserve);
}
let pad = raw[2] & 2 > 0;
//TODO: gen- love, you were trying to get the size of the data field. We need
//to know the sampling rate and the pad bit for that, which happen to be the
//next three bits.
//Things i did not parse because i do not care about them:
// - private bit
// - channels
// - mode extension
// - copyright (lol)
// - original (lmfao)
// - emphasis
Ok(Self {
raw,
version,
layer,
crc,
bitrate,
samplerate,
pad,
})
}
// Algorithm taken from:
// http://www.multiweb.cz/twoinches/mp3inside.htm
/// The length of the header and data
pub fn length(&self) -> usize {
// what, do we not care about crc? won't it add 2 bytes?
let size = (144 * self.bitrate.bitrate().unwrap()) / self.samplerate.freq();
if self.pad {
size + 1
} else {
size
}
}
/// The length of the audio data. This is just the length - 4
pub fn data_length(&self) -> usize {
self.length() - 4
}
}
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("tried to parse header, but first 11 bits were not 1; not synced!")]
HeaderUnsync,
#[error("The version or the layer was a reserved value")]
BitrateReserve,
#[error("Bitrate bits were all 1")]
BitrateBad,
#[error("SampleRate was a reserved value")]
SampleRateReserve,
}
#[derive(Copy, Clone, Debug)]
pub enum Version {
Mpeg2_5,
Reserved,
Mpeg2,
Mpeg1,
}
impl Version {
/// Parse the Version from the second byte of the frame header
fn from_packed(byte: u8) -> Self {
#[allow(clippy::unusual_byte_groupings)]
match byte & 0b000_11_000 {
0b000_00_000 => Version::Mpeg2_5,
0b000_01_000 => Version::Reserved,
0b000_10_000 => Version::Mpeg2,
0b000_11_000 => Version::Mpeg1,
_ => unreachable!(),
}
}
}
#[derive(Copy, Clone, Debug)]
pub enum Layer {
Reserved,
Layer3,
Layer2,
Layer1,
}
impl Layer {
/// Parse the Layer from the second byte of the frame header.
fn from_packed(byte: u8) -> Self {
#[allow(clippy::unusual_byte_groupings)]
match byte & 0b000_00_110 {
0b000_00_000 => Layer::Reserved,
0b000_00_010 => Layer::Layer3,
0b000_00_100 => Layer::Layer2,
0b000_00_110 => Layer::Layer1,
_ => unreachable!(),
}
}
}
#[derive(Copy, Clone, Debug)]
pub enum SampleRate {
Hz44100,
Hz48000,
Hz32000,
Reserved,
}
impl SampleRate {
/// Parse the SampleRate from the third byte of the frame header
fn from_packed(byte: u8) -> Self {
#[allow(clippy::unusual_byte_groupings)]
match byte & 0b0000_11_0_0 {
0b0000_00_0_0 => SampleRate::Hz44100,
0b0000_01_0_0 => SampleRate::Hz48000,
0b0000_10_0_0 => SampleRate::Hz32000,
0b0000_11_0_0 => SampleRate::Reserved,
_ => unreachable!(),
}
}
pub fn freq(&self) -> usize {
match self {
SampleRate::Hz44100 => 44100,
SampleRate::Hz48000 => 48000,
SampleRate::Hz32000 => 32000,
SampleRate::Reserved => {
panic!("sample rate was a reserved value; unable to determien a frequency")
}
}
}
}