Skip to main content

mpeg4_audio_const/
lib.rs

1//! Definitions of types and constants for values defined by
2//! [ISO/IEC 14496 part 3 (Audio)](https://en.wikipedia.org/wiki/MPEG-4_Part_3).
3//!
4//! Currently supported,
5//!
6//!  - [`AudioObjectType`](struct.AudioObjectType.html)
7//!  - [`SamplingFrequencyIndex`](struct.SamplingFrequencyIndex.html)
8//!  - [`ChannelConfiguration`](struct.ChannelConfiguration.html)
9
10use std::convert::TryFrom;
11use std::fmt;
12
13/// Represents an error converting a `u8` into an `AudioObjectType`
14#[derive(PartialEq, Debug)]
15pub enum AudioObjectTypeError {
16    /// Tried to convert the 'escape value', `31`, into an `AudioObjectType` (this is not a legitimate
17    /// AOT value but instead is used as part of encoding the field value.
18    EscapeValue,
19    /// Only values 95 and under can be legitimate Audio Object Types.
20    TooLarge(u8),
21}
22
23/// Represents an error converting a `u8` into a `SamplingFrequencyIndex`
24#[derive(PartialEq, Debug)]
25pub enum SamplingFrequencyIndexError {
26    /// Tried to convert the escape value `0xf`, which signals an explicit 24-bit frequency in the
27    /// bitstream rather than an index.
28    EscapeValue,
29    /// Only 4-bit values (0x0–0xe) are valid sampling frequency indices.
30    TooLarge(u8),
31}
32
33/// Represents an error converting a `u8` into a `ChannelConfiguration`
34#[derive(PartialEq, Debug)]
35pub struct ChannelConfigurationError(
36    /// The invalid value that was provided. Only 4-bit values (0x0–0xf) are valid.
37    pub u8,
38);
39
40/// Represents an
41/// [audio object type](https://en.wikipedia.org/wiki/MPEG-4_Part_3#MPEG-4_Audio_Object_Types)
42/// indicator value.
43///
44/// This type can be constructed from a `u8`,
45///
46/// ```rust
47/// # use mpeg4_audio_const::*;
48/// # use std::convert::TryFrom;
49/// assert_eq!(AudioObjectType::AAC_LC, AudioObjectType::try_from(2).unwrap());
50/// assert_eq!(2u8, AudioObjectType::AAC_LC.into());
51/// ```
52///
53/// and will accept values that are 'reserved' in the spec,
54///
55/// ```rust
56/// # use mpeg4_audio_const::*;
57/// # use std::convert::TryFrom;
58/// assert_eq!("RESERVED(95)", format!("{:?}", AudioObjectType::try_from(95).unwrap()));
59/// ```
60///
61/// but disallows values that can't legitimately be represented because they are too large
62/// (the maximum representable a-o-t value is `95`) and also disallows the 'escape value' (value
63/// `31` see [`AOT_ESCAPE_VALUE`](constant.AOT_ESCAPE_VALUE.html)) which is used as part of the
64/// encoding scheme for the a-o-t field rather than as a distinct field value.
65///
66/// ```rust
67/// # use mpeg4_audio_const::*;
68/// # use std::convert::TryFrom;
69/// assert_eq!(Err(AudioObjectTypeError::EscapeValue), AudioObjectType::try_from(31));
70/// assert_eq!(Err(AudioObjectTypeError::TooLarge(97)), AudioObjectType::try_from(97));
71/// ```
72#[derive(Eq, PartialEq, Copy, Clone)]
73pub struct AudioObjectType(u8);
74
75/// This value, `31`, is not used as an _audio object type_, but is instead used in the encoding of
76/// any _audio object type_ value greater than or equal to `32`.
77pub const AOT_ESCAPE_VALUE: u8 = 0b_11111;
78
79impl AudioObjectType {
80    /// 5-bit escape (31) + 6-bit extension (0–63) → max representable value is 95.
81    const MAX: u8 = 95;
82
83    /// Creates an `AudioObjectType` from a `u8`.
84    ///
85    /// Panics if `value` is `31` (the escape value) or greater than `95`.
86    /// In const context, an invalid value produces a compile-time error.
87    pub const fn new(value: u8) -> Self {
88        assert!(value != AOT_ESCAPE_VALUE, "AudioObjectType: 31 is the escape value, not an audio object type");
89        assert!(value <= Self::MAX, "AudioObjectType: value must be 0..=95");
90        Self(value)
91    }
92}
93
94impl From<AudioObjectType> for u8 {
95    fn from(v: AudioObjectType) -> Self {
96        v.0
97    }
98}
99impl TryFrom<u8> for AudioObjectType {
100    type Error = AudioObjectTypeError;
101
102    fn try_from(value: u8) -> Result<Self, Self::Error> {
103        match value {
104            AOT_ESCAPE_VALUE => Err(AudioObjectTypeError::EscapeValue),
105            0..=Self::MAX => Ok(AudioObjectType(value)),
106            _ => Err(AudioObjectTypeError::TooLarge(value)),
107        }
108    }
109}
110
111macro_rules! implement_aot {
112    (
113        $( $tag:literal $id:ident $desc:literal ),* ,
114    ) => {
115
116        impl AudioObjectType {
117            $(
118                #[doc=$desc]
119                pub const $id: AudioObjectType = AudioObjectType($tag);
120            )*
121        }
122
123        impl fmt::Debug for AudioObjectType {
124            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125                match self.0 {
126                    $(
127                        $tag => write!(f, "{}({})", stringify!($id), $tag)
128                    ),* ,
129                    _ => write!(f, "RESERVED({})", self.0),
130                }
131            }
132        }
133    }
134}
135
136implement_aot! {
137    0 NULL "Null",
138    1 AAC_MAIN "AAC main",
139    3 AAC_SSR "AAC SSR",
140    4 AAC_LTP "AAC LTP",
141    7 TWIN_VQ "TwinVQ",
142    8 CELP "CELP",
143    9 HVXC "HVXC",
144    5 SBR "SBR",
145    6 AAC_SCALABLE "AAC Scalable",
146    2 AAC_LC "AAC LC",
147    12 TTSI "TTSI",
148    13 MAIN_SYNTHETIC "Main synthetic",
149    14 WAVETABLE_SYNTHESIS "Wavetable synthesis",
150    15 GENERAL_MIDI "General MIDI",
151    16 ALGORITHMIC_SYNTHESIS_AND_AUDIO_FX "Algorithmic Synthesis and Audio FX",
152    17 ER_AAC_LC "ER AAC LC",
153    19 ER_AAC_LTP "ER AAC LTP",
154    20 ER_AAC_SCALABLE "ER AAC Scalable",
155    21 ER_TWIN_VQ "ER TwinVQ",
156    22 ER_BSAC "ER BSAC",
157    23 ER_AAC_LD "ER AAC LD",
158    24 ER_CELP "ER CELP",
159    25 ER_HVXC "ER HVXC",
160    26 ER_HILN "ER HILN",
161    27 ER_PARAMETRIC "ER Parametric",
162    28 SSC "SSC",
163    29 PS "PS",
164    30 MPEG_SURROUND "MPEG Surround",
165    // 31 - 'escape' - deliberately skipped
166    32 LAYER1 "Layer-1",
167    33 LAYER2 "Layer-2",
168    34 LAYER3 "Layer-3",
169    35 DST "DST",
170    36 ALS "ALS",
171    37 SLS "SLS",
172    38 SLS_NON_CORE "SLS non-core",
173    39 ER_AAC_ELD "ER AAC ELD",
174    40 SMR_SIMPLE "SMR Simple",
175    41 SMR_MAIN "SMR Main",
176    42 USAC "Unified Speech and Audio Coding",
177    43 SAOC "Spatial Audio Object Coding",
178    44 LD_MPEG_SURROUND "Low Delay MPEG Surround",
179    45 SAOC_DE "Spatial Audio Object Coding Dialogue Enhancement",
180    46 AUDIO_SYNC "Audio synchronization tool",
181}
182
183/// A 4-bit sampling frequency index as defined in ISO 14496-3.
184///
185/// Indices 0x0–0xc map to the 13 standard sampling rates (96 kHz down to
186/// 7.35 kHz). Indices 0xd–0xe are reserved. Index 0xf is excluded from this
187/// type as it signals that an explicit 24-bit frequency value follows in the
188/// bitstream instead.
189///
190/// Use [`freq`](Self::freq) to look up the sampling rate in Hz, which returns
191/// `None` for reserved indices.
192#[derive(PartialEq, Eq, Clone, Copy)]
193pub struct SamplingFrequencyIndex(u8);
194
195impl SamplingFrequencyIndex {
196    /// The largest valid 4-bit index (0xe). Index 0xf is the escape value.
197    const MAX: u8 = 0xe;
198
199    /// 96 kHz (index 0x0).
200    pub const FREQ_96000: Self = Self(0x0);
201    /// 88.2 kHz (index 0x1).
202    pub const FREQ_88200: Self = Self(0x1);
203    /// 64 kHz (index 0x2).
204    pub const FREQ_64000: Self = Self(0x2);
205    /// 48 kHz (index 0x3).
206    pub const FREQ_48000: Self = Self(0x3);
207    /// 44.1 kHz (index 0x4).
208    pub const FREQ_44100: Self = Self(0x4);
209    /// 32 kHz (index 0x5).
210    pub const FREQ_32000: Self = Self(0x5);
211    /// 24 kHz (index 0x6).
212    pub const FREQ_24000: Self = Self(0x6);
213    /// 22.05 kHz (index 0x7).
214    pub const FREQ_22050: Self = Self(0x7);
215    /// 16 kHz (index 0x8).
216    pub const FREQ_16000: Self = Self(0x8);
217    /// 12 kHz (index 0x9).
218    pub const FREQ_12000: Self = Self(0x9);
219    /// 11.025 kHz (index 0xa).
220    pub const FREQ_11025: Self = Self(0xa);
221    /// 8 kHz (index 0xb).
222    pub const FREQ_8000: Self = Self(0xb);
223    /// 7.35 kHz (index 0xc).
224    pub const FREQ_7350: Self = Self(0xc);
225
226    /// Creates a `SamplingFrequencyIndex` from a 4-bit value.
227    ///
228    /// Panics if `value` is `0xf` (the escape value) or greater than `0xe`.
229    /// In const context, an invalid value produces a compile-time error.
230    pub const fn new(value: u8) -> Self {
231        assert!(value <= Self::MAX, "SamplingFrequencyIndex: value must be 0x0..=0xe");
232        Self(value)
233    }
234
235    /// Returns the sampling rate in Hz, or `None` if the index is reserved
236    /// or not yet defined.
237    pub fn freq(&self) -> Option<u32> {
238        match self.0 {
239            0x0 => Some(96000),
240            0x1 => Some(88200),
241            0x2 => Some(64000),
242            0x3 => Some(48000),
243            0x4 => Some(44100),
244            0x5 => Some(32000),
245            0x6 => Some(24000),
246            0x7 => Some(22050),
247            0x8 => Some(16000),
248            0x9 => Some(12000),
249            0xa => Some(11025),
250            0xb => Some(8000),
251            0xc => Some(7350),
252            _ => None,
253        }
254    }
255}
256
257impl fmt::Debug for SamplingFrequencyIndex {
258    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
259        match self.0 {
260            0x0 => write!(f, "FREQ_96000(0)"),
261            0x1 => write!(f, "FREQ_88200(1)"),
262            0x2 => write!(f, "FREQ_64000(2)"),
263            0x3 => write!(f, "FREQ_48000(3)"),
264            0x4 => write!(f, "FREQ_44100(4)"),
265            0x5 => write!(f, "FREQ_32000(5)"),
266            0x6 => write!(f, "FREQ_24000(6)"),
267            0x7 => write!(f, "FREQ_22050(7)"),
268            0x8 => write!(f, "FREQ_16000(8)"),
269            0x9 => write!(f, "FREQ_12000(9)"),
270            0xa => write!(f, "FREQ_11025(10)"),
271            0xb => write!(f, "FREQ_8000(11)"),
272            0xc => write!(f, "FREQ_7350(12)"),
273            _ => write!(f, "RESERVED({})", self.0),
274        }
275    }
276}
277
278impl From<SamplingFrequencyIndex> for u8 {
279    fn from(v: SamplingFrequencyIndex) -> Self {
280        v.0
281    }
282}
283impl TryFrom<u8> for SamplingFrequencyIndex {
284    type Error = SamplingFrequencyIndexError;
285
286    fn try_from(value: u8) -> Result<Self, Self::Error> {
287        match value {
288            0..=Self::MAX => Ok(SamplingFrequencyIndex(value)),
289            0xf => Err(SamplingFrequencyIndexError::EscapeValue),
290            _ => Err(SamplingFrequencyIndexError::TooLarge(value)),
291        }
292    }
293}
294
295/// A channel configuration as defined in ISO 14496-3.
296///
297/// In ADTS headers the `channel_configuration` field is 3 bits, covering only
298/// values 0–7. In `AudioSpecificConfig` (MP4/M4A) the field is 4 bits (0–15),
299/// with values 8–14 defined by later amendments to ISO 14496-3.
300///
301/// This crate provides constants for the original 8 configurations (0–7);
302/// callers can define additional constants for the extended layouts as needed.
303#[derive(PartialEq, Eq, Clone, Copy)]
304pub struct ChannelConfiguration(u8);
305
306impl ChannelConfiguration {
307    /// The largest valid 4-bit value (0xf).
308    const MAX: u8 = 0xf;
309
310    /// Channel configuration defined by the audio object type specific config.
311    pub const OBJECT_TYPE_SPECIFIC_CONFIG: Self = Self(0x0);
312    /// Mono (1 channel).
313    pub const MONO: Self = Self(0x1);
314    /// Stereo (2 channels).
315    pub const STEREO: Self = Self(0x2);
316    /// 3 channels.
317    pub const THREE: Self = Self(0x3);
318    /// 4 channels.
319    pub const FOUR: Self = Self(0x4);
320    /// 5 channels.
321    pub const FIVE: Self = Self(0x5);
322    /// 5.1 surround (6 channels).
323    pub const FIVE_ONE: Self = Self(0x6);
324    /// 7.1 surround (8 channels).
325    pub const SEVEN_ONE: Self = Self(0x7);
326
327    /// Creates a `ChannelConfiguration` from a 4-bit value.
328    ///
329    /// Panics if `value` is greater than `0xf`.
330    /// In const context, an invalid value produces a compile-time error.
331    pub const fn new(value: u8) -> Self {
332        assert!(value <= Self::MAX, "ChannelConfiguration: expected a 4 bit value");
333        Self(value)
334    }
335}
336
337impl fmt::Debug for ChannelConfiguration {
338    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
339        match self.0 {
340            0 => write!(f, "OBJECT_TYPE_SPECIFIC_CONFIG(0)"),
341            1 => write!(f, "MONO(1)"),
342            2 => write!(f, "STEREO(2)"),
343            3 => write!(f, "THREE(3)"),
344            4 => write!(f, "FOUR(4)"),
345            5 => write!(f, "FIVE(5)"),
346            6 => write!(f, "FIVE_ONE(6)"),
347            7 => write!(f, "SEVEN_ONE(7)"),
348            _ => write!(f, "RESERVED({})", self.0),
349        }
350    }
351}
352
353impl From<ChannelConfiguration> for u8 {
354    fn from(v: ChannelConfiguration) -> Self {
355        v.0
356    }
357}
358impl TryFrom<u8> for ChannelConfiguration {
359    type Error = ChannelConfigurationError;
360
361    fn try_from(value: u8) -> Result<Self, Self::Error> {
362        match value {
363            0..=Self::MAX => Ok(ChannelConfiguration(value)),
364            _ => Err(ChannelConfigurationError(value)),
365        }
366    }
367}
368
369#[cfg(test)]
370mod tests {
371    use super::*;
372    use std::convert::TryFrom;
373
374    #[test]
375    fn aot_escape_value() {
376        assert_eq!(
377            Err(AudioObjectTypeError::EscapeValue),
378            AudioObjectType::try_from(AOT_ESCAPE_VALUE)
379        );
380    }
381
382    #[test]
383    fn aot_too_large() {
384        assert_eq!(
385            Err(AudioObjectTypeError::TooLarge(96)),
386            AudioObjectType::try_from(96)
387        );
388    }
389
390    #[test]
391    fn aot_valid() {
392        assert_eq!(AudioObjectType::AAC_LC, AudioObjectType::try_from(2).unwrap());
393        assert_eq!(2u8, AudioObjectType::AAC_LC.into());
394    }
395
396    #[test]
397    fn sampling_frequency_known_indices() {
398        assert_eq!(SamplingFrequencyIndex::FREQ_96000.freq(), Some(96000));
399        assert_eq!(SamplingFrequencyIndex::FREQ_88200.freq(), Some(88200));
400        assert_eq!(SamplingFrequencyIndex::FREQ_64000.freq(), Some(64000));
401        assert_eq!(SamplingFrequencyIndex::FREQ_48000.freq(), Some(48000));
402        assert_eq!(SamplingFrequencyIndex::FREQ_44100.freq(), Some(44100));
403        assert_eq!(SamplingFrequencyIndex::FREQ_32000.freq(), Some(32000));
404        assert_eq!(SamplingFrequencyIndex::FREQ_24000.freq(), Some(24000));
405        assert_eq!(SamplingFrequencyIndex::FREQ_22050.freq(), Some(22050));
406        assert_eq!(SamplingFrequencyIndex::FREQ_16000.freq(), Some(16000));
407        assert_eq!(SamplingFrequencyIndex::FREQ_12000.freq(), Some(12000));
408        assert_eq!(SamplingFrequencyIndex::FREQ_11025.freq(), Some(11025));
409        assert_eq!(SamplingFrequencyIndex::FREQ_8000.freq(), Some(8000));
410        assert_eq!(SamplingFrequencyIndex::FREQ_7350.freq(), Some(7350));
411    }
412
413    #[test]
414    fn sampling_frequency_reserved_indices() {
415        assert_eq!(SamplingFrequencyIndex::new(0xd).freq(), None);
416        assert_eq!(SamplingFrequencyIndex::new(0xe).freq(), None);
417    }
418
419    #[test]
420    fn sampling_frequency_new_valid() {
421        assert_eq!(SamplingFrequencyIndex::new(0x3), SamplingFrequencyIndex::FREQ_48000);
422    }
423
424    #[test]
425    #[should_panic]
426    fn sampling_frequency_new_escape() {
427        SamplingFrequencyIndex::new(0xf);
428    }
429
430    #[test]
431    #[should_panic]
432    fn sampling_frequency_new_too_large() {
433        SamplingFrequencyIndex::new(0x10);
434    }
435
436    #[test]
437    fn channel_configuration_valid() {
438        assert_eq!(ChannelConfiguration::MONO, ChannelConfiguration::new(1));
439        assert_eq!(ChannelConfiguration::STEREO, ChannelConfiguration::new(2));
440        assert_eq!(ChannelConfiguration::FIVE_ONE, ChannelConfiguration::new(6));
441        assert_eq!(ChannelConfiguration::SEVEN_ONE, ChannelConfiguration::new(7));
442    }
443
444    #[test]
445    fn channel_configuration_reserved() {
446        // Values 8-15 are valid 4-bit values, just not assigned constants
447        let _ = ChannelConfiguration::new(0xf);
448    }
449
450    #[test]
451    #[should_panic]
452    fn channel_configuration_too_large() {
453        ChannelConfiguration::new(0x10);
454    }
455
456    #[test]
457    fn sampling_frequency_try_from_valid() {
458        assert_eq!(
459            SamplingFrequencyIndex::try_from(0x3),
460            Ok(SamplingFrequencyIndex::FREQ_48000),
461        );
462        assert_eq!(
463            u8::from(SamplingFrequencyIndex::try_from(0x3).unwrap()),
464            0x3,
465        );
466    }
467
468    #[test]
469    fn sampling_frequency_try_from_escape() {
470        assert_eq!(
471            SamplingFrequencyIndex::try_from(0xf),
472            Err(SamplingFrequencyIndexError::EscapeValue),
473        );
474    }
475
476    #[test]
477    fn sampling_frequency_try_from_too_large() {
478        assert_eq!(
479            SamplingFrequencyIndex::try_from(0x10),
480            Err(SamplingFrequencyIndexError::TooLarge(0x10)),
481        );
482    }
483
484    #[test]
485    fn channel_configuration_try_from_valid() {
486        assert_eq!(
487            ChannelConfiguration::try_from(2),
488            Ok(ChannelConfiguration::STEREO),
489        );
490        assert_eq!(
491            ChannelConfiguration::try_from(0xf),
492            Ok(ChannelConfiguration::new(0xf)),
493        );
494    }
495
496    #[test]
497    fn channel_configuration_try_from_too_large() {
498        assert_eq!(
499            ChannelConfiguration::try_from(0x10),
500            Err(ChannelConfigurationError(0x10)),
501        );
502    }
503}