1#![cfg_attr(feature = "docs", doc = "\n\nSee the [changelog][changelog] for a full release history.")]
3#![cfg_attr(feature = "docs", doc = "## Feature flags")]
4#![cfg_attr(feature = "docs", doc = document_features::document_features!())]
5#![cfg_attr(all(coverage_nightly, test), feature(coverage_attribute))]
12#![cfg_attr(docsrs, feature(doc_auto_cfg))]
13#![deny(missing_docs)]
14#![deny(unsafe_code)]
15#![deny(unreachable_pub)]
16
17use std::io;
18
19use num_derive::FromPrimitive;
20use num_traits::FromPrimitive;
21use scuffle_bytes_util::BitReader;
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29#[must_use]
30pub struct PartialAudioSpecificConfig {
31 pub audio_object_type: AudioObjectType,
33 pub sampling_frequency: u32,
35 pub channel_configuration: u8,
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42#[must_use]
43pub enum AudioObjectType {
44 AacMain,
46 AacLowComplexity,
48 Unknown(u16),
50}
51
52impl AudioObjectType {
53 pub const fn as_u16(&self) -> u16 {
55 match self {
56 AudioObjectType::AacMain => 1,
57 AudioObjectType::AacLowComplexity => 2,
58 AudioObjectType::Unknown(value) => *value,
59 }
60 }
61
62 pub const fn from_u16(value: u16) -> Self {
64 match value {
65 1 => AudioObjectType::AacMain,
66 2 => AudioObjectType::AacLowComplexity,
67 _ => AudioObjectType::Unknown(value),
68 }
69 }
70}
71
72impl From<u16> for AudioObjectType {
73 fn from(value: u16) -> Self {
74 Self::from_u16(value)
75 }
76}
77
78impl From<AudioObjectType> for u16 {
79 fn from(value: AudioObjectType) -> Self {
80 value.as_u16()
81 }
82}
83
84#[derive(FromPrimitive, Debug, Clone, PartialEq, Copy, Eq, PartialOrd, Ord)]
92#[repr(u8)]
93#[must_use]
94pub enum SampleFrequencyIndex {
95 Freq96000 = 0x0,
97 Freq88200 = 0x1,
99 Freq64000 = 0x2,
101 Freq48000 = 0x3,
103 Freq44100 = 0x4,
105 Freq32000 = 0x5,
107 Freq24000 = 0x6,
109 Freq22050 = 0x7,
111 Freq16000 = 0x8,
113 Freq12000 = 0x9,
115 Freq11025 = 0xA,
117 Freq8000 = 0xB,
119 Freq7350 = 0xC,
121 FreqReserved = 0xD,
123 FreqReserved2 = 0xE,
125 FreqEscape = 0xF,
128}
129
130impl SampleFrequencyIndex {
131 pub const fn to_freq(&self) -> Option<u32> {
133 match self {
134 SampleFrequencyIndex::Freq96000 => Some(96000),
135 SampleFrequencyIndex::Freq88200 => Some(88200),
136 SampleFrequencyIndex::Freq64000 => Some(64000),
137 SampleFrequencyIndex::Freq48000 => Some(48000),
138 SampleFrequencyIndex::Freq44100 => Some(44100),
139 SampleFrequencyIndex::Freq32000 => Some(32000),
140 SampleFrequencyIndex::Freq24000 => Some(24000),
141 SampleFrequencyIndex::Freq22050 => Some(22050),
142 SampleFrequencyIndex::Freq16000 => Some(16000),
143 SampleFrequencyIndex::Freq12000 => Some(12000),
144 SampleFrequencyIndex::Freq11025 => Some(11025),
145 SampleFrequencyIndex::Freq8000 => Some(8000),
146 SampleFrequencyIndex::Freq7350 => Some(7350),
147 SampleFrequencyIndex::FreqReserved => None,
148 SampleFrequencyIndex::FreqReserved2 => None,
149 SampleFrequencyIndex::FreqEscape => None,
150 }
151 }
152}
153
154impl PartialAudioSpecificConfig {
155 pub fn parse(data: &[u8]) -> io::Result<Self> {
163 let mut bitreader = BitReader::new_from_slice(data);
164
165 let mut audio_object_type = bitreader.read_bits(5)? as u16;
167 if audio_object_type == 31 {
168 audio_object_type = 32 + bitreader.read_bits(6)? as u16;
169 }
170
171 let sampling_frequency_index = SampleFrequencyIndex::from_u8(bitreader.read_bits(4)? as u8)
174 .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Invalid sampling frequency index"))?;
175
176 let sampling_frequency = match sampling_frequency_index {
177 SampleFrequencyIndex::FreqEscape => bitreader.read_bits(24)? as u32,
179 _ => sampling_frequency_index
180 .to_freq()
181 .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Invalid sampling frequency index"))?,
182 };
183
184 let channel_configuration = bitreader.read_bits(4)? as u8;
186
187 Ok(Self {
188 audio_object_type: audio_object_type.into(),
189 sampling_frequency,
190 channel_configuration,
191 })
192 }
193}
194
195#[cfg(test)]
196#[cfg_attr(all(test, coverage_nightly), coverage(off))]
197mod tests {
198 use super::*;
199
200 #[test]
201 fn test_aac_config_parse() {
202 let data = [
203 0x12, 0x10, 0x56, 0xe5, 0x00, 0x2d, 0x96, 0x01, 0x80, 0x80, 0x05, 0x00, 0x00, 0x00, 0x00,
204 ];
205
206 let config = PartialAudioSpecificConfig::parse(&data).unwrap();
207 assert_eq!(config.audio_object_type, AudioObjectType::AacLowComplexity);
208 assert_eq!(config.sampling_frequency, 44100);
209 assert_eq!(config.channel_configuration, 2);
210 }
211
212 #[test]
213 fn test_idx_to_freq() {
214 let cases = [
215 (SampleFrequencyIndex::FreqEscape, None),
216 (SampleFrequencyIndex::FreqReserved2, None),
217 (SampleFrequencyIndex::FreqReserved, None),
218 (SampleFrequencyIndex::Freq7350, Some(7350)),
219 (SampleFrequencyIndex::Freq8000, Some(8000)),
220 (SampleFrequencyIndex::Freq11025, Some(11025)),
221 (SampleFrequencyIndex::Freq12000, Some(12000)),
222 (SampleFrequencyIndex::Freq16000, Some(16000)),
223 (SampleFrequencyIndex::Freq22050, Some(22050)),
224 (SampleFrequencyIndex::Freq24000, Some(24000)),
225 (SampleFrequencyIndex::Freq32000, Some(32000)),
226 (SampleFrequencyIndex::Freq44100, Some(44100)),
227 (SampleFrequencyIndex::Freq48000, Some(48000)),
228 (SampleFrequencyIndex::Freq64000, Some(64000)),
229 (SampleFrequencyIndex::Freq88200, Some(88200)),
230 (SampleFrequencyIndex::Freq96000, Some(96000)),
231 ];
232
233 for (idx, freq) in cases {
234 assert_eq!(freq, idx.to_freq(), "Expected frequency for {idx:?}");
235 }
236 }
237}
238
239#[cfg(feature = "docs")]
241#[scuffle_changelog::changelog]
242pub mod changelog {}