blob: fe435cbf52c3473502a924ec0e4d984bfb7c9da0 [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.
//! Hound, a wav encoding and decoding library.
//!
//! Examples
//! ========
//!
//! The following example renders a 440 Hz sine wave, and stores it as as a
//! mono wav file with a sample rate of 44.1 kHz and 16 bits per sample.
//!
//! ```
//! use std::f32::consts::PI;
//! use std::i16;
//! use hound;
//!
//! let spec = hound::WavSpec {
//! channels: 1,
//! sample_rate: 44100,
//! bits_per_sample: 16,
//! sample_format: hound::SampleFormat::Int,
//! };
//! let mut writer = hound::WavWriter::create("sine.wav", spec).unwrap();
//! for t in (0 .. 44100).map(|x| x as f32 / 44100.0) {
//! let sample = (t * 440.0 * 2.0 * PI).sin();
//! let amplitude = i16::MAX as f32;
//! writer.write_sample((sample * amplitude) as i16).unwrap();
//! }
//! writer.finalize().unwrap();
//! ```
//!
//! The following example computes the root mean square (RMS) of an audio file
//! with at most 16 bits per sample.
//!
//! ```
//! use hound;
//!
//! let mut reader = hound::WavReader::open("testsamples/pop.wav").unwrap();
//! let sqr_sum = reader.samples::<i16>()
//! .fold(0.0, |sqr_sum, s| {
//! let sample = s.unwrap() as f64;
//! sqr_sum + sample * sample
//! });
//! println!("RMS is {}", (sqr_sum / reader.len() as f64).sqrt());
//! ```
#![warn(missing_docs)]
use std::error;
use std::fmt;
use std::io;
use std::result;
use read::ReadExt;
use write::WriteExt;
mod read;
mod write;
pub use read::{WavReader, WavIntoSamples, WavSamples, read_wave_header};
pub use write::{SampleWriter16, WavWriter};
/// A type that can be used to represent audio samples.
///
/// Via this trait, decoding can be generic over `i8`, `i16`, `i32` and `f32`.
///
/// All integer formats with bit depths up to 32 bits per sample can be decoded
/// into `i32`, but it takes up more memory. If you know beforehand that you
/// will be reading a file with 16 bits per sample, then decoding into an `i16`
/// will be sufficient.
pub trait Sample: Sized {
/// Writes the audio sample to the WAVE data chunk.
fn write<W: io::Write>(self, writer: &mut W, bits: u16) -> Result<()>;
/// Writes the audio sample to the WAVE data chunk, zero padding the size of
/// the written sample out to `byte_width`.
fn write_padded<W: io::Write>(self, writer: &mut W, bits: u16, byte_width: u16) -> Result<()>;
/// Reads the audio sample from the WAVE data chunk.
fn read<R: io::Read>(reader: &mut R, SampleFormat, bytes: u16, bits: u16) -> Result<Self>;
/// Cast the sample to a 16-bit sample.
///
/// This does not change the value of the sample, it only casts it. The
/// value is assumed to fit within the range. This is not verified,
/// truncation may occur.
fn as_i16(self) -> i16;
}
/// Converts an unsigned integer in the range 0-255 to a signed one in the range -128-127.
///
/// Presumably, the designers of the WAVE format did not like consistency. For
/// all bit depths except 8, samples are stored as little-endian _signed_
/// integers. However, an 8-bit sample is instead stored as an _unsigned_
/// integer. Hound abstracts away this idiosyncrasy by providing only signed
/// sample types.
fn signed_from_u8(x: u8) -> i8 {
(x as i16 - 128) as i8
}
/// Converts a signed integer in the range -128-127 to an unsigned one in the range 0-255.
fn u8_from_signed(x: i8) -> u8 {
(x as i16 + 128) as u8
}
#[test]
fn u8_sign_conversion_is_bijective() {
for x in 0..255 {
assert_eq!(x, u8_from_signed(signed_from_u8(x)));
}
for x in -128..127 {
assert_eq!(x, signed_from_u8(u8_from_signed(x)));
}
}
/// Tries to cast the sample to an 8-bit signed integer, returning an error on overflow.
#[inline(always)]
fn narrow_to_i8(x: i32) -> Result<i8> {
use std::i8;
if x < i8::MIN as i32 || x > i8::MAX as i32 {
Err(Error::TooWide)
} else {
Ok(x as i8)
}
}
#[test]
fn verify_narrow_to_i8() {
assert!(narrow_to_i8(127).is_ok());
assert!(narrow_to_i8(128).is_err());
assert!(narrow_to_i8(-128).is_ok());
assert!(narrow_to_i8(-129).is_err());
}
/// Tries to cast the sample to a 16-bit signed integer, returning an error on overflow.
#[inline(always)]
fn narrow_to_i16(x: i32) -> Result<i16> {
use std::i16;
if x < i16::MIN as i32 || x > i16::MAX as i32 {
Err(Error::TooWide)
} else {
Ok(x as i16)
}
}
#[test]
fn verify_narrow_to_i16() {
assert!(narrow_to_i16(32767).is_ok());
assert!(narrow_to_i16(32768).is_err());
assert!(narrow_to_i16(-32768).is_ok());
assert!(narrow_to_i16(-32769).is_err());
}
/// Tries to cast the sample to a 24-bit signed integer, returning an error on overflow.
#[inline(always)]
fn narrow_to_i24(x: i32) -> Result<i32> {
if x < -(1 << 23) || x > (1 << 23) - 1 {
Err(Error::TooWide)
} else {
Ok(x)
}
}
#[test]
fn verify_narrow_to_i24() {
assert!(narrow_to_i24(8_388_607).is_ok());
assert!(narrow_to_i24(8_388_608).is_err());
assert!(narrow_to_i24(-8_388_608).is_ok());
assert!(narrow_to_i24(-8_388_609).is_err());
}
impl Sample for i8 {
fn write<W: io::Write>(self, writer: &mut W, bits: u16) -> Result<()> {
self.write_padded(writer, bits, bits / 8)
}
fn write_padded<W: io::Write>(self, writer: &mut W, bits: u16, byte_width: u16) -> Result<()> {
match (bits, byte_width) {
(8, 1) => Ok(try!(writer.write_u8(u8_from_signed(self)))),
(16, 2) => Ok(try!(writer.write_le_i16(self as i16))),
(24, 3) => Ok(try!(writer.write_le_i24(self as i32))),
(24, 4) => Ok(try!(writer.write_le_i24_4(self as i32))),
(32, 4) => Ok(try!(writer.write_le_i32(self as i32))),
_ => Err(Error::Unsupported),
}
}
#[inline(always)]
fn as_i16(self) -> i16 {
self as i16
}
fn read<R: io::Read>(reader: &mut R, fmt: SampleFormat, bytes: u16, bits: u16) -> Result<i8> {
if fmt != SampleFormat::Int {
return Err(Error::InvalidSampleFormat);
}
match (bytes, bits) {
(1, 8) => Ok(try!(reader.read_u8().map(signed_from_u8))),
(n, _) if n > 1 => Err(Error::TooWide),
// TODO: add a genric decoder for any bit depth.
_ => Err(Error::Unsupported),
}
}
}
impl Sample for i16 {
fn write<W: io::Write>(self, writer: &mut W, bits: u16) -> Result<()> {
self.write_padded(writer, bits, bits / 8)
}
fn write_padded<W: io::Write>(self, writer: &mut W, bits: u16, byte_width: u16) -> Result<()> {
match (bits, byte_width) {
(8, 1) => Ok(try!(
writer.write_u8(u8_from_signed(try!(narrow_to_i8(self as i32))))
)),
(16, 2) => Ok(try!(writer.write_le_i16(self))),
(24, 3) => Ok(try!(writer.write_le_i24(self as i32))),
(24, 4) => Ok(try!(writer.write_le_i24_4(self as i32))),
(32, 4) => Ok(try!(writer.write_le_i32(self as i32))),
_ => Err(Error::Unsupported),
}
}
#[inline(always)]
fn as_i16(self) -> i16 {
self
}
fn read<R: io::Read>(reader: &mut R, fmt: SampleFormat, bytes: u16, bits: u16) -> Result<i16> {
if fmt != SampleFormat::Int {
return Err(Error::InvalidSampleFormat);
}
match (bytes, bits) {
(1, 8) => Ok(try!(reader.read_u8().map(signed_from_u8).map(|x| x as i16))),
(2, 16) => Ok(try!(reader.read_le_i16())),
(n, _) if n > 2 => Err(Error::TooWide),
// TODO: add a generic decoder for any bit depth.
_ => Err(Error::Unsupported),
}
}
}
impl Sample for i32 {
fn write<W: io::Write>(self, writer: &mut W, bits: u16) -> Result<()> {
self.write_padded(writer, bits, bits / 8)
}
fn write_padded<W: io::Write>(self, writer: &mut W, bits: u16, byte_width: u16) -> Result<()> {
match (bits, byte_width) {
(8, 1) => Ok(try!(
writer.write_u8(u8_from_signed(try!(narrow_to_i8(self))))
)),
(16, 2) => Ok(try!(writer.write_le_i16(try!(narrow_to_i16(self))))),
(24, 3) => Ok(try!(writer.write_le_i24(try!(narrow_to_i24(self))))),
(24, 4) => Ok(try!(writer.write_le_i24_4(try!(narrow_to_i24(self))))),
(32, 4) => Ok(try!(writer.write_le_i32(self))),
_ => Err(Error::Unsupported),
}
}
#[inline(always)]
fn as_i16(self) -> i16 {
self as i16
}
fn read<R: io::Read>(reader: &mut R, fmt: SampleFormat, bytes: u16, bits: u16) -> Result<i32> {
if fmt != SampleFormat::Int {
return Err(Error::InvalidSampleFormat);
}
match (bytes, bits) {
(1, 8) => Ok(try!(reader.read_u8().map(signed_from_u8).map(|x| x as i32))),
(2, 16) => Ok(try!(reader.read_le_i16().map(|x| x as i32))),
(3, 24) => Ok(try!(reader.read_le_i24())),
(4, 24) => Ok(try!(reader.read_le_i24_4())),
(4, 32) => Ok(try!(reader.read_le_i32())),
(n, _) if n > 4 => Err(Error::TooWide),
// TODO: add a generic decoder for any bit depth.
_ => Err(Error::Unsupported),
}
}
}
impl Sample for f32 {
fn write<W: io::Write>(self, writer: &mut W, bits: u16) -> Result<()> {
self.write_padded(writer, bits, bits / 8)
}
fn write_padded<W: io::Write>(self, writer: &mut W, bits: u16, byte_width: u16) -> Result<()> {
match (bits, byte_width) {
(32, 4) => Ok(try!(writer.write_le_f32(self))),
_ => Err(Error::Unsupported),
}
}
fn as_i16(self) -> i16 {
panic!("Calling as_i16 with an f32 is invalid.");
}
fn read<R: io::Read>(reader: &mut R, fmt: SampleFormat, bytes: u16, bits: u16) -> Result<Self> {
if fmt != SampleFormat::Float {
return Err(Error::InvalidSampleFormat);
}
match (bytes, bits) {
(4, 32) => Ok(try!(reader.read_le_f32())),
(n, _) if n > 4 => Err(Error::TooWide),
_ => Err(Error::Unsupported),
}
}
}
/// Specifies whether a sample is stored as an "IEEE Float" or an integer.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SampleFormat {
/// Wave files with the `WAVE_FORMAT_IEEE_FLOAT` format tag store samples as floating point
/// values.
///
/// Values are normally in the range [-1.0, 1.0].
Float,
/// Wave files with the `WAVE_FORMAT_PCM` format tag store samples as integer values.
Int,
}
/// Specifies properties of the audio data.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct WavSpec {
/// The number of channels.
pub channels: u16,
/// The number of samples per second.
///
/// A common value is 44100, this is 44.1 kHz which is used for CD audio.
pub sample_rate: u32,
/// The number of bits per sample.
///
/// A common value is 16 bits per sample, which is used for CD audio.
pub bits_per_sample: u16,
/// Whether the wav's samples are float or integer values.
pub sample_format: SampleFormat,
}
/// Specifies properties of the audio data, as well as the layout of the stream.
#[derive(Clone, Copy)]
pub struct WavSpecEx {
/// The normal information about the audio data.
///
/// Bits per sample here is the number of _used_ bits per sample, not the
/// number of bits used to _store_ a sample.
pub spec: WavSpec,
/// The number of bytes used to store a sample.
pub bytes_per_sample: u16,
}
/// The error type for operations on `WavReader` and `WavWriter`.
#[derive(Debug)]
pub enum Error {
/// An IO error occured in the underlying reader or writer.
IoError(io::Error),
/// Ill-formed WAVE data was encountered.
FormatError(&'static str),
/// The sample has more bits than the destination type.
///
/// When iterating using the `samples` iterator, this means that the
/// destination type (produced by the iterator) is not wide enough to hold
/// the sample. When writing, this means that the sample cannot be written,
/// because it requires more bits than the bits per sample specified.
TooWide,
/// The number of samples written is not a multiple of the number of channels.
UnfinishedSample,
/// The format is not supported.
Unsupported,
/// The sample format is different than the destination format.
///
/// When iterating using the `samples` iterator, this means the destination
/// type (produced by the iterator) has a different sample format than the
/// samples in the wav file.
///
/// For example, this will occur if the user attempts to produce `i32`
/// samples (which have a `SampleFormat::Int`) from a wav file that
/// contains floating point data (`SampleFormat::Float`).
InvalidSampleFormat,
}
impl fmt::Display for Error {
fn fmt(&self, formatter: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
match *self {
Error::IoError(ref err) => err.fmt(formatter),
Error::FormatError(reason) => {
try!(formatter.write_str("Ill-formed WAVE file: "));
formatter.write_str(reason)
}
Error::TooWide => {
formatter.write_str("The sample has more bits than the destination type.")
}
Error::UnfinishedSample => {
formatter.write_str(
"The number of samples written is not a multiple of the number of channels.")
}
Error::Unsupported => {
formatter.write_str("The wave format of the file is not supported.")
}
Error::InvalidSampleFormat => {
formatter.write_str("The sample format differs from the destination format.")
}
}
}
}
impl error::Error for Error {
fn description(&self) -> &str {
match *self {
Error::IoError(ref err) => err.description(),
Error::FormatError(reason) => reason,
Error::TooWide => "the sample has more bits than the destination type",
Error::UnfinishedSample => "the number of samples written is not a multiple of the number of channels",
Error::Unsupported => "the wave format of the file is not supported",
Error::InvalidSampleFormat => "the sample format differs from the destination format",
}
}
fn cause(&self) -> Option<&error::Error> {
match *self {
Error::IoError(ref err) => Some(err),
Error::FormatError(_) => None,
Error::TooWide => None,
Error::UnfinishedSample => None,
Error::Unsupported => None,
Error::InvalidSampleFormat => None,
}
}
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Error {
Error::IoError(err)
}
}
/// A type for results generated by Hound where the error type is hard-wired.
pub type Result<T> = result::Result<T, Error>;
// The WAVEFORMATEXTENSIBLE struct can contain several subformats.
// These are identified by a GUID. The various GUIDS can be found in the file
// mmreg.h that is part of the Windows SDK. The following GUIDS are defined:
// - PCM: 00000001-0000-0010-8000-00aa00389b71
// - IEEE_FLOAT: 00000003-0000-0010-8000-00aa00389b71
// When written to a wav file, the byte order of a GUID is native for the first
// three sections, which is assumed to be little endian, and big endian for the
// last 8-byte section (which does contain a hyphen, for reasons unknown to me).
/// Subformat type for PCM audio with integer samples.
const KSDATAFORMAT_SUBTYPE_PCM: [u8; 16] = [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80,
0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71];
/// Subformat type for IEEE_FLOAT audio with float samples.
const KSDATAFORMAT_SUBTYPE_IEEE_FLOAT: [u8; 16] = [0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71];
impl WavSpec {
/// Get "stand-alone" wav header representing infinite or unknown size wav file.
/// Use this if you need to write audio data to non-seekable sinks (like stdout).
///
/// Actual samples are supposed to be written using low-level [`Sample::write`] call.
///
/// Such wav files are produced e.g. by FFmpeg and have `0xFFFFFFFF` instead of chunk sizes.
///
/// Note that such files may be non-standard. Consider using [`WavWriter`] for better API.
///
/// Example:
///
/// ```no_run
/// extern crate hound;
/// use std::io::Write;
///
/// let spec = hound::WavSpec {
/// bits_per_sample: 16,
/// channels: 1,
/// sample_format: hound::SampleFormat::Int,
/// sample_rate: 16000,
/// };
///
/// let v = spec.into_header_for_infinite_file();
///
/// let so = std::io::stdout();
/// let mut so = so.lock();
/// so.write_all(&v[..]).unwrap();
///
/// loop {
/// for i in 0..126 {
/// let x : i16 = (i * 256) as i16;
/// hound::Sample::write(x, &mut so, 16).unwrap();
/// }
/// }
/// ```
pub fn into_header_for_infinite_file(self) -> Vec<u8> {
let mut c = std::io::Cursor::new(Vec::with_capacity(0x44));
{
let w = WavWriter::new(&mut c, self);
drop(w);
}
let mut v = c.into_inner();
// Set WAVE chunk size to a special signal value
v[4] = 0xFF; v[5] = 0xFF; v[6] = 0xFF; v[7] = 0xFF;
// Detect fmt size, get offset of data chunk's size and set it to signal value
if v[16] == 0x10 {
// pcm wave
v[0x28] = 0xFF; v[0x29] = 0xFF; v[0x2A] = 0xFF; v[0x2B] = 0xFF;
} else if v[16] == 0x28 {
// extensible
v[0x40] = 0xFF; v[0x41] = 0xFF; v[0x42] = 0xFF; v[0x43] = 0xFF;
} else {
unreachable!()
}
v
}
}
#[test]
fn write_read_i16_is_lossless() {
let mut buffer = io::Cursor::new(Vec::new());
let write_spec = WavSpec {
channels: 2,
sample_rate: 44100,
bits_per_sample: 16,
sample_format: SampleFormat::Int,
};
{
let mut writer = WavWriter::new(&mut buffer, write_spec).unwrap();
for s in -1024_i16..1024 {
writer.write_sample(s).unwrap();
}
writer.finalize().unwrap();
}
{
buffer.set_position(0);
let mut reader = WavReader::new(&mut buffer).unwrap();
assert_eq!(write_spec, reader.spec());
assert_eq!(reader.len(), 2048);
for (expected, read) in (-1024_i16..1024).zip(reader.samples()) {
assert_eq!(expected, read.unwrap());
}
}
}
#[test]
fn write_read_i16_via_sample_writer_is_lossless() {
let mut buffer = io::Cursor::new(Vec::new());
let write_spec = WavSpec {
channels: 2,
sample_rate: 44100,
bits_per_sample: 16,
sample_format: SampleFormat::Int,
};
{
let mut writer = WavWriter::new(&mut buffer, write_spec).unwrap();
{
{
let mut sample_writer = writer.get_i16_writer(1024);
for s in -1024_i16..0 {
sample_writer.write_sample(s);
}
sample_writer.flush().unwrap();
}
{
let mut sample_writer = writer.get_i16_writer(1024);
for s in 0i16..1024 {
unsafe { sample_writer.write_sample_unchecked(s); }
}
sample_writer.flush().unwrap();
}
}
writer.finalize().unwrap();
}
{
buffer.set_position(0);
let mut reader = WavReader::new(&mut buffer).unwrap();
assert_eq!(write_spec, reader.spec());
assert_eq!(reader.len(), 2048);
for (expected, read) in (-1024_i16..1024).zip(reader.samples()) {
assert_eq!(expected, read.unwrap());
}
}
}
#[test]
fn write_read_i8_is_lossless() {
let mut buffer = io::Cursor::new(Vec::new());
let write_spec = WavSpec {
channels: 16,
sample_rate: 48000,
bits_per_sample: 8,
sample_format: SampleFormat::Int,
};
// Write `i8` samples.
{
let mut writer = WavWriter::new(&mut buffer, write_spec).unwrap();
// Iterate over i16 because we cannot specify the upper bound otherwise.
for s in -128_i16..127 + 1 {
writer.write_sample(s as i8).unwrap();
}
writer.finalize().unwrap();
}
// Then read them into `i16`.
{
buffer.set_position(0);
let mut reader = WavReader::new(&mut buffer).unwrap();
assert_eq!(write_spec, reader.spec());
assert_eq!(reader.len(), 256);
for (expected, read) in (-128_i16..127 + 1).zip(reader.samples()) {
assert_eq!(expected, read.unwrap());
}
}
}
#[test]
fn write_read_i24_is_lossless() {
let mut buffer = io::Cursor::new(Vec::new());
let write_spec = WavSpec {
channels: 16,
sample_rate: 96000,
bits_per_sample: 24,
sample_format: SampleFormat::Int,
};
// Write `i32` samples, but with at most 24 bits per sample.
{
let mut writer = WavWriter::new(&mut buffer, write_spec).unwrap();
for s in -128_i32..127 + 1 {
writer.write_sample(s * 256 * 256).unwrap();
}
writer.finalize().unwrap();
}
// Then read them into `i32`. This should extend the sign in the correct
// manner.
{
buffer.set_position(0);
let mut reader = WavReader::new(&mut buffer).unwrap();
assert_eq!(write_spec, reader.spec());
assert_eq!(reader.len(), 256);
for (expected, read) in (-128_i32..127 + 1)
.map(|x| x * 256 * 256)
.zip(reader.samples()) {
assert_eq!(expected, read.unwrap());
}
}
}
#[test]
fn write_read_f32_is_lossless() {
let mut buffer = io::Cursor::new(Vec::new());
let write_spec = WavSpec {
channels: 2,
sample_rate: 44100,
bits_per_sample: 32,
sample_format: SampleFormat::Float,
};
{
let mut writer = WavWriter::new(&mut buffer, write_spec).unwrap();
for s in 1_u32..257 {
writer.write_sample(1.0f32 / s as f32).unwrap();
}
writer.finalize().unwrap();
}
{
buffer.set_position(0);
let mut reader = WavReader::new(&mut buffer).unwrap();
assert_eq!(write_spec, reader.spec());
assert_eq!(reader.len(), 256);
for (expected, read) in (1..257)
.map(|x| 1.0_f32 / x as f32)
.zip(reader.samples()) {
assert_eq!(expected, read.unwrap());
}
}
}
#[test]
#[should_panic]
fn no_32_bps_for_float_sample_format_panics() {
let mut buffer = io::Cursor::new(Vec::new());
let write_spec = WavSpec {
channels: 2,
sample_rate: 44100,
bits_per_sample: 16, // will panic, because value must be 32 for floating point
sample_format: SampleFormat::Float,
};
WavWriter::new(&mut buffer, write_spec).unwrap();
}
#[test]
fn flush_should_produce_valid_file() {
use std::mem;
use std::io::Seek;
let mut buffer = io::Cursor::new(Vec::new());
let samples = &[2, 4, 5, 7, 11, 13];
{
let spec = WavSpec {
channels: 2,
sample_rate: 44100,
bits_per_sample: 16,
sample_format: SampleFormat::Int,
};
let mut writer = WavWriter::new(&mut buffer, spec).unwrap();
for &x in samples {
writer.write_sample(x).unwrap();
}
// We should be able to see everything up to the flush later.
writer.flush().unwrap();
// Write more samples. These should be in the buffer, but not read by the
// reader if we don't finalize the writer.
writer.write_sample(17).unwrap();
writer.write_sample(19).unwrap();
mem::forget(writer);
}
buffer.seek(io::SeekFrom::Start(0)).unwrap();
let mut reader = WavReader::new(&mut buffer).unwrap();
let read_samples: Vec<i16> = reader.samples()
.map(|r| r.unwrap())
.collect();
// We expect to see all samples up to the flush, but not the later ones.
assert_eq!(&read_samples[..], &samples[..]);
}
#[test]
fn new_append_should_append() {
use std::io::Seek;
let mut buffer = io::Cursor::new(Vec::new());
let samples = &[2, 5, 7, 11];
let spec = WavSpec {
channels: 2,
sample_rate: 44100,
bits_per_sample: 16,
sample_format: SampleFormat::Int,
};
// Write initial file.
{
let mut writer = WavWriter::new(&mut buffer, spec).unwrap();
for s in samples { writer.write_sample(*s).unwrap(); }
}
buffer.seek(io::SeekFrom::Start(0)).unwrap();
// Append samples (the same ones a second time).
{
let mut writer = WavWriter::new_append(&mut buffer).unwrap();
assert_eq!(writer.spec(), spec);
for s in samples { writer.write_sample(*s).unwrap(); }
}
buffer.seek(io::SeekFrom::Start(0)).unwrap();
let mut reader = WavReader::new(&mut buffer).unwrap();
let read_samples: Vec<i16> = reader.samples()
.map(|r| r.unwrap())
.collect();
// We expect to see all samples up to the flush, but not the later ones.
assert_eq!(&read_samples[..], &[2, 5, 7, 11, 2, 5, 7, 11]);
}
#[test]
fn new_append_does_not_corrupt_files() {
use std::io::Read;
use std::fs;
let sample_files = [
"testsamples/pcmwaveformat-16bit-44100Hz-mono-extra.wav",
"testsamples/pcmwaveformat-16bit-44100Hz-mono.wav",
"testsamples/pcmwaveformat-8bit-44100Hz-mono.wav",
"testsamples/pop.wav",
"testsamples/waveformatex-16bit-44100Hz-mono-extra.wav",
"testsamples/waveformatex-16bit-44100Hz-mono.wav",
"testsamples/waveformatex-16bit-44100Hz-stereo.wav",
"testsamples/waveformatextensible-24bit-192kHz-mono.wav",
"testsamples/waveformatextensible-32bit-48kHz-stereo.wav",
"testsamples/nonstandard-01.wav",
"testsamples/nonstandard-02.wav",
"testsamples/waveformatex-8bit-11025Hz-mono.wav",
];
for fname in &sample_files {
print!("testing {} ... ", fname);
let mut buffer = Vec::new();
let mut f = fs::File::open(fname).unwrap();
f.read_to_end(&mut buffer).unwrap();
let samples_orig: Vec<i32>;
let samples_after: Vec<i32>;
// Read samples first.
let mut cursor = io::Cursor::new(buffer);
{
let mut reader = WavReader::new(&mut cursor).unwrap();
samples_orig = reader.samples().map(|r| r.unwrap()).collect();
}
buffer = cursor.into_inner();
// Open in append mode and append one sample.
let mut cursor = io::Cursor::new(buffer);
{
let mut writer = WavWriter::new_append(&mut cursor).unwrap();
writer.write_sample(41_i8).unwrap();
writer.write_sample(43_i8).unwrap();
}
buffer = cursor.into_inner();
{
let cursor = io::Cursor::new(buffer);
let mut reader = WavReader::new(cursor)
.expect("Reading wav failed after append.");
samples_after = reader.samples().map(|r| r.unwrap()).collect();
}
assert_eq!(&samples_orig[..], &samples_after[..samples_orig.len()]);
assert_eq!(samples_after[samples_after.len() - 2], 41_i32);
assert_eq!(samples_after[samples_after.len() - 1], 43_i32);
println!("ok");
}
}
#[cfg(test)]
fn assert_contents(fname: &str, expected: &[i16]) {
let mut reader = WavReader::open(fname).unwrap();
let samples: Vec<i16> = reader.samples().map(|s| s.unwrap()).collect();
assert_eq!(&samples[..], expected);
}
#[test]
fn append_works_on_files() {
use std::fs;
let spec = WavSpec {
channels: 1,
sample_rate: 44100,
bits_per_sample: 16,
sample_format: SampleFormat::Int,
};
let mut writer = WavWriter::create("append.wav", spec).unwrap();
writer.write_sample(11_i16).unwrap();
writer.write_sample(13_i16).unwrap();
writer.write_sample(17_i16).unwrap();
writer.finalize().unwrap();
assert_contents("append.wav", &[11, 13, 17]);
let len = fs::metadata("append.wav").unwrap().len();
let mut appender = WavWriter::append("append.wav").unwrap();
appender.write_sample(19_i16).unwrap();
appender.write_sample(23_i16).unwrap();
appender.finalize().unwrap();
// We appended four bytes of audio data (2 16-bit samples), so the file
// should have grown by 4 bytes.
assert_eq!(fs::metadata("append.wav").unwrap().len(), len + 4);
assert_contents("append.wav", &[11, 13, 17, 19, 23]);
}
#[cfg(test)]
#[test]
fn test_into_header_for_infinite_file() {
let spec = WavSpec {
bits_per_sample: 16,
channels: 1,
sample_format: SampleFormat::Int,
sample_rate: 16000,
};
let v = spec.into_header_for_infinite_file();
assert_eq!(&v[..], &b"RIFF\xFF\xFF\xFF\xFFWAVE\
fmt \x10\x00\x00\x00\x01\x00\x01\x00\x80\x3e\x00\x00\x00\x7d\x00\x00\x02\x00\x10\x00\
data\xFF\xFF\xFF\xFF"[..]);
let spec = WavSpec {
bits_per_sample: 16,
channels: 10,
sample_format: SampleFormat::Int,
sample_rate: 16000,
};
let v = spec.into_header_for_infinite_file();
assert_eq!(&v[..], &b"RIFF\xFF\xFF\xFF\xFFWAVE\
fmt \x28\x00\x00\x00\xfe\xff\x0a\x00\x80\x3e\x00\x00\x00\xe2\x04\x00\
\x14\x00\x10\x00\x16\x00\x10\x00\xff\x03\x00\x00\x01\x00\x00\x00\
\x00\x00\x10\x00\x80\x00\x00\xaa\x00\x38\x9b\x71\
data\xFF\xFF\xFF\xFF"[..]);
}