Skip to main content

opus_rs/
celt.rs

1use crate::bands::{
2    SPREAD_NONE, SPREAD_NORMAL, compute_band_energies, denormalise_bands, haar1, log2amp,
3    normalise_bands, quant_all_bands, spreading_decision,
4};
5use crate::modes::{CeltMode, SPREAD_ICDF, TAPSET_ICDF, TF_SELECT_TABLE, TRIM_ICDF};
6use crate::quant_bands::{
7    quant_coarse_energy_advanced, quant_energy_finalise, quant_fine_energy, unquant_coarse_energy,
8    unquant_energy_finalise, unquant_fine_energy,
9};
10use crate::range_coder::RangeCoder;
11use crate::rate::{BITRES, clt_compute_allocation};
12use core::mem::MaybeUninit;
13
14#[cfg(not(feature = "std"))]
15use crate::compat::Math;
16use crate::fixedvec::FixedVec;
17#[inline]
18fn bitrate_to_bits(bitrate: i32, fs: i32, frame_size: i32) -> i32 {
19    bitrate * 6 / (6 * fs / frame_size)
20}
21
22const OPUS_BITRATE_MAX: i32 = -1;
23#[allow(clippy::too_many_arguments)]
24fn compute_vbr(
25    mode: &CeltMode,
26    analysis: &AnalysisInfo,
27    base_target: i32,
28    lm: i32,
29    bitrate: i32,
30    last_coded_bands: i32,
31    c: i32,
32    intensity: i32,
33    constrained_vbr: bool,
34    stereo_saving: f32,
35    tot_boost: i32,
36    tf_estimate: f32,
37    pitch_change: i32,
38    max_depth: f32,
39    lfe: bool,
40    has_surround_mask: bool,
41    surround_masking: f32,
42    temporal_vbr: f32,
43) -> i32 {
44    let nb_ebands = mode.nb_ebands as i32;
45    let mut coded_bands = if last_coded_bands != 0 {
46        last_coded_bands
47    } else {
48        nb_ebands
49    };
50    coded_bands = coded_bands.clamp(0, nb_ebands);
51    let mut coded_bins = mode.e_bands[coded_bands as usize] as i32 * (1 << lm);
52    if c == 2 {
53        let idx = intensity.min(coded_bands) as usize;
54        coded_bins += mode.e_bands[idx] as i32 * (1 << lm);
55    }
56    let mut target = base_target;
57    if analysis.valid && analysis.activity < 0.4 {
58        target -= ((coded_bins << BITRES) as f32 * (0.4 - analysis.activity)) as i32;
59    }
60    if c == 2 {
61        let coded_stereo_bands = intensity.min(coded_bands);
62        let coded_stereo_dof =
63            mode.e_bands[coded_stereo_bands as usize] as i32 * (1 << lm) - coded_stereo_bands;
64        let max_frac = (0.8 * coded_stereo_dof as f32) / coded_bins as f32;
65        let ss = stereo_saving.min(1.0);
66        let adjust = ((ss - 0.1).max(0.0) * coded_stereo_dof as f32 * (1 << BITRES) as f32) as i32;
67        let save = (max_frac * target as f32) as i32;
68        target -= save.min(adjust);
69    }
70    target += tot_boost - (19 << lm);
71    // tf boost: libopus SHL32(MULT16_32_Q15(tf-0.044, target),1) collapses to (tf-0.044)*target in float
72    target += ((tf_estimate - 0.044) * target as f32) as i32;
73    if analysis.valid && !lfe {
74        let tonal = (analysis.tonality - 0.15).max(0.0) - 0.12;
75        let mut tonal_target = target + ((coded_bins << BITRES) as f32 * 1.2 * tonal) as i32;
76        if pitch_change != 0 {
77            tonal_target += ((coded_bins << BITRES) as f32 * 0.8) as i32;
78        }
79        target = tonal_target;
80    }
81    if has_surround_mask && !lfe {
82        let surround_target =
83            target + (surround_masking * coded_bins as f32 * (1 << BITRES) as f32) as i32;
84        target = (target / 4).max(surround_target);
85    }
86    // floor depth: only clamp when max_depth is meaningful (>0); stub call passes 0 to avoid spurious 0.665*base
87    if max_depth > 0.0 {
88        let bins = mode.e_bands[(nb_ebands - 2) as usize] as i32 * (1 << lm);
89        let floor_depth = ((c * bins * (1 << BITRES)) as f32 * max_depth) as i32;
90        let floor_depth = floor_depth.max(target >> 2);
91        target = target.min(floor_depth);
92    }
93    if (!has_surround_mask || lfe) && constrained_vbr {
94        target = base_target + ((target - base_target) as f32 * 0.67) as i32;
95    }
96    if !has_surround_mask && tf_estimate < 0.2 {
97        let amount = 0.0000031 * (32000.min((96000 - bitrate).max(0)) as f32);
98        let tvbr_factor = temporal_vbr * amount;
99        target += (tvbr_factor * target as f32) as i32;
100    }
101    target.min(2 * base_target)
102}
103const EMEANS_F: [f32; 25] = [
104    6.4375, 6.25, 5.75, 5.3125, 5.0625, 4.8125, 4.5, 4.375, 4.875, 4.6875, 4.5625, 4.4375, 4.875,
105    4.625, 4.3125, 4.5, 4.375, 4.625, 4.75, 4.4375, 3.75, 3.75, 3.75, 3.75, 3.75,
106];
107fn compute_max_depth(
108    mode: &CeltMode,
109    band_log_e: &[f32],
110    nb_ebands: usize,
111    c: usize,
112    end: usize,
113    lsb_depth: i32,
114) -> f32 {
115    let mut max_depth: f32 = -31.9;
116    for ch in 0..c {
117        for i in 0..end {
118            let log_n = mode.log_n[i] as f32;
119            let e_mean = if i < EMEANS_F.len() { EMEANS_F[i] } else { 0.0 };
120            let noise_floor = 0.0625 * log_n + 0.5 + (9 - lsb_depth) as f32 - e_mean
121                + 0.0062 * ((i + 5) * (i + 5)) as f32;
122            let v = band_log_e[ch * nb_ebands + i] - noise_floor;
123            if v > max_depth {
124                max_depth = v;
125            }
126        }
127    }
128    max_depth
129}
130
131// --- Heap-free buffer capacity constants (all sized for the worst case:
132//     2 channels, 48 kHz, max frame). Runtime construction uses smaller logical
133//     lengths when fewer channels / bands are active; the FixedVec tracks that. ---
134/// Maximum channel count supported by the API.
135const CELT_MAX_CHANNELS: usize = 2;
136/// Number of energy bands (`nb_ebands`) for the 48 kHz / 120-overlap mode.
137const CELT_NB_EBANDS: usize = 21;
138/// `nb_ebands * max_channels` = worst-case per-band-per-channel element count.
139const CELT_NB_X_CH: usize = CELT_NB_EBANDS * CELT_MAX_CHANNELS;
140/// MDCT overlap for the default mode.
141const CELT_OVERLAP: usize = 120;
142/// CELT synthesis / decode memory window per channel.
143const CELT_CHANNEL_MEM: usize = 2048 + CELT_OVERLAP;
144/// `channels * channel_mem` worst case.
145const CELT_SYN_MEM: usize = CELT_MAX_CHANNELS * CELT_CHANNEL_MEM;
146/// Prefilter comb-filter memory: `channels * COMBFILTER_MAXPERIOD`.
147const CELT_PREFILTER_MEM: usize = CELT_MAX_CHANNELS * COMBFILTER_MAXPERIOD;
148/// Encoder input buffer stride: `(MAX_FRAME_SIZE + overlap) * channels`.
149const CELT_BUFSTRIDE: usize = (MAX_FRAME_SIZE + CELT_OVERLAP) * CELT_MAX_CHANNELS;
150/// `MAX_FRAME_SIZE * channels`.
151const CELT_FRAME_X_CH: usize = MAX_FRAME_SIZE * CELT_MAX_CHANNELS;
152/// Frequency scratch (+4 padding for NEON pre-rotation over-read).
153const CELT_W_FREQ: usize = CELT_FRAME_X_CH + 4;
154/// PVQ stride-access scratch: `frame_x_ch + STRIDE_ACCESS_PAD`.
155const CELT_W_X_ENC: usize = CELT_FRAME_X_CH + STRIDE_ACCESS_PAD;
156/// Prefilter pre-scratch: `channels * (COMBFILTER_MAXPERIOD + MAX_FRAME_SIZE)`.
157const CELT_PREFILTER_PRE: usize = CELT_MAX_CHANNELS * (COMBFILTER_MAXPERIOD + MAX_FRAME_SIZE);
158/// Prefilter pitch buffer: `(COMBFILTER_MAXPERIOD + MAX_FRAME_SIZE) >> 1`.
159const CELT_PREFILTER_PITCH: usize = (COMBFILTER_MAXPERIOD + MAX_FRAME_SIZE) >> 1;
160/// Decoder `w_x`: `DECODE_BUFFER_SIZE * channels + STRIDE_ACCESS_PAD`.
161const CELT_W_X_DEC: usize = DECODE_BUFFER_SIZE * CELT_MAX_CHANNELS + STRIDE_ACCESS_PAD;
162/// Decoder `w_freq`: `DECODE_BUFFER_SIZE * channels + 4`.
163const CELT_W_FREQ_DEC: usize = DECODE_BUFFER_SIZE * CELT_MAX_CHANNELS + 4;
164/// Decoder `decode_mem`: `channels * (DECODE_BUFFER_SIZE + overlap)`.
165const CELT_DECODE_MEM: usize = CELT_MAX_CHANNELS * (DECODE_BUFFER_SIZE + CELT_OVERLAP);
166
167/// CELT internal-to-API decimation factor (port of libopus `resampling_factor`).
168/// The CELT decoder always runs at 48 kHz internally; the output is decimated
169/// by this factor to reach the API sampling rate.
170fn resampling_factor(sampling_rate: i32) -> usize {
171    match sampling_rate {
172        48000 => 1,
173        24000 => 2,
174        16000 => 3,
175        12000 => 4,
176        8000 => 6,
177        _ => 1,
178    }
179}
180
181/// libopus `compute_mdcts` with `upsample != 1`: keep only the first
182/// `N/upsample` coefficients (the API Nyquist band), scale them by `upsample`
183/// to preserve energy after zero-stuffing, and zero the rest.
184fn upsample_scale_zero(freq: &mut [f32], frame_size: usize, upsample: usize, channels: usize) {
185    if upsample <= 1 {
186        return;
187    }
188    let bound = frame_size / upsample;
189    for c in 0..channels {
190        let base = c * frame_size;
191        for v in freq[base..base + bound].iter_mut() {
192            *v *= upsample as f32;
193        }
194        freq[base + bound..base + frame_size].fill(0.0);
195    }
196}
197
198#[cfg(target_arch = "aarch64")]
199use core::arch::aarch64::*;
200
201#[cfg(target_arch = "aarch64")]
202#[inline(always)]
203#[allow(unsafe_op_in_unsafe_fn)]
204unsafe fn sum_abs_neon(x: &[f32], n: usize) -> f32 {
205    let mut sum_vec = vdupq_n_f32(0.0);
206    let mut i = 0;
207
208    while i + 16 <= n {
209        let x0 = vld1q_f32(x.as_ptr().add(i));
210        let x1 = vld1q_f32(x.as_ptr().add(i + 4));
211        let x2 = vld1q_f32(x.as_ptr().add(i + 8));
212        let x3 = vld1q_f32(x.as_ptr().add(i + 12));
213
214        sum_vec = vfmaq_f32(sum_vec, vabsq_f32(x0), vdupq_n_f32(1.0));
215        sum_vec = vfmaq_f32(sum_vec, vabsq_f32(x1), vdupq_n_f32(1.0));
216        sum_vec = vfmaq_f32(sum_vec, vabsq_f32(x2), vdupq_n_f32(1.0));
217        sum_vec = vfmaq_f32(sum_vec, vabsq_f32(x3), vdupq_n_f32(1.0));
218
219        i += 16;
220    }
221
222    while i + 8 <= n {
223        let x0 = vld1q_f32(x.as_ptr().add(i));
224        let x1 = vld1q_f32(x.as_ptr().add(i + 4));
225        sum_vec = vfmaq_f32(sum_vec, vabsq_f32(x0), vdupq_n_f32(1.0));
226        sum_vec = vfmaq_f32(sum_vec, vabsq_f32(x1), vdupq_n_f32(1.0));
227        i += 8;
228    }
229
230    while i + 4 <= n {
231        let x0 = vld1q_f32(x.as_ptr().add(i));
232        sum_vec = vfmaq_f32(sum_vec, vabsq_f32(x0), vdupq_n_f32(1.0));
233        i += 4;
234    }
235
236    let mut sum = vaddvq_f32(sum_vec);
237
238    for j in i..n {
239        sum += x[j].abs();
240    }
241
242    sum
243}
244
245#[inline(always)]
246fn sum_abs(x: &[f32]) -> f32 {
247    #[cfg(target_arch = "x86_64")]
248    unsafe {
249        if crate::compat::x86_has_avx() {
250            return sum_abs_avx(x, x.len());
251        }
252    }
253    #[cfg(target_arch = "aarch64")]
254    unsafe {
255        sum_abs_neon(x, x.len())
256    }
257    #[cfg(not(target_arch = "aarch64"))]
258    {
259        x.iter().map(|&v| v.abs()).sum()
260    }
261}
262
263const MAX_FRAME_SIZE: usize = 2880;
264
265/// Largest CELT-internal synthesis buffer. The CELT decoder writes `n + overlap`
266/// MDCT samples per channel where `n ≤ frame_size ≤ 960` (20 ms @ 48 kHz); 2048
267/// is the libopus reference value (`DECODE_BUFFER_SIZE`) and gives ample margin.
268/// (Reduced from a previous 3072 to shrink the heap-free footprint.)
269const DECODE_BUFFER_SIZE: usize = 2048;
270
271const INV_TABLE: [u8; 128] = [
272    255, 255, 156, 110, 86, 70, 59, 51, 45, 40, 37, 33, 31, 28, 26, 25, 23, 22, 21, 20, 19, 18, 17,
273    16, 16, 15, 15, 14, 13, 13, 12, 12, 12, 12, 11, 11, 11, 10, 10, 10, 9, 9, 9, 9, 9, 9, 8, 8, 8,
274    8, 8, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
275    5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3,
276    3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2,
277];
278
279const MAX_TRANSIENT_LEN: usize = 3000;
280
281#[derive(Debug, Clone, Copy)]
282pub struct AnalysisInfo {
283    pub valid: bool,
284    pub tonality: f32,
285    pub tonality_slope: f32,
286    pub noisiness: f32,
287    pub activity: f32,
288    pub music_prob: f32,
289    pub music_prob_min: f32,
290    pub music_prob_max: f32,
291    pub bandwidth: i32,
292    pub activity_probability: f32,
293    pub max_pitch_ratio: f32,
294    pub leak_boost: [u8; 19], // LEAK_BANDS = 19
295}
296
297impl Default for AnalysisInfo {
298    fn default() -> Self {
299        Self {
300            valid: false,
301            tonality: 0.0,
302            tonality_slope: 0.0,
303            noisiness: 0.0,
304            activity: 0.0,
305            music_prob: 0.0,
306            music_prob_min: 0.0,
307            music_prob_max: 0.0,
308            bandwidth: 0,
309            activity_probability: 0.0,
310            max_pitch_ratio: 1.0,
311            leak_boost: [0; 19],
312        }
313    }
314}
315
316#[allow(clippy::too_many_arguments)]
317fn transient_analysis(
318    input: &[f32],
319    len: usize,
320    channels: usize,
321    tf_estimate: &mut f32,
322    tf_chan: &mut usize,
323    allow_weak_transients: bool,
324    weak_transient: &mut bool,
325    _tone_freq: f32,
326    toneishness: f32,
327    tmp: &mut [f32],
328    tmp2: &mut [f32],
329) -> bool {
330    let mut mask_metric = 0.0f32;
331    let mut forward_decay = 0.0625f32;
332
333    *weak_transient = false;
334    if allow_weak_transients {
335        forward_decay = 0.03125f32;
336    }
337
338    let len2 = len / 2;
339    debug_assert!(len <= MAX_TRANSIENT_LEN);
340
341    for c in 0..channels {
342        let mut mem0 = 0.0f32;
343        let mut mem1 = 0.0f32;
344
345        for i in 0..len {
346            let x = input[c * len + i];
347            let y = mem0 + x;
348            let mem00 = mem0;
349            mem0 = mem0 - x + 0.5 * mem1;
350            mem1 = x - mem00;
351            tmp[i] = y;
352        }
353
354        tmp[..12].fill(0.0);
355
356        let mut mean = 0.0f32;
357        mem0 = 0.0f32;
358        for i in 0..len2 {
359            let x2 = (tmp[2 * i] * tmp[2 * i] + tmp[2 * i + 1] * tmp[2 * i + 1]) / 16.0;
360            // C float build: PSHR32(x2, 12) is an identity, so mean += x2
361            mean += x2;
362            mem0 = x2 + (1.0 - forward_decay) * mem0;
363            tmp2[i] = forward_decay * mem0;
364        }
365
366        mem0 = 0.0f32;
367        let mut max_e = 0.0f32;
368        for i in (0..len2).rev() {
369            mem0 = tmp2[i] + 0.875 * mem0;
370            tmp2[i] = 0.125 * mem0;
371            if tmp2[i] > max_e {
372                max_e = tmp2[i];
373            }
374        }
375
376        mean = (mean * max_e * 0.5 * (len2 as f32)).sqrt();
377        // C (float build): norm = len2 / (EPSILON + mean)  [EPSILON = 1e-15f]
378        let norm = (len2 as f32) / (1e-15 + mean);
379
380        // C computes the harmonic mean with INTEGER arithmetic (mask_metric and
381        // unmask are opus_int32; the 64/6 scaling is an integer division).
382        let mut unmask: i32 = 0;
383        for i in (12..(len2 - 5)).step_by(4) {
384            let id = (64.0 * norm * (tmp2[i] + 1e-15)).floor() as i32;
385            let id = id.clamp(0, 127) as usize;
386            unmask += INV_TABLE[id] as i32;
387        }
388
389        unmask = 64 * unmask * 4 / (6 * (len2 as i32 - 17));
390        if (unmask as f32) > mask_metric {
391            *tf_chan = c;
392            mask_metric = unmask as f32;
393        }
394    }
395
396    let mut is_transient = mask_metric > 200.0;
397
398    if toneishness > 0.98 && _tone_freq < 0.026 {
399        is_transient = false;
400        mask_metric = 0.0;
401    }
402
403    // C (celt_encoder.c:460-461): tf_max = max(0, sqrt(27*mask_metric) - 42);
404    // tf_estimate = sqrt(max(0, 0.0069*min(163, tf_max) - 0.139))  [float build]
405    let tf_max = ((27.0 * mask_metric).sqrt() - 42.0).max(0.0);
406    *tf_estimate = (0.0069 * tf_max.min(163.0) - 0.139).max(0.0).sqrt();
407
408    is_transient
409}
410
411fn l1_metric(tmp: &[f32], n: usize, lm: i32, bias: f32) -> f32 {
412    #[cfg(target_arch = "x86_64")]
413    unsafe {
414        if n >= 16 && crate::compat::x86_has_avx() {
415            return l1_metric_avx(tmp, n, lm, bias);
416        }
417    }
418    #[cfg(target_arch = "aarch64")]
419    {
420        if n >= 16 {
421            return unsafe { l1_metric_neon(tmp, n, lm, bias) };
422        }
423    }
424
425    let mut l1 = 0.0f32;
426    for &tv in tmp[..n].iter() {
427        l1 += tv.abs();
428    }
429    l1 + (lm as f32) * bias * l1
430}
431
432#[cfg(target_arch = "x86_64")]
433#[target_feature(enable = "avx")]
434unsafe fn sum_abs_avx(x: &[f32], n: usize) -> f32 {
435    #[cfg(target_arch = "x86")]
436    use core::arch::x86::*;
437    #[cfg(target_arch = "x86_64")]
438    use core::arch::x86_64::*;
439
440    let mut sum0 = _mm256_setzero_ps();
441    let mut sum1 = _mm256_setzero_ps();
442    let mut i = 0usize;
443    let sign_mask = _mm256_set1_ps(-0.0);
444
445    while i + 16 <= n {
446        let v0 = _mm256_loadu_ps(x.as_ptr().add(i));
447        let v1 = _mm256_loadu_ps(x.as_ptr().add(i + 8));
448        sum0 = _mm256_add_ps(sum0, _mm256_andnot_ps(sign_mask, v0));
449        sum1 = _mm256_add_ps(sum1, _mm256_andnot_ps(sign_mask, v1));
450        i += 16;
451    }
452
453    while i + 8 <= n {
454        let v = _mm256_loadu_ps(x.as_ptr().add(i));
455        sum0 = _mm256_add_ps(sum0, _mm256_andnot_ps(sign_mask, v));
456        i += 8;
457    }
458
459    let sum = _mm256_add_ps(sum0, sum1);
460    let hi = _mm256_extractf128_ps(sum, 1);
461    let lo = _mm256_castps256_ps128(sum);
462    let s4 = _mm_add_ps(lo, hi);
463    let t1 = _mm_movehl_ps(s4, s4);
464    let s2 = _mm_add_ps(s4, t1);
465    let t2 = _mm_shuffle_ps(s2, s2, 0x55);
466    let mut out = _mm_cvtss_f32(_mm_add_ss(s2, t2));
467
468    for j in i..n {
469        out += x[j].abs();
470    }
471
472    out
473}
474
475#[cfg(target_arch = "x86_64")]
476#[target_feature(enable = "avx")]
477unsafe fn l1_metric_avx(tmp: &[f32], n: usize, lm: i32, bias: f32) -> f32 {
478    let l1 = sum_abs_avx(tmp, n);
479    l1 + (lm as f32) * bias * l1
480}
481
482#[cfg(target_arch = "aarch64")]
483#[target_feature(enable = "neon")]
484unsafe fn l1_metric_neon(tmp: &[f32], n: usize, lm: i32, bias: f32) -> f32 {
485    unsafe {
486        let mut sum4 = vdupq_n_f32(0.0);
487        let mut i = 0;
488
489        while i + 15 < n {
490            let v0 = vld1q_f32(tmp.as_ptr().add(i));
491            let v1 = vld1q_f32(tmp.as_ptr().add(i + 4));
492            let v2 = vld1q_f32(tmp.as_ptr().add(i + 8));
493            let v3 = vld1q_f32(tmp.as_ptr().add(i + 12));
494
495            sum4 = vaddq_f32(sum4, vabsq_f32(v0));
496            sum4 = vaddq_f32(sum4, vabsq_f32(v1));
497            sum4 = vaddq_f32(sum4, vabsq_f32(v2));
498            sum4 = vaddq_f32(sum4, vabsq_f32(v3));
499
500            i += 16;
501        }
502
503        while i + 3 < n {
504            let v = vld1q_f32(tmp.as_ptr().add(i));
505            sum4 = vaddq_f32(sum4, vabsq_f32(v));
506            i += 4;
507        }
508
509        let sum2 = vpaddq_f32(sum4, sum4);
510        let sum1 = vpaddq_f32(sum2, sum2);
511        let mut l1 = vgetq_lane_f32(sum1, 0);
512
513        while i < n {
514            l1 += tmp[i].abs();
515            i += 1;
516        }
517
518        l1 + (lm as f32) * bias * l1
519    }
520}
521
522const MAX_NB_EBANDS: usize = 21;
523
524const MAX_TF_TMP: usize = 176;
525
526#[allow(clippy::too_many_arguments)]
527fn tf_analysis(
528    mode: &CeltMode,
529    len: usize,
530    is_transient: bool,
531    tf_res: &mut [i32],
532    lambda: i32,
533    x: &[f32],
534    n0: usize,
535    lm: i32,
536    tf_estimate: f32,
537    tf_chan: usize,
538    importance: &[i32],
539) -> i32 {
540    debug_assert!(len <= MAX_NB_EBANDS);
541    let mut metric = [0i32; MAX_NB_EBANDS];
542    let mut tmp = [0.0f32; MAX_TF_TMP];
543    let mut tmp_1 = [0.0f32; MAX_TF_TMP];
544
545    let bias = 0.04 * (-0.25f32).max(0.5 - tf_estimate);
546
547    for (i, metric_i) in metric[..len].iter_mut().enumerate() {
548        let n = ((mode.e_bands[i + 1] - mode.e_bands[i]) as usize) << lm;
549        let narrow = (mode.e_bands[i + 1] - mode.e_bands[i]) == 1;
550        let offset = tf_chan * n0 + ((mode.e_bands[i] as usize) << lm);
551        tmp[..n].copy_from_slice(&x[offset..offset + n]);
552
553        let mut l1 = l1_metric(&tmp[..n], n, if is_transient { lm } else { 0 }, bias);
554        let mut best_l1 = l1;
555        let mut best_level = 0;
556
557        if is_transient && !narrow {
558            tmp_1[..n].copy_from_slice(&tmp[..n]);
559            haar1(&mut tmp_1[..n], n >> lm, 1 << lm);
560            l1 = l1_metric(&tmp_1[..n], n, lm + 1, bias);
561            if l1 < best_l1 {
562                best_l1 = l1;
563                best_level = -1;
564            }
565        }
566
567        for k in 0..(lm + if is_transient || narrow { 0 } else { 1 }) {
568            let b = if is_transient { lm - k - 1 } else { k + 1 };
569
570            haar1(&mut tmp[..n], n >> k, 1 << k);
571            l1 = l1_metric(&tmp[..n], n, b, bias);
572
573            if l1 < best_l1 {
574                best_l1 = l1;
575                best_level = k + 1;
576            }
577        }
578
579        if is_transient {
580            *metric_i = 2 * best_level;
581        } else {
582            *metric_i = -2 * best_level;
583        }
584
585        if narrow && (*metric_i == 0 || *metric_i == -2 * lm) {
586            *metric_i -= 1;
587        }
588    }
589
590    let mut tf_select = 0;
591    let mut selcost = [0i32; 2];
592
593    for sel in 0..2 {
594        let mut cost0 = importance[0]
595            * (metric[0]
596                - 2 * TF_SELECT_TABLE[lm as usize][4 * (is_transient as usize) + 2 * sel] as i32)
597                .abs();
598        let mut cost1 = importance[0]
599            * (metric[0]
600                - 2 * TF_SELECT_TABLE[lm as usize][4 * (is_transient as usize) + 2 * sel + 1]
601                    as i32)
602                .abs()
603            + if is_transient { 0 } else { lambda };
604
605        for i in 1..len {
606            let curr0 = cost0.min(cost1 + lambda);
607            let curr1 = (cost0 + lambda).min(cost1);
608            cost0 = curr0
609                + importance[i]
610                    * (metric[i]
611                        - 2 * TF_SELECT_TABLE[lm as usize][4 * (is_transient as usize) + 2 * sel]
612                            as i32)
613                        .abs();
614            cost1 = curr1
615                + importance[i]
616                    * (metric[i]
617                        - 2 * TF_SELECT_TABLE[lm as usize]
618                            [4 * (is_transient as usize) + 2 * sel + 1]
619                            as i32)
620                        .abs();
621        }
622        selcost[sel] = cost0.min(cost1);
623    }
624
625    // C: only allow tf_select=1 for transients (celt_encoder.c:787)
626    if selcost[1] < selcost[0] && is_transient {
627        tf_select = 1;
628    }
629
630    // Viterbi forward pass with path storage, then backward pass to
631    // reconstruct the optimal tf_res (C celt_encoder.c:789-822).
632    let sel = tf_select as usize;
633    let mut cost0 = importance[0]
634        * (metric[0]
635            - 2 * TF_SELECT_TABLE[lm as usize][4 * (is_transient as usize) + 2 * sel] as i32)
636            .abs();
637    let mut cost1 = importance[0]
638        * (metric[0]
639            - 2 * TF_SELECT_TABLE[lm as usize][4 * (is_transient as usize) + 2 * sel + 1] as i32)
640            .abs()
641        + if is_transient { 0 } else { lambda };
642
643    let mut path0 = [0i32; MAX_NB_EBANDS];
644    let mut path1 = [0i32; MAX_NB_EBANDS];
645    for i in 1..len {
646        let from0 = cost0;
647        let from1 = cost1 + lambda;
648        let curr0 = if from0 < from1 {
649            path0[i] = 0;
650            from0
651        } else {
652            path0[i] = 1;
653            from1
654        };
655
656        let from0 = cost0 + lambda;
657        let from1 = cost1;
658        let curr1 = if from0 < from1 {
659            path1[i] = 0;
660            from0
661        } else {
662            path1[i] = 1;
663            from1
664        };
665
666        cost0 = curr0
667            + importance[i]
668                * (metric[i]
669                    - 2 * TF_SELECT_TABLE[lm as usize][4 * (is_transient as usize) + 2 * sel]
670                        as i32)
671                    .abs();
672        cost1 = curr1
673            + importance[i]
674                * (metric[i]
675                    - 2 * TF_SELECT_TABLE[lm as usize][4 * (is_transient as usize) + 2 * sel + 1]
676                        as i32)
677                    .abs();
678    }
679    tf_res[len - 1] = if cost0 < cost1 { 0 } else { 1 };
680    for i in (0..len - 1).rev() {
681        tf_res[i] = if tf_res[i + 1] == 1 {
682            path1[i + 1]
683        } else {
684            path0[i + 1]
685        };
686    }
687
688    tf_select
689}
690
691fn tf_encode(
692    start: usize,
693    end: usize,
694    is_transient: bool,
695    tf_res: &mut [i32],
696    lm: i32,
697    mut tf_select: i32,
698    rc: &mut RangeCoder,
699) -> i32 {
700    let mut curr = 0;
701    let mut tf_changed = 0;
702    let mut logp = if is_transient { 2 } else { 4 };
703    let mut budget = rc.storage as i32 * 8;
704    let mut tell = rc.tell();
705
706    let tf_select_rsv = if lm > 0 && tell + logp < budget { 1 } else { 0 };
707    budget -= tf_select_rsv;
708
709    for tf_res_i in tf_res[start..end].iter_mut() {
710        if tell + logp <= budget {
711            rc.encode_bit_logp(*tf_res_i ^ curr != 0, logp as u32);
712            tell = rc.tell();
713            curr = *tf_res_i;
714            tf_changed |= curr;
715        } else {
716            *tf_res_i = curr;
717        }
718        logp = if is_transient { 4 } else { 5 };
719    }
720
721    if tf_select_rsv != 0
722        && TF_SELECT_TABLE[lm as usize][4 * (is_transient as usize) + (tf_changed as usize)]
723            != TF_SELECT_TABLE[lm as usize][4 * (is_transient as usize) + 2 + (tf_changed as usize)]
724    {
725        rc.encode_bit_logp(tf_select != 0, 1);
726    } else {
727        tf_select = 0;
728    }
729
730    for tf_res_i in tf_res[start..end].iter_mut() {
731        *tf_res_i = TF_SELECT_TABLE[lm as usize]
732            [4 * (is_transient as usize) + 2 * (tf_select as usize) + (*tf_res_i as usize)]
733            as i32;
734    }
735
736    tf_changed
737}
738
739fn tf_decode(
740    start: usize,
741    end: usize,
742    is_transient: bool,
743    tf_res: &mut [i32],
744    lm: i32,
745    rc: &mut RangeCoder,
746) {
747    let mut curr = 0;
748    let mut tf_changed = 0;
749    let mut logp = if is_transient { 2 } else { 4 };
750    let budget = rc.storage as i32 * 8;
751    let mut tell = rc.tell();
752
753    let tf_select_rsv = if lm > 0 && tell + logp < budget { 1 } else { 0 };
754    let budget = budget - tf_select_rsv;
755
756    for tf_res_i in tf_res[start..end].iter_mut() {
757        if tell + logp <= budget {
758            curr ^= if rc.decode_bit_logp(logp as u32) {
759                1
760            } else {
761                0
762            };
763            tell = rc.tell();
764            tf_changed |= curr;
765        }
766        *tf_res_i = curr;
767        logp = if is_transient { 4 } else { 5 };
768    }
769
770    let mut tf_select = 0;
771    let _budget = budget + tf_select_rsv;
772    if tf_select_rsv > 0
773        && TF_SELECT_TABLE[lm as usize][4 * (is_transient as usize) + (tf_changed as usize)]
774            != TF_SELECT_TABLE[lm as usize][4 * (is_transient as usize) + 2 + (tf_changed as usize)]
775    {
776        tf_select = if rc.decode_bit_logp(1) { 1 } else { 0 };
777    }
778
779    for tf_res_i in tf_res[start..end].iter_mut() {
780        *tf_res_i = TF_SELECT_TABLE[lm as usize]
781            [4 * (is_transient as usize) + 2 * (tf_select as usize) + (*tf_res_i as usize)]
782            as i32;
783    }
784}
785
786fn stereo_analysis(m: &CeltMode, x: &[f32], lm: i32, n0: usize) -> bool {
787    let mut sum_lr = 1e-9f32;
788    let mut sum_ms = 1e-9f32;
789
790    for i in 0..13 {
791        let start = (m.e_bands[i] as usize) << lm;
792        let end = (m.e_bands[i + 1] as usize) << lm;
793        for j in start..end {
794            let l = x[j];
795            let r = x[n0 + j];
796            let m_val = l + r;
797            let s_val = l - r;
798            sum_lr += l.abs() + r.abs();
799            sum_ms += m_val.abs() + s_val.abs();
800        }
801    }
802
803    sum_ms *= core::f32::consts::FRAC_1_SQRT_2;
804    let mut thetas = 13;
805    if lm <= 1 {
806        thetas -= 8;
807    }
808
809    let left = (((m.e_bands[13] as usize) << (lm + 1)) + thetas) as f32 * sum_ms;
810    let right = ((m.e_bands[13] as usize) << (lm + 1)) as f32 * sum_lr;
811
812    left > right
813}
814
815const COMBFILTER_MINPERIOD: usize = 15;
816const COMBFILTER_MAXPERIOD: usize = 1024;
817
818const PREFILTER_GAINS: [[f32; 3]; 3] = [
819    [0.306_640_6, 0.217_041, 0.129_638_7],
820    [0.463_867_2, 0.268_066_4, 0.0],
821    [0.799_804_7, 0.100_097_7, 0.0],
822];
823
824#[allow(clippy::too_many_arguments)]
825fn comb_filter_const(
826    y: &mut [f32],
827    x: &[f32],
828    y_idx: usize,
829    x_idx: usize,
830    t: usize,
831    n: usize,
832    g10: f32,
833    g11: f32,
834    g12: f32,
835) {
836    #[cfg(target_arch = "aarch64")]
837    {
838        comb_filter_const_neon(y, x, y_idx, x_idx, t, n, g10, g11, g12);
839    }
840    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
841    unsafe {
842        if crate::compat::x86_has_avx_fma() {
843            comb_filter_const_avx(y, x, y_idx, x_idx, t, n, g10, g11, g12);
844            return;
845        }
846    }
847    #[cfg(all(target_arch = "x86_64", target_feature = "sse"))]
848    unsafe {
849        comb_filter_const_sse(y, x, y_idx, x_idx, t, n, g10, g11, g12);
850        #[allow(clippy::needless_return)]
851        return;
852    }
853    #[cfg(not(any(
854        target_arch = "aarch64",
855        all(target_arch = "x86_64", target_feature = "sse")
856    )))]
857    {
858        comb_filter_const_scalar(y, x, y_idx, x_idx, t, n, g10, g11, g12);
859    }
860}
861
862#[inline]
863#[allow(dead_code)]
864fn comb_filter_const_scalar(
865    y: &mut [f32],
866    x: &[f32],
867    y_idx: usize,
868    x_idx: usize,
869    t: usize,
870    n: usize,
871    g10: f32,
872    g11: f32,
873    g12: f32,
874) {
875    let mut x1;
876    let mut x2;
877    let mut x3;
878    let mut x4;
879    let mut x0;
880
881    x4 = x[x_idx - t - 2];
882    x3 = x[x_idx - t - 1];
883    x2 = x[x_idx - t];
884    x1 = x[x_idx - t + 1];
885
886    for i in 0..n {
887        x0 = x[x_idx + i - t + 2];
888        y[y_idx + i] = x[x_idx + i] + g10 * x2 + g11 * (x1 + x3) + g12 * (x0 + x4);
889        x4 = x3;
890        x3 = x2;
891        x2 = x1;
892        x1 = x0;
893    }
894}
895
896#[cfg(target_arch = "aarch64")]
897fn comb_filter_const_neon(
898    y: &mut [f32],
899    x: &[f32],
900    y_idx: usize,
901    x_idx: usize,
902    t: usize,
903    n: usize,
904    g10: f32,
905    g11: f32,
906    g12: f32,
907) {
908    unsafe { comb_filter_const_neon_impl(y, x, y_idx, x_idx, t, n, g10, g11, g12) }
909}
910
911#[cfg(target_arch = "aarch64")]
912#[inline(always)]
913#[allow(unsafe_op_in_unsafe_fn)]
914unsafe fn comb_filter_const_neon_impl(
915    y: &mut [f32],
916    x: &[f32],
917    y_idx: usize,
918    x_idx: usize,
919    t: usize,
920    n: usize,
921    g10: f32,
922    g11: f32,
923    g12: f32,
924) {
925    use core::arch::aarch64::*;
926
927    let g10v = vdupq_n_f32(g10);
928    let g11v = vdupq_n_f32(g11);
929    let g12v = vdupq_n_f32(g12);
930
931    let xbase = x.as_ptr().add(x_idx);
932    let ybase = y.as_mut_ptr().add(y_idx);
933
934    let mut x0v = vld1q_f32(xbase.sub(t + 2));
935
936    let mut i = 0;
937    while i + 4 <= n {
938        let x4v = vld1q_f32(xbase.add(i).sub(t - 2));
939
940        let x2v = vextq_f32(x0v, x4v, 2);
941
942        let x1v = vextq_f32(x0v, x4v, 1);
943
944        let x3v = vextq_f32(x0v, x4v, 3);
945
946        let xi = vld1q_f32(xbase.add(i));
947
948        let mut yi = xi;
949        yi = vfmaq_f32(yi, g10v, x2v);
950        yi = vfmaq_f32(yi, g11v, vaddq_f32(x1v, x3v));
951        yi = vfmaq_f32(yi, g12v, vaddq_f32(x4v, x0v));
952        vst1q_f32(ybase.add(i), yi);
953
954        x0v = x4v;
955        i += 4;
956    }
957
958    let x0v_arr: [f32; 4] = core::mem::transmute(x0v);
959    let mut sx4 = x0v_arr[0];
960    let mut sx3 = x0v_arr[1];
961    let mut sx2 = x0v_arr[2];
962    let mut sx1 = x0v_arr[3];
963
964    while i < n {
965        let sx0 = x[x_idx + i - t + 2];
966        y[y_idx + i] = x[x_idx + i] + g10 * sx2 + g11 * (sx1 + sx3) + g12 * (sx0 + sx4);
967        sx4 = sx3;
968        sx3 = sx2;
969        sx2 = sx1;
970        sx1 = sx0;
971        i += 1;
972    }
973}
974
975#[cfg(all(target_arch = "x86_64", target_feature = "sse"))]
976#[inline(always)]
977#[allow(unsafe_op_in_unsafe_fn)]
978unsafe fn comb_filter_const_sse(
979    y: &mut [f32],
980    x: &[f32],
981    y_idx: usize,
982    x_idx: usize,
983    t: usize,
984    n: usize,
985    g10: f32,
986    g11: f32,
987    g12: f32,
988) {
989    #[cfg(target_arch = "x86")]
990    use core::arch::x86::*;
991    #[cfg(target_arch = "x86_64")]
992    use core::arch::x86_64::*;
993
994    let g10v = _mm_set1_ps(g10);
995    let g11v = _mm_set1_ps(g11);
996    let g12v = _mm_set1_ps(g12);
997
998    let xbase = x.as_ptr().add(x_idx);
999    let ybase = y.as_mut_ptr().add(y_idx);
1000    let mut x0v = _mm_loadu_ps(xbase.sub(t + 2));
1001
1002    let mut i = 0;
1003    while i + 4 <= n {
1004        let x4v = _mm_loadu_ps(xbase.add(i).sub(t - 2));
1005
1006        let x2v = _mm_shuffle_ps(x0v, x4v, 0x4e);
1007
1008        let x1v = _mm_shuffle_ps(x0v, x2v, 0x99);
1009
1010        let x3v = _mm_shuffle_ps(x2v, x4v, 0x99);
1011
1012        let xi = _mm_loadu_ps(xbase.add(i));
1013
1014        let mut yi = xi;
1015        yi = _mm_add_ps(yi, _mm_mul_ps(g10v, x2v));
1016        let yi2 = _mm_add_ps(
1017            _mm_mul_ps(g11v, _mm_add_ps(x3v, x1v)),
1018            _mm_mul_ps(g12v, _mm_add_ps(x4v, x0v)),
1019        );
1020        yi = _mm_add_ps(yi, yi2);
1021        _mm_storeu_ps(ybase.add(i), yi);
1022
1023        x0v = x4v;
1024        i += 4;
1025    }
1026
1027    let x0v_arr: [f32; 4] = core::mem::transmute(x0v);
1028    let mut sx4 = x0v_arr[0];
1029    let mut sx3 = x0v_arr[1];
1030    let mut sx2 = x0v_arr[2];
1031    let mut sx1 = x0v_arr[3];
1032
1033    while i < n {
1034        let sx0 = x[x_idx + i - t + 2];
1035        y[y_idx + i] = x[x_idx + i] + g10 * sx2 + g11 * (sx1 + sx3) + g12 * (sx0 + sx4);
1036        sx4 = sx3;
1037        sx3 = sx2;
1038        sx2 = sx1;
1039        sx1 = sx0;
1040        i += 1;
1041    }
1042}
1043
1044#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
1045#[target_feature(enable = "avx,fma")]
1046#[allow(unsafe_op_in_unsafe_fn)]
1047unsafe fn comb_filter_const_avx(
1048    y: &mut [f32],
1049    x: &[f32],
1050    y_idx: usize,
1051    x_idx: usize,
1052    t: usize,
1053    n: usize,
1054    g10: f32,
1055    g11: f32,
1056    g12: f32,
1057) {
1058    #[cfg(target_arch = "x86")]
1059    use core::arch::x86::*;
1060    #[cfg(target_arch = "x86_64")]
1061    use core::arch::x86_64::*;
1062
1063    let g10v = _mm256_set1_ps(g10);
1064    let g11v = _mm256_set1_ps(g11);
1065    let g12v = _mm256_set1_ps(g12);
1066
1067    let xbase = x.as_ptr().add(x_idx);
1068    let ybase = y.as_mut_ptr().add(y_idx);
1069
1070    let mut i = 0;
1071
1072    while i + 16 <= n {
1073        let xi_a = _mm256_loadu_ps(xbase.add(i));
1074        let x0_a = _mm256_loadu_ps(xbase.add(i).sub(t + 2));
1075        let x4_a = _mm256_loadu_ps(xbase.add(i).sub(t - 2));
1076
1077        let x2_a = _mm256_loadu_ps(xbase.add(i).sub(t));
1078        let x1x3_a = _mm256_add_ps(
1079            _mm256_loadu_ps(xbase.add(i).sub(t + 1)),
1080            _mm256_loadu_ps(xbase.add(i).sub(t - 1)),
1081        );
1082        let x0x4_a = _mm256_add_ps(x0_a, x4_a);
1083
1084        let mut yi_a = xi_a;
1085        yi_a = _mm256_fmadd_ps(g10v, x2_a, yi_a);
1086        yi_a = _mm256_fmadd_ps(g11v, x1x3_a, yi_a);
1087        yi_a = _mm256_fmadd_ps(g12v, x0x4_a, yi_a);
1088        _mm256_storeu_ps(ybase.add(i), yi_a);
1089
1090        let j = i + 8;
1091        let xi_b = _mm256_loadu_ps(xbase.add(j));
1092        let x0_b = _mm256_loadu_ps(xbase.add(j).sub(t + 2));
1093        let x4_b = _mm256_loadu_ps(xbase.add(j).sub(t - 2));
1094        let x2_b = _mm256_loadu_ps(xbase.add(j).sub(t));
1095        let x1x3_b = _mm256_add_ps(
1096            _mm256_loadu_ps(xbase.add(j).sub(t + 1)),
1097            _mm256_loadu_ps(xbase.add(j).sub(t - 1)),
1098        );
1099        let x0x4_b = _mm256_add_ps(x0_b, x4_b);
1100
1101        let mut yi_b = xi_b;
1102        yi_b = _mm256_fmadd_ps(g10v, x2_b, yi_b);
1103        yi_b = _mm256_fmadd_ps(g11v, x1x3_b, yi_b);
1104        yi_b = _mm256_fmadd_ps(g12v, x0x4_b, yi_b);
1105        _mm256_storeu_ps(ybase.add(j), yi_b);
1106
1107        i += 16;
1108    }
1109
1110    while i + 8 <= n {
1111        let xi = _mm256_loadu_ps(xbase.add(i));
1112        let x0 = _mm256_loadu_ps(xbase.add(i).sub(t + 2));
1113        let x4 = _mm256_loadu_ps(xbase.add(i).sub(t - 2));
1114        let x2 = _mm256_loadu_ps(xbase.add(i).sub(t));
1115        let x1x3 = _mm256_add_ps(
1116            _mm256_loadu_ps(xbase.add(i).sub(t + 1)),
1117            _mm256_loadu_ps(xbase.add(i).sub(t - 1)),
1118        );
1119        let x0x4 = _mm256_add_ps(x0, x4);
1120
1121        let mut yi = xi;
1122        yi = _mm256_fmadd_ps(g10v, x2, yi);
1123        yi = _mm256_fmadd_ps(g11v, x1x3, yi);
1124        yi = _mm256_fmadd_ps(g12v, x0x4, yi);
1125        _mm256_storeu_ps(ybase.add(i), yi);
1126
1127        i += 8;
1128    }
1129
1130    if i + 4 <= n {
1131        comb_filter_const_sse_fma(y, x, y_idx + i, x_idx + i, t, n - i, g10, g11, g12);
1132        return;
1133    }
1134
1135    let mut sx4 = x[x_idx + i - t - 2];
1136    let mut sx3 = x[x_idx + i - t - 1];
1137    let mut sx2 = x[x_idx + i - t];
1138    let mut sx1 = x[x_idx + i - t + 1];
1139    while i < n {
1140        let sx0 = x[x_idx + i - t + 2];
1141        y[y_idx + i] = x[x_idx + i] + g10 * sx2 + g11 * (sx1 + sx3) + g12 * (sx0 + sx4);
1142        sx4 = sx3;
1143        sx3 = sx2;
1144        sx2 = sx1;
1145        sx1 = sx0;
1146        i += 1;
1147    }
1148}
1149
1150#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
1151#[target_feature(enable = "avx,fma")]
1152#[allow(unsafe_op_in_unsafe_fn)]
1153unsafe fn comb_filter_const_sse_fma(
1154    y: &mut [f32],
1155    x: &[f32],
1156    y_idx: usize,
1157    x_idx: usize,
1158    t: usize,
1159    n: usize,
1160    g10: f32,
1161    g11: f32,
1162    g12: f32,
1163) {
1164    #[cfg(target_arch = "x86")]
1165    use core::arch::x86::*;
1166    #[cfg(target_arch = "x86_64")]
1167    use core::arch::x86_64::*;
1168
1169    let g10v = _mm_set1_ps(g10);
1170    let g11v = _mm_set1_ps(g11);
1171    let g12v = _mm_set1_ps(g12);
1172
1173    let xbase = x.as_ptr().add(x_idx);
1174    let ybase = y.as_mut_ptr().add(y_idx);
1175    let mut x0v = _mm_loadu_ps(xbase.sub(t + 2));
1176
1177    let mut i = 0;
1178    while i + 4 <= n {
1179        let x4v = _mm_loadu_ps(xbase.add(i).sub(t - 2));
1180        let x2v = _mm_shuffle_ps(x0v, x4v, 0x4e);
1181        let x1v = _mm_shuffle_ps(x0v, x2v, 0x99);
1182        let x3v = _mm_shuffle_ps(x2v, x4v, 0x99);
1183        let xi = _mm_loadu_ps(xbase.add(i));
1184
1185        let mut yi = xi;
1186        yi = _mm_fmadd_ps(g10v, x2v, yi);
1187        yi = _mm_fmadd_ps(g11v, _mm_add_ps(x1v, x3v), yi);
1188        yi = _mm_fmadd_ps(g12v, _mm_add_ps(x0v, x4v), yi);
1189        _mm_storeu_ps(ybase.add(i), yi);
1190
1191        x0v = x4v;
1192        i += 4;
1193    }
1194
1195    let x0v_arr: [f32; 4] = core::mem::transmute(x0v);
1196    let mut sx4 = x0v_arr[0];
1197    let mut sx3 = x0v_arr[1];
1198    let mut sx2 = x0v_arr[2];
1199    let mut sx1 = x0v_arr[3];
1200    while i < n {
1201        let sx0 = x[x_idx + i - t + 2];
1202        y[y_idx + i] = x[x_idx + i] + g10 * sx2 + g11 * (sx1 + sx3) + g12 * (sx0 + sx4);
1203        sx4 = sx3;
1204        sx3 = sx2;
1205        sx2 = sx1;
1206        sx1 = sx0;
1207        i += 1;
1208    }
1209}
1210
1211#[allow(clippy::too_many_arguments)]
1212fn comb_filter(
1213    y: &mut [f32],
1214    x: &[f32],
1215    y_idx: usize,
1216    x_idx: usize,
1217    t0: usize,
1218    t1: usize,
1219    n: usize,
1220    g0: f32,
1221    g1: f32,
1222    tapset0: i32,
1223    tapset1: i32,
1224    window: &[f32],
1225    overlap: usize,
1226) {
1227    if g0 == 0.0 && g1 == 0.0 {
1228        if x_idx != y_idx || !core::ptr::eq(x.as_ptr(), y.as_ptr()) {
1229            y[y_idx..y_idx + n].copy_from_slice(&x[x_idx..x_idx + n]);
1230        }
1231        return;
1232    }
1233
1234    let t0 = t0.clamp(
1235        COMBFILTER_MINPERIOD,
1236        x_idx.saturating_sub(2).max(COMBFILTER_MINPERIOD),
1237    );
1238    let t1 = t1.clamp(
1239        COMBFILTER_MINPERIOD,
1240        x_idx.saturating_sub(2).max(COMBFILTER_MINPERIOD),
1241    );
1242
1243    let g00 = g0 * PREFILTER_GAINS[tapset0 as usize][0];
1244    let g01 = g0 * PREFILTER_GAINS[tapset0 as usize][1];
1245    let g02 = g0 * PREFILTER_GAINS[tapset0 as usize][2];
1246
1247    let g10 = g1 * PREFILTER_GAINS[tapset1 as usize][0];
1248    let g11 = g1 * PREFILTER_GAINS[tapset1 as usize][1];
1249    let g12 = g1 * PREFILTER_GAINS[tapset1 as usize][2];
1250
1251    let mut x1 = x[x_idx - t1 + 1];
1252    let mut x2 = x[x_idx - t1];
1253    let mut x3 = x[x_idx - t1 - 1];
1254    let mut x4 = x[x_idx - t1 - 2];
1255
1256    let mut inner_overlap = overlap;
1257    if g0 == g1 && t0 == t1 && tapset0 == tapset1 {
1258        inner_overlap = 0;
1259    }
1260
1261    let mut i = 0;
1262    while i < inner_overlap && i < n {
1263        let x0 = x[x_idx + i - t1 + 2];
1264        let f = window[i] * window[i];
1265        y[y_idx + i] = x[x_idx + i]
1266            + (1.0 - f)
1267                * (g00 * x[x_idx + i - t0]
1268                    + g01 * (x[x_idx + i - t0 + 1] + x[x_idx + i - t0 - 1])
1269                    + g02 * (x[x_idx + i - t0 + 2] + x[x_idx + i - t0 - 2]))
1270            + f * (g10 * x2 + g11 * (x1 + x3) + g12 * (x0 + x4));
1271
1272        x4 = x3;
1273        x3 = x2;
1274        x2 = x1;
1275        x1 = x0;
1276        i += 1;
1277    }
1278
1279    if i < n {
1280        if g1 == 0.0 {
1281            y[y_idx + i..y_idx + n].copy_from_slice(&x[x_idx + i..x_idx + n]);
1282        } else {
1283            comb_filter_const(y, x, y_idx + i, x_idx + i, t1, n - i, g10, g11, g12);
1284        }
1285    }
1286}
1287
1288/// In-place comb filter: buf[y_idx..y_idx+n] is both input and output.
1289/// Reference samples at buf[y_idx + i - T + offset] may already be filtered
1290/// if T < i, matching C libopus's in-place comb_filter(out, out, ...) behavior.
1291fn comb_filter_inplace(
1292    buf: &mut [f32],
1293    y_idx: usize,
1294    t0: usize,
1295    t1: usize,
1296    n: usize,
1297    g0: f32,
1298    g1: f32,
1299    tapset0: i32,
1300    tapset1: i32,
1301    window: &[f32],
1302    overlap: usize,
1303) {
1304    if g0 == 0.0 && g1 == 0.0 {
1305        // nothing to do; buf[y_idx..] already holds the input
1306        return;
1307    }
1308
1309    let t0 = t0.clamp(COMBFILTER_MINPERIOD, y_idx - 2);
1310    let t1 = t1.clamp(COMBFILTER_MINPERIOD, y_idx - 2);
1311
1312    let g00 = g0 * PREFILTER_GAINS[tapset0 as usize][0];
1313    let g01 = g0 * PREFILTER_GAINS[tapset0 as usize][1];
1314    let g02 = g0 * PREFILTER_GAINS[tapset0 as usize][2];
1315
1316    let g10 = g1 * PREFILTER_GAINS[tapset1 as usize][0];
1317    let g11 = g1 * PREFILTER_GAINS[tapset1 as usize][1];
1318    let g12 = g1 * PREFILTER_GAINS[tapset1 as usize][2];
1319
1320    let mut inner_overlap = overlap;
1321    if g0 == g1 && t0 == t1 && tapset0 == tapset1 {
1322        inner_overlap = 0;
1323    }
1324
1325    let mut i = 0;
1326    while i < inner_overlap && i < n {
1327        let idx = y_idx + i;
1328        let f = window[i] * window[i];
1329        let s = buf[idx]; // original input (not yet overwritten at idx)
1330        let r0 = buf[idx - t0];
1331        let r0p1 = buf[idx - t0 + 1];
1332        let r0m1 = buf[idx - t0 - 1];
1333        let r0p2 = buf[idx - t0 + 2];
1334        let r0m2 = buf[idx - t0 - 2];
1335        let r1 = buf[idx - t1];
1336        let r1p1 = buf[idx - t1 + 1];
1337        let r1m1 = buf[idx - t1 - 1];
1338        let r1p2 = buf[idx - t1 + 2];
1339        let r1m2 = buf[idx - t1 - 2];
1340        buf[idx] = s
1341            + (1.0 - f) * (g00 * r0 + g01 * (r0p1 + r0m1) + g02 * (r0p2 + r0m2))
1342            + f * (g10 * r1 + g11 * (r1p1 + r1m1) + g12 * (r1p2 + r1m2));
1343        i += 1;
1344    }
1345
1346    // Constant region: only new filter (t1, g1)
1347    while i < n {
1348        let idx = y_idx + i;
1349        let s = buf[idx];
1350        let r1 = buf[idx - t1];
1351        let r1p1 = buf[idx - t1 + 1];
1352        let r1m1 = buf[idx - t1 - 1];
1353        let r1p2 = buf[idx - t1 + 2];
1354        let r1m2 = buf[idx - t1 - 2];
1355        buf[idx] = s + g10 * r1 + g11 * (r1p1 + r1m1) + g12 * (r1p2 + r1m2);
1356        i += 1;
1357    }
1358}
1359
1360// 3.1416/3.141593 literals mirror celt_encoder.c; keep them verbatim.
1361#[allow(clippy::approx_constant)]
1362fn run_prefilter(
1363    in_buf: &mut [f32],
1364    prefilter_mem: &mut [f32],
1365    prefilter_period: usize,
1366    prefilter_gain: f32,
1367    prefilter_tapset: i32,
1368    tapset_decision: i32,
1369    window: &[f32],
1370    channels: usize,
1371    frame_size: usize,
1372    overlap: usize,
1373
1374    pre: &mut [f32],
1375    pitch_buf: &mut [f32],
1376    before: &mut [f32],
1377    after: &mut [f32],
1378
1379    analysis: &AnalysisInfo,
1380    loss_rate: i32,
1381    tf_estimate: f32,
1382    nb_available_bytes: usize,
1383    tone_freq: f32,
1384    toneishness: f32,
1385    complexity: i32,
1386) -> (bool, f32, usize) {
1387    let max_period = COMBFILTER_MAXPERIOD;
1388    let min_period = COMBFILTER_MINPERIOD;
1389    let buf_stride = frame_size + overlap;
1390    let pre_size = max_period + frame_size;
1391
1392    for c in 0..channels {
1393        pre[c * pre_size..c * pre_size + max_period]
1394            .copy_from_slice(&prefilter_mem[c * max_period..(c + 1) * max_period]);
1395        pre[c * pre_size + max_period..c * pre_size + pre_size].copy_from_slice(
1396            &in_buf[c * buf_stride + overlap..c * buf_stride + overlap + frame_size],
1397        );
1398    }
1399
1400    let enabled = true;
1401    let mut pitch_index: usize;
1402    let mut gain1: f32;
1403    if enabled && toneishness > 0.99 {
1404        // Tone path (C 1449-1473): bypass the pitch search for pure tones.
1405        let mut multiple = 1.0f32;
1406        let mut tone_freq = tone_freq;
1407        if tone_freq >= 3.1416 {
1408            tone_freq = 3.141593 - tone_freq;
1409        }
1410        while tone_freq >= multiple * 0.39 {
1411            multiple += 1.0;
1412        }
1413        if tone_freq > 0.006148 {
1414            pitch_index = ((0.5 + 2.0 * core::f32::consts::PI * multiple / tone_freq) as usize)
1415                .min(COMBFILTER_MAXPERIOD - 2);
1416        } else {
1417            pitch_index = COMBFILTER_MINPERIOD;
1418        }
1419        gain1 = 0.75;
1420    } else if enabled && complexity >= 5 {
1421        let pitch_buf_len = (max_period + frame_size) >> 1;
1422        {
1423            let mut pre_slices: FixedVec<&[f32], 2> = FixedVec::new();
1424            for c in 0..channels {
1425                pre_slices.push(&pre[c * pre_size..c * pre_size + pre_size]);
1426            }
1427            crate::pitch::pitch_downsample(&pre_slices, pitch_buf, pitch_buf_len, channels, 2);
1428        }
1429
1430        let search_max = max_period - 3 * min_period;
1431        let pitch_result = crate::pitch::pitch_search(
1432            &pitch_buf[max_period >> 1..],
1433            pitch_buf,
1434            frame_size,
1435            search_max,
1436        );
1437        pitch_index = (max_period - pitch_result).min(max_period - 2);
1438
1439        gain1 = crate::pitch::remove_doubling(
1440            pitch_buf,
1441            max_period,
1442            min_period,
1443            frame_size,
1444            &mut pitch_index,
1445            prefilter_period,
1446            prefilter_gain,
1447        );
1448        if pitch_index > max_period - 2 {
1449            pitch_index = max_period - 2;
1450        }
1451        gain1 *= 0.7;
1452        // C: halve at >2% loss, halve again at >4%, zero at >8%
1453        if loss_rate > 2 {
1454            gain1 *= 0.5;
1455        }
1456        if loss_rate > 4 {
1457            gain1 *= 0.5;
1458        }
1459        if loss_rate > 8 {
1460            gain1 = 0.0;
1461        }
1462    } else {
1463        gain1 = 0.0;
1464        pitch_index = COMBFILTER_MINPERIOD;
1465    }
1466
1467    if analysis.valid {
1468        gain1 *= analysis.max_pitch_ratio;
1469    }
1470
1471    let mut pf_threshold = 0.2f32;
1472    if (pitch_index as i32 - prefilter_period as i32).unsigned_abs() as usize * 10 > pitch_index {
1473        pf_threshold += 0.2;
1474        // Completely disable the prefilter on strong transients without continuity.
1475        if tf_estimate > 0.98 {
1476            gain1 = 0.0;
1477        }
1478    }
1479    if nb_available_bytes < 25 {
1480        pf_threshold += 0.1;
1481    }
1482    if nb_available_bytes < 35 {
1483        pf_threshold += 0.1;
1484    }
1485    if prefilter_gain > 0.4 {
1486        pf_threshold -= 0.1;
1487    }
1488    if prefilter_gain > 0.55 {
1489        pf_threshold -= 0.1;
1490    }
1491    pf_threshold = pf_threshold.max(0.2);
1492
1493    let pf_on;
1494    if gain1 < pf_threshold {
1495        gain1 = 0.0;
1496        pf_on = false;
1497    } else {
1498        if (gain1 - prefilter_gain).abs() < 0.1 {
1499            gain1 = prefilter_gain;
1500        }
1501        let qg = ((gain1 * 32.0 / 3.0 + 0.5).floor() as i32 - 1).clamp(0, 7);
1502        gain1 = 0.09375 * (qg + 1) as f32;
1503        pf_on = true;
1504    }
1505
1506    let before = &mut before[..channels];
1507    for c in 0..channels {
1508        let start = c * buf_stride + overlap;
1509        before[c] = sum_abs(&in_buf[start..start + frame_size]);
1510    }
1511
1512    let offset = 0usize;
1513    let prev_period = prefilter_period.clamp(COMBFILTER_MINPERIOD, max_period - 2);
1514
1515    for c in 0..channels {
1516        if offset > 0 {
1517            let pre_c = &pre[c * pre_size..];
1518            comb_filter(
1519                in_buf,
1520                pre_c,
1521                c * buf_stride + overlap,
1522                max_period,
1523                prev_period,
1524                prev_period,
1525                offset,
1526                -prefilter_gain,
1527                -prefilter_gain,
1528                prefilter_tapset,
1529                prefilter_tapset,
1530                window,
1531                0,
1532            );
1533        }
1534
1535        {
1536            let pre_c = &pre[c * pre_size..];
1537            comb_filter(
1538                in_buf,
1539                pre_c,
1540                c * buf_stride + overlap + offset,
1541                max_period + offset,
1542                prev_period,
1543                pitch_index,
1544                frame_size - offset,
1545                -prefilter_gain,
1546                -gain1,
1547                prefilter_tapset,
1548                tapset_decision,
1549                window,
1550                overlap,
1551            );
1552        }
1553    }
1554
1555    let after = &mut after[..channels];
1556    for c in 0..channels {
1557        let start = c * buf_stride + overlap;
1558        after[c] = sum_abs(&in_buf[start..start + frame_size]);
1559    }
1560
1561    let cancel_pitch = (0..channels).any(|c| after[c] > before[c]);
1562
1563    // C 1572-1584: revert to a gain of zero, but still fade the previous
1564    // frame's filter out over the overlap. The decoder's post-filter always
1565    // cross-fades from the old gain, so dropping the fade here leaves 120
1566    // samples it post-filters that were never pre-filtered (issue #54).
1567    if cancel_pitch {
1568        for c in 0..channels {
1569            let body = c * buf_stride + overlap;
1570            in_buf[body..body + frame_size].copy_from_slice(
1571                &pre[c * pre_size + max_period..c * pre_size + max_period + frame_size],
1572            );
1573            comb_filter(
1574                in_buf,
1575                &pre[c * pre_size..],
1576                body + offset,
1577                max_period + offset,
1578                prev_period,
1579                pitch_index,
1580                overlap,
1581                -prefilter_gain,
1582                0.0,
1583                prefilter_tapset,
1584                tapset_decision,
1585                window,
1586                overlap,
1587            );
1588        }
1589    }
1590
1591    for c in 0..channels {
1592        push_prefilter_history(
1593            &mut prefilter_mem[c * max_period..(c + 1) * max_period],
1594            &pre[c * pre_size + max_period..c * pre_size + pre_size],
1595        );
1596    }
1597
1598    if cancel_pitch {
1599        (false, 0.0, pitch_index)
1600    } else {
1601        (pf_on, gain1, pitch_index)
1602    }
1603}
1604
1605/// Append one frame of pre-emphasized, unfiltered input to a channel's
1606/// `prefilter_mem` history (libopus `run_prefilter`, which runs every frame
1607/// whether or not the comb filter is enabled).
1608fn push_prefilter_history(mem: &mut [f32], frame: &[f32]) {
1609    let (n, max_period) = (frame.len(), mem.len());
1610    if n >= max_period {
1611        mem.copy_from_slice(&frame[n - max_period..]);
1612    } else {
1613        mem.copy_within(n.., 0);
1614        mem[max_period - n..].copy_from_slice(frame);
1615    }
1616}
1617
1618/// Padding appended to `w_x` to absorb any SIMD over-shoot past the last band.
1619/// Sized to `MAX_PVQ_N` (352) — far larger than the widest SIMD access (16),
1620/// which is always loop-guarded (`while i + width <= n`). (Previously
1621/// `MAX_PVQ_N * 8`, which had no access pattern justifying it.)
1622const STRIDE_ACCESS_PAD: usize = crate::pvq::MAX_PVQ_N;
1623
1624pub struct CeltEncoder {
1625    mode: &'static CeltMode,
1626    channels: usize,
1627    /// libopus `CELTEncoder.upsample`: the API input is zero-stuffed by this
1628    /// factor so the encoder always works at the mode's 48 kHz geometry.
1629    upsample: usize,
1630    /// End band the packet is coded to, from the Opus bandwidth (libopus
1631    /// `CELT_SET_END_BAND`): NB 13, MB/WB 17, SWB 19, FB 21. The decoder
1632    /// derives the same value from the TOC, so coding even one band more
1633    /// desynchronises the range coder (issue #37).
1634    end_band: usize,
1635    pub complexity: i32,
1636    syn_mem: FixedVec<f32, CELT_SYN_MEM>,
1637    enc_decode_mem: FixedVec<f32, CELT_SYN_MEM>,
1638    old_band_e: FixedVec<f32, CELT_NB_X_CH>,
1639    preemph_mem: FixedVec<f32, CELT_MAX_CHANNELS>,
1640    tonal_average: i32,
1641    hf_average: i32,
1642    tapset_decision: i32,
1643    spread_decision: i32,
1644    intensity: i32,
1645    last_coded_bands: i32,
1646    prefilter_mem: FixedVec<f32, CELT_PREFILTER_MEM>,
1647    prefilter_period: usize,
1648    prefilter_gain: f32,
1649    prefilter_tapset: i32,
1650    old_band_e2: FixedVec<f32, CELT_NB_X_CH>,
1651    old_band_e3: FixedVec<f32, CELT_NB_X_CH>,
1652    last_band_log_e: FixedVec<f32, CELT_NB_X_CH>,
1653    delayed_intra: f32,
1654    lsb_depth: i32,
1655    overlap_max: f32,
1656    bitrate: i32,
1657    vbr: bool,
1658    constrained_vbr: bool,
1659    vbr_reservoir: i32,
1660    vbr_drift: i32,
1661    vbr_offset: i32,
1662    vbr_count: i32,
1663
1664    w_in_buf: FixedVec<f32, CELT_BUFSTRIDE>,
1665    w_freq: FixedVec<f32, CELT_W_FREQ>,
1666    w_band_e: FixedVec<f32, CELT_NB_X_CH>,
1667    w_band_e2: FixedVec<f32, CELT_NB_X_CH>,
1668    w_x: FixedVec<f32, CELT_W_X_ENC>,
1669    w_band_log_e: FixedVec<f32, CELT_NB_X_CH>,
1670    w_band_log_e2: FixedVec<f32, CELT_NB_X_CH>,
1671    w_error: FixedVec<f32, CELT_NB_X_CH>,
1672    w_tf_res: FixedVec<i32, CELT_NB_EBANDS>,
1673    w_cap: FixedVec<i32, CELT_NB_EBANDS>,
1674    w_offsets: FixedVec<i32, CELT_NB_EBANDS>,
1675    w_pulses: FixedVec<i32, CELT_NB_EBANDS>,
1676    w_ebits: FixedVec<i32, CELT_NB_X_CH>,
1677    w_fine_priority: FixedVec<i32, CELT_NB_X_CH>,
1678    w_collapse_masks: FixedVec<u32, CELT_NB_X_CH>,
1679    w_band_amp_synth: FixedVec<f32, CELT_NB_X_CH>,
1680    consec_transient: i32,
1681
1682    w_prefilter_pre: FixedVec<f32, CELT_PREFILTER_PRE>,
1683    w_prefilter_pitch_buf: FixedVec<f32, CELT_PREFILTER_PITCH>,
1684    w_prefilter_before: FixedVec<f32, CELT_MAX_CHANNELS>,
1685    w_prefilter_after: FixedVec<f32, CELT_MAX_CHANNELS>,
1686
1687    w_transient_tmp: FixedVec<f32, MAX_TRANSIENT_LEN>,
1688    w_transient_tmp2: FixedVec<f32, { MAX_TRANSIENT_LEN / 2 }>,
1689
1690    analysis: AnalysisInfo,
1691    loss_rate: i32,
1692}
1693
1694const INTEN_THRESHOLDS: [i32; 21] = [
1695    1, 2, 3, 4, 5, 6, 7, 8, 16, 24, 36, 44, 50, 56, 62, 67, 72, 79, 88, 106, 134,
1696];
1697const INTEN_HYSTERESIS: [i32; 21] = [
1698    1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 4, 5, 6, 8, 8,
1699];
1700
1701fn hysteresis_decision(val: i32, thresholds: &[i32], hysteresis: &[i32], prev: i32) -> i32 {
1702    let mut i = 0;
1703    while i < thresholds.len() {
1704        if val < thresholds[i] {
1705            break;
1706        }
1707        i += 1;
1708    }
1709    let mut res = i as i32;
1710    if res > prev && val < thresholds[prev as usize] + hysteresis[prev as usize] {
1711        res = prev;
1712    }
1713    if res < prev && res > 0 && val > thresholds[prev as usize - 1] - hysteresis[prev as usize - 1]
1714    {
1715        res = prev;
1716    }
1717    res
1718}
1719
1720#[allow(clippy::too_many_arguments)]
1721fn alloc_trim_analysis(
1722    mode: &CeltMode,
1723    x: &[f32],
1724    band_log_e: &[f32],
1725    end: usize,
1726    lm: i32,
1727    channels: usize,
1728    n0: usize,
1729    stereo_saving: &mut f32,
1730    tf_estimate: f32,
1731    intensity: i32,
1732    surround_trim: f32,
1733    equiv_rate: i32,
1734) -> i32 {
1735    let mut trim = 5.0f32;
1736    if equiv_rate < 64000 {
1737        trim = 4.0;
1738    } else if equiv_rate < 80000 {
1739        let frac = (equiv_rate - 64000) as f32 / 1024.0;
1740        trim = 4.0 + (1.0 / 16.0) * frac;
1741    }
1742
1743    if channels == 2 {
1744        let mut sum = 0.0f32;
1745        for i in 0..8 {
1746            let offset = (mode.e_bands[i] as usize) << lm;
1747            let n = ((mode.e_bands[i + 1] - mode.e_bands[i]) as usize) << lm;
1748            let mut partial = 0.0f32;
1749            for j in 0..n {
1750                partial += x[offset + j] * x[n0 + offset + j];
1751            }
1752            sum += partial;
1753        }
1754        sum = (sum / 8.0).abs().min(1.0);
1755        let mut min_xc = sum;
1756        for i in 8..intensity as usize {
1757            let offset = (mode.e_bands[i] as usize) << lm;
1758            let n = ((mode.e_bands[i + 1] - mode.e_bands[i]) as usize) << lm;
1759            let mut partial = 0.0f32;
1760            for j in 0..n {
1761                partial += x[offset + j] * x[n0 + offset + j];
1762            }
1763            min_xc = min_xc.min(partial.abs());
1764        }
1765        min_xc = min_xc.min(1.0);
1766
1767        let log_xc = (1.001 - sum * sum).log2();
1768        let log_xc2 = (log_xc * 0.5).max((1.001 - min_xc * min_xc).log2());
1769
1770        trim += (-4.0f32).max(0.75 * log_xc);
1771        *stereo_saving = (*stereo_saving + 0.25).min(-0.5 * log_xc2);
1772    }
1773
1774    let mut diff = 0.0f32;
1775    for c in 0..channels {
1776        for i in 0..end - 1 {
1777            diff += band_log_e[c * mode.nb_ebands + i] * (2 + 2 * i as i32 - end as i32) as f32;
1778        }
1779    }
1780    diff /= (channels * (end - 1)) as f32;
1781    trim -= (-2.0f32).max(2.0f32.min((diff + 1.0) / 6.0));
1782    trim -= surround_trim;
1783    trim -= 2.0 * tf_estimate;
1784
1785    let trim_index = (trim + 0.5).floor() as i32;
1786    trim_index.clamp(0, 10)
1787}
1788
1789#[inline(always)]
1790fn median3(a: f32, b: f32, c: f32) -> f32 {
1791    let mut v = [a, b, c];
1792    crate::compat::sort_by(&mut v[..], |x, y| {
1793        x.partial_cmp(y).unwrap_or(core::cmp::Ordering::Equal)
1794    });
1795    v[1]
1796}
1797
1798#[inline(always)]
1799fn median5(v: &[f32]) -> f32 {
1800    let mut x = [v[0], v[1], v[2], v[3], v[4]];
1801    crate::compat::sort_by(&mut x[..], |a, b| {
1802        a.partial_cmp(b).unwrap_or(core::cmp::Ordering::Equal)
1803    });
1804    x[2]
1805}
1806
1807// Port of celt_encoder.c tone_lpc / tone_detect (float path, no FIXED_POINT shifts)
1808fn tone_lpc(x: &[f32], len: usize, delay: usize, lpc: &mut [f32; 2]) -> bool {
1809    // returns true on fail
1810    if len <= 2 * delay {
1811        return true;
1812    }
1813    let mut r00 = 0.0f32;
1814    let mut r01 = 0.0f32;
1815    let mut r02 = 0.0f32;
1816    let mut r11: f32;
1817    let mut r12: f32;
1818    
1819    for i in 0..len - 2 * delay {
1820        r00 += x[i] * x[i];
1821        r01 += x[i] * x[i + delay];
1822        r02 += x[i] * x[i + 2 * delay];
1823    }
1824    let mut edges = 0.0f32;
1825    for i in 0..delay {
1826        edges += x[len + i - 2 * delay] * x[len + i - 2 * delay] - x[i] * x[i];
1827    }
1828    r11 = r00 + edges;
1829    edges = 0.0;
1830    for i in 0..delay {
1831        edges += x[len + i - delay] * x[len + i - delay] - x[i + delay] * x[i + delay];
1832    }
1833    let r22: f32 = r11 + edges;
1834    edges = 0.0;
1835    for i in 0..delay {
1836        edges += x[len + i - 2 * delay] * x[len + i - delay] - x[i] * x[i + delay];
1837    }
1838    r12 = r01 + edges;
1839    // Reverse and sum to get backward contribution (float: simple sums)
1840    let (rr00, rr01, rr11, rr02, rr12) = (
1841        r00 + r22,
1842        r01 + r12,
1843        2.0 * r11,
1844        2.0 * r02,
1845        r12 + r01,
1846    );
1847    r00 = rr00;
1848    r01 = rr01;
1849    r11 = rr11;
1850    r02 = rr02;
1851    r12 = rr12;
1852    let den = r00 * r11 - r01 * r01;
1853    if den < 0.001 * r00 * r11 {
1854        return true;
1855    }
1856    let mut lpc1 = (r02 * r11 - r01 * r12) / den;
1857    let mut lpc0 = (r00 * r12 - r02 * r01) / den;
1858    // Clamp as in C (Q29 limits)
1859    lpc1 = lpc1.clamp(-1.0, 1.0);
1860    lpc0 = lpc0.clamp(-1.999999, 1.999999);
1861    // C does HALF check for lpc0, but float clamp suffices
1862    lpc[0] = lpc0;
1863    lpc[1] = lpc1;
1864    false
1865}
1866
1867fn tone_detect(in_buf: &[f32], cc: usize, n: usize, fs: i32) -> (f32, f32) {
1868    // in_buf is at least N samples per channel, we use N = n (frame+overlap)
1869    // For CC==2, average channels; for float, no shift scaling.
1870    // Use FixedVec to stay no_std heap-free.
1871    let mut x: FixedVec<f32, 2048> = FixedVec::from_value(0.0, n);
1872    if cc == 2 {
1873        for i in 0..n {
1874            x[i] = 0.5 * (in_buf[i] + in_buf[i + n]);
1875        }
1876    } else {
1877        for i in 0..n {
1878            x[i] = in_buf[i];
1879        }
1880    }
1881    let mut delay = 1usize;
1882    let mut lpc = [0.0f32; 2];
1883    let mut fail = tone_lpc(&x[..n], n, delay, &mut lpc);
1884    while delay <= (fs as usize / 3000) && (fail || (lpc[0] > 1.0 && lpc[1] < 0.0)) {
1885        delay *= 2;
1886        fail = tone_lpc(&x[..n], n, delay, &mut lpc);
1887    }
1888    if !fail && lpc[0] * lpc[0] + 3.999999 * lpc[1] < 0.0 {
1889        let toneishness = -lpc[1];
1890        // acos needs libm in no_std
1891        #[cfg(feature = "std")]
1892        let freq = (0.5 * lpc[0]).acos() / delay as f32;
1893        #[cfg(not(feature = "std"))]
1894        let freq = crate::compat::Math::acos(0.5 * lpc[0]) / delay as f32;
1895        (freq, toneishness)
1896    } else {
1897        (-1.0, 0.0)
1898    }
1899}
1900
1901#[allow(clippy::too_many_arguments)]
1902fn dynalloc_analysis_simple(
1903    mode: &CeltMode,
1904    band_log_e: &[f32],
1905    band_log_e2: Option<&[f32]>,
1906    old_band_e: &[f32],
1907    start: usize,
1908    end: usize,
1909    channels: usize,
1910    lm: usize,
1911    effective_bytes: usize,
1912    is_transient: bool,
1913    offsets: &mut [i32],
1914    cap: &[i32],
1915    lsb_depth: i32,
1916    vbr: bool,
1917    constrained_vbr: bool,
1918    analysis: &AnalysisInfo,
1919    surround_dynalloc: &[f32],
1920    tone_freq: f32,
1921    toneishness: f32,
1922    importance: &mut [i32],
1923) {
1924    offsets.fill(0);
1925    if effective_bytes < (30 + 5 * lm) {
1926        for v in importance.iter_mut() {
1927            *v = 13;
1928        }
1929        return;
1930    }
1931
1932    let nb = mode.nb_ebands;
1933    let mut follower: FixedVec<f32, CELT_NB_X_CH> = FixedVec::from_value(0.0f32, nb * channels);
1934    for c in 0..channels {
1935        let base = c * nb;
1936        let src = if let Some(b2) = band_log_e2 {
1937            b2
1938        } else {
1939            band_log_e
1940        };
1941        let mut band_log_e3: FixedVec<f32, CELT_NB_EBANDS> = FixedVec::from_value(0.0f32, end);
1942        for i in 0..end {
1943            let mut e = src[base + i];
1944            if lm == 0 && i < 8 {
1945                e = e.max(old_band_e[base + i]);
1946            }
1947            band_log_e3[i] = e;
1948        }
1949
1950        let mut last = 0usize;
1951        follower[base] = band_log_e3[0];
1952        for i in 1..end {
1953            if band_log_e3[i] > band_log_e3[i - 1] + 0.5 {
1954                last = i;
1955            }
1956            follower[base + i] = (follower[base + i - 1] + 1.5).min(band_log_e3[i]);
1957        }
1958        for i in (0..last).rev() {
1959            follower[base + i] =
1960                follower[base + i].min((follower[base + i + 1] + 2.0).min(band_log_e3[i]));
1961        }
1962
1963        let offset = 1.0f32;
1964        if end >= 5 {
1965            for i in 2..end - 2 {
1966                follower[base + i] =
1967                    follower[base + i].max(median5(&band_log_e3[i - 2..i + 3]) - offset);
1968            }
1969        }
1970        if end >= 3 {
1971            let l = median3(band_log_e3[0], band_log_e3[1], band_log_e3[2]) - offset;
1972            follower[base] = follower[base].max(l);
1973            follower[base + 1] = follower[base + 1].max(l);
1974
1975            let r = median3(
1976                band_log_e3[end - 3],
1977                band_log_e3[end - 2],
1978                band_log_e3[end - 1],
1979            ) - offset;
1980            follower[base + end - 2] = follower[base + end - 2].max(r);
1981            follower[base + end - 1] = follower[base + end - 1].max(r);
1982        }
1983        // Clamp to noise floor (float C: GCONST etc are no-ops)
1984        for i in 0..end {
1985            let log_n = if i < mode.log_n.len() {
1986                mode.log_n[i] as f32
1987            } else {
1988                0.0
1989            };
1990            let e_mean = if i < mode.e_means.len() {
1991                mode.e_means[i]
1992            } else {
1993                0.0
1994            };
1995            let noise_floor = 0.0625 * log_n + 0.5 + (9 - lsb_depth) as f32 - e_mean
1996                + 0.0062 * ((i + 5) * (i + 5)) as f32;
1997            follower[base + i] = follower[base + i].max(noise_floor);
1998        }
1999    }
2000    if channels == 2 {
2001        for i in start..end {
2002            let l = follower[i];
2003            let r = follower[nb + i];
2004            let r2 = r.max(l - 4.0);
2005            let l2 = l.max(r - 4.0);
2006            follower[i] =
2007                ((band_log_e[i] - l2).max(0.0) + (band_log_e[nb + i] - r2).max(0.0)) * 0.5;
2008        }
2009    } else {
2010        for i in start..end {
2011            follower[i] = (band_log_e[i] - follower[i]).max(0.0);
2012        }
2013    }
2014    for i in start..end {
2015        // C 1182-1183: follower = max(follower, surround_dynalloc)
2016        follower[i] = follower[i].max(surround_dynalloc[i]);
2017    }
2018    // C 1184-1191: importance = floor(0.5+13*exp2(min(follower,4)))
2019    for i in start..end {
2020        let v = follower[i].min(4.0);
2021        importance[i] = (0.5 + 13.0 * (2.0f32).powf(v)).floor() as i32;
2022    }
2023    if (!vbr || constrained_vbr) && !is_transient {
2024        for i in start..end {
2025            follower[i] *= 0.5;
2026        }
2027    }
2028    // Band weighting before capping: C does scale-then-min(4) (celt_encoder.c 1198-1204 + 1239).
2029    // Rust was min(4)-then-scale, which diverges for follower>2 in i<8 or >4 in i>=12.
2030    for i in start..end {
2031        if i < 8 {
2032            follower[i] *= 2.0;
2033        }
2034        if i >= 12 {
2035            follower[i] *= 0.5;
2036        }
2037    }
2038    // C 1206-1222: Compensate for Opus' under-allocation on tones
2039    if toneishness > 0.98 {
2040        let freq_bin = (tone_freq * 120.0 / core::f32::consts::PI + 0.5).floor() as i32;
2041        for i in start..end {
2042            let lo = mode.e_bands[i] as i32;
2043            let hi = mode.e_bands[i + 1] as i32;
2044            if freq_bin >= lo && freq_bin <= hi {
2045                follower[i] += 2.0;
2046            }
2047            if freq_bin >= lo - 1 && freq_bin <= hi + 1 {
2048                follower[i] += 1.0;
2049            }
2050            if freq_bin >= lo - 2 && freq_bin <= hi + 2 {
2051                follower[i] += 1.0;
2052            }
2053            if freq_bin >= lo - 3 && freq_bin <= hi + 3 {
2054                follower[i] += 0.5;
2055            }
2056        }
2057        if freq_bin >= mode.e_bands[end] as i32 {
2058            if end >= 1 {
2059                follower[end - 1] += 2.0;
2060            }
2061            if end >= 2 {
2062                follower[end - 2] += 1.0;
2063            }
2064        }
2065    }
2066    if analysis.valid {
2067        // C 1226-1230: leak_boost
2068        for i in start..end.min(19) {
2069            follower[i] += (1.0 / 64.0) * analysis.leak_boost[i] as f32;
2070        }
2071    }
2072    // effBytes>320 boost (C 1232) — trivial, kept
2073    if effective_bytes > 320 {
2074        let add = (1.5f32).min(1e-3 * (effective_bytes as f32 - 320.0));
2075        if start < end {
2076            follower[start] += add;
2077        }
2078    }
2079
2080    let mut tot_boost = 0i32;
2081    for i in start..end {
2082        let f = follower[i].min(4.0);
2083
2084        let width = channels as i32 * (mode.e_bands[i + 1] - mode.e_bands[i]) as i32 * (1 << lm);
2085        let (boost, boost_bits) = if width < 6 {
2086            let b = f.floor().max(0.0) as i32;
2087            (b, (b * width) << BITRES)
2088        } else if width > 48 {
2089            let b = (f * 8.0).floor().max(0.0) as i32;
2090            (b, ((b * width) << BITRES) / 8)
2091        } else {
2092            let b = (f * width as f32 / 6.0).floor().max(0.0) as i32;
2093            (b, (b * 6) << BITRES)
2094        };
2095
2096        // Cap only for CBR / non-transient constrained VBR (libopus 1254-1257)
2097        if !vbr || (constrained_vbr && !is_transient) {
2098            let cap_bits = ((2 * effective_bytes as i32) / 3) << (BITRES + 3);
2099            if tot_boost + boost_bits > cap_bits {
2100                offsets[i] = ((cap_bits - tot_boost) >> BITRES).max(0);
2101                break;
2102            }
2103        }
2104
2105        let quanta = (width << BITRES).min((6 << BITRES).max(width));
2106        let mut boost_count = boost;
2107        let mut as_bits = boost_count * quanta;
2108        if as_bits > cap[i] {
2109            as_bits = cap[i];
2110            boost_count = as_bits / quanta;
2111        }
2112
2113        offsets[i] = boost_count.max(0);
2114        tot_boost += boost_bits.max(0);
2115    }
2116}
2117
2118impl CeltEncoder {
2119    pub fn new(mode: &'static CeltMode, channels: usize) -> Self {
2120        Self::with_sampling_rate(mode, channels, 48000)
2121    }
2122
2123    /// CELT encoder for `sampling_rate`, which need not be 48 kHz: the input
2124    /// is zero-stuffed by `resampling_factor(sampling_rate)` so the mode's
2125    /// 48 kHz geometry is used internally, exactly like libopus
2126    /// (`celt_encoder.c` `st->upsample`).
2127    pub fn with_sampling_rate(
2128        mode: &'static CeltMode,
2129        channels: usize,
2130        sampling_rate: i32,
2131    ) -> Self {
2132        // All internal buffers are sized for at most 2 channels; a larger
2133        // value would panic later inside `FixedVec::from_value` with an
2134        // unhelpful message (issue #27 deep scan).
2135        assert!(
2136            (1..=2).contains(&channels),
2137            "CeltEncoder::new: channels must be 1 or 2 (got {channels})"
2138        );
2139        let upsample = resampling_factor(sampling_rate);
2140        let overlap = mode.overlap;
2141        let channel_mem_size = 2048 + overlap;
2142        let syn_mem_size = channels * channel_mem_size;
2143        let nb_ebands = mode.nb_ebands;
2144        let nb_x_ch = nb_ebands * channels;
2145        let frame_x_ch = MAX_FRAME_SIZE * channels;
2146        let bufstride_x_ch = (MAX_FRAME_SIZE + overlap) * channels;
2147        Self {
2148            mode,
2149            channels,
2150            upsample,
2151            end_band: mode.nb_ebands,
2152            complexity: 9,
2153            syn_mem: FixedVec::from_value(0.0, syn_mem_size),
2154            enc_decode_mem: FixedVec::from_value(0.0, syn_mem_size),
2155            old_band_e: FixedVec::from_value(0.0, nb_x_ch),
2156            preemph_mem: FixedVec::from_value(0.0, channels),
2157            tonal_average: 256,
2158            hf_average: 0,
2159            tapset_decision: 0,
2160            spread_decision: SPREAD_NORMAL,
2161            intensity: 0,
2162            last_coded_bands: 0,
2163            prefilter_mem: FixedVec::from_value(0.0, channels * COMBFILTER_MAXPERIOD),
2164            prefilter_period: COMBFILTER_MINPERIOD,
2165            prefilter_gain: 0.0,
2166            prefilter_tapset: 0,
2167            old_band_e2: FixedVec::from_value(0.0, nb_x_ch),
2168            old_band_e3: FixedVec::from_value(0.0, nb_x_ch),
2169            last_band_log_e: FixedVec::from_value(0.0, nb_x_ch),
2170            delayed_intra: 0.0,
2171            lsb_depth: 24,
2172            overlap_max: 0.0,
2173            bitrate: OPUS_BITRATE_MAX,
2174            vbr: false,
2175            constrained_vbr: true,
2176            vbr_reservoir: 0,
2177            vbr_drift: 0,
2178            vbr_offset: 0,
2179            vbr_count: 0,
2180
2181            w_in_buf: FixedVec::from_value(0.0, bufstride_x_ch),
2182            w_freq: FixedVec::from_value(0.0, frame_x_ch + 4),
2183            w_band_e: FixedVec::from_value(0.0, nb_x_ch),
2184            w_band_e2: FixedVec::from_value(0.0, nb_x_ch),
2185
2186            w_x: FixedVec::from_value(0.0, frame_x_ch + STRIDE_ACCESS_PAD),
2187            w_band_log_e: FixedVec::from_value(0.0, nb_x_ch),
2188            w_band_log_e2: FixedVec::from_value(0.0, nb_x_ch),
2189            w_error: FixedVec::from_value(0.0, nb_x_ch),
2190            w_tf_res: FixedVec::from_value(0, nb_ebands),
2191            w_cap: FixedVec::from_value(0, nb_ebands),
2192            w_offsets: FixedVec::from_value(0, nb_ebands),
2193            w_pulses: FixedVec::from_value(0, nb_ebands),
2194            w_ebits: FixedVec::from_value(0, nb_x_ch),
2195            w_fine_priority: FixedVec::from_value(0, nb_x_ch),
2196            w_collapse_masks: FixedVec::from_value(0, nb_x_ch),
2197            w_band_amp_synth: FixedVec::from_value(0.0, nb_x_ch),
2198
2199            w_prefilter_pre: FixedVec::from_value(
2200                0.0,
2201                channels * (COMBFILTER_MAXPERIOD + MAX_FRAME_SIZE),
2202            ),
2203            w_prefilter_pitch_buf: FixedVec::from_value(
2204                0.0,
2205                (COMBFILTER_MAXPERIOD + MAX_FRAME_SIZE) >> 1,
2206            ),
2207            w_prefilter_before: FixedVec::from_value(0.0, channels),
2208            w_prefilter_after: FixedVec::from_value(0.0, channels),
2209            w_transient_tmp: FixedVec::from_value(0.0, MAX_TRANSIENT_LEN),
2210            w_transient_tmp2: FixedVec::from_value(0.0, MAX_TRANSIENT_LEN / 2),
2211            consec_transient: 0,
2212
2213            analysis: AnalysisInfo::default(),
2214            loss_rate: 0,
2215        }
2216    }
2217
2218    /// Set the highest coded band (`CELT_SET_END_BAND`). Must match the
2219    /// bandwidth the packet's TOC signals.
2220    pub fn set_end_band(&mut self, end: usize) {
2221        self.end_band = end;
2222    }
2223
2224    pub fn encode(&mut self, pcm: &[f32], frame_size: usize, rc: &mut RangeCoder) {
2225        self.encode_impl(pcm, frame_size, rc, 0, None, false)
2226    }
2227
2228    pub fn encode_with_start_band(
2229        &mut self,
2230        pcm: &[f32],
2231        frame_size: usize,
2232        rc: &mut RangeCoder,
2233        start_band: usize,
2234    ) {
2235        self.encode_impl(pcm, frame_size, rc, start_band, None, false)
2236    }
2237
2238    pub fn encode_with_budget(
2239        &mut self,
2240        pcm: &[f32],
2241        frame_size: usize,
2242        rc: &mut RangeCoder,
2243        start_band: usize,
2244        total_bits: i32,
2245    ) {
2246        self.encode_impl(pcm, frame_size, rc, start_band, Some(total_bits), false)
2247    }
2248
2249    pub fn encode_with_budget_vbr(
2250        &mut self,
2251        pcm: &[f32],
2252        frame_size: usize,
2253        rc: &mut RangeCoder,
2254        start_band: usize,
2255        total_bits: i32,
2256        is_vbr: bool,
2257    ) {
2258        self.encode_impl(pcm, frame_size, rc, start_band, Some(total_bits), is_vbr)
2259    }
2260
2261    pub fn set_lsb_depth(&mut self, depth: i32) {
2262        self.lsb_depth = depth.clamp(8, 24);
2263    }
2264
2265    pub fn get_lsb_depth(&self) -> i32 {
2266        self.lsb_depth
2267    }
2268    pub fn set_bitrate(&mut self, bitrate: i32) {
2269        self.bitrate = bitrate;
2270    }
2271    pub fn set_vbr(&mut self, vbr: bool) {
2272        self.vbr = vbr;
2273    }
2274    pub fn set_constrained_vbr(&mut self, cvbr: bool) {
2275        self.constrained_vbr = cvbr;
2276    }
2277
2278    fn encode_impl(
2279        &mut self,
2280        pcm: &[f32],
2281        api_frame_size: usize,
2282        rc: &mut RangeCoder,
2283        start_band: usize,
2284        explicit_total_bits: Option<i32>,
2285        is_vbr: bool,
2286    ) {
2287        let mode = self.mode;
2288        let channels = self.channels;
2289        let upsample = self.upsample;
2290        // Internal (48 kHz) frame size; `pcm` is at the API rate.
2291        let frame_size = api_frame_size * upsample;
2292        let nb_ebands = mode.nb_ebands;
2293        let end = self.end_band.clamp(1, nb_ebands).max(start_band);
2294        let overlap = mode.overlap;
2295
2296        // The encoder geometry requires frame_size == short_mdct_size << lm
2297        // (120/240/480/960). Any other size makes the lm search fall back to
2298        // lm=0, which mis-sizes the MDCT buffers and panics mid-transform
2299        // (or worse, silently mis-encodes). Fail fast with a clear message;
2300        // OpusEncoder::encode() validates this before reaching CELT.
2301        assert!(
2302            (0..=mode.max_lm).any(|lm| mode.short_mdct_size << lm == frame_size),
2303            "CeltEncoder: invalid frame_size {} (valid: {}..={} kHz multiples of {})",
2304            frame_size,
2305            mode.short_mdct_size,
2306            mode.short_mdct_size << mode.max_lm,
2307            mode.short_mdct_size
2308        );
2309
2310        let mut lm = 0;
2311        while (mode.short_mdct_size << lm) != frame_size {
2312            lm += 1;
2313            if lm > mode.max_lm {
2314                break;
2315            }
2316        }
2317        if (mode.short_mdct_size << lm) != frame_size {
2318            lm = 0;
2319        }
2320
2321        let syn_mem_size = 2048 + overlap;
2322        for c in 0..channels {
2323            let channel_offset = c * syn_mem_size;
2324
2325            self.syn_mem.copy_within(
2326                channel_offset + frame_size..channel_offset + syn_mem_size,
2327                channel_offset,
2328            );
2329
2330            let mut m = self.preemph_mem[c];
2331            let coef = mode.preemph[0];
2332            // Zero-stuff the API-rate input to 48 kHz (libopus celt_preemphasis
2333            // with `upsample`), then run the pre-emphasis filter over all N
2334            // internal samples.
2335            for i in 0..frame_size {
2336                let x = if i % upsample == 0 {
2337                    pcm[c * api_frame_size + i / upsample] * 32768.0
2338                } else {
2339                    0.0
2340                };
2341                let val = x - m;
2342                self.syn_mem[channel_offset + syn_mem_size - frame_size + i] = val;
2343                m = x * coef;
2344            }
2345            self.preemph_mem[c] = m;
2346        }
2347
2348        let buf_stride = frame_size + overlap;
2349        let in_buf = &mut self.w_in_buf[..buf_stride * channels];
2350        for c in 0..channels {
2351            let channel_offset = c * syn_mem_size;
2352            let in_buf_offset = c * buf_stride;
2353
2354            let src_start = syn_mem_size - frame_size - overlap;
2355            in_buf[in_buf_offset..in_buf_offset + buf_stride].copy_from_slice(
2356                &self.syn_mem[channel_offset + src_start..channel_offset + syn_mem_size],
2357            );
2358        }
2359
2360        // C 2017: tone and transient analysis see the *unfiltered* history in
2361        // the overlap head (the `prefilter_mem` tail). `in_buf`'s head is the
2362        // previous frame's prefiltered overlap, so analysing it as-is puts a
2363        // step between head and body wherever the comb filter was active,
2364        // which reads as a transient on steady pitched input (issue #38).
2365        let max_period = COMBFILTER_MAXPERIOD;
2366        for c in 0..channels {
2367            in_buf[c * buf_stride..c * buf_stride + overlap].copy_from_slice(
2368                &self.prefilter_mem[(c + 1) * max_period - overlap..(c + 1) * max_period],
2369            );
2370        }
2371
2372        // Tone detection (C 2022): use channel-contiguous in_buf with N+overlap, float path
2373        let (tone_freq, mut toneishness) = tone_detect(&*in_buf, channels, buf_stride, mode.fs);
2374        let mut tf_estimate = 0.0f32;
2375        let mut tf_chan = 0;
2376        let mut weak_transient = false;
2377
2378        let is_transient = if self.complexity >= 1 {
2379            transient_analysis(
2380                in_buf,
2381                buf_stride,
2382                channels,
2383                &mut tf_estimate,
2384                &mut tf_chan,
2385                false,
2386                &mut weak_transient,
2387                tone_freq,
2388                toneishness,
2389                &mut self.w_transient_tmp,
2390                &mut self.w_transient_tmp2,
2391            )
2392        } else {
2393            false
2394        };
2395        // Put the prefiltered overlap back for the prefilter and the MDCT
2396        // (C run_prefilter: `OPUS_COPY(in, st->in_mem, overlap)`).
2397        let in_mem_start = syn_mem_size - frame_size - overlap;
2398        for c in 0..channels {
2399            let src = c * syn_mem_size + in_mem_start;
2400            in_buf[c * buf_stride..c * buf_stride + overlap]
2401                .copy_from_slice(&self.syn_mem[src..src + overlap]);
2402        }
2403        // Clamp toneishness as in C 2034: toneishness = min(toneishness, 1 - tf_estimate)
2404        // For float, tf_estimate is log-domain; clamp to [0,1]
2405        toneishness = toneishness.min((1.0 - tf_estimate).clamp(0.0, 1.0));
2406
2407        // Check for pure tone: if tonality is very high, bypass pitch search
2408        // Keep original analysis.tonality check for prefilter gating, but dynalloc uses tone_detect result
2409        let analysis_toneishness = if self.analysis.valid {
2410            self.analysis.tonality
2411        } else {
2412            0.0
2413        };
2414        // analysis_toneishness kept for prefilter gating; tone_freq/toneishness from tone_detect used for dynalloc
2415        let pf_enabled =
2416            start_band == 0 && self.complexity >= 5 && analysis_toneishness < 0.99 && channels == 1;
2417        let (pf_on, gain1, pitch_index) = if pf_enabled {
2418            let tell = rc.tell();
2419            let nb_filled = ((tell + 4) >> 3).max(0) as usize;
2420            let total = explicit_total_bits.unwrap_or_else(|| (rc.buf.len() * 8) as i32);
2421            let nb_available = ((total / 8) as usize).saturating_sub(nb_filled);
2422            run_prefilter(
2423                in_buf,
2424                &mut self.prefilter_mem,
2425                self.prefilter_period,
2426                self.prefilter_gain,
2427                self.prefilter_tapset,
2428                self.tapset_decision,
2429                mode.window,
2430                channels,
2431                frame_size,
2432                overlap,
2433                &mut self.w_prefilter_pre,
2434                &mut self.w_prefilter_pitch_buf,
2435                &mut self.w_prefilter_before,
2436                &mut self.w_prefilter_after,
2437                &self.analysis,
2438                self.loss_rate,
2439                tf_estimate,
2440                nb_available,
2441                tone_freq,
2442                toneishness,
2443                self.complexity,
2444            )
2445        } else {
2446            // libopus runs run_prefilter (disabled) every frame, so its
2447            // history stays current for the analysis head above and for the
2448            // pitch search when the comb filter turns back on.
2449            for c in 0..channels {
2450                let body = c * buf_stride + overlap;
2451                push_prefilter_history(
2452                    &mut self.prefilter_mem[c * max_period..(c + 1) * max_period],
2453                    &in_buf[body..body + frame_size],
2454                );
2455            }
2456            (false, 0.0f32, COMBFILTER_MINPERIOD)
2457        };
2458
2459        // Save the prefiltered overlap for the next frame.
2460        // In libopus, st->in_mem stores the overlap separately and run_prefilter
2461        // copies it to/from in[]. Here we emulate that by updating syn_mem with
2462        // the last overlap samples of in_buf (which were prefiltered in place).
2463        let syn_mem_size = 2048 + overlap;
2464        for c in 0..channels {
2465            let channel_offset = c * syn_mem_size;
2466            let in_buf_offset = c * buf_stride;
2467            self.syn_mem[channel_offset + syn_mem_size - overlap..channel_offset + syn_mem_size]
2468                .copy_from_slice(&in_buf[in_buf_offset + frame_size..in_buf_offset + buf_stride]);
2469        }
2470        let freq = &mut self.w_freq[..frame_size * channels];
2471        let band_e = &mut self.w_band_e[..nb_ebands * channels];
2472        let band_log_e = &mut self.w_band_log_e[..nb_ebands * channels];
2473        let x_pad_end = (frame_size * channels + STRIDE_ACCESS_PAD).min(self.w_x.len());
2474        let x = &mut self.w_x[..x_pad_end];
2475
2476        let mut total_bits = explicit_total_bits.unwrap_or_else(|| (rc.buf.len() * 8) as i32);
2477        self.w_error[..nb_ebands * channels].fill(0.0);
2478        let error = &mut self.w_error[..nb_ebands * channels];
2479
2480        // --- Silence detection (port of libopus celt_encoder.c: sample_max / overlap_max / lsb_depth) ---
2481        // libopus measures sample/overlap maxima on the API-rate `pcm`:
2482        // `(N-overlap)/upsample` and `overlap/upsample` samples.
2483        let api_overlap = overlap / upsample;
2484        let mut sample_max = self.overlap_max;
2485        let n_nonoverlap = api_frame_size.saturating_sub(api_overlap);
2486        for c in 0..channels {
2487            let base = c * api_frame_size;
2488            for i in 0..n_nonoverlap {
2489                let v = pcm[base + i].abs();
2490                if v > sample_max {
2491                    sample_max = v;
2492                }
2493            }
2494        }
2495        let mut new_overlap_max = 0.0f32;
2496        for c in 0..channels {
2497            let base = c * api_frame_size;
2498            for i in n_nonoverlap..api_frame_size {
2499                let v = pcm[base + i].abs();
2500                if v > new_overlap_max {
2501                    new_overlap_max = v;
2502                }
2503            }
2504        }
2505        self.overlap_max = new_overlap_max;
2506        if new_overlap_max > sample_max {
2507            sample_max = new_overlap_max;
2508        }
2509        let threshold = 1.0 / ((1 << self.lsb_depth) as f32);
2510        let mut silence = sample_max <= threshold;
2511        let tell_initial = rc.tell();
2512        let tell_initial_frac = rc.tell_frac();
2513        let nb_filled_bytes_initial = ((tell_initial + 4) >> 3).max(0) as usize;
2514        let mut nb_compressed_bytes = (total_bits / 8) as usize;
2515        if tell_initial == 1 {
2516            rc.encode_bit_logp(silence, 15);
2517        } else {
2518            silence = false;
2519        }
2520        if silence {
2521            if is_vbr {
2522                let target_bytes = nb_filled_bytes_initial + 2;
2523                if target_bytes < nb_compressed_bytes {
2524                    nb_compressed_bytes = target_bytes;
2525                    total_bits = (nb_compressed_bytes * 8) as i32;
2526                    rc.shrink(nb_compressed_bytes as u32);
2527                }
2528            }
2529            let cur_tell = rc.tell();
2530            let new_tell = (nb_compressed_bytes * 8) as i32;
2531            rc.nbits_total += new_tell - cur_tell;
2532        }
2533        // General VBR max bound (libopus 1936-1961) - constrained VBR
2534        if self.vbr && self.bitrate != OPUS_BITRATE_MAX {
2535            let vbr_rate = bitrate_to_bits(self.bitrate, mode.fs, frame_size as i32) << BITRES;
2536            let nb_available = nb_compressed_bytes.saturating_sub(nb_filled_bytes_initial);
2537            if self.constrained_vbr {
2538                let vbr_bound = vbr_rate;
2539                let max_allowed = core::cmp::min(
2540                    core::cmp::max(
2541                        if tell_initial == 1 { 2 } else { 0 },
2542                        (vbr_rate + vbr_bound - self.vbr_reservoir) >> (BITRES + 3),
2543                    ),
2544                    nb_available as i32,
2545                );
2546                if (max_allowed as usize) < nb_available {
2547                    nb_compressed_bytes = nb_filled_bytes_initial + max_allowed as usize;
2548                    total_bits = (nb_compressed_bytes * 8) as i32;
2549                    rc.shrink(nb_compressed_bytes as u32);
2550                }
2551            }
2552            // effectiveBytes would be vbr_rate>>(3+BITRES) for later lambda, but total_bits already reflects bound
2553        }
2554
2555        if start_band == 0 && !silence && rc.tell() + 16 <= total_bits {
2556            rc.encode_bit_logp(pf_on, 1);
2557            if pf_on {
2558                let qg = (gain1 / 0.09375 - 1.0 + 0.5).floor() as i32;
2559                let qg = qg.clamp(0, 7);
2560                let pi = (pitch_index + 1) as u32;
2561                let octave = 32 - pi.leading_zeros();
2562                let octave = (octave as i32 - 5).max(0) as u32;
2563                rc.enc_uint(octave, 6);
2564                rc.enc_bits(pi - (16 << octave), 4 + octave);
2565                rc.enc_bits(qg as u32, 3);
2566                rc.encode_icdf(self.tapset_decision, &TAPSET_ICDF, 2);
2567            }
2568        }
2569
2570        let mut short_blocks = false;
2571        if lm > 0 && rc.tell() + 3 <= total_bits {
2572            rc.encode_bit_logp(is_transient, 3);
2573            if is_transient {
2574                short_blocks = true;
2575            }
2576        }
2577        let second_mdct = short_blocks && self.complexity >= 8;
2578        if second_mdct {
2579            for c in 0..channels {
2580                let c_buf = c * buf_stride;
2581                let c_freq = c * frame_size;
2582                mode.mdct.forward(
2583                    &in_buf[c_buf..],
2584                    &mut freq[c_freq..],
2585                    mode.window,
2586                    overlap,
2587                    mode.max_lm - lm,
2588                    1,
2589                );
2590            }
2591            upsample_scale_zero(freq, frame_size, upsample, channels);
2592            let band_e2 = &mut self.w_band_e2[..nb_ebands * channels];
2593            compute_band_energies(mode, freq, band_e2, end, channels, lm);
2594            let band_log_e2 = &mut self.w_band_log_e2[..nb_ebands * channels];
2595            crate::bands::amp2log2(mode, 0, end, band_e2, band_log_e2, channels);
2596            for c in 0..channels {
2597                for i in 0..end {
2598                    band_log_e2[c * nb_ebands + i] += lm as f32 * 0.5;
2599                }
2600            }
2601        }
2602        if short_blocks {
2603            let b = 1 << lm;
2604            let n = frame_size / b;
2605            for c in 0..channels {
2606                let c_offset = c * buf_stride;
2607                for i in 0..b {
2608                    mode.mdct.forward(
2609                        &in_buf[c_offset + i * n..c_offset + buf_stride],
2610                        &mut freq[c * frame_size + i..],
2611                        mode.window,
2612                        overlap,
2613                        mode.max_lm,
2614                        b,
2615                    );
2616                }
2617            }
2618            upsample_scale_zero(freq, frame_size, upsample, channels);
2619
2620            compute_band_energies(mode, freq, band_e, end, channels, lm);
2621            normalise_bands(
2622                mode,
2623                freq,
2624                x,
2625                band_e,
2626                end,
2627                channels,
2628                (1 << lm) as usize,
2629            );
2630            crate::bands::amp2log2(mode, start_band, end, band_e, band_log_e, channels);
2631        } else {
2632            // Long MDCT (non-transient)
2633            for c in 0..channels {
2634                let c_buf = c * buf_stride;
2635                let c_freq = c * frame_size;
2636                mode.mdct.forward(
2637                    &in_buf[c_buf..],
2638                    &mut freq[c_freq..],
2639                    mode.window,
2640                    overlap,
2641                    mode.max_lm - lm,
2642                    1,
2643                );
2644            }
2645            upsample_scale_zero(freq, frame_size, upsample, channels);
2646            compute_band_energies(mode, freq, band_e, end, channels, lm);
2647            normalise_bands(
2648                mode,
2649                freq,
2650                x,
2651                band_e,
2652                end,
2653                channels,
2654                (1 << lm) as usize,
2655            );
2656            crate::bands::amp2log2(mode, start_band, end, band_e, band_log_e, channels);
2657        }
2658
2659        // C uses st->force_intra (CTL flag, 0 by default) instead of the old
2660        // intra_ener heuristic, so no intra forcing happens here.
2661        quant_coarse_energy_advanced(
2662            mode,
2663            start_band,
2664            end,
2665            end,
2666            band_log_e,
2667            &mut self.old_band_e,
2668            total_bits as u32,
2669            error,
2670            rc,
2671            channels,
2672            lm,
2673            (total_bits / 8) as usize,
2674            false, // force_intra: C st->force_intra (CTL flag, 0 by default)
2675            &mut self.delayed_intra,
2676            self.complexity >= 4,
2677            0,
2678            false,
2679        );
2680        self.w_tf_res[..nb_ebands].fill(0);
2681        let tf_res = &mut self.w_tf_res[..nb_ebands];
2682        let effective_bytes = ((total_bits / 8) as usize).max(1);
2683        let lambda = 80.max(20480 / effective_bytes + 2) as i32;
2684
2685        // C runs dynalloc_analysis before tf_analysis: it produces `importance`
2686        // (used by the tf Viterbi) and `offsets` (encoded later, after spread).
2687        self.w_cap[..nb_ebands].fill(0);
2688        let cap = &mut self.w_cap[..nb_ebands];
2689        for (i, cap_i) in cap.iter_mut().enumerate() {
2690            let n = (mode.e_bands[i + 1] - mode.e_bands[i]) << lm;
2691            *cap_i = ((mode.cache.caps[nb_ebands * (2 * lm + channels - 1) + i] as i32 + 64)
2692                * channels as i32
2693                * n as i32)
2694                >> 2;
2695        }
2696
2697        self.w_offsets[..nb_ebands].fill(0);
2698        let offsets = &mut self.w_offsets[..nb_ebands];
2699
2700        let band_log_e2_opt = if second_mdct {
2701            Some(&self.w_band_log_e2[..nb_ebands * channels] as &[f32])
2702        } else {
2703            None
2704        };
2705        let surround_dynalloc = [0.0f32; 21];
2706        let mut importance = [13i32; MAX_NB_EBANDS];
2707        dynalloc_analysis_simple(
2708            mode,
2709            band_log_e,
2710            band_log_e2_opt,
2711            &self.old_band_e,
2712            start_band,
2713            end,
2714            channels,
2715            lm,
2716            effective_bytes,
2717            is_transient,
2718            offsets,
2719            cap,
2720            self.lsb_depth,
2721            self.vbr,
2722            self.constrained_vbr,
2723            &self.analysis,
2724            &surround_dynalloc,
2725            tone_freq,
2726            toneishness,
2727            &mut importance,
2728        );
2729
2730        let tf_select = if self.complexity >= 2 && effective_bytes >= 15 * channels {
2731            tf_analysis(
2732                mode,
2733                end,
2734                is_transient,
2735                tf_res,
2736                lambda,
2737                x,
2738                frame_size,
2739                lm as i32,
2740                tf_estimate,
2741                tf_chan,
2742                &importance,
2743            )
2744        } else {
2745            0
2746        };
2747        tf_encode(
2748            start_band,
2749            end,
2750            is_transient,
2751            tf_res,
2752            lm as i32,
2753            tf_select,
2754            rc,
2755        );
2756
2757        let mut dual_stereo_val = if channels == 2 {
2758            stereo_analysis(mode, x, lm as i32, frame_size) as i32
2759        } else {
2760            0
2761        };
2762
2763        let mut stereo_saving = 0.0f32;
2764        let equiv_rate = (total_bits * 48000) / frame_size as i32;
2765        if channels == 2 {
2766            self.intensity = hysteresis_decision(
2767                equiv_rate / 1000,
2768                &INTEN_THRESHOLDS,
2769                &INTEN_HYSTERESIS,
2770                self.intensity,
2771            );
2772            self.intensity = self.intensity.clamp(0, end as i32);
2773        }
2774
2775        if self.complexity == 0 {
2776            self.spread_decision = SPREAD_NONE;
2777            if rc.tell() + 4 <= total_bits {
2778                rc.encode_icdf(self.spread_decision, &SPREAD_ICDF, 5);
2779            }
2780        } else if rc.tell() + 4 <= total_bits {
2781            if is_transient || self.complexity < 3 || effective_bytes < 10 * channels {
2782                self.spread_decision = SPREAD_NORMAL;
2783            } else {
2784                let update_hf = lm == mode.max_lm;
2785                let spread_weights = [32i32; 21];
2786                self.spread_decision = spreading_decision(
2787                    mode,
2788                    x,
2789                    &mut self.tonal_average,
2790                    self.spread_decision,
2791                    &mut self.hf_average,
2792                    &mut self.tapset_decision,
2793                    update_hf,
2794                    end,
2795                    channels,
2796                    (1 << lm) as usize,
2797                    &spread_weights,
2798                );
2799            }
2800            rc.encode_icdf(self.spread_decision, &SPREAD_ICDF, 5);
2801        } else {
2802            self.spread_decision = SPREAD_NORMAL;
2803        }
2804
2805        let mut dynalloc_logp = 6i32;
2806        let total_bits_bitres = total_bits << BITRES;
2807        let mut total_boost = 0i32;
2808        let mut tell_frac = rc.tell_frac();
2809
2810        for i in start_band..end {
2811            let width =
2812                channels as i32 * (mode.e_bands[i + 1] - mode.e_bands[i]) as i32 * (1 << lm);
2813            let quanta = (width << BITRES).min((6 << BITRES).max(width));
2814            let mut dynalloc_loop_logp = dynalloc_logp;
2815            let mut boost = 0i32;
2816            let mut j = 0i32;
2817
2818            while tell_frac + (dynalloc_loop_logp << BITRES) < total_bits_bitres - total_boost
2819                && boost < cap[i]
2820            {
2821                let flag = j < offsets[i];
2822                rc.encode_bit_logp(flag, dynalloc_loop_logp as u32);
2823                tell_frac = rc.tell_frac();
2824                if !flag {
2825                    break;
2826                }
2827                boost += quanta;
2828                total_boost += quanta;
2829                dynalloc_loop_logp = 1;
2830                j += 1;
2831            }
2832
2833            if j > 0 {
2834                dynalloc_logp = 2.max(dynalloc_logp - 1);
2835            }
2836            offsets[i] = boost;
2837        }
2838
2839        let alloc_trim = alloc_trim_analysis(
2840            mode,
2841            x,
2842            band_log_e,
2843            end,
2844            lm as i32,
2845            channels,
2846            frame_size,
2847            &mut stereo_saving,
2848            tf_estimate,
2849            self.intensity,
2850            0.0,
2851            equiv_rate,
2852        );
2853        if rc.tell_frac() + (6 << BITRES) <= total_bits_bitres - total_boost {
2854            rc.encode_icdf(alloc_trim, &TRIM_ICDF, 7);
2855        }
2856        // Second VBR: compute target via simplified compute_vbr (libopus 2436-2534)
2857        // This makes packet size variable for VBR (beyond silence) while keeping reservoir in sync
2858        if self.vbr && self.bitrate != OPUS_BITRATE_MAX {
2859            let vbr_rate = bitrate_to_bits(self.bitrate, mode.fs, frame_size as i32) << BITRES;
2860            let hybrid = start_band != 0;
2861            let lm_diff = mode.max_lm as i32 - lm as i32;
2862            let mut base_target = if !hybrid {
2863                vbr_rate - ((40 * channels as i32 + 20) << BITRES)
2864            } else {
2865                (0).max(vbr_rate - ((9 * channels as i32 + 4) << BITRES))
2866            };
2867            if self.constrained_vbr {
2868                base_target += self.vbr_offset >> lm_diff;
2869            }
2870            let tot_boost = total_boost;
2871            let cur_tell_frac = rc.tell_frac();
2872            // Use the exact initial tell_frac captured at entry (libopus tell0_frac),
2873            // not tell_initial<<BITRES which loses the fractional part.
2874            let min_allowed = {
2875                let a = ((cur_tell_frac + tot_boost + (1 << (BITRES + 3)) - 1) >> (BITRES + 3)) + 2;
2876                if hybrid {
2877                    let b = (tell_initial_frac + (37 << BITRES) + tot_boost
2878                        + (1 << (BITRES + 3))
2879                        - 1)
2880                        >> (BITRES + 3);
2881                    a.max(b)
2882                } else {
2883                    a
2884                }
2885            };
2886            // nbCompressedBytes is current max (after first VBR bound), effectiveBytes ~ vbr_rate>>(3+BITRES) for lambda already
2887            let max_depth = compute_max_depth(
2888                mode,
2889                band_log_e,
2890                nb_ebands,
2891                channels,
2892                end,
2893                self.lsb_depth,
2894            );
2895            let mut target = if !hybrid {
2896                compute_vbr(
2897                    mode,
2898                    &self.analysis,
2899                    base_target,
2900                    lm as i32,
2901                    self.bitrate,
2902                    self.last_coded_bands,
2903                    channels as i32,
2904                    self.intensity,
2905                    self.constrained_vbr,
2906                    stereo_saving,
2907                    tot_boost,
2908                    tf_estimate,
2909                    0, // pitch_change stub
2910                    max_depth,
2911                    false,
2912                    false,
2913                    0.0,
2914                    0.0, // temporal_vbr stub
2915                )
2916            } else {
2917                let mut t = base_target;
2918                t += ((tf_estimate - 0.25) * 50.0 * (1 << BITRES) as f32) as i32;
2919                if tf_estimate > 0.7 {
2920                    t = t.max(50 << BITRES);
2921                }
2922                t
2923            };
2924            target += cur_tell_frac;
2925            let mut nb_available = ((target + (1 << (BITRES + 2))) >> (BITRES + 3)) as usize;
2926            nb_available = nb_available.max(min_allowed as usize);
2927            nb_available = nb_available.min(nb_compressed_bytes);
2928            let mut delta = target - vbr_rate;
2929            target = (nb_available as i32) << (BITRES + 3);
2930            if silence {
2931                nb_available = 2;
2932                target = (2 * 8) << BITRES;
2933                delta = 0;
2934            }
2935            // Reservoir / drift update (libopus 2502-2529)
2936            if self.vbr_count < 970 {
2937                self.vbr_count += 1;
2938            }
2939            let alpha = if self.vbr_count < 970 {
2940                // celt_rcp((vbr_count+20)<<16) in Q15 is 32768/(vbr_count+20)
2941                let v = self.vbr_count + 20;
2942                32768 / v  // Q15
2943            } else {
2944                33 // QCONST16(0.001,15) ~33
2945            };
2946            if self.constrained_vbr {
2947                self.vbr_reservoir += target - vbr_rate;
2948                // drift: MULT16_32_Q15(alpha, delta*(1<<lm_diff) - offset - drift) (libopus 2516)
2949                let delta_scaled = delta * (1 << lm_diff);
2950                let delta_minus = delta_scaled - self.vbr_offset - self.vbr_drift;
2951                self.vbr_drift += ((alpha as i64 * delta_minus as i64) >> 15) as i32;
2952                self.vbr_offset = -self.vbr_drift;
2953            }
2954            if self.constrained_vbr && self.vbr_reservoir < 0 {
2955                let adjust = (-self.vbr_reservoir) / (8 << BITRES);
2956                if !silence {
2957                    nb_available = (nb_available as i32 + adjust) as usize;
2958                }
2959                self.vbr_reservoir = 0;
2960            }
2961            nb_compressed_bytes = nb_compressed_bytes.min(nb_available);
2962            total_bits = (nb_compressed_bytes * 8) as i32;
2963            rc.shrink(nb_compressed_bytes as u32);
2964        }
2965
2966        let mut intensity = self.intensity;
2967        self.w_pulses[..nb_ebands].fill(0);
2968        let pulses = &mut self.w_pulses[..nb_ebands];
2969
2970        let stereo = channels > 1;
2971        let ebands_stereo = if stereo {
2972            nb_ebands * channels
2973        } else {
2974            nb_ebands
2975        };
2976        self.w_fine_priority[..ebands_stereo].fill(0);
2977        let fine_priority = &mut self.w_fine_priority[..ebands_stereo];
2978        self.w_ebits[..ebands_stereo].fill(0);
2979        let ebits = &mut self.w_ebits[..ebands_stereo];
2980        let mut balance = 0;
2981
2982        // C: bits = packet_bits - tell - 1; then subtract anti_collapse_rsv
2983        // (celt_encoder.c:2614-2618) BEFORE the allocation.
2984        let mut alloc_bits = (total_bits << BITRES) - rc.tell_frac() - 1;
2985        let anti_collapse_rsv = if is_transient && lm >= 2 {
2986            if alloc_bits >= ((lm as i32 + 2) << BITRES) {
2987                1i32 << BITRES
2988            } else {
2989                0
2990            }
2991        } else {
2992            0
2993        };
2994        alloc_bits -= anti_collapse_rsv;
2995
2996        self.last_coded_bands = clt_compute_allocation(
2997            mode,
2998            start_band,
2999            end,
3000            offsets,
3001            cap,
3002            alloc_trim,
3003            &mut intensity,
3004            &mut dual_stereo_val,
3005            alloc_bits,
3006            &mut balance,
3007            pulses,
3008            ebits,
3009            fine_priority,
3010            channels as i32,
3011            lm as i32,
3012            rc,
3013            true,
3014            0,
3015            nb_ebands as i32 - 1,
3016        );
3017
3018        quant_fine_energy(
3019            mode,
3020            start_band,
3021            end,
3022            &mut self.old_band_e,
3023            error,
3024            ebits,
3025            rc,
3026            channels,
3027        );
3028
3029        self.w_collapse_masks[..nb_ebands * channels].fill(0);
3030        let collapse_masks = &mut self.w_collapse_masks[..nb_ebands * channels];
3031        let (x_split, y_split) = x.split_at_mut(frame_size);
3032        let y_opt = if channels == 2 { Some(y_split) } else { None };
3033
3034        let mut dual_stereo = dual_stereo_val != 0;
3035
3036        let theta_rdo = channels == 2 && !dual_stereo && self.complexity >= 8;
3037        let resynth = theta_rdo;
3038
3039        quant_all_bands(
3040            true,
3041            mode,
3042            start_band,
3043            end,
3044            x_split,
3045            y_opt,
3046            collapse_masks,
3047            band_e,
3048            pulses,
3049            short_blocks,
3050            self.spread_decision,
3051            &mut dual_stereo,
3052            intensity as usize,
3053            tf_res,
3054            (total_bits << BITRES) - anti_collapse_rsv,
3055            &mut balance,
3056            rc,
3057            lm as i32,
3058            self.last_coded_bands,
3059            resynth,
3060            false,
3061            &mut 0u32,
3062        );
3063
3064        if anti_collapse_rsv > 0 {
3065            let anti_collapse_on = if self.consec_transient < 2 {
3066                1u32
3067            } else {
3068                0u32
3069            };
3070            rc.enc_bits(anti_collapse_on, 1);
3071        }
3072
3073        quant_energy_finalise(
3074            mode,
3075            start_band,
3076            end,
3077            &mut self.old_band_e,
3078            error,
3079            ebits,
3080            fine_priority,
3081            total_bits - rc.tell(),
3082            rc,
3083            channels,
3084        );
3085
3086        if silence {
3087            for v in self.old_band_e[..channels * nb_ebands].iter_mut() {
3088                *v = -28.0;
3089            }
3090        }
3091
3092        if resynth {
3093            let band_amp_synth = &mut self.w_band_amp_synth[..nb_ebands * channels];
3094            log2amp(mode, end, band_amp_synth, &self.old_band_e, channels);
3095            // `w_freq` is no longer needed after analysis/quant; reuse it as the
3096            // synthesis scratch (avoids a separate `w_freq_synth` buffer).
3097            self.w_freq[..frame_size * channels].fill(0.0);
3098            let freq_synth = &mut self.w_freq[..frame_size * channels];
3099            denormalise_bands(
3100                mode,
3101                x,
3102                freq_synth,
3103                band_amp_synth,
3104                start_band,
3105                end,
3106                channels,
3107                (1 << lm) as usize,
3108            );
3109            let (syn_shift, syn_b) = if is_transient {
3110                (mode.max_lm, 1 << lm)
3111            } else {
3112                (mode.max_lm - lm, 1)
3113            };
3114            let syn_n = frame_size / syn_b;
3115            let decode_buf_size = 2048;
3116
3117            for c in 0..channels {
3118                let co = c * syn_mem_size;
3119                self.enc_decode_mem
3120                    .copy_within(co + frame_size..co + decode_buf_size + overlap, co);
3121            }
3122
3123            for c in 0..channels {
3124                let co = c * syn_mem_size;
3125                let out_syn_idx = decode_buf_size - frame_size;
3126                for bi in 0..syn_b {
3127                    let syn_stride = if is_transient {
3128                        mode.short_mdct_size
3129                    } else {
3130                        syn_n
3131                    };
3132                    mode.mdct.backward(
3133                        &freq_synth[c * frame_size + bi..],
3134                        &mut self.enc_decode_mem[co + out_syn_idx + bi * syn_stride..],
3135                        mode.window,
3136                        overlap,
3137                        syn_shift,
3138                        syn_b,
3139                    );
3140                }
3141            }
3142        }
3143
3144        self.last_band_log_e.copy_from_slice(&self.old_band_e);
3145
3146        if !is_transient {
3147            self.old_band_e3.copy_from_slice(&self.old_band_e2);
3148            self.old_band_e2.copy_from_slice(&self.old_band_e);
3149        } else {
3150            for i in 0..channels * nb_ebands {
3151                self.old_band_e2[i] = self.old_band_e2[i].min(self.old_band_e[i]);
3152            }
3153        }
3154
3155        rc.pad_to_bits(total_bits);
3156
3157        if pf_on {
3158            self.prefilter_period = pitch_index;
3159            self.prefilter_gain = gain1;
3160            self.prefilter_tapset = self.tapset_decision;
3161        } else {
3162            self.prefilter_period = COMBFILTER_MINPERIOD;
3163            self.prefilter_gain = 0.0;
3164            self.prefilter_tapset = self.tapset_decision;
3165        }
3166
3167        if is_transient {
3168            self.consec_transient += 1;
3169        } else {
3170            self.consec_transient = 0;
3171        }
3172    }
3173}
3174
3175pub struct CeltDecoder {
3176    mode: &'static CeltMode,
3177    channels: usize,
3178    /// Channels coded in the stream (libopus `CELT_SET_CHANNELS`). It drives
3179    /// the bitstream; `channels` drives synthesis and the output.
3180    stream_channels: usize,
3181    /// Decimation factor for non-48kHz API rates (1=48k, 2=24k, 3=16k, 4=12k, 6=8k).
3182    /// Mirrors libopus `CELTDecoder.downsample`.
3183    downsample: usize,
3184    decode_mem: FixedVec<f32, CELT_DECODE_MEM>,
3185    old_band_e: FixedVec<f32, CELT_NB_X_CH>,
3186    preemph_mem: FixedVec<f32, CELT_MAX_CHANNELS>,
3187    prefilter_mem: FixedVec<f32, CELT_PREFILTER_MEM>,
3188    prefilter_period: usize,
3189    prefilter_period_old: usize,
3190    prefilter_gain: f32,
3191    prefilter_gain_old: f32,
3192    prefilter_tapset: i32,
3193    prefilter_tapset_old: i32,
3194    old_band_e2: FixedVec<f32, CELT_NB_X_CH>,
3195    old_band_e3: FixedVec<f32, CELT_NB_X_CH>,
3196    rng: u32,
3197
3198    w_tf_res: FixedVec<i32, CELT_NB_EBANDS>,
3199    w_cap: FixedVec<i32, CELT_NB_EBANDS>,
3200    w_offsets: FixedVec<i32, CELT_NB_EBANDS>,
3201    w_pulses: FixedVec<i32, CELT_NB_EBANDS>,
3202    w_ebits: FixedVec<i32, CELT_NB_X_CH>,
3203    w_fine_priority: FixedVec<i32, CELT_NB_X_CH>,
3204    w_x: FixedVec<f32, CELT_W_X_DEC>,
3205    w_collapse_masks: FixedVec<u32, CELT_NB_X_CH>,
3206    w_freq: FixedVec<f32, CELT_W_FREQ_DEC>,
3207    w_band_amp: FixedVec<f32, CELT_NB_X_CH>,
3208    w_pcm_frame: FixedVec<f32, DECODE_BUFFER_SIZE>,
3209    w_post: FixedVec<f32, { DECODE_BUFFER_SIZE + COMBFILTER_MAXPERIOD }>,
3210}
3211
3212impl CeltDecoder {
3213    /// Create a CELT decoder. `sampling_rate` is the API sampling rate
3214    /// (8000–48000); the decoder always operates at 48 kHz internally and
3215    /// decimates by `resampling_factor(sampling_rate)` on output.
3216    pub fn new(mode: &'static CeltMode, channels: usize, sampling_rate: i32) -> Self {
3217        let mut decoder = MaybeUninit::uninit();
3218        unsafe {
3219            Self::write_new(decoder.as_mut_ptr(), mode, channels, sampling_rate);
3220            decoder.assume_init()
3221        }
3222    }
3223
3224    pub(crate) unsafe fn write_new(
3225        this: *mut Self,
3226        mode: &'static CeltMode,
3227        channels: usize,
3228        sampling_rate: i32,
3229    ) {
3230        // All internal buffers are sized for at most 2 channels
3231        // (issue #27 deep scan).
3232        assert!(
3233            (1..=2).contains(&channels),
3234            "CeltDecoder::new: channels must be 1 or 2 (got {channels})"
3235        );
3236        let overlap = mode.overlap;
3237        let nb_ebands = mode.nb_ebands;
3238        // Band and spectrum buffers are sized for a stereo stream whatever
3239        // the output (libopus keeps `2*nbEBands` energies): a mono decoder
3240        // may be handed one. Capacity is fixed, so this costs no memory.
3241        let nb_x_ch = nb_ebands * CELT_MAX_CHANNELS;
3242        let dec_frame_x_ch = DECODE_BUFFER_SIZE * CELT_MAX_CHANNELS;
3243        unsafe {
3244            (&raw mut (*this).mode).write(mode);
3245            (&raw mut (*this).channels).write(channels);
3246            (&raw mut (*this).stream_channels).write(channels);
3247            (&raw mut (*this).downsample).write(resampling_factor(sampling_rate));
3248            FixedVec::write_from_value(
3249                &raw mut (*this).decode_mem,
3250                0.0,
3251                channels * (DECODE_BUFFER_SIZE + overlap),
3252            );
3253            FixedVec::write_from_value(&raw mut (*this).old_band_e, 0.0, nb_x_ch);
3254            FixedVec::write_from_value(&raw mut (*this).preemph_mem, 0.0, channels);
3255            FixedVec::write_from_value(
3256                &raw mut (*this).prefilter_mem,
3257                0.0,
3258                channels * COMBFILTER_MAXPERIOD,
3259            );
3260            (&raw mut (*this).prefilter_period).write(COMBFILTER_MINPERIOD);
3261            (&raw mut (*this).prefilter_period_old).write(COMBFILTER_MINPERIOD);
3262            (&raw mut (*this).prefilter_gain).write(0.0);
3263            (&raw mut (*this).prefilter_gain_old).write(0.0);
3264            (&raw mut (*this).prefilter_tapset).write(0);
3265            (&raw mut (*this).prefilter_tapset_old).write(0);
3266            // oldLogE and oldLogE2 start at -28 dB, as libopus's reset (run at init) sets them.
3267            FixedVec::write_from_value(&raw mut (*this).old_band_e2, -28.0, nb_x_ch);
3268            FixedVec::write_from_value(&raw mut (*this).old_band_e3, -28.0, nb_x_ch);
3269            (&raw mut (*this).rng).write(0);
3270            FixedVec::write_from_value(&raw mut (*this).w_tf_res, 0, nb_ebands);
3271            FixedVec::write_from_value(&raw mut (*this).w_cap, 0, nb_ebands);
3272            FixedVec::write_from_value(&raw mut (*this).w_offsets, 0, nb_ebands);
3273            FixedVec::write_from_value(&raw mut (*this).w_pulses, 0, nb_ebands);
3274            FixedVec::write_from_value(&raw mut (*this).w_ebits, 0, nb_x_ch);
3275            FixedVec::write_from_value(&raw mut (*this).w_fine_priority, 0, nb_x_ch);
3276            FixedVec::write_from_value(
3277                &raw mut (*this).w_x,
3278                0.0,
3279                dec_frame_x_ch + STRIDE_ACCESS_PAD,
3280            );
3281            FixedVec::write_from_value(&raw mut (*this).w_collapse_masks, 0, nb_x_ch);
3282            FixedVec::write_from_value(&raw mut (*this).w_freq, 0.0, dec_frame_x_ch + 4); // +4: NEON backward pre-rotation reads up to 3 elements past n2
3283            FixedVec::write_from_value(&raw mut (*this).w_band_amp, 0.0, nb_x_ch);
3284            FixedVec::write_from_value(&raw mut (*this).w_pcm_frame, 0.0, DECODE_BUFFER_SIZE);
3285            FixedVec::write_from_value(
3286                &raw mut (*this).w_post,
3287                0.0,
3288                DECODE_BUFFER_SIZE + COMBFILTER_MAXPERIOD,
3289            );
3290        }
3291    }
3292
3293    pub fn decode(&mut self, compressed: &[u8], frame_size: usize, pcm: &mut [f32]) -> usize {
3294        self.decode_impl(compressed, frame_size, pcm, 0, self.mode.nb_ebands)
3295    }
3296
3297    /// Set the number of channels coded in the stream (libopus
3298    /// `CELT_SET_CHANNELS`). A mono stream plays on both channels of a stereo
3299    /// decoder; a stereo stream is downmixed by a mono one.
3300    pub fn set_stream_channels(&mut self, channels: usize) {
3301        assert!(
3302            (1..=CELT_MAX_CHANNELS).contains(&channels),
3303            "CeltDecoder::set_stream_channels: channels must be 1 or 2 (got {channels})"
3304        );
3305        self.stream_channels = channels;
3306    }
3307
3308    /// Reset all decoder state (equivalent to libopus `OPUS_RESET_STATE`).
3309    /// Used at SILK↔CELT mode transitions to avoid cross-mode artifacts.
3310    pub fn reset_state(&mut self) {
3311        self.decode_mem.fill(0.0);
3312        self.old_band_e.fill(0.0);
3313        self.preemph_mem.fill(0.0);
3314        self.prefilter_mem.fill(0.0);
3315        self.prefilter_period = COMBFILTER_MINPERIOD;
3316        self.prefilter_period_old = COMBFILTER_MINPERIOD;
3317        self.prefilter_gain = 0.0;
3318        self.prefilter_gain_old = 0.0;
3319        self.prefilter_tapset = 0;
3320        self.prefilter_tapset_old = 0;
3321        // -28 dB, as libopus's OPUS_RESET_STATE.
3322        self.old_band_e2.fill(-28.0);
3323        self.old_band_e3.fill(-28.0);
3324        self.rng = 0;
3325    }
3326
3327    pub fn decode_with_start_band(
3328        &mut self,
3329        compressed: &[u8],
3330        frame_size: usize,
3331        pcm: &mut [f32],
3332        start_band: usize,
3333    ) -> usize {
3334        self.decode_impl(compressed, frame_size, pcm, start_band, self.mode.nb_ebands)
3335    }
3336
3337    pub fn decode_from_range_coder(
3338        &mut self,
3339        rc: &mut RangeCoder,
3340        total_bits: i32,
3341        frame_size: usize,
3342        pcm: &mut [f32],
3343        start_band: usize,
3344    ) -> usize {
3345        self.decode_impl_from_rc(
3346            rc,
3347            total_bits,
3348            frame_size,
3349            pcm,
3350            start_band,
3351            self.mode.nb_ebands,
3352        )
3353    }
3354
3355    pub fn decode_from_range_coder_with_band_range(
3356        &mut self,
3357        rc: &mut RangeCoder,
3358        total_bits: i32,
3359        frame_size: usize,
3360        pcm: &mut [f32],
3361        start_band: usize,
3362        end_band: usize,
3363    ) -> usize {
3364        self.decode_impl_from_rc(rc, total_bits, frame_size, pcm, start_band, end_band)
3365    }
3366
3367    fn decode_impl(
3368        &mut self,
3369        compressed: &[u8],
3370        frame_size: usize,
3371        pcm: &mut [f32],
3372        start_band: usize,
3373        end_band: usize,
3374    ) -> usize {
3375        let total_bits = (compressed.len() * 8) as i32;
3376        let mut rc = RangeCoder::new_decoder(compressed);
3377        self.decode_impl_from_rc(&mut rc, total_bits, frame_size, pcm, start_band, end_band)
3378    }
3379
3380    fn decode_impl_from_rc(
3381        &mut self,
3382        rc: &mut RangeCoder,
3383        total_bits: i32,
3384        frame_size: usize,
3385        pcm: &mut [f32],
3386        start_band: usize,
3387        end_band: usize,
3388    ) -> usize {
3389        let mode = self.mode;
3390        // `channels` are coded in the stream and drive the bitstream (libopus
3391        // `C`); `out_channels` are synthesised (`CC`).
3392        let channels = self.stream_channels;
3393        let out_channels = self.channels;
3394        let nb_ebands = mode.nb_ebands;
3395        let end_band = end_band.min(nb_ebands).max(start_band);
3396        let overlap = mode.overlap;
3397
3398        // The API frame_size is in output samples. Internally CELT always
3399        // decodes at 48 kHz, so upscale by the downsample factor (libopus
3400        // celt_decoder.c:1196: `frame_size *= st->downsample`).
3401        let api_frame_size = frame_size;
3402        let frame_size = frame_size * self.downsample;
3403
3404        // The decode geometry requires frame_size == short_mdct_size << lm.
3405        // A non-matching size makes the lm search below fall back to lm=0,
3406        // which silently decodes garbage for large frames and indexes out of
3407        // bounds for tiny ones — reject early (issue #27 deep scan).
3408        let mut lm_matched = false;
3409        for cand in 0..=mode.max_lm {
3410            if mode.short_mdct_size << cand == frame_size {
3411                lm_matched = true;
3412                break;
3413            }
3414        }
3415        if !lm_matched || frame_size < mode.short_mdct_size {
3416            return 0;
3417        }
3418
3419        // Reject an undersized output buffer before spending the whole decode
3420        // (it used to panic only at the final sample copy, issue #27 deep
3421        // scan).
3422        if pcm.len() < api_frame_size * out_channels {
3423            return 0;
3424        }
3425
3426        let mut lm = 0;
3427        while (mode.short_mdct_size << lm) != frame_size {
3428            lm += 1;
3429            if lm > mode.max_lm {
3430                break;
3431            }
3432        }
3433        if (mode.short_mdct_size << lm) != frame_size {
3434            lm = 0;
3435        }
3436
3437        // A mono stream predicts from the louder channel of a stereo frame
3438        // before it (celt_decoder.c).
3439        if channels == 1 {
3440            let (first, second) = self.old_band_e.split_at_mut(nb_ebands);
3441            for (e, &e2) in first.iter_mut().zip(&second[..nb_ebands]) {
3442                *e = e.max(e2);
3443            }
3444        }
3445
3446        let mut tell = rc.tell();
3447        let mut silence = false;
3448        if tell >= total_bits {
3449            silence = true;
3450        } else if tell == 1 {
3451            silence = rc.decode_bit_logp(15);
3452        }
3453        if silence {
3454            // Pretend we've read all remaining bits (libopus celt_decoder.c:1324-1328)
3455            // so that every subsequent `tell+...<=total_bits` guard correctly
3456            // sees no budget left and decoding proceeds with defaults.
3457            tell = total_bits;
3458            rc.nbits_total += tell - rc.tell();
3459        }
3460
3461        let mut pf_on = false;
3462        let mut pitch_index = COMBFILTER_MINPERIOD;
3463        let mut gain1 = 0.0f32;
3464        let mut prefilter_tapset = 0;
3465
3466        if start_band == 0 && !silence && rc.tell() + 16 <= total_bits {
3467            pf_on = rc.decode_bit_logp(1);
3468            if pf_on {
3469                let octave = rc.dec_uint(6);
3470                pitch_index = ((16 << octave) + rc.dec_bits(4 + octave)) as usize - 1;
3471                let qg = rc.dec_bits(3);
3472                if rc.tell() + 2 <= total_bits {
3473                    prefilter_tapset = rc.decode_icdf(&TAPSET_ICDF, 2) as usize;
3474                }
3475                gain1 = 0.09375 * (qg as f32 + 1.0);
3476            }
3477        }
3478        if start_band != 0 {
3479            self.prefilter_gain = 0.0;
3480        }
3481
3482        let mut is_transient = false;
3483        if lm > 0 && rc.tell() + 3 <= total_bits {
3484            is_transient = rc.decode_bit_logp(3);
3485        }
3486        let short_blocks = is_transient;
3487
3488        let intra_ener = if rc.tell() + 3 <= total_bits {
3489            rc.decode_bit_logp(3)
3490        } else {
3491            false
3492        };
3493
3494        unquant_coarse_energy(
3495            mode,
3496            start_band,
3497            end_band,
3498            &mut self.old_band_e,
3499            intra_ener,
3500            rc,
3501            channels,
3502            lm,
3503        );
3504        self.w_tf_res[..nb_ebands].fill(0);
3505        let tf_res = &mut self.w_tf_res[..nb_ebands];
3506        tf_decode(start_band, end_band, is_transient, tf_res, lm as i32, rc);
3507
3508        let spread_decision = if rc.tell() + 4 <= total_bits {
3509            rc.decode_icdf(&SPREAD_ICDF, 5)
3510        } else {
3511            SPREAD_NORMAL
3512        };
3513
3514        self.w_cap[..nb_ebands].fill(0);
3515        let cap = &mut self.w_cap[..nb_ebands];
3516        for (i, cap_i) in cap.iter_mut().enumerate() {
3517            let n = (mode.e_bands[i + 1] - mode.e_bands[i]) << lm;
3518            *cap_i = ((mode.cache.caps[nb_ebands * (2 * lm + channels - 1) + i] as i32 + 64)
3519                * channels as i32
3520                * n as i32)
3521                >> 2;
3522        }
3523
3524        self.w_offsets[..nb_ebands].fill(0);
3525        let offsets = &mut self.w_offsets[..nb_ebands];
3526        let mut dynalloc_logp = 6i32;
3527        let mut total_bits_bitres = total_bits << BITRES;
3528        let mut tell_frac = rc.tell_frac();
3529        for i in start_band..end_band {
3530            let width =
3531                channels as i32 * (mode.e_bands[i + 1] - mode.e_bands[i]) as i32 * (1 << lm);
3532            let quanta = (width << BITRES).min((6i32 << BITRES).max(width));
3533            let mut dynalloc_loop_logp = dynalloc_logp;
3534            let mut boost = 0i32;
3535            while tell_frac + (dynalloc_loop_logp << BITRES) < total_bits_bitres && boost < cap[i] {
3536                let flag = rc.decode_bit_logp(dynalloc_loop_logp as u32);
3537                tell_frac = rc.tell_frac();
3538                if !flag {
3539                    break;
3540                }
3541                boost += quanta;
3542                total_bits_bitres -= quanta;
3543                dynalloc_loop_logp = 1;
3544            }
3545            offsets[i] = boost;
3546            if boost > 0 {
3547                dynalloc_logp = dynalloc_logp.max(2) - 1;
3548                dynalloc_logp = dynalloc_logp.max(2);
3549            }
3550        }
3551
3552        let alloc_trim = if rc.tell_frac() + (6 << BITRES) <= total_bits_bitres {
3553            rc.decode_icdf(&TRIM_ICDF, 7)
3554        } else {
3555            5
3556        };
3557        let anti_collapse_rsv = if is_transient && lm >= 2 {
3558            let remaining = (total_bits << BITRES) - rc.tell_frac() - 1;
3559            if remaining >= ((lm as i32 + 2) << BITRES) {
3560                1i32 << BITRES
3561            } else {
3562                0
3563            }
3564        } else {
3565            0
3566        };
3567
3568        let mut intensity = 0;
3569        let mut dual_stereo_val = if channels == 2 { 1 } else { 0 };
3570        let mut balance = 0;
3571        self.w_pulses[..nb_ebands].fill(0);
3572        let pulses = &mut self.w_pulses[..nb_ebands];
3573
3574        let ebands_stereo = if channels > 1 {
3575            nb_ebands * channels
3576        } else {
3577            nb_ebands
3578        };
3579        self.w_fine_priority[..ebands_stereo].fill(0);
3580        let fine_priority = &mut self.w_fine_priority[..ebands_stereo];
3581        self.w_ebits[..ebands_stereo].fill(0);
3582        let ebits = &mut self.w_ebits[..ebands_stereo];
3583
3584        let alloc_bits = (total_bits << BITRES) - rc.tell_frac() - 1 - anti_collapse_rsv;
3585        let coded_bands = clt_compute_allocation(
3586            mode,
3587            start_band,
3588            end_band,
3589            offsets,
3590            cap,
3591            alloc_trim,
3592            &mut intensity,
3593            &mut dual_stereo_val,
3594            alloc_bits,
3595            &mut balance,
3596            pulses,
3597            ebits,
3598            fine_priority,
3599            channels as i32,
3600            lm as i32,
3601            rc,
3602            false,
3603            0,
3604            end_band as i32 - 1,
3605        );
3606
3607        unquant_fine_energy(
3608            mode,
3609            start_band,
3610            end_band,
3611            &mut self.old_band_e,
3612            ebits,
3613            rc,
3614            channels,
3615        );
3616
3617        // Hard bound: `out_syn_idx = DECODE_BUFFER_SIZE - frame_size` below must
3618        // stay non-negative and w_pcm_frame is sized DECODE_BUFFER_SIZE. libopus
3619        // rejects frame sizes beyond DECODE_BUFFER_SIZE the same way
3620        // (celt_decoder.c). The old `+ overlap` bound let 2049..=2168 through and
3621        // caused an underflow/panic (issue #27 deep-scan).
3622        if frame_size > DECODE_BUFFER_SIZE {
3623            return 0;
3624        }
3625
3626        self.w_x[..frame_size * channels].fill(0.0);
3627
3628        let x_pad_end = (frame_size * channels + STRIDE_ACCESS_PAD).min(self.w_x.len());
3629        let x = &mut self.w_x[..x_pad_end];
3630        self.w_collapse_masks[..nb_ebands * channels].fill(0);
3631        let collapse_masks = &mut self.w_collapse_masks[..nb_ebands * channels];
3632
3633        let (x_split, y_split) = x.split_at_mut(frame_size);
3634        let y_opt = if channels == 2 { Some(y_split) } else { None };
3635
3636        let mut dual_stereo = dual_stereo_val != 0;
3637        self.w_band_amp[..nb_ebands * channels].fill(0.0);
3638        let band_amp = &mut self.w_band_amp[..nb_ebands * channels];
3639        log2amp(mode, nb_ebands, band_amp, &self.old_band_e, channels);
3640        quant_all_bands(
3641            false,
3642            mode,
3643            start_band,
3644            end_band,
3645            x_split,
3646            y_opt,
3647            collapse_masks,
3648            band_amp,
3649            pulses,
3650            short_blocks,
3651            spread_decision,
3652            &mut dual_stereo,
3653            intensity as usize,
3654            tf_res,
3655            (total_bits << BITRES) - anti_collapse_rsv,
3656            &mut balance,
3657            rc,
3658            lm as i32,
3659            coded_bands,
3660            true,
3661            false,
3662            &mut self.rng,
3663        );
3664        // Trace X values for comparison with C decoder
3665        let mut anti_collapse_on = false;
3666        if anti_collapse_rsv > 0 {
3667            anti_collapse_on = rc.dec_bits(1) != 0;
3668        }
3669        unquant_energy_finalise(
3670            mode,
3671            start_band,
3672            end_band,
3673            &mut self.old_band_e,
3674            ebits,
3675            fine_priority,
3676            total_bits - rc.tell(),
3677            rc,
3678            channels,
3679        );
3680        if anti_collapse_on {
3681            self.rng = crate::bands::anti_collapse(
3682                mode,
3683                x,
3684                collapse_masks,
3685                lm as i32,
3686                channels,
3687                frame_size,
3688                start_band,
3689                nb_ebands,
3690                &self.old_band_e,
3691                &self.old_band_e2,
3692                &self.old_band_e3,
3693                pulses,
3694                self.rng,
3695            );
3696        }
3697        if silence {
3698            // libopus celt_decoder.c:1530-1534 — silence frames carry no energy;
3699            // force the long-term predictor to a noise floor so the next packet
3700            // decodes cleanly. Without this the decoder's `oldBandE` retains
3701            // the previous frame's energies and the next decode diverges, which
3702            // is the reported "一時的に破壊" (temporarily destroyed) symptom.
3703            for v in self.old_band_e[..channels * nb_ebands].iter_mut() {
3704                *v = -28.0;
3705            }
3706        }
3707        // For silence the MDCT synthesis must still run so that overlap,
3708        // pre-emphasis and prefilter states advance, but with zeroed
3709        // frequency bins (libopus denormalise_bands(silence=1) sets
3710        // bound=0/start=end=0). Filling `w_freq` with zeros and skipping
3711        // denormalisation achieves the same effect without passing a
3712        // dedicated `silence` flag through `denormalise_bands`.
3713        let freq_len = frame_size * channels.max(out_channels);
3714        self.w_freq[..freq_len].fill(0.0);
3715        let freq = &mut self.w_freq[..freq_len];
3716        if !silence {
3717            // Recompute band_amp after unquant_energy_finalise, which adjusts old_band_e.
3718            // (Mirrors the encoder's resynth path: log2amp is called after quant_energy_finalise.)
3719            log2amp(mode, nb_ebands, band_amp, &self.old_band_e, channels);
3720            denormalise_bands(
3721                mode,
3722                x,
3723                freq,
3724                band_amp,
3725                start_band,
3726                end_band,
3727                channels,
3728                (1 << lm) as usize,
3729            );
3730            // Anti-aliasing: zero MDCT bins above the output Nyquist when
3731            // downsampling (libopus denormalise_bands `if(downsample!=1)
3732            // bound=IMIN(bound,N/downsample)`).
3733            if self.downsample > 1 {
3734                let bound = frame_size / self.downsample;
3735                for c in 0..channels {
3736                    for i in bound..frame_size {
3737                        freq[c * frame_size + i] = 0.0;
3738                    }
3739                }
3740            }
3741        }
3742        // Always trace freq and band_amp for comparison
3743
3744        // Copy a mono stream to both outputs, or downmix a stereo stream to
3745        // one (libopus celt_synthesis).
3746        if channels == 1 && out_channels == 2 {
3747            freq.copy_within(..frame_size, frame_size);
3748        } else if channels == 2 && out_channels == 1 {
3749            let (left, right) = freq.split_at_mut(frame_size);
3750            for (l, &r) in left.iter_mut().zip(&right[..frame_size]) {
3751                *l = 0.5 * *l + 0.5 * r;
3752            }
3753        }
3754
3755        let (shift, b) = if short_blocks {
3756            (mode.max_lm, 1 << lm)
3757        } else {
3758            (mode.max_lm - lm, 1)
3759        };
3760        let n = frame_size / b;
3761
3762        for c in 0..out_channels {
3763            let channel_mem_offset = c * (DECODE_BUFFER_SIZE + overlap);
3764
3765            let mem_size = DECODE_BUFFER_SIZE + overlap;
3766            self.decode_mem.copy_within(
3767                channel_mem_offset + frame_size..channel_mem_offset + mem_size,
3768                channel_mem_offset,
3769            );
3770
3771            let out_syn_idx = DECODE_BUFFER_SIZE - frame_size;
3772
3773            for i in 0..b {
3774                let block_freq_idx = c * frame_size + i;
3775                // Stride between short-block MDCT outputs is short_mdct_size (not n).
3776                // In libopus: out_syn[c] + NB*b, where NB = mode->shortMdctSize.
3777                // For non-transient b=1, i*n == 0 either way.
3778                let block_stride = if short_blocks {
3779                    mode.short_mdct_size
3780                } else {
3781                    n
3782                };
3783                let block_out_idx = channel_mem_offset + out_syn_idx + i * block_stride;
3784                let available_len = self.decode_mem.len() - block_out_idx;
3785                if available_len < n + overlap {
3786                    panic!(
3787                        "MDCT backward buffer too small: need {}, have {} (out_syn_idx={}, n={}, overlap={})",
3788                        n + overlap,
3789                        available_len,
3790                        out_syn_idx,
3791                        n,
3792                        overlap
3793                    );
3794                }
3795                self.mode.mdct.backward(
3796                    &freq[block_freq_idx..],
3797                    &mut self.decode_mem[block_out_idx..],
3798                    mode.window,
3799                    overlap,
3800                    shift,
3801                    b,
3802                );
3803            }
3804
3805            const SIG_SAT: f32 = 536870911.0;
3806            for i in 0..frame_size {
3807                let v = &mut self.decode_mem[channel_mem_offset + out_syn_idx + i];
3808                *v = v.clamp(-SIG_SAT, SIG_SAT);
3809            }
3810
3811            self.w_pcm_frame[..frame_size].fill(0.0);
3812            let pcm_frame = &mut self.w_pcm_frame[..frame_size];
3813
3814            pcm_frame.copy_from_slice(
3815                &self.decode_mem[channel_mem_offset + out_syn_idx
3816                    ..channel_mem_offset + out_syn_idx + frame_size],
3817            );
3818            if pf_on || self.prefilter_gain > 0.0 || self.prefilter_gain_old > 0.0 {
3819                // Set up w_post = [prefilter_mem | pcm_frame] for history access.
3820                // We apply combfilter in-place on w_post[COMBFILTER_MAXPERIOD..] so that
3821                // later samples can reference already-filtered earlier samples, matching C's
3822                // in-place comb_filter behavior.
3823                self.w_post[..COMBFILTER_MAXPERIOD].copy_from_slice(
3824                    &self.prefilter_mem[c * COMBFILTER_MAXPERIOD..(c + 1) * COMBFILTER_MAXPERIOD],
3825                );
3826                self.w_post[COMBFILTER_MAXPERIOD..COMBFILTER_MAXPERIOD + frame_size]
3827                    .copy_from_slice(pcm_frame);
3828
3829                let short_n = mode.short_mdct_size;
3830                // Call 1: first short_n samples, transition old→current params
3831                // Apply in-place on w_post[COMBFILTER_MAXPERIOD..], output overwrites input
3832                comb_filter_inplace(
3833                    &mut self.w_post,
3834                    COMBFILTER_MAXPERIOD,
3835                    self.prefilter_period_old,
3836                    self.prefilter_period,
3837                    short_n,
3838                    self.prefilter_gain_old,
3839                    self.prefilter_gain,
3840                    self.prefilter_tapset_old,
3841                    self.prefilter_tapset,
3842                    mode.window,
3843                    overlap,
3844                );
3845                if lm != 0 {
3846                    // Call 2: remaining N-short_n samples, transition current→new params
3847                    comb_filter_inplace(
3848                        &mut self.w_post,
3849                        COMBFILTER_MAXPERIOD + short_n,
3850                        self.prefilter_period,
3851                        pitch_index,
3852                        frame_size - short_n,
3853                        self.prefilter_gain,
3854                        gain1,
3855                        self.prefilter_tapset,
3856                        prefilter_tapset as i32,
3857                        mode.window,
3858                        overlap,
3859                    );
3860                }
3861
3862                pcm_frame.copy_from_slice(
3863                    &self.w_post[COMBFILTER_MAXPERIOD..COMBFILTER_MAXPERIOD + frame_size],
3864                );
3865
3866                self.decode_mem[channel_mem_offset + out_syn_idx
3867                    ..channel_mem_offset + out_syn_idx + frame_size]
3868                    .copy_from_slice(pcm_frame);
3869            }
3870            let mut new_mem = [0.0f32; COMBFILTER_MAXPERIOD];
3871            if frame_size >= COMBFILTER_MAXPERIOD {
3872                new_mem.copy_from_slice(&pcm_frame[frame_size - COMBFILTER_MAXPERIOD..frame_size]);
3873            } else {
3874                new_mem[..COMBFILTER_MAXPERIOD - frame_size].copy_from_slice(
3875                    &self.prefilter_mem
3876                        [c * COMBFILTER_MAXPERIOD + frame_size..(c + 1) * COMBFILTER_MAXPERIOD],
3877                );
3878                new_mem[COMBFILTER_MAXPERIOD - frame_size..].copy_from_slice(pcm_frame);
3879            }
3880            self.prefilter_mem[c * COMBFILTER_MAXPERIOD..(c + 1) * COMBFILTER_MAXPERIOD]
3881                .copy_from_slice(&new_mem);
3882
3883            let coef = mode.preemph[0];
3884            let mut m = self.preemph_mem[c];
3885            const VERY_SMALL: f32 = 1e-30f32;
3886            let ds = self.downsample;
3887            if ds == 1 {
3888                for i in 0..frame_size {
3889                    let x = pcm_frame[i];
3890                    let val = (x + VERY_SMALL + m).clamp(-SIG_SAT, SIG_SAT);
3891                    pcm[c * api_frame_size + i] = val * (1.0 / 32768.0);
3892                    m = val * coef;
3893                }
3894            } else {
3895                // Run deemphasis IIR over all internal samples, but only write
3896                // every downsample-th sample (libopus deemphasis() stride).
3897                for i in 0..frame_size {
3898                    let x = pcm_frame[i];
3899                    let val = (x + VERY_SMALL + m).clamp(-SIG_SAT, SIG_SAT);
3900                    if i % ds == 0 {
3901                        pcm[c * api_frame_size + i / ds] = val * (1.0 / 32768.0);
3902                    }
3903                    m = val * coef;
3904                }
3905            }
3906            self.preemph_mem[c] = m;
3907        }
3908
3909        self.prefilter_period_old = self.prefilter_period;
3910        self.prefilter_gain_old = self.prefilter_gain;
3911        self.prefilter_tapset_old = self.prefilter_tapset;
3912
3913        if pf_on {
3914            self.prefilter_period = pitch_index;
3915            self.prefilter_gain = gain1;
3916            self.prefilter_tapset = prefilter_tapset as i32;
3917        } else {
3918            self.prefilter_period = COMBFILTER_MINPERIOD;
3919            self.prefilter_gain = 0.0;
3920            self.prefilter_tapset = 0;
3921        }
3922
3923        if lm > 0 {
3924            self.prefilter_period_old = self.prefilter_period;
3925            self.prefilter_gain_old = self.prefilter_gain;
3926            self.prefilter_tapset_old = self.prefilter_tapset;
3927        }
3928
3929        if channels == 1 {
3930            self.old_band_e.copy_within(..nb_ebands, nb_ebands);
3931        }
3932        // Both halves, as libopus updates `oldLogE` over `2*nbEBands`.
3933        if !is_transient {
3934            self.old_band_e3.copy_from_slice(&self.old_band_e2);
3935            self.old_band_e2.copy_from_slice(&self.old_band_e);
3936        } else {
3937            for (e2, &e) in self.old_band_e2.iter_mut().zip(self.old_band_e.iter()) {
3938                *e2 = e2.min(e);
3939            }
3940        }
3941        // In case start or end change (libopus celt_decoder.c), the bands this frame didn't code
3942        // forget their energy and history. Both halves, as libopus clears them: a mono stream
3943        // keeps the second, and its next frame predicts from the larger of the two.
3944        for c in 0..CELT_MAX_CHANNELS {
3945            for i in (0..start_band).chain(end_band..nb_ebands) {
3946                self.old_band_e[c * nb_ebands + i] = 0.0;
3947                self.old_band_e2[c * nb_ebands + i] = -28.0;
3948                self.old_band_e3[c * nb_ebands + i] = -28.0;
3949            }
3950        }
3951
3952        self.rng = rc.rng;
3953
3954        api_frame_size
3955    }
3956}
3957
3958#[cfg(all(test, feature = "std"))]
3959mod tests {
3960    use super::*;
3961    use crate::{modes, range_coder::RangeCoder};
3962
3963    // Regression test: directly drive CeltEncoder with an invalid frame_size=48,
3964    // bypassing the OpusEncoder::encode() validation layer.
3965    //
3966    // This reproduces the crash that was reported against opus-rs 0.1.19 when
3967    // G.729-decoded PCM (8 kHz) reached the 48 kHz Opus encoder without correct
3968    // resampling, producing a 48-sample frame instead of 480.
3969    //
3970    // Root cause: the lm-search in encode_impl finds no valid match for frame_size=48
3971    // (valid sizes are 120, 240, 480, 960) and silently falls back to lm=0.
3972    // With lm=0 and shift=max_lm=3: n=1920>>3=240, n2=120, overlap2=60.
3973    // The in_buf slice has only frame_size+overlap=168 elements, but forward()
3974    // requires input.len() >= n2+overlap2 = 180, so it panics immediately.
3975    // In opus-rs 0.1.19 this assertion was absent and the crash reached the MDCT
3976    // output write: "index out of bounds: the len is 48 but the index is 119".
3977    //
3978    // Either way: the call panics, confirming the crash path is real.
3979    // The fix in OpusEncoder::encode() returns Err before reaching CeltEncoder.
3980    #[test]
3981    #[should_panic]
3982    fn test_celt_frame_size_48_panics_confirms_crash_path() {
3983        let mode = modes::default_mode();
3984        let mut enc = CeltEncoder::new(mode, 1);
3985        // frame_size=48: lm-search fails, falls back to lm=0.
3986        // forward() will panic — either on the input-size assertion (0.1.21+) or
3987        // on the output write (0.1.19): "len is 48 but the index is 119".
3988        let pcm = vec![0.0f32; 48 + mode.overlap]; // supply ≥ frame_size samples
3989        let mut rc = RangeCoder::new_encoder(100);
3990        enc.encode_with_budget(&pcm, 48, &mut rc, 0, 800);
3991    }
3992
3993    // Issue #54: a frame whose comb filter is cancelled must still fade the
3994    // previous frame's filter out over the overlap, because the decoder's
3995    // post-filter always does. Pitched history into a silent frame: any
3996    // filtering adds energy, so the filter is cancelled after an "on" frame.
3997    #[test]
3998    fn cancelled_prefilter_still_fades_out_previous_filter() {
3999        let mode = modes::default_mode();
4000        let (n, overlap, max_period) = (960, mode.overlap, COMBFILTER_MAXPERIOD);
4001        let mut mem: Vec<f32> = (0..max_period)
4002            .map(|i| (i as f32 * 0.3).sin() * 1000.0)
4003            .collect();
4004        let mut in_buf = vec![0.0f32; n + overlap];
4005        let mut pre = vec![0.0f32; max_period + n];
4006        let mut pitch_buf = vec![0.0f32; (max_period + n) >> 1];
4007        let (mut before, mut after) = ([0.0f32], [0.0f32]);
4008        let (pf_on, gain, _) = run_prefilter(
4009            &mut in_buf,
4010            &mut mem,
4011            100,
4012            0.5,
4013            0,
4014            0,
4015            mode.window,
4016            1,
4017            n,
4018            overlap,
4019            &mut pre,
4020            &mut pitch_buf,
4021            &mut before,
4022            &mut after,
4023            &AnalysisInfo::default(),
4024            0,
4025            0.0,
4026            100,
4027            -1.0,
4028            0.0,
4029            9,
4030        );
4031        assert!(!pf_on && gain == 0.0, "expected a cancelled filter");
4032        let body = &in_buf[overlap..];
4033        assert!(
4034            body[..overlap].iter().any(|&v| v.abs() > 1.0),
4035            "previous filter was not faded out over the overlap"
4036        );
4037        assert!(
4038            body[overlap..].iter().all(|&v| v == 0.0),
4039            "frame past the overlap must be the unfiltered input"
4040        );
4041    }
4042}