use core::slice;
use std::{
ffi::c_void,
io::Read,
marker::PhantomData,
mem::MaybeUninit,
num::{NonZeroU32, NonZeroU8},
os::raw::c_int,
ptr
};
use aotuv_lancer_vorbis_sys::{
ov_callbacks, ov_clear, ov_open_callbacks, ov_read_float, OggVorbis_File
};
use errno::{set_errno, Errno};
use crate::{common::VorbisError, decoder::VorbisAudioSamples};
pub struct VorbisDecoder<R: Read> {
ogg_vorbis_file: OggVorbis_File,
source: PhantomData<R>,
last_audio_block: Option<VorbisAudioSamples>
}
impl<R: Read> VorbisDecoder<R> {
pub fn new<S: Into<Box<R>>>(source: S) -> Result<Self, VorbisError> {
let mut ogg_vorbis_file = MaybeUninit::uninit();
let source = Box::into_raw(source.into());
unsafe {
match vorbisfile_return_value_to_result!(ov_open_callbacks(
source.cast(),
ogg_vorbis_file.as_mut_ptr(),
ptr::null(),
0,
ov_callbacks {
read_func: {
unsafe extern "C" fn read_func<R: Read>(
ptr: *mut c_void,
size: usize,
count: usize,
datasource: *mut c_void
) -> usize {
let source = &mut *(datasource.cast::<R>());
let buf = slice::from_raw_parts_mut(ptr.cast(), size * count);
match source.read(buf) {
Ok(n) => n / size,
Err(err) => {
set_errno(Errno(err.raw_os_error().unwrap_or(i32::MAX)));
0
}
}
}
Some(read_func::<R>)
},
seek_func: None,
close_func: {
unsafe extern "C" fn close_func<R: Read>(datasource: *mut c_void) -> c_int {
drop(Box::from_raw(datasource.cast::<R>()));
0
}
Some(close_func::<R>)
},
tell_func: None
}
)) {
Ok(_) => Ok(Self {
ogg_vorbis_file: ogg_vorbis_file.assume_init(),
source: PhantomData,
last_audio_block: None
}),
Err(err) => {
drop(Box::from_raw(source));
Err(err)
}
}
}
}
pub fn decode_audio_block(&mut self) -> Result<Option<&VorbisAudioSamples>, VorbisError> {
let mut current_bitstream = MaybeUninit::uninit();
let mut sample_buf = MaybeUninit::uninit();
unsafe {
let samples_read = vorbisfile_return_value_to_result!(ov_read_float(
&mut self.ogg_vorbis_file,
sample_buf.as_mut_ptr(),
2048, current_bitstream.as_mut_ptr()
))?;
if samples_read > 0 {
if current_bitstream.assume_init() != 0 {
return Err(VorbisError::UnsupportedStreamChaining);
}
self.last_audio_block = Some(VorbisAudioSamples::new(
sample_buf.assume_init(),
(*self.ogg_vorbis_file.vi).channels as usize,
samples_read as usize
));
Ok(self.last_audio_block.as_ref())
} else {
Ok(None)
}
}
}
pub fn channels(&self) -> NonZeroU8 {
unsafe { NonZeroU8::new_unchecked((*self.ogg_vorbis_file.vi).channels as u8) }
}
pub fn sampling_frequency(&self) -> NonZeroU32 {
unsafe { NonZeroU32::new_unchecked((*self.ogg_vorbis_file.vi).rate as u32) }
}
}
impl<R: Read> Drop for VorbisDecoder<R> {
fn drop(&mut self) {
unsafe { ov_clear(&mut self.ogg_vorbis_file) };
}
}
#[cfg(test)]
mod test {
use std::io::{self, ErrorKind, Read};
use super::VorbisDecoder;
use crate::{VorbisError, VorbisLibraryError, VorbisLibraryErrorKind};
#[test]
fn decoder_handles_io_failures() {
struct ErrorRead;
impl Read for ErrorRead {
fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
Err(ErrorKind::Other.into())
}
}
let result = VorbisDecoder::new(ErrorRead);
assert!(
matches!(
result,
Err(VorbisError::LibraryError(VorbisLibraryError {
kind: VorbisLibraryErrorKind::Io | VorbisLibraryErrorKind::NotVorbis,
..
}))
),
"Expected I/O library error, got: {:?}",
result.err()
);
}
}