Пример #1
0
        internal static void silk_PLC(
            SilkChannelDecoder psDec,     /* I/O Decoder state        */
            SilkDecoderControl psDecCtrl, /* I/O Decoder control      */
            short[] frame,                /* I/O  signal              */
            int frame_ptr,
            int lost                      /* I Loss flag              */
            )
        {
            /* PLC control function */
            if (psDec.fs_kHz != psDec.sPLC.fs_kHz)
            {
                silk_PLC_Reset(psDec);
                psDec.sPLC.fs_kHz = psDec.fs_kHz;
            }

            if (lost != 0)
            {
                /****************************/
                /* Generate Signal          */
                /****************************/
                silk_PLC_conceal(psDec, psDecCtrl, frame, frame_ptr);

                psDec.lossCnt++;
            }
            else
            {
                /****************************/
                /* Update state             */
                /****************************/
                silk_PLC_update(psDec, psDecCtrl);
            }
        }
Пример #2
0
        private static readonly short[] PLC_RAND_ATTENUATE_UV_Q15 = { 32440, 29491 }; /* 0.99, 0.9 */

        internal static void silk_PLC_Reset(
            SilkChannelDecoder psDec              /* I/O Decoder state        */
            )
        {
            psDec.sPLC.pitchL_Q8       = Inlines.silk_LSHIFT(psDec.frame_length, 8 - 1);
            psDec.sPLC.prevGain_Q16[0] = ((int)((1) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(1, 16)*/;
            psDec.sPLC.prevGain_Q16[1] = ((int)((1) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(1, 16)*/;
            psDec.sPLC.subfr_length    = 20;
            psDec.sPLC.nb_subfr        = 2;
        }
Пример #3
0
        /// <summary>
        /// Resets CNG state
        /// </summary>
        /// <param name="psDec">I/O  Decoder state</param>
        internal static void silk_CNG_Reset(SilkChannelDecoder psDec)
        {
            int i, NLSF_step_Q15, NLSF_acc_Q15;

            NLSF_step_Q15 = Inlines.silk_DIV32_16(short.MaxValue, (short)(psDec.LPC_order + 1));
            NLSF_acc_Q15  = 0;
            for (i = 0; i < psDec.LPC_order; i++)
            {
                NLSF_acc_Q15 += NLSF_step_Q15;
                psDec.sCNG.CNG_smth_NLSF_Q15[i] = (short)(NLSF_acc_Q15);
            }
            psDec.sCNG.CNG_smth_Gain_Q16 = 0;
            psDec.sCNG.rand_seed         = 3176576;
        }
Пример #4
0
        /**********************************************************/
        /* Core decoder. Performs inverse NSQ operation LTP + LPC */
        /**********************************************************/
        internal static void silk_decode_core(
            SilkChannelDecoder psDec,     /* I/O  Decoder state                               */
            SilkDecoderControl psDecCtrl, /* I    Decoder control                             */
            short[] xq,                   /* O    Decoded speech                              */
            int xq_ptr,
            short[] pulses                /* I    Pulse signal [MAX_FRAME_LENGTH]                               */
            )
        {
            int i, k, lag = 0, start_idx, sLTP_buf_idx, NLSF_interpolation_flag, signalType;

            short[] A_Q12;
            short[] B_Q14 = psDecCtrl.LTPCoef_Q14;
            int     B_Q14_ptr;
            int     pxq;

            short[] sLTP;
            int[]   sLTP_Q15;
            int     LTP_pred_Q13, LPC_pred_Q10, Gain_Q10, inv_gain_Q31, gain_adj_Q16, rand_seed, offset_Q10;
            int     pred_lag_ptr;
            int     pexc_Q14;

            int[] pres_Q14;
            int   pres_Q14_ptr;

            int[] res_Q14;
            int[] sLPC_Q14;

            Inlines.OpusAssert(psDec.prev_gain_Q16 != 0);

            sLTP     = new short[psDec.ltp_mem_length];
            sLTP_Q15 = new int[psDec.ltp_mem_length + psDec.frame_length];
            res_Q14  = new int[psDec.subfr_length];
            sLPC_Q14 = new int[psDec.subfr_length + SilkConstants.MAX_LPC_ORDER];

            offset_Q10 = Tables.silk_Quantization_Offsets_Q10[psDec.indices.signalType >> 1][psDec.indices.quantOffsetType];

            if (psDec.indices.NLSFInterpCoef_Q2 < 1 << 2)
            {
                NLSF_interpolation_flag = 1;
            }
            else
            {
                NLSF_interpolation_flag = 0;
            }

            /* Decode excitation */
            rand_seed = psDec.indices.Seed;
            for (i = 0; i < psDec.frame_length; i++)
            {
                rand_seed        = Inlines.silk_RAND(rand_seed);
                psDec.exc_Q14[i] = Inlines.silk_LSHIFT((int)pulses[i], 14);
                if (psDec.exc_Q14[i] > 0)
                {
                    psDec.exc_Q14[i] -= SilkConstants.QUANT_LEVEL_ADJUST_Q10 << 4;
                }
                else
                if (psDec.exc_Q14[i] < 0)
                {
                    psDec.exc_Q14[i] += SilkConstants.QUANT_LEVEL_ADJUST_Q10 << 4;
                }
                psDec.exc_Q14[i] += offset_Q10 << 4;
                if (rand_seed < 0)
                {
                    psDec.exc_Q14[i] = -psDec.exc_Q14[i];
                }

                rand_seed = Inlines.silk_ADD32_ovflw(rand_seed, pulses[i]);
            }

            /* Copy LPC state */
            Array.Copy(psDec.sLPC_Q14_buf, sLPC_Q14, SilkConstants.MAX_LPC_ORDER);

            pexc_Q14     = 0;
            pxq          = xq_ptr;
            sLTP_buf_idx = psDec.ltp_mem_length;
            /* Loop over subframes */
            for (k = 0; k < psDec.nb_subfr; k++)
            {
                pres_Q14     = res_Q14;
                pres_Q14_ptr = 0;
                A_Q12        = psDecCtrl.PredCoef_Q12[k >> 1];
                B_Q14_ptr    = k * SilkConstants.LTP_ORDER;
                signalType   = psDec.indices.signalType;

                Gain_Q10     = Inlines.silk_RSHIFT(psDecCtrl.Gains_Q16[k], 6);
                inv_gain_Q31 = Inlines.silk_INVERSE32_varQ(psDecCtrl.Gains_Q16[k], 47);

                /* Calculate gain adjustment factor */
                if (psDecCtrl.Gains_Q16[k] != psDec.prev_gain_Q16)
                {
                    gain_adj_Q16 = Inlines.silk_DIV32_varQ(psDec.prev_gain_Q16, psDecCtrl.Gains_Q16[k], 16);

                    /* Scale short term state */
                    for (i = 0; i < SilkConstants.MAX_LPC_ORDER; i++)
                    {
                        sLPC_Q14[i] = Inlines.silk_SMULWW(gain_adj_Q16, sLPC_Q14[i]);
                    }
                }
                else
                {
                    gain_adj_Q16 = (int)1 << 16;
                }

                /* Save inv_gain */
                Inlines.OpusAssert(inv_gain_Q31 != 0);
                psDec.prev_gain_Q16 = psDecCtrl.Gains_Q16[k];

                /* Avoid abrupt transition from voiced PLC to unvoiced normal decoding */
                if (psDec.lossCnt != 0 && psDec.prevSignalType == SilkConstants.TYPE_VOICED &&
                    psDec.indices.signalType != SilkConstants.TYPE_VOICED && k < SilkConstants.MAX_NB_SUBFR / 2)
                {
                    Arrays.MemSetWithOffset <short>(B_Q14, 0, B_Q14_ptr, SilkConstants.LTP_ORDER);
                    B_Q14[B_Q14_ptr + (SilkConstants.LTP_ORDER / 2)] = (short)(((int)((0.25f) * ((long)1 << (14)) + 0.5)) /*Inlines.SILK_CONST(0.25f, 14)*/);

                    signalType          = SilkConstants.TYPE_VOICED;
                    psDecCtrl.pitchL[k] = psDec.lagPrev;
                }

                if (signalType == SilkConstants.TYPE_VOICED)
                {
                    /* Voiced */
                    lag = psDecCtrl.pitchL[k];

                    /* Re-whitening */
                    if (k == 0 || (k == 2 && (NLSF_interpolation_flag != 0)))
                    {
                        /* Rewhiten with new A coefs */
                        start_idx = psDec.ltp_mem_length - lag - psDec.LPC_order - SilkConstants.LTP_ORDER / 2;
                        Inlines.OpusAssert(start_idx > 0);

                        if (k == 2)
                        {
                            Array.Copy(xq, xq_ptr, psDec.outBuf, psDec.ltp_mem_length, 2 * psDec.subfr_length);
                        }

                        Filters.silk_LPC_analysis_filter(sLTP, start_idx, psDec.outBuf, (start_idx + k * psDec.subfr_length),
                                                         A_Q12, 0, psDec.ltp_mem_length - start_idx, psDec.LPC_order);

                        /* After rewhitening the LTP state is unscaled */
                        if (k == 0)
                        {
                            /* Do LTP downscaling to reduce inter-packet dependency */
                            inv_gain_Q31 = Inlines.silk_LSHIFT(Inlines.silk_SMULWB(inv_gain_Q31, psDecCtrl.LTP_scale_Q14), 2);
                        }
                        for (i = 0; i < lag + SilkConstants.LTP_ORDER / 2; i++)
                        {
                            sLTP_Q15[sLTP_buf_idx - i - 1] = Inlines.silk_SMULWB(inv_gain_Q31, sLTP[psDec.ltp_mem_length - i - 1]);
                        }
                    }
                    else
                    {
                        /* Update LTP state when Gain changes */
                        if (gain_adj_Q16 != (int)1 << 16)
                        {
                            for (i = 0; i < lag + SilkConstants.LTP_ORDER / 2; i++)
                            {
                                sLTP_Q15[sLTP_buf_idx - i - 1] = Inlines.silk_SMULWW(gain_adj_Q16, sLTP_Q15[sLTP_buf_idx - i - 1]);
                            }
                        }
                    }
                }

                /* Long-term prediction */
                if (signalType == SilkConstants.TYPE_VOICED)
                {
                    /* Set up pointer */
                    pred_lag_ptr = sLTP_buf_idx - lag + SilkConstants.LTP_ORDER / 2;
                    for (i = 0; i < psDec.subfr_length; i++)
                    {
                        /* Unrolled loop */
                        /* Avoids introducing a bias because silk_SMLAWB() always rounds to -inf */
                        LTP_pred_Q13  = 2;
                        LTP_pred_Q13  = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr], B_Q14[B_Q14_ptr]);
                        LTP_pred_Q13  = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr - 1], B_Q14[B_Q14_ptr + 1]);
                        LTP_pred_Q13  = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr - 2], B_Q14[B_Q14_ptr + 2]);
                        LTP_pred_Q13  = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr - 3], B_Q14[B_Q14_ptr + 3]);
                        LTP_pred_Q13  = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr - 4], B_Q14[B_Q14_ptr + 4]);
                        pred_lag_ptr += 1;

                        /* Generate LPC excitation */
                        pres_Q14[pres_Q14_ptr + i] = Inlines.silk_ADD_LSHIFT32(psDec.exc_Q14[pexc_Q14 + i], LTP_pred_Q13, 1);

                        /* Update states */
                        sLTP_Q15[sLTP_buf_idx] = Inlines.silk_LSHIFT(pres_Q14[pres_Q14_ptr + i], 1);
                        sLTP_buf_idx++;
                    }
                }
                else
                {
                    pres_Q14     = psDec.exc_Q14;
                    pres_Q14_ptr = pexc_Q14;
                }

                for (i = 0; i < psDec.subfr_length; i++)
                {
                    /* Short-term prediction */
                    Inlines.OpusAssert(psDec.LPC_order == 10 || psDec.LPC_order == 16);
                    /* Avoids introducing a bias because silk_SMLAWB() always rounds to -inf */
                    LPC_pred_Q10 = Inlines.silk_RSHIFT(psDec.LPC_order, 1);
                    LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 1], A_Q12[0]);
                    LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 2], A_Q12[1]);
                    LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 3], A_Q12[2]);
                    LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 4], A_Q12[3]);
                    LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 5], A_Q12[4]);
                    LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 6], A_Q12[5]);
                    LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 7], A_Q12[6]);
                    LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 8], A_Q12[7]);
                    LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 9], A_Q12[8]);
                    LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 10], A_Q12[9]);
                    if (psDec.LPC_order == 16)
                    {
                        LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 11], A_Q12[10]);
                        LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 12], A_Q12[11]);
                        LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 13], A_Q12[12]);
                        LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 14], A_Q12[13]);
                        LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 15], A_Q12[14]);
                        LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 16], A_Q12[15]);
                    }

                    /* Add prediction to LPC excitation */
                    sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i] = Inlines.silk_ADD_LSHIFT32(pres_Q14[pres_Q14_ptr + i], LPC_pred_Q10, 4);

                    /* Scale with gain */
                    xq[pxq + i] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWW(sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i], Gain_Q10), 8));
                }

                /* DEBUG_STORE_DATA( dec.pcm, pxq, psDec.subfr_length * sizeof( short ) ) */

                /* Update LPC filter state */
                Array.Copy(sLPC_Q14, psDec.subfr_length, sLPC_Q14, 0, SilkConstants.MAX_LPC_ORDER);
                pexc_Q14 += psDec.subfr_length;
                pxq      += psDec.subfr_length;
            }

            /* Save LPC state */
            Array.Copy(sLPC_Q14, 0, psDec.sLPC_Q14_buf, 0, SilkConstants.MAX_LPC_ORDER);
        }
Пример #5
0
        /// <summary>
        /// Updates CNG estimate, and applies the CNG when packet was lost
        /// </summary>
        /// <param name="psDec">I/O  Decoder state</param>
        /// <param name="psDecCtrl">I/O  Decoder control</param>
        /// <param name="frame">I/O  Signal</param>
        /// <param name="length">I    Length of residual</param>
        internal static void silk_CNG(
            SilkChannelDecoder psDec,
            SilkDecoderControl psDecCtrl,
            short[] frame,
            int frame_ptr,
            int length)
        {
            int i, subfr;
            int sum_Q6, max_Gain_Q16, gain_Q16;

            short[]  A_Q12 = new short[psDec.LPC_order];
            CNGState psCNG = psDec.sCNG;

            if (psDec.fs_kHz != psCNG.fs_kHz)
            {
                /* Reset state */
                silk_CNG_Reset(psDec);

                psCNG.fs_kHz = psDec.fs_kHz;
            }

            if (psDec.lossCnt == 0 && psDec.prevSignalType == SilkConstants.TYPE_NO_VOICE_ACTIVITY)
            {
                /* Update CNG parameters */

                /* Smoothing of LSF's  */
                for (i = 0; i < psDec.LPC_order; i++)
                {
                    psCNG.CNG_smth_NLSF_Q15[i] += (short)(Inlines.silk_SMULWB((int)psDec.prevNLSF_Q15[i] - (int)psCNG.CNG_smth_NLSF_Q15[i], SilkConstants.CNG_NLSF_SMTH_Q16));
                }

                /* Find the subframe with the highest gain */
                max_Gain_Q16 = 0;
                subfr        = 0;
                for (i = 0; i < psDec.nb_subfr; i++)
                {
                    if (psDecCtrl.Gains_Q16[i] > max_Gain_Q16)
                    {
                        max_Gain_Q16 = psDecCtrl.Gains_Q16[i];
                        subfr        = i;
                    }
                }

                /* Update CNG excitation buffer with excitation from this subframe */
                Arrays.MemMoveInt(psCNG.CNG_exc_buf_Q14, 0, psDec.subfr_length, (psDec.nb_subfr - 1) * psDec.subfr_length);

                /* Smooth gains */
                for (i = 0; i < psDec.nb_subfr; i++)
                {
                    psCNG.CNG_smth_Gain_Q16 += Inlines.silk_SMULWB(psDecCtrl.Gains_Q16[i] - psCNG.CNG_smth_Gain_Q16, SilkConstants.CNG_GAIN_SMTH_Q16);
                }
            }

            /* Add CNG when packet is lost or during DTX */
            if (psDec.lossCnt != 0)
            {
                int[] CNG_sig_Q10 = new int[length + SilkConstants.MAX_LPC_ORDER];

                /* Generate CNG excitation */
                gain_Q16 = Inlines.silk_SMULWW(psDec.sPLC.randScale_Q14, psDec.sPLC.prevGain_Q16[1]);
                if (gain_Q16 >= (1 << 21) || psCNG.CNG_smth_Gain_Q16 > (1 << 23))
                {
                    gain_Q16 = Inlines.silk_SMULTT(gain_Q16, gain_Q16);
                    gain_Q16 = Inlines.silk_SUB_LSHIFT32(Inlines.silk_SMULTT(psCNG.CNG_smth_Gain_Q16, psCNG.CNG_smth_Gain_Q16), gain_Q16, 5);
                    gain_Q16 = Inlines.silk_LSHIFT32(Inlines.silk_SQRT_APPROX(gain_Q16), 16);
                }
                else
                {
                    gain_Q16 = Inlines.silk_SMULWW(gain_Q16, gain_Q16);
                    gain_Q16 = Inlines.silk_SUB_LSHIFT32(Inlines.silk_SMULWW(psCNG.CNG_smth_Gain_Q16, psCNG.CNG_smth_Gain_Q16), gain_Q16, 5);
                    gain_Q16 = Inlines.silk_LSHIFT32(Inlines.silk_SQRT_APPROX(gain_Q16), 8);
                }
                silk_CNG_exc(CNG_sig_Q10, SilkConstants.MAX_LPC_ORDER, psCNG.CNG_exc_buf_Q14, gain_Q16, length, ref psCNG.rand_seed);

                /* Convert CNG NLSF to filter representation */
                NLSF.silk_NLSF2A(A_Q12, psCNG.CNG_smth_NLSF_Q15, psDec.LPC_order);

                /* Generate CNG signal, by synthesis filtering */
                Array.Copy(psCNG.CNG_synth_state, CNG_sig_Q10, SilkConstants.MAX_LPC_ORDER);

                for (i = 0; i < length; i++)
                {
                    int lpci = SilkConstants.MAX_LPC_ORDER + i;
                    Inlines.OpusAssert(psDec.LPC_order == 10 || psDec.LPC_order == 16);
                    /* Avoids introducing a bias because silk_SMLAWB() always rounds to -inf */
                    sum_Q6 = Inlines.silk_RSHIFT(psDec.LPC_order, 1);
                    sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 1], A_Q12[0]);
                    sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 2], A_Q12[1]);
                    sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 3], A_Q12[2]);
                    sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 4], A_Q12[3]);
                    sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 5], A_Q12[4]);
                    sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 6], A_Q12[5]);
                    sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 7], A_Q12[6]);
                    sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 8], A_Q12[7]);
                    sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 9], A_Q12[8]);
                    sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 10], A_Q12[9]);

                    if (psDec.LPC_order == 16)
                    {
                        sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 11], A_Q12[10]);
                        sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 12], A_Q12[11]);
                        sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 13], A_Q12[12]);
                        sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 14], A_Q12[13]);
                        sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 15], A_Q12[14]);
                        sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 16], A_Q12[15]);
                    }

                    /* Update states */
                    CNG_sig_Q10[lpci] = Inlines.silk_ADD_LSHIFT(CNG_sig_Q10[lpci], sum_Q6, 4);

                    frame[frame_ptr + i] = Inlines.silk_ADD_SAT16(frame[frame_ptr + i], (short)(Inlines.silk_RSHIFT_ROUND(CNG_sig_Q10[lpci], 10)));
                }

                Array.Copy(CNG_sig_Q10, length, psCNG.CNG_synth_state, 0, SilkConstants.MAX_LPC_ORDER);
            }
            else
            {
                Arrays.MemSetInt(psCNG.CNG_synth_state, 0, psDec.LPC_order);
            }
        }
Пример #6
0
        /* Decode parameters from payload */
        internal static void silk_decode_parameters(
            SilkChannelDecoder psDec,           /* I/O  State                                       */
            SilkDecoderControl psDecCtrl,       /* I/O  Decoder control                             */
            int condCoding                      /* I    The type of conditional coding to use       */
            )
        {
            int i, k, Ix;

            short[]   pNLSF_Q15  = new short[psDec.LPC_order];
            short[]   pNLSF0_Q15 = new short[psDec.LPC_order];
            sbyte[][] cbk_ptr_Q7;

            /* Dequant Gains */
            BoxedValueSbyte boxedLastGainIndex = new BoxedValueSbyte(psDec.LastGainIndex);

            GainQuantization.silk_gains_dequant(psDecCtrl.Gains_Q16, psDec.indices.GainsIndices,
                                                boxedLastGainIndex, condCoding == SilkConstants.CODE_CONDITIONALLY ? 1 : 0, psDec.nb_subfr);
            psDec.LastGainIndex = boxedLastGainIndex.Val;

            /****************/
            /* Decode NLSFs */
            /****************/
            NLSF.silk_NLSF_decode(pNLSF_Q15, psDec.indices.NLSFIndices, psDec.psNLSF_CB);

            /* Convert NLSF parameters to AR prediction filter coefficients */
            NLSF.silk_NLSF2A(psDecCtrl.PredCoef_Q12[1], pNLSF_Q15, psDec.LPC_order);

            /* If just reset, e.g., because internal Fs changed, do not allow interpolation */
            /* improves the case of packet loss in the first frame after a switch           */
            if (psDec.first_frame_after_reset == 1)
            {
                psDec.indices.NLSFInterpCoef_Q2 = 4;
            }

            if (psDec.indices.NLSFInterpCoef_Q2 < 4)
            {
                /* Calculation of the interpolated NLSF0 vector from the interpolation factor, */
                /* the previous NLSF1, and the current NLSF1                                   */
                for (i = 0; i < psDec.LPC_order; i++)
                {
                    pNLSF0_Q15[i] = (short)(psDec.prevNLSF_Q15[i] + Inlines.silk_RSHIFT(Inlines.silk_MUL(psDec.indices.NLSFInterpCoef_Q2,
                                                                                                         pNLSF_Q15[i] - psDec.prevNLSF_Q15[i]), 2));
                }

                /* Convert NLSF parameters to AR prediction filter coefficients */
                NLSF.silk_NLSF2A(psDecCtrl.PredCoef_Q12[0], pNLSF0_Q15, psDec.LPC_order);
            }
            else
            {
                /* Copy LPC coefficients for first half from second half */
                Array.Copy(psDecCtrl.PredCoef_Q12[1], psDecCtrl.PredCoef_Q12[0], psDec.LPC_order);
            }

            Array.Copy(pNLSF_Q15, psDec.prevNLSF_Q15, psDec.LPC_order);

            /* After a packet loss do BWE of LPC coefs */
            if (psDec.lossCnt != 0)
            {
                BWExpander.silk_bwexpander(psDecCtrl.PredCoef_Q12[0], psDec.LPC_order, SilkConstants.BWE_AFTER_LOSS_Q16);
                BWExpander.silk_bwexpander(psDecCtrl.PredCoef_Q12[1], psDec.LPC_order, SilkConstants.BWE_AFTER_LOSS_Q16);
            }

            if (psDec.indices.signalType == SilkConstants.TYPE_VOICED)
            {
                /*********************/
                /* Decode pitch lags */
                /*********************/

                /* Decode pitch values */
                DecodePitch.silk_decode_pitch(psDec.indices.lagIndex, psDec.indices.contourIndex, psDecCtrl.pitchL, psDec.fs_kHz, psDec.nb_subfr);

                /* Decode Codebook Index */
                cbk_ptr_Q7 = Tables.silk_LTP_vq_ptrs_Q7[psDec.indices.PERIndex]; /* set pointer to start of codebook */

                for (k = 0; k < psDec.nb_subfr; k++)
                {
                    Ix = psDec.indices.LTPIndex[k];
                    for (i = 0; i < SilkConstants.LTP_ORDER; i++)
                    {
                        psDecCtrl.LTPCoef_Q14[k * SilkConstants.LTP_ORDER + i] = (short)(Inlines.silk_LSHIFT(cbk_ptr_Q7[Ix][i], 7));
                    }
                }

                /**********************/
                /* Decode LTP scaling */
                /**********************/
                Ix = psDec.indices.LTP_scaleIndex;
                psDecCtrl.LTP_scale_Q14 = Tables.silk_LTPScales_table_Q14[Ix];
            }
            else
            {
                Arrays.MemSetInt(psDecCtrl.pitchL, 0, psDec.nb_subfr);
                Arrays.MemSetShort(psDecCtrl.LTPCoef_Q14, 0, SilkConstants.LTP_ORDER * psDec.nb_subfr);
                psDec.indices.PERIndex  = 0;
                psDecCtrl.LTP_scale_Q14 = 0;
            }
        }
Пример #7
0
        /* Decode side-information parameters from payload */
        internal static void silk_decode_indices(
            SilkChannelDecoder psDec,           /* I/O  State                                       */
            EntropyCoder psRangeDec,            /* I/O  Compressor data structure                   */
            int FrameIndex,                     /* I    Frame number                                */
            int decode_LBRR,                    /* I    Flag indicating LBRR data is being decoded  */
            int condCoding                      /* I    The type of conditional coding to use       */
            )
        {
            int i, k, Ix;
            int decode_absolute_lagIndex, delta_lagIndex;

            short[] ec_ix   = new short[psDec.LPC_order];
            byte[]  pred_Q8 = new byte[psDec.LPC_order];

            /*******************************************/
            /* Decode signal type and quantizer offset */
            /*******************************************/
            if (decode_LBRR != 0 || psDec.VAD_flags[FrameIndex] != 0)
            {
                Ix = psRangeDec.dec_icdf(Tables.silk_type_offset_VAD_iCDF, 8) + 2;
            }
            else
            {
                Ix = psRangeDec.dec_icdf(Tables.silk_type_offset_no_VAD_iCDF, 8);
            }
            psDec.indices.signalType      = (sbyte)Inlines.silk_RSHIFT(Ix, 1);
            psDec.indices.quantOffsetType = (sbyte)(Ix & 1);

            /****************/
            /* Decode gains */
            /****************/
            /* First subframe */
            if (condCoding == SilkConstants.CODE_CONDITIONALLY)
            {
                /* Conditional coding */
                psDec.indices.GainsIndices[0] = (sbyte)psRangeDec.dec_icdf(Tables.silk_delta_gain_iCDF, 8);
            }
            else
            {
                /* Independent coding, in two stages: MSB bits followed by 3 LSBs */
                psDec.indices.GainsIndices[0]  = (sbyte)Inlines.silk_LSHIFT(psRangeDec.dec_icdf(Tables.silk_gain_iCDF[psDec.indices.signalType], 8), 3);
                psDec.indices.GainsIndices[0] += (sbyte)psRangeDec.dec_icdf(Tables.silk_uniform8_iCDF, 8);
            }

            /* Remaining subframes */
            for (i = 1; i < psDec.nb_subfr; i++)
            {
                psDec.indices.GainsIndices[i] = (sbyte)psRangeDec.dec_icdf(Tables.silk_delta_gain_iCDF, 8);
            }

            /**********************/
            /* Decode LSF Indices */
            /**********************/
            psDec.indices.NLSFIndices[0] = (sbyte)psRangeDec.dec_icdf(psDec.psNLSF_CB.CB1_iCDF, (psDec.indices.signalType >> 1) * psDec.psNLSF_CB.nVectors, 8);
            NLSF.silk_NLSF_unpack(ec_ix, pred_Q8, psDec.psNLSF_CB, psDec.indices.NLSFIndices[0]);
            Inlines.OpusAssert(psDec.psNLSF_CB.order == psDec.LPC_order);
            for (i = 0; i < psDec.psNLSF_CB.order; i++)
            {
                Ix = psRangeDec.dec_icdf(psDec.psNLSF_CB.ec_iCDF, (ec_ix[i]), 8);
                if (Ix == 0)
                {
                    Ix -= psRangeDec.dec_icdf(Tables.silk_NLSF_EXT_iCDF, 8);
                }
                else if (Ix == 2 * SilkConstants.NLSF_QUANT_MAX_AMPLITUDE)
                {
                    Ix += psRangeDec.dec_icdf(Tables.silk_NLSF_EXT_iCDF, 8);
                }
                psDec.indices.NLSFIndices[i + 1] = (sbyte)(Ix - SilkConstants.NLSF_QUANT_MAX_AMPLITUDE);
            }

            /* Decode LSF interpolation factor */
            if (psDec.nb_subfr == SilkConstants.MAX_NB_SUBFR)
            {
                psDec.indices.NLSFInterpCoef_Q2 = (sbyte)psRangeDec.dec_icdf(Tables.silk_NLSF_interpolation_factor_iCDF, 8);
            }
            else
            {
                psDec.indices.NLSFInterpCoef_Q2 = 4;
            }

            if (psDec.indices.signalType == SilkConstants.TYPE_VOICED)
            {
                /*********************/
                /* Decode pitch lags */
                /*********************/
                /* Get lag index */
                decode_absolute_lagIndex = 1;
                if (condCoding == SilkConstants.CODE_CONDITIONALLY && psDec.ec_prevSignalType == SilkConstants.TYPE_VOICED)
                {
                    /* Decode Delta index */
                    delta_lagIndex = (short)psRangeDec.dec_icdf(Tables.silk_pitch_delta_iCDF, 8);
                    if (delta_lagIndex > 0)
                    {
                        delta_lagIndex           = delta_lagIndex - 9;
                        psDec.indices.lagIndex   = (short)(psDec.ec_prevLagIndex + delta_lagIndex);
                        decode_absolute_lagIndex = 0;
                    }
                }
                if (decode_absolute_lagIndex != 0)
                {
                    /* Absolute decoding */
                    psDec.indices.lagIndex  = (short)(psRangeDec.dec_icdf(Tables.silk_pitch_lag_iCDF, 8) * Inlines.silk_RSHIFT(psDec.fs_kHz, 1));
                    psDec.indices.lagIndex += (short)psRangeDec.dec_icdf(psDec.pitch_lag_low_bits_iCDF, 8);
                }
                psDec.ec_prevLagIndex = psDec.indices.lagIndex;

                /* Get countour index */
                psDec.indices.contourIndex = (sbyte)psRangeDec.dec_icdf(psDec.pitch_contour_iCDF, 8);

                /********************/
                /* Decode LTP gains */
                /********************/
                /* Decode PERIndex value */
                psDec.indices.PERIndex = (sbyte)psRangeDec.dec_icdf(Tables.silk_LTP_per_index_iCDF, 8);

                for (k = 0; k < psDec.nb_subfr; k++)
                {
                    psDec.indices.LTPIndex[k] = (sbyte)psRangeDec.dec_icdf(Tables.silk_LTP_gain_iCDF_ptrs[psDec.indices.PERIndex], 8);
                }

                /**********************/
                /* Decode LTP scaling */
                /**********************/
                if (condCoding == SilkConstants.CODE_INDEPENDENTLY)
                {
                    psDec.indices.LTP_scaleIndex = (sbyte)psRangeDec.dec_icdf(Tables.silk_LTPscale_iCDF, 8);
                }
                else
                {
                    psDec.indices.LTP_scaleIndex = 0;
                }
            }
            psDec.ec_prevSignalType = psDec.indices.signalType;

            /***************/
            /* Decode seed */
            /***************/
            psDec.indices.Seed = (sbyte)psRangeDec.dec_icdf(Tables.silk_uniform4_iCDF, 8);
        }
Пример #8
0
        /**************************************************/
        /* Update state of PLC                            */
        /**************************************************/
        internal static void silk_PLC_update(
            SilkChannelDecoder psDec,             /* I/O Decoder state        */
            SilkDecoderControl psDecCtrl          /* I/O Decoder control      */
            )
        {
            int       LTP_Gain_Q14, temp_LTP_Gain_Q14;
            int       i, j;
            PLCStruct psPLC = psDec.sPLC; // [porting note] pointer on the stack

            /* Update parameters used in case of packet loss */
            psDec.prevSignalType = psDec.indices.signalType;
            LTP_Gain_Q14         = 0;
            if (psDec.indices.signalType == SilkConstants.TYPE_VOICED)
            {
                /* Find the parameters for the last subframe which contains a pitch pulse */
                for (j = 0; j *psDec.subfr_length < psDecCtrl.pitchL[psDec.nb_subfr - 1]; j++)
                {
                    if (j == psDec.nb_subfr)
                    {
                        break;
                    }
                    temp_LTP_Gain_Q14 = 0;
                    for (i = 0; i < SilkConstants.LTP_ORDER; i++)
                    {
                        temp_LTP_Gain_Q14 += psDecCtrl.LTPCoef_Q14[(psDec.nb_subfr - 1 - j) * SilkConstants.LTP_ORDER + i];
                    }
                    if (temp_LTP_Gain_Q14 > LTP_Gain_Q14)
                    {
                        LTP_Gain_Q14 = temp_LTP_Gain_Q14;

                        Array.Copy(psDecCtrl.LTPCoef_Q14, Inlines.silk_SMULBB(psDec.nb_subfr - 1 - j, SilkConstants.LTP_ORDER), psPLC.LTPCoef_Q14, 0, SilkConstants.LTP_ORDER);

                        psPLC.pitchL_Q8 = Inlines.silk_LSHIFT(psDecCtrl.pitchL[psDec.nb_subfr - 1 - j], 8);
                    }
                }

                Arrays.MemSetShort(psPLC.LTPCoef_Q14, 0, SilkConstants.LTP_ORDER);
                psPLC.LTPCoef_Q14[SilkConstants.LTP_ORDER / 2] = (short)(LTP_Gain_Q14);

                /* Limit LT coefs */
                if (LTP_Gain_Q14 < SilkConstants.V_PITCH_GAIN_START_MIN_Q14)
                {
                    int scale_Q10;
                    int tmp;

                    tmp       = Inlines.silk_LSHIFT(SilkConstants.V_PITCH_GAIN_START_MIN_Q14, 10);
                    scale_Q10 = Inlines.silk_DIV32(tmp, Inlines.silk_max(LTP_Gain_Q14, 1));
                    for (i = 0; i < SilkConstants.LTP_ORDER; i++)
                    {
                        psPLC.LTPCoef_Q14[i] = (short)(Inlines.silk_RSHIFT(Inlines.silk_SMULBB(psPLC.LTPCoef_Q14[i], scale_Q10), 10));
                    }
                }
                else if (LTP_Gain_Q14 > SilkConstants.V_PITCH_GAIN_START_MAX_Q14)
                {
                    int scale_Q14;
                    int tmp;

                    tmp       = Inlines.silk_LSHIFT(SilkConstants.V_PITCH_GAIN_START_MAX_Q14, 14);
                    scale_Q14 = Inlines.silk_DIV32(tmp, Inlines.silk_max(LTP_Gain_Q14, 1));
                    for (i = 0; i < SilkConstants.LTP_ORDER; i++)
                    {
                        psPLC.LTPCoef_Q14[i] = (short)(Inlines.silk_RSHIFT(Inlines.silk_SMULBB(psPLC.LTPCoef_Q14[i], scale_Q14), 14));
                    }
                }
            }
            else
            {
                psPLC.pitchL_Q8 = Inlines.silk_LSHIFT(Inlines.silk_SMULBB(psDec.fs_kHz, 18), 8);
                Arrays.MemSetShort(psPLC.LTPCoef_Q14, 0, SilkConstants.LTP_ORDER);
            }

            /* Save LPC coeficients */
            Array.Copy(psDecCtrl.PredCoef_Q12[1], psPLC.prevLPC_Q12, psDec.LPC_order);
            psPLC.prevLTP_scale_Q14 = (short)(psDecCtrl.LTP_scale_Q14);

            /* Save last two gains */
            Array.Copy(psDecCtrl.Gains_Q16, psDec.nb_subfr - 2, psPLC.prevGain_Q16, 0, 2);

            psPLC.subfr_length = psDec.subfr_length;
            psPLC.nb_subfr     = psDec.nb_subfr;
        }
Пример #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;
            }
        }
Пример #10
0
        internal static void silk_PLC_conceal(
            SilkChannelDecoder psDec,     /* I/O Decoder state        */
            SilkDecoderControl psDecCtrl, /* I/O Decoder control      */
            short[] frame,                /* O LPC residual signal    */
            int frame_ptr
            )
        {
            int   i, j, k;
            int   lag, idx, sLTP_buf_idx;
            int   rand_seed, harm_Gain_Q15, rand_Gain_Q15, inv_gain_Q30;
            int   energy1, energy2, shift1, shift2;
            int   rand_ptr;
            int   pred_lag_ptr;
            int   LPC_pred_Q10, LTP_pred_Q12;
            short rand_scale_Q14;

            short[] B_Q14;
            int     sLPC_Q14_ptr;

            short[]   sLTP     = new short[psDec.ltp_mem_length];
            int[]     sLTP_Q14 = new int[psDec.ltp_mem_length + psDec.frame_length];
            PLCStruct psPLC    = psDec.sPLC;

            int[] prevGain_Q10 = new int[2];

            prevGain_Q10[0] = Inlines.silk_RSHIFT(psPLC.prevGain_Q16[0], 6);
            prevGain_Q10[1] = Inlines.silk_RSHIFT(psPLC.prevGain_Q16[1], 6);

            if (psDec.first_frame_after_reset != 0)
            {
                Arrays.MemSetShort(psPLC.prevLPC_Q12, 0, SilkConstants.MAX_LPC_ORDER);
            }

            silk_PLC_energy(out energy1, out shift1, out energy2, out shift2, psDec.exc_Q14, prevGain_Q10, psDec.subfr_length, psDec.nb_subfr);

            if (Inlines.silk_RSHIFT(energy1, shift2) < Inlines.silk_RSHIFT(energy2, shift1))
            {
                /* First sub-frame has lowest energy */
                rand_ptr = Inlines.silk_max_int(0, (psPLC.nb_subfr - 1) * psPLC.subfr_length - SilkConstants.RAND_BUF_SIZE);
            }
            else
            {
                /* Second sub-frame has lowest energy */
                rand_ptr = Inlines.silk_max_int(0, psPLC.nb_subfr * psPLC.subfr_length - SilkConstants.RAND_BUF_SIZE);
            }

            /* Set up Gain to random noise component */
            B_Q14          = psPLC.LTPCoef_Q14;
            rand_scale_Q14 = psPLC.randScale_Q14;

            /* Set up attenuation gains */
            harm_Gain_Q15 = HARM_ATT_Q15[Inlines.silk_min_int(NB_ATT - 1, psDec.lossCnt)];
            if (psDec.prevSignalType == SilkConstants.TYPE_VOICED)
            {
                rand_Gain_Q15 = PLC_RAND_ATTENUATE_V_Q15[Inlines.silk_min_int(NB_ATT - 1, psDec.lossCnt)];
            }
            else
            {
                rand_Gain_Q15 = PLC_RAND_ATTENUATE_UV_Q15[Inlines.silk_min_int(NB_ATT - 1, psDec.lossCnt)];
            }

            /* LPC concealment. Apply BWE to previous LPC */
            BWExpander.silk_bwexpander(psPLC.prevLPC_Q12, psDec.LPC_order, ((int)((SilkConstants.BWE_COEF) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(SilkConstants.BWE_COEF, 16)*/);

            /* First Lost frame */
            if (psDec.lossCnt == 0)
            {
                rand_scale_Q14 = 1 << 14;

                /* Reduce random noise Gain for voiced frames */
                if (psDec.prevSignalType == SilkConstants.TYPE_VOICED)
                {
                    for (i = 0; i < SilkConstants.LTP_ORDER; i++)
                    {
                        rand_scale_Q14 -= B_Q14[i];
                    }
                    rand_scale_Q14 = Inlines.silk_max_16(3277, rand_scale_Q14); /* 0.2 */
                    rand_scale_Q14 = (short)Inlines.silk_RSHIFT(Inlines.silk_SMULBB(rand_scale_Q14, psPLC.prevLTP_scale_Q14), 14);
                }
                else
                {
                    /* Reduce random noise for unvoiced frames with high LPC gain */
                    int invGain_Q30, down_scale_Q30;

                    invGain_Q30 = LPCInversePredGain.silk_LPC_inverse_pred_gain(psPLC.prevLPC_Q12, psDec.LPC_order);

                    down_scale_Q30 = Inlines.silk_min_32(Inlines.silk_RSHIFT((int)1 << 30, SilkConstants.LOG2_INV_LPC_GAIN_HIGH_THRES), invGain_Q30);
                    down_scale_Q30 = Inlines.silk_max_32(Inlines.silk_RSHIFT((int)1 << 30, SilkConstants.LOG2_INV_LPC_GAIN_LOW_THRES), down_scale_Q30);
                    down_scale_Q30 = Inlines.silk_LSHIFT(down_scale_Q30, SilkConstants.LOG2_INV_LPC_GAIN_HIGH_THRES);

                    rand_Gain_Q15 = Inlines.silk_RSHIFT(Inlines.silk_SMULWB(down_scale_Q30, rand_Gain_Q15), 14);
                }
            }

            rand_seed    = psPLC.rand_seed;
            lag          = Inlines.silk_RSHIFT_ROUND(psPLC.pitchL_Q8, 8);
            sLTP_buf_idx = psDec.ltp_mem_length;

            /* Rewhiten LTP state */
            idx = psDec.ltp_mem_length - lag - psDec.LPC_order - SilkConstants.LTP_ORDER / 2;
            Inlines.OpusAssert(idx > 0);
            Filters.silk_LPC_analysis_filter(sLTP, idx, psDec.outBuf, idx, psPLC.prevLPC_Q12, 0, psDec.ltp_mem_length - idx, psDec.LPC_order);
            /* Scale LTP state */
            inv_gain_Q30 = Inlines.silk_INVERSE32_varQ(psPLC.prevGain_Q16[1], 46);
            inv_gain_Q30 = Inlines.silk_min(inv_gain_Q30, int.MaxValue >> 1);
            for (i = idx + psDec.LPC_order; i < psDec.ltp_mem_length; i++)
            {
                sLTP_Q14[i] = Inlines.silk_SMULWB(inv_gain_Q30, sLTP[i]);
            }

            /***************************/
            /* LTP synthesis filtering */
            /***************************/
            for (k = 0; k < psDec.nb_subfr; k++)
            {
                /* Set up pointer */
                pred_lag_ptr = sLTP_buf_idx - lag + SilkConstants.LTP_ORDER / 2;
                for (i = 0; i < psDec.subfr_length; i++)
                {
                    /* Unrolled loop */
                    /* Avoids introducing a bias because Inlines.silk_SMLAWB() always rounds to -inf */
                    LTP_pred_Q12 = 2;
                    LTP_pred_Q12 = Inlines.silk_SMLAWB(LTP_pred_Q12, sLTP_Q14[pred_lag_ptr], B_Q14[0]);
                    LTP_pred_Q12 = Inlines.silk_SMLAWB(LTP_pred_Q12, sLTP_Q14[pred_lag_ptr - 1], B_Q14[1]);
                    LTP_pred_Q12 = Inlines.silk_SMLAWB(LTP_pred_Q12, sLTP_Q14[pred_lag_ptr - 2], B_Q14[2]);
                    LTP_pred_Q12 = Inlines.silk_SMLAWB(LTP_pred_Q12, sLTP_Q14[pred_lag_ptr - 3], B_Q14[3]);
                    LTP_pred_Q12 = Inlines.silk_SMLAWB(LTP_pred_Q12, sLTP_Q14[pred_lag_ptr - 4], B_Q14[4]);
                    pred_lag_ptr++;

                    /* Generate LPC excitation */
                    rand_seed = Inlines.silk_RAND(rand_seed);
                    idx       = Inlines.silk_RSHIFT(rand_seed, 25) & SilkConstants.RAND_BUF_MASK;
                    sLTP_Q14[sLTP_buf_idx] = Inlines.silk_LSHIFT32(Inlines.silk_SMLAWB(LTP_pred_Q12, psDec.exc_Q14[rand_ptr + idx], rand_scale_Q14), 2);
                    sLTP_buf_idx++;
                }

                /* Gradually reduce LTP gain */
                for (j = 0; j < SilkConstants.LTP_ORDER; j++)
                {
                    B_Q14[j] = (short)(Inlines.silk_RSHIFT(Inlines.silk_SMULBB(harm_Gain_Q15, B_Q14[j]), 15));
                }
                /* Gradually reduce excitation gain */
                rand_scale_Q14 = (short)(Inlines.silk_RSHIFT(Inlines.silk_SMULBB(rand_scale_Q14, rand_Gain_Q15), 15));

                /* Slowly increase pitch lag */
                psPLC.pitchL_Q8 = Inlines.silk_SMLAWB(psPLC.pitchL_Q8, psPLC.pitchL_Q8, SilkConstants.PITCH_DRIFT_FAC_Q16);
                psPLC.pitchL_Q8 = Inlines.silk_min_32(psPLC.pitchL_Q8, Inlines.silk_LSHIFT(Inlines.silk_SMULBB(SilkConstants.MAX_PITCH_LAG_MS, psDec.fs_kHz), 8));
                lag             = Inlines.silk_RSHIFT_ROUND(psPLC.pitchL_Q8, 8);
            }

            /***************************/
            /* LPC synthesis filtering */
            /***************************/
            sLPC_Q14_ptr = psDec.ltp_mem_length - SilkConstants.MAX_LPC_ORDER;

            /* Copy LPC state */
            Array.Copy(psDec.sLPC_Q14_buf, 0, sLTP_Q14, sLPC_Q14_ptr, SilkConstants.MAX_LPC_ORDER);

            Inlines.OpusAssert(psDec.LPC_order >= 10); /* check that unrolling works */
            for (i = 0; i < psDec.frame_length; i++)
            {
                /* partly unrolled */
                int sLPCmaxi = sLPC_Q14_ptr + SilkConstants.MAX_LPC_ORDER + i;
                /* Avoids introducing a bias because Inlines.silk_SMLAWB() always rounds to -inf */
                LPC_pred_Q10 = Inlines.silk_RSHIFT(psDec.LPC_order, 1);
                LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 1], psPLC.prevLPC_Q12[0]);
                LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 2], psPLC.prevLPC_Q12[1]);
                LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 3], psPLC.prevLPC_Q12[2]);
                LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 4], psPLC.prevLPC_Q12[3]);
                LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 5], psPLC.prevLPC_Q12[4]);
                LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 6], psPLC.prevLPC_Q12[5]);
                LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 7], psPLC.prevLPC_Q12[6]);
                LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 8], psPLC.prevLPC_Q12[7]);
                LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 9], psPLC.prevLPC_Q12[8]);
                LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 10], psPLC.prevLPC_Q12[9]);
                for (j = 10; j < psDec.LPC_order; j++)
                {
                    LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - j - 1], psPLC.prevLPC_Q12[j]);
                }

                /* Add prediction to LPC excitation */
                sLTP_Q14[sLPCmaxi] = Inlines.silk_ADD_LSHIFT32(sLTP_Q14[sLPCmaxi], LPC_pred_Q10, 4);

                /* Scale with Gain */
                frame[frame_ptr + i] = (short)Inlines.silk_SAT16(Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWW(sLTP_Q14[sLPCmaxi], prevGain_Q10[1]), 8)));
            }

            /* Save LPC state */
            Array.Copy(sLTP_Q14, sLPC_Q14_ptr + psDec.frame_length, psDec.sLPC_Q14_buf, 0, SilkConstants.MAX_LPC_ORDER);

            /**************************************/
            /* Update states                      */
            /**************************************/
            psPLC.rand_seed     = rand_seed;
            psPLC.randScale_Q14 = rand_scale_Q14;
            for (i = 0; i < SilkConstants.MAX_NB_SUBFR; i++)
            {
                psDecCtrl.pitchL[i] = lag;
            }
        }