Skip to main content

opus_rs/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2#![allow(unsafe_op_in_unsafe_fn)]
3#![allow(clippy::too_many_arguments)]
4#![allow(clippy::needless_range_loop)]
5
6mod compat;
7mod fixedvec;
8
9pub mod bands;
10pub mod celt;
11pub mod celt_lpc;
12pub mod hp_cutoff;
13pub mod kiss_fft;
14pub mod mdct;
15pub mod modes;
16pub mod pitch;
17pub mod pvq;
18pub mod quant_bands;
19pub mod range_coder;
20pub mod rate;
21pub mod silk;
22
23pub use silk::{SilkResampler, SilkResamplerDown1_3, SilkResamplerDown1_6};
24
25use crate::fixedvec::FixedVec;
26pub use celt::{CeltDecoder, CeltEncoder};
27use hp_cutoff::{dc_reject_float, hp_cutoff, hp_cutoff_float};
28use range_coder::RangeCoder;
29use silk::control_codec::silk_control_encoder;
30use silk::enc_api::silk_encode;
31use silk::init_encoder::silk_init_encoder;
32use silk::lin2log::silk_lin2log;
33use silk::log2lin::silk_log2lin;
34use silk::macros::*;
35use silk::resampler::{silk_resampler_down2, silk_resampler_down2_3};
36use silk::structs::SilkEncoderState;
37
38// --- Heap-free buffer capacity constants (worst case: 2 channels). ---
39const OPUS_MAX_CHANNELS: usize = 2;
40/// Largest API frame in samples/channel (120 ms @ 48 kHz = 5760). Used by the
41/// encoder's per-frame input buffers (sized `frame_size * channels`).
42const OPUS_MAX_FRAME: usize = 5760;
43/// Largest *single-frame* samples/channel (60 ms @ 48 kHz = 2880). The decoder's
44/// staging buffers hold one sub-frame at a time, so they're sized to this — not
45/// the full packet. (Halves the decoder footprint vs. a naive 5760/channel.)
46const OPUS_MAX_SUBFRAME: usize = 2880;
47/// Decoder per-sub-frame staging cap: `OPUS_MAX_SUBFRAME * max_channels`.
48const OPUS_SUBFRAME_SCRATCH: usize = OPUS_MAX_SUBFRAME * OPUS_MAX_CHANNELS;
49/// High-pass filter state memory (`channels * 2`).
50const OPUS_HP_MEM: usize = OPUS_MAX_CHANNELS * 2;
51/// Decoder `w_pcm_i16` cap (`960 * max_channels`).
52const OPUS_PCM_I16: usize = 960 * OPUS_MAX_CHANNELS;
53/// Decoder `prev_pcm_tail` cap (`240 * max_channels`).
54const OPUS_PCM_TAIL: usize = 240 * OPUS_MAX_CHANNELS;
55/// Max number of frames encoded in one Opus packet (RFC 6716 caps at 48 for
56/// 2.5 ms codes in a 120 ms packet).
57const OPUS_MAX_PACKET_FRAMES: usize = 48;
58/// RFC 6716 §3.1: a single Opus packet carries at most 1276 bytes of data.
59const OPUS_MAX_PACKET_BYTES: usize = 1276;
60/// C `OpusEncoder.delay_buffer[MAX_ENCODER_BUFFER*2]` (480 samples * 2 ch).
61/// Holds the delay-compensation ring feeding the CELT encoder.
62const OPUS_DELAY_BUF: usize = 480 * OPUS_MAX_CHANNELS;
63/// CELT/hybrid input staging cap: `(delay_compensation + frame)*channels`.
64/// Worst case 60 ms stereo @ 48 kHz: (192 + 2880) * 2 = 6144.
65const OPUS_PCM_BUF: usize = (192 + OPUS_MAX_SUBFRAME) * OPUS_MAX_CHANNELS;
66
67#[derive(Debug, Clone, Copy, PartialEq, Eq)]
68pub enum Application {
69    Voip = 2048,
70    Audio = 2049,
71    RestrictedLowDelay = 2051,
72}
73
74#[derive(Debug, Clone, Copy, PartialEq, Eq)]
75pub enum Bandwidth {
76    Auto = -1000,
77    Narrowband = 1101,
78    Mediumband = 1102,
79    Wideband = 1103,
80    Superwideband = 1104,
81    Fullband = 1105,
82}
83
84#[derive(Debug, Clone, Copy, PartialEq, Eq)]
85enum OpusMode {
86    SilkOnly,
87    Hybrid,
88    CeltOnly,
89}
90
91pub struct OpusEncoder {
92    #[cfg(not(feature = "heap"))]
93    celt_enc: CeltEncoder,
94    #[cfg(feature = "heap")]
95    celt_enc: Box<CeltEncoder>,
96    #[cfg(not(feature = "heap"))]
97    silk_enc: SilkEncoderState,
98    #[cfg(feature = "heap")]
99    silk_enc: Box<SilkEncoderState>,
100    application: Application,
101    sampling_rate: i32,
102    channels: usize,
103    bandwidth: Bandwidth,
104    pub bitrate_bps: i32,
105    pub complexity: i32,
106    pub use_cbr: bool,
107
108    pub use_inband_fec: bool,
109
110    pub packet_loss_perc: i32,
111    silk_initialized: bool,
112    mode: OpusMode,
113    prev_enc_mode: Option<OpusMode>,
114
115    variable_hp_smth2_q15: i32,
116    hp_mem: FixedVec<i32, OPUS_HP_MEM>,
117
118    #[cfg(not(feature = "heap"))]
119    buf_filtered: FixedVec<i16, OPUS_MAX_FRAME>,
120    #[cfg(feature = "heap")]
121    buf_filtered: Box<FixedVec<i16, OPUS_MAX_FRAME>>,
122    #[cfg(not(feature = "heap"))]
123    buf_silk_input: FixedVec<i16, OPUS_MAX_FRAME>,
124    #[cfg(feature = "heap")]
125    buf_silk_input: Box<FixedVec<i16, OPUS_MAX_FRAME>>,
126    #[cfg(not(feature = "heap"))]
127    buf_stereo_mid: FixedVec<i16, OPUS_MAX_FRAME>,
128    #[cfg(feature = "heap")]
129    buf_stereo_mid: Box<FixedVec<i16, OPUS_MAX_FRAME>>,
130    #[cfg(not(feature = "heap"))]
131    buf_stereo_side: FixedVec<i16, OPUS_MAX_FRAME>,
132    #[cfg(feature = "heap")]
133    buf_stereo_side: Box<FixedVec<i16, OPUS_MAX_FRAME>>,
134    #[cfg(not(feature = "heap"))]
135    buf_celt_input: FixedVec<f32, OPUS_MAX_FRAME>,
136    #[cfg(feature = "heap")]
137    buf_celt_input: Box<FixedVec<f32, OPUS_MAX_FRAME>>,
138    /// C `delay_buffer[MAX_ENCODER_BUFFER*2]`: delay-compensation ring feeding
139    /// the CELT encoder (see `encoder_buffer` / `delay_compensation`).
140    #[cfg(not(feature = "heap"))]
141    delay_buffer: FixedVec<f32, OPUS_DELAY_BUF>,
142    #[cfg(feature = "heap")]
143    delay_buffer: Box<FixedVec<f32, OPUS_DELAY_BUF>>,
144    /// Interleaved `pcm_buf` staging: `delay prefix + filtered frame` exactly
145    /// like C `opus_encode_frame_native()` builds it before CELT encoding.
146    #[cfg(not(feature = "heap"))]
147    buf_celt_pcm: FixedVec<f32, OPUS_PCM_BUF>,
148    #[cfg(feature = "heap")]
149    buf_celt_pcm: Box<FixedVec<f32, OPUS_PCM_BUF>>,
150    /// C `encoder_buffer` (= Fs/100): samples of ring history kept between
151    /// frames (0 for restricted-latency applications).
152    encoder_buffer: usize,
153    /// C `delay_compensation` (= Fs/250): 4 ms lookahead prefix prepended to
154    /// each CELT frame (0 for restricted-latency applications).
155    delay_compensation: usize,
156    /// Float filter state for `dc_reject_float` / `hp_cutoff_float`
157    /// (C `st->hp_mem[4]`, opus_val32 in the float build).
158    hp_mem_float: [f32; 4],
159    down2_state_first: [i32; 2],
160    down2_state_second: [i32; 2],
161    down2_3_state: [i32; 6],
162    down_1_3_state: silk::resampler::SilkResamplerDown1_3,
163
164    rc: RangeCoder,
165}
166
167fn compute_equiv_rate(
168    bitrate: i32,
169    channels: usize,
170    frame_rate: i32,
171    vbr: bool,
172    complexity: i32,
173    loss: i32,
174) -> i32 {
175    let mut equiv = bitrate;
176    if frame_rate > 50 {
177        equiv -= (40 * channels as i32 + 20) * (frame_rate - 50);
178    }
179    if !vbr {
180        equiv -= equiv / 12;
181    }
182    equiv = equiv * (90 + complexity) / 100;
183    if loss > 0 {
184        equiv -= equiv * loss / (12 * loss + 20);
185    }
186    equiv
187}
188
189fn compute_mode_threshold(
190    application: Application,
191    channels: usize,
192    prev_was_celt: bool,
193    has_prev_mode: bool,
194    voice_est: i32,
195) -> i32 {
196    let mode_voice = if channels == 1 { 64000 } else { 44000 };
197    let mode_music = 10000;
198
199    let diff = mode_voice - mode_music;
200    let offset = (voice_est * voice_est * diff) >> 14;
201    let mut threshold = mode_music + offset;
202
203    if application == Application::Voip {
204        threshold += 8000;
205    }
206
207    if has_prev_mode {
208        if prev_was_celt {
209            threshold -= 4000;
210        } else {
211            threshold += 4000;
212        }
213    }
214
215    if application == Application::RestrictedLowDelay {
216        threshold = 0;
217    }
218
219    threshold
220}
221
222fn compute_silk_rate_for_hybrid(rate_bps: i32, frame20ms: bool) -> i32 {
223    const RATE_TABLE: &[(i32, i32, i32)] = &[
224        (0, 0, 0),
225        (12000, 10000, 10000),
226        (16000, 13500, 13500),
227        (20000, 16000, 16000),
228        (24000, 18000, 18000),
229        (32000, 22000, 22000),
230        (64000, 38000, 38000),
231    ];
232    let n = RATE_TABLE.len();
233    let mut i = 1;
234    while i < n && RATE_TABLE[i].0 <= rate_bps {
235        i += 1;
236    }
237    if i == n {
238        let (x_last, r10_last, r20_last) = RATE_TABLE[n - 1];
239        let base = if frame20ms { r20_last } else { r10_last };
240        base + (rate_bps - x_last) / 2
241    } else {
242        let (x0, lo10, lo20) = RATE_TABLE[i - 1];
243        let (x1, hi10, hi20) = RATE_TABLE[i];
244        let (lo, hi) = if frame20ms {
245            (lo20, hi20)
246        } else {
247            (lo10, hi10)
248        };
249        (lo * (x1 - rate_bps) + hi * (rate_bps - x0)) / (x1 - x0)
250    }
251}
252
253#[cfg(all(test, feature = "std"))]
254mod silk_rate_tests {
255    use super::compute_silk_rate_for_hybrid;
256
257    #[test]
258    fn test_reference_table_exact_entries() {
259        assert_eq!(compute_silk_rate_for_hybrid(12000, true), 10000);
260        assert_eq!(compute_silk_rate_for_hybrid(16000, true), 13500);
261        assert_eq!(compute_silk_rate_for_hybrid(20000, true), 16000);
262        assert_eq!(compute_silk_rate_for_hybrid(24000, true), 18000);
263        assert_eq!(compute_silk_rate_for_hybrid(32000, true), 22000);
264        assert_eq!(compute_silk_rate_for_hybrid(64000, true), 38000);
265    }
266
267    #[test]
268    fn test_32kbps_gives_22kbps_silk() {
269        assert_eq!(compute_silk_rate_for_hybrid(32000, true), 22000);
270    }
271
272    #[test]
273    fn test_interpolation_between_table_entries() {
274        let r = compute_silk_rate_for_hybrid(18000, true);
275        assert_eq!(r, 14750);
276    }
277
278    #[test]
279    fn test_above_table_max_gives_half_extra() {
280        let r = compute_silk_rate_for_hybrid(72000, true);
281        assert_eq!(r, 38000 + (72000 - 64000) / 2);
282    }
283}
284
285/// Uniformly borrow a codec-state field that is a `Box<T>` under the `heap`
286/// feature and a plain `T` otherwise, so call sites don't need `#[cfg]` on
287/// every `&self.field` / `&mut self.field`.
288///
289/// Under `heap` the field is a `Box<...>`, so `T: Deref(DerefMut)` resolves to
290/// the box itself and the `Target` is the inner type; without `heap` the field
291/// is the inner type directly (`?Sized` lets `&mut [i16]`-style coercion choose
292/// the slice target).
293#[cfg(feature = "heap")]
294#[inline(always)]
295fn state_ref<T: core::ops::Deref>(b: &T) -> &T::Target {
296    b
297}
298#[cfg(not(feature = "heap"))]
299#[inline(always)]
300fn state_ref<T: ?Sized>(b: &T) -> &T {
301    b
302}
303
304#[cfg(feature = "heap")]
305#[inline(always)]
306fn state_mut<T: core::ops::DerefMut>(b: &mut T) -> &mut T::Target {
307    b
308}
309#[cfg(not(feature = "heap"))]
310#[inline(always)]
311fn state_mut<T: ?Sized>(b: &mut T) -> &mut T {
312    b
313}
314
315impl OpusEncoder {
316    pub fn new(
317        sampling_rate: i32,
318        channels: usize,
319        application: Application,
320    ) -> Result<Self, &'static str> {
321        if ![8000, 12000, 16000, 24000, 48000].contains(&sampling_rate) {
322            return Err("Invalid sampling rate");
323        }
324        if ![1, 2].contains(&channels) {
325            return Err("Invalid number of channels");
326        }
327
328        let mode = modes::default_mode();
329        #[cfg(feature = "heap")]
330        let celt_enc = Box::new(CeltEncoder::new(mode, channels));
331        #[cfg(not(feature = "heap"))]
332        let celt_enc = CeltEncoder::new(mode, channels);
333
334        #[cfg(feature = "heap")]
335        let mut silk_enc = Box::new(SilkEncoderState::default());
336        #[cfg(not(feature = "heap"))]
337        let mut silk_enc = SilkEncoderState::default();
338        if silk_init_encoder(state_mut(&mut silk_enc), 0) != 0 {
339            return Err("SILK encoder initialization failed");
340        }
341
342        let (opus_mode, bw) = match application {
343            Application::Voip => {
344                let bw = match sampling_rate {
345                    8000 => Bandwidth::Narrowband,
346                    12000 => Bandwidth::Mediumband,
347                    16000 => Bandwidth::Wideband,
348                    24000 => Bandwidth::Superwideband,
349                    48000 => Bandwidth::Fullband,
350                    _ => Bandwidth::Narrowband,
351                };
352
353                let mode = if sampling_rate > 16000 {
354                    OpusMode::Hybrid
355                } else {
356                    OpusMode::SilkOnly
357                };
358                (mode, bw)
359            }
360            Application::RestrictedLowDelay => {
361                let bw = match sampling_rate {
362                    8000 => Bandwidth::Narrowband,
363                    12000 => Bandwidth::Mediumband,
364                    16000 => Bandwidth::Wideband,
365                    24000 => Bandwidth::Superwideband,
366                    _ => Bandwidth::Fullband,
367                };
368                (OpusMode::CeltOnly, bw)
369            }
370            Application::Audio => {
371                if sampling_rate <= 16000 {
372                    let bw = match sampling_rate {
373                        8000 => Bandwidth::Narrowband,
374                        12000 => Bandwidth::Mediumband,
375                        _ => Bandwidth::Wideband,
376                    };
377                    (OpusMode::SilkOnly, bw)
378                } else {
379                    let bw = match sampling_rate {
380                        24000 => Bandwidth::Superwideband,
381                        _ => Bandwidth::Fullband,
382                    };
383                    (OpusMode::Hybrid, bw)
384                }
385            }
386        };
387
388        use silk::lin2log::silk_lin2log;
389        let variable_hp_smth2_q15 = silk_lin2log(60) << 8;
390
391        Ok(Self {
392            celt_enc,
393            silk_enc,
394            application,
395            sampling_rate,
396            channels,
397            bandwidth: bw,
398            bitrate_bps: 64000,
399            complexity: 9,
400            use_cbr: false,
401            use_inband_fec: false,
402            packet_loss_perc: 0,
403            silk_initialized: false,
404            prev_enc_mode: None,
405            mode: opus_mode,
406            variable_hp_smth2_q15,
407            hp_mem: FixedVec::from_value(0, channels * 2),
408
409            #[cfg(not(feature = "heap"))]
410            buf_filtered: FixedVec::new(),
411            #[cfg(feature = "heap")]
412            buf_filtered: Box::new(FixedVec::new()),
413            #[cfg(not(feature = "heap"))]
414            buf_silk_input: FixedVec::new(),
415            #[cfg(feature = "heap")]
416            buf_silk_input: Box::new(FixedVec::new()),
417            #[cfg(not(feature = "heap"))]
418            buf_stereo_mid: FixedVec::new(),
419            #[cfg(feature = "heap")]
420            buf_stereo_mid: Box::new(FixedVec::new()),
421            #[cfg(not(feature = "heap"))]
422            buf_stereo_side: FixedVec::new(),
423            #[cfg(feature = "heap")]
424            buf_stereo_side: Box::new(FixedVec::new()),
425            #[cfg(not(feature = "heap"))]
426            buf_celt_input: FixedVec::new(),
427            #[cfg(feature = "heap")]
428            buf_celt_input: Box::new(FixedVec::new()),
429            #[cfg(not(feature = "heap"))]
430            delay_buffer: FixedVec::from_value(0.0, OPUS_DELAY_BUF),
431            #[cfg(feature = "heap")]
432            delay_buffer: Box::new(FixedVec::from_value(0.0, OPUS_DELAY_BUF)),
433            #[cfg(not(feature = "heap"))]
434            buf_celt_pcm: FixedVec::new(),
435            #[cfg(feature = "heap")]
436            buf_celt_pcm: Box::new(FixedVec::new()),
437            encoder_buffer: if matches!(application, Application::RestrictedLowDelay) {
438                0
439            } else {
440                (sampling_rate / 100) as usize
441            },
442            delay_compensation: if matches!(application, Application::RestrictedLowDelay) {
443                0
444            } else {
445                (sampling_rate / 250) as usize
446            },
447            hp_mem_float: [0.0; 4],
448            down2_state_first: [0; 2],
449            down2_state_second: [0; 2],
450            down2_3_state: [0; 6],
451            down_1_3_state: silk::resampler::SilkResamplerDown1_3::default(),
452            rc: RangeCoder::new_encoder(1),
453        })
454    }
455
456    pub fn enable_hybrid_mode(&mut self) -> Result<(), &'static str> {
457        if self.sampling_rate != 24000 && self.sampling_rate != 48000 {
458            return Err("Hybrid mode requires 24kHz or 48kHz sampling rate");
459        }
460        let bw = if self.sampling_rate == 48000 {
461            Bandwidth::Fullband
462        } else {
463            Bandwidth::Superwideband
464        };
465        self.mode = OpusMode::Hybrid;
466        self.bandwidth = bw;
467        self.silk_initialized = false;
468        Ok(())
469    }
470
471    pub fn encode(
472        &mut self,
473        input: &[f32],
474        frame_size: usize,
475        output: &mut [u8],
476    ) -> Result<usize, &'static str> {
477        if output.len() < 2 {
478            return Err("Output buffer too small");
479        }
480
481        let frame_rate = frame_rate_from_params(self.sampling_rate, frame_size)
482            .ok_or("Invalid frame size for sampling rate")?;
483
484        // Clamp caller-provided control fields the way libopus's ctl
485        // interface does (OPUS_SET_BITRATE clamps to [500, 3000000];
486        // complexity to [0, 10]). Unclamped extremes overflow the bitrate
487        // arithmetic downstream (bitrate*6 in celt, `equiv*(90+complexity)`
488        // in the mode selection) — issue #27 deep scan.
489        self.bitrate_bps = self.bitrate_bps.clamp(500, 3_000_000);
490        self.complexity = self.complexity.clamp(0, 10);
491        self.packet_loss_perc = self.packet_loss_perc.clamp(0, 100);
492
493        // The encoder consumes exactly frame_size * channels samples per call;
494        // reject short input up front instead of panicking inside the HP filter
495        // or the float→int conversion loops (issue #27 deep-scan).
496        if input.len() < frame_size * self.channels {
497            return Err("Input buffer too small for frame");
498        }
499
500        // Mode selection: match C's opus_encode_native() behavior.
501        // C reference auto-selects between SILK_ONLY and CELT_ONLY; Hybrid is
502        // produced afterwards by bandwidth overrides (SILK-only + FB/SWB → Hybrid).
503        let mut mode = if self.application == Application::RestrictedLowDelay {
504            OpusMode::CeltOnly
505        } else {
506            let equiv = compute_equiv_rate(
507                self.bitrate_bps,
508                self.channels,
509                frame_rate,
510                !self.use_cbr,
511                self.complexity,
512                self.packet_loss_perc,
513            );
514            let prev_was_celt = self.prev_enc_mode == Some(OpusMode::CeltOnly);
515            let has_prev_mode = self.prev_enc_mode.is_some();
516            let voice_est = match self.application {
517                Application::Voip => 115,
518                Application::Audio => 48,
519                Application::RestrictedLowDelay => 0,
520            };
521            let threshold = compute_mode_threshold(
522                self.application,
523                self.channels,
524                prev_was_celt,
525                has_prev_mode,
526                voice_est,
527            );
528            if equiv >= threshold && self.sampling_rate >= 24000 {
529                OpusMode::CeltOnly
530            } else {
531                OpusMode::SilkOnly
532            }
533        };
534
535        let curr_bw = self.bandwidth;
536        if mode == OpusMode::SilkOnly
537            && (curr_bw == Bandwidth::Superwideband || curr_bw == Bandwidth::Fullband)
538        {
539            mode = OpusMode::Hybrid;
540        }
541        if mode == OpusMode::Hybrid
542            && (curr_bw == Bandwidth::Narrowband
543                || curr_bw == Bandwidth::Mediumband
544                || curr_bw == Bandwidth::Wideband)
545        {
546            mode = OpusMode::SilkOnly;
547        }
548
549        if mode == OpusMode::CeltOnly {
550            match frame_rate {
551                400 | 200 | 100 | 50 => {}
552                _ => return Err("Unsupported frame size for CELT-only mode"),
553            }
554        }
555
556        if mode == OpusMode::Hybrid {
557            match frame_rate {
558                100 | 50 => {}
559                _ => return Err("Unsupported frame size for Hybrid mode"),
560            }
561        }
562
563        if mode == OpusMode::SilkOnly {
564            match frame_rate {
565                400 | 200 | 100 | 50 | 25 => {}
566                _ => return Err("Unsupported frame size for SILK-only mode"),
567            }
568        }
569
570        let toc = gen_toc(mode, frame_rate, self.bandwidth, self.channels);
571        output[0] = toc;
572
573        let target_bits =
574            (self.bitrate_bps as i64 * frame_size as i64 / self.sampling_rate as i64) as i32;
575        let cbr_bytes = ((target_bits + 4) / 8) as usize;
576        let max_data_bytes = output.len();
577
578        // C parity (opus_encoder.c): the nominal-size cap applies in CBR mode
579        // only. In VBR mode the CELT-internal bound (vbr_rate/reservoir) decides
580        // the per-frame size, allowing overshoot (borrowing) and undershoot.
581        // Capping VBR at nominal defeats the reservoir and starves complex frames.
582        let mut n_bytes = if self.use_cbr {
583            cbr_bytes
584                .min(max_data_bytes)
585                .max(1)
586                .min(OPUS_MAX_PACKET_BYTES)
587        } else {
588            max_data_bytes.max(1).min(OPUS_MAX_PACKET_BYTES)
589        };
590        let init_rc_size = n_bytes - 1;
591        self.rc.reset_for_encode(init_rc_size as u32);
592
593        // C opus_encode_frame_native: high-pass cutoff state is updated for ALL
594        // modes (celt_encoder.c:1969-1977); the filtered signal is what feeds
595        // both SILK (hybrid) and CELT. Compute it here so CELT-only frames also
596        // keep `variable_hp_smth2_q15` in sync with libopus.
597        let hp_freq_smth1 = if mode == OpusMode::CeltOnly {
598            silk_lin2log(60) << 8
599        } else {
600            self.silk_enc.s_cmn.variable_hp_smth1_q15
601        };
602
603        const VARIABLE_HP_SMTH_COEF2_Q16: i32 = 984;
604        self.variable_hp_smth2_q15 = silk_smlawb(
605            self.variable_hp_smth2_q15,
606            hp_freq_smth1 - self.variable_hp_smth2_q15,
607            VARIABLE_HP_SMTH_COEF2_Q16,
608        );
609
610        let cutoff_hz = silk_log2lin(silk_rshift(self.variable_hp_smth2_q15, 8));
611
612        if mode == OpusMode::SilkOnly || mode == OpusMode::Hybrid {
613            let silk_fs_khz = if mode == OpusMode::Hybrid {
614                16
615            } else {
616                self.sampling_rate.min(16000) / 1000
617            };
618
619            let frame_ms = (frame_size as i32 * 1000) / self.sampling_rate;
620            if !self.silk_initialized || self.silk_enc.s_cmn.fs_khz != silk_fs_khz {
621                let silk_init_bitrate = (((n_bytes - 1) * 8) as i64 * self.sampling_rate as i64
622                    / frame_size as i64) as i32;
623                silk_control_encoder(
624                    state_mut(&mut self.silk_enc),
625                    silk_fs_khz,
626                    frame_ms,
627                    silk_init_bitrate,
628                    self.complexity,
629                );
630                self.silk_enc.s_cmn.use_cbr = if self.use_cbr { 1 } else { 0 };
631
632                self.silk_enc.s_cmn.n_channels = self.channels as i32;
633                self.silk_initialized = true;
634                self.down2_state_first = [0; 2];
635                self.down2_state_second = [0; 2];
636                self.down2_3_state = [0; 6];
637                self.down_1_3_state = silk::resampler::SilkResamplerDown1_3::default();
638            }
639
640            self.silk_enc.s_cmn.use_in_band_fec = if self.use_inband_fec { 1 } else { 0 };
641            self.silk_enc.s_cmn.packet_loss_perc = self.packet_loss_perc.clamp(0, 100);
642
643            self.silk_enc.s_cmn.lbrr_enabled = if self.use_inband_fec { 1 } else { 0 };
644
645            if self.silk_enc.s_cmn.lbrr_gain_increases == 0 {
646                self.silk_enc.s_cmn.lbrr_gain_increases = 2;
647            }
648
649            let required_size = frame_size * self.channels;
650            self.buf_filtered.resize(required_size, 0);
651            if self.application == Application::Voip {
652                hp_cutoff(
653                    input,
654                    cutoff_hz,
655                    state_mut(&mut self.buf_filtered),
656                    &mut self.hp_mem,
657                    frame_size,
658                    self.channels,
659                    self.sampling_rate,
660                );
661            } else {
662                let required_size = frame_size * self.channels;
663                for (i, &x) in input.iter().enumerate().take(required_size) {
664                    self.buf_filtered[i] = (x * 32768.0).clamp(-32768.0, 32767.0) as i16;
665                }
666            }
667
668            let input_i16 = state_ref(&self.buf_filtered);
669
670            let silk_input: &[i16] = if mode == OpusMode::SilkOnly && self.sampling_rate > 16000 {
671                if self.sampling_rate == 48000 {
672                    let stage1_size = frame_size / 2;
673                    // 48 kHz SILK-only supports up to 40 ms frames (frame_rate 25):
674                    // 1920 API samples -> 960 stage-1 samples at 24 kHz. Guard the
675                    // stack buffer instead of panicking on out-of-range lengths.
676                    if stage1_size > 960 {
677                        return Err("Invalid frame size for SILK mode");
678                    }
679                    let mut stage1_buf = [0i16; 960];
680                    silk_resampler_down2(
681                        &mut self.down2_state_first,
682                        &mut stage1_buf[..stage1_size],
683                        input_i16,
684                        frame_size as i32,
685                    );
686                    let silk_frame_size = stage1_size * 2 / 3;
687                    self.buf_silk_input.resize(silk_frame_size, 0);
688                    silk_resampler_down2_3(
689                        &mut self.down2_3_state,
690                        state_mut(&mut self.buf_silk_input),
691                        &stage1_buf[..stage1_size],
692                        stage1_size as i32,
693                    );
694                    state_ref(&self.buf_silk_input)
695                } else if self.sampling_rate == 24000 {
696                    let silk_frame_size = frame_size * 2 / 3;
697                    self.buf_silk_input.resize(silk_frame_size, 0);
698                    silk_resampler_down2_3(
699                        &mut self.down2_3_state,
700                        state_mut(&mut self.buf_silk_input),
701                        input_i16,
702                        frame_size as i32,
703                    );
704                    state_ref(&self.buf_silk_input)
705                } else {
706                    input_i16
707                }
708            } else if mode == OpusMode::SilkOnly && self.channels == 2 {
709                let frame_length = input_i16.len() / 2;
710                self.buf_stereo_mid.resize(frame_length, 0);
711                self.buf_stereo_side.resize(frame_length, 0);
712                for i in 0..frame_length {
713                    let l = input_i16[2 * i] as i32;
714                    let r = input_i16[2 * i + 1] as i32;
715                    self.buf_stereo_mid[i] = ((l + r) / 2) as i16;
716                    self.buf_stereo_side[i] = (l - r) as i16;
717                }
718
719                self.silk_enc.stereo.side.resize(frame_length, 0);
720                self.silk_enc
721                    .stereo
722                    .side
723                    .copy_from_slice(&self.buf_stereo_side[..frame_length]);
724                state_ref(&self.buf_stereo_mid)
725            } else if mode == OpusMode::Hybrid && self.sampling_rate > 16000 {
726                if self.sampling_rate == 48000 {
727                    let silk_frame_size = frame_size / 3;
728                    self.buf_silk_input.resize(silk_frame_size, 0);
729                    silk::resampler::silk_resampler_down_1_3(
730                        &mut self.down_1_3_state,
731                        state_mut(&mut self.buf_silk_input),
732                        input_i16,
733                    );
734                } else {
735                    let silk_frame_size = frame_size * 2 / 3;
736                    self.buf_silk_input.resize(silk_frame_size, 0);
737                    silk_resampler_down2_3(
738                        &mut self.down2_3_state,
739                        state_mut(&mut self.buf_silk_input),
740                        input_i16,
741                        frame_size as i32,
742                    );
743                }
744                state_ref(&self.buf_silk_input)
745            } else {
746                input_i16
747            };
748
749            let mut pn_bytes = 0;
750
751            let silk_rate_for_calc = if mode == OpusMode::Hybrid {
752                16000
753            } else {
754                self.sampling_rate
755            };
756            let silk_frame_len = silk_input.len();
757
758            let silk_bitrate = if mode == OpusMode::Hybrid {
759                let frame_duration_ms = frame_size as i32 * 1000 / self.sampling_rate;
760                let frame20ms = frame_duration_ms >= 20;
761                compute_silk_rate_for_hybrid(self.bitrate_bps, frame20ms)
762            } else {
763                (8i64 * (n_bytes - 1) as i64 * silk_rate_for_calc as i64 / silk_frame_len as i64)
764                    as i32
765            };
766            let silk_max_bits = if mode == OpusMode::Hybrid {
767                let total_max_bits = ((n_bytes - 1) * 8) as i32;
768                if self.use_cbr {
769                    let silk_bits = (silk_bitrate as i64 * silk_frame_len as i64
770                        / silk_rate_for_calc as i64) as i32;
771                    let other_bits = 0i32.max(total_max_bits - silk_bits);
772                    0i32.max(total_max_bits - other_bits * 3 / 4)
773                } else {
774                    let frame_duration_ms = frame_size as i32 * 1000 / self.sampling_rate;
775                    let frame20ms = frame_duration_ms >= 20;
776                    let max_bit_rate = compute_silk_rate_for_hybrid(
777                        total_max_bits * self.sampling_rate / frame_size as i32,
778                        frame20ms,
779                    );
780                    max_bit_rate * frame_size as i32 / self.sampling_rate
781                }
782            } else {
783                ((n_bytes - 1) * 8) as i32
784            };
785            let silk_use_cbr = if mode == OpusMode::Hybrid && self.use_cbr {
786                0
787            } else if self.use_cbr {
788                1
789            } else {
790                0
791            };
792            let ret = silk_encode(
793                state_mut(&mut self.silk_enc),
794                silk_input,
795                silk_input.len(),
796                &mut self.rc,
797                &mut pn_bytes,
798                silk_bitrate,
799                silk_max_bits,
800                silk_use_cbr,
801                1,
802            );
803            if ret != 0 {
804                return Err("SILK encoding failed");
805            }
806        }
807
808        if mode == OpusMode::Hybrid {
809            self.rc.encode_bit_logp(false, 12); // redundancy = 0
810        }
811
812        if mode == OpusMode::Hybrid {
813            let nb_compr_bytes = (n_bytes - 1) as u32;
814            self.rc.shrink(nb_compr_bytes);
815        }
816
817        let silk_ret_bytes = if mode == OpusMode::SilkOnly {
818            ((self.rc.tell() + 7) >> 3) as usize
819        } else {
820            0
821        };
822
823        // libopus parity: adjust nbCompressedBytes for CELT/Hybrid based on tell (celt_encoder.c:1913-1921)
824        // tmp = bitrate*frame_size + tell*Fs; nbCompressed = (tmp+4*Fs)/(8*Fs)
825        // This is the CBR branch (vbr==0) in celt_encoder.c; for VBR the encoder
826        // does *not* do this adjustment - it uses the vbr_bound logic instead.
827        // Doing it for VBR would double-shrink and corrupt the budget.
828        if mode != OpusMode::SilkOnly && self.use_cbr {
829            let tell = self.rc.tell();
830            if tell > 1 {
831                let tmp = self.bitrate_bps as i64 * frame_size as i64
832                    + tell as i64 * self.sampling_rate as i64;
833                let adjusted = ((tmp + 4 * self.sampling_rate as i64)
834                    / (8 * self.sampling_rate as i64)) as usize;
835                let new_n = adjusted
836                    .min(max_data_bytes)
837                    .max(1)
838                    .min(OPUS_MAX_PACKET_BYTES);
839                if new_n < n_bytes {
840                    n_bytes = new_n;
841                    // shrink range coder to new size (keep SILK bytes, trim tail)
842                    let new_payload = n_bytes - 1;
843                    self.rc.shrink(new_payload as u32);
844                }
845            }
846        }
847        if mode == OpusMode::CeltOnly || mode == OpusMode::Hybrid {
848            self.celt_enc.complexity = self.complexity;
849            let start_band = if mode == OpusMode::Hybrid { 17 } else { 0 };
850            let total_packet_bits = ((n_bytes - 1) * 8) as i32;
851            // Propagate bitrate/VBR to CeltEncoder for accurate VBR handling (libopus parity)
852            let celt_bitrate = if mode == OpusMode::Hybrid {
853                let frame_ms = frame_size as i32 * 1000 / self.sampling_rate;
854                let frame20ms = frame_ms >= 20;
855                let silk_rate = compute_silk_rate_for_hybrid(self.bitrate_bps, frame20ms);
856                (self.bitrate_bps - silk_rate).max(8000)
857            } else {
858                self.bitrate_bps
859            };
860            self.celt_enc.set_bitrate(celt_bitrate);
861            self.celt_enc.set_vbr(!self.use_cbr);
862            // libopus: Hybrid VBR is unconstrained (can steal from SILK), CELT-only constrained
863            self.celt_enc
864                .set_constrained_vbr(mode == OpusMode::CeltOnly);
865
866            // Build the CELT input exactly like C `opus_encode_frame_native`:
867            //   pcm_buf = [delay-compensation prefix from ring]
868            //             [dc_reject / hp_cutoff'd current frame]
869            // CELT then reads pcm_buf[0..frame_size*channels] (opus_encoder.c:
870            // 1966-2010, 2493). This delay + filter step is what libopus feeds
871            // its CELT encoder; passing the raw input diverges from 1.6.
872            let celt_input: &[f32] = if self.delay_compensation > 0 {
873                let delay = self.delay_compensation;
874                let ebuf = self.encoder_buffer;
875                let ch = self.channels;
876                let total = (delay + frame_size) * ch;
877                self.buf_celt_pcm.resize(total, 0.0);
878                // 1. delay prefix from the ring (C: OPUS_COPY at 1967)
879                let prefix = delay * ch;
880                let src_start = (ebuf - delay) * ch;
881                self.buf_celt_pcm[..prefix]
882                    .copy_from_slice(&self.delay_buffer[src_start..src_start + prefix]);
883                // 2. filter current frame into pcm_buf[delay..] (C: 2002-2010)
884                let out = &mut self.buf_celt_pcm[prefix..];
885                if self.application == Application::Voip {
886                    hp_cutoff_float(
887                        input,
888                        cutoff_hz,
889                        out,
890                        &mut self.hp_mem_float,
891                        frame_size,
892                        ch,
893                        self.sampling_rate,
894                    );
895                } else {
896                    dc_reject_float(
897                        input,
898                        3,
899                        out,
900                        &mut self.hp_mem_float,
901                        frame_size,
902                        ch,
903                        self.sampling_rate,
904                    );
905                }
906                // 3. float NaN guard (C: 2016-2028)
907                let mut sum = 0.0f32;
908                for &v in &self.buf_celt_pcm[prefix..] {
909                    sum += v * v;
910                }
911                if !(sum < 1e9) || sum.is_nan() {
912                    self.buf_celt_pcm[prefix..].fill(0.0);
913                    self.hp_mem_float = [0.0; 4];
914                }
915                // 4. delay ring update (C: 2300-2312)
916                let keep = ebuf as i64 - (frame_size as i64 + delay as i64);
917                if keep > 0 {
918                    let keep = keep as usize;
919                    self.delay_buffer
920                        .copy_within(ch * frame_size..ch * (frame_size + keep), 0);
921                    let dst = ch * keep;
922                    let n = (frame_size + delay) * ch;
923                    self.delay_buffer[dst..dst + n].copy_from_slice(&self.buf_celt_pcm[..n]);
924                } else {
925                    let n = ebuf * ch;
926                    let src = (frame_size + delay - ebuf) * ch;
927                    self.delay_buffer[..n].copy_from_slice(&self.buf_celt_pcm[src..src + n]);
928                }
929                // 5. deinterleave pcm_buf[0..frame_size*ch] (interleaved) to
930                //    channel-major for the Rust CELT encoder.
931                let n = frame_size * ch;
932                self.buf_celt_input.resize(n, 0.0);
933                for i in 0..frame_size {
934                    for c in 0..ch {
935                        self.buf_celt_input[c * frame_size + i] = self.buf_celt_pcm[i * ch + c];
936                    }
937                }
938                state_ref(&self.buf_celt_input)
939            } else if self.channels == 1 {
940                input
941            } else {
942                let n = frame_size * self.channels;
943                self.buf_celt_input.resize(n, 0.0);
944                for i in 0..frame_size {
945                    for ch in 0..self.channels {
946                        self.buf_celt_input[ch * frame_size + i] = input[i * self.channels + ch];
947                    }
948                }
949                state_ref(&self.buf_celt_input)
950            };
951
952            if self.rc.tell() <= total_packet_bits {
953                let is_vbr = !self.use_cbr;
954                self.celt_enc.encode_with_budget_vbr(
955                    celt_input,
956                    frame_size,
957                    &mut self.rc,
958                    start_band,
959                    total_packet_bits,
960                    is_vbr,
961                );
962            }
963        }
964
965        self.rc.done();
966
967        if self.rc.error != 0 {
968            return Err("Range coder buffer overflow: encoded data exceeds packet budget");
969        }
970
971        if mode == OpusMode::SilkOnly {
972            let mut ret = silk_ret_bytes.min(self.rc.storage as usize);
973            while ret > 2 && self.rc.buf[ret - 1] == 0 {
974                ret -= 1;
975            }
976
977            let target_total = if self.use_cbr {
978                n_bytes.min(output.len())
979            } else {
980                (ret + 1).min(output.len())
981            };
982
983            let silk_len = ret;
984
985            if !self.use_cbr || silk_len + 1 >= target_total {
986                // VBR or payload fills the target: simple code 0 packet
987                output[0] = toc;
988                let copy_len = silk_len.min(target_total - 1);
989                output[1..1 + copy_len].copy_from_slice(&self.rc.buf[..copy_len]);
990                return Ok((copy_len + 1).min(output.len()));
991            }
992
993            output[0] = toc | 0x03;
994
995            if silk_len + 2 >= target_total {
996                output[1] = 0x01;
997                let copy_len = (target_total - 2).min(silk_len);
998                output[2..2 + copy_len].copy_from_slice(&self.rc.buf[..copy_len]);
999                self.prev_enc_mode = Some(mode);
1000                return Ok(target_total.min(output.len()));
1001            }
1002
1003            let pad_amount = target_total - silk_len - 2;
1004            output[1] = 0x41;
1005
1006            let nb_255s = (pad_amount - 1) / 255;
1007            let mut ptr = 2;
1008            for _ in 0..nb_255s {
1009                output[ptr] = 255;
1010                ptr += 1;
1011            }
1012            output[ptr] = (pad_amount - 255 * nb_255s - 1) as u8;
1013            ptr += 1;
1014
1015            output[ptr..ptr + silk_len].copy_from_slice(&self.rc.buf[..silk_len]);
1016            ptr += silk_len;
1017
1018            let fill_end = target_total.min(output.len());
1019            for byte in output[ptr..fill_end].iter_mut() {
1020                *byte = 0;
1021            }
1022
1023            self.prev_enc_mode = Some(mode);
1024            return Ok(target_total.min(output.len()));
1025        }
1026
1027        // For CELT/Hybrid, respect possible VBR shrink performed by CeltEncoder (e.g. silence)
1028        let payload_len = (self.rc.storage as usize).min(output.len() - 1);
1029        output[1..1 + payload_len].copy_from_slice(&self.rc.buf[..payload_len]);
1030        self.prev_enc_mode = Some(mode);
1031        Ok(payload_len + 1)
1032    }
1033}
1034
1035pub struct OpusDecoder {
1036    #[cfg(not(feature = "heap"))]
1037    celt_dec: CeltDecoder,
1038    #[cfg(feature = "heap")]
1039    celt_dec: Box<CeltDecoder>,
1040    #[cfg(not(feature = "heap"))]
1041    silk_dec: silk::dec_api::SilkDecoder,
1042    #[cfg(feature = "heap")]
1043    silk_dec: Box<silk::dec_api::SilkDecoder>,
1044    sampling_rate: i32,
1045    channels: usize,
1046
1047    prev_mode: Option<OpusMode>,
1048
1049    /// Whether the previous frame had redundancy (mode transition marker).
1050    prev_redundancy: bool,
1051    frame_size: usize,
1052
1053    bandwidth: Bandwidth,
1054
1055    stream_channels: usize,
1056
1057    silk_resampler: silk::resampler::SilkResampler,
1058
1059    /// Second resampler instance for stereo channel 1.
1060    silk_resampler_2: silk::resampler::SilkResampler,
1061
1062    prev_internal_rate: i32,
1063
1064    pub hybrid_skip_celt: bool,
1065
1066    #[cfg(not(feature = "heap"))]
1067    w_pcm_i16: FixedVec<i16, OPUS_PCM_I16>,
1068    #[cfg(feature = "heap")]
1069    w_pcm_i16: Box<FixedVec<i16, OPUS_PCM_I16>>,
1070    #[cfg(not(feature = "heap"))]
1071    w_silk_out: FixedVec<f32, OPUS_SUBFRAME_SCRATCH>,
1072    #[cfg(feature = "heap")]
1073    w_silk_out: Box<FixedVec<f32, OPUS_SUBFRAME_SCRATCH>>,
1074    #[cfg(not(feature = "heap"))]
1075    w_pcm_resampled: FixedVec<i16, OPUS_SUBFRAME_SCRATCH>,
1076    #[cfg(feature = "heap")]
1077    w_pcm_resampled: Box<FixedVec<i16, OPUS_SUBFRAME_SCRATCH>>,
1078    #[cfg(not(feature = "heap"))]
1079    w_celt_planar: FixedVec<f32, OPUS_SUBFRAME_SCRATCH>,
1080    #[cfg(feature = "heap")]
1081    w_celt_planar: Box<FixedVec<f32, OPUS_SUBFRAME_SCRATCH>>,
1082    #[cfg(not(feature = "heap"))]
1083    w_celt_out: FixedVec<f32, OPUS_SUBFRAME_SCRATCH>,
1084    #[cfg(feature = "heap")]
1085    w_celt_out: Box<FixedVec<f32, OPUS_SUBFRAME_SCRATCH>>,
1086
1087    /// Tail of the previous frame's output, used for smooth_fade at mode
1088    /// transitions (libopus pcm_transition + smooth_fade).
1089    #[cfg(not(feature = "heap"))]
1090    prev_pcm_tail: FixedVec<f32, OPUS_PCM_TAIL>,
1091    #[cfg(feature = "heap")]
1092    prev_pcm_tail: Box<FixedVec<f32, OPUS_PCM_TAIL>>,
1093}
1094
1095impl OpusDecoder {
1096    pub fn new(sampling_rate: i32, channels: usize) -> Result<Self, &'static str> {
1097        if ![8000, 12000, 16000, 24000, 48000].contains(&sampling_rate) {
1098            return Err("Invalid sampling rate");
1099        }
1100        if ![1, 2].contains(&channels) {
1101            return Err("Invalid number of channels");
1102        }
1103
1104        let mode = modes::default_mode();
1105        #[cfg(feature = "heap")]
1106        let celt_dec = Box::new(CeltDecoder::new(mode, channels, sampling_rate));
1107        #[cfg(not(feature = "heap"))]
1108        let celt_dec = CeltDecoder::new(mode, channels, sampling_rate);
1109
1110        #[cfg(feature = "heap")]
1111        let mut silk_dec = Box::new(silk::dec_api::SilkDecoder::new());
1112        #[cfg(not(feature = "heap"))]
1113        let mut silk_dec = silk::dec_api::SilkDecoder::new();
1114        silk_dec.init(sampling_rate.min(16000), channels as i32);
1115        silk_dec.channel_state[0].fs_api_hz = sampling_rate;
1116
1117        Ok(Self {
1118            celt_dec,
1119            silk_dec,
1120            sampling_rate,
1121            channels,
1122            prev_mode: None,
1123            prev_redundancy: false,
1124            frame_size: 0,
1125            bandwidth: Bandwidth::Auto,
1126            stream_channels: channels,
1127            silk_resampler: silk::resampler::SilkResampler::default(),
1128            silk_resampler_2: silk::resampler::SilkResampler::default(),
1129            prev_internal_rate: 0,
1130            hybrid_skip_celt: false,
1131
1132            #[cfg(not(feature = "heap"))]
1133            w_pcm_i16: FixedVec::from_value(0i16, 960 * channels),
1134            #[cfg(feature = "heap")]
1135            w_pcm_i16: Box::new(FixedVec::from_value(0i16, 960 * channels)),
1136
1137            #[cfg(not(feature = "heap"))]
1138            w_silk_out: FixedVec::from_value(0.0f32, OPUS_MAX_SUBFRAME * channels),
1139            #[cfg(feature = "heap")]
1140            w_silk_out: Box::new(FixedVec::from_value(0.0f32, OPUS_MAX_SUBFRAME * channels)),
1141            #[cfg(not(feature = "heap"))]
1142            w_pcm_resampled: FixedVec::from_value(0i16, OPUS_MAX_SUBFRAME * channels),
1143            #[cfg(feature = "heap")]
1144            w_pcm_resampled: Box::new(FixedVec::from_value(0i16, OPUS_MAX_SUBFRAME * channels)),
1145            #[cfg(not(feature = "heap"))]
1146            w_celt_planar: FixedVec::from_value(0.0f32, OPUS_MAX_SUBFRAME * channels),
1147            #[cfg(feature = "heap")]
1148            w_celt_planar: Box::new(FixedVec::from_value(0.0f32, OPUS_MAX_SUBFRAME * channels)),
1149            #[cfg(not(feature = "heap"))]
1150            w_celt_out: FixedVec::from_value(0.0f32, OPUS_MAX_SUBFRAME * channels),
1151            #[cfg(feature = "heap")]
1152            w_celt_out: Box::new(FixedVec::from_value(0.0f32, OPUS_MAX_SUBFRAME * channels)),
1153
1154            #[cfg(not(feature = "heap"))]
1155            prev_pcm_tail: FixedVec::from_value(0.0f32, 240 * channels),
1156            #[cfg(feature = "heap")]
1157            prev_pcm_tail: Box::new(FixedVec::from_value(0.0f32, 240 * channels)),
1158        })
1159    }
1160
1161    pub fn decode(
1162        &mut self,
1163        input: &[u8],
1164        frame_size: usize,
1165        output: &mut [f32],
1166    ) -> Result<usize, &'static str> {
1167        if input.is_empty() {
1168            return Err("Input packet empty");
1169        }
1170
1171        let toc = input[0];
1172        let mode = mode_from_toc(toc);
1173        let packet_channels = channels_from_toc(toc);
1174        let bandwidth = bandwidth_from_toc(toc);
1175        let frame_duration_ms = frame_duration_ms_from_toc(toc);
1176
1177        // A packet of 0 or 1 bytes (ToC only) is a lost/DTX frame. libopus
1178        // triggers PLC in this case (opus_decoder.c:315-321). We decode the
1179        // frame using the previous mode's concealment.
1180        let lost_frame = input.len() <= 1;
1181
1182        if packet_channels != self.channels {
1183            return Err("Channel count mismatch between packet and decoder");
1184        }
1185
1186        let code = toc & 0x03;
1187        let frame_count: usize;
1188        let frame_payloads: FixedVec<&[u8], OPUS_MAX_PACKET_FRAMES>;
1189
1190        match code {
1191            0 => {
1192                frame_count = 1;
1193                frame_payloads = FixedVec::from_slice(&[&input[1..]]);
1194            }
1195            1 => {
1196                frame_count = 2;
1197                let data_len = input.len() - 1;
1198                // RFC 6716 §3.2.1: code 1 carries two equal-size (CBR) frames,
1199                // so the payload length must be even. libopus rejects odd lengths.
1200                if data_len % 2 != 0 {
1201                    return Err("Code 1: payload length must be even");
1202                }
1203                let half = data_len / 2;
1204                if half == 0 {
1205                    return Err("Code 1: empty frame");
1206                }
1207                frame_payloads = FixedVec::from_slice(&[&input[1..1 + half], &input[1 + half..]]);
1208            }
1209            2 => {
1210                frame_count = 2;
1211                let data = &input[1..];
1212                if data.is_empty() {
1213                    return Err("Code 2 packet has no data");
1214                }
1215                let (first_len, header_size) = parse_frame_size(data)?;
1216                if header_size + first_len > data.len() {
1217                    return Err("Code 2: first frame size exceeds packet");
1218                }
1219                frame_payloads = FixedVec::from_slice(&[
1220                    &data[header_size..header_size + first_len],
1221                    &data[header_size + first_len..],
1222                ]);
1223            }
1224            3 => {
1225                if input.len() < 2 {
1226                    return Err("Code 3 packet too short");
1227                }
1228                let count_byte = input[1];
1229                let n_frames = (count_byte & 0x3F) as usize;
1230                if n_frames < 1 || n_frames > 48 {
1231                    return Err("Code 3: invalid frame count");
1232                }
1233                frame_count = n_frames;
1234                // Bit 6 = padding flag, bit 7 = VBR flag (RFC 6716 §3.2.1).
1235                let padding_flag = (count_byte & 0x40) != 0;
1236                let vbr = (count_byte & 0x80) != 0;
1237
1238                // Parse the optional padding length bytes that follow the count
1239                // byte. The padding *content* (pad_len bytes) lives at the end of
1240                // the packet and is not part of any frame.
1241                let mut ptr = 2usize;
1242                let mut pad_len = 0usize;
1243                if padding_flag {
1244                    loop {
1245                        if ptr >= input.len() {
1246                            return Err("Code 3: padding overflow");
1247                        }
1248                        let p = input[ptr] as usize;
1249                        ptr += 1;
1250                        if p == 255 {
1251                            pad_len += 254;
1252                        } else {
1253                            pad_len += p;
1254                            break;
1255                        }
1256                    }
1257                }
1258                if ptr + pad_len > input.len() {
1259                    return Err("Code 3: padding exceeds packet");
1260                }
1261                let payload_end = input.len() - pad_len;
1262                let payload = &input[ptr..payload_end];
1263
1264                let mut payloads: FixedVec<&[u8], OPUS_MAX_PACKET_FRAMES> = FixedVec::new();
1265                if frame_count == 1 {
1266                    // Single frame: the entire payload region is the frame, both
1267                    // for VBR and CBR (no length prefix is present).
1268                    payloads.push(payload);
1269                } else if vbr {
1270                    // VBR (V=1): per-frame lengths for all frames except the last,
1271                    // which takes the remaining bytes (RFC 6716 §3.2.1).
1272                    let mut cursor = 0usize;
1273                    for i in 0..frame_count {
1274                        if i + 1 < frame_count {
1275                            if cursor >= payload.len() {
1276                                return Err("Code 3: unexpected end in VBR header");
1277                            }
1278                            let (frame_len, header_bytes) = parse_frame_size(&payload[cursor..])?;
1279                            cursor += header_bytes;
1280                            if cursor + frame_len > payload.len() {
1281                                return Err("Code 3: frame length exceeds packet");
1282                            }
1283                            payloads.push(&payload[cursor..cursor + frame_len]);
1284                            cursor += frame_len;
1285                        } else {
1286                            // Last frame: remaining bytes, no length prefix.
1287                            if cursor > payload.len() {
1288                                return Err("Code 3: no data for last frame");
1289                            }
1290                            payloads.push(&payload[cursor..]);
1291                        }
1292                    }
1293                } else {
1294                    // CBR (V=0): remaining bytes are split equally into M frames
1295                    // (RFC 6716 §3.2.1: "the remaining bytes are split into M
1296                    // equal chunks").
1297                    if payload.len() % frame_count != 0 {
1298                        return Err("Code 3 CBR: payload not divisible by frame count");
1299                    }
1300                    let frame_len = payload.len() / frame_count;
1301                    for i in 0..frame_count {
1302                        payloads.push(&payload[i * frame_len..(i + 1) * frame_len]);
1303                    }
1304                }
1305                frame_payloads = payloads;
1306            }
1307            _ => unreachable!(),
1308        }
1309
1310        self.frame_size = frame_size;
1311        self.bandwidth = bandwidth;
1312        self.stream_channels = packet_channels;
1313
1314        // Derive the actual per-frame sample count from the TOC, not from the
1315        // caller's frame_size. This prevents panics in bands.rs/celt.rs when
1316        // the caller passes a mismatched frame_size (issue #7 sub-item 1):
1317        // the internal decoders always get the correct geometry.
1318        let toc_frame_size = frame_samples_from_toc(toc, self.sampling_rate)
1319            .ok_or("Invalid TOC for sampling rate")?;
1320        let decoded_total = toc_frame_size * frame_count;
1321        if frame_size < decoded_total {
1322            return Err("frame_size too small for packet");
1323        }
1324        if output.len() < decoded_total * self.channels {
1325            return Err("Output buffer too small for packet");
1326        }
1327        // Zero-fill any extra space the caller provided beyond what the packet
1328        // actually produces, so stale data is never left in the buffer.
1329        if output.len() > decoded_total * self.channels {
1330            for v in &mut output[decoded_total * self.channels..] {
1331                *v = 0.0;
1332            }
1333        }
1334        let sub_frame_size = toc_frame_size;
1335        let sub_output_len = sub_frame_size * self.channels;
1336
1337        // Detect mode transition and reset CELT decoder state to prevent
1338        // cross-mode artifacts (libopus opus_decoder.c:602-604).
1339        // This is the primary fix for issue #8/#9 alignment divergence:
1340        // stale CELT MDCT/prefilter state at SILK↔CELT boundaries causes
1341        // discontinuities that accumulate across transitions.
1342        let mode_transition = match self.prev_mode {
1343            Some(prev) if prev != mode && !self.prev_redundancy => true,
1344            _ => false,
1345        };
1346        if mode_transition {
1347            self.celt_dec.reset_state();
1348        }
1349
1350        // Generate SILK PLC audio for the mode-transition bridge. libopus
1351        // synthesizes 5ms (F5) of pitch-extrapolated audio in the OLD mode
1352        // (opus_decoder.c:387-391) and crossfades it with the new frame. We
1353        // reuse the F5-sized prev_pcm_tail buffer for this bridge.
1354        let f5_bridge = self.sampling_rate as usize / 200; // F5 = Fs/200
1355        if mode_transition
1356            && f5_bridge > 0
1357            && matches!(
1358                self.prev_mode,
1359                Some(OpusMode::SilkOnly) | Some(OpusMode::Hybrid)
1360            )
1361            && self.prev_internal_rate > 0
1362        {
1363            let internal_rate = self.prev_internal_rate;
1364            let plc_internal_len = (10 * internal_rate / 1000) as usize;
1365            let mut plc_rc = RangeCoder::new_decoder(&[]);
1366            let mut plc_i16: FixedVec<i16, OPUS_PCM_I16> =
1367                FixedVec::from_value(0i16, plc_internal_len * self.channels);
1368            let n = self.silk_dec.decode(
1369                &mut plc_rc,
1370                &mut plc_i16,
1371                silk::decode_frame::FLAG_PACKET_LOST,
1372                true,
1373                10,
1374                internal_rate,
1375            );
1376            if n > 0 {
1377                let bridge_ch = f5_bridge * self.channels;
1378                let bridge_len = bridge_ch.min(self.prev_pcm_tail.len());
1379                if internal_rate == self.sampling_rate {
1380                    // No resampling: copy PLC samples directly (ch0 planar).
1381                    let n_us = n as usize;
1382                    for ch in 0..self.channels {
1383                        let src_base = ch * n_us;
1384                        for i in 0..(bridge_len / self.channels).min(n_us) {
1385                            let dst = i * self.channels + ch;
1386                            if dst < bridge_len {
1387                                self.prev_pcm_tail[dst] = plc_i16[src_base + i] as f32 / 32768.0;
1388                            }
1389                        }
1390                    }
1391                } else if self.silk_resampler.is_initialized() {
1392                    // Resample channel 0 to the API rate for the bridge.
1393                    // The resampler must receive an output buffer sized for the
1394                    // FULL resampled length (issue #15): truncating the buffer to
1395                    // `f5_bridge` caused out-of-bounds writes in the Up2HQ/Copy
1396                    // paths. We size it fully, then copy only the bridge window.
1397                    let ratio = self.sampling_rate as f64 / internal_rate as f64;
1398                    let full_len = ((n as f64 * ratio) as usize).max(1);
1399                    let out_len = full_len.min(f5_bridge);
1400                    let n_us = n as usize;
1401                    let mut resampled: FixedVec<i16, OPUS_MAX_FRAME> =
1402                        FixedVec::from_value(0i16, full_len);
1403                    self.silk_resampler
1404                        .process(&mut resampled, &plc_i16[..n_us], n);
1405                    for i in 0..out_len {
1406                        if i < bridge_len / self.channels {
1407                            for ch in 0..self.channels {
1408                                self.prev_pcm_tail[i * self.channels + ch] =
1409                                    resampled[i] as f32 / 32768.0;
1410                            }
1411                        }
1412                    }
1413                }
1414            }
1415        }
1416
1417        // Track whether this packet uses Hybrid redundancy.
1418        let mut has_redundancy = false;
1419
1420        match mode {
1421            OpusMode::SilkOnly => {
1422                let internal_sample_rate = match bandwidth {
1423                    Bandwidth::Narrowband => 8000,
1424                    Bandwidth::Mediumband => 12000,
1425                    Bandwidth::Wideband => 16000,
1426                    _ => 16000,
1427                };
1428                let internal_frame_size =
1429                    (frame_duration_ms * internal_sample_rate / 1000) as usize;
1430
1431                if self.sampling_rate != internal_sample_rate
1432                    && internal_sample_rate != self.prev_internal_rate
1433                {
1434                    self.silk_resampler
1435                        .init(internal_sample_rate, self.sampling_rate);
1436                    self.silk_resampler_2
1437                        .init(internal_sample_rate, self.sampling_rate);
1438                }
1439                // Always track the SILK internal rate so the mode-transition
1440                // PLC bridge can be generated (even when no resampling is
1441                // needed, e.g. 16kHz decoder + SILK WB).
1442                self.prev_internal_rate = internal_sample_rate;
1443
1444                for (fi, payload) in frame_payloads.iter().enumerate() {
1445                    let mut rc = RangeCoder::new_decoder(payload);
1446                    let pcm_i16_len = internal_frame_size * self.channels;
1447                    debug_assert!(pcm_i16_len <= self.w_pcm_i16.len());
1448
1449                    // Decode every SILK frame carried by this payload. A 40/60 ms
1450                    // SILK packet (Code 0) contains 2/3 internal frames in ONE
1451                    // range-coded stream; libopus loops silk_Decode until the full
1452                    // frame duration is produced (opus_decoder.c), with
1453                    // new_packet set only on the first call (issue #27).
1454                    let lost_flag = if lost_frame {
1455                        silk::decode_frame::FLAG_PACKET_LOST
1456                    } else {
1457                        silk::decode_frame::FLAG_DECODE_NORMAL
1458                    };
1459                    let out_start = fi * sub_output_len;
1460                    let mut frame_pos = 0usize; // internal-rate samples decoded so far
1461                    let mut new_packet = true;
1462                    while frame_pos < internal_frame_size {
1463                        let ret = {
1464                            let (silk_dec, pcm_i16) = (
1465                                state_mut(&mut self.silk_dec),
1466                                state_mut(&mut self.w_pcm_i16),
1467                            );
1468                            silk_dec.decode(
1469                                &mut rc,
1470                                &mut pcm_i16[..pcm_i16_len],
1471                                lost_flag,
1472                                new_packet,
1473                                frame_duration_ms,
1474                                internal_sample_rate,
1475                            )
1476                        };
1477                        new_packet = false;
1478
1479                        if ret < 0 {
1480                            return Err("SILK decoding failed");
1481                        }
1482                        let decoded_samples = ret as usize;
1483                        if decoded_samples == 0 {
1484                            break;
1485                        }
1486
1487                        // SILK decoder outputs planar for THIS frame:
1488                        // ch0 at [0..fl], ch1 at [fl..2*fl].
1489                        if self.sampling_rate == internal_sample_rate {
1490                            let frames = decoded_samples.min(internal_frame_size - frame_pos);
1491                            for i in 0..frames {
1492                                for ch in 0..self.channels {
1493                                    let src = if ch == 0 { i } else { decoded_samples + i };
1494                                    let v = self.w_pcm_i16[src] as f32 / 32768.0;
1495                                    let idx = out_start + (frame_pos + i) * self.channels + ch;
1496                                    if idx < output.len() {
1497                                        output[idx] = v;
1498                                    }
1499                                }
1500                            }
1501                        } else {
1502                            let resampled_len = decoded_samples * self.sampling_rate as usize
1503                                / internal_sample_rate as usize;
1504                            let api_pos = frame_pos * self.sampling_rate as usize
1505                                / internal_sample_rate as usize;
1506                            let copy_len = resampled_len.min(sub_frame_size - api_pos);
1507                            debug_assert!(
1508                                resampled_len * self.channels <= self.w_pcm_resampled.len()
1509                            );
1510                            // Resample channel 0.
1511                            {
1512                                let (res, inp, out) = (
1513                                    &mut self.silk_resampler,
1514                                    state_ref(&self.w_pcm_i16),
1515                                    state_mut(&mut self.w_pcm_resampled),
1516                                );
1517                                res.process(
1518                                    &mut out[..resampled_len],
1519                                    &inp[..decoded_samples],
1520                                    decoded_samples as i32,
1521                                );
1522                            }
1523                            // Resample channel 1 (stereo only).
1524                            if self.channels == 2 {
1525                                let (res, inp, out) = (
1526                                    &mut self.silk_resampler_2,
1527                                    state_ref(&self.w_pcm_i16),
1528                                    state_mut(&mut self.w_pcm_resampled),
1529                                );
1530                                res.process(
1531                                    &mut out[resampled_len..2 * resampled_len],
1532                                    &inp[decoded_samples..2 * decoded_samples],
1533                                    decoded_samples as i32,
1534                                );
1535                            }
1536                            for i in 0..copy_len {
1537                                for ch in 0..self.channels {
1538                                    let v = self.w_pcm_resampled[ch * resampled_len + i] as f32
1539                                        / 32768.0;
1540                                    let idx = out_start + (api_pos + i) * self.channels + ch;
1541                                    if idx < output.len() {
1542                                        output[idx] = v;
1543                                    }
1544                                }
1545                            }
1546                        }
1547                        frame_pos += decoded_samples;
1548                    }
1549                }
1550                decoded_total
1551            }
1552
1553            OpusMode::CeltOnly => {
1554                let celt_end_band = self.celt_end_band_from_toc(toc);
1555
1556                for (fi, payload) in frame_payloads.iter().enumerate() {
1557                    let mut rc = RangeCoder::new_decoder(payload);
1558                    let total_bits = (payload.len() * 8) as i32;
1559                    let needed = sub_frame_size * self.channels;
1560                    let out_start = fi * needed;
1561                    let out_end = (out_start + needed).min(output.len());
1562
1563                    if output.len() < out_end {
1564                        return Err("Output buffer too small");
1565                    }
1566
1567                    if self.channels == 1 {
1568                        self.celt_dec.decode_from_range_coder_with_band_range(
1569                            &mut rc,
1570                            total_bits,
1571                            sub_frame_size,
1572                            &mut output[out_start..out_end],
1573                            0,
1574                            celt_end_band,
1575                        );
1576                        for sample in &mut output[out_start..out_end] {
1577                            *sample = sample.clamp(-1.0, 1.0);
1578                        }
1579                    } else {
1580                        self.celt_dec.decode_from_range_coder_with_band_range(
1581                            &mut rc,
1582                            total_bits,
1583                            sub_frame_size,
1584                            &mut self.w_celt_planar[..needed],
1585                            0,
1586                            celt_end_band,
1587                        );
1588                        for i in 0..sub_frame_size {
1589                            for ch in 0..self.channels {
1590                                let idx = out_start + i * self.channels + ch;
1591                                output[idx] =
1592                                    self.w_celt_planar[ch * sub_frame_size + i].clamp(-1.0, 1.0);
1593                            }
1594                        }
1595                    }
1596                }
1597                decoded_total
1598            }
1599
1600            OpusMode::Hybrid => {
1601                let internal_sample_rate = 16000;
1602                let internal_frame_size =
1603                    (frame_duration_ms * internal_sample_rate / 1000) as usize;
1604                let celt_end_band = self.celt_end_band_from_toc(toc);
1605
1606                if self.sampling_rate != internal_sample_rate
1607                    && internal_sample_rate != self.prev_internal_rate
1608                {
1609                    self.silk_resampler
1610                        .init(internal_sample_rate, self.sampling_rate);
1611                    self.silk_resampler_2
1612                        .init(internal_sample_rate, self.sampling_rate);
1613                }
1614                self.prev_internal_rate = internal_sample_rate;
1615
1616                for (fi, payload) in frame_payloads.iter().enumerate() {
1617                    let mut rc = RangeCoder::new_decoder(payload);
1618                    let pcm_silk_i16_len = internal_frame_size * self.channels;
1619                    debug_assert!(pcm_silk_i16_len <= self.w_pcm_i16.len());
1620
1621                    // Decode every SILK frame carried by this payload (issue #27):
1622                    // 40/60 ms Hybrid packets carry 2/3 internal SILK frames in a
1623                    // single range-coded stream, exactly like SILK-only packets.
1624                    let silk_out_len = sub_frame_size * self.channels;
1625                    self.w_silk_out[..silk_out_len].fill(0.0);
1626                    let lost_flag = if lost_frame {
1627                        silk::decode_frame::FLAG_PACKET_LOST
1628                    } else {
1629                        silk::decode_frame::FLAG_DECODE_NORMAL
1630                    };
1631                    let mut frame_pos = 0usize;
1632                    let mut new_packet = true;
1633                    while frame_pos < internal_frame_size {
1634                        let ret = {
1635                            let (silk_dec, pcm_i16) = (
1636                                state_mut(&mut self.silk_dec),
1637                                state_mut(&mut self.w_pcm_i16),
1638                            );
1639                            silk_dec.decode(
1640                                &mut rc,
1641                                &mut pcm_i16[..pcm_silk_i16_len],
1642                                lost_flag,
1643                                new_packet,
1644                                frame_duration_ms,
1645                                internal_sample_rate,
1646                            )
1647                        };
1648                        new_packet = false;
1649
1650                        if ret < 0 {
1651                            return Err("SILK decoding failed");
1652                        }
1653                        let decoded_samples = ret as usize;
1654                        if decoded_samples == 0 {
1655                            break;
1656                        }
1657
1658                        // SILK decoder outputs planar for THIS frame:
1659                        // ch0 at [0..fl], ch1 at [fl..2*fl].
1660                        if self.sampling_rate == internal_sample_rate {
1661                            let frames = decoded_samples.min(internal_frame_size - frame_pos);
1662                            for i in 0..frames {
1663                                for ch in 0..self.channels {
1664                                    let src = if ch == 0 { i } else { decoded_samples + i };
1665                                    let v = self.w_pcm_i16[src] as f32 / 32768.0;
1666                                    let idx = (frame_pos + i) * self.channels + ch;
1667                                    if idx < silk_out_len {
1668                                        self.w_silk_out[idx] = v;
1669                                    }
1670                                }
1671                            }
1672                        } else {
1673                            let resampled_len = decoded_samples * self.sampling_rate as usize
1674                                / internal_sample_rate as usize;
1675                            let api_pos = frame_pos * self.sampling_rate as usize
1676                                / internal_sample_rate as usize;
1677                            let copy_len = resampled_len.min(sub_frame_size - api_pos);
1678                            debug_assert!(
1679                                resampled_len * self.channels <= self.w_pcm_resampled.len()
1680                            );
1681                            // Resample channel 0.
1682                            {
1683                                let (res, inp, out) = (
1684                                    &mut self.silk_resampler,
1685                                    state_ref(&self.w_pcm_i16),
1686                                    state_mut(&mut self.w_pcm_resampled),
1687                                );
1688                                res.process(
1689                                    &mut out[..resampled_len],
1690                                    &inp[..decoded_samples],
1691                                    decoded_samples as i32,
1692                                );
1693                            }
1694                            // Resample channel 1 (stereo only).
1695                            if self.channels == 2 {
1696                                let (res, inp, out) = (
1697                                    &mut self.silk_resampler_2,
1698                                    state_ref(&self.w_pcm_i16),
1699                                    state_mut(&mut self.w_pcm_resampled),
1700                                );
1701                                res.process(
1702                                    &mut out[resampled_len..2 * resampled_len],
1703                                    &inp[decoded_samples..2 * decoded_samples],
1704                                    decoded_samples as i32,
1705                                );
1706                            }
1707                            for i in 0..copy_len {
1708                                for ch in 0..self.channels {
1709                                    let v = self.w_pcm_resampled[ch * resampled_len + i] as f32
1710                                        / 32768.0;
1711                                    let idx = (api_pos + i) * self.channels + ch;
1712                                    if idx < silk_out_len {
1713                                        self.w_silk_out[idx] = v;
1714                                    }
1715                                }
1716                            }
1717                        }
1718                        frame_pos += decoded_samples;
1719                    }
1720
1721                    let total_bits = (payload.len() * 8) as i32;
1722                    let redundancy = rc.decode_bit_logp(12);
1723                    let skip_celt = if redundancy {
1724                        let _celt_to_silk = rc.decode_bit_logp(1);
1725                        has_redundancy = true;
1726                        // When redundancy is present, the redundant CELT frame
1727                        // provides the transition audio. We skip the main CELT
1728                        // decode for this sub-frame (the SILK output stands alone)
1729                        // — a simplified version of libopus's behaviour where the
1730                        // redundant frame is decoded separately and crossfaded.
1731                        true
1732                    } else {
1733                        false
1734                    };
1735
1736                    if skip_celt {
1737                        self.w_celt_out[..silk_out_len].fill(0.0);
1738                    } else {
1739                        let (celt_dec, celt_planar) = (
1740                            state_mut(&mut self.celt_dec),
1741                            state_mut(&mut self.w_celt_planar),
1742                        );
1743                        celt_dec.decode_from_range_coder_with_band_range(
1744                            &mut rc,
1745                            total_bits,
1746                            sub_frame_size,
1747                            &mut celt_planar[..silk_out_len],
1748                            17,
1749                            celt_end_band,
1750                        );
1751
1752                        if self.channels == 1 {
1753                            self.w_celt_out[..silk_out_len]
1754                                .copy_from_slice(&self.w_celt_planar[..silk_out_len]);
1755                        } else {
1756                            for i in 0..sub_frame_size {
1757                                for ch in 0..self.channels {
1758                                    self.w_celt_out[i * self.channels + ch] =
1759                                        self.w_celt_planar[ch * sub_frame_size + i];
1760                                }
1761                            }
1762                        }
1763                    }
1764
1765                    let out_start = fi * silk_out_len;
1766                    let total = silk_out_len.min(output.len() - out_start);
1767                    for j in 0..total {
1768                        output[out_start + j] =
1769                            (self.w_silk_out[j] + self.w_celt_out[j]).clamp(-1.0, 1.0);
1770                    }
1771                }
1772                decoded_total
1773            }
1774        };
1775
1776        // Apply PLC-style bridging at mode transitions (libopus
1777        // opus_decoder.c:660-679). The first F2_5 of the output is replaced
1778        // with the previous frame's tail (PLC bridge), and the next F2_5 is
1779        // crossfaded between the bridge and the new frame's CELT output.
1780        // F5 = Fs/200, F2_5 = Fs/400.
1781        let f2_5 = self.sampling_rate as usize / 400;
1782        let f5 = f2_5 * 2;
1783        if mode_transition && f5 > 0 && decoded_total >= f5 {
1784            let window = modes::default_mode().window;
1785            let inc = (48000 / self.sampling_rate) as usize;
1786            let f2_5_ch = f2_5 * self.channels;
1787            let f5_ch = f5 * self.channels;
1788            // First F2_5: pure bridging audio from previous frame's tail.
1789            output[..f2_5_ch].copy_from_slice(&self.prev_pcm_tail[..f2_5_ch]);
1790            // Next F2_5: crossfade bridge → new CELT output.
1791            let new_mid: FixedVec<f32, OPUS_PCM_TAIL> =
1792                FixedVec::from_slice(&output[f2_5_ch..f5_ch]);
1793            smooth_fade(
1794                &self.prev_pcm_tail[f2_5_ch..f5_ch],
1795                &new_mid,
1796                &mut output[f2_5_ch..f5_ch],
1797                f2_5,
1798                self.channels,
1799                window,
1800                inc,
1801            );
1802        }
1803
1804        // Save the tail of this frame for the next transition (F5 samples).
1805        let tail_len = f5 * self.channels;
1806        let out_total = decoded_total * self.channels;
1807        if out_total >= tail_len && tail_len <= self.prev_pcm_tail.len() {
1808            self.prev_pcm_tail[..tail_len]
1809                .copy_from_slice(&output[out_total - tail_len..out_total]);
1810        }
1811
1812        self.prev_mode = Some(mode);
1813        self.prev_redundancy = has_redundancy;
1814        Ok(decoded_total)
1815    }
1816}
1817
1818impl OpusDecoder {
1819    #[inline(always)]
1820    fn celt_end_band_from_toc(&self, toc: u8) -> usize {
1821        let mode = modes::default_mode();
1822        let top = mode.eff_ebands;
1823        if mode_from_toc(toc) == OpusMode::CeltOnly && toc >= 0x80 {
1824            const FROM_OPUS_TABLE: [u8; 16] = [
1825                0x80, 0x88, 0x90, 0x98, 0x40, 0x48, 0x50, 0x58, 0x20, 0x28, 0x30, 0x38, 0x00, 0x08,
1826                0x10, 0x18,
1827            ];
1828            let idx = ((toc >> 3) - 16) as usize;
1829            let data0 = FROM_OPUS_TABLE[idx] | (toc & 0x7);
1830            let trim = (data0 >> 5) as usize;
1831            return top.saturating_sub(2 * trim).max(1);
1832        }
1833        top
1834    }
1835}
1836
1837fn frame_rate_from_params(sampling_rate: i32, frame_size: usize) -> Option<i32> {
1838    let frame_size = frame_size as i32;
1839    if frame_size == 0 || sampling_rate % frame_size != 0 {
1840        return None;
1841    }
1842    Some(sampling_rate / frame_size)
1843}
1844
1845fn gen_toc(mode: OpusMode, frame_rate: i32, bandwidth: Bandwidth, channels: usize) -> u8 {
1846    let mut rate = frame_rate;
1847    let mut period = 0;
1848    while rate < 400 {
1849        rate <<= 1;
1850        period += 1;
1851    }
1852
1853    let mut toc = match mode {
1854        OpusMode::SilkOnly => {
1855            let bw = (bandwidth as i32 - Bandwidth::Narrowband as i32) << 5;
1856            let per = (period - 2) << 3;
1857            (bw | per) as u8
1858        }
1859        OpusMode::CeltOnly => {
1860            let mut tmp = bandwidth as i32 - Bandwidth::Mediumband as i32;
1861            if tmp < 0 {
1862                tmp = 0;
1863            }
1864            let per = period << 3;
1865            (0x80 | (tmp << 5) | per) as u8
1866        }
1867        OpusMode::Hybrid => {
1868            let base_config = if bandwidth == Bandwidth::Superwideband {
1869                12
1870            } else {
1871                14
1872            };
1873            let period_offset = if frame_rate >= 100 { 0 } else { 1 };
1874            ((base_config + period_offset) << 3) as u8
1875        }
1876    };
1877
1878    if channels == 2 {
1879        toc |= 0x04;
1880    }
1881    toc
1882}
1883
1884fn mode_from_toc(toc: u8) -> OpusMode {
1885    if toc & 0x80 != 0 {
1886        OpusMode::CeltOnly
1887    } else if toc & 0x60 == 0x60 {
1888        OpusMode::Hybrid
1889    } else {
1890        OpusMode::SilkOnly
1891    }
1892}
1893
1894fn bandwidth_from_toc(toc: u8) -> Bandwidth {
1895    let mode = mode_from_toc(toc);
1896    match mode {
1897        OpusMode::SilkOnly => {
1898            let bw_bits = (toc >> 5) & 0x03;
1899            match bw_bits {
1900                0 => Bandwidth::Narrowband,
1901                1 => Bandwidth::Mediumband,
1902                2 => Bandwidth::Wideband,
1903                _ => Bandwidth::Wideband,
1904            }
1905        }
1906        OpusMode::Hybrid => {
1907            let bw_bit = (toc >> 4) & 0x01;
1908            if bw_bit == 0 {
1909                Bandwidth::Superwideband
1910            } else {
1911                Bandwidth::Fullband
1912            }
1913        }
1914        OpusMode::CeltOnly => {
1915            let bw_bits = (toc >> 5) & 0x03;
1916            match bw_bits {
1917                0 => Bandwidth::Mediumband,
1918                1 => Bandwidth::Wideband,
1919                2 => Bandwidth::Superwideband,
1920                3 => Bandwidth::Fullband,
1921                _ => Bandwidth::Fullband,
1922            }
1923        }
1924    }
1925}
1926
1927fn frame_duration_ms_from_toc(toc: u8) -> i32 {
1928    let mode = mode_from_toc(toc);
1929    match mode {
1930        OpusMode::SilkOnly => {
1931            let config = (toc >> 3) & 0x03;
1932            match config {
1933                0 => 10,
1934                1 => 20,
1935                2 => 40,
1936                3 => 60,
1937                _ => 20,
1938            }
1939        }
1940        OpusMode::Hybrid => {
1941            let config = (toc >> 3) & 0x01;
1942            if config == 0 { 10 } else { 20 }
1943        }
1944        OpusMode::CeltOnly => {
1945            let config = (toc >> 3) & 0x03;
1946            match config {
1947                0 => 2,
1948                1 => 5,
1949                2 => 10,
1950                3 => 20,
1951                _ => 20,
1952            }
1953        }
1954    }
1955}
1956
1957/// Compute the per-frame sample count implied by the TOC byte at a given
1958/// sampling rate. For CELT this uses the frame-rate derivation (which handles
1959/// the 2.5 ms case correctly, unlike integer millisecond arithmetic).
1960fn frame_samples_from_toc(toc: u8, sampling_rate: i32) -> Option<usize> {
1961    let mode = mode_from_toc(toc);
1962    match mode {
1963        OpusMode::CeltOnly => {
1964            let period = ((toc >> 3) & 0x03) as i32;
1965            let frame_rate = 400 >> period;
1966            if frame_rate == 0 || sampling_rate % frame_rate != 0 {
1967                return None;
1968            }
1969            Some((sampling_rate / frame_rate) as usize)
1970        }
1971        OpusMode::SilkOnly | OpusMode::Hybrid => {
1972            let duration_ms = frame_duration_ms_from_toc(toc);
1973            Some((sampling_rate as i64 * duration_ms as i64 / 1000) as usize)
1974        }
1975    }
1976}
1977
1978fn channels_from_toc(toc: u8) -> usize {
1979    if toc & 0x04 != 0 { 2 } else { 1 }
1980}
1981
1982/// Crossfade two signals using a squared-sine window (libopus smooth_fade).
1983/// `window` is the 120-sample CELT window at 48 kHz; `inc` = 48000/Fs strides it.
1984fn smooth_fade(
1985    in1: &[f32],
1986    in2: &[f32],
1987    out: &mut [f32],
1988    overlap: usize,
1989    channels: usize,
1990    window: &[f32],
1991    inc: usize,
1992) {
1993    for c in 0..channels {
1994        for i in 0..overlap {
1995            let wi = i * inc;
1996            if wi >= window.len() {
1997                break;
1998            }
1999            let w = window[wi] * window[wi];
2000            out[i * channels + c] = w * in2[i * channels + c] + (1.0 - w) * in1[i * channels + c];
2001        }
2002    }
2003}
2004
2005/// Parse an Opus frame length per RFC 6716 §3.2.1, identical to libopus
2006/// `parse_size()`:
2007///   - `0`: no frame (DTX / lost packet)
2008///   - `1..=251`: length of the frame in bytes (one byte consumed)
2009///   - `252..=255`: a second byte is read; length = `second*4 + first`
2010///
2011/// Returns `(length, bytes_consumed)`.
2012fn parse_frame_size(data: &[u8]) -> Result<(usize, usize), &'static str> {
2013    let first = *data.first().ok_or("truncated frame length")? as usize;
2014    if first < 252 {
2015        Ok((first, 1))
2016    } else {
2017        let second = *data.get(1).ok_or("truncated frame length")? as usize;
2018        Ok((second * 4 + first, 2))
2019    }
2020}
2021
2022#[cfg(all(test, feature = "std"))]
2023mod tests {
2024    use super::*;
2025
2026    fn frame_size_from_toc(toc: u8, sampling_rate: i32) -> Option<usize> {
2027        let mode = mode_from_toc(toc);
2028        match mode {
2029            OpusMode::CeltOnly => {
2030                let period = ((toc >> 3) & 0x03) as i32;
2031                let frame_rate = 400 >> period;
2032                if frame_rate == 0 || sampling_rate % frame_rate != 0 {
2033                    return None;
2034                }
2035                Some((sampling_rate / frame_rate) as usize)
2036            }
2037            OpusMode::SilkOnly => {
2038                let duration_ms = frame_duration_ms_from_toc(toc);
2039                Some((sampling_rate as i64 * duration_ms as i64 / 1000) as usize)
2040            }
2041            OpusMode::Hybrid => {
2042                let duration_ms = frame_duration_ms_from_toc(toc);
2043                Some((sampling_rate as i64 * duration_ms as i64 / 1000) as usize)
2044            }
2045        }
2046    }
2047
2048    #[test]
2049    fn gen_toc_matches_celt_reference_values() {
2050        let sampling_rate = 48_000;
2051        let cases = [
2052            (120usize, 0xE0u8),
2053            (240usize, 0xE8u8),
2054            (480usize, 0xF0u8),
2055            (960usize, 0xF8u8),
2056        ];
2057
2058        for (frame_size, expected_toc) in cases {
2059            let frame_rate = frame_rate_from_params(sampling_rate, frame_size).unwrap();
2060            let toc = gen_toc(OpusMode::CeltOnly, frame_rate, Bandwidth::Fullband, 1);
2061            assert_eq!(
2062                toc, expected_toc,
2063                "frame_size {} expected TOC {:02X} got {:02X}",
2064                frame_size, expected_toc, toc
2065            );
2066            let decoded_size = frame_size_from_toc(toc, sampling_rate).unwrap();
2067            assert_eq!(decoded_size, frame_size);
2068        }
2069
2070        let stereo_toc = gen_toc(
2071            OpusMode::CeltOnly,
2072            frame_rate_from_params(sampling_rate, 960).unwrap(),
2073            Bandwidth::Fullband,
2074            2,
2075        );
2076        assert_eq!(channels_from_toc(stereo_toc), 2);
2077    }
2078
2079    #[test]
2080    fn test_celt_decoder_large_frame_sizes() {
2081        let sampling_rate = 48000;
2082        let channels = 1;
2083
2084        let mut decoder = OpusDecoder::new(sampling_rate, channels).unwrap();
2085
2086        let frame_sizes = [120, 240, 480, 960];
2087
2088        for frame_size in frame_sizes {
2089            let toc = gen_toc(
2090                OpusMode::CeltOnly,
2091                frame_rate_from_params(sampling_rate, frame_size).unwrap(),
2092                Bandwidth::Fullband,
2093                channels,
2094            );
2095            let packet = [toc, 0, 0, 0, 0];
2096
2097            let mut output = vec![0.0f32; frame_size * channels];
2098
2099            let _ = decoder.decode(&packet, frame_size, &mut output);
2100        }
2101
2102        let channels = 2;
2103        let mut decoder = OpusDecoder::new(sampling_rate, channels).unwrap();
2104
2105        for frame_size in frame_sizes {
2106            let toc = gen_toc(
2107                OpusMode::CeltOnly,
2108                frame_rate_from_params(sampling_rate, frame_size).unwrap(),
2109                Bandwidth::Fullband,
2110                channels,
2111            );
2112            let packet = [toc, 0, 0, 0, 0];
2113
2114            let mut output = vec![0.0f32; frame_size * channels];
2115            let _ = decoder.decode(&packet, frame_size, &mut output);
2116        }
2117    }
2118
2119    #[test]
2120    fn test_celt_decoder_edge_case_frame_sizes() {
2121        let sampling_rate = 48000;
2122        let channels = 1;
2123        let mut decoder = OpusDecoder::new(sampling_rate, channels).unwrap();
2124
2125        let edge_sizes = [2048, 2167, 2168, 2169, 2880, 3072];
2126
2127        for frame_size in edge_sizes {
2128            let mut output = vec![0.0f32; frame_size * channels];
2129
2130            let _ = decoder.decode(&[0x80, 0, 0, 0], frame_size, &mut output);
2131        }
2132    }
2133
2134    // Regression test for: "index out of bounds: the len is 48 but the index is 119"
2135    // Root cause: frame_size=48 at 48kHz gives frame_rate=1000, which is not a valid
2136    // Hybrid-mode frame rate but was not validated.  CELT's lm-search then silently
2137    // fell back to lm=0, computed n2=120, and wrote output[119] into a 48-element
2138    // slice.  Triggered via G.729-decoded PCM (8kHz) passed to a 48kHz Opus encoder
2139    // without proper resampling, so the encoder received 48 samples instead of 480.
2140    #[test]
2141    fn test_invalid_small_frame_size_returns_error_not_panic() {
2142        let mut enc = OpusEncoder::new(48000, 2, Application::Voip).unwrap();
2143        enc.bitrate_bps = 64000;
2144        enc.complexity = 5;
2145        enc.use_cbr = true;
2146
2147        // 48 samples at 48kHz = 1ms → frame_rate=1000, invalid for Hybrid mode.
2148        let input = vec![0.0f32; 48 * 2]; // stereo interleaved
2149        let mut output = vec![0u8; 256];
2150
2151        let result = enc.encode(&input, 48, &mut output);
2152        assert!(
2153            result.is_err(),
2154            "encode with invalid frame_size=48 should return Err, not panic"
2155        );
2156    }
2157
2158    // Also verify that the Audio application path (always Hybrid at 48 kHz) rejects
2159    // the same bad frame size.
2160    #[test]
2161    fn test_invalid_small_frame_size_audio_application_returns_error() {
2162        let mut enc = OpusEncoder::new(48000, 1, Application::Audio).unwrap();
2163        let input = vec![0.0f32; 48];
2164        let mut output = vec![0u8; 256];
2165
2166        let result = enc.encode(&input, 48, &mut output);
2167        assert!(
2168            result.is_err(),
2169            "Audio/48kHz encoder with frame_size=48 should return Err"
2170        );
2171    }
2172}