use std::alloc::Layout;
use std::any::TypeId;
use std::fmt::{Debug, Display};
use std::fs::File;
use std::io::{BufWriter, Read, Seek, SeekFrom, Write};
use std::ops::{Deref, DerefMut};
use std::path::Path;
use bytemuck::cast_slice;
#[cfg(feature = "ndarray")]
use ndarray::{Array, Array2};
use crate::conversion::ConvertSlice;
use crate::conversion::{AudioSample, ConvertTo};
use crate::error::{WaversError, WaversResult};
use crate::header::{read_header, WavHeader, DATA};
use crate::i24;
#[cfg(feature = "pyo3")]
use pyo3::prelude::*;
pub trait ReadSeek: Read + Seek {}
impl ReadSeek for std::fs::File {}
impl<T: ReadSeek> ReadSeek for std::io::BufReader<T> {}
pub struct Wav<T: AudioSample>
where
i16: ConvertTo<T>,
i24: ConvertTo<T>,
i32: ConvertTo<T>,
f32: ConvertTo<T>,
f64: ConvertTo<T>,
Box<[i16]>: ConvertSlice<T>,
Box<[i24]>: ConvertSlice<T>,
Box<[i32]>: ConvertSlice<T>,
Box<[f32]>: ConvertSlice<T>,
Box<[f64]>: ConvertSlice<T>,
{
_phantom: std::marker::PhantomData<T>,
reader: Box<dyn ReadSeek>,
pub wav_info: WavInfo,
}
impl<T: AudioSample> Wav<T>
where
i16: ConvertTo<T>,
i24: ConvertTo<T>,
i32: ConvertTo<T>,
f32: ConvertTo<T>,
f64: ConvertTo<T>,
Box<[i16]>: ConvertSlice<T>,
Box<[i24]>: ConvertSlice<T>,
Box<[i32]>: ConvertSlice<T>,
Box<[f32]>: ConvertSlice<T>,
Box<[f64]>: ConvertSlice<T>,
{
pub fn new(mut reader: Box<dyn ReadSeek>) -> WaversResult<Self> {
let wav_info = read_header(&mut reader)?;
let (data_offset, _) = wav_info.wav_header.data().into();
reader.seek(SeekFrom::Start(data_offset as u64))?;
let (_, _, _n_bytes) = wav_info.wav_type.into();
Ok(Self {
_phantom: std::marker::PhantomData,
reader,
wav_info,
})
}
pub fn from_path<P: AsRef<Path>>(path: P) -> WaversResult<Self> {
let f = std::fs::File::open(path)?;
let buf_reader: Box<dyn ReadSeek> = Box::new(std::io::BufReader::new(f));
Self::new(buf_reader)
}
#[inline(always)]
pub fn read(&mut self) -> WaversResult<Samples<T>> {
let (data_offset, data_size_bytes) = self.header().data().into();
let native_type = self.wav_info.wav_type;
let native_size_bytes: usize = native_type.into();
let number_of_samples_already_read = (self.reader.seek(SeekFrom::Current(0))?
- data_offset as u64)
/ native_size_bytes as u64;
let n_samples = data_size_bytes as usize / native_size_bytes;
let samples = self.read_samples(n_samples - number_of_samples_already_read as usize)?;
self.reader.seek(SeekFrom::Start(data_offset as u64))?;
Ok(samples)
}
#[inline(always)]
pub fn read_samples(&mut self, n_samples: usize) -> WaversResult<Samples<T>> {
let native_type = self.wav_info.wav_type;
let native_size_bytes: usize = native_type.into();
let n_native_bytes: usize = n_samples * native_size_bytes;
let mut samples = alloc_box_buffer(n_native_bytes);
self.reader.read_exact(&mut samples)?;
let wav_type_from_file = self.wav_info.wav_type;
let desired_type: WavType = TypeId::of::<T>().try_into()?;
if wav_type_from_file == desired_type {
return Ok(Samples::from(cast_slice::<u8, T>(&samples)));
}
match wav_type_from_file {
WavType::Pcm16 => {
let samples: &[i16] = cast_slice::<u8, i16>(&samples);
Ok(Samples::from(samples).convert())
}
WavType::Pcm24 => {
let samples: &[i24] = cast_slice::<u8, i24>(&samples);
Ok(Samples::from(samples).convert())
}
WavType::Pcm32 => {
let samples: &[i32] = cast_slice::<u8, i32>(&samples);
Ok(Samples::from(samples).convert())
}
WavType::Float32 => {
let samples: &[f32] = cast_slice::<u8, f32>(&samples);
Ok(Samples::from(samples).convert())
}
WavType::Float64 => {
let samples: &[f64] = cast_slice::<u8, f64>(&samples);
Ok(Samples::from(samples).convert())
}
}
}
#[inline(always)]
pub fn write<F: AudioSample, P: AsRef<Path>>(&mut self, p: P) -> WaversResult<()>
where
T: ConvertTo<F>,
Box<[T]>: ConvertSlice<F>,
{
let samples = self.read()?.convert::<F>();
let sample_bytes = samples.as_bytes();
let fmt_chunk = self.wav_info.wav_header.fmt_chunk;
self.wav_info.wav_header =
WavHeader::new_header::<F>(fmt_chunk.sample_rate, fmt_chunk.channels, samples.len())?;
let header_bytes = self.header().as_bytes();
let f = std::fs::File::create(p)?;
let mut buf_writer: BufWriter<File> = BufWriter::new(f);
let data_size_bytes = sample_bytes.len() as u32;
buf_writer.write_all(&header_bytes)?;
buf_writer.write_all(&DATA)?;
buf_writer.write_all(&data_size_bytes.to_ne_bytes())?; buf_writer.write_all(&sample_bytes)?; Ok(())
}
pub fn header(&self) -> &WavHeader {
&self.wav_info.wav_header
}
pub fn header_mut(&mut self) -> &mut WavHeader {
&mut self.wav_info.wav_header
}
pub fn encoding(&self) -> WavType {
self.wav_info.wav_type
}
pub fn sample_rate(&self) -> i32 {
self.header().fmt_chunk.sample_rate
}
pub fn n_channels(&self) -> u16 {
self.header().fmt_chunk.channels
}
pub fn n_samples(&self) -> usize {
let (_, native_data_size_bytes) = self.header().data().into();
let size_of_native_bytes = self.header().fmt_chunk.bits_per_sample as usize / 8;
native_data_size_bytes as usize / size_of_native_bytes
}
pub fn duration(&self) -> u32 {
let data_size = self.header().data().size;
let sample_rate = self.sample_rate() as u32;
let n_channels = self.n_channels() as u32;
let bytes_per_sample = (self.header().fmt_chunk.bits_per_sample / 8) as u32;
data_size / (sample_rate * n_channels * bytes_per_sample)
}
pub fn wav_spec(&self) -> (i32, u16, u32, WavType) {
let sample_rate = self.sample_rate();
let n_channels = self.n_channels();
let duration = self.duration();
(sample_rate, n_channels, duration, self.encoding())
}
}
pub fn wav_spec<P: AsRef<Path>>(p: P) -> WaversResult<(i32, u16, u32, WavType)> {
let wav = Wav::<i16>::from_path(p)?;
Ok(wav.wav_spec())
}
#[cfg(not(feature = "pyo3"))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct WavInfo {
pub wav_type: WavType, pub wav_header: WavHeader,
}
#[cfg(feature = "pyo3")]
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct WavInfo {
pub wav_type: WavType, pub wav_header: WavHeader,
}
impl Into<(WavHeader, WavType)> for WavInfo {
fn into(self) -> (WavHeader, WavType) {
(self.wav_header, self.wav_type)
}
}
impl TryInto<WavType> for TypeId {
type Error = WaversError;
fn try_into(self) -> Result<WavType, Self::Error> {
if self == TypeId::of::<i16>() {
Ok(WavType::Pcm16)
} else if self == TypeId::of::<i24>() {
Ok(WavType::Pcm24)
} else if self == TypeId::of::<i32>() {
Ok(WavType::Pcm32)
} else if self == TypeId::of::<f32>() {
Ok(WavType::Float32)
} else if self == TypeId::of::<f64>() {
Ok(WavType::Float64)
} else {
Err(WaversError::InvalidType(format!("Invalid type {:?}", self)))
}
}
}
impl Into<usize> for WavType {
fn into(self) -> usize {
match self {
WavType::Pcm16 => std::mem::size_of::<i16>(),
WavType::Pcm24 => std::mem::size_of::<i24>(),
WavType::Pcm32 => std::mem::size_of::<i32>(),
WavType::Float32 => std::mem::size_of::<f32>(),
WavType::Float64 => std::mem::size_of::<f64>(),
}
}
}
#[cfg(not(feature = "pyo3"))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WavType {
Pcm16,
Pcm24,
Pcm32,
Float32,
Float64,
}
#[cfg(feature = "pyo3")]
#[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WavType {
Pcm16,
Pcm24,
Pcm32,
Float32,
Float64,
}
impl Display for WavType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
WavType::Pcm16 => write!(f, "PCM16"),
WavType::Pcm24 => write!(f, "PCM24"),
WavType::Pcm32 => write!(f, "PCM32"),
WavType::Float32 => write!(f, "Float32"),
WavType::Float64 => write!(f, "Float64"),
}
}
}
impl TryFrom<(u16, u16)> for WavType {
type Error = WaversError;
fn try_from(value: (u16, u16)) -> Result<Self, Self::Error> {
Ok(match value {
(1, 16) => WavType::Pcm16,
(1, 24) => WavType::Pcm24,
(1, 32) => WavType::Pcm32,
(3, 32) => WavType::Float32,
(3, 64) => WavType::Float64,
_ => {
return Err(WaversError::InvalidType(
format!(
"Invalid wav type. Unsupported type {}, and number of bytes per samples {}",
value.0, value.1
)
.into(),
))
}
})
}
}
impl Into<TypeId> for WavType {
fn into(self) -> TypeId {
match self {
WavType::Pcm16 => TypeId::of::<i16>(),
WavType::Pcm24 => TypeId::of::<i24>(),
WavType::Pcm32 => TypeId::of::<i32>(),
WavType::Float32 => TypeId::of::<f32>(),
WavType::Float64 => TypeId::of::<f64>(),
}
}
}
impl Into<(u16, u16, u16)> for WavType {
fn into(self) -> (u16, u16, u16) {
match self {
WavType::Pcm16 => (1, 16, 2),
WavType::Pcm24 => (1, 24, 3),
WavType::Pcm32 => (1, 32, 4),
WavType::Float32 => (3, 32, 4),
WavType::Float64 => (3, 64, 8),
}
}
}
#[cfg(feature = "ndarray")]
use crate::conversion::{AsNdarray, IntoNdarray};
#[cfg(feature = "ndarray")]
impl<T: AudioSample> IntoNdarray for Wav<T>
where
i16: ConvertTo<T>,
i24: ConvertTo<T>,
i32: ConvertTo<T>,
f32: ConvertTo<T>,
f64: ConvertTo<T>,
{
type Target = T;
fn into_ndarray(mut self) -> WaversResult<(Array2<Self::Target>, i32)> {
let n_channels = self.header().fmt_chunk.channels as usize;
let copied_data: &[T] = &self.read()?.samples;
let length = copied_data.len();
let shape = (length / n_channels, n_channels);
let arr: Array2<T> = Array::from_shape_vec(shape, copied_data.to_vec())?;
Ok((arr, self.sample_rate()))
}
}
#[cfg(feature = "ndarray")]
impl<T: AudioSample> AsNdarray for Wav<T>
where
i16: ConvertTo<T>,
i24: ConvertTo<T>,
i32: ConvertTo<T>,
f32: ConvertTo<T>,
f64: ConvertTo<T>,
{
type Target = T;
fn as_ndarray(&mut self) -> WaversResult<(Array2<Self::Target>, i32)> {
let n_channels = self.header().fmt_chunk.channels as usize;
let copied_data: Box<[T]> = self.read()?.samples.to_owned();
let copied_data: &[T] = &copied_data;
let length = copied_data.len();
let shape = (length / n_channels, n_channels);
let arr: Array2<T> = Array::from_shape_vec(shape, copied_data.to_vec())?;
Ok((arr, self.sample_rate()))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Samples<T>
where
T: AudioSample,
{
pub(crate) samples: Box<[T]>,
}
impl<T> AsRef<[T]> for Samples<T>
where
T: AudioSample,
{
fn as_ref(&self) -> &[T] {
&self.samples
}
}
impl<T> AsMut<[T]> for Samples<T>
where
T: AudioSample,
{
fn as_mut(&mut self) -> &mut [T] {
&mut self.samples
}
}
impl<T> Deref for Samples<T>
where
T: AudioSample,
{
type Target = [T];
fn deref(&self) -> &Self::Target {
&self.samples
}
}
impl<T> DerefMut for Samples<T>
where
T: AudioSample,
{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.samples
}
}
impl<T> From<Vec<T>> for Samples<T>
where
T: AudioSample,
{
fn from(samples: Vec<T>) -> Self {
Samples {
samples: samples.into_boxed_slice(),
}
}
}
impl<T> From<&[T]> for Samples<T>
where
T: AudioSample,
{
fn from(samples: &[T]) -> Self {
Samples {
samples: Box::from(samples),
}
}
}
impl<T> From<Box<[T]>> for Samples<T>
where
T: AudioSample,
{
fn from(samples: Box<[T]>) -> Self {
Samples { samples }
}
}
impl<T> From<&[u8]> for Samples<T>
where
T: AudioSample,
{
fn from(bytes: &[u8]) -> Self {
let casted_samples: &[T] = cast_slice::<u8, T>(bytes);
Samples {
samples: Box::from(casted_samples),
}
}
}
impl<T> Display for Samples<T>
where
T: AudioSample + Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", &self.samples)
}
}
impl<T> Samples<T>
where
T: AudioSample,
{
pub fn new(samples: Box<[T]>) -> Self {
Self { samples }
}
#[inline(always)]
pub fn convert<F: AudioSample>(self) -> Samples<F>
where
T: ConvertTo<F>,
Box<[T]>: ConvertSlice<F>,
{
if TypeId::of::<T>() == TypeId::of::<F>() {
let data: Box<[T]> = self.samples.clone();
return Samples {
samples: Box::from(cast_slice::<T, F>(&data)),
};
}
let converted_samples = self.samples.convert_slice();
Samples {
samples: converted_samples,
}
}
pub fn as_bytes(&self) -> &[u8] {
cast_slice::<T, u8>(&self.samples)
}
}
impl Samples<i16> {}
impl Samples<i24> {}
impl Samples<i32> {}
impl Samples<f32> {}
impl Samples<f64> {}
pub(crate) fn alloc_box_buffer(len: usize) -> Box<[u8]> {
if len == 0 {
return <Box<[u8]>>::default();
}
let layout = match Layout::array::<u8>(len) {
Ok(layout) => layout,
Err(_) => panic!("Failed to allocate buffer of size {}", len),
};
let ptr = unsafe { std::alloc::alloc(layout) };
let slice_ptr = core::ptr::slice_from_raw_parts_mut(ptr, len);
unsafe { Box::from_raw(slice_ptr) }
}
pub(crate) fn alloc_sample_buffer<T>(len: usize) -> Box<[T]>
where
T: AudioSample + Copy + Debug,
{
if len == 0 {
return <Box<[T]>>::default();
}
let layout = match Layout::array::<T>(len) {
Ok(layout) => layout,
Err(_) => panic!("Failed to allocate buffer of size {}", len),
};
let ptr = unsafe { std::alloc::alloc(layout) as *mut T };
let slice_ptr: *mut [T] = core::ptr::slice_from_raw_parts_mut(ptr, len);
unsafe { Box::from_raw(slice_ptr) }
}
#[cfg(test)]
mod core_tests {
use super::*;
#[cfg(feature = "ndarray")]
use crate::IntoNdarray;
use approx_eq::assert_approx_eq;
use std::{fs::File, io::BufRead, path::Path, str::FromStr};
const ONE_CHANNEL_WAV_I16: &str = "./test_resources/one_channel_i16.wav";
const TWO_CHANNEL_WAV_I16: &str = "./test_resources/two_channel_i16.wav";
const ONE_CHANNEL_WAV_I24: &str = "./test_resources/one_channel_i24.wav";
const ONE_CHANNEL_EXPECTED_I24: &str = "./test_resources/one_channel_i24.txt";
const ONE_CHANNEL_EXPECTED_I16: &str = "./test_resources/one_channel_i16.txt";
const ONE_CHANNEL_EXPECTED_F32: &str = "./test_resources/one_channel_f32.txt";
const TEST_OUTPUT: &str = "./test_resources/tmp/";
#[test]
pub fn duration_one_channel() {
let wav: Wav<i16> = Wav::from_path(ONE_CHANNEL_WAV_I16).unwrap();
let duration = wav.duration();
assert_eq!(duration, 10, "Expected duration of 10 seconds");
}
#[test]
pub fn duration_two_channel() {
let wav: Wav<i16> = Wav::from_path(TWO_CHANNEL_WAV_I16).unwrap();
let duration = wav.duration();
assert_eq!(duration, 10, "Expected duration of 10 seconds");
}
#[test]
pub fn n_samples_after_convert() {
let wav: Wav<f32> = Wav::from_path("test_resources/n_samples_test.wav").unwrap();
assert_eq!(wav.n_samples(), 2048, "Expected 2048 samples");
}
#[test]
fn i16_i32_convert() {
let mut wav: Wav<i32> = Wav::from_path(ONE_CHANNEL_WAV_I16).unwrap();
let wav_i32: &[i32] = &wav.read().unwrap();
let expected_i32_samples: &[i32] =
&Wav::<i32>::from_path("test_resources/one_channel_i32.wav")
.unwrap()
.read()
.unwrap();
assert_eq!(
expected_i32_samples.len(),
wav.n_samples(),
"Lengths not equal"
);
for (expected, actual) in expected_i32_samples.iter().zip(wav_i32.iter()) {
assert_eq!(*expected, *actual, "{} != {}", expected, actual);
}
}
#[cfg(feature = "ndarray")]
#[test]
fn wav_as_ndarray() {
let wav: Wav<i16> =
Wav::<i16>::from_path(ONE_CHANNEL_WAV_I16).expect("Failed to read file");
let expected_wav: Vec<i16> = read_text_to_vec(Path::new(ONE_CHANNEL_EXPECTED_I16)).unwrap();
let (arr, sr) = wav.into_ndarray().unwrap();
assert_eq!(arr.shape()[0], 1, "Expected 1 channels");
for (expected, actual) in expected_wav.iter().zip(arr) {
assert_eq!(*expected, actual, "{} != {}", expected, actual);
}
}
#[cfg(feature = "ndarray")]
#[test]
fn two_channel_as_ndarray() {
let wav: Wav<i16> =
Wav::<i16>::from_path(TWO_CHANNEL_WAV_I16).expect("Failed to open file");
let expected_wav: Vec<i16> = read_text_to_vec(Path::new(ONE_CHANNEL_EXPECTED_I16)).unwrap();
let mut new_expected = Vec::with_capacity(expected_wav.len() * 2);
for sample in expected_wav {
new_expected.push(sample);
new_expected.push(sample);
}
let expected_wav = new_expected;
let (two_channel_arr, sr): (Array2<i16>, i32) = wav.into_ndarray().unwrap();
assert_eq!(two_channel_arr.shape()[0], 2, "Expected 2 channels");
for (expected, actual) in expected_wav.iter().zip(two_channel_arr) {
assert_eq!(*expected, actual, "{} != {}", expected, actual);
}
}
#[test]
fn primitive_to_u8_slice() {
let mut test_data: Vec<i16> = Vec::with_capacity(8);
for i in 0..8 {
test_data.push(i);
}
let s_data: &[i16] = test_data.as_slice();
let samples: Samples<i16> = Samples::from(s_data);
let bytes = samples.as_bytes();
let mut expected_bytes: Vec<u8> = Vec::with_capacity(16);
for i in 0..8i16 {
let b: [u8; 2] = i.to_ne_bytes();
expected_bytes.extend_from_slice(&b);
}
for (expected, actual) in expected_bytes.iter().zip(bytes.iter()) {
assert_eq!(
*expected, *actual,
"Expected: {}, Actual: {}",
expected, actual
);
}
}
#[test]
fn read_and_convert() {
let expected_samples =
read_text_to_vec::<f32>(Path::new(ONE_CHANNEL_EXPECTED_F32)).unwrap();
let mut wav: Wav<f32> = Wav::from_path(ONE_CHANNEL_WAV_I16).unwrap();
let samples: &[f32] = &wav.read().unwrap();
assert_eq!(wav.n_samples(), expected_samples.len(), "Lengths not equal");
for (expected, actual) in expected_samples.iter().zip(samples) {
assert_approx_eq!(*expected as f64, *actual as f64, 1e-4);
}
}
#[test]
fn convert_write_read() {
if !Path::new(TEST_OUTPUT).exists() {
std::fs::create_dir(Path::new(TEST_OUTPUT)).unwrap();
}
let mut wav: Wav<f32> = Wav::<f32>::from_path(ONE_CHANNEL_WAV_I16).unwrap();
let out_fp = format!("{}{}", TEST_OUTPUT, "convert_write_read.wav");
wav.write::<f32, _>(Path::new(&out_fp)).unwrap();
let mut wav: Wav<f32> = Wav::<f32>::from_path(&out_fp).unwrap();
let actual_samples: &[f32] = &wav.read().unwrap();
let expected_samples =
read_text_to_vec::<f32>(Path::new(ONE_CHANNEL_EXPECTED_F32)).unwrap();
assert_eq!(wav.n_samples(), expected_samples.len(), "Lengths not equal");
for (expected, actual) in expected_samples.iter().zip(actual_samples) {
assert_approx_eq!(*expected as f64, *actual as f64, 1e-4);
}
std::fs::remove_file(Path::new(&out_fp)).unwrap();
}
#[test]
fn can_read_two_channel() {
let mut wav: Wav<i16> = Wav::<i16>::from_path(TWO_CHANNEL_WAV_I16).unwrap();
let expected_samples =
read_text_to_vec::<i16>(Path::new(ONE_CHANNEL_EXPECTED_I16)).unwrap();
let mut new_expected = Vec::with_capacity(expected_samples.len() * 2);
for sample in expected_samples {
new_expected.push(sample);
new_expected.push(sample);
}
let expected_samples = new_expected;
assert_eq!(wav.n_samples(), expected_samples.len(), "Lengths not equal");
for (expected, actual) in expected_samples.iter().zip(wav.read().unwrap().as_ref()) {
assert_eq!(*expected, *actual, "{} != {}", expected, actual);
}
}
#[test]
fn read_some_then_read_remainder() {
let mut wav: Wav<i16> =
Wav::from_path(ONE_CHANNEL_WAV_I16).expect("Failed to open wav file");
let first_second_samples = wav.read_samples(wav.sample_rate() as usize).unwrap();
assert_eq!(first_second_samples.len(), wav.sample_rate() as usize);
let remaining_samples = wav.read().unwrap();
assert_eq!(
remaining_samples.len(),
wav.n_samples() - wav.sample_rate() as usize
);
let all_samples = wav.read().unwrap();
assert_eq!(all_samples.len(), wav.n_samples());
}
#[test]
fn read_i24_correctly() {
let mut wav: Wav<i24> = Wav::from_path(ONE_CHANNEL_WAV_I24).expect("Failed to open file");
let samples: &[i24] = &wav.read().unwrap();
let expected_samples: Vec<i32> =
read_text_to_vec(Path::new(ONE_CHANNEL_EXPECTED_I24)).unwrap();
let expected_samples: Box<[i32]> = expected_samples.into_boxed_slice();
let expected_samples: Box<[i24]> = expected_samples.convert_slice();
assert_eq!(samples.len(), expected_samples.len(), "Lengths not equal");
for (idx, (expected, actual)) in expected_samples.iter().zip(samples).enumerate() {
assert_eq!(*expected, *actual, "{} != {} at {}", expected, actual, idx);
}
}
#[test]
fn write_i24_correctly() {
let mut wav: Wav<i16> = Wav::from_path(ONE_CHANNEL_WAV_I16).unwrap();
let out_fp = format!("{}{}", TEST_OUTPUT, "write_i24_correctly.wav");
wav.write::<i24, _>(Path::new(&out_fp)).unwrap();
let mut wav: Wav<i24> = Wav::from_path(&out_fp).unwrap();
let samples: &[i24] = &wav.read().unwrap();
let expected_samples: Vec<i32> =
read_text_to_vec(Path::new(ONE_CHANNEL_EXPECTED_I24)).unwrap();
let expected_samples: Box<[i32]> = expected_samples.into_boxed_slice();
let expected_samples: Box<[i24]> = expected_samples.convert_slice();
assert_eq!(samples.len(), expected_samples.len(), "Lengths not equal");
for (idx, (expected, actual)) in expected_samples.iter().zip(samples).enumerate() {
assert_eq!(*expected, *actual, "{} != {} at {}", expected, actual, idx);
}
std::fs::remove_file(Path::new(&out_fp)).unwrap();
}
fn read_lines<P>(filename: P) -> std::io::Result<std::io::Lines<std::io::BufReader<File>>>
where
P: AsRef<Path>,
{
let file = File::open(filename)?;
Ok(std::io::BufReader::new(file).lines())
}
fn read_text_to_vec<T: FromStr>(fp: &Path) -> Result<Vec<T>, Box<dyn std::error::Error>>
where
<T as FromStr>::Err: std::error::Error + 'static,
{
let mut data = Vec::new();
let lines = read_lines(fp)?;
for line in lines {
let line = line?;
for sample in line.split(" ") {
let parsed_sample: T = match sample.trim().parse::<T>() {
Ok(num) => num,
Err(err) => {
eprintln!("Failed to parse {}", sample);
panic!("{}", err)
}
};
data.push(parsed_sample);
}
}
Ok(data)
}
}