Skip to main content

rusty_opus/
lib.rs

1#![allow(unsafe_op_in_unsafe_fn)]
2#![allow(clippy::too_many_arguments)]
3#![allow(clippy::needless_range_loop)]
4
5pub mod analysis;
6pub mod analysis_data;
7pub mod bands;
8pub mod celt;
9pub mod celt_lpc;
10pub mod hp_cutoff;
11pub mod kiss_fft;
12pub mod mdct;
13pub mod modes;
14pub mod parallel;
15pub mod pitch;
16pub mod prof;
17pub mod pvq;
18pub mod quant_bands;
19pub mod range_coder;
20pub mod repacketizer;
21pub mod multistream;
22pub mod rate;
23pub mod silk;
24
25pub use silk::{SilkResampler, SilkResamplerDown1_3, SilkResamplerDown1_6};
26
27pub use celt::{CeltDecoder, CeltEncoder};
28use hp_cutoff::hp_cutoff;
29use range_coder::RangeCoder;
30use silk::control_codec::silk_control_encoder;
31use silk::enc_api::silk_encode;
32use silk::init_encoder::silk_init_encoder;
33use silk::lin2log::silk_lin2log;
34use silk::log2lin::silk_log2lin;
35use silk::macros::*;
36use silk::structs::SilkEncoderState;
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39pub enum Application {
40    Voip = 2048,
41    Audio = 2049,
42    RestrictedLowDelay = 2051,
43}
44
45/// OPUS_SET_SIGNAL hint: bias mode selection toward speech or music. `None` =
46/// OPUS_AUTO (let the analysis decide).
47#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub enum SignalType {
49    Voice,
50    Music,
51}
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
54pub enum Bandwidth {
55    Auto = -1000,
56    Narrowband = 1101,
57    Mediumband = 1102,
58    Wideband = 1103,
59    Superwideband = 1104,
60    Fullband = 1105,
61}
62
63#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64enum OpusMode {
65    SilkOnly,
66    Hybrid,
67    CeltOnly,
68}
69
70pub struct OpusEncoder {
71    celt_enc: CeltEncoder,
72    silk_enc: Box<SilkEncoderState>,
73    application: Application,
74    sampling_rate: i32,
75    channels: usize,
76    bandwidth: Bandwidth,
77    pub bitrate_bps: i32,
78    pub complexity: i32,
79    pub use_cbr: bool,
80
81    pub use_inband_fec: bool,
82
83    /// Discontinuous transmission: after enough consecutive inactive frames,
84    /// emit a 1-byte (TOC-only) packet so the decoder runs comfort-noise/PLC.
85    pub use_dtx: bool,
86    /// Consecutive inactive milliseconds, in Q1 (opus_encoder.c nb_no_activity).
87    nb_no_activity_ms_q1: i32,
88    /// Final range-coder state of the last packet (0 for DTX/PLC packets, which
89    /// carry no coded range — opus_encoder.c st->rangeFinal).
90    range_final: u32,
91
92    pub packet_loss_perc: i32,
93    silk_initialized: bool,
94    mode: OpusMode,
95    prev_enc_mode: Option<OpusMode>,
96
97    variable_hp_smth2_q15: i32,
98    /// Rate-dependent automatic bandwidth (libopus auto_bandwidth), stored as the
99    /// Bandwidth discriminant (1101 NB .. 1105 FB). Hysteresis state.
100    auto_bandwidth: i32,
101    first_frame: bool,
102    /// Overrides automatic bandwidth selection when set (OPUS_SET_BANDWIDTH).
103    pub force_bandwidth: Option<Bandwidth>,
104    /// OPUS_SET_SIGNAL: force the voice/music bias (None = auto from analysis).
105    pub signal_type: Option<SignalType>,
106    /// OPUS_SET_MAX_BANDWIDTH: cap the automatically-selected bandwidth.
107    pub max_bandwidth: Bandwidth,
108    /// Tonality/music/bandwidth analysis (libopus src/analysis.c); runs when
109    /// complexity >= 7 and the API rate is >= 16 kHz.
110    tonality: analysis::TonalityAnalysisState,
111    analysis_kfft: Option<kiss_fft::KissFftState>,
112    /// Input bit depth assumed by the analysis noise floors. The float API
113    /// default is 24; set 16 for s16-sourced content (opus_demo parity).
114    pub lsb_depth: i32,
115    /// 0..100 voice probability from the analysis (-1 = unknown), C voice_ratio.
116    voice_ratio: i32,
117    detected_bandwidth: i32,
118    hp_mem: Vec<i32>,
119
120    buf_filtered: Vec<i16>,
121    buf_silk_input: Vec<i16>,
122    buf_stereo_mid: Vec<i16>,
123    buf_stereo_side: Vec<i16>,
124    buf_celt_input: Vec<f32>,
125    down2_state_first: [i32; 2],
126    down2_state_second: [i32; 2],
127    down2_3_state: [i32; 6],
128    down_1_3_state: silk::resampler::SilkResamplerDown1_3,
129    down2_3_state_r: [i32; 6],
130    down_1_3_state_r: silk::resampler::SilkResamplerDown1_3,
131    down_fir_l: Option<silk::resampler::SilkDownFirResampler>,
132    down_fir_r: Option<silk::resampler::SilkDownFirResampler>,
133    /// Last 10 ms of API-rate mono input, for the SILK prefill after a
134    /// CELT-only -> SILK/hybrid transition (opus_encoder.c:1449 prefill=1).
135    silk_prefill_tail: Vec<i16>,
136    silk_prefill_pending: bool,
137    buf_left: Vec<i16>,
138    buf_right: Vec<i16>,
139    /// Last 2.5 ms of the previous frame's input (planar), for the CELT
140    /// prefill after a mode-transition reset (opus_encoder.c:2060).
141    celt_prefill_tail: Vec<f32>,
142
143    rc: RangeCoder,
144
145    // ---- Great Gate P1 instrumentation (docs/great-gate.md) ----
146    /// Observe-only harvest tap: when `RUSTY_OPUS_GATE_HARVEST=<path>` is set at
147    /// construction, every encoded frame appends one CSV row with the signals
148    /// the mode/bandwidth decision consumed plus the outcome (mode, bw, bytes).
149    /// The bitstream is byte-identical on or off — the tap only reads. Env is
150    /// read ONCE here, never per frame. Serial encoders only (the parallel path
151    /// would interleave rows).
152    gate_tap: Option<std::io::BufWriter<std::fs::File>>,
153    /// Clip label stamped into harvest rows (`RUSTY_OPUS_GATE_CLIP`).
154    gate_clip: String,
155    /// Frame counter for harvest rows.
156    gate_frame: u64,
157    /// Truth-table lever: `RUSTY_OPUS_FORCE_MODE=silk|celt|hybrid` pins the
158    /// coding mode after the auto decision (bandwidth reconciled to a valid TOC
159    /// config). Unset = None = byte-identical to shipped behavior.
160    force_mode: Option<OpusMode>,
161    /// Mode-dwell hysteresis: a proposed mode change must persist this many
162    /// consecutive frames before it is committed. **1 = OFF and
163    /// byte-identical**; set via `RUSTY_OPUS_MODE_DWELL`.
164    ///
165    /// Measured ineffective for the startup-mode defect it was built for and
166    /// left default-off — see the refutation at its use site in `encode`.
167    pub mode_dwell: u32,
168    /// Consecutive frames the current proposal has differed from the coded mode.
169    mode_dwell_run: u32,
170    /// Analysis warm-up guard: ignore the tonality classifier's verdict for
171    /// this many analysis frames and fall back to the application default.
172    /// **Default 10** (`RUSTY_OPUS_ANALYSIS_WARMUP`; 0 = OFF and restores the
173    /// pre-2026-08-07 byte-identical behaviour).
174    ///
175    /// libopus feeds its analysis a lookahead buffer, so the classifier is
176    /// already converged when the first frame is coded. We call `run_analysis`
177    /// with `analysis_frame_size == frame_size` — zero lookahead — so on our
178    /// encoder the classifier spends its first ~20 frames climbing from
179    /// "voice" to its steady-state verdict. On music-ish content that made the
180    /// first 480 ms code as hybrid before flipping to CELT for good.
181    analysis_warmup: u32,
182    /// Analysis frames seen (saturating), compared against `analysis_warmup`.
183    analysis_frames: u32,
184}
185
186// libopus opus_encoder.c bandwidth thresholds: (threshold, hysteresis) pairs for
187// NB<->MB, MB<->WB, WB<->SWB, SWB<->FB, interpolated voice<->music by voice_est^2.
188const MONO_VOICE_BANDWIDTH_THRESHOLDS: [i32; 8] = [9000, 700, 9000, 700, 13500, 1000, 14000, 2000];
189const MONO_MUSIC_BANDWIDTH_THRESHOLDS: [i32; 8] = [9000, 700, 9000, 700, 11000, 1000, 12000, 2000];
190const STEREO_VOICE_BANDWIDTH_THRESHOLDS: [i32; 8] = [9000, 700, 9000, 700, 13500, 1000, 14000, 2000];
191const STEREO_MUSIC_BANDWIDTH_THRESHOLDS: [i32; 8] = [9000, 700, 9000, 700, 11000, 1000, 12000, 2000];
192
193/// Coerce a bandwidth to one the given mode can actually signal in the TOC:
194/// CELT has no mediumband config, SILK-only tops out at wideband, and hybrid
195/// exists only at SWB/FB. Used wherever a mode is overridden after the
196/// bandwidth has already been chosen (dwell hysteresis, forced mode).
197fn reconcile_bandwidth(mode: OpusMode, bw: Bandwidth) -> Bandwidth {
198    match mode {
199        OpusMode::CeltOnly if bw == Bandwidth::Mediumband => Bandwidth::Narrowband,
200        OpusMode::SilkOnly
201            if matches!(bw, Bandwidth::Superwideband | Bandwidth::Fullband) =>
202        {
203            Bandwidth::Wideband
204        }
205        OpusMode::Hybrid
206            if !matches!(bw, Bandwidth::Superwideband | Bandwidth::Fullband) =>
207        {
208            Bandwidth::Superwideband
209        }
210        _ => bw,
211    }
212}
213
214fn compute_equiv_rate(
215    bitrate: i32,
216    channels: usize,
217    frame_rate: i32,
218    vbr: bool,
219    complexity: i32,
220    loss: i32,
221) -> i32 {
222    let mut equiv = bitrate;
223    if frame_rate > 50 {
224        equiv -= (40 * channels as i32 + 20) * (frame_rate - 50);
225    }
226    if !vbr {
227        equiv -= equiv / 12;
228    }
229    equiv = equiv * (90 + complexity) / 100;
230    if loss > 0 {
231        equiv -= equiv * loss / (12 * loss + 20);
232    }
233    equiv
234}
235
236fn compute_mode_threshold(
237    application: Application,
238    channels: usize,
239    prev_was_celt: bool,
240    has_prev_mode: bool,
241    voice_est: i32,
242) -> i32 {
243    let mode_voice = if channels == 1 { 64000 } else { 44000 };
244    let mode_music = 10000;
245
246    let diff = mode_voice - mode_music;
247    let offset = (voice_est * voice_est * diff) >> 14;
248    let mut threshold = mode_music + offset;
249
250    if application == Application::Voip {
251        threshold += 8000;
252    }
253
254    if has_prev_mode {
255        if prev_was_celt {
256            threshold -= 4000;
257        } else {
258            threshold += 4000;
259        }
260    }
261
262    if application == Application::RestrictedLowDelay {
263        threshold = 0;
264    }
265
266    threshold
267}
268
269fn compute_silk_rate_for_hybrid(
270    rate_bps: i32,
271    bandwidth: Bandwidth,
272    frame20ms: bool,
273    vbr: bool,
274) -> i32 {
275    const RATE_TABLE: &[(i32, i32, i32)] = &[
276        (0, 0, 0),
277        (12000, 10000, 10000),
278        (16000, 13500, 13500),
279        (20000, 16000, 16000),
280        (24000, 18000, 18000),
281        (32000, 22000, 22000),
282        (64000, 38000, 38000),
283    ];
284    let n = RATE_TABLE.len();
285    let mut i = 1;
286    while i < n && RATE_TABLE[i].0 <= rate_bps {
287        i += 1;
288    }
289    let mut silk_rate = if i == n {
290        let (x_last, r10_last, r20_last) = RATE_TABLE[n - 1];
291        let base = if frame20ms { r20_last } else { r10_last };
292        base + (rate_bps - x_last) / 2
293    } else {
294        let (x0, lo10, lo20) = RATE_TABLE[i - 1];
295        let (x1, hi10, hi20) = RATE_TABLE[i];
296        let (lo, hi) = if frame20ms {
297            (lo20, hi20)
298        } else {
299            (lo10, hi10)
300        };
301        (lo * (x1 - rate_bps) + hi * (rate_bps - x0)) / (x1 - x0)
302    };
303    // C tail adjustments (opus_encoder.c:789): tiny SILK boost for CBR, and
304    // +300 for SWB hybrid (the CELT part starts at band 17 either way but
305    // covers less spectrum, so SILK earns a bigger share).
306    if !vbr {
307        silk_rate += 100;
308    }
309    if bandwidth == Bandwidth::Superwideband {
310        silk_rate += 300;
311    }
312    silk_rate
313}
314
315#[cfg(test)]
316mod reconcile_bandwidth_tests {
317    use super::{reconcile_bandwidth, Bandwidth, OpusMode};
318
319    #[test]
320    fn celt_maps_mediumband_down_to_narrowband() {
321        // The CELT TOC has no mediumband config.
322        assert_eq!(
323            reconcile_bandwidth(OpusMode::CeltOnly, Bandwidth::Mediumband),
324            Bandwidth::Narrowband
325        );
326    }
327
328    #[test]
329    fn celt_leaves_every_other_bandwidth_alone() {
330        for bw in [
331            Bandwidth::Narrowband,
332            Bandwidth::Wideband,
333            Bandwidth::Superwideband,
334            Bandwidth::Fullband,
335        ] {
336            assert_eq!(reconcile_bandwidth(OpusMode::CeltOnly, bw), bw);
337        }
338    }
339
340    #[test]
341    fn silk_only_caps_at_wideband() {
342        assert_eq!(
343            reconcile_bandwidth(OpusMode::SilkOnly, Bandwidth::Superwideband),
344            Bandwidth::Wideband
345        );
346        assert_eq!(
347            reconcile_bandwidth(OpusMode::SilkOnly, Bandwidth::Fullband),
348            Bandwidth::Wideband
349        );
350        // At or below wideband it is already codeable.
351        for bw in [Bandwidth::Narrowband, Bandwidth::Mediumband, Bandwidth::Wideband] {
352            assert_eq!(reconcile_bandwidth(OpusMode::SilkOnly, bw), bw);
353        }
354    }
355
356    #[test]
357    fn hybrid_floors_at_superwideband() {
358        for bw in [Bandwidth::Narrowband, Bandwidth::Mediumband, Bandwidth::Wideband] {
359            assert_eq!(
360                reconcile_bandwidth(OpusMode::Hybrid, bw),
361                Bandwidth::Superwideband
362            );
363        }
364        // Hybrid exists only at SWB/FB, so those pass through.
365        assert_eq!(
366            reconcile_bandwidth(OpusMode::Hybrid, Bandwidth::Superwideband),
367            Bandwidth::Superwideband
368        );
369        assert_eq!(
370            reconcile_bandwidth(OpusMode::Hybrid, Bandwidth::Fullband),
371            Bandwidth::Fullband
372        );
373    }
374}
375
376#[cfg(test)]
377mod silk_rate_tests {
378    use super::compute_silk_rate_for_hybrid;
379    use crate::Bandwidth;
380
381    #[test]
382    fn test_reference_table_exact_entries() {
383        assert_eq!(compute_silk_rate_for_hybrid(12000, Bandwidth::Fullband, true, true), 10000);
384        assert_eq!(compute_silk_rate_for_hybrid(16000, Bandwidth::Fullband, true, true), 13500);
385        assert_eq!(compute_silk_rate_for_hybrid(20000, Bandwidth::Fullband, true, true), 16000);
386        assert_eq!(compute_silk_rate_for_hybrid(24000, Bandwidth::Fullband, true, true), 18000);
387        assert_eq!(compute_silk_rate_for_hybrid(32000, Bandwidth::Fullband, true, true), 22000);
388        assert_eq!(compute_silk_rate_for_hybrid(64000, Bandwidth::Fullband, true, true), 38000);
389    }
390
391    #[test]
392    fn test_32kbps_gives_22kbps_silk() {
393        assert_eq!(compute_silk_rate_for_hybrid(32000, Bandwidth::Fullband, true, true), 22000);
394    }
395
396    #[test]
397    fn test_interpolation_between_table_entries() {
398        let r = compute_silk_rate_for_hybrid(18000, Bandwidth::Fullband, true, true);
399        assert_eq!(r, 14750);
400    }
401
402    #[test]
403    fn test_above_table_max_gives_half_extra() {
404        let r = compute_silk_rate_for_hybrid(72000, Bandwidth::Fullband, true, true);
405        assert_eq!(r, 38000 + (72000 - 64000) / 2);
406    }
407}
408
409impl OpusEncoder {
410    pub fn new(
411        sampling_rate: i32,
412        channels: usize,
413        application: Application,
414    ) -> Result<Self, &'static str> {
415        if ![8000, 12000, 16000, 24000, 48000].contains(&sampling_rate) {
416            return Err("Invalid sampling rate");
417        }
418        if ![1, 2].contains(&channels) {
419            return Err("Invalid number of channels");
420        }
421
422        let mode = modes::default_mode();
423        let celt_enc = CeltEncoder::new(mode, channels);
424
425        let mut silk_enc = Box::new(SilkEncoderState::default());
426        if silk_init_encoder(&mut silk_enc, 0) != 0 {
427            return Err("SILK encoder initialization failed");
428        }
429
430        let (opus_mode, bw) = match application {
431            Application::Voip => {
432                let bw = match sampling_rate {
433                    8000 => Bandwidth::Narrowband,
434                    12000 => Bandwidth::Mediumband,
435                    16000 => Bandwidth::Wideband,
436                    24000 => Bandwidth::Superwideband,
437                    48000 => Bandwidth::Fullband,
438                    _ => Bandwidth::Narrowband,
439                };
440
441                let mode = if sampling_rate > 16000 {
442                    OpusMode::Hybrid
443                } else {
444                    OpusMode::SilkOnly
445                };
446                (mode, bw)
447            }
448            Application::RestrictedLowDelay => {
449                let bw = match sampling_rate {
450                    8000 => Bandwidth::Narrowband,
451                    12000 => Bandwidth::Mediumband,
452                    16000 => Bandwidth::Wideband,
453                    24000 => Bandwidth::Superwideband,
454                    _ => Bandwidth::Fullband,
455                };
456                (OpusMode::CeltOnly, bw)
457            }
458            Application::Audio => {
459                if sampling_rate <= 16000 {
460                    let bw = match sampling_rate {
461                        8000 => Bandwidth::Narrowband,
462                        12000 => Bandwidth::Mediumband,
463                        _ => Bandwidth::Wideband,
464                    };
465                    (OpusMode::SilkOnly, bw)
466                } else {
467                    let bw = match sampling_rate {
468                        24000 => Bandwidth::Superwideband,
469                        _ => Bandwidth::Fullband,
470                    };
471                    (OpusMode::Hybrid, bw)
472                }
473            }
474        };
475
476        use silk::lin2log::silk_lin2log;
477        let variable_hp_smth2_q15 = silk_lin2log(60) << 8;
478
479        Ok(Self {
480            celt_enc,
481            silk_enc,
482            application,
483            sampling_rate,
484            channels,
485            bandwidth: bw,
486            bitrate_bps: 64000,
487            complexity: 9,
488            use_cbr: false,
489            use_inband_fec: false,
490            use_dtx: false,
491            nb_no_activity_ms_q1: 0,
492            range_final: 0,
493            packet_loss_perc: 0,
494            silk_initialized: false,
495            prev_enc_mode: None,
496            mode: opus_mode,
497            variable_hp_smth2_q15,
498            auto_bandwidth: 0,
499            first_frame: true,
500            force_bandwidth: None,
501            signal_type: None,
502            max_bandwidth: Bandwidth::Fullband,
503            tonality: analysis::TonalityAnalysisState::new(sampling_rate),
504            analysis_kfft: kiss_fft::KissFftState::new(480),
505            // Float-API default, faithful to opus_encoder.c. `RUSTY_OPUS_LSB_DEPTH`
506            // overrides it for the D1 bandwidth-detector investigation: the
507            // analysis noise floor is (5.7e-4 / 2^(lsb_depth-8))^2, so feeding
508            // s16-sourced material at depth 24 puts the floor 2^16 too low.
509            lsb_depth: std::env::var("RUSTY_OPUS_LSB_DEPTH")
510                .ok()
511                .and_then(|s| s.parse().ok())
512                .unwrap_or(24),
513            voice_ratio: -1,
514            detected_bandwidth: 0,
515            hp_mem: vec![0; channels * 2],
516
517            buf_filtered: Vec::new(),
518            buf_silk_input: Vec::new(),
519            buf_stereo_mid: Vec::new(),
520            buf_stereo_side: Vec::new(),
521            buf_celt_input: Vec::new(),
522            down2_state_first: [0; 2],
523            down2_state_second: [0; 2],
524            down2_3_state: [0; 6],
525            down_1_3_state: silk::resampler::SilkResamplerDown1_3::default(),
526            down2_3_state_r: [0; 6],
527            down_1_3_state_r: silk::resampler::SilkResamplerDown1_3::default(),
528            down_fir_l: None,
529            down_fir_r: None,
530            silk_prefill_tail: Vec::new(),
531            silk_prefill_pending: false,
532            buf_left: Vec::new(),
533            buf_right: Vec::new(),
534            celt_prefill_tail: Vec::new(),
535            rc: RangeCoder::new_encoder(1),
536            gate_tap: std::env::var("RUSTY_OPUS_GATE_HARVEST").ok().and_then(|p| {
537                use std::io::Write as _;
538                let mut f = std::fs::OpenOptions::new()
539                    .create(true)
540                    .append(true)
541                    .open(&p)
542                    .ok()?;
543                if f.metadata().map(|m| m.len()).unwrap_or(0) == 0 {
544                    let _ = writeln!(
545                        f,
546                        "clip,frame,mode,bw,ch,bitrate,complexity,equiv,voice_est,\
547                         is_silence,active,valid,tonality,tonality_slope,noisiness,\
548                         activity_prob,music_prob,music_prob_min,music_prob_max,\
549                         det_bw,max_pitch_ratio,bytes"
550                    );
551                }
552                Some(std::io::BufWriter::new(f))
553            }),
554            gate_clip: std::env::var("RUSTY_OPUS_GATE_CLIP").unwrap_or_default(),
555            gate_frame: 0,
556            force_mode: match std::env::var("RUSTY_OPUS_FORCE_MODE").ok().as_deref() {
557                Some("silk") => Some(OpusMode::SilkOnly),
558                Some("celt") => Some(OpusMode::CeltOnly),
559                Some("hybrid") => Some(OpusMode::Hybrid),
560                _ => None,
561            },
562            mode_dwell: std::env::var("RUSTY_OPUS_MODE_DWELL")
563                .ok()
564                .and_then(|s| s.parse().ok())
565                .unwrap_or(1),
566            mode_dwell_run: 0,
567            // DEFAULT-ON at 10 since 2026-08-07: 14 wins / 0 losses / 1 neutral
568            // (-0.005) over a 65-rung, 13-class PEAQ ladder, with all VoIP
569            // classes bit-for-bit unchanged. `RUSTY_OPUS_ANALYSIS_WARMUP=0`
570            // restores the previous byte-identical behaviour.
571            analysis_warmup: std::env::var("RUSTY_OPUS_ANALYSIS_WARMUP")
572                .ok()
573                .and_then(|s| s.parse().ok())
574                .unwrap_or(10),
575            analysis_frames: 0,
576        })
577    }
578
579    pub fn enable_hybrid_mode(&mut self) -> Result<(), &'static str> {
580        if self.sampling_rate != 24000 && self.sampling_rate != 48000 {
581            return Err("Hybrid mode requires 24kHz or 48kHz sampling rate");
582        }
583        let bw = if self.sampling_rate == 48000 {
584            Bandwidth::Fullband
585        } else {
586            Bandwidth::Superwideband
587        };
588        self.mode = OpusMode::Hybrid;
589        self.bandwidth = bw;
590        self.silk_initialized = false;
591        Ok(())
592    }
593
594    /// Final range-coder state of the last encoded packet (libopus
595    /// OPUS_GET_FINAL_RANGE). Stored in opus_demo `.bit` framing so the reference
596    /// decoder can verify encoder/decoder range-coder agreement per packet.
597    pub fn final_range(&self) -> u32 {
598        self.range_final
599    }
600
601    /// opus_encoder.c:1296 voice_est ladder: forced by `signal_type` when set,
602    /// else analysis-driven when voice_ratio is known, else application defaults.
603    fn compute_voice_est(&self) -> i32 {
604        match self.signal_type {
605            Some(SignalType::Voice) => return 127,
606            Some(SignalType::Music) => return 0,
607            None => {}
608        }
609        if self.voice_ratio >= 0 {
610            let mut v = self.voice_ratio * 327 >> 8;
611            // For AUDIO, never be more than 90% confident of having speech.
612            if self.application == Application::Audio {
613                v = v.min(115);
614            }
615            v
616        } else {
617            match self.application {
618                Application::Voip => 115,
619                Application::Audio => 48,
620                Application::RestrictedLowDelay => 0,
621            }
622        }
623    }
624
625    pub fn encode(
626        &mut self,
627        input: &[f32],
628        frame_size: usize,
629        output: &mut [u8],
630    ) -> Result<usize, &'static str> {
631        let _prof_total = crate::prof::scope(crate::prof::Stage::Total);
632        if output.len() < 2 {
633            return Err("Output buffer too small");
634        }
635
636        let frame_rate = frame_rate_from_params(self.sampling_rate, frame_size)
637            .ok_or("Invalid frame size for sampling rate")?;
638
639        // ---- Tonality analysis (opus_encoder.c:1123) ----
640        let mut analysis_info = analysis::AnalysisInfo::default();
641        if self.complexity >= 7 && self.sampling_rate >= 16000 {
642            if let Some(kfft) = &self.analysis_kfft {
643                analysis_info = analysis::run_analysis(
644                    &mut self.tonality,
645                    kfft,
646                    input,
647                    frame_size,
648                    frame_size,
649                    self.channels,
650                    self.sampling_rate,
651                    self.lsb_depth,
652                );
653            }
654        } else if self.tonality.initialized() {
655            self.tonality.reset();
656        }
657
658        // voice_ratio / detected_bandwidth from the analysis (opus_encoder.c:1154).
659        let silence_thresh = 1.0f32 / (1i64 << self.lsb_depth) as f32;
660        let is_silence = input[..(frame_size * self.channels).min(input.len())]
661            .iter()
662            .fold(0.0f32, |m, &v| m.max(v.abs()))
663            <= silence_thresh;
664        if !is_silence {
665            self.voice_ratio = -1;
666        }
667        // Voice-activity flag for DTX (opus_encoder.c:1160). Silence is always
668        // inactive; with analysis, use the VAD probability; without it, assume
669        // active (conservative — never DTX away real audio). We skip the
670        // peak-energy SNR fallback, which only ever ADDS activity.
671        let activity = if is_silence {
672            false
673        } else if analysis_info.valid {
674            analysis_info.activity_probability >= 0.1
675        } else {
676            true
677        };
678        // Analysis warm-up guard (see the `analysis_warmup` field doc): until
679        // the classifier has seen enough frames to converge, leave
680        // `voice_ratio` at -1 so `compute_voice_est` uses the APPLICATION
681        // default instead of a half-climbed verdict. That is the right answer
682        // for both applications — Audio falls back to 48 (music-leaning, which
683        // is what these clips settle on anyway) and Voip falls back to 115
684        // (speech-leaning, which is what voip content wants from frame 0).
685        if analysis_info.valid {
686            self.analysis_frames = self.analysis_frames.saturating_add(1);
687        }
688        let analysis_converged = self.analysis_frames >= self.analysis_warmup;
689
690        self.detected_bandwidth = 0;
691        if analysis_info.valid && analysis_converged {
692            // Auto path (signal_type override applies later in compute_voice_est):
693            // pick the hysteresis-correct probability.
694            let prob = if self.prev_enc_mode.is_none() {
695                analysis_info.music_prob
696            } else if self.prev_enc_mode == Some(OpusMode::CeltOnly) {
697                analysis_info.music_prob_max
698            } else {
699                analysis_info.music_prob_min
700            };
701            self.voice_ratio = (0.5 + 100.0 * (1.0 - prob)).floor() as i32;
702            let ab = analysis_info.bandwidth;
703            self.detected_bandwidth = if ab <= 12 {
704                Bandwidth::Narrowband as i32
705            } else if ab <= 14 {
706                Bandwidth::Mediumband as i32
707            } else if ab <= 16 {
708                Bandwidth::Wideband as i32
709            } else if ab <= 18 {
710                Bandwidth::Superwideband as i32
711            } else {
712                Bandwidth::Fullband as i32
713            };
714        }
715
716        // Mode selection: match C's opus_encode_native() behavior.
717        // C reference auto-selects between SILK_ONLY and CELT_ONLY; Hybrid is
718        // produced afterwards by bandwidth overrides (SILK-only + FB/SWB → Hybrid).
719        let mut mode = if self.application == Application::RestrictedLowDelay {
720            OpusMode::CeltOnly
721        } else {
722            let equiv = compute_equiv_rate(
723                self.bitrate_bps,
724                self.channels,
725                frame_rate,
726                !self.use_cbr,
727                self.complexity,
728                self.packet_loss_perc,
729            );
730            let prev_was_celt = self.prev_enc_mode == Some(OpusMode::CeltOnly);
731            let has_prev_mode = self.prev_enc_mode.is_some();
732            let voice_est = self.compute_voice_est();
733            let threshold = compute_mode_threshold(
734                self.application,
735                self.channels,
736                prev_was_celt,
737                has_prev_mode,
738                voice_est,
739            );
740            if equiv >= threshold && self.sampling_rate >= 24000 {
741                OpusMode::CeltOnly
742            } else {
743                OpusMode::SilkOnly
744            }
745        };
746
747        // ---- Automatic rate-dependent bandwidth selection (opus_encoder.c:1456) ----
748        // Walk down from FB; stop at the first bandwidth whose hysteresis-adjusted
749        // threshold the equivalent rate meets. Thresholds interpolate voice<->music
750        // by voice_est^2. Without the tonality analysis we cannot do
751        // detected-bandwidth reduction, so this reproduces libopus's
752        // complexity-0 choices (measured: WB @16k, SWB @20k, FB @24k+ voip mono).
753        {
754            let equiv = compute_equiv_rate(
755                self.bitrate_bps,
756                self.channels,
757                frame_rate,
758                !self.use_cbr,
759                self.complexity,
760                self.packet_loss_perc,
761            );
762            let voice_est: i32 = self.compute_voice_est();
763            let (vt, mt) = if self.channels == 2 {
764                (
765                    &STEREO_VOICE_BANDWIDTH_THRESHOLDS,
766                    &STEREO_MUSIC_BANDWIDTH_THRESHOLDS,
767                )
768            } else {
769                (
770                    &MONO_VOICE_BANDWIDTH_THRESHOLDS,
771                    &MONO_MUSIC_BANDWIDTH_THRESHOLDS,
772                )
773            };
774            let mut th = [0i32; 8];
775            for i in 0..8 {
776                th[i] = mt[i] + ((voice_est * voice_est * (vt[i] - mt[i])) >> 14);
777            }
778            const NB: i32 = Bandwidth::Narrowband as i32; // 1101
779            const MB: i32 = Bandwidth::Mediumband as i32; // 1102
780            const FB: i32 = Bandwidth::Fullband as i32; // 1105
781            let mut bw = FB;
782            while bw > NB {
783                let idx = (2 * (bw - MB)) as usize;
784                let mut threshold = th[idx];
785                let hysteresis = th[idx + 1];
786                if !self.first_frame {
787                    if self.auto_bandwidth >= bw {
788                        threshold -= hysteresis;
789                    } else {
790                        threshold += hysteresis;
791                    }
792                }
793                if equiv >= threshold {
794                    break;
795                }
796                bw -= 1;
797            }
798            // Mediumband is no longer used by libopus's selector.
799            if bw == MB {
800                bw = Bandwidth::Wideband as i32;
801            }
802            self.auto_bandwidth = bw;
803            // Hybrid at unsafe CBR rates starves SILK: cap at WB below 15 kb/s.
804            if mode != OpusMode::CeltOnly && self.use_cbr && self.bitrate_bps < 15000 {
805                bw = bw.min(Bandwidth::Wideband as i32);
806            }
807            // NB/MB SILK-internal rates (8/12 kHz) aren't wired for >16 kHz API
808            // input yet (no 48k->8k/12k encode resamplers); clamp to WB.
809            if mode != OpusMode::CeltOnly && self.sampling_rate > 16000 {
810                bw = bw.max(Bandwidth::Wideband as i32);
811            }
812            // Never code above the input's Nyquist (opus_encoder.c:1516).
813            if self.sampling_rate <= 24000 {
814                bw = bw.min(Bandwidth::Superwideband as i32);
815            }
816            if self.sampling_rate <= 16000 {
817                bw = bw.min(Bandwidth::Wideband as i32);
818            }
819            if self.sampling_rate <= 12000 {
820                bw = bw.min(Bandwidth::Mediumband as i32);
821            }
822            if self.sampling_rate <= 8000 {
823                bw = bw.min(Bandwidth::Narrowband as i32);
824            }
825            // (MB remap above may have been undone by the caps; keep WB floor
826            // only where the API rate allows it.)
827            if bw == Bandwidth::Mediumband as i32 && self.sampling_rate > 12000 {
828                bw = Bandwidth::Wideband as i32;
829            }
830            // Use the detected bandwidth to reduce the coded bandwidth
831            // (opus_encoder.c:1526), conservatively floored by rate. (For
832            // CELT-only this is currently undone below — no end-band support.)
833            // For CELT-only, hold the detected-bandwidth narrowing until the
834            // leak_boost dynalloc lands: decisions already match libopus
835            // frame-for-frame (64k st music: 27:704/31:680/23:90 both), but our
836            // dynalloc lacks C's leakage compensation at the spectral cut, so
837            // the same narrowing costs 0.25 ODG more than C pays (PEAQ-gated
838            // out). Hybrid/SILK caps (incl. hybrid SWB) stay live.
839            // CELT-only keeps FULL bandwidth by choice: C's detected-bandwidth
840            // narrowing costs PEAQ universally (libopus's own -2.11 at 64k st
841            // IS its narrowed score; our FB encode scores -1.65 on the same
842            // clip). leak_boost did NOT change this verdict (tested 2026-07-09
843            // with the full dynalloc live: narrowing still -2.37). Hybrid/SILK
844            // caps stay (they pick coding MODE, not spectral truncation).
845            if self.detected_bandwidth != 0
846                && self.force_bandwidth.is_none()
847                && mode != OpusMode::CeltOnly
848            {
849                let ch = self.channels as i32;
850                let equiv2 = equiv; // same 20-ms equivalent rate as the walk
851                let min_det = if equiv2 <= 18000 * ch && mode == OpusMode::CeltOnly {
852                    NB
853                } else if equiv2 <= 24000 * ch && mode == OpusMode::CeltOnly {
854                    MB
855                } else if equiv2 <= 30000 * ch {
856                    Bandwidth::Wideband as i32
857                } else if equiv2 <= 44000 * ch {
858                    Bandwidth::Superwideband as i32
859                } else {
860                    FB
861                };
862                bw = bw.min(self.detected_bandwidth.max(min_det));
863            }
864            // Cap by OPUS_SET_MAX_BANDWIDTH before the force override
865            // (opus_encoder.c: bandwidth = IMIN(bandwidth, max_bandwidth)), but
866            // keep the WB floor for non-CELT >16 kHz input — NB/MB SILK from
867            // 48 kHz needs the 48->8/12k encode resamplers we don't have, so a
868            // max_bandwidth of NB/MB there would emit an uncodeable config.
869            let mut max_bw = self.max_bandwidth as i32;
870            if mode != OpusMode::CeltOnly && self.sampling_rate > 16000 {
871                max_bw = max_bw.max(Bandwidth::Wideband as i32);
872            }
873            bw = bw.min(max_bw);
874            // The CELT TOC has no mediumband config; C maps MB down to NB.
875            if mode == OpusMode::CeltOnly && bw == MB {
876                bw = NB;
877            }
878            self.bandwidth = match self.force_bandwidth {
879                Some(f) => f,
880                None => match bw {
881                    x if x == NB => Bandwidth::Narrowband,
882                    x if x == MB => Bandwidth::Mediumband,
883                    x if x == Bandwidth::Wideband as i32 => Bandwidth::Wideband,
884                    x if x == Bandwidth::Superwideband as i32 => Bandwidth::Superwideband,
885                    x if x == FB => Bandwidth::Fullband,
886                    _ => Bandwidth::Wideband,
887                },
888            };
889            self.first_frame = false;
890        }
891
892        let curr_bw = self.bandwidth;
893        if mode == OpusMode::SilkOnly
894            && (curr_bw == Bandwidth::Superwideband || curr_bw == Bandwidth::Fullband)
895        {
896            mode = OpusMode::Hybrid;
897        }
898        if mode == OpusMode::Hybrid
899            && (curr_bw == Bandwidth::Narrowband
900                || curr_bw == Bandwidth::Mediumband
901                || curr_bw == Bandwidth::Wideband)
902        {
903            mode = OpusMode::SilkOnly;
904        }
905
906        // Stereo hybrid is now CONFORMANT (the CELT intensity-clamp fix), but
907        // our FIXED-point stereo SILK executes it worse than plain CELT-FB above
908        // ~28 kb/s: PEAQ on stereo speech (ODG) measured hybrid −2.196/−2.193 vs
909        // CELT-FB −2.136/−2.057 at 32k/48k (CELT-FB wins), while at 24k hybrid
910        // −2.198 beats CELT-FB −2.240. libopus's FLOAT stereo SILK hybrid beats
911        // both everywhere — the gap is fixed-vs-float, not a bug. So route
912        // stereo hybrid to CELT-FB except at the low rates where it wins. (Force
913        // via OPUS_SET_BANDWIDTH if the true hybrid path is wanted.) The clean
914        // fix is float stereo SILK — a large port, tracked in the roadmap.
915        if self.channels == 2 && mode == OpusMode::Hybrid && self.bitrate_bps > 28000 {
916            mode = OpusMode::CeltOnly;
917            self.bandwidth = Bandwidth::Fullband;
918        }
919
920        // ---- Mode-dwell hysteresis (Great Gate P2) — MEASURED INEFFECTIVE ----
921        // Require a proposed mode change to persist for `mode_dwell` frames
922        // before committing. `mode_dwell <= 1` is OFF and byte-identical.
923        //
924        // REFUTED for the defect it was built for (2026-08-07), kept behind the
925        // env toggle so re-testing is cheap if the mode pattern ever changes.
926        // The non-CELT frames it was meant to suppress are NOT isolated flips:
927        // they are a single contiguous run at the START of the stream (frames
928        // 0-23 on every clip measured), while the analysis classifier warms up.
929        // Dwell delays transitions in BOTH directions, so on one long run it
930        // only postpones the exit — measured non-CELT frames went UP with
931        // dwell, 24 -> 25/26/28/33 for dwell 2/3/5/10, i.e. exactly +(N-1).
932        // The fix that works is `analysis_warmup` below.
933        if self.mode_dwell > 1 {
934            match self.prev_enc_mode {
935                Some(prev) if mode != prev => {
936                    self.mode_dwell_run += 1;
937                    if self.mode_dwell_run < self.mode_dwell {
938                        // Not yet persistent: hold the previous mode. Bandwidth
939                        // was chosen for the proposed mode, so reconcile it or
940                        // the TOC config would be invalid.
941                        mode = prev;
942                        self.bandwidth = reconcile_bandwidth(mode, self.bandwidth);
943                    } else {
944                        // Persisted long enough — commit and re-arm.
945                        self.mode_dwell_run = 0;
946                    }
947                }
948                _ => self.mode_dwell_run = 0,
949            }
950        }
951
952        // Great Gate truth-table lever: pin the mode after the auto decision,
953        // reconciling bandwidth to a valid TOC config for the forced mode.
954        // Unset = byte-identical to the auto path above.
955        if let Some(fm) = self.force_mode {
956            mode = fm;
957            self.bandwidth = reconcile_bandwidth(fm, self.bandwidth);
958        }
959
960        if mode == OpusMode::CeltOnly {
961            match frame_rate {
962                400 | 200 | 100 | 50 => {}
963                _ => return Err("Unsupported frame size for CELT-only mode"),
964            }
965        }
966
967        if mode == OpusMode::Hybrid {
968            match frame_rate {
969                100 | 50 => {}
970                _ => return Err("Unsupported frame size for Hybrid mode"),
971            }
972        }
973
974        if mode == OpusMode::SilkOnly {
975            match frame_rate {
976                400 | 200 | 100 | 50 | 25 => {}
977                _ => return Err("Unsupported frame size for SILK-only mode"),
978            }
979        }
980
981        let n400 = (self.sampling_rate / 400) as usize;
982
983        // ---- Mode-transition resets (opus_encoder.c:1449 + 2054) ----
984        // The decoder resets its CELT state on ANY mode change (when there is
985        // no redundancy) and its SILK state when leaving CELT-only; the
986        // encoder must mirror both or the streams desync from that frame on.
987        if let Some(prev) = self.prev_enc_mode {
988            if prev != mode {
989                if mode != OpusMode::SilkOnly {
990                    let ch = self.channels;
991                    self.celt_enc = CeltEncoder::new(modes::default_mode(), ch);
992                    // Prefill 2.5 ms so the fresh state has real preemph/overlap
993                    // history instead of a hard edge (opus_encoder.c:2060).
994                    let n400 = (self.sampling_rate / 400) as usize;
995                    if self.celt_prefill_tail.len() == n400 * ch {
996                        let mut dummy = RangeCoder::new_encoder(2);
997                        let tail = std::mem::take(&mut self.celt_prefill_tail);
998                        self.celt_enc.encode_with_budget(&tail, n400, &mut dummy, 0, 21, 16);
999                        self.celt_prefill_tail = tail;
1000                    }
1001                }
1002                if mode != OpusMode::CeltOnly && prev == OpusMode::CeltOnly {
1003                    self.silk_initialized = false;
1004                    self.silk_prefill_pending = true;
1005                }
1006            }
1007        }
1008
1009        // SILK prefill tail: last 10 ms of API-rate mono input.
1010        if self.channels == 1 {
1011            let n10 = (self.sampling_rate / 100) as usize;
1012            if frame_size >= n10 {
1013                self.silk_prefill_tail.resize(n10, 0);
1014                for i in 0..n10 {
1015                    self.silk_prefill_tail[i] = (input[frame_size - n10 + i] * 32768.0)
1016                        .clamp(-32768.0, 32767.0) as i16;
1017                }
1018            }
1019        }
1020
1021        // Save THIS frame's last 2.5 ms (planar) for a possible prefill at the
1022        // next mode transition. (The transition block above consumed the
1023        // PREVIOUS frame's tail.)
1024        {
1025            let ch = self.channels;
1026            self.celt_prefill_tail.resize(n400 * ch, 0.0);
1027            let base = frame_size - n400;
1028            for c in 0..ch {
1029                for i in 0..n400 {
1030                    self.celt_prefill_tail[c * n400 + i] = input[(base + i) * ch + c];
1031                }
1032            }
1033        }
1034
1035        let toc = gen_toc(mode, frame_rate, self.bandwidth, self.channels);
1036        output[0] = toc;
1037
1038        // ---- DTX decision (opus_encoder.c:2137 decide_dtx_mode) ----
1039        // After enough consecutive inactive frames, emit a TOC-only 1-byte
1040        // packet: the decoder sees an empty payload and runs comfort-noise /
1041        // PLC. We decide before the (skipped) SILK/CELT encode — SILK's own DTX
1042        // likewise stops coding, so the encoder state simply doesn't advance;
1043        // the codecs resync on the next active frame.
1044        if self.use_dtx && (analysis_info.valid || is_silence) {
1045            let frame_ms_q1 = 2 * 1000 * frame_size as i32 / self.sampling_rate;
1046            let dtx = if !activity {
1047                self.nb_no_activity_ms_q1 += frame_ms_q1;
1048                const LO: i32 = silk::define::NB_SPEECH_FRAMES_BEFORE_DTX * 20 * 2; // 400
1049                const HI: i32 = (silk::define::NB_SPEECH_FRAMES_BEFORE_DTX + silk::define::MAX_CONSECUTIVE_DTX) * 20 * 2; // 1200
1050                if self.nb_no_activity_ms_q1 > LO {
1051                    if self.nb_no_activity_ms_q1 <= HI {
1052                        true
1053                    } else {
1054                        self.nb_no_activity_ms_q1 = LO;
1055                        false
1056                    }
1057                } else {
1058                    false
1059                }
1060            } else {
1061                self.nb_no_activity_ms_q1 = 0;
1062                false
1063            };
1064            if dtx {
1065                self.prev_enc_mode = Some(mode);
1066                self.range_final = 0;
1067                return Ok(1);
1068            }
1069        } else {
1070            self.nb_no_activity_ms_q1 = 0;
1071        }
1072
1073        let target_bits =
1074            (self.bitrate_bps as i64 * frame_size as i64 / self.sampling_rate as i64) as i32;
1075        let cbr_bytes = ((target_bits + 4) / 8) as usize;
1076        let max_data_bytes = output.len();
1077
1078        // CBR: the packet is exactly the target size. VBR: start the coder on a
1079        // generous buffer — SILK-only packets end at whatever SILK produced, and
1080        // the CELT layer picks its own frame size (compute_vbr) and shrinks the
1081        // coder to it (libopus opus_encoder.c / celt_encoder.c VBR flow).
1082        let n_bytes = if self.use_cbr {
1083            cbr_bytes.min(max_data_bytes).max(1)
1084        } else {
1085            max_data_bytes.min(1276).max(cbr_bytes.min(max_data_bytes)).max(3)
1086        };
1087
1088        let init_rc_size = n_bytes - 1;
1089        self.rc.reset_for_encode(init_rc_size as u32);
1090
1091        if mode == OpusMode::SilkOnly || mode == OpusMode::Hybrid {
1092            let silk_fs_khz = if mode == OpusMode::Hybrid {
1093                16
1094            } else {
1095                self.sampling_rate.min(16000) / 1000
1096            };
1097
1098            let frame_ms = (frame_size as i32 * 1000) / self.sampling_rate;
1099            if !self.silk_initialized || self.silk_enc.s_cmn.fs_khz != silk_fs_khz {
1100                let silk_init_bitrate = if self.use_cbr {
1101                    (((n_bytes - 1) * 8) as i64 * self.sampling_rate as i64 / frame_size as i64)
1102                        as i32
1103                } else {
1104                    self.bitrate_bps
1105                };
1106                silk_control_encoder(
1107                    &mut self.silk_enc,
1108                    silk_fs_khz,
1109                    frame_ms,
1110                    silk_init_bitrate,
1111                    self.complexity,
1112                );
1113                self.silk_enc.s_cmn.use_cbr = if self.use_cbr { 1 } else { 0 };
1114
1115                self.silk_enc.s_cmn.n_channels = self.channels as i32;
1116                self.silk_initialized = true;
1117                self.down2_state_first = [0; 2];
1118                self.down2_state_second = [0; 2];
1119                self.down2_3_state = [0; 6];
1120                self.down_1_3_state = silk::resampler::SilkResamplerDown1_3::default();
1121                self.down2_3_state_r = [0; 6];
1122                self.down_1_3_state_r = silk::resampler::SilkResamplerDown1_3::default();
1123                self.down_fir_l =
1124                    silk::resampler::SilkDownFirResampler::new(self.sampling_rate, 16000);
1125                self.down_fir_r =
1126                    silk::resampler::SilkDownFirResampler::new(self.sampling_rate, 16000);
1127            }
1128
1129            // SILK prefill after CELT-only (opus_encoder.c prefill=1): run 10 ms
1130            // of the previous audio through the fresh resampler + SILK warmup
1131            // path so the first coded SILK frame has real LTP/shape history.
1132            if self.silk_prefill_pending {
1133                self.silk_prefill_pending = false;
1134                let n10 = (self.sampling_rate / 100) as usize;
1135                if self.channels == 1 && self.silk_prefill_tail.len() == n10 {
1136                    let need = silk_fs_khz as usize * 10;
1137                    let mut resampled = vec![0i16; need];
1138                    if self.sampling_rate > 16000 {
1139                        if let Some(r) = &mut self.down_fir_l {
1140                            r.process(&mut resampled, &self.silk_prefill_tail);
1141                        }
1142                    } else {
1143                        resampled.copy_from_slice(&self.silk_prefill_tail[..need]);
1144                    }
1145                    silk::enc_api::silk_encode_prefill(&mut self.silk_enc, &resampled, 0);
1146                }
1147            }
1148
1149            self.silk_enc.s_cmn.use_in_band_fec = if self.use_inband_fec { 1 } else { 0 };
1150            self.silk_enc.s_cmn.packet_loss_perc = self.packet_loss_perc.clamp(0, 100);
1151
1152            self.silk_enc.s_cmn.lbrr_enabled = if self.use_inband_fec { 1 } else { 0 };
1153
1154            // libopus silk_setup_LBRR: gain bump shrinks as loss rises so LBRR
1155            // frames stay decodable at high loss — was a hardcoded 2 (census
1156            // 2026-08-07). max(7 − 0.4·loss%, 2); FEC-off path unaffected.
1157            self.silk_enc.s_cmn.lbrr_gain_increases =
1158                (7 - ((self.packet_loss_perc.clamp(0, 100) * 26214) >> 16)).max(2);
1159
1160            let hp_freq_smth1 = if mode == OpusMode::CeltOnly {
1161                silk_lin2log(60) << 8
1162            } else {
1163                self.silk_enc.s_cmn.variable_hp_smth1_q15
1164            };
1165
1166            const VARIABLE_HP_SMTH_COEF2_Q16: i32 = 984;
1167            self.variable_hp_smth2_q15 = silk_smlawb(
1168                self.variable_hp_smth2_q15,
1169                hp_freq_smth1 - self.variable_hp_smth2_q15,
1170                VARIABLE_HP_SMTH_COEF2_Q16,
1171            );
1172
1173            let cutoff_hz = silk_log2lin(silk_rshift(self.variable_hp_smth2_q15, 8));
1174
1175            let _prof_rs = crate::prof::scope(crate::prof::Stage::Resample);
1176            let required_size = frame_size * self.channels;
1177            self.buf_filtered.resize(required_size, 0);
1178            if self.application == Application::Voip {
1179                hp_cutoff(
1180                    input,
1181                    cutoff_hz,
1182                    &mut self.buf_filtered,
1183                    &mut self.hp_mem,
1184                    frame_size,
1185                    self.channels,
1186                    self.sampling_rate,
1187                );
1188            } else {
1189                for (i, &x) in input.iter().enumerate() {
1190                    self.buf_filtered[i] = (x * 32768.0).clamp(-32768.0, 32767.0) as i16;
1191                }
1192            }
1193
1194            let input_i16 = &self.buf_filtered;
1195
1196            let silk_input: &[i16] = if self.channels == 2 {
1197                // Stereo SILK/hybrid: deinterleave, resample EACH channel to the
1198                // SILK-internal rate (separate filter states), then split
1199                // mid/side — C's order (per-channel resampling inside
1200                // silk_Encode, then silk_stereo_LR_to_MS). The old code only
1201                // handled stereo at <=16 kHz and fed resampled INTERLEAVED
1202                // audio to a stereo-configured SILK above that (never
1203                // exercised until the analysis started picking stereo hybrid).
1204                let frame_length = input_i16.len() / 2;
1205                self.buf_left.resize(frame_length, 0);
1206                self.buf_right.resize(frame_length, 0);
1207                for i in 0..frame_length {
1208                    self.buf_left[i] = input_i16[2 * i];
1209                    self.buf_right[i] = input_i16[2 * i + 1];
1210                }
1211                let need_resample = self.sampling_rate > 16000;
1212                let ds_len = if !need_resample {
1213                    frame_length
1214                } else if self.sampling_rate == 48000 {
1215                    frame_length / 3
1216                } else {
1217                    frame_length * 2 / 3
1218                };
1219                if need_resample {
1220                    self.buf_stereo_mid.resize(ds_len, 0);
1221                    self.buf_stereo_side.resize(ds_len, 0);
1222                    if let (Some(rl), Some(rr)) = (&mut self.down_fir_l, &mut self.down_fir_r) {
1223                        rl.process(&mut self.buf_stereo_mid, &self.buf_left);
1224                        rr.process(&mut self.buf_stereo_side, &self.buf_right);
1225                    }
1226                    self.buf_left.resize(ds_len, 0);
1227                    self.buf_right.resize(ds_len, 0);
1228                    self.buf_left.copy_from_slice(&self.buf_stereo_mid[..ds_len]);
1229                    self.buf_right.copy_from_slice(&self.buf_stereo_side[..ds_len]);
1230                }
1231                self.buf_stereo_mid.resize(ds_len, 0);
1232                self.buf_stereo_side.resize(ds_len, 0);
1233                for i in 0..ds_len {
1234                    let l = self.buf_left[i] as i32;
1235                    let r = self.buf_right[i] as i32;
1236                    self.buf_stereo_mid[i] = ((l + r) / 2) as i16;
1237                    self.buf_stereo_side[i] = (l - r) as i16;
1238                }
1239                self.silk_enc.stereo.side.resize(ds_len, 0);
1240                self.silk_enc
1241                    .stereo
1242                    .side
1243                    .copy_from_slice(&self.buf_stereo_side[..ds_len]);
1244                &self.buf_stereo_mid
1245            } else if mode == OpusMode::SilkOnly && self.sampling_rate > 16000 {
1246                if self.sampling_rate == 48000 {
1247                    // 48k -> 16k via the same direct FIR the Hybrid path uses. The
1248                    // old down2 + down2_3 two-stage chain ALIASES: a 1 kHz sine
1249                    // came out with a 7 kHz mirror at ~1/3 amplitude (spectrum-
1250                    // verified), wrecking every SILK-only encode from 48 kHz input.
1251                    let silk_frame_size = frame_size / 3;
1252                    self.buf_silk_input.resize(silk_frame_size, 0);
1253                    if let Some(r) = &mut self.down_fir_l {
1254                        r.process(&mut self.buf_silk_input, input_i16);
1255                    }
1256                    &self.buf_silk_input
1257                } else if self.sampling_rate == 24000 {
1258                    let silk_frame_size = frame_size * 2 / 3;
1259                    self.buf_silk_input.resize(silk_frame_size, 0);
1260                    if let Some(r) = &mut self.down_fir_l {
1261                        r.process(&mut self.buf_silk_input, input_i16);
1262                    }
1263                    &self.buf_silk_input
1264                } else {
1265                    input_i16
1266                }
1267            } else if mode == OpusMode::Hybrid && self.sampling_rate > 16000 {
1268                let silk_frame_size = if self.sampling_rate == 48000 {
1269                    frame_size / 3
1270                } else {
1271                    frame_size * 2 / 3
1272                };
1273                self.buf_silk_input.resize(silk_frame_size, 0);
1274                if let Some(r) = &mut self.down_fir_l {
1275                    r.process(&mut self.buf_silk_input, input_i16);
1276                }
1277                &self.buf_silk_input
1278            } else {
1279                input_i16
1280            };
1281
1282            drop(_prof_rs);
1283
1284            let mut pn_bytes = 0;
1285
1286            // The frames-per-second math below divides by silk_input.len(), which is
1287            // at the SILK-INTERNAL rate — so the rate here must be internal too.
1288            // Using the API rate at 48 kHz told SILK to target 3x the real budget
1289            // with a hard max_bits cap -> the gain loop crushed every frame to fit
1290            // -> near-silent output (only worked at 16 kHz API where they coincide).
1291            let silk_rate_for_calc = if mode == OpusMode::Hybrid {
1292                16000
1293            } else {
1294                self.sampling_rate.min(16000)
1295            };
1296            let silk_frame_len = silk_input.len();
1297
1298            let silk_bitrate = if mode == OpusMode::Hybrid {
1299                let frame_duration_ms = frame_size as i32 * 1000 / self.sampling_rate;
1300                let frame20ms = frame_duration_ms >= 20;
1301                compute_silk_rate_for_hybrid(self.bitrate_bps, curr_bw, frame20ms, !self.use_cbr)
1302            } else if self.use_cbr {
1303                (8i64 * (n_bytes - 1) as i64 * silk_rate_for_calc as i64 / silk_frame_len as i64)
1304                    as i32
1305            } else {
1306                // VBR: n_bytes is only the buffer cap; target the configured rate.
1307                self.bitrate_bps
1308            };
1309            let silk_max_bits = if mode == OpusMode::Hybrid {
1310                let total_max_bits = ((n_bytes - 1) * 8) as i32;
1311                if self.use_cbr {
1312                    let silk_bits = (silk_bitrate as i64 * silk_frame_len as i64
1313                        / silk_rate_for_calc as i64) as i32;
1314                    let other_bits = 0i32.max(total_max_bits - silk_bits);
1315                    0i32.max(total_max_bits - other_bits * 3 / 4)
1316                } else {
1317                    let frame_duration_ms = frame_size as i32 * 1000 / self.sampling_rate;
1318                    let frame20ms = frame_duration_ms >= 20;
1319                    let max_bit_rate = compute_silk_rate_for_hybrid(
1320                        total_max_bits * self.sampling_rate / frame_size as i32,
1321                        curr_bw,
1322                        frame20ms,
1323                        !self.use_cbr,
1324                    );
1325                    max_bit_rate * frame_size as i32 / self.sampling_rate
1326                }
1327            } else {
1328                ((n_bytes - 1) * 8) as i32
1329            };
1330            let silk_use_cbr = if mode == OpusMode::Hybrid && self.use_cbr {
1331                0
1332            } else if self.use_cbr {
1333                1
1334            } else {
1335                0
1336            };
1337            let ret = silk_encode(
1338                &mut self.silk_enc,
1339                silk_input,
1340                silk_input.len(),
1341                &mut self.rc,
1342                &mut pn_bytes,
1343                silk_bitrate,
1344                silk_max_bits,
1345                silk_use_cbr,
1346                1,
1347            );
1348            if ret != 0 {
1349                return Err("SILK encoding failed");
1350            }
1351        }
1352
1353        // The hybrid redundancy flag is only present when >=37 bits remain
1354        // (opus_encoder.c: ec_tell+17+20 <= 8*(max_data_bytes-1)); the decoder
1355        // gates its read identically. Writing it unconditionally desynced every
1356        // frame where SILK left fewer than 37 bits (starved low-rate hybrid).
1357        if mode == OpusMode::Hybrid && self.rc.tell() + 37 <= ((n_bytes - 1) * 8) as i32 {
1358            self.rc.encode_bit_logp(false, 12); // redundancy = 0
1359        }
1360
1361        if mode == OpusMode::Hybrid {
1362            let nb_compr_bytes = (n_bytes - 1) as u32;
1363            self.rc.shrink(nb_compr_bytes);
1364        }
1365
1366        let silk_ret_bytes = if mode == OpusMode::SilkOnly {
1367            ((self.rc.tell() + 7) >> 3) as usize
1368        } else {
1369            0
1370        };
1371
1372        if mode == OpusMode::CeltOnly || mode == OpusMode::Hybrid {
1373            self.celt_enc.analysis = celt::AnalysisInfo {
1374                valid: analysis_info.valid,
1375                tonality: analysis_info.tonality,
1376                tonality_slope: analysis_info.tonality_slope,
1377                noisiness: analysis_info.noisiness,
1378                activity: analysis_info.activity,
1379                music_prob: analysis_info.music_prob,
1380                music_prob_min: analysis_info.music_prob_min,
1381                music_prob_max: analysis_info.music_prob_max,
1382                bandwidth: analysis_info.bandwidth,
1383                activity_probability: analysis_info.activity_probability,
1384                max_pitch_ratio: analysis_info.max_pitch_ratio,
1385                leak_boost: analysis_info.leak_boost,
1386            };
1387            self.celt_enc.complexity = self.complexity;
1388            self.celt_enc.lsb_depth = self.lsb_depth;
1389            // Census 2026-08-07 fix: loss_rate was never assigned, so CELT's
1390            // prefilter loss ladder (celt.rs) and coarse-energy intra bias were
1391            // dead even with OPUS_SET_PACKET_LOSS_PERC set. Default 0 = no
1392            // change on the default path (libopus opus_encoder.c parity).
1393            self.celt_enc.loss_rate = self.packet_loss_perc;
1394            let start_band = if mode == OpusMode::Hybrid { 17 } else { 0 };
1395            // CELT end band from the coded bandwidth (mirrors the decoder's
1396            // celt_endband_for_bandwidth): NB->13, MB/WB->17, SWB->19, FB->21.
1397            let end_band = match self.bandwidth {
1398                Bandwidth::Narrowband => 13,
1399                Bandwidth::Mediumband | Bandwidth::Wideband => 17,
1400                Bandwidth::Superwideband => 19,
1401                _ => 21,
1402            };
1403            let total_packet_bits = ((n_bytes - 1) * 8) as i32;
1404            // VBR: hand CELT the target in eighth-bits per frame; it picks the
1405            // frame's size (compute_vbr) and shrinks the range coder to it. The
1406            // hybrid target covers the whole packet (CELT adds back the SILK
1407            // bits via `target += tell`).
1408            self.celt_enc.vbr_rate = if self.use_cbr {
1409                0
1410            } else {
1411                let den = self.sampling_rate >> 3; // Fs >> BITRES
1412                ((self.bitrate_bps as i64 * frame_size as i64 + (den >> 1) as i64)
1413                    / den as i64) as i32
1414            };
1415
1416            let celt_input: &[f32] = if self.channels == 1 {
1417                input
1418            } else {
1419                let n = frame_size * self.channels;
1420                self.buf_celt_input.resize(n, 0.0);
1421                for i in 0..frame_size {
1422                    for ch in 0..self.channels {
1423                        self.buf_celt_input[ch * frame_size + i] = input[i * self.channels + ch];
1424                    }
1425                }
1426                &self.buf_celt_input
1427            };
1428
1429            if self.rc.tell() <= total_packet_bits {
1430                self.celt_enc.encode_with_budget(
1431                    celt_input,
1432                    frame_size,
1433                    &mut self.rc,
1434                    start_band,
1435                    end_band,
1436                    total_packet_bits,
1437                );
1438            }
1439        }
1440
1441        self.rc.done();
1442        self.range_final = self.rc.rng;
1443
1444        if mode == OpusMode::SilkOnly {
1445            let mut ret = silk_ret_bytes.min(self.rc.storage as usize);
1446            while ret > 2 && self.rc.buf[ret - 1] == 0 {
1447                ret -= 1;
1448            }
1449
1450            let target_total = if self.use_cbr {
1451                n_bytes.min(output.len())
1452            } else {
1453                (ret + 1).min(output.len())
1454            };
1455
1456            let silk_len = ret;
1457
1458            if !self.use_cbr || silk_len + 1 >= target_total {
1459                // VBR or payload fills the target: simple code 0 packet
1460                output[0] = toc;
1461                let copy_len = silk_len.min(target_total - 1);
1462                output[1..1 + copy_len].copy_from_slice(&self.rc.buf[..copy_len]);
1463                return Ok((copy_len + 1).min(output.len()));
1464            }
1465
1466            output[0] = toc | 0x03;
1467
1468            if silk_len + 2 >= target_total {
1469                output[1] = 0x01;
1470                let copy_len = (target_total - 2).min(silk_len);
1471                output[2..2 + copy_len].copy_from_slice(&self.rc.buf[..copy_len]);
1472                self.prev_enc_mode = Some(mode);
1473                return Ok(target_total.min(output.len()));
1474            }
1475
1476            let pad_amount = target_total - silk_len - 2;
1477            output[1] = 0x41;
1478
1479            let nb_255s = (pad_amount - 1) / 255;
1480            let mut ptr = 2;
1481            for _ in 0..nb_255s {
1482                output[ptr] = 255;
1483                ptr += 1;
1484            }
1485            output[ptr] = (pad_amount - 255 * nb_255s - 1) as u8;
1486            ptr += 1;
1487
1488            output[ptr..ptr + silk_len].copy_from_slice(&self.rc.buf[..silk_len]);
1489            ptr += silk_len;
1490
1491            let fill_end = target_total.min(output.len());
1492            for byte in output[ptr..fill_end].iter_mut() {
1493                *byte = 0;
1494            }
1495
1496            self.prev_enc_mode = Some(mode);
1497            return Ok(target_total.min(output.len()));
1498        }
1499
1500        // CBR: fixed payload. VBR (CELT/hybrid): the CELT layer shrank the coder
1501        // to this frame's chosen size — emit exactly that many payload bytes.
1502        let payload_len = if self.use_cbr {
1503            n_bytes - 1
1504        } else {
1505            (self.rc.storage as usize).min(n_bytes - 1)
1506        };
1507        output[1..1 + payload_len].copy_from_slice(&self.rc.buf[..payload_len]);
1508        // Great Gate harvest tap (observe-only; see the field doc). Signals are
1509        // recomputed read-only here — the decision code above is untouched.
1510        if self.gate_tap.is_some() {
1511            let equiv = compute_equiv_rate(
1512                self.bitrate_bps,
1513                self.channels,
1514                frame_rate,
1515                !self.use_cbr,
1516                self.complexity,
1517                self.packet_loss_perc,
1518            );
1519            let voice_est = self.compute_voice_est();
1520            let (clip, frame) = (self.gate_clip.clone(), self.gate_frame);
1521            if let Some(tap) = self.gate_tap.as_mut() {
1522                use std::io::Write as _;
1523                let mode_s = match mode {
1524                    OpusMode::SilkOnly => "silk",
1525                    OpusMode::CeltOnly => "celt",
1526                    OpusMode::Hybrid => "hybrid",
1527                };
1528                let _ = writeln!(
1529                    tap,
1530                    "{},{},{},{},{},{},{},{},{},{},{},{},{:.4},{:.4},{:.4},{:.4},{:.4},{:.4},{:.4},{},{:.4},{}",
1531                    clip,
1532                    frame,
1533                    mode_s,
1534                    self.bandwidth as i32,
1535                    self.channels,
1536                    self.bitrate_bps,
1537                    self.complexity,
1538                    equiv,
1539                    voice_est,
1540                    is_silence as u8,
1541                    activity as u8,
1542                    analysis_info.valid as u8,
1543                    analysis_info.tonality,
1544                    analysis_info.tonality_slope,
1545                    analysis_info.noisiness,
1546                    analysis_info.activity_probability,
1547                    analysis_info.music_prob,
1548                    analysis_info.music_prob_min,
1549                    analysis_info.music_prob_max,
1550                    self.detected_bandwidth,
1551                    analysis_info.max_pitch_ratio,
1552                    1 + payload_len,
1553                );
1554            }
1555        }
1556        self.gate_frame += 1;
1557
1558        self.prev_enc_mode = Some(mode);
1559        Ok(1 + payload_len)
1560    }
1561}
1562
1563pub struct OpusDecoder {
1564    celt_dec: CeltDecoder,
1565    silk_dec: silk::dec_api::SilkDecoder,
1566    sampling_rate: i32,
1567    channels: usize,
1568
1569    prev_mode: Option<OpusMode>,
1570    frame_size: usize,
1571
1572    bandwidth: Bandwidth,
1573
1574    stream_channels: usize,
1575
1576    silk_resampler: silk::resampler::SilkResampler,
1577    // Second resampler for the SILK stereo right channel (L uses silk_resampler).
1578    silk_resampler_r: silk::resampler::SilkResampler,
1579
1580    prev_internal_rate: i32,
1581
1582    pub hybrid_skip_celt: bool,
1583
1584    w_pcm_i16: Vec<i16>,
1585    w_silk_out: Vec<f32>,
1586    w_pcm_resampled: Vec<i16>,
1587    w_celt_planar: Vec<f32>,
1588    w_celt_out: Vec<f32>,
1589
1590    // SILK per-frame history: libopus prepends the previous frame's last two
1591    // decoded samples (`sStereo.sMid`) and feeds the resampler from offset 1, a
1592    // 1-internal-sample delay line. Replicated here so our SILK output aligns
1593    // with the reference across every bandwidth (was leading by 1 internal
1594    // sample = 3/4/6 output samples at WB/MB/NB).
1595    silk_s_mid: [i16; 2],
1596
1597    // Range decoder final `rng` from the last decoded frame (conformance/desync
1598    // diagnostic: compare against the encoder's stored final range).
1599    pub last_range: u32,
1600
1601    // Auxiliary decoder for packets whose channel count differs from ours
1602    // (a stream may switch between mono and stereo). It decodes at the packet's
1603    // native channel count; we then up/downmix to our output count. Persistent
1604    // so the "other" channel mode keeps its own inter-frame state.
1605    aux: Option<Box<OpusDecoder>>,
1606    // Set when a packet was just decoded by the aux (a mono packet in a stereo
1607    // stream); triggers seeding the primary CELT decoder's overlap/energy state
1608    // from the aux at the next primary (stereo) CELT/Hybrid packet, so the MDCT
1609    // overlap-add is continuous across the mono->stereo switch.
1610    prev_used_aux: bool,
1611    // libopus st->prev_redundancy: the previous frame carried a SILK->CELT
1612    // redundant frame (redundancy && !celt_to_silk). Suppresses the CELT reset on
1613    // the following mode change (the redundant frame already primed CELT state).
1614    prev_redundancy: bool,
1615}
1616
1617impl OpusDecoder {
1618    pub fn new(sampling_rate: i32, channels: usize) -> Result<Self, &'static str> {
1619        if ![8000, 12000, 16000, 24000, 48000].contains(&sampling_rate) {
1620            return Err("Invalid sampling rate");
1621        }
1622        if ![1, 2].contains(&channels) {
1623            return Err("Invalid number of channels");
1624        }
1625
1626        let mode = modes::default_mode();
1627        let celt_dec = CeltDecoder::new(mode, channels);
1628
1629        let mut silk_dec = silk::dec_api::SilkDecoder::new();
1630        silk_dec.init(sampling_rate.min(16000), channels as i32);
1631        silk_dec.channel_state[0].fs_api_hz = sampling_rate;
1632
1633        Ok(Self {
1634            celt_dec,
1635            silk_dec,
1636            sampling_rate,
1637            channels,
1638            prev_mode: None,
1639            frame_size: 0,
1640            bandwidth: Bandwidth::Auto,
1641            stream_channels: channels,
1642            silk_resampler: silk::resampler::SilkResampler::default(),
1643            silk_resampler_r: silk::resampler::SilkResampler::default(),
1644            prev_internal_rate: 0,
1645            hybrid_skip_celt: false,
1646
1647            // SILK internal scratch: max frame is 60 ms at the 16 kHz WB internal
1648            // rate (960 samples/ch), i.e. 1920 stereo. Sized like the sibling
1649            // buffers below for headroom — the old fixed 640 overflowed on any
1650            // 60 ms SILK frame (panic decoding valid streams).
1651            w_pcm_i16: vec![0i16; 5760 * channels],
1652
1653            w_silk_out: vec![0.0f32; 5760 * channels],
1654            w_pcm_resampled: vec![0i16; 5760 * channels],
1655            w_celt_planar: vec![0.0f32; 5760 * channels],
1656            w_celt_out: vec![0.0f32; 5760 * channels],
1657            silk_s_mid: [0; 2],
1658            last_range: 0,
1659            aux: None,
1660            prev_used_aux: false,
1661            prev_redundancy: false,
1662        })
1663    }
1664
1665    /// Packet-loss concealment for a lost frame (empty/None packet). Runs the
1666    /// SILK PLC (LTP+LPC extrapolation) for the last-known SILK/hybrid mode and
1667    /// resamples to the output rate. CELT-only loss has no CELT PLC yet, so it
1668    /// yields silence (a documented Tier-1 follow-up); the SILK path covers the
1669    /// dominant VoIP case. Mono conceal is duplicated to both channels on a
1670    /// stereo output.
1671    fn decode_plc(
1672        &mut self,
1673        frame_size: usize,
1674        output: &mut [f32],
1675    ) -> Result<usize, &'static str> {
1676        let out_samples = frame_size * self.channels;
1677        for v in output.iter_mut().take(out_samples) {
1678            *v = 0.0;
1679        }
1680        let mode = self.prev_mode.unwrap_or(OpusMode::SilkOnly);
1681        if mode == OpusMode::CeltOnly {
1682            // CELT packet-loss concealment (noise-based celt_decode_lost): real
1683            // attenuating audio instead of silence.
1684            self.celt_dec.conceal_lost(frame_size, output);
1685            self.prev_mode = Some(mode);
1686            return Ok(frame_size);
1687        }
1688
1689        let frame_ms = (frame_size as i32 * 1000 / self.sampling_rate).max(1);
1690        let internal_rate = if mode == OpusMode::Hybrid {
1691            16000
1692        } else {
1693            match self.bandwidth {
1694                Bandwidth::Narrowband => 8000,
1695                Bandwidth::Mediumband => 12000,
1696                _ => 16000,
1697            }
1698        };
1699        if self.sampling_rate != internal_rate && internal_rate != self.prev_internal_rate {
1700            self.silk_resampler.init(internal_rate, self.sampling_rate);
1701            self.prev_internal_rate = internal_rate;
1702        }
1703        let n_silk = match frame_ms {
1704            40 => 2,
1705            60 => 3,
1706            _ => 1,
1707        };
1708        let internal_frame = (frame_ms * internal_rate / 1000) as usize;
1709        let internal_sub = internal_frame / n_silk.max(1);
1710        let ratio = self.sampling_rate as f64 / internal_rate as f64;
1711        // Conceal mono only (the SILK low band); stereo output duplicates it.
1712        self.silk_dec.produce_lr = false;
1713        self.silk_dec.n_channels_internal = 1;
1714
1715        let mut off = 0usize; // output samples/ch written so far
1716        for sf in 0..n_silk {
1717            let mut rc = RangeCoder::new_decoder(&[]);
1718            let n16 = internal_sub;
1719            if n16 + 2 > self.w_pcm_i16.len() {
1720                return Err("opus PLC: frame exceeds buffer");
1721            }
1722            self.w_pcm_i16[0] = self.silk_s_mid[0];
1723            self.w_pcm_i16[1] = self.silk_s_mid[1];
1724            let ret = self.silk_dec.decode(
1725                &mut rc,
1726                &mut self.w_pcm_i16[2..n16 + 2],
1727                silk::decode_frame::FLAG_PACKET_LOST,
1728                sf == 0,
1729                frame_ms,
1730                internal_rate,
1731            );
1732            if ret < 0 {
1733                return Err("SILK PLC failed");
1734            }
1735            let dec = ret as usize;
1736            if dec >= 2 {
1737                self.silk_s_mid[0] = self.w_pcm_i16[dec];
1738                self.silk_s_mid[1] = self.w_pcm_i16[dec + 1];
1739            }
1740            let base = off * self.channels;
1741            let out_len = if self.sampling_rate == internal_rate {
1742                for i in 0..dec {
1743                    let v = self.w_pcm_i16[1 + i] as f32 / 32768.0;
1744                    for ch in 0..self.channels {
1745                        let idx = base + i * self.channels + ch;
1746                        if idx < output.len() {
1747                            output[idx] = v;
1748                        }
1749                    }
1750                }
1751                dec
1752            } else {
1753                let out_len = (dec as f64 * ratio) as usize;
1754                let src: Vec<i16> = self.w_pcm_i16[1..1 + dec].to_vec();
1755                self.silk_resampler
1756                    .process(&mut self.w_pcm_resampled[..out_len], &src, dec as i32);
1757                for i in 0..out_len {
1758                    let v = self.w_pcm_resampled[i] as f32 / 32768.0;
1759                    for ch in 0..self.channels {
1760                        let idx = base + i * self.channels + ch;
1761                        if idx < output.len() {
1762                            output[idx] = v;
1763                        }
1764                    }
1765                }
1766                out_len
1767            };
1768            off += out_len;
1769        }
1770        self.prev_mode = Some(mode);
1771        Ok(frame_size)
1772    }
1773
1774    /// Forward-error-correction decode: reconstruct a LOST frame from the LBRR
1775    /// (low-bitrate redundancy) embedded in the NEXT received `packet`. Drives
1776    /// the SILK decoder in FLAG_DECODE_LBRR mode, which self-selects: it decodes
1777    /// the redundant frame when the packet carries LBRR for it, and falls back
1778    /// to PLC extrapolation when it doesn't. CELT-only or multi-frame packets
1779    /// fall back to plain PLC (no SILK LBRR to recover). After this call the
1780    /// caller decodes `packet` normally for the following frame.
1781    pub fn decode_fec(
1782        &mut self,
1783        packet: &[u8],
1784        frame_size: usize,
1785        output: &mut [f32],
1786    ) -> Result<usize, &'static str> {
1787        if packet.is_empty() {
1788            return self.decode_plc(frame_size, output);
1789        }
1790        let toc = packet[0];
1791        let mode = mode_from_toc(toc);
1792        // FEC only lives in SILK/hybrid low band; code-0 (single frame) only.
1793        if mode == OpusMode::CeltOnly || (toc & 0x03) != 0 {
1794            return self.decode_plc(frame_size, output);
1795        }
1796        let bandwidth = bandwidth_from_toc(toc);
1797        let payload = &packet[1..];
1798
1799        let out_samples = frame_size * self.channels;
1800        for v in output.iter_mut().take(out_samples) {
1801            *v = 0.0;
1802        }
1803        let frame_ms = (frame_size as i32 * 1000 / self.sampling_rate).max(1);
1804        let internal_rate = if mode == OpusMode::Hybrid {
1805            16000
1806        } else {
1807            match bandwidth {
1808                Bandwidth::Narrowband => 8000,
1809                Bandwidth::Mediumband => 12000,
1810                _ => 16000,
1811            }
1812        };
1813        if self.sampling_rate != internal_rate && internal_rate != self.prev_internal_rate {
1814            self.silk_resampler.init(internal_rate, self.sampling_rate);
1815            self.prev_internal_rate = internal_rate;
1816        }
1817        let internal_frame = (frame_ms * internal_rate / 1000) as usize;
1818        let ratio = self.sampling_rate as f64 / internal_rate as f64;
1819        self.silk_dec.produce_lr = false;
1820        self.silk_dec.n_channels_internal = 1;
1821
1822        let mut rc = RangeCoder::new_decoder(payload);
1823        let n16 = internal_frame;
1824        if n16 + 2 > self.w_pcm_i16.len() {
1825            return Err("opus FEC: frame exceeds buffer");
1826        }
1827        self.w_pcm_i16[0] = self.silk_s_mid[0];
1828        self.w_pcm_i16[1] = self.silk_s_mid[1];
1829        let ret = self.silk_dec.decode(
1830            &mut rc,
1831            &mut self.w_pcm_i16[2..n16 + 2],
1832            silk::decode_frame::FLAG_DECODE_LBRR,
1833            true,
1834            frame_ms,
1835            internal_rate,
1836        );
1837        if ret < 0 {
1838            return Err("SILK FEC failed");
1839        }
1840        let dec = ret as usize;
1841        if dec >= 2 {
1842            self.silk_s_mid[0] = self.w_pcm_i16[dec];
1843            self.silk_s_mid[1] = self.w_pcm_i16[dec + 1];
1844        }
1845        if self.sampling_rate == internal_rate {
1846            for i in 0..dec {
1847                let v = self.w_pcm_i16[1 + i] as f32 / 32768.0;
1848                for ch in 0..self.channels {
1849                    let idx = i * self.channels + ch;
1850                    if idx < output.len() {
1851                        output[idx] = v;
1852                    }
1853                }
1854            }
1855        } else {
1856            let out_len = (dec as f64 * ratio) as usize;
1857            let src: Vec<i16> = self.w_pcm_i16[1..1 + dec].to_vec();
1858            self.silk_resampler
1859                .process(&mut self.w_pcm_resampled[..out_len], &src, dec as i32);
1860            for i in 0..out_len {
1861                let v = self.w_pcm_resampled[i] as f32 / 32768.0;
1862                for ch in 0..self.channels {
1863                    let idx = i * self.channels + ch;
1864                    if idx < output.len() {
1865                        output[idx] = v;
1866                    }
1867                }
1868            }
1869        }
1870        self.prev_mode = Some(mode);
1871        Ok(frame_size)
1872    }
1873
1874    pub fn decode(
1875        &mut self,
1876        input: &[u8],
1877        frame_size: usize,
1878        output: &mut [f32],
1879    ) -> Result<usize, &'static str> {
1880        // Lost packet (data==NULL / empty) -> packet-loss concealment.
1881        if input.is_empty() {
1882            return self.decode_plc(frame_size, output);
1883        }
1884
1885        let toc = input[0];
1886        let mode = mode_from_toc(toc);
1887        let packet_channels = channels_from_toc(toc);
1888        let bandwidth = bandwidth_from_toc(toc);
1889        let frame_duration_ms = frame_duration_ms_from_toc(toc);
1890
1891        // A mono SILK packet inside a stereo stream is decoded through the PRIMARY
1892        // decoder (unified path), not a separate aux — the aux's SILK/resampler
1893        // state is blind to the interleaved stereo packets, so its state is stale
1894        // at every mono<->stereo switch. libopus keeps ONE decoder whose channel-0
1895        // resampler and stereo state run continuously across the switches.
1896        // A mono packet of ANY mode in a stereo stream decodes through the PRIMARY
1897        // (unified path) so inter-frame state stays one continuous chain across
1898        // mono<->stereo switches — SILK resampler/stereo state; CELT (and the
1899        // redundant/silence transition frames) via stream_channels=1 (C=1/CC=2) —
1900        // matching libopus's single decoder.
1901        let mono_in_stereo = packet_channels == 1 && self.channels == 2;
1902
1903        if packet_channels != self.channels && !mono_in_stereo {
1904            // The packet's channel count differs from ours (a stream can switch
1905            // between mono and stereo). Decode it at its native channel count in
1906            // a persistent auxiliary decoder, then render to our output count:
1907            // mono->stereo duplicates, stereo->mono averages the two channels.
1908            if self
1909                .aux
1910                .as_ref()
1911                .map(|a| a.channels != packet_channels)
1912                .unwrap_or(true)
1913            {
1914                self.aux = Some(Box::new(OpusDecoder::new(
1915                    self.sampling_rate,
1916                    packet_channels,
1917                )?));
1918            }
1919            // Reverse of the mono->stereo seed: on a stereo->mono switch, seed the
1920            // aux (mono) CELT decoder from the primary (stereo channel 0) so its
1921            // MDCT-overlap/energy state is continuous with the preceding stereo
1922            // packets (the primary was the continuous decoder during them).
1923            if !self.prev_used_aux
1924                && packet_channels == 1
1925                && self.channels == 2
1926                && (mode == OpusMode::CeltOnly || mode == OpusMode::Hybrid)
1927            {
1928                let (aux_opt, primary) = (&mut self.aux, &self.celt_dec);
1929                if let Some(aux) = aux_opt.as_mut() {
1930                    aux.celt_dec.seed_from(primary);
1931                }
1932            }
1933            let aux = self.aux.as_mut().unwrap();
1934            let mut buf = vec![0.0f32; frame_size * packet_channels];
1935            let n = aux.decode(input, frame_size, &mut buf)?;
1936            self.last_range = aux.last_range;
1937            if packet_channels == 1 && self.channels == 2 {
1938                for i in 0..n {
1939                    let v = buf[i];
1940                    output[2 * i] = v;
1941                    output[2 * i + 1] = v;
1942                }
1943            } else if packet_channels == 2 && self.channels == 1 {
1944                for i in 0..n {
1945                    output[i] = 0.5 * (buf[2 * i] + buf[2 * i + 1]);
1946                }
1947            } else {
1948                let m = (n * self.channels).min(output.len()).min(buf.len());
1949                output[..m].copy_from_slice(&buf[..m]);
1950            }
1951            self.prev_mode = Some(mode);
1952            self.prev_used_aux = true;
1953            return Ok(n);
1954        }
1955
1956        // First primary (native-channel) packet after a run of aux (mono-in-stereo)
1957        // packets: seed the primary CELT decoder's inter-frame state from the aux
1958        // so the mono->stereo MDCT overlap-add is continuous (matches libopus's
1959        // single continuous decoder). SILK carries its own state through the
1960        // primary already; this is for the CELT/Hybrid high band.
1961        if self.prev_used_aux {
1962            self.prev_used_aux = false;
1963            if (mode == OpusMode::CeltOnly || mode == OpusMode::Hybrid) && self.channels == 2 {
1964                if let Some(aux) = self.aux.as_ref() {
1965                    self.celt_dec.seed_from(&aux.celt_dec);
1966                }
1967            }
1968        }
1969
1970        let code = toc & 0x03;
1971        let frame_count: usize;
1972        let frame_payloads: Vec<&[u8]>;
1973
1974        match code {
1975            0 => {
1976                frame_count = 1;
1977                frame_payloads = vec![&input[1..]];
1978            }
1979            1 => {
1980                frame_count = 2;
1981                let half = (input.len() - 1) / 2;
1982                if half == 0 {
1983                    return Err("Code 1: empty frame");
1984                }
1985                frame_payloads = vec![&input[1..1 + half], &input[1 + half..]];
1986            }
1987            2 => {
1988                frame_count = 2;
1989                let data = &input[1..];
1990                if data.is_empty() {
1991                    return Err("Code 2 packet has no data");
1992                }
1993                let (first_len, header_size) = read_opus_frame_len(data, 0)?;
1994                if header_size + first_len > data.len() {
1995                    return Err("Code 2: first frame size exceeds packet");
1996                }
1997                frame_payloads = vec![
1998                    &data[header_size..header_size + first_len],
1999                    &data[header_size + first_len..],
2000                ];
2001            }
2002            3 => {
2003                // RFC 6716 §3.2.5. Frame-count byte: bit 7 = VBR flag, bit 6 =
2004                // padding flag, bits 5..0 = frame count M. VBR and padding are
2005                // independent; the earlier code conflated them (and used a
2006                // non-standard length coding), which mis-parsed CBR and padded
2007                // packets — exactly what the RFC test vectors exercise.
2008                if input.len() < 2 {
2009                    return Err("Code 3 packet too short");
2010                }
2011                let count_byte = input[1];
2012                let m = (count_byte & 0x3F) as usize;
2013                if m < 1 || m > 48 {
2014                    return Err("Code 3: invalid frame count");
2015                }
2016                // libopus opus.c opus_packet_parse_impl (code 3):
2017                //   if (count <= 0 || framesize*(opus_int32)count > 5760)
2018                //      return OPUS_INVALID_PACKET;
2019                // (framesize at 48 kHz; 5760 = 120 ms, the RFC 6716 packet cap.)
2020                // A hostile frame count past this cap would otherwise shrink our
2021                // per-frame size below the redundancy-fade windows further down.
2022                if m as i32 * repacketizer::samples_per_frame(toc, 48000) > 5760 {
2023                    return Err("Code 3: packet duration exceeds 120 ms");
2024                }
2025                frame_count = m;
2026                let vbr = (count_byte & 0x80) != 0;
2027                let padding = (count_byte & 0x40) != 0;
2028
2029                // Padding length indicator bytes follow the count byte; the
2030                // padding data itself sits at the end of the packet.
2031                let mut ptr = 2usize;
2032                let mut pad_len = 0usize;
2033                if padding {
2034                    loop {
2035                        let p = *input.get(ptr).ok_or("Code 3: padding overflow")? as usize;
2036                        ptr += 1;
2037                        if p == 255 {
2038                            pad_len += 254;
2039                        } else {
2040                            pad_len += p;
2041                            break;
2042                        }
2043                    }
2044                }
2045                let end = input
2046                    .len()
2047                    .checked_sub(pad_len)
2048                    .ok_or("Code 3: padding exceeds packet")?;
2049                if ptr > end {
2050                    return Err("Code 3: padding exceeds packet");
2051                }
2052                // Frame-data region, with the length headers (VBR) at its front
2053                // and the trailing padding already excluded.
2054                let region = &input[ptr..end];
2055
2056                if vbr {
2057                    // M-1 explicit frame lengths, contiguous, then the frame
2058                    // data; the last frame is the remainder.
2059                    let mut lens = Vec::with_capacity(m.saturating_sub(1));
2060                    let mut hp = 0usize;
2061                    for _ in 0..m - 1 {
2062                        let (l, nb) = read_opus_frame_len(region, hp)?;
2063                        hp += nb;
2064                        lens.push(l);
2065                    }
2066                    let mut payloads = Vec::with_capacity(m);
2067                    let mut fp = hp;
2068                    for &l in &lens {
2069                        if fp + l > region.len() {
2070                            return Err("Code 3 VBR: frame length exceeds packet");
2071                        }
2072                        payloads.push(&region[fp..fp + l]);
2073                        fp += l;
2074                    }
2075                    if fp > region.len() {
2076                        return Err("Code 3 VBR: no data for last frame");
2077                    }
2078                    payloads.push(&region[fp..]);
2079                    frame_payloads = payloads;
2080                } else {
2081                    // CBR: the region splits into M equal frames (possibly all
2082                    // empty, e.g. DTX).
2083                    if region.len() % m != 0 {
2084                        return Err("Code 3 CBR: frame data not divisible by frame count");
2085                    }
2086                    let frame_len = region.len() / m;
2087                    frame_payloads = (0..m)
2088                        .map(|i| &region[i * frame_len..(i + 1) * frame_len])
2089                        .collect();
2090                }
2091            }
2092            _ => unreachable!(),
2093        }
2094
2095        // libopus opus_decoder.c opus_decode_native:
2096        //   if (count*packet_frame_size > frame_size)
2097        //      return OPUS_BUFFER_TOO_SMALL;
2098        // The packet's own TOC duration must fit the caller's frame_size. We split
2099        // the caller's buffer as sub_frame_size = frame_size / frame_count, so a
2100        // malformed multi-frame packet (large frame count vs. a small caller
2101        // buffer) would otherwise make sub_frame_size smaller than the 2.5/5 ms
2102        // redundancy-fade region — the fuzzer-found out-of-bounds/underflow panics
2103        // in redundancy_fade_start/redundancy_fade_end. C rejects such packets
2104        // here; so do we.
2105        let packet_frame_samples =
2106            repacketizer::samples_per_frame(toc, self.sampling_rate) as usize;
2107        if frame_count * packet_frame_samples > frame_size {
2108            return Err("Output buffer too small");
2109        }
2110
2111        self.frame_size = frame_size;
2112        self.bandwidth = bandwidth;
2113        self.stream_channels = packet_channels;
2114
2115        let sub_frame_size = frame_size / frame_count;
2116        let sub_output_len = sub_frame_size * self.channels;
2117
2118        match mode {
2119            OpusMode::SilkOnly => {
2120                let internal_sample_rate = match bandwidth {
2121                    Bandwidth::Narrowband => 8000,
2122                    Bandwidth::Mediumband => 12000,
2123                    Bandwidth::Wideband => 16000,
2124                    _ => 16000,
2125                };
2126                let internal_frame_size =
2127                    (frame_duration_ms * internal_sample_rate / 1000) as usize;
2128
2129                if self.sampling_rate != internal_sample_rate
2130                    && internal_sample_rate != self.prev_internal_rate
2131                {
2132                    self.silk_resampler
2133                        .init(internal_sample_rate, self.sampling_rate);
2134                    self.silk_resampler_r
2135                        .init(internal_sample_rate, self.sampling_rate);
2136                    self.prev_internal_rate = internal_sample_rate;
2137                }
2138
2139                // Pure-SILK stereo (both stream and output are 2ch): reconstruct
2140                // true L/R via SILK MS->LR instead of duplicating the mono mid.
2141                let silk_lr = self.channels == 2 && packet_channels == 2;
2142                self.silk_dec.produce_lr = silk_lr;
2143
2144                // Per-packet internal channel switch (libopus dec_API.c:119-166).
2145                let prev_internal_ch = self.silk_dec.n_channels_internal;
2146                if packet_channels as i32 > prev_internal_ch {
2147                    // mono -> stereo: reset the side channel decoder.
2148                    silk::init_decoder::silk_init_decoder(
2149                        &mut self.silk_dec.channel_state[1],
2150                    );
2151                }
2152                if self.channels == 2 && packet_channels == 2 && prev_internal_ch == 1 {
2153                    // Switching to stereo: clear stereo prediction/side history and
2154                    // seed the right-channel resampler from the (continuous) left.
2155                    self.silk_dec.s_stereo_pred_prev_q13 = [0; 2];
2156                    self.silk_dec.s_stereo_side = [0; 2];
2157                    self.silk_resampler_r = self.silk_resampler.clone();
2158                }
2159                self.silk_dec.n_channels_internal = packet_channels as i32;
2160
2161                // A 40/60 ms Opus frame carries 2/3 internal 20 ms SILK frames;
2162                // 10/20 ms carry one. libopus calls silk_Decode once per internal
2163                // frame (continuing the same range coder within the payload). We
2164                // must too — decoding only the first internal frame leaves the
2165                // rest of a 40/60 ms packet silent (the "collapse" bug).
2166                let n_silk = match frame_duration_ms {
2167                    40 => 2,
2168                    60 => 3,
2169                    _ => 1,
2170                };
2171                let internal_sub_frame_size = internal_frame_size / n_silk;
2172                let ratio = self.sampling_rate as f64 / internal_sample_rate as f64;
2173                // Per-FRAME previous mode (libopus updates prev_mode per frame; for
2174                // payloads after the first, the previous frame is this same packet).
2175                let mut prev_mode_frame = self.prev_mode;
2176
2177                for (fi, payload) in frame_payloads.iter().enumerate() {
2178                    let mut rc = RangeCoder::new_decoder(payload);
2179                    let pcm_i16_len = internal_sub_frame_size * self.channels;
2180                    // A malformed packet can imply a frame larger than our scratch
2181                    // buffer; reject it gracefully instead of slicing out of bounds
2182                    // (a decode-path DoS on attacker-controlled input).
2183                    if pcm_i16_len + 2 > self.w_pcm_i16.len() {
2184                        return Err("opus: SILK frame size exceeds buffer");
2185                    }
2186                    let out_start = fi * sub_output_len;
2187                    let mut silk_off = 0usize; // output samples/ch within this Opus frame
2188
2189                    for sf in 0..n_silk {
2190                        let s_mid = self.silk_s_mid;
2191                        let ret = {
2192                            let (silk_dec, pcm_i16) = (&mut self.silk_dec, &mut self.w_pcm_i16);
2193                            // Prepend the previous frame's last two samples (sMid) at
2194                            // [0..2] and decode at offset 2, matching libopus's
2195                            // samplesOut1_tmp[n][2] layout.
2196                            pcm_i16[0] = s_mid[0];
2197                            pcm_i16[1] = s_mid[1];
2198                            silk_dec.decode(
2199                                &mut rc,
2200                                &mut pcm_i16[2..pcm_i16_len + 2],
2201                                silk::decode_frame::FLAG_DECODE_NORMAL,
2202                                sf == 0,
2203                                frame_duration_ms,
2204                                internal_sample_rate,
2205                            )
2206                        };
2207
2208                        if ret < 0 {
2209                            return Err("SILK decoding failed");
2210                        }
2211
2212                        let decoded_samples = ret as usize;
2213                        // Carry the last two decoded samples as next frame's sMid.
2214                        if decoded_samples >= 2 {
2215                            self.silk_s_mid[0] = self.w_pcm_i16[decoded_samples];
2216                            self.silk_s_mid[1] = self.w_pcm_i16[decoded_samples + 1];
2217                        }
2218                        let base = out_start + silk_off * self.channels;
2219
2220                        // Stereo SILK: L in silk_dec.l_out, R in silk_dec.r_out,
2221                        // both already in the 1-sample-delay-line layout. Resample
2222                        // each channel through its own resampler.
2223                        let out_len = if silk_lr {
2224                            if self.sampling_rate == internal_sample_rate {
2225                                for i in 0..decoded_samples {
2226                                    let l = self.silk_dec.l_out[i] as f32 / 32768.0;
2227                                    let r = self.silk_dec.r_out[i] as f32 / 32768.0;
2228                                    let idx = base + i * 2;
2229                                    if idx + 1 < output.len() {
2230                                        output[idx] = l;
2231                                        output[idx + 1] = r;
2232                                    }
2233                                }
2234                                decoded_samples
2235                            } else {
2236                                let out_len = (decoded_samples as f64 * ratio) as usize;
2237                                // Left
2238                                self.silk_resampler.process(
2239                                    &mut self.w_pcm_resampled[..out_len],
2240                                    &self.silk_dec.l_out[..decoded_samples],
2241                                    decoded_samples as i32,
2242                                );
2243                                for i in 0..out_len {
2244                                    let idx = base + i * 2;
2245                                    if idx < output.len() {
2246                                        output[idx] = self.w_pcm_resampled[i] as f32 / 32768.0;
2247                                    }
2248                                }
2249                                // Right (reuse the scratch)
2250                                self.silk_resampler_r.process(
2251                                    &mut self.w_pcm_resampled[..out_len],
2252                                    &self.silk_dec.r_out[..decoded_samples],
2253                                    decoded_samples as i32,
2254                                );
2255                                for i in 0..out_len {
2256                                    let idx = base + i * 2 + 1;
2257                                    if idx < output.len() {
2258                                        output[idx] = self.w_pcm_resampled[i] as f32 / 32768.0;
2259                                    }
2260                                }
2261                                out_len
2262                            }
2263                        } else if self.sampling_rate == internal_sample_rate {
2264                            let frames = decoded_samples;
2265                            for i in 0..frames {
2266                                let v = self.w_pcm_i16[1 + i] as f32 / 32768.0;
2267                                for ch in 0..self.channels {
2268                                    let idx = base + i * self.channels + ch;
2269                                    if idx < output.len() {
2270                                        output[idx] = v;
2271                                    }
2272                                }
2273                            }
2274                            frames
2275                        } else {
2276                            let out_len = (decoded_samples as f64 * ratio) as usize;
2277                            debug_assert!(out_len <= self.w_pcm_resampled.len());
2278                            {
2279                                let (silk_res, pcm_i16, pcm_out) = (
2280                                    &mut self.silk_resampler,
2281                                    &self.w_pcm_i16,
2282                                    &mut self.w_pcm_resampled,
2283                                );
2284                                silk_res.process(
2285                                    &mut pcm_out[..out_len],
2286                                    &pcm_i16[1..1 + decoded_samples],
2287                                    decoded_samples as i32,
2288                                );
2289                            }
2290                            for i in 0..out_len {
2291                                let v = self.w_pcm_resampled[i] as f32 / 32768.0;
2292                                for ch in 0..self.channels {
2293                                    let idx = base + i * self.channels + ch;
2294                                    if idx < output.len() {
2295                                        output[idx] = v;
2296                                    }
2297                                }
2298                            }
2299                            // Stereo output, mono packet: also run the mono signal
2300                            // through the RIGHT-channel resampler so its state stays
2301                            // continuous for the next stereo packet (libopus
2302                            // dec_API.c:351-355). Its output overwrites channel 1,
2303                            // which is numerically ~identical to the left here.
2304                            if self.channels == 2 {
2305                                self.silk_resampler_r.process(
2306                                    &mut self.w_pcm_resampled[..out_len],
2307                                    &self.w_pcm_i16[1..1 + decoded_samples],
2308                                    decoded_samples as i32,
2309                                );
2310                                for i in 0..out_len {
2311                                    let idx = base + i * 2 + 1;
2312                                    if idx < output.len() {
2313                                        output[idx] = self.w_pcm_resampled[i] as f32 / 32768.0;
2314                                    }
2315                                }
2316                            }
2317                            out_len
2318                        };
2319                        silk_off += out_len;
2320                    }
2321
2322                    // --- Opus redundancy layer (opus_decoder.c:420-580) ---
2323                    // A SILK-only frame carries IMPLICIT CELT redundancy: if >= 17
2324                    // bits remain after SILK, the trailing bytes ARE a 5 ms CELT
2325                    // frame (no flag) used to smooth mode/bandwidth transitions.
2326                    let mut redundant_rng = 0u32;
2327                    let mut redundancy = false;
2328                    let mut celt_to_silk = false;
2329                    let plen = payload.len();
2330                    let f5 = (self.sampling_rate / 200) as usize;
2331                    let f2_5 = f5 / 2;
2332                    let red_end_band = celt_endband_for_bandwidth(bandwidth);
2333                    let mut red_buf = [0.0f32; 480]; // F5 * <=2ch, planar
2334                    let mut red_bytes = 0usize;
2335                    if self.sampling_rate == 48000 && rc.tell() + 17 <= (plen as i32) * 8 {
2336                        redundancy = true;
2337                        celt_to_silk = rc.decode_bit_logp(1);
2338                        red_bytes = plen - (((rc.tell() + 7) >> 3) as usize);
2339                        if red_bytes < 2 || red_bytes >= plen {
2340                            redundancy = false;
2341                            red_bytes = 0;
2342                        }
2343                    }
2344                    // CELT->SILK: the redundant frame continues the prior CELT
2345                    // state (a fade-out of the previous CELT mode). Decode BEFORE
2346                    // the hybrid->SILK silence frame to keep libopus state order.
2347                    if redundancy && celt_to_silk {
2348                        redundant_rng = self.decode_redundant_celt(
2349                            &payload[plen - red_bytes..],
2350                            false,
2351                            packet_channels,
2352                            red_end_band,
2353                            &mut red_buf[..f5 * self.channels],
2354                        );
2355                    }
2356                    // Hybrid->SILK transition: let the CELT MDCT fade out by
2357                    // decoding a 2-byte silence frame; its 2.5 ms overlap tail is
2358                    // ADDED to the output (libopus decodes it into pcm before the
2359                    // SILK sum).
2360                    if self.sampling_rate == 48000
2361                        && prev_mode_frame == Some(OpusMode::Hybrid)
2362                        && !(redundancy && celt_to_silk && self.prev_redundancy)
2363                    {
2364                        let silence = [0xFFu8, 0xFF];
2365                        let mut sil_buf = [0.0f32; 240]; // F2_5 * <=2ch, planar
2366                        self.celt_dec.set_stream_channels(packet_channels);
2367                        let mut src = RangeCoder::new_decoder(&silence);
2368                        self.celt_dec.decode_from_range_coder_with_band_range(
2369                            &mut src,
2370                            16,
2371                            f2_5,
2372                            &mut sil_buf[..f2_5 * self.channels],
2373                            0,
2374                            red_end_band,
2375                        );
2376                        let region = &mut output[out_start..out_start + sub_output_len];
2377                        for i in 0..f2_5 {
2378                            for c in 0..self.channels {
2379                                region[i * self.channels + c] += sil_buf[c * f2_5 + i];
2380                            }
2381                        }
2382                    }
2383                    // SILK->CELT: reset, then decode — this PRIMES the CELT state
2384                    // for the upcoming CELT-mode frames (which is why the next mode
2385                    // change skips its reset when prev_redundancy is set).
2386                    if redundancy && !celt_to_silk {
2387                        redundant_rng = self.decode_redundant_celt(
2388                            &payload[plen - red_bytes..],
2389                            true,
2390                            packet_channels,
2391                            red_end_band,
2392                            &mut red_buf[..f5 * self.channels],
2393                        );
2394                    }
2395                    if redundancy {
2396                        let window = modes::default_mode().window;
2397                        let region = &mut output[out_start..out_start + sub_output_len];
2398                        if celt_to_silk {
2399                            redundancy_fade_start(
2400                                region,
2401                                &red_buf,
2402                                f5,
2403                                f2_5,
2404                                self.channels,
2405                                window,
2406                            );
2407                        } else {
2408                            redundancy_fade_end(
2409                                region,
2410                                sub_frame_size,
2411                                &red_buf,
2412                                f5,
2413                                f2_5,
2414                                self.channels,
2415                                window,
2416                            );
2417                        }
2418                    }
2419                    self.prev_redundancy = redundancy && !celt_to_silk;
2420                    prev_mode_frame = Some(OpusMode::SilkOnly);
2421                    self.last_range = rc.rng ^ redundant_rng;
2422                }
2423                self.prev_mode = Some(OpusMode::SilkOnly);
2424                Ok(frame_size)
2425            }
2426
2427            OpusMode::CeltOnly => {
2428                let celt_end_band = self.celt_end_band_from_toc(toc);
2429                // libopus opus_decoder.c:515 — discard CELT state on a mode change
2430                // unless the previous frame's SILK->CELT redundant frame already
2431                // primed it.
2432                if let Some(pm) = self.prev_mode {
2433                    if pm != OpusMode::CeltOnly && !self.prev_redundancy {
2434                        self.celt_dec.reset();
2435                    }
2436                }
2437                self.prev_redundancy = false;
2438                // Mono packet in a stereo stream => C=1, CC=2 (continuous state).
2439                self.celt_dec.set_stream_channels(packet_channels);
2440
2441                for (fi, payload) in frame_payloads.iter().enumerate() {
2442                    let mut rc = RangeCoder::new_decoder(payload);
2443                    let total_bits = (payload.len() * 8) as i32;
2444                    let needed = sub_frame_size * self.channels;
2445                    let out_start = fi * needed;
2446                    let out_end = (out_start + needed).min(output.len());
2447
2448                    if output.len() < out_end {
2449                        return Err("Output buffer too small");
2450                    }
2451
2452                    if self.channels == 1 {
2453                        self.celt_dec.decode_from_range_coder_with_band_range(
2454                            &mut rc,
2455                            total_bits,
2456                            sub_frame_size,
2457                            &mut output[out_start..out_end],
2458                            0,
2459                            celt_end_band,
2460                        );
2461                        for sample in &mut output[out_start..out_end] {
2462                            *sample = sample.clamp(-1.0, 1.0);
2463                        }
2464                    } else {
2465                        self.celt_dec.decode_from_range_coder_with_band_range(
2466                            &mut rc,
2467                            total_bits,
2468                            sub_frame_size,
2469                            &mut self.w_celt_planar[..needed],
2470                            0,
2471                            celt_end_band,
2472                        );
2473                        for i in 0..sub_frame_size {
2474                            for ch in 0..self.channels {
2475                                let idx = out_start + i * self.channels + ch;
2476                                output[idx] =
2477                                    self.w_celt_planar[ch * sub_frame_size + i].clamp(-1.0, 1.0);
2478                            }
2479                        }
2480                    }
2481                    self.last_range = rc.rng;
2482                }
2483                self.prev_mode = Some(OpusMode::CeltOnly);
2484                Ok(frame_size)
2485            }
2486
2487            OpusMode::Hybrid => {
2488                let internal_sample_rate = 16000;
2489                let internal_frame_size =
2490                    (frame_duration_ms * internal_sample_rate / 1000) as usize;
2491                let celt_end_band = self.celt_end_band_from_toc(toc);
2492
2493                if self.sampling_rate != internal_sample_rate
2494                    && internal_sample_rate != self.prev_internal_rate
2495                {
2496                    self.silk_resampler
2497                        .init(internal_sample_rate, self.sampling_rate);
2498                    self.silk_resampler_r
2499                        .init(internal_sample_rate, self.sampling_rate);
2500                    self.prev_internal_rate = internal_sample_rate;
2501                }
2502
2503                // Same SILK stereo/channel handling as the SilkOnly arm: true L/R
2504                // low band via MS->LR for stereo packets; per-packet internal
2505                // channel switch with side-channel/stereo-state resets.
2506                let silk_lr = self.channels == 2 && packet_channels == 2;
2507                self.silk_dec.produce_lr = silk_lr;
2508                let prev_internal_ch = self.silk_dec.n_channels_internal;
2509                if packet_channels as i32 > prev_internal_ch {
2510                    silk::init_decoder::silk_init_decoder(&mut self.silk_dec.channel_state[1]);
2511                }
2512                if self.channels == 2 && packet_channels == 2 && prev_internal_ch == 1 {
2513                    self.silk_dec.s_stereo_pred_prev_q13 = [0; 2];
2514                    self.silk_dec.s_stereo_side = [0; 2];
2515                    self.silk_resampler_r = self.silk_resampler.clone();
2516                }
2517                self.silk_dec.n_channels_internal = packet_channels as i32;
2518
2519                for (fi, payload) in frame_payloads.iter().enumerate() {
2520                    let mut rc = RangeCoder::new_decoder(payload);
2521                    let pcm_silk_i16_len = internal_frame_size * self.channels;
2522                    if pcm_silk_i16_len + 2 > self.w_pcm_i16.len() {
2523                        return Err("opus: SILK frame size exceeds buffer");
2524                    }
2525
2526                    // Prepend the previous frame's last two samples (sMid) and
2527                    // decode at offset 2, matching libopus's samplesOut1_tmp[n][2]
2528                    // layout — the resampler is fed from offset 1 (the 1-sample
2529                    // delay line), keeping the SILK low band aligned with the CELT
2530                    // high band exactly as in the reference.
2531                    let s_mid = self.silk_s_mid;
2532                    let ret = {
2533                        let (silk_dec, pcm_i16) = (&mut self.silk_dec, &mut self.w_pcm_i16);
2534                        pcm_i16[0] = s_mid[0];
2535                        pcm_i16[1] = s_mid[1];
2536                        silk_dec.decode(
2537                            &mut rc,
2538                            &mut pcm_i16[2..pcm_silk_i16_len + 2],
2539                            silk::decode_frame::FLAG_DECODE_NORMAL,
2540                            true,
2541                            frame_duration_ms,
2542                            internal_sample_rate,
2543                        )
2544                    };
2545
2546                    if ret < 0 {
2547                        return Err("SILK decoding failed");
2548                    }
2549
2550                    let silk_out_len = sub_frame_size * self.channels;
2551                    self.w_silk_out[..silk_out_len].fill(0.0);
2552                    if ret > 0 {
2553                        let decoded_samples = ret as usize;
2554                        if decoded_samples >= 2 {
2555                            self.silk_s_mid[0] = self.w_pcm_i16[decoded_samples];
2556                            self.silk_s_mid[1] = self.w_pcm_i16[decoded_samples + 1];
2557                        }
2558                        let ratio = self.sampling_rate as f64 / internal_sample_rate as f64;
2559                        let out_len =
2560                            ((decoded_samples as f64 * ratio) as usize).min(sub_frame_size);
2561                        debug_assert!(out_len <= self.w_pcm_resampled.len());
2562                        if silk_lr {
2563                            // Stereo low band: L/R from dec_api (already in the
2564                            // 1-sample-delay layout), each through its own resampler.
2565                            self.silk_resampler.process(
2566                                &mut self.w_pcm_resampled[..out_len],
2567                                &self.silk_dec.l_out[..decoded_samples],
2568                                decoded_samples as i32,
2569                            );
2570                            for i in 0..out_len {
2571                                self.w_silk_out[i * 2] = self.w_pcm_resampled[i] as f32 / 32768.0;
2572                            }
2573                            self.silk_resampler_r.process(
2574                                &mut self.w_pcm_resampled[..out_len],
2575                                &self.silk_dec.r_out[..decoded_samples],
2576                                decoded_samples as i32,
2577                            );
2578                            for i in 0..out_len {
2579                                self.w_silk_out[i * 2 + 1] =
2580                                    self.w_pcm_resampled[i] as f32 / 32768.0;
2581                            }
2582                        } else {
2583                            self.silk_resampler.process(
2584                                &mut self.w_pcm_resampled[..out_len],
2585                                &self.w_pcm_i16[1..1 + decoded_samples],
2586                                decoded_samples as i32,
2587                            );
2588                            for i in 0..out_len {
2589                                let v = self.w_pcm_resampled[i] as f32 / 32768.0;
2590                                for ch in 0..self.channels {
2591                                    self.w_silk_out[i * self.channels + ch] = v;
2592                                }
2593                            }
2594                            // Mono packet, stereo output: keep the right-channel
2595                            // resampler continuous (libopus dec_API.c:351-355).
2596                            if self.channels == 2 {
2597                                self.silk_resampler_r.process(
2598                                    &mut self.w_pcm_resampled[..out_len],
2599                                    &self.w_pcm_i16[1..1 + decoded_samples],
2600                                    decoded_samples as i32,
2601                                );
2602                                for i in 0..out_len {
2603                                    self.w_silk_out[i * 2 + 1] =
2604                                        self.w_pcm_resampled[i] as f32 / 32768.0;
2605                                }
2606                            }
2607                        }
2608                    }
2609
2610                    // --- Opus redundancy layer, hybrid form (opus_decoder.c) ---
2611                    // redundancy = bit(12); if set: celt_to_silk = bit(1),
2612                    // redundancy_bytes = uint(256)+2 taken from the END of the
2613                    // packet — the MAIN CELT layer still decodes, but with the
2614                    // range coder's storage shrunk by those bytes (this changes
2615                    // its raw-bit region and tell budget).
2616                    let plen = payload.len();
2617                    let mut redundancy = false;
2618                    let mut celt_to_silk = false;
2619                    let mut red_bytes = 0usize;
2620                    let mut effective_len = plen;
2621                    if rc.tell() + 37 <= (plen as i32) * 8 {
2622                        redundancy = rc.decode_bit_logp(12);
2623                        if redundancy {
2624                            celt_to_silk = rc.decode_bit_logp(1);
2625                            red_bytes = rc.dec_uint(256) as usize + 2;
2626                            if red_bytes <= effective_len {
2627                                effective_len -= red_bytes;
2628                            } else {
2629                                red_bytes = 0;
2630                                redundancy = false;
2631                            }
2632                            if redundancy && (effective_len as i32) * 8 < rc.tell() {
2633                                effective_len = plen;
2634                                red_bytes = 0;
2635                                redundancy = false;
2636                            }
2637                            if redundancy {
2638                                rc.storage -= red_bytes as u32;
2639                            }
2640                        }
2641                    }
2642                    let f5 = (self.sampling_rate / 200) as usize;
2643                    let f2_5 = f5 / 2;
2644                    let red_end_band = celt_endband_for_bandwidth(bandwidth);
2645                    let mut red_buf = [0.0f32; 480];
2646                    let mut redundant_rng = 0u32;
2647                    let do_red = redundancy && self.sampling_rate == 48000;
2648                    // CELT->SILK: redundant frame decodes BEFORE the main CELT,
2649                    // continuing the prior CELT state (fade-out of previous CELT).
2650                    if do_red && celt_to_silk {
2651                        redundant_rng = self.decode_redundant_celt(
2652                            &payload[plen - red_bytes..],
2653                            false,
2654                            packet_channels,
2655                            red_end_band,
2656                            &mut red_buf[..f5 * self.channels],
2657                        );
2658                    }
2659
2660                    // Main CELT high band. libopus opus_decoder.c:515 — reset CELT
2661                    // on a mode change unless primed by prior SILK->CELT redundancy.
2662                    if fi == 0 {
2663                        if let Some(pm) = self.prev_mode {
2664                            if pm != OpusMode::Hybrid && !self.prev_redundancy {
2665                                self.celt_dec.reset();
2666                            }
2667                        }
2668                    }
2669                    self.celt_dec.set_stream_channels(packet_channels);
2670                    let total_bits = (effective_len * 8) as i32;
2671                    {
2672                        let (celt_dec, celt_planar) = (&mut self.celt_dec, &mut self.w_celt_planar);
2673                        celt_dec.decode_from_range_coder_with_band_range(
2674                            &mut rc,
2675                            total_bits,
2676                            sub_frame_size,
2677                            &mut celt_planar[..silk_out_len],
2678                            17,
2679                            celt_end_band,
2680                        );
2681
2682                        if self.channels == 1 {
2683                            self.w_celt_out[..silk_out_len]
2684                                .copy_from_slice(&self.w_celt_planar[..silk_out_len]);
2685                        } else {
2686                            for i in 0..sub_frame_size {
2687                                for ch in 0..self.channels {
2688                                    self.w_celt_out[i * self.channels + ch] =
2689                                        self.w_celt_planar[ch * sub_frame_size + i];
2690                                }
2691                            }
2692                        }
2693                    }
2694
2695                    let out_start = fi * silk_out_len;
2696                    let total = silk_out_len.min(output.len() - out_start);
2697                    for j in 0..total {
2698                        output[out_start + j] =
2699                            (self.w_silk_out[j] + self.w_celt_out[j]).clamp(-1.0, 1.0);
2700                    }
2701
2702                    // SILK->CELT: reset + decode the redundant frame AFTER the main
2703                    // decode; it primes the CELT state for the upcoming CELT mode.
2704                    if do_red && !celt_to_silk {
2705                        redundant_rng = self.decode_redundant_celt(
2706                            &payload[plen - red_bytes..],
2707                            true,
2708                            packet_channels,
2709                            red_end_band,
2710                            &mut red_buf[..f5 * self.channels],
2711                        );
2712                    }
2713                    if do_red {
2714                        let window = modes::default_mode().window;
2715                        let region = &mut output[out_start..out_start + silk_out_len];
2716                        if celt_to_silk {
2717                            redundancy_fade_start(
2718                                region,
2719                                &red_buf,
2720                                f5,
2721                                f2_5,
2722                                self.channels,
2723                                window,
2724                            );
2725                        } else {
2726                            redundancy_fade_end(
2727                                region,
2728                                sub_frame_size,
2729                                &red_buf,
2730                                f5,
2731                                f2_5,
2732                                self.channels,
2733                                window,
2734                            );
2735                        }
2736                    }
2737                    self.prev_redundancy = redundancy && !celt_to_silk;
2738                    self.last_range = rc.rng ^ redundant_rng;
2739                }
2740                self.prev_mode = Some(OpusMode::Hybrid);
2741                Ok(frame_size)
2742            }
2743        }
2744    }
2745}
2746
2747impl OpusDecoder {
2748    #[inline(always)]
2749    fn celt_end_band_from_toc(&self, toc: u8) -> usize {
2750        let mode = modes::default_mode();
2751        let top = mode.eff_ebands;
2752        if mode_from_toc(toc) == OpusMode::CeltOnly && toc >= 0x80 {
2753            const FROM_OPUS_TABLE: [u8; 16] = [
2754                0x80, 0x88, 0x90, 0x98, 0x40, 0x48, 0x50, 0x58, 0x20, 0x28, 0x30, 0x38, 0x00, 0x08,
2755                0x10, 0x18,
2756            ];
2757            let idx = ((toc >> 3) - 16) as usize;
2758            let data0 = FROM_OPUS_TABLE[idx] | (toc & 0x7);
2759            let trim = (data0 >> 5) as usize;
2760            return top.saturating_sub(2 * trim).max(1);
2761        }
2762        // Hybrid: libopus maps the packet bandwidth to a CELT end band
2763        // (opus_decoder.c: SWB -> 19, FB -> 21). Decoding SWB hybrid with 21
2764        // reads two bands the encoder never coded -> range desync every packet.
2765        if mode_from_toc(toc) == OpusMode::Hybrid
2766            && bandwidth_from_toc(toc) == Bandwidth::Superwideband
2767        {
2768            return 19.min(top);
2769        }
2770        top
2771    }
2772
2773    /// Decode a redundant CELT frame (opus_decoder.c "5 ms redundant frame"):
2774    /// start band 0, end band from the packet bandwidth, 5 ms, its own range
2775    /// decoder. Returns the redundant final range; PLANAR output in `buf`
2776    /// (F5 samples per state channel). Only valid at 48 kHz output.
2777    fn decode_redundant_celt(
2778        &mut self,
2779        red: &[u8],
2780        reset_first: bool,
2781        packet_channels: usize,
2782        end_band: usize,
2783        buf: &mut [f32],
2784    ) -> u32 {
2785        if reset_first {
2786            self.celt_dec.reset();
2787        }
2788        self.celt_dec.set_stream_channels(packet_channels);
2789        let f5 = (self.sampling_rate / 200) as usize;
2790        let mut rrc = RangeCoder::new_decoder(red);
2791        let total_bits = (red.len() * 8) as i32;
2792        self.celt_dec.decode_from_range_coder_with_band_range(
2793            &mut rrc, total_bits, f5, buf, 0, end_band,
2794        );
2795        rrc.rng
2796    }
2797}
2798
2799/// libopus opus_decoder.c bandwidth -> CELT end band for the packet.
2800fn celt_endband_for_bandwidth(bw: Bandwidth) -> usize {
2801    match bw {
2802        Bandwidth::Narrowband => 13,
2803        Bandwidth::Mediumband | Bandwidth::Wideband => 17,
2804        Bandwidth::Superwideband => 19,
2805        _ => 21,
2806    }
2807}
2808
2809/// smooth_fade cross-fades (w = window[i]^2, 48 kHz inc=1) applied to the
2810/// interleaved output region of one frame. `red` is PLANAR (F5 per channel).
2811/// celt_to_silk: redundant frame occupies the START of the frame — first 2.5 ms
2812/// copied verbatim, next 2.5 ms fades redundant -> main.
2813///
2814/// Indexing invariant: `out.len() >= f5 * channels` (writes reach sample
2815/// f5-1 = 2*f2_5-1). A malformed multi-frame packet used to violate this (a
2816/// hostile frame count made the per-frame region tinier than F5, fuzzer-found
2817/// OOB panics here); decode() now rejects such packets up front exactly as C
2818/// libopus does (opus_decode_native's count*packet_frame_size > frame_size ->
2819/// OPUS_BUFFER_TOO_SMALL, and the 120 ms cap of opus_packet_parse_impl), so a
2820/// redundant frame always has >= 10 ms of frame to fade into, as in C.
2821fn redundancy_fade_start(
2822    out: &mut [f32],
2823    red: &[f32],
2824    f5: usize,
2825    f2_5: usize,
2826    channels: usize,
2827    window: &[f32],
2828) {
2829    for i in 0..f2_5 {
2830        for c in 0..channels {
2831            out[i * channels + c] = red[c * f5 + i];
2832        }
2833    }
2834    for i in 0..f2_5 {
2835        let w = window[i] * window[i];
2836        for c in 0..channels {
2837            let idx = (f2_5 + i) * channels + c;
2838            out[idx] = (1.0 - w) * red[c * f5 + f2_5 + i] + w * out[idx];
2839        }
2840    }
2841}
2842
2843/// SILK->CELT: redundant frame occupies the END of the frame — the last 2.5 ms
2844/// fades main -> redundant (second half of the redundant frame).
2845///
2846/// Indexing invariant: `frame_samples >= f2_5` and `out.len() >=
2847/// frame_samples * channels` (the index `frame_samples - f2_5 + i` would
2848/// otherwise underflow). A malformed multi-frame packet used to violate this
2849/// (fuzzer-found subtract-with-overflow panic here); decode() now rejects such
2850/// packets up front exactly as C libopus does (opus_decode_native's
2851/// count*packet_frame_size > frame_size -> OPUS_BUFFER_TOO_SMALL, plus the
2852/// 120 ms cap of opus_packet_parse_impl), so redundancy only ever runs on
2853/// frames of >= 10 ms, as in C.
2854fn redundancy_fade_end(
2855    out: &mut [f32],
2856    frame_samples: usize,
2857    red: &[f32],
2858    f5: usize,
2859    f2_5: usize,
2860    channels: usize,
2861    window: &[f32],
2862) {
2863    for i in 0..f2_5 {
2864        let w = window[i] * window[i];
2865        for c in 0..channels {
2866            let idx = (frame_samples - f2_5 + i) * channels + c;
2867            out[idx] = (1.0 - w) * out[idx] + w * red[c * f5 + f2_5 + i];
2868        }
2869    }
2870}
2871
2872fn frame_rate_from_params(sampling_rate: i32, frame_size: usize) -> Option<i32> {
2873    let frame_size = frame_size as i32;
2874    if frame_size == 0 || sampling_rate % frame_size != 0 {
2875        return None;
2876    }
2877    Some(sampling_rate / frame_size)
2878}
2879
2880fn gen_toc(mode: OpusMode, frame_rate: i32, bandwidth: Bandwidth, channels: usize) -> u8 {
2881    let mut rate = frame_rate;
2882    let mut period = 0;
2883    while rate < 400 {
2884        rate <<= 1;
2885        period += 1;
2886    }
2887
2888    let mut toc = match mode {
2889        OpusMode::SilkOnly => {
2890            let bw = (bandwidth as i32 - Bandwidth::Narrowband as i32) << 5;
2891            let per = (period - 2) << 3;
2892            (bw | per) as u8
2893        }
2894        OpusMode::CeltOnly => {
2895            let mut tmp = bandwidth as i32 - Bandwidth::Mediumband as i32;
2896            if tmp < 0 {
2897                tmp = 0;
2898            }
2899            let per = period << 3;
2900            (0x80 | (tmp << 5) | per) as u8
2901        }
2902        OpusMode::Hybrid => {
2903            let base_config = if bandwidth == Bandwidth::Superwideband {
2904                12
2905            } else {
2906                14
2907            };
2908            let period_offset = if frame_rate >= 100 { 0 } else { 1 };
2909            ((base_config + period_offset) << 3) as u8
2910        }
2911    };
2912
2913    if channels == 2 {
2914        toc |= 0x04;
2915    }
2916    toc
2917}
2918
2919fn mode_from_toc(toc: u8) -> OpusMode {
2920    if toc & 0x80 != 0 {
2921        OpusMode::CeltOnly
2922    } else if toc & 0x60 == 0x60 {
2923        OpusMode::Hybrid
2924    } else {
2925        OpusMode::SilkOnly
2926    }
2927}
2928
2929fn bandwidth_from_toc(toc: u8) -> Bandwidth {
2930    let mode = mode_from_toc(toc);
2931    match mode {
2932        OpusMode::SilkOnly => {
2933            let bw_bits = (toc >> 5) & 0x03;
2934            match bw_bits {
2935                0 => Bandwidth::Narrowband,
2936                1 => Bandwidth::Mediumband,
2937                2 => Bandwidth::Wideband,
2938                _ => Bandwidth::Wideband,
2939            }
2940        }
2941        OpusMode::Hybrid => {
2942            let bw_bit = (toc >> 4) & 0x01;
2943            if bw_bit == 0 {
2944                Bandwidth::Superwideband
2945            } else {
2946                Bandwidth::Fullband
2947            }
2948        }
2949        OpusMode::CeltOnly => {
2950            let bw_bits = (toc >> 5) & 0x03;
2951            match bw_bits {
2952                0 => Bandwidth::Mediumband,
2953                1 => Bandwidth::Wideband,
2954                2 => Bandwidth::Superwideband,
2955                3 => Bandwidth::Fullband,
2956                _ => Bandwidth::Fullband,
2957            }
2958        }
2959    }
2960}
2961
2962fn frame_duration_ms_from_toc(toc: u8) -> i32 {
2963    let mode = mode_from_toc(toc);
2964    match mode {
2965        OpusMode::SilkOnly => {
2966            let config = (toc >> 3) & 0x03;
2967            match config {
2968                0 => 10,
2969                1 => 20,
2970                2 => 40,
2971                3 => 60,
2972                _ => 20,
2973            }
2974        }
2975        OpusMode::Hybrid => {
2976            let config = (toc >> 3) & 0x01;
2977            if config == 0 { 10 } else { 20 }
2978        }
2979        OpusMode::CeltOnly => {
2980            let config = (toc >> 3) & 0x03;
2981            match config {
2982                0 => 2,
2983                1 => 5,
2984                2 => 10,
2985                3 => 20,
2986                _ => 20,
2987            }
2988        }
2989    }
2990}
2991
2992fn channels_from_toc(toc: u8) -> usize {
2993    if toc & 0x04 != 0 { 2 } else { 1 }
2994}
2995
2996/// RFC 6716 §3.1 frame-length coding (used by code 2 and VBR code 3): a length
2997/// of 0..=251 is one byte with that value; 252..=1275 is two bytes `b0` (252..255)
2998/// then `b1`, giving `b1*4 + b0`. Returns `(length, bytes_consumed)`.
2999fn read_opus_frame_len(data: &[u8], ptr: usize) -> Result<(usize, usize), &'static str> {
3000    let b0 = *data.get(ptr).ok_or("Opus frame length: truncated")? as usize;
3001    if b0 < 252 {
3002        Ok((b0, 1))
3003    } else {
3004        let b1 = *data.get(ptr + 1).ok_or("Opus frame length: truncated 2-byte")? as usize;
3005        Ok((b1 * 4 + b0, 2))
3006    }
3007}
3008
3009#[cfg(test)]
3010mod tests {
3011    use super::*;
3012
3013    fn frame_size_from_toc(toc: u8, sampling_rate: i32) -> Option<usize> {
3014        let mode = mode_from_toc(toc);
3015        match mode {
3016            OpusMode::CeltOnly => {
3017                let period = ((toc >> 3) & 0x03) as i32;
3018                let frame_rate = 400 >> period;
3019                if frame_rate == 0 || sampling_rate % frame_rate != 0 {
3020                    return None;
3021                }
3022                Some((sampling_rate / frame_rate) as usize)
3023            }
3024            OpusMode::SilkOnly => {
3025                let duration_ms = frame_duration_ms_from_toc(toc);
3026                Some((sampling_rate as i64 * duration_ms as i64 / 1000) as usize)
3027            }
3028            OpusMode::Hybrid => {
3029                let duration_ms = frame_duration_ms_from_toc(toc);
3030                Some((sampling_rate as i64 * duration_ms as i64 / 1000) as usize)
3031            }
3032        }
3033    }
3034
3035    #[test]
3036    fn gen_toc_matches_celt_reference_values() {
3037        let sampling_rate = 48_000;
3038        let cases = [
3039            (120usize, 0xE0u8),
3040            (240usize, 0xE8u8),
3041            (480usize, 0xF0u8),
3042            (960usize, 0xF8u8),
3043        ];
3044
3045        for (frame_size, expected_toc) in cases {
3046            let frame_rate = frame_rate_from_params(sampling_rate, frame_size).unwrap();
3047            let toc = gen_toc(OpusMode::CeltOnly, frame_rate, Bandwidth::Fullband, 1);
3048            assert_eq!(
3049                toc, expected_toc,
3050                "frame_size {} expected TOC {:02X} got {:02X}",
3051                frame_size, expected_toc, toc
3052            );
3053            let decoded_size = frame_size_from_toc(toc, sampling_rate).unwrap();
3054            assert_eq!(decoded_size, frame_size);
3055        }
3056
3057        let stereo_toc = gen_toc(
3058            OpusMode::CeltOnly,
3059            frame_rate_from_params(sampling_rate, 960).unwrap(),
3060            Bandwidth::Fullband,
3061            2,
3062        );
3063        assert_eq!(channels_from_toc(stereo_toc), 2);
3064    }
3065
3066    #[test]
3067    fn test_celt_decoder_large_frame_sizes() {
3068        let sampling_rate = 48000;
3069        let channels = 1;
3070
3071        let mut decoder = OpusDecoder::new(sampling_rate, channels).unwrap();
3072
3073        let frame_sizes = [120, 240, 480, 960];
3074
3075        for frame_size in frame_sizes {
3076            let toc = gen_toc(
3077                OpusMode::CeltOnly,
3078                frame_rate_from_params(sampling_rate, frame_size).unwrap(),
3079                Bandwidth::Fullband,
3080                channels,
3081            );
3082            let packet = [toc, 0, 0, 0, 0];
3083
3084            let mut output = vec![0.0f32; frame_size * channels];
3085
3086            let _ = decoder.decode(&packet, frame_size, &mut output);
3087        }
3088
3089        let channels = 2;
3090        let mut decoder = OpusDecoder::new(sampling_rate, channels).unwrap();
3091
3092        for frame_size in frame_sizes {
3093            let toc = gen_toc(
3094                OpusMode::CeltOnly,
3095                frame_rate_from_params(sampling_rate, frame_size).unwrap(),
3096                Bandwidth::Fullband,
3097                channels,
3098            );
3099            let packet = [toc, 0, 0, 0, 0];
3100
3101            let mut output = vec![0.0f32; frame_size * channels];
3102            let _ = decoder.decode(&packet, frame_size, &mut output);
3103        }
3104    }
3105
3106    #[test]
3107    fn test_celt_decoder_edge_case_frame_sizes() {
3108        let sampling_rate = 48000;
3109        let channels = 1;
3110        let mut decoder = OpusDecoder::new(sampling_rate, channels).unwrap();
3111
3112        let edge_sizes = [2048, 2167, 2168, 2169, 2880, 3072];
3113
3114        for frame_size in edge_sizes {
3115            let mut output = vec![0.0f32; frame_size * channels];
3116
3117            let _ = decoder.decode(&[0x80, 0, 0, 0], frame_size, &mut output);
3118        }
3119    }
3120
3121    // Regression test for: "index out of bounds: the len is 48 but the index is 119"
3122    // Root cause: frame_size=48 at 48kHz gives frame_rate=1000, which is not a valid
3123    // Hybrid-mode frame rate but was not validated.  CELT's lm-search then silently
3124    // fell back to lm=0, computed n2=120, and wrote output[119] into a 48-element
3125    // slice.  Triggered via G.729-decoded PCM (8kHz) passed to a 48kHz Opus encoder
3126    // without proper resampling, so the encoder received 48 samples instead of 480.
3127    #[test]
3128    fn test_invalid_small_frame_size_returns_error_not_panic() {
3129        let mut enc = OpusEncoder::new(48000, 2, Application::Voip).unwrap();
3130        enc.bitrate_bps = 64000;
3131        enc.complexity = 5;
3132        enc.use_cbr = true;
3133
3134        // 48 samples at 48kHz = 1ms → frame_rate=1000, invalid for Hybrid mode.
3135        let input = vec![0.0f32; 48 * 2]; // stereo interleaved
3136        let mut output = vec![0u8; 256];
3137
3138        let result = enc.encode(&input, 48, &mut output);
3139        assert!(
3140            result.is_err(),
3141            "encode with invalid frame_size=48 should return Err, not panic"
3142        );
3143    }
3144
3145    // Also verify that the Audio application path (always Hybrid at 48 kHz) rejects
3146    // the same bad frame size.
3147    #[test]
3148    fn test_invalid_small_frame_size_audio_application_returns_error() {
3149        let mut enc = OpusEncoder::new(48000, 1, Application::Audio).unwrap();
3150        let input = vec![0.0f32; 48];
3151        let mut output = vec![0u8; 256];
3152
3153        let result = enc.encode(&input, 48, &mut output);
3154        assert!(
3155            result.is_err(),
3156            "Audio/48kHz encoder with frame_size=48 should return Err"
3157        );
3158    }
3159}