Skip to main content

opus_pure/
multistream.rs

1//! Opus multistream (surround) — port of the core of
2//! `src/opus_multistream_{encoder,decoder}.c`. Wraps N mono/coupled Opus
3//! coders behind a channel-mapping layout so >2-channel audio (quad, 5.1,
4//! 7.1) can be coded as a set of standard Opus streams concatenated with the
5//! self-delimited framing.
6//!
7//! The channel bitrate allocation here is a simple even split across streams
8//! (coupled streams get 2x a mono stream's share) — libopus adds a
9//! surround-masking analysis on top, a quality refinement, not a conformance
10//! requirement. The bitstream layout, mapping, and per-stream Opus coding are
11//! standard, so streams interoperate with libopus.
12
13use crate::{Error, Result};
14
15use crate::encoder::MAX_ENCODING_DEPTH;
16use crate::repacketizer::{Repacketizer, take_self_delimited_into};
17use crate::soft_clip::{float_to_i16, i16_to_float};
18use crate::{Application, Bandwidth, OpusDecoder, OpusEncoder};
19
20/// Vorbis channel layout for mapping family 1, channels 1..=8:
21/// (nb_streams, nb_coupled_streams, channel_mapping).
22const VORBIS_MAPPINGS: [(usize, usize, &[u8]); 8] = [
23    (1, 0, &[0]),                      // mono
24    (1, 1, &[0, 1]),                   // stereo
25    (2, 1, &[0, 2, 1]),                // 1-d (3.0)
26    (2, 2, &[0, 1, 2, 3]),             // quad
27    (3, 2, &[0, 4, 1, 2, 3]),          // 5.0
28    (4, 2, &[0, 4, 1, 2, 3, 5]),       // 5.1
29    (4, 3, &[0, 4, 1, 2, 3, 5, 6]),    // 6.1
30    (5, 3, &[0, 6, 1, 2, 3, 4, 5, 7]), // 7.1
31];
32
33/// How a set of Opus streams becomes a set of output channels.
34///
35/// A multistream packet is several ordinary Opus streams concatenated. Some are
36/// coded as coupled stereo pairs and carry two channels each, the rest carry
37/// one, and the mapping says which of the resulting channels goes where in the
38/// output. Coupled streams always come first, so the channels a decoder
39/// produces are the coupled pairs in order, then the mono streams in order.
40///
41/// [`surround`](Self::surround) builds the standard layout for a channel count
42/// and mapping family, which is how [`OpusMSEncoder`] and [`OpusMSDecoder`]
43/// obtain theirs. The fields are public so a caller can read what a given
44/// channel count works out to, which is what the
45/// [`OpusHead`](crate::OpusHead) for such a stream has to declare.
46#[derive(Debug, Clone)]
47pub struct ChannelLayout {
48    /// The mapping family this layout came from: 0 for mono/stereo, 1 for the
49    /// Vorbis surround orders. Kept so the layout can describe itself to an
50    /// [`OpusHead`](crate::OpusHead) without being asked twice.
51    pub mapping_family: u8,
52    /// Output channels this layout produces, which is what a caller interleaves.
53    pub nb_channels: usize,
54    /// Opus streams in the packet, coupled and uncoupled together.
55    pub nb_streams: usize,
56    /// How many of those streams are coupled stereo pairs. They are the first
57    /// `nb_coupled_streams` of them, and each carries two channels, so the
58    /// streams carry `nb_coupled_streams + nb_streams` channels in total.
59    pub nb_coupled_streams: usize,
60    /// For each output channel, which of the streams' channels feeds it.
61    ///
62    /// Indices `0..2 * nb_coupled_streams` are the coupled pairs, left then
63    /// right; the rest are the mono streams in order. The value 255 marks a
64    /// channel that is left silent. Length is `nb_channels`.
65    pub mapping: Vec<u8>,
66}
67
68impl ChannelLayout {
69    /// Standard layout for a channel count + mapping family (0 = mono/stereo,
70    /// 1 = Vorbis surround for 1..=8 channels).
71    pub fn surround(channels: usize, mapping_family: u8) -> Result<Self> {
72        match mapping_family {
73            0 => {
74                if channels == 1 {
75                    Ok(ChannelLayout {
76                        mapping_family: 0,
77                        nb_channels: 1,
78                        nb_streams: 1,
79                        nb_coupled_streams: 0,
80                        mapping: vec![0],
81                    })
82                } else if channels == 2 {
83                    Ok(ChannelLayout {
84                        mapping_family: 0,
85                        nb_channels: 2,
86                        nb_streams: 1,
87                        nb_coupled_streams: 1,
88                        mapping: vec![0, 1],
89                    })
90                } else {
91                    Err(Error::InvalidArgument(
92                        "family 0 supports only 1-2 channels",
93                    ))
94                }
95            }
96            1 => {
97                if !(1..=8).contains(&channels) {
98                    return Err(Error::InvalidArgument("family 1 supports 1-8 channels"));
99                }
100                let (ns, nc, m) = VORBIS_MAPPINGS[channels - 1];
101                Ok(ChannelLayout {
102                    mapping_family: 1,
103                    nb_channels: channels,
104                    nb_streams: ns,
105                    nb_coupled_streams: nc,
106                    mapping: m.to_vec(),
107                })
108            }
109            _ => Err(Error::InvalidArgument("unsupported mapping family")),
110        }
111    }
112
113    /// Output-channel indices carrying `target` in the channel mapping.
114    ///
115    /// libopus walks these with a `-1`-sentinel cursor (`get_left_channel` and
116    /// friends, one function per target); an iterator says the same thing once,
117    /// without the sentinel.
118    fn channels_for(&self, target: usize) -> impl Iterator<Item = usize> + '_ {
119        self.mapping
120            .iter()
121            .enumerate()
122            .filter_map(move |(i, &m)| (m as usize == target).then_some(i))
123    }
124
125    /// Mapping value for a coupled stream's left channel.
126    fn left_target(stream_id: usize) -> usize {
127        stream_id * 2
128    }
129    /// Mapping value for a coupled stream's right channel.
130    fn right_target(stream_id: usize) -> usize {
131        stream_id * 2 + 1
132    }
133    /// Mapping value for an uncoupled stream's single channel.
134    fn mono_target(&self, stream_id: usize) -> usize {
135        stream_id + self.nb_coupled_streams
136    }
137}
138
139/// Multistream encoder: one Opus encoder per stream (coupled = stereo, the
140/// rest mono), coded per the channel layout and concatenated self-delimited.
141pub struct OpusMSEncoder {
142    layout: ChannelLayout,
143    encoders: Vec<OpusEncoder>,
144    sample_rate: i32,
145    /// Total target bitrate across all streams; read through
146    /// [`bitrate_bps`](OpusMSEncoder::bitrate_bps), written through
147    /// [`set_bitrate`](OpusMSEncoder::set_bitrate).
148    ///
149    /// Private because the value that matters is the per-stream split derived
150    /// from it, not this number: a writable field here would let a caller set
151    /// it and change nothing.
152    bitrate_bps: i32,
153    /// One stream's channels, de-interleaved out of the caller's input.
154    buf_stream: Vec<f32>,
155    /// One stream's coded packet, before it is framed into the output.
156    buf_packet: Vec<u8>,
157    /// The assembled multistream packet.
158    buf_out: Vec<u8>,
159    /// Float conversion scratch for [`encode_s16`](OpusMSEncoder::encode_s16),
160    /// kept for the same reason [`OpusEncoder`] keeps its own.
161    buf_from_s16: Vec<f32>,
162    /// Reused to apply the self-delimited framing to every stream but the last.
163    framer: Repacketizer,
164}
165
166/// Shows the layout and per-stream settings, not the encoders' coding state.
167impl std::fmt::Debug for OpusMSEncoder {
168    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
169        f.debug_struct("OpusMSEncoder")
170            .field("sample_rate", &self.sample_rate)
171            .field("bitrate_bps", &self.bitrate_bps)
172            .field("layout", &self.layout)
173            .finish_non_exhaustive()
174    }
175}
176
177impl OpusMSEncoder {
178    /// Create a surround encoder for `channels` channels.
179    ///
180    /// `mapping_family` selects the layout: 0 for mono or stereo, 1 for the
181    /// Vorbis surround orders (quad, 5.0, 5.1, 6.1, 7.1) up to 8 channels.
182    /// These are the families RFC 7845 defines for an `.opus` file, and the
183    /// same value belongs in the [`OpusHead`](crate::OpusHead) written
184    /// alongside. Any other value, or more than 8 channels, is
185    /// [`Error::InvalidArgument`];
186    /// [`ChannelLayout::surround`] is where that decision is made.
187    pub fn new(
188        sample_rate: i32,
189        channels: usize,
190        mapping_family: u8,
191        application: Application,
192    ) -> Result<Self> {
193        let layout = ChannelLayout::surround(channels, mapping_family)?;
194        let mut encoders = Vec::with_capacity(layout.nb_streams);
195        for s in 0..layout.nb_streams {
196            let ch = if s < layout.nb_coupled_streams { 2 } else { 1 };
197            encoders.push(OpusEncoder::new(sample_rate, ch, application)?);
198        }
199        let mut enc = OpusMSEncoder {
200            layout,
201            encoders,
202            sample_rate,
203            bitrate_bps: 64000 * channels as i32,
204            buf_stream: Vec::new(),
205            buf_packet: Vec::new(),
206            buf_out: Vec::new(),
207            buf_from_s16: Vec::new(),
208            framer: Repacketizer::new(),
209        };
210        enc.set_bitrate(enc.bitrate_bps);
211        Ok(enc)
212    }
213
214    /// Split the total bitrate across streams (each coupled stream gets 2x a
215    /// mono stream's share, matching its 2 channels).
216    pub fn set_bitrate(&mut self, total: i32) {
217        self.bitrate_bps = total;
218        let units = self.layout.nb_coupled_streams * 2
219            + (self.layout.nb_streams - self.layout.nb_coupled_streams);
220        let per_unit = if units > 0 {
221            total / units as i32
222        } else {
223            total
224        };
225        for (s, e) in self.encoders.iter_mut().enumerate() {
226            e.bitrate_bps = if s < self.layout.nb_coupled_streams {
227                per_unit * 2
228            } else {
229                per_unit
230            };
231        }
232    }
233
234    /// Opus streams this encoder writes into each packet. Needed for the
235    /// [`OpusHead`](crate::OpusHead) that describes the stream.
236    pub fn nb_streams(&self) -> usize {
237        self.layout.nb_streams
238    }
239
240    /// The channel layout this encoder codes to.
241    ///
242    /// This is what an [`OpusHead`](crate::OpusHead) for the stream has to
243    /// declare, and [`OpusHead::for_ms_encoder`](crate::OpusHead::for_ms_encoder)
244    /// takes it from here rather than making you derive it a second time.
245    pub fn layout(&self) -> &ChannelLayout {
246        &self.layout
247    }
248
249    /// Total target bitrate across all streams. Set it with
250    /// [`set_bitrate`](Self::set_bitrate).
251    pub fn bitrate_bps(&self) -> i32 {
252        self.bitrate_bps
253    }
254
255    /// The per-stream encoders, so every [`OpusEncoder`] setting is reachable.
256    ///
257    /// A multistream encoder is N ordinary encoders, and rather than mirror ten
258    /// settings here — a list that would go stale the moment `OpusEncoder`
259    /// gained an eleventh — this hands them over. Set the same thing on all of
260    /// them:
261    ///
262    /// ```
263    /// # use opus_pure::{Application, OpusMSEncoder};
264    /// # let mut enc = OpusMSEncoder::new(48_000, 6, 1, Application::Audio)?;
265    /// for e in enc.streams_mut() {
266    ///     e.use_inband_fec = true;
267    ///     e.packet_loss_perc = 10;
268    /// }
269    /// # Ok::<(), opus_pure::Error>(())
270    /// ```
271    ///
272    /// [`bitrate_bps`](OpusEncoder::bitrate_bps) is the exception: it is split
273    /// across streams from a single total, so set it with
274    /// [`set_bitrate`](Self::set_bitrate) and let the split happen.
275    pub fn streams_mut(&mut self) -> &mut [OpusEncoder] {
276        &mut self.encoders
277    }
278
279    /// The per-stream encoders, for reading. See
280    /// [`streams_mut`](Self::streams_mut) to change their settings.
281    pub fn streams(&self) -> &[OpusEncoder] {
282        &self.encoders
283    }
284
285    /// Encode one frame of interleaved `input` into a multistream packet,
286    /// returning how many bytes of `output` it filled.
287    ///
288    /// `input` holds `frame_size * nb_channels` samples. Unlike
289    /// [`OpusEncoder::encode`], `output.len()` is a plain capacity here and not
290    /// a byte budget: a buffer too small is
291    /// [`Error::BufferTooSmall`], whose `needed`
292    /// tells you exactly how big to make it, rather than a quietly smaller
293    /// packet. Each stream is coded to its own share of
294    /// [`bitrate_bps`](Self::bitrate_bps).
295    pub fn encode(&mut self, input: &[f32], frame_size: usize, output: &mut [u8]) -> Result<usize> {
296        self.encode_native(input, frame_size, output, MAX_ENCODING_DEPTH)
297    }
298
299    /// [`encode`](Self::encode) from 16-bit PCM.
300    ///
301    /// Every stream is told its input came from 16 bits, exactly as
302    /// [`OpusEncoder::encode_s16`] does for a single stream, and for the same
303    /// reason: the digital-silence floor sits at the source's own precision.
304    pub fn encode_s16(
305        &mut self,
306        input: &[i16],
307        frame_size: usize,
308        output: &mut [u8],
309    ) -> Result<usize> {
310        let wanted = frame_size * self.layout.nb_channels;
311        if input.len() < wanted {
312            return Err(Error::InvalidArgument(
313                "input is shorter than frame_size * channels",
314            ));
315        }
316        // Converted through a member rather than a fresh `Vec` per call, the
317        // way `OpusEncoder::encode_s16` does it.
318        let mut converted = std::mem::take(&mut self.buf_from_s16);
319        converted.clear();
320        converted.extend(input[..wanted].iter().copied().map(i16_to_float));
321        let r = self.encode_native(&converted, frame_size, output, 16);
322        self.buf_from_s16 = converted;
323        r
324    }
325
326    fn encode_native(
327        &mut self,
328        input: &[f32],
329        frame_size: usize,
330        output: &mut [u8],
331        api_lsb_depth: i32,
332    ) -> Result<usize> {
333        let nch = self.layout.nb_channels;
334        if input.len() < frame_size * nch {
335            return Err(Error::InvalidArgument(
336                "input is shorter than frame_size * channels",
337            ));
338        }
339
340        // Taken out of `self` so the per-stream encoder below can borrow `self`
341        // mutably at the same time; put back before every return.
342        let mut stream_buf = std::mem::take(&mut self.buf_stream);
343        let mut pkt = std::mem::take(&mut self.buf_packet);
344        let mut out = std::mem::take(&mut self.buf_out);
345        let mut framer = std::mem::take(&mut self.framer);
346        stream_buf.clear();
347        stream_buf.resize(frame_size * 2, 0.0);
348        pkt.clear();
349        pkt.resize(1500 + frame_size, 0);
350        out.clear();
351
352        let result = (|| -> Result<usize> {
353            for s in 0..self.layout.nb_streams {
354                let coupled = s < self.layout.nb_coupled_streams;
355                let sch = if coupled { 2 } else { 1 };
356                // Gather this stream's channels from the interleaved input.
357                if coupled {
358                    let l = self
359                        .layout
360                        .channels_for(ChannelLayout::left_target(s))
361                        .next();
362                    let r = self
363                        .layout
364                        .channels_for(ChannelLayout::right_target(s))
365                        .next();
366                    for i in 0..frame_size {
367                        stream_buf[i * 2] = l.map_or(0.0, |c| input[i * nch + c]);
368                        stream_buf[i * 2 + 1] = r.map_or(0.0, |c| input[i * nch + c]);
369                    }
370                } else {
371                    let m = self.layout.channels_for(self.layout.mono_target(s)).next();
372                    for i in 0..frame_size {
373                        stream_buf[i] = m.map_or(0.0, |c| input[i * nch + c]);
374                    }
375                }
376                let n = self.encoders[s].encode_native(
377                    &stream_buf[..frame_size * sch],
378                    frame_size,
379                    &mut pkt,
380                    api_lsb_depth,
381                )?;
382                // All streams but the last are self-delimited so the decoder can
383                // find each stream's boundary.
384                if s != self.layout.nb_streams - 1 {
385                    framer.clear();
386                    framer.cat(&pkt[..n])?;
387                    framer.out_self_delimited_into(&mut out)?;
388                } else {
389                    out.extend_from_slice(&pkt[..n]);
390                }
391            }
392            if output.len() < out.len() {
393                return Err(Error::buffer_too_small(out.len(), output.len()));
394            }
395            output[..out.len()].copy_from_slice(&out);
396            Ok(out.len())
397        })();
398
399        self.buf_stream = stream_buf;
400        self.buf_packet = pkt;
401        self.buf_out = out;
402        self.framer = framer;
403        result
404    }
405
406    /// The sample rate this encoder was created with.
407    pub fn sample_rate(&self) -> i32 {
408        self.sample_rate
409    }
410
411    /// The number of output channels this encoder codes.
412    pub fn channels(&self) -> usize {
413        self.layout.nb_channels
414    }
415
416    /// Discard every stream's coding state, keeping all settings, as
417    /// [`OpusEncoder::reset_state`] does for one.
418    pub fn reset_state(&mut self) -> Result<()> {
419        for e in &mut self.encoders {
420            e.reset_state()?;
421        }
422        Ok(())
423    }
424}
425
426/// Multistream decoder: decode each stream and remux to the output channels.
427pub struct OpusMSDecoder {
428    layout: ChannelLayout,
429    decoders: Vec<OpusDecoder>,
430    /// One stream's decoded channels, before they are remuxed to the output.
431    buf_stream: Vec<f32>,
432    /// Float scratch [`decode_s16`](OpusMSDecoder::decode_s16) decodes into
433    /// before converting, kept rather than allocated per call.
434    buf_f32: Vec<f32>,
435    /// Scratch buffer for un-delimiting multistream packets.
436    buf_rebuilt: Vec<u8>,
437}
438
439/// Shows the layout, not the decoders' coding state.
440impl std::fmt::Debug for OpusMSDecoder {
441    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
442        f.debug_struct("OpusMSDecoder")
443            .field("layout", &self.layout)
444            .finish_non_exhaustive()
445    }
446}
447
448impl OpusMSDecoder {
449    /// Create a surround decoder for `channels` channels.
450    ///
451    /// `mapping_family` selects the layout, as in
452    /// [`OpusMSEncoder::new`]; for a stream read from a file it is the one
453    /// [`OpusHead::mapping_family`](crate::OpusHead::mapping_family) carries.
454    pub fn new(sample_rate: i32, channels: usize, mapping_family: u8) -> Result<Self> {
455        let layout = ChannelLayout::surround(channels, mapping_family)?;
456        let mut decoders = Vec::with_capacity(layout.nb_streams);
457        for s in 0..layout.nb_streams {
458            let ch = if s < layout.nb_coupled_streams { 2 } else { 1 };
459            decoders.push(OpusDecoder::new(sample_rate, ch)?);
460        }
461        Ok(OpusMSDecoder {
462            layout,
463            decoders,
464            buf_stream: Vec::new(),
465            buf_f32: Vec::new(),
466            buf_rebuilt: Vec::new(),
467        })
468    }
469
470    /// Decode a multistream packet into interleaved `output` (nb_channels per
471    /// sample). Returns the number of samples per channel.
472    /// Decode one multistream packet into interleaved float PCM.
473    ///
474    /// As with [`OpusDecoder::decode`], the output is not bounded by ±1.
475    pub fn decode(
476        &mut self,
477        packet: &[u8],
478        frame_size: usize,
479        output: &mut [f32],
480    ) -> Result<usize> {
481        self.decode_native(packet, frame_size, output, false)
482    }
483
484    /// Decode one multistream packet into interleaved 16-bit PCM.
485    ///
486    /// Each stream is soft-clipped on its own before the channels are remuxed,
487    /// which is where libopus applies it too (`opus_multistream_decode_native`
488    /// hands `soft_clip` to each stream's decoder). Clipping the remuxed result
489    /// instead would let one stream's peak bend another's audio.
490    pub fn decode_s16(
491        &mut self,
492        packet: &[u8],
493        frame_size: usize,
494        output: &mut [i16],
495    ) -> Result<usize> {
496        let nch = self.layout.nb_channels;
497        let capacity = frame_size * nch;
498        if output.len() < capacity {
499            return Err(Error::buffer_too_small(capacity, output.len()));
500        }
501        let mut pcm = std::mem::take(&mut self.buf_f32);
502        pcm.clear();
503        pcm.resize(capacity, 0.0);
504        let result = self.decode_native(packet, frame_size, &mut pcm, true);
505        if let Ok(produced) = result {
506            let n = produced * nch;
507            for (o, &s) in output[..n].iter_mut().zip(&pcm[..n]) {
508                *o = float_to_i16(s);
509            }
510        }
511        self.buf_f32 = pcm;
512        result
513    }
514
515    fn decode_native(
516        &mut self,
517        packet: &[u8],
518        frame_size: usize,
519        output: &mut [f32],
520        soft_clip: bool,
521    ) -> Result<usize> {
522        let nch = self.layout.nb_channels;
523        // Every remux below writes `frame_size * nch` samples. libopus takes the
524        // buffer on trust because C hands it a bare pointer; here the slice
525        // knows its length, so a short one is an error rather than a panic.
526        if output.len() < frame_size * nch {
527            return Err(Error::buffer_too_small(frame_size * nch, output.len()));
528        }
529        // Taken out of `self` so a stream's decoder can borrow `self` mutably
530        // at the same time; put back before the return.
531        let mut buf = std::mem::take(&mut self.buf_stream);
532        let mut rebuilt = std::mem::take(&mut self.buf_rebuilt);
533        buf.clear();
534        buf.resize(frame_size * 2, 0.0);
535        let mut data = packet;
536        let mut produced = frame_size;
537
538        let result = (|| -> Result<usize> {
539            for s in 0..self.layout.nb_streams {
540                let coupled = s < self.layout.nb_coupled_streams;
541                let last = s == self.layout.nb_streams - 1;
542                // The last stream is a normal packet; every earlier one is
543                // self-delimited and must have its length prefix stripped before the
544                // single-stream decoder, which does not understand that framing.
545                let (stream_slice, advance) = if last {
546                    (data, data.len())
547                } else {
548                    let off = take_self_delimited_into(data, &mut rebuilt)?;
549                    (rebuilt.as_slice(), off)
550                };
551                let n = self.decoders[s].decode_native(
552                    stream_slice,
553                    frame_size,
554                    &mut buf,
555                    soft_clip,
556                )?;
557                produced = n;
558                // Remux this stream's channel(s) to the output.
559                if coupled {
560                    for chan in self.layout.channels_for(ChannelLayout::left_target(s)) {
561                        for i in 0..n {
562                            output[i * nch + chan] = buf[i * 2];
563                        }
564                    }
565                    for chan in self.layout.channels_for(ChannelLayout::right_target(s)) {
566                        for i in 0..n {
567                            output[i * nch + chan] = buf[i * 2 + 1];
568                        }
569                    }
570                } else {
571                    for chan in self.layout.channels_for(self.layout.mono_target(s)) {
572                        for i in 0..n {
573                            output[i * nch + chan] = buf[i];
574                        }
575                    }
576                }
577                if !last {
578                    data = &data[advance..];
579                }
580            }
581            // Unmapped channels (mapping == 255) are silenced.
582            for c in 0..nch {
583                if self.layout.mapping.get(c).copied() == Some(255) {
584                    for i in 0..produced {
585                        output[i * nch + c] = 0.0;
586                    }
587                }
588            }
589            Ok(produced)
590        })();
591        self.buf_stream = buf;
592        self.buf_rebuilt = rebuilt;
593        result
594    }
595
596    /// The channel layout this decoder produces.
597    pub fn layout(&self) -> &ChannelLayout {
598        &self.layout
599    }
600
601    /// Opus streams this decoder expects in each packet.
602    pub fn nb_streams(&self) -> usize {
603        self.layout.nb_streams
604    }
605
606    /// The number of output channels this decoder produces.
607    pub fn channels(&self) -> usize {
608        self.layout.nb_channels
609    }
610
611    /// The sample rate this decoder was created with, in Hz.
612    pub fn sample_rate(&self) -> i32 {
613        self.decoders[0].sample_rate()
614    }
615
616    /// The per-stream decoders, so every [`OpusDecoder`] setting is reachable.
617    ///
618    /// The counterpart of [`OpusMSEncoder::streams_mut`], and the only route to
619    /// [`gain_q8`](OpusDecoder::gain_q8) — which is not a nicety. RFC 7845 §5.1
620    /// says a player SHOULD apply the gain a file declares, and it says nothing
621    /// about mapping family, so a surround stream carrying a non-zero
622    /// [`OpusHead::output_gain_q8`](crate::OpusHead::output_gain_q8) plays at
623    /// the wrong level with no other symptom. A multistream decoder is N
624    /// ordinary decoders and the gain belongs on every one of them:
625    ///
626    /// ```
627    /// # use opus_pure::{OpusHead, OpusMSDecoder};
628    /// # let head = OpusHead::for_layout(
629    /// #     &opus_pure::ChannelLayout::surround(6, 1)?, 48_000);
630    /// let mut dec = OpusMSDecoder::new(48_000, 6, head.mapping_family)?;
631    /// for d in dec.streams_mut() {
632    ///     d.gain_q8 = head.output_gain_q8 as i32;
633    /// }
634    /// # Ok::<(), opus_pure::Error>(())
635    /// ```
636    ///
637    /// For mono and stereo, [`OpusHead::decoder`](crate::OpusHead::decoder)
638    /// does this for you and there is nothing to remember.
639    pub fn streams_mut(&mut self) -> &mut [OpusDecoder] {
640        &mut self.decoders
641    }
642
643    /// The per-stream decoders, for reading. See
644    /// [`streams_mut`](Self::streams_mut) to change their settings.
645    ///
646    /// [`final_range`](OpusDecoder::final_range) per stream is what a
647    /// conformance check compares against a reference decoder's.
648    pub fn streams(&self) -> &[OpusDecoder] {
649        &self.decoders
650    }
651
652    /// Discard every stream's coding state, as [`OpusDecoder::reset_state`]
653    /// does for one.
654    pub fn reset_state(&mut self) -> Result<()> {
655        for d in &mut self.decoders {
656            d.reset_state()?;
657        }
658        Ok(())
659    }
660}
661
662/// Bandwidth passthrough helper (so callers can cap all streams at once).
663impl OpusMSEncoder {
664    /// Cap the audio bandwidth of every stream at once, as
665    /// [`OpusEncoder::max_bandwidth`] does for one.
666    pub fn set_max_bandwidth(&mut self, bw: Bandwidth) {
667        for e in &mut self.encoders {
668            e.max_bandwidth = bw;
669        }
670    }
671}