use crate::{AudioFormat, PcmReader, PcmReaderError, PcmSpecs};
use arbitrary_int::u4;
pub use fixed::types::I1F15;
use heapless::spsc::Queue;
use winnow::Parser;
use winnow::binary::bits::{bits, take};
use winnow::binary::{le_i8, le_i16, le_u8};
use winnow::error::{ContextError, ErrMode, ModalResult};
const INDEX_TABLE: [i8; 16] = [-1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8];
const STEP_SIZE_TABLE: [i16; 89] = [
7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66,
73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449,
494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 2272,
2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493,
10442, 11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767,
];
const MAX_NUM_CHANNELS: usize = 2;
#[derive(Default, Debug)]
struct BlockHeader {
i_samp_0: I1F15,
b_step_table_index: i8,
}
#[derive(Debug, thiserror::Error)]
pub enum ImaAdpcmError {
#[error("IMA-ADPCM is not supported in decode_sample(). Use ImaAdpcmPlayer.")]
CantDecodeImaAdpcm,
#[error("The audio format is not IMA-ADPCM.")]
NotImaAdpcm,
#[error(
"The number of elements in the output buffer must be at least equal to the number of IMA-ADPCM channels."
)]
InsufficientOutputBufferChannels,
#[error("Finish playing.")]
FinishPlaying,
#[error("Block length does not match block align")]
BlockLengthMismatch,
#[error("IMA-ADPCM read data or nibble error.")]
ReadError,
}
fn parse_block_header(input: &mut &[u8]) -> ModalResult<BlockHeader> {
let i_samp_0 = le_i16.map(I1F15::from_bits).parse_next(input)?;
let b_step_table_index = le_i8.parse_next(input)?;
le_u8.void().parse_next(input)?;
Ok(BlockHeader {
i_samp_0,
b_step_table_index,
})
}
fn decode_sample(
nibble: u4,
last_predicted_sample: I1F15,
step_size_table_index: i8,
) -> (I1F15, i8) {
let mut diff = 0i32;
let step_size = STEP_SIZE_TABLE[step_size_table_index as usize] as i32;
let n = nibble.value();
if (n & 4) == 4 {
diff += step_size;
}
if (n & 2) == 2 {
diff += step_size >> 1;
}
if (n & 1) == 1 {
diff += step_size >> 2;
}
diff += step_size >> 3;
if (n & 8) == 8 {
diff = -diff;
}
let predicted_sample = last_predicted_sample.saturating_add(I1F15::from_bits(diff as i16));
let step_size_table_index = compute_step_size(nibble, step_size_table_index);
(predicted_sample, step_size_table_index)
}
fn compute_step_size(nibble: u4, mut step_size_table_index: i8) -> i8 {
step_size_table_index += INDEX_TABLE[nibble.value() as usize];
step_size_table_index = step_size_table_index.clamp(0, 88); step_size_table_index
}
pub(crate) fn calc_num_samples_per_channel(
data_chunk_size_in_bytes: u32,
spec: &PcmSpecs,
) -> Result<u32, ImaAdpcmError> {
if spec.audio_format != AudioFormat::ImaAdpcmLe {
return Err(ImaAdpcmError::NotImaAdpcm);
}
let num_block_align = spec.ima_adpcm_num_block_align.unwrap() as u32;
let num_samples_per_block = spec.ima_adpcm_num_samples_per_block.unwrap() as u32;
let num_blocks = data_chunk_size_in_bytes / num_block_align;
let num_samples = num_blocks * num_samples_per_block;
Ok(num_samples)
}
#[derive(Default)]
pub struct ImaAdpcmPlayer<'a> {
pub reader: PcmReader<'a>,
frame_index: u32,
last_predicted_sample: [I1F15; MAX_NUM_CHANNELS],
step_size_table_index: [i8; MAX_NUM_CHANNELS],
reading_block: &'a [u8],
nibble_queue: [Queue<u4, 9>; MAX_NUM_CHANNELS],
}
impl<'a> ImaAdpcmPlayer<'a> {
pub fn new(input: &mut &'a [u8]) -> Result<Self, PcmReaderError> {
let reader = PcmReader::new(input)?;
Ok(ImaAdpcmPlayer {
reader,
frame_index: 0,
..Default::default()
})
}
pub fn get_next_frame(&mut self, out: &mut [I1F15]) -> Result<(), ImaAdpcmError> {
let num_channels = self.reader.specs.num_channels;
if out.len() < num_channels as usize {
return Err(ImaAdpcmError::InsufficientOutputBufferChannels);
}
if self.frame_index >= self.reader.specs.num_samples {
return Err(ImaAdpcmError::FinishPlaying);
}
if self.reading_block.is_empty() && self.nibble_queue[0].is_empty() {
self.update_block()?;
out[..(num_channels as usize)]
.copy_from_slice(&self.last_predicted_sample[..(num_channels as usize)]);
self.frame_index += 1; return Ok(());
}
if self.nibble_queue[0].is_empty() {
for ch in 0..num_channels as usize {
let Ok(nibbles) = parse_data_word.parse_next(&mut self.reading_block) else {
return Err(ImaAdpcmError::ReadError);
};
self.nibble_queue[ch].enqueue(u4::new(nibbles.1)).unwrap();
self.nibble_queue[ch].enqueue(u4::new(nibbles.0)).unwrap();
self.nibble_queue[ch].enqueue(u4::new(nibbles.3)).unwrap();
self.nibble_queue[ch].enqueue(u4::new(nibbles.2)).unwrap();
self.nibble_queue[ch].enqueue(u4::new(nibbles.5)).unwrap();
self.nibble_queue[ch].enqueue(u4::new(nibbles.4)).unwrap();
self.nibble_queue[ch].enqueue(u4::new(nibbles.7)).unwrap();
self.nibble_queue[ch].enqueue(u4::new(nibbles.6)).unwrap();
}
}
for (ch, output_value) in out.iter_mut().enumerate().take(num_channels as usize) {
let nibble = self.nibble_queue[ch].dequeue().unwrap();
let (predicted_sample, table_index) = decode_sample(
nibble,
self.last_predicted_sample[ch],
self.step_size_table_index[ch],
);
self.last_predicted_sample[ch] = predicted_sample;
self.step_size_table_index[ch] = table_index;
*output_value = predicted_sample;
}
self.frame_index += 1;
Ok(())
}
fn update_block(&mut self) -> Result<(), ImaAdpcmError> {
let samples_per_block = self.reader.specs.ima_adpcm_num_samples_per_block.unwrap() as u32;
let block_align = self.reader.specs.ima_adpcm_num_block_align.unwrap() as u32;
let offset = (self.frame_index / samples_per_block) * block_align;
self.reading_block = &self.reader.data[offset as usize..(offset + block_align) as usize];
if self.reading_block.len() != block_align as usize {
return Err(ImaAdpcmError::BlockLengthMismatch);
}
for ch in 0..self.reader.specs.num_channels as usize {
let input: &mut &[u8] = &mut self.reading_block;
let Ok(block_header) = parse_block_header(input) else {
return Err(ImaAdpcmError::BlockLengthMismatch);
};
self.last_predicted_sample[ch] = block_header.i_samp_0;
self.step_size_table_index[ch] = block_header.b_step_table_index;
self.reading_block = input; }
Ok(())
}
pub fn rewind(&mut self) {
self.frame_index = 0;
if !self.reading_block.is_empty() {
self.reading_block = &self.reading_block[0..0]; }
for q in &mut self.nibble_queue {
for _ in 0..q.len() {
q.dequeue().unwrap();
}
}
}
}
type DataWordNibbles = (u8, u8, u8, u8, u8, u8, u8, u8);
fn parse_data_word(input: &mut &[u8]) -> ModalResult<DataWordNibbles> {
bits::<_, _, ErrMode<ContextError>, _, _>((
take(4usize),
take(4usize),
take(4usize),
take(4usize),
take(4usize),
take(4usize),
take(4usize),
take(4usize),
))
.parse_next(input)
}
#[cfg(test)]
mod tests {
use crate::imaadpcm::{I1F15, decode_sample};
use arbitrary_int::u4;
#[test]
fn ima_adpcm_decode() {
let nibble = u4::new(3);
let (sample, step_size_table_index) = decode_sample(
nibble,
I1F15::from_bits(-30976), 24,
);
assert_eq!(sample, I1F15::from_bits(-30913)); assert_eq!(step_size_table_index, 23);
}
}