#![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};
pub trait Sample: Sized {
fn write<W: io::Write>(self, writer: &mut W, bits: u16) -> Result<()>;
fn write_padded<W: io::Write>(self, writer: &mut W, bits: u16, byte_width: u16) -> Result<()>;
fn read<R: io::Read>(reader: &mut R, SampleFormat, bytes: u16, bits: u16) -> Result<Self>;
fn as_i16(self) -> i16;
}
fn signed_from_u8(x: u8) -> i8 {
(x as i16 - 128) as i8
}
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)));
}
}
#[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());
}
#[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());
}
#[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),
_ => 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),
_ => 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),
_ => 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),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SampleFormat {
Float,
Int,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct WavSpec {
pub channels: u16,
pub sample_rate: u32,
pub bits_per_sample: u16,
pub sample_format: SampleFormat,
}
#[derive(Clone, Copy)]
pub struct WavSpecEx {
pub spec: WavSpec,
pub bytes_per_sample: u16,
}
#[derive(Debug)]
pub enum Error {
IoError(io::Error),
FormatError(&'static str),
TooWide,
UnfinishedSample,
Unsupported,
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)
}
}
pub type Result<T> = result::Result<T, Error>;
const KSDATAFORMAT_SUBTYPE_PCM: [u8; 16] = [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80,
0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71];
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 {
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();
v[4] = 0xFF; v[5] = 0xFF; v[6] = 0xFF; v[7] = 0xFF;
if v[16] == 0x10 {
v[0x28] = 0xFF; v[0x29] = 0xFF; v[0x2A] = 0xFF; v[0x2B] = 0xFF;
} else if v[16] == 0x28 {
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,
};
{
let mut writer = WavWriter::new(&mut buffer, write_spec).unwrap();
for s in -128_i16..127 + 1 {
writer.write_sample(s as i8).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 (-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,
};
{
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();
}
{
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, 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();
}
writer.flush().unwrap();
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();
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,
};
{
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();
{
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();
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>;
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();
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();
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"[..]);
}