#![allow(
clippy::question_mark,
)]
#![forbid(
unsafe_code,
clippy::panic,
clippy::exit,
clippy::unwrap_used,
clippy::expect_used,
clippy::unimplemented,
clippy::todo,
clippy::unreachable,
)]
#![deny(
clippy::cast_ptr_alignment,
clippy::char_lit_as_u8,
clippy::unnecessary_cast,
clippy::cast_lossless,
clippy::cast_possible_truncation,
clippy::cast_possible_wrap,
clippy::cast_sign_loss,
clippy::checked_conversions,
)]
#[doc = include_str!("../README.md")]
#[cfg(doctest)]
pub struct ReadmeDoctests;
use std::io::{Read, Write, Seek, SeekFrom};
mod auresult;
mod aureader;
mod austreamparser;
mod auwriter;
mod cast;
pub use auresult::{AuResult, AuError};
pub use aureader::{AuReader, Sample, Samples};
pub use austreamparser::{AuStreamParser, AuEvent};
pub use auwriter::AuWriter;
const UNKNOWN_DATA_SIZE: u32 = 0xffffffff;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum SampleFormat {
CompressedUlaw,
I8,
I16,
I24,
I32,
F32,
F64,
CompressedAlaw,
Custom(u32),
}
impl SampleFormat {
pub fn decoded_size(&self) -> usize {
match &self {
SampleFormat::I8 => 1,
SampleFormat::I16 => 2,
SampleFormat::I24 => 3,
SampleFormat::I32 => 4,
SampleFormat::F32 => 4,
SampleFormat::F64 => 8,
SampleFormat::CompressedUlaw => 2,
SampleFormat::CompressedAlaw => 2,
SampleFormat::Custom(_) => 0,
}
}
#[inline(always)]
fn bytesize_u8(&self) -> u8 {
match &self {
SampleFormat::I8 => 1,
SampleFormat::I16 => 2,
SampleFormat::I24 => 3,
SampleFormat::I32 => 4,
SampleFormat::F32 => 4,
SampleFormat::F64 => 8,
SampleFormat::CompressedUlaw => 1,
SampleFormat::CompressedAlaw => 1,
SampleFormat::Custom(_) => 1,
}
}
#[inline(always)]
fn bytesize_u64(&self) -> u64 {
u64::from(self.bytesize_u8())
}
fn as_u32(&self) -> u32 {
match &self {
SampleFormat::CompressedUlaw => 1,
SampleFormat::I8 => 2,
SampleFormat::I16 => 3,
SampleFormat::I24 => 4,
SampleFormat::I32 => 5,
SampleFormat::F32 => 6,
SampleFormat::F64 => 7,
SampleFormat::CompressedAlaw => 27,
SampleFormat::Custom(val) => *val,
}
}
fn from_u32(value: u32) -> SampleFormat {
match value {
1 => SampleFormat::CompressedUlaw,
2 => SampleFormat::I8,
3 => SampleFormat::I16,
4 => SampleFormat::I24,
5 => SampleFormat::I32,
6 => SampleFormat::F32,
7 => SampleFormat::F64,
27 => SampleFormat::CompressedAlaw,
_ => SampleFormat::Custom(value),
}
}
}
const HEADER_SIZE: u8 = 24;
#[derive(Debug, Clone, PartialEq)]
pub struct AuReadInfo {
pub channels: u32,
pub sample_rate: u32,
pub sample_format: SampleFormat,
pub description_byte_len: u32,
pub sample_len: Option<u64>,
pub sample_byte_len: Option<u32>
}
impl AuReadInfo {
fn parse_bytes(header: &[u8; 24]) -> AuResult<(u64, AuReadInfo)> {
if header[0] != b'.' || header[1] != b's' || header[2] != b'n' || header[3] != b'd' {
return Err(AuError::UnrecognizedFormat);
}
let offset = u32::from_be_bytes([ header[4], header[5], header[6], header[7] ]);
let data_size = u32::from_be_bytes([ header[8], header[9], header[10], header[11] ]);
let encoding = u32::from_be_bytes([ header[12], header[13], header[14], header[15] ]);
let sample_rate= u32::from_be_bytes([ header[16], header[17], header[18], header[19] ]);
let channels = u32::from_be_bytes([ header[20], header[21], header[22], header[23] ]);
if offset < 24 {
return Err(AuError::InvalidAudioDataOffset);
}
let sample_format = SampleFormat::from_u32(encoding);
let (sample_len, sample_byte_len) = if data_size != UNKNOWN_DATA_SIZE {
let slen = if let SampleFormat::Custom(_) = sample_format {
None
} else {
Some(u64::from(data_size) / sample_format.bytesize_u64())
};
(slen, Some(data_size))
} else {
(None, None)
};
Ok((u64::from(offset), AuReadInfo {
channels,
sample_rate,
sample_format,
description_byte_len: offset - u32::from(HEADER_SIZE),
sample_byte_len,
sample_len
}))
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct AuWriteInfo {
pub channels: u32,
pub sample_rate: u32,
pub sample_format: SampleFormat,
}
impl Default for AuWriteInfo {
fn default() -> Self {
AuWriteInfo {
channels: 2,
sample_rate: 44100,
sample_format: SampleFormat::I16,
}
}
}
pub fn recognize(data: &[u8]) -> bool {
if data.len() < 24 ||
data[0] != b'.' || data[1] != b's' || data[2] != b'n' || data[3] != b'd' {
return false;
}
true
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_recognize() {
assert_eq!(recognize(&[]), false);
assert_eq!(recognize(b".snd"), false);
assert_eq!(recognize(b".snd4567890123456789012"), false);
assert_eq!(recognize(b".snd45678901234567890123"), true);
assert_eq!(recognize(b",snd45678901234567890123"), false);
}
}