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