Skip to main content

mpeg_audio/
types.rs

1//! Freestanding MPEG audio frame-header types (no Mediaway dependency).
2//!
3//! Layer III only (v1 scope) — see crate-local ADR-0001.
4
5#![forbid(unsafe_code)]
6#![allow(
7    clippy::redundant_pub_crate,
8    reason = "crate-private helpers used by mux.rs/demux.rs; module itself is private"
9)]
10
11/// MPEG audio version (2-bit `ID` field combined with the layer's version bit).
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13#[non_exhaustive]
14pub enum MpegVersion {
15    /// MPEG Version 1 (44100/48000/32000 Hz family).
16    Mpeg1,
17    /// MPEG Version 2 (22050/24000/16000 Hz family).
18    Mpeg2,
19    /// MPEG Version 2.5 (11025/12000/8000 Hz family, unofficial low-rate extension).
20    Mpeg25,
21}
22
23/// Channel mode (2-bit field).
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25#[non_exhaustive]
26pub enum ChannelMode {
27    /// Stereo.
28    Stereo,
29    /// Joint stereo (intensity/MS).
30    JointStereo,
31    /// Dual mono (two independent channels).
32    DualChannel,
33    /// Mono.
34    Mono,
35}
36
37impl ChannelMode {
38    pub(crate) const fn bits(self) -> u8 {
39        match self {
40            Self::Stereo => 0,
41            Self::JointStereo => 1,
42            Self::DualChannel => 2,
43            Self::Mono => 3,
44        }
45    }
46
47    pub(crate) const fn from_bits(bits: u8) -> Self {
48        match bits & 0x03 {
49            0 => Self::Stereo,
50            1 => Self::JointStereo,
51            2 => Self::DualChannel,
52            _ => Self::Mono,
53        }
54    }
55}
56
57const MPEG1_SAMPLE_RATES: [u32; 3] = [44_100, 48_000, 32_000];
58const MPEG2_SAMPLE_RATES: [u32; 3] = [22_050, 24_000, 16_000];
59const MPEG25_SAMPLE_RATES: [u32; 3] = [11_025, 12_000, 8_000];
60
61const MPEG1_LAYER3_BITRATES_KBPS: [u16; 14] = [
62    32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320,
63];
64const MPEG2_LAYER3_BITRATES_KBPS: [u16; 14] =
65    [8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160];
66
67pub(crate) const fn sample_rate_table(version: MpegVersion) -> [u32; 3] {
68    match version {
69        MpegVersion::Mpeg1 => MPEG1_SAMPLE_RATES,
70        MpegVersion::Mpeg2 => MPEG2_SAMPLE_RATES,
71        MpegVersion::Mpeg25 => MPEG25_SAMPLE_RATES,
72    }
73}
74
75pub(crate) const fn bitrate_table(version: MpegVersion) -> [u16; 14] {
76    match version {
77        MpegVersion::Mpeg1 => MPEG1_LAYER3_BITRATES_KBPS,
78        MpegVersion::Mpeg2 | MpegVersion::Mpeg25 => MPEG2_LAYER3_BITRATES_KBPS,
79    }
80}
81
82/// Layer III frame-length coefficient: `floor(coeff * bitrate_bps / sample_rate) + padding`.
83pub(crate) const fn frame_len_coefficient(version: MpegVersion) -> u32 {
84    match version {
85        MpegVersion::Mpeg1 => 144_000,
86        MpegVersion::Mpeg2 | MpegVersion::Mpeg25 => 72_000,
87    }
88}
89
90/// Layer III frame header fields.
91///
92/// The padding bit is **not** part of this struct: real Layer III streams flip it
93/// per frame (a bit-reservoir accounting detail owned by the encoder, not the
94/// container), so it is a parameter of [`FrameHeader::frame_len`] and
95/// `Muxer::write_frame` instead of a fixed per-session value.
96#[derive(Debug, Clone, Copy, PartialEq, Eq)]
97pub struct FrameHeader {
98    /// MPEG version.
99    pub version: MpegVersion,
100    /// Bitrate in kbps — must be one of the 14 standard values for `version` (Layer III).
101    pub bitrate_kbps: u16,
102    /// Sample rate — must be one of the 3 standard rates for `version`.
103    pub sample_rate: u32,
104    /// Channel mode.
105    pub channel_mode: ChannelMode,
106}
107
108impl FrameHeader {
109    /// Total frame length in bytes (4-byte header included), Layer III formula.
110    #[must_use]
111    pub const fn frame_len(self, padding: bool) -> usize {
112        let coeff = frame_len_coefficient(self.version) * self.bitrate_kbps as u32;
113        let base = coeff / self.sample_rate;
114        base as usize + if padding { 1 } else { 0 }
115    }
116}