1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12#[repr(u8)]
13pub enum ChannelPosition {
14 Fl = 0,
16 Fr = 1,
18 Fc = 2,
20 Lfe = 3,
22 Bl = 4,
24 Br = 5,
26 Flc = 6,
28 Frc = 7,
30 Bc = 8,
32 Sl = 9,
34 Sr = 10,
36}
37
38impl ChannelPosition {
39 pub const ALL: [ChannelPosition; 11] = [
41 ChannelPosition::Fl,
42 ChannelPosition::Fr,
43 ChannelPosition::Fc,
44 ChannelPosition::Lfe,
45 ChannelPosition::Bl,
46 ChannelPosition::Br,
47 ChannelPosition::Flc,
48 ChannelPosition::Frc,
49 ChannelPosition::Bc,
50 ChannelPosition::Sl,
51 ChannelPosition::Sr,
52 ];
53
54 const fn bit(self) -> u16 {
55 1 << (self as u16)
56 }
57}
58
59#[derive(Debug, Clone, Copy, PartialEq, Eq)]
61pub struct ChannelLayout(u16);
62
63impl ChannelLayout {
64 pub const MONO: ChannelLayout = ChannelLayout::of(&[ChannelPosition::Fc]);
66 pub const STEREO: ChannelLayout =
68 ChannelLayout::of(&[ChannelPosition::Fl, ChannelPosition::Fr]);
69 pub const SURROUND_5_1: ChannelLayout = ChannelLayout::of(&[
71 ChannelPosition::Fl,
72 ChannelPosition::Fr,
73 ChannelPosition::Fc,
74 ChannelPosition::Lfe,
75 ChannelPosition::Bl,
76 ChannelPosition::Br,
77 ]);
78 pub const SURROUND_7_1: ChannelLayout = ChannelLayout::of(&[
80 ChannelPosition::Fl,
81 ChannelPosition::Fr,
82 ChannelPosition::Fc,
83 ChannelPosition::Lfe,
84 ChannelPosition::Bl,
85 ChannelPosition::Br,
86 ChannelPosition::Sl,
87 ChannelPosition::Sr,
88 ]);
89
90 pub const fn of(positions: &[ChannelPosition]) -> Self {
92 let mut mask = 0u16;
93 let mut i = 0;
94 while i < positions.len() {
95 mask |= positions[i].bit();
96 i += 1;
97 }
98 ChannelLayout(mask)
99 }
100
101 pub const fn default_for(channels: u8) -> Option<Self> {
105 use ChannelPosition::*;
106 Some(match channels {
107 1 => Self::MONO,
108 2 => Self::STEREO,
109 3 => Self::of(&[Fl, Fr, Lfe]),
110 4 => Self::of(&[Fl, Fr, Fc, Bc]),
111 5 => Self::of(&[Fl, Fr, Fc, Bl, Br]),
112 6 => Self::SURROUND_5_1,
113 7 => Self::of(&[Fl, Fr, Fc, Lfe, Bc, Sl, Sr]),
114 8 => Self::SURROUND_7_1,
115 _ => return None,
116 })
117 }
118
119 pub const fn channels(self) -> u8 {
121 self.0.count_ones() as u8
122 }
123
124 pub const fn contains(self, position: ChannelPosition) -> bool {
125 self.0 & position.bit() != 0
126 }
127
128 pub const fn index_of(self, position: ChannelPosition) -> Option<usize> {
131 if !self.contains(position) {
132 return None;
133 }
134 Some((self.0 & (position.bit() - 1)).count_ones() as usize)
135 }
136
137 pub fn positions(self) -> impl Iterator<Item = ChannelPosition> {
139 ChannelPosition::ALL
140 .into_iter()
141 .filter(move |p| self.contains(*p))
142 }
143}
144
145#[cfg(test)]
146mod tests {
147 use super::*;
148
149 #[test]
150 fn default_layouts_match_their_counts() {
151 for n in 1..=8 {
152 let layout = ChannelLayout::default_for(n).expect("default exists");
153 assert_eq!(layout.channels(), n, "count {n}");
154 }
155 assert_eq!(ChannelLayout::default_for(0), None);
156 assert_eq!(ChannelLayout::default_for(9), None);
157 }
158
159 #[test]
160 fn index_follows_interleave_order() {
161 let l = ChannelLayout::SURROUND_5_1;
163 assert_eq!(l.index_of(ChannelPosition::Fl), Some(0));
164 assert_eq!(l.index_of(ChannelPosition::Fc), Some(2));
165 assert_eq!(l.index_of(ChannelPosition::Lfe), Some(3));
166 assert_eq!(l.index_of(ChannelPosition::Br), Some(5));
167 assert_eq!(l.index_of(ChannelPosition::Sl), None);
168 let expected = [
169 ChannelPosition::Fl,
170 ChannelPosition::Fr,
171 ChannelPosition::Fc,
172 ChannelPosition::Lfe,
173 ChannelPosition::Bl,
174 ChannelPosition::Br,
175 ];
176 assert!(l.positions().eq(expected));
177 }
178}