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 compose_byte(
168 mode: Mode,
169 bandwidth: Bandwidth,
170 frame_size_tenths_ms: u16,
171 stereo: bool,
172 frame_count_code: FrameCountCode,
173 ) -> Result<u8, Error> {
174 let config = (0u8..32)
177 .find(|&c| decode_config(c) == (mode, bandwidth, frame_size_tenths_ms))
178 .ok_or(Error::MalformedPacket)?;
179 let s = u8::from(stereo);
180 let c = match frame_count_code {
181 FrameCountCode::One => 0u8,
182 FrameCountCode::TwoEqual => 1,
183 FrameCountCode::TwoUnequal => 2,
184 FrameCountCode::Arbitrary => 3,
185 };
186 Ok((config << 3) | (s << 2) | c)
187 }
188
189 pub fn frame_count_range(self) -> (u8, u8) {
194 match self.frame_count_code {
195 FrameCountCode::One => (1, 1),
196 FrameCountCode::TwoEqual | FrameCountCode::TwoUnequal => (2, 2),
197 FrameCountCode::Arbitrary => (1, 48),
199 }
200 }
201}
202
203impl fmt::Display for Mode {
204 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
205 f.write_str(match self {
206 Mode::SilkOnly => "SILK-only",
207 Mode::Hybrid => "Hybrid",
208 Mode::CeltOnly => "CELT-only",
209 })
210 }
211}
212
213impl fmt::Display for Bandwidth {
214 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215 f.write_str(match self {
216 Bandwidth::Nb => "NB",
217 Bandwidth::Mb => "MB",
218 Bandwidth::Wb => "WB",
219 Bandwidth::Swb => "SWB",
220 Bandwidth::Fb => "FB",
221 })
222 }
223}
224
225fn decode_config(config: u8) -> (Mode, Bandwidth, u16) {
233 debug_assert!(config < 32);
234 match config {
235 0 => (Mode::SilkOnly, Bandwidth::Nb, 100),
237 1 => (Mode::SilkOnly, Bandwidth::Nb, 200),
238 2 => (Mode::SilkOnly, Bandwidth::Nb, 400),
239 3 => (Mode::SilkOnly, Bandwidth::Nb, 600),
240 4 => (Mode::SilkOnly, Bandwidth::Mb, 100),
241 5 => (Mode::SilkOnly, Bandwidth::Mb, 200),
242 6 => (Mode::SilkOnly, Bandwidth::Mb, 400),
243 7 => (Mode::SilkOnly, Bandwidth::Mb, 600),
244 8 => (Mode::SilkOnly, Bandwidth::Wb, 100),
245 9 => (Mode::SilkOnly, Bandwidth::Wb, 200),
246 10 => (Mode::SilkOnly, Bandwidth::Wb, 400),
247 11 => (Mode::SilkOnly, Bandwidth::Wb, 600),
248 12 => (Mode::Hybrid, Bandwidth::Swb, 100),
250 13 => (Mode::Hybrid, Bandwidth::Swb, 200),
251 14 => (Mode::Hybrid, Bandwidth::Fb, 100),
252 15 => (Mode::Hybrid, Bandwidth::Fb, 200),
253 16 => (Mode::CeltOnly, Bandwidth::Nb, 25),
255 17 => (Mode::CeltOnly, Bandwidth::Nb, 50),
256 18 => (Mode::CeltOnly, Bandwidth::Nb, 100),
257 19 => (Mode::CeltOnly, Bandwidth::Nb, 200),
258 20 => (Mode::CeltOnly, Bandwidth::Wb, 25),
259 21 => (Mode::CeltOnly, Bandwidth::Wb, 50),
260 22 => (Mode::CeltOnly, Bandwidth::Wb, 100),
261 23 => (Mode::CeltOnly, Bandwidth::Wb, 200),
262 24 => (Mode::CeltOnly, Bandwidth::Swb, 25),
263 25 => (Mode::CeltOnly, Bandwidth::Swb, 50),
264 26 => (Mode::CeltOnly, Bandwidth::Swb, 100),
265 27 => (Mode::CeltOnly, Bandwidth::Swb, 200),
266 28 => (Mode::CeltOnly, Bandwidth::Fb, 25),
267 29 => (Mode::CeltOnly, Bandwidth::Fb, 50),
268 30 => (Mode::CeltOnly, Bandwidth::Fb, 100),
269 31 => (Mode::CeltOnly, Bandwidth::Fb, 200),
270 _ => unreachable!("config is masked to 5 bits"),
271 }
272}
273
274#[cfg(test)]
275mod tests {
276 use super::*;
277
278 #[test]
284 fn table2_all_32_configs() {
285 let expected: [(Mode, Bandwidth, u16); 32] = [
287 (Mode::SilkOnly, Bandwidth::Nb, 100),
288 (Mode::SilkOnly, Bandwidth::Nb, 200),
289 (Mode::SilkOnly, Bandwidth::Nb, 400),
290 (Mode::SilkOnly, Bandwidth::Nb, 600),
291 (Mode::SilkOnly, Bandwidth::Mb, 100),
292 (Mode::SilkOnly, Bandwidth::Mb, 200),
293 (Mode::SilkOnly, Bandwidth::Mb, 400),
294 (Mode::SilkOnly, Bandwidth::Mb, 600),
295 (Mode::SilkOnly, Bandwidth::Wb, 100),
296 (Mode::SilkOnly, Bandwidth::Wb, 200),
297 (Mode::SilkOnly, Bandwidth::Wb, 400),
298 (Mode::SilkOnly, Bandwidth::Wb, 600),
299 (Mode::Hybrid, Bandwidth::Swb, 100),
300 (Mode::Hybrid, Bandwidth::Swb, 200),
301 (Mode::Hybrid, Bandwidth::Fb, 100),
302 (Mode::Hybrid, Bandwidth::Fb, 200),
303 (Mode::CeltOnly, Bandwidth::Nb, 25),
304 (Mode::CeltOnly, Bandwidth::Nb, 50),
305 (Mode::CeltOnly, Bandwidth::Nb, 100),
306 (Mode::CeltOnly, Bandwidth::Nb, 200),
307 (Mode::CeltOnly, Bandwidth::Wb, 25),
308 (Mode::CeltOnly, Bandwidth::Wb, 50),
309 (Mode::CeltOnly, Bandwidth::Wb, 100),
310 (Mode::CeltOnly, Bandwidth::Wb, 200),
311 (Mode::CeltOnly, Bandwidth::Swb, 25),
312 (Mode::CeltOnly, Bandwidth::Swb, 50),
313 (Mode::CeltOnly, Bandwidth::Swb, 100),
314 (Mode::CeltOnly, Bandwidth::Swb, 200),
315 (Mode::CeltOnly, Bandwidth::Fb, 25),
316 (Mode::CeltOnly, Bandwidth::Fb, 50),
317 (Mode::CeltOnly, Bandwidth::Fb, 100),
318 (Mode::CeltOnly, Bandwidth::Fb, 200),
319 ];
320 for (config, &(mode, bw, dur)) in expected.iter().enumerate() {
321 let toc = OpusTocByte::from_byte((config as u8) << 3);
322 assert_eq!(toc.config, config as u8, "config field");
323 assert_eq!(toc.mode, mode, "config {config}: mode");
324 assert_eq!(toc.bandwidth, bw, "config {config}: bandwidth");
325 assert_eq!(
326 toc.frame_size_tenths_ms, dur,
327 "config {config}: frame-size (tenths of ms)"
328 );
329 }
330 }
331
332 #[test]
336 fn stereo_bit_independent_of_config_and_code() {
337 for config in 0u8..32 {
338 for code in 0u8..4 {
339 let mono = OpusTocByte::from_byte((config << 3) | code);
340 let stereo = OpusTocByte::from_byte((config << 3) | (1 << 2) | code);
341 assert_eq!(mono.channels, ChannelMapping::Mono);
342 assert_eq!(stereo.channels, ChannelMapping::Stereo);
343 assert_eq!(mono.config, stereo.config);
345 assert_eq!(mono.mode, stereo.mode);
346 assert_eq!(mono.bandwidth, stereo.bandwidth);
347 assert_eq!(mono.frame_size_tenths_ms, stereo.frame_size_tenths_ms);
348 assert_eq!(mono.frame_count_code, stereo.frame_count_code);
349 }
350 }
351 }
352
353 #[test]
356 fn frame_count_codes() {
357 let cases = [
358 (0u8, FrameCountCode::One, (1u8, 1u8)),
359 (1, FrameCountCode::TwoEqual, (2, 2)),
360 (2, FrameCountCode::TwoUnequal, (2, 2)),
361 (3, FrameCountCode::Arbitrary, (1, 48)),
362 ];
363 for (c, expected_code, range) in cases {
364 let toc = OpusTocByte::from_byte(c);
365 assert_eq!(toc.frame_count_code, expected_code, "c={c}");
366 assert_eq!(toc.frame_count_range(), range, "c={c} frame-count range");
367 }
368 }
369
370 #[test]
372 fn parse_empty_rejects() {
373 assert_eq!(OpusTocByte::parse(&[]), Err(Error::EmptyPacket));
374 }
375
376 #[test]
380 fn parse_known_byte() {
381 let toc = OpusTocByte::parse(&[0x6E, 0x00, 0x00]).unwrap();
382 assert_eq!(toc.config, 13);
383 assert_eq!(toc.mode, Mode::Hybrid);
384 assert_eq!(toc.bandwidth, Bandwidth::Swb);
385 assert_eq!(toc.frame_size_tenths_ms, 200);
386 assert_eq!(toc.channels, ChannelMapping::Stereo);
387 assert_eq!(toc.frame_count_code, FrameCountCode::TwoUnequal);
388 let toc2 = OpusTocByte::from_byte(0xF8);
391 assert_eq!(toc2.config, 31);
392 assert_eq!(toc2.mode, Mode::CeltOnly);
393 assert_eq!(toc2.bandwidth, Bandwidth::Fb);
394 assert_eq!(toc2.frame_size_tenths_ms, 200);
395 assert_eq!(toc2.channels, ChannelMapping::Mono);
396 assert_eq!(toc2.frame_count_code, FrameCountCode::One);
397 }
398}