909 lines
28 KiB
C
909 lines
28 KiB
C
/*
|
|
** 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<numOfBands; band++) {
|
|
temp = bandStartOffset[band + 1] - lineNumber;
|
|
if (temp > 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 __unused;*/
|
|
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 __unused;*/
|
|
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; i<TRANS_FAC; i++) {
|
|
tnsInfo->tnsActive[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: 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<lpcStopLine; i++){
|
|
tmp_s = sfbOffset[sfb + 1] - i;
|
|
if ( tmp_s == 0 ) {
|
|
sfb = sfb + 1;
|
|
tmp2_s = sfb + 1 - lpcStopBand;
|
|
if (tmp2_s <= 0) {
|
|
tmp = tnsSfbMean[sfb];
|
|
}
|
|
}
|
|
pWork32[i] = tmp;
|
|
}
|
|
/*filter down*/
|
|
for (i=(lpcStopLine - 2); i>=lpcStartLine; i--){
|
|
pWork32[i] = (pWork32[i] + pWork32[i + 1]) >> 1;
|
|
}
|
|
/* filter up */
|
|
for (i=(lpcStartLine + 1); i<lpcStopLine; i++){
|
|
pWork32[i] = (pWork32[i] + pWork32[i - 1]) >> 1;
|
|
}
|
|
|
|
/* weight and normalize */
|
|
for (i=lpcStartLine; i<lpcStopLine; i++){
|
|
pWork32[i] = MULHIGH(pWork32[i], spectrum[i]);
|
|
maxWS |= L_abs(pWork32[i]);
|
|
}
|
|
maxShift = norm_l(maxWS);
|
|
|
|
maxShift = 16 - maxShift;
|
|
if(maxShift >= 0)
|
|
{
|
|
for (i=lpcStartLine; i<lpcStopLine; i++){
|
|
weightedSpectrum[i] = pWork32[i] >> maxShift;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
maxShift = -maxShift;
|
|
for (i=lpcStartLine; i<lpcStopLine; i++){
|
|
weightedSpectrum[i] = saturate(pWork32[i] << maxShift);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* function name: CalcTnsFilter
|
|
* description: LPC calculation for one TNS filter
|
|
* returns: prediction gain
|
|
* input: signal spectrum, acf window, no. of spectral lines,
|
|
* max. TNS order, ptr. to reflection ocefficients
|
|
* output: reflection coefficients
|
|
*(half) window size must be larger than tnsOrder !!*
|
|
******************************************************************************/
|
|
|
|
static Word16 CalcTnsFilter(const Word16 *signal,
|
|
const Word32 window[],
|
|
Word16 numOfLines,
|
|
Word16 tnsOrder,
|
|
Word32 parcor[])
|
|
{
|
|
Word32 parcorWorkBuffer[2*TNS_MAX_ORDER+1];
|
|
Word32 predictionGain;
|
|
Word32 i;
|
|
Word32 tnsOrderPlus1 = tnsOrder + 1;
|
|
|
|
UNUSED(window);
|
|
|
|
assert(tnsOrder <= TNS_MAX_ORDER); /* remove asserts later? (btg) */
|
|
|
|
for(i=0;i<tnsOrder;i++) {
|
|
parcor[i] = 0;
|
|
}
|
|
|
|
AutoCorrelation(signal, parcorWorkBuffer, numOfLines, tnsOrderPlus1);
|
|
|
|
/* early return if signal is very low: signal prediction off, with zero parcor coeffs */
|
|
if (parcorWorkBuffer[0] == 0)
|
|
return 0;
|
|
|
|
predictionGain = AutoToParcor(parcorWorkBuffer, parcor, tnsOrder);
|
|
|
|
return(predictionGain);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* function name: AutoCorrelation
|
|
* description: calc. autocorrelation (acf)
|
|
* returns: -
|
|
* input: input values, no. of input values, no. of acf values
|
|
* output: acf values
|
|
*
|
|
*****************************************************************************/
|
|
#ifndef ARMV5E
|
|
void AutoCorrelation(const Word16 input[],
|
|
Word32 corr[],
|
|
Word16 samples,
|
|
Word16 corrCoeff) {
|
|
Word32 i, j, isamples;
|
|
Word32 accu;
|
|
Word32 scf;
|
|
|
|
scf = 10 - 1;
|
|
|
|
isamples = samples;
|
|
/* calc first corrCoef: R[0] = sum { t[i] * t[i] } ; i = 0..N-1 */
|
|
accu = 0;
|
|
for(j=0; j<isamples; j++) {
|
|
accu = L_add(accu, ((input[j] * input[j]) >> 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<corrCoeff; i++) {
|
|
isamples = isamples - 1;
|
|
accu = 0;
|
|
for(j=0; j<isamples; j++) {
|
|
accu = L_add(accu, ((input[j] * input[j+i]) >> scf));
|
|
}
|
|
corr[i] = accu;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* function name: AutoToParcor
|
|
* description: conversion autocorrelation to reflection coefficients
|
|
* returns: prediction gain
|
|
* input: <order+1> input values, no. of output values (=order),
|
|
* ptr. to workbuffer (required size: 2*order)
|
|
* output: <order> 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<numOfCoeff-1; i++) {
|
|
workBuffer[i + numOfCoeff] = workBuffer[i + 1];
|
|
}
|
|
workBuffer[i + numOfCoeff] = temp;
|
|
|
|
for(i=0; i<numOfCoeff; i++) {
|
|
Word32 refc;
|
|
|
|
|
|
if (workBuffer[0] < L_abs(workBuffer[i + numOfCoeff])) {
|
|
return 0 ;
|
|
}
|
|
shift = norm_l(workBuffer[0]);
|
|
workBuffer0 = Div_32(1 << shift, workBuffer[0] << shift);
|
|
/* calculate refc = -workBuffer[numOfCoeff+i] / workBuffer[0]; -1 <= refc < 1 */
|
|
refc = L_negate(fixmul(workBuffer[numOfCoeff + i], workBuffer0));
|
|
|
|
reflCoeff[i] = refc;
|
|
|
|
pWorkBuffer = &(workBuffer[numOfCoeff]);
|
|
|
|
for(j=i; j<numOfCoeff; j++) {
|
|
Word32 accu1, accu2;
|
|
accu1 = L_add(pWorkBuffer[j], fixmul(refc, workBuffer[j - i]));
|
|
accu2 = L_add(workBuffer[j - i], fixmul(refc, pWorkBuffer[j]));
|
|
pWorkBuffer[j] = accu1;
|
|
workBuffer[j - i] = accu2;
|
|
}
|
|
}
|
|
|
|
denom = MULHIGH(workBuffer[0], NORM_COEF);
|
|
|
|
if (denom != 0) {
|
|
Word32 temp;
|
|
shift = norm_l(denom);
|
|
temp = Div_32(1 << shift, denom << shift);
|
|
predictionGain = fixmul(num, temp);
|
|
}
|
|
|
|
return extract_l(predictionGain);
|
|
}
|
|
|
|
|
|
|
|
static Word16 Search3(Word32 parcor)
|
|
{
|
|
Word32 index = 0;
|
|
Word32 i;
|
|
Word32 temp;
|
|
|
|
for (i=0;i<8;i++) {
|
|
temp = L_sub( parcor, tnsCoeff3Borders[i]);
|
|
if (temp > 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<order; i++) {
|
|
temp = bitsPerCoeff - 3;
|
|
if (temp == 0) {
|
|
index[i] = Search3(parcor[i]);
|
|
}
|
|
else {
|
|
index[i] = Search4(parcor[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* functionname: Index2Parcor
|
|
* description: Inverse quantization for reflection coefficients
|
|
*
|
|
*****************************************************************************/
|
|
static void Index2Parcor(const Word16 index[], /*!< quantized values */
|
|
Word32 parcor[], /*!< ptr. to reflection coefficients (output) */
|
|
Word16 order, /*!< no. of coefficients */
|
|
Word16 bitsPerCoeff) /*!< quantizer resolution */
|
|
{
|
|
Word32 i;
|
|
Word32 temp;
|
|
|
|
for (i=0; i<order; i++) {
|
|
temp = bitsPerCoeff - 4;
|
|
if ( temp == 0 ) {
|
|
parcor[i] = tnsCoeff4[index[i] + 8];
|
|
}
|
|
else {
|
|
parcor[i] = tnsCoeff3[index[i] + 4];
|
|
}
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* functionname: FIRLattice
|
|
* description: in place lattice filtering of spectral data
|
|
* returns: pointer to modified data
|
|
*
|
|
*****************************************************************************/
|
|
static Word32 FIRLattice(Word16 order, /*!< filter order */
|
|
Word32 x, /*!< spectral data */
|
|
Word32 *state_par, /*!< filter states */
|
|
const Word32 *coef_par) /*!< filter coefficients */
|
|
{
|
|
Word32 i;
|
|
Word32 accu,tmp,tmpSave;
|
|
|
|
x = x >> 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<TNS_MAX_ORDER; j++ ) {
|
|
state_par[j] = 0;
|
|
}
|
|
|
|
for(j=0; j<numOfLines; j++) {
|
|
output[j] = FIRLattice(order,signal[j],state_par,parCoeff);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* functionname: ApplyTnsMultTableToRatios
|
|
* description: Change thresholds according to tns
|
|
*
|
|
*****************************************************************************/
|
|
void ApplyTnsMultTableToRatios(Word16 startCb,
|
|
Word16 stopCb,
|
|
TNS_SUBBLOCK_INFO subInfo, /*!< TNS subblock info */
|
|
Word32 *thresholds) /*!< thresholds (modified) */
|
|
{
|
|
Word32 i;
|
|
if (subInfo.tnsActive) {
|
|
for(i=startCb; i<stopCb; i++) {
|
|
/* thresholds[i] * 0.25 */
|
|
thresholds[i] = (thresholds[i] >> 2);
|
|
}
|
|
}
|
|
}
|