opusic_c/multistream/mod.rs
1//!The multistream API allows individual Opus streams to be combined into a single packet, enabling support for up to 255 channels
2//!
3//!Multistream Opus streams can contain up to 255 elementary Opus streams.
4//!These may be either "uncoupled" or "coupled", indicating that the decoder is configured to decode them to either 1 or 2 channels, respectively.
5//!The streams are ordered so that all coupled streams appear at the beginning.
6//!
7//!The output channels specified by the encoder should use the [Vorbis channel ordering](https://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-810004.3.9).
8//!A decoder may wish to apply an additional permutation to the mapping the encoder used to achieve a different output channel order (e.g. for outputting in WAV order).
9//!
10//!Each multistream packet contains an Opus packet for each stream, and all of the Opus packets in
11//!a single multistream packet must have the same duration. Therefore the duration of a multistream
12//!packet can be extracted from the TOC sequence of the first stream, which is located at the
13//!beginning of the packet.
14
15mod encoder;
16pub use encoder::Encoder;
17mod decoder;
18pub use decoder::Decoder;
19
20///Multistream configuration
21///
22///## Parameters
23///
24///`CH` - Number of channels to use. Up to 255.
25pub struct Config<const CH: usize> {
26 streams: u8,
27 coupled_streams: u8,
28 mapping: [u8; CH],
29}
30
31impl<const CH: usize> Config<CH> {
32 const CHANNELS: u8 = match CH {
33 0 => panic!("Unsupported number of channels. Allowed range: 0..=255"),
34 _ => CH as _,
35 };
36
37 ///Creates new config, verifying that specified number of channels is valid and do not exceed `CH`
38 ///
39 ///Following constraints are imposed:
40 ///
41 ///- `streams` cannot be 0 or exceed `CH`
42 ///- `coupled_streams` cannot exceed `streams`
43 ///- Sum of both cannot exceed `CH`
44 pub const fn try_new(streams: u8, coupled_streams: u8, mapping: [u8; CH]) -> Option<Self> {
45 if streams == 0 || coupled_streams > streams {
46 return None;
47 }
48
49 let mut idx = 0;
50 while idx < CH {
51 if mapping[idx] != 255 && (mapping[idx] as usize) >= CH {
52 return None;
53 }
54 idx = idx.saturating_add(1);
55 }
56
57 match streams.checked_add(coupled_streams) {
58 Some(total) => if total == 0 || total > Self::CHANNELS {
59 None
60 } else {
61 Some(Self {
62 streams,
63 coupled_streams,
64 mapping,
65 })
66 }
67 None => None,
68 }
69 }
70
71 ///Creates new config, verifying that specified number of channels is valid and do not exceed `CH`
72 ///
73 ///Following constraints are imposed and will cause panic:
74 ///
75 ///- `streams` cannot be 0 or exceed `CH`
76 ///- `coupled_streams` cannot exceed `streams`
77 ///- Sum of both cannot exceed `CH`
78 pub const fn new(streams: u8, coupled_streams: u8, mapping: [u8; CH]) -> Self {
79 assert!(streams != 0);
80 assert!(coupled_streams <= streams);
81
82 let mut idx = 0;
83 while idx < CH {
84 if mapping[idx] != 255 {
85 assert!((mapping[idx] as usize) < CH, "Non 255 mapping values must be in range 0..CH");
86 }
87 idx = idx.saturating_add(1);
88 }
89
90 match streams.checked_add(coupled_streams) {
91 Some(total_streams) => {
92 assert!(total_streams != 0);
93 assert!(total_streams <= Self::CHANNELS);
94
95 Self {
96 streams,
97 coupled_streams,
98 mapping
99 }
100 },
101 None => panic!("sum(streams, coupled_streams) cannot exceed 255"),
102 }
103 }
104
105 #[inline(always)]
106 ///Accesses mapping
107 pub fn mapping(&self) -> &[u8; CH] {
108 &self.mapping
109 }
110
111 #[inline(always)]
112 ///Accesses mapping
113 pub fn mapping_mut(&mut self) -> &mut [u8; CH] {
114 &mut self.mapping
115 }
116}