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;
16/// Multistream decode uses `Vec`-based plumbing, so it is only available
17/// with the `std` (default) feature set; the core codec stays `no_std`
18/// + no-`alloc` capable (see `split_self_delimited` docs).
19#[cfg(feature = "std")]
20pub mod multistream;
21pub mod pitch;
22pub mod pvq;
23pub mod quant_bands;
24pub mod range_coder;
25pub mod rate;
26pub mod silk;
27
28pub use silk::{SilkResampler, SilkResamplerDown1_3, SilkResamplerDown1_6};
29
30use crate::fixedvec::FixedVec;
31pub use celt::{CeltDecoder, CeltEncoder};
32use hp_cutoff::{dc_reject_float, hp_cutoff, hp_cutoff_float, hp_cutoff_i16};
33use range_coder::RangeCoder;
34use silk::control_codec::{silk_control_encoder, silk_setup_lbrr};
35use silk::enc_api::{silk_encode, silk_encode_prefill};
36use silk::init_encoder::silk_init_encoder;
37use silk::lin2log::silk_lin2log;
38use silk::log2lin::silk_log2lin;
39use silk::macros::*;
40use silk::structs::SilkEncoderState;
41
42// --- Heap-free buffer capacity constants (worst case: 2 channels). ---
43const OPUS_MAX_CHANNELS: usize = 2;
44/// Largest API frame in samples/channel (120 ms @ 48 kHz = 5760). Used by the
45/// encoder's per-frame input buffers (sized `frame_size * channels`).
46const OPUS_MAX_FRAME: usize = 5760;
47/// Largest *single-frame* samples/channel (60 ms @ 48 kHz = 2880). The decoder's
48/// staging buffers hold one sub-frame at a time, so they're sized to this — not
49/// the full packet. (Halves the decoder footprint vs. a naive 5760/channel.)
50const OPUS_MAX_SUBFRAME: usize = 2880;
51/// Decoder per-sub-frame staging cap: `OPUS_MAX_SUBFRAME * max_channels`.
52const OPUS_SUBFRAME_SCRATCH: usize = OPUS_MAX_SUBFRAME * OPUS_MAX_CHANNELS;
53/// High-pass filter state memory (`channels * 2`).
54const OPUS_HP_MEM: usize = OPUS_MAX_CHANNELS * 2;
55/// Decoder `w_pcm_i16` cap (`960 * max_channels`).
56const OPUS_PCM_I16: usize = 960 * OPUS_MAX_CHANNELS;
57/// Decoder `prev_pcm_tail` cap (`240 * max_channels`).
58const OPUS_PCM_TAIL: usize = 240 * OPUS_MAX_CHANNELS;
59/// Max number of frames encoded in one Opus packet (RFC 6716 caps at 48 for
60/// 2.5 ms codes in a 120 ms packet).
61const OPUS_MAX_PACKET_FRAMES: usize = 48;
62/// RFC 6716 §3.1: a single Opus packet carries at most 1276 bytes of data.
63const OPUS_MAX_PACKET_BYTES: usize = 1276;
64/// C `OpusEncoder.delay_buffer[MAX_ENCODER_BUFFER*2]` (480 samples * 2 ch).
65/// Holds the delay-compensation ring feeding the CELT encoder.
66const OPUS_DELAY_BUF: usize = 480 * OPUS_MAX_CHANNELS;
67/// CELT/hybrid input staging cap: `(delay_compensation + frame)*channels`.
68/// Worst case 60 ms stereo @ 48 kHz: (192 + 2880) * 2 = 6144.
69const OPUS_PCM_BUF: usize = (192 + OPUS_MAX_SUBFRAME) * OPUS_MAX_CHANNELS;
70
71#[derive(Debug, Clone, Copy, PartialEq, Eq)]
72pub enum Application {
73    Voip = 2048,
74    Audio = 2049,
75    RestrictedLowDelay = 2051,
76}
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq)]
79pub enum Bandwidth {
80    Auto = -1000,
81    Narrowband = 1101,
82    Mediumband = 1102,
83    Wideband = 1103,
84    Superwideband = 1104,
85    Fullband = 1105,
86}
87
88#[derive(Debug, Clone, Copy, PartialEq, Eq)]
89enum OpusMode {
90    SilkOnly,
91    Hybrid,
92    CeltOnly,
93}
94
95pub struct OpusEncoder {
96    #[cfg(not(feature = "heap"))]
97    celt_enc: CeltEncoder,
98    #[cfg(feature = "heap")]
99    celt_enc: Box<CeltEncoder>,
100    #[cfg(not(feature = "heap"))]
101    silk_enc: SilkEncoderState,
102    #[cfg(feature = "heap")]
103    silk_enc: Box<SilkEncoderState>,
104    application: Application,
105    sampling_rate: i32,
106    channels: usize,
107    bandwidth: Bandwidth,
108    pub bitrate_bps: i32,
109    pub complexity: i32,
110    pub use_cbr: bool,
111
112    pub use_inband_fec: bool,
113
114    pub packet_loss_perc: i32,
115    silk_initialized: bool,
116    mode: OpusMode,
117    prev_enc_mode: Option<OpusMode>,
118    /// When set, `encode_impl` uses this mode instead of running the
119    /// mode-selection heuristic. Used to keep all sub-frames of a multiframe
120    /// packet in the same mode (libopus `user_forced_mode`).
121    forced_mode: Option<OpusMode>,
122
123    variable_hp_smth2_q15: i32,
124    hp_mem: FixedVec<i32, OPUS_HP_MEM>,
125
126    #[cfg(not(feature = "heap"))]
127    buf_filtered: FixedVec<i16, OPUS_MAX_FRAME>,
128    #[cfg(feature = "heap")]
129    buf_filtered: Box<FixedVec<i16, OPUS_MAX_FRAME>>,
130    #[cfg(not(feature = "heap"))]
131    buf_silk_input: FixedVec<i16, OPUS_MAX_FRAME>,
132    #[cfg(feature = "heap")]
133    buf_silk_input: Box<FixedVec<i16, OPUS_MAX_FRAME>>,
134    #[cfg(not(feature = "heap"))]
135    buf_stereo_mid: FixedVec<i16, OPUS_MAX_FRAME>,
136    #[cfg(feature = "heap")]
137    buf_stereo_mid: Box<FixedVec<i16, OPUS_MAX_FRAME>>,
138    #[cfg(not(feature = "heap"))]
139    buf_stereo_side: FixedVec<i16, OPUS_MAX_FRAME>,
140    #[cfg(feature = "heap")]
141    buf_stereo_side: Box<FixedVec<i16, OPUS_MAX_FRAME>>,
142    #[cfg(not(feature = "heap"))]
143    buf_celt_input: FixedVec<f32, OPUS_MAX_FRAME>,
144    #[cfg(feature = "heap")]
145    buf_celt_input: Box<FixedVec<f32, OPUS_MAX_FRAME>>,
146    /// C `delay_buffer[MAX_ENCODER_BUFFER*2]`: delay-compensation ring feeding
147    /// the CELT encoder (see `encoder_buffer` / `delay_compensation`).
148    #[cfg(not(feature = "heap"))]
149    delay_buffer: FixedVec<f32, OPUS_DELAY_BUF>,
150    #[cfg(feature = "heap")]
151    delay_buffer: Box<FixedVec<f32, OPUS_DELAY_BUF>>,
152    /// Interleaved `pcm_buf` staging: `delay prefix + filtered frame` exactly
153    /// like C `opus_encode_frame_native()` builds it before CELT encoding.
154    #[cfg(not(feature = "heap"))]
155    buf_celt_pcm: FixedVec<f32, OPUS_PCM_BUF>,
156    #[cfg(feature = "heap")]
157    buf_celt_pcm: Box<FixedVec<f32, OPUS_PCM_BUF>>,
158    /// f32 staging of the current frame for the CELT/Hybrid float pipeline
159    /// when the caller feeds `encode_i16()` (issue #28).
160    #[cfg(not(feature = "heap"))]
161    buf_f32_frame: FixedVec<f32, OPUS_MAX_FRAME>,
162    #[cfg(feature = "heap")]
163    buf_f32_frame: Box<FixedVec<f32, OPUS_MAX_FRAME>>,
164    /// C `encoder_buffer` (= Fs/100): samples of ring history kept between
165    /// frames (0 for restricted-latency applications).
166    encoder_buffer: usize,
167    /// C `delay_compensation` (= Fs/250): 4 ms lookahead prefix prepended to
168    /// each CELT frame (0 for restricted-latency applications).
169    delay_compensation: usize,
170    /// Float filter state for `dc_reject_float` / `hp_cutoff_float`
171    /// (C `st->hp_mem[4]`, opus_val32 in the float build).
172    hp_mem_float: [f32; 4],
173    /// Encoder-direction SILK input resampler (C `resampler_state` with
174    /// `forEnc = 1`): API rate -> SILK internal rate, carrying the
175    /// `delay_matrix_enc` input delay and the private down-FIR chain. One
176    /// instance per signal (mid and side).
177    silk_resampler_enc: silk::resampler::SilkResampler,
178    silk_resampler_enc_side: silk::resampler::SilkResampler,
179
180    rc: RangeCoder,
181}
182
183/// Format-agnostic PCM input for `encode_impl` (issue #28): `F32` is the
184/// classic float path; `I16` reaches the SILK integer pipeline directly
185/// and is converted to f32 only where the CELT pipeline requires it.
186#[derive(Clone, Copy)]
187enum PcmInput<'a> {
188    F32(&'a [f32]),
189    I16(&'a [i16]),
190}
191
192impl PcmInput<'_> {
193    fn len(&self) -> usize {
194        match self {
195            PcmInput::F32(s) => s.len(),
196            PcmInput::I16(s) => s.len(),
197        }
198    }
199}
200
201fn compute_equiv_rate(
202    bitrate: i32,
203    channels: usize,
204    frame_rate: i32,
205    vbr: bool,
206    complexity: i32,
207    loss: i32,
208) -> i32 {
209    let mut equiv = bitrate;
210    if frame_rate > 50 {
211        equiv -= (40 * channels as i32 + 20) * (frame_rate - 50);
212    }
213    if !vbr {
214        equiv -= equiv / 12;
215    }
216    equiv = equiv * (90 + complexity) / 100;
217    if loss > 0 {
218        equiv -= equiv * loss / (12 * loss + 20);
219    }
220    equiv
221}
222
223/// `fec_thresholds` (opus_encoder.c:160): LBRR rate threshold and hysteresis
224/// per bandwidth, starting at narrowband.
225const FEC_THRESHOLDS: [i32; 10] = [
226    12000, 1000, // NB
227    14000, 1000, // MB
228    16000, 1000, // WB
229    20000, 1000, // SWB
230    22000, 1000, // FB
231];
232
233fn bandwidth_fec_index(bw: Bandwidth) -> usize {
234    match bw {
235        Bandwidth::Narrowband => 0,
236        Bandwidth::Mediumband => 1,
237        Bandwidth::Wideband => 2,
238        Bandwidth::Superwideband => 3,
239        Bandwidth::Fullband | Bandwidth::Auto => 4,
240    }
241}
242
243fn bandwidth_from_fec_index(idx: usize) -> Bandwidth {
244    match idx {
245        0 => Bandwidth::Narrowband,
246        1 => Bandwidth::Mediumband,
247        2 => Bandwidth::Wideband,
248        3 => Bandwidth::Superwideband,
249        _ => Bandwidth::Fullband,
250    }
251}
252
253/// Port of opus_encoder.c `decide_fec()`: gate in-band FEC on the packet loss
254/// setting and on whether the equivalent rate can afford LBRR at the current
255/// bandwidth. With loss > 5%, the bandwidth is reduced until FEC fits; the
256/// caller's per-frame bandwidth is updated accordingly.
257fn decide_fec(
258    use_in_band_fec: i32,
259    packet_loss_perc: i32,
260    last_fec: i32,
261    mode: OpusMode,
262    bandwidth: &mut Bandwidth,
263    rate: i32,
264) -> i32 {
265    if use_in_band_fec == 0 || packet_loss_perc == 0 || mode == OpusMode::CeltOnly {
266        return 0;
267    }
268    let orig_bandwidth = *bandwidth;
269    loop {
270        let idx = bandwidth_fec_index(*bandwidth);
271        let mut lbrr_rate_thres_bps = FEC_THRESHOLDS[2 * idx];
272        let hysteresis = FEC_THRESHOLDS[2 * idx + 1];
273        if last_fec == 1 {
274            lbrr_rate_thres_bps -= hysteresis;
275        }
276        if last_fec == 0 {
277            lbrr_rate_thres_bps += hysteresis;
278        }
279        // silk_SMULWB(threshold * (125 - min(loss, 25)), 0.01 in Q16)
280        lbrr_rate_thres_bps = silk_smulwb(
281            lbrr_rate_thres_bps * (125 - packet_loss_perc.min(25)),
282            655,
283        );
284        // If loss <= 5%, we look at whether we have enough rate to enable FEC.
285        // If loss > 5%, we decrease the bandwidth until we can enable FEC.
286        if rate > lbrr_rate_thres_bps {
287            return 1;
288        } else if packet_loss_perc <= 5 {
289            return 0;
290        } else if bandwidth_fec_index(*bandwidth) > 0 {
291            let lower = bandwidth_fec_index(*bandwidth) - 1;
292            *bandwidth = bandwidth_from_fec_index(lower);
293        } else {
294            break;
295        }
296    }
297    // Couldn't find any bandwidth to enable FEC, keep original bandwidth.
298    *bandwidth = orig_bandwidth;
299    0
300}
301
302fn compute_mode_threshold(
303    application: Application,
304    channels: usize,
305    prev_was_celt: bool,
306    has_prev_mode: bool,
307    voice_est: i32,
308) -> i32 {
309    let mode_voice = if channels == 1 { 64000 } else { 44000 };
310    let mode_music = 10000;
311
312    let diff = mode_voice - mode_music;
313    let offset = (voice_est * voice_est * diff) >> 14;
314    let mut threshold = mode_music + offset;
315
316    if application == Application::Voip {
317        threshold += 8000;
318    }
319
320    if has_prev_mode {
321        if prev_was_celt {
322            threshold -= 4000;
323        } else {
324            threshold += 4000;
325        }
326    }
327
328    if application == Application::RestrictedLowDelay {
329        threshold = 0;
330    }
331
332    threshold
333}
334
335fn compute_silk_rate_for_hybrid(rate_bps: i32, frame20ms: bool) -> i32 {
336    const RATE_TABLE: &[(i32, i32, i32)] = &[
337        (0, 0, 0),
338        (12000, 10000, 10000),
339        (16000, 13500, 13500),
340        (20000, 16000, 16000),
341        (24000, 18000, 18000),
342        (32000, 22000, 22000),
343        (64000, 38000, 38000),
344    ];
345    let n = RATE_TABLE.len();
346    let mut i = 1;
347    while i < n && RATE_TABLE[i].0 <= rate_bps {
348        i += 1;
349    }
350    if i == n {
351        let (x_last, r10_last, r20_last) = RATE_TABLE[n - 1];
352        let base = if frame20ms { r20_last } else { r10_last };
353        base + (rate_bps - x_last) / 2
354    } else {
355        let (x0, lo10, lo20) = RATE_TABLE[i - 1];
356        let (x1, hi10, hi20) = RATE_TABLE[i];
357        let (lo, hi) = if frame20ms {
358            (lo20, hi20)
359        } else {
360            (lo10, hi10)
361        };
362        (lo * (x1 - rate_bps) + hi * (rate_bps - x0)) / (x1 - x0)
363    }
364}
365
366#[cfg(all(test, feature = "std"))]
367mod silk_rate_tests {
368    use super::compute_silk_rate_for_hybrid;
369
370    #[test]
371    fn test_reference_table_exact_entries() {
372        assert_eq!(compute_silk_rate_for_hybrid(12000, true), 10000);
373        assert_eq!(compute_silk_rate_for_hybrid(16000, true), 13500);
374        assert_eq!(compute_silk_rate_for_hybrid(20000, true), 16000);
375        assert_eq!(compute_silk_rate_for_hybrid(24000, true), 18000);
376        assert_eq!(compute_silk_rate_for_hybrid(32000, true), 22000);
377        assert_eq!(compute_silk_rate_for_hybrid(64000, true), 38000);
378    }
379
380    #[test]
381    fn test_32kbps_gives_22kbps_silk() {
382        assert_eq!(compute_silk_rate_for_hybrid(32000, true), 22000);
383    }
384
385    #[test]
386    fn test_interpolation_between_table_entries() {
387        let r = compute_silk_rate_for_hybrid(18000, true);
388        assert_eq!(r, 14750);
389    }
390
391    #[test]
392    fn test_above_table_max_gives_half_extra() {
393        let r = compute_silk_rate_for_hybrid(72000, true);
394        assert_eq!(r, 38000 + (72000 - 64000) / 2);
395    }
396}
397
398/// Uniformly borrow a codec-state field that is a `Box<T>` under the `heap`
399/// feature and a plain `T` otherwise, so call sites don't need `#[cfg]` on
400/// every `&self.field` / `&mut self.field`.
401///
402/// Under `heap` the field is a `Box<...>`, so `T: Deref(DerefMut)` resolves to
403/// the box itself and the `Target` is the inner type; without `heap` the field
404/// is the inner type directly (`?Sized` lets `&mut [i16]`-style coercion choose
405/// the slice target).
406#[cfg(feature = "heap")]
407#[inline(always)]
408fn state_ref<T: core::ops::Deref>(b: &T) -> &T::Target {
409    b
410}
411#[cfg(not(feature = "heap"))]
412#[inline(always)]
413fn state_ref<T: ?Sized>(b: &T) -> &T {
414    b
415}
416
417#[cfg(feature = "heap")]
418#[inline(always)]
419fn state_mut<T: core::ops::DerefMut>(b: &mut T) -> &mut T::Target {
420    b
421}
422#[cfg(not(feature = "heap"))]
423#[inline(always)]
424fn state_mut<T: ?Sized>(b: &mut T) -> &mut T {
425    b
426}
427
428impl OpusEncoder {
429    pub fn new(
430        sampling_rate: i32,
431        channels: usize,
432        application: Application,
433    ) -> Result<Self, &'static str> {
434        if ![8000, 12000, 16000, 24000, 48000].contains(&sampling_rate) {
435            return Err("Invalid sampling rate");
436        }
437        if ![1, 2].contains(&channels) {
438            return Err("Invalid number of channels");
439        }
440
441        let mode = modes::default_mode();
442        #[cfg(feature = "heap")]
443        let celt_enc = Box::new(CeltEncoder::with_sampling_rate(mode, channels, sampling_rate));
444        #[cfg(not(feature = "heap"))]
445        let celt_enc = CeltEncoder::with_sampling_rate(mode, channels, sampling_rate);
446
447        #[cfg(feature = "heap")]
448        let mut silk_enc = Box::new(SilkEncoderState::default());
449        #[cfg(not(feature = "heap"))]
450        let mut silk_enc = SilkEncoderState::default();
451        if silk_init_encoder(state_mut(&mut silk_enc), 0) != 0 {
452            return Err("SILK encoder initialization failed");
453        }
454
455        let (opus_mode, bw) = match application {
456            Application::Voip => {
457                let bw = match sampling_rate {
458                    8000 => Bandwidth::Narrowband,
459                    12000 => Bandwidth::Mediumband,
460                    16000 => Bandwidth::Wideband,
461                    24000 => Bandwidth::Superwideband,
462                    48000 => Bandwidth::Fullband,
463                    _ => Bandwidth::Narrowband,
464                };
465
466                let mode = if sampling_rate > 16000 {
467                    OpusMode::Hybrid
468                } else {
469                    OpusMode::SilkOnly
470                };
471                (mode, bw)
472            }
473            Application::RestrictedLowDelay => {
474                let bw = match sampling_rate {
475                    8000 => Bandwidth::Narrowband,
476                    12000 => Bandwidth::Mediumband,
477                    16000 => Bandwidth::Wideband,
478                    24000 => Bandwidth::Superwideband,
479                    _ => Bandwidth::Fullband,
480                };
481                (OpusMode::CeltOnly, bw)
482            }
483            Application::Audio => {
484                if sampling_rate <= 16000 {
485                    let bw = match sampling_rate {
486                        8000 => Bandwidth::Narrowband,
487                        12000 => Bandwidth::Mediumband,
488                        _ => Bandwidth::Wideband,
489                    };
490                    (OpusMode::SilkOnly, bw)
491                } else {
492                    let bw = match sampling_rate {
493                        24000 => Bandwidth::Superwideband,
494                        _ => Bandwidth::Fullband,
495                    };
496                    (OpusMode::Hybrid, bw)
497                }
498            }
499        };
500
501        use silk::lin2log::silk_lin2log;
502        let variable_hp_smth2_q15 = silk_lin2log(60) << 8;
503
504        Ok(Self {
505            celt_enc,
506            silk_enc,
507            application,
508            sampling_rate,
509            channels,
510            bandwidth: bw,
511            bitrate_bps: 64000,
512            complexity: 9,
513            use_cbr: false,
514            use_inband_fec: false,
515            packet_loss_perc: 0,
516            silk_initialized: false,
517            prev_enc_mode: None,
518            forced_mode: None,
519            mode: opus_mode,
520            variable_hp_smth2_q15,
521            hp_mem: FixedVec::from_value(0, channels * 2),
522
523            #[cfg(not(feature = "heap"))]
524            buf_filtered: FixedVec::new(),
525            #[cfg(feature = "heap")]
526            buf_filtered: Box::new(FixedVec::new()),
527            #[cfg(not(feature = "heap"))]
528            buf_silk_input: FixedVec::new(),
529            #[cfg(feature = "heap")]
530            buf_silk_input: Box::new(FixedVec::new()),
531            #[cfg(not(feature = "heap"))]
532            buf_stereo_mid: FixedVec::new(),
533            #[cfg(feature = "heap")]
534            buf_stereo_mid: Box::new(FixedVec::new()),
535            #[cfg(not(feature = "heap"))]
536            buf_stereo_side: FixedVec::new(),
537            #[cfg(feature = "heap")]
538            buf_stereo_side: Box::new(FixedVec::new()),
539            #[cfg(not(feature = "heap"))]
540            buf_celt_input: FixedVec::new(),
541            #[cfg(feature = "heap")]
542            buf_celt_input: Box::new(FixedVec::new()),
543            #[cfg(not(feature = "heap"))]
544            delay_buffer: FixedVec::from_value(0.0, OPUS_DELAY_BUF),
545            #[cfg(feature = "heap")]
546            delay_buffer: Box::new(FixedVec::from_value(0.0, OPUS_DELAY_BUF)),
547            #[cfg(not(feature = "heap"))]
548            buf_celt_pcm: FixedVec::new(),
549            #[cfg(feature = "heap")]
550            buf_celt_pcm: Box::new(FixedVec::new()),
551            #[cfg(not(feature = "heap"))]
552            buf_f32_frame: FixedVec::new(),
553            #[cfg(feature = "heap")]
554            buf_f32_frame: Box::new(FixedVec::new()),
555            encoder_buffer: if matches!(application, Application::RestrictedLowDelay) {
556                0
557            } else {
558                (sampling_rate / 100) as usize
559            },
560            delay_compensation: if matches!(application, Application::RestrictedLowDelay) {
561                0
562            } else {
563                (sampling_rate / 250) as usize
564            },
565            hp_mem_float: [0.0; 4],
566            silk_resampler_enc: silk::resampler::SilkResampler::default(),
567            silk_resampler_enc_side: silk::resampler::SilkResampler::default(),
568            rc: RangeCoder::new_encoder(1),
569        })
570    }
571
572    pub fn enable_hybrid_mode(&mut self) -> Result<(), &'static str> {
573        if self.sampling_rate != 24000 && self.sampling_rate != 48000 {
574            return Err("Hybrid mode requires 24kHz or 48kHz sampling rate");
575        }
576        let bw = if self.sampling_rate == 48000 {
577            Bandwidth::Fullband
578        } else {
579            Bandwidth::Superwideband
580        };
581        self.mode = OpusMode::Hybrid;
582        self.bandwidth = bw;
583        self.silk_initialized = false;
584        Ok(())
585    }
586
587    /// Encode a frame of interleaved f32 PCM (scale ±1.0; C `opus_encode_float`
588    /// convention where 1.0 maps to PCM16 32768). Samples are consumed exactly
589    /// up to `frame_size * channels`.
590    pub fn encode(
591        &mut self,
592        input: &[f32],
593        frame_size: usize,
594        output: &mut [u8],
595    ) -> Result<usize, &'static str> {
596        self.encode_impl(PcmInput::F32(input), frame_size, output)
597    }
598
599    /// Encode a frame of interleaved PCM16 (`opus_int16` C `opus_encode()`
600    /// convention). Produces byte-for-byte identical packets to
601    /// `encode()` called with the same audio converted via
602    /// `sample as f32 / 32768.0`, while skipping the f32 round-trip in the
603    /// SILK/VoIP path: the integer high-pass biquad and SILK pipeline are fed
604    /// natively, with no temporary conversion buffer (issue #28).
605    pub fn encode_i16(
606        &mut self,
607        input: &[i16],
608        frame_size: usize,
609        output: &mut [u8],
610    ) -> Result<usize, &'static str> {
611        self.encode_impl(PcmInput::I16(input), frame_size, output)
612    }
613
614    fn encode_impl(
615        &mut self,
616        input: PcmInput<'_>,
617        frame_size: usize,
618        output: &mut [u8],
619    ) -> Result<usize, &'static str> {
620        if output.len() < 2 {
621            return Err("Output buffer too small");
622        }
623
624        let frame_rate = frame_rate_from_params(self.sampling_rate, frame_size)
625            .ok_or("Invalid frame size for sampling rate")?;
626
627        // Clamp caller-provided control fields the way libopus's ctl
628        // interface does (OPUS_SET_BITRATE clamps to [500, 3000000];
629        // complexity to [0, 10]). Unclamped extremes overflow the bitrate
630        // arithmetic downstream (bitrate*6 in celt, `equiv*(90+complexity)`
631        // in the mode selection) — issue #27 deep scan.
632        self.bitrate_bps = self.bitrate_bps.clamp(500, 3_000_000);
633        self.complexity = self.complexity.clamp(0, 10);
634        self.packet_loss_perc = self.packet_loss_perc.clamp(0, 100);
635
636        // The encoder consumes exactly frame_size * channels samples per call;
637        // reject short input up front instead of panicking inside the HP filter
638        // or the float→int conversion loops (issue #27 deep-scan).
639        if input.len() < frame_size * self.channels {
640            return Err("Input buffer too small for frame");
641        }
642
643        // Mode selection: match C's opus_encode_native() behavior.
644        // C reference auto-selects between SILK_ONLY and CELT_ONLY; Hybrid is
645        // produced afterwards by bandwidth overrides (SILK-only + FB/SWB → Hybrid).
646        let mut mode = if let Some(forced) = self.forced_mode {
647            forced
648        } else if self.application == Application::RestrictedLowDelay {
649            OpusMode::CeltOnly
650        } else {
651            let equiv = compute_equiv_rate(
652                self.bitrate_bps,
653                self.channels,
654                frame_rate,
655                !self.use_cbr,
656                self.complexity,
657                self.packet_loss_perc,
658            );
659            let prev_was_celt = self.prev_enc_mode == Some(OpusMode::CeltOnly);
660            let has_prev_mode = self.prev_enc_mode.is_some();
661            let voice_est = match self.application {
662                Application::Voip => 115,
663                Application::Audio => 48,
664                Application::RestrictedLowDelay => 0,
665            };
666            let threshold = compute_mode_threshold(
667                self.application,
668                self.channels,
669                prev_was_celt,
670                has_prev_mode,
671                voice_est,
672            );
673            if equiv >= threshold && self.sampling_rate >= 24000 {
674                OpusMode::CeltOnly
675            } else {
676                OpusMode::SilkOnly
677            }
678        };
679
680        if self.forced_mode.is_none() {
681            let curr_bw = self.bandwidth;
682            if mode == OpusMode::SilkOnly
683                && (curr_bw == Bandwidth::Superwideband || curr_bw == Bandwidth::Fullband)
684            {
685                mode = OpusMode::Hybrid;
686            }
687            if mode == OpusMode::Hybrid
688                && (curr_bw == Bandwidth::Narrowband
689                    || curr_bw == Bandwidth::Mediumband
690                    || curr_bw == Bandwidth::Wideband)
691            {
692                mode = OpusMode::SilkOnly;
693            }
694        }
695
696        // Per-frame packet budget (C computes this before the mode-dependent
697        // encode; the starved-budget rules below need it too).
698        let target_bits =
699            (self.bitrate_bps as i64 * frame_size as i64 / self.sampling_rate as i64) as i32;
700        let cbr_bytes = ((target_bits + 4) / 8) as usize;
701        let max_data_bytes = output.len();
702
703        // C parity (opus_encoder.c): the nominal-size cap applies in CBR mode
704        // only. In VBR mode the CELT-internal bound (vbr_rate/reservoir) decides
705        // the per-frame size, allowing overshoot (borrowing) and undershoot.
706        // Capping VBR at nominal defeats the reservoir and starves complex frames.
707        let mut n_bytes = if self.use_cbr {
708            cbr_bytes
709                .min(max_data_bytes)
710                .clamp(1, OPUS_MAX_PACKET_BYTES)
711        } else {
712            max_data_bytes.clamp(1, OPUS_MAX_PACKET_BYTES)
713        };
714
715        // C opus_encoder.c:1525-1527: a budget representing less than 6 kb/s
716        // (9 kb/s for frames longer than 20 ms) cannot carry SILK — libopus
717        // switches to CELT-only mode, which codes it as a small valid packet
718        // instead of a PLC placeholder (issue #46).
719        let tiny_budget_rate: i64 = if frame_rate > 50 { 9000 } else { 6000 };
720        if ((n_bytes as i64) * 8 * (self.sampling_rate as i64))
721            < (tiny_budget_rate * (frame_size as i64))
722        {
723            mode = OpusMode::CeltOnly;
724        }
725
726        // 40/60 ms frames are only legal as a single Opus frame in SILK-only
727        // mode. CELT and Hybrid cap a frame at 20 ms, so libopus encodes 20 ms
728        // sub-frames and repacketizes them into one code 1/2/3 packet
729        // (opus_encoder.c `encode_multiframe_packet`). (issue #35)
730        if self.forced_mode.is_none()
731            && mode != OpusMode::SilkOnly
732            && frame_size > self.sampling_rate as usize / 50
733        {
734            return self.encode_multiframe(input, frame_size, mode, output);
735        }
736
737        // C opus_encoder.c: with mode and bandwidth settled, decide whether
738        // this packet codes in-band FEC (decide_fec()); it can lower the
739        // frame's bandwidth when a high loss rate demands FEC at any cost.
740        // The reduction is per-frame: `self.bandwidth` (the user/auto state)
741        // is left untouched, matching C where the bandwidth is re-derived
742        // every frame.
743        let mut frame_bandwidth = self.bandwidth;
744        let equiv_rate = compute_equiv_rate(
745            self.bitrate_bps,
746            self.channels,
747            frame_rate,
748            !self.use_cbr,
749            self.complexity,
750            self.packet_loss_perc,
751        );
752        let lbrr_coded = decide_fec(
753            if self.use_inband_fec { 1 } else { 0 },
754            self.packet_loss_perc,
755            self.silk_enc.s_cmn.lbrr_enabled,
756            mode,
757            &mut frame_bandwidth,
758            equiv_rate,
759        );
760        if mode == OpusMode::CeltOnly {
761            // silk_mode.LBRR_coded persists across frames even while SILK is
762            // idle; it feeds decide_fec's hysteresis on the next SILK frame.
763            self.silk_enc.s_cmn.lbrr_enabled = 0;
764        }
765
766        if mode == OpusMode::CeltOnly {
767            match frame_rate {
768                400 | 200 | 100 | 50 => {}
769                _ => return Err("Unsupported frame size for CELT-only mode"),
770            }
771        }
772
773        if mode == OpusMode::Hybrid {
774            match frame_rate {
775                100 | 50 => {}
776                _ => return Err("Unsupported frame size for Hybrid mode"),
777            }
778        }
779
780        if mode == OpusMode::SilkOnly {
781            match frame_rate {
782                100 | 50 | 25 | 16 => {}
783                // 2.5/5 ms are CELT-only durations: SILK frames are 10 ms or
784                // longer (libopus), and sub-10 ms frames break the SILK
785                // analysis geometry (e.g. the VAD band split at 12 kHz).
786                _ => return Err("Unsupported frame size for SILK-only mode"),
787            }
788        }
789
790        let toc = gen_toc(mode, frame_rate, frame_bandwidth, self.channels);
791        output[0] = toc;
792
793        // C opus_encoder.c:1226-1243: a budget too small to code anything
794        // useful produces a TOC-only "PLC" packet — decoders conceal it as a
795        // loss. Encoding into 0-2 bytes would otherwise overflow the range
796        // coder (issue #45). Under CBR the packet is padded to the nominal
797        // size, exactly like libopus's opus_packet_pad.
798        let sub_3_bytes_per_frame =
799            (self.bitrate_bps as i64) < 3 * frame_rate as i64 * 8;
800        let tiny_long_frame =
801            frame_rate < 50
802                && (n_bytes * (frame_rate as usize) < 300 || self.bitrate_bps < 2400);
803        if n_bytes < 3 || sub_3_bytes_per_frame || tiny_long_frame {
804            let mut toc_mode = mode;
805            let mut packet_code = 0u8;
806            let mut num_multiframes = 0u8;
807            let mut frame_rate_adj = frame_rate;
808            if frame_rate > 100 {
809                toc_mode = OpusMode::CeltOnly;
810            }
811            // 40 ms -> 2 x 20 ms in CELT/hybrid mode.
812            if frame_rate == 25 && toc_mode != OpusMode::SilkOnly {
813                frame_rate_adj = 50;
814                packet_code = 1;
815            }
816            // >= 60 ms frames: 1 x 60 ms, or 2 x 40 ms / 2 x 60 ms multiframes.
817            if frame_rate <= 16 {
818                if n_bytes == 1 || (toc_mode == OpusMode::SilkOnly && frame_rate != 10) {
819                    toc_mode = OpusMode::SilkOnly;
820                    packet_code = if frame_rate <= 12 { 1 } else { 0 };
821                    frame_rate_adj = if frame_rate == 12 { 25 } else { 16 };
822                } else {
823                    num_multiframes = (50 / frame_rate) as u8;
824                    frame_rate_adj = 50;
825                    packet_code = 3;
826                }
827            }
828            let mut plc_bw = frame_bandwidth;
829            if toc_mode == OpusMode::SilkOnly && bandwidth_fec_index(plc_bw) > 2 {
830                // SilkOnly is wideband at most (C: bw > OPUS_BANDWIDTH_WIDEBAND).
831                plc_bw = Bandwidth::Wideband;
832            }
833            output[0] = gen_toc(toc_mode, frame_rate_adj, plc_bw, self.channels) | packet_code;
834            let mut ret = if packet_code <= 1 { 1 } else { 2 };
835            if packet_code == 3 {
836                output[1] = num_multiframes;
837            }
838            if self.use_cbr {
839                let target_total = n_bytes.min(output.len());
840                if target_total >= 2 {
841                    // opus_packet_pad of the TOC-only packet: code 3 with
842                    // `m` empty frames and zero padding.
843                    let m: usize = if packet_code == 3 {
844                        num_multiframes as usize
845                    } else if packet_code == 1 {
846                        2
847                    } else {
848                        1
849                    };
850                    output[0] = (output[0] & 0xFC) | 0x03;
851                    output[1] = 0x40 | m as u8; // padding present, CBR, m frames
852                    if target_total > 2 {
853                        let pad_amount = target_total - 2;
854                        let nb_255s = (pad_amount - 1) / 255;
855                        let mut ptr = 2;
856                        for _ in 0..nb_255s {
857                            output[ptr] = 255;
858                            ptr += 1;
859                        }
860                        output[ptr] = (pad_amount - 255 * nb_255s - 1) as u8;
861                        ptr += 1;
862                        for b in &mut output[ptr..target_total] {
863                            *b = 0;
864                        }
865                    }
866                    ret = target_total;
867                }
868            }
869            // C leaves st->prev_mode untouched on this path.
870            return Ok(ret);
871        }
872
873        let init_rc_size = n_bytes - 1;
874        self.rc.reset_for_encode(init_rc_size as u32);
875
876        // C opus_encode_frame_native: high-pass cutoff state is updated for ALL
877        // modes (celt_encoder.c:1969-1977); the filtered signal is what feeds
878        // both SILK (hybrid) and CELT. Compute it here so CELT-only frames also
879        // keep `variable_hp_smth2_q15` in sync with libopus.
880        let hp_freq_smth1 = if mode == OpusMode::CeltOnly {
881            silk_lin2log(60) << 8
882        } else {
883            self.silk_enc.s_cmn.variable_hp_smth1_q15
884        };
885
886        const VARIABLE_HP_SMTH_COEF2_Q16: i32 = 984;
887        self.variable_hp_smth2_q15 = silk_smlawb(
888            self.variable_hp_smth2_q15,
889            hp_freq_smth1 - self.variable_hp_smth2_q15,
890            VARIABLE_HP_SMTH_COEF2_Q16,
891        );
892
893        let cutoff_hz = silk_log2lin(silk_rshift(self.variable_hp_smth2_q15, 8));
894
895        if mode == OpusMode::SilkOnly || mode == OpusMode::Hybrid {
896            let silk_fs_khz = if mode == OpusMode::Hybrid {
897                16
898            } else {
899                self.sampling_rate.min(16000) / 1000
900            };
901
902            let frame_ms = (frame_size as i32 * 1000) / self.sampling_rate;
903            // C opus_encoder.c:1448-1455: (re)entering SILK/hybrid from CELT
904            // resets the SILK encoder and prefills it with recent (ramped)
905            // audio, so the first SILK frame starts from valid history.
906            let silk_restart_after_celt =
907                self.prev_enc_mode == Some(OpusMode::CeltOnly);
908            if silk_restart_after_celt {
909                silk_init_encoder(state_mut(&mut self.silk_enc), 0);
910                self.silk_initialized = false;
911            }
912            if !self.silk_initialized || self.silk_enc.s_cmn.fs_khz != silk_fs_khz {
913                let silk_init_bitrate = (((n_bytes - 1) * 8) as i64 * self.sampling_rate as i64
914                    / frame_size as i64) as i32;
915                silk_control_encoder(
916                    state_mut(&mut self.silk_enc),
917                    silk_fs_khz,
918                    frame_ms,
919                    silk_init_bitrate,
920                    self.complexity,
921                );
922                self.silk_enc.s_cmn.use_cbr = if self.use_cbr { 1 } else { 0 };
923
924                self.silk_enc.s_cmn.n_channels = self.channels as i32;
925                self.silk_initialized = true;
926                // C silk_setup_resamplers: re-init the encoder-direction
927                // resampler whenever the SILK rate changes; the input delay
928                // comes from delay_matrix_enc.
929                let ret = self
930                    .silk_resampler_enc
931                    .init_for_enc(self.sampling_rate as i32, silk_fs_khz * 1000);
932                if ret != 0 {
933                    return Err("Unsupported SILK resampling ratio");
934                }
935                self.silk_resampler_enc_side = self.silk_resampler_enc.clone();
936            }
937
938            self.silk_enc.s_cmn.use_in_band_fec = if self.use_inband_fec { 1 } else { 0 };
939            self.silk_enc.s_cmn.packet_loss_perc = self.packet_loss_perc.clamp(0, 100);
940
941            // C silk_setup_LBRR (control_codec.c): per-packet FEC state and
942            // LBRR gain increase (7 on the first FEC packet, then
943            // max(7 - 0.2*loss, 2)).
944            silk_setup_lbrr(state_mut(&mut self.silk_enc), lbrr_coded);
945
946            // C opus_encoder.c:1806-1826: prefill the fresh SILK encoder with
947            // the tail of the delay buffer, zeroed before a 2.5 ms fade-in,
948            // resampled through the fresh encoder resampler so its delay
949            // state also starts consistent.
950            if silk_restart_after_celt && self.encoder_buffer > 0 && self.delay_compensation > 0
951            {
952                let ch = self.channels;
953                let f2_5 = self.sampling_rate / 400;
954                let eb = self.encoder_buffer;
955                let offset = eb - self.delay_compensation.min(eb) - f2_5 as usize;
956                // Stack-fixed: encoder_buffer = Fs/100 <= 480, and the
957                // resampled prefill is at most the same length (Copy at equal
958                // rates). No allocation: this crate is heap-free without the
959                // `heap` feature.
960                let mut pre = [0i16; 480];
961                let pre = &mut pre[..eb];
962                if offset < eb {
963                    for i in offset..eb {
964                        let fade = (((i - offset) as f32) / f2_5 as f32).min(1.0);
965                        let mut acc = 0f32;
966                        for c in 0..ch {
967                            acc += self.delay_buffer[i * ch + c];
968                        }
969                        pre[i] = (acc / ch as f32 * fade * 32768.0) as i16;
970                    }
971                }
972                let silk_rate = self.sampling_rate.min(16000);
973                let pre_resampled_len = eb * silk_rate as usize / self.sampling_rate as usize;
974                let mut pre_internal = [0i16; 480];
975                let pre_internal = &mut pre_internal[..pre_resampled_len];
976                self.silk_resampler_enc
977                    .process(pre_internal, pre, eb as i32);
978                silk_encode_prefill(state_mut(&mut self.silk_enc), pre_internal, 1);
979            }
980
981            let required_size = frame_size * self.channels;
982            self.buf_filtered.resize(required_size, 0);
983            let voip = self.application == Application::Voip;
984            match input {
985                PcmInput::I16(samples) => {
986                    if voip {
987                        // Native i16 path: feed the integer HP biquad directly,
988                        // skipping the f32 round-trip and its temporary
989                        // conversion buffer (issue #28).
990                        hp_cutoff_i16(
991                            samples,
992                            cutoff_hz,
993                            state_mut(&mut self.buf_filtered),
994                            &mut self.hp_mem,
995                            frame_size,
996                            self.channels,
997                            self.sampling_rate,
998                        );
999                    } else {
1000                        let filtered = state_mut(&mut self.buf_filtered);
1001                        filtered[..required_size].copy_from_slice(&samples[..required_size]);
1002                    }
1003                }
1004                PcmInput::F32(samples) => {
1005                    if voip {
1006                        hp_cutoff(
1007                            samples,
1008                            cutoff_hz,
1009                            state_mut(&mut self.buf_filtered),
1010                            &mut self.hp_mem,
1011                            frame_size,
1012                            self.channels,
1013                            self.sampling_rate,
1014                        );
1015                    } else {
1016                        let filtered = state_mut(&mut self.buf_filtered);
1017                        for (i, &x) in samples.iter().enumerate().take(required_size) {
1018                            filtered[i] = (x * 32768.0).clamp(-32768.0, 32767.0) as i16;
1019                        }
1020                    }
1021                }
1022            }
1023
1024            let input_i16 = state_ref(&self.buf_filtered);
1025
1026            // Stereo mid/side split at the API rate (C silk_stereo_LR_to_MS
1027            // works at the internal rate, but this port splits first and
1028            // resamples each signal below).
1029            let is_stereo_silk = mode == OpusMode::SilkOnly && self.channels == 2;
1030            if is_stereo_silk {
1031                let frame_length = input_i16.len() / 2;
1032                self.buf_stereo_mid.resize(frame_length, 0);
1033                self.buf_stereo_side.resize(frame_length, 0);
1034                for i in 0..frame_length {
1035                    let l = input_i16[2 * i] as i32;
1036                    let r = input_i16[2 * i + 1] as i32;
1037                    self.buf_stereo_mid[i] = ((l + r) / 2) as i16;
1038                    self.buf_stereo_side[i] = (l - r) as i16;
1039                }
1040            }
1041
1042            // SILK input resampling (C silk_Encode always runs silk_resampler
1043            // on the API-rate input; the Copy path still applies its
1044            // delay_matrix_enc input delay at equal rates).
1045            let silk_frame_len_samples =
1046                frame_size * silk_fs_khz as usize / (self.sampling_rate.max(1000) / 1000) as usize;
1047            let needs_resampler = mode == OpusMode::SilkOnly || mode == OpusMode::Hybrid;
1048            let silk_input: &[i16] = if needs_resampler {
1049                self.buf_silk_input.resize(silk_frame_len_samples, 0);
1050                if is_stereo_silk {
1051                    self.buf_stereo_side.resize(silk_frame_len_samples, 0);
1052                }
1053                let mid_src: &[i16] = if is_stereo_silk {
1054                    state_ref(&self.buf_stereo_mid)
1055                } else {
1056                    input_i16
1057                };
1058                let got = self.silk_resampler_enc.process(
1059                    state_mut(&mut self.buf_silk_input),
1060                    mid_src,
1061                    frame_size as i32,
1062                );
1063                if got != 0 {
1064                    return Err("SILK input resampling failed");
1065                }
1066                if is_stereo_silk {
1067                    // Resample the API-rate side (buf_stereo_side) into the
1068                    // encoder's side buffer.
1069                    let side_src: &[i16] = state_ref(&self.buf_stereo_side);
1070                    self.silk_enc.stereo.side.resize(silk_frame_len_samples, 0);
1071                    let side_dst: &mut [i16] = state_mut(&mut self.silk_enc.stereo.side);
1072                    let got = self
1073                        .silk_resampler_enc_side
1074                        .process(side_dst, side_src, frame_size as i32);
1075                    if got != 0 {
1076                        return Err("SILK input resampling failed");
1077                    }
1078                }
1079                state_ref(&self.buf_silk_input)
1080            } else if is_stereo_silk {
1081                let frame_length = input_i16.len() / 2;
1082                self.silk_enc.stereo.side.resize(frame_length, 0);
1083                self.silk_enc
1084                    .stereo
1085                    .side
1086                    .copy_from_slice(&self.buf_stereo_side[..frame_length]);
1087                state_ref(&self.buf_stereo_mid)
1088            } else {
1089                input_i16
1090            };
1091
1092            let mut pn_bytes = 0;
1093
1094            let silk_rate_for_calc = if mode == OpusMode::Hybrid {
1095                16000
1096            } else {
1097                self.sampling_rate
1098            };
1099            let silk_frame_len = silk_input.len();
1100
1101            let silk_bitrate = if mode == OpusMode::Hybrid {
1102                let frame_duration_ms = frame_size as i32 * 1000 / self.sampling_rate;
1103                let frame20ms = frame_duration_ms >= 20;
1104                compute_silk_rate_for_hybrid(self.bitrate_bps, frame20ms)
1105            } else {
1106                (8i64 * (n_bytes - 1) as i64 * silk_rate_for_calc as i64 / silk_frame_len as i64)
1107                    as i32
1108            };
1109            let silk_max_bits = if mode == OpusMode::Hybrid {
1110                let total_max_bits = ((n_bytes - 1) * 8) as i32;
1111                if self.use_cbr {
1112                    let silk_bits = (silk_bitrate as i64 * silk_frame_len as i64
1113                        / silk_rate_for_calc as i64) as i32;
1114                    let other_bits = 0i32.max(total_max_bits - silk_bits);
1115                    0i32.max(total_max_bits - other_bits * 3 / 4)
1116                } else {
1117                    let frame_duration_ms = frame_size as i32 * 1000 / self.sampling_rate;
1118                    let frame20ms = frame_duration_ms >= 20;
1119                    let max_bit_rate = compute_silk_rate_for_hybrid(
1120                        total_max_bits * self.sampling_rate / frame_size as i32,
1121                        frame20ms,
1122                    );
1123                    max_bit_rate * frame_size as i32 / self.sampling_rate
1124                }
1125            } else {
1126                ((n_bytes - 1) * 8) as i32
1127            };
1128            let silk_use_cbr = if mode == OpusMode::Hybrid && self.use_cbr {
1129                0
1130            } else if self.use_cbr {
1131                1
1132            } else {
1133                0
1134            };
1135            let ret = silk_encode(
1136                state_mut(&mut self.silk_enc),
1137                silk_input,
1138                silk_input.len(),
1139                &mut self.rc,
1140                &mut pn_bytes,
1141                silk_bitrate,
1142                silk_max_bits,
1143                silk_use_cbr,
1144                1,
1145            );
1146            if ret != 0 {
1147                return Err("SILK encoding failed");
1148            }
1149        }
1150
1151        if mode == OpusMode::Hybrid {
1152            self.rc.encode_bit_logp(false, 12); // redundancy = 0
1153        }
1154
1155        if mode == OpusMode::Hybrid {
1156            let nb_compr_bytes = (n_bytes - 1) as u32;
1157            self.rc.shrink(nb_compr_bytes);
1158        }
1159
1160        let silk_ret_bytes = if mode == OpusMode::SilkOnly {
1161            ((self.rc.tell() + 7) >> 3) as usize
1162        } else {
1163            0
1164        };
1165
1166        // libopus parity: adjust nbCompressedBytes for CELT/Hybrid based on tell (celt_encoder.c:1913-1921)
1167        // tmp = bitrate*frame_size + tell*Fs; nbCompressed = (tmp+4*Fs)/(8*Fs)
1168        // This is the CBR branch (vbr==0) in celt_encoder.c; for VBR the encoder
1169        // does *not* do this adjustment - it uses the vbr_bound logic instead.
1170        // Doing it for VBR would double-shrink and corrupt the budget.
1171        if mode != OpusMode::SilkOnly && self.use_cbr {
1172            let tell = self.rc.tell();
1173            if tell > 1 {
1174                let tmp = self.bitrate_bps as i64 * frame_size as i64
1175                    + tell as i64 * self.sampling_rate as i64;
1176                let adjusted = ((tmp + 4 * self.sampling_rate as i64)
1177                    / (8 * self.sampling_rate as i64)) as usize;
1178                let new_n = adjusted.clamp(1, OPUS_MAX_PACKET_BYTES).min(max_data_bytes);
1179                if new_n < n_bytes {
1180                    n_bytes = new_n;
1181                    // shrink range coder to new size (keep SILK bytes, trim tail)
1182                    let new_payload = n_bytes - 1;
1183                    self.rc.shrink(new_payload as u32);
1184                }
1185            }
1186        }
1187        if mode == OpusMode::CeltOnly || mode == OpusMode::Hybrid {
1188            self.celt_enc.complexity = self.complexity;
1189            let start_band = if mode == OpusMode::Hybrid { 17 } else { 0 };
1190            // libopus `CELT_SET_END_BAND`: match the TOC bandwidth so the
1191            // decoder reads the same number of coded bands (issue #37).
1192            let end_band = match frame_bandwidth {
1193                Bandwidth::Narrowband => 13,
1194                Bandwidth::Mediumband | Bandwidth::Wideband => 17,
1195                Bandwidth::Superwideband => 19,
1196                Bandwidth::Fullband | Bandwidth::Auto => 21,
1197            };
1198            self.celt_enc.set_end_band(end_band);
1199            let total_packet_bits = ((n_bytes - 1) * 8) as i32;
1200            // Propagate bitrate/VBR to CeltEncoder for accurate VBR handling (libopus parity)
1201            let celt_bitrate = if mode == OpusMode::Hybrid {
1202                let frame_ms = frame_size as i32 * 1000 / self.sampling_rate;
1203                let frame20ms = frame_ms >= 20;
1204                let silk_rate = compute_silk_rate_for_hybrid(self.bitrate_bps, frame20ms);
1205                (self.bitrate_bps - silk_rate).max(8000)
1206            } else {
1207                self.bitrate_bps
1208            };
1209            self.celt_enc.set_bitrate(celt_bitrate);
1210            self.celt_enc.set_vbr(!self.use_cbr);
1211            // libopus: Hybrid VBR is unconstrained (can steal from SILK), CELT-only constrained
1212            self.celt_enc
1213                .set_constrained_vbr(mode == OpusMode::CeltOnly);
1214
1215            // Float view of the current frame for the CELT pipeline. For the
1216            // i16 entry point this converts once into a scratch buffer with
1217            // the exact per-sample scaling a caller of `encode()` performs
1218            // (`s as f32 / 32768.0`), so packets stay byte-for-byte identical
1219            // across the two entry points (issue #28).
1220            let input_f32: &[f32] = match input {
1221                PcmInput::F32(samples) => samples,
1222                PcmInput::I16(samples) => {
1223                    let n = frame_size * self.channels;
1224                    self.buf_f32_frame.resize(n, 0.0);
1225                    let staging = state_mut(&mut self.buf_f32_frame);
1226                    for (dst, &src) in staging[..n].iter_mut().zip(samples[..n].iter()) {
1227                        *dst = src as f32 / 32768.0;
1228                    }
1229                    state_ref(&self.buf_f32_frame)
1230                }
1231            };
1232
1233            // Build the CELT input exactly like C `opus_encode_frame_native`:
1234            //   pcm_buf = [delay-compensation prefix from ring]
1235            //             [dc_reject / hp_cutoff'd current frame]
1236            // CELT then reads pcm_buf[0..frame_size*channels] (opus_encoder.c:
1237            // 1966-2010, 2493). This delay + filter step is what libopus feeds
1238            // its CELT encoder; passing the raw input diverges from 1.6.
1239            let celt_input: &[f32] = if self.delay_compensation > 0 {
1240                let delay = self.delay_compensation;
1241                let ebuf = self.encoder_buffer;
1242                let ch = self.channels;
1243                let total = (delay + frame_size) * ch;
1244                self.buf_celt_pcm.resize(total, 0.0);
1245                // 1. delay prefix from the ring (C: OPUS_COPY at 1967)
1246                let prefix = delay * ch;
1247                let src_start = (ebuf - delay) * ch;
1248                self.buf_celt_pcm[..prefix]
1249                    .copy_from_slice(&self.delay_buffer[src_start..src_start + prefix]);
1250                // 2. filter current frame into pcm_buf[delay..] (C: 2002-2010)
1251                let out = &mut self.buf_celt_pcm[prefix..];
1252                if self.application == Application::Voip {
1253                    hp_cutoff_float(
1254                        input_f32,
1255                        cutoff_hz,
1256                        out,
1257                        &mut self.hp_mem_float,
1258                        frame_size,
1259                        ch,
1260                        self.sampling_rate,
1261                    );
1262                } else {
1263                    dc_reject_float(
1264                        input_f32,
1265                        3,
1266                        out,
1267                        &mut self.hp_mem_float,
1268                        frame_size,
1269                        ch,
1270                        self.sampling_rate,
1271                    );
1272                }
1273                // 3. float NaN guard (C: 2016-2028)
1274                let mut sum = 0.0f32;
1275                for &v in &self.buf_celt_pcm[prefix..] {
1276                    sum += v * v;
1277                }
1278                // C shape kept verbatim (`!(sum < 1e9f) || celt_isnan(sum)`):
1279                // the negated comparison is deliberate NaN handling.
1280                #[allow(clippy::neg_cmp_op_on_partial_ord)]
1281                if !(sum < 1e9) || sum.is_nan() {
1282                    self.buf_celt_pcm[prefix..].fill(0.0);
1283                    self.hp_mem_float = [0.0; 4];
1284                }
1285                // 4. delay ring update (C: 2300-2312)
1286                let keep = ebuf as i64 - (frame_size as i64 + delay as i64);
1287                if keep > 0 {
1288                    let keep = keep as usize;
1289                    self.delay_buffer
1290                        .copy_within(ch * frame_size..ch * (frame_size + keep), 0);
1291                    let dst = ch * keep;
1292                    let n = (frame_size + delay) * ch;
1293                    self.delay_buffer[dst..dst + n].copy_from_slice(&self.buf_celt_pcm[..n]);
1294                } else {
1295                    let n = ebuf * ch;
1296                    let src = (frame_size + delay - ebuf) * ch;
1297                    self.delay_buffer[..n].copy_from_slice(&self.buf_celt_pcm[src..src + n]);
1298                }
1299                // 5. deinterleave pcm_buf[0..frame_size*ch] (interleaved) to
1300                //    channel-major for the Rust CELT encoder.
1301                let n = frame_size * ch;
1302                self.buf_celt_input.resize(n, 0.0);
1303                for i in 0..frame_size {
1304                    for c in 0..ch {
1305                        self.buf_celt_input[c * frame_size + i] = self.buf_celt_pcm[i * ch + c];
1306                    }
1307                }
1308                state_ref(&self.buf_celt_input)
1309            } else if self.channels == 1 {
1310                input_f32
1311            } else {
1312                let n = frame_size * self.channels;
1313                self.buf_celt_input.resize(n, 0.0);
1314                for i in 0..frame_size {
1315                    for ch in 0..self.channels {
1316                        self.buf_celt_input[ch * frame_size + i] =
1317                            input_f32[i * self.channels + ch];
1318                    }
1319                }
1320                state_ref(&self.buf_celt_input)
1321            };
1322
1323            if self.rc.tell() <= total_packet_bits {
1324                let is_vbr = !self.use_cbr;
1325                self.celt_enc.encode_with_budget_vbr(
1326                    celt_input,
1327                    frame_size,
1328                    &mut self.rc,
1329                    start_band,
1330                    total_packet_bits,
1331                    is_vbr,
1332                );
1333            }
1334        }
1335
1336        self.rc.done();
1337
1338        // C opus_encoder.c:2170-2179: "In the unlikely case that the SILK
1339        // encoder busted its target, tell the decoder to call the PLC" — emit
1340        // a minimal packet with an EMPTY frame instead of failing hard. The
1341        // zero-length frame makes the decoder conceal (libopus pads the same
1342        // 2-byte packet with opus_packet_pad under CBR). With FEC the LBRR
1343        // section can squeeze the main frame below what even the
1344        // damage-control re-encode fits, which is exactly the case this
1345        // fallback exists for.
1346        if self.rc.error != 0 || self.rc.tell() > (n_bytes as i32 - 1) * 8 {
1347            if output.len() < 2 {
1348                return Err("Output buffer too small");
1349            }
1350            let mut ret = 2usize;
1351            if self.use_cbr && n_bytes >= 2 {
1352                // CBR: pad to the nominal size as a code-3 packet holding a
1353                // single EMPTY frame (opus_packet_pad of the 2-byte packet).
1354                let target_total = n_bytes.min(output.len());
1355                if target_total > 2 {
1356                    output[0] = toc | 0x03;
1357                    output[1] = 0x41; // 1 frame, padding present, CBR
1358                    let pad_amount = target_total - 2;
1359                    let nb_255s = (pad_amount - 1) / 255;
1360                    let mut ptr = 2;
1361                    for _ in 0..nb_255s {
1362                        output[ptr] = 255;
1363                        ptr += 1;
1364                    }
1365                    output[ptr] = (pad_amount - 255 * nb_255s - 1) as u8;
1366                    ptr += 1;
1367                    for b in &mut output[ptr..target_total] {
1368                        *b = 0;
1369                    }
1370                    self.prev_enc_mode = Some(mode);
1371                    return Ok(target_total);
1372                }
1373                ret = target_total.max(1);
1374            }
1375            // VBR: a 2-byte code-0 packet whose frame length byte is 0.
1376            output[0] = toc;
1377            output[1] = 0;
1378            self.prev_enc_mode = Some(mode);
1379            return Ok(ret);
1380        }
1381
1382        if mode == OpusMode::SilkOnly {
1383            let mut ret = silk_ret_bytes.min(self.rc.storage as usize);
1384            while ret > 2 && self.rc.buf[ret - 1] == 0 {
1385                ret -= 1;
1386            }
1387
1388            let target_total = if self.use_cbr {
1389                n_bytes.min(output.len())
1390            } else {
1391                (ret + 1).min(output.len())
1392            };
1393
1394            let silk_len = ret;
1395
1396            if !self.use_cbr || silk_len + 1 >= target_total {
1397                // VBR or payload fills the target: simple code 0 packet
1398                output[0] = toc;
1399                let copy_len = silk_len.min(target_total - 1);
1400                output[1..1 + copy_len].copy_from_slice(&self.rc.buf[..copy_len]);
1401                return Ok((copy_len + 1).min(output.len()));
1402            }
1403
1404            output[0] = toc | 0x03;
1405
1406            if silk_len + 2 >= target_total {
1407                output[1] = 0x01;
1408                let copy_len = (target_total - 2).min(silk_len);
1409                output[2..2 + copy_len].copy_from_slice(&self.rc.buf[..copy_len]);
1410                self.prev_enc_mode = Some(mode);
1411                return Ok(target_total.min(output.len()));
1412            }
1413
1414            let pad_amount = target_total - silk_len - 2;
1415            output[1] = 0x41;
1416
1417            let nb_255s = (pad_amount - 1) / 255;
1418            let mut ptr = 2;
1419            for _ in 0..nb_255s {
1420                output[ptr] = 255;
1421                ptr += 1;
1422            }
1423            output[ptr] = (pad_amount - 255 * nb_255s - 1) as u8;
1424            ptr += 1;
1425
1426            output[ptr..ptr + silk_len].copy_from_slice(&self.rc.buf[..silk_len]);
1427            ptr += silk_len;
1428
1429            let fill_end = target_total.min(output.len());
1430            for byte in output[ptr..fill_end].iter_mut() {
1431                *byte = 0;
1432            }
1433
1434            self.prev_enc_mode = Some(mode);
1435            return Ok(target_total.min(output.len()));
1436        }
1437
1438        // For CELT/Hybrid, respect possible VBR shrink performed by CeltEncoder (e.g. silence)
1439        let payload_len = (self.rc.storage as usize).min(output.len() - 1);
1440        output[1..1 + payload_len].copy_from_slice(&self.rc.buf[..payload_len]);
1441        self.prev_enc_mode = Some(mode);
1442        Ok(payload_len + 1)
1443    }
1444
1445    /// Repacketize 40/60 ms CELT/Hybrid input as 20 ms sub-frames in one code
1446    /// 1/2/3 Opus packet (libopus `encode_multiframe_packet`). Every sub-frame
1447    /// is forced to `mode` so the packet carries a single config.
1448    fn encode_multiframe(
1449        &mut self,
1450        input: PcmInput<'_>,
1451        frame_size: usize,
1452        mode: OpusMode,
1453        output: &mut [u8],
1454    ) -> Result<usize, &'static str> {
1455        const MAX_SUB: usize = 3; // up to 60 ms of 20 ms frames
1456        let ch = self.channels;
1457        let enc_frame = (self.sampling_rate / 50) as usize; // 20 ms
1458        if enc_frame == 0 || !frame_size.is_multiple_of(enc_frame) {
1459            return Err("Invalid multi-frame size");
1460        }
1461        let nb = frame_size / enc_frame;
1462        if !(2..=MAX_SUB).contains(&nb) {
1463            return Err("Unsupported multi-frame size");
1464        }
1465
1466        // RFC 6716 size field: one byte below 252, else two.
1467        fn put_size(out: &mut [u8], pos: &mut usize, len: usize) -> Result<(), &'static str> {
1468            if len < 252 {
1469                if *pos >= out.len() {
1470                    return Err("Output buffer too small");
1471                }
1472                out[*pos] = len as u8;
1473                *pos += 1;
1474            } else {
1475                if *pos + 1 >= out.len() {
1476                    return Err("Output buffer too small");
1477                }
1478                let first = 252 + (len & 3);
1479                out[*pos] = first as u8;
1480                out[*pos + 1] = ((len - first) >> 2) as u8;
1481                *pos += 2;
1482            }
1483            Ok(())
1484        }
1485
1486        let mut sub = [[0u8; 1276]; MAX_SUB];
1487        let mut lens = [0usize; MAX_SUB];
1488        self.forced_mode = Some(mode);
1489        let enc_res = (|| -> Result<(), &'static str> {
1490            for i in 0..nb {
1491                let start = i * enc_frame * ch;
1492                let end = start + enc_frame * ch;
1493                let sub_input = match input {
1494                    PcmInput::F32(s) => PcmInput::F32(&s[start..end]),
1495                    PcmInput::I16(s) => PcmInput::I16(&s[start..end]),
1496                };
1497                let n = self.encode_impl(sub_input, enc_frame, &mut sub[i])?;
1498                if n == 0 {
1499                    return Err("Empty sub-frame");
1500                }
1501                lens[i] = n - 1;
1502            }
1503            Ok(())
1504        })();
1505        self.forced_mode = None;
1506        enc_res?;
1507
1508        let toc = sub[0][0] & 0xFC;
1509        let mut pos = 0usize;
1510        let push =
1511            |out: &mut [u8], pos: &mut usize, byte: u8| -> Result<(), &'static str> {
1512                if *pos >= out.len() {
1513                    return Err("Output buffer too small");
1514                }
1515                out[*pos] = byte;
1516                *pos += 1;
1517                Ok(())
1518            };
1519        // Use explicit per-frame lengths (code 2 / code 3 VBR) for every
1520        // sub-frame packet: it is always valid, and avoids the code-3 CBR
1521        // assumption that all frames are byte-identical in size.
1522        if nb == 2 {
1523            push(output, &mut pos, toc | 0x02)?;
1524            put_size(output, &mut pos, lens[0])?;
1525        } else {
1526            push(output, &mut pos, toc | 0x03)?;
1527            push(output, &mut pos, nb as u8 | 0x80)?;
1528            for i in 0..nb - 1 {
1529                put_size(output, &mut pos, lens[i])?;
1530            }
1531        }
1532
1533        for i in 0..nb {
1534            let payload = &sub[i][1..1 + lens[i]];
1535            if pos + payload.len() > output.len() {
1536                return Err("Output buffer too small");
1537            }
1538            output[pos..pos + payload.len()].copy_from_slice(payload);
1539            pos += payload.len();
1540        }
1541        self.prev_enc_mode = Some(mode);
1542        Ok(pos)
1543    }
1544}
1545
1546pub struct OpusDecoder {
1547    #[cfg(not(feature = "heap"))]
1548    celt_dec: CeltDecoder,
1549    #[cfg(feature = "heap")]
1550    celt_dec: Box<CeltDecoder>,
1551    #[cfg(not(feature = "heap"))]
1552    silk_dec: silk::dec_api::SilkDecoder,
1553    #[cfg(feature = "heap")]
1554    silk_dec: Box<silk::dec_api::SilkDecoder>,
1555    sampling_rate: i32,
1556    channels: usize,
1557
1558    prev_mode: Option<OpusMode>,
1559
1560    /// Whether the previous frame had redundancy (mode transition marker).
1561    prev_redundancy: bool,
1562    frame_size: usize,
1563
1564    bandwidth: Bandwidth,
1565
1566    stream_channels: usize,
1567
1568    silk_resampler: silk::resampler::SilkResampler,
1569
1570    /// Second resampler instance for stereo channel 1.
1571    silk_resampler_2: silk::resampler::SilkResampler,
1572
1573    prev_internal_rate: i32,
1574
1575    pub hybrid_skip_celt: bool,
1576
1577    #[cfg(not(feature = "heap"))]
1578    w_pcm_i16: FixedVec<i16, OPUS_PCM_I16>,
1579    #[cfg(feature = "heap")]
1580    w_pcm_i16: Box<FixedVec<i16, OPUS_PCM_I16>>,
1581    #[cfg(not(feature = "heap"))]
1582    w_silk_out: FixedVec<f32, OPUS_SUBFRAME_SCRATCH>,
1583    #[cfg(feature = "heap")]
1584    w_silk_out: Box<FixedVec<f32, OPUS_SUBFRAME_SCRATCH>>,
1585    #[cfg(not(feature = "heap"))]
1586    w_pcm_resampled: FixedVec<i16, OPUS_SUBFRAME_SCRATCH>,
1587    #[cfg(feature = "heap")]
1588    w_pcm_resampled: Box<FixedVec<i16, OPUS_SUBFRAME_SCRATCH>>,
1589    #[cfg(not(feature = "heap"))]
1590    w_celt_planar: FixedVec<f32, OPUS_SUBFRAME_SCRATCH>,
1591    #[cfg(feature = "heap")]
1592    w_celt_planar: Box<FixedVec<f32, OPUS_SUBFRAME_SCRATCH>>,
1593    #[cfg(not(feature = "heap"))]
1594    w_celt_out: FixedVec<f32, OPUS_SUBFRAME_SCRATCH>,
1595    #[cfg(feature = "heap")]
1596    w_celt_out: Box<FixedVec<f32, OPUS_SUBFRAME_SCRATCH>>,
1597
1598    /// Tail of the previous frame's output, used for smooth_fade at mode
1599    /// transitions (libopus pcm_transition + smooth_fade).
1600    #[cfg(not(feature = "heap"))]
1601    prev_pcm_tail: FixedVec<f32, OPUS_PCM_TAIL>,
1602    #[cfg(feature = "heap")]
1603    prev_pcm_tail: Box<FixedVec<f32, OPUS_PCM_TAIL>>,
1604}
1605
1606impl OpusDecoder {
1607    pub fn new(sampling_rate: i32, channels: usize) -> Result<Self, &'static str> {
1608        if ![8000, 12000, 16000, 24000, 48000].contains(&sampling_rate) {
1609            return Err("Invalid sampling rate");
1610        }
1611        if ![1, 2].contains(&channels) {
1612            return Err("Invalid number of channels");
1613        }
1614
1615        let mode = modes::default_mode();
1616        #[cfg(feature = "heap")]
1617        let celt_dec = Box::new(CeltDecoder::new(mode, channels, sampling_rate));
1618        #[cfg(not(feature = "heap"))]
1619        let celt_dec = CeltDecoder::new(mode, channels, sampling_rate);
1620
1621        #[cfg(feature = "heap")]
1622        let mut silk_dec = Box::new(silk::dec_api::SilkDecoder::new());
1623        #[cfg(not(feature = "heap"))]
1624        let mut silk_dec = silk::dec_api::SilkDecoder::new();
1625        silk_dec.init(sampling_rate.min(16000), channels as i32);
1626        silk_dec.channel_state[0].fs_api_hz = sampling_rate;
1627
1628        Ok(Self {
1629            celt_dec,
1630            silk_dec,
1631            sampling_rate,
1632            channels,
1633            prev_mode: None,
1634            prev_redundancy: false,
1635            frame_size: 0,
1636            bandwidth: Bandwidth::Auto,
1637            stream_channels: channels,
1638            silk_resampler: silk::resampler::SilkResampler::default(),
1639            silk_resampler_2: silk::resampler::SilkResampler::default(),
1640            prev_internal_rate: 0,
1641            hybrid_skip_celt: false,
1642
1643            #[cfg(not(feature = "heap"))]
1644            w_pcm_i16: FixedVec::from_value(0i16, 960 * channels),
1645            #[cfg(feature = "heap")]
1646            w_pcm_i16: Box::new(FixedVec::from_value(0i16, 960 * channels)),
1647
1648            #[cfg(not(feature = "heap"))]
1649            w_silk_out: FixedVec::from_value(0.0f32, OPUS_MAX_SUBFRAME * channels),
1650            #[cfg(feature = "heap")]
1651            w_silk_out: Box::new(FixedVec::from_value(0.0f32, OPUS_MAX_SUBFRAME * channels)),
1652            #[cfg(not(feature = "heap"))]
1653            w_pcm_resampled: FixedVec::from_value(0i16, OPUS_MAX_SUBFRAME * channels),
1654            #[cfg(feature = "heap")]
1655            w_pcm_resampled: Box::new(FixedVec::from_value(0i16, OPUS_MAX_SUBFRAME * channels)),
1656            #[cfg(not(feature = "heap"))]
1657            w_celt_planar: FixedVec::from_value(0.0f32, OPUS_MAX_SUBFRAME * channels),
1658            #[cfg(feature = "heap")]
1659            w_celt_planar: Box::new(FixedVec::from_value(0.0f32, OPUS_MAX_SUBFRAME * channels)),
1660            #[cfg(not(feature = "heap"))]
1661            w_celt_out: FixedVec::from_value(0.0f32, OPUS_MAX_SUBFRAME * channels),
1662            #[cfg(feature = "heap")]
1663            w_celt_out: Box::new(FixedVec::from_value(0.0f32, OPUS_MAX_SUBFRAME * channels)),
1664
1665            #[cfg(not(feature = "heap"))]
1666            prev_pcm_tail: FixedVec::from_value(0.0f32, 240 * channels),
1667            #[cfg(feature = "heap")]
1668            prev_pcm_tail: Box::new(FixedVec::from_value(0.0f32, 240 * channels)),
1669        })
1670    }
1671
1672    /// Decode one Opus packet into interleaved `f32` PCM, returning the
1673    /// number of samples per channel written.
1674    ///
1675    /// Packet loss follows libopus: pass an empty packet (`&[]`) to conceal
1676    /// `frame_size` samples (a multiple of 2.5 ms), or a ToC-only 1-byte
1677    /// packet to conceal the duration that ToC describes. Either way the
1678    /// frame is concealed in the previous packet's mode; the placeholder's
1679    /// mode, bandwidth and channel bits are ignored.
1680    pub fn decode(
1681        &mut self,
1682        input: &[u8],
1683        frame_size: usize,
1684        output: &mut [f32],
1685    ) -> Result<usize, &'static str> {
1686        // A packet of 0 or 1 bytes (ToC only) is a lost/DTX frame: conceal it
1687        // in the previous mode without touching the mode state (issue #15).
1688        if input.len() <= 1 {
1689            return self.decode_lost(input.first().copied(), frame_size, output);
1690        }
1691
1692        let toc = input[0];
1693        let mode = mode_from_toc(toc);
1694        let packet_channels = channels_from_toc(toc);
1695        let bandwidth = bandwidth_from_toc(toc);
1696        let frame_duration_ms = frame_duration_ms_from_toc(toc);
1697
1698        if packet_channels != self.channels {
1699            return Err("Channel count mismatch between packet and decoder");
1700        }
1701
1702        let code = toc & 0x03;
1703        let frame_count: usize;
1704        let frame_payloads: FixedVec<&[u8], OPUS_MAX_PACKET_FRAMES>;
1705
1706        match code {
1707            0 => {
1708                frame_count = 1;
1709                frame_payloads = FixedVec::from_slice(&[&input[1..]]);
1710            }
1711            1 => {
1712                frame_count = 2;
1713                let data_len = input.len() - 1;
1714                // RFC 6716 §3.2.1: code 1 carries two equal-size (CBR) frames,
1715                // so the payload length must be even. libopus rejects odd lengths.
1716                if !data_len.is_multiple_of(2) {
1717                    return Err("Code 1: payload length must be even");
1718                }
1719                let half = data_len / 2;
1720                if half == 0 {
1721                    return Err("Code 1: empty frame");
1722                }
1723                frame_payloads = FixedVec::from_slice(&[&input[1..1 + half], &input[1 + half..]]);
1724            }
1725            2 => {
1726                frame_count = 2;
1727                let data = &input[1..];
1728                if data.is_empty() {
1729                    return Err("Code 2 packet has no data");
1730                }
1731                let (first_len, header_size) = parse_frame_size(data)?;
1732                if header_size + first_len > data.len() {
1733                    return Err("Code 2: first frame size exceeds packet");
1734                }
1735                frame_payloads = FixedVec::from_slice(&[
1736                    &data[header_size..header_size + first_len],
1737                    &data[header_size + first_len..],
1738                ]);
1739            }
1740            3 => {
1741                if input.len() < 2 {
1742                    return Err("Code 3 packet too short");
1743                }
1744                let count_byte = input[1];
1745                let n_frames = (count_byte & 0x3F) as usize;
1746                if !(1..=48).contains(&n_frames) {
1747                    return Err("Code 3: invalid frame count");
1748                }
1749                frame_count = n_frames;
1750                // Bit 6 = padding flag, bit 7 = VBR flag (RFC 6716 §3.2.1).
1751                let padding_flag = (count_byte & 0x40) != 0;
1752                let vbr = (count_byte & 0x80) != 0;
1753
1754                // Parse the optional padding length bytes that follow the count
1755                // byte. The padding *content* (pad_len bytes) lives at the end of
1756                // the packet and is not part of any frame.
1757                let mut ptr = 2usize;
1758                let mut pad_len = 0usize;
1759                if padding_flag {
1760                    loop {
1761                        if ptr >= input.len() {
1762                            return Err("Code 3: padding overflow");
1763                        }
1764                        let p = input[ptr] as usize;
1765                        ptr += 1;
1766                        if p == 255 {
1767                            pad_len += 254;
1768                        } else {
1769                            pad_len += p;
1770                            break;
1771                        }
1772                    }
1773                }
1774                if ptr + pad_len > input.len() {
1775                    return Err("Code 3: padding exceeds packet");
1776                }
1777                let payload_end = input.len() - pad_len;
1778                let payload = &input[ptr..payload_end];
1779
1780                let mut payloads: FixedVec<&[u8], OPUS_MAX_PACKET_FRAMES> = FixedVec::new();
1781                if frame_count == 1 {
1782                    // Single frame: the entire payload region is the frame, both
1783                    // for VBR and CBR (no length prefix is present).
1784                    payloads.push(payload);
1785                } else if vbr {
1786                    // VBR (V=1): the per-frame length fields for all frames
1787                    // except the last come first, then all frame payloads
1788                    // (RFC 6716 §3.2.1; libopus `opus_packet_parse_impl`).
1789                    // Interleaving length and data — as this used to — parsed
1790                    // any libopus code-3 VBR packet wrong.
1791                    let mut cursor = 0usize;
1792                    let mut frame_bytes = 0usize;
1793                    let mut sizes = [0usize; OPUS_MAX_PACKET_FRAMES];
1794                    for i in 0..frame_count - 1 {
1795                        if cursor >= payload.len() {
1796                            return Err("Code 3: unexpected end in VBR header");
1797                        }
1798                        let (frame_len, header_bytes) = parse_frame_size(&payload[cursor..])?;
1799                        cursor += header_bytes;
1800                        sizes[i] = frame_len;
1801                        frame_bytes += frame_len;
1802                    }
1803                    if frame_bytes > payload.len() - cursor {
1804                        return Err("Code 3: frame length exceeds packet");
1805                    }
1806                    sizes[frame_count - 1] = payload.len() - cursor - frame_bytes;
1807                    let mut data_pos = cursor;
1808                    for i in 0..frame_count {
1809                        payloads.push(&payload[data_pos..data_pos + sizes[i]]);
1810                        data_pos += sizes[i];
1811                    }
1812                } else {
1813                    // CBR (V=0): remaining bytes are split equally into M frames
1814                    // (RFC 6716 §3.2.1: "the remaining bytes are split into M
1815                    // equal chunks").
1816                    if !payload.len().is_multiple_of(frame_count) {
1817                        return Err("Code 3 CBR: payload not divisible by frame count");
1818                    }
1819                    let frame_len = payload.len() / frame_count;
1820                    for i in 0..frame_count {
1821                        payloads.push(&payload[i * frame_len..(i + 1) * frame_len]);
1822                    }
1823                }
1824                frame_payloads = payloads;
1825            }
1826            _ => unreachable!(),
1827        }
1828
1829        self.frame_size = frame_size;
1830        self.bandwidth = bandwidth;
1831        self.stream_channels = packet_channels;
1832
1833        // Derive the actual per-frame sample count from the TOC, not from the
1834        // caller's frame_size. This prevents panics in bands.rs/celt.rs when
1835        // the caller passes a mismatched frame_size (issue #7 sub-item 1):
1836        // the internal decoders always get the correct geometry.
1837        let toc_frame_size = frame_samples_from_toc(toc, self.sampling_rate)
1838            .ok_or("Invalid TOC for sampling rate")?;
1839        let decoded_total = toc_frame_size * frame_count;
1840        if frame_size < decoded_total {
1841            return Err("frame_size too small for packet");
1842        }
1843        if output.len() < decoded_total * self.channels {
1844            return Err("Output buffer too small for packet");
1845        }
1846        // Zero-fill any extra space the caller provided beyond what the packet
1847        // actually produces, so stale data is never left in the buffer.
1848        if output.len() > decoded_total * self.channels {
1849            for v in &mut output[decoded_total * self.channels..] {
1850                *v = 0.0;
1851            }
1852        }
1853        let sub_frame_size = toc_frame_size;
1854        let sub_output_len = sub_frame_size * self.channels;
1855
1856        // Detect mode transition and reset CELT decoder state to prevent
1857        // cross-mode artifacts (libopus opus_decoder.c:602-604).
1858        // This is the primary fix for issue #8/#9 alignment divergence:
1859        // stale CELT MDCT/prefilter state at SILK↔CELT boundaries causes
1860        // discontinuities that accumulate across transitions.
1861        let mode_transition = matches!(
1862            self.prev_mode,
1863            Some(prev) if prev != mode && !self.prev_redundancy
1864        );
1865        if mode_transition {
1866            self.celt_dec.reset_state();
1867        }
1868
1869        // Generate SILK PLC audio for the mode-transition bridge. libopus
1870        // synthesizes 5ms (F5) of pitch-extrapolated audio in the OLD mode
1871        // (opus_decoder.c:387-391) and crossfades it with the new frame. We
1872        // reuse the F5-sized prev_pcm_tail buffer for this bridge.
1873        let f5_bridge = self.sampling_rate as usize / 200; // F5 = Fs/200
1874        if mode_transition
1875            && f5_bridge > 0
1876            && matches!(
1877                self.prev_mode,
1878                Some(OpusMode::SilkOnly) | Some(OpusMode::Hybrid)
1879            )
1880            && self.prev_internal_rate > 0
1881        {
1882            let internal_rate = self.prev_internal_rate;
1883            let plc_internal_len = (10 * internal_rate / 1000) as usize;
1884            let mut plc_rc = RangeCoder::new_decoder(&[]);
1885            let mut plc_i16: FixedVec<i16, OPUS_PCM_I16> =
1886                FixedVec::from_value(0i16, plc_internal_len * self.channels);
1887            let n = self.silk_dec.decode(
1888                &mut plc_rc,
1889                &mut plc_i16,
1890                silk::decode_frame::FLAG_PACKET_LOST,
1891                true,
1892                10,
1893                internal_rate,
1894            );
1895            if n > 0 {
1896                let bridge_ch = f5_bridge * self.channels;
1897                let bridge_len = bridge_ch.min(self.prev_pcm_tail.len());
1898                if internal_rate == self.sampling_rate {
1899                    // No resampling: copy PLC samples directly (ch0 planar).
1900                    let n_us = n as usize;
1901                    for ch in 0..self.channels {
1902                        let src_base = ch * n_us;
1903                        for i in 0..(bridge_len / self.channels).min(n_us) {
1904                            let dst = i * self.channels + ch;
1905                            if dst < bridge_len {
1906                                self.prev_pcm_tail[dst] = plc_i16[src_base + i] as f32 / 32768.0;
1907                            }
1908                        }
1909                    }
1910                } else if self.silk_resampler.is_initialized() {
1911                    // Resample channel 0 to the API rate for the bridge.
1912                    // The resampler must receive an output buffer sized for the
1913                    // FULL resampled length (issue #15): truncating the buffer to
1914                    // `f5_bridge` caused out-of-bounds writes in the Up2HQ/Copy
1915                    // paths. We size it fully, then copy only the bridge window.
1916                    let ratio = self.sampling_rate as f64 / internal_rate as f64;
1917                    let full_len = ((n as f64 * ratio) as usize).max(1);
1918                    let out_len = full_len.min(f5_bridge);
1919                    let n_us = n as usize;
1920                    let mut resampled: FixedVec<i16, OPUS_MAX_FRAME> =
1921                        FixedVec::from_value(0i16, full_len);
1922                    self.silk_resampler
1923                        .process(&mut resampled, &plc_i16[..n_us], n);
1924                    for i in 0..out_len {
1925                        if i < bridge_len / self.channels {
1926                            for ch in 0..self.channels {
1927                                self.prev_pcm_tail[i * self.channels + ch] =
1928                                    resampled[i] as f32 / 32768.0;
1929                            }
1930                        }
1931                    }
1932                }
1933            }
1934        }
1935
1936        // Track whether this packet uses Hybrid redundancy.
1937        let mut has_redundancy = false;
1938
1939        match mode {
1940            OpusMode::SilkOnly => {
1941                let internal_sample_rate = match bandwidth {
1942                    Bandwidth::Narrowband => 8000,
1943                    Bandwidth::Mediumband => 12000,
1944                    Bandwidth::Wideband => 16000,
1945                    _ => 16000,
1946                };
1947                if internal_sample_rate != self.prev_internal_rate
1948                    || !self.silk_resampler.is_initialized()
1949                {
1950                    self.silk_resampler
1951                        .init(internal_sample_rate, self.sampling_rate);
1952                    self.silk_resampler_2
1953                        .init(internal_sample_rate, self.sampling_rate);
1954                }
1955                // Always track the SILK internal rate so the mode-transition
1956                // PLC bridge can be generated (even when no resampling is
1957                // needed, e.g. 16kHz decoder + SILK WB).
1958                self.prev_internal_rate = internal_sample_rate;
1959
1960                for (fi, payload) in frame_payloads.iter().enumerate() {
1961                    let out_start = fi * sub_output_len;
1962                    // C opus_decode_frame: a zero-length frame (e.g. the
1963                    // encoder's busted-budget fallback packet) is concealed,
1964                    // not decoded.
1965                    let flag = if payload.is_empty() {
1966                        silk::decode_frame::FLAG_PACKET_LOST
1967                    } else {
1968                        silk::decode_frame::FLAG_DECODE_NORMAL
1969                    };
1970                    let mut rc = RangeCoder::new_decoder(payload);
1971                    self.decode_silk_frames(
1972                        &mut rc,
1973                        flag,
1974                        frame_duration_ms,
1975                        internal_sample_rate,
1976                        sub_frame_size,
1977                    )?;
1978                    output[out_start..out_start + sub_output_len]
1979                        .copy_from_slice(&self.w_silk_out[..sub_output_len]);
1980                }
1981                decoded_total
1982            }
1983
1984            OpusMode::CeltOnly => {
1985                let celt_end_band = self.celt_end_band_from_toc(toc);
1986
1987                for (fi, payload) in frame_payloads.iter().enumerate() {
1988                    let mut rc = RangeCoder::new_decoder(payload);
1989                    let total_bits = (payload.len() * 8) as i32;
1990                    let needed = sub_frame_size * self.channels;
1991                    let out_start = fi * needed;
1992                    let out_end = (out_start + needed).min(output.len());
1993
1994                    if output.len() < out_end {
1995                        return Err("Output buffer too small");
1996                    }
1997
1998                    if self.channels == 1 {
1999                        self.celt_dec.decode_from_range_coder_with_band_range(
2000                            &mut rc,
2001                            total_bits,
2002                            sub_frame_size,
2003                            &mut output[out_start..out_end],
2004                            0,
2005                            celt_end_band,
2006                        );
2007                        for sample in &mut output[out_start..out_end] {
2008                            *sample = sample.clamp(-1.0, 1.0);
2009                        }
2010                    } else {
2011                        self.celt_dec.decode_from_range_coder_with_band_range(
2012                            &mut rc,
2013                            total_bits,
2014                            sub_frame_size,
2015                            &mut self.w_celt_planar[..needed],
2016                            0,
2017                            celt_end_band,
2018                        );
2019                        for i in 0..sub_frame_size {
2020                            for ch in 0..self.channels {
2021                                let idx = out_start + i * self.channels + ch;
2022                                output[idx] =
2023                                    self.w_celt_planar[ch * sub_frame_size + i].clamp(-1.0, 1.0);
2024                            }
2025                        }
2026                    }
2027                }
2028                decoded_total
2029            }
2030
2031            OpusMode::Hybrid => {
2032                let internal_sample_rate = 16000;
2033                let celt_end_band = self.celt_end_band_from_toc(toc);
2034
2035                if internal_sample_rate != self.prev_internal_rate
2036                    || !self.silk_resampler.is_initialized()
2037                {
2038                    self.silk_resampler
2039                        .init(internal_sample_rate, self.sampling_rate);
2040                    self.silk_resampler_2
2041                        .init(internal_sample_rate, self.sampling_rate);
2042                }
2043                self.prev_internal_rate = internal_sample_rate;
2044
2045                for (fi, payload) in frame_payloads.iter().enumerate() {
2046                    let mut rc = RangeCoder::new_decoder(payload);
2047                    // SILK layer of this frame -> w_silk_out (interleaved, API rate).
2048                    // A zero-length frame is concealed, not decoded (C
2049                    // opus_decode_frame).
2050                    let flag = if payload.is_empty() {
2051                        silk::decode_frame::FLAG_PACKET_LOST
2052                    } else {
2053                        silk::decode_frame::FLAG_DECODE_NORMAL
2054                    };
2055                    self.decode_silk_frames(
2056                        &mut rc,
2057                        flag,
2058                        frame_duration_ms,
2059                        internal_sample_rate,
2060                        sub_frame_size,
2061                    )?;
2062                    let silk_out_len = sub_frame_size * self.channels;
2063
2064                    let total_bits = (payload.len() * 8) as i32;
2065                    let redundancy = rc.decode_bit_logp(12);
2066                    let skip_celt = if redundancy {
2067                        let _celt_to_silk = rc.decode_bit_logp(1);
2068                        has_redundancy = true;
2069                        // When redundancy is present, the redundant CELT frame
2070                        // provides the transition audio. We skip the main CELT
2071                        // decode for this sub-frame (the SILK output stands alone)
2072                        // — a simplified version of libopus's behaviour where the
2073                        // redundant frame is decoded separately and crossfaded.
2074                        true
2075                    } else {
2076                        false
2077                    };
2078
2079                    if skip_celt {
2080                        self.w_celt_out[..silk_out_len].fill(0.0);
2081                    } else {
2082                        let (celt_dec, celt_planar) = (
2083                            state_mut(&mut self.celt_dec),
2084                            state_mut(&mut self.w_celt_planar),
2085                        );
2086                        celt_dec.decode_from_range_coder_with_band_range(
2087                            &mut rc,
2088                            total_bits,
2089                            sub_frame_size,
2090                            &mut celt_planar[..silk_out_len],
2091                            17,
2092                            celt_end_band,
2093                        );
2094
2095                        if self.channels == 1 {
2096                            self.w_celt_out[..silk_out_len]
2097                                .copy_from_slice(&self.w_celt_planar[..silk_out_len]);
2098                        } else {
2099                            for i in 0..sub_frame_size {
2100                                for ch in 0..self.channels {
2101                                    self.w_celt_out[i * self.channels + ch] =
2102                                        self.w_celt_planar[ch * sub_frame_size + i];
2103                                }
2104                            }
2105                        }
2106                    }
2107
2108                    let out_start = fi * silk_out_len;
2109                    let total = silk_out_len.min(output.len() - out_start);
2110                    for j in 0..total {
2111                        output[out_start + j] =
2112                            (self.w_silk_out[j] + self.w_celt_out[j]).clamp(-1.0, 1.0);
2113                    }
2114                }
2115                decoded_total
2116            }
2117        };
2118
2119        // Apply PLC-style bridging at mode transitions (libopus
2120        // opus_decoder.c:660-679). The first F2_5 of the output is replaced
2121        // with the previous frame's tail (PLC bridge), and the next F2_5 is
2122        // crossfaded between the bridge and the new frame's CELT output.
2123        // F5 = Fs/200, F2_5 = Fs/400.
2124        let f2_5 = self.sampling_rate as usize / 400;
2125        let f5 = f2_5 * 2;
2126        if mode_transition && f5 > 0 && decoded_total >= f5 {
2127            let window = modes::default_mode().window;
2128            let inc = (48000 / self.sampling_rate) as usize;
2129            let f2_5_ch = f2_5 * self.channels;
2130            let f5_ch = f5 * self.channels;
2131            // First F2_5: pure bridging audio from previous frame's tail.
2132            output[..f2_5_ch].copy_from_slice(&self.prev_pcm_tail[..f2_5_ch]);
2133            // Next F2_5: crossfade bridge → new CELT output.
2134            let new_mid: FixedVec<f32, OPUS_PCM_TAIL> =
2135                FixedVec::from_slice(&output[f2_5_ch..f5_ch]);
2136            smooth_fade(
2137                &self.prev_pcm_tail[f2_5_ch..f5_ch],
2138                &new_mid,
2139                &mut output[f2_5_ch..f5_ch],
2140                f2_5,
2141                self.channels,
2142                window,
2143                inc,
2144            );
2145        }
2146
2147        self.save_pcm_tail(output, decoded_total);
2148        self.prev_mode = Some(mode);
2149        self.prev_redundancy = has_redundancy;
2150        Ok(decoded_total)
2151    }
2152
2153    /// Conceal a lost frame (libopus `opus_decode_frame` with `data == NULL`).
2154    ///
2155    /// Only the duration comes from the caller: the placeholder ToC of a
2156    /// 1-byte packet, or `frame_size` for an empty one. Mode, bandwidth, SILK
2157    /// rate and resamplers stay the previous packet's, so a placeholder such
2158    /// as `[0]` (SILK NB 10 ms) can no longer switch a CELT or wideband stream
2159    /// into another mode and fake two mode transitions (issue #15).
2160    fn decode_lost(
2161        &mut self,
2162        toc: Option<u8>,
2163        frame_size: usize,
2164        output: &mut [f32],
2165    ) -> Result<usize, &'static str> {
2166        let f2_5 = self.sampling_rate as usize / 400;
2167        let (f5, f10, f20) = (2 * f2_5, 4 * f2_5, 8 * f2_5);
2168
2169        let total = match toc {
2170            None => {
2171                if frame_size == 0 || !frame_size.is_multiple_of(f2_5) {
2172                    return Err("Lost frame: frame_size must be a multiple of 2.5 ms");
2173                }
2174                frame_size
2175            }
2176            Some(toc) => {
2177                // A ToC-only packet: code 0 is one empty frame, codes 1 and 2
2178                // are two; code 3 lacks its frame-count byte (libopus rejects).
2179                let count = match toc & 0x03 {
2180                    0 => 1,
2181                    1 | 2 => 2,
2182                    _ => return Err("Code 3 packet too short"),
2183                };
2184                let per_frame = frame_samples_from_toc(toc, self.sampling_rate)
2185                    .ok_or("Invalid TOC for sampling rate")?;
2186                let total = per_frame * count;
2187                if frame_size < total {
2188                    return Err("frame_size too small for packet");
2189                }
2190                total
2191            }
2192        };
2193        let channels = self.channels;
2194        if output.len() < total * channels {
2195            return Err("Output buffer too small for packet");
2196        }
2197        output[total * channels..].fill(0.0);
2198
2199        let mut pos = 0usize;
2200        while pos < total {
2201            let remaining = total - pos;
2202            let dst = &mut output[pos * channels..total * channels];
2203            let produced = match self.prev_mode {
2204                None => {
2205                    dst.fill(0.0);
2206                    remaining
2207                }
2208                Some(OpusMode::CeltOnly) => {
2209                    // Largest CELT frame that fits; `remaining` is a multiple
2210                    // of 2.5 ms, so one always does.
2211                    let n = [f20, f10, f5, f2_5]
2212                        .into_iter()
2213                        .find(|&c| c <= remaining)
2214                        .unwrap_or(f2_5);
2215                    self.conceal_celt(n, 0)?;
2216                    for (d, s) in dst.iter_mut().zip(&self.w_celt_out[..n * channels]) {
2217                        *d = s.clamp(-1.0, 1.0);
2218                    }
2219                    n
2220                }
2221                Some(prev @ (OpusMode::SilkOnly | OpusMode::Hybrid)) => {
2222                    // SILK conceals in 10/20 ms frames; a shorter request
2223                    // takes the head of a 10 ms frame, as libopus does.
2224                    let (ms, n) = if remaining >= f20 {
2225                        (20, f20)
2226                    } else {
2227                        (10, f10)
2228                    };
2229                    let hybrid = prev == OpusMode::Hybrid;
2230                    let rate = if hybrid {
2231                        16000
2232                    } else {
2233                        self.prev_internal_rate
2234                    };
2235                    let mut rc = RangeCoder::new_decoder(&[]);
2236                    self.decode_silk_frames(
2237                        &mut rc,
2238                        silk::decode_frame::FLAG_PACKET_LOST,
2239                        ms,
2240                        rate,
2241                        n,
2242                    )?;
2243                    if hybrid {
2244                        self.conceal_celt(n, 17)?;
2245                    } else {
2246                        self.w_celt_out[..n * channels].fill(0.0);
2247                    }
2248                    let take = n.min(remaining);
2249                    for (i, d) in dst[..take * channels].iter_mut().enumerate() {
2250                        *d = (self.w_silk_out[i] + self.w_celt_out[i]).clamp(-1.0, 1.0);
2251                    }
2252                    take
2253                }
2254            };
2255            pos += produced;
2256        }
2257
2258        self.save_pcm_tail(output, total);
2259        Ok(total)
2260    }
2261
2262    /// CELT concealment for `n` samples: decode an empty payload, which takes
2263    /// the silence path — the previous frame's MDCT overlap decays into
2264    /// silence with the decoder state kept continuous. Output lands
2265    /// interleaved in `w_celt_out[..n * channels]`.
2266    fn conceal_celt(&mut self, n: usize, start_band: usize) -> Result<(), &'static str> {
2267        let len = n * self.channels;
2268        let end_band = modes::default_mode().eff_ebands;
2269        let mut rc = RangeCoder::new_decoder(&[]);
2270        let decoded = {
2271            let (celt_dec, planar) = (
2272                state_mut(&mut self.celt_dec),
2273                state_mut(&mut self.w_celt_planar),
2274            );
2275            celt_dec.decode_from_range_coder_with_band_range(
2276                &mut rc,
2277                0,
2278                n,
2279                &mut planar[..len],
2280                start_band,
2281                end_band,
2282            )
2283        };
2284        if decoded != n {
2285            return Err("CELT concealment failed");
2286        }
2287        for i in 0..n {
2288            for ch in 0..self.channels {
2289                self.w_celt_out[i * self.channels + ch] = self.w_celt_planar[ch * n + i];
2290            }
2291        }
2292        Ok(())
2293    }
2294
2295    /// Keep the last F5 of output for the next mode-transition bridge.
2296    fn save_pcm_tail(&mut self, output: &[f32], decoded: usize) {
2297        let tail_len = (self.sampling_rate as usize / 200) * self.channels;
2298        let out_total = decoded * self.channels;
2299        if out_total >= tail_len && tail_len <= self.prev_pcm_tail.len() {
2300            self.prev_pcm_tail[..tail_len]
2301                .copy_from_slice(&output[out_total - tail_len..out_total]);
2302        }
2303    }
2304}
2305
2306impl OpusDecoder {
2307    /// Decode one Opus frame of SILK audio — `duration_ms` at `internal_rate`
2308    /// — into `w_silk_out[..api_len * channels]`, interleaved at the API rate.
2309    ///
2310    /// A 40/60 ms payload carries 2/3 internal SILK frames in one range-coded
2311    /// stream; libopus loops silk_Decode until the full duration is produced,
2312    /// with new_packet set only on the first call (issue #27). With
2313    /// `FLAG_PACKET_LOST` the same loop drives SILK packet-loss concealment.
2314    fn decode_silk_frames(
2315        &mut self,
2316        rc: &mut RangeCoder,
2317        lost_flag: i32,
2318        duration_ms: i32,
2319        internal_rate: i32,
2320        api_len: usize,
2321    ) -> Result<(), &'static str> {
2322        let internal_frame_size = (duration_ms * internal_rate / 1000) as usize;
2323        let pcm_i16_len = internal_frame_size * self.channels;
2324        debug_assert!(pcm_i16_len <= self.w_pcm_i16.len());
2325        let out_len = api_len * self.channels;
2326        self.w_silk_out[..out_len].fill(0.0);
2327
2328        let mut frame_pos = 0usize; // internal-rate samples decoded so far
2329        let mut new_packet = true;
2330        while frame_pos < internal_frame_size {
2331            let ret = {
2332                let (silk_dec, pcm_i16) = (
2333                    state_mut(&mut self.silk_dec),
2334                    state_mut(&mut self.w_pcm_i16),
2335                );
2336                silk_dec.decode(
2337                    rc,
2338                    &mut pcm_i16[..pcm_i16_len],
2339                    lost_flag,
2340                    new_packet,
2341                    duration_ms,
2342                    internal_rate,
2343                )
2344            };
2345            new_packet = false;
2346
2347            if ret < 0 {
2348                return Err("SILK decoding failed");
2349            }
2350            let decoded_samples = ret as usize;
2351            if decoded_samples == 0 {
2352                break;
2353            }
2354
2355            // SILK decoder outputs planar for THIS frame:
2356            // ch0 at [0..fl], ch1 at [fl..2*fl].
2357            //
2358            // libopus always runs `silk_resampler`, even when the API and SILK
2359            // internal rates are equal: the Copy path still carries the
2360            // resampler's `inputDelay` (12 samples at 16k, 4 at 8k). Bypassing
2361            // it made equal-rate SILK output early versus libopus (and versus
2362            // opus-rs's own resampled paths).
2363            let resampled_len =
2364                decoded_samples * self.sampling_rate as usize / internal_rate as usize;
2365            let api_pos = frame_pos * self.sampling_rate as usize / internal_rate as usize;
2366            let copy_len = resampled_len.min(api_len - api_pos);
2367            debug_assert!(resampled_len * self.channels <= self.w_pcm_resampled.len());
2368            // Resample channel 0.
2369            {
2370                let (res, inp, out) = (
2371                    &mut self.silk_resampler,
2372                    state_ref(&self.w_pcm_i16),
2373                    state_mut(&mut self.w_pcm_resampled),
2374                );
2375                res.process(
2376                    &mut out[..resampled_len],
2377                    &inp[..decoded_samples],
2378                    decoded_samples as i32,
2379                );
2380            }
2381            // Resample channel 1 (stereo only).
2382            if self.channels == 2 {
2383                let (res, inp, out) = (
2384                    &mut self.silk_resampler_2,
2385                    state_ref(&self.w_pcm_i16),
2386                    state_mut(&mut self.w_pcm_resampled),
2387                );
2388                res.process(
2389                    &mut out[resampled_len..2 * resampled_len],
2390                    &inp[decoded_samples..2 * decoded_samples],
2391                    decoded_samples as i32,
2392                );
2393            }
2394            for i in 0..copy_len {
2395                for ch in 0..self.channels {
2396                    let v = self.w_pcm_resampled[ch * resampled_len + i] as f32 / 32768.0;
2397                    let idx = (api_pos + i) * self.channels + ch;
2398                    if idx < out_len {
2399                        self.w_silk_out[idx] = v;
2400                    }
2401                }
2402            }
2403            frame_pos += decoded_samples;
2404        }
2405        Ok(())
2406    }
2407
2408    #[inline(always)]
2409    fn celt_end_band_from_toc(&self, toc: u8) -> usize {
2410        let mode = modes::default_mode();
2411        let top = mode.eff_ebands;
2412        if mode_from_toc(toc) == OpusMode::CeltOnly && toc >= 0x80 {
2413            const FROM_OPUS_TABLE: [u8; 16] = [
2414                0x80, 0x88, 0x90, 0x98, 0x40, 0x48, 0x50, 0x58, 0x20, 0x28, 0x30, 0x38, 0x00, 0x08,
2415                0x10, 0x18,
2416            ];
2417            let idx = ((toc >> 3) - 16) as usize;
2418            let data0 = FROM_OPUS_TABLE[idx] | (toc & 0x7);
2419            let trim = (data0 >> 5) as usize;
2420            return top.saturating_sub(2 * trim).max(1);
2421        }
2422        top
2423    }
2424}
2425
2426fn frame_rate_from_params(sampling_rate: i32, frame_size: usize) -> Option<i32> {
2427    let frame_size = frame_size as i32;
2428    if frame_size <= 0 {
2429        return None;
2430    }
2431    // libopus `frame_size_select`: a frame is 2.5, 5, 10, 20, 40 or 60 ms.
2432    // Validating by duration (not by `sampling_rate % frame_size == 0`) is what
2433    // lets 60 ms through: its rate is 16.67 frames/s, so the old divisibility
2434    // test rejected it before encoding could start (issue #35). Longer frames
2435    // (>60 ms) are handled by the caller's multiframe path.
2436    let fs = sampling_rate;
2437    let valid = frame_size == fs / 400
2438        || frame_size == fs / 200
2439        || frame_size == fs / 100
2440        || frame_size == fs / 50
2441        || frame_size == fs / 25
2442        || 50 * frame_size == 3 * fs;
2443    if !valid {
2444        return None;
2445    }
2446    Some(fs / frame_size)
2447}
2448
2449fn gen_toc(mode: OpusMode, frame_rate: i32, bandwidth: Bandwidth, channels: usize) -> u8 {
2450    let mut rate = frame_rate;
2451    let mut period = 0;
2452    while rate < 400 {
2453        rate <<= 1;
2454        period += 1;
2455    }
2456
2457    let mut toc = match mode {
2458        OpusMode::SilkOnly => {
2459            let bw = (bandwidth as i32 - Bandwidth::Narrowband as i32) << 5;
2460            let per = (period - 2) << 3;
2461            (bw | per) as u8
2462        }
2463        OpusMode::CeltOnly => {
2464            let mut tmp = bandwidth as i32 - Bandwidth::Mediumband as i32;
2465            if tmp < 0 {
2466                tmp = 0;
2467            }
2468            let per = period << 3;
2469            (0x80 | (tmp << 5) | per) as u8
2470        }
2471        OpusMode::Hybrid => {
2472            let base_config = if bandwidth == Bandwidth::Superwideband {
2473                12
2474            } else {
2475                14
2476            };
2477            let period_offset = if frame_rate >= 100 { 0 } else { 1 };
2478            ((base_config + period_offset) << 3) as u8
2479        }
2480    };
2481
2482    if channels == 2 {
2483        toc |= 0x04;
2484    }
2485    toc
2486}
2487
2488fn mode_from_toc(toc: u8) -> OpusMode {
2489    if toc & 0x80 != 0 {
2490        OpusMode::CeltOnly
2491    } else if toc & 0x60 == 0x60 {
2492        OpusMode::Hybrid
2493    } else {
2494        OpusMode::SilkOnly
2495    }
2496}
2497
2498fn bandwidth_from_toc(toc: u8) -> Bandwidth {
2499    let mode = mode_from_toc(toc);
2500    match mode {
2501        OpusMode::SilkOnly => {
2502            let bw_bits = (toc >> 5) & 0x03;
2503            match bw_bits {
2504                0 => Bandwidth::Narrowband,
2505                1 => Bandwidth::Mediumband,
2506                2 => Bandwidth::Wideband,
2507                _ => Bandwidth::Wideband,
2508            }
2509        }
2510        OpusMode::Hybrid => {
2511            let bw_bit = (toc >> 4) & 0x01;
2512            if bw_bit == 0 {
2513                Bandwidth::Superwideband
2514            } else {
2515                Bandwidth::Fullband
2516            }
2517        }
2518        OpusMode::CeltOnly => {
2519            let bw_bits = (toc >> 5) & 0x03;
2520            match bw_bits {
2521                0 => Bandwidth::Mediumband,
2522                1 => Bandwidth::Wideband,
2523                2 => Bandwidth::Superwideband,
2524                3 => Bandwidth::Fullband,
2525                _ => Bandwidth::Fullband,
2526            }
2527        }
2528    }
2529}
2530
2531fn frame_duration_ms_from_toc(toc: u8) -> i32 {
2532    let mode = mode_from_toc(toc);
2533    match mode {
2534        OpusMode::SilkOnly => {
2535            let config = (toc >> 3) & 0x03;
2536            match config {
2537                0 => 10,
2538                1 => 20,
2539                2 => 40,
2540                3 => 60,
2541                _ => 20,
2542            }
2543        }
2544        OpusMode::Hybrid => {
2545            let config = (toc >> 3) & 0x01;
2546            if config == 0 { 10 } else { 20 }
2547        }
2548        OpusMode::CeltOnly => {
2549            let config = (toc >> 3) & 0x03;
2550            match config {
2551                0 => 2,
2552                1 => 5,
2553                2 => 10,
2554                3 => 20,
2555                _ => 20,
2556            }
2557        }
2558    }
2559}
2560
2561/// Compute the per-frame sample count implied by the TOC byte at a given
2562/// sampling rate. For CELT this uses the frame-rate derivation (which handles
2563/// the 2.5 ms case correctly, unlike integer millisecond arithmetic).
2564fn frame_samples_from_toc(toc: u8, sampling_rate: i32) -> Option<usize> {
2565    let mode = mode_from_toc(toc);
2566    match mode {
2567        OpusMode::CeltOnly => {
2568            let period = ((toc >> 3) & 0x03) as i32;
2569            let frame_rate = 400 >> period;
2570            if frame_rate == 0 || sampling_rate % frame_rate != 0 {
2571                return None;
2572            }
2573            Some((sampling_rate / frame_rate) as usize)
2574        }
2575        OpusMode::SilkOnly | OpusMode::Hybrid => {
2576            let duration_ms = frame_duration_ms_from_toc(toc);
2577            Some((sampling_rate as i64 * duration_ms as i64 / 1000) as usize)
2578        }
2579    }
2580}
2581
2582fn channels_from_toc(toc: u8) -> usize {
2583    if toc & 0x04 != 0 { 2 } else { 1 }
2584}
2585
2586/// Crossfade two signals using a squared-sine window (libopus smooth_fade).
2587/// `window` is the 120-sample CELT window at 48 kHz; `inc` = 48000/Fs strides it.
2588fn smooth_fade(
2589    in1: &[f32],
2590    in2: &[f32],
2591    out: &mut [f32],
2592    overlap: usize,
2593    channels: usize,
2594    window: &[f32],
2595    inc: usize,
2596) {
2597    for c in 0..channels {
2598        for i in 0..overlap {
2599            let wi = i * inc;
2600            if wi >= window.len() {
2601                break;
2602            }
2603            let w = window[wi] * window[wi];
2604            out[i * channels + c] = w * in2[i * channels + c] + (1.0 - w) * in1[i * channels + c];
2605        }
2606    }
2607}
2608
2609/// Parse an Opus frame length per RFC 6716 §3.2.1, identical to libopus
2610/// `parse_size()`:
2611///   - `0`: no frame (DTX / lost packet)
2612///   - `1..=251`: length of the frame in bytes (one byte consumed)
2613///   - `252..=255`: a second byte is read; length = `second*4 + first`
2614///
2615/// Returns `(length, bytes_consumed)`.
2616pub(crate) fn parse_frame_size(data: &[u8]) -> Result<(usize, usize), &'static str> {
2617    let first = *data.first().ok_or("truncated frame length")? as usize;
2618    if first < 252 {
2619        Ok((first, 1))
2620    } else {
2621        let second = *data.get(1).ok_or("truncated frame length")? as usize;
2622        Ok((second * 4 + first, 2))
2623    }
2624}
2625
2626#[cfg(all(test, feature = "std"))]
2627mod tests {
2628    use super::*;
2629
2630    fn frame_size_from_toc(toc: u8, sampling_rate: i32) -> Option<usize> {
2631        let mode = mode_from_toc(toc);
2632        match mode {
2633            OpusMode::CeltOnly => {
2634                let period = ((toc >> 3) & 0x03) as i32;
2635                let frame_rate = 400 >> period;
2636                if frame_rate == 0 || sampling_rate % frame_rate != 0 {
2637                    return None;
2638                }
2639                Some((sampling_rate / frame_rate) as usize)
2640            }
2641            OpusMode::SilkOnly => {
2642                let duration_ms = frame_duration_ms_from_toc(toc);
2643                Some((sampling_rate as i64 * duration_ms as i64 / 1000) as usize)
2644            }
2645            OpusMode::Hybrid => {
2646                let duration_ms = frame_duration_ms_from_toc(toc);
2647                Some((sampling_rate as i64 * duration_ms as i64 / 1000) as usize)
2648            }
2649        }
2650    }
2651
2652    #[test]
2653    fn gen_toc_matches_celt_reference_values() {
2654        let sampling_rate = 48_000;
2655        let cases = [
2656            (120usize, 0xE0u8),
2657            (240usize, 0xE8u8),
2658            (480usize, 0xF0u8),
2659            (960usize, 0xF8u8),
2660        ];
2661
2662        for (frame_size, expected_toc) in cases {
2663            let frame_rate = frame_rate_from_params(sampling_rate, frame_size).unwrap();
2664            let toc = gen_toc(OpusMode::CeltOnly, frame_rate, Bandwidth::Fullband, 1);
2665            assert_eq!(
2666                toc, expected_toc,
2667                "frame_size {} expected TOC {:02X} got {:02X}",
2668                frame_size, expected_toc, toc
2669            );
2670            let decoded_size = frame_size_from_toc(toc, sampling_rate).unwrap();
2671            assert_eq!(decoded_size, frame_size);
2672        }
2673
2674        let stereo_toc = gen_toc(
2675            OpusMode::CeltOnly,
2676            frame_rate_from_params(sampling_rate, 960).unwrap(),
2677            Bandwidth::Fullband,
2678            2,
2679        );
2680        assert_eq!(channels_from_toc(stereo_toc), 2);
2681    }
2682
2683    #[test]
2684    fn test_celt_decoder_large_frame_sizes() {
2685        let sampling_rate = 48000;
2686        let channels = 1;
2687
2688        let mut decoder = OpusDecoder::new(sampling_rate, channels).unwrap();
2689
2690        let frame_sizes = [120, 240, 480, 960];
2691
2692        for frame_size in frame_sizes {
2693            let toc = gen_toc(
2694                OpusMode::CeltOnly,
2695                frame_rate_from_params(sampling_rate, frame_size).unwrap(),
2696                Bandwidth::Fullband,
2697                channels,
2698            );
2699            let packet = [toc, 0, 0, 0, 0];
2700
2701            let mut output = vec![0.0f32; frame_size * channels];
2702
2703            let _ = decoder.decode(&packet, frame_size, &mut output);
2704        }
2705
2706        let channels = 2;
2707        let mut decoder = OpusDecoder::new(sampling_rate, channels).unwrap();
2708
2709        for frame_size in frame_sizes {
2710            let toc = gen_toc(
2711                OpusMode::CeltOnly,
2712                frame_rate_from_params(sampling_rate, frame_size).unwrap(),
2713                Bandwidth::Fullband,
2714                channels,
2715            );
2716            let packet = [toc, 0, 0, 0, 0];
2717
2718            let mut output = vec![0.0f32; frame_size * channels];
2719            let _ = decoder.decode(&packet, frame_size, &mut output);
2720        }
2721    }
2722
2723    #[test]
2724    fn test_celt_decoder_edge_case_frame_sizes() {
2725        let sampling_rate = 48000;
2726        let channels = 1;
2727        let mut decoder = OpusDecoder::new(sampling_rate, channels).unwrap();
2728
2729        let edge_sizes = [2048, 2167, 2168, 2169, 2880, 3072];
2730
2731        for frame_size in edge_sizes {
2732            let mut output = vec![0.0f32; frame_size * channels];
2733
2734            let _ = decoder.decode(&[0x80, 0, 0, 0], frame_size, &mut output);
2735        }
2736    }
2737
2738    // Regression test for: "index out of bounds: the len is 48 but the index is 119"
2739    // Root cause: frame_size=48 at 48kHz gives frame_rate=1000, which is not a valid
2740    // Hybrid-mode frame rate but was not validated.  CELT's lm-search then silently
2741    // fell back to lm=0, computed n2=120, and wrote output[119] into a 48-element
2742    // slice.  Triggered via G.729-decoded PCM (8kHz) passed to a 48kHz Opus encoder
2743    // without proper resampling, so the encoder received 48 samples instead of 480.
2744    #[test]
2745    fn test_invalid_small_frame_size_returns_error_not_panic() {
2746        let mut enc = OpusEncoder::new(48000, 2, Application::Voip).unwrap();
2747        enc.bitrate_bps = 64000;
2748        enc.complexity = 5;
2749        enc.use_cbr = true;
2750
2751        // 48 samples at 48kHz = 1ms → frame_rate=1000, invalid for Hybrid mode.
2752        let input = vec![0.0f32; 48 * 2]; // stereo interleaved
2753        let mut output = vec![0u8; 256];
2754
2755        let result = enc.encode(&input, 48, &mut output);
2756        assert!(
2757            result.is_err(),
2758            "encode with invalid frame_size=48 should return Err, not panic"
2759        );
2760    }
2761
2762    // Also verify that the Audio application path (always Hybrid at 48 kHz) rejects
2763    // the same bad frame size.
2764    #[test]
2765    fn test_invalid_small_frame_size_audio_application_returns_error() {
2766        let mut enc = OpusEncoder::new(48000, 1, Application::Audio).unwrap();
2767        let input = vec![0.0f32; 48];
2768        let mut output = vec![0u8; 256];
2769
2770        let result = enc.encode(&input, 48, &mut output);
2771        assert!(
2772            result.is_err(),
2773            "Audio/48kHz encoder with frame_size=48 should return Err"
2774        );
2775    }
2776}