use crate::ffi::{
CriticalBandInfo, DequantInfo, FrameHeader, HuffmanInfo, IMDCTInfo, MP3DecInfo,
ScaleFactorInfo, ScaleFactorInfoSub, ScaleFactorJS, SideInfo, SideInfoSub, SubbandInfo,
};
use core::ffi::c_void;
pub use crate::ffi::_MP3FrameInfo as MP3FrameInfo;
#[derive(Debug)]
pub struct Id3v2Flags {
pub unsynchronisation: bool,
pub extended_header: bool,
pub experimental: bool,
pub footer_present: bool,
}
#[derive(PartialEq, Eq, Debug)]
pub enum Id3v2Version {
ID3v2_0,
ID3v2_1,
ID3v2_2,
ID3v2_3,
ID3v2_4,
Invalid,
}
#[derive(Debug)]
pub struct Id3v2 {
pub version: Id3v2Version,
pub flags: Id3v2Flags,
pub size: usize,
}
impl MP3FrameInfo {
pub fn new() -> MP3FrameInfo {
MP3FrameInfo {
bitrate: 0,
nChans: 0,
samprate: 0,
bitsPerSample: 0,
outputSamps: 0,
layer: 0,
version: 0,
size: 0,
}
}
}
impl Default for MP3FrameInfo {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
pub enum DecodeErr {
Okay,
InDataUnderflow,
MaindataUnderfow,
FreeBitrateSync,
OutOfMemory,
NullPointer,
InvalidFrameheader,
InvalidSideinfo,
InvalidScalefact,
InvalidHuffcodes,
InvalidDequantize,
InvalidImdct,
InvalidSubband,
Unknown,
InvalidError,
}
impl From<i32> for DecodeErr {
fn from(value: i32) -> Self {
use DecodeErr::*;
match value {
0 => Okay,
-1 => InDataUnderflow,
-2 => MaindataUnderfow,
-3 => FreeBitrateSync,
-4 => OutOfMemory,
-5 => NullPointer,
-6 => InvalidFrameheader,
-7 => InvalidSideinfo,
-8 => InvalidScalefact,
-9 => InvalidHuffcodes,
-10 => InvalidDequantize,
-11 => InvalidImdct,
-12 => InvalidSubband,
-9999 => Unknown,
_ => InvalidError,
}
}
}
#[allow(dead_code)]
#[derive(Debug)]
pub struct Mp3 {
mp3_dec_info: MP3DecInfo,
}
impl Mp3 {
pub const fn new() -> Self {
let mp3_dec_info = MP3DecInfo {
mainBuf: [0; 1940],
freeBitrateFlag: 0,
freeBitrateSlots: 0,
bitrate: 0,
nChans: 0,
samprate: 0,
nGrans: 0,
nGranSamps: 0,
nSlots: 0,
layer: 0,
version: 0,
size: 0,
mainDataBegin: 0,
mainDataBytes: 0,
part23Length: [[0; 2]; 2],
di: DequantInfo {
workBuf: [0; 198],
cbi: [CriticalBandInfo {
cbType: 0,
cbEndS: [0; 3],
cbEndSMax: 0,
cbEndL: 0,
}; 2],
},
fh: FrameHeader {
ver: 0,
layer: 0,
crc: 0,
brIdx: 0,
srIdx: 0,
paddingBit: 0,
privateBit: 0,
sMode: 0,
modeExt: 0,
copyFlag: 0,
origFlag: 0,
emphasis: 0,
CRCWord: 0,
},
si: SideInfo {
mainDataBegin: 0,
privateBits: 0,
scfsi: [[0; 4]; 2],
sis: [[SideInfoSub {
part23Length: 0,
nBigvals: 0,
globalGain: 0,
sfCompress: 0,
winSwitchFlag: 0,
blockType: 0,
mixedBlock: 0,
tableSelect: [0; 3],
subBlockGain: [0; 3],
region0Count: 0,
region1Count: 0,
preFlag: 0,
sfactScale: 0,
count1TableSelect: 0,
}; 2]; 2],
},
sfi: ScaleFactorInfo {
sfis: [[ScaleFactorInfoSub {
l: [0; 23],
s: [[0; 3]; 13],
}; 2]; 2],
sfjs: ScaleFactorJS {
intensityScale: 0,
slen: [0; 4],
nr: [0; 4],
}, },
hi: HuffmanInfo {
huffDecBuf: [[0; 576]; 2],
nonZeroBound: [0; 2],
gb: [0; 2],
},
mi: IMDCTInfo {
outBuf: [[[0; 32]; 18]; 2],
overBuf: [[0; 288]; 2],
numPrevIMDCT: [0; 2],
prevType: [0; 2],
prevWinSwitch: [0; 2],
gb: [0; 2],
},
sbi: SubbandInfo {
vbuf: [0; 2176],
vindex: 0,
},
};
Self { mp3_dec_info }
}
pub fn find_sync_word(mp3buf: &[u8]) -> i32 {
unsafe { crate::ffi::MP3FindSyncWord(mp3buf.as_ptr(), mp3buf.len() as i32) }
}
pub fn get_last_frame_info(&mut self) -> MP3FrameInfo {
let mut frame = MP3FrameInfo::new();
unsafe { crate::ffi::MP3GetLastFrameInfo(self.ptr(), &mut frame) };
frame
}
pub fn get_next_frame_info(&mut self, mp3buf: &[u8]) -> Result<MP3FrameInfo, DecodeErr> {
let mut frame = MP3FrameInfo::new();
let err =
unsafe { crate::ffi::MP3GetNextFrameInfo(self.ptr(), &mut frame, mp3buf.as_ptr()) };
if err == 0 {
Ok(frame)
} else {
Err(err.into())
}
}
pub fn decode(
&mut self,
mp3buf: &[u8],
newlen: i32,
buf: &mut [i16],
) -> Result<i32, DecodeErr> {
let mut newlen = newlen;
let err = unsafe {
crate::ffi::MP3Decode(
self.ptr(),
&mut mp3buf.as_ptr(),
&mut newlen,
buf.as_mut_ptr(),
0,
)
};
if err == 0 {
Ok(newlen)
} else {
Err(err.into())
}
}
pub fn find_id3v2(mp3buf: &[u8]) -> Option<(usize, Id3v2)> {
let window = mp3buf.windows(10);
for (offset, slice) in window.enumerate() {
if let [b'I', b'D', b'3', major, minor, flags, s1, s2, s3, s4] = slice {
let version = match (major, minor) {
(2, 2) => Id3v2Version::ID3v2_2,
(2, 3) => Id3v2Version::ID3v2_3,
(2, 4) => Id3v2Version::ID3v2_4,
(_, _) => Id3v2Version::Invalid,
};
let id3v2_flags = Id3v2Flags {
unsynchronisation: flags & 0b1000_0000 == 0b1000_0000,
extended_header: flags & 0b0100_0000 == 0b0100_0000,
experimental: flags & 0b0010_0000 == 0b0010_0000,
footer_present: flags & 0b0001_0000 == 0b0001_0000,
};
let valid_flags = flags & 0b0000_1111 != 0b0000_1111;
let valid_syncsafe = (s1 | s2 | s3 | s4) & 0b1000_0000 != 0b1000_0000;
if version == Id3v2Version::Invalid && valid_syncsafe && valid_flags {
let (s1, s2, s3, s4) = (*s1 as usize, *s2 as usize, *s3 as usize, *s4 as usize);
let size = s4 | s3 << 7 | s2 << 14 | s1 << 21;
return Some((
offset,
Id3v2 {
version,
flags: id3v2_flags,
size,
},
));
}
}
}
None
}
pub unsafe fn ptr(&mut self) -> *mut c_void {
core::ptr::addr_of_mut!(self.mp3_dec_info) as *mut c_void
}
}
impl Default for Mp3 {
fn default() -> Self {
Self::new()
}
}