#![deny(
clippy::all,
clippy::cargo,
clippy::nursery,
clippy::must_use_candidate,
// clippy::restriction,
// clippy::pedantic
)]
#![allow(
clippy::suboptimal_flops,
clippy::redundant_pub_crate,
clippy::fallible_impl_from
)]
#![deny(missing_debug_implementations)]
#![deny(rustdoc::all)]
#![no_std]
extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
pub use error::Error;
pub use minimp3_sys as ffi;
use alloc::boxed::Box;
use alloc::vec::Vec;
use core::mem;
use slice_ring_buffer::SliceRingBuffer;
#[cfg(feature = "std")]
use std::io;
mod error;
pub const MAX_SAMPLES_PER_FRAME: usize = ffi::MINIMP3_MAX_SAMPLES_PER_FRAME as usize;
const BUFFER_SIZE: usize = MAX_SAMPLES_PER_FRAME * 15;
const REFILL_TRIGGER: usize = MAX_SAMPLES_PER_FRAME * 8;
#[derive(Debug)]
pub struct Decoder<R> {
reader: R,
buffer: SliceRingBuffer<u8>,
buffer_refill: Box<[u8; MAX_SAMPLES_PER_FRAME * 5]>,
decoder: Box<ffi::mp3dec_t>,
}
unsafe impl<R: Send> Send for Decoder<R> {}
#[derive(Debug, Clone)]
pub struct Frame {
pub data: Vec<i16>,
pub sample_rate: i32,
pub channels: usize,
pub layer: usize,
pub bitrate: i32,
}
impl<R> Decoder<R> {
pub fn new(reader: R) -> Self {
let mut minidec = unsafe { Box::new(mem::zeroed()) };
unsafe { ffi::mp3dec_init(&mut *minidec) }
Self {
reader,
buffer: SliceRingBuffer::with_capacity(BUFFER_SIZE),
buffer_refill: Box::new([0; MAX_SAMPLES_PER_FRAME * 5]),
decoder: minidec,
}
}
pub const fn reader(&self) -> &R {
&self.reader
}
pub const fn reader_mut(&mut self) -> &mut R {
&mut self.reader
}
pub fn into_inner(self) -> R {
self.reader
}
fn decode_frame(&mut self) -> Result<Frame, Error> {
let mut frame_info = unsafe { mem::zeroed() };
let mut pcm = Vec::with_capacity(MAX_SAMPLES_PER_FRAME);
let samples: usize = unsafe {
ffi::mp3dec_decode_frame(
&mut *self.decoder,
self.buffer.as_ptr(),
self.buffer.len() as _,
pcm.as_mut_ptr(),
&mut frame_info,
) as _
};
if samples > 0 {
unsafe {
pcm.set_len(samples * frame_info.channels as usize);
}
}
let frame = Frame {
data: pcm,
sample_rate: frame_info.hz,
channels: frame_info.channels as usize,
layer: frame_info.layer as usize,
bitrate: frame_info.bitrate_kbps,
};
let current_len = self.buffer.len();
self.buffer
.truncate_front(current_len - frame_info.frame_bytes as usize);
if samples == 0 {
if frame_info.frame_bytes > 0 {
Err(Error::SkippedData)
} else {
Err(Error::InsufficientData)
}
} else {
Ok(frame)
}
}
}
#[cfg(feature = "async_tokio")]
impl<R: tokio::io::AsyncRead + std::marker::Unpin> Decoder<R> {
pub async fn next_frame_future(&mut self) -> Result<Frame, Error> {
loop {
let bytes_read = if self.buffer.len() < REFILL_TRIGGER {
Some(self.refill_future().await?)
} else {
None
};
match self.decode_frame() {
Ok(frame) => return Ok(frame),
Err(Error::InsufficientData) | Err(Error::SkippedData) => {
if bytes_read == Some(0) {
return Err(Error::Eof);
}
}
Err(e) => return Err(e),
}
}
}
async fn refill_future(&mut self) -> Result<usize, io::Error> {
use tokio::io::AsyncReadExt;
let read_bytes = self.reader.read(&mut self.buffer_refill[..]).await?;
self.buffer.extend(self.buffer_refill[..read_bytes].iter());
Ok(read_bytes)
}
}
#[cfg(feature = "std")]
impl<R: io::Read> Decoder<R> {
pub fn next_frame(&mut self) -> Result<Frame, Error> {
loop {
let bytes_read = if self.buffer.len() < REFILL_TRIGGER {
Some(self.refill()?)
} else {
None
};
match self.decode_frame() {
Ok(frame) => return Ok(frame),
Err(Error::InsufficientData) | Err(Error::SkippedData) => {
if bytes_read == Some(0) {
return Err(Error::Eof);
}
}
Err(e) => return Err(e),
}
}
}
fn refill(&mut self) -> Result<usize, io::Error> {
let read_bytes = self.reader.read(&mut self.buffer_refill[..])?;
self.buffer.extend(self.buffer_refill[..read_bytes].iter());
Ok(read_bytes)
}
}