Skip to content

Commit

Permalink
Merge pull request #52 from AkiyukiOkayasu/develop
Browse files Browse the repository at this point in the history
Replace `anyhow` with `thiserror`
  • Loading branch information
AkiyukiOkayasu committed Aug 20, 2024
2 parents 8b54097 + 46e9fce commit 792525b
Show file tree
Hide file tree
Showing 10 changed files with 213 additions and 122 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ edition = "2021"
rust-version = "1.79.0"

[dependencies]
anyhow = { version = "1.0.86", default-features = false }
arbitrary-int = { version = "1.2.7", default-features = false }
fixed = "1.28.0"
heapless = "0.8.0"
nom = { version = "7.1.3", default-features = false }
thiserror = { version = "1.0", git = "https://github.com/quartiq/thiserror.git", default-features = false, branch = "no-std" }

[dev-dependencies]
cpal = "0.15.3"
Expand Down
4 changes: 2 additions & 2 deletions benches/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ fn parse_wav(c: &mut Criterion) {
let wav = include_bytes!("../tests/resources/Sine440Hz_1ch_48000Hz_16.wav");
c.bench_function("Parse WAV 16bit", |b| {
b.iter(|| {
let _reader = PcmReader::new(black_box(wav));
let _reader = PcmReader::new(black_box(wav)).unwrap();
})
});
}

fn read_sample(c: &mut Criterion) {
let wav = include_bytes!("../tests/resources/Sine440Hz_1ch_48000Hz_16.wav");
let reader = PcmReader::new(wav);
let reader = PcmReader::new(wav).unwrap();
let pcm_specs = reader.get_pcm_specs();
c.bench_function("Read a sample 16bit", |b| {
b.iter(|| {
Expand Down
2 changes: 1 addition & 1 deletion examples/beep.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ fn main() {
println!("Default output config: {config:?}");
let channels = config.channels() as usize;

let reader = PcmReader::new(wav);
let reader = PcmReader::new(wav).unwrap();
let mut sample_index = 0;

println!("PCM spec: {:?}", reader.get_pcm_specs());
Expand Down
2 changes: 1 addition & 1 deletion examples/print_sample_values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::{fs, io::Write};
fn main() {
let wav = include_bytes!("../tests/resources/Sine440Hz_1ch_48000Hz_64FP.wav");
println!("Wave length in bytes: {}", wav.len());
let reader = PcmReader::new(wav);
let reader = PcmReader::new(wav).unwrap();
println!("PCM spec: {:?}", reader.get_pcm_specs());

let mut file = fs::File::create("sinewave.txt").unwrap();
Expand Down
2 changes: 1 addition & 1 deletion examples/read_wav_no_std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use pacmog::PcmReader;

fn main() {
let wav = include_bytes!("../tests/resources/Sine440Hz_1ch_48000Hz_16.wav");
let reader = PcmReader::new(wav);
let reader = PcmReader::new(wav).unwrap();
for sample in 0..48000 {
let _s = reader.read_sample(0, sample);
}
Expand Down
34 changes: 26 additions & 8 deletions src/aiff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,20 @@ use nom::bytes::complete::{tag, take};
use nom::number::complete::{be_i16, be_i32, be_u32};
use nom::IResult;

#[derive(thiserror::Error, Debug)]
enum AiffError {
#[error("Buffer length must be exactly 10 bytes")]
InvalidBufferLength,
}

/// AiffErrorをnom::Errに変換する
/// 変換時に情報が失われてしまう。とりあえずnom::error::ErrorKind::Failとしたが、IResultを使わずに実装できるか検討したい
impl<'a> From<AiffError> for nom::Err<nom::error::Error<&'a [u8]>> {
fn from(_err: AiffError) -> Self {
nom::Err::Error(nom::error::Error::new(&[], nom::error::ErrorKind::Fail))
}
}

/// ckID chunkの種類
#[derive(Debug, PartialEq, Default)]
pub(super) enum ChunkId {
Expand Down Expand Up @@ -82,14 +96,15 @@ impl TryFrom<&[u8]> for CompressionTypeId {
#[derive(Debug, Default)]
pub(super) struct Chunk<'a> {
pub id: ChunkId,
#[allow(dead_code)]
pub size: u32,
pub data: &'a [u8],
}

/// AIFFチャンクの情報
/// * 'size' - ファイルサイズ(byte) - 8
pub(super) struct AiffHeader {
pub _size: u32,
pub size: u32,
}

/// SSNDチャンクのOffset, BlockSize
Expand All @@ -108,7 +123,7 @@ pub(super) fn parse_aiff_header(input: &[u8]) -> IResult<&[u8], AiffHeader> {
let (input, _) = tag(b"FORM")(input)?;
let (input, size) = be_u32(input)?;
let (input, _id) = alt((tag(b"AIFF"), tag(b"AIFC")))(input)?;
Ok((input, AiffHeader { _size: size }))
Ok((input, AiffHeader { size }))
}

/// 先頭のチャンクを取得する
Expand All @@ -131,7 +146,7 @@ pub(super) fn parse_comm(input: &[u8]) -> IResult<&[u8], PcmSpecs> {
let (input, bit_depth) = be_i16(input)?;
let bit_depth = bit_depth as u16;
let (input, sample_rate) = take(10usize)(input)?;
let sample_rate = extended2double(sample_rate) as u32;
let sample_rate = extended2double(sample_rate).map_err(nom::Err::from)? as u32;

if input.len() >= 4 {
//AIFF-C parameters
Expand Down Expand Up @@ -168,8 +183,10 @@ pub(super) fn parse_ssnd(input: &[u8]) -> IResult<&[u8], SsndBlockInfo> {
/// 80 bit floating point value according to the IEEE-754 specification and the Standard Apple Numeric Environment specification:
/// 1 bit sign, 15 bit exponent, 1 bit normalization indication, 63 bit mantissa
/// https://stackoverflow.com/a/3949358
fn extended2double(buffer: &[u8]) -> f64 {
assert!(buffer.len() == 10);
fn extended2double(buffer: &[u8]) -> Result<f64, AiffError> {
if buffer.len() != 10 {
return Err(AiffError::InvalidBufferLength);
}

let sign = if (buffer[0] & 0x80) == 0x00 {
1f64
Expand All @@ -195,8 +212,9 @@ fn extended2double(buffer: &[u8]) -> f64 {
mantissa &= 0x7FFFFFFFFFFFFFFF;

//value = (-1) ^ s * (normalizeCorrection + m / 2 ^ 63) * 2 ^ (e - 16383)
sign * (normalize_correction + mantissa as f64 / (1u64 << 63) as f64)
* (1u64 << (exponent as i32 - 16383)) as f64
Ok(sign
* (normalize_correction + mantissa as f64 / (1u64 << 63) as f64)
* (1u64 << (exponent as i32 - 16383)) as f64)
}

#[cfg(test)]
Expand All @@ -209,7 +227,7 @@ mod tests {
#[test]
fn extended2double_test() {
let array: [u8; 10] = [64, 14, 187, 128, 0, 0, 0, 0, 0, 0];
assert_relative_eq!(extended2double(&array), 48000.0f64);
assert_relative_eq!(extended2double(&array).unwrap(), 48000.0f64);
}

#[test]
Expand Down
61 changes: 38 additions & 23 deletions src/imaadpcm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,9 @@
//! ```

use crate::{AudioFormat, PcmReader, PcmSpecs};
use anyhow::ensure;
use arbitrary_int::u4;
use heapless::spsc::Queue;
use nom::bits::{bits, complete::take};
use nom::error::Error;
use nom::number::complete::{le_i16, le_i8, le_u8};
use nom::sequence::tuple;
use nom::IResult;
Expand Down Expand Up @@ -51,6 +49,21 @@ struct BlockHeader {
b_step_table_index: i8,
}

/// Error type for IMA-ADPCM.
#[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,
}

/// IMA-ADPCMのHeader Wordをパースする
/// Multimedia Data Standards Update April 15, 1994 Page 32 of 74
/// http://elm-chan.org/junk/adpcm/RIFF_NEW.pdf
Expand Down Expand Up @@ -118,11 +131,11 @@ fn compute_step_size(nibble: u4, mut step_size_table_index: i8) -> i8 {
pub(crate) fn calc_num_samples_per_channel(
data_chunk_size_in_bytes: u32,
spec: &PcmSpecs,
) -> anyhow::Result<u32> {
ensure!(
spec.audio_format == AudioFormat::ImaAdpcmLe,
"IMA-ADPCM only"
);
) -> 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;
Expand Down Expand Up @@ -151,7 +164,8 @@ pub struct ImaAdpcmPlayer<'a> {
impl<'a> ImaAdpcmPlayer<'a> {
/// * 'input' - PCM data byte array.
pub fn new(input: &'a [u8]) -> Self {
let reader = PcmReader::new(input);
//TODO unwrapではなくきちんとエラーハンドリングする
let reader = PcmReader::new(input).unwrap();

ImaAdpcmPlayer {
reader,
Expand All @@ -162,24 +176,22 @@ impl<'a> ImaAdpcmPlayer<'a> {

/// Return samples value of the next frame.
/// * 'out' - Output buffer which the sample values are written. Number of elements must be equal to or greater than the number of channels in the PCM file.
pub fn get_next_frame(&mut self, out: &mut [I1F15]) -> anyhow::Result<()> {
pub fn get_next_frame(&mut self, out: &mut [I1F15]) -> Result<(), ImaAdpcmError> {
let num_channels = self.reader.specs.num_channels;

// outバッファーのチャンネル数が不足
ensure!(
out.len() >= num_channels as usize,
"Number of elements in \"out\" must be greater than or equal to the number of IMA-ADPCM channels"
);
// outバッファーのチャンネル数が不足している場合はエラーを返す
if out.len() < num_channels as usize {
return Err(ImaAdpcmError::InsufficientOutputBufferChannels);
}

// 再生終了
ensure!(
self.frame_index < self.reader.specs.num_samples,
"Played to the end."
);
// 再生終了している場合はエラーを返す
if self.frame_index >= self.reader.specs.num_samples {
return Err(ImaAdpcmError::FinishPlaying);
}

//IMA-ADPCMのBlock切り替わりかどうか判定
if self.reading_block.is_empty() && self.nibble_queue[0].is_empty() {
self.update_block();
self.update_block()?;
out[..(num_channels as usize)]
.copy_from_slice(&self.last_predicted_sample[..(num_channels as usize)]);
self.frame_index += 1; //Blockの最初のサンプルはHeaderに記録されている
Expand Down Expand Up @@ -220,13 +232,15 @@ impl<'a> ImaAdpcmPlayer<'a> {
}

/// IMA-ADPCMのブロック更新.
fn update_block(&mut self) {
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]; //新しいBlockをreading_blockへ更新

assert_eq!(self.reading_block.len(), 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 {
// BlockのHeader wordを読み出す
Expand All @@ -235,6 +249,7 @@ impl<'a> ImaAdpcmPlayer<'a> {
self.step_size_table_index[ch] = block_header.b_step_table_index;
self.reading_block = block;
}
Ok(())
}

/// Move the playback position back to the beginning.
Expand All @@ -256,7 +271,7 @@ type DataWordNibbles = (u8, u8, u8, u8, u8, u8, u8, u8);

/// IMA-ADPCMのBlockのData word(32bit長)を8つのnibble(4bit長)にパースする.
fn parse_data_word(input: &[u8]) -> IResult<&[u8], DataWordNibbles> {
bits::<_, _, Error<(&[u8], usize)>, _, _>(tuple((
bits::<_, _, nom::error::Error<(&[u8], usize)>, _, _>(tuple((
take(4usize),
take(4usize),
take(4usize),
Expand Down
Loading

0 comments on commit 792525b

Please sign in to comment.