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