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); } }
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; }
/// <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; }
/**********************************************************/ /* 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); }
/// <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); } }
/* 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; } }
/* 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); }
/**************************************************/ /* 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; }
/* 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; } }
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; } }