Skip to main content

moq_audio/
layout.rs

1use crate::Error;
2
3/// Speaker meaning and interleaving order for PCM channels.
4///
5/// Named layouts interleave in the SMPTE/WAVE order (front left, front right,
6/// center, LFE, back left, back right, back center, side left, side right),
7/// keeping only the speakers the layout has. Every decoder reorders its codec's
8/// native order into this one.
9#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
10#[non_exhaustive]
11pub enum Layout {
12	/// One center channel.
13	Mono,
14	/// Left then right channels.
15	#[default]
16	Stereo,
17	/// Channels with no declared speaker positions, in source order.
18	///
19	/// Passes through unchanged but can't be remixed into another layout.
20	Discrete(u32),
21	/// 2.1: left, right, LFE.
22	TwoPointOne,
23	/// 3.0: left, right, center.
24	ThreePointZero,
25	/// Quad: left, right, back left, back right.
26	Quad,
27	/// 4.0: left, right, center, back center.
28	FourPointZero,
29	/// 5.0: left, right, center, side left, side right.
30	FivePointZero,
31	/// 5.1: left, right, center, LFE, side left, side right.
32	FivePointOne,
33	/// 6.1: left, right, center, LFE, back center, side left, side right.
34	SixPointOne,
35	/// 7.1: left, right, center, LFE, back left, back right, side left, side right.
36	SevenPointOne,
37}
38
39/// One speaker position, declared in the canonical interleaving order.
40#[derive(Clone, Copy, Debug, PartialEq, Eq)]
41pub(crate) enum Speaker {
42	FrontLeft,
43	FrontRight,
44	FrontCenter,
45	Lfe,
46	BackLeft,
47	BackRight,
48	BackCenter,
49	SideLeft,
50	SideRight,
51}
52
53impl Layout {
54	/// The default layout for a channel count, by the WAVE convention: 1 is mono,
55	/// 2 stereo, 3 2.1, 4 quad, 5 5.0, 6 5.1, 7 6.1, and 8 7.1.
56	///
57	/// Any other count is [`Discrete`](Self::Discrete), since there is no
58	/// convention to take speaker positions from.
59	pub fn from_channels(channels: u32) -> Result<Self, Error> {
60		match channels {
61			0 => Err(Error::Unsupported(
62				"audio layout must contain at least one channel".into(),
63			)),
64			1 => Ok(Self::Mono),
65			2 => Ok(Self::Stereo),
66			3 => Ok(Self::TwoPointOne),
67			4 => Ok(Self::Quad),
68			5 => Ok(Self::FivePointZero),
69			6 => Ok(Self::FivePointOne),
70			7 => Ok(Self::SixPointOne),
71			8 => Ok(Self::SevenPointOne),
72			channels => Ok(Self::Discrete(channels)),
73		}
74	}
75
76	/// Number of interleaved channels in this layout.
77	pub fn channels(self) -> u32 {
78		match self {
79			Self::Discrete(channels) => channels,
80			layout => layout.speakers().map_or(0, <[_]>::len) as u32,
81		}
82	}
83
84	/// The speaker each channel feeds, in interleaved order, or `None` for a
85	/// discrete layout.
86	pub(crate) fn speakers(self) -> Option<&'static [Speaker]> {
87		use Speaker::*;
88
89		Some(match self {
90			Self::Mono => &[FrontCenter],
91			Self::Stereo => &[FrontLeft, FrontRight],
92			Self::Discrete(_) => return None,
93			Self::TwoPointOne => &[FrontLeft, FrontRight, Lfe],
94			Self::ThreePointZero => &[FrontLeft, FrontRight, FrontCenter],
95			Self::Quad => &[FrontLeft, FrontRight, BackLeft, BackRight],
96			Self::FourPointZero => &[FrontLeft, FrontRight, FrontCenter, BackCenter],
97			Self::FivePointZero => &[FrontLeft, FrontRight, FrontCenter, SideLeft, SideRight],
98			Self::FivePointOne => &[FrontLeft, FrontRight, FrontCenter, Lfe, SideLeft, SideRight],
99			Self::SixPointOne => &[FrontLeft, FrontRight, FrontCenter, Lfe, BackCenter, SideLeft, SideRight],
100			Self::SevenPointOne => &[
101				FrontLeft,
102				FrontRight,
103				FrontCenter,
104				Lfe,
105				BackLeft,
106				BackRight,
107				SideLeft,
108				SideRight,
109			],
110		})
111	}
112
113	pub(crate) fn validate(self) -> Result<(), Error> {
114		if self.channels() == 0 {
115			return Err(Error::Unsupported(
116				"audio layout must contain at least one channel".into(),
117			));
118		}
119		Ok(())
120	}
121}
122
123#[cfg(test)]
124mod tests {
125	use super::*;
126
127	#[test]
128	fn counts_map_to_the_wave_defaults() {
129		let expected = [
130			Layout::Mono,
131			Layout::Stereo,
132			Layout::TwoPointOne,
133			Layout::Quad,
134			Layout::FivePointZero,
135			Layout::FivePointOne,
136			Layout::SixPointOne,
137			Layout::SevenPointOne,
138		];
139		for (count, layout) in (1..).zip(expected) {
140			assert_eq!(Layout::from_channels(count).unwrap(), layout);
141			assert_eq!(layout.channels(), count);
142		}
143
144		assert_eq!(Layout::from_channels(9).unwrap(), Layout::Discrete(9));
145		assert!(Layout::from_channels(0).is_err());
146	}
147
148	/// Canonical order is the WAVE bit order, so every layout's speakers must
149	/// ascend through [`Speaker`]'s declaration order without repeating.
150	#[test]
151	fn speakers_follow_the_canonical_order() {
152		let layouts = [
153			Layout::Mono,
154			Layout::Stereo,
155			Layout::TwoPointOne,
156			Layout::ThreePointZero,
157			Layout::Quad,
158			Layout::FourPointZero,
159			Layout::FivePointZero,
160			Layout::FivePointOne,
161			Layout::SixPointOne,
162			Layout::SevenPointOne,
163		];
164		for layout in layouts {
165			let speakers = layout.speakers().unwrap();
166			assert!(
167				speakers.windows(2).all(|pair| (pair[0] as u8) < (pair[1] as u8)),
168				"{layout:?} is out of order"
169			);
170		}
171	}
172}