#![cfg_attr(not(test), no_std)]
use heapless::Vec;
use num_traits::float::Float;
use winnow::binary::{
be_f32, be_f64, be_i16, be_i24, be_i32, le_f32, le_f64, le_i16, le_i24, le_i32,
};
use winnow::{ModalResult, Parser};
mod aiff;
pub mod imaadpcm;
mod wav;
const MAX_NUM_CHUNKS: usize = 16;
#[derive(Debug, thiserror::Error)]
pub enum PcmReaderError {
#[error("Unsupported bit-depth")]
UnsupportedBitDepth,
#[error("Unsupported audio format")]
UnsupportedAudioFormat,
#[error("Invalid channel")]
InvalidChannel,
#[error("Invalid sample")]
InvalidSample,
#[error("RIFF or AIFF header size mismatch")]
HeaderSizeMismatch,
#[error("fmt parse error")]
FmtParseError,
#[error("Header parse error")]
HeaderParseError,
}
#[derive(Debug, Default, PartialEq, Eq, Clone)]
pub enum AudioFormat {
#[default]
Unknown,
LinearPcmLe,
LinearPcmBe,
IeeeFloatLe,
IeeeFloatBe,
ImaAdpcmLe,
}
#[derive(Default, Debug, Clone)]
pub struct PcmSpecs {
pub audio_format: AudioFormat,
pub num_channels: u16,
pub sample_rate: u32,
pub bit_depth: u16,
pub num_samples: u32,
pub(crate) ima_adpcm_num_block_align: Option<u16>,
pub(crate) ima_adpcm_num_samples_per_block: Option<u16>,
}
#[derive(Default)]
pub struct PcmReader<'a> {
pub(crate) specs: PcmSpecs,
pub(crate) data: &'a [u8],
}
impl<'a> PcmReader<'a> {
pub fn new(input: &mut &'a [u8]) -> Result<Self, PcmReaderError> {
let mut reader = PcmReader {
data: &[],
specs: PcmSpecs::default(),
};
reader.reload(input)?;
Ok(reader)
}
pub fn reload(&mut self, input: &mut &'a [u8]) -> Result<(), PcmReaderError> {
let file_length = input.len();
self.data = &[];
self.specs = PcmSpecs::default();
if let Ok((_, riff)) = wav::parse_riff_header.parse_peek(input) {
if file_length - 8 != riff.size as usize {
return Err(PcmReaderError::HeaderSizeMismatch);
}
return self.parse_wav(input);
};
if let Ok((_, aiff)) = aiff::parse_aiff_header.parse_peek(input) {
if (file_length - 8) != aiff.size as usize {
return Err(PcmReaderError::HeaderSizeMismatch);
}
return self.parse_aiff(input);
};
Err(PcmReaderError::UnsupportedAudioFormat)
}
fn parse_aiff(&mut self, input: &mut &'a [u8]) -> Result<(), PcmReaderError> {
let Ok(_) = aiff::parse_aiff_header.parse_next(input) else {
return Err(PcmReaderError::HeaderParseError);
};
let mut chunk_vec = Vec::<aiff::Chunk, MAX_NUM_CHUNKS>::new();
while let Ok(chunk) = aiff::parse_chunk.parse_next(input) {
chunk_vec.push(chunk).unwrap();
}
for mut chunk in chunk_vec {
match chunk.id {
aiff::ChunkId::Common => {
let Ok(spec) = aiff::parse_comm.parse_next(&mut chunk.data) else {
return Err(PcmReaderError::UnsupportedAudioFormat);
};
self.specs = spec;
}
aiff::ChunkId::SoundData => {
let Ok(_ssnd_block_info) = aiff::parse_ssnd.parse_next(&mut chunk.data) else {
return Err(PcmReaderError::UnsupportedAudioFormat);
};
self.data = chunk.data;
}
aiff::ChunkId::FormatVersion => {}
aiff::ChunkId::Marker => {}
aiff::ChunkId::Instrument => {}
aiff::ChunkId::Midi => {}
aiff::ChunkId::AudioRecording => {}
aiff::ChunkId::ApplicationSpecific => {}
aiff::ChunkId::Comment => {}
aiff::ChunkId::Name => {}
aiff::ChunkId::Author => {}
aiff::ChunkId::Copyright => {}
aiff::ChunkId::Annotation => {}
aiff::ChunkId::Unknown => {}
}
}
Ok(())
}
fn parse_wav(&mut self, input: &mut &'a [u8]) -> Result<(), PcmReaderError> {
let Ok(_) = wav::parse_riff_header.parse_next(input) else {
return Err(PcmReaderError::HeaderParseError);
};
let mut chunk_vec = Vec::<wav::Chunk, MAX_NUM_CHUNKS>::new();
while let Ok(chunk) = wav::parse_chunk.parse_next(input) {
chunk_vec.push(chunk).unwrap();
}
for mut chunk in chunk_vec {
match chunk.id {
wav::ChunkId::Fmt => {
let Ok(spec) = wav::parse_fmt.parse_next(&mut chunk.data) else {
return Err(PcmReaderError::FmtParseError);
};
self.specs.num_channels = spec.num_channels;
self.specs.sample_rate = spec.sample_rate;
self.specs.audio_format = spec.audio_format;
self.specs.bit_depth = spec.bit_depth;
if self.specs.audio_format == AudioFormat::ImaAdpcmLe {
self.specs.ima_adpcm_num_block_align = spec.ima_adpcm_num_block_align;
self.specs.ima_adpcm_num_samples_per_block =
spec.ima_adpcm_num_samples_per_block;
}
}
wav::ChunkId::Data => {
self.data = chunk.data;
}
wav::ChunkId::Fact => {}
wav::ChunkId::IDv3 => {}
wav::ChunkId::Junk => {}
wav::ChunkId::List => {}
wav::ChunkId::Peak => {}
wav::ChunkId::Unknown => {}
}
}
match self.specs.audio_format {
AudioFormat::ImaAdpcmLe => {
self.specs.num_samples =
imaadpcm::calc_num_samples_per_channel(self.data.len() as u32, &self.specs)
.unwrap();
}
AudioFormat::LinearPcmLe | AudioFormat::IeeeFloatLe => {
self.specs.num_samples =
wav::calc_num_samples_per_channel(self.data.len() as u32, &self.specs).unwrap();
}
_ => {
unreachable!();
}
}
Ok(())
}
#[must_use]
pub fn get_pcm_specs(&self) -> PcmSpecs {
self.specs.clone()
}
pub fn read_sample<T: Float>(&self, channel: u16, sample: u32) -> Result<T, PcmReaderError> {
let num_channels = self.specs.num_channels;
if channel >= num_channels {
return Err(PcmReaderError::InvalidChannel);
}
if sample >= self.specs.num_samples {
return Err(PcmReaderError::InvalidSample);
}
let byte_depth = self.specs.bit_depth / 8u16;
let byte_offset = ((byte_depth as u32 * sample * num_channels as u32)
+ (byte_depth * channel) as u32) as usize;
let mut data = &self.data[byte_offset..];
decode_sample(&self.specs, &mut data)
}
}
fn decode_sample<T: Float>(specs: &PcmSpecs, data: &mut &[u8]) -> Result<T, PcmReaderError> {
match specs.audio_format {
AudioFormat::Unknown => Err(PcmReaderError::UnsupportedAudioFormat),
AudioFormat::LinearPcmLe => match specs.bit_depth {
16 => {
const MAX: u32 = 2u32.pow(15);
let res: ModalResult<i16> = le_i16.parse_next(data);
let Ok(sample) = res else {
return Err(PcmReaderError::InvalidSample);
};
Ok(T::from(sample).unwrap() / T::from(MAX).unwrap())
}
24 => {
const MAX: u32 = 2u32.pow(23);
let res: ModalResult<i32> = le_i24.parse_next(data);
let Ok(sample) = res else {
return Err(PcmReaderError::InvalidSample);
};
Ok(T::from(sample).unwrap() / T::from(MAX).unwrap())
}
32 => {
const MAX: u32 = 2u32.pow(31);
let res: ModalResult<i32> = le_i32.parse_next(data);
let Ok(sample) = res else {
return Err(PcmReaderError::InvalidSample);
};
Ok(T::from(sample).unwrap() / T::from(MAX).unwrap())
}
_ => Err(PcmReaderError::UnsupportedBitDepth),
},
AudioFormat::LinearPcmBe => match specs.bit_depth {
16 => {
const MAX: u32 = 2u32.pow(15);
let res: ModalResult<i16> = be_i16.parse_next(data);
let Ok(sample) = res else {
return Err(PcmReaderError::InvalidSample);
};
Ok(T::from(sample).unwrap() / T::from(MAX).unwrap())
}
24 => {
const MAX: u32 = 2u32.pow(23);
let res: ModalResult<i32> = be_i24.parse_next(data);
let Ok(sample) = res else {
return Err(PcmReaderError::InvalidSample);
};
Ok(T::from(sample).unwrap() / T::from(MAX).unwrap())
}
32 => {
const MAX: u32 = 2u32.pow(31);
let res: ModalResult<i32> = be_i32.parse_next(data);
let Ok(sample) = res else {
return Err(PcmReaderError::InvalidSample);
};
Ok(T::from(sample).unwrap() / T::from(MAX).unwrap())
}
_ => Err(PcmReaderError::UnsupportedBitDepth),
},
AudioFormat::IeeeFloatLe => match specs.bit_depth {
32 => {
let res: ModalResult<f32> = le_f32.parse_next(data);
let Ok(sample) = res else {
return Err(PcmReaderError::InvalidSample);
};
Ok(T::from(sample).unwrap())
}
64 => {
let res: ModalResult<f64> = le_f64.parse_next(data);
let Ok(sample) = res else {
return Err(PcmReaderError::InvalidSample);
};
Ok(T::from(sample).unwrap())
}
_ => Err(PcmReaderError::UnsupportedBitDepth),
},
AudioFormat::IeeeFloatBe => match specs.bit_depth {
32 => {
let res: ModalResult<f32> = be_f32.parse_next(data);
let Ok(sample) = res else {
return Err(PcmReaderError::InvalidSample);
};
Ok(T::from(sample).unwrap())
}
64 => {
let res: ModalResult<f64> = be_f64.parse_next(data);
let Ok(sample) = res else {
return Err(PcmReaderError::InvalidSample);
};
Ok(T::from(sample).unwrap())
}
_ => Err(PcmReaderError::UnsupportedBitDepth),
},
AudioFormat::ImaAdpcmLe => Err(PcmReaderError::UnsupportedAudioFormat),
}
}
#[derive(Debug, thiserror::Error)]
pub enum PcmPlayerError {
#[error("Output buffer too short")]
OutputBufferTooShort,
#[error("Invalid position")]
InvalidPosition,
#[error("Finish playing")]
FinishPlaying,
}
#[derive(Default)]
pub struct PcmPlayer<'a> {
pub reader: PcmReader<'a>,
playback_position: u32,
loop_playing: bool,
}
impl<'a> PcmPlayer<'a> {
pub fn new(reader: PcmReader<'a>) -> Self {
PcmPlayer {
reader,
playback_position: 0,
loop_playing: false,
}
}
pub fn set_position(&mut self, sample: u32) -> Result<(), PcmPlayerError> {
if self.reader.specs.num_samples <= sample {
return Err(PcmPlayerError::InvalidPosition);
}
self.playback_position = sample;
Ok(())
}
pub fn set_loop_playing(&mut self, en: bool) {
self.loop_playing = en;
}
pub fn get_next_frame<T: Float>(&mut self, out: &mut [T]) -> Result<(), PcmPlayerError> {
if out.len() < self.reader.specs.num_channels as usize {
return Err(PcmPlayerError::OutputBufferTooShort);
}
let num_samples = self.reader.specs.num_samples;
if self.playback_position >= num_samples {
if self.loop_playing {
self.set_position(0)?;
} else {
return Err(PcmPlayerError::FinishPlaying);
}
}
let num_chennels = self.reader.specs.num_channels;
for ch in 0..num_chennels {
let Ok(sample) = self.reader.read_sample(ch, self.playback_position) else {
return Err(PcmPlayerError::InvalidPosition);
};
out[ch as usize] = sample;
}
self.playback_position += 1;
Ok(())
}
}