Skip to main content

rusty_opus/silk/
nsq_del_dec.rs

1use crate::silk::define::*;
2use crate::silk::macros::*;
3use crate::silk::sigproc_fix::*;
4use crate::silk::structs::*;
5use crate::silk::tables::*;
6
7#[derive(Copy, Clone)]
8pub struct NSQDelDecStruct {
9    pub s_lpc_q14: [i32; MAX_SUB_FRAME_LENGTH + NSQ_LPC_BUF_LENGTH],
10    pub rand_state: [i32; DECISION_DELAY],
11    pub q_q10: [i32; DECISION_DELAY],
12    pub xq_q14: [i32; DECISION_DELAY],
13    pub pred_q15: [i32; DECISION_DELAY],
14    pub shape_q14: [i32; DECISION_DELAY],
15    pub s_ar2_q14: [i32; MAX_SHAPE_LPC_ORDER],
16    pub lf_ar_q14: i32,
17    pub diff_q14: i32,
18    pub seed: i32,
19    pub seed_init: i32,
20    pub rd_q10: i32,
21}
22
23impl Default for NSQDelDecStruct {
24    fn default() -> Self {
25        Self {
26            s_lpc_q14: [0; MAX_SUB_FRAME_LENGTH + NSQ_LPC_BUF_LENGTH],
27            rand_state: [0; DECISION_DELAY],
28            q_q10: [0; DECISION_DELAY],
29            xq_q14: [0; DECISION_DELAY],
30            pred_q15: [0; DECISION_DELAY],
31            shape_q14: [0; DECISION_DELAY],
32            s_ar2_q14: [0; MAX_SHAPE_LPC_ORDER],
33            lf_ar_q14: 0,
34            diff_q14: 0,
35            seed: 0,
36            seed_init: 0,
37            rd_q10: 0,
38        }
39    }
40}
41
42#[derive(Copy, Clone, Default)]
43pub struct NSQSampleStruct {
44    pub q_q10: i32,
45    pub rd_q10: i32,
46    pub xq_q14: i32,
47    pub lf_ar_q14: i32,
48    pub diff_q14: i32,
49    pub s_ltp_shp_q14: i32,
50    pub lpc_exc_q14: i32,
51}
52
53pub type NSQSamplePair = [NSQSampleStruct; 2];
54
55#[inline]
56fn silk_nsq_del_dec_scale_states(
57    ps_enc_c: &SilkEncoderStateCommon,
58    nsq: &mut SilkNSQState,
59    ps_del_dec: &mut [NSQDelDecStruct],
60    x16: &[i16],
61    x_sc_q10: &mut [i32],
62    s_ltp: &[i16],
63    s_ltp_q15: &mut [i32],
64    subfr: usize,
65    n_states_delayed_decision: i32,
66    ltp_scale_q14: i32,
67    gains_q16: &[i32],
68    pitch_l: &[i32],
69    signal_type: i32,
70    decision_delay: i32,
71) {
72    let lag = pitch_l[subfr] as usize;
73    let inv_gain_q31 = silk_inverse32_varq(gains_q16[subfr].max(1), 47);
74
75    let inv_gain_q26 = silk_rshift_round(inv_gain_q31, 5);
76    let n = ps_enc_c.subfr_length as usize;
77    for (x_out, &x_in) in x_sc_q10[..n].iter_mut().zip(x16[..n].iter()) {
78        *x_out = silk_smulww(x_in as i32, inv_gain_q26);
79    }
80
81    if nsq.rewhite_flag != 0 {
82        let mut inv_gain_q31_scaled = inv_gain_q31;
83        if subfr == 0 {
84            inv_gain_q31_scaled = silk_lshift(silk_smulwb(inv_gain_q31, ltp_scale_q14), 2);
85        }
86        for i in (nsq.s_ltp_buf_idx as usize - lag - LTP_ORDER / 2)..(nsq.s_ltp_buf_idx as usize) {
87            s_ltp_q15[i] = silk_smulwb(inv_gain_q31_scaled, s_ltp[i] as i32);
88        }
89    }
90
91    if gains_q16[subfr] != nsq.prev_gain_q16 {
92        let gain_adj_q16 = silk_div32_varq(nsq.prev_gain_q16, gains_q16[subfr], 16);
93
94        for i in (nsq.s_ltp_shp_buf_idx as usize - ps_enc_c.ltp_mem_length as usize)
95            ..(nsq.s_ltp_shp_buf_idx as usize)
96        {
97            nsq.s_ltp_shp_q14[i] = silk_smulww(gain_adj_q16, nsq.s_ltp_shp_q14[i]);
98        }
99
100        if signal_type == TYPE_VOICED && nsq.rewhite_flag == 0 {
101            let ltp_start = nsq.s_ltp_buf_idx as usize - lag - LTP_ORDER / 2;
102            let ltp_end = nsq.s_ltp_buf_idx as usize - decision_delay as usize;
103            for v in s_ltp_q15[ltp_start..ltp_end].iter_mut() {
104                *v = silk_smulww(gain_adj_q16, *v);
105            }
106        }
107
108        let n_states = (n_states_delayed_decision as usize).min(NSQ_MAX_STATES_OPERATING);
109        for k in 0..n_states {
110            let ps_dd = &mut ps_del_dec[k];
111            ps_dd.lf_ar_q14 = silk_smulww(gain_adj_q16, ps_dd.lf_ar_q14);
112            ps_dd.diff_q14 = silk_smulww(gain_adj_q16, ps_dd.diff_q14);
113            for i in 0..NSQ_LPC_BUF_LENGTH {
114                ps_dd.s_lpc_q14[i] = silk_smulww(gain_adj_q16, ps_dd.s_lpc_q14[i]);
115            }
116            for i in 0..MAX_SHAPE_LPC_ORDER {
117                ps_dd.s_ar2_q14[i] = silk_smulww(gain_adj_q16, ps_dd.s_ar2_q14[i]);
118            }
119            for i in 0..DECISION_DELAY {
120                ps_dd.pred_q15[i] = silk_smulww(gain_adj_q16, ps_dd.pred_q15[i]);
121                ps_dd.shape_q14[i] = silk_smulww(gain_adj_q16, ps_dd.shape_q14[i]);
122            }
123        }
124        nsq.prev_gain_q16 = gains_q16[subfr];
125    }
126}
127
128#[cfg(target_arch = "aarch64")]
129#[inline(always)]
130#[allow(unsafe_op_in_unsafe_fn)]
131unsafe fn silk_lpc_prediction_neon(
132    ps_lpc_q14: &[i32],
133    idx: usize,
134    a_q12: &[i16],
135    predict_lpc_order: i32,
136) -> i32 {
137    use std::arch::aarch64::*;
138
139    let order = predict_lpc_order as usize;
140    let lpc_base = ps_lpc_q14.as_ptr().add(idx);
141    let a_ptr = a_q12.as_ptr();
142
143    let mut acc = vdupq_n_s32(0i32);
144    let neon_taps = order & !3;
145
146    let mut j = 0usize;
147    while j < neon_taps {
148        let lpc_asc = vld1q_s32(lpc_base.sub(j + 3));
149
150        let coef_narrow = vld1_s16(a_ptr.add(j));
151        let coef_wide = vmovl_s16(coef_narrow);
152
153        let coef_rev = vrev64q_s32(vcombine_s32(
154            vget_high_s32(coef_wide),
155            vget_low_s32(coef_wide),
156        ));
157
158        let prod_lo = vmull_s32(vget_low_s32(lpc_asc), vget_low_s32(coef_rev));
159        let prod_hi = vmull_s32(vget_high_s32(lpc_asc), vget_high_s32(coef_rev));
160
161        let shr_lo = vshrn_n_s64::<16>(prod_lo); // int32x2
162        let shr_hi = vshrn_n_s64::<16>(prod_hi); // int32x2
163
164        acc = vaddq_s32(acc, vcombine_s32(shr_lo, shr_hi));
165        j += 4;
166    }
167
168    let mut out = silk_rshift(predict_lpc_order, 1) + vaddvq_s32(acc);
169
170    while j < order {
171        out = silk_smlawb(out, ps_lpc_q14[idx - j], a_q12[j] as i32);
172        j += 1;
173    }
174
175    out
176}
177
178#[inline]
179/// AVX2 twin of the 16/10-tap LPC short-prediction dot product (S1c).
180///
181/// Byte-identical to the scalar: each per-tap product is
182/// `(lpc[idx-j] as i64 * a[j] as i64) >> 16` narrowed to i32, and i32
183/// wrapping-addition is associative so the lane reduction matches the sequential
184/// sum exactly. AVX2 has no signed 64-bit shift, so `>>16` is emulated
185/// (logical shift + sign fill). Processes 8 taps/iteration; scalar tail.
186///
187/// SAFETY: caller guarantees `idx >= predict_lpc_order - 1` (SILK frame sizing,
188/// same precondition the scalar path relies on) so the 8 loads at
189/// `lpc[idx-j-7 ..= idx-j]` are in bounds; AVX2 availability is checked by the
190/// caller via `is_x86_feature_detected!`.
191#[cfg(target_arch = "x86_64")]
192#[target_feature(enable = "avx2")]
193unsafe fn silk_lpc_prediction_avx2(
194    ps_lpc_q14: &[i32],
195    idx: usize,
196    a_q12: &[i16],
197    predict_lpc_order: i32,
198) -> i32 {
199    use core::arch::x86_64::*;
200
201    let order = predict_lpc_order as usize;
202    let lpc = ps_lpc_q14.as_ptr();
203    // Each per-tap product `(lpc*a)>>16` fits in i32 (|lpc|<2^31, |a|<2^15 →
204    // |product|<2^46, >>16 <2^31), so accumulating the products as i64 lanes and
205    // truncating to i32 once at the end equals the scalar's i32 wrapping sum.
206    let mut acc_e = _mm256_setzero_si256();
207    let mut acc_o = _mm256_setzero_si256();
208    // Arithmetic >>16 of packed i64 (0<16<32): logical shift + sign fill.
209    let asr16 = |x: __m256i| -> __m256i {
210        let sign = _mm256_cmpgt_epi64(_mm256_setzero_si256(), x); // -1 where x<0
211        let fill = _mm256_slli_epi64(sign, 64 - 16);
212        _mm256_or_si256(_mm256_srli_epi64(x, 16), fill)
213    };
214
215    let mut j = 0usize;
216    let main = order & !7; // multiple of 8
217    while j < main {
218        // L[k] = lpc[idx-j-7+k] (k=0..7); product_t = lpc[idx-j-t]*a[j+t]
219        //      = L[7-t]*C[t]. Order of the sum is irrelevant (associative), so
220        //      pair L[k] with a_q12[j+7-k] by loading coefs reversed.
221        let l = _mm256_loadu_si256(lpc.add(idx - j - 7) as *const __m256i);
222        let c16 = _mm_loadu_si128(a_q12.as_ptr().add(j) as *const __m128i);
223        let c = _mm256_cvtepi16_epi32(c16); // [a[j]..a[j+7]] i32
224        let crev = _mm256_permutevar8x32_epi32(c, _mm256_setr_epi32(7, 6, 5, 4, 3, 2, 1, 0));
225
226        // Even 32-bit lanes -> 4 i64 products; odd lanes via a dword shuffle.
227        let pe = _mm256_mul_epi32(l, crev);
228        let lo = _mm256_shuffle_epi32(l, 0b11_11_01_01);
229        let co = _mm256_shuffle_epi32(crev, 0b11_11_01_01);
230        let po = _mm256_mul_epi32(lo, co);
231
232        acc_e = _mm256_add_epi64(acc_e, asr16(pe));
233        acc_o = _mm256_add_epi64(acc_o, asr16(po));
234        j += 8;
235    }
236
237    // Sum the 8 i64 lanes; only the low 32 bits matter (== i32 wrapping sum).
238    let s = _mm256_add_epi64(acc_e, acc_o); // 4 i64
239    let s2 = _mm_add_epi64(
240        _mm256_castsi256_si128(s),
241        _mm256_extracti128_si256(s, 1),
242    ); // 2 i64
243    let s3 = _mm_add_epi64(s2, _mm_unpackhi_epi64(s2, s2)); // 1 i64 in low lane
244    let mut out = silk_rshift(predict_lpc_order, 1).wrapping_add(_mm_cvtsi128_si32(s3));
245
246    while j < order {
247        out = silk_smlawb(out, ps_lpc_q14[idx - j], a_q12[j] as i32);
248        j += 1;
249    }
250    out
251}
252
253/// Cached AVX2 dispatch decision: `is_x86_feature_detected!("avx2")` unless the
254/// `RUSTY_OPUS_NO_AVX2` env var is set (for interleaved A/B against the scalar
255/// twin). Cached so the hot path pays no per-call feature-detect/env cost.
256#[cfg(target_arch = "x86_64")]
257#[inline(always)]
258fn lpc_avx2_enabled() -> bool {
259    use std::sync::atomic::{AtomicU8, Ordering};
260    static STATE: AtomicU8 = AtomicU8::new(0); // 0=unknown, 1=on, 2=off
261    match STATE.load(Ordering::Relaxed) {
262        1 => true,
263        2 => false,
264        _ => {
265            let on = is_x86_feature_detected!("avx2")
266                && std::env::var_os("RUSTY_OPUS_NO_AVX2").is_none();
267            STATE.store(if on { 1 } else { 2 }, Ordering::Relaxed);
268            on
269        }
270    }
271}
272
273#[inline(always)]
274fn silk_lpc_prediction_scalar(
275    ps_lpc_q14: &[i32],
276    idx: usize,
277    a_q12: &[i16],
278    predict_lpc_order: i32,
279) -> i32 {
280    let mut out = silk_rshift(predict_lpc_order, 1);
281    for j in 0..predict_lpc_order as usize {
282        out = silk_smlawb(out, ps_lpc_q14[idx - j], a_q12[j] as i32);
283    }
284    out
285}
286
287fn silk_noise_shape_quantizer_short_prediction(
288    ps_lpc_q14: &[i32],
289    idx: usize,
290    a_q12: &[i16],
291    predict_lpc_order: i32,
292) -> i32 {
293    #[cfg(target_arch = "aarch64")]
294    // SAFETY: aarch64 always has NEON; bounds are guaranteed by SILK frame sizing.
295    unsafe {
296        return silk_lpc_prediction_neon(ps_lpc_q14, idx, a_q12, predict_lpc_order);
297    }
298    #[cfg(target_arch = "x86_64")]
299    {
300        // Runtime-dispatched; scalar twin stays the oracle/fallback. The AVX2 path
301        // is cached once (feature-detect + `RUSTY_OPUS_NO_AVX2` A/B override).
302        if lpc_avx2_enabled() && idx + 1 >= predict_lpc_order as usize {
303            // SAFETY: avx2 verified at runtime; idx precondition guarantees the loads.
304            return unsafe {
305                silk_lpc_prediction_avx2(ps_lpc_q14, idx, a_q12, predict_lpc_order)
306            };
307        }
308    }
309    #[allow(unreachable_code)]
310    silk_lpc_prediction_scalar(ps_lpc_q14, idx, a_q12, predict_lpc_order)
311}
312
313/// Cached AVX2 dispatch for the cross-state NSQ shaping filter (S1d/Path 2); own
314/// `RUSTY_OPUS_NO_NSQ_AVX2` knob for isolated A/B against the scalar twin.
315#[cfg(target_arch = "x86_64")]
316#[inline(always)]
317fn nsq_shape_avx2_enabled() -> bool {
318    use std::sync::atomic::{AtomicU8, Ordering};
319    static STATE: AtomicU8 = AtomicU8::new(0);
320    match STATE.load(Ordering::Relaxed) {
321        1 => true,
322        2 => false,
323        _ => {
324            let on = is_x86_feature_detected!("avx2")
325                && std::env::var_os("RUSTY_OPUS_NO_AVX2").is_none()
326                && std::env::var_os("RUSTY_OPUS_NO_NSQ_AVX2").is_none();
327            STATE.store(if on { 1 } else { 2 }, Ordering::Relaxed);
328            on
329        }
330    }
331}
332
333/// Cross-state warped shaping AR filter over a state-minor SoA buffer
334/// `sar[tap][state]` (the 4 del-dec states are the lanes). Serial in the tap
335/// dimension; scalar twin = the oracle for the AVX2 version below.
336#[inline(always)]
337fn nsq_shape_filter_soa_scalar(
338    sar: &mut [[i32; 4]],
339    diff: &[i32; 4],
340    warp: i32,
341    ar_shp_q13: &[i16],
342    order: usize,
343    base: i32,
344    n_ar: &mut [i32; 4],
345) {
346    for k in 0..4 {
347        let mut tmp2 = silk_smlawb(diff[k], sar[0][k], warp);
348        let mut tmp1 = silk_smlawb(sar[0][k], silk_sub32_ovflw(sar[1][k], tmp2), warp);
349        sar[0][k] = tmp2;
350        let mut acc = silk_smlawb(base, tmp2, ar_shp_q13[0] as i32);
351        let mut j = 2;
352        while j < order {
353            tmp2 = silk_smlawb(sar[j - 1][k], silk_sub32_ovflw(sar[j][k], tmp1), warp);
354            sar[j - 1][k] = tmp1;
355            acc = silk_smlawb(acc, tmp1, ar_shp_q13[j - 1] as i32);
356            tmp1 = silk_smlawb(sar[j][k], silk_sub32_ovflw(sar[j + 1][k], tmp2), warp);
357            sar[j][k] = tmp2;
358            acc = silk_smlawb(acc, tmp2, ar_shp_q13[j] as i32);
359            j += 2;
360        }
361        sar[order - 1][k] = tmp1;
362        n_ar[k] = silk_smlawb(acc, tmp1, ar_shp_q13[order - 1] as i32);
363    }
364}
365
366/// Hand-AVX2 twin: the 4 states run as 4 **i64 lanes** of a `__m256i` throughout
367/// the recurrence (values stay i32-range) — the key over the reverted S1d attempt
368/// is that nothing narrows/permutes per op; the narrow-to-i32 happens once, on
369/// store. Byte-identical to the scalar twin (`silk_sub32_ovflw` never wraps here:
370/// the shaping states are bounded Q14 values, verified by the oracle + unit test).
371/// Micro-benchmarked at ~1.56× the 4-chain scalar.
372///
373/// SAFETY: AVX2 verified by the caller; `sar`/`n_ar` sized ≥ order/4.
374#[cfg(target_arch = "x86_64")]
375#[target_feature(enable = "avx2")]
376unsafe fn nsq_shape_filter_soa_avx2(
377    sar: &mut [[i32; 4]],
378    diff: &[i32; 4],
379    warp: i32,
380    ar_shp_q13: &[i16],
381    order: usize,
382    base: i32,
383    n_ar: &mut [i32; 4],
384) {
385    use core::arch::x86_64::*;
386    let wb = _mm256_set1_epi64x(warp as i64);
387    let asr16 = |x: __m256i| {
388        let s = _mm256_cmpgt_epi64(_mm256_setzero_si256(), x);
389        _mm256_or_si256(_mm256_srli_epi64(x, 16), _mm256_slli_epi64(s, 48))
390    };
391    let smlawb_v =
392        |a: __m256i, b: __m256i, cb: __m256i| _mm256_add_epi64(a, asr16(_mm256_mul_epi32(b, cb)));
393    let ldv = |p: &[i32; 4]| _mm256_cvtepi32_epi64(_mm_loadu_si128(p.as_ptr() as *const __m128i));
394    let stv = |p: &mut [i32; 4], v: __m256i| {
395        let g = _mm256_permutevar8x32_epi32(v, _mm256_setr_epi32(0, 2, 4, 6, 0, 2, 4, 6));
396        _mm_storeu_si128(p.as_mut_ptr() as *mut __m128i, _mm256_castsi256_si128(g));
397    };
398    let cbv = |c: i32| _mm256_set1_epi64x(c as i16 as i64);
399
400    // Load the state column-vectors once (i64 lanes); operate in-register.
401    let mut vsar = [_mm256_setzero_si256(); MAX_SHAPE_LPC_ORDER];
402    for j in 0..order {
403        vsar[j] = ldv(&sar[j]);
404    }
405    let vdiff = ldv(diff);
406    let mut tmp2 = smlawb_v(vdiff, vsar[0], wb);
407    let mut tmp1 = smlawb_v(vsar[0], _mm256_sub_epi64(vsar[1], tmp2), wb);
408    vsar[0] = tmp2;
409    let mut acc = smlawb_v(_mm256_set1_epi64x(base as i64), tmp2, cbv(ar_shp_q13[0] as i32));
410    let mut j = 2;
411    while j < order {
412        tmp2 = smlawb_v(vsar[j - 1], _mm256_sub_epi64(vsar[j], tmp1), wb);
413        vsar[j - 1] = tmp1;
414        acc = smlawb_v(acc, tmp1, cbv(ar_shp_q13[j - 1] as i32));
415        tmp1 = smlawb_v(vsar[j], _mm256_sub_epi64(vsar[j + 1], tmp2), wb);
416        vsar[j] = tmp2;
417        acc = smlawb_v(acc, tmp2, cbv(ar_shp_q13[j] as i32));
418        j += 2;
419    }
420    vsar[order - 1] = tmp1;
421    acc = smlawb_v(acc, tmp1, cbv(ar_shp_q13[order - 1] as i32));
422    for j in 0..order {
423        stv(&mut sar[j], vsar[j]);
424    }
425    stv(n_ar, acc);
426}
427
428#[inline(always)]
429fn nsq_shape_filter_soa(
430    sar: &mut [[i32; 4]],
431    diff: &[i32; 4],
432    warp: i32,
433    ar_shp_q13: &[i16],
434    order: usize,
435    base: i32,
436    n_ar: &mut [i32; 4],
437) {
438    #[cfg(target_arch = "x86_64")]
439    {
440        if nsq_shape_avx2_enabled() {
441            // SAFETY: avx2 checked; sar/n_ar sized ≥ order/4.
442            unsafe { nsq_shape_filter_soa_avx2(sar, diff, warp, ar_shp_q13, order, base, n_ar) };
443            return;
444        }
445    }
446    nsq_shape_filter_soa_scalar(sar, diff, warp, ar_shp_q13, order, base, n_ar);
447}
448
449#[cfg(all(test, target_arch = "x86_64"))]
450mod lpc_pred_avx2_tests {
451    use super::*;
452
453    /// AVX2 twin must be BYTE-IDENTICAL to the scalar oracle over random inputs
454    /// at every valid LPC order.
455    #[test]
456    fn avx2_matches_scalar() {
457        if !is_x86_feature_detected!("avx2") {
458            return;
459        }
460        // xorshift for deterministic pseudo-random coverage.
461        let mut s: u64 = 0x1234_5678_9abc_def1;
462        let mut rng = || {
463            s ^= s << 13;
464            s ^= s >> 7;
465            s ^= s << 17;
466            s
467        };
468        for order in [10usize, 12, 14, 16] {
469            for _ in 0..50_000 {
470                // buffer big enough for idx and the order-1 look-back
471                let mut lpc = [0i32; 64];
472                for v in lpc.iter_mut() {
473                    // Q14-ish magnitudes, full sign range.
474                    *v = (rng() as i32) >> (rng() as u32 % 12);
475                }
476                let mut a = [0i16; 16];
477                for v in a.iter_mut().take(order) {
478                    *v = (rng() as i16) >> (rng() as u32 % 3);
479                }
480                let idx = 32 + (rng() as usize % 16);
481                let got = unsafe {
482                    silk_lpc_prediction_avx2(&lpc, idx, &a, order as i32)
483                };
484                let want = silk_lpc_prediction_scalar(&lpc, idx, &a, order as i32);
485                assert_eq!(got, want, "order={order} idx={idx}");
486            }
487        }
488    }
489
490    /// Cross-state shaping filter (Path 2): the i64-lane AVX2 must match the
491    /// scalar SoA twin over random states, incl. large magnitudes (stresses the
492    /// `silk_sub32_ovflw` i32-wrap assumption — the shaping states are bounded in
493    /// practice, but this covers well beyond the realistic range).
494    #[test]
495    fn nsq_shape_filter_avx2_matches_scalar() {
496        if !is_x86_feature_detected!("avx2") {
497            return;
498        }
499        let mut s: u64 = 0xC0FF_EE00_1234_5678;
500        let mut rng = || {
501            s ^= s << 13;
502            s ^= s >> 7;
503            s ^= s << 17;
504            s
505        };
506        for order in [16usize, 24] {
507            for _ in 0..20_000 {
508                let sh = 6 + (rng() % 20) as u32;
509                let mut sar_a = vec![[0i32; 4]; order];
510                for row in sar_a.iter_mut() {
511                    for v in row.iter_mut() {
512                        *v = (rng() as i32) >> sh;
513                    }
514                }
515                let mut sar_b = sar_a.clone();
516                let diff = [
517                    (rng() as i32) >> sh,
518                    (rng() as i32) >> sh,
519                    (rng() as i32) >> sh,
520                    (rng() as i32) >> sh,
521                ];
522                let ar: Vec<i16> = (0..order).map(|_| (rng() >> 8) as i16).collect();
523                let warp = 13421;
524                let base = (order as i32) >> 1;
525                let mut na = [0i32; 4];
526                let mut nb = [0i32; 4];
527                nsq_shape_filter_soa_scalar(&mut sar_a, &diff, warp, &ar, order, base, &mut na);
528                unsafe {
529                    nsq_shape_filter_soa_avx2(&mut sar_b, &diff, warp, &ar, order, base, &mut nb)
530                };
531                assert_eq!(na, nb, "n_ar mismatch order={order} sh={sh}");
532                assert_eq!(sar_a, sar_b, "sar mismatch order={order} sh={sh}");
533            }
534        }
535    }
536}
537
538pub fn silk_noise_shape_quantizer_del_dec(
539    nsq: &mut SilkNSQState,
540    ps_del_dec: &mut [NSQDelDecStruct],
541    signal_type: i32,
542    x_q10: &[i32],
543    pulses: &mut [i8],
544    pulses_offset: i32,
545    xq_ptr: i32,
546    s_ltp_q15: &mut [i32],
547    delayed_ga_q10: &mut [i32],
548    a_q12: &[i16],
549    b_q14: &[i16],
550    ar_shp_q13: &[i16],
551    lag: i32,
552    harm_shape_fir_packed_q14: i32,
553    tilt_q14: i32,
554    lf_shp_q14: i32,
555    gain_q16: i32,
556    lambda_q10: i32,
557    offset_q10: i32,
558    length: i32,
559    subfr: i32,
560    shaping_lpc_order: i32,
561    predict_lpc_order: i32,
562    warping_q16: i32,
563    n_states_delayed_decision: i32,
564    smpl_buf_idx: &mut i32,
565    decision_delay: i32,
566    _frame_counter: i32,
567) {
568    let mut ps_sample_state = [[NSQSampleStruct::default(); 2]; NSQ_MAX_STATES_OPERATING];
569    let gain_q10 = silk_rshift(gain_q16, 6);
570
571    let n_states = (n_states_delayed_decision as usize).min(NSQ_MAX_STATES_OPERATING);
572
573    let pred_lag_ptr_base = nsq.s_ltp_buf_idx - lag + LTP_ORDER as i32 / 2;
574    let shp_lag_ptr_base = nsq.s_ltp_shp_buf_idx - lag + HARM_SHAPE_FIR_TAPS as i32 / 2;
575
576    // Persistent cross-state SoA for the warped-shaping state `s_ar2` (Path 2):
577    // transposed AoS→SoA once here and back at the end (amortised over `length`
578    // samples), so the per-sample shaping filter runs cross-state (4 states = 4
579    // lanes) with no per-sample transpose. Within the sample loop, `s_ar2` lives
580    // ONLY in `sar_soa` (the shaping filter + the RD state-swap update it there).
581    let shp_ord = shaping_lpc_order as usize;
582    let shp_base = silk_rshift(shaping_lpc_order, 1);
583    let mut sar_soa = [[0i32; 4]; MAX_SHAPE_LPC_ORDER];
584    for k in 0..n_states {
585        for j in 0..shp_ord {
586            sar_soa[j][k] = ps_del_dec[k].s_ar2_q14[j];
587        }
588    }
589
590    for i in 0..length {
591        let idx = i as usize;
592        let mut ltp_pred_q14 = 0;
593        if signal_type == TYPE_VOICED {
594            let pred_lag_idx_calc = pred_lag_ptr_base + i;
595            if pred_lag_idx_calc >= LTP_ORDER as i32 && pred_lag_idx_calc < s_ltp_q15.len() as i32 {
596                let pred_lag_idx = pred_lag_idx_calc as usize;
597                ltp_pred_q14 = 2;
598                ltp_pred_q14 = silk_smlawb(ltp_pred_q14, s_ltp_q15[pred_lag_idx], b_q14[0] as i32);
599                ltp_pred_q14 =
600                    silk_smlawb(ltp_pred_q14, s_ltp_q15[pred_lag_idx - 1], b_q14[1] as i32);
601                ltp_pred_q14 =
602                    silk_smlawb(ltp_pred_q14, s_ltp_q15[pred_lag_idx - 2], b_q14[2] as i32);
603                ltp_pred_q14 =
604                    silk_smlawb(ltp_pred_q14, s_ltp_q15[pred_lag_idx - 3], b_q14[3] as i32);
605                ltp_pred_q14 =
606                    silk_smlawb(ltp_pred_q14, s_ltp_q15[pred_lag_idx - 4], b_q14[4] as i32);
607                ltp_pred_q14 = silk_lshift(ltp_pred_q14, 1);
608            }
609        }
610
611        let mut n_ltp_q14 = 0;
612        if lag > 0 {
613            let shp_lag_idx_calc = shp_lag_ptr_base + i;
614            if shp_lag_idx_calc >= HARM_SHAPE_FIR_TAPS as i32
615                && shp_lag_idx_calc < nsq.s_ltp_shp_q14.len() as i32
616            {
617                let shp_lag_idx = shp_lag_idx_calc as usize;
618                n_ltp_q14 = silk_smulwb(
619                    silk_add_sat32(
620                        nsq.s_ltp_shp_q14[shp_lag_idx],
621                        nsq.s_ltp_shp_q14[shp_lag_idx - 2],
622                    ),
623                    harm_shape_fir_packed_q14,
624                );
625                n_ltp_q14 = silk_smlawt(
626                    n_ltp_q14,
627                    nsq.s_ltp_shp_q14[shp_lag_idx - 1],
628                    harm_shape_fir_packed_q14,
629                );
630                n_ltp_q14 = silk_sub_lshift32(ltp_pred_q14, n_ltp_q14, 2);
631            }
632        }
633
634        // Shaping pre-pass (Path 2): seed + LPC prediction (per state), then the
635        // warped shaping AR filter run CROSS-STATE over `sar_soa` (4 states = 4
636        // i64 lanes). Byte-identical to the per-state scalar; ~1.56× the kernel.
637        // The states are independent within a sample, so this hoist is exact.
638        let mut lpc_pred_arr = [0i32; NSQ_MAX_STATES_OPERATING];
639        let mut n_ar_arr = [0i32; NSQ_MAX_STATES_OPERATING];
640        let mut n_lf_arr = [0i32; NSQ_MAX_STATES_OPERATING];
641        let mut diff_arr = [0i32; 4];
642        for k in 0..n_states {
643            let ps_dd = &mut ps_del_dec[k];
644            ps_dd.seed = silk_rand(ps_dd.seed);
645            let ps_lpc_q14_idx = NSQ_LPC_BUF_LENGTH - 1 + idx;
646            lpc_pred_arr[k] = silk_lshift(
647                silk_noise_shape_quantizer_short_prediction(
648                    &ps_dd.s_lpc_q14,
649                    ps_lpc_q14_idx,
650                    a_q12,
651                    predict_lpc_order,
652                ),
653                4,
654            );
655            diff_arr[k] = ps_dd.diff_q14;
656        }
657        let mut n_ar_raw = [0i32; 4];
658        nsq_shape_filter_soa(
659            &mut sar_soa,
660            &diff_arr,
661            warping_q16,
662            ar_shp_q13,
663            shp_ord,
664            shp_base,
665            &mut n_ar_raw,
666        );
667        let smpl_idx = (*smpl_buf_idx as usize).min(DECISION_DELAY - 1);
668        for k in 0..n_states {
669            let ps_dd = &ps_del_dec[k];
670            let mut n_ar_q14 = silk_lshift(n_ar_raw[k], 1);
671            n_ar_q14 = silk_smlawb(n_ar_q14, ps_dd.lf_ar_q14, tilt_q14);
672            n_ar_q14 = silk_lshift(n_ar_q14, 2);
673            n_ar_arr[k] = n_ar_q14;
674            let mut n_lf_q14 = silk_smulwb(ps_dd.shape_q14[smpl_idx], lf_shp_q14);
675            n_lf_q14 = silk_smlawt(n_lf_q14, ps_dd.lf_ar_q14, lf_shp_q14);
676            n_lf_q14 = silk_lshift(n_lf_q14, 2);
677            n_lf_arr[k] = n_lf_q14;
678        }
679
680        // RD decision pass (per state; branchy — scalar).
681        for k in 0..n_states {
682            let ps_dd = &mut ps_del_dec[k];
683            let ps_ss = &mut ps_sample_state[k];
684            let lpc_pred_q14 = lpc_pred_arr[k];
685            let n_ar_q14 = n_ar_arr[k];
686            let n_lf_q14 = n_lf_arr[k];
687
688            let tmp1_val = silk_sub_sat32(
689                silk_add32_ovflw(n_ltp_q14, lpc_pred_q14),
690                silk_add_sat32(n_ar_q14, n_lf_q14),
691            );
692            let r_q10 = x_q10[idx] - silk_rshift_round(tmp1_val, 4);
693
694            let r_q10_signed = if ps_dd.seed < 0 { -r_q10 } else { r_q10 };
695            let r_q10_signed = silk_limit_32(r_q10_signed, -(31 << 10), 30 << 10);
696
697            let q1_q10_in = r_q10_signed - offset_q10;
698            let mut q1_q0 = silk_rshift(q1_q10_in, 10);
699            if lambda_q10 > 2048 {
700                let rdo_offset = lambda_q10 / 2 - 512;
701                if q1_q10_in > rdo_offset {
702                    q1_q0 = silk_rshift(q1_q10_in - rdo_offset, 10);
703                } else if q1_q10_in < -rdo_offset {
704                    q1_q0 = silk_rshift(q1_q10_in + rdo_offset, 10);
705                } else if q1_q10_in < 0 {
706                    q1_q0 = -1;
707                } else {
708                    q1_q0 = 0;
709                }
710            }
711
712            let (rd1_q10, rd2_q10, q1_q10_val, q2_q10_val);
713            if q1_q0 > 0 {
714                q1_q10_val =
715                    silk_sub32(silk_lshift(q1_q0, 10), QUANT_LEVEL_ADJUST_Q10) + offset_q10;
716                q2_q10_val = q1_q10_val + 1024;
717                rd1_q10 = silk_smulbb(q1_q10_val, lambda_q10);
718                rd2_q10 = silk_smulbb(q2_q10_val, lambda_q10);
719            } else if q1_q0 == 0 {
720                q1_q10_val = offset_q10;
721                q2_q10_val = q1_q10_val + 1024 - QUANT_LEVEL_ADJUST_Q10;
722                rd1_q10 = silk_smulbb(q1_q10_val, lambda_q10);
723                rd2_q10 = silk_smulbb(q2_q10_val, lambda_q10);
724            } else if q1_q0 == -1 {
725                q2_q10_val = offset_q10;
726                q1_q10_val = q2_q10_val - (1024 - QUANT_LEVEL_ADJUST_Q10);
727                rd1_q10 = silk_smulbb(-q1_q10_val, lambda_q10);
728                rd2_q10 = silk_smulbb(q2_q10_val, lambda_q10);
729            } else {
730                q1_q10_val =
731                    silk_add32(silk_lshift(q1_q0, 10), QUANT_LEVEL_ADJUST_Q10) + offset_q10;
732                q2_q10_val = q1_q10_val + 1024;
733                rd1_q10 = silk_smulbb(-q1_q10_val, lambda_q10);
734                rd2_q10 = silk_smulbb(-q2_q10_val, lambda_q10);
735            }
736
737            let mut rr_q10 = r_q10_signed - q1_q10_val;
738            let rd1_q10_final = silk_rshift(silk_smlabb(rd1_q10, rr_q10, rr_q10), 10);
739            rr_q10 = r_q10_signed - q2_q10_val;
740            let rd2_q10_final = silk_rshift(silk_smlabb(rd2_q10, rr_q10, rr_q10), 10);
741
742            if rd1_q10_final < rd2_q10_final {
743                ps_ss[0].rd_q10 = ps_dd.rd_q10 + rd1_q10_final;
744                ps_ss[1].rd_q10 = ps_dd.rd_q10 + rd2_q10_final;
745                ps_ss[0].q_q10 = q1_q10_val;
746                ps_ss[1].q_q10 = q2_q10_val;
747            } else {
748                ps_ss[0].rd_q10 = ps_dd.rd_q10 + rd2_q10_final;
749                ps_ss[1].rd_q10 = ps_dd.rd_q10 + rd1_q10_final;
750                ps_ss[0].q_q10 = q2_q10_val;
751                ps_ss[1].q_q10 = q1_q10_val;
752            }
753
754            for j in 0..2 {
755                let mut exc_q14 = silk_lshift(ps_ss[j].q_q10, 4);
756                if ps_dd.seed < 0 {
757                    exc_q14 = -exc_q14;
758                }
759                let lpc_exc_q14 = silk_add32(exc_q14, ltp_pred_q14);
760                let xq_q14 = silk_add32_ovflw(lpc_exc_q14, lpc_pred_q14);
761                ps_ss[j].diff_q14 = silk_sub32_ovflw(xq_q14, silk_lshift(x_q10[idx], 4));
762                let s_lf_ar_shp_q14 = silk_sub32_ovflw(ps_ss[j].diff_q14, n_ar_q14);
763                ps_ss[j].s_ltp_shp_q14 = silk_sub_sat32(s_lf_ar_shp_q14, n_lf_q14);
764                ps_ss[j].lf_ar_q14 = s_lf_ar_shp_q14;
765                ps_ss[j].lpc_exc_q14 = lpc_exc_q14;
766                ps_ss[j].xq_q14 = xq_q14;
767            }
768        }
769
770        *smpl_buf_idx = (*smpl_buf_idx - 1 + DECISION_DELAY as i32) % DECISION_DELAY as i32;
771        let last_smple_idx = ((*smpl_buf_idx + decision_delay) % DECISION_DELAY as i32) as usize;
772
773        let mut winner_ind = 0;
774        let mut rd_min_q10 = ps_sample_state[0][0].rd_q10;
775        for k in 1..n_states {
776            if ps_sample_state[k][0].rd_q10 < rd_min_q10 {
777                rd_min_q10 = ps_sample_state[k][0].rd_q10;
778                winner_ind = k;
779            }
780        }
781
782        let winner_rand_state = ps_del_dec[winner_ind].rand_state[last_smple_idx];
783        for k in 0..n_states {
784            if ps_del_dec[k].rand_state[last_smple_idx] != winner_rand_state {
785                ps_sample_state[k][0].rd_q10 =
786                    ps_sample_state[k][0].rd_q10.saturating_add(i32::MAX >> 4);
787                ps_sample_state[k][1].rd_q10 =
788                    ps_sample_state[k][1].rd_q10.saturating_add(i32::MAX >> 4);
789            }
790        }
791
792        let mut rd_max_q10 = ps_sample_state[0][0].rd_q10;
793        let mut rd_max_ind = 0;
794        let mut rd_min_q10_2 = ps_sample_state[0][1].rd_q10;
795        let mut rd_min_ind = 0;
796        for k in 1..n_states {
797            if ps_sample_state[k][0].rd_q10 > rd_max_q10 {
798                rd_max_q10 = ps_sample_state[k][0].rd_q10;
799                rd_max_ind = k;
800            }
801            if ps_sample_state[k][1].rd_q10 < rd_min_q10_2 {
802                rd_min_q10_2 = ps_sample_state[k][1].rd_q10;
803                rd_min_ind = k;
804            }
805        }
806
807        if rd_min_q10_2 < rd_max_q10 {
808            if rd_min_ind != rd_max_ind {
809                let (min_state, max_state) = if rd_min_ind < rd_max_ind {
810                    let (left, right) = ps_del_dec.split_at_mut(rd_max_ind);
811                    (&left[rd_min_ind], &mut right[0])
812                } else {
813                    let (left, right) = ps_del_dec.split_at_mut(rd_min_ind);
814                    (&right[0], &mut left[rd_max_ind])
815                };
816                max_state.s_lpc_q14[idx..].copy_from_slice(&min_state.s_lpc_q14[idx..]);
817                max_state.rand_state = min_state.rand_state;
818                max_state.q_q10 = min_state.q_q10;
819                max_state.xq_q14 = min_state.xq_q14;
820                max_state.pred_q15 = min_state.pred_q15;
821                max_state.shape_q14 = min_state.shape_q14;
822                max_state.lf_ar_q14 = min_state.lf_ar_q14;
823                max_state.diff_q14 = min_state.diff_q14;
824                max_state.seed = min_state.seed;
825                max_state.seed_init = min_state.seed_init;
826                max_state.rd_q10 = min_state.rd_q10;
827                // s_ar2 lives in the SoA buffer during the loop — swap its column.
828                for j in 0..shp_ord {
829                    sar_soa[j][rd_max_ind] = sar_soa[j][rd_min_ind];
830                }
831            }
832
833            ps_sample_state[rd_max_ind][0] = ps_sample_state[rd_min_ind][1];
834        }
835
836        let ps_dd = &ps_del_dec[winner_ind];
837        if subfr > 0 || i >= decision_delay {
838            let pulse_idx = (pulses_offset + i - decision_delay) as isize;
839            let xq_idx = (xq_ptr + i - decision_delay) as isize;
840            let shp_idx = (nsq.s_ltp_shp_buf_idx - decision_delay) as isize;
841            let ltp_idx = (nsq.s_ltp_buf_idx - decision_delay) as isize;
842
843            if pulse_idx >= 0 && pulse_idx < pulses.len() as isize {
844                pulses[pulse_idx as usize] =
845                    silk_rshift_round(ps_dd.q_q10[last_smple_idx], 10) as i8;
846            }
847            if xq_idx >= 0 && xq_idx < nsq.xq.len() as isize {
848                nsq.xq[xq_idx as usize] = silk_sat16(silk_rshift_round(
849                    silk_smulww(ps_dd.xq_q14[last_smple_idx], delayed_ga_q10[last_smple_idx]),
850                    8,
851                )) as i16;
852            }
853            if shp_idx >= 0 && shp_idx < nsq.s_ltp_shp_q14.len() as isize {
854                nsq.s_ltp_shp_q14[shp_idx as usize] = ps_dd.shape_q14[last_smple_idx];
855            }
856            if ltp_idx >= 0 && ltp_idx < s_ltp_q15.len() as isize {
857                s_ltp_q15[ltp_idx as usize] = ps_dd.pred_q15[last_smple_idx];
858            }
859        }
860        nsq.s_ltp_shp_buf_idx += 1;
861        nsq.s_ltp_buf_idx += 1;
862
863        for k in 0..n_states {
864            let ps_ss = &ps_sample_state[k][0];
865            let ps_dd = &mut ps_del_dec[k];
866            ps_dd.lf_ar_q14 = ps_ss.lf_ar_q14;
867            ps_dd.diff_q14 = ps_ss.diff_q14;
868
869            let lpc_idx = NSQ_LPC_BUF_LENGTH + idx;
870            if lpc_idx < ps_dd.s_lpc_q14.len() {
871                ps_dd.s_lpc_q14[lpc_idx] = ps_ss.xq_q14;
872            }
873            let smpl_idx = (*smpl_buf_idx as usize).min(DECISION_DELAY - 1);
874            ps_dd.xq_q14[smpl_idx] = ps_ss.xq_q14;
875            ps_dd.q_q10[smpl_idx] = ps_ss.q_q10;
876            ps_dd.pred_q15[smpl_idx] = silk_lshift(ps_ss.lpc_exc_q14, 1);
877            ps_dd.shape_q14[smpl_idx] = ps_ss.s_ltp_shp_q14;
878            ps_dd.seed = silk_add32_ovflw(ps_dd.seed, silk_rshift_round(ps_ss.q_q10, 10));
879            ps_dd.rand_state[smpl_idx] = ps_dd.seed;
880            ps_dd.rd_q10 = ps_ss.rd_q10;
881        }
882        let smpl_idx = (*smpl_buf_idx as usize).min(DECISION_DELAY - 1);
883        delayed_ga_q10[smpl_idx] = gain_q10;
884    }
885    // Transpose the cross-state shaping state back to the AoS structs (once), so
886    // the next subframe's scale_states and the final copy see the updated s_ar2.
887    for k in 0..n_states {
888        for j in 0..shp_ord {
889            ps_del_dec[k].s_ar2_q14[j] = sar_soa[j][k];
890        }
891    }
892    for k in 0..n_states {
893        let ps_dd = &mut ps_del_dec[k];
894        let mut tmp = [0i32; NSQ_LPC_BUF_LENGTH];
895        tmp.copy_from_slice(
896            &ps_dd.s_lpc_q14[length as usize..length as usize + NSQ_LPC_BUF_LENGTH],
897        );
898        ps_dd.s_lpc_q14[..NSQ_LPC_BUF_LENGTH].copy_from_slice(&tmp);
899    }
900}
901
902pub fn silk_nsq_del_dec(
903    ps_common: &SilkEncoderStateCommon,
904    ps_nsq: &mut SilkNSQState,
905    ps_indices: &SideInfoIndices,
906
907    x16: &[i16],
908    pulses: &mut [i8],
909    pred_coef_q12: &[i16],
910    ltp_coef_q14: &[i16],
911    ar_q13: &[i16],
912    harm_shape_gain_q14: &[i32],
913    tilt_q14: &[i32],
914    lf_shp_q14: &[i32],
915    gains_q16: &[i32],
916    pitch_l: &[i32],
917    lambda_q10: i32,
918    ltp_scale_q14: i32,
919) -> i32 {
920    let _prof = crate::prof::scope(crate::prof::Stage::SilkNsq);
921    let mut x_sc_q10 = [0i32; MAX_SUB_FRAME_LENGTH];
922    let mut delayed_ga_q10 = [0i32; DECISION_DELAY];
923    let mut s_ltp_q15 = [0i32; LTP_MEM_LENGTH_MS * MAX_FS_KHZ + MAX_FRAME_LENGTH];
924    let mut s_ltp = [0i16; LTP_MEM_LENGTH_MS * MAX_FS_KHZ + MAX_FRAME_LENGTH];
925    let mut ps_del_dec = [NSQDelDecStruct::default(); NSQ_MAX_STATES_OPERATING];
926
927    let mut lag = ps_nsq.lag_prev;
928
929    let n_states = (ps_common.n_states_delayed_decision as usize).min(NSQ_MAX_STATES_OPERATING);
930
931    for k in 0..n_states {
932        let ps_dd = &mut ps_del_dec[k];
933        ps_dd.seed = (k as i32 + ps_indices.seed as i32) & 3;
934        ps_dd.seed_init = ps_dd.seed;
935        ps_dd.rd_q10 = 0;
936        ps_dd.lf_ar_q14 = ps_nsq.s_lf_ar_q14;
937        ps_dd.diff_q14 = ps_nsq.s_diff_shp_q14;
938
939        if ps_common.ltp_mem_length > 0 {
940            ps_dd.shape_q14[0] = ps_nsq.s_ltp_shp_q14[ps_common.ltp_mem_length as usize - 1];
941        }
942        ps_dd.s_lpc_q14[..NSQ_LPC_BUF_LENGTH]
943            .copy_from_slice(&ps_nsq.s_lpc_q14[..NSQ_LPC_BUF_LENGTH]);
944        ps_dd.s_ar2_q14.copy_from_slice(&ps_nsq.s_ar2_q14);
945    }
946
947    let offset_q10 = SILK_QUANT_OFFSETS_Q10[(ps_indices.signal_type >> 1) as usize]
948        [ps_indices.quant_offset_type as usize] as i32;
949    let mut smpl_buf_idx = 0i32;
950    let mut decision_delay = (DECISION_DELAY as i32).min(ps_common.subfr_length);
951
952    if ps_indices.signal_type as i32 == TYPE_VOICED {
953        for k in 0..ps_common.nb_subfr as usize {
954            let pitch_constraint = pitch_l[k] - LTP_ORDER as i32 / 2 - 1;
955            if pitch_constraint > 0 {
956                decision_delay = decision_delay.min(pitch_constraint);
957            }
958        }
959    } else if lag > 0 {
960        let lag_constraint = lag - LTP_ORDER as i32 / 2 - 1;
961        if lag_constraint > 0 {
962            decision_delay = decision_delay.min(lag_constraint);
963        }
964    }
965
966    let lsf_interpolation_flag = if ps_indices.nlsf_interp_coef_q2 == 4 {
967        0
968    } else {
969        1
970    };
971
972    ps_nsq.s_ltp_shp_buf_idx = ps_common.ltp_mem_length;
973    ps_nsq.s_ltp_buf_idx = ps_common.ltp_mem_length;
974
975    let mut x_ptr = 0;
976    let mut pulses_ptr = 0;
977    let mut xq_ptr = ps_common.ltp_mem_length as usize;
978    let mut subfr_nsq = 0;
979
980    for k in 0..ps_common.nb_subfr as usize {
981        let a_q12 =
982            &pred_coef_q12[((k >> 1) | (1 - lsf_interpolation_flag as usize)) * MAX_LPC_ORDER..];
983        let b_q14 = &ltp_coef_q14[k * LTP_ORDER..];
984        let ar_shp_q13 = &ar_q13[k * MAX_SHAPE_LPC_ORDER..];
985
986        let harm_shape_gain = harm_shape_gain_q14[k];
987        let mut harm_shape_fir_packed_q14 = silk_rshift(harm_shape_gain, 2);
988        harm_shape_fir_packed_q14 |= silk_lshift(silk_rshift(harm_shape_gain, 1), 16);
989
990        ps_nsq.rewhite_flag = 0;
991        if ps_indices.signal_type as i32 == TYPE_VOICED {
992            lag = pitch_l[k];
993            if (k & (3 - silk_lshift(lsf_interpolation_flag, 1) as usize)) == 0 {
994                if k == 2 {
995                    let mut rd_min_q10 = ps_del_dec[0].rd_q10;
996                    let mut winner_ind = 0;
997                    for i in 1..n_states {
998                        if ps_del_dec[i].rd_q10 < rd_min_q10 {
999                            rd_min_q10 = ps_del_dec[i].rd_q10;
1000                            winner_ind = i;
1001                        }
1002                    }
1003                    for i in 0..n_states {
1004                        if i != winner_ind {
1005                            ps_del_dec[i].rd_q10 =
1006                                ps_del_dec[i].rd_q10.saturating_add(i32::MAX >> 4);
1007                        }
1008                    }
1009
1010                    let ps_dd = &ps_del_dec[winner_ind];
1011                    let mut last_smple_idx =
1012                        (smpl_buf_idx + decision_delay) % DECISION_DELAY as i32;
1013                    for i in 0..decision_delay {
1014                        last_smple_idx =
1015                            (last_smple_idx - 1 + DECISION_DELAY as i32) % DECISION_DELAY as i32;
1016                        let pulse_idx = (pulses_ptr as i32 + i - decision_delay) as isize;
1017                        let xq_idx = (xq_ptr as i32 + i - decision_delay) as isize;
1018                        let shp_idx = (ps_nsq.s_ltp_shp_buf_idx + i - decision_delay) as isize;
1019                        if pulse_idx >= 0 && xq_idx >= 0 && shp_idx >= 0 {
1020                            pulses[pulse_idx as usize] =
1021                                silk_rshift_round(ps_dd.q_q10[last_smple_idx as usize], 10) as i8;
1022                            ps_nsq.xq[xq_idx as usize] = silk_sat16(silk_rshift_round(
1023                                silk_smulww(ps_dd.xq_q14[last_smple_idx as usize], gains_q16[1]),
1024                                14,
1025                            )) as i16;
1026                            ps_nsq.s_ltp_shp_q14[shp_idx as usize] =
1027                                ps_dd.shape_q14[last_smple_idx as usize];
1028                        }
1029                    }
1030                    subfr_nsq = 0;
1031                }
1032
1033                let start_idx_calc = ps_common.ltp_mem_length
1034                    - lag
1035                    - ps_common.predict_lpc_order
1036                    - LTP_ORDER as i32 / 2;
1037                if start_idx_calc < 0 {
1038                    continue;
1039                }
1040                let start_idx = start_idx_calc as usize;
1041                let xq_start = start_idx + k * ps_common.subfr_length as usize;
1042                let filter_len = ps_common.ltp_mem_length as usize - start_idx;
1043
1044                if start_idx + filter_len > s_ltp.len() || xq_start + filter_len > ps_nsq.xq.len() {
1045                    continue;
1046                }
1047                silk_lpc_analysis_filter(
1048                    &mut s_ltp[start_idx..],
1049                    &ps_nsq.xq[xq_start..],
1050                    a_q12,
1051                    filter_len,
1052                    ps_common.predict_lpc_order as usize,
1053                    0,
1054                );
1055                ps_nsq.s_ltp_buf_idx = ps_common.ltp_mem_length;
1056                ps_nsq.rewhite_flag = 1;
1057            }
1058        }
1059
1060        silk_nsq_del_dec_scale_states(
1061            ps_common,
1062            ps_nsq,
1063            &mut ps_del_dec,
1064            &x16[x_ptr..],
1065            &mut x_sc_q10,
1066            &s_ltp,
1067            &mut s_ltp_q15,
1068            k,
1069            ps_common.n_states_delayed_decision,
1070            ltp_scale_q14,
1071            gains_q16,
1072            pitch_l,
1073            ps_indices.signal_type as i32,
1074            decision_delay,
1075        );
1076
1077        silk_noise_shape_quantizer_del_dec(
1078            ps_nsq,
1079            &mut ps_del_dec,
1080            ps_indices.signal_type as i32,
1081            &x_sc_q10,
1082            pulses,
1083            pulses_ptr as i32,
1084            xq_ptr as i32,
1085            &mut s_ltp_q15,
1086            &mut delayed_ga_q10,
1087            a_q12,
1088            b_q14,
1089            ar_shp_q13,
1090            lag,
1091            harm_shape_fir_packed_q14,
1092            tilt_q14[k],
1093            lf_shp_q14[k],
1094            gains_q16[k],
1095            lambda_q10,
1096            offset_q10,
1097            ps_common.subfr_length,
1098            subfr_nsq,
1099            ps_common.shaping_lpc_order,
1100            ps_common.predict_lpc_order,
1101            ps_common.warping_q16,
1102            ps_common.n_states_delayed_decision,
1103            &mut smpl_buf_idx,
1104            decision_delay,
1105            ps_common.frame_counter,
1106        );
1107
1108        x_ptr += ps_common.subfr_length as usize;
1109        pulses_ptr += ps_common.subfr_length as usize;
1110        xq_ptr += ps_common.subfr_length as usize;
1111        subfr_nsq += 1;
1112    }
1113
1114    let mut rd_min_q10 = ps_del_dec[0].rd_q10;
1115    let mut winner_ind = 0;
1116    for k in 1..n_states {
1117        if ps_del_dec[k].rd_q10 < rd_min_q10 {
1118            rd_min_q10 = ps_del_dec[k].rd_q10;
1119            winner_ind = k;
1120        }
1121    }
1122
1123    let ps_dd = &ps_del_dec[winner_ind];
1124    ps_nsq.s_lf_ar_q14 = ps_dd.lf_ar_q14;
1125    ps_nsq.s_diff_shp_q14 = ps_dd.diff_q14;
1126    ps_nsq.lag_prev = pitch_l[ps_common.nb_subfr as usize - 1];
1127
1128    let gain_q10_final = silk_rshift(gains_q16[ps_common.nb_subfr as usize - 1], 6);
1129    let mut last_smple_idx = smpl_buf_idx + decision_delay;
1130    for i in 0..decision_delay {
1131        last_smple_idx = (last_smple_idx - 1 + DECISION_DELAY as i32) % DECISION_DELAY as i32;
1132        let pulse_idx = (pulses_ptr as i32 + i - decision_delay) as isize;
1133        let xq_idx = (xq_ptr as i32 + i - decision_delay) as isize;
1134        if pulse_idx >= 0 && (pulse_idx as usize) < pulses.len() {
1135            pulses[pulse_idx as usize] =
1136                silk_rshift_round(ps_dd.q_q10[last_smple_idx as usize], 10) as i8;
1137        }
1138        if xq_idx >= 0 && (xq_idx as usize) < ps_nsq.xq.len() {
1139            ps_nsq.xq[xq_idx as usize] = silk_sat16(silk_rshift_round(
1140                silk_smulww(ps_dd.xq_q14[last_smple_idx as usize], gain_q10_final),
1141                8,
1142            )) as i16;
1143        }
1144        let shp_idx = (ps_nsq.s_ltp_shp_buf_idx - decision_delay + i) as isize;
1145        if shp_idx >= 0 && (shp_idx as usize) < ps_nsq.s_ltp_shp_q14.len() {
1146            ps_nsq.s_ltp_shp_q14[shp_idx as usize] = ps_dd.shape_q14[last_smple_idx as usize];
1147        }
1148    }
1149
1150    let subfr_len = ps_common.subfr_length as usize;
1151    ps_nsq.s_lpc_q14[..NSQ_LPC_BUF_LENGTH]
1152        .copy_from_slice(&ps_dd.s_lpc_q14[subfr_len..subfr_len + NSQ_LPC_BUF_LENGTH]);
1153    ps_nsq.s_ar2_q14.copy_from_slice(&ps_dd.s_ar2_q14);
1154
1155    let ltp_mem_len = ps_common.ltp_mem_length as usize;
1156    let frame_len = ps_common.frame_length as usize;
1157    ps_nsq.xq.copy_within(frame_len..frame_len + ltp_mem_len, 0);
1158    ps_nsq
1159        .s_ltp_shp_q14
1160        .copy_within(frame_len..frame_len + ltp_mem_len, 0);
1161
1162    ps_del_dec[winner_ind].seed_init
1163}