Skip to main content

opus_rs/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2#![allow(unsafe_op_in_unsafe_fn)]
3#![allow(clippy::too_many_arguments)]
4#![allow(clippy::needless_range_loop)]
5
6mod compat;
7mod fixedvec;
8
9pub mod bands;
10pub mod celt;
11pub mod celt_lpc;
12pub mod hp_cutoff;
13pub mod kiss_fft;
14pub mod mdct;
15pub mod modes;
16/// Multistream decode uses `Vec`-based plumbing, so it is only available
17/// with the `std` (default) feature set; the core codec stays `no_std`
18/// + no-`alloc` capable (see `split_self_delimited` docs).
19#[cfg(feature = "std")]
20pub mod multistream;
21pub mod pitch;
22pub mod pvq;
23pub mod quant_bands;
24pub mod range_coder;
25pub mod rate;
26pub mod silk;
27
28pub use silk::{SilkResampler, SilkResamplerDown1_3, SilkResamplerDown1_6};
29
30use crate::fixedvec::FixedVec;
31use core::mem::MaybeUninit;
32pub use celt::{CeltDecoder, CeltEncoder};
33use hp_cutoff::{dc_reject_float, hp_cutoff, hp_cutoff_float, hp_cutoff_i16};
34use range_coder::RangeCoder;
35use silk::control_codec::{silk_control_encoder, silk_setup_lbrr};
36use silk::enc_api::{
37    silk_encode, silk_encode_prefill, silk_encode_prefill_stereo, silk_encode_stereo_packet,
38};
39use silk::init_encoder::silk_init_encoder;
40use silk::lin2log::silk_lin2log;
41use silk::log2lin::silk_log2lin;
42use silk::macros::*;
43use silk::structs::SilkEncoderState;
44
45// --- Heap-free buffer capacity constants (worst case: 2 channels). ---
46const OPUS_MAX_CHANNELS: usize = 2;
47/// Largest API frame in samples/channel (120 ms @ 48 kHz = 5760). Used by the
48/// encoder's per-frame input buffers (sized `frame_size * channels`).
49const OPUS_MAX_FRAME: usize = 5760;
50/// Largest SILK packet at the internal rate: 60 ms at 16 kHz.
51const SILK_MAX_PACKET_SAMPLES: usize = 3 * silk::define::MAX_FRAME_LENGTH;
52/// Largest *single-frame* samples/channel (60 ms @ 48 kHz = 2880). The decoder's
53/// staging buffers hold one sub-frame at a time, so they're sized to this — not
54/// the full packet. (Halves the decoder footprint vs. a naive 5760/channel.)
55const OPUS_MAX_SUBFRAME: usize = 2880;
56/// Decoder per-sub-frame staging cap: `OPUS_MAX_SUBFRAME * max_channels`.
57const OPUS_SUBFRAME_SCRATCH: usize = OPUS_MAX_SUBFRAME * OPUS_MAX_CHANNELS;
58/// High-pass filter state memory (`channels * 2`).
59const OPUS_HP_MEM: usize = OPUS_MAX_CHANNELS * 2;
60/// Decoder `w_pcm_i16` cap (`960 * max_channels`).
61const OPUS_PCM_I16: usize = 960 * OPUS_MAX_CHANNELS;
62/// Decoder `prev_pcm_tail` cap (`240 * max_channels`).
63const OPUS_PCM_TAIL: usize = 240 * OPUS_MAX_CHANNELS;
64/// Max number of frames encoded in one Opus packet (RFC 6716 caps at 48 for
65/// 2.5 ms codes in a 120 ms packet).
66const OPUS_MAX_PACKET_FRAMES: usize = 48;
67/// RFC 6716 §3.1: a single Opus packet carries at most 1276 bytes of data.
68const OPUS_MAX_PACKET_BYTES: usize = 1276;
69/// C `OpusEncoder.delay_buffer[MAX_ENCODER_BUFFER*2]` (480 samples * 2 ch).
70/// Holds the delay-compensation ring feeding the CELT encoder.
71const OPUS_DELAY_BUF: usize = 480 * OPUS_MAX_CHANNELS;
72/// CELT/hybrid input staging cap: `(delay_compensation + frame)*channels`.
73/// Worst case 60 ms stereo @ 48 kHz: (192 + 2880) * 2 = 6144.
74const OPUS_PCM_BUF: usize = (192 + OPUS_MAX_SUBFRAME) * OPUS_MAX_CHANNELS;
75
76#[derive(Debug, Clone, Copy, PartialEq, Eq)]
77pub enum Application {
78    Voip = 2048,
79    Audio = 2049,
80    RestrictedLowDelay = 2051,
81}
82
83#[derive(Debug, Clone, Copy, PartialEq, Eq)]
84pub enum Bandwidth {
85    Auto = -1000,
86    Narrowband = 1101,
87    Mediumband = 1102,
88    Wideband = 1103,
89    Superwideband = 1104,
90    Fullband = 1105,
91}
92
93#[derive(Debug, Clone, Copy, PartialEq, Eq)]
94enum OpusMode {
95    SilkOnly,
96    Hybrid,
97    CeltOnly,
98}
99
100pub struct OpusEncoder {
101    #[cfg(not(feature = "heap"))]
102    celt_enc: CeltEncoder,
103    #[cfg(feature = "heap")]
104    celt_enc: Box<CeltEncoder>,
105    /// SILK encoder for mono input, or for the mid of stereo input (it also
106    /// holds the stereo state).
107    #[cfg(not(feature = "heap"))]
108    silk_enc: SilkEncoderState,
109    #[cfg(feature = "heap")]
110    silk_enc: Box<SilkEncoderState>,
111    /// SILK encoder for the side of stereo input (libopus `state_Fxx[1]`).
112    #[cfg(not(feature = "heap"))]
113    silk_enc_side: SilkEncoderState,
114    #[cfg(feature = "heap")]
115    silk_enc_side: Box<SilkEncoderState>,
116    application: Application,
117    sampling_rate: i32,
118    channels: usize,
119    bandwidth: Bandwidth,
120    pub bitrate_bps: i32,
121    pub complexity: i32,
122    pub use_cbr: bool,
123
124    pub use_inband_fec: bool,
125
126    pub packet_loss_perc: i32,
127    silk_initialized: bool,
128    mode: OpusMode,
129    prev_enc_mode: Option<OpusMode>,
130    /// When set, `encode_impl` uses this mode instead of running the
131    /// mode-selection heuristic. Used to keep all sub-frames of a multiframe
132    /// packet in the same mode (libopus `user_forced_mode`).
133    forced_mode: Option<OpusMode>,
134
135    variable_hp_smth2_q15: i32,
136    hp_mem: FixedVec<i32, OPUS_HP_MEM>,
137
138    #[cfg(not(feature = "heap"))]
139    buf_filtered: FixedVec<i16, OPUS_MAX_FRAME>,
140    #[cfg(feature = "heap")]
141    buf_filtered: Box<FixedVec<i16, OPUS_MAX_FRAME>>,
142    #[cfg(not(feature = "heap"))]
143    buf_silk_input: FixedVec<i16, OPUS_MAX_FRAME>,
144    #[cfg(feature = "heap")]
145    buf_silk_input: Box<FixedVec<i16, OPUS_MAX_FRAME>>,
146    /// Right channel of a stereo frame at the SILK internal rate, beside the
147    /// left in `buf_silk_input`.
148    #[cfg(not(feature = "heap"))]
149    buf_silk_input_right: FixedVec<i16, SILK_MAX_PACKET_SAMPLES>,
150    #[cfg(feature = "heap")]
151    buf_silk_input_right: Box<FixedVec<i16, SILK_MAX_PACKET_SAMPLES>>,
152    /// One channel of a stereo frame at the API rate, de-interleaved for its
153    /// SILK resampler.
154    #[cfg(not(feature = "heap"))]
155    buf_silk_api_channel: FixedVec<i16, OPUS_MAX_FRAME>,
156    #[cfg(feature = "heap")]
157    buf_silk_api_channel: Box<FixedVec<i16, OPUS_MAX_FRAME>>,
158    #[cfg(not(feature = "heap"))]
159    buf_celt_input: FixedVec<f32, OPUS_MAX_FRAME>,
160    #[cfg(feature = "heap")]
161    buf_celt_input: Box<FixedVec<f32, OPUS_MAX_FRAME>>,
162    /// C `delay_buffer[MAX_ENCODER_BUFFER*2]`: delay-compensation ring feeding
163    /// the CELT encoder (see `encoder_buffer` / `delay_compensation`).
164    #[cfg(not(feature = "heap"))]
165    delay_buffer: FixedVec<f32, OPUS_DELAY_BUF>,
166    #[cfg(feature = "heap")]
167    delay_buffer: Box<FixedVec<f32, OPUS_DELAY_BUF>>,
168    /// Interleaved `pcm_buf` staging: `delay prefix + filtered frame` exactly
169    /// like C `opus_encode_frame_native()` builds it before CELT encoding.
170    #[cfg(not(feature = "heap"))]
171    buf_celt_pcm: FixedVec<f32, OPUS_PCM_BUF>,
172    #[cfg(feature = "heap")]
173    buf_celt_pcm: Box<FixedVec<f32, OPUS_PCM_BUF>>,
174    /// f32 staging of the current frame for the CELT/Hybrid float pipeline
175    /// when the caller feeds `encode_i16()` (issue #28).
176    #[cfg(not(feature = "heap"))]
177    buf_f32_frame: FixedVec<f32, OPUS_MAX_FRAME>,
178    #[cfg(feature = "heap")]
179    buf_f32_frame: Box<FixedVec<f32, OPUS_MAX_FRAME>>,
180    /// C `encoder_buffer` (= Fs/100): samples of ring history kept between
181    /// frames (0 for restricted-latency applications).
182    encoder_buffer: usize,
183    /// C `delay_compensation` (= Fs/250): 4 ms lookahead prefix prepended to
184    /// each CELT frame (0 for restricted-latency applications).
185    delay_compensation: usize,
186    /// Float filter state for `dc_reject_float` / `hp_cutoff_float`
187    /// (C `st->hp_mem[4]`, opus_val32 in the float build).
188    hp_mem_float: [f32; 4],
189    /// Encoder-direction SILK input resampler (C `resampler_state` with
190    /// `forEnc = 1`): API rate -> SILK internal rate, carrying the
191    /// `delay_matrix_enc` input delay and the private down-FIR chain. It
192    /// resamples mono input, or the left channel of stereo input.
193    silk_resampler_enc: silk::resampler::SilkResampler,
194    /// The right channel's SILK input resampler: libopus resamples each
195    /// stereo channel separately and splits mid/side at the internal rate.
196    silk_resampler_enc_right: silk::resampler::SilkResampler,
197
198    rc: RangeCoder,
199}
200
201/// Format-agnostic PCM input for `encode_impl` (issue #28): `F32` is the
202/// classic float path; `I16` reaches the SILK integer pipeline directly
203/// and is converted to f32 only where the CELT pipeline requires it.
204#[derive(Clone, Copy)]
205enum PcmInput<'a> {
206    F32(&'a [f32]),
207    I16(&'a [i16]),
208}
209
210impl PcmInput<'_> {
211    fn len(&self) -> usize {
212        match self {
213            PcmInput::F32(s) => s.len(),
214            PcmInput::I16(s) => s.len(),
215        }
216    }
217}
218
219fn compute_equiv_rate(
220    bitrate: i32,
221    channels: usize,
222    frame_rate: i32,
223    vbr: bool,
224    complexity: i32,
225    loss: i32,
226) -> i32 {
227    let mut equiv = bitrate;
228    if frame_rate > 50 {
229        equiv -= (40 * channels as i32 + 20) * (frame_rate - 50);
230    }
231    if !vbr {
232        equiv -= equiv / 12;
233    }
234    equiv = equiv * (90 + complexity) / 100;
235    if loss > 0 {
236        equiv -= equiv * loss / (12 * loss + 20);
237    }
238    equiv
239}
240
241/// `fec_thresholds` (opus_encoder.c:160): LBRR rate threshold and hysteresis
242/// per bandwidth, starting at narrowband.
243const FEC_THRESHOLDS: [i32; 10] = [
244    12000, 1000, // NB
245    14000, 1000, // MB
246    16000, 1000, // WB
247    20000, 1000, // SWB
248    22000, 1000, // FB
249];
250
251fn bandwidth_fec_index(bw: Bandwidth) -> usize {
252    match bw {
253        Bandwidth::Narrowband => 0,
254        Bandwidth::Mediumband => 1,
255        Bandwidth::Wideband => 2,
256        Bandwidth::Superwideband => 3,
257        Bandwidth::Fullband | Bandwidth::Auto => 4,
258    }
259}
260
261fn bandwidth_from_fec_index(idx: usize) -> Bandwidth {
262    match idx {
263        0 => Bandwidth::Narrowband,
264        1 => Bandwidth::Mediumband,
265        2 => Bandwidth::Wideband,
266        3 => Bandwidth::Superwideband,
267        _ => Bandwidth::Fullband,
268    }
269}
270
271/// Port of opus_encoder.c `decide_fec()`: gate in-band FEC on the packet loss
272/// setting and on whether the equivalent rate can afford LBRR at the current
273/// bandwidth. With loss > 5%, the bandwidth is reduced until FEC fits; the
274/// caller's per-frame bandwidth is updated accordingly.
275fn decide_fec(
276    use_in_band_fec: i32,
277    packet_loss_perc: i32,
278    last_fec: i32,
279    mode: OpusMode,
280    bandwidth: &mut Bandwidth,
281    rate: i32,
282) -> i32 {
283    if use_in_band_fec == 0 || packet_loss_perc == 0 || mode == OpusMode::CeltOnly {
284        return 0;
285    }
286    let orig_bandwidth = *bandwidth;
287    loop {
288        let idx = bandwidth_fec_index(*bandwidth);
289        let mut lbrr_rate_thres_bps = FEC_THRESHOLDS[2 * idx];
290        let hysteresis = FEC_THRESHOLDS[2 * idx + 1];
291        if last_fec == 1 {
292            lbrr_rate_thres_bps -= hysteresis;
293        }
294        if last_fec == 0 {
295            lbrr_rate_thres_bps += hysteresis;
296        }
297        // silk_SMULWB(threshold * (125 - min(loss, 25)), 0.01 in Q16)
298        lbrr_rate_thres_bps = silk_smulwb(
299            lbrr_rate_thres_bps * (125 - packet_loss_perc.min(25)),
300            655,
301        );
302        // If loss <= 5%, we look at whether we have enough rate to enable FEC.
303        // If loss > 5%, we decrease the bandwidth until we can enable FEC.
304        if rate > lbrr_rate_thres_bps {
305            return 1;
306        } else if packet_loss_perc <= 5 {
307            return 0;
308        } else if bandwidth_fec_index(*bandwidth) > 0 {
309            let lower = bandwidth_fec_index(*bandwidth) - 1;
310            *bandwidth = bandwidth_from_fec_index(lower);
311        } else {
312            break;
313        }
314    }
315    // Couldn't find any bandwidth to enable FEC, keep original bandwidth.
316    *bandwidth = orig_bandwidth;
317    0
318}
319
320fn compute_mode_threshold(
321    application: Application,
322    channels: usize,
323    prev_was_celt: bool,
324    has_prev_mode: bool,
325    voice_est: i32,
326) -> i32 {
327    let mode_voice = if channels == 1 { 64000 } else { 44000 };
328    let mode_music = 10000;
329
330    let diff = mode_voice - mode_music;
331    let offset = (voice_est * voice_est * diff) >> 14;
332    let mut threshold = mode_music + offset;
333
334    if application == Application::Voip {
335        threshold += 8000;
336    }
337
338    if has_prev_mode {
339        if prev_was_celt {
340            threshold -= 4000;
341        } else {
342            threshold += 4000;
343        }
344    }
345
346    if application == Application::RestrictedLowDelay {
347        threshold = 0;
348    }
349
350    threshold
351}
352
353/// C `bits_to_bitrate` (opus_private.h, libopus 1.6): bits per frame to b/s,
354/// through the 6x frame rate so 60 ms frames (16.67 Hz) round like libopus.
355fn bits_to_bitrate(bits: i32, fs: i32, frame_size: usize) -> i32 {
356    (bits as i64 * (6 * fs as i64 / frame_size as i64) / 6) as i32
357}
358
359/// C `bitrate_to_bits` (opus_private.h, libopus 1.6): b/s to bits per frame.
360fn bitrate_to_bits(bitrate: i32, fs: i32, frame_size: usize) -> i32 {
361    (bitrate as i64 * 6 / (6 * fs as i64 / frame_size as i64)) as i32
362}
363
364/// SILK's share of a Hybrid packet's `rate_bps` (libopus
365/// `compute_silk_rate_for_hybrid`), allocated per channel: stereo codes the
366/// side with SILK too. Not ported: the FEC column, the CBR boost and the SWB
367/// boost.
368fn compute_silk_rate_for_hybrid(rate_bps: i32, frame20ms: bool, channels: usize) -> i32 {
369    let channels = channels as i32;
370    let rate_bps = rate_bps / channels;
371    let silk_rate = silk_rate_for_hybrid_per_channel(rate_bps, frame20ms) * channels;
372    // Small adjustment for stereo (libopus: "calibrated for 32 kb/s").
373    if channels == 2 && rate_bps >= 12000 {
374        silk_rate - 1000
375    } else {
376        silk_rate
377    }
378}
379
380fn silk_rate_for_hybrid_per_channel(rate_bps: i32, frame20ms: bool) -> i32 {
381    const RATE_TABLE: &[(i32, i32, i32)] = &[
382        (0, 0, 0),
383        (12000, 10000, 10000),
384        (16000, 13500, 13500),
385        (20000, 16000, 16000),
386        (24000, 18000, 18000),
387        (32000, 22000, 22000),
388        (64000, 38000, 38000),
389    ];
390    let n = RATE_TABLE.len();
391    let mut i = 1;
392    while i < n && RATE_TABLE[i].0 <= rate_bps {
393        i += 1;
394    }
395    if i == n {
396        let (x_last, r10_last, r20_last) = RATE_TABLE[n - 1];
397        let base = if frame20ms { r20_last } else { r10_last };
398        base + (rate_bps - x_last) / 2
399    } else {
400        let (x0, lo10, lo20) = RATE_TABLE[i - 1];
401        let (x1, hi10, hi20) = RATE_TABLE[i];
402        let (lo, hi) = if frame20ms {
403            (lo20, hi20)
404        } else {
405            (lo10, hi10)
406        };
407        (lo * (x1 - rate_bps) + hi * (rate_bps - x0)) / (x1 - x0)
408    }
409}
410
411#[cfg(all(test, feature = "std"))]
412mod silk_rate_tests {
413    use super::compute_silk_rate_for_hybrid;
414
415    #[test]
416    fn test_reference_table_exact_entries() {
417        assert_eq!(compute_silk_rate_for_hybrid(12000, true, 1), 10000);
418        assert_eq!(compute_silk_rate_for_hybrid(16000, true, 1), 13500);
419        assert_eq!(compute_silk_rate_for_hybrid(20000, true, 1), 16000);
420        assert_eq!(compute_silk_rate_for_hybrid(24000, true, 1), 18000);
421        assert_eq!(compute_silk_rate_for_hybrid(32000, true, 1), 22000);
422        assert_eq!(compute_silk_rate_for_hybrid(64000, true, 1), 38000);
423    }
424
425    #[test]
426    fn test_32kbps_gives_22kbps_silk() {
427        assert_eq!(compute_silk_rate_for_hybrid(32000, true, 1), 22000);
428    }
429
430    #[test]
431    fn test_interpolation_between_table_entries() {
432        let r = compute_silk_rate_for_hybrid(18000, true, 1);
433        assert_eq!(r, 14750);
434    }
435
436    #[test]
437    fn test_above_table_max_gives_half_extra() {
438        let r = compute_silk_rate_for_hybrid(72000, true, 1);
439        assert_eq!(r, 38000 + (72000 - 64000) / 2);
440    }
441
442    /// Stereo looks the rate up per channel, doubles it and, from 12 kb/s per
443    /// channel, takes 1 kb/s off (libopus opus_encoder.c).
444    #[test]
445    fn test_stereo_allocates_per_channel() {
446        let stereo = |rate| compute_silk_rate_for_hybrid(rate, true, 2);
447        assert_eq!(stereo(32000), 2 * 13500 - 1000);
448        assert_eq!(stereo(24000), 2 * 10000 - 1000);
449        assert_eq!(stereo(20000), 2 * 8333);
450        assert_eq!(stereo(64000), 2 * 22000 - 1000);
451    }
452}
453
454/// Uniformly borrow a codec-state field that is a `Box<T>` under the `heap`
455/// feature and a plain `T` otherwise, so call sites don't need `#[cfg]` on
456/// every `&self.field` / `&mut self.field`.
457///
458/// Under `heap` the field is a `Box<...>`, so `T: Deref(DerefMut)` resolves to
459/// the box itself and the `Target` is the inner type; without `heap` the field
460/// is the inner type directly (`?Sized` lets `&mut [i16]`-style coercion choose
461/// the slice target).
462#[cfg(feature = "heap")]
463#[inline(always)]
464fn state_ref<T: core::ops::Deref>(b: &T) -> &T::Target {
465    b
466}
467#[cfg(not(feature = "heap"))]
468#[inline(always)]
469fn state_ref<T: ?Sized>(b: &T) -> &T {
470    b
471}
472
473#[cfg(feature = "heap")]
474#[inline(always)]
475fn state_mut<T: core::ops::DerefMut>(b: &mut T) -> &mut T::Target {
476    b
477}
478#[cfg(not(feature = "heap"))]
479#[inline(always)]
480fn state_mut<T: ?Sized>(b: &mut T) -> &mut T {
481    b
482}
483
484impl OpusEncoder {
485    pub fn new(
486        sampling_rate: i32,
487        channels: usize,
488        application: Application,
489    ) -> Result<Self, &'static str> {
490        if ![8000, 12000, 16000, 24000, 48000].contains(&sampling_rate) {
491            return Err("Invalid sampling rate");
492        }
493        if ![1, 2].contains(&channels) {
494            return Err("Invalid number of channels");
495        }
496
497        let mode = modes::default_mode();
498        #[cfg(feature = "heap")]
499        let celt_enc = Box::new(CeltEncoder::with_sampling_rate(mode, channels, sampling_rate));
500        #[cfg(not(feature = "heap"))]
501        let celt_enc = CeltEncoder::with_sampling_rate(mode, channels, sampling_rate);
502
503        #[cfg(feature = "heap")]
504        let mut silk_enc = Box::new(SilkEncoderState::default());
505        #[cfg(not(feature = "heap"))]
506        let mut silk_enc = SilkEncoderState::default();
507        if silk_init_encoder(state_mut(&mut silk_enc), 0) != 0 {
508            return Err("SILK encoder initialization failed");
509        }
510        #[cfg(feature = "heap")]
511        let mut silk_enc_side = Box::new(SilkEncoderState::default());
512        #[cfg(not(feature = "heap"))]
513        let mut silk_enc_side = SilkEncoderState::default();
514        if silk_init_encoder(state_mut(&mut silk_enc_side), 0) != 0 {
515            return Err("SILK encoder initialization failed");
516        }
517
518        let (opus_mode, bw) = match application {
519            Application::Voip => {
520                let bw = match sampling_rate {
521                    8000 => Bandwidth::Narrowband,
522                    12000 => Bandwidth::Mediumband,
523                    16000 => Bandwidth::Wideband,
524                    24000 => Bandwidth::Superwideband,
525                    48000 => Bandwidth::Fullband,
526                    _ => Bandwidth::Narrowband,
527                };
528
529                let mode = if sampling_rate > 16000 {
530                    OpusMode::Hybrid
531                } else {
532                    OpusMode::SilkOnly
533                };
534                (mode, bw)
535            }
536            Application::RestrictedLowDelay => {
537                let bw = match sampling_rate {
538                    8000 => Bandwidth::Narrowband,
539                    12000 => Bandwidth::Mediumband,
540                    16000 => Bandwidth::Wideband,
541                    24000 => Bandwidth::Superwideband,
542                    _ => Bandwidth::Fullband,
543                };
544                (OpusMode::CeltOnly, bw)
545            }
546            Application::Audio => {
547                if sampling_rate <= 16000 {
548                    let bw = match sampling_rate {
549                        8000 => Bandwidth::Narrowband,
550                        12000 => Bandwidth::Mediumband,
551                        _ => Bandwidth::Wideband,
552                    };
553                    (OpusMode::SilkOnly, bw)
554                } else {
555                    let bw = match sampling_rate {
556                        24000 => Bandwidth::Superwideband,
557                        _ => Bandwidth::Fullband,
558                    };
559                    (OpusMode::Hybrid, bw)
560                }
561            }
562        };
563
564        use silk::lin2log::silk_lin2log;
565        let variable_hp_smth2_q15 = silk_lin2log(60) << 8;
566
567        Ok(Self {
568            celt_enc,
569            silk_enc,
570            silk_enc_side,
571            application,
572            sampling_rate,
573            channels,
574            bandwidth: bw,
575            bitrate_bps: 64000,
576            complexity: 9,
577            use_cbr: false,
578            use_inband_fec: false,
579            packet_loss_perc: 0,
580            silk_initialized: false,
581            prev_enc_mode: None,
582            forced_mode: None,
583            mode: opus_mode,
584            variable_hp_smth2_q15,
585            hp_mem: FixedVec::from_value(0, channels * 2),
586
587            #[cfg(not(feature = "heap"))]
588            buf_filtered: FixedVec::new(),
589            #[cfg(feature = "heap")]
590            buf_filtered: Box::new(FixedVec::new()),
591            #[cfg(not(feature = "heap"))]
592            buf_silk_input: FixedVec::new(),
593            #[cfg(feature = "heap")]
594            buf_silk_input: Box::new(FixedVec::new()),
595            #[cfg(not(feature = "heap"))]
596            buf_silk_input_right: FixedVec::new(),
597            #[cfg(feature = "heap")]
598            buf_silk_input_right: Box::new(FixedVec::new()),
599            #[cfg(not(feature = "heap"))]
600            buf_silk_api_channel: FixedVec::new(),
601            #[cfg(feature = "heap")]
602            buf_silk_api_channel: Box::new(FixedVec::new()),
603            #[cfg(not(feature = "heap"))]
604            buf_celt_input: FixedVec::new(),
605            #[cfg(feature = "heap")]
606            buf_celt_input: Box::new(FixedVec::new()),
607            #[cfg(not(feature = "heap"))]
608            delay_buffer: FixedVec::from_value(0.0, OPUS_DELAY_BUF),
609            #[cfg(feature = "heap")]
610            delay_buffer: Box::new(FixedVec::from_value(0.0, OPUS_DELAY_BUF)),
611            #[cfg(not(feature = "heap"))]
612            buf_celt_pcm: FixedVec::new(),
613            #[cfg(feature = "heap")]
614            buf_celt_pcm: Box::new(FixedVec::new()),
615            #[cfg(not(feature = "heap"))]
616            buf_f32_frame: FixedVec::new(),
617            #[cfg(feature = "heap")]
618            buf_f32_frame: Box::new(FixedVec::new()),
619            encoder_buffer: if matches!(application, Application::RestrictedLowDelay) {
620                0
621            } else {
622                (sampling_rate / 100) as usize
623            },
624            delay_compensation: if matches!(application, Application::RestrictedLowDelay) {
625                0
626            } else {
627                (sampling_rate / 250) as usize
628            },
629            hp_mem_float: [0.0; 4],
630            silk_resampler_enc: silk::resampler::SilkResampler::default(),
631            silk_resampler_enc_right: silk::resampler::SilkResampler::default(),
632            rc: RangeCoder::new_encoder(1),
633        })
634    }
635
636    pub fn enable_hybrid_mode(&mut self) -> Result<(), &'static str> {
637        if self.sampling_rate != 24000 && self.sampling_rate != 48000 {
638            return Err("Hybrid mode requires 24kHz or 48kHz sampling rate");
639        }
640        let bw = if self.sampling_rate == 48000 {
641            Bandwidth::Fullband
642        } else {
643            Bandwidth::Superwideband
644        };
645        self.mode = OpusMode::Hybrid;
646        self.bandwidth = bw;
647        self.silk_initialized = false;
648        Ok(())
649    }
650
651    /// Encode a frame of interleaved f32 PCM (scale ±1.0; C `opus_encode_float`
652    /// convention where 1.0 maps to PCM16 32768). Samples are consumed exactly
653    /// up to `frame_size * channels`.
654    pub fn encode(
655        &mut self,
656        input: &[f32],
657        frame_size: usize,
658        output: &mut [u8],
659    ) -> Result<usize, &'static str> {
660        self.encode_impl(PcmInput::F32(input), frame_size, output)
661    }
662
663    /// Encode a frame of interleaved PCM16 (`opus_int16` C `opus_encode()`
664    /// convention). Produces byte-for-byte identical packets to
665    /// `encode()` called with the same audio converted via
666    /// `sample as f32 / 32768.0`, while skipping the f32 round-trip in the
667    /// SILK/VoIP path: the integer high-pass biquad and SILK pipeline are fed
668    /// natively, with no temporary conversion buffer (issue #28).
669    pub fn encode_i16(
670        &mut self,
671        input: &[i16],
672        frame_size: usize,
673        output: &mut [u8],
674    ) -> Result<usize, &'static str> {
675        self.encode_impl(PcmInput::I16(input), frame_size, output)
676    }
677
678    fn encode_impl(
679        &mut self,
680        input: PcmInput<'_>,
681        frame_size: usize,
682        output: &mut [u8],
683    ) -> Result<usize, &'static str> {
684        if output.len() < 2 {
685            return Err("Output buffer too small");
686        }
687
688        let frame_rate = frame_rate_from_params(self.sampling_rate, frame_size)
689            .ok_or("Invalid frame size for sampling rate")?;
690
691        // Clamp caller-provided control fields the way libopus's ctl
692        // interface does (OPUS_SET_BITRATE clamps to [500, 3000000];
693        // complexity to [0, 10]). Unclamped extremes overflow the bitrate
694        // arithmetic downstream (bitrate*6 in celt, `equiv*(90+complexity)`
695        // in the mode selection) — issue #27 deep scan.
696        self.bitrate_bps = self.bitrate_bps.clamp(500, 3_000_000);
697        self.complexity = self.complexity.clamp(0, 10);
698        self.packet_loss_perc = self.packet_loss_perc.clamp(0, 100);
699
700        // The encoder consumes exactly frame_size * channels samples per call;
701        // reject short input up front instead of panicking inside the HP filter
702        // or the float→int conversion loops (issue #27 deep-scan).
703        if input.len() < frame_size * self.channels {
704            return Err("Input buffer too small for frame");
705        }
706
707        // Mode selection: match C's opus_encode_native() behavior.
708        // C reference auto-selects between SILK_ONLY and CELT_ONLY; Hybrid is
709        // produced afterwards by bandwidth overrides (SILK-only + FB/SWB → Hybrid).
710        let mut mode = if let Some(forced) = self.forced_mode {
711            forced
712        } else if self.application == Application::RestrictedLowDelay {
713            OpusMode::CeltOnly
714        } else {
715            let equiv = compute_equiv_rate(
716                self.bitrate_bps,
717                self.channels,
718                frame_rate,
719                !self.use_cbr,
720                self.complexity,
721                self.packet_loss_perc,
722            );
723            let prev_was_celt = self.prev_enc_mode == Some(OpusMode::CeltOnly);
724            let has_prev_mode = self.prev_enc_mode.is_some();
725            let voice_est = match self.application {
726                Application::Voip => 115,
727                Application::Audio => 48,
728                Application::RestrictedLowDelay => 0,
729            };
730            let threshold = compute_mode_threshold(
731                self.application,
732                self.channels,
733                prev_was_celt,
734                has_prev_mode,
735                voice_est,
736            );
737            if equiv >= threshold && self.sampling_rate >= 24000 {
738                OpusMode::CeltOnly
739            } else {
740                OpusMode::SilkOnly
741            }
742        };
743
744        if self.forced_mode.is_none() {
745            let curr_bw = self.bandwidth;
746            if mode == OpusMode::SilkOnly
747                && (curr_bw == Bandwidth::Superwideband || curr_bw == Bandwidth::Fullband)
748            {
749                mode = OpusMode::Hybrid;
750            }
751            if mode == OpusMode::Hybrid
752                && (curr_bw == Bandwidth::Narrowband
753                    || curr_bw == Bandwidth::Mediumband
754                    || curr_bw == Bandwidth::Wideband)
755            {
756                mode = OpusMode::SilkOnly;
757            }
758        }
759
760        // Per-frame packet budget (C computes this before the mode-dependent
761        // encode; the starved-budget rules below need it too).
762        let target_bits =
763            (self.bitrate_bps as i64 * frame_size as i64 / self.sampling_rate as i64) as i32;
764        let cbr_bytes = ((target_bits + 4) / 8) as usize;
765        let max_data_bytes = output.len();
766
767        // C parity (opus_encoder.c): the nominal-size cap applies in CBR mode
768        // only. In VBR mode the CELT-internal bound (vbr_rate/reservoir) decides
769        // the per-frame size, allowing overshoot (borrowing) and undershoot.
770        // Capping VBR at nominal defeats the reservoir and starves complex frames.
771        let mut n_bytes = if self.use_cbr {
772            cbr_bytes
773                .min(max_data_bytes)
774                .clamp(1, OPUS_MAX_PACKET_BYTES)
775        } else {
776            max_data_bytes.clamp(1, OPUS_MAX_PACKET_BYTES)
777        };
778
779        // C opus_encoder.c:1525-1527: a budget representing less than 6 kb/s
780        // (9 kb/s for frames longer than 20 ms) cannot carry SILK — libopus
781        // switches to CELT-only mode, which codes it as a small valid packet
782        // instead of a PLC placeholder (issue #46).
783        let tiny_budget_rate: i64 = if frame_rate > 50 { 9000 } else { 6000 };
784        if ((n_bytes as i64) * 8 * (self.sampling_rate as i64))
785            < (tiny_budget_rate * (frame_size as i64))
786        {
787            mode = OpusMode::CeltOnly;
788        }
789
790        // 40/60 ms frames are only legal as a single Opus frame in SILK-only
791        // mode. CELT and Hybrid cap a frame at 20 ms, so libopus encodes 20 ms
792        // sub-frames and repacketizes them into one code 1/2/3 packet
793        // (opus_encoder.c `encode_multiframe_packet`). (issue #35)
794        if self.forced_mode.is_none()
795            && mode != OpusMode::SilkOnly
796            && frame_size > self.sampling_rate as usize / 50
797        {
798            return self.encode_multiframe(input, frame_size, mode, output);
799        }
800
801        // C opus_encoder.c: with mode and bandwidth settled, decide whether
802        // this packet codes in-band FEC (decide_fec()); it can lower the
803        // frame's bandwidth when a high loss rate demands FEC at any cost.
804        // The reduction is per-frame: `self.bandwidth` (the user/auto state)
805        // is left untouched, matching C where the bandwidth is re-derived
806        // every frame.
807        let mut frame_bandwidth = self.bandwidth;
808        let equiv_rate = compute_equiv_rate(
809            self.bitrate_bps,
810            self.channels,
811            frame_rate,
812            !self.use_cbr,
813            self.complexity,
814            self.packet_loss_perc,
815        );
816        let lbrr_coded = decide_fec(
817            if self.use_inband_fec { 1 } else { 0 },
818            self.packet_loss_perc,
819            self.silk_enc.s_cmn.lbrr_enabled,
820            mode,
821            &mut frame_bandwidth,
822            equiv_rate,
823        );
824        if mode == OpusMode::CeltOnly {
825            // silk_mode.LBRR_coded persists across frames even while SILK is
826            // idle; it feeds decide_fec's hysteresis on the next SILK frame.
827            self.silk_enc.s_cmn.lbrr_enabled = 0;
828            self.silk_enc_side.s_cmn.lbrr_enabled = 0;
829        }
830
831        if mode == OpusMode::CeltOnly {
832            match frame_rate {
833                400 | 200 | 100 | 50 => {}
834                _ => return Err("Unsupported frame size for CELT-only mode"),
835            }
836        }
837
838        if mode == OpusMode::Hybrid {
839            match frame_rate {
840                100 | 50 => {}
841                _ => return Err("Unsupported frame size for Hybrid mode"),
842            }
843        }
844
845        if mode == OpusMode::SilkOnly {
846            match frame_rate {
847                100 | 50 | 25 | 16 => {}
848                // 2.5/5 ms are CELT-only durations: SILK frames are 10 ms or
849                // longer (libopus), and sub-10 ms frames break the SILK
850                // analysis geometry (e.g. the VAD band split at 12 kHz).
851                _ => return Err("Unsupported frame size for SILK-only mode"),
852            }
853        }
854
855        let toc = gen_toc(mode, frame_rate, frame_bandwidth, self.channels);
856        output[0] = toc;
857
858        // C opus_encoder.c:1226-1243: a budget too small to code anything
859        // useful produces a TOC-only "PLC" packet — decoders conceal it as a
860        // loss. Encoding into 0-2 bytes would otherwise overflow the range
861        // coder (issue #45). Under CBR the packet is padded to the nominal
862        // size, exactly like libopus's opus_packet_pad.
863        let sub_3_bytes_per_frame =
864            (self.bitrate_bps as i64) < 3 * frame_rate as i64 * 8;
865        let tiny_long_frame =
866            frame_rate < 50
867                && (n_bytes * (frame_rate as usize) < 300 || self.bitrate_bps < 2400);
868        if n_bytes < 3 || sub_3_bytes_per_frame || tiny_long_frame {
869            let mut toc_mode = mode;
870            let mut packet_code = 0u8;
871            let mut num_multiframes = 0u8;
872            let mut frame_rate_adj = frame_rate;
873            if frame_rate > 100 {
874                toc_mode = OpusMode::CeltOnly;
875            }
876            // 40 ms -> 2 x 20 ms in CELT/hybrid mode.
877            if frame_rate == 25 && toc_mode != OpusMode::SilkOnly {
878                frame_rate_adj = 50;
879                packet_code = 1;
880            }
881            // >= 60 ms frames: 1 x 60 ms, or 2 x 40 ms / 2 x 60 ms multiframes.
882            if frame_rate <= 16 {
883                if n_bytes == 1 || (toc_mode == OpusMode::SilkOnly && frame_rate != 10) {
884                    toc_mode = OpusMode::SilkOnly;
885                    packet_code = if frame_rate <= 12 { 1 } else { 0 };
886                    frame_rate_adj = if frame_rate == 12 { 25 } else { 16 };
887                } else {
888                    num_multiframes = (50 / frame_rate) as u8;
889                    frame_rate_adj = 50;
890                    packet_code = 3;
891                }
892            }
893            let mut plc_bw = frame_bandwidth;
894            if toc_mode == OpusMode::SilkOnly && bandwidth_fec_index(plc_bw) > 2 {
895                // SilkOnly is wideband at most (C: bw > OPUS_BANDWIDTH_WIDEBAND).
896                plc_bw = Bandwidth::Wideband;
897            }
898            output[0] = gen_toc(toc_mode, frame_rate_adj, plc_bw, self.channels) | packet_code;
899            let mut ret = if packet_code <= 1 { 1 } else { 2 };
900            if packet_code == 3 {
901                output[1] = num_multiframes;
902            }
903            if self.use_cbr {
904                let target_total = n_bytes.min(output.len());
905                if target_total >= 2 {
906                    // opus_packet_pad of the TOC-only packet: code 3 with
907                    // `m` empty frames and zero padding.
908                    let m: usize = if packet_code == 3 {
909                        num_multiframes as usize
910                    } else if packet_code == 1 {
911                        2
912                    } else {
913                        1
914                    };
915                    output[0] = (output[0] & 0xFC) | 0x03;
916                    output[1] = 0x40 | m as u8; // padding present, CBR, m frames
917                    if target_total > 2 {
918                        let pad_amount = target_total - 2;
919                        let nb_255s = (pad_amount - 1) / 255;
920                        let mut ptr = 2;
921                        for _ in 0..nb_255s {
922                            output[ptr] = 255;
923                            ptr += 1;
924                        }
925                        output[ptr] = (pad_amount - 255 * nb_255s - 1) as u8;
926                        ptr += 1;
927                        for b in &mut output[ptr..target_total] {
928                            *b = 0;
929                        }
930                    }
931                    ret = target_total;
932                }
933            }
934            // C leaves st->prev_mode untouched on this path.
935            return Ok(ret);
936        }
937
938        let init_rc_size = n_bytes - 1;
939        self.rc.reset_for_encode(init_rc_size as u32);
940
941        // C opus_encode_frame_native: high-pass cutoff state is updated for ALL
942        // modes (celt_encoder.c:1969-1977); the filtered signal is what feeds
943        // both SILK (hybrid) and CELT. Compute it here so CELT-only frames also
944        // keep `variable_hp_smth2_q15` in sync with libopus.
945        let hp_freq_smth1 = if mode == OpusMode::CeltOnly {
946            silk_lin2log(60) << 8
947        } else {
948            self.silk_enc.s_cmn.variable_hp_smth1_q15
949        };
950
951        const VARIABLE_HP_SMTH_COEF2_Q16: i32 = 984;
952        self.variable_hp_smth2_q15 = silk_smlawb(
953            self.variable_hp_smth2_q15,
954            hp_freq_smth1 - self.variable_hp_smth2_q15,
955            VARIABLE_HP_SMTH_COEF2_Q16,
956        );
957
958        let cutoff_hz = silk_log2lin(silk_rshift(self.variable_hp_smth2_q15, 8));
959
960        if mode == OpusMode::SilkOnly || mode == OpusMode::Hybrid {
961            let silk_fs_khz = if mode == OpusMode::Hybrid {
962                16
963            } else {
964                self.sampling_rate.min(16000) / 1000
965            };
966
967            let frame_ms = (frame_size as i32 * 1000) / self.sampling_rate;
968
969            // C opus_encoder.c `bits_target` / `total_bitRate` (1.6): SILK
970            // codes at the requested bitrate, capped by the packet budget,
971            // less the TOC byte. Under VBR the budget is the caller's output
972            // buffer, which must cap the rate, not set it (issue #43). CBR
973            // first re-quantizes the bitrate to the whole bytes it codes, as
974            // opus_encode_native does, so the cap is the packet itself.
975            let rate_bps = if self.use_cbr {
976                bits_to_bitrate(8 * n_bytes as i32, self.sampling_rate, frame_size)
977            } else {
978                self.bitrate_bps
979            };
980            let rate_bits = bitrate_to_bits(rate_bps, self.sampling_rate, frame_size);
981            let bits_target = (8 * n_bytes as i32).min(rate_bits) - 8;
982            let total_bitrate = bits_to_bitrate(bits_target, self.sampling_rate, frame_size);
983
984            // C opus_encoder.c:1448-1455: (re)entering SILK/hybrid from CELT
985            // resets the SILK encoder and prefills it with recent (ramped)
986            // audio, so the first SILK frame starts from valid history.
987            let silk_restart_after_celt =
988                self.prev_enc_mode == Some(OpusMode::CeltOnly);
989            if silk_restart_after_celt {
990                silk_init_encoder(state_mut(&mut self.silk_enc), 0);
991                // C silk_InitEncoder also clears the packet-level rate state.
992                self.silk_enc.n_bits_used_lbrr = 0;
993                self.silk_enc.n_bits_exceeded = 0;
994                if self.channels == 2 {
995                    // C silk_InitEncoder clears the side encoder and the
996                    // stereo state too; the next stereo frame starts afresh.
997                    *state_mut(&mut self.silk_enc_side) = SilkEncoderState::default();
998                    silk_init_encoder(state_mut(&mut self.silk_enc_side), 0);
999                    self.silk_enc.stereo.reset();
1000                }
1001                self.silk_initialized = false;
1002            }
1003            if !self.silk_initialized || self.silk_enc.s_cmn.fs_khz != silk_fs_khz {
1004                silk_control_encoder(
1005                    state_mut(&mut self.silk_enc),
1006                    silk_fs_khz,
1007                    frame_ms,
1008                    total_bitrate,
1009                    self.complexity,
1010                );
1011                self.silk_enc.s_cmn.use_cbr = if self.use_cbr { 1 } else { 0 };
1012
1013                self.silk_enc.s_cmn.n_channels = self.channels as i32;
1014                self.silk_initialized = true;
1015                // C silk_setup_resamplers: re-init the encoder-direction
1016                // resampler whenever the SILK rate changes; the input delay
1017                // comes from delay_matrix_enc.
1018                let ret = self
1019                    .silk_resampler_enc
1020                    .init_for_enc(self.sampling_rate as i32, silk_fs_khz * 1000);
1021                if ret != 0 {
1022                    return Err("Unsupported SILK resampling ratio");
1023                }
1024                if self.channels == 2 {
1025                    self.silk_resampler_enc_right = self.silk_resampler_enc.clone();
1026                    // The side runs at the mid's rate and frame size (C
1027                    // silk_control_encoder with force_fs_kHz).
1028                    let side = state_mut(&mut self.silk_enc_side);
1029                    silk_control_encoder(
1030                        side,
1031                        silk_fs_khz,
1032                        frame_ms,
1033                        total_bitrate,
1034                        self.complexity,
1035                    );
1036                    side.s_cmn.use_cbr = self.silk_enc.s_cmn.use_cbr;
1037                    side.s_cmn.n_channels = 2;
1038                }
1039            }
1040
1041            // C silk_setup_LBRR (control_codec.c): per-packet FEC state and
1042            // LBRR gain increase (7 on the first FEC packet, then
1043            // max(7 - 0.2*loss, 2)), for each coded channel.
1044            for enc in [&mut self.silk_enc, &mut self.silk_enc_side]
1045                .into_iter()
1046                .take(self.channels)
1047            {
1048                enc.s_cmn.use_in_band_fec = if self.use_inband_fec { 1 } else { 0 };
1049                enc.s_cmn.packet_loss_perc = self.packet_loss_perc.clamp(0, 100);
1050                silk_setup_lbrr(state_mut(enc), lbrr_coded);
1051            }
1052
1053            let silk_frame_len_samples =
1054                frame_size * silk_fs_khz as usize / (self.sampling_rate.max(1000) / 1000) as usize;
1055            let silk_bitrate = if mode == OpusMode::Hybrid {
1056                let frame_duration_ms = frame_size as i32 * 1000 / self.sampling_rate;
1057                let frame20ms = frame_duration_ms >= 20;
1058                compute_silk_rate_for_hybrid(self.bitrate_bps, frame20ms, self.channels)
1059            } else {
1060                // C: SILK gets all bits, at the requested rate (issue #43).
1061                total_bitrate
1062            };
1063
1064            // C opus_encoder.c:1806-1826: prefill the fresh SILK encoder with
1065            // the tail of the delay buffer, zeroed before a 2.5 ms fade-in,
1066            // resampled through the fresh encoder resampler so its delay
1067            // state also starts consistent.
1068            if silk_restart_after_celt && self.encoder_buffer > 0 && self.delay_compensation > 0
1069            {
1070                let ch = self.channels;
1071                let f2_5 = self.sampling_rate / 400;
1072                let eb = self.encoder_buffer;
1073                let offset = eb - self.delay_compensation.min(eb) - f2_5 as usize;
1074                let silk_rate = self.sampling_rate.min(16000);
1075                let pre_resampled_len = eb * silk_rate as usize / self.sampling_rate as usize;
1076                // Stack-fixed: encoder_buffer = Fs/100 <= 480, and the
1077                // resampled prefill is at most the same length (Copy at equal
1078                // rates). No allocation: this crate is heap-free without the
1079                // `heap` feature.
1080                if ch == 2 {
1081                    // Each channel through its own resampler, then mid/side
1082                    // at the internal rate, as C silk_Encode prefills two
1083                    // internal channels.
1084                    let (mut pre_l, mut pre_r) = ([0i16; 480], [0i16; 480]);
1085                    if offset < eb {
1086                        for i in offset..eb {
1087                            let fade = (((i - offset) as f32) / f2_5 as f32).min(1.0);
1088                            pre_l[i] = (self.delay_buffer[2 * i] * fade * 32768.0) as i16;
1089                            pre_r[i] = (self.delay_buffer[2 * i + 1] * fade * 32768.0) as i16;
1090                        }
1091                    }
1092                    let (mut int_l, mut int_r) = ([0i16; 480], [0i16; 480]);
1093                    let int_l = &mut int_l[..pre_resampled_len];
1094                    let int_r = &mut int_r[..pre_resampled_len];
1095                    self.silk_resampler_enc
1096                        .process(int_l, &pre_l[..eb], eb as i32);
1097                    self.silk_resampler_enc_right
1098                        .process(int_r, &pre_r[..eb], eb as i32);
1099                    silk_encode_prefill_stereo(
1100                        state_mut(&mut self.silk_enc),
1101                        state_mut(&mut self.silk_enc_side),
1102                        int_l,
1103                        int_r,
1104                        silk_bitrate,
1105                        1,
1106                    );
1107                } else {
1108                    let mut pre = [0i16; 480];
1109                    let pre = &mut pre[..eb];
1110                    if offset < eb {
1111                        for i in offset..eb {
1112                            let fade = (((i - offset) as f32) / f2_5 as f32).min(1.0);
1113                            pre[i] = (self.delay_buffer[i] * fade * 32768.0) as i16;
1114                        }
1115                    }
1116                    let mut pre_internal = [0i16; 480];
1117                    let pre_internal = &mut pre_internal[..pre_resampled_len];
1118                    self.silk_resampler_enc
1119                        .process(pre_internal, pre, eb as i32);
1120                    silk_encode_prefill(state_mut(&mut self.silk_enc), pre_internal, 1);
1121                }
1122            }
1123
1124            let required_size = frame_size * self.channels;
1125            self.buf_filtered.resize(required_size, 0);
1126            let voip = self.application == Application::Voip;
1127            match input {
1128                PcmInput::I16(samples) => {
1129                    if voip {
1130                        // Native i16 path: feed the integer HP biquad directly,
1131                        // skipping the f32 round-trip and its temporary
1132                        // conversion buffer (issue #28).
1133                        hp_cutoff_i16(
1134                            samples,
1135                            cutoff_hz,
1136                            state_mut(&mut self.buf_filtered),
1137                            &mut self.hp_mem,
1138                            frame_size,
1139                            self.channels,
1140                            self.sampling_rate,
1141                        );
1142                    } else {
1143                        let filtered = state_mut(&mut self.buf_filtered);
1144                        filtered[..required_size].copy_from_slice(&samples[..required_size]);
1145                    }
1146                }
1147                PcmInput::F32(samples) => {
1148                    if voip {
1149                        hp_cutoff(
1150                            samples,
1151                            cutoff_hz,
1152                            state_mut(&mut self.buf_filtered),
1153                            &mut self.hp_mem,
1154                            frame_size,
1155                            self.channels,
1156                            self.sampling_rate,
1157                        );
1158                    } else {
1159                        let filtered = state_mut(&mut self.buf_filtered);
1160                        for (i, &x) in samples.iter().enumerate().take(required_size) {
1161                            filtered[i] = (x * 32768.0).clamp(-32768.0, 32767.0) as i16;
1162                        }
1163                    }
1164                }
1165            }
1166
1167            let input_i16 = state_ref(&self.buf_filtered);
1168
1169            // SILK input resampling (C silk_Encode always runs silk_resampler
1170            // on the API-rate input; the Copy path still applies its
1171            // delay_matrix_enc input delay at equal rates). Stereo resamples
1172            // each channel on its own and splits mid/side at the internal
1173            // rate (enc_API.c, nChannelsInternal 2), in SILK-only and Hybrid
1174            // alike.
1175            self.buf_silk_input.resize(silk_frame_len_samples, 0);
1176            if self.channels == 2 {
1177                self.buf_silk_input_right.resize(silk_frame_len_samples, 0);
1178                for c in 0..2 {
1179                    self.buf_silk_api_channel.resize(frame_size, 0);
1180                    for (s, lr) in self
1181                        .buf_silk_api_channel
1182                        .iter_mut()
1183                        .zip(input_i16.chunks_exact(2))
1184                    {
1185                        *s = lr[c];
1186                    }
1187                    let (resampler, out) = if c == 0 {
1188                        (
1189                            &mut self.silk_resampler_enc,
1190                            state_mut(&mut self.buf_silk_input).as_mut_slice(),
1191                        )
1192                    } else {
1193                        (
1194                            &mut self.silk_resampler_enc_right,
1195                            state_mut(&mut self.buf_silk_input_right).as_mut_slice(),
1196                        )
1197                    };
1198                    if resampler.process(out, &self.buf_silk_api_channel, frame_size as i32) != 0 {
1199                        return Err("SILK input resampling failed");
1200                    }
1201                }
1202            } else {
1203                let got = self.silk_resampler_enc.process(
1204                    state_mut(&mut self.buf_silk_input),
1205                    input_i16,
1206                    frame_size as i32,
1207                );
1208                if got != 0 {
1209                    return Err("SILK input resampling failed");
1210                }
1211            }
1212            let silk_input: &[i16] = state_ref(&self.buf_silk_input);
1213
1214            let mut pn_bytes = 0;
1215
1216            let silk_max_bits = if mode == OpusMode::Hybrid {
1217                let total_max_bits = ((n_bytes - 1) * 8) as i32;
1218                if self.use_cbr {
1219                    let silk_bits = (silk_bitrate as i64 * frame_size as i64
1220                        / self.sampling_rate as i64) as i32;
1221                    let other_bits = 0i32.max(total_max_bits - silk_bits);
1222                    0i32.max(total_max_bits - other_bits * 3 / 4)
1223                } else {
1224                    let frame_duration_ms = frame_size as i32 * 1000 / self.sampling_rate;
1225                    let frame20ms = frame_duration_ms >= 20;
1226                    let max_bit_rate = compute_silk_rate_for_hybrid(
1227                        total_max_bits * self.sampling_rate / frame_size as i32,
1228                        frame20ms,
1229                        self.channels,
1230                    );
1231                    max_bit_rate * frame_size as i32 / self.sampling_rate
1232                }
1233            } else {
1234                ((n_bytes - 1) * 8) as i32
1235            };
1236            let silk_use_cbr = if mode == OpusMode::Hybrid && self.use_cbr {
1237                0
1238            } else if self.use_cbr {
1239                1
1240            } else {
1241                0
1242            };
1243            let ret = if self.channels == 2 {
1244                silk_encode_stereo_packet(
1245                    state_mut(&mut self.silk_enc),
1246                    state_mut(&mut self.silk_enc_side),
1247                    silk_input,
1248                    state_ref(&self.buf_silk_input_right),
1249                    silk_input.len(),
1250                    &mut self.rc,
1251                    &mut pn_bytes,
1252                    silk_bitrate,
1253                    silk_max_bits,
1254                    silk_use_cbr,
1255                    1,
1256                )
1257            } else {
1258                silk_encode(
1259                    state_mut(&mut self.silk_enc),
1260                    silk_input,
1261                    silk_input.len(),
1262                    &mut self.rc,
1263                    &mut pn_bytes,
1264                    silk_bitrate,
1265                    silk_max_bits,
1266                    silk_use_cbr,
1267                    1,
1268                )
1269            };
1270            if ret != 0 {
1271                return Err("SILK encoding failed");
1272            }
1273        }
1274
1275        if mode == OpusMode::Hybrid {
1276            self.rc.encode_bit_logp(false, 12); // redundancy = 0
1277        }
1278
1279        if mode == OpusMode::Hybrid {
1280            let nb_compr_bytes = (n_bytes - 1) as u32;
1281            self.rc.shrink(nb_compr_bytes);
1282        }
1283
1284        let silk_ret_bytes = if mode == OpusMode::SilkOnly {
1285            ((self.rc.tell() + 7) >> 3) as usize
1286        } else {
1287            0
1288        };
1289
1290        // libopus parity: adjust nbCompressedBytes for CELT/Hybrid based on tell (celt_encoder.c:1913-1921)
1291        // tmp = bitrate*frame_size + tell*Fs; nbCompressed = (tmp+4*Fs)/(8*Fs)
1292        // This is the CBR branch (vbr==0) in celt_encoder.c; for VBR the encoder
1293        // does *not* do this adjustment - it uses the vbr_bound logic instead.
1294        // Doing it for VBR would double-shrink and corrupt the budget.
1295        if mode != OpusMode::SilkOnly && self.use_cbr {
1296            let tell = self.rc.tell();
1297            if tell > 1 {
1298                let tmp = self.bitrate_bps as i64 * frame_size as i64
1299                    + tell as i64 * self.sampling_rate as i64;
1300                let adjusted = ((tmp + 4 * self.sampling_rate as i64)
1301                    / (8 * self.sampling_rate as i64)) as usize;
1302                let new_n = adjusted.clamp(1, OPUS_MAX_PACKET_BYTES).min(max_data_bytes);
1303                if new_n < n_bytes {
1304                    n_bytes = new_n;
1305                    // shrink range coder to new size (keep SILK bytes, trim tail)
1306                    let new_payload = n_bytes - 1;
1307                    self.rc.shrink(new_payload as u32);
1308                }
1309            }
1310        }
1311        if mode == OpusMode::CeltOnly || mode == OpusMode::Hybrid {
1312            self.celt_enc.complexity = self.complexity;
1313            let start_band = if mode == OpusMode::Hybrid { 17 } else { 0 };
1314            // libopus `CELT_SET_END_BAND`: match the TOC bandwidth so the
1315            // decoder reads the same number of coded bands (issue #37).
1316            let end_band = match frame_bandwidth {
1317                Bandwidth::Narrowband => 13,
1318                Bandwidth::Mediumband | Bandwidth::Wideband => 17,
1319                Bandwidth::Superwideband => 19,
1320                Bandwidth::Fullband | Bandwidth::Auto => 21,
1321            };
1322            self.celt_enc.set_end_band(end_band);
1323            let total_packet_bits = ((n_bytes - 1) * 8) as i32;
1324            // Propagate bitrate/VBR to CeltEncoder for accurate VBR handling (libopus parity)
1325            let celt_bitrate = if mode == OpusMode::Hybrid {
1326                let frame_ms = frame_size as i32 * 1000 / self.sampling_rate;
1327                let frame20ms = frame_ms >= 20;
1328                let silk_rate =
1329                    compute_silk_rate_for_hybrid(self.bitrate_bps, frame20ms, self.channels);
1330                (self.bitrate_bps - silk_rate).max(8000)
1331            } else {
1332                self.bitrate_bps
1333            };
1334            self.celt_enc.set_bitrate(celt_bitrate);
1335            self.celt_enc.set_vbr(!self.use_cbr);
1336            // libopus: Hybrid VBR is unconstrained (can steal from SILK), CELT-only constrained
1337            self.celt_enc
1338                .set_constrained_vbr(mode == OpusMode::CeltOnly);
1339
1340            // Float view of the current frame for the CELT pipeline. For the
1341            // i16 entry point this converts once into a scratch buffer with
1342            // the exact per-sample scaling a caller of `encode()` performs
1343            // (`s as f32 / 32768.0`), so packets stay byte-for-byte identical
1344            // across the two entry points (issue #28).
1345            let input_f32: &[f32] = match input {
1346                PcmInput::F32(samples) => samples,
1347                PcmInput::I16(samples) => {
1348                    let n = frame_size * self.channels;
1349                    self.buf_f32_frame.resize(n, 0.0);
1350                    let staging = state_mut(&mut self.buf_f32_frame);
1351                    for (dst, &src) in staging[..n].iter_mut().zip(samples[..n].iter()) {
1352                        *dst = src as f32 / 32768.0;
1353                    }
1354                    state_ref(&self.buf_f32_frame)
1355                }
1356            };
1357
1358            // Build the CELT input exactly like C `opus_encode_frame_native`:
1359            //   pcm_buf = [delay-compensation prefix from ring]
1360            //             [dc_reject / hp_cutoff'd current frame]
1361            // CELT then reads pcm_buf[0..frame_size*channels] (opus_encoder.c:
1362            // 1966-2010, 2493). This delay + filter step is what libopus feeds
1363            // its CELT encoder; passing the raw input diverges from 1.6.
1364            let celt_input: &[f32] = if self.delay_compensation > 0 {
1365                let delay = self.delay_compensation;
1366                let ebuf = self.encoder_buffer;
1367                let ch = self.channels;
1368                let total = (delay + frame_size) * ch;
1369                self.buf_celt_pcm.resize(total, 0.0);
1370                // 1. delay prefix from the ring (C: OPUS_COPY at 1967)
1371                let prefix = delay * ch;
1372                let src_start = (ebuf - delay) * ch;
1373                self.buf_celt_pcm[..prefix]
1374                    .copy_from_slice(&self.delay_buffer[src_start..src_start + prefix]);
1375                // 2. filter current frame into pcm_buf[delay..] (C: 2002-2010)
1376                let out = &mut self.buf_celt_pcm[prefix..];
1377                if self.application == Application::Voip {
1378                    hp_cutoff_float(
1379                        input_f32,
1380                        cutoff_hz,
1381                        out,
1382                        &mut self.hp_mem_float,
1383                        frame_size,
1384                        ch,
1385                        self.sampling_rate,
1386                    );
1387                } else {
1388                    dc_reject_float(
1389                        input_f32,
1390                        3,
1391                        out,
1392                        &mut self.hp_mem_float,
1393                        frame_size,
1394                        ch,
1395                        self.sampling_rate,
1396                    );
1397                }
1398                // 3. float NaN guard (C: 2016-2028)
1399                let mut sum = 0.0f32;
1400                for &v in &self.buf_celt_pcm[prefix..] {
1401                    sum += v * v;
1402                }
1403                // C shape kept verbatim (`!(sum < 1e9f) || celt_isnan(sum)`):
1404                // the negated comparison is deliberate NaN handling.
1405                #[allow(clippy::neg_cmp_op_on_partial_ord)]
1406                if !(sum < 1e9) || sum.is_nan() {
1407                    self.buf_celt_pcm[prefix..].fill(0.0);
1408                    self.hp_mem_float = [0.0; 4];
1409                }
1410                // 4. delay ring update (C: 2300-2312)
1411                let keep = ebuf as i64 - (frame_size as i64 + delay as i64);
1412                if keep > 0 {
1413                    let keep = keep as usize;
1414                    self.delay_buffer
1415                        .copy_within(ch * frame_size..ch * (frame_size + keep), 0);
1416                    let dst = ch * keep;
1417                    let n = (frame_size + delay) * ch;
1418                    self.delay_buffer[dst..dst + n].copy_from_slice(&self.buf_celt_pcm[..n]);
1419                } else {
1420                    let n = ebuf * ch;
1421                    let src = (frame_size + delay - ebuf) * ch;
1422                    self.delay_buffer[..n].copy_from_slice(&self.buf_celt_pcm[src..src + n]);
1423                }
1424                // 5. deinterleave pcm_buf[0..frame_size*ch] (interleaved) to
1425                //    channel-major for the Rust CELT encoder.
1426                let n = frame_size * ch;
1427                self.buf_celt_input.resize(n, 0.0);
1428                for i in 0..frame_size {
1429                    for c in 0..ch {
1430                        self.buf_celt_input[c * frame_size + i] = self.buf_celt_pcm[i * ch + c];
1431                    }
1432                }
1433                state_ref(&self.buf_celt_input)
1434            } else if self.channels == 1 {
1435                input_f32
1436            } else {
1437                let n = frame_size * self.channels;
1438                self.buf_celt_input.resize(n, 0.0);
1439                for i in 0..frame_size {
1440                    for ch in 0..self.channels {
1441                        self.buf_celt_input[ch * frame_size + i] =
1442                            input_f32[i * self.channels + ch];
1443                    }
1444                }
1445                state_ref(&self.buf_celt_input)
1446            };
1447
1448            if self.rc.tell() <= total_packet_bits {
1449                let is_vbr = !self.use_cbr;
1450                self.celt_enc.encode_with_budget_vbr(
1451                    celt_input,
1452                    frame_size,
1453                    &mut self.rc,
1454                    start_band,
1455                    total_packet_bits,
1456                    is_vbr,
1457                );
1458            }
1459        }
1460
1461        self.rc.done();
1462
1463        // C opus_encoder.c:2170-2179: "In the unlikely case that the SILK
1464        // encoder busted its target, tell the decoder to call the PLC" — emit
1465        // a minimal packet with an EMPTY frame instead of failing hard. The
1466        // zero-length frame makes the decoder conceal (libopus pads the same
1467        // 2-byte packet with opus_packet_pad under CBR). With FEC the LBRR
1468        // section can squeeze the main frame below what even the
1469        // damage-control re-encode fits, which is exactly the case this
1470        // fallback exists for.
1471        if self.rc.error != 0 || self.rc.tell() > (n_bytes as i32 - 1) * 8 {
1472            if output.len() < 2 {
1473                return Err("Output buffer too small");
1474            }
1475            let mut ret = 2usize;
1476            if self.use_cbr && n_bytes >= 2 {
1477                // CBR: pad to the nominal size as a code-3 packet holding a
1478                // single EMPTY frame (opus_packet_pad of the 2-byte packet).
1479                let target_total = n_bytes.min(output.len());
1480                if target_total > 2 {
1481                    output[0] = toc | 0x03;
1482                    output[1] = 0x41; // 1 frame, padding present, CBR
1483                    let pad_amount = target_total - 2;
1484                    let nb_255s = (pad_amount - 1) / 255;
1485                    let mut ptr = 2;
1486                    for _ in 0..nb_255s {
1487                        output[ptr] = 255;
1488                        ptr += 1;
1489                    }
1490                    output[ptr] = (pad_amount - 255 * nb_255s - 1) as u8;
1491                    ptr += 1;
1492                    for b in &mut output[ptr..target_total] {
1493                        *b = 0;
1494                    }
1495                    self.prev_enc_mode = Some(mode);
1496                    return Ok(target_total);
1497                }
1498                ret = target_total.max(1);
1499            }
1500            // VBR: a 2-byte code-0 packet whose frame length byte is 0.
1501            output[0] = toc;
1502            output[1] = 0;
1503            self.prev_enc_mode = Some(mode);
1504            return Ok(ret);
1505        }
1506
1507        if mode == OpusMode::SilkOnly {
1508            let mut ret = silk_ret_bytes.min(self.rc.storage as usize);
1509            while ret > 2 && self.rc.buf[ret - 1] == 0 {
1510                ret -= 1;
1511            }
1512
1513            let target_total = if self.use_cbr {
1514                n_bytes.min(output.len())
1515            } else {
1516                (ret + 1).min(output.len())
1517            };
1518
1519            let silk_len = ret;
1520
1521            if !self.use_cbr || silk_len + 1 >= target_total {
1522                // VBR or payload fills the target: simple code 0 packet
1523                output[0] = toc;
1524                let copy_len = silk_len.min(target_total - 1);
1525                output[1..1 + copy_len].copy_from_slice(&self.rc.buf[..copy_len]);
1526                return Ok((copy_len + 1).min(output.len()));
1527            }
1528
1529            output[0] = toc | 0x03;
1530
1531            if silk_len + 2 >= target_total {
1532                output[1] = 0x01;
1533                let copy_len = (target_total - 2).min(silk_len);
1534                output[2..2 + copy_len].copy_from_slice(&self.rc.buf[..copy_len]);
1535                self.prev_enc_mode = Some(mode);
1536                return Ok(target_total.min(output.len()));
1537            }
1538
1539            let pad_amount = target_total - silk_len - 2;
1540            output[1] = 0x41;
1541
1542            let nb_255s = (pad_amount - 1) / 255;
1543            let mut ptr = 2;
1544            for _ in 0..nb_255s {
1545                output[ptr] = 255;
1546                ptr += 1;
1547            }
1548            output[ptr] = (pad_amount - 255 * nb_255s - 1) as u8;
1549            ptr += 1;
1550
1551            output[ptr..ptr + silk_len].copy_from_slice(&self.rc.buf[..silk_len]);
1552            ptr += silk_len;
1553
1554            let fill_end = target_total.min(output.len());
1555            for byte in output[ptr..fill_end].iter_mut() {
1556                *byte = 0;
1557            }
1558
1559            self.prev_enc_mode = Some(mode);
1560            return Ok(target_total.min(output.len()));
1561        }
1562
1563        // For CELT/Hybrid, respect possible VBR shrink performed by CeltEncoder (e.g. silence)
1564        let payload_len = (self.rc.storage as usize).min(output.len() - 1);
1565        output[1..1 + payload_len].copy_from_slice(&self.rc.buf[..payload_len]);
1566        self.prev_enc_mode = Some(mode);
1567        Ok(payload_len + 1)
1568    }
1569
1570    /// Repacketize 40/60 ms CELT/Hybrid input as 20 ms sub-frames in one code
1571    /// 1/2/3 Opus packet (libopus `encode_multiframe_packet`). Every sub-frame
1572    /// is forced to `mode` so the packet carries a single config.
1573    fn encode_multiframe(
1574        &mut self,
1575        input: PcmInput<'_>,
1576        frame_size: usize,
1577        mode: OpusMode,
1578        output: &mut [u8],
1579    ) -> Result<usize, &'static str> {
1580        const MAX_SUB: usize = 3; // up to 60 ms of 20 ms frames
1581        let ch = self.channels;
1582        let enc_frame = (self.sampling_rate / 50) as usize; // 20 ms
1583        if enc_frame == 0 || !frame_size.is_multiple_of(enc_frame) {
1584            return Err("Invalid multi-frame size");
1585        }
1586        let nb = frame_size / enc_frame;
1587        if !(2..=MAX_SUB).contains(&nb) {
1588            return Err("Unsupported multi-frame size");
1589        }
1590
1591        // RFC 6716 size field: one byte below 252, else two.
1592        fn put_size(out: &mut [u8], pos: &mut usize, len: usize) -> Result<(), &'static str> {
1593            if len < 252 {
1594                if *pos >= out.len() {
1595                    return Err("Output buffer too small");
1596                }
1597                out[*pos] = len as u8;
1598                *pos += 1;
1599            } else {
1600                if *pos + 1 >= out.len() {
1601                    return Err("Output buffer too small");
1602                }
1603                let first = 252 + (len & 3);
1604                out[*pos] = first as u8;
1605                out[*pos + 1] = ((len - first) >> 2) as u8;
1606                *pos += 2;
1607            }
1608            Ok(())
1609        }
1610
1611        let mut sub = [[0u8; 1276]; MAX_SUB];
1612        let mut lens = [0usize; MAX_SUB];
1613        self.forced_mode = Some(mode);
1614        let enc_res = (|| -> Result<(), &'static str> {
1615            for i in 0..nb {
1616                let start = i * enc_frame * ch;
1617                let end = start + enc_frame * ch;
1618                let sub_input = match input {
1619                    PcmInput::F32(s) => PcmInput::F32(&s[start..end]),
1620                    PcmInput::I16(s) => PcmInput::I16(&s[start..end]),
1621                };
1622                let n = self.encode_impl(sub_input, enc_frame, &mut sub[i])?;
1623                if n == 0 {
1624                    return Err("Empty sub-frame");
1625                }
1626                lens[i] = n - 1;
1627            }
1628            Ok(())
1629        })();
1630        self.forced_mode = None;
1631        enc_res?;
1632
1633        let toc = sub[0][0] & 0xFC;
1634        let mut pos = 0usize;
1635        let push =
1636            |out: &mut [u8], pos: &mut usize, byte: u8| -> Result<(), &'static str> {
1637                if *pos >= out.len() {
1638                    return Err("Output buffer too small");
1639                }
1640                out[*pos] = byte;
1641                *pos += 1;
1642                Ok(())
1643            };
1644        // Use explicit per-frame lengths (code 2 / code 3 VBR) for every
1645        // sub-frame packet: it is always valid, and avoids the code-3 CBR
1646        // assumption that all frames are byte-identical in size.
1647        if nb == 2 {
1648            push(output, &mut pos, toc | 0x02)?;
1649            put_size(output, &mut pos, lens[0])?;
1650        } else {
1651            push(output, &mut pos, toc | 0x03)?;
1652            push(output, &mut pos, nb as u8 | 0x80)?;
1653            for i in 0..nb - 1 {
1654                put_size(output, &mut pos, lens[i])?;
1655            }
1656        }
1657
1658        for i in 0..nb {
1659            let payload = &sub[i][1..1 + lens[i]];
1660            if pos + payload.len() > output.len() {
1661                return Err("Output buffer too small");
1662            }
1663            output[pos..pos + payload.len()].copy_from_slice(payload);
1664            pos += payload.len();
1665        }
1666        self.prev_enc_mode = Some(mode);
1667        Ok(pos)
1668    }
1669}
1670
1671pub struct OpusDecoder {
1672    #[cfg(not(feature = "heap"))]
1673    celt_dec: CeltDecoder,
1674    #[cfg(feature = "heap")]
1675    celt_dec: Box<CeltDecoder>,
1676    #[cfg(not(feature = "heap"))]
1677    silk_dec: silk::dec_api::SilkDecoder,
1678    #[cfg(feature = "heap")]
1679    silk_dec: Box<silk::dec_api::SilkDecoder>,
1680    sampling_rate: i32,
1681    channels: usize,
1682
1683    prev_mode: Option<OpusMode>,
1684
1685    /// Whether the previous frame had redundancy (mode transition marker).
1686    prev_redundancy: bool,
1687    frame_size: usize,
1688
1689    bandwidth: Bandwidth,
1690
1691    stream_channels: usize,
1692
1693    silk_resampler: silk::resampler::SilkResampler,
1694
1695    /// Second resampler instance for stereo channel 1.
1696    silk_resampler_2: silk::resampler::SilkResampler,
1697
1698    prev_internal_rate: i32,
1699
1700    pub hybrid_skip_celt: bool,
1701
1702    #[cfg(not(feature = "heap"))]
1703    w_pcm_i16: FixedVec<i16, OPUS_PCM_I16>,
1704    #[cfg(feature = "heap")]
1705    w_pcm_i16: Box<FixedVec<i16, OPUS_PCM_I16>>,
1706    #[cfg(not(feature = "heap"))]
1707    w_silk_out: FixedVec<f32, OPUS_SUBFRAME_SCRATCH>,
1708    #[cfg(feature = "heap")]
1709    w_silk_out: Box<FixedVec<f32, OPUS_SUBFRAME_SCRATCH>>,
1710    #[cfg(not(feature = "heap"))]
1711    w_pcm_resampled: FixedVec<i16, OPUS_SUBFRAME_SCRATCH>,
1712    #[cfg(feature = "heap")]
1713    w_pcm_resampled: Box<FixedVec<i16, OPUS_SUBFRAME_SCRATCH>>,
1714    #[cfg(not(feature = "heap"))]
1715    w_celt_planar: FixedVec<f32, OPUS_SUBFRAME_SCRATCH>,
1716    #[cfg(feature = "heap")]
1717    w_celt_planar: Box<FixedVec<f32, OPUS_SUBFRAME_SCRATCH>>,
1718    #[cfg(not(feature = "heap"))]
1719    w_celt_out: FixedVec<f32, OPUS_SUBFRAME_SCRATCH>,
1720    #[cfg(feature = "heap")]
1721    w_celt_out: Box<FixedVec<f32, OPUS_SUBFRAME_SCRATCH>>,
1722
1723    /// Tail of the previous frame's output, used for smooth_fade at mode
1724    /// transitions (libopus pcm_transition + smooth_fade).
1725    #[cfg(not(feature = "heap"))]
1726    prev_pcm_tail: FixedVec<f32, OPUS_PCM_TAIL>,
1727    #[cfg(feature = "heap")]
1728    prev_pcm_tail: Box<FixedVec<f32, OPUS_PCM_TAIL>>,
1729}
1730
1731impl OpusDecoder {
1732    pub fn new(sampling_rate: i32, channels: usize) -> Result<Self, &'static str> {
1733        let mut decoder = MaybeUninit::uninit();
1734        Self::init(&mut decoder, sampling_rate, channels)?;
1735        Ok(unsafe { decoder.assume_init() })
1736    }
1737
1738    /// Builds a decoder in `slot` instead of returning it.
1739    ///
1740    /// Without the `heap` feature the decoder is about 175 KB, and `new` returns it
1741    /// through the stack. `init` writes it straight into memory you provide, such as
1742    /// a `static`, so it never has to fit on the stack.
1743    pub fn init(
1744        slot: &mut MaybeUninit<Self>,
1745        sampling_rate: i32,
1746        channels: usize,
1747    ) -> Result<&mut Self, &'static str> {
1748        if ![8000, 12000, 16000, 24000, 48000].contains(&sampling_rate) {
1749            return Err("Invalid sampling rate");
1750        }
1751        if ![1, 2].contains(&channels) {
1752            return Err("Invalid number of channels");
1753        }
1754
1755        let mode = modes::default_mode();
1756        let this = slot.as_mut_ptr();
1757        unsafe {
1758            #[cfg(feature = "heap")]
1759            {
1760                (&raw mut (*this).celt_dec).write(Box::new(CeltDecoder::new(mode, channels, sampling_rate)));
1761                (&raw mut (*this).silk_dec).write(Box::new(silk::dec_api::SilkDecoder::new()));
1762            }
1763            #[cfg(not(feature = "heap"))]
1764            {
1765                CeltDecoder::write_new(&raw mut (*this).celt_dec, mode, channels, sampling_rate);
1766                silk::dec_api::SilkDecoder::write_new(&raw mut (*this).silk_dec);
1767            }
1768            (&raw mut (*this).sampling_rate).write(sampling_rate);
1769            (&raw mut (*this).channels).write(channels);
1770            (&raw mut (*this).prev_mode).write(None);
1771            (&raw mut (*this).prev_redundancy).write(false);
1772            (&raw mut (*this).frame_size).write(0);
1773            (&raw mut (*this).bandwidth).write(Bandwidth::Auto);
1774            (&raw mut (*this).stream_channels).write(channels);
1775            (&raw mut (*this).silk_resampler).write(silk::resampler::SilkResampler::default());
1776            (&raw mut (*this).silk_resampler_2).write(silk::resampler::SilkResampler::default());
1777            (&raw mut (*this).prev_internal_rate).write(0);
1778            (&raw mut (*this).hybrid_skip_celt).write(false);
1779            #[cfg(feature = "heap")]
1780            {
1781                (&raw mut (*this).w_pcm_i16).write(Box::new(FixedVec::from_value(0i16, 960 * channels)));
1782                (&raw mut (*this).w_silk_out).write(Box::new(FixedVec::from_value(0.0f32, OPUS_MAX_SUBFRAME * channels)));
1783                (&raw mut (*this).w_pcm_resampled).write(Box::new(FixedVec::from_value(0i16, OPUS_MAX_SUBFRAME * channels)));
1784                (&raw mut (*this).w_celt_planar).write(Box::new(FixedVec::from_value(0.0f32, OPUS_MAX_SUBFRAME * channels)));
1785                (&raw mut (*this).w_celt_out).write(Box::new(FixedVec::from_value(0.0f32, OPUS_MAX_SUBFRAME * channels)));
1786                (&raw mut (*this).prev_pcm_tail).write(Box::new(FixedVec::from_value(0.0f32, 240 * channels)));
1787            }
1788            #[cfg(not(feature = "heap"))]
1789            {
1790                FixedVec::write_from_value(&raw mut (*this).w_pcm_i16, 0i16, 960 * channels);
1791                FixedVec::write_from_value(&raw mut (*this).w_silk_out, 0.0f32, OPUS_MAX_SUBFRAME * channels);
1792                FixedVec::write_from_value(&raw mut (*this).w_pcm_resampled, 0i16, OPUS_MAX_SUBFRAME * channels);
1793                FixedVec::write_from_value(&raw mut (*this).w_celt_planar, 0.0f32, OPUS_MAX_SUBFRAME * channels);
1794                FixedVec::write_from_value(&raw mut (*this).w_celt_out, 0.0f32, OPUS_MAX_SUBFRAME * channels);
1795                FixedVec::write_from_value(&raw mut (*this).prev_pcm_tail, 0.0f32, 240 * channels);
1796            }
1797        }
1798
1799        let decoder = unsafe { slot.assume_init_mut() };
1800        decoder.silk_dec.init(sampling_rate.min(16000), channels as i32);
1801        decoder.silk_dec.channel_state[0].fs_api_hz = sampling_rate;
1802        Ok(decoder)
1803    }
1804
1805    /// Decode one Opus packet into interleaved `f32` PCM, returning the
1806    /// number of samples per channel written.
1807    ///
1808    /// Packet loss follows libopus: pass an empty packet (`&[]`) to conceal
1809    /// `frame_size` samples (a multiple of 2.5 ms), or a ToC-only 1-byte
1810    /// packet to conceal the duration that ToC describes. Either way the
1811    /// frame is concealed in the previous packet's mode; the placeholder's
1812    /// mode, bandwidth and channel bits are ignored.
1813    ///
1814    /// A packet's channel count need not match the decoder's, as in libopus:
1815    /// a stereo decoder plays a mono packet on both channels, and a mono
1816    /// decoder downmixes a stereo packet (SILK plays the mid). libopus
1817    /// encoders send mono packets in stereo streams at low bitrates.
1818    pub fn decode(
1819        &mut self,
1820        input: &[u8],
1821        frame_size: usize,
1822        output: &mut [f32],
1823    ) -> Result<usize, &'static str> {
1824        // A packet of 0 or 1 bytes (ToC only) is a lost/DTX frame: conceal it
1825        // in the previous mode without touching the mode state (issue #15).
1826        if input.len() <= 1 {
1827            return self.decode_lost(input.first().copied(), frame_size, output);
1828        }
1829
1830        let toc = input[0];
1831        let mode = mode_from_toc(toc);
1832        let packet_channels = channels_from_toc(toc);
1833        let bandwidth = bandwidth_from_toc(toc);
1834        let frame_duration_ms = frame_duration_ms_from_toc(toc);
1835
1836        let code = toc & 0x03;
1837        let frame_count: usize;
1838        let frame_payloads: FixedVec<&[u8], OPUS_MAX_PACKET_FRAMES>;
1839
1840        match code {
1841            0 => {
1842                frame_count = 1;
1843                frame_payloads = FixedVec::from_slice(&[&input[1..]]);
1844            }
1845            1 => {
1846                frame_count = 2;
1847                let data_len = input.len() - 1;
1848                // RFC 6716 §3.2.1: code 1 carries two equal-size (CBR) frames,
1849                // so the payload length must be even. libopus rejects odd lengths.
1850                if !data_len.is_multiple_of(2) {
1851                    return Err("Code 1: payload length must be even");
1852                }
1853                let half = data_len / 2;
1854                if half == 0 {
1855                    return Err("Code 1: empty frame");
1856                }
1857                frame_payloads = FixedVec::from_slice(&[&input[1..1 + half], &input[1 + half..]]);
1858            }
1859            2 => {
1860                frame_count = 2;
1861                let data = &input[1..];
1862                if data.is_empty() {
1863                    return Err("Code 2 packet has no data");
1864                }
1865                let (first_len, header_size) = parse_frame_size(data)?;
1866                if header_size + first_len > data.len() {
1867                    return Err("Code 2: first frame size exceeds packet");
1868                }
1869                frame_payloads = FixedVec::from_slice(&[
1870                    &data[header_size..header_size + first_len],
1871                    &data[header_size + first_len..],
1872                ]);
1873            }
1874            3 => {
1875                if input.len() < 2 {
1876                    return Err("Code 3 packet too short");
1877                }
1878                let count_byte = input[1];
1879                let n_frames = (count_byte & 0x3F) as usize;
1880                if !(1..=48).contains(&n_frames) {
1881                    return Err("Code 3: invalid frame count");
1882                }
1883                frame_count = n_frames;
1884                // Bit 6 = padding flag, bit 7 = VBR flag (RFC 6716 §3.2.1).
1885                let padding_flag = (count_byte & 0x40) != 0;
1886                let vbr = (count_byte & 0x80) != 0;
1887
1888                // Parse the optional padding length bytes that follow the count
1889                // byte. The padding *content* (pad_len bytes) lives at the end of
1890                // the packet and is not part of any frame.
1891                let mut ptr = 2usize;
1892                let mut pad_len = 0usize;
1893                if padding_flag {
1894                    loop {
1895                        if ptr >= input.len() {
1896                            return Err("Code 3: padding overflow");
1897                        }
1898                        let p = input[ptr] as usize;
1899                        ptr += 1;
1900                        if p == 255 {
1901                            pad_len += 254;
1902                        } else {
1903                            pad_len += p;
1904                            break;
1905                        }
1906                    }
1907                }
1908                if ptr + pad_len > input.len() {
1909                    return Err("Code 3: padding exceeds packet");
1910                }
1911                let payload_end = input.len() - pad_len;
1912                let payload = &input[ptr..payload_end];
1913
1914                let mut payloads: FixedVec<&[u8], OPUS_MAX_PACKET_FRAMES> = FixedVec::new();
1915                if frame_count == 1 {
1916                    // Single frame: the entire payload region is the frame, both
1917                    // for VBR and CBR (no length prefix is present).
1918                    payloads.push(payload);
1919                } else if vbr {
1920                    // VBR (V=1): the per-frame length fields for all frames
1921                    // except the last come first, then all frame payloads
1922                    // (RFC 6716 §3.2.1; libopus `opus_packet_parse_impl`).
1923                    // Interleaving length and data — as this used to — parsed
1924                    // any libopus code-3 VBR packet wrong.
1925                    let mut cursor = 0usize;
1926                    let mut frame_bytes = 0usize;
1927                    let mut sizes = [0usize; OPUS_MAX_PACKET_FRAMES];
1928                    for i in 0..frame_count - 1 {
1929                        if cursor >= payload.len() {
1930                            return Err("Code 3: unexpected end in VBR header");
1931                        }
1932                        let (frame_len, header_bytes) = parse_frame_size(&payload[cursor..])?;
1933                        cursor += header_bytes;
1934                        sizes[i] = frame_len;
1935                        frame_bytes += frame_len;
1936                    }
1937                    if frame_bytes > payload.len() - cursor {
1938                        return Err("Code 3: frame length exceeds packet");
1939                    }
1940                    sizes[frame_count - 1] = payload.len() - cursor - frame_bytes;
1941                    let mut data_pos = cursor;
1942                    for i in 0..frame_count {
1943                        payloads.push(&payload[data_pos..data_pos + sizes[i]]);
1944                        data_pos += sizes[i];
1945                    }
1946                } else {
1947                    // CBR (V=0): remaining bytes are split equally into M frames
1948                    // (RFC 6716 §3.2.1: "the remaining bytes are split into M
1949                    // equal chunks").
1950                    if !payload.len().is_multiple_of(frame_count) {
1951                        return Err("Code 3 CBR: payload not divisible by frame count");
1952                    }
1953                    let frame_len = payload.len() / frame_count;
1954                    for i in 0..frame_count {
1955                        payloads.push(&payload[i * frame_len..(i + 1) * frame_len]);
1956                    }
1957                }
1958                frame_payloads = payloads;
1959            }
1960            _ => unreachable!(),
1961        }
1962
1963        self.frame_size = frame_size;
1964        self.bandwidth = bandwidth;
1965
1966        // Derive the actual per-frame sample count from the TOC, not from the
1967        // caller's frame_size. This prevents panics in bands.rs/celt.rs when
1968        // the caller passes a mismatched frame_size (issue #7 sub-item 1):
1969        // the internal decoders always get the correct geometry.
1970        let toc_frame_size = frame_samples_from_toc(toc, self.sampling_rate)
1971            .ok_or("Invalid TOC for sampling rate")?;
1972        let decoded_total = toc_frame_size * frame_count;
1973        if frame_size < decoded_total {
1974            return Err("frame_size too small for packet");
1975        }
1976        if output.len() < decoded_total * self.channels {
1977            return Err("Output buffer too small for packet");
1978        }
1979        // The packet's channel count steers both layers from here on.
1980        self.stream_channels = packet_channels;
1981        self.celt_dec.set_stream_channels(packet_channels);
1982        // Zero-fill any extra space the caller provided beyond what the packet
1983        // actually produces, so stale data is never left in the buffer.
1984        if output.len() > decoded_total * self.channels {
1985            for v in &mut output[decoded_total * self.channels..] {
1986                *v = 0.0;
1987            }
1988        }
1989        let sub_frame_size = toc_frame_size;
1990        let sub_output_len = sub_frame_size * self.channels;
1991
1992        // Detect mode transition and reset CELT decoder state to prevent
1993        // cross-mode artifacts (libopus opus_decoder.c:602-604).
1994        // This is the primary fix for issue #8/#9 alignment divergence:
1995        // stale CELT MDCT/prefilter state at SILK↔CELT boundaries causes
1996        // discontinuities that accumulate across transitions.
1997        let mode_transition = matches!(
1998            self.prev_mode,
1999            Some(prev) if prev != mode && !self.prev_redundancy
2000        );
2001        // The transitions libopus's opus_decode_frame bridges with 5 ms of the old mode concealed
2002        // forward: into CELT from SILK or Hybrid (unless that frame's redundancy did it), and out of CELT. A Hybrid
2003        // frame carrying redundancy clears it below (libopus: `if (redundancy) transition = 0`).
2004        let mut transition = match self.prev_mode {
2005            Some(prev) => {
2006                (mode == OpusMode::CeltOnly && prev != OpusMode::CeltOnly && !self.prev_redundancy)
2007                    || (mode != OpusMode::CeltOnly && prev == OpusMode::CeltOnly)
2008            }
2009            None => false,
2010        };
2011        let from_celt = self.prev_mode == Some(OpusMode::CeltOnly) && mode != OpusMode::CeltOnly;
2012        if from_celt {
2013            // SILK starts over, as libopus's silk_ResetDecoder on a switch from CELT (its resampler too: the reset
2014            // clears the SILK rate, so the next set_fs initialises it again).
2015            self.silk_dec.reset();
2016            self.prev_internal_rate = 0;
2017        }
2018        // A SILK-only frame decides its out-of-CELT bridge only after its SILK
2019        // layer runs: when the frame carries a redundancy tail there is nothing
2020        // to bridge (libopus: `if (redundancy) transition = 0`), and without one
2021        // the old CELT state conceals forward from inside the branch below.
2022        // Only a switch into CELT resets CELT here (libopus's reset at
2023        // opus_decode_frame 551-553 sits inside `mode != MODE_SILK_ONLY`): a
2024        // Hybrid frame resets it after its CELT-to-SILK redundant frame is
2025        // decoded from the old state, and a SILK-only frame resets it right
2026        // before its own redundant frame, if it carries one.
2027        if mode_transition && mode == OpusMode::CeltOnly {
2028            self.celt_dec.reset_state();
2029        }
2030
2031        // Generate SILK PLC audio for the mode-transition bridge. libopus
2032        // synthesizes 5ms (F5) of pitch-extrapolated audio in the OLD mode
2033        // (opus_decoder.c:387-391) and crossfades it with the new frame. We
2034        // reuse the F5-sized prev_pcm_tail buffer for this bridge.
2035        let f5_bridge = self.sampling_rate as usize / 200; // F5 = Fs/200
2036        if transition
2037            && mode == OpusMode::CeltOnly
2038            && f5_bridge > 0
2039            && matches!(
2040                self.prev_mode,
2041                Some(OpusMode::SilkOnly) | Some(OpusMode::Hybrid)
2042            )
2043            && self.prev_internal_rate > 0
2044        {
2045            let internal_rate = self.prev_internal_rate;
2046            let plc_internal_len = (10 * internal_rate / 1000) as usize;
2047            let mut plc_rc = RangeCoder::new_decoder(&[]);
2048            let mut plc_i16: FixedVec<i16, OPUS_PCM_I16> =
2049                FixedVec::from_value(0i16, plc_internal_len * self.channels);
2050            let n = self.silk_dec.decode(
2051                &mut plc_rc,
2052                &mut plc_i16,
2053                silk::decode_frame::FLAG_PACKET_LOST,
2054                true,
2055                10,
2056                internal_rate,
2057            );
2058            if n > 0 {
2059                let bridge_ch = f5_bridge * self.channels;
2060                let bridge_len = bridge_ch.min(self.prev_pcm_tail.len());
2061                if internal_rate == self.sampling_rate {
2062                    // No resampling: copy PLC samples directly (planar). A
2063                    // mono stream fills both channels of a stereo decoder.
2064                    let n_us = n as usize;
2065                    let silk_channels = self
2066                        .channels
2067                        .min(self.silk_dec.n_channels_internal as usize);
2068                    for ch in 0..self.channels {
2069                        let src_base = ch.min(silk_channels - 1) * n_us;
2070                        for i in 0..(bridge_len / self.channels).min(n_us) {
2071                            let dst = i * self.channels + ch;
2072                            if dst < bridge_len {
2073                                self.prev_pcm_tail[dst] = plc_i16[src_base + i] as f32 / 32768.0;
2074                            }
2075                        }
2076                    }
2077                } else if self.silk_resampler.is_initialized() {
2078                    // Resample channel 0 to the API rate for the bridge.
2079                    // The resampler must receive an output buffer sized for the
2080                    // FULL resampled length (issue #15): truncating the buffer to
2081                    // `f5_bridge` caused out-of-bounds writes in the Up2HQ/Copy
2082                    // paths. We size it fully, then copy only the bridge window.
2083                    let ratio = self.sampling_rate as f64 / internal_rate as f64;
2084                    let full_len = ((n as f64 * ratio) as usize).max(1);
2085                    let out_len = full_len.min(f5_bridge);
2086                    let n_us = n as usize;
2087                    let mut resampled: FixedVec<i16, OPUS_MAX_FRAME> =
2088                        FixedVec::from_value(0i16, full_len);
2089                    self.silk_resampler
2090                        .process(&mut resampled, &plc_i16[..n_us], n);
2091                    for i in 0..out_len {
2092                        if i < bridge_len / self.channels {
2093                            for ch in 0..self.channels {
2094                                self.prev_pcm_tail[i * self.channels + ch] =
2095                                    resampled[i] as f32 / 32768.0;
2096                            }
2097                        }
2098                    }
2099                }
2100            }
2101        }
2102
2103        // Track whether this packet uses Hybrid redundancy.
2104        let mut has_redundancy = false;
2105
2106        match mode {
2107            OpusMode::SilkOnly => {
2108                let internal_sample_rate = match bandwidth {
2109                    Bandwidth::Narrowband => 8000,
2110                    Bandwidth::Mediumband => 12000,
2111                    Bandwidth::Wideband => 16000,
2112                    _ => 16000,
2113                };
2114                if internal_sample_rate != self.prev_internal_rate
2115                    || !self.silk_resampler.is_initialized()
2116                {
2117                    self.silk_resampler
2118                        .init(internal_sample_rate, self.sampling_rate);
2119                    self.silk_resampler_2
2120                        .init(internal_sample_rate, self.sampling_rate);
2121                }
2122                // Always track the SILK internal rate so the mode-transition
2123                // PLC bridge can be generated (even when no resampling is
2124                // needed, e.g. 16kHz decoder + SILK WB).
2125                self.prev_internal_rate = internal_sample_rate;
2126
2127                // 5 ms redundant frames and their crossfade window, shared by
2128                // every frame of the packet. The redundant frame ends where
2129                // the packet's bandwidth does (libopus 499-524): narrowband
2130                // 13, mediumband and wideband 17.
2131                let f5 = self.sampling_rate as usize / 200;
2132                let f2_5 = f5 / 2;
2133                let redundant_end_band = match bandwidth {
2134                    Bandwidth::Narrowband => 13,
2135                    Bandwidth::Mediumband | Bandwidth::Wideband => 17,
2136                    _ => 21,
2137                };
2138                let window = modes::default_mode().window;
2139                let inc = (48000 / self.sampling_rate) as usize;
2140
2141                for (fi, payload) in frame_payloads.iter().enumerate() {
2142                    let out_start = fi * sub_output_len;
2143                    // C opus_decode_frame: a zero-length frame (e.g. the
2144                    // encoder's busted-budget fallback packet) is concealed,
2145                    // not decoded.
2146                    let flag = if payload.is_empty() {
2147                        silk::decode_frame::FLAG_PACKET_LOST
2148                    } else {
2149                        silk::decode_frame::FLAG_DECODE_NORMAL
2150                    };
2151                    let mut rc = RangeCoder::new_decoder(payload);
2152                    self.decode_silk_frames(
2153                        &mut rc,
2154                        flag,
2155                        frame_duration_ms,
2156                        internal_sample_rate,
2157                        sub_frame_size,
2158                    )?;
2159                    output[out_start..out_start + sub_output_len]
2160                        .copy_from_slice(&self.w_silk_out[..sub_output_len]);
2161
2162                    // A SILK-only frame's redundancy is implicit (libopus
2163                    // opus_decode_frame 452-479): no flag is coded — when bits
2164                    // are left for it, redundancy is on, `celt_to_silk` reads
2165                    // as one bit, and the frame's tail past the SILK layer is
2166                    // a 5 ms redundant CELT frame. Going to CELT it decodes
2167                    // from a reset CELT state — which the next CELT frame
2168                    // carries on — and its second half fades into the frame's
2169                    // last 2.5 ms (595-603). Coming from CELT it decodes from
2170                    // the old state, opens the frame and fades into its first
2171                    // 5 ms (608-617). Only the SILK-to-CELT direction counts
2172                    // as `prev_redundancy` (657), which spares the next frame
2173                    // its transition bridge and CELT reset (346-352, 551-553).
2174                    let len = payload.len();
2175                    let mut redundancy_bytes = 0usize;
2176                    let mut celt_to_silk = false;
2177                    let mut redundant = [0.0f32; REDUNDANT_MAX];
2178                    let has_tail = !payload.is_empty() && rc.tell() + 17 <= (8 * len) as i32;
2179                    if has_tail {
2180                        celt_to_silk = rc.decode_bit_logp(1);
2181                        // The tail is everything the SILK layer left:
2182                        // `redundancy_bytes = len - ceil(ec_tell/8)`.
2183                        redundancy_bytes = len - ((rc.tell() as usize + 7) >> 3);
2184                        // Keep the main range coder in sync, as libopus's
2185                        // `dec.storage -= redundancy_bytes`.
2186                        rc.storage -= redundancy_bytes as u32;
2187                        if celt_to_silk {
2188                            // From the old CELT state, before any reset — a
2189                            // CELT-to-SILK switch's bridge (532-541).
2190                            self.decode_redundant(
2191                                &payload[len - redundancy_bytes..],
2192                                redundant_end_band,
2193                                f5,
2194                                &mut redundant,
2195                            );
2196                        }
2197                    }
2198                    if fi == 0 {
2199                        if has_tail {
2200                            // libopus: `if (redundancy) transition = 0`.
2201                            transition = false;
2202                        } else if from_celt {
2203                            // Without a tail there is nothing to fade into:
2204                            // bridge out of CELT with 5 ms of the old CELT
2205                            // state concealed forward (libopus's
2206                            // `pcm_transition`), not the previous frame's last
2207                            // 5 ms, which already played.
2208                            self.conceal_celt_bridge()?;
2209                        }
2210                    }
2211                    let ch = self.channels;
2212                    if has_tail && !celt_to_silk && f5 > 0 && sub_output_len >= f5 * ch {
2213                        // SILK to CELT: reset CELT, decode the redundant frame
2214                        // with all bands from that fresh state — the next CELT
2215                        // frame carries it on — and fade its second half into
2216                        // the frame's last 2.5 ms (595-603).
2217                        self.celt_dec.reset_state();
2218                        self.decode_redundant(
2219                            &payload[len - redundancy_bytes..],
2220                            redundant_end_band,
2221                            f5,
2222                            &mut redundant,
2223                        );
2224                        let at = out_start + (sub_frame_size - f2_5) * ch;
2225                        let mut tail = [0.0f32; REDUNDANT_MAX];
2226                        tail[..f2_5 * ch].copy_from_slice(&output[at..at + f2_5 * ch]);
2227                        smooth_fade(
2228                            &tail[..f2_5 * ch],
2229                            &redundant[f2_5 * ch..f5 * ch],
2230                            &mut output[at..at + f2_5 * ch],
2231                            f2_5,
2232                            ch,
2233                            window,
2234                            inc,
2235                        );
2236                    } else if has_tail
2237                        && celt_to_silk
2238                        && (self.prev_mode != Some(OpusMode::SilkOnly) || self.prev_redundancy)
2239                        && f5 > 0
2240                        && sub_output_len >= f5 * ch
2241                    {
2242                        // CELT to SILK: the redundant frame opens this one and
2243                        // fades into its first 5 ms (608-617). It is ignored
2244                        // when the CELT state it came from is stale (606-608).
2245                        output[out_start..out_start + f2_5 * ch]
2246                            .copy_from_slice(&redundant[..f2_5 * ch]);
2247                        let at = out_start + f2_5 * ch;
2248                        let mut next = [0.0f32; REDUNDANT_MAX];
2249                        next[..f2_5 * ch].copy_from_slice(&output[at..at + f2_5 * ch]);
2250                        smooth_fade(
2251                            &redundant[f2_5 * ch..f5 * ch],
2252                            &next[..f2_5 * ch],
2253                            &mut output[at..at + f2_5 * ch],
2254                            f2_5,
2255                            ch,
2256                            window,
2257                            inc,
2258                        );
2259                    }
2260                    has_redundancy = has_tail && !celt_to_silk;
2261                }
2262                decoded_total
2263            }
2264
2265            OpusMode::CeltOnly => {
2266                let celt_end_band = self.celt_end_band_from_toc(toc);
2267
2268                for (fi, payload) in frame_payloads.iter().enumerate() {
2269                    let mut rc = RangeCoder::new_decoder(payload);
2270                    let total_bits = (payload.len() * 8) as i32;
2271                    let needed = sub_frame_size * self.channels;
2272                    let out_start = fi * needed;
2273                    let out_end = (out_start + needed).min(output.len());
2274
2275                    if output.len() < out_end {
2276                        return Err("Output buffer too small");
2277                    }
2278
2279                    if self.channels == 1 {
2280                        self.celt_dec.decode_from_range_coder_with_band_range(
2281                            &mut rc,
2282                            total_bits,
2283                            sub_frame_size,
2284                            &mut output[out_start..out_end],
2285                            0,
2286                            celt_end_band,
2287                        );
2288                        for sample in &mut output[out_start..out_end] {
2289                            *sample = sample.clamp(-1.0, 1.0);
2290                        }
2291                    } else {
2292                        self.celt_dec.decode_from_range_coder_with_band_range(
2293                            &mut rc,
2294                            total_bits,
2295                            sub_frame_size,
2296                            &mut self.w_celt_planar[..needed],
2297                            0,
2298                            celt_end_band,
2299                        );
2300                        for i in 0..sub_frame_size {
2301                            for ch in 0..self.channels {
2302                                let idx = out_start + i * self.channels + ch;
2303                                output[idx] =
2304                                    self.w_celt_planar[ch * sub_frame_size + i].clamp(-1.0, 1.0);
2305                            }
2306                        }
2307                    }
2308                }
2309                decoded_total
2310            }
2311
2312            OpusMode::Hybrid => {
2313                let internal_sample_rate = 16000;
2314                let celt_end_band = self.celt_end_band_from_toc(toc);
2315
2316                if internal_sample_rate != self.prev_internal_rate
2317                    || !self.silk_resampler.is_initialized()
2318                {
2319                    self.silk_resampler
2320                        .init(internal_sample_rate, self.sampling_rate);
2321                    self.silk_resampler_2
2322                        .init(internal_sample_rate, self.sampling_rate);
2323                }
2324                self.prev_internal_rate = internal_sample_rate;
2325
2326                for (fi, payload) in frame_payloads.iter().enumerate() {
2327                    let mut rc = RangeCoder::new_decoder(payload);
2328                    // SILK layer of this frame -> w_silk_out (interleaved, API rate).
2329                    // A zero-length frame is concealed, not decoded (C
2330                    // opus_decode_frame).
2331                    let flag = if payload.is_empty() {
2332                        silk::decode_frame::FLAG_PACKET_LOST
2333                    } else {
2334                        silk::decode_frame::FLAG_DECODE_NORMAL
2335                    };
2336                    self.decode_silk_frames(
2337                        &mut rc,
2338                        flag,
2339                        frame_duration_ms,
2340                        internal_sample_rate,
2341                        sub_frame_size,
2342                    )?;
2343                    let silk_out_len = sub_frame_size * self.channels;
2344
2345                    // A mode switch's redundancy (RFC 6716 §4.5.1; libopus
2346                    // opus_decode_frame): after the SILK layer, when enough bits
2347                    // are left, a flag; if set, which way the switch goes and the
2348                    // size of a 5 ms CELT frame carried at the packet's end. The
2349                    // main CELT layer reads only the bytes before it; the
2350                    // redundant frame is decoded on its own and crossfaded in
2351                    // below.
2352                    let mut len = payload.len();
2353                    let mut redundancy = false;
2354                    let mut celt_to_silk = false;
2355                    let mut redundancy_bytes = 0usize;
2356                    if !payload.is_empty() && rc.tell() + 17 + 20 <= (8 * len) as i32 {
2357                        redundancy = rc.decode_bit_logp(12);
2358                        if redundancy {
2359                            celt_to_silk = rc.decode_bit_logp(1);
2360                            redundancy_bytes = rc.dec_uint(256) as usize + 2;
2361                            if redundancy_bytes > len
2362                                || ((len - redundancy_bytes) * 8) < rc.tell() as usize
2363                            {
2364                                len = 0;
2365                                redundancy_bytes = 0;
2366                                redundancy = false;
2367                            } else {
2368                                len -= redundancy_bytes;
2369                                rc.storage -= redundancy_bytes as u32;
2370                            }
2371                        }
2372                    }
2373                    // libopus remembers only a SILK-to-CELT frame's redundancy (`prev_redundancy = redundancy &&
2374                    // !celt_to_silk`).
2375                    has_redundancy = redundancy && !celt_to_silk;
2376                    let f5 = self.sampling_rate as usize / 200;
2377                    let f2_5 = f5 / 2;
2378                    let mut redundant = [0.0f32; REDUNDANT_MAX];
2379                    if fi == 0 {
2380                        if redundancy {
2381                            transition = false;
2382                        } else if from_celt {
2383                            self.conceal_celt_bridge()?;
2384                        }
2385                    }
2386                    if redundancy && celt_to_silk {
2387                        self.decode_redundant(
2388                            &payload[len..len + redundancy_bytes],
2389                            celt_end_band,
2390                            f5,
2391                            &mut redundant,
2392                        );
2393                    }
2394                    if fi == 0 && mode_transition {
2395                        self.celt_dec.reset_state();
2396                    }
2397
2398                    let total_bits = (len * 8) as i32;
2399                    if len <= 1 {
2400                        self.w_celt_out[..silk_out_len].fill(0.0);
2401                    } else {
2402                        let (celt_dec, celt_planar) = (
2403                            state_mut(&mut self.celt_dec),
2404                            state_mut(&mut self.w_celt_planar),
2405                        );
2406                        celt_dec.decode_from_range_coder_with_band_range(
2407                            &mut rc,
2408                            total_bits,
2409                            sub_frame_size,
2410                            &mut celt_planar[..silk_out_len],
2411                            17,
2412                            celt_end_band,
2413                        );
2414
2415                        if self.channels == 1 {
2416                            self.w_celt_out[..silk_out_len]
2417                                .copy_from_slice(&self.w_celt_planar[..silk_out_len]);
2418                        } else {
2419                            for i in 0..sub_frame_size {
2420                                for ch in 0..self.channels {
2421                                    self.w_celt_out[i * self.channels + ch] =
2422                                        self.w_celt_planar[ch * sub_frame_size + i];
2423                                }
2424                            }
2425                        }
2426                    }
2427
2428                    let out_start = fi * silk_out_len;
2429                    let total = silk_out_len.min(output.len() - out_start);
2430                    for j in 0..total {
2431                        output[out_start + j] =
2432                            (self.w_silk_out[j] + self.w_celt_out[j]).clamp(-1.0, 1.0);
2433                    }
2434
2435                    // The redundant frame crossfaded in (libopus
2436                    // opus_decode_frame): going SILK to CELT, the frame's last
2437                    // 2.5 ms fade into its second half (decoded from a fresh CELT
2438                    // state, which the next CELT frame then carries on from);
2439                    // going CELT to SILK, its first half opens the frame and its
2440                    // second half fades into the frame's next 2.5 ms.
2441                    let ch = self.channels;
2442                    let use_redundant = !celt_to_silk
2443                        || self.prev_mode != Some(OpusMode::SilkOnly)
2444                        || self.prev_redundancy;
2445                    if redundancy && use_redundant && f5 > 0 && total >= f5 * ch {
2446                        let window = modes::default_mode().window;
2447                        let inc = (48000 / self.sampling_rate) as usize;
2448                        if !celt_to_silk {
2449                            self.celt_dec.reset_state();
2450                            self.decode_redundant(
2451                                &payload[len..len + redundancy_bytes],
2452                                celt_end_band,
2453                                f5,
2454                                &mut redundant,
2455                            );
2456                            let at = out_start + (sub_frame_size - f2_5) * ch;
2457                            let mut tail = [0.0f32; REDUNDANT_MAX];
2458                            tail[..f2_5 * ch].copy_from_slice(&output[at..at + f2_5 * ch]);
2459                            smooth_fade(
2460                                &tail[..f2_5 * ch],
2461                                &redundant[f2_5 * ch..f5 * ch],
2462                                &mut output[at..at + f2_5 * ch],
2463                                f2_5,
2464                                ch,
2465                                window,
2466                                inc,
2467                            );
2468                        } else {
2469                            output[out_start..out_start + f2_5 * ch]
2470                                .copy_from_slice(&redundant[..f2_5 * ch]);
2471                            let at = out_start + f2_5 * ch;
2472                            let mut next = [0.0f32; REDUNDANT_MAX];
2473                            next[..f2_5 * ch].copy_from_slice(&output[at..at + f2_5 * ch]);
2474                            smooth_fade(
2475                                &redundant[f2_5 * ch..f5 * ch],
2476                                &next[..f2_5 * ch],
2477                                &mut output[at..at + f2_5 * ch],
2478                                f2_5,
2479                                ch,
2480                                window,
2481                                inc,
2482                            );
2483                        }
2484                    }
2485                }
2486                decoded_total
2487            }
2488        };
2489
2490        // Apply PLC-style bridging at mode transitions (libopus
2491        // opus_decoder.c:660-679). The first F2_5 of the output is replaced
2492        // with the previous frame's tail (PLC bridge), and the next F2_5 is
2493        // crossfaded between the bridge and the new frame's CELT output.
2494        // F5 = Fs/200, F2_5 = Fs/400.
2495        let f2_5 = self.sampling_rate as usize / 400;
2496        let f5 = f2_5 * 2;
2497        if transition && f5 > 0 && decoded_total >= f5 {
2498            let window = modes::default_mode().window;
2499            let inc = (48000 / self.sampling_rate) as usize;
2500            let f2_5_ch = f2_5 * self.channels;
2501            let f5_ch = f5 * self.channels;
2502            // First F2_5: pure bridging audio from previous frame's tail.
2503            output[..f2_5_ch].copy_from_slice(&self.prev_pcm_tail[..f2_5_ch]);
2504            // Next F2_5: crossfade bridge → new CELT output.
2505            let new_mid: FixedVec<f32, OPUS_PCM_TAIL> =
2506                FixedVec::from_slice(&output[f2_5_ch..f5_ch]);
2507            smooth_fade(
2508                &self.prev_pcm_tail[f2_5_ch..f5_ch],
2509                &new_mid,
2510                &mut output[f2_5_ch..f5_ch],
2511                f2_5,
2512                self.channels,
2513                window,
2514                inc,
2515            );
2516        }
2517
2518        self.save_pcm_tail(output, decoded_total);
2519        self.prev_mode = Some(mode);
2520        self.prev_redundancy = has_redundancy;
2521        Ok(decoded_total)
2522    }
2523
2524    /// Conceal a lost frame (libopus `opus_decode_frame` with `data == NULL`).
2525    ///
2526    /// Only the duration comes from the caller: the placeholder ToC of a
2527    /// 1-byte packet, or `frame_size` for an empty one. Mode, bandwidth, SILK
2528    /// rate and resamplers stay the previous packet's, so a placeholder such
2529    /// as `[0]` (SILK NB 10 ms) can no longer switch a CELT or wideband stream
2530    /// into another mode and fake two mode transitions (issue #15).
2531    fn decode_lost(
2532        &mut self,
2533        toc: Option<u8>,
2534        frame_size: usize,
2535        output: &mut [f32],
2536    ) -> Result<usize, &'static str> {
2537        let f2_5 = self.sampling_rate as usize / 400;
2538        let (f5, f10, f20) = (2 * f2_5, 4 * f2_5, 8 * f2_5);
2539
2540        let total = match toc {
2541            None => {
2542                if frame_size == 0 || !frame_size.is_multiple_of(f2_5) {
2543                    return Err("Lost frame: frame_size must be a multiple of 2.5 ms");
2544                }
2545                frame_size
2546            }
2547            Some(toc) => {
2548                // A ToC-only packet: code 0 is one empty frame, codes 1 and 2
2549                // are two; code 3 lacks its frame-count byte (libopus rejects).
2550                let count = match toc & 0x03 {
2551                    0 => 1,
2552                    1 | 2 => 2,
2553                    _ => return Err("Code 3 packet too short"),
2554                };
2555                let per_frame = frame_samples_from_toc(toc, self.sampling_rate)
2556                    .ok_or("Invalid TOC for sampling rate")?;
2557                let total = per_frame * count;
2558                if frame_size < total {
2559                    return Err("frame_size too small for packet");
2560                }
2561                total
2562            }
2563        };
2564        let channels = self.channels;
2565        if output.len() < total * channels {
2566            return Err("Output buffer too small for packet");
2567        }
2568        output[total * channels..].fill(0.0);
2569
2570        let mut pos = 0usize;
2571        while pos < total {
2572            let remaining = total - pos;
2573            let dst = &mut output[pos * channels..total * channels];
2574            let produced = match self.prev_mode {
2575                None => {
2576                    dst.fill(0.0);
2577                    remaining
2578                }
2579                Some(OpusMode::CeltOnly) => {
2580                    // Largest CELT frame that fits; `remaining` is a multiple
2581                    // of 2.5 ms, so one always does.
2582                    let n = [f20, f10, f5, f2_5]
2583                        .into_iter()
2584                        .find(|&c| c <= remaining)
2585                        .unwrap_or(f2_5);
2586                    self.conceal_celt(n, 0)?;
2587                    for (d, s) in dst.iter_mut().zip(&self.w_celt_out[..n * channels]) {
2588                        *d = s.clamp(-1.0, 1.0);
2589                    }
2590                    n
2591                }
2592                Some(prev @ (OpusMode::SilkOnly | OpusMode::Hybrid)) => {
2593                    // SILK conceals in 10/20 ms frames; a shorter request
2594                    // takes the head of a 10 ms frame, as libopus does.
2595                    let (ms, n) = if remaining >= f20 {
2596                        (20, f20)
2597                    } else {
2598                        (10, f10)
2599                    };
2600                    let hybrid = prev == OpusMode::Hybrid;
2601                    let rate = if hybrid {
2602                        16000
2603                    } else {
2604                        self.prev_internal_rate
2605                    };
2606                    let mut rc = RangeCoder::new_decoder(&[]);
2607                    self.decode_silk_frames(
2608                        &mut rc,
2609                        silk::decode_frame::FLAG_PACKET_LOST,
2610                        ms,
2611                        rate,
2612                        n,
2613                    )?;
2614                    if hybrid {
2615                        self.conceal_celt(n, 17)?;
2616                    } else {
2617                        self.w_celt_out[..n * channels].fill(0.0);
2618                    }
2619                    let take = n.min(remaining);
2620                    for (i, d) in dst[..take * channels].iter_mut().enumerate() {
2621                        *d = (self.w_silk_out[i] + self.w_celt_out[i]).clamp(-1.0, 1.0);
2622                    }
2623                    take
2624                }
2625            };
2626            pos += produced;
2627        }
2628
2629        self.save_pcm_tail(output, total);
2630        Ok(total)
2631    }
2632
2633    /// 5 ms of the old CELT state concealed forward, the bridge out of CELT (libopus
2634    /// opus_decode_frame's `pcm_transition`), into `prev_pcm_tail`.
2635    fn conceal_celt_bridge(&mut self) -> Result<(), &'static str> {
2636        let f5 = self.sampling_rate as usize / 200;
2637        if f5 == 0 {
2638            return Ok(());
2639        }
2640        self.conceal_celt(f5, 0)?;
2641        let n = (f5 * self.channels).min(self.prev_pcm_tail.len());
2642        for i in 0..n {
2643            self.prev_pcm_tail[i] = self.w_celt_out[i].clamp(-1.0, 1.0);
2644        }
2645        Ok(())
2646    }
2647
2648    /// CELT concealment for `n` samples: decode an empty payload, which takes
2649    /// the silence path — the previous frame's MDCT overlap decays into
2650    /// silence with the decoder state kept continuous. Output lands
2651    /// interleaved in `w_celt_out[..n * channels]`.
2652    fn conceal_celt(&mut self, n: usize, start_band: usize) -> Result<(), &'static str> {
2653        let len = n * self.channels;
2654        let end_band = modes::default_mode().eff_ebands;
2655        let mut rc = RangeCoder::new_decoder(&[]);
2656        let decoded = {
2657            let (celt_dec, planar) = (
2658                state_mut(&mut self.celt_dec),
2659                state_mut(&mut self.w_celt_planar),
2660            );
2661            celt_dec.decode_from_range_coder_with_band_range(
2662                &mut rc,
2663                0,
2664                n,
2665                &mut planar[..len],
2666                start_band,
2667                end_band,
2668            )
2669        };
2670        if decoded != n {
2671            return Err("CELT concealment failed");
2672        }
2673        for i in 0..n {
2674            for ch in 0..self.channels {
2675                self.w_celt_out[i * self.channels + ch] = self.w_celt_planar[ch * n + i];
2676            }
2677        }
2678        Ok(())
2679    }
2680
2681    /// Keep the last F5 of output for the next mode-transition bridge.
2682    fn save_pcm_tail(&mut self, output: &[f32], decoded: usize) {
2683        let tail_len = (self.sampling_rate as usize / 200) * self.channels;
2684        let out_total = decoded * self.channels;
2685        if out_total >= tail_len && tail_len <= self.prev_pcm_tail.len() {
2686            self.prev_pcm_tail[..tail_len]
2687                .copy_from_slice(&output[out_total - tail_len..out_total]);
2688        }
2689    }
2690}
2691
2692impl OpusDecoder {
2693    /// Decode one Opus frame of SILK audio — `duration_ms` at `internal_rate`
2694    /// — into `w_silk_out[..api_len * channels]`, interleaved at the API rate.
2695    ///
2696    /// A 40/60 ms payload carries 2/3 internal SILK frames in one range-coded
2697    /// stream; libopus loops silk_Decode until the full duration is produced,
2698    /// with new_packet set only on the first call (issue #27). With
2699    /// `FLAG_PACKET_LOST` the same loop drives SILK packet-loss concealment.
2700    fn decode_silk_frames(
2701        &mut self,
2702        rc: &mut RangeCoder,
2703        lost_flag: i32,
2704        duration_ms: i32,
2705        internal_rate: i32,
2706        api_len: usize,
2707    ) -> Result<(), &'static str> {
2708        let internal_frame_size = (duration_ms * internal_rate / 1000) as usize;
2709        let pcm_i16_len = internal_frame_size * self.channels;
2710        debug_assert!(pcm_i16_len <= self.w_pcm_i16.len());
2711        let out_len = api_len * self.channels;
2712        self.w_silk_out[..out_len].fill(0.0);
2713
2714        let mut frame_pos = 0usize; // internal-rate samples decoded so far
2715        let mut new_packet = true;
2716        while frame_pos < internal_frame_size {
2717            // libopus takes the channel count from the packet only when there
2718            // is data; concealment keeps the last frame's.
2719            let prev_channels = self.silk_dec.n_channels_internal as usize;
2720            let stream_channels = if lost_flag == silk::decode_frame::FLAG_PACKET_LOST {
2721                prev_channels
2722            } else {
2723                self.stream_channels
2724            };
2725            // libopus keeps a resampler per SILK channel (dec_API.c). When a
2726            // stereo decoder's stream turns stereo, the right one starts from
2727            // the left's. The first mono frame after stereo, at an unchanged
2728            // rate, still resamples the right channel with its own; libopus
2729            // resets SILK after CELT, so that frame never follows CELT there.
2730            if self.channels == 2 && stream_channels == 2 && prev_channels == 1 {
2731                self.silk_resampler_2 = self.silk_resampler.clone();
2732            }
2733            let stereo_to_mono = stream_channels == 1
2734                && prev_channels == 2
2735                && internal_rate == self.silk_dec.sample_rate()
2736                && matches!(
2737                    self.prev_mode,
2738                    Some(OpusMode::SilkOnly) | Some(OpusMode::Hybrid)
2739                );
2740            let ret = {
2741                let (silk_dec, pcm_i16) = (
2742                    state_mut(&mut self.silk_dec),
2743                    state_mut(&mut self.w_pcm_i16),
2744                );
2745                silk_dec.decode_with_channels(
2746                    rc,
2747                    &mut pcm_i16[..pcm_i16_len],
2748                    lost_flag,
2749                    new_packet,
2750                    duration_ms,
2751                    internal_rate,
2752                    stream_channels as i32,
2753                )
2754            };
2755            new_packet = false;
2756
2757            if ret < 0 {
2758                return Err("SILK decoding failed");
2759            }
2760            let decoded_samples = ret as usize;
2761            if decoded_samples == 0 {
2762                break;
2763            }
2764
2765            // SILK decoder outputs planar for THIS frame:
2766            // ch0 at [0..fl], ch1 at [fl..2*fl].
2767            //
2768            // libopus always runs `silk_resampler`, even when the API and SILK
2769            // internal rates are equal: the Copy path still carries the
2770            // resampler's `inputDelay` (12 samples at 16k, 4 at 8k). Bypassing
2771            // it made equal-rate SILK output early versus libopus (and versus
2772            // opus-rs's own resampled paths).
2773            let resampled_len =
2774                decoded_samples * self.sampling_rate as usize / internal_rate as usize;
2775            let api_pos = frame_pos * self.sampling_rate as usize / internal_rate as usize;
2776            let copy_len = resampled_len.min(api_len - api_pos);
2777            debug_assert!(resampled_len * self.channels <= self.w_pcm_resampled.len());
2778            // Resample channel 0.
2779            {
2780                let (res, inp, out) = (
2781                    &mut self.silk_resampler,
2782                    state_ref(&self.w_pcm_i16),
2783                    state_mut(&mut self.w_pcm_resampled),
2784                );
2785                res.process(
2786                    &mut out[..resampled_len],
2787                    &inp[..decoded_samples],
2788                    decoded_samples as i32,
2789                );
2790            }
2791            // Channel 1 (stereo output only). A mono stream is copied from
2792            // channel 0, except on the frame that collapses stereo to mono.
2793            if self.channels == 2 {
2794                let (res, inp, out) = (
2795                    &mut self.silk_resampler_2,
2796                    state_ref(&self.w_pcm_i16),
2797                    state_mut(&mut self.w_pcm_resampled),
2798                );
2799                if stream_channels == 2 || stereo_to_mono {
2800                    let src = if stream_channels == 2 {
2801                        &inp[decoded_samples..2 * decoded_samples]
2802                    } else {
2803                        &inp[..decoded_samples]
2804                    };
2805                    res.process(
2806                        &mut out[resampled_len..2 * resampled_len],
2807                        src,
2808                        decoded_samples as i32,
2809                    );
2810                } else {
2811                    out.copy_within(..resampled_len, resampled_len);
2812                }
2813            }
2814            for i in 0..copy_len {
2815                for ch in 0..self.channels {
2816                    let v = self.w_pcm_resampled[ch * resampled_len + i] as f32 / 32768.0;
2817                    let idx = (api_pos + i) * self.channels + ch;
2818                    if idx < out_len {
2819                        self.w_silk_out[idx] = v;
2820                    }
2821                }
2822            }
2823            frame_pos += decoded_samples;
2824        }
2825        Ok(())
2826    }
2827
2828    #[inline(always)]
2829    fn celt_end_band_from_toc(&self, toc: u8) -> usize {
2830        let mode = modes::default_mode();
2831        let top = mode.eff_ebands;
2832        if mode_from_toc(toc) == OpusMode::CeltOnly && toc >= 0x80 {
2833            const FROM_OPUS_TABLE: [u8; 16] = [
2834                0x80, 0x88, 0x90, 0x98, 0x40, 0x48, 0x50, 0x58, 0x20, 0x28, 0x30, 0x38, 0x00, 0x08,
2835                0x10, 0x18,
2836            ];
2837            let idx = ((toc >> 3) - 16) as usize;
2838            let data0 = FROM_OPUS_TABLE[idx] | (toc & 0x7);
2839            let trim = (data0 >> 5) as usize;
2840            return top.saturating_sub(2 * trim).max(1);
2841        }
2842        // A Hybrid frame's CELT layer ends where its bandwidth does (libopus opus_decode_frame:
2843        // super-wideband 19 bands, fullband 21); it decoded to 21 at every bandwidth.
2844        if mode_from_toc(toc) == OpusMode::Hybrid && (toc >> 3) < 14 {
2845            return top.saturating_sub(2).max(1);
2846        }
2847        top
2848    }
2849}
2850
2851fn frame_rate_from_params(sampling_rate: i32, frame_size: usize) -> Option<i32> {
2852    let frame_size = frame_size as i32;
2853    if frame_size <= 0 {
2854        return None;
2855    }
2856    // libopus `frame_size_select`: a frame is 2.5, 5, 10, 20, 40 or 60 ms.
2857    // Validating by duration (not by `sampling_rate % frame_size == 0`) is what
2858    // lets 60 ms through: its rate is 16.67 frames/s, so the old divisibility
2859    // test rejected it before encoding could start (issue #35). Longer frames
2860    // (>60 ms) are handled by the caller's multiframe path.
2861    let fs = sampling_rate;
2862    let valid = frame_size == fs / 400
2863        || frame_size == fs / 200
2864        || frame_size == fs / 100
2865        || frame_size == fs / 50
2866        || frame_size == fs / 25
2867        || 50 * frame_size == 3 * fs;
2868    if !valid {
2869        return None;
2870    }
2871    Some(fs / frame_size)
2872}
2873
2874fn gen_toc(mode: OpusMode, frame_rate: i32, bandwidth: Bandwidth, channels: usize) -> u8 {
2875    let mut rate = frame_rate;
2876    let mut period = 0;
2877    while rate < 400 {
2878        rate <<= 1;
2879        period += 1;
2880    }
2881
2882    let mut toc = match mode {
2883        OpusMode::SilkOnly => {
2884            let bw = (bandwidth as i32 - Bandwidth::Narrowband as i32) << 5;
2885            let per = (period - 2) << 3;
2886            (bw | per) as u8
2887        }
2888        OpusMode::CeltOnly => {
2889            let mut tmp = bandwidth as i32 - Bandwidth::Mediumband as i32;
2890            if tmp < 0 {
2891                tmp = 0;
2892            }
2893            let per = period << 3;
2894            (0x80 | (tmp << 5) | per) as u8
2895        }
2896        OpusMode::Hybrid => {
2897            let base_config = if bandwidth == Bandwidth::Superwideband {
2898                12
2899            } else {
2900                14
2901            };
2902            let period_offset = if frame_rate >= 100 { 0 } else { 1 };
2903            ((base_config + period_offset) << 3) as u8
2904        }
2905    };
2906
2907    if channels == 2 {
2908        toc |= 0x04;
2909    }
2910    toc
2911}
2912
2913fn mode_from_toc(toc: u8) -> OpusMode {
2914    if toc & 0x80 != 0 {
2915        OpusMode::CeltOnly
2916    } else if toc & 0x60 == 0x60 {
2917        OpusMode::Hybrid
2918    } else {
2919        OpusMode::SilkOnly
2920    }
2921}
2922
2923fn bandwidth_from_toc(toc: u8) -> Bandwidth {
2924    let mode = mode_from_toc(toc);
2925    match mode {
2926        OpusMode::SilkOnly => {
2927            let bw_bits = (toc >> 5) & 0x03;
2928            match bw_bits {
2929                0 => Bandwidth::Narrowband,
2930                1 => Bandwidth::Mediumband,
2931                2 => Bandwidth::Wideband,
2932                _ => Bandwidth::Wideband,
2933            }
2934        }
2935        OpusMode::Hybrid => {
2936            let bw_bit = (toc >> 4) & 0x01;
2937            if bw_bit == 0 {
2938                Bandwidth::Superwideband
2939            } else {
2940                Bandwidth::Fullband
2941            }
2942        }
2943        OpusMode::CeltOnly => {
2944            let bw_bits = (toc >> 5) & 0x03;
2945            match bw_bits {
2946                0 => Bandwidth::Mediumband,
2947                1 => Bandwidth::Wideband,
2948                2 => Bandwidth::Superwideband,
2949                3 => Bandwidth::Fullband,
2950                _ => Bandwidth::Fullband,
2951            }
2952        }
2953    }
2954}
2955
2956fn frame_duration_ms_from_toc(toc: u8) -> i32 {
2957    let mode = mode_from_toc(toc);
2958    match mode {
2959        OpusMode::SilkOnly => {
2960            let config = (toc >> 3) & 0x03;
2961            match config {
2962                0 => 10,
2963                1 => 20,
2964                2 => 40,
2965                3 => 60,
2966                _ => 20,
2967            }
2968        }
2969        OpusMode::Hybrid => {
2970            let config = (toc >> 3) & 0x01;
2971            if config == 0 { 10 } else { 20 }
2972        }
2973        OpusMode::CeltOnly => {
2974            let config = (toc >> 3) & 0x03;
2975            match config {
2976                0 => 2,
2977                1 => 5,
2978                2 => 10,
2979                3 => 20,
2980                _ => 20,
2981            }
2982        }
2983    }
2984}
2985
2986/// Compute the per-frame sample count implied by the TOC byte at a given
2987/// sampling rate. For CELT this uses the frame-rate derivation (which handles
2988/// the 2.5 ms case correctly, unlike integer millisecond arithmetic).
2989fn frame_samples_from_toc(toc: u8, sampling_rate: i32) -> Option<usize> {
2990    let mode = mode_from_toc(toc);
2991    match mode {
2992        OpusMode::CeltOnly => {
2993            let period = ((toc >> 3) & 0x03) as i32;
2994            let frame_rate = 400 >> period;
2995            if frame_rate == 0 || sampling_rate % frame_rate != 0 {
2996                return None;
2997            }
2998            Some((sampling_rate / frame_rate) as usize)
2999        }
3000        OpusMode::SilkOnly | OpusMode::Hybrid => {
3001            let duration_ms = frame_duration_ms_from_toc(toc);
3002            Some((sampling_rate as i64 * duration_ms as i64 / 1000) as usize)
3003        }
3004    }
3005}
3006
3007fn channels_from_toc(toc: u8) -> usize {
3008    if toc & 0x04 != 0 { 2 } else { 1 }
3009}
3010
3011/// A 5 ms redundant CELT frame at 48 kHz in stereo, the most it holds.
3012const REDUNDANT_MAX: usize = 480;
3013
3014impl OpusDecoder {
3015    /// A mode switch's 5 ms redundant CELT frame (libopus opus_decode_frame:
3016    /// all bands from 0, its own range coder over its bytes), interleaved into
3017    /// `out`.
3018    fn decode_redundant(&mut self, data: &[u8], end_band: usize, f5: usize, out: &mut [f32]) {
3019        let n = f5 * self.channels;
3020        if data.is_empty() || n > REDUNDANT_MAX {
3021            return;
3022        }
3023        let mut planar = [0.0f32; REDUNDANT_MAX];
3024        let mut rrc = RangeCoder::new_decoder(data);
3025        self.celt_dec.decode_from_range_coder_with_band_range(
3026            &mut rrc,
3027            (data.len() * 8) as i32,
3028            f5,
3029            &mut planar[..n],
3030            0,
3031            end_band,
3032        );
3033        if self.channels == 1 {
3034            out[..n].copy_from_slice(&planar[..n]);
3035        } else {
3036            for i in 0..f5 {
3037                for c in 0..self.channels {
3038                    out[i * self.channels + c] = planar[c * f5 + i];
3039                }
3040            }
3041        }
3042    }
3043}
3044
3045/// Crossfade two signals using a squared-sine window (libopus smooth_fade).
3046/// `window` is the 120-sample CELT window at 48 kHz; `inc` = 48000/Fs strides it.
3047fn smooth_fade(
3048    in1: &[f32],
3049    in2: &[f32],
3050    out: &mut [f32],
3051    overlap: usize,
3052    channels: usize,
3053    window: &[f32],
3054    inc: usize,
3055) {
3056    for c in 0..channels {
3057        for i in 0..overlap {
3058            let wi = i * inc;
3059            if wi >= window.len() {
3060                break;
3061            }
3062            let w = window[wi] * window[wi];
3063            out[i * channels + c] = w * in2[i * channels + c] + (1.0 - w) * in1[i * channels + c];
3064        }
3065    }
3066}
3067
3068/// Parse an Opus frame length per RFC 6716 §3.2.1, identical to libopus
3069/// `parse_size()`:
3070///   - `0`: no frame (DTX / lost packet)
3071///   - `1..=251`: length of the frame in bytes (one byte consumed)
3072///   - `252..=255`: a second byte is read; length = `second*4 + first`
3073///
3074/// Returns `(length, bytes_consumed)`.
3075pub(crate) fn parse_frame_size(data: &[u8]) -> Result<(usize, usize), &'static str> {
3076    let first = *data.first().ok_or("truncated frame length")? as usize;
3077    if first < 252 {
3078        Ok((first, 1))
3079    } else {
3080        let second = *data.get(1).ok_or("truncated frame length")? as usize;
3081        Ok((second * 4 + first, 2))
3082    }
3083}
3084
3085#[cfg(all(test, feature = "std"))]
3086mod tests {
3087    use super::*;
3088
3089    fn frame_size_from_toc(toc: u8, sampling_rate: i32) -> Option<usize> {
3090        let mode = mode_from_toc(toc);
3091        match mode {
3092            OpusMode::CeltOnly => {
3093                let period = ((toc >> 3) & 0x03) as i32;
3094                let frame_rate = 400 >> period;
3095                if frame_rate == 0 || sampling_rate % frame_rate != 0 {
3096                    return None;
3097                }
3098                Some((sampling_rate / frame_rate) as usize)
3099            }
3100            OpusMode::SilkOnly => {
3101                let duration_ms = frame_duration_ms_from_toc(toc);
3102                Some((sampling_rate as i64 * duration_ms as i64 / 1000) as usize)
3103            }
3104            OpusMode::Hybrid => {
3105                let duration_ms = frame_duration_ms_from_toc(toc);
3106                Some((sampling_rate as i64 * duration_ms as i64 / 1000) as usize)
3107            }
3108        }
3109    }
3110
3111    #[test]
3112    fn gen_toc_matches_celt_reference_values() {
3113        let sampling_rate = 48_000;
3114        let cases = [
3115            (120usize, 0xE0u8),
3116            (240usize, 0xE8u8),
3117            (480usize, 0xF0u8),
3118            (960usize, 0xF8u8),
3119        ];
3120
3121        for (frame_size, expected_toc) in cases {
3122            let frame_rate = frame_rate_from_params(sampling_rate, frame_size).unwrap();
3123            let toc = gen_toc(OpusMode::CeltOnly, frame_rate, Bandwidth::Fullband, 1);
3124            assert_eq!(
3125                toc, expected_toc,
3126                "frame_size {} expected TOC {:02X} got {:02X}",
3127                frame_size, expected_toc, toc
3128            );
3129            let decoded_size = frame_size_from_toc(toc, sampling_rate).unwrap();
3130            assert_eq!(decoded_size, frame_size);
3131        }
3132
3133        let stereo_toc = gen_toc(
3134            OpusMode::CeltOnly,
3135            frame_rate_from_params(sampling_rate, 960).unwrap(),
3136            Bandwidth::Fullband,
3137            2,
3138        );
3139        assert_eq!(channels_from_toc(stereo_toc), 2);
3140    }
3141
3142    #[test]
3143    fn test_celt_decoder_large_frame_sizes() {
3144        let sampling_rate = 48000;
3145        let channels = 1;
3146
3147        let mut decoder = OpusDecoder::new(sampling_rate, channels).unwrap();
3148
3149        let frame_sizes = [120, 240, 480, 960];
3150
3151        for frame_size in frame_sizes {
3152            let toc = gen_toc(
3153                OpusMode::CeltOnly,
3154                frame_rate_from_params(sampling_rate, frame_size).unwrap(),
3155                Bandwidth::Fullband,
3156                channels,
3157            );
3158            let packet = [toc, 0, 0, 0, 0];
3159
3160            let mut output = vec![0.0f32; frame_size * channels];
3161
3162            let _ = decoder.decode(&packet, frame_size, &mut output);
3163        }
3164
3165        let channels = 2;
3166        let mut decoder = OpusDecoder::new(sampling_rate, channels).unwrap();
3167
3168        for frame_size in frame_sizes {
3169            let toc = gen_toc(
3170                OpusMode::CeltOnly,
3171                frame_rate_from_params(sampling_rate, frame_size).unwrap(),
3172                Bandwidth::Fullband,
3173                channels,
3174            );
3175            let packet = [toc, 0, 0, 0, 0];
3176
3177            let mut output = vec![0.0f32; frame_size * channels];
3178            let _ = decoder.decode(&packet, frame_size, &mut output);
3179        }
3180    }
3181
3182    #[test]
3183    fn test_celt_decoder_edge_case_frame_sizes() {
3184        let sampling_rate = 48000;
3185        let channels = 1;
3186        let mut decoder = OpusDecoder::new(sampling_rate, channels).unwrap();
3187
3188        let edge_sizes = [2048, 2167, 2168, 2169, 2880, 3072];
3189
3190        for frame_size in edge_sizes {
3191            let mut output = vec![0.0f32; frame_size * channels];
3192
3193            let _ = decoder.decode(&[0x80, 0, 0, 0], frame_size, &mut output);
3194        }
3195    }
3196
3197    // Regression test for: "index out of bounds: the len is 48 but the index is 119"
3198    // Root cause: frame_size=48 at 48kHz gives frame_rate=1000, which is not a valid
3199    // Hybrid-mode frame rate but was not validated.  CELT's lm-search then silently
3200    // fell back to lm=0, computed n2=120, and wrote output[119] into a 48-element
3201    // slice.  Triggered via G.729-decoded PCM (8kHz) passed to a 48kHz Opus encoder
3202    // without proper resampling, so the encoder received 48 samples instead of 480.
3203    #[test]
3204    fn test_invalid_small_frame_size_returns_error_not_panic() {
3205        let mut enc = OpusEncoder::new(48000, 2, Application::Voip).unwrap();
3206        enc.bitrate_bps = 64000;
3207        enc.complexity = 5;
3208        enc.use_cbr = true;
3209
3210        // 48 samples at 48kHz = 1ms → frame_rate=1000, invalid for Hybrid mode.
3211        let input = vec![0.0f32; 48 * 2]; // stereo interleaved
3212        let mut output = vec![0u8; 256];
3213
3214        let result = enc.encode(&input, 48, &mut output);
3215        assert!(
3216            result.is_err(),
3217            "encode with invalid frame_size=48 should return Err, not panic"
3218        );
3219    }
3220
3221    // Also verify that the Audio application path (always Hybrid at 48 kHz) rejects
3222    // the same bad frame size.
3223    #[test]
3224    fn test_invalid_small_frame_size_audio_application_returns_error() {
3225        let mut enc = OpusEncoder::new(48000, 1, Application::Audio).unwrap();
3226        let input = vec![0.0f32; 48];
3227        let mut output = vec![0u8; 256];
3228
3229        let result = enc.encode(&input, 48, &mut output);
3230        assert!(
3231            result.is_err(),
3232            "Audio/48kHz encoder with frame_size=48 should return Err"
3233        );
3234    }
3235}