/*==================================================================================== EVS Codec 3GPP TS26.442 Jun 30, 2015. Version CR 26.442-0010 ====================================================================================*/ #define _USE_MATH_DEFINES #include #include "stl.h" #include "basop_util.h" #include "options.h" #include "typedef.h" #include "cnst_fx.h" #include "prot_fx.h" #include "stat_com.h" /************************************************************************************/ /* forward declarations for local functions, see implementation at end of this file */ /************************************************************************************/ static void CalcMDXT(TonalMDCTConcealPtr const self, Word16 const type, Word16 const * const timeSignal, Word32 * const mdxtOutput, Word16 * const mdxtOutput_e); static void CalcPowerSpec(Word32 * mdctSpec, /* i: MDCT spectrum */ Word16 mdctSpec_exp, /* i: exponent of MDCT spectrum */ Word32 * mdstSpec, /* i: MDST spectrum */ Word16 mdstSpec_exp, /* i: exponent of MDST spectrum */ Word16 nSamples, /* i: frame size */ Word16 floorPowerSpectrum, /* i: lower limit for power spectrum bins */ Word32 * powerSpec, /* o: power spectrum */ Word16 * powerSpec_exp); static void CalcPowerSpecAndDetectTonalComponents(TonalMDCTConcealPtr const self, Word32 secondLastMDST[], Word16 secondLastMDST_exp, Word32 secondLastMDCT[], Word16 secondLastMDCT_exp, Word32 const pitchLag); static void FindPhases( /* o: current phase [-pi;pi] 2Q13 */ TonalMDCTConcealPtr const self, /* i: pointer to internal structure */ Word32 secondLastMDCT[], /* i: MDST spectrum data */ Word32 secondLastMDST[], /* i: MDCT spectrum data */ Word16 diff_exp); /* i: exp_MDST - exp_MDCT */ static void FindPhaseDifferences( /* o: Phase difference [-pi;pi] 2Q13*/ TonalMDCTConcealPtr const self, /* i: Pointer to internal structure */ Word32 powerSpectrum[]); /* i: Power spectrum data */ /*******************************************************/ /*-------------- public functions -------------------- */ /*******************************************************/ TONALMDCTCONCEAL_ERROR TonalMDCTConceal_Init( TonalMDCTConcealPtr self, Word16 nSamples, Word16 nSamplesCore, Word16 nScaleFactors, TCX_config * tcx_cfg ) { test(); IF (sub(nSamples,L_FRAME_MAX) > 0 || sub(nScaleFactors,FDNS_NPTS) > 0) { assert(nSamples <= L_FRAME_MAX); assert(nScaleFactors <= FDNS_NPTS); return TONALMDCTCONCEAL_NSAMPLES_LARGER_THAN_MAXBLOCKSIZE; } assert((self->nScaleFactors == nScaleFactors) || (self->nSamples != nSamples)); /* If nSamples doesn't change then also nScaleFactors must stay the same */ self->tcx_cfg = tcx_cfg; self->lastBlockData.spectralData = self->spectralDataBuffers[0]; move16(); self->secondLastBlockData.spectralData = self->spectralDataBuffers[1]; move16(); self->secondLastPowerSpectrum = self->secondLastBlockData.spectralData; move16(); self->lastBlockData.scaleFactors = self->scaleFactorsBuffers[0]; move16(); self->secondLastBlockData.scaleFactors = self->scaleFactorsBuffers[1]; move16(); self->lastBlockData.scaleFactors_exp = self->scaleFactorsBuffers_exp[0]; move16(); self->secondLastBlockData.scaleFactors_exp = self->scaleFactorsBuffers_exp[1]; move16(); self->lastBlockData.blockIsValid = 0; move16(); self->secondLastBlockData.blockIsValid = 0; move16(); self->nSamples = 0; move16(); self->nScaleFactors = 0; move16(); self->lastBlockData.blockIsConcealed = 0; move16(); self->secondLastBlockData.blockIsConcealed = 0; move16(); self->pTCI = (TonalComponentsInfo *)self->timeDataBuffer; move16(); self->lastPitchLag = L_deposit_l(0); IF (sub(self->nSamples,nSamples) != 0) { self->secondLastBlockData.blockIsValid = 0; move16(); self->lastBlockData.blockIsValid = 0; move16(); } self->nSamples = nSamples; move16(); self->nSamplesCore = nSamplesCore; move16(); self->nScaleFactors = nScaleFactors; move16(); /* Offset the pointer to the end of buffer, so that pTCI is not destroyed when new time samples are stored in lastPcmOut */ move16(); move16(); /* just the second half of the second last pcm output is needed */ self->secondLastPcmOut = &self->timeDataBuffer[sub((3*L_FRAME_MAX)/2,3*(s_min(L_FRAME_MAX, nSamples))/2)]; self->lastPcmOut = &self->timeDataBuffer[sub((3*L_FRAME_MAX)/2, s_min(L_FRAME_MAX, nSamples)) ]; /* If the second last frame was lost, we reuse saved TonalComponentsInfo and don't update pcm buffers */ assert(sizeof(*self->pTCI) <= (self->lastPcmOut-self->timeDataBuffer)*sizeof(self->timeDataBuffer[0])); return TONALMDCTCONCEAL_OK; } TONALMDCTCONCEAL_ERROR TonalMDCTConceal_SaveFreqSignal( TonalMDCTConcealPtr self, Word32 const *mdctSpectrum, Word16 const mdctSpectrum_exp, Word16 nNewSamples, Word16 nNewSamplesCore, Word16 const *scaleFactors, Word16 const *scaleFactors_exp, Word16 const gain_tcx_exp ) { Word16 * temp; Word16 nOldSamples, tmp_exp, s, i, max_exp; assert(nNewSamples > 0 && nNewSamples <= 2*L_FRAME_MAX); /* Avoid overwriting self->secondLastPowerSpectrum stored in spectralData, because it is needed if the second last and the current frame are lost and concealed using the Tonal MDCT PLC */ test(); IF (!self->lastBlockData.tonalConcealmentActive || sub(self->lastBlockData.nSamples,nNewSamples) != 0) { IF (sub(nNewSamples,L_FRAME_MAX) <= 0) { /* Shift the buffers */ temp = self->secondLastBlockData.spectralData; /* Save the pointer */ move16(); self->secondLastBlockData.spectralData = self->lastBlockData.spectralData; move16(); self->lastBlockData.spectralData = temp; move16(); tmp_exp = self->secondLastBlockData.spectralData_exp; /* Save the pointer */ move16(); self->secondLastBlockData.spectralData_exp = self->lastBlockData.spectralData_exp; move16(); self->lastBlockData.spectralData_exp = tmp_exp; move16(); tmp_exp = self->secondLastBlockData.gain_tcx_exp; /* Save the pointer */ move16(); self->secondLastBlockData.gain_tcx_exp = self->lastBlockData.gain_tcx_exp; move16(); self->lastBlockData.gain_tcx_exp = tmp_exp; move16(); tmp_exp = self->secondLastBlockData.scaleFactors_max_e; /* Save the pointer */ move16(); self->secondLastBlockData.scaleFactors_max_e = self->lastBlockData.scaleFactors_max_e; move16(); self->lastBlockData.scaleFactors_max_e = tmp_exp; move16(); temp = self->secondLastBlockData.scaleFactors; move16(); self->secondLastBlockData.scaleFactors = self->lastBlockData.scaleFactors; move16(); self->lastBlockData.scaleFactors = temp; move16(); temp = self->secondLastBlockData.scaleFactors_exp; move16(); self->secondLastBlockData.scaleFactors_exp = self->lastBlockData.scaleFactors_exp; move16(); self->lastBlockData.scaleFactors_exp = temp; move16(); } ELSE { self->lastBlockData.spectralData = self->spectralDataBuffers[0]; move16(); self->secondLastBlockData.spectralData = self->spectralDataBuffers[1]; move16(); self->lastBlockData.scaleFactors = self->scaleFactorsBuffers[0]; move16(); self->secondLastBlockData.scaleFactors = self->scaleFactorsBuffers[1]; move16(); self->lastBlockData.scaleFactors_exp = self->scaleFactorsBuffers_exp[0]; move16(); self->secondLastBlockData.scaleFactors_exp = self->scaleFactorsBuffers_exp[1]; move16(); } nOldSamples = self->lastBlockData.nSamples; move16(); self->lastBlockData.nSamples = nNewSamples; move16(); self->secondLastBlockData.nSamples = nOldSamples; move16(); nOldSamples = self->lastBlockData.nSamplesCore; move16(); self->lastBlockData.nSamplesCore = nNewSamplesCore; move16(); self->secondLastBlockData.nSamplesCore = nOldSamples; move16(); } test(); IF ((nNewSamples > 0) && (sub(nNewSamples,2*L_FRAME_MAX) <= 0)) { /* Store new data */ s = getScaleFactor32(mdctSpectrum, nNewSamples); /*Copy(scaleFactors_exp, self->lastBlockData.scaleFactors_exp, self->nScaleFactors);*/ max_exp = 0; FOR (i = 0; i < self->nScaleFactors; i++) { self->lastBlockData.scaleFactors_exp[i] = scaleFactors_exp[i]; move16(); max_exp = s_max(max_exp, scaleFactors_exp[i]); } /*s = sub(s, max_exp);*/ self->lastBlockData.scaleFactors_max_e = max_exp; FOR (i = 0; i < nNewSamples; i++) { self->lastBlockData.spectralData[i] = extract_h(L_shl(mdctSpectrum[i], s)); move16(); } self->lastBlockData.spectralData_exp = sub(mdctSpectrum_exp,s); move16(); self->lastBlockData.gain_tcx_exp = gain_tcx_exp; Copy(scaleFactors, self->lastBlockData.scaleFactors, self->nScaleFactors); } return TONALMDCTCONCEAL_OK; } TONALMDCTCONCEAL_ERROR TonalMDCTConceal_UpdateState(TonalMDCTConcealPtr self, Word16 nNewSamples, Word32 pitchLag, Word16 badBlock, Word8 tonalConcealmentActive ) { Word8 newBlockIsValid; assert(!(!badBlock && tonalConcealmentActive)); IF (badBlock) { newBlockIsValid = self->lastBlockData.blockIsValid; move16(); } ELSE { newBlockIsValid = 0; move16(); test(); if((sub(nNewSamples,2*L_FRAME_MAX) <= 0) && (nNewSamples > 0)) { newBlockIsValid = 1; move16(); } } /* Shift old state */ move16(); move16(); move16(); self->secondLastBlockData.blockIsConcealed = self->lastBlockData.blockIsConcealed; self->secondLastBlockData.blockIsValid = self->lastBlockData.blockIsValid; self->secondLastBlockData.tonalConcealmentActive = self->lastBlockData.tonalConcealmentActive; /* Store new state */ move16(); move16(); move16(); self->lastBlockData.blockIsConcealed = badBlock; self->lastBlockData.blockIsValid = newBlockIsValid; self->lastBlockData.tonalConcealmentActive = tonalConcealmentActive; self->lastPitchLag = pitchLag; move32(); return TONALMDCTCONCEAL_OK; } static void FindPhases( /* o: currenc phase [-pi;pi] 2Q13 */ TonalMDCTConcealPtr const self, /* i: pointer to internal structure */ Word32 secondLastMDCT[], /* i: MDST spectrum data */ Word32 secondLastMDST[], /* i: MDCT spectrum data */ Word16 diff_exp) /* i: exp_MDST - exp_MDCT */ { Word16 i; Word16 l; Word16 *pCurrentPhase; pCurrentPhase = self->pTCI->phase_currentFramePredicted; /* for each index/index group */ FOR( i = 0; i < self->pTCI->numIndexes; i++) { FOR (l = self->pTCI->lowerIndex[i]; l <= self->pTCI->upperIndex[i]; l++) { /* in contrast to the float code, the parameter secondLastMDST[l] needs not to be negated - due to a different implementation of the MDST */ *pCurrentPhase++ = BASOP_util_atan2(secondLastMDST[l], secondLastMDCT[l], diff_exp); move16(); } } } #define BANDWIDTH 7.0f #define G 789516047l/*1.0/(2*1.36) Q31*/ #define MAXRATIO 22938/*44.8f Q9*/ /* Maximum ratio |ODFT[k-1]|/|ODFT[k+1]| is 16.5 dB, that is maximum ratio (for fractional = 0) is (cos(PI/bandwidth)/cos(3PI/bandwidth))^1.36 */ #define MM 1934815907 /* FL2WORD32(cos(EVS_PI/BANDWIDTH)); */ #define SS 29166 /* FL2WORD16(cos((3*EVS_PI)/BANDWIDTH)*4); Q17*/ #define N 931758243 /* FL2WORD32(sin(EVS_PI/BANDWIDTH)); */ #define J 31946 /* FL2WORD16(sin((3*EVS_PI)/BANDWIDTH)); */ static void FindPhaseDifferences( /* o: Phase difference [-pi;pi] 2Q13*/ TonalMDCTConcealPtr const self, /* i: Pointer to internal structure */ Word32 powerSpectrum[]) /* i: Power spectrum data */ { Word16 i, k; Word16 * phaseDiff; Word16 fractional, sf, sfn, sfd; Word16 divi, s, j; Word32 a, Q, L_tmp, m, n; s = SS; move16(); j = J; move16(); phaseDiff = self->pTCI->phaseDiff; FOR (i = 0; i < self->pTCI->numIndexes; i++) { m = MM; move16(); n = N; move16(); k = self->pTCI->indexOfTonalPeak[i]; move16(); IF (L_sub(Mpy_32_16_1(powerSpectrum[k-1],512/*1.0f Q9*/),Mpy_32_16_1(powerSpectrum[k+1], MAXRATIO)) >= 0) { phaseDiff[i] = 0; /*(float)tan(0.0f*EVS_PI/bandwidth);*/ move16(); if(s_and(k,1) != 0) phaseDiff[i] = -12868/*-EVS_PI 3Q12*/; } ELSE { IF (L_sub(Mpy_32_16_1(powerSpectrum[k+1],512/*1.0f Q9*/),Mpy_32_16_1(powerSpectrum[k-1], MAXRATIO)) >= 0) { phaseDiff[i] = 12868/*EVS_PI 3Q12*/; /*(float)tan(2.0f*PI/bandwidth);*/ move16(); if(s_and(k,1) != 0) phaseDiff[i] = 0/*0 Q13*/; /*2Q13*/ } ELSE { /*Q = (float)pow(odft_left/odft_right, G); a = (m - Q * s) / (n + Q * j); phaseDiff[i] = (float)atan(a) * (bandwidth/2.0f);*/ /*max divi=44.8 & sf=6*/ divi = BASOP_Util_Divide3232_uu_1616_Scale(powerSpectrum[k-1],powerSpectrum[k+1], &sf); Q = BASOP_Util_fPow(L_deposit_h(divi), sf, G, 0, &sf); L_tmp = Mpy_32_16_1(Q,s); sfn = sub(sf, 2); if(sfn > 0) m = L_shr(m, sfn); IF(sfn < 0) { L_tmp = L_shl(L_tmp, sfn); sfn = 0; } a = L_sub(m, L_tmp); /*sf*/ L_tmp = Mpy_32_16_1(Q,j); IF(sf >= 0) { L_tmp = L_shr(L_tmp, 1); sfd = add(sf,1); n = L_shr(n,sfd); } ELSE{ sfd = 0; L_tmp = L_shl(L_tmp, sf); } L_tmp = L_add(n,L_tmp); fractional = BASOP_util_atan2(a, L_tmp, sub(sfn,sfd)); /*2Q13*/ L_tmp = L_mult(fractional, 28672/*BANDWIDTH/2.0f Q13*/); /*2Q13*2Q13=4Q27*/ move16(); /* fractional is in the range 0..+pi */ /* we need to stay in the range -2pi..+2pi */ if(sub(s_and(k,3),1) == 0) { L_tmp = L_add(L_tmp, 421657440l/*+1*EVS_PI Q27*/); } if(sub(s_and(k,3),2) == 0) { L_tmp = L_sub(L_tmp, 843314880l/*+2*EVS_PI=-2*EVS_PI Q27*/); } if(sub(s_and(k,3),3) == 0) { L_tmp = L_sub(L_tmp, 421657440l/*+3*EVS_PI=-1*EVS_PI Q27*/); } phaseDiff[i] = round_fx(L_shl(L_tmp,1)); /*3Q12*/ } } } } static void CalcPowerSpecAndDetectTonalComponents(TonalMDCTConcealPtr const self, Word32 secondLastMDST[], Word16 secondLastMDST_exp, Word32 secondLastMDCT[], Word16 secondLastMDCT_exp, Word32 const pitchLag) { Word16 nSamples; Word16 i; Word16 floorPowerSpectrum; /* Minimum significant value of a spectral line in the power spectrum */ Word32 powerSpectrum[L_FRAME_MAX]; Word16 invScaleFactors[FDNS_NPTS]; Word16 invScaleFactors_exp[FDNS_NPTS]; Word16 powerSpectrum_exp, tmp_exp, old_exp; nSamples = self->nNonZeroSamples; move16(); /* It is taken into account that the MDCT is not normalized. */ floorPowerSpectrum/*Q0*/ = extract_l(Mpy_32_16_1(L_mult0(self->nSamples,self->nSamples),82)); /*1/400 = 82 Q15*/ powerSpectrum_exp = 0; move16(); CalcPowerSpec(secondLastMDCT, secondLastMDCT_exp, secondLastMDST, secondLastMDST_exp, nSamples, floorPowerSpectrum, powerSpectrum, &powerSpectrum_exp); /* This setting to minimal level is required because the power spectrum is used in the threshold adaptation using the pitch up to self->nSamples. */ set32_fx(powerSpectrum+nSamples, floorPowerSpectrum, sub(self->nSamples, nSamples)); /* this setting to zero is needed since the FDNS needs to be called with self->nSamplesCore; it relevant only for nb; it has no effect to the output, but memory checker may complain otherwise due to the usage of uninitialized values */ IF ( sub(self->nSamplesCore, self->nSamples) > 0 ) { set32_fx(powerSpectrum+self->nSamples, 0, sub(self->nSamplesCore, self->nSamples)); } DetectTonalComponents(self->pTCI->indexOfTonalPeak, self->pTCI->lowerIndex, self->pTCI->upperIndex, &self->pTCI->numIndexes, self->lastPitchLag, pitchLag, self->lastBlockData.spectralData, add(self->lastBlockData.spectralData_exp,self->lastBlockData.gain_tcx_exp), self->lastBlockData.scaleFactors, self->lastBlockData.scaleFactors_exp, self->lastBlockData.scaleFactors_max_e, powerSpectrum, nSamples, self->nSamplesCore, floorPowerSpectrum); FindPhases(self, secondLastMDCT, secondLastMDST, sub(secondLastMDST_exp,secondLastMDCT_exp)); FindPhaseDifferences(self, powerSpectrum); IF (self->pTCI->numIndexes > 0) { self->secondLastPowerSpectrum = self->secondLastBlockData.spectralData; /*sqrtFLOAT(powerSpectrum, powerSpectrum, nSamples);*/ old_exp = powerSpectrum_exp; powerSpectrum_exp = mult_r(sub(powerSpectrum_exp,2), 1 << 14); /*remove 2 bits of headroom from CalcPowerSpec*/ FOR (i = 0; i < nSamples; i++) { tmp_exp = old_exp; powerSpectrum[i] = Sqrt32(powerSpectrum[i], &tmp_exp); powerSpectrum[i] = L_shr(powerSpectrum[i], sub(powerSpectrum_exp, tmp_exp)); move32(); } FOR (i = 0; i < self->nScaleFactors; i++) { move16(); move16(); invScaleFactors_exp[i] = self->secondLastBlockData.scaleFactors_exp[i]; invScaleFactors[i] = Inv16(self->secondLastBlockData.scaleFactors[i], &invScaleFactors_exp[i]); } /* here mdct_shaping() is intentionally used rather then mdct_shaping_16() */ mdct_shaping(powerSpectrum, self->nSamplesCore, invScaleFactors, invScaleFactors_exp); FOR (i = self->nSamplesCore; i < nSamples; i++) { powerSpectrum[i] = L_shl(Mpy_32_16_1(powerSpectrum[i], invScaleFactors[FDNS_NPTS-1]), invScaleFactors_exp[FDNS_NPTS-1]); move32(); } /* 16 bits are now enough for storing the power spectrum */ FOR (i = 0; i < nSamples; i++) { self->secondLastPowerSpectrum[i] = round_fx(powerSpectrum[i]); } powerSpectrum_exp = sub(powerSpectrum_exp, self->secondLastBlockData.gain_tcx_exp); self->secondLastPowerSpectrum_exp = powerSpectrum_exp; move16(); } } static void CalcMDXT(TonalMDCTConcealPtr const self, Word16 const type, Word16 const * const timeSignal, Word32 * const mdxtOutput, Word16 * const mdxtOutput_e) { Word16 windowedTimeSignal[L_FRAME_PLUS+2*L_MDCT_OVLP_MAX]; Word16 left_overlap, right_overlap, L_frame; L_frame = self->nSamples; move16(); WindowSignal(self->tcx_cfg, self->tcx_cfg->tcx_offsetFB, FULL_OVERLAP, FULL_OVERLAP, &left_overlap, &right_overlap, timeSignal, &L_frame, windowedTimeSignal, 1); IF (type == 0) { TCX_MDST(windowedTimeSignal, mdxtOutput, mdxtOutput_e, left_overlap, sub(L_frame, shr(add(left_overlap, right_overlap), 1)), right_overlap); } ELSE { TCX_MDCT(windowedTimeSignal, mdxtOutput, mdxtOutput_e, left_overlap, sub(L_frame, shr(add(left_overlap, right_overlap), 1)), right_overlap); } } TONALMDCTCONCEAL_ERROR TonalMDCTConceal_Detect( TonalMDCTConcealPtr const self, Word32 const pitchLag, Word16 * const numIndices) { Word32 secondLastMDST[L_FRAME_MAX]; Word32 secondLastMDCT[L_FRAME_MAX]; Word16 secondLastMDCT_exp; Word32 * powerSpectrum = secondLastMDST; Word16 i, powerSpectrum_exp, secondLastMDST_exp, s; Word16 nSamples; nSamples = self->nSamples; move16(); secondLastMDST_exp = 16; /*time signal Q-1*/ secondLastMDCT_exp = 16; /*time signal Q-1*/ test(); test(); test(); test(); test(); IF (self->lastBlockData.blockIsValid && self->secondLastBlockData.blockIsValid && (sub(self->lastBlockData.nSamples,nSamples) == 0) && (sub(self->secondLastBlockData.nSamples,nSamples) == 0) && (!self->secondLastBlockData.blockIsConcealed || self->secondLastBlockData.tonalConcealmentActive || (pitchLag != 0)) /* Safety if the second last frame was concealed and tonal concealment was inactive */ ) { IF (self->lastBlockData.blockIsConcealed == 0) { IF (self->secondLastBlockData.tonalConcealmentActive == 0) { CalcMDXT(self, 0, self->secondLastPcmOut, secondLastMDST, &secondLastMDST_exp); CalcMDXT(self, 1, self->secondLastPcmOut, secondLastMDCT, &secondLastMDCT_exp); self->nNonZeroSamples = 0; FOR (i = 0; i < self->nSamples; i++) { if (self->secondLastBlockData.spectralData[i] != 0) { self->nNonZeroSamples = i; move16(); } } /* 23 is the maximum length of the MA filter in getEnvelope */ self->nNonZeroSamples = s_min(self->nSamples, add(self->nNonZeroSamples, 23)); move16(); nSamples = self->nNonZeroSamples; move16(); s = getScaleFactor32(secondLastMDST, nSamples); FOR (i = 0; i < nSamples; i++) { secondLastMDST[i] = L_shl(secondLastMDST[i], s); move32(); } secondLastMDST_exp = sub(secondLastMDST_exp, s); move16(); s = getScaleFactor32(secondLastMDCT, nSamples); FOR (i = 0; i < nSamples; i++) { secondLastMDCT[i] = L_shl(secondLastMDCT[i], s); move32(); } secondLastMDCT_exp = sub(secondLastMDCT_exp, s); move16(); CalcPowerSpecAndDetectTonalComponents(self, secondLastMDST, secondLastMDST_exp, secondLastMDCT, secondLastMDCT_exp, pitchLag); } ELSE { /* If the second last frame was also lost, it is expected that pastTimeSignal could hold a bit different signal (e.g. including fade-out) from the one stored in TonalMDCTConceal_SaveTimeSignal. */ /* That is why we reuse the already stored information about the concealed spectrum in the second last frame */ nSamples = self->nNonZeroSamples; move16(); mdct_shaping_16(self->secondLastPowerSpectrum, self->nSamplesCore, nSamples, self->secondLastBlockData.scaleFactors, self->secondLastBlockData.scaleFactors_exp, self->secondLastBlockData.scaleFactors_max_e, powerSpectrum); powerSpectrum_exp = getScaleFactor32(powerSpectrum, nSamples); powerSpectrum_exp = sub(powerSpectrum_exp, 3); /*extra 3 bits of headroom for MA filter in getEnvelope*/ /* multFLOAT(powerSpectrum, powerSpectrum, powerSpectrum, nSamples); */ FOR(i = 0; i < nSamples; i++) { Word32 const t = L_shl(powerSpectrum[i], powerSpectrum_exp); powerSpectrum[i] = Mpy_32_32(t, t); move32(); } RefineTonalComponents(self->pTCI->indexOfTonalPeak, self->pTCI->lowerIndex, self->pTCI->upperIndex, self->pTCI->phaseDiff, self->pTCI->phase_currentFramePredicted, &self->pTCI->numIndexes, self->lastPitchLag, pitchLag, self->lastBlockData.spectralData, add(self->lastBlockData.spectralData_exp,self->lastBlockData.gain_tcx_exp), self->lastBlockData.scaleFactors, self->lastBlockData.scaleFactors_exp, self->lastBlockData.scaleFactors_max_e, powerSpectrum, nSamples, self->nSamplesCore, extract_l(Mpy_32_16_1(L_mult0(self->nSamples,self->nSamples),82))); /* floorPowerSpectrum */ } } } ELSE { self->pTCI->numIndexes = 0; move16(); } *numIndices = self->pTCI->numIndexes; move16(); return TONALMDCTCONCEAL_OK; } TONALMDCTCONCEAL_ERROR TonalMDCTConceal_InsertNoise( TonalMDCTConcealPtr self, /*IN */ Word32* mdctSpectrum, /*OUT*/ Word16* mdctSpectrum_exp, /*OUT*/ Word8 tonalConcealmentActive, Word16* pSeed, /*IN/OUT*/ Word16 tiltCompFactor, Word16 crossfadeGain, Word16 crossOverFreq) { Word16 i, ld, fac; Word16 rnd, exp, exp_last, exp_noise, inv_samples, inv_exp; Word16 g, tiltFactor, tilt, tmp; Word32 nrgNoiseInLastFrame, nrgWhiteNoise, L_tmp, L_tmp2; g = sub(32767/*1.0f Q15*/,crossfadeGain); rnd = 1977; move16(); if (self->lastBlockData.blockIsConcealed) { rnd = *pSeed; move16(); } IF (self->lastBlockData.blockIsValid == 0) { /* may just become active if the very first frame is lost */ set32_fx(mdctSpectrum, 0, self->nSamples); *mdctSpectrum_exp = SPEC_EXP_DEC; } ELSE { L_tmp = 805306368l/*0.375f Q31*/; inv_exp = 15; move16(); inv_samples = Inv16(self->lastBlockData.nSamples, &inv_exp); tiltFactor = round_fx(BASOP_Util_fPow(L_max(L_tmp, L_deposit_h(tiltCompFactor)), 0, L_deposit_h(inv_samples),inv_exp, &exp)); BASOP_SATURATE_WARNING_OFF /*next op may result in 32768*/ tiltFactor = shl(tiltFactor, exp); BASOP_SATURATE_WARNING_ON tilt = 32767/*1.0f Q15*/; move16(); nrgNoiseInLastFrame = L_deposit_h(0); nrgWhiteNoise = L_deposit_h(0); exp_last = exp_noise = 0; move16(); move16(); IF (!tonalConcealmentActive) { ld = sub(14,norm_s(self->lastBlockData.nSamples)); fac = shr(-32768,ld); FOR (i = 0; i < crossOverFreq; i++) { Word16 x = self->lastBlockData.spectralData[i]; Word32 y; rnd = extract_l(L_mac0(13849, rnd, 31821)); y = L_mult(tilt,rnd); nrgNoiseInLastFrame = L_add(nrgNoiseInLastFrame, Mpy_32_16_1(L_msu(0, x,fac),x)); x = round_fx(y); nrgWhiteNoise = L_add(nrgWhiteNoise, Mpy_32_16_1(L_msu(0, x,fac),x)); mdctSpectrum[i] = y; /* 15Q16 */ move32(); tilt = mult_r(tilt,tiltFactor); } IF (nrgNoiseInLastFrame == 0) { set32_fx(mdctSpectrum, 0, crossOverFreq); *mdctSpectrum_exp = SPEC_EXP_DEC; } ELSE { exp_last = add(ld,shl(self->lastBlockData.spectralData_exp,1)); exp_noise = add(ld,30); IF (nrgWhiteNoise > 0) { ld = norm_l(nrgNoiseInLastFrame); nrgNoiseInLastFrame = L_shl(nrgNoiseInLastFrame,ld); exp_last = sub(exp_last,ld); ld = norm_l(nrgWhiteNoise); nrgWhiteNoise = L_shl(nrgWhiteNoise,ld); exp_noise = sub(exp_noise,ld); exp = sub(exp_last, exp_noise); IF(nrgNoiseInLastFrame > nrgWhiteNoise) { nrgNoiseInLastFrame = L_shr(nrgNoiseInLastFrame,1); exp = add(exp,1); } tmp = div_l(nrgNoiseInLastFrame,round_fx(nrgWhiteNoise)); tmp = Sqrt16(tmp, &exp); g = mult_r(g,tmp); L_tmp = L_deposit_h(0); ld = sub(self->lastBlockData.spectralData_exp, 15); exp = sub(ld, exp); IF(exp > 0) { g = shr(g,exp); *mdctSpectrum_exp = self->lastBlockData.spectralData_exp; } ELSE { crossfadeGain = shl(crossfadeGain,exp); *mdctSpectrum_exp = sub(self->lastBlockData.spectralData_exp,exp); } /*make a headroom for mdct_shaping*/ exp = sub(*mdctSpectrum_exp, SPEC_EXP_DEC); /* assert(exp < 0);*/ IF(exp < 0) { *mdctSpectrum_exp = SPEC_EXP_DEC; } ELSE { exp = 0; } } FOR (i = 0; i < crossOverFreq; i++) { Word16 const x = self->lastBlockData.spectralData[i]; Word32 const y = mdctSpectrum[i]; if(g > 0) { L_tmp = Mpy_32_16_1(y,g); } L_tmp2 = L_msu(L_tmp,crossfadeGain,x); if(y > 0) { L_tmp2 = L_mac(L_tmp,crossfadeGain,x); } mdctSpectrum[i] = L_shl(L_tmp2, exp); move32(); } } exp = sub(self->lastBlockData.spectralData_exp, sub(*mdctSpectrum_exp,16)); FOR (i = crossOverFreq; i < self->lastBlockData.nSamples; i++) { mdctSpectrum[i] = L_shl(L_deposit_l(self->lastBlockData.spectralData[i]), exp); move32(); } } ELSE { Word16 l; assert(self->pTCI->numIndexes > 0); FOR (l = self->pTCI->lowerIndex[0]; l <= self->pTCI->upperIndex[0]; l++) { mdctSpectrum[l] = L_deposit_l(0); } ld = sub(14,norm_s(self->lastBlockData.nSamples)); fac = shr(-32768,ld); FOR (l = 0; l < self->pTCI->lowerIndex[0]; l++) { Word16 x = self->lastBlockData.spectralData[l]; Word32 y; rnd = extract_l(L_mac0(13849, rnd, 31821)); y = L_mult(tilt,rnd); nrgNoiseInLastFrame = L_add(nrgNoiseInLastFrame, Mpy_32_16_1(L_msu(0, x,fac),x)); x = round_fx(y); nrgWhiteNoise = L_add(nrgWhiteNoise, Mpy_32_16_1(L_msu(0, x,fac),x)); mdctSpectrum[l] = y; /* 15Q16 L_deposit_l(y);*/ move32(); tilt = mult_r(tilt,tiltFactor); } FOR (i = 1; i < self->pTCI->numIndexes; i++) { /*tilt *= (float)pow(tiltFactor, self->pTCI->upperIndex[i-1]-self->pTCI->lowerIndex[i-1]+1);*/ tmp= round_fx(BASOP_Util_fPow(L_deposit_h(tiltFactor), 0, L_deposit_h(self->pTCI->upperIndex[i-1]-self->pTCI->lowerIndex[i-1]+1),15, &exp)); tmp = shl(tmp, exp); tilt = mult_r(tilt,tmp); FOR (l = self->pTCI->lowerIndex[i]; l <= self->pTCI->upperIndex[i]; l++) { mdctSpectrum[l] = L_deposit_l(0); } FOR (l = self->pTCI->upperIndex[i-1]+1; l < self->pTCI->lowerIndex[i]; l++) { Word16 x = self->lastBlockData.spectralData[l]; Word32 y; rnd = extract_l(L_mac0(13849, rnd, 31821)); y = L_mult(tilt,rnd); nrgNoiseInLastFrame = L_add(nrgNoiseInLastFrame, Mpy_32_16_1(L_msu(0, x,fac),x)); x = round_fx(y); nrgWhiteNoise = L_add(nrgWhiteNoise, Mpy_32_16_1(L_msu(0, x,fac),x)); mdctSpectrum[l] = y; /* 15Q16 L_deposit_l(y);*/ move32(); tilt = mult_r(tilt,tiltFactor); } } tmp = round_fx(BASOP_Util_fPow(L_deposit_h(tiltFactor), 0, L_deposit_h(self->pTCI->upperIndex[self->pTCI->numIndexes-1]-self->pTCI->lowerIndex[self->pTCI->numIndexes-1]+1),15, &exp)); BASOP_SATURATE_WARNING_OFF /*next op may result in 32768*/ tmp = shl(tmp, exp); BASOP_SATURATE_WARNING_ON tilt = mult_r(tilt,tmp); FOR (l = add(self->pTCI->upperIndex[self->pTCI->numIndexes-1], 1); l < crossOverFreq; l++) { Word16 x = self->lastBlockData.spectralData[l]; Word32 y; rnd = extract_l(L_mac0(13849, rnd, 31821)); y = L_mult(tilt,rnd); nrgNoiseInLastFrame = L_add(nrgNoiseInLastFrame, Mpy_32_16_1(L_msu(0, x,fac),x)); x = round_fx(y); nrgWhiteNoise = L_add(nrgWhiteNoise, Mpy_32_16_1(L_msu(0, x,fac),x)); mdctSpectrum[l] = y; /* 15Q16 L_deposit_l(y);*/ move32(); tilt = mult_r(tilt,tiltFactor); } IF (nrgNoiseInLastFrame == 0) { set32_fx(mdctSpectrum, 0, crossOverFreq); *mdctSpectrum_exp = SPEC_EXP_DEC; } ELSE { exp_last = add(ld,shl(self->lastBlockData.spectralData_exp,1)); exp_noise = add(ld,shl(15,1)); ld = norm_l(nrgNoiseInLastFrame); nrgNoiseInLastFrame = L_shl(nrgNoiseInLastFrame,ld); exp_last = sub(exp_last,ld); ld = norm_l(nrgWhiteNoise); nrgWhiteNoise = L_shl(nrgWhiteNoise,ld); exp_noise = sub(exp_noise,ld); exp = sub(exp_last, exp_noise); IF(nrgNoiseInLastFrame > nrgWhiteNoise) { nrgNoiseInLastFrame = L_shr(nrgNoiseInLastFrame,1); exp = add(exp,1); } tmp = div_l(nrgNoiseInLastFrame,round_fx(nrgWhiteNoise)); tmp = Sqrt16(tmp, &exp); g = mult_r(g,tmp); L_tmp = L_deposit_h(0); ld = sub(self->lastBlockData.spectralData_exp, 15); exp = sub(ld, exp); IF(exp > 0) { g = shr(g,exp); *mdctSpectrum_exp = self->lastBlockData.spectralData_exp; } ELSE { crossfadeGain = shl(crossfadeGain,exp); *mdctSpectrum_exp = sub(self->lastBlockData.spectralData_exp,exp); } /*make a headroom for mdct_shaping*/ exp = sub(*mdctSpectrum_exp, SPEC_EXP_DEC); IF(exp < 0) { *mdctSpectrum_exp = SPEC_EXP_DEC; } ELSE { exp = 0; } FOR (l = 0; l < self->pTCI->lowerIndex[0]; l++) { Word16 const x = self->lastBlockData.spectralData[l]; Word32 const y = mdctSpectrum[l]; if(g > 0) { L_tmp = Mpy_32_16_1(y,g); } L_tmp2 = L_msu(L_tmp,crossfadeGain,x); if(y > 0) { L_tmp2 = L_mac(L_tmp,crossfadeGain,x); } mdctSpectrum[l] = L_shl(L_tmp2, exp); move32(); } FOR (i = 1; i < self->pTCI->numIndexes; i++) { FOR (l = self->pTCI->upperIndex[i-1]+1; l < self->pTCI->lowerIndex[i]; l++) { Word16 const x = self->lastBlockData.spectralData[l]; Word32 const y = mdctSpectrum[l]; if(g > 0) { L_tmp = Mpy_32_16_1(y,g); } L_tmp2 = L_msu(L_tmp,crossfadeGain,x); if(y > 0) { L_tmp2 = L_mac(L_tmp,crossfadeGain,x); } mdctSpectrum[l] = L_shl(L_tmp2, exp); move32(); } } /* initialize bins of tonal components with zero: basically not necessary, but currently the whole spectrum is rescaled in mdct_noiseShaping() and then there would be a processing of uninitialized values */ FOR (i = 0; i < self->pTCI->numIndexes; i++) { FOR (l = self->pTCI->lowerIndex[i]; l <= self->pTCI->upperIndex[i]; l++) { mdctSpectrum[l] = L_deposit_l(0); } } FOR (l = add(self->pTCI->upperIndex[self->pTCI->numIndexes-1], 1); l < crossOverFreq; l++) { Word16 const x = self->lastBlockData.spectralData[l]; Word32 const y = mdctSpectrum[l]; if(g > 0) { L_tmp = Mpy_32_16_1(y,g); } L_tmp2 = L_msu(L_tmp,crossfadeGain,x); if(y > 0) { L_tmp2 = L_mac(L_tmp,crossfadeGain,x); } mdctSpectrum[l] = L_shl(L_tmp2, exp); move32(); } } exp = sub(self->lastBlockData.spectralData_exp, sub(*mdctSpectrum_exp,16)); FOR (l = crossOverFreq; l < self->lastBlockData.nSamples; l++) { mdctSpectrum[l] = L_shl(L_deposit_l(self->lastBlockData.spectralData[l]), exp); move32(); } } } *pSeed = rnd; move16(); return TONALMDCTCONCEAL_OK; } TONALMDCTCONCEAL_ERROR TonalMDCTConceal_Apply(TonalMDCTConcealPtr self, /*IN */ Word32 *mdctSpectrum, /*IN/OUT*/ Word16 *mdctSpectrum_exp /*IN */ ) { Word16 i, l, exp; Word16 * phaseDiff, * pCurrentPhase; Word32 phaseToAdd, currentPhase; Word32 powerSpectrum[L_FRAME_MAX]; Word16 nSamples; IF (self->lastBlockData.blockIsValid & self->secondLastBlockData.blockIsValid) { assert(self->pTCI->numIndexes > 0); nSamples = self->nNonZeroSamples; move16(); assert(self->pTCI->upperIndex[self->pTCI->numIndexes-1] < nSamples); mdct_shaping_16(self->secondLastPowerSpectrum, self->nSamplesCore, nSamples, self->secondLastBlockData.scaleFactors, self->secondLastBlockData.scaleFactors_exp, self->secondLastBlockData.scaleFactors_max_e, powerSpectrum); phaseDiff = self->pTCI->phaseDiff; /* if multiple frame loss occurs use the phase from the last frame and continue rotating */ pCurrentPhase = self->pTCI->phase_currentFramePredicted; exp = sub(*mdctSpectrum_exp, add(add(self->secondLastPowerSpectrum_exp, add(self->secondLastBlockData.gain_tcx_exp,1)),self->secondLastBlockData.scaleFactors_max_e)); IF (!self->lastBlockData.blockIsConcealed) { if (self->secondLastBlockData.tonalConcealmentActive != 0) { self->nFramesLost = add(self->nFramesLost,2); /*Q1*/ move16(); } if (self->secondLastBlockData.tonalConcealmentActive == 0) { self->nFramesLost = 3; /*Q1*/ move16(); } } /* for each index group */ FOR (i = 0; i < self->pTCI->numIndexes; i++) { /*phaseToAdd = self->nFramesLost*phaseDiff[i]; */ phaseToAdd = L_mult0(self->nFramesLost,phaseDiff[i]); /*Q1*3Q12=2Q13*/ /* Move phaseToAdd to range -PI..PI */ WHILE (L_sub(phaseToAdd, 25736l/*EVS_PI Q13*/) > 0) { phaseToAdd = L_sub(phaseToAdd, 51472l/*2*EVS_PI Q13*/); } WHILE (L_sub(phaseToAdd, -25736l/*-EVS_PI Q13*/) < 0) { phaseToAdd = L_add(phaseToAdd, 51472l/*2*EVS_PI Q13*/); } FOR (l = self->pTCI->lowerIndex[i]; l <= self->pTCI->upperIndex[i]; l++) { /* *pCurrentPhase and phaseToAdd are in range -PI..PI */ currentPhase = L_mac0(phaseToAdd, (*pCurrentPhase++), 1); /*2Q13+2Q13=3Q13*/ if (L_sub(currentPhase, 25736l/*EVS_PI Q13*/) > 0) { currentPhase = L_sub(currentPhase, 51472l/*2*EVS_PI Q13*/); } if (L_sub(currentPhase, -25736l/*-EVS_PI Q13*/) < 0) { currentPhase = L_add(currentPhase, 51472l/*2*EVS_PI Q13*/); } /* getCosWord16 returns 1Q14*/ mdctSpectrum[l] = Mpy_32_16_1(powerSpectrum[l],getCosWord16(extract_l(currentPhase))); move32(); mdctSpectrum[l] = L_shr(mdctSpectrum[l], exp); } } } self->nFramesLost = add(self->nFramesLost,2); /*Q1*/ move16(); return TONALMDCTCONCEAL_OK; } TONALMDCTCONCEAL_ERROR TonalMDCTConceal_SaveTimeSignal( TonalMDCTConcealPtr self, Word16* timeSignal, Word16 nNewSamples ) { IF (sub(nNewSamples,self->nSamples) == 0) { assert(nNewSamples <= L_FRAME_MAX); IF (!self->secondLastBlockData.tonalConcealmentActive) { Copy(self->lastPcmOut + self->nSamples/2, self->secondLastPcmOut, self->nSamples/2); } Copy(timeSignal, self->lastPcmOut, self->nSamples); } return TONALMDCTCONCEAL_OK; } static void CalcPowerSpec(Word32 * mdctSpec, /* i: MDCT spectrum Q31,mdctSpec_exp */ Word16 mdctSpec_exp, /* i: exponent of MDCT spectrum */ Word32 * mdstSpec, /* i: MDST spectrum Q31,mdstSpec_exp */ Word16 mdstSpec_exp, /* i: exponent of MDST spectrum */ Word16 nSamples, /* i: frame size */ Word16 floorPowerSpectrum, /* i: lower limit for power spectrum bins Q0 */ Word32 * powerSpec, /* o: power spectrum */ Word16 * powerSpec_exp) /* o: exponent of power spectrum */ { Word16 k, s1, s2, tmp; Word32 x, L_tmp, L_tmp_floor; k = s_max(mdctSpec_exp, mdstSpec_exp); *powerSpec_exp = add(add(k, k), 3); /*extra 3 bits of headroom for MA filter in getEnvelope*/ move16(); s1 = sub(*powerSpec_exp, add(mdctSpec_exp, mdctSpec_exp)); s2 = sub(*powerSpec_exp, add(mdstSpec_exp, mdstSpec_exp)); k = sub(31, *powerSpec_exp); /* If the signal is bellow floor, special care is needed for *powerSpec_exp */ IF (sub(add(16-3, norm_s(floorPowerSpectrum)), k) < 0) /*extra 3 bits of headroom for MA filter in getEnvelope*/ { k = sub(k, add(16-3, norm_s(floorPowerSpectrum))); /*extra 3 bits of headroom for MA filter in getEnvelope*/ *powerSpec_exp = add(*powerSpec_exp, k); s1 = add(s1, k); s2 = add(s2, k); k = add(16-3, norm_s(floorPowerSpectrum)); } L_tmp_floor = L_shl(L_deposit_l(floorPowerSpectrum), k); tmp = sub(nSamples, 2); FOR (k = 1; k <= tmp; k++) { x = Mpy_32_32(mdctSpec[k], mdctSpec[k]); /*Q31,2*mdctSpec_exp*/ L_tmp = Mpy_32_32(mdstSpec[k], mdstSpec[k]); /*Q31,2*mdstSpec_exp*/ x = L_add(L_shr(x,s1), L_shr(L_tmp,s2)); /*Q31,*powerSpec_exp*/ powerSpec[k] = L_max(L_tmp_floor, x); move32(); } powerSpec[0] = L_shr(powerSpec[1], 1); move32(); powerSpec[nSamples-1] = L_shr(powerSpec[nSamples-2], 1); move32(); }