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        // Mode selection: match C's opus_encode_native() behavior.
485        // C reference auto-selects between SILK_ONLY and CELT_ONLY; Hybrid is
486        // produced afterwards by bandwidth overrides (SILK-only + FB/SWB → Hybrid).
487        let mut mode = if self.application == Application::RestrictedLowDelay {
488            OpusMode::CeltOnly
489        } else {
490            let equiv = compute_equiv_rate(
491                self.bitrate_bps,
492                self.channels,
493                frame_rate,
494                !self.use_cbr,
495                self.complexity,
496                self.packet_loss_perc,
497            );
498            let prev_was_celt = self.prev_enc_mode == Some(OpusMode::CeltOnly);
499            let has_prev_mode = self.prev_enc_mode.is_some();
500            let voice_est = match self.application {
501                Application::Voip => 115,
502                Application::Audio => 48,
503                Application::RestrictedLowDelay => 0,
504            };
505            let threshold = compute_mode_threshold(
506                self.application,
507                self.channels,
508                prev_was_celt,
509                has_prev_mode,
510                voice_est,
511            );
512            if equiv >= threshold && self.sampling_rate >= 24000 {
513                OpusMode::CeltOnly
514            } else {
515                OpusMode::SilkOnly
516            }
517        };
518
519        let curr_bw = self.bandwidth;
520        if mode == OpusMode::SilkOnly
521            && (curr_bw == Bandwidth::Superwideband || curr_bw == Bandwidth::Fullband)
522        {
523            mode = OpusMode::Hybrid;
524        }
525        if mode == OpusMode::Hybrid
526            && (curr_bw == Bandwidth::Narrowband
527                || curr_bw == Bandwidth::Mediumband
528                || curr_bw == Bandwidth::Wideband)
529        {
530            mode = OpusMode::SilkOnly;
531        }
532
533        if mode == OpusMode::CeltOnly {
534            match frame_rate {
535                400 | 200 | 100 | 50 => {}
536                _ => return Err("Unsupported frame size for CELT-only mode"),
537            }
538        }
539
540        if mode == OpusMode::Hybrid {
541            match frame_rate {
542                100 | 50 => {}
543                _ => return Err("Unsupported frame size for Hybrid mode"),
544            }
545        }
546
547        if mode == OpusMode::SilkOnly {
548            match frame_rate {
549                400 | 200 | 100 | 50 | 25 => {}
550                _ => return Err("Unsupported frame size for SILK-only mode"),
551            }
552        }
553
554        let toc = gen_toc(mode, frame_rate, self.bandwidth, self.channels);
555        output[0] = toc;
556
557        let target_bits =
558            (self.bitrate_bps as i64 * frame_size as i64 / self.sampling_rate as i64) as i32;
559        let cbr_bytes = ((target_bits + 4) / 8) as usize;
560        let max_data_bytes = output.len();
561
562        // Cap at the Opus per-packet maximum (RFC 6716); the range coder's buffer
563        // is heap-free and sized to this constant.
564        let mut n_bytes = cbr_bytes
565            .min(max_data_bytes)
566            .max(1)
567            .min(OPUS_MAX_PACKET_BYTES);
568        let init_rc_size = n_bytes - 1;
569        self.rc.reset_for_encode(init_rc_size as u32);
570
571        // C opus_encode_frame_native: high-pass cutoff state is updated for ALL
572        // modes (celt_encoder.c:1969-1977); the filtered signal is what feeds
573        // both SILK (hybrid) and CELT. Compute it here so CELT-only frames also
574        // keep `variable_hp_smth2_q15` in sync with libopus.
575        let hp_freq_smth1 = if mode == OpusMode::CeltOnly {
576            silk_lin2log(60) << 8
577        } else {
578            self.silk_enc.s_cmn.variable_hp_smth1_q15
579        };
580
581        const VARIABLE_HP_SMTH_COEF2_Q16: i32 = 984;
582        self.variable_hp_smth2_q15 = silk_smlawb(
583            self.variable_hp_smth2_q15,
584            hp_freq_smth1 - self.variable_hp_smth2_q15,
585            VARIABLE_HP_SMTH_COEF2_Q16,
586        );
587
588        let cutoff_hz = silk_log2lin(silk_rshift(self.variable_hp_smth2_q15, 8));
589
590        if mode == OpusMode::SilkOnly || mode == OpusMode::Hybrid {
591            let silk_fs_khz = if mode == OpusMode::Hybrid {
592                16
593            } else {
594                self.sampling_rate.min(16000) / 1000
595            };
596
597            let frame_ms = (frame_size as i32 * 1000) / self.sampling_rate;
598            if !self.silk_initialized || self.silk_enc.s_cmn.fs_khz != silk_fs_khz {
599                let silk_init_bitrate = (((n_bytes - 1) * 8) as i64 * self.sampling_rate as i64
600                    / frame_size as i64) as i32;
601                silk_control_encoder(
602                    state_mut(&mut self.silk_enc),
603                    silk_fs_khz,
604                    frame_ms,
605                    silk_init_bitrate,
606                    self.complexity,
607                );
608                self.silk_enc.s_cmn.use_cbr = if self.use_cbr { 1 } else { 0 };
609
610                self.silk_enc.s_cmn.n_channels = self.channels as i32;
611                self.silk_initialized = true;
612                self.down2_state_first = [0; 2];
613                self.down2_state_second = [0; 2];
614                self.down2_3_state = [0; 6];
615                self.down_1_3_state = silk::resampler::SilkResamplerDown1_3::default();
616            }
617
618            self.silk_enc.s_cmn.use_in_band_fec = if self.use_inband_fec { 1 } else { 0 };
619            self.silk_enc.s_cmn.packet_loss_perc = self.packet_loss_perc.clamp(0, 100);
620
621            self.silk_enc.s_cmn.lbrr_enabled = if self.use_inband_fec { 1 } else { 0 };
622
623            if self.silk_enc.s_cmn.lbrr_gain_increases == 0 {
624                self.silk_enc.s_cmn.lbrr_gain_increases = 2;
625            }
626
627            let required_size = frame_size * self.channels;
628            self.buf_filtered.resize(required_size, 0);
629            if self.application == Application::Voip {
630                hp_cutoff(
631                    input,
632                    cutoff_hz,
633                    state_mut(&mut self.buf_filtered),
634                    &mut self.hp_mem,
635                    frame_size,
636                    self.channels,
637                    self.sampling_rate,
638                );
639            } else {
640                for (i, &x) in input.iter().enumerate() {
641                    self.buf_filtered[i] = (x * 32768.0).clamp(-32768.0, 32767.0) as i16;
642                }
643            }
644
645            let input_i16 = state_ref(&self.buf_filtered);
646
647            let silk_input: &[i16] = if mode == OpusMode::SilkOnly && self.sampling_rate > 16000 {
648                if self.sampling_rate == 48000 {
649                    let stage1_size = frame_size / 2;
650                    let mut stage1_buf = [0i16; 480];
651                    silk_resampler_down2(
652                        &mut self.down2_state_first,
653                        &mut stage1_buf[..stage1_size],
654                        input_i16,
655                        frame_size as i32,
656                    );
657                    let silk_frame_size = stage1_size * 2 / 3;
658                    self.buf_silk_input.resize(silk_frame_size, 0);
659                    silk_resampler_down2_3(
660                        &mut self.down2_3_state,
661                        state_mut(&mut self.buf_silk_input),
662                        &stage1_buf[..stage1_size],
663                        stage1_size as i32,
664                    );
665                    state_ref(&self.buf_silk_input)
666                } else if self.sampling_rate == 24000 {
667                    let silk_frame_size = frame_size * 2 / 3;
668                    self.buf_silk_input.resize(silk_frame_size, 0);
669                    silk_resampler_down2_3(
670                        &mut self.down2_3_state,
671                        state_mut(&mut self.buf_silk_input),
672                        input_i16,
673                        frame_size as i32,
674                    );
675                    state_ref(&self.buf_silk_input)
676                } else {
677                    input_i16
678                }
679            } else if mode == OpusMode::SilkOnly && self.channels == 2 {
680                let frame_length = input_i16.len() / 2;
681                self.buf_stereo_mid.resize(frame_length, 0);
682                self.buf_stereo_side.resize(frame_length, 0);
683                for i in 0..frame_length {
684                    let l = input_i16[2 * i] as i32;
685                    let r = input_i16[2 * i + 1] as i32;
686                    self.buf_stereo_mid[i] = ((l + r) / 2) as i16;
687                    self.buf_stereo_side[i] = (l - r) as i16;
688                }
689
690                self.silk_enc.stereo.side.resize(frame_length, 0);
691                self.silk_enc
692                    .stereo
693                    .side
694                    .copy_from_slice(&self.buf_stereo_side[..frame_length]);
695                state_ref(&self.buf_stereo_mid)
696            } else if mode == OpusMode::Hybrid && self.sampling_rate > 16000 {
697                if self.sampling_rate == 48000 {
698                    let silk_frame_size = frame_size / 3;
699                    self.buf_silk_input.resize(silk_frame_size, 0);
700                    silk::resampler::silk_resampler_down_1_3(
701                        &mut self.down_1_3_state,
702                        state_mut(&mut self.buf_silk_input),
703                        input_i16,
704                    );
705                } else {
706                    let silk_frame_size = frame_size * 2 / 3;
707                    self.buf_silk_input.resize(silk_frame_size, 0);
708                    silk_resampler_down2_3(
709                        &mut self.down2_3_state,
710                        state_mut(&mut self.buf_silk_input),
711                        input_i16,
712                        frame_size as i32,
713                    );
714                }
715                state_ref(&self.buf_silk_input)
716            } else {
717                input_i16
718            };
719
720            let mut pn_bytes = 0;
721
722            let silk_rate_for_calc = if mode == OpusMode::Hybrid {
723                16000
724            } else {
725                self.sampling_rate
726            };
727            let silk_frame_len = silk_input.len();
728
729            let silk_bitrate = if mode == OpusMode::Hybrid {
730                let frame_duration_ms = frame_size as i32 * 1000 / self.sampling_rate;
731                let frame20ms = frame_duration_ms >= 20;
732                compute_silk_rate_for_hybrid(self.bitrate_bps, frame20ms)
733            } else {
734                (8i64 * (n_bytes - 1) as i64 * silk_rate_for_calc as i64 / silk_frame_len as i64)
735                    as i32
736            };
737            let silk_max_bits = if mode == OpusMode::Hybrid {
738                let total_max_bits = ((n_bytes - 1) * 8) as i32;
739                if self.use_cbr {
740                    let silk_bits = (silk_bitrate as i64 * silk_frame_len as i64
741                        / silk_rate_for_calc as i64) as i32;
742                    let other_bits = 0i32.max(total_max_bits - silk_bits);
743                    0i32.max(total_max_bits - other_bits * 3 / 4)
744                } else {
745                    let frame_duration_ms = frame_size as i32 * 1000 / self.sampling_rate;
746                    let frame20ms = frame_duration_ms >= 20;
747                    let max_bit_rate = compute_silk_rate_for_hybrid(
748                        total_max_bits * self.sampling_rate / frame_size as i32,
749                        frame20ms,
750                    );
751                    max_bit_rate * frame_size as i32 / self.sampling_rate
752                }
753            } else {
754                ((n_bytes - 1) * 8) as i32
755            };
756            let silk_use_cbr = if mode == OpusMode::Hybrid && self.use_cbr {
757                0
758            } else if self.use_cbr {
759                1
760            } else {
761                0
762            };
763            let ret = silk_encode(
764                state_mut(&mut self.silk_enc),
765                silk_input,
766                silk_input.len(),
767                &mut self.rc,
768                &mut pn_bytes,
769                silk_bitrate,
770                silk_max_bits,
771                silk_use_cbr,
772                1,
773            );
774            if ret != 0 {
775                return Err("SILK encoding failed");
776            }
777        }
778
779        if mode == OpusMode::Hybrid {
780            self.rc.encode_bit_logp(false, 12); // redundancy = 0
781        }
782
783        if mode == OpusMode::Hybrid {
784            let nb_compr_bytes = (n_bytes - 1) as u32;
785            self.rc.shrink(nb_compr_bytes);
786        }
787
788        let silk_ret_bytes = if mode == OpusMode::SilkOnly {
789            ((self.rc.tell() + 7) >> 3) as usize
790        } else {
791            0
792        };
793
794        // libopus parity: adjust nbCompressedBytes for CELT/Hybrid based on tell (celt_encoder.c:1913-1921)
795        // tmp = bitrate*frame_size + tell*Fs; nbCompressed = (tmp+4*Fs)/(8*Fs)
796        // This is the CBR branch (vbr==0) in celt_encoder.c; for VBR the encoder
797        // does *not* do this adjustment - it uses the vbr_bound logic instead.
798        // Doing it for VBR would double-shrink and corrupt the budget.
799        if mode != OpusMode::SilkOnly && self.use_cbr {
800            let tell = self.rc.tell();
801            if tell > 1 {
802                let tmp = self.bitrate_bps as i64 * frame_size as i64
803                    + tell as i64 * self.sampling_rate as i64;
804                let adjusted = ((tmp + 4 * self.sampling_rate as i64)
805                    / (8 * self.sampling_rate as i64)) as usize;
806                let new_n = adjusted
807                    .min(max_data_bytes)
808                    .max(1)
809                    .min(OPUS_MAX_PACKET_BYTES);
810                if new_n < n_bytes {
811                    n_bytes = new_n;
812                    // shrink range coder to new size (keep SILK bytes, trim tail)
813                    let new_payload = n_bytes - 1;
814                    self.rc.shrink(new_payload as u32);
815                }
816            }
817        }
818        if mode == OpusMode::CeltOnly || mode == OpusMode::Hybrid {
819            self.celt_enc.complexity = self.complexity;
820            let start_band = if mode == OpusMode::Hybrid { 17 } else { 0 };
821            let total_packet_bits = ((n_bytes - 1) * 8) as i32;
822            // Propagate bitrate/VBR to CeltEncoder for accurate VBR handling (libopus parity)
823            let celt_bitrate = if mode == OpusMode::Hybrid {
824                let frame_ms = frame_size as i32 * 1000 / self.sampling_rate;
825                let frame20ms = frame_ms >= 20;
826                let silk_rate = compute_silk_rate_for_hybrid(self.bitrate_bps, frame20ms);
827                (self.bitrate_bps - silk_rate).max(8000)
828            } else {
829                self.bitrate_bps
830            };
831            self.celt_enc.set_bitrate(celt_bitrate);
832            self.celt_enc.set_vbr(!self.use_cbr);
833            // libopus: Hybrid VBR is unconstrained (can steal from SILK), CELT-only constrained
834            self.celt_enc
835                .set_constrained_vbr(mode == OpusMode::CeltOnly);
836
837            // Build the CELT input exactly like C `opus_encode_frame_native`:
838            //   pcm_buf = [delay-compensation prefix from ring]
839            //             [dc_reject / hp_cutoff'd current frame]
840            // CELT then reads pcm_buf[0..frame_size*channels] (opus_encoder.c:
841            // 1966-2010, 2493). This delay + filter step is what libopus feeds
842            // its CELT encoder; passing the raw input diverges from 1.6.
843            let celt_input: &[f32] = if self.delay_compensation > 0 {
844                let delay = self.delay_compensation;
845                let ebuf = self.encoder_buffer;
846                let ch = self.channels;
847                let total = (delay + frame_size) * ch;
848                self.buf_celt_pcm.resize(total, 0.0);
849                // 1. delay prefix from the ring (C: OPUS_COPY at 1967)
850                let prefix = delay * ch;
851                let src_start = (ebuf - delay) * ch;
852                self.buf_celt_pcm[..prefix]
853                    .copy_from_slice(&self.delay_buffer[src_start..src_start + prefix]);
854                // 2. filter current frame into pcm_buf[delay..] (C: 2002-2010)
855                let out = &mut self.buf_celt_pcm[prefix..];
856                if self.application == Application::Voip {
857                    hp_cutoff_float(
858                        input,
859                        cutoff_hz,
860                        out,
861                        &mut self.hp_mem_float,
862                        frame_size,
863                        ch,
864                        self.sampling_rate,
865                    );
866                } else {
867                    dc_reject_float(
868                        input,
869                        3,
870                        out,
871                        &mut self.hp_mem_float,
872                        frame_size,
873                        ch,
874                        self.sampling_rate,
875                    );
876                }
877                // 3. float NaN guard (C: 2016-2028)
878                let mut sum = 0.0f32;
879                for &v in &self.buf_celt_pcm[prefix..] {
880                    sum += v * v;
881                }
882                if !(sum < 1e9) || sum.is_nan() {
883                    self.buf_celt_pcm[prefix..].fill(0.0);
884                    self.hp_mem_float = [0.0; 4];
885                }
886                // 4. delay ring update (C: 2300-2312)
887                let keep = ebuf as i64 - (frame_size as i64 + delay as i64);
888                if keep > 0 {
889                    let keep = keep as usize;
890                    self.delay_buffer
891                        .copy_within(ch * frame_size..ch * (frame_size + keep), 0);
892                    let dst = ch * keep;
893                    let n = (frame_size + delay) * ch;
894                    self.delay_buffer[dst..dst + n].copy_from_slice(&self.buf_celt_pcm[..n]);
895                } else {
896                    let n = ebuf * ch;
897                    let src = (frame_size + delay - ebuf) * ch;
898                    self.delay_buffer[..n].copy_from_slice(&self.buf_celt_pcm[src..src + n]);
899                }
900                // 5. deinterleave pcm_buf[0..frame_size*ch] (interleaved) to
901                //    channel-major for the Rust CELT encoder.
902                let n = frame_size * ch;
903                self.buf_celt_input.resize(n, 0.0);
904                for i in 0..frame_size {
905                    for c in 0..ch {
906                        self.buf_celt_input[c * frame_size + i] = self.buf_celt_pcm[i * ch + c];
907                    }
908                }
909                state_ref(&self.buf_celt_input)
910            } else if self.channels == 1 {
911                input
912            } else {
913                let n = frame_size * self.channels;
914                self.buf_celt_input.resize(n, 0.0);
915                for i in 0..frame_size {
916                    for ch in 0..self.channels {
917                        self.buf_celt_input[ch * frame_size + i] = input[i * self.channels + ch];
918                    }
919                }
920                state_ref(&self.buf_celt_input)
921            };
922
923            if self.rc.tell() <= total_packet_bits {
924                let is_vbr = !self.use_cbr;
925                self.celt_enc.encode_with_budget_vbr(
926                    celt_input,
927                    frame_size,
928                    &mut self.rc,
929                    start_band,
930                    total_packet_bits,
931                    is_vbr,
932                );
933            }
934        }
935
936        self.rc.done();
937
938        if self.rc.error != 0 {
939            return Err("Range coder buffer overflow: encoded data exceeds packet budget");
940        }
941
942        if mode == OpusMode::SilkOnly {
943            let mut ret = silk_ret_bytes.min(self.rc.storage as usize);
944            while ret > 2 && self.rc.buf[ret - 1] == 0 {
945                ret -= 1;
946            }
947
948            let target_total = if self.use_cbr {
949                n_bytes.min(output.len())
950            } else {
951                (ret + 1).min(output.len())
952            };
953
954            let silk_len = ret;
955
956            if !self.use_cbr || silk_len + 1 >= target_total {
957                // VBR or payload fills the target: simple code 0 packet
958                output[0] = toc;
959                let copy_len = silk_len.min(target_total - 1);
960                output[1..1 + copy_len].copy_from_slice(&self.rc.buf[..copy_len]);
961                return Ok((copy_len + 1).min(output.len()));
962            }
963
964            output[0] = toc | 0x03;
965
966            if silk_len + 2 >= target_total {
967                output[1] = 0x01;
968                let copy_len = (target_total - 2).min(silk_len);
969                output[2..2 + copy_len].copy_from_slice(&self.rc.buf[..copy_len]);
970                self.prev_enc_mode = Some(mode);
971                return Ok(target_total.min(output.len()));
972            }
973
974            let pad_amount = target_total - silk_len - 2;
975            output[1] = 0x41;
976
977            let nb_255s = (pad_amount - 1) / 255;
978            let mut ptr = 2;
979            for _ in 0..nb_255s {
980                output[ptr] = 255;
981                ptr += 1;
982            }
983            output[ptr] = (pad_amount - 255 * nb_255s - 1) as u8;
984            ptr += 1;
985
986            output[ptr..ptr + silk_len].copy_from_slice(&self.rc.buf[..silk_len]);
987            ptr += silk_len;
988
989            let fill_end = target_total.min(output.len());
990            for byte in output[ptr..fill_end].iter_mut() {
991                *byte = 0;
992            }
993
994            self.prev_enc_mode = Some(mode);
995            return Ok(target_total.min(output.len()));
996        }
997
998        // For CELT/Hybrid, respect possible VBR shrink performed by CeltEncoder (e.g. silence)
999        let payload_len = (self.rc.storage as usize).min(output.len() - 1);
1000        output[1..1 + payload_len].copy_from_slice(&self.rc.buf[..payload_len]);
1001        self.prev_enc_mode = Some(mode);
1002        Ok(payload_len + 1)
1003    }
1004}
1005
1006pub struct OpusDecoder {
1007    #[cfg(not(feature = "heap"))]
1008    celt_dec: CeltDecoder,
1009    #[cfg(feature = "heap")]
1010    celt_dec: Box<CeltDecoder>,
1011    #[cfg(not(feature = "heap"))]
1012    silk_dec: silk::dec_api::SilkDecoder,
1013    #[cfg(feature = "heap")]
1014    silk_dec: Box<silk::dec_api::SilkDecoder>,
1015    sampling_rate: i32,
1016    channels: usize,
1017
1018    prev_mode: Option<OpusMode>,
1019
1020    /// Whether the previous frame had redundancy (mode transition marker).
1021    prev_redundancy: bool,
1022    frame_size: usize,
1023
1024    bandwidth: Bandwidth,
1025
1026    stream_channels: usize,
1027
1028    silk_resampler: silk::resampler::SilkResampler,
1029
1030    /// Second resampler instance for stereo channel 1.
1031    silk_resampler_2: silk::resampler::SilkResampler,
1032
1033    prev_internal_rate: i32,
1034
1035    pub hybrid_skip_celt: bool,
1036
1037    #[cfg(not(feature = "heap"))]
1038    w_pcm_i16: FixedVec<i16, OPUS_PCM_I16>,
1039    #[cfg(feature = "heap")]
1040    w_pcm_i16: Box<FixedVec<i16, OPUS_PCM_I16>>,
1041    #[cfg(not(feature = "heap"))]
1042    w_silk_out: FixedVec<f32, OPUS_SUBFRAME_SCRATCH>,
1043    #[cfg(feature = "heap")]
1044    w_silk_out: Box<FixedVec<f32, OPUS_SUBFRAME_SCRATCH>>,
1045    #[cfg(not(feature = "heap"))]
1046    w_pcm_resampled: FixedVec<i16, OPUS_SUBFRAME_SCRATCH>,
1047    #[cfg(feature = "heap")]
1048    w_pcm_resampled: Box<FixedVec<i16, OPUS_SUBFRAME_SCRATCH>>,
1049    #[cfg(not(feature = "heap"))]
1050    w_celt_planar: FixedVec<f32, OPUS_SUBFRAME_SCRATCH>,
1051    #[cfg(feature = "heap")]
1052    w_celt_planar: Box<FixedVec<f32, OPUS_SUBFRAME_SCRATCH>>,
1053    #[cfg(not(feature = "heap"))]
1054    w_celt_out: FixedVec<f32, OPUS_SUBFRAME_SCRATCH>,
1055    #[cfg(feature = "heap")]
1056    w_celt_out: Box<FixedVec<f32, OPUS_SUBFRAME_SCRATCH>>,
1057
1058    /// Tail of the previous frame's output, used for smooth_fade at mode
1059    /// transitions (libopus pcm_transition + smooth_fade).
1060    #[cfg(not(feature = "heap"))]
1061    prev_pcm_tail: FixedVec<f32, OPUS_PCM_TAIL>,
1062    #[cfg(feature = "heap")]
1063    prev_pcm_tail: Box<FixedVec<f32, OPUS_PCM_TAIL>>,
1064}
1065
1066impl OpusDecoder {
1067    pub fn new(sampling_rate: i32, channels: usize) -> Result<Self, &'static str> {
1068        if ![8000, 12000, 16000, 24000, 48000].contains(&sampling_rate) {
1069            return Err("Invalid sampling rate");
1070        }
1071        if ![1, 2].contains(&channels) {
1072            return Err("Invalid number of channels");
1073        }
1074
1075        let mode = modes::default_mode();
1076        #[cfg(feature = "heap")]
1077        let celt_dec = Box::new(CeltDecoder::new(mode, channels, sampling_rate));
1078        #[cfg(not(feature = "heap"))]
1079        let celt_dec = CeltDecoder::new(mode, channels, sampling_rate);
1080
1081        #[cfg(feature = "heap")]
1082        let mut silk_dec = Box::new(silk::dec_api::SilkDecoder::new());
1083        #[cfg(not(feature = "heap"))]
1084        let mut silk_dec = silk::dec_api::SilkDecoder::new();
1085        silk_dec.init(sampling_rate.min(16000), channels as i32);
1086        silk_dec.channel_state[0].fs_api_hz = sampling_rate;
1087
1088        Ok(Self {
1089            celt_dec,
1090            silk_dec,
1091            sampling_rate,
1092            channels,
1093            prev_mode: None,
1094            prev_redundancy: false,
1095            frame_size: 0,
1096            bandwidth: Bandwidth::Auto,
1097            stream_channels: channels,
1098            silk_resampler: silk::resampler::SilkResampler::default(),
1099            silk_resampler_2: silk::resampler::SilkResampler::default(),
1100            prev_internal_rate: 0,
1101            hybrid_skip_celt: false,
1102
1103            #[cfg(not(feature = "heap"))]
1104            w_pcm_i16: FixedVec::from_value(0i16, 960 * channels),
1105            #[cfg(feature = "heap")]
1106            w_pcm_i16: Box::new(FixedVec::from_value(0i16, 960 * channels)),
1107
1108            #[cfg(not(feature = "heap"))]
1109            w_silk_out: FixedVec::from_value(0.0f32, OPUS_MAX_SUBFRAME * channels),
1110            #[cfg(feature = "heap")]
1111            w_silk_out: Box::new(FixedVec::from_value(0.0f32, OPUS_MAX_SUBFRAME * channels)),
1112            #[cfg(not(feature = "heap"))]
1113            w_pcm_resampled: FixedVec::from_value(0i16, OPUS_MAX_SUBFRAME * channels),
1114            #[cfg(feature = "heap")]
1115            w_pcm_resampled: Box::new(FixedVec::from_value(0i16, OPUS_MAX_SUBFRAME * channels)),
1116            #[cfg(not(feature = "heap"))]
1117            w_celt_planar: FixedVec::from_value(0.0f32, OPUS_MAX_SUBFRAME * channels),
1118            #[cfg(feature = "heap")]
1119            w_celt_planar: Box::new(FixedVec::from_value(0.0f32, OPUS_MAX_SUBFRAME * channels)),
1120            #[cfg(not(feature = "heap"))]
1121            w_celt_out: FixedVec::from_value(0.0f32, OPUS_MAX_SUBFRAME * channels),
1122            #[cfg(feature = "heap")]
1123            w_celt_out: Box::new(FixedVec::from_value(0.0f32, OPUS_MAX_SUBFRAME * channels)),
1124
1125            #[cfg(not(feature = "heap"))]
1126            prev_pcm_tail: FixedVec::from_value(0.0f32, 240 * channels),
1127            #[cfg(feature = "heap")]
1128            prev_pcm_tail: Box::new(FixedVec::from_value(0.0f32, 240 * channels)),
1129        })
1130    }
1131
1132    pub fn decode(
1133        &mut self,
1134        input: &[u8],
1135        frame_size: usize,
1136        output: &mut [f32],
1137    ) -> Result<usize, &'static str> {
1138        if input.is_empty() {
1139            return Err("Input packet empty");
1140        }
1141
1142        let toc = input[0];
1143        let mode = mode_from_toc(toc);
1144        let packet_channels = channels_from_toc(toc);
1145        let bandwidth = bandwidth_from_toc(toc);
1146        let frame_duration_ms = frame_duration_ms_from_toc(toc);
1147
1148        // A packet of 0 or 1 bytes (ToC only) is a lost/DTX frame. libopus
1149        // triggers PLC in this case (opus_decoder.c:315-321). We decode the
1150        // frame using the previous mode's concealment.
1151        let lost_frame = input.len() <= 1;
1152
1153        if packet_channels != self.channels {
1154            return Err("Channel count mismatch between packet and decoder");
1155        }
1156
1157        let code = toc & 0x03;
1158        let frame_count: usize;
1159        let frame_payloads: FixedVec<&[u8], OPUS_MAX_PACKET_FRAMES>;
1160
1161        match code {
1162            0 => {
1163                frame_count = 1;
1164                frame_payloads = FixedVec::from_slice(&[&input[1..]]);
1165            }
1166            1 => {
1167                frame_count = 2;
1168                let data_len = input.len() - 1;
1169                // RFC 6716 §3.2.1: code 1 carries two equal-size (CBR) frames,
1170                // so the payload length must be even. libopus rejects odd lengths.
1171                if data_len % 2 != 0 {
1172                    return Err("Code 1: payload length must be even");
1173                }
1174                let half = data_len / 2;
1175                if half == 0 {
1176                    return Err("Code 1: empty frame");
1177                }
1178                frame_payloads = FixedVec::from_slice(&[&input[1..1 + half], &input[1 + half..]]);
1179            }
1180            2 => {
1181                frame_count = 2;
1182                let data = &input[1..];
1183                if data.is_empty() {
1184                    return Err("Code 2 packet has no data");
1185                }
1186                let (first_len, header_size) = parse_frame_size(data)?;
1187                if header_size + first_len > data.len() {
1188                    return Err("Code 2: first frame size exceeds packet");
1189                }
1190                frame_payloads = FixedVec::from_slice(&[
1191                    &data[header_size..header_size + first_len],
1192                    &data[header_size + first_len..],
1193                ]);
1194            }
1195            3 => {
1196                if input.len() < 2 {
1197                    return Err("Code 3 packet too short");
1198                }
1199                let count_byte = input[1];
1200                let n_frames = (count_byte & 0x3F) as usize;
1201                if n_frames < 1 || n_frames > 48 {
1202                    return Err("Code 3: invalid frame count");
1203                }
1204                frame_count = n_frames;
1205                // Bit 6 = padding flag, bit 7 = VBR flag (RFC 6716 §3.2.1).
1206                let padding_flag = (count_byte & 0x40) != 0;
1207                let vbr = (count_byte & 0x80) != 0;
1208
1209                // Parse the optional padding length bytes that follow the count
1210                // byte. The padding *content* (pad_len bytes) lives at the end of
1211                // the packet and is not part of any frame.
1212                let mut ptr = 2usize;
1213                let mut pad_len = 0usize;
1214                if padding_flag {
1215                    loop {
1216                        if ptr >= input.len() {
1217                            return Err("Code 3: padding overflow");
1218                        }
1219                        let p = input[ptr] as usize;
1220                        ptr += 1;
1221                        if p == 255 {
1222                            pad_len += 254;
1223                        } else {
1224                            pad_len += p;
1225                            break;
1226                        }
1227                    }
1228                }
1229                if ptr + pad_len > input.len() {
1230                    return Err("Code 3: padding exceeds packet");
1231                }
1232                let payload_end = input.len() - pad_len;
1233                let payload = &input[ptr..payload_end];
1234
1235                let mut payloads: FixedVec<&[u8], OPUS_MAX_PACKET_FRAMES> = FixedVec::new();
1236                if frame_count == 1 {
1237                    // Single frame: the entire payload region is the frame, both
1238                    // for VBR and CBR (no length prefix is present).
1239                    payloads.push(payload);
1240                } else if vbr {
1241                    // VBR (V=1): per-frame lengths for all frames except the last,
1242                    // which takes the remaining bytes (RFC 6716 §3.2.1).
1243                    let mut cursor = 0usize;
1244                    for i in 0..frame_count {
1245                        if i + 1 < frame_count {
1246                            if cursor >= payload.len() {
1247                                return Err("Code 3: unexpected end in VBR header");
1248                            }
1249                            let (frame_len, header_bytes) = parse_frame_size(&payload[cursor..])?;
1250                            cursor += header_bytes;
1251                            if cursor + frame_len > payload.len() {
1252                                return Err("Code 3: frame length exceeds packet");
1253                            }
1254                            payloads.push(&payload[cursor..cursor + frame_len]);
1255                            cursor += frame_len;
1256                        } else {
1257                            // Last frame: remaining bytes, no length prefix.
1258                            if cursor > payload.len() {
1259                                return Err("Code 3: no data for last frame");
1260                            }
1261                            payloads.push(&payload[cursor..]);
1262                        }
1263                    }
1264                } else {
1265                    // CBR (V=0): remaining bytes are split equally into M frames
1266                    // (RFC 6716 §3.2.1: "the remaining bytes are split into M
1267                    // equal chunks").
1268                    if payload.len() % frame_count != 0 {
1269                        return Err("Code 3 CBR: payload not divisible by frame count");
1270                    }
1271                    let frame_len = payload.len() / frame_count;
1272                    for i in 0..frame_count {
1273                        payloads.push(&payload[i * frame_len..(i + 1) * frame_len]);
1274                    }
1275                }
1276                frame_payloads = payloads;
1277            }
1278            _ => unreachable!(),
1279        }
1280
1281        self.frame_size = frame_size;
1282        self.bandwidth = bandwidth;
1283        self.stream_channels = packet_channels;
1284
1285        // Derive the actual per-frame sample count from the TOC, not from the
1286        // caller's frame_size. This prevents panics in bands.rs/celt.rs when
1287        // the caller passes a mismatched frame_size (issue #7 sub-item 1):
1288        // the internal decoders always get the correct geometry.
1289        let toc_frame_size = frame_samples_from_toc(toc, self.sampling_rate)
1290            .ok_or("Invalid TOC for sampling rate")?;
1291        let decoded_total = toc_frame_size * frame_count;
1292        if frame_size < decoded_total {
1293            return Err("frame_size too small for packet");
1294        }
1295        if output.len() < decoded_total * self.channels {
1296            return Err("Output buffer too small for packet");
1297        }
1298        // Zero-fill any extra space the caller provided beyond what the packet
1299        // actually produces, so stale data is never left in the buffer.
1300        if output.len() > decoded_total * self.channels {
1301            for v in &mut output[decoded_total * self.channels..] {
1302                *v = 0.0;
1303            }
1304        }
1305        let sub_frame_size = toc_frame_size;
1306        let sub_output_len = sub_frame_size * self.channels;
1307
1308        // Detect mode transition and reset CELT decoder state to prevent
1309        // cross-mode artifacts (libopus opus_decoder.c:602-604).
1310        // This is the primary fix for issue #8/#9 alignment divergence:
1311        // stale CELT MDCT/prefilter state at SILK↔CELT boundaries causes
1312        // discontinuities that accumulate across transitions.
1313        let mode_transition = match self.prev_mode {
1314            Some(prev) if prev != mode && !self.prev_redundancy => true,
1315            _ => false,
1316        };
1317        if mode_transition {
1318            self.celt_dec.reset_state();
1319        }
1320
1321        // Generate SILK PLC audio for the mode-transition bridge. libopus
1322        // synthesizes 5ms (F5) of pitch-extrapolated audio in the OLD mode
1323        // (opus_decoder.c:387-391) and crossfades it with the new frame. We
1324        // reuse the F5-sized prev_pcm_tail buffer for this bridge.
1325        let f5_bridge = self.sampling_rate as usize / 200; // F5 = Fs/200
1326        if mode_transition
1327            && f5_bridge > 0
1328            && matches!(
1329                self.prev_mode,
1330                Some(OpusMode::SilkOnly) | Some(OpusMode::Hybrid)
1331            )
1332            && self.prev_internal_rate > 0
1333        {
1334            let internal_rate = self.prev_internal_rate;
1335            let plc_internal_len = (10 * internal_rate / 1000) as usize;
1336            let mut plc_rc = RangeCoder::new_decoder(&[]);
1337            let mut plc_i16: FixedVec<i16, OPUS_PCM_I16> =
1338                FixedVec::from_value(0i16, plc_internal_len * self.channels);
1339            let n = self.silk_dec.decode(
1340                &mut plc_rc,
1341                &mut plc_i16,
1342                silk::decode_frame::FLAG_PACKET_LOST,
1343                true,
1344                10,
1345                internal_rate,
1346            );
1347            if n > 0 {
1348                let bridge_ch = f5_bridge * self.channels;
1349                let bridge_len = bridge_ch.min(self.prev_pcm_tail.len());
1350                if internal_rate == self.sampling_rate {
1351                    // No resampling: copy PLC samples directly (ch0 planar).
1352                    let n_us = n as usize;
1353                    for ch in 0..self.channels {
1354                        let src_base = ch * n_us;
1355                        for i in 0..(bridge_len / self.channels).min(n_us) {
1356                            let dst = i * self.channels + ch;
1357                            if dst < bridge_len {
1358                                self.prev_pcm_tail[dst] = plc_i16[src_base + i] as f32 / 32768.0;
1359                            }
1360                        }
1361                    }
1362                } else if self.silk_resampler.is_initialized() {
1363                    // Resample channel 0 to the API rate for the bridge.
1364                    let ratio = self.sampling_rate as f64 / internal_rate as f64;
1365                    let out_len = ((n as f64 * ratio) as usize).min(f5_bridge);
1366                    let n_us = n as usize;
1367                    let mut resampled: FixedVec<i16, OPUS_MAX_FRAME> =
1368                        FixedVec::from_value(0i16, out_len);
1369                    self.silk_resampler
1370                        .process(&mut resampled, &plc_i16[..n_us], n);
1371                    for i in 0..out_len {
1372                        if i < bridge_len / self.channels {
1373                            for ch in 0..self.channels {
1374                                self.prev_pcm_tail[i * self.channels + ch] =
1375                                    resampled[i] as f32 / 32768.0;
1376                            }
1377                        }
1378                    }
1379                }
1380            }
1381        }
1382
1383        // Track whether this packet uses Hybrid redundancy.
1384        let mut has_redundancy = false;
1385
1386        match mode {
1387            OpusMode::SilkOnly => {
1388                let internal_sample_rate = match bandwidth {
1389                    Bandwidth::Narrowband => 8000,
1390                    Bandwidth::Mediumband => 12000,
1391                    Bandwidth::Wideband => 16000,
1392                    _ => 16000,
1393                };
1394                let internal_frame_size =
1395                    (frame_duration_ms * internal_sample_rate / 1000) as usize;
1396
1397                if self.sampling_rate != internal_sample_rate
1398                    && internal_sample_rate != self.prev_internal_rate
1399                {
1400                    self.silk_resampler
1401                        .init(internal_sample_rate, self.sampling_rate);
1402                    self.silk_resampler_2
1403                        .init(internal_sample_rate, self.sampling_rate);
1404                }
1405                // Always track the SILK internal rate so the mode-transition
1406                // PLC bridge can be generated (even when no resampling is
1407                // needed, e.g. 16kHz decoder + SILK WB).
1408                self.prev_internal_rate = internal_sample_rate;
1409
1410                for (fi, payload) in frame_payloads.iter().enumerate() {
1411                    let mut rc = RangeCoder::new_decoder(payload);
1412                    let pcm_i16_len = internal_frame_size * self.channels;
1413                    debug_assert!(pcm_i16_len <= self.w_pcm_i16.len());
1414
1415                    let ret = {
1416                        let (silk_dec, pcm_i16) = (
1417                            state_mut(&mut self.silk_dec),
1418                            state_mut(&mut self.w_pcm_i16),
1419                        );
1420                        let lost_flag = if lost_frame {
1421                            silk::decode_frame::FLAG_PACKET_LOST
1422                        } else {
1423                            silk::decode_frame::FLAG_DECODE_NORMAL
1424                        };
1425                        silk_dec.decode(
1426                            &mut rc,
1427                            &mut pcm_i16[..pcm_i16_len],
1428                            lost_flag,
1429                            true,
1430                            frame_duration_ms,
1431                            internal_sample_rate,
1432                        )
1433                    };
1434
1435                    if ret < 0 {
1436                        return Err("SILK decoding failed");
1437                    }
1438
1439                    let decoded_samples = ret as usize;
1440                    let out_start = fi * sub_output_len;
1441
1442                    // SILK decoder outputs planar: ch0 at [0..fl], ch1 at [fl..2*fl].
1443                    if self.sampling_rate == internal_sample_rate {
1444                        let frames = decoded_samples.min(sub_frame_size);
1445                        for i in 0..frames {
1446                            for ch in 0..self.channels {
1447                                let src = if ch == 0 { i } else { internal_frame_size + i };
1448                                let v = self.w_pcm_i16[src] as f32 / 32768.0;
1449                                let idx = out_start + i * self.channels + ch;
1450                                if idx < output.len() {
1451                                    output[idx] = v;
1452                                }
1453                            }
1454                        }
1455                    } else {
1456                        let ratio = self.sampling_rate as f64 / internal_sample_rate as f64;
1457                        let out_len =
1458                            ((decoded_samples as f64 * ratio) as usize).min(sub_frame_size);
1459                        debug_assert!(out_len * self.channels <= self.w_pcm_resampled.len());
1460                        // Resample channel 0.
1461                        {
1462                            let (res, inp, out) = (
1463                                &mut self.silk_resampler,
1464                                state_ref(&self.w_pcm_i16),
1465                                state_mut(&mut self.w_pcm_resampled),
1466                            );
1467                            res.process(
1468                                &mut out[..out_len],
1469                                &inp[..decoded_samples],
1470                                decoded_samples as i32,
1471                            );
1472                        }
1473                        // Resample channel 1 (stereo only).
1474                        if self.channels == 2 {
1475                            let (res, inp, out) = (
1476                                &mut self.silk_resampler_2,
1477                                state_ref(&self.w_pcm_i16),
1478                                state_mut(&mut self.w_pcm_resampled),
1479                            );
1480                            res.process(
1481                                &mut out[out_len..2 * out_len],
1482                                &inp[internal_frame_size..internal_frame_size + decoded_samples],
1483                                decoded_samples as i32,
1484                            );
1485                        }
1486                        let frames = out_len.min(sub_frame_size);
1487                        for i in 0..frames {
1488                            for ch in 0..self.channels {
1489                                let v = self.w_pcm_resampled[ch * out_len + i] as f32 / 32768.0;
1490                                let idx = out_start + i * self.channels + ch;
1491                                if idx < output.len() {
1492                                    output[idx] = v;
1493                                }
1494                            }
1495                        }
1496                    }
1497                }
1498                decoded_total
1499            }
1500
1501            OpusMode::CeltOnly => {
1502                let celt_end_band = self.celt_end_band_from_toc(toc);
1503
1504                for (fi, payload) in frame_payloads.iter().enumerate() {
1505                    let mut rc = RangeCoder::new_decoder(payload);
1506                    let total_bits = (payload.len() * 8) as i32;
1507                    let needed = sub_frame_size * self.channels;
1508                    let out_start = fi * needed;
1509                    let out_end = (out_start + needed).min(output.len());
1510
1511                    if output.len() < out_end {
1512                        return Err("Output buffer too small");
1513                    }
1514
1515                    if self.channels == 1 {
1516                        self.celt_dec.decode_from_range_coder_with_band_range(
1517                            &mut rc,
1518                            total_bits,
1519                            sub_frame_size,
1520                            &mut output[out_start..out_end],
1521                            0,
1522                            celt_end_band,
1523                        );
1524                        for sample in &mut output[out_start..out_end] {
1525                            *sample = sample.clamp(-1.0, 1.0);
1526                        }
1527                    } else {
1528                        self.celt_dec.decode_from_range_coder_with_band_range(
1529                            &mut rc,
1530                            total_bits,
1531                            sub_frame_size,
1532                            &mut self.w_celt_planar[..needed],
1533                            0,
1534                            celt_end_band,
1535                        );
1536                        for i in 0..sub_frame_size {
1537                            for ch in 0..self.channels {
1538                                let idx = out_start + i * self.channels + ch;
1539                                output[idx] =
1540                                    self.w_celt_planar[ch * sub_frame_size + i].clamp(-1.0, 1.0);
1541                            }
1542                        }
1543                    }
1544                }
1545                decoded_total
1546            }
1547
1548            OpusMode::Hybrid => {
1549                let internal_sample_rate = 16000;
1550                let internal_frame_size =
1551                    (frame_duration_ms * internal_sample_rate / 1000) as usize;
1552                let celt_end_band = self.celt_end_band_from_toc(toc);
1553
1554                if self.sampling_rate != internal_sample_rate
1555                    && internal_sample_rate != self.prev_internal_rate
1556                {
1557                    self.silk_resampler
1558                        .init(internal_sample_rate, self.sampling_rate);
1559                    self.silk_resampler_2
1560                        .init(internal_sample_rate, self.sampling_rate);
1561                }
1562                self.prev_internal_rate = internal_sample_rate;
1563
1564                for (fi, payload) in frame_payloads.iter().enumerate() {
1565                    let mut rc = RangeCoder::new_decoder(payload);
1566                    let pcm_silk_i16_len = internal_frame_size * self.channels;
1567                    debug_assert!(pcm_silk_i16_len <= self.w_pcm_i16.len());
1568
1569                    let ret = {
1570                        let (silk_dec, pcm_i16) = (
1571                            state_mut(&mut self.silk_dec),
1572                            state_mut(&mut self.w_pcm_i16),
1573                        );
1574                        let lost_flag = if lost_frame {
1575                            silk::decode_frame::FLAG_PACKET_LOST
1576                        } else {
1577                            silk::decode_frame::FLAG_DECODE_NORMAL
1578                        };
1579                        silk_dec.decode(
1580                            &mut rc,
1581                            &mut pcm_i16[..pcm_silk_i16_len],
1582                            lost_flag,
1583                            true,
1584                            frame_duration_ms,
1585                            internal_sample_rate,
1586                        )
1587                    };
1588
1589                    if ret < 0 {
1590                        return Err("SILK decoding failed");
1591                    }
1592
1593                    let silk_out_len = sub_frame_size * self.channels;
1594                    self.w_silk_out[..silk_out_len].fill(0.0);
1595                    if ret > 0 {
1596                        let decoded_samples = ret as usize;
1597                        // SILK decoder outputs planar: ch0 at [0..fl], ch1 at [fl..2*fl].
1598                        if self.sampling_rate == internal_sample_rate {
1599                            let frames = decoded_samples.min(sub_frame_size);
1600                            for i in 0..frames {
1601                                for ch in 0..self.channels {
1602                                    let src = if ch == 0 { i } else { internal_frame_size + i };
1603                                    let v = self.w_pcm_i16[src] as f32 / 32768.0;
1604                                    let idx = i * self.channels + ch;
1605                                    if idx < silk_out_len {
1606                                        self.w_silk_out[idx] = v;
1607                                    }
1608                                }
1609                            }
1610                        } else {
1611                            let ratio = self.sampling_rate as f64 / internal_sample_rate as f64;
1612                            let out_len =
1613                                ((decoded_samples as f64 * ratio) as usize).min(sub_frame_size);
1614                            debug_assert!(out_len * self.channels <= self.w_pcm_resampled.len());
1615                            // Resample channel 0.
1616                            {
1617                                let (res, inp, out) = (
1618                                    &mut self.silk_resampler,
1619                                    state_ref(&self.w_pcm_i16),
1620                                    state_mut(&mut self.w_pcm_resampled),
1621                                );
1622                                res.process(
1623                                    &mut out[..out_len],
1624                                    &inp[..decoded_samples],
1625                                    decoded_samples as i32,
1626                                );
1627                            }
1628                            // Resample channel 1 (stereo only).
1629                            if self.channels == 2 {
1630                                let (res, inp, out) = (
1631                                    &mut self.silk_resampler_2,
1632                                    state_ref(&self.w_pcm_i16),
1633                                    state_mut(&mut self.w_pcm_resampled),
1634                                );
1635                                res.process(
1636                                    &mut out[out_len..2 * out_len],
1637                                    &inp[internal_frame_size
1638                                        ..internal_frame_size + decoded_samples],
1639                                    decoded_samples as i32,
1640                                );
1641                            }
1642                            let frames = out_len.min(sub_frame_size);
1643                            for i in 0..frames {
1644                                for ch in 0..self.channels {
1645                                    let v = self.w_pcm_resampled[ch * out_len + i] as f32 / 32768.0;
1646                                    let idx = i * self.channels + ch;
1647                                    if idx < silk_out_len {
1648                                        self.w_silk_out[idx] = v;
1649                                    }
1650                                }
1651                            }
1652                        }
1653                    }
1654
1655                    let total_bits = (payload.len() * 8) as i32;
1656                    let redundancy = rc.decode_bit_logp(12);
1657                    let skip_celt = if redundancy {
1658                        let _celt_to_silk = rc.decode_bit_logp(1);
1659                        has_redundancy = true;
1660                        // When redundancy is present, the redundant CELT frame
1661                        // provides the transition audio. We skip the main CELT
1662                        // decode for this sub-frame (the SILK output stands alone)
1663                        // — a simplified version of libopus's behaviour where the
1664                        // redundant frame is decoded separately and crossfaded.
1665                        true
1666                    } else {
1667                        false
1668                    };
1669
1670                    if skip_celt {
1671                        self.w_celt_out[..silk_out_len].fill(0.0);
1672                    } else {
1673                        let (celt_dec, celt_planar) = (
1674                            state_mut(&mut self.celt_dec),
1675                            state_mut(&mut self.w_celt_planar),
1676                        );
1677                        celt_dec.decode_from_range_coder_with_band_range(
1678                            &mut rc,
1679                            total_bits,
1680                            sub_frame_size,
1681                            &mut celt_planar[..silk_out_len],
1682                            17,
1683                            celt_end_band,
1684                        );
1685
1686                        if self.channels == 1 {
1687                            self.w_celt_out[..silk_out_len]
1688                                .copy_from_slice(&self.w_celt_planar[..silk_out_len]);
1689                        } else {
1690                            for i in 0..sub_frame_size {
1691                                for ch in 0..self.channels {
1692                                    self.w_celt_out[i * self.channels + ch] =
1693                                        self.w_celt_planar[ch * sub_frame_size + i];
1694                                }
1695                            }
1696                        }
1697                    }
1698
1699                    let out_start = fi * silk_out_len;
1700                    let total = silk_out_len.min(output.len() - out_start);
1701                    for j in 0..total {
1702                        output[out_start + j] =
1703                            (self.w_silk_out[j] + self.w_celt_out[j]).clamp(-1.0, 1.0);
1704                    }
1705                }
1706                decoded_total
1707            }
1708        };
1709
1710        // Apply PLC-style bridging at mode transitions (libopus
1711        // opus_decoder.c:660-679). The first F2_5 of the output is replaced
1712        // with the previous frame's tail (PLC bridge), and the next F2_5 is
1713        // crossfaded between the bridge and the new frame's CELT output.
1714        // F5 = Fs/200, F2_5 = Fs/400.
1715        let f2_5 = self.sampling_rate as usize / 400;
1716        let f5 = f2_5 * 2;
1717        if mode_transition && f5 > 0 && decoded_total >= f5 {
1718            let window = modes::default_mode().window;
1719            let inc = (48000 / self.sampling_rate) as usize;
1720            let f2_5_ch = f2_5 * self.channels;
1721            let f5_ch = f5 * self.channels;
1722            // First F2_5: pure bridging audio from previous frame's tail.
1723            output[..f2_5_ch].copy_from_slice(&self.prev_pcm_tail[..f2_5_ch]);
1724            // Next F2_5: crossfade bridge → new CELT output.
1725            let new_mid: FixedVec<f32, OPUS_PCM_TAIL> =
1726                FixedVec::from_slice(&output[f2_5_ch..f5_ch]);
1727            smooth_fade(
1728                &self.prev_pcm_tail[f2_5_ch..f5_ch],
1729                &new_mid,
1730                &mut output[f2_5_ch..f5_ch],
1731                f2_5,
1732                self.channels,
1733                window,
1734                inc,
1735            );
1736        }
1737
1738        // Save the tail of this frame for the next transition (F5 samples).
1739        let tail_len = f5 * self.channels;
1740        let out_total = decoded_total * self.channels;
1741        if out_total >= tail_len && tail_len <= self.prev_pcm_tail.len() {
1742            self.prev_pcm_tail[..tail_len]
1743                .copy_from_slice(&output[out_total - tail_len..out_total]);
1744        }
1745
1746        self.prev_mode = Some(mode);
1747        self.prev_redundancy = has_redundancy;
1748        Ok(decoded_total)
1749    }
1750}
1751
1752impl OpusDecoder {
1753    #[inline(always)]
1754    fn celt_end_band_from_toc(&self, toc: u8) -> usize {
1755        let mode = modes::default_mode();
1756        let top = mode.eff_ebands;
1757        if mode_from_toc(toc) == OpusMode::CeltOnly && toc >= 0x80 {
1758            const FROM_OPUS_TABLE: [u8; 16] = [
1759                0x80, 0x88, 0x90, 0x98, 0x40, 0x48, 0x50, 0x58, 0x20, 0x28, 0x30, 0x38, 0x00, 0x08,
1760                0x10, 0x18,
1761            ];
1762            let idx = ((toc >> 3) - 16) as usize;
1763            let data0 = FROM_OPUS_TABLE[idx] | (toc & 0x7);
1764            let trim = (data0 >> 5) as usize;
1765            return top.saturating_sub(2 * trim).max(1);
1766        }
1767        top
1768    }
1769}
1770
1771fn frame_rate_from_params(sampling_rate: i32, frame_size: usize) -> Option<i32> {
1772    let frame_size = frame_size as i32;
1773    if frame_size == 0 || sampling_rate % frame_size != 0 {
1774        return None;
1775    }
1776    Some(sampling_rate / frame_size)
1777}
1778
1779fn gen_toc(mode: OpusMode, frame_rate: i32, bandwidth: Bandwidth, channels: usize) -> u8 {
1780    let mut rate = frame_rate;
1781    let mut period = 0;
1782    while rate < 400 {
1783        rate <<= 1;
1784        period += 1;
1785    }
1786
1787    let mut toc = match mode {
1788        OpusMode::SilkOnly => {
1789            let bw = (bandwidth as i32 - Bandwidth::Narrowband as i32) << 5;
1790            let per = (period - 2) << 3;
1791            (bw | per) as u8
1792        }
1793        OpusMode::CeltOnly => {
1794            let mut tmp = bandwidth as i32 - Bandwidth::Mediumband as i32;
1795            if tmp < 0 {
1796                tmp = 0;
1797            }
1798            let per = period << 3;
1799            (0x80 | (tmp << 5) | per) as u8
1800        }
1801        OpusMode::Hybrid => {
1802            let base_config = if bandwidth == Bandwidth::Superwideband {
1803                12
1804            } else {
1805                14
1806            };
1807            let period_offset = if frame_rate >= 100 { 0 } else { 1 };
1808            ((base_config + period_offset) << 3) as u8
1809        }
1810    };
1811
1812    if channels == 2 {
1813        toc |= 0x04;
1814    }
1815    toc
1816}
1817
1818fn mode_from_toc(toc: u8) -> OpusMode {
1819    if toc & 0x80 != 0 {
1820        OpusMode::CeltOnly
1821    } else if toc & 0x60 == 0x60 {
1822        OpusMode::Hybrid
1823    } else {
1824        OpusMode::SilkOnly
1825    }
1826}
1827
1828fn bandwidth_from_toc(toc: u8) -> Bandwidth {
1829    let mode = mode_from_toc(toc);
1830    match mode {
1831        OpusMode::SilkOnly => {
1832            let bw_bits = (toc >> 5) & 0x03;
1833            match bw_bits {
1834                0 => Bandwidth::Narrowband,
1835                1 => Bandwidth::Mediumband,
1836                2 => Bandwidth::Wideband,
1837                _ => Bandwidth::Wideband,
1838            }
1839        }
1840        OpusMode::Hybrid => {
1841            let bw_bit = (toc >> 4) & 0x01;
1842            if bw_bit == 0 {
1843                Bandwidth::Superwideband
1844            } else {
1845                Bandwidth::Fullband
1846            }
1847        }
1848        OpusMode::CeltOnly => {
1849            let bw_bits = (toc >> 5) & 0x03;
1850            match bw_bits {
1851                0 => Bandwidth::Mediumband,
1852                1 => Bandwidth::Wideband,
1853                2 => Bandwidth::Superwideband,
1854                3 => Bandwidth::Fullband,
1855                _ => Bandwidth::Fullband,
1856            }
1857        }
1858    }
1859}
1860
1861fn frame_duration_ms_from_toc(toc: u8) -> i32 {
1862    let mode = mode_from_toc(toc);
1863    match mode {
1864        OpusMode::SilkOnly => {
1865            let config = (toc >> 3) & 0x03;
1866            match config {
1867                0 => 10,
1868                1 => 20,
1869                2 => 40,
1870                3 => 60,
1871                _ => 20,
1872            }
1873        }
1874        OpusMode::Hybrid => {
1875            let config = (toc >> 3) & 0x01;
1876            if config == 0 { 10 } else { 20 }
1877        }
1878        OpusMode::CeltOnly => {
1879            let config = (toc >> 3) & 0x03;
1880            match config {
1881                0 => 2,
1882                1 => 5,
1883                2 => 10,
1884                3 => 20,
1885                _ => 20,
1886            }
1887        }
1888    }
1889}
1890
1891/// Compute the per-frame sample count implied by the TOC byte at a given
1892/// sampling rate. For CELT this uses the frame-rate derivation (which handles
1893/// the 2.5 ms case correctly, unlike integer millisecond arithmetic).
1894fn frame_samples_from_toc(toc: u8, sampling_rate: i32) -> Option<usize> {
1895    let mode = mode_from_toc(toc);
1896    match mode {
1897        OpusMode::CeltOnly => {
1898            let period = ((toc >> 3) & 0x03) as i32;
1899            let frame_rate = 400 >> period;
1900            if frame_rate == 0 || sampling_rate % frame_rate != 0 {
1901                return None;
1902            }
1903            Some((sampling_rate / frame_rate) as usize)
1904        }
1905        OpusMode::SilkOnly | OpusMode::Hybrid => {
1906            let duration_ms = frame_duration_ms_from_toc(toc);
1907            Some((sampling_rate as i64 * duration_ms as i64 / 1000) as usize)
1908        }
1909    }
1910}
1911
1912fn channels_from_toc(toc: u8) -> usize {
1913    if toc & 0x04 != 0 { 2 } else { 1 }
1914}
1915
1916/// Crossfade two signals using a squared-sine window (libopus smooth_fade).
1917/// `window` is the 120-sample CELT window at 48 kHz; `inc` = 48000/Fs strides it.
1918fn smooth_fade(
1919    in1: &[f32],
1920    in2: &[f32],
1921    out: &mut [f32],
1922    overlap: usize,
1923    channels: usize,
1924    window: &[f32],
1925    inc: usize,
1926) {
1927    for c in 0..channels {
1928        for i in 0..overlap {
1929            let wi = i * inc;
1930            if wi >= window.len() {
1931                break;
1932            }
1933            let w = window[wi] * window[wi];
1934            out[i * channels + c] = w * in2[i * channels + c] + (1.0 - w) * in1[i * channels + c];
1935        }
1936    }
1937}
1938
1939/// Parse an Opus frame length per RFC 6716 §3.2.1, identical to libopus
1940/// `parse_size()`:
1941///   - `0`: no frame (DTX / lost packet)
1942///   - `1..=251`: length of the frame in bytes (one byte consumed)
1943///   - `252..=255`: a second byte is read; length = `second*4 + first`
1944///
1945/// Returns `(length, bytes_consumed)`.
1946fn parse_frame_size(data: &[u8]) -> Result<(usize, usize), &'static str> {
1947    let first = *data.first().ok_or("truncated frame length")? as usize;
1948    if first < 252 {
1949        Ok((first, 1))
1950    } else {
1951        let second = *data.get(1).ok_or("truncated frame length")? as usize;
1952        Ok((second * 4 + first, 2))
1953    }
1954}
1955
1956#[cfg(all(test, feature = "std"))]
1957mod tests {
1958    use super::*;
1959
1960    fn frame_size_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 => {
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            OpusMode::Hybrid => {
1976                let duration_ms = frame_duration_ms_from_toc(toc);
1977                Some((sampling_rate as i64 * duration_ms as i64 / 1000) as usize)
1978            }
1979        }
1980    }
1981
1982    #[test]
1983    fn gen_toc_matches_celt_reference_values() {
1984        let sampling_rate = 48_000;
1985        let cases = [
1986            (120usize, 0xE0u8),
1987            (240usize, 0xE8u8),
1988            (480usize, 0xF0u8),
1989            (960usize, 0xF8u8),
1990        ];
1991
1992        for (frame_size, expected_toc) in cases {
1993            let frame_rate = frame_rate_from_params(sampling_rate, frame_size).unwrap();
1994            let toc = gen_toc(OpusMode::CeltOnly, frame_rate, Bandwidth::Fullband, 1);
1995            assert_eq!(
1996                toc, expected_toc,
1997                "frame_size {} expected TOC {:02X} got {:02X}",
1998                frame_size, expected_toc, toc
1999            );
2000            let decoded_size = frame_size_from_toc(toc, sampling_rate).unwrap();
2001            assert_eq!(decoded_size, frame_size);
2002        }
2003
2004        let stereo_toc = gen_toc(
2005            OpusMode::CeltOnly,
2006            frame_rate_from_params(sampling_rate, 960).unwrap(),
2007            Bandwidth::Fullband,
2008            2,
2009        );
2010        assert_eq!(channels_from_toc(stereo_toc), 2);
2011    }
2012
2013    #[test]
2014    fn test_celt_decoder_large_frame_sizes() {
2015        let sampling_rate = 48000;
2016        let channels = 1;
2017
2018        let mut decoder = OpusDecoder::new(sampling_rate, channels).unwrap();
2019
2020        let frame_sizes = [120, 240, 480, 960];
2021
2022        for frame_size in frame_sizes {
2023            let toc = gen_toc(
2024                OpusMode::CeltOnly,
2025                frame_rate_from_params(sampling_rate, frame_size).unwrap(),
2026                Bandwidth::Fullband,
2027                channels,
2028            );
2029            let packet = [toc, 0, 0, 0, 0];
2030
2031            let mut output = vec![0.0f32; frame_size * channels];
2032
2033            let _ = decoder.decode(&packet, frame_size, &mut output);
2034        }
2035
2036        let channels = 2;
2037        let mut decoder = OpusDecoder::new(sampling_rate, channels).unwrap();
2038
2039        for frame_size in frame_sizes {
2040            let toc = gen_toc(
2041                OpusMode::CeltOnly,
2042                frame_rate_from_params(sampling_rate, frame_size).unwrap(),
2043                Bandwidth::Fullband,
2044                channels,
2045            );
2046            let packet = [toc, 0, 0, 0, 0];
2047
2048            let mut output = vec![0.0f32; frame_size * channels];
2049            let _ = decoder.decode(&packet, frame_size, &mut output);
2050        }
2051    }
2052
2053    #[test]
2054    fn test_celt_decoder_edge_case_frame_sizes() {
2055        let sampling_rate = 48000;
2056        let channels = 1;
2057        let mut decoder = OpusDecoder::new(sampling_rate, channels).unwrap();
2058
2059        let edge_sizes = [2048, 2167, 2168, 2169, 2880, 3072];
2060
2061        for frame_size in edge_sizes {
2062            let mut output = vec![0.0f32; frame_size * channels];
2063
2064            let _ = decoder.decode(&[0x80, 0, 0, 0], frame_size, &mut output);
2065        }
2066    }
2067
2068    // Regression test for: "index out of bounds: the len is 48 but the index is 119"
2069    // Root cause: frame_size=48 at 48kHz gives frame_rate=1000, which is not a valid
2070    // Hybrid-mode frame rate but was not validated.  CELT's lm-search then silently
2071    // fell back to lm=0, computed n2=120, and wrote output[119] into a 48-element
2072    // slice.  Triggered via G.729-decoded PCM (8kHz) passed to a 48kHz Opus encoder
2073    // without proper resampling, so the encoder received 48 samples instead of 480.
2074    #[test]
2075    fn test_invalid_small_frame_size_returns_error_not_panic() {
2076        let mut enc = OpusEncoder::new(48000, 2, Application::Voip).unwrap();
2077        enc.bitrate_bps = 64000;
2078        enc.complexity = 5;
2079        enc.use_cbr = true;
2080
2081        // 48 samples at 48kHz = 1ms → frame_rate=1000, invalid for Hybrid mode.
2082        let input = vec![0.0f32; 48 * 2]; // stereo interleaved
2083        let mut output = vec![0u8; 256];
2084
2085        let result = enc.encode(&input, 48, &mut output);
2086        assert!(
2087            result.is_err(),
2088            "encode with invalid frame_size=48 should return Err, not panic"
2089        );
2090    }
2091
2092    // Also verify that the Audio application path (always Hybrid at 48 kHz) rejects
2093    // the same bad frame size.
2094    #[test]
2095    fn test_invalid_small_frame_size_audio_application_returns_error() {
2096        let mut enc = OpusEncoder::new(48000, 1, Application::Audio).unwrap();
2097        let input = vec![0.0f32; 48];
2098        let mut output = vec![0u8; 256];
2099
2100        let result = enc.encode(&input, 48, &mut output);
2101        assert!(
2102            result.is_err(),
2103            "Audio/48kHz encoder with frame_size=48 should return Err"
2104        );
2105    }
2106}