1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
//! Public channel layout and per-frame Copy metadata (TASK-61).
//!
//! No heap: a stable format is an 8-slot `[Option<Channel>; 8]`. Priming
//! and remainder live on the decode tallies.
/// Product plane ceiling ([`crate::DEFAULT_MAX_CHANNELS`]).
pub const MAX_PLANES: usize = 8;
/// One output plane's identity.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[non_exhaustive]
pub enum Channel {
FrontLeft,
FrontRight,
FrontCenter,
Lfe,
BackLeft,
BackRight,
BackCenter,
SideLeft,
SideRight,
/// PCE leftover, speech downmix, or an unlabeled extra element.
#[default]
Other,
}
/// How the plane vector was produced.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[non_exhaustive]
pub enum Layout {
/// ISO Table 4.1 `channel_configuration` 1–7.
Mpeg(u8),
/// PCE declaration order (front, side, back, LFE).
Pce,
/// [`crate::ChannelMode::Mono`]: mean of non-LFE planes.
SpeechMono,
/// Config 0 without PCE, or extra elements appended.
#[default]
Unspecified,
}
/// Copy metadata for one decoded frame or a finished stream.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[non_exhaustive]
pub struct FrameMeta {
pub layout: Layout,
/// AAC core rate (before SBR upsample). `0` if nothing was decoded.
pub core_rate: u32,
/// Presentation rate (2× core for HE). Matches [`super::Frame::sample_rate`].
pub output_rate: u32,
/// Per-plane labels; unused slots are `None`.
pub channels: [Option<Channel>; MAX_PLANES],
}
impl FrameMeta {
pub const EMPTY: Self = Self {
layout: Layout::Unspecified,
core_rate: 0,
output_rate: 0,
channels: [None; MAX_PLANES],
};
#[must_use]
pub fn from_labels(
layout: Layout,
core_rate: u32,
output_rate: u32,
labels: &[Channel],
) -> Self {
let mut channels = [None; MAX_PLANES];
for (slot, &c) in channels.iter_mut().zip(labels.iter()) {
*slot = Some(c);
}
Self {
layout,
core_rate,
output_rate,
channels,
}
}
#[must_use]
pub fn speech_mono(core_rate: u32, output_rate: u32) -> Self {
Self::from_labels(
Layout::SpeechMono,
core_rate,
output_rate,
&[Channel::Other],
)
}
/// Labels of live planes, in output order.
pub fn labels(&self) -> impl Iterator<Item = Channel> + '_ {
self.channels.iter().copied().flatten()
}
/// Index of the LFE plane, if any.
#[must_use]
pub fn lfe_index(&self) -> Option<usize> {
self.channels.iter().position(|&c| c == Some(Channel::Lfe))
}
}
/// Per-frame metadata from the decode-side facts.
#[must_use]
pub(crate) fn frame_meta(
speech: bool,
pce: bool,
cfg: u8,
core_rate: u32,
output_rate: u32,
labels: &[Channel],
) -> FrameMeta {
if speech {
return FrameMeta::speech_mono(core_rate, output_rate);
}
let layout = if pce { Layout::Pce } else { mpeg_layout(cfg) };
FrameMeta::from_labels(layout, core_rate, output_rate, labels)
}
/// [`Layout::Mpeg`] for the default configurations 1–7, else
/// [`Layout::Unspecified`].
#[must_use]
pub(crate) fn mpeg_layout(cfg: u8) -> Layout {
if (1..=7).contains(&cfg) {
Layout::Mpeg(cfg)
} else {
Layout::Unspecified
}
}
/// MPEG default-layout labels (lavc / Table 4.1 order). Empty if `cfg` is
/// not 1–7. Config 7 (7.1) is FL FR FC LFE BL BR SL SR: ISO calls the
/// third CPE "left/right outside front"; lavc presents it as the side
/// pair, and so does syom.
#[must_use]
pub fn mpeg_channels(cfg: u8) -> &'static [Channel] {
use Channel::{
BackCenter, BackLeft, BackRight, FrontCenter, FrontLeft, FrontRight, Lfe, SideLeft,
SideRight,
};
match cfg {
1 => &[FrontCenter],
2 => &[FrontLeft, FrontRight],
3 => &[FrontLeft, FrontRight, FrontCenter],
4 => &[FrontLeft, FrontRight, FrontCenter, BackCenter],
5 => &[FrontLeft, FrontRight, FrontCenter, BackLeft, BackRight],
6 => &[FrontLeft, FrontRight, FrontCenter, Lfe, BackLeft, BackRight],
7 => &[
FrontLeft,
FrontRight,
FrontCenter,
Lfe,
BackLeft,
BackRight,
SideLeft,
SideRight,
],
_ => &[],
}
}
#[cfg(test)]
#[path = "layout_tests.rs"]
mod layout_tests;