#![deny(missing_docs)]
#![no_std]
mod ffi;
use core::{fmt, marker::PhantomData, mem, num, 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]),
}
#[derive(Clone)]
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> {
pub fn bitrate(&self) -> u16 {
self.bitrate
}
pub fn channels(&self) -> u8 {
self.channels
}
pub fn mpeg_layer(&self) -> u8 {
self.mpeg_layer
}
pub fn sample_count(&self) -> u16 {
self.sample_count
}
pub fn sample_rate(&self) -> u16 {
self.sample_rate
}
#[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 * self.channels as u16)) }
} else {
&[]
}
}
pub fn source(&self) -> &'src [u8] {
self.src
}
}
impl fmt::Debug for Frame<'_, '_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Audio(audio) => f.debug_tuple("Audio").field(audio).finish(),
Self::Other(_) => f.debug_tuple("Other").field(&format_args!("&[...]")).finish(),
}
}
}
impl fmt::Debug for Audio<'_, '_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Audio")
.field("bitrate", &self.bitrate)
.field("channels", &self.channels)
.field("mpeg_layer", &self.mpeg_layer)
.field("sample_count", &self.sample_count)
.field("sample_rate", &self.sample_rate)
.field("samples", {
&if self.pcm.is_some() {
format_args!("&[...]")
} else {
format_args!("&[not decoded]")
}
})
.finish()
}
}
pub struct Decoder(mem::MaybeUninit<ffi::mp3dec_t>);
impl Decoder {
pub const fn new() -> Self {
Self(mem::MaybeUninit::uninit())
}
pub fn decode<'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 {
ffi::mp3dec_init(state.as_mut_ptr());
let mut info_recv = mem::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)
}
pub struct DecoderStream<'src> {
decoder: Decoder,
buffer: mem::MaybeUninit<[f32; MAX_SAMPLES]>,
base: &'src [u8], view: &'src [u8],
cache: Option<num::NonZeroUsize>, }
impl<'src> DecoderStream<'src> {
pub const fn new(src: &'src [u8]) -> Self {
Self {
decoder: Decoder::new(),
buffer: mem::MaybeUninit::uninit(),
base: src,
view: src,
cache: None,
}
}
pub fn next<'pcm>(&'pcm mut self) -> Option<Frame<'src, 'pcm>> {
self.cache = None;
unsafe {
let (frame, bytes_read) = self.decoder.decode(self.view, Some(&mut *self.buffer.as_mut_ptr()))?;
self.view = self.view.get_unchecked(bytes_read..);
Some(frame)
}
}
pub fn peek(&mut self) -> Option<Frame<'src, 'static>> {
let (frame, bytes_read) = self.decoder.decode(self.view, None)?;
self.cache = num::NonZeroUsize::new(bytes_read);
Some(frame)
}
pub fn skip(&mut self) -> Option<usize> {
let bytes_to_skip = match self.cache.take() {
Some(amount) => amount.get(),
None => self.decoder.decode(self.view, None)?.1,
};
unsafe { self.view = self.view.get_unchecked(bytes_to_skip..) };
Some(bytes_to_skip)
}
pub fn offset(&self) -> usize {
let base = self.base.as_ptr() as usize;
let view = self.view.as_ptr() as usize;
view - base
}
pub fn set_offset(&mut self, offset: usize) -> Result<(), usize> {
self.view = self.base.get(offset..).ok_or(self.base.len())?;
self.cache = None;
Ok(())
}
}
pub fn f32_to_i16_pcm(f32pcm: &[f32], i16pcm: &mut [i16]) {
assert_eq!(f32pcm.len(), i16pcm.len());
assert!(c_int::max_value() as u128 <= usize::max_value() as u128);
let mut remaining = f32pcm.len();
loop {
let batch_len = remaining.min(c_int::max_value() as usize);
unsafe { ffi::mp3dec_f32_to_s16(f32pcm.as_ptr(), i16pcm.as_mut_ptr(), batch_len as c_int) };
remaining -= batch_len;
if remaining == 0 {
break
}
}
}