/* ** Copyright 2003-2010, VisualOn, Inc. ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. */ /******************************************************************************* File: tns.c Content: Definition TNS tools functions *******************************************************************************/ #include "basic_op.h" #include "oper_32b.h" #include "assert.h" #include "aac_rom.h" #include "psy_const.h" #include "tns.h" #include "tns_param.h" #include "psy_configuration.h" #include "tns_func.h" #define UNUSED(x) (void)(x) #define TNS_MODIFY_BEGIN 2600 /* Hz */ #define RATIO_PATCH_LOWER_BORDER 380 /* Hz */ #define TNS_GAIN_THRESH 141 /* 1.41*100 */ #define NORM_COEF 0x028f5c28 static const Word32 TNS_PARCOR_THRESH = 0x0ccccccd; /* 0.1*(1 << 31) */ /* Limit bands to > 2.0 kHz */ static unsigned short tnsMinBandNumberLong[12] = { 11, 12, 15, 16, 17, 20, 25, 26, 24, 28, 30, 31 }; static unsigned short tnsMinBandNumberShort[12] = { 2, 2, 2, 3, 3, 4, 6, 6, 8, 10, 10, 12 }; /**************************************/ /* Main/Low Profile TNS Parameters */ /**************************************/ static unsigned short tnsMaxBandsLongMainLow[12] = { 31, 31, 34, 40, 42, 51, 46, 46, 42, 42, 42, 39 }; static unsigned short tnsMaxBandsShortMainLow[12] = { 9, 9, 10, 14, 14, 14, 14, 14, 14, 14, 14, 14 }; static void CalcWeightedSpectrum(const Word32 spectrum[], Word16 weightedSpectrum[], Word32* sfbEnergy, const Word16* sfbOffset, Word16 lpcStartLine, Word16 lpcStopLine, Word16 lpcStartBand,Word16 lpcStopBand, Word32 *pWork32); void AutoCorrelation(const Word16 input[], Word32 corr[], Word16 samples, Word16 corrCoeff); static Word16 AutoToParcor(Word32 workBuffer[], Word32 reflCoeff[], Word16 numOfCoeff); static Word16 CalcTnsFilter(const Word16* signal, const Word32 window[], Word16 numOfLines, Word16 tnsOrder, Word32 parcor[]); static void Parcor2Index(const Word32 parcor[], Word16 index[], Word16 order, Word16 bitsPerCoeff); static void Index2Parcor(const Word16 index[], Word32 parcor[], Word16 order, Word16 bitsPerCoeff); static void AnalysisFilterLattice(const Word32 signal[], Word16 numOfLines, const Word32 parCoeff[], Word16 order, Word32 output[]); /** * * function name: FreqToBandWithRounding * description: Retrieve index of nearest band border * returnt: index * */ static Word16 FreqToBandWithRounding(Word32 freq, /*!< frequency in Hertz */ Word32 fs, /*!< Sampling frequency in Hertz */ Word16 numOfBands, /*!< total number of bands */ const Word16 *bandStartOffset) /*!< table of band borders */ { Word32 lineNumber, band; Word32 temp, shift; /* assert(freq >= 0); */ shift = norm_l(fs); lineNumber = (extract_l(fixmul((bandStartOffset[numOfBands] << 2),Div_32(freq << shift,fs << shift))) + 1) >> 1; /* freq > fs/2 */ temp = lineNumber - bandStartOffset[numOfBands] ; if (temp >= 0) return numOfBands; /* find band the line number lies in */ for (band=0; band 0) break; } temp = (lineNumber - bandStartOffset[band]); temp = (temp - (bandStartOffset[band + 1] - lineNumber)); if ( temp > 0 ) { band = band + 1; } return extract_l(band); } /** * * function name: InitTnsConfigurationLong * description: Fill TNS_CONFIG structure with sensible content for long blocks * returns: 0 if success * */ Word16 InitTnsConfigurationLong(Word32 bitRate, /*!< bitrate */ Word32 sampleRate, /*!< Sampling frequency */ Word16 channels, /*!< number of channels */ TNS_CONFIG *tC, /*!< TNS Config struct (modified) */ PSY_CONFIGURATION_LONG *pC, /*!< psy config struct */ Word16 active) /*!< tns active flag */ { Word32 bitratePerChannel; tC->maxOrder = TNS_MAX_ORDER; tC->tnsStartFreq = 1275; tC->coefRes = 4; /* to avoid integer division */ if ( sub(channels,2) == 0 ) { bitratePerChannel = bitRate >> 1; } else { bitratePerChannel = bitRate; } tC->tnsMaxSfb = tnsMaxBandsLongMainLow[pC->sampRateIdx]; tC->tnsActive = active; /* now calc band and line borders */ tC->tnsStopBand = min(pC->sfbCnt, tC->tnsMaxSfb); tC->tnsStopLine = pC->sfbOffset[tC->tnsStopBand]; tC->tnsStartBand = FreqToBandWithRounding(tC->tnsStartFreq, sampleRate, pC->sfbCnt, (const Word16*)pC->sfbOffset); tC->tnsModifyBeginCb = FreqToBandWithRounding(TNS_MODIFY_BEGIN, sampleRate, pC->sfbCnt, (const Word16*)pC->sfbOffset); tC->tnsRatioPatchLowestCb = FreqToBandWithRounding(RATIO_PATCH_LOWER_BORDER, sampleRate, pC->sfbCnt, (const Word16*)pC->sfbOffset); tC->tnsStartLine = pC->sfbOffset[tC->tnsStartBand]; tC->lpcStopBand = tnsMaxBandsLongMainLow[pC->sampRateIdx]; tC->lpcStopBand = min(tC->lpcStopBand, pC->sfbActive); tC->lpcStopLine = pC->sfbOffset[tC->lpcStopBand]; tC->lpcStartBand = tnsMinBandNumberLong[pC->sampRateIdx]; tC->lpcStartLine = pC->sfbOffset[tC->lpcStartBand]; tC->threshold = TNS_GAIN_THRESH; return(0); } /** * * function name: InitTnsConfigurationShort * description: Fill TNS_CONFIG structure with sensible content for short blocks * returns: 0 if success * */ Word16 InitTnsConfigurationShort(Word32 bitRate, /*!< bitrate */ Word32 sampleRate, /*!< Sampling frequency */ Word16 channels, /*!< number of channels */ TNS_CONFIG *tC, /*!< TNS Config struct (modified) */ PSY_CONFIGURATION_SHORT *pC, /*!< psy config struct */ Word16 active) /*!< tns active flag */ { Word32 bitratePerChannel; tC->maxOrder = TNS_MAX_ORDER_SHORT; tC->tnsStartFreq = 2750; tC->coefRes = 3; /* to avoid integer division */ if ( sub(channels,2) == 0 ) { bitratePerChannel = L_shr(bitRate,1); } else { bitratePerChannel = bitRate; } tC->tnsMaxSfb = tnsMaxBandsShortMainLow[pC->sampRateIdx]; tC->tnsActive = active; /* now calc band and line borders */ tC->tnsStopBand = min(pC->sfbCnt, tC->tnsMaxSfb); tC->tnsStopLine = pC->sfbOffset[tC->tnsStopBand]; tC->tnsStartBand=FreqToBandWithRounding(tC->tnsStartFreq, sampleRate, pC->sfbCnt, (const Word16*)pC->sfbOffset); tC->tnsModifyBeginCb = FreqToBandWithRounding(TNS_MODIFY_BEGIN, sampleRate, pC->sfbCnt, (const Word16*)pC->sfbOffset); tC->tnsRatioPatchLowestCb = FreqToBandWithRounding(RATIO_PATCH_LOWER_BORDER, sampleRate, pC->sfbCnt, (const Word16*)pC->sfbOffset); tC->tnsStartLine = pC->sfbOffset[tC->tnsStartBand]; tC->lpcStopBand = tnsMaxBandsShortMainLow[pC->sampRateIdx]; tC->lpcStopBand = min(tC->lpcStopBand, pC->sfbActive); tC->lpcStopLine = pC->sfbOffset[tC->lpcStopBand]; tC->lpcStartBand = tnsMinBandNumberShort[pC->sampRateIdx]; tC->lpcStartLine = pC->sfbOffset[tC->lpcStartBand]; tC->threshold = TNS_GAIN_THRESH; return(0); } /** * * function name: TnsDetect * description: Calculate TNS filter and decide on TNS usage * returns: 0 if success * */ Word32 TnsDetect(TNS_DATA* tnsData, /*!< tns data structure (modified) */ TNS_CONFIG tC, /*!< tns config structure */ Word32* pScratchTns, /*!< pointer to scratch space */ const Word16 sfbOffset[], /*!< scalefactor size and table */ Word32* spectrum, /*!< spectral data */ Word16 subBlockNumber, /*!< subblock num */ Word16 blockType, /*!< blocktype (long or short) */ Word32 * sfbEnergy) /*!< sfb-wise energy */ { Word32 predictionGain; Word32 temp; Word32* pWork32 = &pScratchTns[subBlockNumber >> 8]; Word16* pWeightedSpectrum = (Word16 *)&pScratchTns[subBlockNumber >> 8]; if (tC.tnsActive) { CalcWeightedSpectrum(spectrum, pWeightedSpectrum, sfbEnergy, sfbOffset, tC.lpcStartLine, tC.lpcStopLine, tC.lpcStartBand, tC.lpcStopBand, pWork32); temp = blockType - SHORT_WINDOW; if ( temp != 0 ) { predictionGain = CalcTnsFilter( &pWeightedSpectrum[tC.lpcStartLine], tC.acfWindow, tC.lpcStopLine - tC.lpcStartLine, tC.maxOrder, tnsData->dataRaw.tnsLong.subBlockInfo.parcor); temp = predictionGain - tC.threshold; if ( temp > 0 ) { tnsData->dataRaw.tnsLong.subBlockInfo.tnsActive = 1; } else { tnsData->dataRaw.tnsLong.subBlockInfo.tnsActive = 0; } tnsData->dataRaw.tnsLong.subBlockInfo.predictionGain = predictionGain; } else{ predictionGain = CalcTnsFilter( &pWeightedSpectrum[tC.lpcStartLine], tC.acfWindow, tC.lpcStopLine - tC.lpcStartLine, tC.maxOrder, tnsData->dataRaw.tnsShort.subBlockInfo[subBlockNumber].parcor); temp = predictionGain - tC.threshold; if ( temp > 0 ) { tnsData->dataRaw.tnsShort.subBlockInfo[subBlockNumber].tnsActive = 1; } else { tnsData->dataRaw.tnsShort.subBlockInfo[subBlockNumber].tnsActive = 0; } tnsData->dataRaw.tnsShort.subBlockInfo[subBlockNumber].predictionGain = predictionGain; } } else{ temp = blockType - SHORT_WINDOW; if ( temp != 0 ) { tnsData->dataRaw.tnsLong.subBlockInfo.tnsActive = 0; tnsData->dataRaw.tnsLong.subBlockInfo.predictionGain = 0; } else { tnsData->dataRaw.tnsShort.subBlockInfo[subBlockNumber].tnsActive = 0; tnsData->dataRaw.tnsShort.subBlockInfo[subBlockNumber].predictionGain = 0; } } return(0); } /***************************************************************************** * * function name: TnsSync * description: update tns parameter * *****************************************************************************/ void TnsSync(TNS_DATA *tnsDataDest, const TNS_DATA *tnsDataSrc, const TNS_CONFIG tC, const Word16 subBlockNumber, const Word16 blockType) { TNS_SUBBLOCK_INFO *sbInfoDest; const TNS_SUBBLOCK_INFO *sbInfoSrc; Word32 i, temp; temp = blockType - SHORT_WINDOW; if ( temp != 0 ) { sbInfoDest = &tnsDataDest->dataRaw.tnsLong.subBlockInfo; sbInfoSrc = &tnsDataSrc->dataRaw.tnsLong.subBlockInfo; } else { sbInfoDest = &tnsDataDest->dataRaw.tnsShort.subBlockInfo[subBlockNumber]; sbInfoSrc = &tnsDataSrc->dataRaw.tnsShort.subBlockInfo[subBlockNumber]; } if (100*abs_s(sbInfoDest->predictionGain - sbInfoSrc->predictionGain) < (3 * sbInfoDest->predictionGain)) { sbInfoDest->tnsActive = sbInfoSrc->tnsActive; for ( i=0; i< tC.maxOrder; i++) { sbInfoDest->parcor[i] = sbInfoSrc->parcor[i]; } } } /***************************************************************************** * * function name: TnsEncode * description: do TNS filtering * returns: 0 if success * *****************************************************************************/ Word16 TnsEncode(TNS_INFO* tnsInfo, /*!< tns info structure (modified) */ TNS_DATA* tnsData, /*!< tns data structure (modified) */ Word16 numOfSfb, /*!< number of scale factor bands */ TNS_CONFIG tC, /*!< tns config structure */ Word16 lowPassLine, /*!< lowpass line */ Word32* spectrum, /*!< spectral data (modified) */ Word16 subBlockNumber, /*!< subblock num */ Word16 blockType) /*!< blocktype (long or short) */ { Word32 i; Word32 temp_s; Word32 temp; TNS_SUBBLOCK_INFO *psubBlockInfo; temp_s = blockType - SHORT_WINDOW; if ( temp_s != 0) { psubBlockInfo = &tnsData->dataRaw.tnsLong.subBlockInfo; if (psubBlockInfo->tnsActive == 0) { tnsInfo->tnsActive[subBlockNumber] = 0; return(0); } else { Parcor2Index(psubBlockInfo->parcor, tnsInfo->coef, tC.maxOrder, tC.coefRes); Index2Parcor(tnsInfo->coef, psubBlockInfo->parcor, tC.maxOrder, tC.coefRes); for (i=tC.maxOrder - 1; i>=0; i--) { temp = psubBlockInfo->parcor[i] - TNS_PARCOR_THRESH; if ( temp > 0 ) break; temp = psubBlockInfo->parcor[i] + TNS_PARCOR_THRESH; if ( temp < 0 ) break; } tnsInfo->order[subBlockNumber] = i + 1; tnsInfo->tnsActive[subBlockNumber] = 1; for (i=subBlockNumber+1; itnsActive[i] = 0; } tnsInfo->coefRes[subBlockNumber] = tC.coefRes; tnsInfo->length[subBlockNumber] = numOfSfb - tC.tnsStartBand; AnalysisFilterLattice(&(spectrum[tC.tnsStartLine]), (min(tC.tnsStopLine,lowPassLine) - tC.tnsStartLine), psubBlockInfo->parcor, tnsInfo->order[subBlockNumber], &(spectrum[tC.tnsStartLine])); } } /* if (blockType!=SHORT_WINDOW) */ else /*short block*/ { psubBlockInfo = &tnsData->dataRaw.tnsShort.subBlockInfo[subBlockNumber]; if (psubBlockInfo->tnsActive == 0) { tnsInfo->tnsActive[subBlockNumber] = 0; return(0); } else { Parcor2Index(psubBlockInfo->parcor, &tnsInfo->coef[subBlockNumber*TNS_MAX_ORDER_SHORT], tC.maxOrder, tC.coefRes); Index2Parcor(&tnsInfo->coef[subBlockNumber*TNS_MAX_ORDER_SHORT], psubBlockInfo->parcor, tC.maxOrder, tC.coefRes); for (i=(tC.maxOrder - 1); i>=0; i--) { temp = psubBlockInfo->parcor[i] - TNS_PARCOR_THRESH; if ( temp > 0 ) break; temp = psubBlockInfo->parcor[i] + TNS_PARCOR_THRESH; if ( temp < 0 ) break; } tnsInfo->order[subBlockNumber] = i + 1; tnsInfo->tnsActive[subBlockNumber] = 1; tnsInfo->coefRes[subBlockNumber] = tC.coefRes; tnsInfo->length[subBlockNumber] = numOfSfb - tC.tnsStartBand; AnalysisFilterLattice(&(spectrum[tC.tnsStartLine]), (tC.tnsStopLine - tC.tnsStartLine), psubBlockInfo->parcor, tnsInfo->order[subBlockNumber], &(spectrum[tC.tnsStartLine])); } } return(0); } /***************************************************************************** * * function name: m_pow2_cordic * description: Iterative power function * * Calculates pow(2.0,x-1.0*(scale+1)) with INT_BITS bit precision * using modified cordic algorithm * returns: the result of pow2 * *****************************************************************************/ static Word32 m_pow2_cordic(Word32 x, Word16 scale) { Word32 k; Word32 accu_y = 0x40000000; accu_y = L_shr(accu_y,scale); for(k=1; k= 0) { x = L_sub(x, z); accu_y = L_add(accu_y, (accu_y >> k)); } } return(accu_y); } /***************************************************************************** * * function name: CalcWeightedSpectrum * description: Calculate weighted spectrum for LPC calculation * *****************************************************************************/ static void CalcWeightedSpectrum(const Word32 spectrum[], /*!< input spectrum */ Word16 weightedSpectrum[], Word32 *sfbEnergy, /*!< sfb energies */ const Word16 *sfbOffset, Word16 lpcStartLine, Word16 lpcStopLine, Word16 lpcStartBand, Word16 lpcStopBand, Word32 *pWork32) { #define INT_BITS_SCAL 1<<(INT_BITS/2) Word32 i, sfb, shift; Word32 maxShift; Word32 tmp_s, tmp2_s; Word32 tmp, tmp2; Word32 maxWS; Word32 tnsSfbMean[MAX_SFB]; /* length [lpcStopBand-lpcStartBand] should be sufficient here */ maxWS = 0; /* calc 1.0*2^-INT_BITS/2/sqrt(en) */ for( sfb = lpcStartBand; sfb < lpcStopBand; sfb++) { tmp2 = sfbEnergy[sfb] - 2; if( tmp2 > 0) { tmp = rsqrt(sfbEnergy[sfb], INT_BITS); if(tmp > INT_BITS_SCAL) { shift = norm_l(tmp); tmp = Div_32( INT_BITS_SCAL << shift, tmp << shift ); } else { tmp = 0x7fffffff; } } else { tmp = 0x7fffffff; } tnsSfbMean[sfb] = tmp; } /* spread normalized values from sfbs to lines */ sfb = lpcStartBand; tmp = tnsSfbMean[sfb]; for ( i=lpcStartLine; i=lpcStartLine; i--){ pWork32[i] = (pWork32[i] + pWork32[i + 1]) >> 1; } /* filter up */ for (i=(lpcStartLine + 1); i> 1; } /* weight and normalize */ for (i=lpcStartLine; i= 0) { for (i=lpcStartLine; i> maxShift; } } else { maxShift = -maxShift; for (i=lpcStartLine; i> scf)); } corr[0] = accu; /* early termination if all corr coeffs are likely going to be zero */ if(corr[0] == 0) return ; /* calc all other corrCoef: R[j] = sum { t[i] * t[i+j] } ; i = 0..(N-j-1), j=1..p */ for(i=1; i> scf)); } corr[i] = accu; } } #endif /***************************************************************************** * * function name: AutoToParcor * description: conversion autocorrelation to reflection coefficients * returns: prediction gain * input: input values, no. of output values (=order), * ptr. to workbuffer (required size: 2*order) * output: reflection coefficients * *****************************************************************************/ static Word16 AutoToParcor(Word32 workBuffer[], Word32 reflCoeff[], Word16 numOfCoeff) { Word32 i, j, shift; Word32 *pWorkBuffer; /* temp pointer */ Word32 predictionGain = 0; Word32 num, denom; Word32 temp, workBuffer0; num = workBuffer[0]; temp = workBuffer[numOfCoeff]; for(i=0; i 0) index=i; } return extract_l(index - 4); } static Word16 Search4(Word32 parcor) { Word32 index = 0; Word32 i; Word32 temp; for (i=0;i<16;i++) { temp = L_sub(parcor, tnsCoeff4Borders[i]); if (temp > 0) index=i; } return extract_l(index - 8); } /***************************************************************************** * * functionname: Parcor2Index * description: quantization index for reflection coefficients * *****************************************************************************/ static void Parcor2Index(const Word32 parcor[], /*!< parcor coefficients */ Word16 index[], /*!< quantized coeff indices */ Word16 order, /*!< filter order */ Word16 bitsPerCoeff) { /*!< quantizer resolution */ Word32 i; Word32 temp; for(i=0; i> 1; tmpSave = x; for (i=0; i<(order - 1); i++) { tmp = L_add(fixmul(coef_par[i], x), state_par[i]); x = L_add(fixmul(coef_par[i], state_par[i]), x); state_par[i] = tmpSave; tmpSave = tmp; } /* last stage: only need half operations */ accu = fixmul(state_par[order - 1], coef_par[(order - 1)]); state_par[(order - 1)] = tmpSave; x = L_add(accu, x); x = L_add(x, x); return x; } /***************************************************************************** * * functionname: AnalysisFilterLattice * description: filters spectral lines with TNS filter * *****************************************************************************/ static void AnalysisFilterLattice(const Word32 signal[], /*!< input spectrum */ Word16 numOfLines, /*!< no. of lines */ const Word32 parCoeff[],/*!< PARC coefficients */ Word16 order, /*!< filter order */ Word32 output[]) /*!< filtered signal values */ { Word32 state_par[TNS_MAX_ORDER]; Word32 j; for ( j=0; j> 2); } } }