blob: 9c9a9a40db68c2a4ba37e8e5fcb0bce7f1b09001 [file] [log] [blame] [edit]
// Hound -- A wav encoding and decoding library in Rust
// Copyright (C) 2015 Ruud van Asseldonk
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// A copy of the License has been included in the root of the repository.
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::cmp;
use std::fs;
use std::io;
use std::marker;
use std::path;
use super::{Error, Result, Sample, SampleFormat, WavSpec, WavSpecEx};
/// Extends the functionality of `io::Read` with additional methods.
///
/// The methods may be used on any type that implements `io::Read`.
pub trait ReadExt: io::Read {
/// Reads as many bytes as `buf` is long.
///
/// This may issue multiple `read` calls internally. An error is returned
/// if `read` read 0 bytes before the buffer is full.
// TODO: There is an RFC proposing a method like this for the standard library.
fn read_into(&mut self, buf: &mut [u8]) -> io::Result<()>;
/// Reads 4 bytes and returns them in an array.
fn read_4_bytes(&mut self) -> io::Result<[u8; 4]>;
/// Skip over `n` bytes.
fn skip_bytes(&mut self, n: usize) -> io::Result<()>;
/// Reads a single byte and interprets it as an 8-bit signed integer.
fn read_i8(&mut self) -> io::Result<i8>;
/// Reads a single byte and interprets it as an 8-bit unsigned integer.
fn read_u8(&mut self) -> io::Result<u8>;
/// Reads two bytes and interprets them as a little-endian 16-bit signed integer.
fn read_le_i16(&mut self) -> io::Result<i16>;
/// Reads two bytes and interprets them as a little-endian 16-bit unsigned integer.
fn read_le_u16(&mut self) -> io::Result<u16>;
/// Reads three bytes and interprets them as a little-endian 24-bit signed integer.
///
/// The sign bit will be extended into the most significant byte.
fn read_le_i24(&mut self) -> io::Result<i32>;
/// Reads four bytes and interprets them as a little-endian 24-bit signed integer.
///
/// The sign bit will be extended into the most significant byte.
fn read_le_i24_4(&mut self) -> io::Result<i32>;
/// Reads three bytes and interprets them as a little-endian 24-bit unsigned integer.
///
/// The most significant byte will be 0.
fn read_le_u24(&mut self) -> io::Result<u32>;
/// Reads four bytes and interprets them as a little-endian 32-bit signed integer.
fn read_le_i32(&mut self) -> io::Result<i32>;
/// Reads four bytes and interprets them as a little-endian 32-bit unsigned integer.
fn read_le_u32(&mut self) -> io::Result<u32>;
/// Reads four bytes and interprets them as a little-endian 32-bit IEEE float.
fn read_le_f32(&mut self) -> io::Result<f32>;
}
impl<R> ReadExt for R
where R: io::Read
{
#[inline(always)]
fn read_into(&mut self, buf: &mut [u8]) -> io::Result<()> {
let mut n = 0;
while n < buf.len() {
let progress = try!(self.read(&mut buf[n..]));
if progress > 0 {
n += progress;
} else {
return Err(io::Error::new(io::ErrorKind::Other, "Failed to read enough bytes."));
}
}
Ok(())
}
#[inline(always)]
fn skip_bytes(&mut self, n: usize) -> io::Result<()> {
// Read from the input in chunks of 1024 bytes at a time, and discard
// the result. 1024 is a tradeoff between doing a lot of calls, and
// using too much stack space. This method is not in a hot path, so it
// can afford to do this.
let mut n_read = 0;
let mut buf = [0u8; 1024];
while n_read < n {
let end = cmp::min(n - n_read, 1024);
let progress = try!(self.read(&mut buf[0..end]));
if progress > 0 {
n_read += progress;
} else {
return Err(io::Error::new(io::ErrorKind::Other, "Failed to read enough bytes."));
}
}
Ok(())
}
#[inline(always)]
fn read_4_bytes(&mut self) -> io::Result<[u8; 4]> {
let mut buf = [0_u8; 4];
try!(self.read_into(&mut buf[..]));
Ok(buf)
}
#[inline(always)]
fn read_i8(&mut self) -> io::Result<i8> {
self.read_u8().map(|x| x as i8)
}
#[inline(always)]
fn read_u8(&mut self) -> io::Result<u8> {
let mut buf = [0u8; 1];
try!(self.read_into(&mut buf));
Ok(buf[0])
}
#[inline(always)]
fn read_le_i16(&mut self) -> io::Result<i16> {
self.read_le_u16().map(|x| x as i16)
}
#[inline(always)]
fn read_le_u16(&mut self) -> io::Result<u16> {
let mut buf = [0u8; 2];
try!(self.read_into(&mut buf));
Ok((buf[1] as u16) << 8 | (buf[0] as u16))
}
#[inline(always)]
fn read_le_i24(&mut self) -> io::Result<i32> {
self.read_le_u24().map(|x|
// Test the sign bit, if it is set, extend the sign bit into the
// most significant byte.
if x & (1 << 23) == 0 {
x as i32
} else {
(x | 0xff_00_00_00) as i32
}
)
}
#[inline(always)]
fn read_le_i24_4(&mut self) -> io::Result<i32> {
self.read_le_u32().map(|x|
// Test the sign bit, if it is set, extend the sign bit into the
// most significant byte. Otherwise, mask out the top byte.
if x & (1 << 23) == 0 {
(x & 0x00_ff_ff_ff) as i32
} else {
(x | 0xff_00_00_00) as i32
}
)
}
#[inline(always)]
fn read_le_u24(&mut self) -> io::Result<u32> {
let mut buf = [0u8; 3];
try!(self.read_into(&mut buf));
Ok((buf[2] as u32) << 16 | (buf[1] as u32) << 8 | (buf[0] as u32))
}
#[inline(always)]
fn read_le_i32(&mut self) -> io::Result<i32> {
self.read_le_u32().map(|x| x as i32)
}
#[inline(always)]
fn read_le_u32(&mut self) -> io::Result<u32> {
let mut buf = [0u8; 4];
try!(self.read_into(&mut buf));
Ok((buf[3] as u32) << 24 | (buf[2] as u32) << 16 |
(buf[1] as u32) << 8 | (buf[0] as u32) << 0)
}
#[inline(always)]
fn read_le_f32(&mut self) -> io::Result<f32> {
let mut buf = [0u8; 4];
try!(self.read_into(&mut buf));
Ok(f32::from_le_bytes(buf))
}
}
/// The different chunks that a WAVE file can contain.
enum ChunkKind {
Fmt,
Fact,
Data,
Unknown,
}
/// Describes the structure of a chunk in the WAVE file.
struct ChunkHeader {
pub kind: ChunkKind,
pub len: u32,
}
/// A reader that reads the WAVE format from the underlying reader.
///
/// A `WavReader` is a streaming reader. It reads data from the underlying
/// reader on demand, and it reads no more than strictly necessary. No internal
/// buffering is performed on the underlying reader, but this can easily be
/// added by wrapping the reader in an `io::BufReader`. The `open` constructor
/// takes care of this for you.
pub struct WavReader<R> {
/// Specification of the file as found in the fmt chunk.
spec: WavSpec,
/// The number of bytes used to store a sample in the stream.
bytes_per_sample: u16,
/// The number of samples in the data chunk.
///
/// The data chunk is limited to a 4 GiB length because its header has a
/// 32-bit length field. A sample takes at least one byte to store, so the
/// number of samples is always less than 2^32.
num_samples: u32,
/// The number of samples read so far.
samples_read: u32,
/// The reader from which the WAVE format is read.
reader: R,
}
/// An iterator that yields samples of type `S` read from a `WavReader`.
///
/// The type `S` must have at least as many bits as the bits per sample of the
/// file, otherwise every iteration will return an error.
pub struct WavSamples<'wr, R, S>
where R: 'wr
{
reader: &'wr mut WavReader<R>,
phantom_sample: marker::PhantomData<S>,
}
/// An iterator that yields samples of type `S` read from a `WavReader`.
///
/// The type `S` must have at least as many bits as the bits per sample of the
/// file, otherwise every iteration will return an error.
pub struct WavIntoSamples<R, S> {
reader: WavReader<R>,
phantom_sample: marker::PhantomData<S>,
}
/// Reads the RIFF WAVE header, returns the supposed file size.
///
/// This function can be used to quickly check if the file could be a wav file
/// by reading 12 bytes of the header. If an `Ok` is returned, the file is
/// likely a wav file. If an `Err` is returned, it is definitely not a wav
/// file.
///
/// The returned file size cannot be larger than 2<sup>32</sup> + 7 bytes.
pub fn read_wave_header<R: io::Read>(reader: &mut R) -> Result<u64> {
// Every WAVE file starts with the four bytes 'RIFF' and a file length.
// TODO: the old approach of having a slice on the stack and reading
// into it is more cumbersome, but also avoids a heap allocation. Is
// the compiler smart enough to avoid the heap allocation anyway? I
// would not expect it to be.
if b"RIFF" != &try!(reader.read_4_bytes())[..] {
return Err(Error::FormatError("no RIFF tag found"));
}
let file_len = try!(reader.read_le_u32());
// Next four bytes indicate the file type, which should be WAVE.
if b"WAVE" != &try!(reader.read_4_bytes())[..] {
return Err(Error::FormatError("no WAVE tag found"));
}
// The stored file length does not include the "RIFF" magic and 4-byte
// length field, so the total size is 8 bytes more than what is stored.
Ok(file_len as u64 + 8)
}
/// Reads chunks until a data chunk is encountered.
///
/// Returns the information from the fmt chunk and the length of the data
/// chunk in bytes. Afterwards, the reader will be positioned at the first
/// content byte of the data chunk.
pub fn read_until_data<R: io::Read>(mut reader: R) -> Result<(WavSpecEx, u32)> {
let mut spec_opt = None;
loop {
let header = try!(WavReader::read_chunk_header(&mut reader));
match header.kind {
ChunkKind::Fmt => {
let spec = try!(WavReader::read_fmt_chunk(&mut reader, header.len));
spec_opt = Some(spec);
}
ChunkKind::Fact => {
// All (compressed) non-PCM formats must have a fact chunk
// (Rev. 3 documentation). The chunk contains at least one
// value, the number of samples in the file.
//
// The number of samples field is redundant for sampled
// data, since the Data chunk indicates the length of the
// data. The number of samples can be determined from the
// length of the data and the container size as determined
// from the Format chunk.
// http://www-mmsp.ece.mcgill.ca/documents/audioformats/wave/wave.html
let _samples_per_channel = reader.read_le_u32();
}
ChunkKind::Data => {
// The "fmt" chunk must precede the "data" chunk. Any
// chunks that come after the data chunk will be ignored.
if let Some(spec) = spec_opt {
return Ok((spec, header.len));
} else {
return Err(Error::FormatError("missing fmt chunk"));
}
}
ChunkKind::Unknown => {
// Ignore the chunk; skip all of its bytes.
try!(reader.skip_bytes(header.len as usize));
}
}
// If no data chunk is ever encountered, the function will return
// via one of the try! macros that return an Err on end of file.
}
}
impl<R> WavReader<R>
where R: io::Read
{
/// Attempts to read an 8-byte chunk header.
fn read_chunk_header(reader: &mut R) -> Result<ChunkHeader> {
let mut kind_str = [0; 4];
try!(reader.read_into(&mut kind_str));
let len = try!(reader.read_le_u32());
let kind = match &kind_str[..] {
b"fmt " => ChunkKind::Fmt,
b"fact" => ChunkKind::Fact,
b"data" => ChunkKind::Data,
_ => ChunkKind::Unknown,
};
Ok(ChunkHeader { kind: kind, len: len })
}
/// Reads the fmt chunk of the file, returns the information it provides.
fn read_fmt_chunk(reader: &mut R, chunk_len: u32) -> Result<WavSpecEx> {
// A minimum chunk length of at least 16 is assumed. Note: actually,
// the first 14 bytes contain enough information to fully specify the
// file. I have not encountered a file with a 14-byte fmt section
// though. If you ever encounter such file, please contact me.
if chunk_len < 16 {
return Err(Error::FormatError("invalid fmt chunk size"));
}
// Read the WAVEFORMAT struct, as defined at
// https://msdn.microsoft.com/en-us/library/ms713498.aspx.
// ```
// typedef struct {
// WORD wFormatTag;
// WORD nChannels;
// DWORD nSamplesPerSec;
// DWORD nAvgBytesPerSec;
// WORD nBlockAlign;
// } WAVEFORMAT;
// ```
// The WAVEFORMATEX struct has two more members, as defined at
// https://msdn.microsoft.com/en-us/library/ms713497.aspx
// ```
// typedef struct {
// WORD wFormatTag;
// WORD nChannels;
// DWORD nSamplesPerSec;
// DWORD nAvgBytesPerSec;
// WORD nBlockAlign;
// WORD wBitsPerSample;
// WORD cbSize;
// } WAVEFORMATEX;
// ```
// There is also PCMWAVEFORMAT as defined at
// https://msdn.microsoft.com/en-us/library/dd743663.aspx.
// ```
// typedef struct {
// WAVEFORMAT wf;
// WORD wBitsPerSample;
// } PCMWAVEFORMAT;
// ```
// In either case, the minimal length of the fmt section is 16 bytes,
// meaning that it does include the `wBitsPerSample` field. (The name
// is misleading though, because it is the number of bits used to store
// a sample, not all of the bits need to be valid for all versions of
// the WAVE format.)
let format_tag = try!(reader.read_le_u16());
let n_channels = try!(reader.read_le_u16());
let n_samples_per_sec = try!(reader.read_le_u32());
let n_bytes_per_sec = try!(reader.read_le_u32());
let block_align = try!(reader.read_le_u16());
let bits_per_sample = try!(reader.read_le_u16());
if n_channels == 0 {
return Err(Error::FormatError("file contains zero channels"));
}
let bytes_per_sample = block_align / n_channels;
// We allow bits_per_sample to be less than bytes_per_sample so that
// we can support things such as 24 bit samples in 4 byte containers.
if Some(bits_per_sample) > bytes_per_sample.checked_mul(8) {
return Err(Error::FormatError("sample bits exceeds size of sample"));
}
// This field is redundant, and may be ignored. We do validate it to
// fail early for ill-formed files.
if Some(n_bytes_per_sec) != (block_align as u32).checked_mul(n_samples_per_sec) {
return Err(Error::FormatError("inconsistent fmt chunk"));
}
// The bits per sample for a WAVEFORMAT struct is the number of bits
// used to store a sample. Therefore, it must be a multiple of 8.
if bits_per_sample % 8 != 0 {
return Err(Error::FormatError("bits per sample is not a multiple of 8"));
}
if bits_per_sample == 0 {
return Err(Error::FormatError("bits per sample is 0"));
}
let mut spec = WavSpec {
channels: n_channels,
sample_rate: n_samples_per_sec,
bits_per_sample: bits_per_sample,
sample_format: SampleFormat::Int,
};
// The different format tag definitions can be found in mmreg.h that is
// part of the Windows SDK. The vast majority are esoteric vendor-
// specific formats. We handle only a few. The following values could
// be of interest:
const PCM: u16 = 0x0001;
const ADPCM: u16 = 0x0002;
const IEEE_FLOAT: u16 = 0x0003;
const EXTENSIBLE: u16 = 0xfffe;
// We may update our WavSpec based on more data we read from the header.
match format_tag {
PCM => try!(WavReader::read_wave_format_pcm(reader, chunk_len, &spec)),
ADPCM => return Err(Error::Unsupported),
IEEE_FLOAT => try!(WavReader::read_wave_format_ieee_float(reader, chunk_len, &mut spec)),
EXTENSIBLE => try!(WavReader::read_wave_format_extensible(reader, chunk_len, &mut spec)),
_ => return Err(Error::Unsupported),
};
Ok(WavSpecEx {
spec: spec,
bytes_per_sample: bytes_per_sample,
})
}
fn read_wave_format_pcm(mut reader: R, chunk_len: u32, spec: &WavSpec) -> Result<()> {
// When there is a PCMWAVEFORMAT struct, the chunk is 16 bytes long.
// The WAVEFORMATEX structs includes two extra bytes, `cbSize`.
let is_wave_format_ex = match chunk_len {
16 => false,
18 => true,
// Other sizes are unexpected, but such files do occur in the wild,
// and reading these files is still possible, so we allow this.
40 => true,
_ => return Err(Error::FormatError("unexpected fmt chunk size")),
};
if is_wave_format_ex {
// `cbSize` can be used for non-PCM formats to specify the size of
// additional data. However, for WAVE_FORMAT_PCM, the member should
// be ignored, see https://msdn.microsoft.com/en-us/library/ms713497.aspx.
// Nonzero values do in fact occur in practice.
let _cb_size = try!(reader.read_le_u16());
// For WAVE_FORMAT_PCM in WAVEFORMATEX, only 8 or 16 bits per
// sample are valid according to
// https://msdn.microsoft.com/en-us/library/ms713497.aspx.
// 24 bits per sample is explicitly not valid inside a WAVEFORMATEX
// structure, but such files do occur in the wild nonetheless, and
// there is no good reason why we couldn't read them.
match spec.bits_per_sample {
8 => {}
16 => {}
24 => {}
_ => return Err(Error::FormatError("bits per sample is not 8 or 16")),
}
}
// If the chunk len was longer than expected, ignore the additional bytes.
if chunk_len == 40 {
try!(reader.skip_bytes(22));
}
Ok(())
}
fn read_wave_format_ieee_float(mut reader: R, chunk_len: u32, spec: &mut WavSpec) -> Result<()> {
// When there is a PCMWAVEFORMAT struct, the chunk is 16 bytes long.
// The WAVEFORMATEX structs includes two extra bytes, `cbSize`.
let is_wave_format_ex = chunk_len == 18;
if !is_wave_format_ex && chunk_len != 16 {
return Err(Error::FormatError("unexpected fmt chunk size"));
}
if is_wave_format_ex {
// For WAVE_FORMAT_IEEE_FLOAT which we are reading, there should
// be no extra data, so `cbSize` should be 0.
let cb_size = try!(reader.read_le_u16());
if cb_size != 0 {
return Err(Error::FormatError("unexpected WAVEFORMATEX size"));
}
}
// For WAVE_FORMAT_IEEE_FLOAT, the bits_per_sample field should be
// set to `32` according to
// https://msdn.microsoft.com/en-us/library/windows/hardware/ff538799(v=vs.85).aspx.
//
// Note that some applications support 64 bits per sample. This is
// not yet supported by hound.
if spec.bits_per_sample != 32 {
return Err(Error::FormatError("bits per sample is not 32"));
}
spec.sample_format = SampleFormat::Float;
Ok(())
}
fn read_wave_format_extensible(mut reader: R, chunk_len: u32, spec: &mut WavSpec) -> Result<()> {
// 16 bytes were read already, there must be two more for the `cbSize`
// field, and `cbSize` itself must be at least 22, so the chunk length
// must be at least 40.
if chunk_len < 40 {
return Err(Error::FormatError("unexpected fmt chunk size"));
}
// `cbSize` is the last field of the WAVEFORMATEX struct.
let cb_size = try!(reader.read_le_u16());
// `cbSize` must be at least 22, but in this case we assume that it is
// 22, because we would not know how to handle extra data anyway.
if cb_size != 22 {
return Err(Error::FormatError("unexpected WAVEFORMATEXTENSIBLE size"));
}
// What follows is the rest of the `WAVEFORMATEXTENSIBLE` struct, as
// defined at https://msdn.microsoft.com/en-us/library/ms713496.aspx.
// ```
// typedef struct {
// WAVEFORMATEX Format;
// union {
// WORD wValidBitsPerSample;
// WORD wSamplesPerBlock;
// WORD wReserved;
// } Samples;
// DWORD dwChannelMask;
// GUID SubFormat;
// } WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
// ```
let valid_bits_per_sample = try!(reader.read_le_u16());
let _channel_mask = try!(reader.read_le_u32()); // Not used for now.
let mut subformat = [0u8; 16];
try!(reader.read_into(&mut subformat));
// Several GUIDS are defined. At the moment, only the following are supported:
//
// * KSDATAFORMAT_SUBTYPE_PCM (PCM audio with integer samples).
// * KSDATAFORMAT_SUBTYPE_IEEE_FLOAT (PCM audio with floating point samples).
let sample_format = match subformat {
super::KSDATAFORMAT_SUBTYPE_PCM => SampleFormat::Int,
super::KSDATAFORMAT_SUBTYPE_IEEE_FLOAT => SampleFormat::Float,
_ => return Err(Error::Unsupported),
};
// Fallback to bits_per_sample if the valid_bits_per_sample is obviously wrong to support non standard headers found in the wild.
if valid_bits_per_sample > 0 {
spec.bits_per_sample = valid_bits_per_sample;
}
spec.sample_format = sample_format;
Ok(())
}
/// Attempts to create a reader that reads the WAVE format.
///
/// The header is read immediately. Reading the data will be done on
/// demand.
pub fn new(mut reader: R) -> Result<WavReader<R>> {
try!(read_wave_header(&mut reader));
let (spec_ex, data_len) = try!(read_until_data(&mut reader));
let num_samples = data_len / spec_ex.bytes_per_sample as u32;
// It could be that num_samples * bytes_per_sample < data_len.
// If data_len is not a multiple of bytes_per_sample, there is some
// trailing data. Either somebody is playing some steganography game,
// but more likely something is very wrong, and we should refuse to
// decode the file, as it is invalid.
if num_samples * spec_ex.bytes_per_sample as u32 != data_len {
let msg = "data chunk length is not a multiple of sample size";
return Err(Error::FormatError(msg));
}
// The number of samples must be a multiple of the number of channels,
// otherwise the last inter-channel sample would not have data for all
// channels.
if num_samples % spec_ex.spec.channels as u32 != 0 {
return Err(Error::FormatError("invalid data chunk length"));
}
let wav_reader = WavReader {
spec: spec_ex.spec,
bytes_per_sample: spec_ex.bytes_per_sample,
num_samples: num_samples,
samples_read: 0,
reader: reader,
};
Ok(wav_reader)
}
/// Returns information about the WAVE file.
pub fn spec(&self) -> WavSpec {
self.spec
}
/// Returns an iterator over all samples.
///
/// The channel data is interleaved. The iterator is streaming. That is,
/// if you call this method once, read a few samples, and call this method
/// again, the second iterator will not start again from the beginning of
/// the file, it will continue where the first iterator stopped.
///
/// The type `S` must have at least `spec().bits_per_sample` bits,
/// otherwise every iteration will return an error. All bit depths up to
/// 32 bits per sample can be decoded into an `i32`, but if you know
/// beforehand that you will be reading a file with 16 bits per sample, you
/// can save memory by decoding into an `i16`.
///
/// The type of `S` (int or float) must match `spec().sample_format`,
/// otherwise every iteration will return an error.
pub fn samples<'wr, S: Sample>(&'wr mut self) -> WavSamples<'wr, R, S> {
WavSamples {
reader: self,
phantom_sample: marker::PhantomData,
}
}
/// Same as `samples`, but takes ownership of the `WavReader`.
///
/// See `samples()` for more info.
pub fn into_samples<S: Sample>(self) -> WavIntoSamples<R, S> {
WavIntoSamples {
reader: self,
phantom_sample: marker::PhantomData,
}
}
/// Returns the duration of the file in samples.
///
/// The duration is independent of the number of channels. It is expressed
/// in units of samples. The duration in seconds can be obtained by
/// dividing this number by the sample rate. The duration is independent of
/// how many samples have been read already.
pub fn duration(&self) -> u32 {
self.num_samples / self.spec.channels as u32
}
/// Returns the number of values that the sample iterator will yield.
///
/// The length of the file is its duration (in samples) times the number of
/// channels. The length is independent of how many samples have been read
/// already. To get the number of samples left, use `len()` on the
/// `samples()` iterator.
pub fn len(&self) -> u32 {
self.num_samples
}
/// Destroys the `WavReader` and returns the underlying reader.
pub fn into_inner(self) -> R {
self.reader
}
/// Seek to the given time within the file.
///
/// The given time is measured in number of samples (independent of the
/// number of channels) since the beginning of the audio data. To seek to
/// a particular time in seconds, multiply the number of seconds with
/// `WavSpec::sample_rate`. The given time should not exceed the duration of
/// the file (returned by `duration()`). The behavior when seeking beyond
/// `duration()` depends on the reader's `Seek` implementation.
///
/// This method requires that the inner reader `R` implements `Seek`.
pub fn seek(&mut self, time: u32) -> io::Result<()>
where R: io::Seek,
{
let bytes_per_sample = self.spec.bits_per_sample / 8;
let sample_position = time * self.spec.channels as u32;
let offset_samples = sample_position as i64 - self.samples_read as i64;
let offset_bytes = offset_samples * bytes_per_sample as i64;
try!(self.reader.seek(io::SeekFrom::Current(offset_bytes)));
self.samples_read = sample_position;
Ok(())
}
}
impl WavReader<io::BufReader<fs::File>> {
/// Attempts to create a reader that reads from the specified file.
///
/// This is a convenience constructor that opens a `File`, wraps it in a
/// `BufReader` and then constructs a `WavReader` from it.
pub fn open<P: AsRef<path::Path>>(filename: P) -> Result<WavReader<io::BufReader<fs::File>>> {
let file = try!(fs::File::open(filename));
let buf_reader = io::BufReader::new(file);
WavReader::new(buf_reader)
}
}
fn iter_next<R, S>(reader: &mut WavReader<R>) -> Option<Result<S>>
where R: io::Read,
S: Sample
{
if reader.samples_read < reader.num_samples {
reader.samples_read += 1;
let sample = Sample::read(&mut reader.reader,
reader.spec.sample_format,
reader.bytes_per_sample,
reader.spec.bits_per_sample);
Some(sample.map_err(Error::from))
} else {
None
}
}
fn iter_size_hint<R>(reader: &WavReader<R>) -> (usize, Option<usize>) {
let samples_left = reader.num_samples - reader.samples_read;
(samples_left as usize, Some(samples_left as usize))
}
impl<'wr, R, S> Iterator for WavSamples<'wr, R, S>
where R: io::Read,
S: Sample
{
type Item = Result<S>;
fn next(&mut self) -> Option<Result<S>> {
iter_next(&mut self.reader)
}
fn size_hint(&self) -> (usize, Option<usize>) {
iter_size_hint(&self.reader)
}
}
impl<'wr, R, S> ExactSizeIterator for WavSamples<'wr, R, S>
where R: io::Read,
S: Sample
{
}
impl<R, S> Iterator for WavIntoSamples<R, S>
where R: io::Read,
S: Sample
{
type Item = Result<S>;
fn next(&mut self) -> Option<Result<S>> {
iter_next(&mut self.reader)
}
fn size_hint(&self) -> (usize, Option<usize>) {
iter_size_hint(&self.reader)
}
}
impl<R, S> ExactSizeIterator for WavIntoSamples<R, S>
where R: io::Read,
S: Sample
{
}
#[test]
fn duration_and_len_agree() {
let files = &["testsamples/pcmwaveformat-16bit-44100Hz-mono.wav",
"testsamples/waveformatex-16bit-44100Hz-stereo.wav",
"testsamples/waveformatextensible-32bit-48kHz-stereo.wav"];
for fname in files {
let reader = WavReader::open(fname).unwrap();
assert_eq!(reader.spec().channels as u32 * reader.duration(),
reader.len());
}
}
/// Tests reading a wave file with the PCMWAVEFORMAT struct.
#[test]
fn read_wav_pcm_wave_format_pcm() {
let mut wav_reader = WavReader::open("testsamples/pcmwaveformat-16bit-44100Hz-mono.wav")
.unwrap();
assert_eq!(wav_reader.spec().channels, 1);
assert_eq!(wav_reader.spec().sample_rate, 44100);
assert_eq!(wav_reader.spec().bits_per_sample, 16);
assert_eq!(wav_reader.spec().sample_format, SampleFormat::Int);
let samples: Vec<i16> = wav_reader.samples()
.map(|r| r.unwrap())
.collect();
// The test file has been prepared with these exact four samples.
assert_eq!(&samples[..], &[2, -3, 5, -7]);
}
#[test]
fn read_wav_skips_unknown_chunks() {
// The test samples are the same as without the -extra suffix, but ffmpeg
// has kindly added some useless chunks in between the fmt and data chunk.
let files = ["testsamples/pcmwaveformat-16bit-44100Hz-mono-extra.wav",
"testsamples/waveformatex-16bit-44100Hz-mono-extra.wav"];
for file in &files {
let mut wav_reader = WavReader::open(file).unwrap();
assert_eq!(wav_reader.spec().channels, 1);
assert_eq!(wav_reader.spec().sample_rate, 44100);
assert_eq!(wav_reader.spec().bits_per_sample, 16);
assert_eq!(wav_reader.spec().sample_format, SampleFormat::Int);
let sample = wav_reader.samples::<i16>().next().unwrap().unwrap();
assert_eq!(sample, 2);
}
}
#[test]
fn read_wav_0_valid_bits_fallback() {
let mut wav_reader = WavReader::open("testsamples/nonstandard-02.wav")
.unwrap();
assert_eq!(wav_reader.spec().channels, 2);
assert_eq!(wav_reader.spec().sample_rate, 48000);
assert_eq!(wav_reader.spec().bits_per_sample, 32);
assert_eq!(wav_reader.spec().sample_format, SampleFormat::Int);
let samples: Vec<i32> = wav_reader.samples()
.map(|r| r.unwrap())
.collect();
// The test file has been prepared with these exact four samples.
assert_eq!(&samples[..], &[19, -229373, 33587161, -2147483497]);
}
#[test]
fn len_and_size_hint_are_correct() {
let mut wav_reader = WavReader::open("testsamples/pcmwaveformat-16bit-44100Hz-mono.wav")
.unwrap();
assert_eq!(wav_reader.len(), 4);
{
let mut samples = wav_reader.samples::<i16>();
assert_eq!(samples.size_hint(), (4, Some(4)));
samples.next();
assert_eq!(samples.size_hint(), (3, Some(3)));
}
// Reading should not affect the initial length.
assert_eq!(wav_reader.len(), 4);
// Creating a new iterator resumes where the previous iterator stopped.
{
let mut samples = wav_reader.samples::<i16>();
assert_eq!(samples.size_hint(), (3, Some(3)));
samples.next();
assert_eq!(samples.size_hint(), (2, Some(2)));
}
}
#[test]
fn size_hint_is_exact() {
let files = &["testsamples/pcmwaveformat-16bit-44100Hz-mono.wav",
"testsamples/waveformatex-16bit-44100Hz-stereo.wav",
"testsamples/waveformatextensible-32bit-48kHz-stereo.wav"];
for fname in files {
let mut reader = WavReader::open(fname).unwrap();
let len = reader.len();
let mut iter = reader.samples::<i32>();
for i in 0..len {
let remaining = (len - i) as usize;
assert_eq!(iter.size_hint(), (remaining, Some(remaining)));
assert!(iter.next().is_some());
}
assert!(iter.next().is_none());
}
}
#[test]
fn samples_equals_into_samples() {
let wav_reader_val = WavReader::open("testsamples/pcmwaveformat-8bit-44100Hz-mono.wav").unwrap();
let mut wav_reader_ref = WavReader::open("testsamples/pcmwaveformat-8bit-44100Hz-mono.wav").unwrap();
let samples_val: Vec<i16> = wav_reader_val.into_samples()
.map(|r| r.unwrap())
.collect();
let samples_ref: Vec<i16> = wav_reader_ref.samples()
.map(|r| r.unwrap())
.collect();
assert_eq!(samples_val, samples_ref);
}
/// Tests reading a wave file with the WAVEFORMATEX struct.
#[test]
fn read_wav_wave_format_ex_pcm() {
let mut wav_reader = WavReader::open("testsamples/waveformatex-16bit-44100Hz-mono.wav")
.unwrap();
assert_eq!(wav_reader.spec().channels, 1);
assert_eq!(wav_reader.spec().sample_rate, 44100);
assert_eq!(wav_reader.spec().bits_per_sample, 16);
assert_eq!(wav_reader.spec().sample_format, SampleFormat::Int);
let samples: Vec<i16> = wav_reader.samples()
.map(|r| r.unwrap())
.collect();
// The test file has been prepared with these exact four samples.
assert_eq!(&samples[..], &[2, -3, 5, -7]);
}
#[test]
fn read_wav_wave_format_ex_ieee_float() {
let mut wav_reader = WavReader::open("testsamples/waveformatex-ieeefloat-44100Hz-mono.wav")
.unwrap();
assert_eq!(wav_reader.spec().channels, 1);
assert_eq!(wav_reader.spec().sample_rate, 44100);
assert_eq!(wav_reader.spec().bits_per_sample, 32);
assert_eq!(wav_reader.spec().sample_format, SampleFormat::Float);
let samples: Vec<f32> = wav_reader.samples()
.map(|r| r.unwrap())
.collect();
// The test file has been prepared with these exact four samples.
assert_eq!(&samples[..], &[2.0, 3.0, -16411.0, 1019.0]);
}
#[test]
fn read_wav_stereo() {
let mut wav_reader = WavReader::open("testsamples/waveformatex-16bit-44100Hz-stereo.wav")
.unwrap();
assert_eq!(wav_reader.spec().channels, 2);
assert_eq!(wav_reader.spec().sample_rate, 44100);
assert_eq!(wav_reader.spec().bits_per_sample, 16);
assert_eq!(wav_reader.spec().sample_format, SampleFormat::Int);
let samples: Vec<i16> = wav_reader.samples()
.map(|r| r.unwrap())
.collect();
// The test file has been prepared with these exact eight samples.
assert_eq!(&samples[..], &[2, -3, 5, -7, 11, -13, 17, -19]);
}
#[test]
fn read_wav_pcm_wave_format_8bit() {
let mut wav_reader = WavReader::open("testsamples/pcmwaveformat-8bit-44100Hz-mono.wav")
.unwrap();
assert_eq!(wav_reader.spec().channels, 1);
assert_eq!(wav_reader.spec().bits_per_sample, 8);
assert_eq!(wav_reader.spec().sample_format, SampleFormat::Int);
let samples: Vec<i16> = wav_reader.samples()
.map(|r| r.unwrap())
.collect();
// The test file has been prepared with these exact four samples.
assert_eq!(&samples[..], &[19, -53, 89, -127]);
}
/// Test reading 24 bit samples in a 4 byte container using the pcmwaveformat header. This is
/// technically a non-compliant wave file, but it is the sort of file generated by
/// 'arecord -f S24_LE -r 48000 -c 2 input.wav' so it should be supported.
#[test]
fn read_wav_pcm_wave_format_24bit_4byte() {
let mut wav_reader = WavReader::open("testsamples/pcmwaveformat-24bit-4byte-48kHz-stereo.wav")
.unwrap();
assert_eq!(wav_reader.spec().channels, 2);
assert_eq!(wav_reader.spec().sample_rate, 48_000);
assert_eq!(wav_reader.spec().bits_per_sample, 24);
assert_eq!(wav_reader.spec().sample_format, SampleFormat::Int);
let samples: Vec<i32> = wav_reader.samples()
.map(|r| r.unwrap())
.collect();
// The test file has been prepared with these exact four samples.
assert_eq!(&samples[..], &[-96, 23_052, 8_388_607, -8_360_672]);
}
/// Regression test for a real-world wav file encountered in Quake.
#[test]
fn read_wav_wave_format_ex_8bit() {
let mut wav_reader = WavReader::open("testsamples/waveformatex-8bit-11025Hz-mono.wav").unwrap();
assert_eq!(wav_reader.spec().channels, 1);
assert_eq!(wav_reader.spec().bits_per_sample, 8);
assert_eq!(wav_reader.spec().sample_format, SampleFormat::Int);
let samples: Vec<i32> = wav_reader.samples()
.map(|r| r.unwrap())
.collect();
// The audio data has been zeroed out, but for 8-bit files, a zero means a
// sample value of 128.
assert_eq!(&samples[..], &[-128, -128, -128, -128]);
}
/// This test sample tests both reading the WAVEFORMATEXTENSIBLE header, and 24-bit samples.
#[test]
fn read_wav_wave_format_extensible_pcm_24bit() {
let mut wav_reader = WavReader::open("testsamples/waveformatextensible-24bit-192kHz-mono.wav")
.unwrap();
assert_eq!(wav_reader.spec().channels, 1);
assert_eq!(wav_reader.spec().sample_rate, 192_000);
assert_eq!(wav_reader.spec().bits_per_sample, 24);
assert_eq!(wav_reader.spec().sample_format, SampleFormat::Int);
let samples: Vec<i32> = wav_reader.samples()
.map(|r| r.unwrap())
.collect();
// The test file has been prepared with these exact four samples.
assert_eq!(&samples[..], &[-17, 4_194_319, -6_291_437, 8_355_817]);
}
/// This test sample tests both reading the WAVEFORMATEXTENSIBLE header, and 24-bit samples with a
/// 4 byte container size.
#[test]
fn read_wav_wave_format_extensible_pcm_24bit_4byte() {
let mut wav_reader = WavReader::open("testsamples/waveformatextensible-24bit-4byte-48kHz-stereo.wav")
.unwrap();
assert_eq!(wav_reader.spec().channels, 2);
assert_eq!(wav_reader.spec().sample_rate, 48_000);
assert_eq!(wav_reader.spec().bits_per_sample, 24);
assert_eq!(wav_reader.spec().sample_format, SampleFormat::Int);
let samples: Vec<i32> = wav_reader.samples()
.map(|r| r.unwrap())
.collect();
// The test file has been prepared with these exact four samples.
assert_eq!(&samples[..], &[-96, 23_052, 8_388_607, -8_360_672]);
}
#[test]
fn read_wav_32bit() {
let mut wav_reader = WavReader::open("testsamples/waveformatextensible-32bit-48kHz-stereo.wav")
.unwrap();
assert_eq!(wav_reader.spec().bits_per_sample, 32);
assert_eq!(wav_reader.spec().sample_format, SampleFormat::Int);
let samples: Vec<i32> = wav_reader.samples()
.map(|r| r.unwrap())
.collect();
// The test file has been prepared with these exact four samples.
assert_eq!(&samples[..], &[19, -229_373, 33_587_161, -2_147_483_497]);
}
#[test]
fn read_wav_wave_format_extensible_ieee_float() {
let mut wav_reader =
WavReader::open("testsamples/waveformatextensible-ieeefloat-44100Hz-mono.wav").unwrap();
assert_eq!(wav_reader.spec().channels, 1);
assert_eq!(wav_reader.spec().sample_rate, 44100);
assert_eq!(wav_reader.spec().bits_per_sample, 32);
assert_eq!(wav_reader.spec().sample_format, SampleFormat::Float);
let samples: Vec<f32> = wav_reader.samples()
.map(|r| r.unwrap())
.collect();
// The test file has been prepared with these exact four samples.
assert_eq!(&samples[..], &[2.0, 3.0, -16411.0, 1019.0]);
}
#[test]
fn read_wav_nonstandard_01() {
// The test sample here is adapted from a file encountered in the wild (data
// chunk replaced with two zero samples, some metadata dropped, and the file
// length in the header fixed). It is not a valid file according to the
// standard, but many players can deal with it nonetheless. (The file even
// contains some metadata; open it in a hex editor if you would like to know
// which program created it.) The file contains a regular PCM format tag,
// but the size of the fmt chunk is one that would be expected of a
// WAVEFORMATEXTENSIBLE chunk. The bits per sample is 24, which is invalid
// for WAVEFORMATEX, but we can read it nonetheless.
let mut wav_reader = WavReader::open("testsamples/nonstandard-01.wav").unwrap();
assert_eq!(wav_reader.spec().bits_per_sample, 24);
assert_eq!(wav_reader.spec().sample_format, SampleFormat::Int);
let samples: Vec<i32> = wav_reader.samples()
.map(|r| r.unwrap())
.collect();
assert_eq!(&samples[..], &[0, 0]);
}
#[test]
fn wide_read_should_signal_error() {
let mut reader24 = WavReader::open("testsamples/waveformatextensible-24bit-192kHz-mono.wav")
.unwrap();
// Even though we know the first value is 17, and it should fit in an `i8`,
// a general 24-bit sample will not fit in an `i8`, so this should fail.
// 16-bit is still not wide enough, but 32-bit should do the trick.
assert!(reader24.samples::<i8>().next().unwrap().is_err());
assert!(reader24.samples::<i16>().next().unwrap().is_err());
assert!(reader24.samples::<i32>().next().unwrap().is_ok());
let mut reader32 = WavReader::open("testsamples/waveformatextensible-32bit-48kHz-stereo.wav")
.unwrap();
// In general, 32-bit samples will not fit in anything but an `i32`.
assert!(reader32.samples::<i8>().next().unwrap().is_err());
assert!(reader32.samples::<i16>().next().unwrap().is_err());
assert!(reader32.samples::<i32>().next().unwrap().is_ok());
}
#[test]
fn sample_format_mismatch_should_signal_error() {
let mut reader_f32 = WavReader::open("testsamples/waveformatex-ieeefloat-44100Hz-mono.wav")
.unwrap();
assert!(reader_f32.samples::<i8>().next().unwrap().is_err());
assert!(reader_f32.samples::<i16>().next().unwrap().is_err());
assert!(reader_f32.samples::<i32>().next().unwrap().is_err());
assert!(reader_f32.samples::<f32>().next().unwrap().is_ok());
let mut reader_i8 = WavReader::open("testsamples/pcmwaveformat-8bit-44100Hz-mono.wav").unwrap();
assert!(reader_i8.samples::<i8>().next().unwrap().is_ok());
assert!(reader_i8.samples::<i16>().next().unwrap().is_ok());
assert!(reader_i8.samples::<i32>().next().unwrap().is_ok());
assert!(reader_i8.samples::<f32>().next().unwrap().is_err());
}
#[test]
fn fuzz_crashes_should_be_fixed() {
use std::fs;
use std::ffi::OsStr;
// This is a regression test: all crashes and other issues found through
// fuzzing should not cause a crash.
let dir = fs::read_dir("testsamples/fuzz").ok()
.expect("failed to enumerate fuzz test corpus");
for path in dir {
let path = path.ok().expect("failed to obtain path info").path();
let is_file = fs::metadata(&path).unwrap().file_type().is_file();
if is_file && path.extension() == Some(OsStr::new("wav")) {
println!(" testing {} ...", path.to_str()
.expect("unsupported filename"));
let mut reader = match WavReader::open(path) {
Ok(r) => r,
Err(..) => continue,
};
match reader.spec().sample_format {
SampleFormat::Int => {
for sample in reader.samples::<i32>() {
match sample {
Ok(..) => { }
Err(..) => break,
}
}
}
SampleFormat::Float => {
for sample in reader.samples::<f32>() {
match sample {
Ok(..) => { }
Err(..) => break,
}
}
}
}
}
}
}
#[test]
fn seek_is_consistent() {
let files = &["testsamples/pcmwaveformat-16bit-44100Hz-mono.wav",
"testsamples/waveformatex-16bit-44100Hz-stereo.wav",
"testsamples/waveformatextensible-32bit-48kHz-stereo.wav"];
for fname in files {
let mut reader = WavReader::open(fname).unwrap();
// Seeking back to the start should "reset" the reader.
let count = reader.samples::<i32>().count();
reader.seek(0).unwrap();
assert_eq!(reader.samples_read, 0);
assert_eq!(count, reader.samples::<i32>().count());
// Seek to the last sample.
let last_time = reader.duration() - 1;
let channels = reader.spec.channels;
reader.seek(last_time).unwrap();
{
let mut samples = reader.samples::<i32>();
for _ in 0..channels {
assert!(samples.next().is_some());
}
assert!(samples.next().is_none());
}
// Seeking beyond the audio data produces no samples.
let num_samples = reader.len();
reader.seek(num_samples).unwrap();
assert!(reader.samples::<i32>().next().is_none());
reader.seek(::std::u32::MAX / channels as u32).unwrap();
assert!(reader.samples::<i32>().next().is_none());
}
}