#![no_std]
mod ffi;
use core::{marker::PhantomData, mem::MaybeUninit, num::NonZeroU16, ptr, slice};
use chlorine::c_int;
pub const MAX_SAMPLES: usize = 1152 * 2;
pub enum Frame<'src, 'pcm> {
Audio(Audio<'src, 'pcm>),
Other(&'src [u8]),
}
pub struct Audio<'src, 'pcm> {
bitrate: u16,
channels: u8,
mpeg_layer: u8,
sample_count: u16,
sample_rate: u16,
src: &'src [u8],
pcm: Option<ptr::NonNull<f32>>,
phantom: PhantomData<&'pcm [f32]>,
}
unsafe impl<'src, 'pcm> Send for Audio<'src, 'pcm> {}
unsafe impl<'src, 'pcm> Sync for Audio<'src, 'pcm> {}
impl<'src, 'pcm> Audio<'src, 'pcm> {
#[inline]
pub fn bitrate(&self) -> u16 {
self.bitrate
}
#[inline]
pub fn channels(&self) -> NonZeroU16 {
unsafe { NonZeroU16::new_unchecked(self.channels as u16) }
}
#[inline]
pub fn mpeg_layer(&self) -> u8 {
self.mpeg_layer as u8
}
#[inline]
pub fn sample_count(&self) -> NonZeroU16 {
unsafe { NonZeroU16::new_unchecked(self.sample_count as u16) }
}
#[inline]
pub fn sample_rate(&self) -> NonZeroU16 {
unsafe { NonZeroU16::new_unchecked(self.sample_rate as u16) }
}
#[inline]
pub fn samples(&self) -> &'pcm [f32] {
if let Some(buf) = self.pcm {
unsafe { slice::from_raw_parts(buf.as_ptr(), usize::from(self.sample_count)) }
} else {
&[]
}
}
#[inline]
pub fn source(&self) -> &'src [u8] {
self.src
}
}
pub struct Decoder(MaybeUninit<ffi::mp3dec_t>);
impl Decoder {
#[inline]
pub fn new() -> Self {
let mut state = MaybeUninit::uninit();
unsafe { ffi::mp3dec_init(state.as_mut_ptr()) };
Self(state)
}
#[inline]
pub fn decode<'src, 'pcm>(
&mut self,
src: &'src [u8],
dest: &'pcm mut [f32; MAX_SAMPLES],
) -> Option<(Frame<'src, 'pcm>, usize)> {
self.process(src, Some(dest))
}
#[inline]
pub fn inspect<'src>(
&mut self,
src: &'src [u8],
) -> Option<(Frame<'src, 'static>, usize)> {
self.process(src, None)
}
fn process<'src, 'pcm>(
&mut self,
src: &'src [u8],
dest: Option<&'pcm mut [f32; MAX_SAMPLES]>,
) -> Option<(Frame<'src, 'pcm>, usize)> {
let Self(state) = self;
let src_c_len = src.len().min(c_int::max_value() as usize) as c_int;
let dest_ptr: *mut f32 = dest.map_or(ptr::null_mut(), |x| x).cast();
unsafe {
let mut info_recv = MaybeUninit::uninit();
let sample_count = ffi::mp3dec_decode_frame(
state.as_mut_ptr(),
src.as_ptr(),
src_c_len,
dest_ptr,
info_recv.as_mut_ptr(),
);
let info = &*info_recv.as_ptr();
if sample_count != 0 {
let audio = Audio {
bitrate: info.bitrate_kbps as u16, channels: info.channels as u8, mpeg_layer: info.layer as u8, sample_count: sample_count as u16, sample_rate: info.hz as u16,
src: frame_src(src, info),
pcm: ptr::NonNull::new(dest_ptr),
phantom: PhantomData,
};
Some((Frame::Audio(audio), info.frame_bytes as usize))
} else if info.frame_bytes != 0 {
Some((Frame::Other(frame_src(src, info)), info.frame_bytes as usize))
} else {
None
}
}
}
}
#[inline]
unsafe fn frame_src<'src>(
data: &'src [u8],
info: &ffi::mp3dec_frame_info_t,
) -> &'src [u8] {
data.get_unchecked(info.frame_offset as usize..info.frame_bytes as usize)
}