Skip to main content

broadcast_loudness/
channel_layout.rs

1//! Channel layout and per-channel weighting (ITU-R BS.1770-5 §Annex 1, Table 3).
2//!
3//! Each layout carries the BS.1770 channel-weighting coefficients G_i.
4//! The LFE channel is always excluded from measurement (weight = 0.0).
5
6use broadcast_common::impl_spec_display;
7
8/// Channel layout defining the set of channels to measure and their
9/// BS.1770-5 per-channel weighting coefficients G_i.
10///
11/// See ITU-R BS.1770-5 Annex 1 Table 3.
12#[derive(Debug, Clone, Copy, PartialEq)]
13#[non_exhaustive]
14pub enum ChannelLayout {
15    /// Mono (centre channel): 1 channel, G = 1.0.
16    Mono,
17    /// Stereo (left, right): 2 channels, G_L = 1.0, G_R = 1.0.
18    Stereo,
19    /// 5.1 surround (L, R, C, LFE, Ls, Rs): 6 channels, LFE excluded.
20    /// G_L=1.0, G_R=1.0, G_C=1.0, G_Ls=1.41, G_Rs=1.41, G_LFE=0.0.
21    Surround51,
22    /// Custom channel count with explicit per-channel weights.
23    /// `weights[i]` is G_i for channel `i`.
24    Custom {
25        /// Per-channel weighting coefficients (length = channel count).
26        weights: &'static [f64],
27    },
28}
29
30impl ChannelLayout {
31    /// Number of audio channels (including LFE if present).
32    #[must_use]
33    pub fn channel_count(&self) -> usize {
34        match self {
35            Self::Mono => 1,
36            Self::Stereo => 2,
37            Self::Surround51 => 6,
38            Self::Custom { weights } => weights.len(),
39        }
40    }
41
42    /// BS.1770-5 weighting coefficient G_i for channel `index`.
43    ///
44    /// Returns 0.0 for LFE channels (excluded from measurement).
45    #[must_use]
46    pub fn weight(&self, index: usize) -> f64 {
47        match self {
48            Self::Mono => 1.0,
49            Self::Stereo => {
50                match index {
51                    0 => 1.0, // L
52                    1 => 1.0, // R
53                    _ => 0.0,
54                }
55            }
56            Self::Surround51 => {
57                match index {
58                    0 => 1.0,  // L
59                    1 => 1.0,  // R
60                    2 => 1.0,  // C
61                    3 => 0.0,  // LFE (excluded)
62                    4 => 1.41, // Ls
63                    5 => 1.41, // Rs
64                    _ => 0.0,
65                }
66            }
67            Self::Custom { weights } => weights.get(index).copied().unwrap_or(0.0),
68        }
69    }
70
71    /// Display name for this layout.
72    #[must_use]
73    pub fn name(&self) -> &'static str {
74        match self {
75            Self::Mono => "Mono",
76            Self::Stereo => "Stereo",
77            Self::Surround51 => "5.1 Surround",
78            Self::Custom { .. } => "Custom",
79        }
80    }
81}
82
83impl_spec_display!(ChannelLayout);