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#![deny(clippy::mod_module_files)]
17
18use std::io;
19
20use num_derive::FromPrimitive;
21use num_traits::FromPrimitive;
22use scuffle_bytes_util::BitReader;
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30#[must_use]
31pub struct PartialAudioSpecificConfig {
32 pub audio_object_type: AudioObjectType,
34 pub sampling_frequency: u32,
36 pub channel_configuration: u8,
38}
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
43#[must_use]
44pub enum AudioObjectType {
45 AacMain,
47 AacLowComplexity,
49 Unknown(u16),
51}
52
53impl AudioObjectType {
54 pub const fn as_u16(&self) -> u16 {
56 match self {
57 AudioObjectType::AacMain => 1,
58 AudioObjectType::AacLowComplexity => 2,
59 AudioObjectType::Unknown(value) => *value,
60 }
61 }
62
63 pub const fn from_u16(value: u16) -> Self {
65 match value {
66 1 => AudioObjectType::AacMain,
67 2 => AudioObjectType::AacLowComplexity,
68 _ => AudioObjectType::Unknown(value),
69 }
70 }
71}
72
73impl From<u16> for AudioObjectType {
74 fn from(value: u16) -> Self {
75 Self::from_u16(value)
76 }
77}
78
79impl From<AudioObjectType> for u16 {
80 fn from(value: AudioObjectType) -> Self {
81 value.as_u16()
82 }
83}
84
85#[derive(FromPrimitive, Debug, Clone, PartialEq, Copy, Eq, PartialOrd, Ord)]
93#[repr(u8)]
94#[must_use]
95pub enum SampleFrequencyIndex {
96 Freq96000 = 0x0,
98 Freq88200 = 0x1,
100 Freq64000 = 0x2,
102 Freq48000 = 0x3,
104 Freq44100 = 0x4,
106 Freq32000 = 0x5,
108 Freq24000 = 0x6,
110 Freq22050 = 0x7,
112 Freq16000 = 0x8,
114 Freq12000 = 0x9,
116 Freq11025 = 0xA,
118 Freq8000 = 0xB,
120 Freq7350 = 0xC,
122 FreqReserved = 0xD,
124 FreqReserved2 = 0xE,
126 FreqEscape = 0xF,
129}
130
131impl SampleFrequencyIndex {
132 pub const fn to_freq(&self) -> Option<u32> {
134 match self {
135 SampleFrequencyIndex::Freq96000 => Some(96000),
136 SampleFrequencyIndex::Freq88200 => Some(88200),
137 SampleFrequencyIndex::Freq64000 => Some(64000),
138 SampleFrequencyIndex::Freq48000 => Some(48000),
139 SampleFrequencyIndex::Freq44100 => Some(44100),
140 SampleFrequencyIndex::Freq32000 => Some(32000),
141 SampleFrequencyIndex::Freq24000 => Some(24000),
142 SampleFrequencyIndex::Freq22050 => Some(22050),
143 SampleFrequencyIndex::Freq16000 => Some(16000),
144 SampleFrequencyIndex::Freq12000 => Some(12000),
145 SampleFrequencyIndex::Freq11025 => Some(11025),
146 SampleFrequencyIndex::Freq8000 => Some(8000),
147 SampleFrequencyIndex::Freq7350 => Some(7350),
148 SampleFrequencyIndex::FreqReserved => None,
149 SampleFrequencyIndex::FreqReserved2 => None,
150 SampleFrequencyIndex::FreqEscape => None,
151 }
152 }
153}
154
155impl PartialAudioSpecificConfig {
156 pub fn parse(data: &[u8]) -> io::Result<Self> {
164 let mut bitreader = BitReader::new_from_slice(data);
165
166 let mut audio_object_type = bitreader.read_bits(5)? as u16;
168 if audio_object_type == 31 {
169 audio_object_type = 32 + bitreader.read_bits(6)? as u16;
170 }
171
172 let sampling_frequency_index = SampleFrequencyIndex::from_u8(bitreader.read_bits(4)? as u8)
175 .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Invalid sampling frequency index"))?;
176
177 let sampling_frequency = match sampling_frequency_index {
178 SampleFrequencyIndex::FreqEscape => bitreader.read_bits(24)? as u32,
180 _ => sampling_frequency_index
181 .to_freq()
182 .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "Invalid sampling frequency index"))?,
183 };
184
185 let channel_configuration = bitreader.read_bits(4)? as u8;
187
188 Ok(Self {
189 audio_object_type: audio_object_type.into(),
190 sampling_frequency,
191 channel_configuration,
192 })
193 }
194}
195
196#[cfg(test)]
197#[cfg_attr(all(test, coverage_nightly), coverage(off))]
198mod tests {
199 use super::*;
200
201 #[test]
202 fn test_aac_config_parse() {
203 let data = [
204 0x12, 0x10, 0x56, 0xe5, 0x00, 0x2d, 0x96, 0x01, 0x80, 0x80, 0x05, 0x00, 0x00, 0x00, 0x00,
205 ];
206
207 let config = PartialAudioSpecificConfig::parse(&data).unwrap();
208 assert_eq!(config.audio_object_type, AudioObjectType::AacLowComplexity);
209 assert_eq!(config.sampling_frequency, 44100);
210 assert_eq!(config.channel_configuration, 2);
211 }
212
213 #[test]
214 fn test_idx_to_freq() {
215 let cases = [
216 (SampleFrequencyIndex::FreqEscape, None),
217 (SampleFrequencyIndex::FreqReserved2, None),
218 (SampleFrequencyIndex::FreqReserved, None),
219 (SampleFrequencyIndex::Freq7350, Some(7350)),
220 (SampleFrequencyIndex::Freq8000, Some(8000)),
221 (SampleFrequencyIndex::Freq11025, Some(11025)),
222 (SampleFrequencyIndex::Freq12000, Some(12000)),
223 (SampleFrequencyIndex::Freq16000, Some(16000)),
224 (SampleFrequencyIndex::Freq22050, Some(22050)),
225 (SampleFrequencyIndex::Freq24000, Some(24000)),
226 (SampleFrequencyIndex::Freq32000, Some(32000)),
227 (SampleFrequencyIndex::Freq44100, Some(44100)),
228 (SampleFrequencyIndex::Freq48000, Some(48000)),
229 (SampleFrequencyIndex::Freq64000, Some(64000)),
230 (SampleFrequencyIndex::Freq88200, Some(88200)),
231 (SampleFrequencyIndex::Freq96000, Some(96000)),
232 ];
233
234 for (idx, freq) in cases {
235 assert_eq!(freq, idx.to_freq(), "Expected frequency for {idx:?}");
236 }
237 }
238}
239
240#[cfg(feature = "docs")]
242#[scuffle_changelog::changelog]
243pub mod changelog {}