#![warn(missing_docs)]
use std::fs;
use std::io;
use std::mem;
use std::path;
use error::fmt_err;
use frame::FrameReader;
use input::{BufferedReader, ReadBytes};
use metadata::{MetadataBlock, MetadataBlockReader, StreamInfo, VorbisComment};
mod crc;
mod error;
pub mod frame;
pub mod input;
pub mod metadata;
pub mod subframe;
pub use error::{Error, Result};
pub use frame::Block;
pub struct FlacReader<R: io::Read> {
streaminfo: StreamInfo,
vorbis_comment: Option<VorbisComment>,
input: FlacReaderState<BufferedReader<R>>,
}
enum FlacReaderState<T> {
Full(T),
MetadataOnly(T),
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct FlacReaderOptions {
pub metadata_only: bool,
pub read_vorbis_comment: bool,
}
impl Default for FlacReaderOptions {
fn default() -> FlacReaderOptions {
FlacReaderOptions {
read_vorbis_comment: true,
metadata_only: false,
}
}
}
impl FlacReaderOptions {
fn has_desired_blocks(&self) -> bool {
if !self.metadata_only {
return true
}
self.read_vorbis_comment
}
}
pub struct FlacSamples<R: ReadBytes> {
frame_reader: FrameReader<R>,
block: Block,
sample: u32,
channel: u32,
has_failed: bool,
}
fn read_stream_header<R: ReadBytes>(input: &mut R) -> Result<()> {
const FLAC_HEADER: u32 = 0x66_4c_61_43;
const ID3_HEADER: u32 = 0x49_44_33_00;
let header = try!(input.read_be_u32());
if header != FLAC_HEADER {
if (header & 0xff_ff_ff_00) == ID3_HEADER {
fmt_err("stream starts with ID3 header rather than FLAC header")
} else {
fmt_err("invalid stream header")
}
} else {
Ok(())
}
}
impl<R: io::Read> FlacReader<R> {
pub fn new(reader: R) -> Result<FlacReader<R>> {
FlacReader::new_ext(reader, FlacReaderOptions::default())
}
pub fn new_ext(reader: R, options: FlacReaderOptions) -> Result<FlacReader<R>> {
let mut buf_reader = BufferedReader::new(reader);
let mut opts_current = options;
try!(read_stream_header(&mut buf_reader));
let (streaminfo, vorbis_comment) = {
let mut metadata_iter = MetadataBlockReader::new(&mut buf_reader);
let streaminfo_block = try!(metadata_iter.next().unwrap());
let streaminfo = match streaminfo_block {
MetadataBlock::StreamInfo(info) => info,
_ => return fmt_err("streaminfo block missing"),
};
let mut vorbis_comment = None;
for block_result in metadata_iter {
match try!(block_result) {
MetadataBlock::VorbisComment(vc) => {
if vorbis_comment.is_some() {
return fmt_err("encountered second Vorbis comment block")
} else {
vorbis_comment = Some(vc);
}
opts_current.read_vorbis_comment = false;
}
MetadataBlock::StreamInfo(..) => {
return fmt_err("encountered second streaminfo block")
}
_block => {}
}
if !opts_current.has_desired_blocks() {
break
}
}
if !options.read_vorbis_comment {
vorbis_comment = None;
}
(streaminfo, vorbis_comment)
};
let state = if options.metadata_only {
FlacReaderState::MetadataOnly(buf_reader)
} else {
FlacReaderState::Full(buf_reader)
};
let flac_reader = FlacReader {
streaminfo: streaminfo,
vorbis_comment: vorbis_comment,
input: state,
};
Ok(flac_reader)
}
pub fn streaminfo(&self) -> StreamInfo {
self.streaminfo
}
pub fn vendor(&self) -> Option<&str> {
self.vorbis_comment.as_ref().map(|vc| &vc.vendor[..])
}
pub fn tags<'a>(&'a self) -> metadata::Tags<'a> {
match self.vorbis_comment.as_ref() {
Some(vc) => metadata::Tags::new(&vc.comments[..]),
None => metadata::Tags::new(&[]),
}
}
pub fn get_tag<'a>(&'a self, tag_name: &'a str) -> metadata::GetTag<'a> {
match self.vorbis_comment.as_ref() {
Some(vc) => metadata::GetTag::new(&vc.comments[..], tag_name),
None => metadata::GetTag::new(&[], tag_name),
}
}
pub fn blocks<'r>(&'r mut self) -> FrameReader<&'r mut BufferedReader<R>> {
match self.input {
FlacReaderState::Full(ref mut inp) => FrameReader::new(inp),
FlacReaderState::MetadataOnly(..) =>
panic!("FlacReaderOptions::metadata_only must be false \
to be able to use FlacReader::blocks()"),
}
}
pub fn samples<'r>(&'r mut self) -> FlacSamples<&'r mut BufferedReader<R>> {
match self.input {
FlacReaderState::Full(ref mut inp) => {
FlacSamples {
frame_reader: frame::FrameReader::new(inp),
block: Block::empty(),
sample: 0,
channel: 0,
has_failed: false,
}
}
FlacReaderState::MetadataOnly(..) => {
panic!("FlacReaderOptions::metadata_only must be false \
to be able to use FlacReader::samples()")
}
}
}
pub fn into_inner(self) -> R {
match self.input {
FlacReaderState::Full(inp) => inp.into_inner(),
FlacReaderState::MetadataOnly(inp) => inp.into_inner(),
}
}
}
impl FlacReader<fs::File> {
pub fn open<P: AsRef<path::Path>>(filename: P) -> Result<FlacReader<fs::File>> {
let file = try!(fs::File::open(filename));
FlacReader::new(file)
}
pub fn open_ext<P: AsRef<path::Path>>(filename: P,
options: FlacReaderOptions)
-> Result<FlacReader<fs::File>> {
let file = try!(fs::File::open(filename));
FlacReader::new_ext(file, options)
}
}
impl<R: ReadBytes> Iterator for FlacSamples<R> {
type Item = Result<i32>;
fn next(&mut self) -> Option<Result<i32>> {
if self.has_failed {
return None;
}
self.channel += 1;
if self.channel >= self.block.channels() {
self.channel = 0;
self.sample += 1;
if self.sample >= self.block.duration() {
self.sample = 0;
let current_block = mem::replace(&mut self.block, Block::empty());
match self.frame_reader.read_next_or_eof(current_block.into_buffer()) {
Ok(Some(next_block)) => {
self.block = next_block;
}
Ok(None) => {
return None;
}
Err(error) => {
self.has_failed = true;
return Some(Err(error));
}
}
}
}
Some(Ok(self.block.sample(self.channel, self.sample)))
}
}