rodio/src/decoder/wav.rs
Aaron Kofsky d20a3c661e Fix reading WAV of different encodings. (#199)
* Read 32 bit float and 24 bit int encoded files correctly.

Before, we were assuming that all WAV files are encoded as 16 bit integers.
This is not true, WAVs can actually also be encoded as 32 bit floats and
24 bit integers. Hound returns `Err` when we attempt to read files as the
wrong encoding, which was getting `unwrap_or`'d to just zero. This explains
why the file would load but not play any sound. We fix this by first
checking for the appropriate encoding, and then converting to an i16. This
might cause some slight dither issues but hopefully it is quiet enough that
it doesn't matter.

Fixes #195.

* Add tests of reading wav files different encodings

* Use `.map()` instead of control flow & unwrapping.

This makes things slightly cleaner to read.
Also remove extraneous print statement.
2018-11-09 21:58:43 +01:00

173 lines
4.2 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use std::io::{Read, Seek, SeekFrom};
use std::time::Duration;
use Source;
use hound::{SampleFormat, WavReader};
/// Decoder for the WAV format.
pub struct WavDecoder<R>
where
R: Read + Seek,
{
reader: SamplesIterator<R>,
sample_rate: u32,
channels: u16,
}
impl<R> WavDecoder<R>
where
R: Read + Seek,
{
/// Attempts to decode the data as WAV.
pub fn new(mut data: R) -> Result<WavDecoder<R>, R> {
if !is_wave(data.by_ref()) {
return Err(data);
}
let reader = WavReader::new(data).unwrap();
let spec = reader.spec();
let reader = SamplesIterator {
reader: reader,
samples_read: 0,
};
Ok(WavDecoder {
reader: reader,
sample_rate: spec.sample_rate,
channels: spec.channels,
})
}
}
struct SamplesIterator<R>
where
R: Read + Seek,
{
reader: WavReader<R>,
samples_read: u32,
}
impl<R> Iterator for SamplesIterator<R>
where
R: Read + Seek,
{
type Item = i16;
#[inline]
fn next(&mut self) -> Option<i16> {
let spec = self.reader.spec();
match (spec.sample_format, spec.bits_per_sample) {
(SampleFormat::Float, 32) => self.reader.samples().next().map(|value| {
self.samples_read += 1;
f32_to_i16(value.unwrap_or(0.0))
}),
(SampleFormat::Int, 16) => self.reader.samples().next().map(|value| {
self.samples_read += 1;
value.unwrap_or(0)
}),
(SampleFormat::Int, 24) => self.reader.samples().next().map(|value| {
self.samples_read += 1;
i24_to_i16(value.unwrap_or(0))
}),
(sample_format, bits_per_sample) => panic!(
"Unimplemented wav spec: {:?}, {}",
sample_format, bits_per_sample
),
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let len = (self.reader.len() - self.samples_read) as usize;
(len, Some(len))
}
}
impl<R> ExactSizeIterator for SamplesIterator<R>
where
R: Read + Seek,
{
}
impl<R> Source for WavDecoder<R>
where
R: Read + Seek,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
None
}
#[inline]
fn channels(&self) -> u16 {
self.channels
}
#[inline]
fn sample_rate(&self) -> u32 {
self.sample_rate
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
let ms = self.len() * 1000 / (self.channels as usize * self.sample_rate as usize);
Some(Duration::from_millis(ms as u64))
}
}
impl<R> Iterator for WavDecoder<R>
where
R: Read + Seek,
{
type Item = i16;
#[inline]
fn next(&mut self) -> Option<i16> {
self.reader.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.reader.size_hint()
}
}
impl<R> ExactSizeIterator for WavDecoder<R>
where
R: Read + Seek,
{
}
/// Returns true if the stream contains WAV data, then resets it to where it was.
fn is_wave<R>(mut data: R) -> bool
where
R: Read + Seek,
{
let stream_pos = data.seek(SeekFrom::Current(0)).unwrap();
if WavReader::new(data.by_ref()).is_err() {
data.seek(SeekFrom::Start(stream_pos)).unwrap();
return false;
}
data.seek(SeekFrom::Start(stream_pos)).unwrap();
true
}
/// Returns a 32 bit WAV float as an i16. WAV floats are typically in the range of
/// [-1.0, 1.0] while i16s are in the range [-32768, 32767]. Note that this
/// function definitely causes precision loss but hopefully this isn't too
/// audiable when actually playing?
fn f32_to_i16(f: f32) -> i16 {
// prefer to clip the input rather than be excessively loud.
(f.max(-1.0).min(1.0) * i16::max_value() as f32) as i16
}
/// Returns a 24 bit WAV int as an i16. Note that this is a 24 bit integer, not a
/// 32 bit one. 24 bit ints are in the range [8,388,608, 8,388,607] while i16s
/// are in the range [-32768, 32767]. Note that this function definitely causes
/// precision loss but hopefully this isn't too audiable when actually playing?
fn i24_to_i16(i: i32) -> i16 {
(i >> 8) as i16
}