/// <summary> /// /// </summary> /// <param name="energy1">O</param> /// <param name="shift1">O</param> /// <param name="energy2">O</param> /// <param name="shift2">O</param> /// <param name="exc_Q14">I</param> /// <param name="prevGain_Q10">I</param> /// <param name="subfr_length">I</param> /// <param name="nb_subfr">I</param> internal static void silk_PLC_energy( out int energy1, out int shift1, out int energy2, out int shift2, int[] exc_Q14, int[] prevGain_Q10, int subfr_length, int nb_subfr) { int i, k; int exc_buf_ptr = 0; short[] exc_buf = new short[2 * subfr_length]; /* Find random noise component */ /* Scale previous excitation signal */ for (k = 0; k < 2; k++) { for (i = 0; i < subfr_length; i++) { exc_buf[exc_buf_ptr + i] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT( Inlines.silk_SMULWW(exc_Q14[i + (k + nb_subfr - 2) * subfr_length], prevGain_Q10[k]), 8)); } exc_buf_ptr += subfr_length; } /* Find the subframe with lowest energy of the last two and use that as random noise generator */ SumSqrShift.silk_sum_sqr_shift(out energy1, out shift1, exc_buf, subfr_length); SumSqrShift.silk_sum_sqr_shift(out energy2, out shift2, exc_buf, subfr_length, subfr_length); }
/// <summary> /// Find least-squares prediction gain for one signal based on another and quantize it /// </summary> /// <param name="ratio_Q14">O Ratio of residual and mid energies</param> /// <param name="x">I Basis signal</param> /// <param name="y">I Target signal</param> /// <param name="mid_res_amp_Q0">I/O Smoothed mid, residual norms</param> /// <param name="mid_res_amp_Q0_ptr"></param> /// <param name="length">I Number of samples</param> /// <param name="smooth_coef_Q16">I Smoothing coefficient</param> /// <returns>O Returns predictor in Q13</returns> internal static int silk_stereo_find_predictor( BoxedValueInt ratio_Q14, short[] x, short[] y, int[] mid_res_amp_Q0, int mid_res_amp_Q0_ptr, int length, int smooth_coef_Q16) { int scale; int nrgx, nrgy, scale1, scale2; int corr, pred_Q13, pred2_Q10; /* Find predictor */ SumSqrShift.silk_sum_sqr_shift(out nrgx, out scale1, x, length); SumSqrShift.silk_sum_sqr_shift(out nrgy, out scale2, y, length); scale = Inlines.silk_max_int(scale1, scale2); scale = scale + (scale & 1); /* make even */ nrgy = Inlines.silk_RSHIFT32(nrgy, scale - scale2); nrgx = Inlines.silk_RSHIFT32(nrgx, scale - scale1); nrgx = Inlines.silk_max_int(nrgx, 1); corr = Inlines.silk_inner_prod_aligned_scale(x, y, scale, length); pred_Q13 = Inlines.silk_DIV32_varQ(corr, nrgx, 13); pred_Q13 = Inlines.silk_LIMIT(pred_Q13, -(1 << 14), 1 << 14); pred2_Q10 = Inlines.silk_SMULWB(pred_Q13, pred_Q13); /* Faster update for signals with large prediction parameters */ smooth_coef_Q16 = (int)Inlines.silk_max_int(smooth_coef_Q16, Inlines.silk_abs(pred2_Q10)); /* Smoothed mid and residual norms */ Inlines.OpusAssert(smooth_coef_Q16 < 32768); scale = Inlines.silk_RSHIFT(scale, 1); mid_res_amp_Q0[mid_res_amp_Q0_ptr] = Inlines.silk_SMLAWB(mid_res_amp_Q0[mid_res_amp_Q0_ptr], Inlines.silk_LSHIFT(Inlines.silk_SQRT_APPROX(nrgx), scale) - mid_res_amp_Q0[mid_res_amp_Q0_ptr], smooth_coef_Q16); /* Residual energy = nrgy - 2 * pred * corr + pred^2 * nrgx */ nrgy = Inlines.silk_SUB_LSHIFT32(nrgy, Inlines.silk_SMULWB(corr, pred_Q13), 3 + 1); nrgy = Inlines.silk_ADD_LSHIFT32(nrgy, Inlines.silk_SMULWB(nrgx, pred2_Q10), 6); mid_res_amp_Q0[mid_res_amp_Q0_ptr + 1] = Inlines.silk_SMLAWB(mid_res_amp_Q0[mid_res_amp_Q0_ptr + 1], Inlines.silk_LSHIFT(Inlines.silk_SQRT_APPROX(nrgy), scale) - mid_res_amp_Q0[mid_res_amp_Q0_ptr + 1], smooth_coef_Q16); /* Ratio of smoothed residual and mid norms */ ratio_Q14.Val = Inlines.silk_DIV32_varQ(mid_res_amp_Q0[mid_res_amp_Q0_ptr + 1], Inlines.silk_max(mid_res_amp_Q0[mid_res_amp_Q0_ptr], 1), 14); ratio_Q14.Val = Inlines.silk_LIMIT(ratio_Q14.Val, 0, 32767); return(pred_Q13); }
/// <summary> /// Finds linear prediction coeffecients and weights /// </summary> /// <param name="b_Q14"></param> /// <param name="WLTP"></param> /// <param name="LTPredCodGain_Q7"></param> /// <param name="r_lpc"></param> /// <param name="lag"></param> /// <param name="Wght_Q15"></param> /// <param name="subfr_length"></param> /// <param name="nb_subfr"></param> /// <param name="mem_offset"></param> /// <param name="corr_rshifts"></param> /// <param name="arch"></param> internal static void silk_find_LTP( short[] b_Q14, /* O LTP coefs [SilkConstants.MAX_NB_SUBFR * SilkConstants.LTP_ORDER] */ int[] WLTP, /* O Weight for LTP quantization [SilkConstants.MAX_NB_SUBFR * SilkConstants.LTP_ORDER * SilkConstants.LTP_ORDER] */ BoxedValueInt LTPredCodGain_Q7, /* O LTP coding gain */ short[] r_lpc, /* I residual signal after LPC signal + state for first 10 ms */ int[] lag, /* I LTP lags [SilkConstants.MAX_NB_SUBFR] */ int[] Wght_Q15, /* I weights [SilkConstants.MAX_NB_SUBFR] */ int subfr_length, /* I subframe length */ int nb_subfr, /* I number of subframes */ int mem_offset, /* I number of samples in LTP memory */ int[] corr_rshifts /* O right shifts applied to correlations [SilkConstants.MAX_NB_SUBFR] */ ) { int i, k, lshift; int r_ptr; int lag_ptr; int b_Q14_ptr; int regu; int WLTP_ptr; int[] b_Q16 = new int[SilkConstants.LTP_ORDER]; int[] delta_b_Q14 = new int[SilkConstants.LTP_ORDER]; int[] d_Q14 = new int[SilkConstants.MAX_NB_SUBFR]; int[] nrg = new int[SilkConstants.MAX_NB_SUBFR]; int g_Q26; int[] w = new int[SilkConstants.MAX_NB_SUBFR]; int WLTP_max, max_abs_d_Q14, max_w_bits; int temp32, denom32; int extra_shifts; int rr_shifts, maxRshifts, maxRshifts_wxtra, LZs; int LPC_res_nrg, LPC_LTP_res_nrg, div_Q16; int[] Rr = new int[SilkConstants.LTP_ORDER]; int[] rr = new int[SilkConstants.MAX_NB_SUBFR]; int wd, m_Q12; b_Q14_ptr = 0; WLTP_ptr = 0; r_ptr = mem_offset; for (k = 0; k < nb_subfr; k++) { lag_ptr = r_ptr - (lag[k] + SilkConstants.LTP_ORDER / 2); SumSqrShift.silk_sum_sqr_shift(out rr[k], out rr_shifts, r_lpc, r_ptr, subfr_length); /* rr[ k ] in Q( -rr_shifts ) */ /* Assure headroom */ LZs = Inlines.silk_CLZ32(rr[k]); if (LZs < LTP_CORRS_HEAD_ROOM) { rr[k] = Inlines.silk_RSHIFT_ROUND(rr[k], LTP_CORRS_HEAD_ROOM - LZs); rr_shifts += (LTP_CORRS_HEAD_ROOM - LZs); } corr_rshifts[k] = rr_shifts; BoxedValueInt boxed_shifts = new BoxedValueInt(corr_rshifts[k]); CorrelateMatrix.silk_corrMatrix(r_lpc, lag_ptr, subfr_length, SilkConstants.LTP_ORDER, LTP_CORRS_HEAD_ROOM, WLTP, WLTP_ptr, boxed_shifts); /* WLTP_ptr in Q( -corr_rshifts[ k ] ) */ corr_rshifts[k] = boxed_shifts.Val; /* The correlation vector always has lower max abs value than rr and/or RR so head room is assured */ CorrelateMatrix.silk_corrVector(r_lpc, lag_ptr, r_lpc, r_ptr, subfr_length, SilkConstants.LTP_ORDER, Rr, corr_rshifts[k]); /* Rr_ptr in Q( -corr_rshifts[ k ] ) */ if (corr_rshifts[k] > rr_shifts) { rr[k] = Inlines.silk_RSHIFT(rr[k], corr_rshifts[k] - rr_shifts); /* rr[ k ] in Q( -corr_rshifts[ k ] ) */ } Inlines.OpusAssert(rr[k] >= 0); regu = 1; regu = Inlines.silk_SMLAWB(regu, rr[k], ((int)((TuningParameters.LTP_DAMPING / 3) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.LTP_DAMPING / 3, 16)*/); regu = Inlines.silk_SMLAWB(regu, Inlines.MatrixGet(WLTP, WLTP_ptr, 0, 0, SilkConstants.LTP_ORDER), ((int)((TuningParameters.LTP_DAMPING / 3) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.LTP_DAMPING / 3, 16)*/); regu = Inlines.silk_SMLAWB(regu, Inlines.MatrixGet(WLTP, WLTP_ptr, SilkConstants.LTP_ORDER - 1, SilkConstants.LTP_ORDER - 1, SilkConstants.LTP_ORDER), ((int)((TuningParameters.LTP_DAMPING / 3) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.LTP_DAMPING / 3, 16)*/); RegularizeCorrelations.silk_regularize_correlations(WLTP, WLTP_ptr, rr, k, regu, SilkConstants.LTP_ORDER); LinearAlgebra.silk_solve_LDL(WLTP, WLTP_ptr, SilkConstants.LTP_ORDER, Rr, b_Q16); /* WLTP_ptr and Rr_ptr both in Q(-corr_rshifts[k]) */ /* Limit and store in Q14 */ silk_fit_LTP(b_Q16, b_Q14, b_Q14_ptr); /* Calculate residual energy */ nrg[k] = ResidualEnergy.silk_residual_energy16_covar(b_Q14, b_Q14_ptr, WLTP, WLTP_ptr, Rr, rr[k], SilkConstants.LTP_ORDER, 14); /* nrg in Q( -corr_rshifts[ k ] ) */ /* temp = Wght[ k ] / ( nrg[ k ] * Wght[ k ] + 0.01f * subfr_length ); */ extra_shifts = Inlines.silk_min_int(corr_rshifts[k], LTP_CORRS_HEAD_ROOM); denom32 = Inlines.silk_LSHIFT_SAT32(Inlines.silk_SMULWB(nrg[k], Wght_Q15[k]), 1 + extra_shifts) + /* Q( -corr_rshifts[ k ] + extra_shifts ) */ Inlines.silk_RSHIFT(Inlines.silk_SMULWB((int)subfr_length, 655), corr_rshifts[k] - extra_shifts); /* Q( -corr_rshifts[ k ] + extra_shifts ) */ denom32 = Inlines.silk_max(denom32, 1); Inlines.OpusAssert(((long)Wght_Q15[k] << 16) < int.MaxValue); /* Wght always < 0.5 in Q0 */ temp32 = Inlines.silk_DIV32(Inlines.silk_LSHIFT((int)Wght_Q15[k], 16), denom32); /* Q( 15 + 16 + corr_rshifts[k] - extra_shifts ) */ temp32 = Inlines.silk_RSHIFT(temp32, 31 + corr_rshifts[k] - extra_shifts - 26); /* Q26 */ /* Limit temp such that the below scaling never wraps around */ WLTP_max = 0; for (i = WLTP_ptr; i < WLTP_ptr + (SilkConstants.LTP_ORDER * SilkConstants.LTP_ORDER); i++) { WLTP_max = Inlines.silk_max(WLTP[i], WLTP_max); } lshift = Inlines.silk_CLZ32(WLTP_max) - 1 - 3; /* keep 3 bits free for vq_nearest_neighbor */ Inlines.OpusAssert(26 - 18 + lshift >= 0); if (26 - 18 + lshift < 31) { temp32 = Inlines.silk_min_32(temp32, Inlines.silk_LSHIFT((int)1, 26 - 18 + lshift)); } Inlines.silk_scale_vector32_Q26_lshift_18(WLTP, WLTP_ptr, temp32, SilkConstants.LTP_ORDER * SilkConstants.LTP_ORDER); /* WLTP_ptr in Q( 18 - corr_rshifts[ k ] ) */ w[k] = Inlines.MatrixGet(WLTP, WLTP_ptr, SilkConstants.LTP_ORDER / 2, SilkConstants.LTP_ORDER / 2, SilkConstants.LTP_ORDER); /* w in Q( 18 - corr_rshifts[ k ] ) */ Inlines.OpusAssert(w[k] >= 0); r_ptr += subfr_length; b_Q14_ptr += SilkConstants.LTP_ORDER; WLTP_ptr += (SilkConstants.LTP_ORDER * SilkConstants.LTP_ORDER); } maxRshifts = 0; for (k = 0; k < nb_subfr; k++) { maxRshifts = Inlines.silk_max_int(corr_rshifts[k], maxRshifts); } /* Compute LTP coding gain */ if (LTPredCodGain_Q7 != null) { LPC_LTP_res_nrg = 0; LPC_res_nrg = 0; Inlines.OpusAssert(LTP_CORRS_HEAD_ROOM >= 2); /* Check that no overflow will happen when adding */ for (k = 0; k < nb_subfr; k++) { LPC_res_nrg = Inlines.silk_ADD32(LPC_res_nrg, Inlines.silk_RSHIFT(Inlines.silk_ADD32(Inlines.silk_SMULWB(rr[k], Wght_Q15[k]), 1), 1 + (maxRshifts - corr_rshifts[k]))); /* Q( -maxRshifts ) */ LPC_LTP_res_nrg = Inlines.silk_ADD32(LPC_LTP_res_nrg, Inlines.silk_RSHIFT(Inlines.silk_ADD32(Inlines.silk_SMULWB(nrg[k], Wght_Q15[k]), 1), 1 + (maxRshifts - corr_rshifts[k]))); /* Q( -maxRshifts ) */ } LPC_LTP_res_nrg = Inlines.silk_max(LPC_LTP_res_nrg, 1); /* avoid division by zero */ div_Q16 = Inlines.silk_DIV32_varQ(LPC_res_nrg, LPC_LTP_res_nrg, 16); LTPredCodGain_Q7.Val = (int)Inlines.silk_SMULBB(3, Inlines.silk_lin2log(div_Q16) - (16 << 7)); Inlines.OpusAssert(LTPredCodGain_Q7.Val == (int)Inlines.silk_SAT16(Inlines.silk_MUL(3, Inlines.silk_lin2log(div_Q16) - (16 << 7)))); } /* smoothing */ /* d = sum( B, 1 ); */ b_Q14_ptr = 0; for (k = 0; k < nb_subfr; k++) { d_Q14[k] = 0; for (i = b_Q14_ptr; i < b_Q14_ptr + SilkConstants.LTP_ORDER; i++) { d_Q14[k] += b_Q14[i]; } b_Q14_ptr += SilkConstants.LTP_ORDER; } /* m = ( w * d' ) / ( sum( w ) + 1e-3 ); */ /* Find maximum absolute value of d_Q14 and the bits used by w in Q0 */ max_abs_d_Q14 = 0; max_w_bits = 0; for (k = 0; k < nb_subfr; k++) { max_abs_d_Q14 = Inlines.silk_max_32(max_abs_d_Q14, Inlines.silk_abs(d_Q14[k])); /* w[ k ] is in Q( 18 - corr_rshifts[ k ] ) */ /* Find bits needed in Q( 18 - maxRshifts ) */ max_w_bits = Inlines.silk_max_32(max_w_bits, 32 - Inlines.silk_CLZ32(w[k]) + corr_rshifts[k] - maxRshifts); } /* max_abs_d_Q14 = (5 << 15); worst case, i.e. SilkConstants.LTP_ORDER * -silk_int16_MIN */ Inlines.OpusAssert(max_abs_d_Q14 <= (5 << 15)); /* How many bits is needed for w*d' in Q( 18 - maxRshifts ) in the worst case, of all d_Q14's being equal to max_abs_d_Q14 */ extra_shifts = max_w_bits + 32 - Inlines.silk_CLZ32(max_abs_d_Q14) - 14; /* Subtract what we got available; bits in output var plus maxRshifts */ extra_shifts -= (32 - 1 - 2 + maxRshifts); /* Keep sign bit free as well as 2 bits for accumulation */ extra_shifts = Inlines.silk_max_int(extra_shifts, 0); maxRshifts_wxtra = maxRshifts + extra_shifts; temp32 = Inlines.silk_RSHIFT(262, maxRshifts + extra_shifts) + 1; /* 1e-3f in Q( 18 - (maxRshifts + extra_shifts) ) */ wd = 0; for (k = 0; k < nb_subfr; k++) { /* w has at least 2 bits of headroom so no overflow should happen */ temp32 = Inlines.silk_ADD32(temp32, Inlines.silk_RSHIFT(w[k], maxRshifts_wxtra - corr_rshifts[k])); /* Q( 18 - maxRshifts_wxtra ) */ wd = Inlines.silk_ADD32(wd, Inlines.silk_LSHIFT(Inlines.silk_SMULWW(Inlines.silk_RSHIFT(w[k], maxRshifts_wxtra - corr_rshifts[k]), d_Q14[k]), 2)); /* Q( 18 - maxRshifts_wxtra ) */ } m_Q12 = Inlines.silk_DIV32_varQ(wd, temp32, 12); b_Q14_ptr = 0; for (k = 0; k < nb_subfr; k++) { /* w[ k ] from Q( 18 - corr_rshifts[ k ] ) to Q( 16 ) */ if (2 - corr_rshifts[k] > 0) { temp32 = Inlines.silk_RSHIFT(w[k], 2 - corr_rshifts[k]); } else { temp32 = Inlines.silk_LSHIFT_SAT32(w[k], corr_rshifts[k] - 2); } g_Q26 = Inlines.silk_MUL( Inlines.silk_DIV32( ((int)((TuningParameters.LTP_SMOOTHING) * ((long)1 << (26)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.LTP_SMOOTHING, 26)*/, Inlines.silk_RSHIFT(((int)((TuningParameters.LTP_SMOOTHING) * ((long)1 << (26)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.LTP_SMOOTHING, 26)*/, 10) + temp32), /* Q10 */ Inlines.silk_LSHIFT_SAT32(Inlines.silk_SUB_SAT32((int)m_Q12, Inlines.silk_RSHIFT(d_Q14[k], 2)), 4)); /* Q16 */ temp32 = 0; for (i = 0; i < SilkConstants.LTP_ORDER; i++) { delta_b_Q14[i] = Inlines.silk_max_16(b_Q14[b_Q14_ptr + i], 1638); /* 1638_Q14 = 0.1_Q0 */ temp32 += delta_b_Q14[i]; /* Q14 */ } temp32 = Inlines.silk_DIV32(g_Q26, temp32); /* Q14 . Q12 */ for (i = 0; i < SilkConstants.LTP_ORDER; i++) { b_Q14[b_Q14_ptr + i] = (short)(Inlines.silk_LIMIT_32((int)b_Q14[b_Q14_ptr + i] + Inlines.silk_SMULWB(Inlines.silk_LSHIFT_SAT32(temp32, 4), delta_b_Q14[i]), -16000, 28000)); } b_Q14_ptr += SilkConstants.LTP_ORDER; } }
/* Calculates correlation matrix X'*X */ internal static void silk_corrMatrix( short[] x, /* I x vector [L + order - 1] used to form data matrix X */ int x_ptr, int L, /* I Length of vectors */ int order, /* I Max lag for correlation */ int head_room, /* I Desired headroom */ int[] XX, /* O Pointer to X'*X correlation matrix [ order x order ] */ int XX_ptr, BoxedValueInt rshifts /* I/O Right shifts of correlations */ ) { int i, j, lag, head_room_rshifts; int energy, rshifts_local; int ptr1, ptr2; /* Calculate energy to find shift used to fit in 32 bits */ SumSqrShift.silk_sum_sqr_shift(out energy, out rshifts_local, x, x_ptr, L + order - 1); /* Add shifts to get the desired head room */ head_room_rshifts = Inlines.silk_max(head_room - Inlines.silk_CLZ32(energy), 0); energy = Inlines.silk_RSHIFT32(energy, head_room_rshifts); rshifts_local += head_room_rshifts; /* Calculate energy of first column (0) of X: X[:,0]'*X[:,0] */ /* Remove contribution of first order - 1 samples */ for (i = x_ptr; i < x_ptr + order - 1; i++) { energy -= Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[i], x[i]), rshifts_local); } if (rshifts_local < rshifts.Val) { /* Adjust energy */ energy = Inlines.silk_RSHIFT32(energy, rshifts.Val - rshifts_local); rshifts_local = rshifts.Val; } /* Calculate energy of remaining columns of X: X[:,j]'*X[:,j] */ /* Fill out the diagonal of the correlation matrix */ Inlines.MatrixSet(XX, XX_ptr, 0, 0, order, energy); ptr1 = x_ptr + order - 1; /* First sample of column 0 of X */ for (j = 1; j < order; j++) { energy = Inlines.silk_SUB32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 + L - j], x[ptr1 + L - j]), rshifts_local)); energy = Inlines.silk_ADD32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 - j], x[ptr1 - j]), rshifts_local)); Inlines.MatrixSet(XX, XX_ptr, j, j, order, energy); } ptr2 = x_ptr + order - 2; /* First sample of column 1 of X */ /* Calculate the remaining elements of the correlation matrix */ if (rshifts_local > 0) { /* Right shifting used */ for (lag = 1; lag < order; lag++) { /* Inner product of column 0 and column lag: X[:,0]'*X[:,lag] */ energy = 0; for (i = 0; i < L; i++) { energy += Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 + i], x[ptr2 + i]), rshifts_local); } /* Calculate remaining off diagonal: X[:,j]'*X[:,j + lag] */ Inlines.MatrixSet(XX, XX_ptr, lag, 0, order, energy); Inlines.MatrixSet(XX, XX_ptr, 0, lag, order, energy); for (j = 1; j < (order - lag); j++) { energy = Inlines.silk_SUB32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 + L - j], x[ptr2 + L - j]), rshifts_local)); energy = Inlines.silk_ADD32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 - j], x[ptr2 - j]), rshifts_local)); Inlines.MatrixSet(XX, XX_ptr, lag + j, j, order, energy); Inlines.MatrixSet(XX, XX_ptr, j, lag + j, order, energy); } ptr2--; /* Update pointer to first sample of next column (lag) in X */ } } else { for (lag = 1; lag < order; lag++) { /* Inner product of column 0 and column lag: X[:,0]'*X[:,lag] */ energy = Inlines.silk_inner_prod(x, ptr1, x, ptr2, L); Inlines.MatrixSet(XX, XX_ptr, lag, 0, order, energy); Inlines.MatrixSet(XX, XX_ptr, 0, lag, order, energy); /* Calculate remaining off diagonal: X[:,j]'*X[:,j + lag] */ for (j = 1; j < (order - lag); j++) { energy = Inlines.silk_SUB32(energy, Inlines.silk_SMULBB(x[ptr1 + L - j], x[ptr2 + L - j])); energy = Inlines.silk_SMLABB(energy, x[ptr1 - j], x[ptr2 - j]); Inlines.MatrixSet(XX, XX_ptr, lag + j, j, order, energy); Inlines.MatrixSet(XX, XX_ptr, j, lag + j, order, energy); } ptr2--;/* Update pointer to first sample of next column (lag) in X */ } } rshifts.Val = rshifts_local; }
/* 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)); }
/* Calculates residual energies of input subframes where all subframes have LPC_order */ /* of preceding samples */ internal static void silk_residual_energy( int[] nrgs, /* O Residual energy per subframe [MAX_NB_SUBFR] */ int[] nrgsQ, /* O Q value per subframe [MAX_NB_SUBFR] */ short[] x, /* I Input signal */ short[][] a_Q12, /* I AR coefs for each frame half [2][MAX_LPC_ORDER] */ int[] gains, /* I Quantization gains [SilkConstants.MAX_NB_SUBFR] */ int subfr_length, /* I Subframe length */ int nb_subfr, /* I Number of subframes */ int LPC_order /* I LPC order */ ) { int offset, i, j, lz1, lz2; int rshift, energy; int LPC_res_ptr; short[] LPC_res; int x_ptr; int tmp32; x_ptr = 0; offset = LPC_order + subfr_length; /* Filter input to create the LPC residual for each frame half, and measure subframe energies */ LPC_res = new short[(SilkConstants.MAX_NB_SUBFR >> 1) * offset]; Inlines.OpusAssert((nb_subfr >> 1) * (SilkConstants.MAX_NB_SUBFR >> 1) == nb_subfr); for (i = 0; i < nb_subfr >> 1; i++) { /* Calculate half frame LPC residual signal including preceding samples */ Filters.silk_LPC_analysis_filter(LPC_res, 0, x, x_ptr, a_Q12[i], 0, (SilkConstants.MAX_NB_SUBFR >> 1) * offset, LPC_order); /* Point to first subframe of the just calculated LPC residual signal */ LPC_res_ptr = LPC_order; for (j = 0; j < (SilkConstants.MAX_NB_SUBFR >> 1); j++) { /* Measure subframe energy */ SumSqrShift.silk_sum_sqr_shift(out energy, out rshift, LPC_res, LPC_res_ptr, subfr_length); nrgs[i * (SilkConstants.MAX_NB_SUBFR >> 1) + j] = energy; /* Set Q values for the measured energy */ nrgsQ[i * (SilkConstants.MAX_NB_SUBFR >> 1) + j] = 0 - rshift; /* Move to next subframe */ LPC_res_ptr += offset; } /* Move to next frame half */ x_ptr += (SilkConstants.MAX_NB_SUBFR >> 1) * offset; } /* Apply the squared subframe gains */ for (i = 0; i < nb_subfr; i++) { /* Fully upscale gains and energies */ lz1 = Inlines.silk_CLZ32(nrgs[i]) - 1; lz2 = Inlines.silk_CLZ32(gains[i]) - 1; tmp32 = Inlines.silk_LSHIFT32(gains[i], lz2); /* Find squared gains */ tmp32 = Inlines.silk_SMMUL(tmp32, tmp32); /* Q( 2 * lz2 - 32 )*/ /* Scale energies */ nrgs[i] = Inlines.silk_SMMUL(tmp32, Inlines.silk_LSHIFT32(nrgs[i], lz1)); /* Q( nrgsQ[ i ] + lz1 + 2 * lz2 - 32 - 32 )*/ nrgsQ[i] += lz1 + 2 * lz2 - 32 - 32; } }
/*************************************************************/ /* FIXED POINT CORE PITCH ANALYSIS FUNCTION */ /*************************************************************/ internal static int silk_pitch_analysis_core( /* O Voicing estimate: 0 voiced, 1 unvoiced */ short[] frame, /* I Signal of length PE_FRAME_LENGTH_MS*Fs_kHz */ int[] pitch_out, /* O 4 pitch lag values */ BoxedValueShort lagIndex, /* O Lag Index */ BoxedValueSbyte contourIndex, /* O Pitch contour Index */ BoxedValueInt LTPCorr_Q15, /* I/O Normalized correlation; input: value from previous frame */ int prevLag, /* I Last lag of previous frame; set to zero is unvoiced */ int search_thres1_Q16, /* I First stage threshold for lag candidates 0 - 1 */ int search_thres2_Q13, /* I Final threshold for lag candidates 0 - 1 */ int Fs_kHz, /* I Sample frequency (kHz) */ int complexity, /* I Complexity setting, 0-2, where 2 is highest */ int nb_subfr /* I number of 5 ms subframes */ ) { short[] frame_8kHz; short[] frame_4kHz; int[] filt_state = new int[6]; short[] input_frame_ptr; int i, k, d, j; short[] C; int[] xcorr32; short[] basis; int basis_ptr; short[] target; int target_ptr; int cross_corr, normalizer, energy, shift, energy_basis, energy_target; int Cmax, length_d_srch, length_d_comp; int[] d_srch = new int[SilkConstants.PE_D_SRCH_LENGTH]; short[] d_comp; int sum, threshold, lag_counter; int CBimax, CBimax_new, CBimax_old, lag, start_lag, end_lag, lag_new; int CCmax, CCmax_b, CCmax_new_b, CCmax_new; int[] CC = new int[SilkConstants.PE_NB_CBKS_STAGE2_EXT]; silk_pe_stage3_vals[] energies_st3; silk_pe_stage3_vals[] cross_corr_st3; int frame_length, frame_length_8kHz, frame_length_4kHz; int sf_length; int min_lag; int max_lag; int contour_bias_Q15, diff; int nb_cbk_search; int delta_lag_log2_sqr_Q7, lag_log2_Q7, prevLag_log2_Q7, prev_lag_bias_Q13; sbyte[][] Lag_CB_ptr; /* Check for valid sampling frequency */ Inlines.OpusAssert(Fs_kHz == 8 || Fs_kHz == 12 || Fs_kHz == 16); /* Check for valid complexity setting */ Inlines.OpusAssert(complexity >= SilkConstants.SILK_PE_MIN_COMPLEX); Inlines.OpusAssert(complexity <= SilkConstants.SILK_PE_MAX_COMPLEX); Inlines.OpusAssert(search_thres1_Q16 >= 0 && search_thres1_Q16 <= (1 << 16)); Inlines.OpusAssert(search_thres2_Q13 >= 0 && search_thres2_Q13 <= (1 << 13)); /* Set up frame lengths max / min lag for the sampling frequency */ frame_length = (SilkConstants.PE_LTP_MEM_LENGTH_MS + nb_subfr * SilkConstants.PE_SUBFR_LENGTH_MS) * Fs_kHz; frame_length_4kHz = (SilkConstants.PE_LTP_MEM_LENGTH_MS + nb_subfr * SilkConstants.PE_SUBFR_LENGTH_MS) * 4; frame_length_8kHz = (SilkConstants.PE_LTP_MEM_LENGTH_MS + nb_subfr * SilkConstants.PE_SUBFR_LENGTH_MS) * 8; sf_length = SilkConstants.PE_SUBFR_LENGTH_MS * Fs_kHz; min_lag = SilkConstants.PE_MIN_LAG_MS * Fs_kHz; max_lag = SilkConstants.PE_MAX_LAG_MS * Fs_kHz - 1; /* Resample from input sampled at Fs_kHz to 8 kHz */ frame_8kHz = new short[frame_length_8kHz]; if (Fs_kHz == 16) { Arrays.MemSetInt(filt_state, 0, 2); Resampler.silk_resampler_down2(filt_state, frame_8kHz, frame, frame_length); } else if (Fs_kHz == 12) { Arrays.MemSetInt(filt_state, 0, 6); Resampler.silk_resampler_down2_3(filt_state, frame_8kHz, frame, frame_length); } else { Inlines.OpusAssert(Fs_kHz == 8); Array.Copy(frame, frame_8kHz, frame_length_8kHz); } /* Decimate again to 4 kHz */ Arrays.MemSetInt(filt_state, 0, 2); /* Set state to zero */ frame_4kHz = new short[frame_length_4kHz]; Resampler.silk_resampler_down2(filt_state, frame_4kHz, frame_8kHz, frame_length_8kHz); /* Low-pass filter */ for (i = frame_length_4kHz - 1; i > 0; i--) { frame_4kHz[i] = Inlines.silk_ADD_SAT16(frame_4kHz[i], frame_4kHz[i - 1]); } /******************************************************************************* ** Scale 4 kHz signal down to prevent correlations measures from overflowing ** find scaling as max scaling for each 8kHz(?) subframe *******************************************************************************/ /* Inner product is calculated with different lengths, so scale for the worst case */ SumSqrShift.silk_sum_sqr_shift(out energy, out shift, frame_4kHz, frame_length_4kHz); if (shift > 0) { shift = Inlines.silk_RSHIFT(shift, 1); for (i = 0; i < frame_length_4kHz; i++) { frame_4kHz[i] = Inlines.silk_RSHIFT16(frame_4kHz[i], shift); } } /****************************************************************************** * FIRST STAGE, operating in 4 khz ******************************************************************************/ C = new short[nb_subfr * CSTRIDE_8KHZ]; xcorr32 = new int[MAX_LAG_4KHZ - MIN_LAG_4KHZ + 1]; Arrays.MemSetShort(C, 0, (nb_subfr >> 1) * CSTRIDE_4KHZ); target = frame_4kHz; target_ptr = Inlines.silk_LSHIFT(SF_LENGTH_4KHZ, 2); for (k = 0; k < nb_subfr >> 1; k++) { basis = target; basis_ptr = target_ptr - MIN_LAG_4KHZ; CeltPitchXCorr.pitch_xcorr(target, target_ptr, target, target_ptr - MAX_LAG_4KHZ, xcorr32, SF_LENGTH_8KHZ, MAX_LAG_4KHZ - MIN_LAG_4KHZ + 1); /* Calculate first vector products before loop */ cross_corr = xcorr32[MAX_LAG_4KHZ - MIN_LAG_4KHZ]; normalizer = Inlines.silk_inner_prod_self(target, target_ptr, SF_LENGTH_8KHZ); normalizer = Inlines.silk_ADD32(normalizer, Inlines.silk_inner_prod_self(basis, basis_ptr, SF_LENGTH_8KHZ)); normalizer = Inlines.silk_ADD32(normalizer, Inlines.silk_SMULBB(SF_LENGTH_8KHZ, 4000)); Inlines.MatrixSet(C, k, 0, CSTRIDE_4KHZ, (short)Inlines.silk_DIV32_varQ(cross_corr, normalizer, 13 + 1)); /* Q13 */ /* From now on normalizer is computed recursively */ for (d = MIN_LAG_4KHZ + 1; d <= MAX_LAG_4KHZ; d++) { basis_ptr--; cross_corr = xcorr32[MAX_LAG_4KHZ - d]; /* Add contribution of new sample and remove contribution from oldest sample */ normalizer = Inlines.silk_ADD32(normalizer, Inlines.silk_SMULBB(basis[basis_ptr], basis[basis_ptr]) - Inlines.silk_SMULBB(basis[basis_ptr + SF_LENGTH_8KHZ], basis[basis_ptr + SF_LENGTH_8KHZ])); Inlines.MatrixSet(C, k, d - MIN_LAG_4KHZ, CSTRIDE_4KHZ, (short)Inlines.silk_DIV32_varQ(cross_corr, normalizer, 13 + 1)); /* Q13 */ } /* Update target pointer */ target_ptr += SF_LENGTH_8KHZ; } /* Combine two subframes into single correlation measure and apply short-lag bias */ if (nb_subfr == SilkConstants.PE_MAX_NB_SUBFR) { for (i = MAX_LAG_4KHZ; i >= MIN_LAG_4KHZ; i--) { sum = (int)Inlines.MatrixGet(C, 0, i - MIN_LAG_4KHZ, CSTRIDE_4KHZ) + (int)Inlines.MatrixGet(C, 1, i - MIN_LAG_4KHZ, CSTRIDE_4KHZ); /* Q14 */ sum = Inlines.silk_SMLAWB(sum, sum, Inlines.silk_LSHIFT(-i, 4)); /* Q14 */ C[i - MIN_LAG_4KHZ] = (short)sum; /* Q14 */ } } else { /* Only short-lag bias */ for (i = MAX_LAG_4KHZ; i >= MIN_LAG_4KHZ; i--) { sum = Inlines.silk_LSHIFT((int)C[i - MIN_LAG_4KHZ], 1); /* Q14 */ sum = Inlines.silk_SMLAWB(sum, sum, Inlines.silk_LSHIFT(-i, 4)); /* Q14 */ C[i - MIN_LAG_4KHZ] = (short)sum; /* Q14 */ } } /* Sort */ length_d_srch = Inlines.silk_ADD_LSHIFT32(4, complexity, 1); Inlines.OpusAssert(3 * length_d_srch <= SilkConstants.PE_D_SRCH_LENGTH); Sort.silk_insertion_sort_decreasing_int16(C, d_srch, CSTRIDE_4KHZ, length_d_srch); /* Escape if correlation is very low already here */ Cmax = (int)C[0]; /* Q14 */ if (Cmax < ((int)((0.2f) * ((long)1 << (14)) + 0.5)) /*Inlines.SILK_CONST(0.2f, 14)*/) { Arrays.MemSetInt(pitch_out, 0, nb_subfr); LTPCorr_Q15.Val = 0; lagIndex.Val = 0; contourIndex.Val = 0; return(1); } threshold = Inlines.silk_SMULWB(search_thres1_Q16, Cmax); for (i = 0; i < length_d_srch; i++) { /* Convert to 8 kHz indices for the sorted correlation that exceeds the threshold */ if (C[i] > threshold) { d_srch[i] = Inlines.silk_LSHIFT(d_srch[i] + MIN_LAG_4KHZ, 1); } else { length_d_srch = i; break; } } Inlines.OpusAssert(length_d_srch > 0); d_comp = new short[D_COMP_STRIDE]; for (i = D_COMP_MIN; i < D_COMP_MAX; i++) { d_comp[i - D_COMP_MIN] = 0; } for (i = 0; i < length_d_srch; i++) { d_comp[d_srch[i] - D_COMP_MIN] = 1; } /* Convolution */ for (i = D_COMP_MAX - 1; i >= MIN_LAG_8KHZ; i--) { d_comp[i - D_COMP_MIN] += (short)(d_comp[i - 1 - D_COMP_MIN] + d_comp[i - 2 - D_COMP_MIN]); } length_d_srch = 0; for (i = MIN_LAG_8KHZ; i < MAX_LAG_8KHZ + 1; i++) { if (d_comp[i + 1 - D_COMP_MIN] > 0) { d_srch[length_d_srch] = i; length_d_srch++; } } /* Convolution */ for (i = D_COMP_MAX - 1; i >= MIN_LAG_8KHZ; i--) { d_comp[i - D_COMP_MIN] += (short)(d_comp[i - 1 - D_COMP_MIN] + d_comp[i - 2 - D_COMP_MIN] + d_comp[i - 3 - D_COMP_MIN]); } length_d_comp = 0; for (i = MIN_LAG_8KHZ; i < D_COMP_MAX; i++) { if (d_comp[i - D_COMP_MIN] > 0) { d_comp[length_d_comp] = (short)(i - 2); length_d_comp++; } } /********************************************************************************** ** SECOND STAGE, operating at 8 kHz, on lag sections with high correlation *************************************************************************************/ /****************************************************************************** ** Scale signal down to avoid correlations measures from overflowing *******************************************************************************/ /* find scaling as max scaling for each subframe */ SumSqrShift.silk_sum_sqr_shift(out energy, out shift, frame_8kHz, frame_length_8kHz); if (shift > 0) { shift = Inlines.silk_RSHIFT(shift, 1); for (i = 0; i < frame_length_8kHz; i++) { frame_8kHz[i] = Inlines.silk_RSHIFT16(frame_8kHz[i], shift); } } /********************************************************************************* * Find energy of each subframe projected onto its history, for a range of delays *********************************************************************************/ Arrays.MemSetShort(C, 0, nb_subfr * CSTRIDE_8KHZ); target = frame_8kHz; target_ptr = SilkConstants.PE_LTP_MEM_LENGTH_MS * 8; for (k = 0; k < nb_subfr; k++) { energy_target = Inlines.silk_ADD32(Inlines.silk_inner_prod(target, target_ptr, target, target_ptr, SF_LENGTH_8KHZ), 1); for (j = 0; j < length_d_comp; j++) { d = d_comp[j]; basis = target; basis_ptr = target_ptr - d; cross_corr = Inlines.silk_inner_prod(target, target_ptr, basis, basis_ptr, SF_LENGTH_8KHZ); if (cross_corr > 0) { energy_basis = Inlines.silk_inner_prod_self(basis, basis_ptr, SF_LENGTH_8KHZ); Inlines.MatrixSet(C, k, d - (MIN_LAG_8KHZ - 2), CSTRIDE_8KHZ, (short)Inlines.silk_DIV32_varQ(cross_corr, Inlines.silk_ADD32(energy_target, energy_basis), 13 + 1)); /* Q13 */ } else { Inlines.MatrixSet <short>(C, k, d - (MIN_LAG_8KHZ - 2), CSTRIDE_8KHZ, 0); } } target_ptr += SF_LENGTH_8KHZ; } /* search over lag range and lags codebook */ /* scale factor for lag codebook, as a function of center lag */ CCmax = int.MinValue; CCmax_b = int.MinValue; CBimax = 0; /* To avoid returning undefined lag values */ lag = -1; /* To check if lag with strong enough correlation has been found */ if (prevLag > 0) { if (Fs_kHz == 12) { prevLag = Inlines.silk_DIV32_16(Inlines.silk_LSHIFT(prevLag, 1), 3); } else if (Fs_kHz == 16) { prevLag = Inlines.silk_RSHIFT(prevLag, 1); } prevLag_log2_Q7 = Inlines.silk_lin2log((int)prevLag); } else { prevLag_log2_Q7 = 0; } Inlines.OpusAssert(search_thres2_Q13 == Inlines.silk_SAT16(search_thres2_Q13)); /* Set up stage 2 codebook based on number of subframes */ if (nb_subfr == SilkConstants.PE_MAX_NB_SUBFR) { Lag_CB_ptr = Tables.silk_CB_lags_stage2; if (Fs_kHz == 8 && complexity > SilkConstants.SILK_PE_MIN_COMPLEX) { /* If input is 8 khz use a larger codebook here because it is last stage */ nb_cbk_search = SilkConstants.PE_NB_CBKS_STAGE2_EXT; } else { nb_cbk_search = SilkConstants.PE_NB_CBKS_STAGE2; } } else { Lag_CB_ptr = Tables.silk_CB_lags_stage2_10_ms; nb_cbk_search = SilkConstants.PE_NB_CBKS_STAGE2_10MS; } for (k = 0; k < length_d_srch; k++) { d = d_srch[k]; for (j = 0; j < nb_cbk_search; j++) { CC[j] = 0; for (i = 0; i < nb_subfr; i++) { int d_subfr; /* Try all codebooks */ d_subfr = d + Lag_CB_ptr[i][j]; CC[j] = CC[j] + (int)Inlines.MatrixGet(C, i, d_subfr - (MIN_LAG_8KHZ - 2), CSTRIDE_8KHZ); } } /* Find best codebook */ CCmax_new = int.MinValue; CBimax_new = 0; for (i = 0; i < nb_cbk_search; i++) { if (CC[i] > CCmax_new) { CCmax_new = CC[i]; CBimax_new = i; } } /* Bias towards shorter lags */ lag_log2_Q7 = Inlines.silk_lin2log(d); /* Q7 */ Inlines.OpusAssert(lag_log2_Q7 == Inlines.silk_SAT16(lag_log2_Q7)); Inlines.OpusAssert(nb_subfr * ((int)((SilkConstants.PE_SHORTLAG_BIAS) * ((long)1 << (13)) + 0.5)) /*Inlines.SILK_CONST(SilkConstants.PE_SHORTLAG_BIAS, 13)*/ == Inlines.silk_SAT16(nb_subfr * ((int)((SilkConstants.PE_SHORTLAG_BIAS) * ((long)1 << (13)) + 0.5)) /*Inlines.SILK_CONST(SilkConstants.PE_SHORTLAG_BIAS, 13)*/)); CCmax_new_b = CCmax_new - Inlines.silk_RSHIFT(Inlines.silk_SMULBB(nb_subfr * ((int)((SilkConstants.PE_SHORTLAG_BIAS) * ((long)1 << (13)) + 0.5)) /*Inlines.SILK_CONST(SilkConstants.PE_SHORTLAG_BIAS, 13)*/, lag_log2_Q7), 7); /* Q13 */ /* Bias towards previous lag */ Inlines.OpusAssert(nb_subfr * ((int)((SilkConstants.PE_PREVLAG_BIAS) * ((long)1 << (13)) + 0.5)) /*Inlines.SILK_CONST(SilkConstants.PE_PREVLAG_BIAS, 13)*/ == Inlines.silk_SAT16(nb_subfr * ((int)((SilkConstants.PE_PREVLAG_BIAS) * ((long)1 << (13)) + 0.5)) /*Inlines.SILK_CONST(SilkConstants.PE_PREVLAG_BIAS, 13)*/)); if (prevLag > 0) { delta_lag_log2_sqr_Q7 = lag_log2_Q7 - prevLag_log2_Q7; Inlines.OpusAssert(delta_lag_log2_sqr_Q7 == Inlines.silk_SAT16(delta_lag_log2_sqr_Q7)); delta_lag_log2_sqr_Q7 = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(delta_lag_log2_sqr_Q7, delta_lag_log2_sqr_Q7), 7); prev_lag_bias_Q13 = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(nb_subfr * ((int)((SilkConstants.PE_PREVLAG_BIAS) * ((long)1 << (13)) + 0.5)) /*Inlines.SILK_CONST(SilkConstants.PE_PREVLAG_BIAS, 13)*/, LTPCorr_Q15.Val), 15); /* Q13 */ prev_lag_bias_Q13 = Inlines.silk_DIV32(Inlines.silk_MUL(prev_lag_bias_Q13, delta_lag_log2_sqr_Q7), delta_lag_log2_sqr_Q7 + ((int)((0.5f) * ((long)1 << (7)) + 0.5)) /*Inlines.SILK_CONST(0.5f, 7)*/); CCmax_new_b -= prev_lag_bias_Q13; /* Q13 */ } if (CCmax_new_b > CCmax_b && /* Find maximum biased correlation */ CCmax_new > Inlines.silk_SMULBB(nb_subfr, search_thres2_Q13) && /* Correlation needs to be high enough to be voiced */ Tables.silk_CB_lags_stage2[0][CBimax_new] <= MIN_LAG_8KHZ /* Lag must be in range */ ) { CCmax_b = CCmax_new_b; CCmax = CCmax_new; lag = d; CBimax = CBimax_new; } } if (lag == -1) { /* No suitable candidate found */ Arrays.MemSetInt(pitch_out, 0, nb_subfr); LTPCorr_Q15.Val = 0; lagIndex.Val = 0; contourIndex.Val = 0; return(1); } /* Output normalized correlation */ LTPCorr_Q15.Val = (int)Inlines.silk_LSHIFT(Inlines.silk_DIV32_16(CCmax, nb_subfr), 2); Inlines.OpusAssert(LTPCorr_Q15.Val >= 0); if (Fs_kHz > 8) { short[] scratch_mem; /***************************************************************************/ /* Scale input signal down to avoid correlations measures from overflowing */ /***************************************************************************/ /* find scaling as max scaling for each subframe */ SumSqrShift.silk_sum_sqr_shift(out energy, out shift, frame, frame_length); if (shift > 0) { scratch_mem = new short[frame_length]; /* Move signal to scratch mem because the input signal should be unchanged */ shift = Inlines.silk_RSHIFT(shift, 1); for (i = 0; i < frame_length; i++) { scratch_mem[i] = Inlines.silk_RSHIFT16(frame[i], shift); } input_frame_ptr = scratch_mem; } else { input_frame_ptr = frame; } /* Search in original signal */ CBimax_old = CBimax; /* Compensate for decimation */ Inlines.OpusAssert(lag == Inlines.silk_SAT16(lag)); if (Fs_kHz == 12) { lag = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(lag, 3), 1); } else if (Fs_kHz == 16) { lag = Inlines.silk_LSHIFT(lag, 1); } else { lag = Inlines.silk_SMULBB(lag, 3); } lag = Inlines.silk_LIMIT_int(lag, min_lag, max_lag); start_lag = Inlines.silk_max_int(lag - 2, min_lag); end_lag = Inlines.silk_min_int(lag + 2, max_lag); lag_new = lag; /* to avoid undefined lag */ CBimax = 0; /* to avoid undefined lag */ CCmax = int.MinValue; /* pitch lags according to second stage */ for (k = 0; k < nb_subfr; k++) { pitch_out[k] = lag + 2 * Tables.silk_CB_lags_stage2[k][CBimax_old]; } /* Set up codebook parameters according to complexity setting and frame length */ if (nb_subfr == SilkConstants.PE_MAX_NB_SUBFR) { nb_cbk_search = (int)Tables.silk_nb_cbk_searchs_stage3[complexity]; Lag_CB_ptr = Tables.silk_CB_lags_stage3; } else { nb_cbk_search = SilkConstants.PE_NB_CBKS_STAGE3_10MS; Lag_CB_ptr = Tables.silk_CB_lags_stage3_10_ms; } /* Calculate the correlations and energies needed in stage 3 */ energies_st3 = new silk_pe_stage3_vals[nb_subfr * nb_cbk_search]; cross_corr_st3 = new silk_pe_stage3_vals[nb_subfr * nb_cbk_search]; for (int c = 0; c < nb_subfr * nb_cbk_search; c++) { energies_st3[c] = new silk_pe_stage3_vals(); // fixme: these can be replaced with a linearized array probably, or at least a struct cross_corr_st3[c] = new silk_pe_stage3_vals(); } silk_P_Ana_calc_corr_st3(cross_corr_st3, input_frame_ptr, start_lag, sf_length, nb_subfr, complexity); silk_P_Ana_calc_energy_st3(energies_st3, input_frame_ptr, start_lag, sf_length, nb_subfr, complexity); lag_counter = 0; Inlines.OpusAssert(lag == Inlines.silk_SAT16(lag)); contour_bias_Q15 = Inlines.silk_DIV32_16(((int)((SilkConstants.PE_FLATCONTOUR_BIAS) * ((long)1 << (15)) + 0.5)) /*Inlines.SILK_CONST(SilkConstants.PE_FLATCONTOUR_BIAS, 15)*/, lag); target = input_frame_ptr; target_ptr = SilkConstants.PE_LTP_MEM_LENGTH_MS * Fs_kHz; energy_target = Inlines.silk_ADD32(Inlines.silk_inner_prod_self(target, target_ptr, nb_subfr * sf_length), 1); for (d = start_lag; d <= end_lag; d++) { for (j = 0; j < nb_cbk_search; j++) { cross_corr = 0; energy = energy_target; for (k = 0; k < nb_subfr; k++) { cross_corr = Inlines.silk_ADD32(cross_corr, Inlines.MatrixGet(cross_corr_st3, k, j, nb_cbk_search).Values[lag_counter]); energy = Inlines.silk_ADD32(energy, Inlines.MatrixGet(energies_st3, k, j, nb_cbk_search).Values[lag_counter]); Inlines.OpusAssert(energy >= 0); } if (cross_corr > 0) { CCmax_new = Inlines.silk_DIV32_varQ(cross_corr, energy, 13 + 1); /* Q13 */ /* Reduce depending on flatness of contour */ diff = short.MaxValue - Inlines.silk_MUL(contour_bias_Q15, j); /* Q15 */ Inlines.OpusAssert(diff == Inlines.silk_SAT16(diff)); CCmax_new = Inlines.silk_SMULWB(CCmax_new, diff); /* Q14 */ } else { CCmax_new = 0; } if (CCmax_new > CCmax && (d + Tables.silk_CB_lags_stage3[0][j]) <= max_lag) { CCmax = CCmax_new; lag_new = d; CBimax = j; } } lag_counter++; } for (k = 0; k < nb_subfr; k++) { pitch_out[k] = lag_new + Lag_CB_ptr[k][CBimax]; pitch_out[k] = Inlines.silk_LIMIT(pitch_out[k], min_lag, SilkConstants.PE_MAX_LAG_MS * Fs_kHz); } lagIndex.Val = (short)(lag_new - min_lag); contourIndex.Val = (sbyte)CBimax; } else /* Fs_kHz == 8 */ /* Save Lags */ { for (k = 0; k < nb_subfr; k++) { pitch_out[k] = lag + Lag_CB_ptr[k][CBimax]; pitch_out[k] = Inlines.silk_LIMIT(pitch_out[k], MIN_LAG_8KHZ, SilkConstants.PE_MAX_LAG_MS * 8); } lagIndex.Val = (short)(lag - MIN_LAG_8KHZ); contourIndex.Val = (sbyte)CBimax; } Inlines.OpusAssert(lagIndex.Val >= 0); /* return as voiced */ return(0); }
/**************************************************************/ /* Compute noise shaping coefficients and initial gain values */ /**************************************************************/ internal static void silk_noise_shape_analysis( SilkChannelEncoder psEnc, /* I/O Encoder state FIX */ SilkEncoderControl psEncCtrl, /* I/O Encoder control FIX */ short[] pitch_res, /* I LPC residual from pitch analysis */ int pitch_res_ptr, short[] x, /* I Input signal [ frame_length + la_shape ] */ int x_ptr ) { SilkShapeState psShapeSt = psEnc.sShape; int k, i, nSamples, Qnrg, b_Q14, warping_Q16, scale = 0; int SNR_adj_dB_Q7, HarmBoost_Q16, HarmShapeGain_Q16, Tilt_Q16, tmp32; int nrg, pre_nrg_Q30, log_energy_Q7, log_energy_prev_Q7, energy_variation_Q7; int delta_Q16, BWExp1_Q16, BWExp2_Q16, gain_mult_Q16, gain_add_Q16, strength_Q16, b_Q8; int[] auto_corr = new int[SilkConstants.MAX_SHAPE_LPC_ORDER + 1]; int[] refl_coef_Q16 = new int[SilkConstants.MAX_SHAPE_LPC_ORDER]; int[] AR1_Q24 = new int[SilkConstants.MAX_SHAPE_LPC_ORDER]; int[] AR2_Q24 = new int[SilkConstants.MAX_SHAPE_LPC_ORDER]; short[] x_windowed; int pitch_res_ptr2; int x_ptr2; /* Point to start of first LPC analysis block */ x_ptr2 = x_ptr - psEnc.la_shape; /****************/ /* GAIN CONTROL */ /****************/ SNR_adj_dB_Q7 = psEnc.SNR_dB_Q7; /* Input quality is the average of the quality in the lowest two VAD bands */ psEncCtrl.input_quality_Q14 = (int)Inlines.silk_RSHIFT((int)psEnc.input_quality_bands_Q15[0] + psEnc.input_quality_bands_Q15[1], 2); /* Coding quality level, between 0.0_Q0 and 1.0_Q0, but in Q14 */ psEncCtrl.coding_quality_Q14 = Inlines.silk_RSHIFT(Sigmoid.silk_sigm_Q15(Inlines.silk_RSHIFT_ROUND(SNR_adj_dB_Q7 - ((int)((20.0f) * ((long)1 << (7)) + 0.5)) /*Inlines.SILK_CONST(20.0f, 7)*/, 4)), 1); /* Reduce coding SNR during low speech activity */ if (psEnc.useCBR == 0) { b_Q8 = ((int)((1.0f) * ((long)1 << (8)) + 0.5)) /*Inlines.SILK_CONST(1.0f, 8)*/ - psEnc.speech_activity_Q8; b_Q8 = Inlines.silk_SMULWB(Inlines.silk_LSHIFT(b_Q8, 8), b_Q8); SNR_adj_dB_Q7 = Inlines.silk_SMLAWB(SNR_adj_dB_Q7, Inlines.silk_SMULBB(((int)((0 - TuningParameters.BG_SNR_DECR_dB) * ((long)1 << (7)) + 0.5)) /*Inlines.SILK_CONST(0 - TuningParameters.BG_SNR_DECR_dB, 7)*/ >> (4 + 1), b_Q8), /* Q11*/ Inlines.silk_SMULWB(((int)((1.0f) * ((long)1 << (14)) + 0.5)) /*Inlines.SILK_CONST(1.0f, 14)*/ + psEncCtrl.input_quality_Q14, psEncCtrl.coding_quality_Q14)); /* Q12*/ } if (psEnc.indices.signalType == SilkConstants.TYPE_VOICED) { /* Reduce gains for periodic signals */ SNR_adj_dB_Q7 = Inlines.silk_SMLAWB(SNR_adj_dB_Q7, ((int)((TuningParameters.HARM_SNR_INCR_dB) * ((long)1 << (8)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.HARM_SNR_INCR_dB, 8)*/, psEnc.LTPCorr_Q15); } else { /* For unvoiced signals and low-quality input, adjust the quality slower than SNR_dB setting */ SNR_adj_dB_Q7 = Inlines.silk_SMLAWB(SNR_adj_dB_Q7, Inlines.silk_SMLAWB(((int)((6.0f) * ((long)1 << (9)) + 0.5)) /*Inlines.SILK_CONST(6.0f, 9)*/, -((int)((0.4f) * ((long)1 << (18)) + 0.5)) /*Inlines.SILK_CONST(0.4f, 18)*/, psEnc.SNR_dB_Q7), ((int)((1.0f) * ((long)1 << (14)) + 0.5)) /*Inlines.SILK_CONST(1.0f, 14)*/ - psEncCtrl.input_quality_Q14); } /*************************/ /* SPARSENESS PROCESSING */ /*************************/ /* Set quantizer offset */ if (psEnc.indices.signalType == SilkConstants.TYPE_VOICED) { /* Initially set to 0; may be overruled in process_gains(..) */ psEnc.indices.quantOffsetType = 0; psEncCtrl.sparseness_Q8 = 0; } else { /* Sparseness measure, based on relative fluctuations of energy per 2 milliseconds */ nSamples = Inlines.silk_LSHIFT(psEnc.fs_kHz, 1); energy_variation_Q7 = 0; log_energy_prev_Q7 = 0; pitch_res_ptr2 = pitch_res_ptr; for (k = 0; k < Inlines.silk_SMULBB(SilkConstants.SUB_FRAME_LENGTH_MS, psEnc.nb_subfr) / 2; k++) { SumSqrShift.silk_sum_sqr_shift(out nrg, out scale, pitch_res, pitch_res_ptr2, nSamples); nrg += Inlines.silk_RSHIFT(nSamples, scale); /* Q(-scale)*/ log_energy_Q7 = Inlines.silk_lin2log(nrg); if (k > 0) { energy_variation_Q7 += Inlines.silk_abs(log_energy_Q7 - log_energy_prev_Q7); } log_energy_prev_Q7 = log_energy_Q7; pitch_res_ptr2 += nSamples; } psEncCtrl.sparseness_Q8 = Inlines.silk_RSHIFT(Sigmoid.silk_sigm_Q15(Inlines.silk_SMULWB(energy_variation_Q7 - ((int)((5.0f) * ((long)1 << (7)) + 0.5)) /*Inlines.SILK_CONST(5.0f, 7)*/, ((int)((0.1f) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(0.1f, 16)*/)), 7); /* Set quantization offset depending on sparseness measure */ if (psEncCtrl.sparseness_Q8 > ((int)((TuningParameters.SPARSENESS_THRESHOLD_QNT_OFFSET) * ((long)1 << (8)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.SPARSENESS_THRESHOLD_QNT_OFFSET, 8)*/) { psEnc.indices.quantOffsetType = 0; } else { psEnc.indices.quantOffsetType = 1; } /* Increase coding SNR for sparse signals */ SNR_adj_dB_Q7 = Inlines.silk_SMLAWB(SNR_adj_dB_Q7, ((int)((TuningParameters.SPARSE_SNR_INCR_dB) * ((long)1 << (15)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.SPARSE_SNR_INCR_dB, 15)*/, psEncCtrl.sparseness_Q8 - ((int)((0.5f) * ((long)1 << (8)) + 0.5)) /*Inlines.SILK_CONST(0.5f, 8)*/); } /*******************************/ /* Control bandwidth expansion */ /*******************************/ /* More BWE for signals with high prediction gain */ strength_Q16 = Inlines.silk_SMULWB(psEncCtrl.predGain_Q16, ((int)((TuningParameters.FIND_PITCH_WHITE_NOISE_FRACTION) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.FIND_PITCH_WHITE_NOISE_FRACTION, 16)*/); BWExp1_Q16 = BWExp2_Q16 = Inlines.silk_DIV32_varQ(((int)((TuningParameters.BANDWIDTH_EXPANSION) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.BANDWIDTH_EXPANSION, 16)*/, Inlines.silk_SMLAWW(((int)((1.0f) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(1.0f, 16)*/, strength_Q16, strength_Q16), 16); delta_Q16 = Inlines.silk_SMULWB(((int)((1.0f) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(1.0f, 16)*/ - Inlines.silk_SMULBB(3, psEncCtrl.coding_quality_Q14), ((int)((TuningParameters.LOW_RATE_BANDWIDTH_EXPANSION_DELTA) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.LOW_RATE_BANDWIDTH_EXPANSION_DELTA, 16)*/); BWExp1_Q16 = Inlines.silk_SUB32(BWExp1_Q16, delta_Q16); BWExp2_Q16 = Inlines.silk_ADD32(BWExp2_Q16, delta_Q16); /* BWExp1 will be applied after BWExp2, so make it relative */ BWExp1_Q16 = Inlines.silk_DIV32_16(Inlines.silk_LSHIFT(BWExp1_Q16, 14), Inlines.silk_RSHIFT(BWExp2_Q16, 2)); if (psEnc.warping_Q16 > 0) { /* Slightly more warping in analysis will move quantization noise up in frequency, where it's better masked */ warping_Q16 = Inlines.silk_SMLAWB(psEnc.warping_Q16, (int)psEncCtrl.coding_quality_Q14, ((int)((0.01f) * ((long)1 << (18)) + 0.5)) /*Inlines.SILK_CONST(0.01f, 18)*/); } else { warping_Q16 = 0; } /********************************************/ /* Compute noise shaping AR coefs and gains */ /********************************************/ x_windowed = new short[psEnc.shapeWinLength]; for (k = 0; k < psEnc.nb_subfr; k++) { /* Apply window: sine slope followed by flat part followed by cosine slope */ int shift, slope_part, flat_part; flat_part = psEnc.fs_kHz * 3; slope_part = Inlines.silk_RSHIFT(psEnc.shapeWinLength - flat_part, 1); ApplySineWindow.silk_apply_sine_window(x_windowed, 0, x, x_ptr2, 1, slope_part); shift = slope_part; Array.Copy(x, x_ptr2 + shift, x_windowed, shift, flat_part); shift += flat_part; ApplySineWindow.silk_apply_sine_window(x_windowed, shift, x, x_ptr2 + shift, 2, slope_part); /* Update pointer: next LPC analysis block */ x_ptr2 += psEnc.subfr_length; BoxedValueInt scale_boxed = new BoxedValueInt(scale); if (psEnc.warping_Q16 > 0) { /* Calculate warped auto correlation */ Autocorrelation.silk_warped_autocorrelation(auto_corr, scale_boxed, x_windowed, warping_Q16, psEnc.shapeWinLength, psEnc.shapingLPCOrder); } else { /* Calculate regular auto correlation */ Autocorrelation.silk_autocorr(auto_corr, scale_boxed, x_windowed, psEnc.shapeWinLength, psEnc.shapingLPCOrder + 1); } scale = scale_boxed.Val; /* Add white noise, as a fraction of energy */ auto_corr[0] = Inlines.silk_ADD32(auto_corr[0], Inlines.silk_max_32(Inlines.silk_SMULWB(Inlines.silk_RSHIFT(auto_corr[0], 4), ((int)((TuningParameters.SHAPE_WHITE_NOISE_FRACTION) * ((long)1 << (20)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.SHAPE_WHITE_NOISE_FRACTION, 20)*/), 1)); /* Calculate the reflection coefficients using schur */ nrg = Schur.silk_schur64(refl_coef_Q16, auto_corr, psEnc.shapingLPCOrder); Inlines.OpusAssert(nrg >= 0); /* Convert reflection coefficients to prediction coefficients */ K2A.silk_k2a_Q16(AR2_Q24, refl_coef_Q16, psEnc.shapingLPCOrder); Qnrg = -scale; /* range: -12...30*/ Inlines.OpusAssert(Qnrg >= -12); Inlines.OpusAssert(Qnrg <= 30); /* Make sure that Qnrg is an even number */ if ((Qnrg & 1) != 0) { Qnrg -= 1; nrg >>= 1; } tmp32 = Inlines.silk_SQRT_APPROX(nrg); Qnrg >>= 1; /* range: -6...15*/ psEncCtrl.Gains_Q16[k] = Inlines.silk_LSHIFT_SAT32(tmp32, 16 - Qnrg); if (psEnc.warping_Q16 > 0) { /* Adjust gain for warping */ gain_mult_Q16 = warped_gain(AR2_Q24, warping_Q16, psEnc.shapingLPCOrder); Inlines.OpusAssert(psEncCtrl.Gains_Q16[k] >= 0); if (Inlines.silk_SMULWW(Inlines.silk_RSHIFT_ROUND(psEncCtrl.Gains_Q16[k], 1), gain_mult_Q16) >= (int.MaxValue >> 1)) { psEncCtrl.Gains_Q16[k] = int.MaxValue; } else { psEncCtrl.Gains_Q16[k] = Inlines.silk_SMULWW(psEncCtrl.Gains_Q16[k], gain_mult_Q16); } } /* Bandwidth expansion for synthesis filter shaping */ BWExpander.silk_bwexpander_32(AR2_Q24, psEnc.shapingLPCOrder, BWExp2_Q16); /* Compute noise shaping filter coefficients */ Array.Copy(AR2_Q24, AR1_Q24, psEnc.shapingLPCOrder); /* Bandwidth expansion for analysis filter shaping */ Inlines.OpusAssert(BWExp1_Q16 <= ((int)((1.0f) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(1.0f, 16)*/); BWExpander.silk_bwexpander_32(AR1_Q24, psEnc.shapingLPCOrder, BWExp1_Q16); /* Ratio of prediction gains, in energy domain */ pre_nrg_Q30 = LPCInversePredGain.silk_LPC_inverse_pred_gain_Q24(AR2_Q24, psEnc.shapingLPCOrder); nrg = LPCInversePredGain.silk_LPC_inverse_pred_gain_Q24(AR1_Q24, psEnc.shapingLPCOrder); /*psEncCtrl.GainsPre[ k ] = 1.0f - 0.7f * ( 1.0f - pre_nrg / nrg ) = 0.3f + 0.7f * pre_nrg / nrg;*/ pre_nrg_Q30 = Inlines.silk_LSHIFT32(Inlines.silk_SMULWB(pre_nrg_Q30, ((int)((0.7f) * ((long)1 << (15)) + 0.5)) /*Inlines.SILK_CONST(0.7f, 15)*/), 1); psEncCtrl.GainsPre_Q14[k] = (int)((int)((0.3f) * ((long)1 << (14)) + 0.5)) /*Inlines.SILK_CONST(0.3f, 14)*/ + Inlines.silk_DIV32_varQ(pre_nrg_Q30, nrg, 14); /* Convert to monic warped prediction coefficients and limit absolute values */ limit_warped_coefs(AR2_Q24, AR1_Q24, warping_Q16, ((int)((3.999f) * ((long)1 << (24)) + 0.5)) /*Inlines.SILK_CONST(3.999f, 24)*/, psEnc.shapingLPCOrder); /* Convert from Q24 to Q13 and store in int16 */ for (i = 0; i < psEnc.shapingLPCOrder; i++) { psEncCtrl.AR1_Q13[k * SilkConstants.MAX_SHAPE_LPC_ORDER + i] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(AR1_Q24[i], 11)); psEncCtrl.AR2_Q13[k * SilkConstants.MAX_SHAPE_LPC_ORDER + i] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(AR2_Q24[i], 11)); } } /*****************/ /* Gain tweaking */ /*****************/ /* Increase gains during low speech activity and put lower limit on gains */ gain_mult_Q16 = Inlines.silk_log2lin(-Inlines.silk_SMLAWB(-((int)((16.0f) * ((long)1 << (7)) + 0.5)) /*Inlines.SILK_CONST(16.0f, 7)*/, SNR_adj_dB_Q7, ((int)((0.16f) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(0.16f, 16)*/)); gain_add_Q16 = Inlines.silk_log2lin(Inlines.silk_SMLAWB(((int)((16.0f) * ((long)1 << (7)) + 0.5)) /*Inlines.SILK_CONST(16.0f, 7)*/, ((int)((SilkConstants.MIN_QGAIN_DB) * ((long)1 << (7)) + 0.5)) /*Inlines.SILK_CONST(SilkConstants.MIN_QGAIN_DB, 7)*/, ((int)((0.16f) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(0.16f, 16)*/)); Inlines.OpusAssert(gain_mult_Q16 > 0); for (k = 0; k < psEnc.nb_subfr; k++) { psEncCtrl.Gains_Q16[k] = Inlines.silk_SMULWW(psEncCtrl.Gains_Q16[k], gain_mult_Q16); Inlines.OpusAssert(psEncCtrl.Gains_Q16[k] >= 0); psEncCtrl.Gains_Q16[k] = Inlines.silk_ADD_POS_SAT32(psEncCtrl.Gains_Q16[k], gain_add_Q16); } gain_mult_Q16 = ((int)((1.0f) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(1.0f, 16)*/ + Inlines.silk_RSHIFT_ROUND(Inlines.silk_MLA(((int)((TuningParameters.INPUT_TILT) * ((long)1 << (26)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.INPUT_TILT, 26)*/, psEncCtrl.coding_quality_Q14, ((int)((TuningParameters.HIGH_RATE_INPUT_TILT) * ((long)1 << (12)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.HIGH_RATE_INPUT_TILT, 12)*/), 10); for (k = 0; k < psEnc.nb_subfr; k++) { psEncCtrl.GainsPre_Q14[k] = Inlines.silk_SMULWB(gain_mult_Q16, psEncCtrl.GainsPre_Q14[k]); } /************************************************/ /* Control low-frequency shaping and noise tilt */ /************************************************/ /* Less low frequency shaping for noisy inputs */ strength_Q16 = Inlines.silk_MUL(((int)((TuningParameters.LOW_FREQ_SHAPING) * ((long)1 << (4)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.LOW_FREQ_SHAPING, 4)*/, Inlines.silk_SMLAWB(((int)((1.0f) * ((long)1 << (12)) + 0.5)) /*Inlines.SILK_CONST(1.0f, 12)*/, ((int)((TuningParameters.LOW_QUALITY_LOW_FREQ_SHAPING_DECR) * ((long)1 << (13)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.LOW_QUALITY_LOW_FREQ_SHAPING_DECR, 13)*/, psEnc.input_quality_bands_Q15[0] - ((int)((1.0f) * ((long)1 << (15)) + 0.5)) /*Inlines.SILK_CONST(1.0f, 15)*/)); strength_Q16 = Inlines.silk_RSHIFT(Inlines.silk_MUL(strength_Q16, psEnc.speech_activity_Q8), 8); if (psEnc.indices.signalType == SilkConstants.TYPE_VOICED) { /* Reduce low frequencies quantization noise for periodic signals, depending on pitch lag */ /*f = 400; freqz([1, -0.98 + 2e-4 * f], [1, -0.97 + 7e-4 * f], 2^12, Fs); axis([0, 1000, -10, 1])*/ int fs_kHz_inv = Inlines.silk_DIV32_16(((int)((0.2f) * ((long)1 << (14)) + 0.5)) /*Inlines.SILK_CONST(0.2f, 14)*/, psEnc.fs_kHz); for (k = 0; k < psEnc.nb_subfr; k++) { b_Q14 = fs_kHz_inv + Inlines.silk_DIV32_16(((int)((3.0f) * ((long)1 << (14)) + 0.5)) /*Inlines.SILK_CONST(3.0f, 14)*/, psEncCtrl.pitchL[k]); /* Pack two coefficients in one int32 */ psEncCtrl.LF_shp_Q14[k] = Inlines.silk_LSHIFT(((int)((1.0f) * ((long)1 << (14)) + 0.5)) /*Inlines.SILK_CONST(1.0f, 14)*/ - b_Q14 - Inlines.silk_SMULWB(strength_Q16, b_Q14), 16); psEncCtrl.LF_shp_Q14[k] |= (b_Q14 - ((int)((1.0f) * ((long)1 << (14)) + 0.5)) /*Inlines.SILK_CONST(1.0f, 14)*/) & 0xFFFF; // opus bug: again, cast to ushort was done here where bitwise masking was intended } Inlines.OpusAssert(((int)((TuningParameters.HARM_HP_NOISE_COEF) * ((long)1 << (24)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.HARM_HP_NOISE_COEF, 24)*/ < ((int)((0.5f) * ((long)1 << (24)) + 0.5)) /*Inlines.SILK_CONST(0.5f, 24)*/); /* Guarantees that second argument to SMULWB() is within range of an short*/ Tilt_Q16 = -((int)((TuningParameters.HP_NOISE_COEF) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.HP_NOISE_COEF, 16)*/ - Inlines.silk_SMULWB(((int)((1.0f) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(1.0f, 16)*/ - ((int)((TuningParameters.HP_NOISE_COEF) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.HP_NOISE_COEF, 16)*/, Inlines.silk_SMULWB(((int)((TuningParameters.HARM_HP_NOISE_COEF) * ((long)1 << (24)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.HARM_HP_NOISE_COEF, 24)*/, psEnc.speech_activity_Q8)); } else { b_Q14 = Inlines.silk_DIV32_16(21299, psEnc.fs_kHz); /* 1.3_Q0 = 21299_Q14*/ /* Pack two coefficients in one int32 */ psEncCtrl.LF_shp_Q14[0] = Inlines.silk_LSHIFT(((int)((1.0f) * ((long)1 << (14)) + 0.5)) /*Inlines.SILK_CONST(1.0f, 14)*/ - b_Q14 - Inlines.silk_SMULWB(strength_Q16, Inlines.silk_SMULWB(((int)((0.6f) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(0.6f, 16)*/, b_Q14)), 16); psEncCtrl.LF_shp_Q14[0] |= (b_Q14 - ((int)((1.0f) * ((long)1 << (14)) + 0.5)) /*Inlines.SILK_CONST(1.0f, 14)*/) & 0xFFFF; // opus bug: cast to ushort is better expressed as a bitwise operator, otherwise runtime analysis might flag it as an overflow error for (k = 1; k < psEnc.nb_subfr; k++) { psEncCtrl.LF_shp_Q14[k] = psEncCtrl.LF_shp_Q14[0]; } Tilt_Q16 = -((int)((TuningParameters.HP_NOISE_COEF) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.HP_NOISE_COEF, 16)*/; } /****************************/ /* HARMONIC SHAPING CONTROL */ /****************************/ /* Control boosting of harmonic frequencies */ HarmBoost_Q16 = Inlines.silk_SMULWB(Inlines.silk_SMULWB(((int)((1.0f) * ((long)1 << (17)) + 0.5)) /*Inlines.SILK_CONST(1.0f, 17)*/ - Inlines.silk_LSHIFT(psEncCtrl.coding_quality_Q14, 3), psEnc.LTPCorr_Q15), ((int)((TuningParameters.LOW_RATE_HARMONIC_BOOST) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.LOW_RATE_HARMONIC_BOOST, 16)*/); /* More harmonic boost for noisy input signals */ HarmBoost_Q16 = Inlines.silk_SMLAWB(HarmBoost_Q16, ((int)((1.0f) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(1.0f, 16)*/ - Inlines.silk_LSHIFT(psEncCtrl.input_quality_Q14, 2), ((int)((TuningParameters.LOW_INPUT_QUALITY_HARMONIC_BOOST) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.LOW_INPUT_QUALITY_HARMONIC_BOOST, 16)*/); if (SilkConstants.USE_HARM_SHAPING != 0 && psEnc.indices.signalType == SilkConstants.TYPE_VOICED) { /* More harmonic noise shaping for high bitrates or noisy input */ HarmShapeGain_Q16 = Inlines.silk_SMLAWB(((int)((TuningParameters.HARMONIC_SHAPING) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.HARMONIC_SHAPING, 16)*/, ((int)((1.0f) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(1.0f, 16)*/ - Inlines.silk_SMULWB(((int)((1.0f) * ((long)1 << (18)) + 0.5)) /*Inlines.SILK_CONST(1.0f, 18)*/ - Inlines.silk_LSHIFT(psEncCtrl.coding_quality_Q14, 4), psEncCtrl.input_quality_Q14), ((int)((TuningParameters.HIGH_RATE_OR_LOW_QUALITY_HARMONIC_SHAPING) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.HIGH_RATE_OR_LOW_QUALITY_HARMONIC_SHAPING, 16)*/); /* Less harmonic noise shaping for less periodic signals */ HarmShapeGain_Q16 = Inlines.silk_SMULWB(Inlines.silk_LSHIFT(HarmShapeGain_Q16, 1), Inlines.silk_SQRT_APPROX(Inlines.silk_LSHIFT(psEnc.LTPCorr_Q15, 15))); } else { HarmShapeGain_Q16 = 0; } /*************************/ /* Smooth over subframes */ /*************************/ for (k = 0; k < SilkConstants.MAX_NB_SUBFR; k++) { psShapeSt.HarmBoost_smth_Q16 = Inlines.silk_SMLAWB(psShapeSt.HarmBoost_smth_Q16, HarmBoost_Q16 - psShapeSt.HarmBoost_smth_Q16, ((int)((TuningParameters.SUBFR_SMTH_COEF) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.SUBFR_SMTH_COEF, 16)*/); psShapeSt.HarmShapeGain_smth_Q16 = Inlines.silk_SMLAWB(psShapeSt.HarmShapeGain_smth_Q16, HarmShapeGain_Q16 - psShapeSt.HarmShapeGain_smth_Q16, ((int)((TuningParameters.SUBFR_SMTH_COEF) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.SUBFR_SMTH_COEF, 16)*/); psShapeSt.Tilt_smth_Q16 = Inlines.silk_SMLAWB(psShapeSt.Tilt_smth_Q16, Tilt_Q16 - psShapeSt.Tilt_smth_Q16, ((int)((TuningParameters.SUBFR_SMTH_COEF) * ((long)1 << (16)) + 0.5)) /*Inlines.SILK_CONST(TuningParameters.SUBFR_SMTH_COEF, 16)*/); psEncCtrl.HarmBoost_Q14[k] = (int)Inlines.silk_RSHIFT_ROUND(psShapeSt.HarmBoost_smth_Q16, 2); psEncCtrl.HarmShapeGain_Q14[k] = (int)Inlines.silk_RSHIFT_ROUND(psShapeSt.HarmShapeGain_smth_Q16, 2); psEncCtrl.Tilt_Q14[k] = (int)Inlines.silk_RSHIFT_ROUND(psShapeSt.Tilt_smth_Q16, 2); } }
/* 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; } }