1use core::fmt;
27
28use crate::Error;
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub enum Mode {
37 SilkOnly,
39 Hybrid,
42 CeltOnly,
45}
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57pub enum Bandwidth {
58 Nb,
59 Mb,
60 Wb,
61 Swb,
62 Fb,
63}
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq)]
67pub enum ChannelMapping {
68 Mono,
70 Stereo,
72}
73
74#[derive(Debug, Clone, Copy, PartialEq, Eq)]
77pub enum FrameCountCode {
78 One,
80 TwoEqual,
82 TwoUnequal,
84 Arbitrary,
86}
87
88#[derive(Debug, Clone, Copy, PartialEq, Eq)]
94pub struct OpusTocByte {
95 pub config: u8,
97 pub mode: Mode,
99 pub bandwidth: Bandwidth,
101 pub frame_size_tenths_ms: u16,
107 pub channels: ChannelMapping,
109 pub frame_count_code: FrameCountCode,
111}
112
113impl OpusTocByte {
114 pub fn parse(packet: &[u8]) -> Result<Self, Error> {
122 let first = *packet.first().ok_or(Error::EmptyPacket)?;
123 Ok(Self::from_byte(first))
124 }
125
126 pub fn from_byte(byte: u8) -> Self {
129 let config = byte >> 3;
133 let s = (byte >> 2) & 0x01;
134 let c = byte & 0x03;
135
136 let (mode, bandwidth, frame_size_tenths_ms) = decode_config(config);
137 let channels = if s == 0 {
138 ChannelMapping::Mono
139 } else {
140 ChannelMapping::Stereo
141 };
142 let frame_count_code = match c {
143 0 => FrameCountCode::One,
144 1 => FrameCountCode::TwoEqual,
145 2 => FrameCountCode::TwoUnequal,
146 3 => FrameCountCode::Arbitrary,
147 _ => unreachable!("c is masked to 2 bits"),
148 };
149
150 Self {
151 config,
152 mode,
153 bandwidth,
154 frame_size_tenths_ms,
155 channels,
156 frame_count_code,
157 }
158 }
159
160 pub fn frame_count_range(self) -> (u8, u8) {
165 match self.frame_count_code {
166 FrameCountCode::One => (1, 1),
167 FrameCountCode::TwoEqual | FrameCountCode::TwoUnequal => (2, 2),
168 FrameCountCode::Arbitrary => (1, 48),
170 }
171 }
172}
173
174impl fmt::Display for Mode {
175 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176 f.write_str(match self {
177 Mode::SilkOnly => "SILK-only",
178 Mode::Hybrid => "Hybrid",
179 Mode::CeltOnly => "CELT-only",
180 })
181 }
182}
183
184impl fmt::Display for Bandwidth {
185 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186 f.write_str(match self {
187 Bandwidth::Nb => "NB",
188 Bandwidth::Mb => "MB",
189 Bandwidth::Wb => "WB",
190 Bandwidth::Swb => "SWB",
191 Bandwidth::Fb => "FB",
192 })
193 }
194}
195
196fn decode_config(config: u8) -> (Mode, Bandwidth, u16) {
204 debug_assert!(config < 32);
205 match config {
206 0 => (Mode::SilkOnly, Bandwidth::Nb, 100),
208 1 => (Mode::SilkOnly, Bandwidth::Nb, 200),
209 2 => (Mode::SilkOnly, Bandwidth::Nb, 400),
210 3 => (Mode::SilkOnly, Bandwidth::Nb, 600),
211 4 => (Mode::SilkOnly, Bandwidth::Mb, 100),
212 5 => (Mode::SilkOnly, Bandwidth::Mb, 200),
213 6 => (Mode::SilkOnly, Bandwidth::Mb, 400),
214 7 => (Mode::SilkOnly, Bandwidth::Mb, 600),
215 8 => (Mode::SilkOnly, Bandwidth::Wb, 100),
216 9 => (Mode::SilkOnly, Bandwidth::Wb, 200),
217 10 => (Mode::SilkOnly, Bandwidth::Wb, 400),
218 11 => (Mode::SilkOnly, Bandwidth::Wb, 600),
219 12 => (Mode::Hybrid, Bandwidth::Swb, 100),
221 13 => (Mode::Hybrid, Bandwidth::Swb, 200),
222 14 => (Mode::Hybrid, Bandwidth::Fb, 100),
223 15 => (Mode::Hybrid, Bandwidth::Fb, 200),
224 16 => (Mode::CeltOnly, Bandwidth::Nb, 25),
226 17 => (Mode::CeltOnly, Bandwidth::Nb, 50),
227 18 => (Mode::CeltOnly, Bandwidth::Nb, 100),
228 19 => (Mode::CeltOnly, Bandwidth::Nb, 200),
229 20 => (Mode::CeltOnly, Bandwidth::Wb, 25),
230 21 => (Mode::CeltOnly, Bandwidth::Wb, 50),
231 22 => (Mode::CeltOnly, Bandwidth::Wb, 100),
232 23 => (Mode::CeltOnly, Bandwidth::Wb, 200),
233 24 => (Mode::CeltOnly, Bandwidth::Swb, 25),
234 25 => (Mode::CeltOnly, Bandwidth::Swb, 50),
235 26 => (Mode::CeltOnly, Bandwidth::Swb, 100),
236 27 => (Mode::CeltOnly, Bandwidth::Swb, 200),
237 28 => (Mode::CeltOnly, Bandwidth::Fb, 25),
238 29 => (Mode::CeltOnly, Bandwidth::Fb, 50),
239 30 => (Mode::CeltOnly, Bandwidth::Fb, 100),
240 31 => (Mode::CeltOnly, Bandwidth::Fb, 200),
241 _ => unreachable!("config is masked to 5 bits"),
242 }
243}
244
245#[cfg(test)]
246mod tests {
247 use super::*;
248
249 #[test]
255 fn table2_all_32_configs() {
256 let expected: [(Mode, Bandwidth, u16); 32] = [
258 (Mode::SilkOnly, Bandwidth::Nb, 100),
259 (Mode::SilkOnly, Bandwidth::Nb, 200),
260 (Mode::SilkOnly, Bandwidth::Nb, 400),
261 (Mode::SilkOnly, Bandwidth::Nb, 600),
262 (Mode::SilkOnly, Bandwidth::Mb, 100),
263 (Mode::SilkOnly, Bandwidth::Mb, 200),
264 (Mode::SilkOnly, Bandwidth::Mb, 400),
265 (Mode::SilkOnly, Bandwidth::Mb, 600),
266 (Mode::SilkOnly, Bandwidth::Wb, 100),
267 (Mode::SilkOnly, Bandwidth::Wb, 200),
268 (Mode::SilkOnly, Bandwidth::Wb, 400),
269 (Mode::SilkOnly, Bandwidth::Wb, 600),
270 (Mode::Hybrid, Bandwidth::Swb, 100),
271 (Mode::Hybrid, Bandwidth::Swb, 200),
272 (Mode::Hybrid, Bandwidth::Fb, 100),
273 (Mode::Hybrid, Bandwidth::Fb, 200),
274 (Mode::CeltOnly, Bandwidth::Nb, 25),
275 (Mode::CeltOnly, Bandwidth::Nb, 50),
276 (Mode::CeltOnly, Bandwidth::Nb, 100),
277 (Mode::CeltOnly, Bandwidth::Nb, 200),
278 (Mode::CeltOnly, Bandwidth::Wb, 25),
279 (Mode::CeltOnly, Bandwidth::Wb, 50),
280 (Mode::CeltOnly, Bandwidth::Wb, 100),
281 (Mode::CeltOnly, Bandwidth::Wb, 200),
282 (Mode::CeltOnly, Bandwidth::Swb, 25),
283 (Mode::CeltOnly, Bandwidth::Swb, 50),
284 (Mode::CeltOnly, Bandwidth::Swb, 100),
285 (Mode::CeltOnly, Bandwidth::Swb, 200),
286 (Mode::CeltOnly, Bandwidth::Fb, 25),
287 (Mode::CeltOnly, Bandwidth::Fb, 50),
288 (Mode::CeltOnly, Bandwidth::Fb, 100),
289 (Mode::CeltOnly, Bandwidth::Fb, 200),
290 ];
291 for (config, &(mode, bw, dur)) in expected.iter().enumerate() {
292 let toc = OpusTocByte::from_byte((config as u8) << 3);
293 assert_eq!(toc.config, config as u8, "config field");
294 assert_eq!(toc.mode, mode, "config {config}: mode");
295 assert_eq!(toc.bandwidth, bw, "config {config}: bandwidth");
296 assert_eq!(
297 toc.frame_size_tenths_ms, dur,
298 "config {config}: frame-size (tenths of ms)"
299 );
300 }
301 }
302
303 #[test]
307 fn stereo_bit_independent_of_config_and_code() {
308 for config in 0u8..32 {
309 for code in 0u8..4 {
310 let mono = OpusTocByte::from_byte((config << 3) | code);
311 let stereo = OpusTocByte::from_byte((config << 3) | (1 << 2) | code);
312 assert_eq!(mono.channels, ChannelMapping::Mono);
313 assert_eq!(stereo.channels, ChannelMapping::Stereo);
314 assert_eq!(mono.config, stereo.config);
316 assert_eq!(mono.mode, stereo.mode);
317 assert_eq!(mono.bandwidth, stereo.bandwidth);
318 assert_eq!(mono.frame_size_tenths_ms, stereo.frame_size_tenths_ms);
319 assert_eq!(mono.frame_count_code, stereo.frame_count_code);
320 }
321 }
322 }
323
324 #[test]
327 fn frame_count_codes() {
328 let cases = [
329 (0u8, FrameCountCode::One, (1u8, 1u8)),
330 (1, FrameCountCode::TwoEqual, (2, 2)),
331 (2, FrameCountCode::TwoUnequal, (2, 2)),
332 (3, FrameCountCode::Arbitrary, (1, 48)),
333 ];
334 for (c, expected_code, range) in cases {
335 let toc = OpusTocByte::from_byte(c);
336 assert_eq!(toc.frame_count_code, expected_code, "c={c}");
337 assert_eq!(toc.frame_count_range(), range, "c={c} frame-count range");
338 }
339 }
340
341 #[test]
343 fn parse_empty_rejects() {
344 assert_eq!(OpusTocByte::parse(&[]), Err(Error::EmptyPacket));
345 }
346
347 #[test]
351 fn parse_known_byte() {
352 let toc = OpusTocByte::parse(&[0x6E, 0x00, 0x00]).unwrap();
353 assert_eq!(toc.config, 13);
354 assert_eq!(toc.mode, Mode::Hybrid);
355 assert_eq!(toc.bandwidth, Bandwidth::Swb);
356 assert_eq!(toc.frame_size_tenths_ms, 200);
357 assert_eq!(toc.channels, ChannelMapping::Stereo);
358 assert_eq!(toc.frame_count_code, FrameCountCode::TwoUnequal);
359 let toc2 = OpusTocByte::from_byte(0xF8);
362 assert_eq!(toc2.config, 31);
363 assert_eq!(toc2.mode, Mode::CeltOnly);
364 assert_eq!(toc2.bandwidth, Bandwidth::Fb);
365 assert_eq!(toc2.frame_size_tenths_ms, 200);
366 assert_eq!(toc2.channels, ChannelMapping::Mono);
367 assert_eq!(toc2.frame_count_code, FrameCountCode::One);
368 }
369}