| /* |
| * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| |
| /* |
| * lpc_masking_model.c |
| * |
| * LPC analysis and filtering functions |
| * |
| */ |
| |
| #include "lpc_masking_model.h" |
| |
| #include <limits.h> /* For LLONG_MAX and LLONG_MIN. */ |
| #include "codec.h" |
| #include "entropy_coding.h" |
| #include "settings.h" |
| |
| /* The conversion is implemented by the step-down algorithm */ |
| void WebRtcSpl_AToK_JSK( |
| int16_t *a16, /* Q11 */ |
| int16_t useOrder, |
| int16_t *k16 /* Q15 */ |
| ) |
| { |
| int m, k; |
| int32_t tmp32[MAX_AR_MODEL_ORDER]; |
| int32_t tmp32b; |
| int32_t tmp_inv_denum32; |
| int16_t tmp_inv_denum16; |
| |
| k16[useOrder-1] = a16[useOrder] << 4; // Q11<<4 => Q15 |
| |
| for (m=useOrder-1; m>0; m--) { |
| // (1 - k^2) in Q30 |
| tmp_inv_denum32 = 1073741823 - k16[m] * k16[m]; |
| tmp_inv_denum16 = (int16_t)(tmp_inv_denum32 >> 15); // (1 - k^2) in Q15. |
| |
| for (k=1; k<=m; k++) { |
| tmp32b = (a16[k] << 16) - ((k16[m] * a16[m - k + 1]) << 1); |
| |
| tmp32[k] = WebRtcSpl_DivW32W16(tmp32b, tmp_inv_denum16); //Q27/Q15 = Q12 |
| } |
| |
| for (k=1; k<m; k++) { |
| a16[k] = (int16_t)(tmp32[k] >> 1); // Q12>>1 => Q11 |
| } |
| |
| tmp32[m] = WEBRTC_SPL_SAT(4092, tmp32[m], -4092); |
| k16[m - 1] = (int16_t)(tmp32[m] << 3); // Q12<<3 => Q15 |
| } |
| |
| return; |
| } |
| |
| |
| |
| |
| |
| int16_t WebRtcSpl_LevinsonW32_JSK( |
| int32_t *R, /* (i) Autocorrelation of length >= order+1 */ |
| int16_t *A, /* (o) A[0..order] LPC coefficients (Q11) */ |
| int16_t *K, /* (o) K[0...order-1] Reflection coefficients (Q15) */ |
| int16_t order /* (i) filter order */ |
| ) { |
| int16_t i, j; |
| int16_t R_hi[LEVINSON_MAX_ORDER+1], R_low[LEVINSON_MAX_ORDER+1]; |
| /* Aurocorr coefficients in high precision */ |
| int16_t A_hi[LEVINSON_MAX_ORDER+1], A_low[LEVINSON_MAX_ORDER+1]; |
| /* LPC coefficients in high precicion */ |
| int16_t A_upd_hi[LEVINSON_MAX_ORDER+1], A_upd_low[LEVINSON_MAX_ORDER+1]; |
| /* LPC coefficients for next iteration */ |
| int16_t K_hi, K_low; /* reflection coefficient in high precision */ |
| int16_t Alpha_hi, Alpha_low, Alpha_exp; /* Prediction gain Alpha in high precision |
| and with scale factor */ |
| int16_t tmp_hi, tmp_low; |
| int32_t temp1W32, temp2W32, temp3W32; |
| int16_t norm; |
| |
| /* Normalize the autocorrelation R[0]...R[order+1] */ |
| |
| norm = WebRtcSpl_NormW32(R[0]); |
| |
| for (i=order;i>=0;i--) { |
| temp1W32 = R[i] << norm; |
| /* Put R in hi and low format */ |
| R_hi[i] = (int16_t)(temp1W32 >> 16); |
| R_low[i] = (int16_t)((temp1W32 - ((int32_t)R_hi[i] << 16)) >> 1); |
| } |
| |
| /* K = A[1] = -R[1] / R[0] */ |
| |
| temp2W32 = (R_hi[1] << 16) + (R_low[1] << 1); /* R[1] in Q31 */ |
| temp3W32 = WEBRTC_SPL_ABS_W32(temp2W32); /* abs R[1] */ |
| temp1W32 = WebRtcSpl_DivW32HiLow(temp3W32, R_hi[0], R_low[0]); /* abs(R[1])/R[0] in Q31 */ |
| /* Put back the sign on R[1] */ |
| if (temp2W32 > 0) { |
| temp1W32 = -temp1W32; |
| } |
| |
| /* Put K in hi and low format */ |
| K_hi = (int16_t)(temp1W32 >> 16); |
| K_low = (int16_t)((temp1W32 - ((int32_t)K_hi << 16)) >> 1); |
| |
| /* Store first reflection coefficient */ |
| K[0] = K_hi; |
| |
| temp1W32 >>= 4; /* A[1] in Q27. */ |
| |
| /* Put A[1] in hi and low format */ |
| A_hi[1] = (int16_t)(temp1W32 >> 16); |
| A_low[1] = (int16_t)((temp1W32 - ((int32_t)A_hi[1] << 16)) >> 1); |
| |
| /* Alpha = R[0] * (1-K^2) */ |
| |
| temp1W32 = (((K_hi * K_low) >> 14) + K_hi * K_hi) << 1; /* = k^2 in Q31 */ |
| |
| temp1W32 = WEBRTC_SPL_ABS_W32(temp1W32); /* Guard against <0 */ |
| temp1W32 = (int32_t)0x7fffffffL - temp1W32; /* temp1W32 = (1 - K[0]*K[0]) in Q31 */ |
| |
| /* Store temp1W32 = 1 - K[0]*K[0] on hi and low format */ |
| tmp_hi = (int16_t)(temp1W32 >> 16); |
| tmp_low = (int16_t)((temp1W32 - ((int32_t)tmp_hi << 16)) >> 1); |
| |
| /* Calculate Alpha in Q31 */ |
| temp1W32 = (R_hi[0] * tmp_hi + ((R_hi[0] * tmp_low) >> 15) + |
| ((R_low[0] * tmp_hi) >> 15)) << 1; |
| |
| /* Normalize Alpha and put it in hi and low format */ |
| |
| Alpha_exp = WebRtcSpl_NormW32(temp1W32); |
| temp1W32 <<= Alpha_exp; |
| Alpha_hi = (int16_t)(temp1W32 >> 16); |
| Alpha_low = (int16_t)((temp1W32 - ((int32_t)Alpha_hi<< 16)) >> 1); |
| |
| /* Perform the iterative calculations in the |
| Levinson Durbin algorithm */ |
| |
| for (i=2; i<=order; i++) |
| { |
| |
| /* ---- |
| \ |
| temp1W32 = R[i] + > R[j]*A[i-j] |
| / |
| ---- |
| j=1..i-1 |
| */ |
| |
| temp1W32 = 0; |
| |
| for(j=1; j<i; j++) { |
| /* temp1W32 is in Q31 */ |
| temp1W32 += ((R_hi[j] * A_hi[i - j]) << 1) + |
| ((((R_hi[j] * A_low[i - j]) >> 15) + |
| ((R_low[j] * A_hi[i - j]) >> 15)) << 1); |
| } |
| |
| temp1W32 <<= 4; |
| temp1W32 += (R_hi[i] << 16) + (R_low[i] << 1); |
| |
| /* K = -temp1W32 / Alpha */ |
| temp2W32 = WEBRTC_SPL_ABS_W32(temp1W32); /* abs(temp1W32) */ |
| temp3W32 = WebRtcSpl_DivW32HiLow(temp2W32, Alpha_hi, Alpha_low); /* abs(temp1W32)/Alpha */ |
| |
| /* Put the sign of temp1W32 back again */ |
| if (temp1W32 > 0) { |
| temp3W32 = -temp3W32; |
| } |
| |
| /* Use the Alpha shifts from earlier to denormalize */ |
| norm = WebRtcSpl_NormW32(temp3W32); |
| if ((Alpha_exp <= norm)||(temp3W32==0)) { |
| temp3W32 <<= Alpha_exp; |
| } else { |
| if (temp3W32 > 0) |
| { |
| temp3W32 = (int32_t)0x7fffffffL; |
| } else |
| { |
| temp3W32 = (int32_t)0x80000000L; |
| } |
| } |
| |
| /* Put K on hi and low format */ |
| K_hi = (int16_t)(temp3W32 >> 16); |
| K_low = (int16_t)((temp3W32 - ((int32_t)K_hi << 16)) >> 1); |
| |
| /* Store Reflection coefficient in Q15 */ |
| K[i-1] = K_hi; |
| |
| /* Test for unstable filter. If unstable return 0 and let the |
| user decide what to do in that case |
| */ |
| |
| if ((int32_t)WEBRTC_SPL_ABS_W16(K_hi) > (int32_t)32740) { |
| return(-i); /* Unstable filter */ |
| } |
| |
| /* |
| Compute updated LPC coefficient: Anew[i] |
| Anew[j]= A[j] + K*A[i-j] for j=1..i-1 |
| Anew[i]= K |
| */ |
| |
| for(j=1; j<i; j++) |
| { |
| temp1W32 = (A_hi[j] << 16) + (A_low[j] << 1); // temp1W32 = A[j] in Q27 |
| |
| temp1W32 += (K_hi * A_hi[i - j] + ((K_hi * A_low[i - j]) >> 15) + |
| ((K_low * A_hi[i - j]) >> 15)) << 1; // temp1W32 += K*A[i-j] in Q27. |
| |
| /* Put Anew in hi and low format */ |
| A_upd_hi[j] = (int16_t)(temp1W32 >> 16); |
| A_upd_low[j] = (int16_t)((temp1W32 - ((int32_t)A_upd_hi[j] << 16)) >> 1); |
| } |
| |
| temp3W32 >>= 4; /* temp3W32 = K in Q27 (Convert from Q31 to Q27) */ |
| |
| /* Store Anew in hi and low format */ |
| A_upd_hi[i] = (int16_t)(temp3W32 >> 16); |
| A_upd_low[i] = (int16_t)((temp3W32 - ((int32_t)A_upd_hi[i] << 16)) >> 1); |
| |
| /* Alpha = Alpha * (1-K^2) */ |
| |
| temp1W32 = (((K_hi * K_low) >> 14) + K_hi * K_hi) << 1; /* K*K in Q31 */ |
| |
| temp1W32 = WEBRTC_SPL_ABS_W32(temp1W32); /* Guard against <0 */ |
| temp1W32 = (int32_t)0x7fffffffL - temp1W32; /* 1 - K*K in Q31 */ |
| |
| /* Convert 1- K^2 in hi and low format */ |
| tmp_hi = (int16_t)(temp1W32 >> 16); |
| tmp_low = (int16_t)((temp1W32 - ((int32_t)tmp_hi << 16)) >> 1); |
| |
| /* Calculate Alpha = Alpha * (1-K^2) in Q31 */ |
| temp1W32 = (Alpha_hi * tmp_hi + ((Alpha_hi * tmp_low) >> 15) + |
| ((Alpha_low * tmp_hi) >> 15)) << 1; |
| |
| /* Normalize Alpha and store it on hi and low format */ |
| |
| norm = WebRtcSpl_NormW32(temp1W32); |
| temp1W32 <<= norm; |
| |
| Alpha_hi = (int16_t)(temp1W32 >> 16); |
| Alpha_low = (int16_t)((temp1W32 - ((int32_t)Alpha_hi << 16)) >> 1); |
| |
| /* Update the total nomalization of Alpha */ |
| Alpha_exp = Alpha_exp + norm; |
| |
| /* Update A[] */ |
| |
| for(j=1; j<=i; j++) |
| { |
| A_hi[j] =A_upd_hi[j]; |
| A_low[j] =A_upd_low[j]; |
| } |
| } |
| |
| /* |
| Set A[0] to 1.0 and store the A[i] i=1...order in Q12 |
| (Convert from Q27 and use rounding) |
| */ |
| |
| A[0] = 2048; |
| |
| for(i=1; i<=order; i++) { |
| /* temp1W32 in Q27 */ |
| temp1W32 = (A_hi[i] << 16) + (A_low[i] << 1); |
| /* Round and store upper word */ |
| A[i] = (int16_t)((temp1W32 + 32768) >> 16); |
| } |
| return(1); /* Stable filters */ |
| } |
| |
| |
| |
| |
| |
| /* window */ |
| /* Matlab generation of floating point code: |
| * t = (1:256)/257; r = 1-(1-t).^.45; w = sin(r*pi).^3; w = w/sum(w); plot((1:256)/8, w); grid; |
| * for k=1:16, fprintf(1, '%.8f, ', w(k*16 + (-15:0))); fprintf(1, '\n'); end |
| * All values are multiplyed with 2^21 in fixed point code. |
| */ |
| static const int16_t kWindowAutocorr[WINLEN] = { |
| 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 5, 6, |
| 8, 10, 12, 14, 17, 20, 24, 28, 33, 38, 43, 49, |
| 56, 63, 71, 79, 88, 98, 108, 119, 131, 143, 157, 171, |
| 186, 202, 219, 237, 256, 275, 296, 318, 341, 365, 390, 416, |
| 444, 472, 502, 533, 566, 600, 635, 671, 709, 748, 789, 831, |
| 875, 920, 967, 1015, 1065, 1116, 1170, 1224, 1281, 1339, 1399, 1461, |
| 1525, 1590, 1657, 1726, 1797, 1870, 1945, 2021, 2100, 2181, 2263, 2348, |
| 2434, 2523, 2614, 2706, 2801, 2898, 2997, 3099, 3202, 3307, 3415, 3525, |
| 3637, 3751, 3867, 3986, 4106, 4229, 4354, 4481, 4611, 4742, 4876, 5012, |
| 5150, 5291, 5433, 5578, 5725, 5874, 6025, 6178, 6333, 6490, 6650, 6811, |
| 6974, 7140, 7307, 7476, 7647, 7820, 7995, 8171, 8349, 8529, 8711, 8894, |
| 9079, 9265, 9453, 9642, 9833, 10024, 10217, 10412, 10607, 10803, 11000, 11199, |
| 11398, 11597, 11797, 11998, 12200, 12401, 12603, 12805, 13008, 13210, 13412, 13614, |
| 13815, 14016, 14216, 14416, 14615, 14813, 15009, 15205, 15399, 15591, 15782, 15971, |
| 16157, 16342, 16524, 16704, 16881, 17056, 17227, 17395, 17559, 17720, 17877, 18030, |
| 18179, 18323, 18462, 18597, 18727, 18851, 18970, 19082, 19189, 19290, 19384, 19471, |
| 19551, 19623, 19689, 19746, 19795, 19835, 19867, 19890, 19904, 19908, 19902, 19886, |
| 19860, 19823, 19775, 19715, 19644, 19561, 19465, 19357, 19237, 19102, 18955, 18793, |
| 18618, 18428, 18223, 18004, 17769, 17518, 17252, 16970, 16672, 16357, 16025, 15677, |
| 15311, 14929, 14529, 14111, 13677, 13225, 12755, 12268, 11764, 11243, 10706, 10152, |
| 9583, 8998, 8399, 7787, 7162, 6527, 5883, 5231, 4576, 3919, 3265, 2620, |
| 1990, 1386, 825, 333 |
| }; |
| |
| |
| /* By using a hearing threshold level in dB of -28 dB (higher value gives more noise), |
| the H_T_H (in float) can be calculated as: |
| H_T_H = pow(10.0, 0.05 * (-28.0)) = 0.039810717055350 |
| In Q19, H_T_H becomes round(0.039810717055350*2^19) ~= 20872, i.e. |
| H_T_H = 20872/524288.0, and H_T_HQ19 = 20872; |
| */ |
| |
| |
| /* The bandwidth expansion vectors are created from: |
| kPolyVecLo=[0.900000,0.810000,0.729000,0.656100,0.590490,0.531441,0.478297,0.430467,0.387420,0.348678,0.313811,0.282430]; |
| kPolyVecHi=[0.800000,0.640000,0.512000,0.409600,0.327680,0.262144]; |
| round(kPolyVecLo*32768) |
| round(kPolyVecHi*32768) |
| */ |
| static const int16_t kPolyVecLo[12] = { |
| 29491, 26542, 23888, 21499, 19349, 17414, 15673, 14106, 12695, 11425, 10283, 9255 |
| }; |
| static const int16_t kPolyVecHi[6] = { |
| 26214, 20972, 16777, 13422, 10737, 8590 |
| }; |
| |
| static __inline int32_t log2_Q8_LPC( uint32_t x ) { |
| |
| int32_t zeros; |
| int16_t frac; |
| |
| zeros=WebRtcSpl_NormU32(x); |
| frac = (int16_t)(((x << zeros) & 0x7FFFFFFF) >> 23); |
| |
| /* log2(x) */ |
| return ((31 - zeros) << 8) + frac; |
| } |
| |
| static const int16_t kMulPitchGain = -25; /* 200/256 in Q5 */ |
| static const int16_t kChngFactor = 3523; /* log10(2)*10/4*0.4/1.4=log10(2)/1.4= 0.2150 in Q14 */ |
| static const int16_t kExp2 = 11819; /* 1/log(2) */ |
| const int kShiftLowerBand = 11; /* Shift value for lower band in Q domain. */ |
| const int kShiftHigherBand = 12; /* Shift value for higher band in Q domain. */ |
| |
| void WebRtcIsacfix_GetVars(const int16_t *input, const int16_t *pitchGains_Q12, |
| uint32_t *oldEnergy, int16_t *varscale) |
| { |
| int k; |
| uint32_t nrgQ[4]; |
| int16_t nrgQlog[4]; |
| int16_t tmp16, chng1, chng2, chng3, chng4, tmp, chngQ, oldNrgQlog, pgQ, pg3; |
| int32_t expPg32; |
| int16_t expPg, divVal; |
| int16_t tmp16_1, tmp16_2; |
| |
| /* Calculate energies of first and second frame halfs */ |
| nrgQ[0]=0; |
| for (k = QLOOKAHEAD/2; k < (FRAMESAMPLES/4 + QLOOKAHEAD) / 2; k++) { |
| nrgQ[0] += (uint32_t)(input[k] * input[k]); |
| } |
| nrgQ[1]=0; |
| for ( ; k < (FRAMESAMPLES/2 + QLOOKAHEAD) / 2; k++) { |
| nrgQ[1] += (uint32_t)(input[k] * input[k]); |
| } |
| nrgQ[2]=0; |
| for ( ; k < (FRAMESAMPLES * 3 / 4 + QLOOKAHEAD) / 2; k++) { |
| nrgQ[2] += (uint32_t)(input[k] * input[k]); |
| } |
| nrgQ[3]=0; |
| for ( ; k < (FRAMESAMPLES + QLOOKAHEAD) / 2; k++) { |
| nrgQ[3] += (uint32_t)(input[k] * input[k]); |
| } |
| |
| for ( k=0; k<4; k++) { |
| nrgQlog[k] = (int16_t)log2_Q8_LPC(nrgQ[k]); /* log2(nrgQ) */ |
| } |
| oldNrgQlog = (int16_t)log2_Q8_LPC(*oldEnergy); |
| |
| /* Calculate average level change */ |
| chng1 = WEBRTC_SPL_ABS_W16(nrgQlog[3]-nrgQlog[2]); |
| chng2 = WEBRTC_SPL_ABS_W16(nrgQlog[2]-nrgQlog[1]); |
| chng3 = WEBRTC_SPL_ABS_W16(nrgQlog[1]-nrgQlog[0]); |
| chng4 = WEBRTC_SPL_ABS_W16(nrgQlog[0]-oldNrgQlog); |
| tmp = chng1+chng2+chng3+chng4; |
| chngQ = (int16_t)(tmp * kChngFactor >> 10); /* Q12 */ |
| chngQ += 2926; /* + 1.0/1.4 in Q12 */ |
| |
| /* Find average pitch gain */ |
| pgQ = 0; |
| for (k=0; k<4; k++) |
| { |
| pgQ += pitchGains_Q12[k]; |
| } |
| |
| pg3 = (int16_t)(pgQ * pgQ >> 11); // pgQ in Q(12+2)=Q14. Q14*Q14>>11 => Q17 |
| pg3 = (int16_t)(pgQ * pg3 >> 13); /* Q14*Q17>>13 =>Q18 */ |
| /* kMulPitchGain = -25 = -200 in Q-3. */ |
| pg3 = (int16_t)(pg3 * kMulPitchGain >> 5); // Q10 |
| tmp16=(int16_t)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(kExp2,pg3,13);/* Q13*Q10>>13 => Q10*/ |
| if (tmp16<0) { |
| tmp16_2 = (0x0400 | (tmp16 & 0x03FF)); |
| tmp16_1 = ((uint16_t)(tmp16 ^ 0xFFFF) >> 10) - 3; /* Gives result in Q14 */ |
| if (tmp16_1<0) |
| expPg = -(tmp16_2 << -tmp16_1); |
| else |
| expPg = -(tmp16_2 >> tmp16_1); |
| } else |
| expPg = (int16_t) -16384; /* 1 in Q14, since 2^0=1 */ |
| |
| expPg32 = (int32_t)expPg << 8; /* Q22 */ |
| divVal = WebRtcSpl_DivW32W16ResW16(expPg32, chngQ); /* Q22/Q12=Q10 */ |
| |
| tmp16=(int16_t)WEBRTC_SPL_MUL_16_16_RSFT_WITH_ROUND(kExp2,divVal,13);/* Q13*Q10>>13 => Q10*/ |
| if (tmp16<0) { |
| tmp16_2 = (0x0400 | (tmp16 & 0x03FF)); |
| tmp16_1 = ((uint16_t)(tmp16 ^ 0xFFFF) >> 10) - 3; /* Gives result in Q14 */ |
| if (tmp16_1<0) |
| expPg = tmp16_2 << -tmp16_1; |
| else |
| expPg = tmp16_2 >> tmp16_1; |
| } else |
| expPg = (int16_t) 16384; /* 1 in Q14, since 2^0=1 */ |
| |
| *varscale = expPg-1; |
| *oldEnergy = nrgQ[3]; |
| } |
| |
| |
| |
| static __inline int16_t exp2_Q10_T(int16_t x) { // Both in and out in Q10 |
| |
| int16_t tmp16_1, tmp16_2; |
| |
| tmp16_2=(int16_t)(0x0400|(x&0x03FF)); |
| tmp16_1 = -(x >> 10); |
| if(tmp16_1>0) |
| return tmp16_2 >> tmp16_1; |
| else |
| return tmp16_2 << -tmp16_1; |
| |
| } |
| |
| |
| // Declare function pointers. |
| AutocorrFix WebRtcIsacfix_AutocorrFix; |
| CalculateResidualEnergy WebRtcIsacfix_CalculateResidualEnergy; |
| |
| /* This routine calculates the residual energy for LPC. |
| * Formula as shown in comments inside. |
| */ |
| int32_t WebRtcIsacfix_CalculateResidualEnergyC(int lpc_order, |
| int32_t q_val_corr, |
| int q_val_polynomial, |
| int16_t* a_polynomial, |
| int32_t* corr_coeffs, |
| int* q_val_residual_energy) { |
| int i = 0, j = 0; |
| int shift_internal = 0, shift_norm = 0; |
| int32_t tmp32 = 0, word32_high = 0, word32_low = 0, residual_energy = 0; |
| int64_t sum64 = 0, sum64_tmp = 0; |
| |
| for (i = 0; i <= lpc_order; i++) { |
| for (j = i; j <= lpc_order; j++) { |
| /* For the case of i == 0: residual_energy += |
| * a_polynomial[j] * corr_coeffs[i] * a_polynomial[j - i]; |
| * For the case of i != 0: residual_energy += |
| * a_polynomial[j] * corr_coeffs[i] * a_polynomial[j - i] * 2; |
| */ |
| |
| tmp32 = a_polynomial[j] * a_polynomial[j - i]; |
| /* tmp32 in Q(q_val_polynomial * 2). */ |
| if (i != 0) { |
| tmp32 <<= 1; |
| } |
| sum64_tmp = (int64_t)tmp32 * (int64_t)corr_coeffs[i]; |
| sum64_tmp >>= shift_internal; |
| |
| /* Test overflow and sum the result. */ |
| if(((sum64_tmp > 0 && sum64 > 0) && (LLONG_MAX - sum64 < sum64_tmp)) || |
| ((sum64_tmp < 0 && sum64 < 0) && (LLONG_MIN - sum64 > sum64_tmp))) { |
| /* Shift right for overflow. */ |
| shift_internal += 1; |
| sum64 >>= 1; |
| sum64 += sum64_tmp >> 1; |
| } else { |
| sum64 += sum64_tmp; |
| } |
| } |
| } |
| |
| word32_high = (int32_t)(sum64 >> 32); |
| word32_low = (int32_t)sum64; |
| |
| // Calculate the value of shifting (shift_norm) for the 64-bit sum. |
| if(word32_high != 0) { |
| shift_norm = 32 - WebRtcSpl_NormW32(word32_high); |
| residual_energy = (int32_t)(sum64 >> shift_norm); |
| } else { |
| if((word32_low & 0x80000000) != 0) { |
| shift_norm = 1; |
| residual_energy = (uint32_t)word32_low >> 1; |
| } else { |
| shift_norm = WebRtcSpl_NormW32(word32_low); |
| residual_energy = word32_low << shift_norm; |
| shift_norm = -shift_norm; |
| } |
| } |
| |
| /* Q(q_val_polynomial * 2) * Q(q_val_corr) >> shift_internal >> shift_norm |
| * = Q(q_val_corr - shift_internal - shift_norm + q_val_polynomial * 2) |
| */ |
| *q_val_residual_energy = q_val_corr - shift_internal - shift_norm |
| + q_val_polynomial * 2; |
| |
| return residual_energy; |
| } |
| |
| void WebRtcIsacfix_GetLpcCoef(int16_t *inLoQ0, |
| int16_t *inHiQ0, |
| MaskFiltstr_enc *maskdata, |
| int16_t snrQ10, |
| const int16_t *pitchGains_Q12, |
| int32_t *gain_lo_hiQ17, |
| int16_t *lo_coeffQ15, |
| int16_t *hi_coeffQ15) |
| { |
| int k, n, ii; |
| int pos1, pos2; |
| int sh_lo, sh_hi, sh, ssh, shMem; |
| int16_t varscaleQ14; |
| |
| int16_t tmpQQlo, tmpQQhi; |
| int32_t tmp32; |
| int16_t tmp16,tmp16b; |
| |
| int16_t polyHI[ORDERHI+1]; |
| int16_t rcQ15_lo[ORDERLO], rcQ15_hi[ORDERHI]; |
| |
| |
| int16_t DataLoQ6[WINLEN], DataHiQ6[WINLEN]; |
| int32_t corrloQQ[ORDERLO+2]; |
| int32_t corrhiQQ[ORDERHI+1]; |
| int32_t corrlo2QQ[ORDERLO+1]; |
| int16_t scale; |
| int16_t QdomLO, QdomHI, newQdomHI, newQdomLO; |
| |
| int32_t res_nrgQQ; |
| int32_t sqrt_nrg; |
| |
| /* less-noise-at-low-frequencies factor */ |
| int16_t aaQ14; |
| |
| /* Multiplication with 1/sqrt(12) ~= 0.28901734104046 can be done by convertion to |
| Q15, i.e. round(0.28901734104046*32768) = 9471, and use 9471/32768.0 ~= 0.289032 |
| */ |
| int16_t snrq; |
| int shft; |
| |
| int16_t tmp16a; |
| int32_t tmp32a, tmp32b, tmp32c; |
| |
| int16_t a_LOQ11[ORDERLO+1]; |
| int16_t k_vecloQ15[ORDERLO]; |
| int16_t a_HIQ12[ORDERHI+1]; |
| int16_t k_vechiQ15[ORDERHI]; |
| |
| int16_t stab; |
| |
| snrq=snrQ10; |
| |
| /* SNR= C * 2 ^ (D * snrq) ; C=0.289, D=0.05*log2(10)=0.166 (~=172 in Q10)*/ |
| tmp16 = (int16_t)(snrq * 172 >> 10); // Q10 |
| tmp16b = exp2_Q10_T(tmp16); // Q10 |
| snrq = (int16_t)(tmp16b * 285 >> 10); // Q10 |
| |
| /* change quallevel depending on pitch gains and level fluctuations */ |
| WebRtcIsacfix_GetVars(inLoQ0, pitchGains_Q12, &(maskdata->OldEnergy), &varscaleQ14); |
| |
| /* less-noise-at-low-frequencies factor */ |
| /* Calculation of 0.35 * (0.5 + 0.5 * varscale) in fixpoint: |
| With 0.35 in Q16 (0.35 ~= 22938/65536.0 = 0.3500061) and varscaleQ14 in Q14, |
| we get Q16*Q14>>16 = Q14 |
| */ |
| aaQ14 = (int16_t)((22938 * (8192 + (varscaleQ14 >> 1)) + 32768) >> 16); |
| |
| /* Calculate tmp = (1.0 + aa*aa); in Q12 */ |
| tmp16 = (int16_t)(aaQ14 * aaQ14 >> 15); // Q14*Q14>>15 = Q13 |
| tmpQQlo = 4096 + (tmp16 >> 1); // Q12 + Q13>>1 = Q12. |
| |
| /* Calculate tmp = (1.0+aa) * (1.0+aa); */ |
| tmp16 = 8192 + (aaQ14 >> 1); // 1+a in Q13. |
| tmpQQhi = (int16_t)(tmp16 * tmp16 >> 14); // Q13*Q13>>14 = Q12 |
| |
| /* replace data in buffer by new look-ahead data */ |
| for (pos1 = 0; pos1 < QLOOKAHEAD; pos1++) { |
| maskdata->DataBufferLoQ0[pos1 + WINLEN - QLOOKAHEAD] = inLoQ0[pos1]; |
| } |
| |
| for (k = 0; k < SUBFRAMES; k++) { |
| |
| /* Update input buffer and multiply signal with window */ |
| for (pos1 = 0; pos1 < WINLEN - UPDATE/2; pos1++) { |
| maskdata->DataBufferLoQ0[pos1] = maskdata->DataBufferLoQ0[pos1 + UPDATE/2]; |
| maskdata->DataBufferHiQ0[pos1] = maskdata->DataBufferHiQ0[pos1 + UPDATE/2]; |
| DataLoQ6[pos1] = (int16_t)(maskdata->DataBufferLoQ0[pos1] * |
| kWindowAutocorr[pos1] >> 15); // Q0*Q21>>15 = Q6 |
| DataHiQ6[pos1] = (int16_t)(maskdata->DataBufferHiQ0[pos1] * |
| kWindowAutocorr[pos1] >> 15); // Q0*Q21>>15 = Q6 |
| } |
| pos2 = (int16_t)(k * UPDATE / 2); |
| for (n = 0; n < UPDATE/2; n++, pos1++) { |
| maskdata->DataBufferLoQ0[pos1] = inLoQ0[QLOOKAHEAD + pos2]; |
| maskdata->DataBufferHiQ0[pos1] = inHiQ0[pos2++]; |
| DataLoQ6[pos1] = (int16_t)(maskdata->DataBufferLoQ0[pos1] * |
| kWindowAutocorr[pos1] >> 15); // Q0*Q21>>15 = Q6 |
| DataHiQ6[pos1] = (int16_t)(maskdata->DataBufferHiQ0[pos1] * |
| kWindowAutocorr[pos1] >> 15); // Q0*Q21>>15 = Q6 |
| } |
| |
| /* Get correlation coefficients */ |
| /* The highest absolute value measured inside DataLo in the test set |
| For DataHi, corresponding value was 160. |
| |
| This means that it should be possible to represent the input values |
| to WebRtcSpl_AutoCorrelation() as Q6 values (since 307*2^6 = |
| 19648). Of course, Q0 will also work, but due to the low energy in |
| DataLo and DataHi, the outputted autocorrelation will be more accurate |
| and mimic the floating point code better, by being in an high as possible |
| Q-domain. |
| */ |
| |
| WebRtcIsacfix_AutocorrFix(corrloQQ,DataLoQ6,WINLEN, ORDERLO+1, &scale); |
| QdomLO = 12-scale; // QdomLO is the Q-domain of corrloQQ |
| sh_lo = WebRtcSpl_NormW32(corrloQQ[0]); |
| QdomLO += sh_lo; |
| for (ii=0; ii<ORDERLO+2; ii++) { |
| corrloQQ[ii] <<= sh_lo; |
| } |
| /* It is investigated whether it was possible to use 16 bits for the |
| 32-bit vector corrloQQ, but it didn't work. */ |
| |
| WebRtcIsacfix_AutocorrFix(corrhiQQ,DataHiQ6,WINLEN, ORDERHI, &scale); |
| |
| QdomHI = 12-scale; // QdomHI is the Q-domain of corrhiQQ |
| sh_hi = WebRtcSpl_NormW32(corrhiQQ[0]); |
| QdomHI += sh_hi; |
| for (ii=0; ii<ORDERHI+1; ii++) { |
| corrhiQQ[ii] <<= sh_hi; |
| } |
| |
| /* less noise for lower frequencies, by filtering/scaling autocorrelation sequences */ |
| |
| /* Calculate corrlo2[0] = tmpQQlo * corrlo[0] - 2.0*tmpQQlo * corrlo[1];*/ |
| // |corrlo2QQ| in Q(QdomLO-5). |
| corrlo2QQ[0] = (WEBRTC_SPL_MUL_16_32_RSFT16(tmpQQlo, corrloQQ[0]) >> 1) - |
| (WEBRTC_SPL_MUL_16_32_RSFT16(aaQ14, corrloQQ[1]) >> 2); |
| |
| /* Calculate corrlo2[n] = tmpQQlo * corrlo[n] - tmpQQlo * (corrlo[n-1] + corrlo[n+1]);*/ |
| for (n = 1; n <= ORDERLO; n++) { |
| |
| tmp32 = (corrloQQ[n - 1] >> 1) + (corrloQQ[n + 1] >> 1); // Q(QdomLO-1). |
| corrlo2QQ[n] = (WEBRTC_SPL_MUL_16_32_RSFT16(tmpQQlo, corrloQQ[n]) >> 1) - |
| (WEBRTC_SPL_MUL_16_32_RSFT16(aaQ14, tmp32) >> 2); |
| } |
| QdomLO -= 5; |
| |
| /* Calculate corrhi[n] = tmpQQhi * corrhi[n]; */ |
| for (n = 0; n <= ORDERHI; n++) { |
| corrhiQQ[n] = WEBRTC_SPL_MUL_16_32_RSFT16(tmpQQhi, corrhiQQ[n]); // Q(12+QdomHI-16) = Q(QdomHI-4) |
| } |
| QdomHI -= 4; |
| |
| /* add white noise floor */ |
| /* corrlo2QQ is in Q(QdomLO) and corrhiQQ is in Q(QdomHI) */ |
| /* Calculate corrlo2[0] += 9.5367431640625e-7; and |
| corrhi[0] += 9.5367431640625e-7, where the constant is 1/2^20 */ |
| |
| tmp32 = WEBRTC_SPL_SHIFT_W32((int32_t) 1, QdomLO-20); |
| corrlo2QQ[0] += tmp32; |
| tmp32 = WEBRTC_SPL_SHIFT_W32((int32_t) 1, QdomHI-20); |
| corrhiQQ[0] += tmp32; |
| |
| /* corrlo2QQ is in Q(QdomLO) and corrhiQQ is in Q(QdomHI) before the following |
| code segment, where we want to make sure we get a 1-bit margin */ |
| for (n = 0; n <= ORDERLO; n++) { |
| corrlo2QQ[n] >>= 1; // Make sure we have a 1-bit margin. |
| } |
| QdomLO -= 1; // Now, corrlo2QQ is in Q(QdomLO), with a 1-bit margin |
| |
| for (n = 0; n <= ORDERHI; n++) { |
| corrhiQQ[n] >>= 1; // Make sure we have a 1-bit margin. |
| } |
| QdomHI -= 1; // Now, corrhiQQ is in Q(QdomHI), with a 1-bit margin |
| |
| |
| newQdomLO = QdomLO; |
| |
| for (n = 0; n <= ORDERLO; n++) { |
| int32_t tmp, tmpB, tmpCorr; |
| int16_t alpha=328; //0.01 in Q15 |
| int16_t beta=324; //(1-0.01)*0.01=0.0099 in Q15 |
| int16_t gamma=32440; //(1-0.01)=0.99 in Q15 |
| |
| if (maskdata->CorrBufLoQQ[n] != 0) { |
| shMem=WebRtcSpl_NormW32(maskdata->CorrBufLoQQ[n]); |
| sh = QdomLO - maskdata->CorrBufLoQdom[n]; |
| if (sh<=shMem) { |
| tmp = WEBRTC_SPL_SHIFT_W32(maskdata->CorrBufLoQQ[n], sh); // Get CorrBufLoQQ to same domain as corrlo2 |
| tmp = WEBRTC_SPL_MUL_16_32_RSFT15(alpha, tmp); |
| } else if ((sh-shMem)<7){ |
| tmp = WEBRTC_SPL_SHIFT_W32(maskdata->CorrBufLoQQ[n], shMem); // Shift up CorrBufLoQQ as much as possible |
| // Shift |alpha| the number of times required to get |tmp| in QdomLO. |
| tmp = WEBRTC_SPL_MUL_16_32_RSFT15(alpha << (sh - shMem), tmp); |
| } else { |
| tmp = WEBRTC_SPL_SHIFT_W32(maskdata->CorrBufLoQQ[n], shMem); // Shift up CorrBufHiQQ as much as possible |
| // Shift |alpha| as much as possible without overflow the number of |
| // times required to get |tmp| in QdomLO. |
| tmp = WEBRTC_SPL_MUL_16_32_RSFT15(alpha << 6, tmp); |
| tmpCorr = corrloQQ[n] >> (sh - shMem - 6); |
| tmp = tmp + tmpCorr; |
| maskdata->CorrBufLoQQ[n] = tmp; |
| newQdomLO = QdomLO-(sh-shMem-6); |
| maskdata->CorrBufLoQdom[n] = newQdomLO; |
| } |
| } else |
| tmp = 0; |
| |
| tmp = tmp + corrlo2QQ[n]; |
| |
| maskdata->CorrBufLoQQ[n] = tmp; |
| maskdata->CorrBufLoQdom[n] = QdomLO; |
| |
| tmp=WEBRTC_SPL_MUL_16_32_RSFT15(beta, tmp); |
| tmpB=WEBRTC_SPL_MUL_16_32_RSFT15(gamma, corrlo2QQ[n]); |
| corrlo2QQ[n] = tmp + tmpB; |
| } |
| if( newQdomLO!=QdomLO) { |
| for (n = 0; n <= ORDERLO; n++) { |
| if (maskdata->CorrBufLoQdom[n] != newQdomLO) |
| corrloQQ[n] >>= maskdata->CorrBufLoQdom[n] - newQdomLO; |
| } |
| QdomLO = newQdomLO; |
| } |
| |
| |
| newQdomHI = QdomHI; |
| |
| for (n = 0; n <= ORDERHI; n++) { |
| int32_t tmp, tmpB, tmpCorr; |
| int16_t alpha=328; //0.01 in Q15 |
| int16_t beta=324; //(1-0.01)*0.01=0.0099 in Q15 |
| int16_t gamma=32440; //(1-0.01)=0.99 in Q1 |
| if (maskdata->CorrBufHiQQ[n] != 0) { |
| shMem=WebRtcSpl_NormW32(maskdata->CorrBufHiQQ[n]); |
| sh = QdomHI - maskdata->CorrBufHiQdom[n]; |
| if (sh<=shMem) { |
| tmp = WEBRTC_SPL_SHIFT_W32(maskdata->CorrBufHiQQ[n], sh); // Get CorrBufHiQQ to same domain as corrhi |
| tmp = WEBRTC_SPL_MUL_16_32_RSFT15(alpha, tmp); |
| tmpCorr = corrhiQQ[n]; |
| tmp = tmp + tmpCorr; |
| maskdata->CorrBufHiQQ[n] = tmp; |
| maskdata->CorrBufHiQdom[n] = QdomHI; |
| } else if ((sh-shMem)<7) { |
| tmp = WEBRTC_SPL_SHIFT_W32(maskdata->CorrBufHiQQ[n], shMem); // Shift up CorrBufHiQQ as much as possible |
| // Shift |alpha| the number of times required to get |tmp| in QdomHI. |
| tmp = WEBRTC_SPL_MUL_16_32_RSFT15(alpha << (sh - shMem), tmp); |
| tmpCorr = corrhiQQ[n]; |
| tmp = tmp + tmpCorr; |
| maskdata->CorrBufHiQQ[n] = tmp; |
| maskdata->CorrBufHiQdom[n] = QdomHI; |
| } else { |
| tmp = WEBRTC_SPL_SHIFT_W32(maskdata->CorrBufHiQQ[n], shMem); // Shift up CorrBufHiQQ as much as possible |
| // Shift |alpha| as much as possible without overflow the number of |
| // times required to get |tmp| in QdomHI. |
| tmp = WEBRTC_SPL_MUL_16_32_RSFT15(alpha << 6, tmp); |
| tmpCorr = corrhiQQ[n] >> (sh - shMem - 6); |
| tmp = tmp + tmpCorr; |
| maskdata->CorrBufHiQQ[n] = tmp; |
| newQdomHI = QdomHI-(sh-shMem-6); |
| maskdata->CorrBufHiQdom[n] = newQdomHI; |
| } |
| } else { |
| tmp = corrhiQQ[n]; |
| tmpCorr = tmp; |
| maskdata->CorrBufHiQQ[n] = tmp; |
| maskdata->CorrBufHiQdom[n] = QdomHI; |
| } |
| |
| tmp=WEBRTC_SPL_MUL_16_32_RSFT15(beta, tmp); |
| tmpB=WEBRTC_SPL_MUL_16_32_RSFT15(gamma, tmpCorr); |
| corrhiQQ[n] = tmp + tmpB; |
| } |
| |
| if( newQdomHI!=QdomHI) { |
| for (n = 0; n <= ORDERHI; n++) { |
| if (maskdata->CorrBufHiQdom[n] != newQdomHI) |
| corrhiQQ[n] >>= maskdata->CorrBufHiQdom[n] - newQdomHI; |
| } |
| QdomHI = newQdomHI; |
| } |
| |
| stab=WebRtcSpl_LevinsonW32_JSK(corrlo2QQ, a_LOQ11, k_vecloQ15, ORDERLO); |
| |
| if (stab<0) { // If unstable use lower order |
| a_LOQ11[0]=2048; |
| for (n = 1; n <= ORDERLO; n++) { |
| a_LOQ11[n]=0; |
| } |
| |
| stab=WebRtcSpl_LevinsonW32_JSK(corrlo2QQ, a_LOQ11, k_vecloQ15, 8); |
| } |
| |
| |
| WebRtcSpl_LevinsonDurbin(corrhiQQ, a_HIQ12, k_vechiQ15, ORDERHI); |
| |
| /* bandwidth expansion */ |
| for (n = 1; n <= ORDERLO; n++) { |
| a_LOQ11[n] = (int16_t)((kPolyVecLo[n - 1] * a_LOQ11[n] + (1 << 14)) >> |
| 15); |
| } |
| |
| |
| polyHI[0] = a_HIQ12[0]; |
| for (n = 1; n <= ORDERHI; n++) { |
| a_HIQ12[n] = (int16_t)(((int32_t)(kPolyVecHi[n - 1] * a_HIQ12[n]) + |
| (1 << 14)) >> 15); |
| polyHI[n] = a_HIQ12[n]; |
| } |
| |
| /* Normalize the corrlo2 vector */ |
| sh = WebRtcSpl_NormW32(corrlo2QQ[0]); |
| for (n = 0; n <= ORDERLO; n++) { |
| corrlo2QQ[n] <<= sh; |
| } |
| QdomLO += sh; /* Now, corrlo2QQ is still in Q(QdomLO) */ |
| |
| |
| /* residual energy */ |
| |
| sh_lo = 31; |
| res_nrgQQ = WebRtcIsacfix_CalculateResidualEnergy(ORDERLO, QdomLO, |
| kShiftLowerBand, a_LOQ11, corrlo2QQ, &sh_lo); |
| |
| /* Convert to reflection coefficients */ |
| WebRtcSpl_AToK_JSK(a_LOQ11, ORDERLO, rcQ15_lo); |
| |
| if (sh_lo & 0x0001) { |
| res_nrgQQ >>= 1; |
| sh_lo-=1; |
| } |
| |
| |
| if( res_nrgQQ > 0 ) |
| { |
| sqrt_nrg=WebRtcSpl_Sqrt(res_nrgQQ); |
| |
| /* add hearing threshold and compute the gain */ |
| /* lo_coeff = varscale * S_N_R / (sqrt_nrg + varscale * H_T_H); */ |
| |
| tmp32a = varscaleQ14 >> 1; // H_T_HQ19=65536 (16-17=-1) |
| ssh = sh_lo >> 1; // sqrt_nrg is in Qssh. |
| sh = ssh - 14; |
| tmp32b = WEBRTC_SPL_SHIFT_W32(tmp32a, sh); // Q14->Qssh |
| tmp32c = sqrt_nrg + tmp32b; // Qssh (denominator) |
| tmp32a = varscaleQ14 * snrq; // Q24 (numerator) |
| |
| sh = WebRtcSpl_NormW32(tmp32c); |
| shft = 16 - sh; |
| tmp16a = (int16_t) WEBRTC_SPL_SHIFT_W32(tmp32c, -shft); // Q(ssh-shft) (denominator) |
| |
| tmp32b = WebRtcSpl_DivW32W16(tmp32a, tmp16a); // Q(24-ssh+shft) |
| sh = ssh-shft-7; |
| *gain_lo_hiQ17 = WEBRTC_SPL_SHIFT_W32(tmp32b, sh); // Gains in Q17 |
| } |
| else |
| { |
| *gain_lo_hiQ17 = 100; // Gains in Q17 |
| } |
| gain_lo_hiQ17++; |
| |
| /* copy coefficients to output array */ |
| for (n = 0; n < ORDERLO; n++) { |
| *lo_coeffQ15 = (int16_t) (rcQ15_lo[n]); |
| lo_coeffQ15++; |
| } |
| /* residual energy */ |
| sh_hi = 31; |
| res_nrgQQ = WebRtcIsacfix_CalculateResidualEnergy(ORDERHI, QdomHI, |
| kShiftHigherBand, a_HIQ12, corrhiQQ, &sh_hi); |
| |
| /* Convert to reflection coefficients */ |
| WebRtcSpl_LpcToReflCoef(polyHI, ORDERHI, rcQ15_hi); |
| |
| if (sh_hi & 0x0001) { |
| res_nrgQQ >>= 1; |
| sh_hi-=1; |
| } |
| |
| |
| if( res_nrgQQ > 0 ) |
| { |
| sqrt_nrg=WebRtcSpl_Sqrt(res_nrgQQ); |
| |
| |
| /* add hearing threshold and compute the gain */ |
| /* hi_coeff = varscale * S_N_R / (sqrt_nrg + varscale * H_T_H); */ |
| |
| tmp32a = varscaleQ14 >> 1; // H_T_HQ19=65536 (16-17=-1) |
| |
| ssh = sh_hi >> 1; // |sqrt_nrg| is in Qssh. |
| sh = ssh - 14; |
| tmp32b = WEBRTC_SPL_SHIFT_W32(tmp32a, sh); // Q14->Qssh |
| tmp32c = sqrt_nrg + tmp32b; // Qssh (denominator) |
| tmp32a = varscaleQ14 * snrq; // Q24 (numerator) |
| |
| sh = WebRtcSpl_NormW32(tmp32c); |
| shft = 16 - sh; |
| tmp16a = (int16_t) WEBRTC_SPL_SHIFT_W32(tmp32c, -shft); // Q(ssh-shft) (denominator) |
| |
| tmp32b = WebRtcSpl_DivW32W16(tmp32a, tmp16a); // Q(24-ssh+shft) |
| sh = ssh-shft-7; |
| *gain_lo_hiQ17 = WEBRTC_SPL_SHIFT_W32(tmp32b, sh); // Gains in Q17 |
| } |
| else |
| { |
| *gain_lo_hiQ17 = 100; // Gains in Q17 |
| } |
| gain_lo_hiQ17++; |
| |
| |
| /* copy coefficients to output array */ |
| for (n = 0; n < ORDERHI; n++) { |
| *hi_coeffQ15 = rcQ15_hi[n]; |
| hi_coeffQ15++; |
| } |
| } |
| } |