#![doc(html_root_url = "http://www.rust-ci.org/tari/vorbisfile.rs/doc/libvorbisfile/")]
#![deny(dead_code, missing_docs)]
extern crate libc;
use libc::{c_void, c_int, c_long, size_t};
use std::error::Error;
use std::ffi::CStr;
use std::fmt;
use std::io::Read;
use std::mem;
use std::str;
use std::ptr;
use std::slice::from_raw_parts_mut;
#[allow(dead_code, non_snake_case)]
mod ffi;
pub type OVResult<T> = Result<T, OVError>;
#[derive(Debug, Clone)]
pub enum OVError {
EndOfStream,
StreamInterrupted,
ReadError,
InternalFault,
NotImplemented,
InvalidArgument,
NotVorbis,
InvalidHeader,
UnsupportedVersion,
CorruptLink,
NotSeekable,
}
impl Error for OVError {
fn description(&self) -> &str {
match *self {
OVError::EndOfStream => "End of stream",
OVError::StreamInterrupted => "Stream interrupted",
OVError::ReadError => "Read error",
OVError::InternalFault => "Internal library fault",
OVError::NotImplemented => "Feature not implemented",
OVError::InvalidArgument => "Invalid argument",
OVError::NotVorbis => "Not a Vorbis stream",
OVError::InvalidHeader => "Invalid Vorbis header",
OVError::UnsupportedVersion => "Bitstream format revision not supported",
OVError::CorruptLink => "Vorbis link is corrupt",
OVError::NotSeekable => "Not seekable",
}
}
}
impl fmt::Display for OVError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
f.write_str(self.description())
}
}
impl Copy for OVError { }
impl OVError {
fn from_native(code: c_int) -> OVError {
match code {
ffi::OV_HOLE => OVError::StreamInterrupted,
ffi::OV_EREAD => OVError::ReadError,
ffi::OV_EFAULT => OVError::InternalFault,
ffi::OV_EIMPL => OVError::NotImplemented,
ffi::OV_EINVAL => OVError::InvalidArgument,
ffi::OV_ENOTVORBIS => OVError::NotVorbis,
ffi::OV_EBADHEADER => OVError::InvalidHeader,
ffi::OV_EVERSION => OVError::UnsupportedVersion,
ffi::OV_EBADLINK => OVError::CorruptLink,
ffi::OV_ENOSEEK => OVError::NotSeekable,
x => panic!("Unexpected OVError code: {}", x)
}
}
}
pub struct VorbisFile<R: Read> {
src: R,
decoder: ffi::OggVorbis_File,
channels: Vec<&'static [f32]>,
}
pub struct Comments<'a> {
pub vendor: &'a str,
pub comments: Vec<&'a str>
}
#[allow(unused_variables)]
extern "C" fn seek(datasource: *mut c_void, offset: i64, whence: c_int) -> c_int {
-1
}
#[allow(unused_variables)]
extern "C" fn close(datasource: *mut c_void) -> c_int {
0
}
#[allow(unused_variables)]
extern "C" fn tell(datasource: *mut c_void) -> c_long {
-1
}
impl<R: Read> VorbisFile<R> {
fn callback_setup(&mut self) {
let ds = self as *mut _ as *mut c_void;
self.decoder.datasource = ds;
}
pub fn new(src: R) -> OVResult<VorbisFile<R>> {
let mut vf = VorbisFile {
src: src,
decoder: unsafe { mem::uninitialized() },
channels: Vec::new()
};
let callbacks = ffi::ov_callbacks {
read: VorbisFile::<R>::read,
seek: seek,
tell: tell,
close: close,
};
let status = unsafe {
ffi::ov_open_callbacks(&mut vf.src as *mut _ as *mut c_void,
&mut vf.decoder,
ptr::null_mut(), 0, callbacks)
};
match status {
0 => Ok(vf),
f => {
mem::forget(vf);
Err(OVError::from_native(f))
}
}
}
pub fn comment<'a>(&'a mut self, link: isize) -> Option<Comments<'a>> {
let cm = unsafe {
let p = ffi::ov_comment(&mut self.decoder, link as c_int);
if p.is_null() {
return None;
} else {
p
}
};
unsafe fn make_str<'a>(data: *const u8, len: usize) -> Option<&'a str> {
let slice = std::slice::from_raw_parts(data, len);
str::from_utf8(slice).ok()
}
Some(Comments {
vendor: unsafe {
match str::from_utf8(CStr::from_ptr((*cm).vendor).to_bytes()) {
Ok(x) => x,
Err(_) => "<INVALID UTF-8>"
}
},
comments: unsafe {
let mut v = Vec::with_capacity((*cm).comments as usize);
for i in 0..(*cm).comments {
let len = *(*cm).comment_lengths.offset(i as isize);
match make_str(*(*cm).user_comments.offset(i as isize) as *const _,
len as usize) {
Some(s) => {
v.push(s);
}
None => {
}
}
}
v
}
})
}
pub fn decode<'a>(&'a mut self) -> OVResult<&'a mut [&'a mut [f32]]> {
let max_samples = 4096;
self.callback_setup();
let mut sample_buffer: *mut *mut f32 = unsafe {
mem::uninitialized()
};
let mut bitstream_idx: c_int = unsafe {
mem::uninitialized()
};
let n_samples = unsafe {
match ffi::ov_read_float(&mut self.decoder, &mut sample_buffer,
max_samples, &mut bitstream_idx) {
0 => {
return Err(OVError::EndOfStream);
}
x if x < 0 => {
return Err(OVError::from_native(x as c_int));
}
x => x
}
};
let n_channels = unsafe {
(*ffi::ov_info(&mut self.decoder, bitstream_idx)).channels
};
self.channels.truncate(0);
for i in 0..n_channels {
unsafe {
let channel_buffer = *sample_buffer.offset(i as isize);
let channel_slice = std::slice::from_raw_parts(channel_buffer, n_samples as usize);
self.channels.push(channel_slice);
};
}
Ok(unsafe {
mem::transmute(&mut self.channels[..])
})
}
extern "C" fn read(buffer: *mut c_void, size: size_t, nmemb: size_t,
datasource: *mut c_void) -> size_t {
let vf: *mut VorbisFile<R> = unsafe { mem::transmute(datasource) };
let ptr = buffer as *mut u8;
for i in 0..nmemb {
let more = unsafe {
let bufp = ptr.offset(i as isize);
let buf = from_raw_parts_mut(bufp, size as usize);
match (*vf).src.by_ref().take(size).read(buf) {
Ok(n) if n == (size as usize) => true,
Ok(0) | Err(_) => false,
Ok(n) => {
VorbisFile::<R>::read(bufp as *mut _, size - n as size_t, 1, datasource);
true
}
}
};
if !more {
return i;
}
}
return nmemb;
}
}
impl<R: Read> Drop for VorbisFile<R> {
fn drop(&mut self) {
self.callback_setup();
unsafe {
ffi::ov_clear(&mut self.decoder);
}
}
}