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    w_pcm_i16: Vec<i16>,
1583    w_silk_out: Vec<f32>,
1584    w_pcm_resampled: Vec<i16>,
1585    w_celt_planar: Vec<f32>,
1586    w_celt_out: Vec<f32>,
1587
1588    // SILK per-frame history: libopus prepends the previous frame's last two
1589    // decoded samples (`sStereo.sMid`) and feeds the resampler from offset 1, a
1590    // 1-internal-sample delay line. Replicated here so our SILK output aligns
1591    // with the reference across every bandwidth (was leading by 1 internal
1592    // sample = 3/4/6 output samples at WB/MB/NB).
1593    silk_s_mid: [i16; 2],
1594
1595    // Range decoder final `rng` from the last decoded frame (conformance/desync
1596    // diagnostic: compare against the encoder's stored final range).
1597    pub last_range: u32,
1598
1599    // Auxiliary decoder for packets whose channel count differs from ours
1600    // (a stream may switch between mono and stereo). It decodes at the packet's
1601    // native channel count; we then up/downmix to our output count. Persistent
1602    // so the "other" channel mode keeps its own inter-frame state.
1603    aux: Option<Box<OpusDecoder>>,
1604    // Set when a packet was just decoded by the aux (a mono packet in a stereo
1605    // stream); triggers seeding the primary CELT decoder's overlap/energy state
1606    // from the aux at the next primary (stereo) CELT/Hybrid packet, so the MDCT
1607    // overlap-add is continuous across the mono->stereo switch.
1608    prev_used_aux: bool,
1609    // libopus st->prev_redundancy: the previous frame carried a SILK->CELT
1610    // redundant frame (redundancy && !celt_to_silk). Suppresses the CELT reset on
1611    // the following mode change (the redundant frame already primed CELT state).
1612    prev_redundancy: bool,
1613}
1614
1615impl OpusDecoder {
1616    pub fn new(sampling_rate: i32, channels: usize) -> Result<Self, &'static str> {
1617        if ![8000, 12000, 16000, 24000, 48000].contains(&sampling_rate) {
1618            return Err("Invalid sampling rate");
1619        }
1620        if ![1, 2].contains(&channels) {
1621            return Err("Invalid number of channels");
1622        }
1623
1624        let mode = modes::default_mode();
1625        let celt_dec = CeltDecoder::new(mode, channels);
1626
1627        let mut silk_dec = silk::dec_api::SilkDecoder::new();
1628        silk_dec.init(sampling_rate.min(16000), channels as i32);
1629        silk_dec.channel_state[0].fs_api_hz = sampling_rate;
1630
1631        Ok(Self {
1632            celt_dec,
1633            silk_dec,
1634            sampling_rate,
1635            channels,
1636            prev_mode: None,
1637            frame_size: 0,
1638            bandwidth: Bandwidth::Auto,
1639            stream_channels: channels,
1640            silk_resampler: silk::resampler::SilkResampler::default(),
1641            silk_resampler_r: silk::resampler::SilkResampler::default(),
1642            prev_internal_rate: 0,
1643
1644            // SILK internal scratch: max frame is 60 ms at the 16 kHz WB internal
1645            // rate (960 samples/ch), i.e. 1920 stereo. Sized like the sibling
1646            // buffers below for headroom — the old fixed 640 overflowed on any
1647            // 60 ms SILK frame (panic decoding valid streams).
1648            w_pcm_i16: vec![0i16; 5760 * channels],
1649
1650            w_silk_out: vec![0.0f32; 5760 * channels],
1651            w_pcm_resampled: vec![0i16; 5760 * channels],
1652            w_celt_planar: vec![0.0f32; 5760 * channels],
1653            w_celt_out: vec![0.0f32; 5760 * channels],
1654            silk_s_mid: [0; 2],
1655            last_range: 0,
1656            aux: None,
1657            prev_used_aux: false,
1658            prev_redundancy: false,
1659        })
1660    }
1661
1662    /// Packet-loss concealment for a lost frame (empty/None packet). Runs the
1663    /// SILK PLC (LTP+LPC extrapolation) for the last-known SILK/hybrid mode and
1664    /// resamples to the output rate. CELT-only loss has no CELT PLC yet, so it
1665    /// yields silence (a documented Tier-1 follow-up); the SILK path covers the
1666    /// dominant VoIP case. Mono conceal is duplicated to both channels on a
1667    /// stereo output.
1668    fn decode_plc(
1669        &mut self,
1670        frame_size: usize,
1671        output: &mut [f32],
1672    ) -> Result<usize, &'static str> {
1673        let out_samples = frame_size * self.channels;
1674        for v in output.iter_mut().take(out_samples) {
1675            *v = 0.0;
1676        }
1677        let mode = self.prev_mode.unwrap_or(OpusMode::SilkOnly);
1678        if mode == OpusMode::CeltOnly {
1679            // CELT packet-loss concealment (noise-based celt_decode_lost): real
1680            // attenuating audio instead of silence.
1681            self.celt_dec.conceal_lost(frame_size, output);
1682            self.prev_mode = Some(mode);
1683            return Ok(frame_size);
1684        }
1685
1686        let frame_ms = (frame_size as i32 * 1000 / self.sampling_rate).max(1);
1687        let internal_rate = if mode == OpusMode::Hybrid {
1688            16000
1689        } else {
1690            match self.bandwidth {
1691                Bandwidth::Narrowband => 8000,
1692                Bandwidth::Mediumband => 12000,
1693                _ => 16000,
1694            }
1695        };
1696        if self.sampling_rate != internal_rate && internal_rate != self.prev_internal_rate {
1697            self.silk_resampler.init(internal_rate, self.sampling_rate);
1698            self.prev_internal_rate = internal_rate;
1699        }
1700        let n_silk = match frame_ms {
1701            40 => 2,
1702            60 => 3,
1703            _ => 1,
1704        };
1705        let internal_frame = (frame_ms * internal_rate / 1000) as usize;
1706        let internal_sub = internal_frame / n_silk.max(1);
1707        let ratio = self.sampling_rate as f64 / internal_rate as f64;
1708        // Conceal mono only (the SILK low band); stereo output duplicates it.
1709        self.silk_dec.produce_lr = false;
1710        self.silk_dec.n_channels_internal = 1;
1711
1712        let mut off = 0usize; // output samples/ch written so far
1713        for sf in 0..n_silk {
1714            let mut rc = RangeCoder::new_decoder(&[]);
1715            let n16 = internal_sub;
1716            if n16 + 2 > self.w_pcm_i16.len() {
1717                return Err("opus PLC: frame exceeds buffer");
1718            }
1719            self.w_pcm_i16[0] = self.silk_s_mid[0];
1720            self.w_pcm_i16[1] = self.silk_s_mid[1];
1721            let ret = self.silk_dec.decode(
1722                &mut rc,
1723                &mut self.w_pcm_i16[2..n16 + 2],
1724                silk::decode_frame::FLAG_PACKET_LOST,
1725                sf == 0,
1726                frame_ms,
1727                internal_rate,
1728            );
1729            if ret < 0 {
1730                return Err("SILK PLC failed");
1731            }
1732            let dec = ret as usize;
1733            if dec >= 2 {
1734                self.silk_s_mid[0] = self.w_pcm_i16[dec];
1735                self.silk_s_mid[1] = self.w_pcm_i16[dec + 1];
1736            }
1737            let base = off * self.channels;
1738            let out_len = if self.sampling_rate == internal_rate {
1739                for i in 0..dec {
1740                    let v = self.w_pcm_i16[1 + i] as f32 / 32768.0;
1741                    for ch in 0..self.channels {
1742                        let idx = base + i * self.channels + ch;
1743                        if idx < output.len() {
1744                            output[idx] = v;
1745                        }
1746                    }
1747                }
1748                dec
1749            } else {
1750                let out_len = (dec as f64 * ratio) as usize;
1751                let src: Vec<i16> = self.w_pcm_i16[1..1 + dec].to_vec();
1752                self.silk_resampler
1753                    .process(&mut self.w_pcm_resampled[..out_len], &src, dec as i32);
1754                for i in 0..out_len {
1755                    let v = self.w_pcm_resampled[i] as f32 / 32768.0;
1756                    for ch in 0..self.channels {
1757                        let idx = base + i * self.channels + ch;
1758                        if idx < output.len() {
1759                            output[idx] = v;
1760                        }
1761                    }
1762                }
1763                out_len
1764            };
1765            off += out_len;
1766        }
1767        self.prev_mode = Some(mode);
1768        Ok(frame_size)
1769    }
1770
1771    /// Forward-error-correction decode: reconstruct a LOST frame from the LBRR
1772    /// (low-bitrate redundancy) embedded in the NEXT received `packet`. Drives
1773    /// the SILK decoder in FLAG_DECODE_LBRR mode, which self-selects: it decodes
1774    /// the redundant frame when the packet carries LBRR for it, and falls back
1775    /// to PLC extrapolation when it doesn't. CELT-only or multi-frame packets
1776    /// fall back to plain PLC (no SILK LBRR to recover). After this call the
1777    /// caller decodes `packet` normally for the following frame.
1778    pub fn decode_fec(
1779        &mut self,
1780        packet: &[u8],
1781        frame_size: usize,
1782        output: &mut [f32],
1783    ) -> Result<usize, &'static str> {
1784        if packet.is_empty() {
1785            return self.decode_plc(frame_size, output);
1786        }
1787        let toc = packet[0];
1788        let mode = mode_from_toc(toc);
1789        // FEC only lives in SILK/hybrid low band; code-0 (single frame) only.
1790        if mode == OpusMode::CeltOnly || (toc & 0x03) != 0 {
1791            return self.decode_plc(frame_size, output);
1792        }
1793        let bandwidth = bandwidth_from_toc(toc);
1794        let payload = &packet[1..];
1795
1796        let out_samples = frame_size * self.channels;
1797        for v in output.iter_mut().take(out_samples) {
1798            *v = 0.0;
1799        }
1800        let frame_ms = (frame_size as i32 * 1000 / self.sampling_rate).max(1);
1801        let internal_rate = if mode == OpusMode::Hybrid {
1802            16000
1803        } else {
1804            match bandwidth {
1805                Bandwidth::Narrowband => 8000,
1806                Bandwidth::Mediumband => 12000,
1807                _ => 16000,
1808            }
1809        };
1810        if self.sampling_rate != internal_rate && internal_rate != self.prev_internal_rate {
1811            self.silk_resampler.init(internal_rate, self.sampling_rate);
1812            self.prev_internal_rate = internal_rate;
1813        }
1814        let internal_frame = (frame_ms * internal_rate / 1000) as usize;
1815        let ratio = self.sampling_rate as f64 / internal_rate as f64;
1816        self.silk_dec.produce_lr = false;
1817        self.silk_dec.n_channels_internal = 1;
1818
1819        let mut rc = RangeCoder::new_decoder(payload);
1820        let n16 = internal_frame;
1821        if n16 + 2 > self.w_pcm_i16.len() {
1822            return Err("opus FEC: frame exceeds buffer");
1823        }
1824        self.w_pcm_i16[0] = self.silk_s_mid[0];
1825        self.w_pcm_i16[1] = self.silk_s_mid[1];
1826        let ret = self.silk_dec.decode(
1827            &mut rc,
1828            &mut self.w_pcm_i16[2..n16 + 2],
1829            silk::decode_frame::FLAG_DECODE_LBRR,
1830            true,
1831            frame_ms,
1832            internal_rate,
1833        );
1834        if ret < 0 {
1835            return Err("SILK FEC failed");
1836        }
1837        let dec = ret as usize;
1838        if dec >= 2 {
1839            self.silk_s_mid[0] = self.w_pcm_i16[dec];
1840            self.silk_s_mid[1] = self.w_pcm_i16[dec + 1];
1841        }
1842        if self.sampling_rate == internal_rate {
1843            for i in 0..dec {
1844                let v = self.w_pcm_i16[1 + i] as f32 / 32768.0;
1845                for ch in 0..self.channels {
1846                    let idx = i * self.channels + ch;
1847                    if idx < output.len() {
1848                        output[idx] = v;
1849                    }
1850                }
1851            }
1852        } else {
1853            let out_len = (dec as f64 * ratio) as usize;
1854            let src: Vec<i16> = self.w_pcm_i16[1..1 + dec].to_vec();
1855            self.silk_resampler
1856                .process(&mut self.w_pcm_resampled[..out_len], &src, dec as i32);
1857            for i in 0..out_len {
1858                let v = self.w_pcm_resampled[i] as f32 / 32768.0;
1859                for ch in 0..self.channels {
1860                    let idx = i * self.channels + ch;
1861                    if idx < output.len() {
1862                        output[idx] = v;
1863                    }
1864                }
1865            }
1866        }
1867        self.prev_mode = Some(mode);
1868        Ok(frame_size)
1869    }
1870
1871    pub fn decode(
1872        &mut self,
1873        input: &[u8],
1874        frame_size: usize,
1875        output: &mut [f32],
1876    ) -> Result<usize, &'static str> {
1877        // Lost packet (data==NULL / empty) -> packet-loss concealment.
1878        if input.is_empty() {
1879            return self.decode_plc(frame_size, output);
1880        }
1881
1882        let toc = input[0];
1883        let mode = mode_from_toc(toc);
1884        let packet_channels = channels_from_toc(toc);
1885        let bandwidth = bandwidth_from_toc(toc);
1886        let frame_duration_ms = frame_duration_ms_from_toc(toc);
1887
1888        // A mono SILK packet inside a stereo stream is decoded through the PRIMARY
1889        // decoder (unified path), not a separate aux — the aux's SILK/resampler
1890        // state is blind to the interleaved stereo packets, so its state is stale
1891        // at every mono<->stereo switch. libopus keeps ONE decoder whose channel-0
1892        // resampler and stereo state run continuously across the switches.
1893        // A mono packet of ANY mode in a stereo stream decodes through the PRIMARY
1894        // (unified path) so inter-frame state stays one continuous chain across
1895        // mono<->stereo switches — SILK resampler/stereo state; CELT (and the
1896        // redundant/silence transition frames) via stream_channels=1 (C=1/CC=2) —
1897        // matching libopus's single decoder.
1898        let mono_in_stereo = packet_channels == 1 && self.channels == 2;
1899
1900        if packet_channels != self.channels && !mono_in_stereo {
1901            // The packet's channel count differs from ours (a stream can switch
1902            // between mono and stereo). Decode it at its native channel count in
1903            // a persistent auxiliary decoder, then render to our output count:
1904            // mono->stereo duplicates, stereo->mono averages the two channels.
1905            if self
1906                .aux
1907                .as_ref()
1908                .map(|a| a.channels != packet_channels)
1909                .unwrap_or(true)
1910            {
1911                self.aux = Some(Box::new(OpusDecoder::new(
1912                    self.sampling_rate,
1913                    packet_channels,
1914                )?));
1915            }
1916            // Reverse of the mono->stereo seed: on a stereo->mono switch, seed the
1917            // aux (mono) CELT decoder from the primary (stereo channel 0) so its
1918            // MDCT-overlap/energy state is continuous with the preceding stereo
1919            // packets (the primary was the continuous decoder during them).
1920            if !self.prev_used_aux
1921                && packet_channels == 1
1922                && self.channels == 2
1923                && (mode == OpusMode::CeltOnly || mode == OpusMode::Hybrid)
1924            {
1925                let (aux_opt, primary) = (&mut self.aux, &self.celt_dec);
1926                if let Some(aux) = aux_opt.as_mut() {
1927                    aux.celt_dec.seed_from(primary);
1928                }
1929            }
1930            let aux = self.aux.as_mut().unwrap();
1931            let mut buf = vec![0.0f32; frame_size * packet_channels];
1932            let n = aux.decode(input, frame_size, &mut buf)?;
1933            self.last_range = aux.last_range;
1934            if packet_channels == 1 && self.channels == 2 {
1935                for i in 0..n {
1936                    let v = buf[i];
1937                    output[2 * i] = v;
1938                    output[2 * i + 1] = v;
1939                }
1940            } else if packet_channels == 2 && self.channels == 1 {
1941                for i in 0..n {
1942                    output[i] = 0.5 * (buf[2 * i] + buf[2 * i + 1]);
1943                }
1944            } else {
1945                let m = (n * self.channels).min(output.len()).min(buf.len());
1946                output[..m].copy_from_slice(&buf[..m]);
1947            }
1948            self.prev_mode = Some(mode);
1949            self.prev_used_aux = true;
1950            return Ok(n);
1951        }
1952
1953        // First primary (native-channel) packet after a run of aux (mono-in-stereo)
1954        // packets: seed the primary CELT decoder's inter-frame state from the aux
1955        // so the mono->stereo MDCT overlap-add is continuous (matches libopus's
1956        // single continuous decoder). SILK carries its own state through the
1957        // primary already; this is for the CELT/Hybrid high band.
1958        if self.prev_used_aux {
1959            self.prev_used_aux = false;
1960            if (mode == OpusMode::CeltOnly || mode == OpusMode::Hybrid) && self.channels == 2 {
1961                if let Some(aux) = self.aux.as_ref() {
1962                    self.celt_dec.seed_from(&aux.celt_dec);
1963                }
1964            }
1965        }
1966
1967        let code = toc & 0x03;
1968        let frame_count: usize;
1969        let frame_payloads: Vec<&[u8]>;
1970
1971        match code {
1972            0 => {
1973                frame_count = 1;
1974                frame_payloads = vec![&input[1..]];
1975            }
1976            1 => {
1977                frame_count = 2;
1978                let half = (input.len() - 1) / 2;
1979                if half == 0 {
1980                    return Err("Code 1: empty frame");
1981                }
1982                frame_payloads = vec![&input[1..1 + half], &input[1 + half..]];
1983            }
1984            2 => {
1985                frame_count = 2;
1986                let data = &input[1..];
1987                if data.is_empty() {
1988                    return Err("Code 2 packet has no data");
1989                }
1990                let (first_len, header_size) = read_opus_frame_len(data, 0)?;
1991                if header_size + first_len > data.len() {
1992                    return Err("Code 2: first frame size exceeds packet");
1993                }
1994                frame_payloads = vec![
1995                    &data[header_size..header_size + first_len],
1996                    &data[header_size + first_len..],
1997                ];
1998            }
1999            3 => {
2000                // RFC 6716 §3.2.5. Frame-count byte: bit 7 = VBR flag, bit 6 =
2001                // padding flag, bits 5..0 = frame count M. VBR and padding are
2002                // independent; the earlier code conflated them (and used a
2003                // non-standard length coding), which mis-parsed CBR and padded
2004                // packets — exactly what the RFC test vectors exercise.
2005                if input.len() < 2 {
2006                    return Err("Code 3 packet too short");
2007                }
2008                let count_byte = input[1];
2009                let m = (count_byte & 0x3F) as usize;
2010                if m < 1 || m > 48 {
2011                    return Err("Code 3: invalid frame count");
2012                }
2013                // libopus opus.c opus_packet_parse_impl (code 3):
2014                //   if (count <= 0 || framesize*(opus_int32)count > 5760)
2015                //      return OPUS_INVALID_PACKET;
2016                // (framesize at 48 kHz; 5760 = 120 ms, the RFC 6716 packet cap.)
2017                // A hostile frame count past this cap would otherwise shrink our
2018                // per-frame size below the redundancy-fade windows further down.
2019                if m as i32 * repacketizer::samples_per_frame(toc, 48000) > 5760 {
2020                    return Err("Code 3: packet duration exceeds 120 ms");
2021                }
2022                frame_count = m;
2023                let vbr = (count_byte & 0x80) != 0;
2024                let padding = (count_byte & 0x40) != 0;
2025
2026                // Padding length indicator bytes follow the count byte; the
2027                // padding data itself sits at the end of the packet.
2028                let mut ptr = 2usize;
2029                let mut pad_len = 0usize;
2030                if padding {
2031                    loop {
2032                        let p = *input.get(ptr).ok_or("Code 3: padding overflow")? as usize;
2033                        ptr += 1;
2034                        if p == 255 {
2035                            pad_len += 254;
2036                        } else {
2037                            pad_len += p;
2038                            break;
2039                        }
2040                    }
2041                }
2042                let end = input
2043                    .len()
2044                    .checked_sub(pad_len)
2045                    .ok_or("Code 3: padding exceeds packet")?;
2046                if ptr > end {
2047                    return Err("Code 3: padding exceeds packet");
2048                }
2049                // Frame-data region, with the length headers (VBR) at its front
2050                // and the trailing padding already excluded.
2051                let region = &input[ptr..end];
2052
2053                if vbr {
2054                    // M-1 explicit frame lengths, contiguous, then the frame
2055                    // data; the last frame is the remainder.
2056                    let mut lens = Vec::with_capacity(m.saturating_sub(1));
2057                    let mut hp = 0usize;
2058                    for _ in 0..m - 1 {
2059                        let (l, nb) = read_opus_frame_len(region, hp)?;
2060                        hp += nb;
2061                        lens.push(l);
2062                    }
2063                    let mut payloads = Vec::with_capacity(m);
2064                    let mut fp = hp;
2065                    for &l in &lens {
2066                        if fp + l > region.len() {
2067                            return Err("Code 3 VBR: frame length exceeds packet");
2068                        }
2069                        payloads.push(&region[fp..fp + l]);
2070                        fp += l;
2071                    }
2072                    if fp > region.len() {
2073                        return Err("Code 3 VBR: no data for last frame");
2074                    }
2075                    payloads.push(&region[fp..]);
2076                    frame_payloads = payloads;
2077                } else {
2078                    // CBR: the region splits into M equal frames (possibly all
2079                    // empty, e.g. DTX).
2080                    if region.len() % m != 0 {
2081                        return Err("Code 3 CBR: frame data not divisible by frame count");
2082                    }
2083                    let frame_len = region.len() / m;
2084                    frame_payloads = (0..m)
2085                        .map(|i| &region[i * frame_len..(i + 1) * frame_len])
2086                        .collect();
2087                }
2088            }
2089            _ => unreachable!(),
2090        }
2091
2092        // libopus opus_decoder.c opus_decode_native:
2093        //   if (count*packet_frame_size > frame_size)
2094        //      return OPUS_BUFFER_TOO_SMALL;
2095        // The packet's own TOC duration must fit the caller's frame_size. We split
2096        // the caller's buffer as sub_frame_size = frame_size / frame_count, so a
2097        // malformed multi-frame packet (large frame count vs. a small caller
2098        // buffer) would otherwise make sub_frame_size smaller than the 2.5/5 ms
2099        // redundancy-fade region — the fuzzer-found out-of-bounds/underflow panics
2100        // in redundancy_fade_start/redundancy_fade_end. C rejects such packets
2101        // here; so do we.
2102        let packet_frame_samples =
2103            repacketizer::samples_per_frame(toc, self.sampling_rate) as usize;
2104        if frame_count * packet_frame_samples > frame_size {
2105            return Err("Output buffer too small");
2106        }
2107
2108        self.frame_size = frame_size;
2109        self.bandwidth = bandwidth;
2110        self.stream_channels = packet_channels;
2111
2112        let sub_frame_size = frame_size / frame_count;
2113        let sub_output_len = sub_frame_size * self.channels;
2114
2115        match mode {
2116            OpusMode::SilkOnly => {
2117                let internal_sample_rate = match bandwidth {
2118                    Bandwidth::Narrowband => 8000,
2119                    Bandwidth::Mediumband => 12000,
2120                    Bandwidth::Wideband => 16000,
2121                    _ => 16000,
2122                };
2123                let internal_frame_size =
2124                    (frame_duration_ms * internal_sample_rate / 1000) as usize;
2125
2126                if self.sampling_rate != internal_sample_rate
2127                    && internal_sample_rate != self.prev_internal_rate
2128                {
2129                    self.silk_resampler
2130                        .init(internal_sample_rate, self.sampling_rate);
2131                    self.silk_resampler_r
2132                        .init(internal_sample_rate, self.sampling_rate);
2133                    self.prev_internal_rate = internal_sample_rate;
2134                }
2135
2136                // Pure-SILK stereo (both stream and output are 2ch): reconstruct
2137                // true L/R via SILK MS->LR instead of duplicating the mono mid.
2138                let silk_lr = self.channels == 2 && packet_channels == 2;
2139                self.silk_dec.produce_lr = silk_lr;
2140
2141                // Per-packet internal channel switch (libopus dec_API.c:119-166).
2142                let prev_internal_ch = self.silk_dec.n_channels_internal;
2143                if packet_channels as i32 > prev_internal_ch {
2144                    // mono -> stereo: reset the side channel decoder.
2145                    silk::init_decoder::silk_init_decoder(
2146                        &mut self.silk_dec.channel_state[1],
2147                    );
2148                }
2149                if self.channels == 2 && packet_channels == 2 && prev_internal_ch == 1 {
2150                    // Switching to stereo: clear stereo prediction/side history and
2151                    // seed the right-channel resampler from the (continuous) left.
2152                    self.silk_dec.s_stereo_pred_prev_q13 = [0; 2];
2153                    self.silk_dec.s_stereo_side = [0; 2];
2154                    self.silk_resampler_r = self.silk_resampler.clone();
2155                }
2156                self.silk_dec.n_channels_internal = packet_channels as i32;
2157
2158                // A 40/60 ms Opus frame carries 2/3 internal 20 ms SILK frames;
2159                // 10/20 ms carry one. libopus calls silk_Decode once per internal
2160                // frame (continuing the same range coder within the payload). We
2161                // must too — decoding only the first internal frame leaves the
2162                // rest of a 40/60 ms packet silent (the "collapse" bug).
2163                let n_silk = match frame_duration_ms {
2164                    40 => 2,
2165                    60 => 3,
2166                    _ => 1,
2167                };
2168                let internal_sub_frame_size = internal_frame_size / n_silk;
2169                let ratio = self.sampling_rate as f64 / internal_sample_rate as f64;
2170                // Per-FRAME previous mode (libopus updates prev_mode per frame; for
2171                // payloads after the first, the previous frame is this same packet).
2172                let mut prev_mode_frame = self.prev_mode;
2173
2174                for (fi, payload) in frame_payloads.iter().enumerate() {
2175                    let mut rc = RangeCoder::new_decoder(payload);
2176                    let pcm_i16_len = internal_sub_frame_size * self.channels;
2177                    // A malformed packet can imply a frame larger than our scratch
2178                    // buffer; reject it gracefully instead of slicing out of bounds
2179                    // (a decode-path DoS on attacker-controlled input).
2180                    if pcm_i16_len + 2 > self.w_pcm_i16.len() {
2181                        return Err("opus: SILK frame size exceeds buffer");
2182                    }
2183                    let out_start = fi * sub_output_len;
2184                    let mut silk_off = 0usize; // output samples/ch within this Opus frame
2185
2186                    for sf in 0..n_silk {
2187                        let s_mid = self.silk_s_mid;
2188                        let ret = {
2189                            let (silk_dec, pcm_i16) = (&mut self.silk_dec, &mut self.w_pcm_i16);
2190                            // Prepend the previous frame's last two samples (sMid) at
2191                            // [0..2] and decode at offset 2, matching libopus's
2192                            // samplesOut1_tmp[n][2] layout.
2193                            pcm_i16[0] = s_mid[0];
2194                            pcm_i16[1] = s_mid[1];
2195                            silk_dec.decode(
2196                                &mut rc,
2197                                &mut pcm_i16[2..pcm_i16_len + 2],
2198                                silk::decode_frame::FLAG_DECODE_NORMAL,
2199                                sf == 0,
2200                                frame_duration_ms,
2201                                internal_sample_rate,
2202                            )
2203                        };
2204
2205                        if ret < 0 {
2206                            return Err("SILK decoding failed");
2207                        }
2208
2209                        let decoded_samples = ret as usize;
2210                        // Carry the last two decoded samples as next frame's sMid.
2211                        if decoded_samples >= 2 {
2212                            self.silk_s_mid[0] = self.w_pcm_i16[decoded_samples];
2213                            self.silk_s_mid[1] = self.w_pcm_i16[decoded_samples + 1];
2214                        }
2215                        let base = out_start + silk_off * self.channels;
2216
2217                        // Stereo SILK: L in silk_dec.l_out, R in silk_dec.r_out,
2218                        // both already in the 1-sample-delay-line layout. Resample
2219                        // each channel through its own resampler.
2220                        let out_len = if silk_lr {
2221                            if self.sampling_rate == internal_sample_rate {
2222                                for i in 0..decoded_samples {
2223                                    let l = self.silk_dec.l_out[i] as f32 / 32768.0;
2224                                    let r = self.silk_dec.r_out[i] as f32 / 32768.0;
2225                                    let idx = base + i * 2;
2226                                    if idx + 1 < output.len() {
2227                                        output[idx] = l;
2228                                        output[idx + 1] = r;
2229                                    }
2230                                }
2231                                decoded_samples
2232                            } else {
2233                                let out_len = (decoded_samples as f64 * ratio) as usize;
2234                                // Left
2235                                self.silk_resampler.process(
2236                                    &mut self.w_pcm_resampled[..out_len],
2237                                    &self.silk_dec.l_out[..decoded_samples],
2238                                    decoded_samples as i32,
2239                                );
2240                                for i in 0..out_len {
2241                                    let idx = base + i * 2;
2242                                    if idx < output.len() {
2243                                        output[idx] = self.w_pcm_resampled[i] as f32 / 32768.0;
2244                                    }
2245                                }
2246                                // Right (reuse the scratch)
2247                                self.silk_resampler_r.process(
2248                                    &mut self.w_pcm_resampled[..out_len],
2249                                    &self.silk_dec.r_out[..decoded_samples],
2250                                    decoded_samples as i32,
2251                                );
2252                                for i in 0..out_len {
2253                                    let idx = base + i * 2 + 1;
2254                                    if idx < output.len() {
2255                                        output[idx] = self.w_pcm_resampled[i] as f32 / 32768.0;
2256                                    }
2257                                }
2258                                out_len
2259                            }
2260                        } else if self.sampling_rate == internal_sample_rate {
2261                            let frames = decoded_samples;
2262                            for i in 0..frames {
2263                                let v = self.w_pcm_i16[1 + i] as f32 / 32768.0;
2264                                for ch in 0..self.channels {
2265                                    let idx = base + i * self.channels + ch;
2266                                    if idx < output.len() {
2267                                        output[idx] = v;
2268                                    }
2269                                }
2270                            }
2271                            frames
2272                        } else {
2273                            let out_len = (decoded_samples as f64 * ratio) as usize;
2274                            debug_assert!(out_len <= self.w_pcm_resampled.len());
2275                            {
2276                                let (silk_res, pcm_i16, pcm_out) = (
2277                                    &mut self.silk_resampler,
2278                                    &self.w_pcm_i16,
2279                                    &mut self.w_pcm_resampled,
2280                                );
2281                                silk_res.process(
2282                                    &mut pcm_out[..out_len],
2283                                    &pcm_i16[1..1 + decoded_samples],
2284                                    decoded_samples as i32,
2285                                );
2286                            }
2287                            for i in 0..out_len {
2288                                let v = self.w_pcm_resampled[i] as f32 / 32768.0;
2289                                for ch in 0..self.channels {
2290                                    let idx = base + i * self.channels + ch;
2291                                    if idx < output.len() {
2292                                        output[idx] = v;
2293                                    }
2294                                }
2295                            }
2296                            // Stereo output, mono packet: also run the mono signal
2297                            // through the RIGHT-channel resampler so its state stays
2298                            // continuous for the next stereo packet (libopus
2299                            // dec_API.c:351-355). Its output overwrites channel 1,
2300                            // which is numerically ~identical to the left here.
2301                            if self.channels == 2 {
2302                                self.silk_resampler_r.process(
2303                                    &mut self.w_pcm_resampled[..out_len],
2304                                    &self.w_pcm_i16[1..1 + decoded_samples],
2305                                    decoded_samples as i32,
2306                                );
2307                                for i in 0..out_len {
2308                                    let idx = base + i * 2 + 1;
2309                                    if idx < output.len() {
2310                                        output[idx] = self.w_pcm_resampled[i] as f32 / 32768.0;
2311                                    }
2312                                }
2313                            }
2314                            out_len
2315                        };
2316                        silk_off += out_len;
2317                    }
2318
2319                    // --- Opus redundancy layer (opus_decoder.c:420-580) ---
2320                    // A SILK-only frame carries IMPLICIT CELT redundancy: if >= 17
2321                    // bits remain after SILK, the trailing bytes ARE a 5 ms CELT
2322                    // frame (no flag) used to smooth mode/bandwidth transitions.
2323                    let mut redundant_rng = 0u32;
2324                    let mut redundancy = false;
2325                    let mut celt_to_silk = false;
2326                    let plen = payload.len();
2327                    let f5 = (self.sampling_rate / 200) as usize;
2328                    let f2_5 = f5 / 2;
2329                    let red_end_band = celt_endband_for_bandwidth(bandwidth);
2330                    let mut red_buf = [0.0f32; 480]; // F5 * <=2ch, planar
2331                    let mut red_bytes = 0usize;
2332                    if self.sampling_rate == 48000 && rc.tell() + 17 <= (plen as i32) * 8 {
2333                        redundancy = true;
2334                        celt_to_silk = rc.decode_bit_logp(1);
2335                        red_bytes = plen - (((rc.tell() + 7) >> 3) as usize);
2336                        if red_bytes < 2 || red_bytes >= plen {
2337                            redundancy = false;
2338                            red_bytes = 0;
2339                        }
2340                    }
2341                    // CELT->SILK: the redundant frame continues the prior CELT
2342                    // state (a fade-out of the previous CELT mode). Decode BEFORE
2343                    // the hybrid->SILK silence frame to keep libopus state order.
2344                    if redundancy && celt_to_silk {
2345                        redundant_rng = self.decode_redundant_celt(
2346                            &payload[plen - red_bytes..],
2347                            false,
2348                            packet_channels,
2349                            red_end_band,
2350                            &mut red_buf[..f5 * self.channels],
2351                        );
2352                    }
2353                    // Hybrid->SILK transition: let the CELT MDCT fade out by
2354                    // decoding a 2-byte silence frame; its 2.5 ms overlap tail is
2355                    // ADDED to the output (libopus decodes it into pcm before the
2356                    // SILK sum).
2357                    if self.sampling_rate == 48000
2358                        && prev_mode_frame == Some(OpusMode::Hybrid)
2359                        && !(redundancy && celt_to_silk && self.prev_redundancy)
2360                    {
2361                        let silence = [0xFFu8, 0xFF];
2362                        let mut sil_buf = [0.0f32; 240]; // F2_5 * <=2ch, planar
2363                        self.celt_dec.set_stream_channels(packet_channels);
2364                        let mut src = RangeCoder::new_decoder(&silence);
2365                        self.celt_dec.decode_from_range_coder_with_band_range(
2366                            &mut src,
2367                            16,
2368                            f2_5,
2369                            &mut sil_buf[..f2_5 * self.channels],
2370                            0,
2371                            red_end_band,
2372                        );
2373                        let region = &mut output[out_start..out_start + sub_output_len];
2374                        for i in 0..f2_5 {
2375                            for c in 0..self.channels {
2376                                region[i * self.channels + c] += sil_buf[c * f2_5 + i];
2377                            }
2378                        }
2379                    }
2380                    // SILK->CELT: reset, then decode — this PRIMES the CELT state
2381                    // for the upcoming CELT-mode frames (which is why the next mode
2382                    // change skips its reset when prev_redundancy is set).
2383                    if redundancy && !celt_to_silk {
2384                        redundant_rng = self.decode_redundant_celt(
2385                            &payload[plen - red_bytes..],
2386                            true,
2387                            packet_channels,
2388                            red_end_band,
2389                            &mut red_buf[..f5 * self.channels],
2390                        );
2391                    }
2392                    if redundancy {
2393                        let window = modes::default_mode().window;
2394                        let region = &mut output[out_start..out_start + sub_output_len];
2395                        if celt_to_silk {
2396                            redundancy_fade_start(
2397                                region,
2398                                &red_buf,
2399                                f5,
2400                                f2_5,
2401                                self.channels,
2402                                window,
2403                            );
2404                        } else {
2405                            redundancy_fade_end(
2406                                region,
2407                                sub_frame_size,
2408                                &red_buf,
2409                                f5,
2410                                f2_5,
2411                                self.channels,
2412                                window,
2413                            );
2414                        }
2415                    }
2416                    self.prev_redundancy = redundancy && !celt_to_silk;
2417                    prev_mode_frame = Some(OpusMode::SilkOnly);
2418                    self.last_range = rc.rng ^ redundant_rng;
2419                }
2420                self.prev_mode = Some(OpusMode::SilkOnly);
2421                Ok(frame_size)
2422            }
2423
2424            OpusMode::CeltOnly => {
2425                let celt_end_band = self.celt_end_band_from_toc(toc);
2426                // libopus opus_decoder.c:515 — discard CELT state on a mode change
2427                // unless the previous frame's SILK->CELT redundant frame already
2428                // primed it.
2429                if let Some(pm) = self.prev_mode {
2430                    if pm != OpusMode::CeltOnly && !self.prev_redundancy {
2431                        self.celt_dec.reset();
2432                    }
2433                }
2434                self.prev_redundancy = false;
2435                // Mono packet in a stereo stream => C=1, CC=2 (continuous state).
2436                self.celt_dec.set_stream_channels(packet_channels);
2437
2438                for (fi, payload) in frame_payloads.iter().enumerate() {
2439                    let mut rc = RangeCoder::new_decoder(payload);
2440                    let total_bits = (payload.len() * 8) as i32;
2441                    let needed = sub_frame_size * self.channels;
2442                    let out_start = fi * needed;
2443                    let out_end = (out_start + needed).min(output.len());
2444
2445                    if output.len() < out_end {
2446                        return Err("Output buffer too small");
2447                    }
2448
2449                    if self.channels == 1 {
2450                        self.celt_dec.decode_from_range_coder_with_band_range(
2451                            &mut rc,
2452                            total_bits,
2453                            sub_frame_size,
2454                            &mut output[out_start..out_end],
2455                            0,
2456                            celt_end_band,
2457                        );
2458                        for sample in &mut output[out_start..out_end] {
2459                            *sample = sample.clamp(-1.0, 1.0);
2460                        }
2461                    } else {
2462                        self.celt_dec.decode_from_range_coder_with_band_range(
2463                            &mut rc,
2464                            total_bits,
2465                            sub_frame_size,
2466                            &mut self.w_celt_planar[..needed],
2467                            0,
2468                            celt_end_band,
2469                        );
2470                        for i in 0..sub_frame_size {
2471                            for ch in 0..self.channels {
2472                                let idx = out_start + i * self.channels + ch;
2473                                output[idx] =
2474                                    self.w_celt_planar[ch * sub_frame_size + i].clamp(-1.0, 1.0);
2475                            }
2476                        }
2477                    }
2478                    self.last_range = rc.rng;
2479                }
2480                self.prev_mode = Some(OpusMode::CeltOnly);
2481                Ok(frame_size)
2482            }
2483
2484            OpusMode::Hybrid => {
2485                let internal_sample_rate = 16000;
2486                let internal_frame_size =
2487                    (frame_duration_ms * internal_sample_rate / 1000) as usize;
2488                let celt_end_band = self.celt_end_band_from_toc(toc);
2489
2490                if self.sampling_rate != internal_sample_rate
2491                    && internal_sample_rate != self.prev_internal_rate
2492                {
2493                    self.silk_resampler
2494                        .init(internal_sample_rate, self.sampling_rate);
2495                    self.silk_resampler_r
2496                        .init(internal_sample_rate, self.sampling_rate);
2497                    self.prev_internal_rate = internal_sample_rate;
2498                }
2499
2500                // Same SILK stereo/channel handling as the SilkOnly arm: true L/R
2501                // low band via MS->LR for stereo packets; per-packet internal
2502                // channel switch with side-channel/stereo-state resets.
2503                let silk_lr = self.channels == 2 && packet_channels == 2;
2504                self.silk_dec.produce_lr = silk_lr;
2505                let prev_internal_ch = self.silk_dec.n_channels_internal;
2506                if packet_channels as i32 > prev_internal_ch {
2507                    silk::init_decoder::silk_init_decoder(&mut self.silk_dec.channel_state[1]);
2508                }
2509                if self.channels == 2 && packet_channels == 2 && prev_internal_ch == 1 {
2510                    self.silk_dec.s_stereo_pred_prev_q13 = [0; 2];
2511                    self.silk_dec.s_stereo_side = [0; 2];
2512                    self.silk_resampler_r = self.silk_resampler.clone();
2513                }
2514                self.silk_dec.n_channels_internal = packet_channels as i32;
2515
2516                for (fi, payload) in frame_payloads.iter().enumerate() {
2517                    let mut rc = RangeCoder::new_decoder(payload);
2518                    let pcm_silk_i16_len = internal_frame_size * self.channels;
2519                    if pcm_silk_i16_len + 2 > self.w_pcm_i16.len() {
2520                        return Err("opus: SILK frame size exceeds buffer");
2521                    }
2522
2523                    // Prepend the previous frame's last two samples (sMid) and
2524                    // decode at offset 2, matching libopus's samplesOut1_tmp[n][2]
2525                    // layout — the resampler is fed from offset 1 (the 1-sample
2526                    // delay line), keeping the SILK low band aligned with the CELT
2527                    // high band exactly as in the reference.
2528                    let s_mid = self.silk_s_mid;
2529                    let ret = {
2530                        let (silk_dec, pcm_i16) = (&mut self.silk_dec, &mut self.w_pcm_i16);
2531                        pcm_i16[0] = s_mid[0];
2532                        pcm_i16[1] = s_mid[1];
2533                        silk_dec.decode(
2534                            &mut rc,
2535                            &mut pcm_i16[2..pcm_silk_i16_len + 2],
2536                            silk::decode_frame::FLAG_DECODE_NORMAL,
2537                            true,
2538                            frame_duration_ms,
2539                            internal_sample_rate,
2540                        )
2541                    };
2542
2543                    if ret < 0 {
2544                        return Err("SILK decoding failed");
2545                    }
2546
2547                    let silk_out_len = sub_frame_size * self.channels;
2548                    self.w_silk_out[..silk_out_len].fill(0.0);
2549                    if ret > 0 {
2550                        let decoded_samples = ret as usize;
2551                        if decoded_samples >= 2 {
2552                            self.silk_s_mid[0] = self.w_pcm_i16[decoded_samples];
2553                            self.silk_s_mid[1] = self.w_pcm_i16[decoded_samples + 1];
2554                        }
2555                        let ratio = self.sampling_rate as f64 / internal_sample_rate as f64;
2556                        let out_len =
2557                            ((decoded_samples as f64 * ratio) as usize).min(sub_frame_size);
2558                        debug_assert!(out_len <= self.w_pcm_resampled.len());
2559                        if silk_lr {
2560                            // Stereo low band: L/R from dec_api (already in the
2561                            // 1-sample-delay layout), each through its own resampler.
2562                            self.silk_resampler.process(
2563                                &mut self.w_pcm_resampled[..out_len],
2564                                &self.silk_dec.l_out[..decoded_samples],
2565                                decoded_samples as i32,
2566                            );
2567                            for i in 0..out_len {
2568                                self.w_silk_out[i * 2] = self.w_pcm_resampled[i] as f32 / 32768.0;
2569                            }
2570                            self.silk_resampler_r.process(
2571                                &mut self.w_pcm_resampled[..out_len],
2572                                &self.silk_dec.r_out[..decoded_samples],
2573                                decoded_samples as i32,
2574                            );
2575                            for i in 0..out_len {
2576                                self.w_silk_out[i * 2 + 1] =
2577                                    self.w_pcm_resampled[i] as f32 / 32768.0;
2578                            }
2579                        } else {
2580                            self.silk_resampler.process(
2581                                &mut self.w_pcm_resampled[..out_len],
2582                                &self.w_pcm_i16[1..1 + decoded_samples],
2583                                decoded_samples as i32,
2584                            );
2585                            for i in 0..out_len {
2586                                let v = self.w_pcm_resampled[i] as f32 / 32768.0;
2587                                for ch in 0..self.channels {
2588                                    self.w_silk_out[i * self.channels + ch] = v;
2589                                }
2590                            }
2591                            // Mono packet, stereo output: keep the right-channel
2592                            // resampler continuous (libopus dec_API.c:351-355).
2593                            if self.channels == 2 {
2594                                self.silk_resampler_r.process(
2595                                    &mut self.w_pcm_resampled[..out_len],
2596                                    &self.w_pcm_i16[1..1 + decoded_samples],
2597                                    decoded_samples as i32,
2598                                );
2599                                for i in 0..out_len {
2600                                    self.w_silk_out[i * 2 + 1] =
2601                                        self.w_pcm_resampled[i] as f32 / 32768.0;
2602                                }
2603                            }
2604                        }
2605                    }
2606
2607                    // --- Opus redundancy layer, hybrid form (opus_decoder.c) ---
2608                    // redundancy = bit(12); if set: celt_to_silk = bit(1),
2609                    // redundancy_bytes = uint(256)+2 taken from the END of the
2610                    // packet — the MAIN CELT layer still decodes, but with the
2611                    // range coder's storage shrunk by those bytes (this changes
2612                    // its raw-bit region and tell budget).
2613                    let plen = payload.len();
2614                    let mut redundancy = false;
2615                    let mut celt_to_silk = false;
2616                    let mut red_bytes = 0usize;
2617                    let mut effective_len = plen;
2618                    if rc.tell() + 37 <= (plen as i32) * 8 {
2619                        redundancy = rc.decode_bit_logp(12);
2620                        if redundancy {
2621                            celt_to_silk = rc.decode_bit_logp(1);
2622                            red_bytes = rc.dec_uint(256) as usize + 2;
2623                            if red_bytes <= effective_len {
2624                                effective_len -= red_bytes;
2625                            } else {
2626                                red_bytes = 0;
2627                                redundancy = false;
2628                            }
2629                            if redundancy && (effective_len as i32) * 8 < rc.tell() {
2630                                effective_len = plen;
2631                                red_bytes = 0;
2632                                redundancy = false;
2633                            }
2634                            if redundancy {
2635                                rc.storage -= red_bytes as u32;
2636                            }
2637                        }
2638                    }
2639                    let f5 = (self.sampling_rate / 200) as usize;
2640                    let f2_5 = f5 / 2;
2641                    let red_end_band = celt_endband_for_bandwidth(bandwidth);
2642                    let mut red_buf = [0.0f32; 480];
2643                    let mut redundant_rng = 0u32;
2644                    let do_red = redundancy && self.sampling_rate == 48000;
2645                    // CELT->SILK: redundant frame decodes BEFORE the main CELT,
2646                    // continuing the prior CELT state (fade-out of previous CELT).
2647                    if do_red && celt_to_silk {
2648                        redundant_rng = self.decode_redundant_celt(
2649                            &payload[plen - red_bytes..],
2650                            false,
2651                            packet_channels,
2652                            red_end_band,
2653                            &mut red_buf[..f5 * self.channels],
2654                        );
2655                    }
2656
2657                    // Main CELT high band. libopus opus_decoder.c:515 — reset CELT
2658                    // on a mode change unless primed by prior SILK->CELT redundancy.
2659                    if fi == 0 {
2660                        if let Some(pm) = self.prev_mode {
2661                            if pm != OpusMode::Hybrid && !self.prev_redundancy {
2662                                self.celt_dec.reset();
2663                            }
2664                        }
2665                    }
2666                    self.celt_dec.set_stream_channels(packet_channels);
2667                    let total_bits = (effective_len * 8) as i32;
2668                    {
2669                        let (celt_dec, celt_planar) = (&mut self.celt_dec, &mut self.w_celt_planar);
2670                        celt_dec.decode_from_range_coder_with_band_range(
2671                            &mut rc,
2672                            total_bits,
2673                            sub_frame_size,
2674                            &mut celt_planar[..silk_out_len],
2675                            17,
2676                            celt_end_band,
2677                        );
2678
2679                        if self.channels == 1 {
2680                            self.w_celt_out[..silk_out_len]
2681                                .copy_from_slice(&self.w_celt_planar[..silk_out_len]);
2682                        } else {
2683                            for i in 0..sub_frame_size {
2684                                for ch in 0..self.channels {
2685                                    self.w_celt_out[i * self.channels + ch] =
2686                                        self.w_celt_planar[ch * sub_frame_size + i];
2687                                }
2688                            }
2689                        }
2690                    }
2691
2692                    let out_start = fi * silk_out_len;
2693                    let total = silk_out_len.min(output.len() - out_start);
2694                    for j in 0..total {
2695                        output[out_start + j] =
2696                            (self.w_silk_out[j] + self.w_celt_out[j]).clamp(-1.0, 1.0);
2697                    }
2698
2699                    // SILK->CELT: reset + decode the redundant frame AFTER the main
2700                    // decode; it primes the CELT state for the upcoming CELT mode.
2701                    if do_red && !celt_to_silk {
2702                        redundant_rng = self.decode_redundant_celt(
2703                            &payload[plen - red_bytes..],
2704                            true,
2705                            packet_channels,
2706                            red_end_band,
2707                            &mut red_buf[..f5 * self.channels],
2708                        );
2709                    }
2710                    if do_red {
2711                        let window = modes::default_mode().window;
2712                        let region = &mut output[out_start..out_start + silk_out_len];
2713                        if celt_to_silk {
2714                            redundancy_fade_start(
2715                                region,
2716                                &red_buf,
2717                                f5,
2718                                f2_5,
2719                                self.channels,
2720                                window,
2721                            );
2722                        } else {
2723                            redundancy_fade_end(
2724                                region,
2725                                sub_frame_size,
2726                                &red_buf,
2727                                f5,
2728                                f2_5,
2729                                self.channels,
2730                                window,
2731                            );
2732                        }
2733                    }
2734                    self.prev_redundancy = redundancy && !celt_to_silk;
2735                    self.last_range = rc.rng ^ redundant_rng;
2736                }
2737                self.prev_mode = Some(OpusMode::Hybrid);
2738                Ok(frame_size)
2739            }
2740        }
2741    }
2742}
2743
2744impl OpusDecoder {
2745    #[inline(always)]
2746    fn celt_end_band_from_toc(&self, toc: u8) -> usize {
2747        let mode = modes::default_mode();
2748        let top = mode.eff_ebands;
2749        if mode_from_toc(toc) == OpusMode::CeltOnly && toc >= 0x80 {
2750            const FROM_OPUS_TABLE: [u8; 16] = [
2751                0x80, 0x88, 0x90, 0x98, 0x40, 0x48, 0x50, 0x58, 0x20, 0x28, 0x30, 0x38, 0x00, 0x08,
2752                0x10, 0x18,
2753            ];
2754            let idx = ((toc >> 3) - 16) as usize;
2755            let data0 = FROM_OPUS_TABLE[idx] | (toc & 0x7);
2756            let trim = (data0 >> 5) as usize;
2757            return top.saturating_sub(2 * trim).max(1);
2758        }
2759        // Hybrid: libopus maps the packet bandwidth to a CELT end band
2760        // (opus_decoder.c: SWB -> 19, FB -> 21). Decoding SWB hybrid with 21
2761        // reads two bands the encoder never coded -> range desync every packet.
2762        if mode_from_toc(toc) == OpusMode::Hybrid
2763            && bandwidth_from_toc(toc) == Bandwidth::Superwideband
2764        {
2765            return 19.min(top);
2766        }
2767        top
2768    }
2769
2770    /// Decode a redundant CELT frame (opus_decoder.c "5 ms redundant frame"):
2771    /// start band 0, end band from the packet bandwidth, 5 ms, its own range
2772    /// decoder. Returns the redundant final range; PLANAR output in `buf`
2773    /// (F5 samples per state channel). Only valid at 48 kHz output.
2774    fn decode_redundant_celt(
2775        &mut self,
2776        red: &[u8],
2777        reset_first: bool,
2778        packet_channels: usize,
2779        end_band: usize,
2780        buf: &mut [f32],
2781    ) -> u32 {
2782        if reset_first {
2783            self.celt_dec.reset();
2784        }
2785        self.celt_dec.set_stream_channels(packet_channels);
2786        let f5 = (self.sampling_rate / 200) as usize;
2787        let mut rrc = RangeCoder::new_decoder(red);
2788        let total_bits = (red.len() * 8) as i32;
2789        self.celt_dec.decode_from_range_coder_with_band_range(
2790            &mut rrc, total_bits, f5, buf, 0, end_band,
2791        );
2792        rrc.rng
2793    }
2794}
2795
2796/// libopus opus_decoder.c bandwidth -> CELT end band for the packet.
2797fn celt_endband_for_bandwidth(bw: Bandwidth) -> usize {
2798    match bw {
2799        Bandwidth::Narrowband => 13,
2800        Bandwidth::Mediumband | Bandwidth::Wideband => 17,
2801        Bandwidth::Superwideband => 19,
2802        _ => 21,
2803    }
2804}
2805
2806/// smooth_fade cross-fades (w = window[i]^2, 48 kHz inc=1) applied to the
2807/// interleaved output region of one frame. `red` is PLANAR (F5 per channel).
2808/// celt_to_silk: redundant frame occupies the START of the frame — first 2.5 ms
2809/// copied verbatim, next 2.5 ms fades redundant -> main.
2810///
2811/// Indexing invariant: `out.len() >= f5 * channels` (writes reach sample
2812/// f5-1 = 2*f2_5-1). A malformed multi-frame packet used to violate this (a
2813/// hostile frame count made the per-frame region tinier than F5, fuzzer-found
2814/// OOB panics here); decode() now rejects such packets up front exactly as C
2815/// libopus does (opus_decode_native's count*packet_frame_size > frame_size ->
2816/// OPUS_BUFFER_TOO_SMALL, and the 120 ms cap of opus_packet_parse_impl), so a
2817/// redundant frame always has >= 10 ms of frame to fade into, as in C.
2818fn redundancy_fade_start(
2819    out: &mut [f32],
2820    red: &[f32],
2821    f5: usize,
2822    f2_5: usize,
2823    channels: usize,
2824    window: &[f32],
2825) {
2826    for i in 0..f2_5 {
2827        for c in 0..channels {
2828            out[i * channels + c] = red[c * f5 + i];
2829        }
2830    }
2831    for i in 0..f2_5 {
2832        let w = window[i] * window[i];
2833        for c in 0..channels {
2834            let idx = (f2_5 + i) * channels + c;
2835            out[idx] = (1.0 - w) * red[c * f5 + f2_5 + i] + w * out[idx];
2836        }
2837    }
2838}
2839
2840/// SILK->CELT: redundant frame occupies the END of the frame — the last 2.5 ms
2841/// fades main -> redundant (second half of the redundant frame).
2842///
2843/// Indexing invariant: `frame_samples >= f2_5` and `out.len() >=
2844/// frame_samples * channels` (the index `frame_samples - f2_5 + i` would
2845/// otherwise underflow). A malformed multi-frame packet used to violate this
2846/// (fuzzer-found subtract-with-overflow panic here); decode() now rejects such
2847/// packets up front exactly as C libopus does (opus_decode_native's
2848/// count*packet_frame_size > frame_size -> OPUS_BUFFER_TOO_SMALL, plus the
2849/// 120 ms cap of opus_packet_parse_impl), so redundancy only ever runs on
2850/// frames of >= 10 ms, as in C.
2851fn redundancy_fade_end(
2852    out: &mut [f32],
2853    frame_samples: usize,
2854    red: &[f32],
2855    f5: usize,
2856    f2_5: usize,
2857    channels: usize,
2858    window: &[f32],
2859) {
2860    for i in 0..f2_5 {
2861        let w = window[i] * window[i];
2862        for c in 0..channels {
2863            let idx = (frame_samples - f2_5 + i) * channels + c;
2864            out[idx] = (1.0 - w) * out[idx] + w * red[c * f5 + f2_5 + i];
2865        }
2866    }
2867}
2868
2869fn frame_rate_from_params(sampling_rate: i32, frame_size: usize) -> Option<i32> {
2870    let frame_size = frame_size as i32;
2871    if frame_size == 0 || sampling_rate % frame_size != 0 {
2872        return None;
2873    }
2874    Some(sampling_rate / frame_size)
2875}
2876
2877fn gen_toc(mode: OpusMode, frame_rate: i32, bandwidth: Bandwidth, channels: usize) -> u8 {
2878    let mut rate = frame_rate;
2879    let mut period = 0;
2880    while rate < 400 {
2881        rate <<= 1;
2882        period += 1;
2883    }
2884
2885    let mut toc = match mode {
2886        OpusMode::SilkOnly => {
2887            let bw = (bandwidth as i32 - Bandwidth::Narrowband as i32) << 5;
2888            let per = (period - 2) << 3;
2889            (bw | per) as u8
2890        }
2891        OpusMode::CeltOnly => {
2892            let mut tmp = bandwidth as i32 - Bandwidth::Mediumband as i32;
2893            if tmp < 0 {
2894                tmp = 0;
2895            }
2896            let per = period << 3;
2897            (0x80 | (tmp << 5) | per) as u8
2898        }
2899        OpusMode::Hybrid => {
2900            let base_config = if bandwidth == Bandwidth::Superwideband {
2901                12
2902            } else {
2903                14
2904            };
2905            let period_offset = if frame_rate >= 100 { 0 } else { 1 };
2906            ((base_config + period_offset) << 3) as u8
2907        }
2908    };
2909
2910    if channels == 2 {
2911        toc |= 0x04;
2912    }
2913    toc
2914}
2915
2916fn mode_from_toc(toc: u8) -> OpusMode {
2917    if toc & 0x80 != 0 {
2918        OpusMode::CeltOnly
2919    } else if toc & 0x60 == 0x60 {
2920        OpusMode::Hybrid
2921    } else {
2922        OpusMode::SilkOnly
2923    }
2924}
2925
2926fn bandwidth_from_toc(toc: u8) -> Bandwidth {
2927    let mode = mode_from_toc(toc);
2928    match mode {
2929        OpusMode::SilkOnly => {
2930            let bw_bits = (toc >> 5) & 0x03;
2931            match bw_bits {
2932                0 => Bandwidth::Narrowband,
2933                1 => Bandwidth::Mediumband,
2934                2 => Bandwidth::Wideband,
2935                _ => Bandwidth::Wideband,
2936            }
2937        }
2938        OpusMode::Hybrid => {
2939            let bw_bit = (toc >> 4) & 0x01;
2940            if bw_bit == 0 {
2941                Bandwidth::Superwideband
2942            } else {
2943                Bandwidth::Fullband
2944            }
2945        }
2946        OpusMode::CeltOnly => {
2947            let bw_bits = (toc >> 5) & 0x03;
2948            match bw_bits {
2949                0 => Bandwidth::Mediumband,
2950                1 => Bandwidth::Wideband,
2951                2 => Bandwidth::Superwideband,
2952                3 => Bandwidth::Fullband,
2953                _ => Bandwidth::Fullband,
2954            }
2955        }
2956    }
2957}
2958
2959fn frame_duration_ms_from_toc(toc: u8) -> i32 {
2960    let mode = mode_from_toc(toc);
2961    match mode {
2962        OpusMode::SilkOnly => {
2963            let config = (toc >> 3) & 0x03;
2964            match config {
2965                0 => 10,
2966                1 => 20,
2967                2 => 40,
2968                3 => 60,
2969                _ => 20,
2970            }
2971        }
2972        OpusMode::Hybrid => {
2973            let config = (toc >> 3) & 0x01;
2974            if config == 0 { 10 } else { 20 }
2975        }
2976        OpusMode::CeltOnly => {
2977            let config = (toc >> 3) & 0x03;
2978            match config {
2979                0 => 2,
2980                1 => 5,
2981                2 => 10,
2982                3 => 20,
2983                _ => 20,
2984            }
2985        }
2986    }
2987}
2988
2989fn channels_from_toc(toc: u8) -> usize {
2990    if toc & 0x04 != 0 { 2 } else { 1 }
2991}
2992
2993/// RFC 6716 §3.1 frame-length coding (used by code 2 and VBR code 3): a length
2994/// of 0..=251 is one byte with that value; 252..=1275 is two bytes `b0` (252..255)
2995/// then `b1`, giving `b1*4 + b0`. Returns `(length, bytes_consumed)`.
2996fn read_opus_frame_len(data: &[u8], ptr: usize) -> Result<(usize, usize), &'static str> {
2997    let b0 = *data.get(ptr).ok_or("Opus frame length: truncated")? as usize;
2998    if b0 < 252 {
2999        Ok((b0, 1))
3000    } else {
3001        let b1 = *data.get(ptr + 1).ok_or("Opus frame length: truncated 2-byte")? as usize;
3002        Ok((b1 * 4 + b0, 2))
3003    }
3004}
3005
3006#[cfg(test)]
3007mod tests {
3008    use super::*;
3009
3010    fn frame_size_from_toc(toc: u8, sampling_rate: i32) -> Option<usize> {
3011        let mode = mode_from_toc(toc);
3012        match mode {
3013            OpusMode::CeltOnly => {
3014                let period = ((toc >> 3) & 0x03) as i32;
3015                let frame_rate = 400 >> period;
3016                if frame_rate == 0 || sampling_rate % frame_rate != 0 {
3017                    return None;
3018                }
3019                Some((sampling_rate / frame_rate) as usize)
3020            }
3021            OpusMode::SilkOnly => {
3022                let duration_ms = frame_duration_ms_from_toc(toc);
3023                Some((sampling_rate as i64 * duration_ms as i64 / 1000) as usize)
3024            }
3025            OpusMode::Hybrid => {
3026                let duration_ms = frame_duration_ms_from_toc(toc);
3027                Some((sampling_rate as i64 * duration_ms as i64 / 1000) as usize)
3028            }
3029        }
3030    }
3031
3032    #[test]
3033    fn gen_toc_matches_celt_reference_values() {
3034        let sampling_rate = 48_000;
3035        let cases = [
3036            (120usize, 0xE0u8),
3037            (240usize, 0xE8u8),
3038            (480usize, 0xF0u8),
3039            (960usize, 0xF8u8),
3040        ];
3041
3042        for (frame_size, expected_toc) in cases {
3043            let frame_rate = frame_rate_from_params(sampling_rate, frame_size).unwrap();
3044            let toc = gen_toc(OpusMode::CeltOnly, frame_rate, Bandwidth::Fullband, 1);
3045            assert_eq!(
3046                toc, expected_toc,
3047                "frame_size {} expected TOC {:02X} got {:02X}",
3048                frame_size, expected_toc, toc
3049            );
3050            let decoded_size = frame_size_from_toc(toc, sampling_rate).unwrap();
3051            assert_eq!(decoded_size, frame_size);
3052        }
3053
3054        let stereo_toc = gen_toc(
3055            OpusMode::CeltOnly,
3056            frame_rate_from_params(sampling_rate, 960).unwrap(),
3057            Bandwidth::Fullband,
3058            2,
3059        );
3060        assert_eq!(channels_from_toc(stereo_toc), 2);
3061    }
3062
3063    #[test]
3064    fn test_celt_decoder_large_frame_sizes() {
3065        let sampling_rate = 48000;
3066        let channels = 1;
3067
3068        let mut decoder = OpusDecoder::new(sampling_rate, channels).unwrap();
3069
3070        let frame_sizes = [120, 240, 480, 960];
3071
3072        for frame_size in frame_sizes {
3073            let toc = gen_toc(
3074                OpusMode::CeltOnly,
3075                frame_rate_from_params(sampling_rate, frame_size).unwrap(),
3076                Bandwidth::Fullband,
3077                channels,
3078            );
3079            let packet = [toc, 0, 0, 0, 0];
3080
3081            let mut output = vec![0.0f32; frame_size * channels];
3082
3083            let _ = decoder.decode(&packet, frame_size, &mut output);
3084        }
3085
3086        let channels = 2;
3087        let mut decoder = OpusDecoder::new(sampling_rate, channels).unwrap();
3088
3089        for frame_size in frame_sizes {
3090            let toc = gen_toc(
3091                OpusMode::CeltOnly,
3092                frame_rate_from_params(sampling_rate, frame_size).unwrap(),
3093                Bandwidth::Fullband,
3094                channels,
3095            );
3096            let packet = [toc, 0, 0, 0, 0];
3097
3098            let mut output = vec![0.0f32; frame_size * channels];
3099            let _ = decoder.decode(&packet, frame_size, &mut output);
3100        }
3101    }
3102
3103    #[test]
3104    fn test_celt_decoder_edge_case_frame_sizes() {
3105        let sampling_rate = 48000;
3106        let channels = 1;
3107        let mut decoder = OpusDecoder::new(sampling_rate, channels).unwrap();
3108
3109        let edge_sizes = [2048, 2167, 2168, 2169, 2880, 3072];
3110
3111        for frame_size in edge_sizes {
3112            let mut output = vec![0.0f32; frame_size * channels];
3113
3114            let _ = decoder.decode(&[0x80, 0, 0, 0], frame_size, &mut output);
3115        }
3116    }
3117
3118    // Regression test for: "index out of bounds: the len is 48 but the index is 119"
3119    // Root cause: frame_size=48 at 48kHz gives frame_rate=1000, which is not a valid
3120    // Hybrid-mode frame rate but was not validated.  CELT's lm-search then silently
3121    // fell back to lm=0, computed n2=120, and wrote output[119] into a 48-element
3122    // slice.  Triggered via G.729-decoded PCM (8kHz) passed to a 48kHz Opus encoder
3123    // without proper resampling, so the encoder received 48 samples instead of 480.
3124    #[test]
3125    fn test_invalid_small_frame_size_returns_error_not_panic() {
3126        let mut enc = OpusEncoder::new(48000, 2, Application::Voip).unwrap();
3127        enc.bitrate_bps = 64000;
3128        enc.complexity = 5;
3129        enc.use_cbr = true;
3130
3131        // 48 samples at 48kHz = 1ms → frame_rate=1000, invalid for Hybrid mode.
3132        let input = vec![0.0f32; 48 * 2]; // stereo interleaved
3133        let mut output = vec![0u8; 256];
3134
3135        let result = enc.encode(&input, 48, &mut output);
3136        assert!(
3137            result.is_err(),
3138            "encode with invalid frame_size=48 should return Err, not panic"
3139        );
3140    }
3141
3142    // Also verify that the Audio application path (always Hybrid at 48 kHz) rejects
3143    // the same bad frame size.
3144    #[test]
3145    fn test_invalid_small_frame_size_audio_application_returns_error() {
3146        let mut enc = OpusEncoder::new(48000, 1, Application::Audio).unwrap();
3147        let input = vec![0.0f32; 48];
3148        let mut output = vec![0u8; 256];
3149
3150        let result = enc.encode(&input, 48, &mut output);
3151        assert!(
3152            result.is_err(),
3153            "Audio/48kHz encoder with frame_size=48 should return Err"
3154        );
3155    }
3156}