mirror of
https://github.com/RustAudio/rodio
synced 2024-11-10 06:04:16 +00:00
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.
This commit is contained in:
parent
a55cac78ae
commit
d20a3c661e
7 changed files with 67 additions and 6 deletions
|
@ -3,7 +3,7 @@ use std::time::Duration;
|
|||
|
||||
use Source;
|
||||
|
||||
use hound::WavReader;
|
||||
use hound::{SampleFormat, WavReader};
|
||||
|
||||
/// Decoder for the WAV format.
|
||||
pub struct WavDecoder<R>
|
||||
|
@ -56,11 +56,24 @@ where
|
|||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<i16> {
|
||||
if let Some(value) = self.reader.samples().next() {
|
||||
self.samples_read += 1;
|
||||
Some(value.unwrap_or(0))
|
||||
} else {
|
||||
None
|
||||
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
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -141,3 +154,20 @@ where
|
|||
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
|
||||
}
|
||||
|
|
BIN
tests/audacity16bit.wav
Normal file
BIN
tests/audacity16bit.wav
Normal file
Binary file not shown.
BIN
tests/audacity32bit.wav
Normal file
BIN
tests/audacity32bit.wav
Normal file
Binary file not shown.
BIN
tests/lmms16bit.wav
Normal file
BIN
tests/lmms16bit.wav
Normal file
Binary file not shown.
BIN
tests/lmms24bit.wav
Normal file
BIN
tests/lmms24bit.wav
Normal file
Binary file not shown.
BIN
tests/lmms32bit.wav
Normal file
BIN
tests/lmms32bit.wav
Normal file
Binary file not shown.
31
tests/wav_test.rs
Normal file
31
tests/wav_test.rs
Normal file
|
@ -0,0 +1,31 @@
|
|||
extern crate rodio;
|
||||
|
||||
use std::io::BufReader;
|
||||
|
||||
#[test]
|
||||
fn test_wav_encodings() {
|
||||
// 16 bit wav file exported from Audacity (1 channel)
|
||||
let file = std::fs::File::open("tests/audacity16bit.wav").unwrap();
|
||||
let mut decoder = rodio::Decoder::new(BufReader::new(file)).unwrap();
|
||||
assert!(decoder.any(|x| x != 0)); // Assert not all zeros
|
||||
|
||||
// 16 bit wav file exported from LMMS (2 channels)
|
||||
let file = std::fs::File::open("tests/lmms16bit.wav").unwrap();
|
||||
let mut decoder = rodio::Decoder::new(BufReader::new(file)).unwrap();
|
||||
assert!(decoder.any(|x| x != 0));
|
||||
|
||||
// 24 bit wav file exported from LMMS (2 channels)
|
||||
let file = std::fs::File::open("tests/lmms24bit.wav").unwrap();
|
||||
let mut decoder = rodio::Decoder::new(BufReader::new(file)).unwrap();
|
||||
assert!(decoder.any(|x| x != 0));
|
||||
|
||||
// 32 bit wav file exported from Audacity (1 channel)
|
||||
let file = std::fs::File::open("tests/audacity32bit.wav").unwrap();
|
||||
let mut decoder = rodio::Decoder::new(BufReader::new(file)).unwrap();
|
||||
assert!(decoder.any(|x| x != 0));
|
||||
|
||||
// 32 bit wav file exported from LMMS (2 channels)
|
||||
let file = std::fs::File::open("tests/lmms32bit.wav").unwrap();
|
||||
let mut decoder = rodio::Decoder::new(BufReader::new(file)).unwrap();
|
||||
assert!(decoder.any(|x| x != 0));
|
||||
}
|
Loading…
Reference in a new issue