#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum AudioCodingType {
ReferToStream,
Lpcm,
Ac3,
Mpeg1,
Mp3,
Mpeg2Multichannel,
AacLc,
Dts,
Atrac,
OneBitAudio,
EnhancedAc3,
DtsHd,
MlpTrueHd,
Dst,
WmaPro,
Extension,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ChannelCount {
ReferToStream,
Count(u8),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum SampleFrequency {
ReferToStream,
Hz32000,
Hz44100,
Hz48000,
Hz88200,
Hz96000,
Hz176400,
Hz192000,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum SampleSize {
ReferToStream,
Bits16,
Bits20,
Bits24,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum LfePlaybackLevel {
NoInfo,
Plus10Db,
Ref0Db,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AudioInfoFrame {
pub coding_type: AudioCodingType,
pub channel_count: ChannelCount,
pub sample_freq: SampleFrequency,
pub sample_size: SampleSize,
pub coding_ext: u8,
pub channel_allocation: u8,
pub lfe_playback_level: LfePlaybackLevel,
pub downmix_inhibit: bool,
}
use crate::decoded::Decoded;
use crate::encode::{IntoPackets, SinglePacketIter};
use crate::error::DecodeError;
use crate::warn::AudioWarning;
impl AudioInfoFrame {
pub fn decode(packet: &[u8; 31]) -> Result<Decoded<AudioInfoFrame, AudioWarning>, DecodeError> {
let length = packet[2];
if length > 27 {
return Err(DecodeError::Truncated { claimed: length });
}
let mut decoded = Decoded::new(AudioInfoFrame {
coding_type: AudioCodingType::ReferToStream,
channel_count: ChannelCount::ReferToStream,
sample_freq: SampleFrequency::ReferToStream,
sample_size: SampleSize::ReferToStream,
coding_ext: 0,
channel_allocation: 0,
lfe_playback_level: LfePlaybackLevel::NoInfo,
downmix_inhibit: false,
});
let total: u8 = packet.iter().fold(0u8, |acc, &b| acc.wrapping_add(b));
if total != 0x00 {
let expected = crate::checksum::compute_checksum(packet[..30].try_into().unwrap());
decoded.push_warning(AudioWarning::ChecksumMismatch {
expected,
found: packet[3],
});
}
let pb1 = packet[4];
if pb1 & 0x80 != 0 {
decoded.push_warning(AudioWarning::ReservedFieldNonZero { byte: 4, bit: 7 });
}
let ct = (pb1 >> 3) & 0x0F;
let cc = pb1 & 0x07;
decoded.value.coding_type = match ct {
0 => AudioCodingType::ReferToStream,
1 => AudioCodingType::Lpcm,
2 => AudioCodingType::Ac3,
3 => AudioCodingType::Mpeg1,
4 => AudioCodingType::Mp3,
5 => AudioCodingType::Mpeg2Multichannel,
6 => AudioCodingType::AacLc,
7 => AudioCodingType::Dts,
8 => AudioCodingType::Atrac,
9 => AudioCodingType::OneBitAudio,
10 => AudioCodingType::EnhancedAc3,
11 => AudioCodingType::DtsHd,
12 => AudioCodingType::MlpTrueHd,
13 => AudioCodingType::Dst,
14 => AudioCodingType::WmaPro,
15 => AudioCodingType::Extension,
_ => unreachable!(), };
decoded.value.channel_count = match cc {
0 => ChannelCount::ReferToStream,
n => ChannelCount::Count(n + 1), };
let pb2 = packet[5];
for bit in [6u8, 7] {
if pb2 & (1 << bit) != 0 {
decoded.push_warning(AudioWarning::ReservedFieldNonZero { byte: 5, bit });
}
}
let sf = (pb2 >> 2) & 0x07;
let ss = pb2 & 0x03;
decoded.value.sample_freq = match sf {
0 => SampleFrequency::ReferToStream,
1 => SampleFrequency::Hz32000,
2 => SampleFrequency::Hz44100,
3 => SampleFrequency::Hz48000,
4 => SampleFrequency::Hz88200,
5 => SampleFrequency::Hz96000,
6 => SampleFrequency::Hz176400,
7 => SampleFrequency::Hz192000,
_ => unreachable!(), };
decoded.value.sample_size = match ss {
0 => SampleSize::ReferToStream,
1 => SampleSize::Bits16,
2 => SampleSize::Bits20,
3 => SampleSize::Bits24,
_ => unreachable!(), };
let pb3 = packet[6];
for bit in [5u8, 6, 7] {
if pb3 & (1 << bit) != 0 {
decoded.push_warning(AudioWarning::ReservedFieldNonZero { byte: 6, bit });
}
}
decoded.value.coding_ext = pb3 & 0x1F;
decoded.value.channel_allocation = packet[7];
let pb5 = packet[8];
for bit in [0u8, 1, 2] {
if pb5 & (1 << bit) != 0 {
decoded.push_warning(AudioWarning::ReservedFieldNonZero { byte: 8, bit });
}
}
decoded.value.downmix_inhibit = pb5 & 0x80 != 0;
let lsv = (pb5 >> 3) & 0x0F;
decoded.value.lfe_playback_level = match lsv {
0 => LfePlaybackLevel::NoInfo,
1 => LfePlaybackLevel::Plus10Db,
2 => LfePlaybackLevel::Ref0Db,
_ => {
decoded.push_warning(AudioWarning::UnknownEnumValue {
field: "lfe_playback_level",
raw: lsv,
});
LfePlaybackLevel::NoInfo
}
};
Ok(decoded)
}
}
impl IntoPackets for AudioInfoFrame {
type Iter = SinglePacketIter;
type Warning = AudioWarning;
fn into_packets(self) -> crate::decoded::Decoded<SinglePacketIter, AudioWarning> {
let ct: u8 = match self.coding_type {
AudioCodingType::ReferToStream => 0,
AudioCodingType::Lpcm => 1,
AudioCodingType::Ac3 => 2,
AudioCodingType::Mpeg1 => 3,
AudioCodingType::Mp3 => 4,
AudioCodingType::Mpeg2Multichannel => 5,
AudioCodingType::AacLc => 6,
AudioCodingType::Dts => 7,
AudioCodingType::Atrac => 8,
AudioCodingType::OneBitAudio => 9,
AudioCodingType::EnhancedAc3 => 10,
AudioCodingType::DtsHd => 11,
AudioCodingType::MlpTrueHd => 12,
AudioCodingType::Dst => 13,
AudioCodingType::WmaPro => 14,
AudioCodingType::Extension => 15,
};
let cc: u8 = match self.channel_count {
ChannelCount::ReferToStream => 0,
ChannelCount::Count(n) => (n.saturating_sub(1)).min(7),
};
let sf: u8 = match self.sample_freq {
SampleFrequency::ReferToStream => 0,
SampleFrequency::Hz32000 => 1,
SampleFrequency::Hz44100 => 2,
SampleFrequency::Hz48000 => 3,
SampleFrequency::Hz88200 => 4,
SampleFrequency::Hz96000 => 5,
SampleFrequency::Hz176400 => 6,
SampleFrequency::Hz192000 => 7,
};
let ss: u8 = match self.sample_size {
SampleSize::ReferToStream => 0,
SampleSize::Bits16 => 1,
SampleSize::Bits20 => 2,
SampleSize::Bits24 => 3,
};
let lsv: u8 = match self.lfe_playback_level {
LfePlaybackLevel::NoInfo => 0,
LfePlaybackLevel::Plus10Db => 1,
LfePlaybackLevel::Ref0Db => 2,
};
let mut hp = [0u8; 30];
hp[0] = 0x84; hp[1] = 0x01; hp[2] = 0x0A; hp[3] = (ct << 3) | (cc & 0x07); hp[4] = (sf << 2) | (ss & 0x03); hp[5] = self.coding_ext & 0x1F; hp[6] = self.channel_allocation; hp[7] = ((self.downmix_inhibit as u8) << 7) | (lsv << 3);
let checksum = crate::checksum::compute_checksum(&hp);
let mut packet = [0u8; 31];
packet[..3].copy_from_slice(&hp[..3]); packet[3] = checksum;
packet[4..].copy_from_slice(&hp[3..]);
crate::decoded::Decoded::new(SinglePacketIter::new(packet))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::encode::IntoPackets;
fn default_frame() -> AudioInfoFrame {
AudioInfoFrame {
coding_type: AudioCodingType::Lpcm,
channel_count: ChannelCount::Count(8),
sample_freq: SampleFrequency::Hz48000,
sample_size: SampleSize::Bits24,
coding_ext: 0,
channel_allocation: 0x13, lfe_playback_level: LfePlaybackLevel::Ref0Db,
downmix_inhibit: true,
}
}
#[test]
fn round_trip() {
let frame = default_frame();
let packet = frame.clone().into_packets().value.next().unwrap();
let decoded = AudioInfoFrame::decode(&packet).unwrap();
assert!(decoded.iter_warnings().next().is_none());
assert_eq!(decoded.value, frame);
}
#[test]
fn checksum_mismatch_warning() {
let mut packet = default_frame().into_packets().value.next().unwrap();
packet[3] = packet[3].wrapping_add(1); let decoded = AudioInfoFrame::decode(&packet).unwrap();
let warnings: alloc::vec::Vec<_> = decoded.iter_warnings().collect();
assert!(
warnings
.iter()
.any(|w| matches!(w, AudioWarning::ChecksumMismatch { .. }))
);
assert_eq!(decoded.value, default_frame());
}
#[test]
fn truncated_length_is_error() {
let mut packet = default_frame().into_packets().value.next().unwrap();
packet[2] = 28; assert!(matches!(
AudioInfoFrame::decode(&packet),
Err(crate::error::DecodeError::Truncated { claimed: 28 })
));
}
#[test]
fn reserved_bit_warning() {
let mut packet = default_frame().into_packets().value.next().unwrap();
packet[4] |= 0x80; let sum: u8 = packet[..31].iter().fold(0u8, |a, &b| a.wrapping_add(b));
packet[3] = packet[3].wrapping_sub(sum);
let decoded = AudioInfoFrame::decode(&packet).unwrap();
assert!(
decoded
.iter_warnings()
.any(|w| matches!(w, AudioWarning::ReservedFieldNonZero { byte: 4, bit: 7 }))
);
}
#[test]
fn unknown_lfe_playback_level_warns_and_falls_back() {
let mut packet = default_frame().into_packets().value.next().unwrap();
packet[8] = (packet[8] & 0x87) | (0x0F << 3);
let sum: u8 = packet.iter().fold(0u8, |a, &b| a.wrapping_add(b));
packet[3] = packet[3].wrapping_sub(sum);
let decoded = AudioInfoFrame::decode(&packet).unwrap();
assert!(decoded.iter_warnings().any(|w| matches!(
w,
AudioWarning::UnknownEnumValue {
field: "lfe_playback_level",
raw: 15
}
)));
assert_eq!(decoded.value.lfe_playback_level, LfePlaybackLevel::NoInfo);
}
#[test]
fn coding_types_round_trip() {
for ct in [
AudioCodingType::Ac3,
AudioCodingType::Mpeg1,
AudioCodingType::Mp3,
AudioCodingType::Mpeg2Multichannel,
AudioCodingType::AacLc,
AudioCodingType::Dts,
AudioCodingType::Atrac,
AudioCodingType::OneBitAudio,
AudioCodingType::EnhancedAc3,
AudioCodingType::DtsHd,
AudioCodingType::MlpTrueHd,
AudioCodingType::Dst,
AudioCodingType::WmaPro,
AudioCodingType::Extension,
] {
let frame = AudioInfoFrame {
coding_type: ct,
..default_frame()
};
let packet = frame.clone().into_packets().value.next().unwrap();
let decoded = AudioInfoFrame::decode(&packet).unwrap();
assert_eq!(decoded.value.coding_type, ct);
}
}
#[test]
fn sample_freq_and_size_variants_round_trip() {
for (sf, ss) in [
(SampleFrequency::ReferToStream, SampleSize::ReferToStream),
(SampleFrequency::Hz32000, SampleSize::Bits16),
(SampleFrequency::Hz44100, SampleSize::Bits20),
(SampleFrequency::Hz48000, SampleSize::Bits24),
(SampleFrequency::Hz88200, SampleSize::Bits16),
(SampleFrequency::Hz96000, SampleSize::Bits16),
(SampleFrequency::Hz176400, SampleSize::Bits16),
(SampleFrequency::Hz192000, SampleSize::Bits16),
] {
let frame = AudioInfoFrame {
sample_freq: sf,
sample_size: ss,
..default_frame()
};
let packet = frame.clone().into_packets().value.next().unwrap();
let decoded = AudioInfoFrame::decode(&packet).unwrap();
assert_eq!(decoded.value.sample_freq, sf);
assert_eq!(decoded.value.sample_size, ss);
}
}
#[test]
fn lfe_playback_levels_round_trip() {
for lsv in [
LfePlaybackLevel::NoInfo,
LfePlaybackLevel::Plus10Db,
LfePlaybackLevel::Ref0Db,
] {
let frame = AudioInfoFrame {
lfe_playback_level: lsv,
..default_frame()
};
let packet = frame.clone().into_packets().value.next().unwrap();
let decoded = AudioInfoFrame::decode(&packet).unwrap();
assert_eq!(decoded.value.lfe_playback_level, lsv);
}
}
#[test]
fn reserved_pb2_bits_warning() {
let mut packet = default_frame().into_packets().value.next().unwrap();
packet[5] |= 0x80; let sum: u8 = packet.iter().fold(0u8, |a, &b| a.wrapping_add(b));
packet[3] = packet[3].wrapping_sub(sum);
let decoded = AudioInfoFrame::decode(&packet).unwrap();
assert!(
decoded
.iter_warnings()
.any(|w| matches!(w, AudioWarning::ReservedFieldNonZero { byte: 5, bit: 7 }))
);
}
#[test]
fn reserved_pb3_bits_warning() {
let mut packet = default_frame().into_packets().value.next().unwrap();
packet[6] |= 0x20; let sum: u8 = packet.iter().fold(0u8, |a, &b| a.wrapping_add(b));
packet[3] = packet[3].wrapping_sub(sum);
let decoded = AudioInfoFrame::decode(&packet).unwrap();
assert!(
decoded
.iter_warnings()
.any(|w| matches!(w, AudioWarning::ReservedFieldNonZero { byte: 6, bit: 5 }))
);
}
#[test]
fn reserved_pb5_bits_warning() {
let mut packet = default_frame().into_packets().value.next().unwrap();
packet[8] |= 0x01; let sum: u8 = packet.iter().fold(0u8, |a, &b| a.wrapping_add(b));
packet[3] = packet[3].wrapping_sub(sum);
let decoded = AudioInfoFrame::decode(&packet).unwrap();
assert!(
decoded
.iter_warnings()
.any(|w| matches!(w, AudioWarning::ReservedFieldNonZero { byte: 8, bit: 0 }))
);
}
#[test]
fn refer_to_stream_fields_round_trip() {
let frame = AudioInfoFrame {
coding_type: AudioCodingType::ReferToStream,
channel_count: ChannelCount::ReferToStream,
sample_freq: SampleFrequency::ReferToStream,
sample_size: SampleSize::ReferToStream,
coding_ext: 0,
channel_allocation: 0,
lfe_playback_level: LfePlaybackLevel::NoInfo,
downmix_inhibit: false,
};
let packet = frame.clone().into_packets().value.next().unwrap();
let decoded = AudioInfoFrame::decode(&packet).unwrap();
assert!(decoded.iter_warnings().next().is_none());
assert_eq!(decoded.value, frame);
}
}