Beispiel #1
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="energy1">O</param>
        /// <param name="shift1">O</param>
        /// <param name="energy2">O</param>
        /// <param name="shift2">O</param>
        /// <param name="exc_Q14">I</param>
        /// <param name="prevGain_Q10">I</param>
        /// <param name="subfr_length">I</param>
        /// <param name="nb_subfr">I</param>
        internal static void silk_PLC_energy(
            out int energy1,
            out int shift1,
            out int energy2,
            out int shift2,
            int[] exc_Q14,
            int[] prevGain_Q10,
            int subfr_length,
            int nb_subfr)
        {
            int i, k;
            int exc_buf_ptr = 0;

            short[] exc_buf = new short[2 * subfr_length];

            /* Find random noise component */
            /* Scale previous excitation signal */
            for (k = 0; k < 2; k++)
            {
                for (i = 0; i < subfr_length; i++)
                {
                    exc_buf[exc_buf_ptr + i] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT(
                                                                             Inlines.silk_SMULWW(exc_Q14[i + (k + nb_subfr - 2) * subfr_length], prevGain_Q10[k]), 8));
                }
                exc_buf_ptr += subfr_length;
            }

            /* Find the subframe with lowest energy of the last two and use that as random noise generator */
            SumSqrShift.silk_sum_sqr_shift(out energy1, out shift1, exc_buf, subfr_length);
            SumSqrShift.silk_sum_sqr_shift(out energy2, out shift2, exc_buf, subfr_length, subfr_length);
        }
Beispiel #2
0
        /// <summary>
        /// Find least-squares prediction gain for one signal based on another and quantize it
        /// </summary>
        /// <param name="ratio_Q14">O    Ratio of residual and mid energies</param>
        /// <param name="x">I    Basis signal</param>
        /// <param name="y">I    Target signal</param>
        /// <param name="mid_res_amp_Q0">I/O  Smoothed mid, residual norms</param>
        /// <param name="mid_res_amp_Q0_ptr"></param>
        /// <param name="length">I    Number of samples</param>
        /// <param name="smooth_coef_Q16">I    Smoothing coefficient</param>
        /// <returns>O    Returns predictor in Q13</returns>
        internal static int silk_stereo_find_predictor(
            BoxedValueInt ratio_Q14,
            short[] x,
            short[] y,
            int[] mid_res_amp_Q0,
            int mid_res_amp_Q0_ptr,
            int length,
            int smooth_coef_Q16)
        {
            int scale;
            int nrgx, nrgy, scale1, scale2;
            int corr, pred_Q13, pred2_Q10;

            /* Find predictor */
            SumSqrShift.silk_sum_sqr_shift(out nrgx, out scale1, x, length);
            SumSqrShift.silk_sum_sqr_shift(out nrgy, out scale2, y, length);
            scale     = Inlines.silk_max_int(scale1, scale2);
            scale     = scale + (scale & 1);      /* make even */
            nrgy      = Inlines.silk_RSHIFT32(nrgy, scale - scale2);
            nrgx      = Inlines.silk_RSHIFT32(nrgx, scale - scale1);
            nrgx      = Inlines.silk_max_int(nrgx, 1);
            corr      = Inlines.silk_inner_prod_aligned_scale(x, y, scale, length);
            pred_Q13  = Inlines.silk_DIV32_varQ(corr, nrgx, 13);
            pred_Q13  = Inlines.silk_LIMIT(pred_Q13, -(1 << 14), 1 << 14);
            pred2_Q10 = Inlines.silk_SMULWB(pred_Q13, pred_Q13);

            /* Faster update for signals with large prediction parameters */
            smooth_coef_Q16 = (int)Inlines.silk_max_int(smooth_coef_Q16, Inlines.silk_abs(pred2_Q10));

            /* Smoothed mid and residual norms */
            Inlines.OpusAssert(smooth_coef_Q16 < 32768);
            scale = Inlines.silk_RSHIFT(scale, 1);
            mid_res_amp_Q0[mid_res_amp_Q0_ptr] = Inlines.silk_SMLAWB(mid_res_amp_Q0[mid_res_amp_Q0_ptr],
                                                                     Inlines.silk_LSHIFT(Inlines.silk_SQRT_APPROX(nrgx), scale) - mid_res_amp_Q0[mid_res_amp_Q0_ptr], smooth_coef_Q16);
            /* Residual energy = nrgy - 2 * pred * corr + pred^2 * nrgx */
            nrgy = Inlines.silk_SUB_LSHIFT32(nrgy, Inlines.silk_SMULWB(corr, pred_Q13), 3 + 1);
            nrgy = Inlines.silk_ADD_LSHIFT32(nrgy, Inlines.silk_SMULWB(nrgx, pred2_Q10), 6);
            mid_res_amp_Q0[mid_res_amp_Q0_ptr + 1] = Inlines.silk_SMLAWB(mid_res_amp_Q0[mid_res_amp_Q0_ptr + 1],
                                                                         Inlines.silk_LSHIFT(Inlines.silk_SQRT_APPROX(nrgy), scale) - mid_res_amp_Q0[mid_res_amp_Q0_ptr + 1], smooth_coef_Q16);

            /* Ratio of smoothed residual and mid norms */
            ratio_Q14.Val = Inlines.silk_DIV32_varQ(mid_res_amp_Q0[mid_res_amp_Q0_ptr + 1], Inlines.silk_max(mid_res_amp_Q0[mid_res_amp_Q0_ptr], 1), 14);
            ratio_Q14.Val = Inlines.silk_LIMIT(ratio_Q14.Val, 0, 32767);

            return(pred_Q13);
        }
Beispiel #3
0
        /// <summary>
        /// Finds linear prediction coeffecients and weights
        /// </summary>
        /// <param name="b_Q14"></param>
        /// <param name="WLTP"></param>
        /// <param name="LTPredCodGain_Q7"></param>
        /// <param name="r_lpc"></param>
        /// <param name="lag"></param>
        /// <param name="Wght_Q15"></param>
        /// <param name="subfr_length"></param>
        /// <param name="nb_subfr"></param>
        /// <param name="mem_offset"></param>
        /// <param name="corr_rshifts"></param>
        /// <param name="arch"></param>
        internal static void silk_find_LTP(
            short[] b_Q14,                  /* O    LTP coefs [SilkConstants.MAX_NB_SUBFR * SilkConstants.LTP_ORDER]                                                                  */
            int[] WLTP,                     /* O    Weight for LTP quantization [SilkConstants.MAX_NB_SUBFR * SilkConstants.LTP_ORDER * SilkConstants.LTP_ORDER]                                          */
            BoxedValueInt LTPredCodGain_Q7, /* O    LTP coding gain                                                             */
            short[] r_lpc,                  /* I    residual signal after LPC signal + state for first 10 ms                    */
            int[] lag,                      /* I    LTP lags   [SilkConstants.MAX_NB_SUBFR]                                                                 */
            int[] Wght_Q15,                 /* I    weights [SilkConstants.MAX_NB_SUBFR]                                                                    */
            int subfr_length,               /* I    subframe length                                                             */
            int nb_subfr,                   /* I    number of subframes                                                         */
            int mem_offset,                 /* I    number of samples in LTP memory                                             */
            int[] corr_rshifts              /* O    right shifts applied to correlations  [SilkConstants.MAX_NB_SUBFR]                                      */
            )
        {
            int i, k, lshift;
            int r_ptr;
            int lag_ptr;
            int b_Q14_ptr;

            int regu;
            int WLTP_ptr;

            int[] b_Q16       = new int[SilkConstants.LTP_ORDER];
            int[] delta_b_Q14 = new int[SilkConstants.LTP_ORDER];
            int[] d_Q14       = new int[SilkConstants.MAX_NB_SUBFR];
            int[] nrg         = new int[SilkConstants.MAX_NB_SUBFR];
            int   g_Q26;

            int[] w = new int[SilkConstants.MAX_NB_SUBFR];
            int   WLTP_max, max_abs_d_Q14, max_w_bits;

            int temp32, denom32;
            int extra_shifts;
            int rr_shifts, maxRshifts, maxRshifts_wxtra, LZs;
            int LPC_res_nrg, LPC_LTP_res_nrg, div_Q16;

            int[] Rr = new int[SilkConstants.LTP_ORDER];
            int[] rr = new int[SilkConstants.MAX_NB_SUBFR];
            int   wd, m_Q12;

            b_Q14_ptr = 0;
            WLTP_ptr  = 0;
            r_ptr     = mem_offset;
            for (k = 0; k < nb_subfr; k++)
            {
                lag_ptr = r_ptr - (lag[k] + SilkConstants.LTP_ORDER / 2);

                SumSqrShift.silk_sum_sqr_shift(out rr[k], out rr_shifts, r_lpc, r_ptr, subfr_length); /* rr[ k ] in Q( -rr_shifts ) */

                /* Assure headroom */
                LZs = Inlines.silk_CLZ32(rr[k]);
                if (LZs < LTP_CORRS_HEAD_ROOM)
                {
                    rr[k]      = Inlines.silk_RSHIFT_ROUND(rr[k], LTP_CORRS_HEAD_ROOM - LZs);
                    rr_shifts += (LTP_CORRS_HEAD_ROOM - LZs);
                }
                corr_rshifts[k] = rr_shifts;
                BoxedValueInt boxed_shifts = new BoxedValueInt(corr_rshifts[k]);
                CorrelateMatrix.silk_corrMatrix(r_lpc, lag_ptr, subfr_length, SilkConstants.LTP_ORDER, LTP_CORRS_HEAD_ROOM, WLTP, WLTP_ptr, boxed_shifts);  /* WLTP_ptr in Q( -corr_rshifts[ k ] ) */
                corr_rshifts[k] = boxed_shifts.Val;

                /* The correlation vector always has lower max abs value than rr and/or RR so head room is assured */
                CorrelateMatrix.silk_corrVector(r_lpc, lag_ptr, r_lpc, r_ptr, subfr_length, SilkConstants.LTP_ORDER, Rr, corr_rshifts[k]);  /* Rr_ptr   in Q( -corr_rshifts[ k ] ) */
                if (corr_rshifts[k] > rr_shifts)
                {
                    rr[k] = Inlines.silk_RSHIFT(rr[k], corr_rshifts[k] - rr_shifts); /* rr[ k ] in Q( -corr_rshifts[ k ] ) */
                }
                Inlines.OpusAssert(rr[k] >= 0);

                regu = 1;
                regu = Inlines.silk_SMLAWB(regu, rr[k], ((int)((TuningParameters.LTP_DAMPING / 3) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.LTP_DAMPING / 3, 16)*/);
                regu = Inlines.silk_SMLAWB(regu, Inlines.MatrixGet(WLTP, WLTP_ptr, 0, 0, SilkConstants.LTP_ORDER), ((int)((TuningParameters.LTP_DAMPING / 3) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.LTP_DAMPING / 3, 16)*/);
                regu = Inlines.silk_SMLAWB(regu, Inlines.MatrixGet(WLTP, WLTP_ptr, SilkConstants.LTP_ORDER - 1, SilkConstants.LTP_ORDER - 1, SilkConstants.LTP_ORDER), ((int)((TuningParameters.LTP_DAMPING / 3) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.LTP_DAMPING / 3, 16)*/);
                RegularizeCorrelations.silk_regularize_correlations(WLTP, WLTP_ptr, rr, k, regu, SilkConstants.LTP_ORDER);

                LinearAlgebra.silk_solve_LDL(WLTP, WLTP_ptr, SilkConstants.LTP_ORDER, Rr, b_Q16); /* WLTP_ptr and Rr_ptr both in Q(-corr_rshifts[k]) */

                /* Limit and store in Q14 */
                silk_fit_LTP(b_Q16, b_Q14, b_Q14_ptr);

                /* Calculate residual energy */
                nrg[k] = ResidualEnergy.silk_residual_energy16_covar(b_Q14, b_Q14_ptr, WLTP, WLTP_ptr, Rr, rr[k], SilkConstants.LTP_ORDER, 14); /* nrg in Q( -corr_rshifts[ k ] ) */

                /* temp = Wght[ k ] / ( nrg[ k ] * Wght[ k ] + 0.01f * subfr_length ); */
                extra_shifts = Inlines.silk_min_int(corr_rshifts[k], LTP_CORRS_HEAD_ROOM);
                denom32      = Inlines.silk_LSHIFT_SAT32(Inlines.silk_SMULWB(nrg[k], Wght_Q15[k]), 1 + extra_shifts) +           /* Q( -corr_rshifts[ k ] + extra_shifts ) */
                               Inlines.silk_RSHIFT(Inlines.silk_SMULWB((int)subfr_length, 655), corr_rshifts[k] - extra_shifts); /* Q( -corr_rshifts[ k ] + extra_shifts ) */
                denom32 = Inlines.silk_max(denom32, 1);
                Inlines.OpusAssert(((long)Wght_Q15[k] << 16) < int.MaxValue);                                                    /* Wght always < 0.5 in Q0 */
                temp32 = Inlines.silk_DIV32(Inlines.silk_LSHIFT((int)Wght_Q15[k], 16), denom32);                                 /* Q( 15 + 16 + corr_rshifts[k] - extra_shifts ) */
                temp32 = Inlines.silk_RSHIFT(temp32, 31 + corr_rshifts[k] - extra_shifts - 26);                                  /* Q26 */

                /* Limit temp such that the below scaling never wraps around */
                WLTP_max = 0;
                for (i = WLTP_ptr; i < WLTP_ptr + (SilkConstants.LTP_ORDER * SilkConstants.LTP_ORDER); i++)
                {
                    WLTP_max = Inlines.silk_max(WLTP[i], WLTP_max);
                }
                lshift = Inlines.silk_CLZ32(WLTP_max) - 1 - 3; /* keep 3 bits free for vq_nearest_neighbor */
                Inlines.OpusAssert(26 - 18 + lshift >= 0);
                if (26 - 18 + lshift < 31)
                {
                    temp32 = Inlines.silk_min_32(temp32, Inlines.silk_LSHIFT((int)1, 26 - 18 + lshift));
                }

                Inlines.silk_scale_vector32_Q26_lshift_18(WLTP, WLTP_ptr, temp32, SilkConstants.LTP_ORDER * SilkConstants.LTP_ORDER);        /* WLTP_ptr in Q( 18 - corr_rshifts[ k ] ) */

                w[k] = Inlines.MatrixGet(WLTP, WLTP_ptr, SilkConstants.LTP_ORDER / 2, SilkConstants.LTP_ORDER / 2, SilkConstants.LTP_ORDER); /* w in Q( 18 - corr_rshifts[ k ] ) */
                Inlines.OpusAssert(w[k] >= 0);

                r_ptr     += subfr_length;
                b_Q14_ptr += SilkConstants.LTP_ORDER;
                WLTP_ptr  += (SilkConstants.LTP_ORDER * SilkConstants.LTP_ORDER);
            }

            maxRshifts = 0;
            for (k = 0; k < nb_subfr; k++)
            {
                maxRshifts = Inlines.silk_max_int(corr_rshifts[k], maxRshifts);
            }

            /* Compute LTP coding gain */
            if (LTPredCodGain_Q7 != null)
            {
                LPC_LTP_res_nrg = 0;
                LPC_res_nrg     = 0;
                Inlines.OpusAssert(LTP_CORRS_HEAD_ROOM >= 2); /* Check that no overflow will happen when adding */
                for (k = 0; k < nb_subfr; k++)
                {
                    LPC_res_nrg     = Inlines.silk_ADD32(LPC_res_nrg, Inlines.silk_RSHIFT(Inlines.silk_ADD32(Inlines.silk_SMULWB(rr[k], Wght_Q15[k]), 1), 1 + (maxRshifts - corr_rshifts[k])));      /* Q( -maxRshifts ) */
                    LPC_LTP_res_nrg = Inlines.silk_ADD32(LPC_LTP_res_nrg, Inlines.silk_RSHIFT(Inlines.silk_ADD32(Inlines.silk_SMULWB(nrg[k], Wght_Q15[k]), 1), 1 + (maxRshifts - corr_rshifts[k]))); /* Q( -maxRshifts ) */
                }
                LPC_LTP_res_nrg = Inlines.silk_max(LPC_LTP_res_nrg, 1);                                                                                                                              /* avoid division by zero */

                div_Q16 = Inlines.silk_DIV32_varQ(LPC_res_nrg, LPC_LTP_res_nrg, 16);
                LTPredCodGain_Q7.Val = (int)Inlines.silk_SMULBB(3, Inlines.silk_lin2log(div_Q16) - (16 << 7));

                Inlines.OpusAssert(LTPredCodGain_Q7.Val == (int)Inlines.silk_SAT16(Inlines.silk_MUL(3, Inlines.silk_lin2log(div_Q16) - (16 << 7))));
            }

            /* smoothing */
            /* d = sum( B, 1 ); */
            b_Q14_ptr = 0;
            for (k = 0; k < nb_subfr; k++)
            {
                d_Q14[k] = 0;
                for (i = b_Q14_ptr; i < b_Q14_ptr + SilkConstants.LTP_ORDER; i++)
                {
                    d_Q14[k] += b_Q14[i];
                }
                b_Q14_ptr += SilkConstants.LTP_ORDER;
            }

            /* m = ( w * d' ) / ( sum( w ) + 1e-3 ); */

            /* Find maximum absolute value of d_Q14 and the bits used by w in Q0 */
            max_abs_d_Q14 = 0;
            max_w_bits    = 0;
            for (k = 0; k < nb_subfr; k++)
            {
                max_abs_d_Q14 = Inlines.silk_max_32(max_abs_d_Q14, Inlines.silk_abs(d_Q14[k]));
                /* w[ k ] is in Q( 18 - corr_rshifts[ k ] ) */
                /* Find bits needed in Q( 18 - maxRshifts ) */
                max_w_bits = Inlines.silk_max_32(max_w_bits, 32 - Inlines.silk_CLZ32(w[k]) + corr_rshifts[k] - maxRshifts);
            }

            /* max_abs_d_Q14 = (5 << 15); worst case, i.e. SilkConstants.LTP_ORDER * -silk_int16_MIN */
            Inlines.OpusAssert(max_abs_d_Q14 <= (5 << 15));

            /* How many bits is needed for w*d' in Q( 18 - maxRshifts ) in the worst case, of all d_Q14's being equal to max_abs_d_Q14 */
            extra_shifts = max_w_bits + 32 - Inlines.silk_CLZ32(max_abs_d_Q14) - 14;

            /* Subtract what we got available; bits in output var plus maxRshifts */
            extra_shifts -= (32 - 1 - 2 + maxRshifts); /* Keep sign bit free as well as 2 bits for accumulation */
            extra_shifts  = Inlines.silk_max_int(extra_shifts, 0);

            maxRshifts_wxtra = maxRshifts + extra_shifts;

            temp32 = Inlines.silk_RSHIFT(262, maxRshifts + extra_shifts) + 1; /* 1e-3f in Q( 18 - (maxRshifts + extra_shifts) ) */
            wd     = 0;
            for (k = 0; k < nb_subfr; k++)
            {
                /* w has at least 2 bits of headroom so no overflow should happen */
                temp32 = Inlines.silk_ADD32(temp32, Inlines.silk_RSHIFT(w[k], maxRshifts_wxtra - corr_rshifts[k]));                                                    /* Q( 18 - maxRshifts_wxtra ) */
                wd     = Inlines.silk_ADD32(wd, Inlines.silk_LSHIFT(Inlines.silk_SMULWW(Inlines.silk_RSHIFT(w[k], maxRshifts_wxtra - corr_rshifts[k]), d_Q14[k]), 2)); /* Q( 18 - maxRshifts_wxtra ) */
            }
            m_Q12 = Inlines.silk_DIV32_varQ(wd, temp32, 12);

            b_Q14_ptr = 0;
            for (k = 0; k < nb_subfr; k++)
            {
                /* w[ k ] from Q( 18 - corr_rshifts[ k ] ) to Q( 16 ) */
                if (2 - corr_rshifts[k] > 0)
                {
                    temp32 = Inlines.silk_RSHIFT(w[k], 2 - corr_rshifts[k]);
                }
                else
                {
                    temp32 = Inlines.silk_LSHIFT_SAT32(w[k], corr_rshifts[k] - 2);
                }

                g_Q26 = Inlines.silk_MUL(
                    Inlines.silk_DIV32(
                        ((int)((TuningParameters.LTP_SMOOTHING) * ((long)1 << (26)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.LTP_SMOOTHING, 26)*/,
                        Inlines.silk_RSHIFT(((int)((TuningParameters.LTP_SMOOTHING) * ((long)1 << (26)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.LTP_SMOOTHING, 26)*/, 10) + temp32), /* Q10 */
                    Inlines.silk_LSHIFT_SAT32(Inlines.silk_SUB_SAT32((int)m_Q12, Inlines.silk_RSHIFT(d_Q14[k], 2)), 4));                                                                   /* Q16 */

                temp32 = 0;
                for (i = 0; i < SilkConstants.LTP_ORDER; i++)
                {
                    delta_b_Q14[i] = Inlines.silk_max_16(b_Q14[b_Q14_ptr + i], 1638); /* 1638_Q14 = 0.1_Q0 */
                    temp32        += delta_b_Q14[i];                                  /* Q14 */
                }
                temp32 = Inlines.silk_DIV32(g_Q26, temp32);                           /* Q14 . Q12 */
                for (i = 0; i < SilkConstants.LTP_ORDER; i++)
                {
                    b_Q14[b_Q14_ptr + i] = (short)(Inlines.silk_LIMIT_32((int)b_Q14[b_Q14_ptr + i] + Inlines.silk_SMULWB(Inlines.silk_LSHIFT_SAT32(temp32, 4), delta_b_Q14[i]), -16000, 28000));
                }
                b_Q14_ptr += SilkConstants.LTP_ORDER;
            }
        }
        /* Calculates correlation matrix X'*X */
        internal static void silk_corrMatrix(
            short[] x,                                  /* I    x vector [L + order - 1] used to form data matrix X                         */
            int x_ptr,
            int L,                                      /* I    Length of vectors                                                           */
            int order,                                  /* I    Max lag for correlation                                                     */
            int head_room,                              /* I    Desired headroom                                                            */
            int[] XX,                                   /* O    Pointer to X'*X correlation matrix [ order x order ]                        */
            int XX_ptr,
            BoxedValueInt rshifts                       /* I/O  Right shifts of correlations                                                */
            )
        {
            int i, j, lag, head_room_rshifts;
            int energy, rshifts_local;
            int ptr1, ptr2;

            /* Calculate energy to find shift used to fit in 32 bits */
            SumSqrShift.silk_sum_sqr_shift(out energy, out rshifts_local, x, x_ptr, L + order - 1);
            /* Add shifts to get the desired head room */
            head_room_rshifts = Inlines.silk_max(head_room - Inlines.silk_CLZ32(energy), 0);

            energy         = Inlines.silk_RSHIFT32(energy, head_room_rshifts);
            rshifts_local += head_room_rshifts;

            /* Calculate energy of first column (0) of X: X[:,0]'*X[:,0] */
            /* Remove contribution of first order - 1 samples */
            for (i = x_ptr; i < x_ptr + order - 1; i++)
            {
                energy -= Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[i], x[i]), rshifts_local);
            }
            if (rshifts_local < rshifts.Val)
            {
                /* Adjust energy */
                energy        = Inlines.silk_RSHIFT32(energy, rshifts.Val - rshifts_local);
                rshifts_local = rshifts.Val;
            }

            /* Calculate energy of remaining columns of X: X[:,j]'*X[:,j] */
            /* Fill out the diagonal of the correlation matrix */
            Inlines.MatrixSet(XX, XX_ptr, 0, 0, order, energy);
            ptr1 = x_ptr + order - 1; /* First sample of column 0 of X */
            for (j = 1; j < order; j++)
            {
                energy = Inlines.silk_SUB32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 + L - j], x[ptr1 + L - j]), rshifts_local));
                energy = Inlines.silk_ADD32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 - j], x[ptr1 - j]), rshifts_local));
                Inlines.MatrixSet(XX, XX_ptr, j, j, order, energy);
            }

            ptr2 = x_ptr + order - 2; /* First sample of column 1 of X */
            /* Calculate the remaining elements of the correlation matrix */
            if (rshifts_local > 0)
            {
                /* Right shifting used */
                for (lag = 1; lag < order; lag++)
                {
                    /* Inner product of column 0 and column lag: X[:,0]'*X[:,lag] */
                    energy = 0;
                    for (i = 0; i < L; i++)
                    {
                        energy += Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 + i], x[ptr2 + i]), rshifts_local);
                    }
                    /* Calculate remaining off diagonal: X[:,j]'*X[:,j + lag] */
                    Inlines.MatrixSet(XX, XX_ptr, lag, 0, order, energy);
                    Inlines.MatrixSet(XX, XX_ptr, 0, lag, order, energy);
                    for (j = 1; j < (order - lag); j++)
                    {
                        energy = Inlines.silk_SUB32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 + L - j], x[ptr2 + L - j]), rshifts_local));
                        energy = Inlines.silk_ADD32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 - j], x[ptr2 - j]), rshifts_local));
                        Inlines.MatrixSet(XX, XX_ptr, lag + j, j, order, energy);
                        Inlines.MatrixSet(XX, XX_ptr, j, lag + j, order, energy);
                    }
                    ptr2--; /* Update pointer to first sample of next column (lag) in X */
                }
            }
            else
            {
                for (lag = 1; lag < order; lag++)
                {
                    /* Inner product of column 0 and column lag: X[:,0]'*X[:,lag] */
                    energy = Inlines.silk_inner_prod(x, ptr1, x, ptr2, L);
                    Inlines.MatrixSet(XX, XX_ptr, lag, 0, order, energy);
                    Inlines.MatrixSet(XX, XX_ptr, 0, lag, order, energy);
                    /* Calculate remaining off diagonal: X[:,j]'*X[:,j + lag] */
                    for (j = 1; j < (order - lag); j++)
                    {
                        energy = Inlines.silk_SUB32(energy, Inlines.silk_SMULBB(x[ptr1 + L - j], x[ptr2 + L - j]));
                        energy = Inlines.silk_SMLABB(energy, x[ptr1 - j], x[ptr2 - j]);
                        Inlines.MatrixSet(XX, XX_ptr, lag + j, j, order, energy);
                        Inlines.MatrixSet(XX, XX_ptr, j, lag + j, order, energy);
                    }
                    ptr2--;/* Update pointer to first sample of next column (lag) in X */
                }
            }
            rshifts.Val = rshifts_local;
        }
Beispiel #5
0
        /* Finds LPC vector from correlations, and converts to NLSF */
        internal static void silk_find_LPC(
            SilkChannelEncoder psEncC,                  /* I/O  Encoder state                                                               */
            short[] NLSF_Q15,                           /* O    NLSFs                                                                       */
            short[] x,                                  /* I    Input signal                                                                */
            int minInvGain_Q30                          /* I    Inverse of max prediction gain                                              */
            )
        {
            int k, subfr_length;

            int[]         a_Q16 = new int[SilkConstants.MAX_LPC_ORDER];
            int           isInterpLower, shift;
            int           res_nrg0, res_nrg1;
            int           rshift0, rshift1;
            BoxedValueInt scratch_box1 = new BoxedValueInt();
            BoxedValueInt scratch_box2 = new BoxedValueInt();

            /* Used only for LSF interpolation */
            int[] a_tmp_Q16 = new int[SilkConstants.MAX_LPC_ORDER];
            int   res_nrg_interp, res_nrg, res_tmp_nrg;
            int   res_nrg_interp_Q, res_nrg_Q, res_tmp_nrg_Q;

            short[] a_tmp_Q12 = new short[SilkConstants.MAX_LPC_ORDER];
            short[] NLSF0_Q15 = new short[SilkConstants.MAX_LPC_ORDER];

            subfr_length = psEncC.subfr_length + psEncC.predictLPCOrder;

            /* Default: no interpolation */
            psEncC.indices.NLSFInterpCoef_Q2 = 4;

            /* Burg AR analysis for the full frame */
            BurgModified.silk_burg_modified(scratch_box1, scratch_box2, a_Q16, x, 0, minInvGain_Q30, subfr_length, psEncC.nb_subfr, psEncC.predictLPCOrder);
            res_nrg   = scratch_box1.Val;
            res_nrg_Q = scratch_box2.Val;

            if (psEncC.useInterpolatedNLSFs != 0 && psEncC.first_frame_after_reset == 0 && psEncC.nb_subfr == SilkConstants.MAX_NB_SUBFR)
            {
                short[] LPC_res;

                /* Optimal solution for last 10 ms */
                BurgModified.silk_burg_modified(scratch_box1, scratch_box2, a_tmp_Q16, x, (2 * subfr_length), minInvGain_Q30, subfr_length, 2, psEncC.predictLPCOrder);
                res_tmp_nrg   = scratch_box1.Val;
                res_tmp_nrg_Q = scratch_box2.Val;

                /* subtract residual energy here, as that's easier than adding it to the    */
                /* residual energy of the first 10 ms in each iteration of the search below */
                shift = res_tmp_nrg_Q - res_nrg_Q;
                if (shift >= 0)
                {
                    if (shift < 32)
                    {
                        res_nrg = res_nrg - Inlines.silk_RSHIFT(res_tmp_nrg, shift);
                    }
                }
                else
                {
                    Inlines.OpusAssert(shift > -32);
                    res_nrg   = Inlines.silk_RSHIFT(res_nrg, -shift) - res_tmp_nrg;
                    res_nrg_Q = res_tmp_nrg_Q;
                }

                /* Convert to NLSFs */
                NLSF.silk_A2NLSF(NLSF_Q15, a_tmp_Q16, psEncC.predictLPCOrder);

                LPC_res = new short[2 * subfr_length];

                /* Search over interpolation indices to find the one with lowest residual energy */
                for (k = 3; k >= 0; k--)
                {
                    /* Interpolate NLSFs for first half */
                    Inlines.silk_interpolate(NLSF0_Q15, psEncC.prev_NLSFq_Q15, NLSF_Q15, k, psEncC.predictLPCOrder);

                    /* Convert to LPC for residual energy evaluation */
                    NLSF.silk_NLSF2A(a_tmp_Q12, NLSF0_Q15, psEncC.predictLPCOrder);

                    /* Calculate residual energy with NLSF interpolation */
                    Filters.silk_LPC_analysis_filter(LPC_res, 0, x, 0, a_tmp_Q12, 0, 2 * subfr_length, psEncC.predictLPCOrder);

                    SumSqrShift.silk_sum_sqr_shift(out res_nrg0, out rshift0, LPC_res, psEncC.predictLPCOrder, subfr_length - psEncC.predictLPCOrder);

                    SumSqrShift.silk_sum_sqr_shift(out res_nrg1, out rshift1, LPC_res, psEncC.predictLPCOrder + subfr_length, subfr_length - psEncC.predictLPCOrder);

                    /* Add subframe energies from first half frame */
                    shift = rshift0 - rshift1;
                    if (shift >= 0)
                    {
                        res_nrg1         = Inlines.silk_RSHIFT(res_nrg1, shift);
                        res_nrg_interp_Q = -rshift0;
                    }
                    else
                    {
                        res_nrg0         = Inlines.silk_RSHIFT(res_nrg0, -shift);
                        res_nrg_interp_Q = -rshift1;
                    }
                    res_nrg_interp = Inlines.silk_ADD32(res_nrg0, res_nrg1);

                    /* Compare with first half energy without NLSF interpolation, or best interpolated value so far */
                    shift = res_nrg_interp_Q - res_nrg_Q;
                    if (shift >= 0)
                    {
                        if (Inlines.silk_RSHIFT(res_nrg_interp, shift) < res_nrg)
                        {
                            isInterpLower = (true ? 1 : 0);
                        }
                        else
                        {
                            isInterpLower = (false ? 1 : 0);
                        }
                    }
                    else
                    {
                        if (-shift < 32)
                        {
                            if (res_nrg_interp < Inlines.silk_RSHIFT(res_nrg, -shift))
                            {
                                isInterpLower = (true ? 1 : 0);
                            }
                            else
                            {
                                isInterpLower = (false ? 1 : 0);
                            }
                        }
                        else
                        {
                            isInterpLower = (false ? 1 : 0);
                        }
                    }

                    /* Determine whether current interpolated NLSFs are best so far */
                    if (isInterpLower == (true ? 1 : 0))
                    {
                        /* Interpolation has lower residual energy */
                        res_nrg   = res_nrg_interp;
                        res_nrg_Q = res_nrg_interp_Q;
                        psEncC.indices.NLSFInterpCoef_Q2 = (sbyte)k;
                    }
                }
            }

            if (psEncC.indices.NLSFInterpCoef_Q2 == 4)
            {
                /* NLSF interpolation is currently inactive, calculate NLSFs from full frame AR coefficients */
                NLSF.silk_A2NLSF(NLSF_Q15, a_Q16, psEncC.predictLPCOrder);
            }

            Inlines.OpusAssert(psEncC.indices.NLSFInterpCoef_Q2 == 4 || (psEncC.useInterpolatedNLSFs != 0 && psEncC.first_frame_after_reset == 0 && psEncC.nb_subfr == SilkConstants.MAX_NB_SUBFR));
        }
Beispiel #6
0
        /* Calculates residual energies of input subframes where all subframes have LPC_order   */
        /* of preceding samples                                                                 */
        internal static void silk_residual_energy(
            int[] nrgs,                 /* O    Residual energy per subframe  [MAX_NB_SUBFR]                                              */
            int[] nrgsQ,                /* O    Q value per subframe   [MAX_NB_SUBFR]                                                     */
            short[] x,                  /* I    Input signal                                                                */
            short[][] a_Q12,            /* I    AR coefs for each frame half [2][MAX_LPC_ORDER]                                                */
            int[] gains,                /* I    Quantization gains [SilkConstants.MAX_NB_SUBFR]                                                         */
            int subfr_length,           /* I    Subframe length                                                             */
            int nb_subfr,               /* I    Number of subframes                                                         */
            int LPC_order               /* I    LPC order                                                                   */
            )
        {
            int offset, i, j, lz1, lz2;
            int rshift, energy;
            int LPC_res_ptr;

            short[] LPC_res;
            int     x_ptr;
            int     tmp32;

            x_ptr  = 0;
            offset = LPC_order + subfr_length;

            /* Filter input to create the LPC residual for each frame half, and measure subframe energies */
            LPC_res = new short[(SilkConstants.MAX_NB_SUBFR >> 1) * offset];
            Inlines.OpusAssert((nb_subfr >> 1) * (SilkConstants.MAX_NB_SUBFR >> 1) == nb_subfr);
            for (i = 0; i < nb_subfr >> 1; i++)
            {
                /* Calculate half frame LPC residual signal including preceding samples */
                Filters.silk_LPC_analysis_filter(LPC_res, 0, x, x_ptr, a_Q12[i], 0, (SilkConstants.MAX_NB_SUBFR >> 1) * offset, LPC_order);

                /* Point to first subframe of the just calculated LPC residual signal */
                LPC_res_ptr = LPC_order;
                for (j = 0; j < (SilkConstants.MAX_NB_SUBFR >> 1); j++)
                {
                    /* Measure subframe energy */
                    SumSqrShift.silk_sum_sqr_shift(out energy, out rshift, LPC_res, LPC_res_ptr, subfr_length);
                    nrgs[i * (SilkConstants.MAX_NB_SUBFR >> 1) + j] = energy;

                    /* Set Q values for the measured energy */
                    nrgsQ[i * (SilkConstants.MAX_NB_SUBFR >> 1) + j] = 0 - rshift;

                    /* Move to next subframe */
                    LPC_res_ptr += offset;
                }
                /* Move to next frame half */
                x_ptr += (SilkConstants.MAX_NB_SUBFR >> 1) * offset;
            }

            /* Apply the squared subframe gains */
            for (i = 0; i < nb_subfr; i++)
            {
                /* Fully upscale gains and energies */
                lz1 = Inlines.silk_CLZ32(nrgs[i]) - 1;
                lz2 = Inlines.silk_CLZ32(gains[i]) - 1;

                tmp32 = Inlines.silk_LSHIFT32(gains[i], lz2);

                /* Find squared gains */
                tmp32 = Inlines.silk_SMMUL(tmp32, tmp32); /* Q( 2 * lz2 - 32 )*/

                /* Scale energies */
                nrgs[i]   = Inlines.silk_SMMUL(tmp32, Inlines.silk_LSHIFT32(nrgs[i], lz1)); /* Q( nrgsQ[ i ] + lz1 + 2 * lz2 - 32 - 32 )*/
                nrgsQ[i] += lz1 + 2 * lz2 - 32 - 32;
            }
        }
        /*************************************************************/
        /*      FIXED POINT CORE PITCH ANALYSIS FUNCTION             */
        /*************************************************************/
        internal static int silk_pitch_analysis_core( /* O    Voicing estimate: 0 voiced, 1 unvoiced                      */
            short[] frame,                            /* I    Signal of length PE_FRAME_LENGTH_MS*Fs_kHz                  */
            int[] pitch_out,                          /* O    4 pitch lag values                                          */
            BoxedValueShort lagIndex,                 /* O    Lag Index                                                   */
            BoxedValueSbyte contourIndex,             /* O    Pitch contour Index                                         */
            BoxedValueInt LTPCorr_Q15,                /* I/O  Normalized correlation; input: value from previous frame    */
            int prevLag,                              /* I    Last lag of previous frame; set to zero is unvoiced         */
            int search_thres1_Q16,                    /* I    First stage threshold for lag candidates 0 - 1              */
            int search_thres2_Q13,                    /* I    Final threshold for lag candidates 0 - 1                    */
            int Fs_kHz,                               /* I    Sample frequency (kHz)                                      */
            int complexity,                           /* I    Complexity setting, 0-2, where 2 is highest                 */
            int nb_subfr                              /* I    number of 5 ms subframes                                    */
            )
        {
            short[] frame_8kHz;
            short[] frame_4kHz;
            int[]   filt_state = new int[6];
            short[] input_frame_ptr;
            int     i, k, d, j;

            short[] C;
            int[]   xcorr32;
            short[] basis;
            int     basis_ptr;

            short[] target;
            int     target_ptr;
            int     cross_corr, normalizer, energy, shift, energy_basis, energy_target;
            int     Cmax, length_d_srch, length_d_comp;

            int[]   d_srch = new int[SilkConstants.PE_D_SRCH_LENGTH];
            short[] d_comp;
            int     sum, threshold, lag_counter;
            int     CBimax, CBimax_new, CBimax_old, lag, start_lag, end_lag, lag_new;
            int     CCmax, CCmax_b, CCmax_new_b, CCmax_new;

            int[] CC = new int[SilkConstants.PE_NB_CBKS_STAGE2_EXT];
            silk_pe_stage3_vals[] energies_st3;
            silk_pe_stage3_vals[] cross_corr_st3;
            int frame_length, frame_length_8kHz, frame_length_4kHz;
            int sf_length;
            int min_lag;
            int max_lag;
            int contour_bias_Q15, diff;
            int nb_cbk_search;
            int delta_lag_log2_sqr_Q7, lag_log2_Q7, prevLag_log2_Q7, prev_lag_bias_Q13;

            sbyte[][] Lag_CB_ptr;

            /* Check for valid sampling frequency */
            Inlines.OpusAssert(Fs_kHz == 8 || Fs_kHz == 12 || Fs_kHz == 16);

            /* Check for valid complexity setting */
            Inlines.OpusAssert(complexity >= SilkConstants.SILK_PE_MIN_COMPLEX);
            Inlines.OpusAssert(complexity <= SilkConstants.SILK_PE_MAX_COMPLEX);

            Inlines.OpusAssert(search_thres1_Q16 >= 0 && search_thres1_Q16 <= (1 << 16));
            Inlines.OpusAssert(search_thres2_Q13 >= 0 && search_thres2_Q13 <= (1 << 13));

            /* Set up frame lengths max / min lag for the sampling frequency */
            frame_length      = (SilkConstants.PE_LTP_MEM_LENGTH_MS + nb_subfr * SilkConstants.PE_SUBFR_LENGTH_MS) * Fs_kHz;
            frame_length_4kHz = (SilkConstants.PE_LTP_MEM_LENGTH_MS + nb_subfr * SilkConstants.PE_SUBFR_LENGTH_MS) * 4;
            frame_length_8kHz = (SilkConstants.PE_LTP_MEM_LENGTH_MS + nb_subfr * SilkConstants.PE_SUBFR_LENGTH_MS) * 8;
            sf_length         = SilkConstants.PE_SUBFR_LENGTH_MS * Fs_kHz;
            min_lag           = SilkConstants.PE_MIN_LAG_MS * Fs_kHz;
            max_lag           = SilkConstants.PE_MAX_LAG_MS * Fs_kHz - 1;

            /* Resample from input sampled at Fs_kHz to 8 kHz */
            frame_8kHz = new short[frame_length_8kHz];
            if (Fs_kHz == 16)
            {
                Arrays.MemSetInt(filt_state, 0, 2);
                Resampler.silk_resampler_down2(filt_state, frame_8kHz, frame, frame_length);
            }
            else if (Fs_kHz == 12)
            {
                Arrays.MemSetInt(filt_state, 0, 6);
                Resampler.silk_resampler_down2_3(filt_state, frame_8kHz, frame, frame_length);
            }
            else
            {
                Inlines.OpusAssert(Fs_kHz == 8);
                Array.Copy(frame, frame_8kHz, frame_length_8kHz);
            }

            /* Decimate again to 4 kHz */
            Arrays.MemSetInt(filt_state, 0, 2); /* Set state to zero */
            frame_4kHz = new short[frame_length_4kHz];
            Resampler.silk_resampler_down2(filt_state, frame_4kHz, frame_8kHz, frame_length_8kHz);

            /* Low-pass filter */
            for (i = frame_length_4kHz - 1; i > 0; i--)
            {
                frame_4kHz[i] = Inlines.silk_ADD_SAT16(frame_4kHz[i], frame_4kHz[i - 1]);
            }

            /*******************************************************************************
            ** Scale 4 kHz signal down to prevent correlations measures from overflowing
            ** find scaling as max scaling for each 8kHz(?) subframe
            *******************************************************************************/

            /* Inner product is calculated with different lengths, so scale for the worst case */
            SumSqrShift.silk_sum_sqr_shift(out energy, out shift, frame_4kHz, frame_length_4kHz);
            if (shift > 0)
            {
                shift = Inlines.silk_RSHIFT(shift, 1);
                for (i = 0; i < frame_length_4kHz; i++)
                {
                    frame_4kHz[i] = Inlines.silk_RSHIFT16(frame_4kHz[i], shift);
                }
            }

            /******************************************************************************
            * FIRST STAGE, operating in 4 khz
            ******************************************************************************/
            C       = new short[nb_subfr * CSTRIDE_8KHZ];
            xcorr32 = new int[MAX_LAG_4KHZ - MIN_LAG_4KHZ + 1];
            Arrays.MemSetShort(C, 0, (nb_subfr >> 1) * CSTRIDE_4KHZ);
            target     = frame_4kHz;
            target_ptr = Inlines.silk_LSHIFT(SF_LENGTH_4KHZ, 2);
            for (k = 0; k < nb_subfr >> 1; k++)
            {
                basis     = target;
                basis_ptr = target_ptr - MIN_LAG_4KHZ;

                CeltPitchXCorr.pitch_xcorr(target, target_ptr, target, target_ptr - MAX_LAG_4KHZ, xcorr32, SF_LENGTH_8KHZ, MAX_LAG_4KHZ - MIN_LAG_4KHZ + 1);

                /* Calculate first vector products before loop */
                cross_corr = xcorr32[MAX_LAG_4KHZ - MIN_LAG_4KHZ];
                normalizer = Inlines.silk_inner_prod_self(target, target_ptr, SF_LENGTH_8KHZ);
                normalizer = Inlines.silk_ADD32(normalizer, Inlines.silk_inner_prod_self(basis, basis_ptr, SF_LENGTH_8KHZ));
                normalizer = Inlines.silk_ADD32(normalizer, Inlines.silk_SMULBB(SF_LENGTH_8KHZ, 4000));

                Inlines.MatrixSet(C, k, 0, CSTRIDE_4KHZ,
                                  (short)Inlines.silk_DIV32_varQ(cross_corr, normalizer, 13 + 1));        /* Q13 */

                /* From now on normalizer is computed recursively */
                for (d = MIN_LAG_4KHZ + 1; d <= MAX_LAG_4KHZ; d++)
                {
                    basis_ptr--;

                    cross_corr = xcorr32[MAX_LAG_4KHZ - d];

                    /* Add contribution of new sample and remove contribution from oldest sample */
                    normalizer = Inlines.silk_ADD32(normalizer,
                                                    Inlines.silk_SMULBB(basis[basis_ptr], basis[basis_ptr]) -
                                                    Inlines.silk_SMULBB(basis[basis_ptr + SF_LENGTH_8KHZ], basis[basis_ptr + SF_LENGTH_8KHZ]));

                    Inlines.MatrixSet(C, k, d - MIN_LAG_4KHZ, CSTRIDE_4KHZ,
                                      (short)Inlines.silk_DIV32_varQ(cross_corr, normalizer, 13 + 1));    /* Q13 */
                }
                /* Update target pointer */
                target_ptr += SF_LENGTH_8KHZ;
            }

            /* Combine two subframes into single correlation measure and apply short-lag bias */
            if (nb_subfr == SilkConstants.PE_MAX_NB_SUBFR)
            {
                for (i = MAX_LAG_4KHZ; i >= MIN_LAG_4KHZ; i--)
                {
                    sum = (int)Inlines.MatrixGet(C, 0, i - MIN_LAG_4KHZ, CSTRIDE_4KHZ)
                          + (int)Inlines.MatrixGet(C, 1, i - MIN_LAG_4KHZ, CSTRIDE_4KHZ);        /* Q14 */
                    sum = Inlines.silk_SMLAWB(sum, sum, Inlines.silk_LSHIFT(-i, 4));             /* Q14 */
                    C[i - MIN_LAG_4KHZ] = (short)sum;                                            /* Q14 */
                }
            }
            else
            {
                /* Only short-lag bias */
                for (i = MAX_LAG_4KHZ; i >= MIN_LAG_4KHZ; i--)
                {
                    sum = Inlines.silk_LSHIFT((int)C[i - MIN_LAG_4KHZ], 1);                      /* Q14 */
                    sum = Inlines.silk_SMLAWB(sum, sum, Inlines.silk_LSHIFT(-i, 4));             /* Q14 */
                    C[i - MIN_LAG_4KHZ] = (short)sum;                                            /* Q14 */
                }
            }

            /* Sort */
            length_d_srch = Inlines.silk_ADD_LSHIFT32(4, complexity, 1);
            Inlines.OpusAssert(3 * length_d_srch <= SilkConstants.PE_D_SRCH_LENGTH);
            Sort.silk_insertion_sort_decreasing_int16(C, d_srch, CSTRIDE_4KHZ, length_d_srch);

            /* Escape if correlation is very low already here */
            Cmax = (int)C[0];                                                    /* Q14 */
            if (Cmax < ((int)((0.2f) * ((long)1 << (14)) + 0.5)) /*Inlines.SILK_CONST(0.2f, 14)*/)
            {
                Arrays.MemSetInt(pitch_out, 0, nb_subfr);
                LTPCorr_Q15.Val  = 0;
                lagIndex.Val     = 0;
                contourIndex.Val = 0;

                return(1);
            }

            threshold = Inlines.silk_SMULWB(search_thres1_Q16, Cmax);
            for (i = 0; i < length_d_srch; i++)
            {
                /* Convert to 8 kHz indices for the sorted correlation that exceeds the threshold */
                if (C[i] > threshold)
                {
                    d_srch[i] = Inlines.silk_LSHIFT(d_srch[i] + MIN_LAG_4KHZ, 1);
                }
                else
                {
                    length_d_srch = i;
                    break;
                }
            }
            Inlines.OpusAssert(length_d_srch > 0);

            d_comp = new short[D_COMP_STRIDE];
            for (i = D_COMP_MIN; i < D_COMP_MAX; i++)
            {
                d_comp[i - D_COMP_MIN] = 0;
            }
            for (i = 0; i < length_d_srch; i++)
            {
                d_comp[d_srch[i] - D_COMP_MIN] = 1;
            }

            /* Convolution */
            for (i = D_COMP_MAX - 1; i >= MIN_LAG_8KHZ; i--)
            {
                d_comp[i - D_COMP_MIN] += (short)(d_comp[i - 1 - D_COMP_MIN] + d_comp[i - 2 - D_COMP_MIN]);
            }

            length_d_srch = 0;
            for (i = MIN_LAG_8KHZ; i < MAX_LAG_8KHZ + 1; i++)
            {
                if (d_comp[i + 1 - D_COMP_MIN] > 0)
                {
                    d_srch[length_d_srch] = i;
                    length_d_srch++;
                }
            }

            /* Convolution */
            for (i = D_COMP_MAX - 1; i >= MIN_LAG_8KHZ; i--)
            {
                d_comp[i - D_COMP_MIN] += (short)(d_comp[i - 1 - D_COMP_MIN] + d_comp[i - 2 - D_COMP_MIN] + d_comp[i - 3 - D_COMP_MIN]);
            }

            length_d_comp = 0;
            for (i = MIN_LAG_8KHZ; i < D_COMP_MAX; i++)
            {
                if (d_comp[i - D_COMP_MIN] > 0)
                {
                    d_comp[length_d_comp] = (short)(i - 2);
                    length_d_comp++;
                }
            }

            /**********************************************************************************
            ** SECOND STAGE, operating at 8 kHz, on lag sections with high correlation
            *************************************************************************************/

            /******************************************************************************
            ** Scale signal down to avoid correlations measures from overflowing
            *******************************************************************************/
            /* find scaling as max scaling for each subframe */
            SumSqrShift.silk_sum_sqr_shift(out energy, out shift, frame_8kHz, frame_length_8kHz);
            if (shift > 0)
            {
                shift = Inlines.silk_RSHIFT(shift, 1);
                for (i = 0; i < frame_length_8kHz; i++)
                {
                    frame_8kHz[i] = Inlines.silk_RSHIFT16(frame_8kHz[i], shift);
                }
            }

            /*********************************************************************************
            * Find energy of each subframe projected onto its history, for a range of delays
            *********************************************************************************/
            Arrays.MemSetShort(C, 0, nb_subfr * CSTRIDE_8KHZ);

            target     = frame_8kHz;
            target_ptr = SilkConstants.PE_LTP_MEM_LENGTH_MS * 8;
            for (k = 0; k < nb_subfr; k++)
            {
                energy_target = Inlines.silk_ADD32(Inlines.silk_inner_prod(target, target_ptr, target, target_ptr, SF_LENGTH_8KHZ), 1);
                for (j = 0; j < length_d_comp; j++)
                {
                    d         = d_comp[j];
                    basis     = target;
                    basis_ptr = target_ptr - d;

                    cross_corr = Inlines.silk_inner_prod(target, target_ptr, basis, basis_ptr, SF_LENGTH_8KHZ);
                    if (cross_corr > 0)
                    {
                        energy_basis = Inlines.silk_inner_prod_self(basis, basis_ptr, SF_LENGTH_8KHZ);
                        Inlines.MatrixSet(C, k, d - (MIN_LAG_8KHZ - 2), CSTRIDE_8KHZ,
                                          (short)Inlines.silk_DIV32_varQ(cross_corr,
                                                                         Inlines.silk_ADD32(energy_target,
                                                                                            energy_basis),
                                                                         13 + 1));                      /* Q13 */
                    }
                    else
                    {
                        Inlines.MatrixSet <short>(C, k, d - (MIN_LAG_8KHZ - 2), CSTRIDE_8KHZ, 0);
                    }
                }
                target_ptr += SF_LENGTH_8KHZ;
            }

            /* search over lag range and lags codebook */
            /* scale factor for lag codebook, as a function of center lag */

            CCmax   = int.MinValue;
            CCmax_b = int.MinValue;

            CBimax = 0;  /* To avoid returning undefined lag values */
            lag    = -1; /* To check if lag with strong enough correlation has been found */

            if (prevLag > 0)
            {
                if (Fs_kHz == 12)
                {
                    prevLag = Inlines.silk_DIV32_16(Inlines.silk_LSHIFT(prevLag, 1), 3);
                }
                else if (Fs_kHz == 16)
                {
                    prevLag = Inlines.silk_RSHIFT(prevLag, 1);
                }
                prevLag_log2_Q7 = Inlines.silk_lin2log((int)prevLag);
            }
            else
            {
                prevLag_log2_Q7 = 0;
            }
            Inlines.OpusAssert(search_thres2_Q13 == Inlines.silk_SAT16(search_thres2_Q13));
            /* Set up stage 2 codebook based on number of subframes */
            if (nb_subfr == SilkConstants.PE_MAX_NB_SUBFR)
            {
                Lag_CB_ptr = Tables.silk_CB_lags_stage2;
                if (Fs_kHz == 8 && complexity > SilkConstants.SILK_PE_MIN_COMPLEX)
                {
                    /* If input is 8 khz use a larger codebook here because it is last stage */
                    nb_cbk_search = SilkConstants.PE_NB_CBKS_STAGE2_EXT;
                }
                else
                {
                    nb_cbk_search = SilkConstants.PE_NB_CBKS_STAGE2;
                }
            }
            else
            {
                Lag_CB_ptr    = Tables.silk_CB_lags_stage2_10_ms;
                nb_cbk_search = SilkConstants.PE_NB_CBKS_STAGE2_10MS;
            }

            for (k = 0; k < length_d_srch; k++)
            {
                d = d_srch[k];
                for (j = 0; j < nb_cbk_search; j++)
                {
                    CC[j] = 0;
                    for (i = 0; i < nb_subfr; i++)
                    {
                        int d_subfr;
                        /* Try all codebooks */
                        d_subfr = d + Lag_CB_ptr[i][j];
                        CC[j]   = CC[j]
                                  + (int)Inlines.MatrixGet(C, i,
                                                           d_subfr - (MIN_LAG_8KHZ - 2),
                                                           CSTRIDE_8KHZ);
                    }
                }
                /* Find best codebook */
                CCmax_new  = int.MinValue;
                CBimax_new = 0;
                for (i = 0; i < nb_cbk_search; i++)
                {
                    if (CC[i] > CCmax_new)
                    {
                        CCmax_new  = CC[i];
                        CBimax_new = i;
                    }
                }

                /* Bias towards shorter lags */
                lag_log2_Q7 = Inlines.silk_lin2log(d); /* Q7 */
                Inlines.OpusAssert(lag_log2_Q7 == Inlines.silk_SAT16(lag_log2_Q7));
                Inlines.OpusAssert(nb_subfr * ((int)((SilkConstants.PE_SHORTLAG_BIAS) * ((long)1 << (13)) + 0.5)) /*Inlines.SILK_CONST(SilkConstants.PE_SHORTLAG_BIAS, 13)*/ == Inlines.silk_SAT16(nb_subfr * ((int)((SilkConstants.PE_SHORTLAG_BIAS) * ((long)1 << (13)) + 0.5)) /*Inlines.SILK_CONST(SilkConstants.PE_SHORTLAG_BIAS, 13)*/));
                CCmax_new_b = CCmax_new - Inlines.silk_RSHIFT(Inlines.silk_SMULBB(nb_subfr * ((int)((SilkConstants.PE_SHORTLAG_BIAS) * ((long)1 << (13)) + 0.5)) /*Inlines.SILK_CONST(SilkConstants.PE_SHORTLAG_BIAS, 13)*/, lag_log2_Q7), 7); /* Q13 */

                /* Bias towards previous lag */
                Inlines.OpusAssert(nb_subfr * ((int)((SilkConstants.PE_PREVLAG_BIAS) * ((long)1 << (13)) + 0.5)) /*Inlines.SILK_CONST(SilkConstants.PE_PREVLAG_BIAS, 13)*/ == Inlines.silk_SAT16(nb_subfr * ((int)((SilkConstants.PE_PREVLAG_BIAS) * ((long)1 << (13)) + 0.5)) /*Inlines.SILK_CONST(SilkConstants.PE_PREVLAG_BIAS, 13)*/));
                if (prevLag > 0)
                {
                    delta_lag_log2_sqr_Q7 = lag_log2_Q7 - prevLag_log2_Q7;
                    Inlines.OpusAssert(delta_lag_log2_sqr_Q7 == Inlines.silk_SAT16(delta_lag_log2_sqr_Q7));
                    delta_lag_log2_sqr_Q7 = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(delta_lag_log2_sqr_Q7, delta_lag_log2_sqr_Q7), 7);
                    prev_lag_bias_Q13     = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(nb_subfr * ((int)((SilkConstants.PE_PREVLAG_BIAS) * ((long)1 << (13)) + 0.5)) /*Inlines.SILK_CONST(SilkConstants.PE_PREVLAG_BIAS, 13)*/, LTPCorr_Q15.Val), 15); /* Q13 */
                    prev_lag_bias_Q13     = Inlines.silk_DIV32(Inlines.silk_MUL(prev_lag_bias_Q13, delta_lag_log2_sqr_Q7), delta_lag_log2_sqr_Q7 + ((int)((0.5f) * ((long)1 << (7)) + 0.5)) /*Inlines.SILK_CONST(0.5f, 7)*/);
                    CCmax_new_b          -= prev_lag_bias_Q13;                                                                                                                                                                                      /* Q13 */
                }

                if (CCmax_new_b > CCmax_b &&                                        /* Find maximum biased correlation                  */
                    CCmax_new > Inlines.silk_SMULBB(nb_subfr, search_thres2_Q13) && /* Correlation needs to be high enough to be voiced */
                    Tables.silk_CB_lags_stage2[0][CBimax_new] <= MIN_LAG_8KHZ       /* Lag must be in range                             */
                    )
                {
                    CCmax_b = CCmax_new_b;
                    CCmax   = CCmax_new;
                    lag     = d;
                    CBimax  = CBimax_new;
                }
            }

            if (lag == -1)
            {
                /* No suitable candidate found */
                Arrays.MemSetInt(pitch_out, 0, nb_subfr);
                LTPCorr_Q15.Val  = 0;
                lagIndex.Val     = 0;
                contourIndex.Val = 0;

                return(1);
            }

            /* Output normalized correlation */
            LTPCorr_Q15.Val = (int)Inlines.silk_LSHIFT(Inlines.silk_DIV32_16(CCmax, nb_subfr), 2);
            Inlines.OpusAssert(LTPCorr_Q15.Val >= 0);

            if (Fs_kHz > 8)
            {
                short[] scratch_mem;
                /***************************************************************************/
                /* Scale input signal down to avoid correlations measures from overflowing */
                /***************************************************************************/
                /* find scaling as max scaling for each subframe */
                SumSqrShift.silk_sum_sqr_shift(out energy, out shift, frame, frame_length);
                if (shift > 0)
                {
                    scratch_mem = new short[frame_length];
                    /* Move signal to scratch mem because the input signal should be unchanged */
                    shift = Inlines.silk_RSHIFT(shift, 1);
                    for (i = 0; i < frame_length; i++)
                    {
                        scratch_mem[i] = Inlines.silk_RSHIFT16(frame[i], shift);
                    }
                    input_frame_ptr = scratch_mem;
                }
                else
                {
                    input_frame_ptr = frame;
                }

                /* Search in original signal */
                CBimax_old = CBimax;
                /* Compensate for decimation */
                Inlines.OpusAssert(lag == Inlines.silk_SAT16(lag));
                if (Fs_kHz == 12)
                {
                    lag = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(lag, 3), 1);
                }
                else if (Fs_kHz == 16)
                {
                    lag = Inlines.silk_LSHIFT(lag, 1);
                }
                else
                {
                    lag = Inlines.silk_SMULBB(lag, 3);
                }

                lag       = Inlines.silk_LIMIT_int(lag, min_lag, max_lag);
                start_lag = Inlines.silk_max_int(lag - 2, min_lag);
                end_lag   = Inlines.silk_min_int(lag + 2, max_lag);
                lag_new   = lag;                                 /* to avoid undefined lag */
                CBimax    = 0;                                   /* to avoid undefined lag */

                CCmax = int.MinValue;
                /* pitch lags according to second stage */
                for (k = 0; k < nb_subfr; k++)
                {
                    pitch_out[k] = lag + 2 * Tables.silk_CB_lags_stage2[k][CBimax_old];
                }

                /* Set up codebook parameters according to complexity setting and frame length */
                if (nb_subfr == SilkConstants.PE_MAX_NB_SUBFR)
                {
                    nb_cbk_search = (int)Tables.silk_nb_cbk_searchs_stage3[complexity];
                    Lag_CB_ptr    = Tables.silk_CB_lags_stage3;
                }
                else
                {
                    nb_cbk_search = SilkConstants.PE_NB_CBKS_STAGE3_10MS;
                    Lag_CB_ptr    = Tables.silk_CB_lags_stage3_10_ms;
                }

                /* Calculate the correlations and energies needed in stage 3 */
                energies_st3   = new silk_pe_stage3_vals[nb_subfr * nb_cbk_search];
                cross_corr_st3 = new silk_pe_stage3_vals[nb_subfr * nb_cbk_search];
                for (int c = 0; c < nb_subfr * nb_cbk_search; c++)
                {
                    energies_st3[c]   = new silk_pe_stage3_vals(); // fixme: these can be replaced with a linearized array probably, or at least a struct
                    cross_corr_st3[c] = new silk_pe_stage3_vals();
                }
                silk_P_Ana_calc_corr_st3(cross_corr_st3, input_frame_ptr, start_lag, sf_length, nb_subfr, complexity);
                silk_P_Ana_calc_energy_st3(energies_st3, input_frame_ptr, start_lag, sf_length, nb_subfr, complexity);

                lag_counter = 0;
                Inlines.OpusAssert(lag == Inlines.silk_SAT16(lag));
                contour_bias_Q15 = Inlines.silk_DIV32_16(((int)((SilkConstants.PE_FLATCONTOUR_BIAS) * ((long)1 << (15)) + 0.5)) /*Inlines.SILK_CONST(SilkConstants.PE_FLATCONTOUR_BIAS, 15)*/, lag);

                target        = input_frame_ptr;
                target_ptr    = SilkConstants.PE_LTP_MEM_LENGTH_MS * Fs_kHz;
                energy_target = Inlines.silk_ADD32(Inlines.silk_inner_prod_self(target, target_ptr, nb_subfr * sf_length), 1);
                for (d = start_lag; d <= end_lag; d++)
                {
                    for (j = 0; j < nb_cbk_search; j++)
                    {
                        cross_corr = 0;
                        energy     = energy_target;
                        for (k = 0; k < nb_subfr; k++)
                        {
                            cross_corr = Inlines.silk_ADD32(cross_corr,
                                                            Inlines.MatrixGet(cross_corr_st3, k, j,
                                                                              nb_cbk_search).Values[lag_counter]);
                            energy = Inlines.silk_ADD32(energy,
                                                        Inlines.MatrixGet(energies_st3, k, j,
                                                                          nb_cbk_search).Values[lag_counter]);
                            Inlines.OpusAssert(energy >= 0);
                        }
                        if (cross_corr > 0)
                        {
                            CCmax_new = Inlines.silk_DIV32_varQ(cross_corr, energy, 13 + 1);          /* Q13 */
                                                                                                      /* Reduce depending on flatness of contour */
                            diff = short.MaxValue - Inlines.silk_MUL(contour_bias_Q15, j);            /* Q15 */
                            Inlines.OpusAssert(diff == Inlines.silk_SAT16(diff));
                            CCmax_new = Inlines.silk_SMULWB(CCmax_new, diff);                         /* Q14 */
                        }
                        else
                        {
                            CCmax_new = 0;
                        }

                        if (CCmax_new > CCmax && (d + Tables.silk_CB_lags_stage3[0][j]) <= max_lag)
                        {
                            CCmax   = CCmax_new;
                            lag_new = d;
                            CBimax  = j;
                        }
                    }
                    lag_counter++;
                }

                for (k = 0; k < nb_subfr; k++)
                {
                    pitch_out[k] = lag_new + Lag_CB_ptr[k][CBimax];
                    pitch_out[k] = Inlines.silk_LIMIT(pitch_out[k], min_lag, SilkConstants.PE_MAX_LAG_MS * Fs_kHz);
                }
                lagIndex.Val     = (short)(lag_new - min_lag);
                contourIndex.Val = (sbyte)CBimax;
            }
            else          /* Fs_kHz == 8 */
                          /* Save Lags */
            {
                for (k = 0; k < nb_subfr; k++)
                {
                    pitch_out[k] = lag + Lag_CB_ptr[k][CBimax];
                    pitch_out[k] = Inlines.silk_LIMIT(pitch_out[k], MIN_LAG_8KHZ, SilkConstants.PE_MAX_LAG_MS * 8);
                }
                lagIndex.Val     = (short)(lag - MIN_LAG_8KHZ);
                contourIndex.Val = (sbyte)CBimax;
            }
            Inlines.OpusAssert(lagIndex.Val >= 0);
            /* return as voiced */

            return(0);
        }
        /**************************************************************/
        /* Compute noise shaping coefficients and initial gain values */
        /**************************************************************/
        internal static void silk_noise_shape_analysis(
            SilkChannelEncoder psEnc,                      /* I/O  Encoder state FIX                                                           */
            SilkEncoderControl psEncCtrl,                  /* I/O  Encoder control FIX                                                         */
            short[] pitch_res,                             /* I    LPC residual from pitch analysis                                            */
            int pitch_res_ptr,
            short[] x,                                     /* I    Input signal [ frame_length + la_shape ]                                    */
            int x_ptr
            )
        {
            SilkShapeState psShapeSt = psEnc.sShape;
            int            k, i, nSamples, Qnrg, b_Q14, warping_Q16, scale = 0;
            int            SNR_adj_dB_Q7, HarmBoost_Q16, HarmShapeGain_Q16, Tilt_Q16, tmp32;
            int            nrg, pre_nrg_Q30, log_energy_Q7, log_energy_prev_Q7, energy_variation_Q7;
            int            delta_Q16, BWExp1_Q16, BWExp2_Q16, gain_mult_Q16, gain_add_Q16, strength_Q16, b_Q8;

            int[]   auto_corr     = new int[SilkConstants.MAX_SHAPE_LPC_ORDER + 1];
            int[]   refl_coef_Q16 = new int[SilkConstants.MAX_SHAPE_LPC_ORDER];
            int[]   AR1_Q24       = new int[SilkConstants.MAX_SHAPE_LPC_ORDER];
            int[]   AR2_Q24       = new int[SilkConstants.MAX_SHAPE_LPC_ORDER];
            short[] x_windowed;
            int     pitch_res_ptr2;
            int     x_ptr2;

            /* Point to start of first LPC analysis block */
            x_ptr2 = x_ptr - psEnc.la_shape;

            /****************/
            /* GAIN CONTROL */
            /****************/
            SNR_adj_dB_Q7 = psEnc.SNR_dB_Q7;

            /* Input quality is the average of the quality in the lowest two VAD bands */
            psEncCtrl.input_quality_Q14 = (int)Inlines.silk_RSHIFT((int)psEnc.input_quality_bands_Q15[0]
                                                                   + psEnc.input_quality_bands_Q15[1], 2);

            /* Coding quality level, between 0.0_Q0 and 1.0_Q0, but in Q14 */
            psEncCtrl.coding_quality_Q14 = Inlines.silk_RSHIFT(Sigmoid.silk_sigm_Q15(Inlines.silk_RSHIFT_ROUND(SNR_adj_dB_Q7 -
                                                                                                               ((int)((20.0f) * ((long)1 << (7)) + 0.5)) /*Inlines.SILK_CONST(20.0f, 7)*/, 4)), 1);

            /* Reduce coding SNR during low speech activity */
            if (psEnc.useCBR == 0)
            {
                b_Q8          = ((int)((1.0f) * ((long)1 << (8)) + 0.5)) /*Inlines.SILK_CONST(1.0f, 8)*/ - psEnc.speech_activity_Q8;
                b_Q8          = Inlines.silk_SMULWB(Inlines.silk_LSHIFT(b_Q8, 8), b_Q8);
                SNR_adj_dB_Q7 = Inlines.silk_SMLAWB(SNR_adj_dB_Q7,
                                                    Inlines.silk_SMULBB(((int)((0 - TuningParameters.BG_SNR_DECR_dB) * ((long)1 << (7)) + 0.5)) /*Inlines.SILK_CONST(0 - TuningParameters.BG_SNR_DECR_dB, 7)*/ >> (4 + 1), b_Q8), /* Q11*/
                                                    Inlines.silk_SMULWB(((int)((1.0f) * ((long)1 << (14)) + 0.5)) /*Inlines.SILK_CONST(1.0f, 14)*/ + psEncCtrl.input_quality_Q14, psEncCtrl.coding_quality_Q14));                 /* Q12*/
            }

            if (psEnc.indices.signalType == SilkConstants.TYPE_VOICED)
            {
                /* Reduce gains for periodic signals */
                SNR_adj_dB_Q7 = Inlines.silk_SMLAWB(SNR_adj_dB_Q7, ((int)((TuningParameters.HARM_SNR_INCR_dB) * ((long)1 << (8)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.HARM_SNR_INCR_dB, 8)*/, psEnc.LTPCorr_Q15);
            }
            else
            {
                /* For unvoiced signals and low-quality input, adjust the quality slower than SNR_dB setting */
                SNR_adj_dB_Q7 = Inlines.silk_SMLAWB(SNR_adj_dB_Q7,
                                                    Inlines.silk_SMLAWB(((int)((6.0f) * ((long)1 << (9)) + 0.5)) /*Inlines.SILK_CONST(6.0f, 9)*/, -((int)((0.4f) * ((long)1 << (18)) + 0.5)) /*Inlines.SILK_CONST(0.4f, 18)*/, psEnc.SNR_dB_Q7),
                                                    ((int)((1.0f) * ((long)1 << (14)) + 0.5)) /*Inlines.SILK_CONST(1.0f, 14)*/ - psEncCtrl.input_quality_Q14);
            }

            /*************************/
            /* SPARSENESS PROCESSING */
            /*************************/
            /* Set quantizer offset */
            if (psEnc.indices.signalType == SilkConstants.TYPE_VOICED)
            {
                /* Initially set to 0; may be overruled in process_gains(..) */
                psEnc.indices.quantOffsetType = 0;
                psEncCtrl.sparseness_Q8       = 0;
            }
            else
            {
                /* Sparseness measure, based on relative fluctuations of energy per 2 milliseconds */
                nSamples            = Inlines.silk_LSHIFT(psEnc.fs_kHz, 1);
                energy_variation_Q7 = 0;
                log_energy_prev_Q7  = 0;
                pitch_res_ptr2      = pitch_res_ptr;
                for (k = 0; k < Inlines.silk_SMULBB(SilkConstants.SUB_FRAME_LENGTH_MS, psEnc.nb_subfr) / 2; k++)
                {
                    SumSqrShift.silk_sum_sqr_shift(out nrg, out scale, pitch_res, pitch_res_ptr2, nSamples);
                    nrg += Inlines.silk_RSHIFT(nSamples, scale);           /* Q(-scale)*/

                    log_energy_Q7 = Inlines.silk_lin2log(nrg);
                    if (k > 0)
                    {
                        energy_variation_Q7 += Inlines.silk_abs(log_energy_Q7 - log_energy_prev_Q7);
                    }
                    log_energy_prev_Q7 = log_energy_Q7;
                    pitch_res_ptr2    += nSamples;
                }

                psEncCtrl.sparseness_Q8 = Inlines.silk_RSHIFT(Sigmoid.silk_sigm_Q15(Inlines.silk_SMULWB(energy_variation_Q7 -
                                                                                                        ((int)((5.0f) * ((long)1 << (7)) + 0.5)) /*Inlines.SILK_CONST(5.0f, 7)*/, ((int)((0.1f) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(0.1f, 16)*/)), 7);

                /* Set quantization offset depending on sparseness measure */
                if (psEncCtrl.sparseness_Q8 > ((int)((TuningParameters.SPARSENESS_THRESHOLD_QNT_OFFSET) * ((long)1 << (8)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.SPARSENESS_THRESHOLD_QNT_OFFSET, 8)*/)
                {
                    psEnc.indices.quantOffsetType = 0;
                }
                else
                {
                    psEnc.indices.quantOffsetType = 1;
                }

                /* Increase coding SNR for sparse signals */
                SNR_adj_dB_Q7 = Inlines.silk_SMLAWB(SNR_adj_dB_Q7, ((int)((TuningParameters.SPARSE_SNR_INCR_dB) * ((long)1 << (15)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.SPARSE_SNR_INCR_dB, 15)*/, psEncCtrl.sparseness_Q8 - ((int)((0.5f) * ((long)1 << (8)) + 0.5)) /*Inlines.SILK_CONST(0.5f, 8)*/);
            }

            /*******************************/
            /* Control bandwidth expansion */
            /*******************************/
            /* More BWE for signals with high prediction gain */
            strength_Q16 = Inlines.silk_SMULWB(psEncCtrl.predGain_Q16, ((int)((TuningParameters.FIND_PITCH_WHITE_NOISE_FRACTION) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.FIND_PITCH_WHITE_NOISE_FRACTION, 16)*/);
            BWExp1_Q16   = BWExp2_Q16 = Inlines.silk_DIV32_varQ(((int)((TuningParameters.BANDWIDTH_EXPANSION) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.BANDWIDTH_EXPANSION, 16)*/,
                                                                Inlines.silk_SMLAWW(((int)((1.0f) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(1.0f, 16)*/, strength_Q16, strength_Q16), 16);
            delta_Q16 = Inlines.silk_SMULWB(((int)((1.0f) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(1.0f, 16)*/ - Inlines.silk_SMULBB(3, psEncCtrl.coding_quality_Q14),
                                            ((int)((TuningParameters.LOW_RATE_BANDWIDTH_EXPANSION_DELTA) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.LOW_RATE_BANDWIDTH_EXPANSION_DELTA, 16)*/);
            BWExp1_Q16 = Inlines.silk_SUB32(BWExp1_Q16, delta_Q16);
            BWExp2_Q16 = Inlines.silk_ADD32(BWExp2_Q16, delta_Q16);
            /* BWExp1 will be applied after BWExp2, so make it relative */
            BWExp1_Q16 = Inlines.silk_DIV32_16(Inlines.silk_LSHIFT(BWExp1_Q16, 14), Inlines.silk_RSHIFT(BWExp2_Q16, 2));

            if (psEnc.warping_Q16 > 0)
            {
                /* Slightly more warping in analysis will move quantization noise up in frequency, where it's better masked */
                warping_Q16 = Inlines.silk_SMLAWB(psEnc.warping_Q16, (int)psEncCtrl.coding_quality_Q14, ((int)((0.01f) * ((long)1 << (18)) + 0.5)) /*Inlines.SILK_CONST(0.01f, 18)*/);
            }
            else
            {
                warping_Q16 = 0;
            }

            /********************************************/
            /* Compute noise shaping AR coefs and gains */
            /********************************************/
            x_windowed = new short[psEnc.shapeWinLength];
            for (k = 0; k < psEnc.nb_subfr; k++)
            {
                /* Apply window: sine slope followed by flat part followed by cosine slope */
                int shift, slope_part, flat_part;
                flat_part  = psEnc.fs_kHz * 3;
                slope_part = Inlines.silk_RSHIFT(psEnc.shapeWinLength - flat_part, 1);

                ApplySineWindow.silk_apply_sine_window(x_windowed, 0, x, x_ptr2, 1, slope_part);
                shift = slope_part;
                Array.Copy(x, x_ptr2 + shift, x_windowed, shift, flat_part);
                shift += flat_part;
                ApplySineWindow.silk_apply_sine_window(x_windowed, shift, x, x_ptr2 + shift, 2, slope_part);

                /* Update pointer: next LPC analysis block */
                x_ptr2 += psEnc.subfr_length;
                BoxedValueInt scale_boxed = new BoxedValueInt(scale);
                if (psEnc.warping_Q16 > 0)
                {
                    /* Calculate warped auto correlation */
                    Autocorrelation.silk_warped_autocorrelation(auto_corr, scale_boxed, x_windowed, warping_Q16, psEnc.shapeWinLength, psEnc.shapingLPCOrder);
                }
                else
                {
                    /* Calculate regular auto correlation */
                    Autocorrelation.silk_autocorr(auto_corr, scale_boxed, x_windowed, psEnc.shapeWinLength, psEnc.shapingLPCOrder + 1);
                }
                scale = scale_boxed.Val;

                /* Add white noise, as a fraction of energy */
                auto_corr[0] = Inlines.silk_ADD32(auto_corr[0], Inlines.silk_max_32(Inlines.silk_SMULWB(Inlines.silk_RSHIFT(auto_corr[0], 4),
                                                                                                        ((int)((TuningParameters.SHAPE_WHITE_NOISE_FRACTION) * ((long)1 << (20)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.SHAPE_WHITE_NOISE_FRACTION, 20)*/), 1));

                /* Calculate the reflection coefficients using schur */
                nrg = Schur.silk_schur64(refl_coef_Q16, auto_corr, psEnc.shapingLPCOrder);
                Inlines.OpusAssert(nrg >= 0);

                /* Convert reflection coefficients to prediction coefficients */
                K2A.silk_k2a_Q16(AR2_Q24, refl_coef_Q16, psEnc.shapingLPCOrder);

                Qnrg = -scale;          /* range: -12...30*/
                Inlines.OpusAssert(Qnrg >= -12);
                Inlines.OpusAssert(Qnrg <= 30);

                /* Make sure that Qnrg is an even number */
                if ((Qnrg & 1) != 0)
                {
                    Qnrg -= 1;
                    nrg >>= 1;
                }

                tmp32  = Inlines.silk_SQRT_APPROX(nrg);
                Qnrg >>= 1;             /* range: -6...15*/

                psEncCtrl.Gains_Q16[k] = Inlines.silk_LSHIFT_SAT32(tmp32, 16 - Qnrg);

                if (psEnc.warping_Q16 > 0)
                {
                    /* Adjust gain for warping */
                    gain_mult_Q16 = warped_gain(AR2_Q24, warping_Q16, psEnc.shapingLPCOrder);
                    Inlines.OpusAssert(psEncCtrl.Gains_Q16[k] >= 0);
                    if (Inlines.silk_SMULWW(Inlines.silk_RSHIFT_ROUND(psEncCtrl.Gains_Q16[k], 1), gain_mult_Q16) >= (int.MaxValue >> 1))
                    {
                        psEncCtrl.Gains_Q16[k] = int.MaxValue;
                    }
                    else
                    {
                        psEncCtrl.Gains_Q16[k] = Inlines.silk_SMULWW(psEncCtrl.Gains_Q16[k], gain_mult_Q16);
                    }
                }

                /* Bandwidth expansion for synthesis filter shaping */
                BWExpander.silk_bwexpander_32(AR2_Q24, psEnc.shapingLPCOrder, BWExp2_Q16);

                /* Compute noise shaping filter coefficients */
                Array.Copy(AR2_Q24, AR1_Q24, psEnc.shapingLPCOrder);

                /* Bandwidth expansion for analysis filter shaping */
                Inlines.OpusAssert(BWExp1_Q16 <= ((int)((1.0f) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(1.0f, 16)*/);
                BWExpander.silk_bwexpander_32(AR1_Q24, psEnc.shapingLPCOrder, BWExp1_Q16);

                /* Ratio of prediction gains, in energy domain */
                pre_nrg_Q30 = LPCInversePredGain.silk_LPC_inverse_pred_gain_Q24(AR2_Q24, psEnc.shapingLPCOrder);
                nrg         = LPCInversePredGain.silk_LPC_inverse_pred_gain_Q24(AR1_Q24, psEnc.shapingLPCOrder);

                /*psEncCtrl.GainsPre[ k ] = 1.0f - 0.7f * ( 1.0f - pre_nrg / nrg ) = 0.3f + 0.7f * pre_nrg / nrg;*/
                pre_nrg_Q30 = Inlines.silk_LSHIFT32(Inlines.silk_SMULWB(pre_nrg_Q30, ((int)((0.7f) * ((long)1 << (15)) + 0.5)) /*Inlines.SILK_CONST(0.7f, 15)*/), 1);
                psEncCtrl.GainsPre_Q14[k] = (int)((int)((0.3f) * ((long)1 << (14)) + 0.5)) /*Inlines.SILK_CONST(0.3f, 14)*/ + Inlines.silk_DIV32_varQ(pre_nrg_Q30, nrg, 14);

                /* Convert to monic warped prediction coefficients and limit absolute values */
                limit_warped_coefs(AR2_Q24, AR1_Q24, warping_Q16, ((int)((3.999f) * ((long)1 << (24)) + 0.5)) /*Inlines.SILK_CONST(3.999f, 24)*/, psEnc.shapingLPCOrder);

                /* Convert from Q24 to Q13 and store in int16 */
                for (i = 0; i < psEnc.shapingLPCOrder; i++)
                {
                    psEncCtrl.AR1_Q13[k * SilkConstants.MAX_SHAPE_LPC_ORDER + i] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(AR1_Q24[i], 11));
                    psEncCtrl.AR2_Q13[k * SilkConstants.MAX_SHAPE_LPC_ORDER + i] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(AR2_Q24[i], 11));
                }
            }

            /*****************/
            /* Gain tweaking */
            /*****************/
            /* Increase gains during low speech activity and put lower limit on gains */
            gain_mult_Q16 = Inlines.silk_log2lin(-Inlines.silk_SMLAWB(-((int)((16.0f) * ((long)1 << (7)) + 0.5)) /*Inlines.SILK_CONST(16.0f, 7)*/, SNR_adj_dB_Q7, ((int)((0.16f) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(0.16f, 16)*/));
            gain_add_Q16  = Inlines.silk_log2lin(Inlines.silk_SMLAWB(((int)((16.0f) * ((long)1 << (7)) + 0.5)) /*Inlines.SILK_CONST(16.0f, 7)*/, ((int)((SilkConstants.MIN_QGAIN_DB) * ((long)1 << (7)) + 0.5)) /*Inlines.SILK_CONST(SilkConstants.MIN_QGAIN_DB, 7)*/, ((int)((0.16f) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(0.16f, 16)*/));
            Inlines.OpusAssert(gain_mult_Q16 > 0);
            for (k = 0; k < psEnc.nb_subfr; k++)
            {
                psEncCtrl.Gains_Q16[k] = Inlines.silk_SMULWW(psEncCtrl.Gains_Q16[k], gain_mult_Q16);
                Inlines.OpusAssert(psEncCtrl.Gains_Q16[k] >= 0);
                psEncCtrl.Gains_Q16[k] = Inlines.silk_ADD_POS_SAT32(psEncCtrl.Gains_Q16[k], gain_add_Q16);
            }

            gain_mult_Q16 = ((int)((1.0f) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(1.0f, 16)*/ + Inlines.silk_RSHIFT_ROUND(Inlines.silk_MLA(((int)((TuningParameters.INPUT_TILT) * ((long)1 << (26)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.INPUT_TILT, 26)*/,
                                                                                                                                                    psEncCtrl.coding_quality_Q14, ((int)((TuningParameters.HIGH_RATE_INPUT_TILT) * ((long)1 << (12)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.HIGH_RATE_INPUT_TILT, 12)*/), 10);
            for (k = 0; k < psEnc.nb_subfr; k++)
            {
                psEncCtrl.GainsPre_Q14[k] = Inlines.silk_SMULWB(gain_mult_Q16, psEncCtrl.GainsPre_Q14[k]);
            }

            /************************************************/
            /* Control low-frequency shaping and noise tilt */
            /************************************************/
            /* Less low frequency shaping for noisy inputs */
            strength_Q16 = Inlines.silk_MUL(((int)((TuningParameters.LOW_FREQ_SHAPING) * ((long)1 << (4)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.LOW_FREQ_SHAPING, 4)*/, Inlines.silk_SMLAWB(((int)((1.0f) * ((long)1 << (12)) + 0.5)) /*Inlines.SILK_CONST(1.0f, 12)*/,
                                                                                                                                                                                                    ((int)((TuningParameters.LOW_QUALITY_LOW_FREQ_SHAPING_DECR) * ((long)1 << (13)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.LOW_QUALITY_LOW_FREQ_SHAPING_DECR, 13)*/, psEnc.input_quality_bands_Q15[0] - ((int)((1.0f) * ((long)1 << (15)) + 0.5)) /*Inlines.SILK_CONST(1.0f, 15)*/));
            strength_Q16 = Inlines.silk_RSHIFT(Inlines.silk_MUL(strength_Q16, psEnc.speech_activity_Q8), 8);
            if (psEnc.indices.signalType == SilkConstants.TYPE_VOICED)
            {
                /* Reduce low frequencies quantization noise for periodic signals, depending on pitch lag */
                /*f = 400; freqz([1, -0.98 + 2e-4 * f], [1, -0.97 + 7e-4 * f], 2^12, Fs); axis([0, 1000, -10, 1])*/
                int fs_kHz_inv = Inlines.silk_DIV32_16(((int)((0.2f) * ((long)1 << (14)) + 0.5)) /*Inlines.SILK_CONST(0.2f, 14)*/, psEnc.fs_kHz);
                for (k = 0; k < psEnc.nb_subfr; k++)
                {
                    b_Q14 = fs_kHz_inv + Inlines.silk_DIV32_16(((int)((3.0f) * ((long)1 << (14)) + 0.5)) /*Inlines.SILK_CONST(3.0f, 14)*/, psEncCtrl.pitchL[k]);
                    /* Pack two coefficients in one int32 */
                    psEncCtrl.LF_shp_Q14[k]  = Inlines.silk_LSHIFT(((int)((1.0f) * ((long)1 << (14)) + 0.5)) /*Inlines.SILK_CONST(1.0f, 14)*/ - b_Q14 - Inlines.silk_SMULWB(strength_Q16, b_Q14), 16);
                    psEncCtrl.LF_shp_Q14[k] |= (b_Q14 - ((int)((1.0f) * ((long)1 << (14)) + 0.5)) /*Inlines.SILK_CONST(1.0f, 14)*/) & 0xFFFF;                                                                                                              // opus bug: again, cast to ushort was done here where bitwise masking was intended
                }
                Inlines.OpusAssert(((int)((TuningParameters.HARM_HP_NOISE_COEF) * ((long)1 << (24)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.HARM_HP_NOISE_COEF, 24)*/ < ((int)((0.5f) * ((long)1 << (24)) + 0.5)) /*Inlines.SILK_CONST(0.5f, 24)*/); /* Guarantees that second argument to SMULWB() is within range of an short*/
                Tilt_Q16 = -((int)((TuningParameters.HP_NOISE_COEF) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.HP_NOISE_COEF, 16)*/ -
                           Inlines.silk_SMULWB(((int)((1.0f) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(1.0f, 16)*/ - ((int)((TuningParameters.HP_NOISE_COEF) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.HP_NOISE_COEF, 16)*/,
                                               Inlines.silk_SMULWB(((int)((TuningParameters.HARM_HP_NOISE_COEF) * ((long)1 << (24)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.HARM_HP_NOISE_COEF, 24)*/, psEnc.speech_activity_Q8));
            }
            else
            {
                b_Q14 = Inlines.silk_DIV32_16(21299, psEnc.fs_kHz); /* 1.3_Q0 = 21299_Q14*/
                /* Pack two coefficients in one int32 */
                psEncCtrl.LF_shp_Q14[0] = Inlines.silk_LSHIFT(((int)((1.0f) * ((long)1 << (14)) + 0.5)) /*Inlines.SILK_CONST(1.0f, 14)*/ - b_Q14 -
                                                              Inlines.silk_SMULWB(strength_Q16, Inlines.silk_SMULWB(((int)((0.6f) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(0.6f, 16)*/, b_Q14)), 16);
                psEncCtrl.LF_shp_Q14[0] |= (b_Q14 - ((int)((1.0f) * ((long)1 << (14)) + 0.5)) /*Inlines.SILK_CONST(1.0f, 14)*/) & 0xFFFF; // opus bug: cast to ushort is better expressed as a bitwise operator, otherwise runtime analysis might flag it as an overflow error
                for (k = 1; k < psEnc.nb_subfr; k++)
                {
                    psEncCtrl.LF_shp_Q14[k] = psEncCtrl.LF_shp_Q14[0];
                }
                Tilt_Q16 = -((int)((TuningParameters.HP_NOISE_COEF) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.HP_NOISE_COEF, 16)*/;
            }

            /****************************/
            /* HARMONIC SHAPING CONTROL */
            /****************************/
            /* Control boosting of harmonic frequencies */
            HarmBoost_Q16 = Inlines.silk_SMULWB(Inlines.silk_SMULWB(((int)((1.0f) * ((long)1 << (17)) + 0.5)) /*Inlines.SILK_CONST(1.0f, 17)*/ - Inlines.silk_LSHIFT(psEncCtrl.coding_quality_Q14, 3),
                                                                    psEnc.LTPCorr_Q15), ((int)((TuningParameters.LOW_RATE_HARMONIC_BOOST) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.LOW_RATE_HARMONIC_BOOST, 16)*/);

            /* More harmonic boost for noisy input signals */
            HarmBoost_Q16 = Inlines.silk_SMLAWB(HarmBoost_Q16,
                                                ((int)((1.0f) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(1.0f, 16)*/ - Inlines.silk_LSHIFT(psEncCtrl.input_quality_Q14, 2), ((int)((TuningParameters.LOW_INPUT_QUALITY_HARMONIC_BOOST) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.LOW_INPUT_QUALITY_HARMONIC_BOOST, 16)*/);

            if (SilkConstants.USE_HARM_SHAPING != 0 && psEnc.indices.signalType == SilkConstants.TYPE_VOICED)
            {
                /* More harmonic noise shaping for high bitrates or noisy input */
                HarmShapeGain_Q16 = Inlines.silk_SMLAWB(((int)((TuningParameters.HARMONIC_SHAPING) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.HARMONIC_SHAPING, 16)*/,
                                                        ((int)((1.0f) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(1.0f, 16)*/ - Inlines.silk_SMULWB(((int)((1.0f) * ((long)1 << (18)) + 0.5)) /*Inlines.SILK_CONST(1.0f, 18)*/ - Inlines.silk_LSHIFT(psEncCtrl.coding_quality_Q14, 4),
                                                                                                                                                         psEncCtrl.input_quality_Q14), ((int)((TuningParameters.HIGH_RATE_OR_LOW_QUALITY_HARMONIC_SHAPING) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.HIGH_RATE_OR_LOW_QUALITY_HARMONIC_SHAPING, 16)*/);

                /* Less harmonic noise shaping for less periodic signals */
                HarmShapeGain_Q16 = Inlines.silk_SMULWB(Inlines.silk_LSHIFT(HarmShapeGain_Q16, 1),
                                                        Inlines.silk_SQRT_APPROX(Inlines.silk_LSHIFT(psEnc.LTPCorr_Q15, 15)));
            }
            else
            {
                HarmShapeGain_Q16 = 0;
            }

            /*************************/
            /* Smooth over subframes */
            /*************************/
            for (k = 0; k < SilkConstants.MAX_NB_SUBFR; k++)
            {
                psShapeSt.HarmBoost_smth_Q16 =
                    Inlines.silk_SMLAWB(psShapeSt.HarmBoost_smth_Q16, HarmBoost_Q16 - psShapeSt.HarmBoost_smth_Q16, ((int)((TuningParameters.SUBFR_SMTH_COEF) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.SUBFR_SMTH_COEF, 16)*/);
                psShapeSt.HarmShapeGain_smth_Q16 =
                    Inlines.silk_SMLAWB(psShapeSt.HarmShapeGain_smth_Q16, HarmShapeGain_Q16 - psShapeSt.HarmShapeGain_smth_Q16, ((int)((TuningParameters.SUBFR_SMTH_COEF) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.SUBFR_SMTH_COEF, 16)*/);
                psShapeSt.Tilt_smth_Q16 =
                    Inlines.silk_SMLAWB(psShapeSt.Tilt_smth_Q16, Tilt_Q16 - psShapeSt.Tilt_smth_Q16, ((int)((TuningParameters.SUBFR_SMTH_COEF) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.SUBFR_SMTH_COEF, 16)*/);

                psEncCtrl.HarmBoost_Q14[k]     = (int)Inlines.silk_RSHIFT_ROUND(psShapeSt.HarmBoost_smth_Q16, 2);
                psEncCtrl.HarmShapeGain_Q14[k] = (int)Inlines.silk_RSHIFT_ROUND(psShapeSt.HarmShapeGain_smth_Q16, 2);
                psEncCtrl.Tilt_Q14[k]          = (int)Inlines.silk_RSHIFT_ROUND(psShapeSt.Tilt_smth_Q16, 2);
            }
        }
Beispiel #9
0
        /* Glues concealed frames with new good received frames */
        internal static void silk_PLC_glue_frames(
            SilkChannelDecoder psDec, /* I/O decoder state        */
            short[] frame,            /* I/O signal               */
            int frame_ptr,
            int length                /* I length of signal       */
            )
        {
            int       i;
            int       energy_shift, energy;
            PLCStruct psPLC = psDec.sPLC;

            if (psDec.lossCnt != 0)
            {
                /* Calculate energy in concealed residual */
                SumSqrShift.silk_sum_sqr_shift(out psPLC.conc_energy, out psPLC.conc_energy_shift, frame, frame_ptr, length);

                psPLC.last_frame_lost = 1;
            }
            else
            {
                if (psDec.sPLC.last_frame_lost != 0)
                {
                    /* Calculate residual in decoded signal if last frame was lost */
                    SumSqrShift.silk_sum_sqr_shift(out energy, out energy_shift, frame, frame_ptr, length);

                    /* Normalize energies */
                    if (energy_shift > psPLC.conc_energy_shift)
                    {
                        psPLC.conc_energy = Inlines.silk_RSHIFT(psPLC.conc_energy, energy_shift - psPLC.conc_energy_shift);
                    }
                    else if (energy_shift < psPLC.conc_energy_shift)
                    {
                        energy = Inlines.silk_RSHIFT(energy, psPLC.conc_energy_shift - energy_shift);
                    }

                    /* Fade in the energy difference */
                    if (energy > psPLC.conc_energy)
                    {
                        int frac_Q24, LZ;
                        int gain_Q16, slope_Q16;

                        LZ = Inlines.silk_CLZ32(psPLC.conc_energy);
                        LZ = LZ - 1;
                        psPLC.conc_energy = Inlines.silk_LSHIFT(psPLC.conc_energy, LZ);
                        energy            = Inlines.silk_RSHIFT(energy, Inlines.silk_max_32(24 - LZ, 0));

                        frac_Q24 = Inlines.silk_DIV32(psPLC.conc_energy, Inlines.silk_max(energy, 1));

                        gain_Q16  = Inlines.silk_LSHIFT(Inlines.silk_SQRT_APPROX(frac_Q24), 4);
                        slope_Q16 = Inlines.silk_DIV32_16(((int)1 << 16) - gain_Q16, length);
                        /* Make slope 4x steeper to avoid missing onsets after DTX */
                        slope_Q16 = Inlines.silk_LSHIFT(slope_Q16, 2);

                        for (i = frame_ptr; i < frame_ptr + length; i++)
                        {
                            frame[i]  = (short)(Inlines.silk_SMULWB(gain_Q16, frame[i]));
                            gain_Q16 += slope_Q16;
                            if (gain_Q16 > (int)1 << 16)
                            {
                                break;
                            }
                        }
                    }
                }
                psPLC.last_frame_lost = 0;
            }
        }