Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace anyhow with thiserror #52

Merged
merged 15 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading