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") } } } }