#![cfg_attr(all(coverage_nightly, test), feature(coverage_attribute))]
use std::io;
use num_derive::FromPrimitive;
use num_traits::FromPrimitive;
use scuffle_bytes_util::BitReader;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[must_use]
pub struct PartialAudioSpecificConfig {
pub audio_object_type: AudioObjectType,
pub sampling_frequency: u32,
pub channel_configuration: u8,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[must_use]
pub enum AudioObjectType {
AacMain,
AacLowComplexity,
Unknown(u16),
}
impl AudioObjectType {
pub const fn as_u16(&self) -> u16 {
match self {
AudioObjectType::AacMain => 1,
AudioObjectType::AacLowComplexity => 2,
AudioObjectType::Unknown(value) => *value,
}
}
pub const fn from_u16(value: u16) -> Self {
match value {
1 => AudioObjectType::AacMain,
2 => AudioObjectType::AacLowComplexity,
_ => AudioObjectType::Unknown(value),
}
}
}
impl From<u16> for AudioObjectType {
fn from(value: u16) -> Self {
Self::from_u16(value)
}
}
impl From<AudioObjectType> for u16 {
fn from(value: AudioObjectType) -> Self {
value.as_u16()
}
}
#[derive(FromPrimitive, Debug, Clone, PartialEq, Copy, Eq, PartialOrd, Ord)]
#[repr(u8)]
#[must_use]
pub enum SampleFrequencyIndex {
Freq96000 = 0x0,
Freq88200 = 0x1,
Freq64000 = 0x2,
Freq48000 = 0x3,
Freq44100 = 0x4,
Freq32000 = 0x5,
Freq24000 = 0x6,
Freq22050 = 0x7,
Freq16000 = 0x8,
Freq12000 = 0x9,
Freq11025 = 0xA,
Freq8000 = 0xB,
Freq7350 = 0xC,
FreqReserved = 0xD,
FreqReserved2 = 0xE,
FreqEscape = 0xF,
}
impl SampleFrequencyIndex {
pub const fn to_freq(&self) -> Option<u32> {
match self {
SampleFrequencyIndex::Freq96000 => Some(96000),
SampleFrequencyIndex::Freq88200 => Some(88200),
SampleFrequencyIndex::Freq64000 => Some(64000),
SampleFrequencyIndex::Freq48000 => Some(48000),
SampleFrequencyIndex::Freq44100 => Some(44100),
SampleFrequencyIndex::Freq32000 => Some(32000),
SampleFrequencyIndex::Freq24000 => Some(24000),
SampleFrequencyIndex::Freq22050 => Some(22050),
SampleFrequencyIndex::Freq16000 => Some(16000),
SampleFrequencyIndex::Freq12000 => Some(12000),
SampleFrequencyIndex::Freq11025 => Some(11025),
SampleFrequencyIndex::Freq8000 => Some(8000),
SampleFrequencyIndex::Freq7350 => Some(7350),
SampleFrequencyIndex::FreqReserved => None,
SampleFrequencyIndex::FreqReserved2 => None,
SampleFrequencyIndex::FreqEscape => None,
}
}
}
impl PartialAudioSpecificConfig {
pub fn parse(data: &[u8]) -> io::Result<Self> {
let mut bitreader = BitReader::new_from_slice(data);
let mut audio_object_type = bitreader.read_bits(5)? as u16;
if audio_object_type == 31 {
audio_object_type = 32 + bitreader.read_bits(6)? as u16;
}
let sampling_frequency_index = SampleFrequencyIndex::from_u8(bitreader.read_bits(4)? as u8)
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Invalid sampling frequency index"))?;
let sampling_frequency = match sampling_frequency_index {
SampleFrequencyIndex::FreqEscape => bitreader.read_bits(24)? as u32,
_ => sampling_frequency_index
.to_freq()
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Invalid sampling frequency index"))?,
};
let channel_configuration = bitreader.read_bits(4)? as u8;
Ok(Self {
audio_object_type: audio_object_type.into(),
sampling_frequency,
channel_configuration,
})
}
}
#[cfg(test)]
#[cfg_attr(all(test, coverage_nightly), coverage(off))]
mod tests {
use super::*;
#[test]
fn test_aac_config_parse() {
let data = [
0x12, 0x10, 0x56, 0xe5, 0x00, 0x2d, 0x96, 0x01, 0x80, 0x80, 0x05, 0x00, 0x00, 0x00, 0x00,
];
let config = PartialAudioSpecificConfig::parse(&data).unwrap();
assert_eq!(config.audio_object_type, AudioObjectType::AacLowComplexity);
assert_eq!(config.sampling_frequency, 44100);
assert_eq!(config.channel_configuration, 2);
}
#[test]
fn test_idx_to_freq() {
let cases = [
(SampleFrequencyIndex::FreqEscape, None),
(SampleFrequencyIndex::FreqReserved2, None),
(SampleFrequencyIndex::FreqReserved, None),
(SampleFrequencyIndex::Freq7350, Some(7350)),
(SampleFrequencyIndex::Freq8000, Some(8000)),
(SampleFrequencyIndex::Freq11025, Some(11025)),
(SampleFrequencyIndex::Freq12000, Some(12000)),
(SampleFrequencyIndex::Freq16000, Some(16000)),
(SampleFrequencyIndex::Freq22050, Some(22050)),
(SampleFrequencyIndex::Freq24000, Some(24000)),
(SampleFrequencyIndex::Freq32000, Some(32000)),
(SampleFrequencyIndex::Freq44100, Some(44100)),
(SampleFrequencyIndex::Freq48000, Some(48000)),
(SampleFrequencyIndex::Freq64000, Some(64000)),
(SampleFrequencyIndex::Freq88200, Some(88200)),
(SampleFrequencyIndex::Freq96000, Some(96000)),
];
for (idx, freq) in cases {
assert_eq!(freq, idx.to_freq(), "Expected frequency for {:?}", idx);
}
}
}