summaryrefslogtreecommitdiffstats
path: root/libAACenc/src/aacenc_tns.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libAACenc/src/aacenc_tns.cpp')
-rw-r--r--libAACenc/src/aacenc_tns.cpp1348
1 files changed, 1348 insertions, 0 deletions
diff --git a/libAACenc/src/aacenc_tns.cpp b/libAACenc/src/aacenc_tns.cpp
new file mode 100644
index 0000000..933e4e7
--- /dev/null
+++ b/libAACenc/src/aacenc_tns.cpp
@@ -0,0 +1,1348 @@
+
+/* -----------------------------------------------------------------------------------------------------------
+Software License for The Fraunhofer FDK AAC Codec Library for Android
+
+© Copyright 1995 - 2012 Fraunhofer-Gesellschaft zur Förderung der angewandten Forschung e.V.
+ All rights reserved.
+
+ 1. INTRODUCTION
+The Fraunhofer FDK AAC Codec Library for Android ("FDK AAC Codec") is software that implements
+the MPEG Advanced Audio Coding ("AAC") encoding and decoding scheme for digital audio.
+This FDK AAC Codec software is intended to be used on a wide variety of Android devices.
+
+AAC's HE-AAC and HE-AAC v2 versions are regarded as today's most efficient general perceptual
+audio codecs. AAC-ELD is considered the best-performing full-bandwidth communications codec by
+independent studies and is widely deployed. AAC has been standardized by ISO and IEC as part
+of the MPEG specifications.
+
+Patent licenses for necessary patent claims for the FDK AAC Codec (including those of Fraunhofer)
+may be obtained through Via Licensing (www.vialicensing.com) or through the respective patent owners
+individually for the purpose of encoding or decoding bit streams in products that are compliant with
+the ISO/IEC MPEG audio standards. Please note that most manufacturers of Android devices already license
+these patent claims through Via Licensing or directly from the patent owners, and therefore FDK AAC Codec
+software may already be covered under those patent licenses when it is used for those licensed purposes only.
+
+Commercially-licensed AAC software libraries, including floating-point versions with enhanced sound quality,
+are also available from Fraunhofer. Users are encouraged to check the Fraunhofer website for additional
+applications information and documentation.
+
+2. COPYRIGHT LICENSE
+
+Redistribution and use in source and binary forms, with or without modification, are permitted without
+payment of copyright license fees provided that you satisfy the following conditions:
+
+You must retain the complete text of this software license in redistributions of the FDK AAC Codec or
+your modifications thereto in source code form.
+
+You must retain the complete text of this software license in the documentation and/or other materials
+provided with redistributions of the FDK AAC Codec or your modifications thereto in binary form.
+You must make available free of charge copies of the complete source code of the FDK AAC Codec and your
+modifications thereto to recipients of copies in binary form.
+
+The name of Fraunhofer may not be used to endorse or promote products derived from this library without
+prior written permission.
+
+You may not charge copyright license fees for anyone to use, copy or distribute the FDK AAC Codec
+software or your modifications thereto.
+
+Your modified versions of the FDK AAC Codec must carry prominent notices stating that you changed the software
+and the date of any change. For modified versions of the FDK AAC Codec, the term
+"Fraunhofer FDK AAC Codec Library for Android" must be replaced by the term
+"Third-Party Modified Version of the Fraunhofer FDK AAC Codec Library for Android."
+
+3. NO PATENT LICENSE
+
+NO EXPRESS OR IMPLIED LICENSES TO ANY PATENT CLAIMS, including without limitation the patents of Fraunhofer,
+ARE GRANTED BY THIS SOFTWARE LICENSE. Fraunhofer provides no warranty of patent non-infringement with
+respect to this software.
+
+You may use this FDK AAC Codec software or modifications thereto only for purposes that are authorized
+by appropriate patent licenses.
+
+4. DISCLAIMER
+
+This FDK AAC Codec software is provided by Fraunhofer on behalf of the copyright holders and contributors
+"AS IS" and WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, including but not limited to the implied warranties
+of merchantability and fitness for a particular purpose. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE for any direct, indirect, incidental, special, exemplary, or consequential damages,
+including but not limited to procurement of substitute goods or services; loss of use, data, or profits,
+or business interruption, however caused and on any theory of liability, whether in contract, strict
+liability, or tort (including negligence), arising in any way out of the use of this software, even if
+advised of the possibility of such damage.
+
+5. CONTACT INFORMATION
+
+Fraunhofer Institute for Integrated Circuits IIS
+Attention: Audio and Multimedia Departments - FDK AAC LL
+Am Wolfsmantel 33
+91058 Erlangen, Germany
+
+www.iis.fraunhofer.de/amm
+amm-info@iis.fraunhofer.de
+----------------------------------------------------------------------------------------------------------- */
+
+/******************************** MPEG Audio Encoder **************************
+
+ Initial author: Alex Groeschel
+ contents/description: Temporal noise shaping
+
+******************************************************************************/
+
+#include "aacenc_tns.h"
+#include "psy_const.h"
+#include "psy_configuration.h"
+#include "tns_func.h"
+#include "aacEnc_rom.h"
+#include "aacenc_tns.h"
+
+enum {
+ HIFILT = 0, /* index of higher filter */
+ LOFILT = 1 /* index of lower filter */
+};
+
+
+#define FILTER_DIRECTION 0
+
+static const FIXP_DBL acfWindowLong[12+3+1] = {
+ 0x7fffffff,0x7fb80000,0x7ee00000,0x7d780000,0x7b800000,0x78f80000,0x75e00000,0x72380000,
+ 0x6e000000,0x69380000,0x63e00000,0x5df80000,0x57800000,0x50780000,0x48e00000,0x40b80000
+};
+
+static const FIXP_DBL acfWindowShort[4+3+1] = {
+ 0x7fffffff,0x7e000000,0x78000000,0x6e000000,0x60000000,0x4e000000,0x38000000,0x1e000000
+};
+
+
+typedef struct {
+ INT filterEnabled[MAX_NUM_OF_FILTERS];
+ INT threshOn[MAX_NUM_OF_FILTERS]; /* min. prediction gain for using tns TABUL*/
+ INT filterStartFreq[MAX_NUM_OF_FILTERS]; /* lowest freq for lpc TABUL*/
+ INT tnsLimitOrder[MAX_NUM_OF_FILTERS]; /* Limit for TNS order TABUL*/
+ INT tnsFilterDirection[MAX_NUM_OF_FILTERS]; /* Filtering direction, 0=up, 1=down TABUL */
+ INT acfSplit[MAX_NUM_OF_FILTERS];
+ FIXP_DBL tnsTimeResolution[MAX_NUM_OF_FILTERS]; /* TNS max. time resolution TABUL. Should be fract but MSVC won't compile then */
+ INT seperateFiltersAllowed;
+
+} TNS_PARAMETER_TABULATED;
+
+
+typedef struct{
+ INT bitRateFrom[2]; /* noneSbr=0, useSbr=1 */
+ INT bitRateTo[2]; /* noneSbr=0, useSbr=1 */
+ TNS_PARAMETER_TABULATED paramTab[2]; /* mono=0, stereo=1 */
+
+} TNS_INFO_TAB;
+
+#define TNS_TIMERES_SCALE (1)
+#define FL2_TIMERES_FIX(a) ( FL2FXCONST_DBL(a/(float)(1<<TNS_TIMERES_SCALE)) )
+
+static const TNS_INFO_TAB tnsInfoTab[] =
+{
+ {
+ { 16000, 13500},
+ { 32000, 28000},
+ {
+ { {1, 1}, {1437, 1500}, {1400, 600}, {12, 12}, {FILTER_DIRECTION, FILTER_DIRECTION}, {3, 1}, {FL2_TIMERES_FIX(0.4f), FL2_TIMERES_FIX(1.2f)}, 1 },
+ { {1, 1}, {1437, 1500}, {1400, 600}, {12, 12}, {FILTER_DIRECTION, FILTER_DIRECTION}, {3, 1}, {FL2_TIMERES_FIX(0.4f), FL2_TIMERES_FIX(1.2f)}, 1 }
+ }
+ },
+ {
+ { 32001, 28001},
+ { 60000, 52000},
+ {
+ { {1, 1}, {1437, 1500}, {1400, 600}, {12, 10}, {FILTER_DIRECTION, FILTER_DIRECTION}, {3, 1}, {FL2_TIMERES_FIX(0.4f), FL2_TIMERES_FIX(1.0f)}, 1 },
+ { {1, 1}, {1437, 1500}, {1400, 600}, {12, 10}, {FILTER_DIRECTION, FILTER_DIRECTION}, {3, 1}, {FL2_TIMERES_FIX(0.4f), FL2_TIMERES_FIX(1.0f)}, 1 }
+ }
+ },
+ {
+ { 60001, 52001},
+ { 384000, 384000},
+ {
+ { {1, 1}, {1437, 1500}, {1400, 600}, {12, 8}, {FILTER_DIRECTION, FILTER_DIRECTION}, {3, 1}, {FL2_TIMERES_FIX(0.4f), FL2_TIMERES_FIX(1.0f)}, 1 },
+ { {1, 1}, {1437, 1500}, {1400, 600}, {12, 8}, {FILTER_DIRECTION, FILTER_DIRECTION}, {3, 1}, {FL2_TIMERES_FIX(0.4f), FL2_TIMERES_FIX(1.0f)}, 1 }
+ }
+ }
+};
+
+typedef struct {
+ INT samplingRate;
+ SCHAR maxBands[2]; /* long=0; short=1 */
+
+} TNS_MAX_TAB_ENTRY;
+
+static const TNS_MAX_TAB_ENTRY tnsMaxBandsTab1024[] =
+{
+ { 96000, { 31, 9}},
+ { 88200, { 31, 9}},
+ { 64000, { 34, 10}},
+ { 48000, { 40, 14}},
+ { 44100, { 42, 14}},
+ { 32000, { 51, 14}},
+ { 24000, { 46, 14}},
+ { 22050, { 46, 14}},
+ { 16000, { 42, 14}},
+ { 12000, { 42, 14}},
+ { 11025, { 42, 14}},
+ { 8000, { 39, 14}}
+};
+
+static const TNS_MAX_TAB_ENTRY tnsMaxBandsTab480[] =
+{
+ { 48000, { 31, -1}},
+ { 44100, { 32, -1}},
+ { 32000, { 37, -1}},
+ { 24000, { 30, -1}},
+ { 22050, { 30, -1}}
+};
+
+static const TNS_MAX_TAB_ENTRY tnsMaxBandsTab512[] =
+{
+ { 48000, { 31, -1}},
+ { 44100, { 32, -1}},
+ { 32000, { 37, -1}},
+ { 24000, { 31, -1}},
+ { 22050, { 31, -1}}
+};
+
+static INT FDKaacEnc_AutoToParcor(
+ FIXP_DBL *RESTRICT input,
+ FIXP_DBL *RESTRICT reflCoeff,
+ const INT numOfCoeff
+ );
+
+static void FDKaacEnc_Parcor2Index(
+ const FIXP_DBL *parcor,
+ INT *RESTRICT index,
+ const INT order,
+ const INT bitsPerCoeff
+ );
+
+static void FDKaacEnc_Index2Parcor(
+ const INT *index,
+ FIXP_DBL *RESTRICT parcor,
+ const INT order,
+ const INT bitsPerCoeff
+ );
+
+static INT FDKaacEnc_ParcorToLpc(
+ const FIXP_DBL *reflCoeff,
+ FIXP_DBL *RESTRICT LpcCoeff,
+ const INT numOfCoeff,
+ FIXP_DBL *RESTRICT workBuffer
+ );
+
+static void FDKaacEnc_AnalysisFilter(
+ FIXP_DBL *RESTRICT signal,
+ const INT numOfLines,
+ const FIXP_DBL *predictorCoeff,
+ const INT order,
+ const INT lpcGainFactor
+ );
+
+static void FDKaacEnc_CalcGaussWindow(
+ FIXP_DBL *win,
+ const int winSize,
+ const INT samplingRate,
+ const INT transformResolution,
+ const FIXP_DBL timeResolution,
+ const INT timeResolution_e
+ );
+
+static const TNS_PARAMETER_TABULATED* FDKaacEnc_GetTnsParam(
+ const INT bitRate,
+ const INT channels,
+ const INT sbrLd
+ )
+{
+ int i;
+ const TNS_PARAMETER_TABULATED *tnsConfigTab = NULL;
+
+ for (i = 0; i < (int) (sizeof(tnsInfoTab)/sizeof(TNS_INFO_TAB)); i++) {
+ if ((bitRate >= tnsInfoTab[i].bitRateFrom[sbrLd?1:0]) &&
+ bitRate <= tnsInfoTab[i].bitRateTo[sbrLd?1:0])
+ {
+ tnsConfigTab = &tnsInfoTab[i].paramTab[(channels==1)?0:1];
+ }
+ }
+
+ return tnsConfigTab;
+}
+
+
+static INT getTnsMaxBands(
+ const INT sampleRate,
+ const INT granuleLength,
+ const INT isShortBlock
+ )
+{
+ int i;
+ INT numBands = -1;
+ const TNS_MAX_TAB_ENTRY *pMaxBandsTab = NULL;
+ int maxBandsTabSize = 0;
+
+ switch (granuleLength) {
+ case 960:
+ case 1024:
+ pMaxBandsTab = tnsMaxBandsTab1024;
+ maxBandsTabSize = sizeof(tnsMaxBandsTab1024)/sizeof(TNS_MAX_TAB_ENTRY);
+ break;
+ case 480:
+ pMaxBandsTab = tnsMaxBandsTab480;
+ maxBandsTabSize = sizeof(tnsMaxBandsTab480)/sizeof(TNS_MAX_TAB_ENTRY);
+ break;
+ case 512:
+ pMaxBandsTab = tnsMaxBandsTab512;
+ maxBandsTabSize = sizeof(tnsMaxBandsTab512)/sizeof(TNS_MAX_TAB_ENTRY);
+ break;
+ default:
+ numBands = -1;
+ }
+
+ if (pMaxBandsTab!=NULL) {
+ for (i=0; i<maxBandsTabSize; i++) {
+ numBands = pMaxBandsTab[i].maxBands[(!isShortBlock)?0:1];
+ if (sampleRate >= pMaxBandsTab[i].samplingRate) {
+ break;
+ }
+ }
+ }
+
+ return numBands;
+}
+
+/***************************************************************************/
+/*!
+ \brief FDKaacEnc_FreqToBandWithRounding
+
+ Returns index of nearest band border
+
+ \param frequency
+ \param sampling frequency
+ \param total number of bands
+ \param pointer to table of band borders
+
+ \return band border
+****************************************************************************/
+
+INT FDKaacEnc_FreqToBandWithRounding(
+ const INT freq,
+ const INT fs,
+ const INT numOfBands,
+ const INT *bandStartOffset
+ )
+{
+ INT lineNumber, band;
+
+ /* assert(freq >= 0); */
+ lineNumber = (freq*bandStartOffset[numOfBands]*4/fs+1)/2;
+
+ /* freq > fs/2 */
+ if (lineNumber >= bandStartOffset[numOfBands])
+ return numOfBands;
+
+ /* find band the line number lies in */
+ for (band=0; band<numOfBands; band++) {
+ if (bandStartOffset[band+1]>lineNumber) break;
+ }
+
+ /* round to nearest band border */
+ if (lineNumber - bandStartOffset[band] >
+ bandStartOffset[band+1] - lineNumber )
+ {
+ band++;
+ }
+
+ return(band);
+}
+
+
+/*****************************************************************************
+
+ functionname: FDKaacEnc_InitTnsConfiguration
+ description: fill TNS_CONFIG structure with sensible content
+ returns:
+ input: bitrate, samplerate, number of channels,
+ blocktype (long or short),
+ TNS Config struct (modified),
+ psy config struct,
+ tns active flag
+ output:
+
+*****************************************************************************/
+AAC_ENCODER_ERROR FDKaacEnc_InitTnsConfiguration(INT bitRate,
+ INT sampleRate,
+ INT channels,
+ INT blockType,
+ INT granuleLength,
+ INT ldSbrPresent,
+ TNS_CONFIG *tC,
+ PSY_CONFIGURATION *pC,
+ INT active,
+ INT useTnsPeak)
+{
+ int i;
+ //float acfTimeRes = (blockType == SHORT_WINDOW) ? 0.125f : 0.046875f;
+
+ if (channels <= 0)
+ return (AAC_ENCODER_ERROR)1;
+
+ /* initialize TNS filter flag, order, and coefficient resolution (in bits per coeff) */
+ tC->tnsActive = (active) ? TRUE : FALSE;
+ tC->maxOrder = (blockType == SHORT_WINDOW) ? 5 : 12; /* maximum: 7, 20 */
+ if (bitRate < 16000)
+ tC->maxOrder -= 2;
+ tC->coefRes = (blockType == SHORT_WINDOW) ? 3 : 4;
+
+ /* LPC stop line: highest MDCT line to be coded, but do not go beyond TNS_MAX_BANDS! */
+ tC->lpcStopBand = getTnsMaxBands(sampleRate, granuleLength, (blockType == SHORT_WINDOW) ? 1 : 0);
+
+ if (tC->lpcStopBand < 0) {
+ return (AAC_ENCODER_ERROR)1;
+ }
+
+ tC->lpcStopBand = FDKmin(tC->lpcStopBand, pC->sfbActive);
+ tC->lpcStopLine = pC->sfbOffset[tC->lpcStopBand];
+
+ switch (granuleLength) {
+ case 960:
+ case 1024:
+ /* TNS start line: skip lower MDCT lines to prevent artifacts due to filter mismatch */
+ tC->lpcStartBand[LOFILT] = (blockType == SHORT_WINDOW) ? 0 : ((sampleRate < 18783) ? 4 : 8);
+ tC->lpcStartLine[LOFILT] = pC->sfbOffset[tC->lpcStartBand[LOFILT]];
+
+ i = tC->lpcStopBand;
+ while (pC->sfbOffset[i] > (tC->lpcStartLine[LOFILT] + (tC->lpcStopLine - tC->lpcStartLine[LOFILT]) / 4)) i--;
+ tC->lpcStartBand[HIFILT] = i;
+ tC->lpcStartLine[HIFILT] = pC->sfbOffset[i];
+
+ tC->confTab.threshOn[HIFILT] = 1437;
+ tC->confTab.threshOn[LOFILT] = 1500;
+
+ tC->confTab.tnsLimitOrder[HIFILT] = tC->maxOrder;
+ tC->confTab.tnsLimitOrder[LOFILT] = tC->maxOrder - 7;
+
+ tC->confTab.tnsFilterDirection[HIFILT] = FILTER_DIRECTION;
+ tC->confTab.tnsFilterDirection[LOFILT] = FILTER_DIRECTION;
+
+ tC->confTab.acfSplit[HIFILT] = -1; /* signal Merged4to2QuartersAutoCorrelation in FDKaacEnc_MergedAutoCorrelation*/
+ tC->confTab.acfSplit[LOFILT] = -1; /* signal Merged4to2QuartersAutoCorrelation in FDKaacEnc_MergedAutoCorrelation */
+
+ tC->confTab.filterEnabled[HIFILT] = 1;
+ tC->confTab.filterEnabled[LOFILT] = 1;
+ tC->confTab.seperateFiltersAllowed = 1;
+
+ /* compute autocorrelation window based on maximum filter order for given block type */
+ /* for (i = 0; i <= tC->maxOrder + 3; i++) {
+ float acfWinTemp = acfTimeRes * i;
+ acfWindow[i] = FL2FXCONST_DBL(1.0f - acfWinTemp * acfWinTemp);
+ }
+ */
+ if (blockType == SHORT_WINDOW) {
+ FDKmemcpy(tC->acfWindow[HIFILT], acfWindowShort, FDKmin(sizeof(acfWindowShort), sizeof(tC->acfWindow[HIFILT])));
+ FDKmemcpy(tC->acfWindow[LOFILT], acfWindowShort, FDKmin(sizeof(acfWindowShort), sizeof(tC->acfWindow[HIFILT])));
+ }
+ else {
+ FDKmemcpy(tC->acfWindow[HIFILT], acfWindowLong, FDKmin(sizeof(acfWindowLong), sizeof(tC->acfWindow[HIFILT])));
+ FDKmemcpy(tC->acfWindow[LOFILT], acfWindowLong, FDKmin(sizeof(acfWindowLong), sizeof(tC->acfWindow[HIFILT])));
+ }
+ break;
+ case 480:
+ case 512:
+ {
+ const TNS_PARAMETER_TABULATED* pCfg = FDKaacEnc_GetTnsParam(bitRate, channels, ldSbrPresent);
+
+ if ( pCfg != NULL ) {
+ tC->lpcStartBand[HIFILT] = FDKaacEnc_FreqToBandWithRounding(pCfg->filterStartFreq[HIFILT], sampleRate, pC->sfbCnt, pC->sfbOffset);
+ tC->lpcStartLine[HIFILT] = pC->sfbOffset[tC->lpcStartBand[HIFILT]];
+ tC->lpcStartBand[LOFILT] = FDKaacEnc_FreqToBandWithRounding(pCfg->filterStartFreq[LOFILT], sampleRate, pC->sfbCnt, pC->sfbOffset);
+ tC->lpcStartLine[LOFILT] = pC->sfbOffset[tC->lpcStartBand[LOFILT]];
+
+ tC->confTab.threshOn[HIFILT] = pCfg->threshOn[HIFILT];
+ tC->confTab.threshOn[LOFILT] = pCfg->threshOn[LOFILT];
+
+ tC->confTab.tnsLimitOrder[HIFILT] = pCfg->tnsLimitOrder[HIFILT];
+ tC->confTab.tnsLimitOrder[LOFILT] = pCfg->tnsLimitOrder[LOFILT];
+
+ tC->confTab.tnsFilterDirection[HIFILT] = pCfg->tnsFilterDirection[HIFILT];
+ tC->confTab.tnsFilterDirection[LOFILT] = pCfg->tnsFilterDirection[LOFILT];
+
+ tC->confTab.acfSplit[HIFILT] = pCfg->acfSplit[HIFILT];
+ tC->confTab.acfSplit[LOFILT] = pCfg->acfSplit[LOFILT];
+
+ tC->confTab.filterEnabled[HIFILT] = pCfg->filterEnabled[HIFILT];
+ tC->confTab.filterEnabled[LOFILT] = pCfg->filterEnabled[LOFILT];
+ tC->confTab.seperateFiltersAllowed = pCfg->seperateFiltersAllowed;
+
+ FDKaacEnc_CalcGaussWindow(tC->acfWindow[HIFILT], tC->maxOrder+1, sampleRate, granuleLength, pCfg->tnsTimeResolution[HIFILT], TNS_TIMERES_SCALE);
+ FDKaacEnc_CalcGaussWindow(tC->acfWindow[LOFILT], tC->maxOrder+1, sampleRate, granuleLength, pCfg->tnsTimeResolution[LOFILT], TNS_TIMERES_SCALE);
+ }
+ else {
+ tC->tnsActive = FALSE; /* no configuration available, disable tns tool */
+ }
+ }
+ break;
+ default:
+ tC->tnsActive = FALSE; /* no configuration available, disable tns tool */
+ }
+
+ return AAC_ENC_OK;
+
+}
+
+/***************************************************************************/
+/*!
+ \brief FDKaacEnc_ScaleUpSpectrum
+
+ Scales up spectrum lines in a given frequency section
+
+ \param scaled spectrum
+ \param original spectrum
+ \param frequency line to start scaling
+ \param frequency line to enc scaling
+
+ \return scale factor
+
+****************************************************************************/
+static inline INT FDKaacEnc_ScaleUpSpectrum(
+ FIXP_DBL *dest,
+ const FIXP_DBL *src,
+ const INT startLine,
+ const INT stopLine
+ )
+{
+ INT i, scale;
+
+ FIXP_DBL maxVal = FL2FXCONST_DBL(0.f);
+
+ /* Get highest value in given spectrum */
+ for (i=startLine; i<stopLine; i++) {
+ maxVal = fixMax(maxVal,fixp_abs(src[i]));
+ }
+ scale = CountLeadingBits(maxVal);
+
+ /* Scale spectrum according to highest value */
+ for (i=startLine; i<stopLine; i++) {
+ dest[i] = src[i]<<scale;
+ }
+
+ return scale;
+}
+
+/***************************************************************************/
+/*!
+ \brief FDKaacEnc_CalcAutoCorrValue
+
+ Calculate autocorellation value for one lag
+
+ \param pointer to spectrum
+ \param start line
+ \param stop line
+ \param lag to be calculated
+ \param scaling of the lag
+
+****************************************************************************/
+static inline FIXP_DBL FDKaacEnc_CalcAutoCorrValue(
+ const FIXP_DBL *spectrum,
+ const INT startLine,
+ const INT stopLine,
+ const INT lag,
+ const INT scale
+ )
+{
+ int i;
+ FIXP_DBL result = FL2FXCONST_DBL(0.f);
+
+ if (lag==0) {
+ for (i=startLine; i<stopLine; i++) {
+ result += (fPow2(spectrum[i])>>scale);
+ }
+ }
+ else {
+ for (i=startLine; i<(stopLine-lag); i++) {
+ result += (fMult(spectrum[i], spectrum[i+lag])>>scale);
+ }
+ }
+
+ return result;
+}
+
+/***************************************************************************/
+/*!
+ \brief FDKaacEnc_AutoCorrNormFac
+
+ Autocorrelation function for 1st and 2nd half of the spectrum
+
+ \param pointer to spectrum
+ \param pointer to autocorrelation window
+ \param filter start line
+
+****************************************************************************/
+static inline FIXP_DBL FDKaacEnc_AutoCorrNormFac(
+ const FIXP_DBL value,
+ const INT scale,
+ INT *sc
+ )
+{
+ #define HLM_MIN_NRG 0.0000000037252902984619140625f /* 2^-28 */
+ #define MAX_INV_NRGFAC (1.f/HLM_MIN_NRG)
+
+ FIXP_DBL retValue;
+ FIXP_DBL A, B;
+
+ if (scale>=0) {
+ A = value;
+ B = FL2FXCONST_DBL(HLM_MIN_NRG)>>fixMin(DFRACT_BITS-1,scale);
+ }
+ else {
+ A = value>>fixMin(DFRACT_BITS-1,(-scale));
+ B = FL2FXCONST_DBL(HLM_MIN_NRG);
+ }
+
+ if (A > B) {
+ int shift = 0;
+ FIXP_DBL tmp = invSqrtNorm2(value,&shift);
+
+ retValue = fMult(tmp,tmp);
+ *sc += (2*shift);
+ }
+ else {
+ /* MAX_INV_NRGFAC*FDKpow(2,-28) = 1/2^-28 * 2^-28 = 1.0 */
+ retValue = /*FL2FXCONST_DBL(MAX_INV_NRGFAC*FDKpow(2,-28))*/ (FIXP_DBL)MAXVAL_DBL;
+ *sc += scale+28;
+ }
+
+ return retValue;
+}
+
+static void FDKaacEnc_MergedAutoCorrelation(
+ const FIXP_DBL *spectrum,
+ const FIXP_DBL acfWindow[MAX_NUM_OF_FILTERS][TNS_MAX_ORDER+3+1],
+ const INT lpcStartLine[MAX_NUM_OF_FILTERS],
+ const INT lpcStopLine,
+ const INT maxOrder,
+ const INT acfSplit[MAX_NUM_OF_FILTERS],
+ FIXP_DBL *_rxx1,
+ FIXP_DBL *_rxx2
+ )
+{
+ int i, idx0, idx1, idx2, idx3, idx4, lag;
+ FIXP_DBL rxx1_0, rxx2_0, rxx3_0, rxx4_0;
+
+ /* buffer for temporal spectrum */
+ C_ALLOC_SCRATCH_START(pSpectrum, FIXP_DBL, (1024));
+
+ /* pre-initialization output */
+ FDKmemclear(&_rxx1[0], sizeof(FIXP_DBL)*(maxOrder+1));
+ FDKmemclear(&_rxx2[0], sizeof(FIXP_DBL)*(maxOrder+1));
+
+ /* MDCT line indices separating the 1st, 2nd, 3rd, and 4th analysis quarters */
+ if ( (acfSplit[LOFILT]==-1) || (acfSplit[HIFILT]==-1) ) {
+ /* autocorrelation function for 1st, 2nd, 3rd, and 4th quarter of the spectrum */
+ idx0 = lpcStartLine[LOFILT];
+ i = lpcStopLine - lpcStartLine[LOFILT];
+ idx1 = idx0 + i / 4;
+ idx2 = idx0 + i / 2;
+ idx3 = idx0 + i * 3 / 4;
+ idx4 = lpcStopLine;
+ }
+ else {
+ FDK_ASSERT(acfSplit[LOFILT]==1);
+ FDK_ASSERT(acfSplit[HIFILT]==3);
+ i = (lpcStopLine - lpcStartLine[HIFILT]) / 3;
+ idx0 = lpcStartLine[LOFILT];
+ idx1 = lpcStartLine[HIFILT];
+ idx2 = idx1 + i;
+ idx3 = idx2 + i;
+ idx4 = lpcStopLine;
+ }
+
+ /* copy spectrum to temporal buffer and scale up as much as possible */
+ INT sc1 = FDKaacEnc_ScaleUpSpectrum(pSpectrum, spectrum, idx0, idx1);
+ INT sc2 = FDKaacEnc_ScaleUpSpectrum(pSpectrum, spectrum, idx1, idx2);
+ INT sc3 = FDKaacEnc_ScaleUpSpectrum(pSpectrum, spectrum, idx2, idx3);
+ INT sc4 = FDKaacEnc_ScaleUpSpectrum(pSpectrum, spectrum, idx3, idx4);
+
+ /* get scaling values for summation */
+ INT nsc1, nsc2, nsc3, nsc4;
+ for (nsc1=1; (1<<nsc1)<(idx1-idx0); nsc1++);
+ for (nsc2=1; (1<<nsc2)<(idx2-idx1); nsc2++);
+ for (nsc3=1; (1<<nsc3)<(idx3-idx2); nsc3++);
+ for (nsc4=1; (1<<nsc4)<(idx4-idx3); nsc4++);
+
+ /* compute autocorrelation value at lag zero, i. e. energy, for each quarter */
+ rxx1_0 = FDKaacEnc_CalcAutoCorrValue(pSpectrum, idx0, idx1, 0, nsc1);
+ rxx2_0 = FDKaacEnc_CalcAutoCorrValue(pSpectrum, idx1, idx2, 0, nsc2);
+ rxx3_0 = FDKaacEnc_CalcAutoCorrValue(pSpectrum, idx2, idx3, 0, nsc3);
+ rxx4_0 = FDKaacEnc_CalcAutoCorrValue(pSpectrum, idx3, idx4, 0, nsc4);
+
+ /* compute energy normalization factors, i. e. 1/energy (saves some divisions) */
+ if (rxx1_0 != FL2FXCONST_DBL(0.f))
+ {
+ INT sc_fac1 = -1;
+ FIXP_DBL fac1 = FDKaacEnc_AutoCorrNormFac(rxx1_0, ((-2*sc1)+nsc1), &sc_fac1);
+ _rxx1[0] = scaleValue(fMult(rxx1_0,fac1),sc_fac1);
+
+ for (lag = 1; lag <= maxOrder; lag++) {
+ /* compute energy-normalized and windowed autocorrelation values at this lag */
+ if ((3 * lag) <= maxOrder + 3) {
+ FIXP_DBL x1 = FDKaacEnc_CalcAutoCorrValue(pSpectrum, idx0, idx1, lag, nsc1);
+ _rxx1[lag] = fMult(scaleValue(fMult(x1,fac1),sc_fac1), acfWindow[LOFILT][3*lag]);
+ }
+ }
+ }
+
+ /* auto corr over upper 3/4 of spectrum */
+ if ( !((rxx2_0 == FL2FXCONST_DBL(0.f)) && (rxx3_0 == FL2FXCONST_DBL(0.f)) && (rxx4_0 == FL2FXCONST_DBL(0.f))) )
+ {
+ FIXP_DBL fac2, fac3, fac4;
+ fac2 = fac3 = fac4 = FL2FXCONST_DBL(0.f);
+ INT sc_fac2, sc_fac3, sc_fac4;
+ sc_fac2 = sc_fac3 = sc_fac4 = 0;
+
+ if (rxx2_0!=FL2FXCONST_DBL(0.f)) {
+ fac2 = FDKaacEnc_AutoCorrNormFac(rxx2_0, ((-2*sc2)+nsc2), &sc_fac2);
+ sc_fac2 -= 2;
+ }
+ if (rxx3_0!=FL2FXCONST_DBL(0.f)) {
+ fac3 = FDKaacEnc_AutoCorrNormFac(rxx3_0, ((-2*sc3)+nsc3), &sc_fac3);
+ sc_fac3 -= 2;
+ }
+ if (rxx4_0!=FL2FXCONST_DBL(0.f)) {
+ fac4 = FDKaacEnc_AutoCorrNormFac(rxx4_0, ((-2*sc4)+nsc4), &sc_fac4);
+ sc_fac4 -= 2;
+ }
+
+ _rxx2[0] = scaleValue(fMult(rxx2_0,fac2),sc_fac2) +
+ scaleValue(fMult(rxx3_0,fac3),sc_fac3) +
+ scaleValue(fMult(rxx4_0,fac4),sc_fac4);
+
+ for (lag = 1; lag <= maxOrder; lag++) {
+ /* merge quarters 2, 3, 4 into one autocorrelation; quarter 1 stays separate */
+ FIXP_DBL x2 = scaleValue(fMult(FDKaacEnc_CalcAutoCorrValue(pSpectrum, idx1, idx2, lag, nsc2), fac2),sc_fac2) +
+ scaleValue(fMult(FDKaacEnc_CalcAutoCorrValue(pSpectrum, idx2, idx3, lag, nsc3), fac3),sc_fac3) +
+ scaleValue(fMult(FDKaacEnc_CalcAutoCorrValue(pSpectrum, idx3, idx4, lag, nsc4), fac4),sc_fac4);
+
+ _rxx2[lag] = fMult(x2, acfWindow[HIFILT][lag]);
+ }
+ }
+
+ C_ALLOC_SCRATCH_END(pSpectrum, FIXP_DBL, (1024));
+}
+
+
+/*****************************************************************************
+ functionname: FDKaacEnc_TnsDetect
+ description: do decision, if TNS shall be used or not
+ returns:
+ input: tns data structure (modified),
+ tns config structure,
+ scalefactor size and table,
+ spectrum,
+ subblock num, blocktype,
+ sfb-wise energy.
+
+*****************************************************************************/
+INT FDKaacEnc_TnsDetect(
+ TNS_DATA *tnsData,
+ const TNS_CONFIG *tC,
+ TNS_INFO* tnsInfo,
+ INT sfbCnt,
+ FIXP_DBL *spectrum,
+ INT subBlockNumber,
+ INT blockType
+ )
+{
+ /* autocorrelation function for 1st, 2nd, 3rd, and 4th quarter of the spectrum. */
+ FIXP_DBL rxx1[TNS_MAX_ORDER+1]; /* higher part */
+ FIXP_DBL rxx2[TNS_MAX_ORDER+1]; /* lower part */
+ FIXP_DBL parcor_tmp[TNS_MAX_ORDER];
+
+ int i;
+
+ TNS_SUBBLOCK_INFO *tsbi = (blockType == SHORT_WINDOW)
+ ? &tnsData->dataRaw.Short.subBlockInfo[subBlockNumber]
+ : &tnsData->dataRaw.Long.subBlockInfo;
+
+ tnsData->filtersMerged = FALSE;
+ tsbi->tnsActive = FALSE;
+ tsbi->predictionGain = 1000;
+ tnsInfo->numOfFilters[subBlockNumber] = 0;
+ tnsInfo->coefRes[subBlockNumber] = tC->coefRes;
+ for (i = 0; i < tC->maxOrder; i++) {
+ tnsInfo->coef[subBlockNumber][HIFILT][i] = tnsInfo->coef[subBlockNumber][LOFILT][i] = 0;
+ }
+
+ tnsInfo->length[subBlockNumber][HIFILT] = tnsInfo->length[subBlockNumber][LOFILT] = 0;
+ tnsInfo->order [subBlockNumber][HIFILT] = tnsInfo->order [subBlockNumber][LOFILT] = 0;
+
+ if ( (tC->tnsActive) && (tC->maxOrder>0) )
+ {
+ int sumSqrCoef;
+
+ FDKaacEnc_MergedAutoCorrelation(
+ spectrum,
+ tC->acfWindow,
+ tC->lpcStartLine,
+ tC->lpcStopLine,
+ tC->maxOrder,
+ tC->confTab.acfSplit,
+ rxx1,
+ rxx2);
+
+ /* compute higher TNS filter in lattice (ParCor) form with LeRoux-Gueguen algorithm */
+ tsbi->predictionGain = FDKaacEnc_AutoToParcor(rxx2, parcor_tmp, tC->confTab.tnsLimitOrder[HIFILT]);
+
+ /* non-linear quantization of TNS lattice coefficients with given resolution */
+ FDKaacEnc_Parcor2Index(
+ parcor_tmp,
+ tnsInfo->coef[subBlockNumber][HIFILT],
+ tC->confTab.tnsLimitOrder[HIFILT],
+ tC->coefRes);
+
+ /* reduce filter order by truncating trailing zeros, compute sum(abs(coefs)) */
+ for (i = tC->confTab.tnsLimitOrder[HIFILT] - 1; i >= 0; i--) {
+ if (tnsInfo->coef[subBlockNumber][HIFILT][i] != 0) {
+ break;
+ }
+ }
+
+ tnsInfo->order[subBlockNumber][HIFILT] = i + 1;
+
+ sumSqrCoef = 0;
+ for (; i >= 0; i--) {
+ sumSqrCoef += tnsInfo->coef[subBlockNumber][HIFILT][i] * tnsInfo->coef[subBlockNumber][HIFILT][i];
+ }
+
+ tnsInfo->direction[subBlockNumber][HIFILT] = tC->confTab.tnsFilterDirection[HIFILT];
+ tnsInfo->length[subBlockNumber][HIFILT] = sfbCnt - tC->lpcStartBand[HIFILT];
+
+ /* disable TNS if predictionGain is less than 3dB or sumSqrCoef is too small */
+ if ((tsbi->predictionGain > tC->confTab.threshOn[HIFILT]) || (sumSqrCoef > (tC->confTab.tnsLimitOrder[HIFILT]/2 + 2)))
+ {
+ tsbi->tnsActive = TRUE;
+ tnsInfo->numOfFilters[subBlockNumber]++;
+
+ /* compute second filter for lower quarter; only allowed for long windows! */
+ if ( (blockType != SHORT_WINDOW) &&
+ (tC->confTab.filterEnabled[LOFILT]) && (tC->confTab.seperateFiltersAllowed) )
+ {
+ /* compute second filter for lower frequencies */
+
+ /* compute TNS filter in lattice (ParCor) form with LeRoux-Gueguen algorithm */
+ INT predGain = FDKaacEnc_AutoToParcor(rxx1, parcor_tmp, tC->confTab.tnsLimitOrder[LOFILT]);
+
+ /* non-linear quantization of TNS lattice coefficients with given resolution */
+ FDKaacEnc_Parcor2Index(
+ parcor_tmp,
+ tnsInfo->coef[subBlockNumber][LOFILT],
+ tC->confTab.tnsLimitOrder[LOFILT],
+ tC->coefRes);
+
+ /* reduce filter order by truncating trailing zeros, compute sum(abs(coefs)) */
+ for (i = tC->confTab.tnsLimitOrder[LOFILT] - 1; i >= 0; i--) {
+ if (tnsInfo->coef[subBlockNumber][LOFILT][i] != 0) {
+ break;
+ }
+ }
+ tnsInfo->order[subBlockNumber][LOFILT] = i + 1;
+
+ sumSqrCoef = 0;
+ for (; i >= 0; i--) {
+ sumSqrCoef += tnsInfo->coef[subBlockNumber][LOFILT][i] * tnsInfo->coef[subBlockNumber][LOFILT][i];
+ }
+
+ tnsInfo->direction[subBlockNumber][LOFILT] = tC->confTab.tnsFilterDirection[LOFILT];
+ tnsInfo->length[subBlockNumber][LOFILT] = tC->lpcStartBand[HIFILT] - tC->lpcStartBand[LOFILT];
+
+ /* filter lower quarter if gain is high enough, but not if it's too high */
+ if ( ( (predGain > tC->confTab.threshOn[LOFILT]) && (predGain < (16000 * tC->confTab.tnsLimitOrder[LOFILT])) )
+ || ( (sumSqrCoef > 9) && (sumSqrCoef < 22 * tC->confTab.tnsLimitOrder[LOFILT]) ) )
+ {
+ /* compare lower to upper filter; if they are very similar, merge them */
+ sumSqrCoef = 0;
+ for (i = 0; i < tC->confTab.tnsLimitOrder[LOFILT]; i++) {
+ sumSqrCoef += FDKabs(tnsInfo->coef[subBlockNumber][HIFILT][i] - tnsInfo->coef[subBlockNumber][LOFILT][i]);
+ }
+ if ( (sumSqrCoef < 2) &&
+ (tnsInfo->direction[subBlockNumber][LOFILT] == tnsInfo->direction[subBlockNumber][HIFILT]) )
+ {
+ tnsData->filtersMerged = TRUE;
+ tnsInfo->length[subBlockNumber][HIFILT] = sfbCnt - tC->lpcStartBand[LOFILT];
+ for (; i < tnsInfo->order[subBlockNumber][HIFILT]; i++) {
+ if (FDKabs(tnsInfo->coef[subBlockNumber][HIFILT][i]) > 1) {
+ break;
+ }
+ }
+ for (i--; i >= 0; i--) {
+ if (tnsInfo->coef[subBlockNumber][HIFILT][i] != 0) {
+ break;
+ }
+ }
+ if (i < tnsInfo->order[subBlockNumber][HIFILT]) {
+ tnsInfo->order[subBlockNumber][HIFILT] = i + 1;
+ }
+ }
+ else {
+ tnsInfo->numOfFilters[subBlockNumber]++;
+ }
+ } /* filter lower part */
+ } /* second filter allowed */
+ } /* if predictionGain > 1437 ... */
+ } /* maxOrder > 0 && tnsActive */
+
+ return 0;
+
+}
+
+
+/***************************************************************************/
+/*!
+ \brief FDKaacLdEnc_TnsSync
+
+ synchronize TNS parameters when TNS gain difference small (relative)
+
+ \param pointer to TNS data structure (destination)
+ \param pointer to TNS data structure (source)
+ \param pointer to TNS config structure
+ \param number of sub-block
+ \param block type
+
+ \return void
+****************************************************************************/
+void FDKaacEnc_TnsSync(
+ TNS_DATA *tnsDataDest,
+ const TNS_DATA *tnsDataSrc,
+ TNS_INFO *tnsInfoDest,
+ TNS_INFO *tnsInfoSrc,
+ const INT blockTypeDest,
+ const INT blockTypeSrc,
+ const TNS_CONFIG *tC
+ )
+{
+ int i, w, absDiff, nWindows;
+ TNS_SUBBLOCK_INFO *sbInfoDest;
+ const TNS_SUBBLOCK_INFO *sbInfoSrc;
+
+ /* if one channel contains short blocks and the other not, do not synchronize */
+ if ( (blockTypeSrc == SHORT_WINDOW && blockTypeDest != SHORT_WINDOW) ||
+ (blockTypeDest == SHORT_WINDOW && blockTypeSrc != SHORT_WINDOW) )
+ {
+ return;
+ }
+
+ if (blockTypeDest != SHORT_WINDOW) {
+ sbInfoDest = &tnsDataDest->dataRaw.Long.subBlockInfo;
+ sbInfoSrc = &tnsDataSrc->dataRaw.Long.subBlockInfo;
+ nWindows = 1;
+ } else {
+ sbInfoDest = &tnsDataDest->dataRaw.Short.subBlockInfo[0];
+ sbInfoSrc = &tnsDataSrc->dataRaw.Short.subBlockInfo[0];
+ nWindows = 8;
+ }
+
+ for (w=0; w<nWindows; w++) {
+ const TNS_SUBBLOCK_INFO *pSbInfoSrcW = sbInfoSrc + w;
+ TNS_SUBBLOCK_INFO *pSbInfoDestW = sbInfoDest + w;
+ INT doSync = 1, absDiffSum = 0;
+
+ /* if TNS is active in at least one channel, check if ParCor coefficients of higher filter are similar */
+ if (pSbInfoDestW->tnsActive || pSbInfoSrcW->tnsActive) {
+ for (i = 0; i < tC->maxOrder; i++) {
+ absDiff = FDKabs(tnsInfoDest->coef[w][HIFILT][i] - tnsInfoSrc->coef[w][HIFILT][i]);
+ absDiffSum += absDiff;
+ /* if coefficients diverge too much between channels, do not synchronize */
+ if ((absDiff > 1) || (absDiffSum > 2)) {
+ doSync = 0;
+ break;
+ }
+ }
+
+ if (doSync) {
+ /* if no significant difference was detected, synchronize coefficient sets */
+ if (pSbInfoSrcW->tnsActive) {
+ /* no dest filter, or more dest than source filters: use one dest filter */
+ if ((!pSbInfoDestW->tnsActive) ||
+ ((pSbInfoDestW->tnsActive) && (tnsInfoDest->numOfFilters[w] > tnsInfoSrc->numOfFilters[w])))
+ {
+ pSbInfoDestW->tnsActive = tnsInfoDest->numOfFilters[w] = 1;
+ }
+ tnsDataDest->filtersMerged = tnsDataSrc->filtersMerged;
+ tnsInfoDest->order [w][HIFILT] = tnsInfoSrc->order [w][HIFILT];
+ tnsInfoDest->length [w][HIFILT] = tnsInfoSrc->length [w][HIFILT];
+ tnsInfoDest->direction [w][HIFILT] = tnsInfoSrc->direction [w][HIFILT];
+ tnsInfoDest->coefCompress[w][HIFILT] = tnsInfoSrc->coefCompress[w][HIFILT];
+
+ for (i = 0; i < tC->maxOrder; i++) {
+ tnsInfoDest->coef[w][HIFILT][i] = tnsInfoSrc->coef[w][HIFILT][i];
+ }
+ }
+ else
+ pSbInfoDestW->tnsActive = tnsInfoDest->numOfFilters[w] = 0;
+ }
+ }
+
+ }
+}
+
+/***************************************************************************/
+/*!
+ \brief FDKaacEnc_TnsEncode
+
+ perform TNS encoding
+
+ \param pointer to TNS info structure
+ \param pointer to TNS data structure
+ \param number of sfbs
+ \param pointer to TNS config structure
+ \param low-pass line
+ \param pointer to spectrum
+ \param number of sub-block
+ \param block type
+
+ \return ERROR STATUS
+****************************************************************************/
+INT FDKaacEnc_TnsEncode(
+ TNS_INFO* tnsInfo,
+ TNS_DATA* tnsData,
+ const INT numOfSfb,
+ const TNS_CONFIG *tC,
+ const INT lowPassLine,
+ FIXP_DBL* spectrum,
+ const INT subBlockNumber,
+ const INT blockType
+ )
+{
+ INT i, startLine, stopLine;
+
+ if ( ( (blockType == SHORT_WINDOW) && (!tnsData->dataRaw.Short.subBlockInfo[subBlockNumber].tnsActive) )
+ || ( (blockType != SHORT_WINDOW) && (!tnsData->dataRaw.Long.subBlockInfo.tnsActive) ) )
+ {
+ return 1;
+ }
+
+ startLine = (tnsData->filtersMerged) ? tC->lpcStartLine[LOFILT] : tC->lpcStartLine[HIFILT];
+ stopLine = tC->lpcStopLine;
+
+ for (i=0; i<tnsInfo->numOfFilters[subBlockNumber]; i++) {
+
+ INT lpcGainFactor;
+ FIXP_DBL LpcCoeff[TNS_MAX_ORDER];
+ FIXP_DBL workBuffer[TNS_MAX_ORDER];
+ FIXP_DBL parcor_tmp[TNS_MAX_ORDER];
+
+ FDKaacEnc_Index2Parcor(
+ tnsInfo->coef[subBlockNumber][i],
+ parcor_tmp,
+ tnsInfo->order[subBlockNumber][i],
+ tC->coefRes);
+
+ lpcGainFactor = FDKaacEnc_ParcorToLpc(
+ parcor_tmp,
+ LpcCoeff,
+ tnsInfo->order[subBlockNumber][i],
+ workBuffer);
+
+ FDKaacEnc_AnalysisFilter(
+ &spectrum[startLine],
+ stopLine - startLine,
+ LpcCoeff,
+ tnsInfo->order[subBlockNumber][i],
+ lpcGainFactor);
+
+ /* update for second filter */
+ startLine = tC->lpcStartLine[LOFILT];
+ stopLine = tC->lpcStartLine[HIFILT];
+ }
+
+ return(0);
+
+}
+
+static void FDKaacEnc_CalcGaussWindow(
+ FIXP_DBL *win,
+ const int winSize,
+ const INT samplingRate,
+ const INT transformResolution,
+ const FIXP_DBL timeResolution,
+ const INT timeResolution_e
+ )
+{
+ #define PI_SCALE (2)
+ #define PI_FIX FL2FXCONST_DBL(3.1416f/(float)(1<<PI_SCALE))
+
+ #define EULER_SCALE (2)
+ #define EULER_FIX FL2FXCONST_DBL(2.7183/(float)(1<<EULER_SCALE))
+
+ #define COEFF_LOOP_SCALE (4)
+
+ INT i, e1, e2, gaussExp_e;
+ FIXP_DBL gaussExp_m;
+
+ /* calc. window exponent from time resolution:
+ *
+ * gaussExp = PI * samplingRate * 0.001f * timeResolution / transformResolution;
+ * gaussExp = -0.5f * gaussExp * gaussExp;
+ */
+ gaussExp_m = fMultNorm(timeResolution, fMult(PI_FIX, fDivNorm( (FIXP_DBL)(samplingRate), (FIXP_DBL)(LONG)(transformResolution*1000.f), &e1)), &e2);
+ gaussExp_m = -fPow2Div2(gaussExp_m);
+ gaussExp_e = 2*(e1+e2+timeResolution_e+PI_SCALE);
+
+ FDK_ASSERT( winSize < (1<<COEFF_LOOP_SCALE) );
+
+ /* calc. window coefficients
+ * win[i] = (float)exp( gaussExp * (i+0.5) * (i+0.5) );
+ */
+ for( i=0; i<winSize; i++) {
+
+ win[i] = fPow(
+ EULER_FIX,
+ EULER_SCALE,
+ fMult(gaussExp_m, fPow2((i*FL2FXCONST_DBL(1.f/(float)(1<<COEFF_LOOP_SCALE)) + FL2FXCONST_DBL(.5f/(float)(1<<COEFF_LOOP_SCALE))))),
+ gaussExp_e + 2*COEFF_LOOP_SCALE,
+ &e1);
+
+ win[i] = scaleValue(win[i], e1);
+ }
+}
+
+
+/***************************************************************************/
+/*!
+ \brief FDKaacEnc_AutoToParcor
+
+ conversion autocorrelation to reflection coefficients
+
+ \param pointer to input (acf)
+ \param pointer to output (reflection coefficients)
+ \param number of coefficients
+
+ \return prediction gain
+****************************************************************************/
+static INT FDKaacEnc_AutoToParcor(
+ FIXP_DBL *RESTRICT input,
+ FIXP_DBL *RESTRICT reflCoeff,
+ const INT numOfCoeff
+ )
+{
+ INT i, j, scale=0;
+ FIXP_DBL tmp, parcorWorkBuffer[TNS_MAX_ORDER];
+ INT predictionGain = (INT)(TNS_PREDGAIN_SCALE);
+
+ FIXP_DBL *RESTRICT workBuffer = parcorWorkBuffer;
+ const FIXP_DBL autoCorr_0 = input[0];
+
+ if((FIXP_DBL)input[0] == FL2FXCONST_DBL(0.0)) {
+ FDKmemclear(reflCoeff,numOfCoeff*sizeof(FIXP_DBL));
+ return(predictionGain);
+ }
+
+ FDKmemcpy(workBuffer,&input[1],numOfCoeff*sizeof(FIXP_DBL));
+ for(i=0; i<numOfCoeff; i++) {
+ LONG sign = ((LONG)workBuffer[0] >> (DFRACT_BITS-1));
+ tmp = (FIXP_DBL)((LONG)workBuffer[0]^sign);
+
+ if(input[0]<tmp)
+ break;
+
+ tmp = (FIXP_DBL)((LONG)schur_div(tmp, input[0], FRACT_BITS)^(~sign));
+ reflCoeff[i] = tmp;
+
+ for(j=numOfCoeff-i-1; j>=0; j--) {
+ FIXP_DBL accu1 = fMult(tmp, input[j]);
+ FIXP_DBL accu2 = fMult(tmp, workBuffer[j]);
+ workBuffer[j] += accu1;
+ input[j] += accu2;
+ }
+
+ workBuffer++;
+ }
+
+ tmp = fMult((FIXP_DBL)((LONG)TNS_PREDGAIN_SCALE<<21), fDivNorm(autoCorr_0, input[0], &scale));
+ predictionGain = (LONG)scaleValue(tmp,scale-21);
+
+ return (predictionGain);
+}
+
+
+static INT FDKaacEnc_Search3(FIXP_DBL parcor)
+{
+ INT i, index=0;
+
+ for(i=0;i<8;i++){
+ if(parcor > FDKaacEnc_tnsCoeff3Borders[i])
+ index=i;
+ }
+ return(index-4);
+}
+
+static INT FDKaacEnc_Search4(FIXP_DBL parcor)
+{
+ INT i, index=0;
+
+ for(i=0;i<16;i++){
+ if(parcor > FDKaacEnc_tnsCoeff4Borders[i])
+ index=i;
+ }
+ return(index-8);
+}
+
+
+/*****************************************************************************
+
+ functionname: FDKaacEnc_Parcor2Index
+
+*****************************************************************************/
+static void FDKaacEnc_Parcor2Index(
+ const FIXP_DBL *parcor,
+ INT *RESTRICT index,
+ const INT order,
+ const INT bitsPerCoeff
+ )
+{
+ INT i;
+ for(i=0; i<order; i++) {
+ if(bitsPerCoeff == 3)
+ index[i] = FDKaacEnc_Search3(parcor[i]);
+ else
+ index[i] = FDKaacEnc_Search4(parcor[i]);
+ }
+}
+
+
+/*****************************************************************************
+
+ functionname: FDKaacEnc_Index2Parcor
+ description: inverse quantization for reflection coefficients
+ returns: -
+ input: quantized values, ptr. to reflection coefficients,
+ no. of coefficients, resolution
+ output: reflection coefficients
+
+*****************************************************************************/
+static void FDKaacEnc_Index2Parcor(
+ const INT *index,
+ FIXP_DBL *RESTRICT parcor,
+ const INT order,
+ const INT bitsPerCoeff
+ )
+{
+ INT i;
+ for(i=0; i<order; i++)
+ parcor[i] = bitsPerCoeff == 4 ? FDKaacEnc_tnsEncCoeff4[index[i]+8] : FDKaacEnc_tnsEncCoeff3[index[i]+4];
+}
+
+
+/*****************************************************************************
+
+ functionname: FDKaacEnc_ParcorToLpc
+ description: conversion reflection coefficients to LPC coefficients
+ returns: Gain factor
+ input: reflection coefficients, no. of reflection coefficients <order>,
+ ptr. to work buffer (required size: order)
+ output: <order> LPC coefficients
+
+*****************************************************************************/
+static INT FDKaacEnc_ParcorToLpc(
+ const FIXP_DBL *reflCoeff,
+ FIXP_DBL *RESTRICT LpcCoeff,
+ const INT numOfCoeff,
+ FIXP_DBL *RESTRICT workBuffer
+ )
+{
+ INT i, j;
+ INT shiftval, par2LpcShiftVal = 6; /* 6 should be enough, bec. max(numOfCoeff) = 20 */
+ FIXP_DBL maxVal = FL2FXCONST_DBL(0.0f);
+
+ LpcCoeff[0] = reflCoeff[0] >> par2LpcShiftVal;
+ for(i=1; i<numOfCoeff; i++) {
+ for(j=0; j<i; j++) {
+ workBuffer[j] = LpcCoeff[i-1-j];
+ }
+
+ for(j=0; j<i; j++) {
+ LpcCoeff[j] += fMult(reflCoeff[i],workBuffer[j]);
+ }
+
+ LpcCoeff[i] = reflCoeff[i] >> par2LpcShiftVal;
+ }
+
+ /* normalize LpcCoeff and calc shiftfactor */
+ for(i=0; i<numOfCoeff; i++) {
+ maxVal = fixMax(maxVal,(FIXP_DBL)fixp_abs(LpcCoeff[i]));
+ }
+
+ shiftval = CountLeadingBits(maxVal);
+ shiftval = (shiftval>=par2LpcShiftVal) ? par2LpcShiftVal : shiftval;
+
+ for(i=0; i<numOfCoeff; i++)
+ LpcCoeff[i] = LpcCoeff[i]<<shiftval;
+
+ return (par2LpcShiftVal - shiftval);
+}
+
+/***************************************************************************/
+/*!
+ \brief FDKaacEnc_AnalysisFilter
+
+ TNS analysis filter (all-zero filter)
+
+ \param pointer to signal spectrum
+ \param number of lines
+ \param pointer to lpc coefficients
+ \param filter order
+ \param lpc gain factor
+
+ \return void
+****************************************************************************/
+/* Note: in-place computation possible */
+static void FDKaacEnc_AnalysisFilter(
+ FIXP_DBL *RESTRICT signal,
+ const INT numOfLines,
+ const FIXP_DBL *predictorCoeff,
+ const INT order,
+ const INT lpcGainFactor
+ )
+{
+ FIXP_DBL statusVar[TNS_MAX_ORDER];
+ INT i, j;
+ const INT shift = lpcGainFactor + 1; /* +1, because fMultDiv2 */
+ FIXP_DBL tmp;
+
+ if (order>0) {
+
+ INT idx = 0;
+
+ /* keep filter coefficients twice and save memory copy operation in
+ modulo state buffer */
+#if defined(ARCH_PREFER_MULT_32x16)
+ FIXP_SGL coeff[2*TNS_MAX_ORDER];
+ const FIXP_SGL *pCoeff;
+ for(i=0;i<order;i++) {
+ coeff[i] = FX_DBL2FX_SGL(predictorCoeff[i]);
+ }
+ FDKmemcpy(&coeff[order], coeff, order*sizeof(FIXP_SGL));
+#else
+ FIXP_DBL coeff[2*TNS_MAX_ORDER];
+ const FIXP_DBL *pCoeff;
+ FDKmemcpy(&coeff[0], predictorCoeff, order*sizeof(FIXP_DBL));
+ FDKmemcpy(&coeff[order], predictorCoeff, order*sizeof(FIXP_DBL));
+#endif
+ FDKmemclear(statusVar, order*sizeof(FIXP_DBL));
+
+ for(j=0; j<numOfLines; j++) {
+ pCoeff = &coeff[(order-idx)];
+ tmp = FL2FXCONST_DBL(0);
+ for(i=0; i<order; i++) {
+ tmp = fMultAddDiv2(tmp, pCoeff[i], statusVar[i]) ;
+ }
+
+ if(--idx<0) { idx = order-1; }
+ statusVar[idx] = signal[j];
+
+ FDK_ASSERT(lpcGainFactor>=0);
+ signal[j] = (tmp<<shift) + signal[j];
+ }
+ }
+}
+
+