diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2012-07-11 10:15:24 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2012-07-11 10:15:24 -0700 |
commit | 2228e360595641dd906bf1773307f43d304f5b2e (patch) | |
tree | 57f3d390ebb0782cc0de0fb984c8ea7e45b4f386 /libAACenc/src/adj_thr.cpp | |
download | fdk-aac-2228e360595641dd906bf1773307f43d304f5b2e.tar.gz fdk-aac-2228e360595641dd906bf1773307f43d304f5b2e.tar.bz2 fdk-aac-2228e360595641dd906bf1773307f43d304f5b2e.zip |
Snapshot 2bda038c163298531d47394bc2c09e1409c5d0db
Change-Id: If584e579464f28b97d50e51fc76ba654a5536c54
Diffstat (limited to 'libAACenc/src/adj_thr.cpp')
-rw-r--r-- | libAACenc/src/adj_thr.cpp | 2324 |
1 files changed, 2324 insertions, 0 deletions
diff --git a/libAACenc/src/adj_thr.cpp b/libAACenc/src/adj_thr.cpp new file mode 100644 index 0000000..a779357 --- /dev/null +++ b/libAACenc/src/adj_thr.cpp @@ -0,0 +1,2324 @@ + +/* ----------------------------------------------------------------------------------------------------------- +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: M. Werner + contents/description: Threshold compensation + +******************************************************************************/ + +#include "common_fix.h" + +#include "adj_thr_data.h" +#include "adj_thr.h" +#include "qc_data.h" +#include "sf_estim.h" +#include "aacEnc_ram.h" + + + + +#define INV_INT_TAB_SIZE (8) +static const FIXP_DBL invInt[INV_INT_TAB_SIZE] = +{ + 0x7fffffff, 0x7fffffff, 0x40000000, 0x2aaaaaaa, 0x20000000, 0x19999999, 0x15555555, 0x12492492 +}; + + +#define INV_SQRT4_TAB_SIZE (8) +static const FIXP_DBL invSqrt4[INV_SQRT4_TAB_SIZE] = +{ + 0x7fffffff, 0x7fffffff, 0x6ba27e65, 0x61424bb5, 0x5a827999, 0x55994845, 0x51c8e33c, 0x4eb160d1 +}; + + +/*static const INT invRedExp = 4;*/ +static const FIXP_DBL SnrLdMin1 = (FIXP_DBL)0xfcad0ddf; /*FL2FXCONST_DBL(FDKlog(0.316)/FDKlog(2.0)/LD_DATA_SCALING);*/ +static const FIXP_DBL SnrLdMin2 = (FIXP_DBL)0x0351e1a2; /*FL2FXCONST_DBL(FDKlog(3.16) /FDKlog(2.0)/LD_DATA_SCALING);*/ +static const FIXP_DBL SnrLdFac = (FIXP_DBL)0xff5b2c3e; /*FL2FXCONST_DBL(FDKlog(0.8) /FDKlog(2.0)/LD_DATA_SCALING);*/ + +static const FIXP_DBL SnrLdMin3 = (FIXP_DBL)0xfe000000; /*FL2FXCONST_DBL(FDKlog(0.5) /FDKlog(2.0)/LD_DATA_SCALING);*/ +static const FIXP_DBL SnrLdMin4 = (FIXP_DBL)0x02000000; /*FL2FXCONST_DBL(FDKlog(2.0) /FDKlog(2.0)/LD_DATA_SCALING);*/ +static const FIXP_DBL SnrLdMin5 = (FIXP_DBL)0xfc000000; /*FL2FXCONST_DBL(FDKlog(0.25) /FDKlog(2.0)/LD_DATA_SCALING);*/ + + +/* values for avoid hole flag */ +enum _avoid_hole_state { + NO_AH =0, + AH_INACTIVE =1, + AH_ACTIVE =2 +}; + + +/* Q format definitions */ +#define Q_BITFAC (24) /* Q scaling used in FDKaacEnc_bitresCalcBitFac() calculation */ +#define Q_AVGBITS (17) /* scale bit values */ + +static INT FDKaacEnc_bits2pe2( + const INT bits, + const FIXP_DBL factor_m, + const INT factor_e + ) +{ + return (INT)(fMult(factor_m, (FIXP_DBL)(bits<<Q_AVGBITS)) >> (Q_AVGBITS-factor_e)); +} + +/***************************************************************************** +functionname: FDKaacEnc_calcThreshExp +description: loudness calculation (threshold to the power of redExp) +*****************************************************************************/ +static void FDKaacEnc_calcThreshExp(FIXP_DBL thrExp[(2)][MAX_GROUPED_SFB], + QC_OUT_CHANNEL* qcOutChannel[(2)], + PSY_OUT_CHANNEL* psyOutChannel[(2)], + const INT nChannels) +{ + INT ch, sfb, sfbGrp; + FIXP_DBL thrExpLdData; + + for (ch=0; ch<nChannels; ch++) { + for(sfbGrp = 0;sfbGrp < psyOutChannel[ch]->sfbCnt;sfbGrp+= psyOutChannel[ch]->sfbPerGroup) { + for (sfb=0; sfb<psyOutChannel[ch]->maxSfbPerGroup; sfb++) { + thrExpLdData = psyOutChannel[ch]->sfbThresholdLdData[sfbGrp+sfb]>>2 ; + thrExp[ch][sfbGrp+sfb] = CalcInvLdData(thrExpLdData); + } + } + } +} + + +/***************************************************************************** + functionname: FDKaacEnc_adaptMinSnr + description: reduce minSnr requirements for bands with relative low energies +*****************************************************************************/ +static void FDKaacEnc_adaptMinSnr(QC_OUT_CHANNEL *qcOutChannel[(2)], + PSY_OUT_CHANNEL *psyOutChannel[(2)], + MINSNR_ADAPT_PARAM *msaParam, + const INT nChannels) +{ + INT ch, sfb, sfbGrp, nSfb; + FIXP_DBL avgEnLD64, dbRatio, minSnrRed; + FIXP_DBL minSnrLimitLD64 = FL2FXCONST_DBL(-0.00503012648262f); /* ld64(0.8f) */ + FIXP_DBL nSfbLD64; + FIXP_DBL accu; + + for (ch=0; ch<nChannels; ch++) { + /* calc average energy per scalefactor band */ + nSfb = 0; + accu = FL2FXCONST_DBL(0.0f); + + for (sfbGrp=0; sfbGrp < psyOutChannel[ch]->sfbCnt; sfbGrp+=psyOutChannel[ch]->sfbPerGroup) { + for (sfb=0; sfb<psyOutChannel[ch]->maxSfbPerGroup; sfb++) { + accu += psyOutChannel[ch]->sfbEnergy[sfbGrp+sfb]>>6; + nSfb++; + } + } + + if ((accu == FL2FXCONST_DBL(0.0f)) || (nSfb == 0)) { + avgEnLD64 = FL2FXCONST_DBL(-1.0f); + } + else { + nSfbLD64 = CalcLdInt(nSfb); + avgEnLD64 = CalcLdData(accu); + avgEnLD64 = avgEnLD64 + FL2FXCONST_DBL(0.09375f) - nSfbLD64; /* 0.09375f: compensate shift with 6 */ + } + + /* reduce minSnr requirement by minSnr^minSnrRed dependent on avgEn/sfbEn */ + for (sfbGrp=0; sfbGrp < psyOutChannel[ch]->sfbCnt; sfbGrp+=psyOutChannel[ch]->sfbPerGroup) { + for (sfb=0; sfb<psyOutChannel[ch]->maxSfbPerGroup; sfb++) { + if ( (msaParam->startRatio + qcOutChannel[ch]->sfbEnergyLdData[sfbGrp+sfb]) < avgEnLD64 ) { + dbRatio = fMult((avgEnLD64 - qcOutChannel[ch]->sfbEnergyLdData[sfbGrp+sfb]),FL2FXCONST_DBL(0.3010299956f)); /* scaled by (1.0f/(10.0f*64.0f)) */ + minSnrRed = msaParam->redOffs + fMult(msaParam->redRatioFac,dbRatio); /* scaled by 1.0f/64.0f*/ + minSnrRed = fixMax(minSnrRed, msaParam->maxRed); /* scaled by 1.0f/64.0f*/ + qcOutChannel[ch]->sfbMinSnrLdData[sfbGrp+sfb] = (fMult(qcOutChannel[ch]->sfbMinSnrLdData[sfbGrp+sfb],minSnrRed)) << 6; + qcOutChannel[ch]->sfbMinSnrLdData[sfbGrp+sfb] = fixMin(minSnrLimitLD64, qcOutChannel[ch]->sfbMinSnrLdData[sfbGrp+sfb]); + } + } + } + } +} + + +/***************************************************************************** +functionname: FDKaacEnc_initAvoidHoleFlag +description: determine bands where avoid hole is not necessary resp. possible +*****************************************************************************/ +static void FDKaacEnc_initAvoidHoleFlag(QC_OUT_CHANNEL *qcOutChannel[(2)], + PSY_OUT_CHANNEL *psyOutChannel[(2)], + UCHAR ahFlag[(2)][MAX_GROUPED_SFB], + struct TOOLSINFO *toolsInfo, + const INT nChannels, + const PE_DATA *peData, + AH_PARAM *ahParam) +{ + INT ch, sfb, sfbGrp; + FIXP_DBL sfbEn, sfbEnm1; + FIXP_DBL sfbEnLdData; + FIXP_DBL avgEnLdData; + + /* decrease spread energy by 3dB for long blocks, resp. 2dB for shorts + (avoid more holes in long blocks) */ + for (ch=0; ch<nChannels; ch++) { + INT sfbGrp, sfb; + QC_OUT_CHANNEL* qcOutChan = qcOutChannel[ch]; + + if (psyOutChannel[ch]->lastWindowSequence != SHORT_WINDOW) { + for (sfbGrp = 0;sfbGrp < psyOutChannel[ch]->sfbCnt;sfbGrp+= psyOutChannel[ch]->sfbPerGroup) + for (sfb=0; sfb<psyOutChannel[ch]->maxSfbPerGroup; sfb++) + qcOutChan->sfbSpreadEnergy[sfbGrp+sfb] >>= 1 ; + } + else { + for (sfbGrp = 0;sfbGrp < psyOutChannel[ch]->sfbCnt;sfbGrp+= psyOutChannel[ch]->sfbPerGroup) + for (sfb=0; sfb<psyOutChannel[ch]->maxSfbPerGroup; sfb++) + qcOutChan->sfbSpreadEnergy[sfbGrp+sfb] = + fMult(FL2FXCONST_DBL(0.63f), + qcOutChan->sfbSpreadEnergy[sfbGrp+sfb]) ; + } + } + + /* increase minSnr for local peaks, decrease it for valleys */ + if (ahParam->modifyMinSnr) { + for(ch=0; ch<nChannels; ch++) { + QC_OUT_CHANNEL* qcOutChan = qcOutChannel[ch]; + for(sfbGrp = 0;sfbGrp < psyOutChannel[ch]->sfbCnt;sfbGrp+= psyOutChannel[ch]->sfbPerGroup){ + for (sfb=0; sfb<psyOutChannel[ch]->maxSfbPerGroup; sfb++) { + FIXP_DBL sfbEnp1, avgEn; + if (sfb > 0) + sfbEnm1 = qcOutChan->sfbEnergy[sfbGrp+sfb-1]; + else + sfbEnm1 = qcOutChan->sfbEnergy[sfbGrp+sfb]; + + if (sfb < psyOutChannel[ch]->maxSfbPerGroup-1) + sfbEnp1 = qcOutChan->sfbEnergy[sfbGrp+sfb+1]; + else + sfbEnp1 = qcOutChan->sfbEnergy[sfbGrp+sfb]; + + avgEn = (sfbEnm1>>1) + (sfbEnp1>>1); + avgEnLdData = CalcLdData(avgEn); + sfbEn = qcOutChan->sfbEnergy[sfbGrp+sfb]; + sfbEnLdData = qcOutChan->sfbEnergyLdData[sfbGrp+sfb]; + /* peak ? */ + if (sfbEn > avgEn) { + FIXP_DBL tmpMinSnrLdData; + if (psyOutChannel[ch]->lastWindowSequence==LONG_WINDOW) + tmpMinSnrLdData = fixMax( SnrLdFac + (FIXP_DBL)(avgEnLdData - sfbEnLdData), (FIXP_DBL)SnrLdMin1 ) ; + else + tmpMinSnrLdData = fixMax( SnrLdFac + (FIXP_DBL)(avgEnLdData - sfbEnLdData), (FIXP_DBL)SnrLdMin3 ) ; + + qcOutChan->sfbMinSnrLdData[sfbGrp+sfb] = + fixMin(qcOutChan->sfbMinSnrLdData[sfbGrp+sfb], tmpMinSnrLdData); + } + /* valley ? */ + if ( ((sfbEnLdData+(FIXP_DBL)SnrLdMin4) < (FIXP_DBL)avgEnLdData) && (sfbEn > FL2FXCONST_DBL(0.0)) ) { + FIXP_DBL tmpMinSnrLdData = avgEnLdData - sfbEnLdData -(FIXP_DBL)SnrLdMin4 + qcOutChan->sfbMinSnrLdData[sfbGrp+sfb]; + tmpMinSnrLdData = fixMin((FIXP_DBL)SnrLdFac, tmpMinSnrLdData); + qcOutChan->sfbMinSnrLdData[sfbGrp+sfb] = fixMin(tmpMinSnrLdData, + (FIXP_DBL)(qcOutChan->sfbMinSnrLdData[sfbGrp+sfb] + SnrLdMin2 )); + } + } + } + } + } + + /* stereo: adapt the minimum requirements sfbMinSnr of mid and + side channels to avoid spending unnoticable bits */ + if (nChannels == 2) { + QC_OUT_CHANNEL* qcOutChanM = qcOutChannel[0]; + QC_OUT_CHANNEL* qcOutChanS = qcOutChannel[1]; + PSY_OUT_CHANNEL* psyOutChanM = psyOutChannel[0]; + for(sfbGrp = 0;sfbGrp < psyOutChanM->sfbCnt;sfbGrp+= psyOutChanM->sfbPerGroup){ + for (sfb=0; sfb<psyOutChanM->maxSfbPerGroup; sfb++) { + if (toolsInfo->msMask[sfbGrp+sfb]) { + FIXP_DBL maxSfbEnLd = fixMax(qcOutChanM->sfbEnergyLdData[sfbGrp+sfb],qcOutChanS->sfbEnergyLdData[sfbGrp+sfb]); + FIXP_DBL maxThrLd, sfbMinSnrTmpLd; + + if ( ((SnrLdMin5>>1) + (maxSfbEnLd>>1) + (qcOutChanM->sfbMinSnrLdData[sfbGrp+sfb]>>1)) <= FL2FXCONST_DBL(-0.5f)) + maxThrLd = FL2FXCONST_DBL(-1.0f) ; + else + maxThrLd = SnrLdMin5 + maxSfbEnLd + qcOutChanM->sfbMinSnrLdData[sfbGrp+sfb]; + + if (qcOutChanM->sfbEnergy[sfbGrp+sfb] > FL2FXCONST_DBL(0.0f)) + sfbMinSnrTmpLd = maxThrLd - qcOutChanM->sfbEnergyLdData[sfbGrp+sfb]; + else + sfbMinSnrTmpLd = FL2FXCONST_DBL(0.0f); + + qcOutChanM->sfbMinSnrLdData[sfbGrp+sfb] = fixMax(qcOutChanM->sfbMinSnrLdData[sfbGrp+sfb],sfbMinSnrTmpLd); + + if (qcOutChanM->sfbMinSnrLdData[sfbGrp+sfb] <= FL2FXCONST_DBL(0.0f)) + qcOutChanM->sfbMinSnrLdData[sfbGrp+sfb] = fixMin(qcOutChanM->sfbMinSnrLdData[sfbGrp+sfb], (FIXP_DBL)SnrLdFac); + + if (qcOutChanS->sfbEnergy[sfbGrp+sfb] > FL2FXCONST_DBL(0.0f)) + sfbMinSnrTmpLd = maxThrLd - qcOutChanS->sfbEnergyLdData[sfbGrp+sfb]; + else + sfbMinSnrTmpLd = FL2FXCONST_DBL(0.0f); + + qcOutChanS->sfbMinSnrLdData[sfbGrp+sfb] = fixMax(qcOutChanS->sfbMinSnrLdData[sfbGrp+sfb],sfbMinSnrTmpLd); + + if (qcOutChanS->sfbMinSnrLdData[sfbGrp+sfb] <= FL2FXCONST_DBL(0.0f)) + qcOutChanS->sfbMinSnrLdData[sfbGrp+sfb] = fixMin(qcOutChanS->sfbMinSnrLdData[sfbGrp+sfb],(FIXP_DBL)SnrLdFac); + + if (qcOutChanM->sfbEnergy[sfbGrp+sfb]>qcOutChanM->sfbSpreadEnergy[sfbGrp+sfb]) + qcOutChanS->sfbSpreadEnergy[sfbGrp+sfb] = + fMult(qcOutChanS->sfbEnergy[sfbGrp+sfb], FL2FXCONST_DBL(0.9f)); + + if (qcOutChanS->sfbEnergy[sfbGrp+sfb]>qcOutChanS->sfbSpreadEnergy[sfbGrp+sfb]) + qcOutChanM->sfbSpreadEnergy[sfbGrp+sfb] = + fMult(qcOutChanM->sfbEnergy[sfbGrp+sfb], FL2FXCONST_DBL(0.9f)); + } + } + } + } + + /* init ahFlag (0: no ah necessary, 1: ah possible, 2: ah active */ + for(ch=0; ch<nChannels; ch++) { + QC_OUT_CHANNEL *qcOutChan = qcOutChannel[ch]; + PSY_OUT_CHANNEL *psyOutChan = psyOutChannel[ch]; + for(sfbGrp = 0;sfbGrp < psyOutChan->sfbCnt;sfbGrp+= psyOutChan->sfbPerGroup){ + for (sfb=0; sfb<psyOutChan->maxSfbPerGroup; sfb++) { + if ((qcOutChan->sfbSpreadEnergy[sfbGrp+sfb] > qcOutChan->sfbEnergy[sfbGrp+sfb]) + || (qcOutChan->sfbMinSnrLdData[sfbGrp+sfb] > FL2FXCONST_DBL(0.0f))) { + ahFlag[ch][sfbGrp+sfb] = NO_AH; + } + else { + ahFlag[ch][sfbGrp+sfb] = AH_INACTIVE; + } + } + } + } +} + + + +/** + * \brief Calculate constants that do not change during successive pe calculations. + * + * \param peData Pointer to structure containing PE data of current element. + * \param psyOutChannel Pointer to PSY_OUT_CHANNEL struct holding nChannels elements. + * \param qcOutChannel Pointer to QC_OUT_CHANNEL struct holding nChannels elements. + * \param nChannels Number of channels in element. + * \param peOffset Fixed PE offset defined while FDKaacEnc_AdjThrInit() depending on bitrate. + * + * \return void + */ +static +void FDKaacEnc_preparePe(PE_DATA *peData, + PSY_OUT_CHANNEL* psyOutChannel[(2)], + QC_OUT_CHANNEL* qcOutChannel[(2)], + const INT nChannels, + const INT peOffset) +{ + INT ch; + + for(ch=0; ch<nChannels; ch++) { + PSY_OUT_CHANNEL *psyOutChan = psyOutChannel[ch]; + FDKaacEnc_prepareSfbPe(&peData->peChannelData[ch], + psyOutChan->sfbEnergyLdData, + psyOutChan->sfbThresholdLdData, + qcOutChannel[ch]->sfbFormFactorLdData, + psyOutChan->sfbOffsets, + psyOutChan->sfbCnt, + psyOutChan->sfbPerGroup, + psyOutChan->maxSfbPerGroup); + } + peData->offset = peOffset; +} + +/** + * \brief Calculate weighting factor for threshold adjustment. + * + * Calculate weighting factor to be applied at energies and thresholds in ld64 format. + * + * \param peData, Pointer to PE data in current element. + * \param psyOutChannel Pointer to PSY_OUT_CHANNEL struct holding nChannels elements. + * \param qcOutChannel Pointer to QC_OUT_CHANNEL struct holding nChannels elements. + * \param toolsInfo Pointer to tools info struct of current element. + * \param adjThrStateElement Pointer to ATS_ELEMENT holding enFacPatch states. + * \param nChannels Number of channels in element. + * \param usePatchTool Apply the weighting tool 0 (no) else (yes). + * + * \return void + */ +static +void FDKaacEnc_calcWeighting(PE_DATA *peData, + PSY_OUT_CHANNEL* psyOutChannel[(2)], + QC_OUT_CHANNEL* qcOutChannel[(2)], + struct TOOLSINFO *toolsInfo, + ATS_ELEMENT* adjThrStateElement, + const INT nChannels, + const INT usePatchTool) +{ + int ch, noShortWindowInFrame = TRUE; + INT exePatchM = 0; + + for (ch=0; ch<nChannels; ch++) { + if (psyOutChannel[ch]->lastWindowSequence == SHORT_WINDOW) { + noShortWindowInFrame = FALSE; + } + FDKmemclear(qcOutChannel[ch]->sfbEnFacLd, MAX_GROUPED_SFB*sizeof(FIXP_DBL)); + } + + if (usePatchTool==0) { + return; /* tool is disabled */ + } + + for (ch=0; ch<nChannels; ch++) { + + PSY_OUT_CHANNEL *psyOutChan = psyOutChannel[ch]; + + if (noShortWindowInFrame) { /* retain energy ratio between blocks of different length */ + + FIXP_DBL nrgSum14, nrgSum12, nrgSum34, nrgTotal; + FIXP_DBL nrgFacLd_14, nrgFacLd_12, nrgFacLd_34; + INT usePatch, exePatch; + int sfb, nLinesSum = 0; + + nrgSum14 = nrgSum12 = nrgSum34 = nrgTotal = FL2FXCONST_DBL(0.f); + + /* calculate flatness of audible spectrum, i.e. spectrum above masking threshold. */ + for (sfb = 0; sfb < psyOutChan->sfbCnt; sfb++) { + + FIXP_DBL nrgFac12 = CalcInvLdData(psyOutChan->sfbEnergyLdData[sfb]>>1); /* nrg^(1/2) */ + FIXP_DBL nrgFac14 = CalcInvLdData(psyOutChan->sfbEnergyLdData[sfb]>>2); /* nrg^(1/4) */ + + /* maximal number of bands is 64, results scaling factor 6 */ + nLinesSum += peData->peChannelData[ch].sfbNLines[sfb]; /* relevant lines */ + nrgTotal += ( psyOutChan->sfbEnergy[sfb] >> 6 ); /* sum up nrg */ + nrgSum12 += ( nrgFac12 >> 6 ); /* sum up nrg^(2/4) */ + nrgSum14 += ( nrgFac14 >> 6 ); /* sum up nrg^(1/4) */ + nrgSum34 += ( fMult(nrgFac14, nrgFac12) >> 6 ); /* sum up nrg^(3/4) */ + } + + nrgTotal = CalcLdData(nrgTotal); /* get ld64 of total nrg */ + + nrgFacLd_14 = CalcLdData(nrgSum14) - nrgTotal; /* ld64(nrgSum14/nrgTotal) */ + nrgFacLd_12 = CalcLdData(nrgSum12) - nrgTotal; /* ld64(nrgSum12/nrgTotal) */ + nrgFacLd_34 = CalcLdData(nrgSum34) - nrgTotal; /* ld64(nrgSum34/nrgTotal) */ + + adjThrStateElement->chaosMeasureEnFac[ch] = FDKmax( FL2FXCONST_DBL(0.1875f), fDivNorm(nLinesSum,psyOutChan->sfbOffsets[psyOutChan->sfbCnt]) ); + + usePatch = (adjThrStateElement->chaosMeasureEnFac[ch] > FL2FXCONST_DBL(0.78125f)); + exePatch = ((usePatch) && (adjThrStateElement->lastEnFacPatch[ch])); + + for (sfb = 0; sfb < psyOutChan->sfbCnt; sfb++) { + INT sfbExePatch; + + /* for MS coupled SFBs, also execute patch in side channel if done in mid channel */ + if ((ch == 1) && (toolsInfo->msMask[sfb])) { + sfbExePatch = exePatchM; + } + else { + sfbExePatch = exePatch; + } + + if ( (sfbExePatch) && (psyOutChan->sfbEnergy[sfb]>FL2FXCONST_DBL(0.f)) ) + { + /* execute patch based on spectral flatness calculated above */ + if (adjThrStateElement->chaosMeasureEnFac[ch] > FL2FXCONST_DBL(0.8125f)) { + qcOutChannel[ch]->sfbEnFacLd[sfb] = ( (nrgFacLd_14 + (psyOutChan->sfbEnergyLdData[sfb]+(psyOutChan->sfbEnergyLdData[sfb]>>1)))>>1 ); /* sfbEnergy^(3/4) */ + } + else if (adjThrStateElement->chaosMeasureEnFac[ch] > FL2FXCONST_DBL(0.796875f)) { + qcOutChannel[ch]->sfbEnFacLd[sfb] = ( (nrgFacLd_12 + psyOutChan->sfbEnergyLdData[sfb])>>1 ); /* sfbEnergy^(2/4) */ + } + else { + qcOutChannel[ch]->sfbEnFacLd[sfb] = ( (nrgFacLd_34 + (psyOutChan->sfbEnergyLdData[sfb]>>1))>>1 ); /* sfbEnergy^(1/4) */ + } + qcOutChannel[ch]->sfbEnFacLd[sfb] = fixMin(qcOutChannel[ch]->sfbEnFacLd[sfb],(FIXP_DBL)0); + + } + } /* sfb loop */ + + adjThrStateElement->lastEnFacPatch[ch] = usePatch; + exePatchM = exePatch; + } + else { + /* !noShortWindowInFrame */ + adjThrStateElement->chaosMeasureEnFac[ch] = FL2FXCONST_DBL(0.75f); + adjThrStateElement->lastEnFacPatch[ch] = TRUE; /* allow use of sfbEnFac patch in upcoming frame */ + } + + } /* ch loop */ + +} + + + + +/***************************************************************************** +functionname: FDKaacEnc_calcPe +description: calculate pe for both channels +*****************************************************************************/ +static +void FDKaacEnc_calcPe(PSY_OUT_CHANNEL* psyOutChannel[(2)], + QC_OUT_CHANNEL* qcOutChannel[(2)], + PE_DATA *peData, + const INT nChannels) +{ + INT ch; + + peData->pe = peData->offset; + peData->constPart = 0; + peData->nActiveLines = 0; + for(ch=0; ch<nChannels; ch++) { + PE_CHANNEL_DATA *peChanData = &peData->peChannelData[ch]; + FDKaacEnc_calcSfbPe(&peData->peChannelData[ch], + qcOutChannel[ch]->sfbWeightedEnergyLdData, + qcOutChannel[ch]->sfbThresholdLdData, + psyOutChannel[ch]->sfbCnt, + psyOutChannel[ch]->sfbPerGroup, + psyOutChannel[ch]->maxSfbPerGroup, + psyOutChannel[ch]->isBook, + psyOutChannel[ch]->isScale); + + peData->pe += peChanData->pe; + peData->constPart += peChanData->constPart; + peData->nActiveLines += peChanData->nActiveLines; + } +} + +void FDKaacEnc_peCalculation(PE_DATA *peData, + PSY_OUT_CHANNEL* psyOutChannel[(2)], + QC_OUT_CHANNEL* qcOutChannel[(2)], + struct TOOLSINFO *toolsInfo, + ATS_ELEMENT* adjThrStateElement, + const INT nChannels) +{ + /* constants that will not change during successive pe calculations */ + FDKaacEnc_preparePe(peData, psyOutChannel, qcOutChannel, nChannels, adjThrStateElement->peOffset); + + /* calculate weighting factor for threshold adjustment */ + FDKaacEnc_calcWeighting(peData, psyOutChannel, qcOutChannel, toolsInfo, adjThrStateElement, nChannels, 1); +{ + /* no weighting of threholds and energies for mlout */ + /* weight energies and thresholds */ + int ch; + for (ch=0; ch<nChannels; ch++) { + + int sfb, sfbGrp; + QC_OUT_CHANNEL* pQcOutCh = qcOutChannel[ch]; + + for (sfbGrp = 0;sfbGrp < psyOutChannel[ch]->sfbCnt; sfbGrp+=psyOutChannel[ch]->sfbPerGroup) { + for (sfb=0; sfb<psyOutChannel[ch]->maxSfbPerGroup; sfb++) { + pQcOutCh->sfbWeightedEnergyLdData[sfb+sfbGrp] = pQcOutCh->sfbEnergyLdData[sfb+sfbGrp] - pQcOutCh->sfbEnFacLd[sfb+sfbGrp]; + pQcOutCh->sfbThresholdLdData[sfb+sfbGrp] -= pQcOutCh->sfbEnFacLd[sfb+sfbGrp]; + } + } + } +} + + /* pe without reduction */ + FDKaacEnc_calcPe(psyOutChannel, qcOutChannel, peData, nChannels); +} + + + +/***************************************************************************** +functionname: FDKaacEnc_FDKaacEnc_calcPeNoAH +description: sum the pe data only for bands where avoid hole is inactive +*****************************************************************************/ +static void FDKaacEnc_FDKaacEnc_calcPeNoAH(INT *pe, + INT *constPart, + INT *nActiveLines, + PE_DATA *peData, + UCHAR ahFlag[(2)][MAX_GROUPED_SFB], + PSY_OUT_CHANNEL* psyOutChannel[(2)], + const INT nChannels) +{ + INT ch, sfb,sfbGrp; + + INT pe_tmp = peData->offset; + INT constPart_tmp = 0; + INT nActiveLines_tmp = 0; + for(ch=0; ch<nChannels; ch++) { + PE_CHANNEL_DATA *peChanData = &peData->peChannelData[ch]; + for(sfbGrp = 0;sfbGrp < psyOutChannel[ch]->sfbCnt;sfbGrp+= psyOutChannel[ch]->sfbPerGroup){ + for (sfb=0; sfb<psyOutChannel[ch]->maxSfbPerGroup; sfb++) { + if(ahFlag[ch][sfbGrp+sfb] < AH_ACTIVE) { + pe_tmp += peChanData->sfbPe[sfbGrp+sfb]; + constPart_tmp += peChanData->sfbConstPart[sfbGrp+sfb]; + nActiveLines_tmp += peChanData->sfbNActiveLines[sfbGrp+sfb]; + } + } + } + } + /* correct scaled pe and constPart values */ + *pe = pe_tmp >> PE_CONSTPART_SHIFT; + *constPart = constPart_tmp >> PE_CONSTPART_SHIFT; + + *nActiveLines = nActiveLines_tmp; +} + + +/***************************************************************************** +functionname: FDKaacEnc_reduceThresholdsCBR +description: apply reduction formula +*****************************************************************************/ +static const FIXP_DBL limitThrReducedLdData = (FIXP_DBL)0x00008000; /*FL2FXCONST_DBL(FDKpow(2.0,-LD_DATA_SCALING/4.0));*/ + +static void FDKaacEnc_reduceThresholdsCBR(QC_OUT_CHANNEL* qcOutChannel[(2)], + PSY_OUT_CHANNEL* psyOutChannel[(2)], + UCHAR ahFlag[(2)][MAX_GROUPED_SFB], + FIXP_DBL thrExp[(2)][MAX_GROUPED_SFB], + const INT nChannels, + const FIXP_DBL redVal, + const SCHAR redValScaling) +{ + INT ch, sfb, sfbGrp; + FIXP_DBL sfbEnLdData, sfbThrLdData, sfbThrReducedLdData; + FIXP_DBL sfbThrExp; + + for(ch=0; ch<nChannels; ch++) { + QC_OUT_CHANNEL *qcOutChan = qcOutChannel[ch]; + for(sfbGrp = 0; sfbGrp < psyOutChannel[ch]->sfbCnt; sfbGrp+= psyOutChannel[ch]->sfbPerGroup){ + for (sfb=0; sfb<psyOutChannel[ch]->maxSfbPerGroup; sfb++) { + sfbEnLdData = qcOutChan->sfbWeightedEnergyLdData[sfbGrp+sfb]; + sfbThrLdData = qcOutChan->sfbThresholdLdData[sfbGrp+sfb]; + sfbThrExp = thrExp[ch][sfbGrp+sfb]; + if ((sfbEnLdData > sfbThrLdData) && (ahFlag[ch][sfbGrp+sfb] != AH_ACTIVE)) { + + /* threshold reduction formula: + float tmp = thrExp[ch][sfb]+redVal; + tmp *= tmp; + sfbThrReduced = tmp*tmp; + */ + int minScale = fixMin(CountLeadingBits(sfbThrExp), CountLeadingBits(redVal) - (DFRACT_BITS-1-redValScaling) )-1; + + /* 4*log( sfbThrExp + redVal ) */ + sfbThrReducedLdData = CalcLdData(fAbs(scaleValue(sfbThrExp, minScale) + scaleValue(redVal,(DFRACT_BITS-1-redValScaling)+minScale))) + - (FIXP_DBL)(minScale<<(DFRACT_BITS-1-LD_DATA_SHIFT)); + sfbThrReducedLdData <<= 2; + + /* avoid holes */ + if ( ((sfbThrReducedLdData - sfbEnLdData) > qcOutChan->sfbMinSnrLdData[sfbGrp+sfb] ) + && (ahFlag[ch][sfbGrp+sfb] != NO_AH) ) + { + if (qcOutChan->sfbMinSnrLdData[sfbGrp+sfb] > (FL2FXCONST_DBL(-1.0f) - sfbEnLdData) ){ + sfbThrReducedLdData = fixMax((qcOutChan->sfbMinSnrLdData[sfbGrp+sfb] + sfbEnLdData), sfbThrLdData); + } + else sfbThrReducedLdData = sfbThrLdData; + ahFlag[ch][sfbGrp+sfb] = AH_ACTIVE; + } + + /* minimum of 29 dB Ratio for Thresholds */ + if ((sfbEnLdData+(FIXP_DBL)MAXVAL_DBL) > FL2FXCONST_DBL(9.6336206/LD_DATA_SCALING)){ + sfbThrReducedLdData = fixMax(sfbThrReducedLdData, (sfbEnLdData - FL2FXCONST_DBL(9.6336206/LD_DATA_SCALING))); + } + + qcOutChan->sfbThresholdLdData[sfbGrp+sfb] = sfbThrReducedLdData; + } + } + } + } +} + +/* similar to prepareSfbPe1() */ +static FIXP_DBL FDKaacEnc_calcChaosMeasure(PSY_OUT_CHANNEL *psyOutChannel, + const FIXP_DBL *sfbFormFactorLdData) +{ + #define SCALE_FORM_FAC (4) /* (SCALE_FORM_FAC+FORM_FAC_SHIFT) >= ld(FRAME_LENGTH)*/ + #define SCALE_NRGS (8) + #define SCALE_NLINES (16) + #define SCALE_NRGS_SQRT4 (2) /* 0.25 * SCALE_NRGS */ + #define SCALE_NLINES_P34 (12) /* 0.75 * SCALE_NLINES */ + + INT sfbGrp, sfb; + FIXP_DBL chaosMeasure; + INT frameNLines = 0; + FIXP_DBL frameFormFactor = FL2FXCONST_DBL(0.f); + FIXP_DBL frameEnergy = FL2FXCONST_DBL(0.f); + + for (sfbGrp=0; sfbGrp<psyOutChannel->sfbCnt; sfbGrp+=psyOutChannel->sfbPerGroup) { + for (sfb=0; sfb<psyOutChannel->maxSfbPerGroup; sfb++){ + if (psyOutChannel->sfbEnergyLdData[sfbGrp+sfb] > psyOutChannel->sfbThresholdLdData[sfbGrp+sfb]) { + frameFormFactor += (CalcInvLdData(sfbFormFactorLdData[sfbGrp+sfb])>>SCALE_FORM_FAC); + frameNLines += (psyOutChannel->sfbOffsets[sfbGrp+sfb+1] - psyOutChannel->sfbOffsets[sfbGrp+sfb]); + frameEnergy += (psyOutChannel->sfbEnergy[sfbGrp+sfb]>>SCALE_NRGS); + } + } + } + + if(frameNLines > 0){ + + /* frameNActiveLines = frameFormFactor*2^FORM_FAC_SHIFT * ((frameEnergy *2^SCALE_NRGS)/frameNLines)^-0.25 + chaosMeasure = frameNActiveLines / frameNLines */ + chaosMeasure = + CalcInvLdData( (((CalcLdData(frameFormFactor)>>1) - + (CalcLdData(frameEnergy)>>(2+1))) - + (fMultDiv2(FL2FXCONST_DBL(0.75f),CalcLdData((FIXP_DBL)frameNLines<<(DFRACT_BITS-1-SCALE_NLINES))) - + (((FIXP_DBL)(SCALE_FORM_FAC-SCALE_NRGS_SQRT4+FORM_FAC_SHIFT-(SCALE_NLINES_P34))<<(DFRACT_BITS-1-LD_DATA_SHIFT))>>1)) + )<<1 ); + } else { + + /* assuming total chaos, if no sfb is above thresholds */ + chaosMeasure = FL2FXCONST_DBL(1.f); + } + + return chaosMeasure; +} + + +/* apply reduction formula for VBR-mode */ +static void FDKaacEnc_reduceThresholdsVBR(QC_OUT_CHANNEL* qcOutChannel[(2)], + PSY_OUT_CHANNEL* psyOutChannel[(2)], + UCHAR ahFlag[(2)][MAX_GROUPED_SFB], + FIXP_DBL thrExp[(2)][MAX_GROUPED_SFB], + const INT nChannels, + const FIXP_DBL vbrQualFactor, + FIXP_DBL* chaosMeasureOld) +{ + INT ch, sfbGrp, sfb; + FIXP_DBL chGroupEnergy[TRANS_FAC][2];/*energy for each group and channel*/ + FIXP_DBL chChaosMeasure[2]; + FIXP_DBL frameEnergy = FL2FXCONST_DBL(1e-10f); + FIXP_DBL chaosMeasure = FL2FXCONST_DBL(0.f); + FIXP_DBL sfbEnLdData, sfbThrLdData, sfbThrExp; + FIXP_DBL sfbThrReducedLdData; + FIXP_DBL chaosMeasureAvg; + INT groupCnt; /* loop counter */ + FIXP_DBL redVal[TRANS_FAC]; /* reduction values; in short-block case one redVal for each group */ + QC_OUT_CHANNEL *qcOutChan = NULL; + PSY_OUT_CHANNEL *psyOutChan = NULL; + +#define SCALE_GROUP_ENERGY (8) + +#define CONST_CHAOS_MEAS_AVG_FAC_0 (FL2FXCONST_DBL(0.25f)) +#define CONST_CHAOS_MEAS_AVG_FAC_1 (FL2FXCONST_DBL(1.f-0.25f)) + +#define MIN_LDTHRESH (FL2FXCONST_DBL(-0.515625f)) + + + for(ch=0; ch<nChannels; ch++){ + qcOutChan = qcOutChannel[ch]; + psyOutChan = psyOutChannel[ch]; + + /* adding up energy for each channel and each group separately */ + FIXP_DBL chEnergy = FL2FXCONST_DBL(0.f); + groupCnt=0; + + for (sfbGrp=0; sfbGrp<psyOutChan->sfbCnt; sfbGrp+=psyOutChan->sfbPerGroup, groupCnt++) { + chGroupEnergy[groupCnt][ch] = FL2FXCONST_DBL(0.f); + for (sfb=0; sfb<psyOutChan->maxSfbPerGroup; sfb++){ + chGroupEnergy[groupCnt][ch] += (psyOutChan->sfbEnergy[sfbGrp+sfb]>>SCALE_GROUP_ENERGY); + } + chEnergy += chGroupEnergy[groupCnt][ch]; + } + frameEnergy += chEnergy; + + /* chaosMeasure */ + if (psyOutChannel[0]->lastWindowSequence == SHORT_WINDOW) { + chChaosMeasure[ch] = FL2FXCONST_DBL(0.5f); /* assume a constant chaos measure of 0.5f for short blocks */ + } else { + chChaosMeasure[ch] = FDKaacEnc_calcChaosMeasure(psyOutChannel[ch], qcOutChannel[ch]->sfbFormFactorLdData); + } + chaosMeasure += fMult(chChaosMeasure[ch], chEnergy); + } + + if(frameEnergy > chaosMeasure) { + INT scale = CntLeadingZeros(frameEnergy) - 1; + FIXP_DBL num = chaosMeasure<<scale; + FIXP_DBL denum = frameEnergy<<scale; + chaosMeasure = schur_div(num,denum,16); + } + else { + chaosMeasure = FL2FXCONST_DBL(1.f); + } + + chaosMeasureAvg = fMult(CONST_CHAOS_MEAS_AVG_FAC_0, chaosMeasure) + + fMult(CONST_CHAOS_MEAS_AVG_FAC_1, *chaosMeasureOld); /* averaging chaos measure */ + *chaosMeasureOld = chaosMeasure = (fixMin(chaosMeasure, chaosMeasureAvg)); /* use min-value, safe for next frame */ + + /* characteristic curve + chaosMeasure = 0.2f + 0.7f/0.3f * (chaosMeasure - 0.2f); + chaosMeasure = fixMin(1.0f, fixMax(0.1f, chaosMeasure)); + constants scaled by 4.f + */ + chaosMeasure = ((FL2FXCONST_DBL(0.2f)>>2) + fMult(FL2FXCONST_DBL(0.7f/(4.f*0.3f)), (chaosMeasure - FL2FXCONST_DBL(0.2f)))); + chaosMeasure = (fixMin((FIXP_DBL)(FL2FXCONST_DBL(1.0f)>>2), fixMax((FIXP_DBL)(FL2FXCONST_DBL(0.1f)>>2), chaosMeasure)))<<2; + + /* calculation of reduction value */ + if (psyOutChannel[0]->lastWindowSequence == SHORT_WINDOW){ /* short-blocks */ + FDK_ASSERT(TRANS_FAC==8); + #define WIN_TYPE_SCALE (3) + + INT sfbGrp, groupCnt=0; + for (sfbGrp=0; sfbGrp<psyOutChan->sfbCnt; sfbGrp+=psyOutChan->sfbPerGroup,groupCnt++) { + + FIXP_DBL groupEnergy = FL2FXCONST_DBL(0.f); + + for(ch=0;ch<nChannels;ch++){ + groupEnergy += chGroupEnergy[groupCnt][ch]; /* adding up the channels groupEnergy */ + } + + FDK_ASSERT(psyOutChannel[0]->groupLen[groupCnt]<=INV_INT_TAB_SIZE); + groupEnergy = fMult(groupEnergy,invInt[psyOutChannel[0]->groupLen[groupCnt]]); /* correction of group energy */ + groupEnergy = fixMin(groupEnergy, frameEnergy>>WIN_TYPE_SCALE); /* do not allow an higher redVal as calculated framewise */ + + groupEnergy>>=2; /* 2*WIN_TYPE_SCALE = 6 => 6+2 = 8 ==> 8/4 = int number */ + + redVal[groupCnt] = fMult(fMult(vbrQualFactor,chaosMeasure), + CalcInvLdData(CalcLdData(groupEnergy)>>2) ) + << (int)( ( 2 + (2*WIN_TYPE_SCALE) + SCALE_GROUP_ENERGY )>>2 ) ; + + } + } else { /* long-block */ + + redVal[0] = fMult( fMult(vbrQualFactor,chaosMeasure), + CalcInvLdData(CalcLdData(frameEnergy)>>2) ) + << (int)( SCALE_GROUP_ENERGY>>2 ) ; + } + + for(ch=0; ch<nChannels; ch++) { + qcOutChan = qcOutChannel[ch]; + psyOutChan = psyOutChannel[ch]; + + for (sfbGrp=0; sfbGrp<psyOutChan->sfbCnt; sfbGrp+=psyOutChan->sfbPerGroup) { + for (sfb=0; sfb<psyOutChan->maxSfbPerGroup; sfb++){ + + sfbEnLdData = (qcOutChan->sfbWeightedEnergyLdData[sfbGrp+sfb]); + sfbThrLdData = (qcOutChan->sfbThresholdLdData[sfbGrp+sfb]); + sfbThrExp = thrExp[ch][sfbGrp+sfb]; + + if ( (sfbThrLdData>=MIN_LDTHRESH) && (sfbEnLdData > sfbThrLdData) && (ahFlag[ch][sfbGrp+sfb] != AH_ACTIVE)) { + + /* Short-Window */ + if (psyOutChannel[ch]->lastWindowSequence == SHORT_WINDOW) { + const int groupNumber = (int) sfb/psyOutChan->sfbPerGroup; + + FDK_ASSERT(INV_SQRT4_TAB_SIZE>psyOutChan->groupLen[groupNumber]); + + sfbThrExp = fMult(sfbThrExp, fMult( FL2FXCONST_DBL(2.82f/4.f), invSqrt4[psyOutChan->groupLen[groupNumber]]))<<2 ; + + if ( sfbThrExp <= (limitThrReducedLdData-redVal[groupNumber]) ) { + sfbThrReducedLdData = FL2FXCONST_DBL(-1.0f); + } + else { + if ((FIXP_DBL)redVal[groupNumber] >= FL2FXCONST_DBL(1.0f)-sfbThrExp) + sfbThrReducedLdData = FL2FXCONST_DBL(0.0f); + else { + /* threshold reduction formula */ + sfbThrReducedLdData = CalcLdData(sfbThrExp + redVal[groupNumber]); + sfbThrReducedLdData <<= 2; + } + } + sfbThrReducedLdData += ( CalcLdInt(psyOutChan->groupLen[groupNumber]) - + ((FIXP_DBL)6<<(DFRACT_BITS-1-LD_DATA_SHIFT)) ); + } + + /* Long-Window */ + else { + if ((FIXP_DBL)redVal[0] >= FL2FXCONST_DBL(1.0f)-sfbThrExp) { + sfbThrReducedLdData = FL2FXCONST_DBL(0.0f); + } + else { + /* threshold reduction formula */ + sfbThrReducedLdData = CalcLdData(sfbThrExp + redVal[0]); + sfbThrReducedLdData <<= 2; + } + } + + /* avoid holes */ + if ( ((sfbThrReducedLdData - sfbEnLdData) > qcOutChan->sfbMinSnrLdData[sfbGrp+sfb] ) + && (ahFlag[ch][sfbGrp+sfb] != NO_AH) ) + { + if (qcOutChan->sfbMinSnrLdData[sfbGrp+sfb] > (FL2FXCONST_DBL(-1.0f) - sfbEnLdData) ){ + sfbThrReducedLdData = fixMax((qcOutChan->sfbMinSnrLdData[sfbGrp+sfb] + sfbEnLdData), sfbThrLdData); + } + else sfbThrReducedLdData = sfbThrLdData; + ahFlag[ch][sfbGrp+sfb] = AH_ACTIVE; + } + + if (sfbThrReducedLdData<FL2FXCONST_DBL(-0.5f)) + sfbThrReducedLdData = FL2FXCONST_DBL(-1.f); + + /* minimum of 29 dB Ratio for Thresholds */ + if ((sfbEnLdData+FL2FXCONST_DBL(1.0f)) > FL2FXCONST_DBL(9.6336206/LD_DATA_SCALING)){ + sfbThrReducedLdData = fixMax(sfbThrReducedLdData, sfbEnLdData - FL2FXCONST_DBL(9.6336206/LD_DATA_SCALING)); + } + + sfbThrReducedLdData = fixMax(MIN_LDTHRESH,sfbThrReducedLdData); + + qcOutChan->sfbThresholdLdData[sfbGrp+sfb] = sfbThrReducedLdData; + } + } + } + } +} + + +/***************************************************************************** +functionname: FDKaacEnc_correctThresh +description: if pe difference deltaPe between desired pe and real pe is small enough, +the difference can be distributed among the scale factor bands. +New thresholds can be derived from this pe-difference +*****************************************************************************/ +static void FDKaacEnc_correctThresh(CHANNEL_MAPPING* cm, + QC_OUT_ELEMENT* qcElement[(6)], + PSY_OUT_ELEMENT* psyOutElement[(6)], + UCHAR ahFlag[(6)][(2)][MAX_GROUPED_SFB], + FIXP_DBL thrExp[(6)][(2)][MAX_GROUPED_SFB], + const FIXP_DBL redVal[(6)], + const SCHAR redValScaling[(6)], + const INT deltaPe, + const INT processElements, + const INT elementOffset) +{ + INT ch, sfb, sfbGrp; + QC_OUT_CHANNEL *qcOutChan; + PSY_OUT_CHANNEL *psyOutChan; + PE_CHANNEL_DATA *peChanData; + FIXP_DBL thrFactorLdData; + FIXP_DBL sfbEnLdData, sfbThrLdData, sfbThrReducedLdData; + FIXP_DBL *sfbPeFactorsLdData[(6)][(2)]; + FIXP_DBL sfbNActiveLinesLdData[(2)][MAX_GROUPED_SFB]; + INT normFactorInt; + FIXP_DBL normFactorLdData; + + INT nElements = elementOffset+processElements; + INT elementId; + + /* scratch is empty; use temporal memory from quantSpec in QC_OUT_CHANNEL */ + for(elementId=elementOffset;elementId<nElements;elementId++) { + for(ch=0; ch<cm->elInfo[elementId].nChannelsInEl; ch++) { + SHORT* ptr = qcElement[elementId]->qcOutChannel[ch]->quantSpec; + sfbPeFactorsLdData[elementId][ch] = (FIXP_DBL*)ptr; + } + } + + /* for each sfb calc relative factors for pe changes */ + normFactorInt = 0; + + for(elementId=elementOffset;elementId<nElements;elementId++) { + if (cm->elInfo[elementId].elType != ID_DSE) { + + for(ch=0; ch<cm->elInfo[elementId].nChannelsInEl; ch++) { + + qcOutChan = qcElement[elementId]->qcOutChannel[ch]; + psyOutChan = psyOutElement[elementId]->psyOutChannel[ch]; + peChanData = &qcElement[elementId]->peData.peChannelData[ch]; + + for(sfbGrp = 0; sfbGrp < psyOutChan->sfbCnt; sfbGrp+= psyOutChan->sfbPerGroup){ + for (sfb=0; sfb<psyOutChan->maxSfbPerGroup; sfb++) { + + if ( peChanData->sfbNActiveLines[sfbGrp+sfb] == 0 ) { + sfbNActiveLinesLdData[ch][sfbGrp+sfb] = FL2FXCONST_DBL(-1.0f); + } + else { + /* Both CalcLdInt and CalcLdData can be used! + * No offset has to be subtracted, because sfbNActiveLinesLdData + * is shorted while thrFactor calculation */ + sfbNActiveLinesLdData[ch][sfbGrp+sfb] = CalcLdInt(peChanData->sfbNActiveLines[sfbGrp+sfb]); + } + if ( ((ahFlag[elementId][ch][sfbGrp+sfb] < AH_ACTIVE) || (deltaPe > 0)) && + peChanData->sfbNActiveLines[sfbGrp+sfb] != 0 ) + { + if (thrExp[elementId][ch][sfbGrp+sfb] > -redVal[elementId]) { + + /* sfbPeFactors[ch][sfbGrp+sfb] = peChanData->sfbNActiveLines[sfbGrp+sfb] / + (thrExp[elementId][ch][sfbGrp+sfb] + redVal[elementId]); */ + + int minScale = fixMin(CountLeadingBits(thrExp[elementId][ch][sfbGrp+sfb]), CountLeadingBits(redVal[elementId]) - (DFRACT_BITS-1-redValScaling[elementId]) ) - 1; + + /* sumld = ld64( sfbThrExp + redVal ) */ + FIXP_DBL sumLd = CalcLdData(scaleValue(thrExp[elementId][ch][sfbGrp+sfb], minScale) + scaleValue(redVal[elementId], (DFRACT_BITS-1-redValScaling[elementId])+minScale)) + - (FIXP_DBL)(minScale<<(DFRACT_BITS-1-LD_DATA_SHIFT)); + + if (sumLd < FL2FXCONST_DBL(0.f)) { + sfbPeFactorsLdData[elementId][ch][sfbGrp+sfb] = sfbNActiveLinesLdData[ch][sfbGrp+sfb] - sumLd; + } + else { + if ( sfbNActiveLinesLdData[ch][sfbGrp+sfb] > (FL2FXCONST_DBL(-1.f) + sumLd) ) { + sfbPeFactorsLdData[elementId][ch][sfbGrp+sfb] = sfbNActiveLinesLdData[ch][sfbGrp+sfb] - sumLd; + } + else { + sfbPeFactorsLdData[elementId][ch][sfbGrp+sfb] = sfbNActiveLinesLdData[ch][sfbGrp+sfb]; + } + } + + normFactorInt += (INT)CalcInvLdData(sfbPeFactorsLdData[elementId][ch][sfbGrp+sfb]); + } + else sfbPeFactorsLdData[elementId][ch][sfbGrp+sfb] = FL2FXCONST_DBL(1.0f); + } + else sfbPeFactorsLdData[elementId][ch][sfbGrp+sfb] = FL2FXCONST_DBL(-1.0f); + } + } + } + } + } + + /* normFactorLdData = ld64(deltaPe/normFactorInt) */ + normFactorLdData = CalcLdData((FIXP_DBL)((deltaPe<0) ? (-deltaPe) : (deltaPe))) - CalcLdData((FIXP_DBL)normFactorInt); + + /* distribute the pe difference to the scalefactors + and calculate the according thresholds */ + for(elementId=elementOffset;elementId<nElements;elementId++) { + if (cm->elInfo[elementId].elType != ID_DSE) { + + for(ch=0; ch<cm->elInfo[elementId].nChannelsInEl; ch++) { + qcOutChan = qcElement[elementId]->qcOutChannel[ch]; + psyOutChan = psyOutElement[elementId]->psyOutChannel[ch]; + peChanData = &qcElement[elementId]->peData.peChannelData[ch]; + + for(sfbGrp = 0;sfbGrp < psyOutChan->sfbCnt;sfbGrp+= psyOutChan->sfbPerGroup){ + for (sfb=0; sfb<psyOutChan->maxSfbPerGroup; sfb++) { + + if (peChanData->sfbNActiveLines[sfbGrp+sfb] > 0) { + + /* pe difference for this sfb */ + if ( (sfbPeFactorsLdData[elementId][ch][sfbGrp+sfb]==FL2FXCONST_DBL(-1.0f)) || + (deltaPe==0) ) + { + thrFactorLdData = FL2FXCONST_DBL(0.f); + } + else { + /* new threshold */ + FIXP_DBL tmp = CalcInvLdData(sfbPeFactorsLdData[elementId][ch][sfbGrp+sfb] + normFactorLdData - sfbNActiveLinesLdData[ch][sfbGrp+sfb] - FL2FXCONST_DBL((float)LD_DATA_SHIFT/LD_DATA_SCALING)); + + /* limit thrFactor to 60dB */ + tmp = (deltaPe<0) ? tmp : (-tmp); + thrFactorLdData = FDKmin(tmp, FL2FXCONST_DBL(20.f/LD_DATA_SCALING)); + } + + /* new threshold */ + sfbThrLdData = qcOutChan->sfbThresholdLdData[sfbGrp+sfb]; + sfbEnLdData = qcOutChan->sfbWeightedEnergyLdData[sfbGrp+sfb]; + + if (thrFactorLdData < FL2FXCONST_DBL(0.f)) { + if( sfbThrLdData > (FL2FXCONST_DBL(-1.f)-thrFactorLdData) ) { + sfbThrReducedLdData = sfbThrLdData + thrFactorLdData; + } + else { + sfbThrReducedLdData = FL2FXCONST_DBL(-1.f); + } + } + else{ + sfbThrReducedLdData = sfbThrLdData + thrFactorLdData; + } + + /* avoid hole */ + if ( (sfbThrReducedLdData - sfbEnLdData > qcOutChan->sfbMinSnrLdData[sfbGrp+sfb]) && + (ahFlag[elementId][ch][sfbGrp+sfb] == AH_INACTIVE) ) + { + /* sfbThrReduced = max(psyOutChan[ch]->sfbMinSnr[i] * sfbEn, sfbThr); */ + if ( sfbEnLdData > (sfbThrLdData-qcOutChan->sfbMinSnrLdData[sfbGrp+sfb]) ) { + sfbThrReducedLdData = qcOutChan->sfbMinSnrLdData[sfbGrp+sfb] + sfbEnLdData; + } + else { + sfbThrReducedLdData = sfbThrLdData; + } + ahFlag[elementId][ch][sfbGrp+sfb] = AH_ACTIVE; + } + + qcOutChan->sfbThresholdLdData[sfbGrp+sfb] = sfbThrReducedLdData; + } + } + } + } + } + } +} + +/***************************************************************************** + functionname: FDKaacEnc_reduceMinSnr + description: if the desired pe can not be reached, reduce pe by + reducing minSnr +*****************************************************************************/ +void FDKaacEnc_reduceMinSnr(CHANNEL_MAPPING* cm, + QC_OUT_ELEMENT* qcElement[(6)], + PSY_OUT_ELEMENT* psyOutElement[(6)], + UCHAR ahFlag[(6)][(2)][MAX_GROUPED_SFB], + const INT desiredPe, + INT* redPeGlobal, + const INT processElements, + const INT elementOffset) + +{ + INT elementId; + INT nElements = elementOffset+processElements; + + INT newGlobalPe = *redPeGlobal; + + for(elementId=elementOffset;elementId<nElements;elementId++) { + if (cm->elInfo[elementId].elType != ID_DSE) { + INT ch; + INT maxSfbPerGroup[2]; + INT sfbCnt[2]; + INT sfbPerGroup[2]; + + for(ch=0; ch<cm->elInfo[elementId].nChannelsInEl; ch++) { + maxSfbPerGroup[ch] = psyOutElement[elementId]->psyOutChannel[ch]->maxSfbPerGroup-1; + sfbCnt[ch] = psyOutElement[elementId]->psyOutChannel[ch]->sfbCnt; + sfbPerGroup[ch] = psyOutElement[elementId]->psyOutChannel[ch]->sfbPerGroup; + } + + PE_DATA *peData = &qcElement[elementId]->peData; + + do + { + for(ch=0; ch<cm->elInfo[elementId].nChannelsInEl; ch++) { + + INT sfb, sfbGrp; + QC_OUT_CHANNEL *qcOutChan = qcElement[elementId]->qcOutChannel[ch]; + INT noReduction = 1; + + if (maxSfbPerGroup[ch]>=0) { /* sfb in next channel */ + INT deltaPe = 0; + sfb = maxSfbPerGroup[ch]--; + noReduction = 0; + + for (sfbGrp = 0; sfbGrp < sfbCnt[ch]; sfbGrp += sfbPerGroup[ch]) { + + if (ahFlag[elementId][ch][sfbGrp+sfb] != NO_AH && + qcOutChan->sfbMinSnrLdData[sfbGrp+sfb] < SnrLdFac) + { + /* increase threshold to new minSnr of 1dB */ + qcOutChan->sfbMinSnrLdData[sfbGrp+sfb] = SnrLdFac; + + /* sfbThrReduced = max(psyOutChan[ch]->sfbMinSnr[i] * sfbEn, sfbThr); */ + if ( qcOutChan->sfbWeightedEnergyLdData[sfbGrp+sfb] >= qcOutChan->sfbThresholdLdData[sfbGrp+sfb] - qcOutChan->sfbMinSnrLdData[sfbGrp+sfb] ) { + + qcOutChan->sfbThresholdLdData[sfbGrp+sfb] = qcOutChan->sfbWeightedEnergyLdData[sfbGrp+sfb] + qcOutChan->sfbMinSnrLdData[sfbGrp+sfb]; + + /* calc new pe */ + /* C2 + C3*ld(1/0.8) = 1.5 */ + deltaPe -= (peData->peChannelData[ch].sfbPe[sfbGrp+sfb]>>PE_CONSTPART_SHIFT); + + /* sfbPe = 1.5 * sfbNLines */ + peData->peChannelData[ch].sfbPe[sfbGrp+sfb] = (3*peData->peChannelData[ch].sfbNLines[sfbGrp+sfb]) << (PE_CONSTPART_SHIFT-1); + deltaPe += (peData->peChannelData[ch].sfbPe[sfbGrp+sfb]>>PE_CONSTPART_SHIFT); + } + } + + } /* sfbGrp loop */ + + peData->pe += deltaPe; + peData->peChannelData[ch].pe += deltaPe; + newGlobalPe += deltaPe; + + /* stop if enough has been saved */ + if (peData->pe <= desiredPe) { + goto bail; + } + + } /* sfb > 0 */ + + if ( (ch==(cm->elInfo[elementId].nChannelsInEl-1)) && noReduction ) { + goto bail; + } + + } /* ch loop */ + + } while ( peData->pe > desiredPe); + + } /* != ID_DSE */ + } /* element loop */ + + +bail: + /* update global PE */ + *redPeGlobal = newGlobalPe; +} + + +/***************************************************************************** + functionname: FDKaacEnc_allowMoreHoles + description: if the desired pe can not be reached, some more scalefactor + bands have to be quantized to zero +*****************************************************************************/ +static void FDKaacEnc_allowMoreHoles(CHANNEL_MAPPING* cm, + QC_OUT_ELEMENT* qcElement[(6)], + PSY_OUT_ELEMENT* psyOutElement[(6)], + ATS_ELEMENT* AdjThrStateElement[(6)], + UCHAR ahFlag[(6)][(2)][MAX_GROUPED_SFB], + const INT desiredPe, + const INT currentPe, + const int processElements, + const int elementOffset) +{ + INT elementId; + INT nElements = elementOffset+processElements; + INT actPe = currentPe; + + if (actPe <= desiredPe) { + return; /* nothing to do */ + } + + for (elementId = elementOffset;elementId<nElements;elementId++) { + if (cm->elInfo[elementId].elType != ID_DSE) { + + INT ch, sfb, sfbGrp; + + PE_DATA *peData = &qcElement[elementId]->peData; + const INT nChannels = cm->elInfo[elementId].nChannelsInEl; + + QC_OUT_CHANNEL* qcOutChannel[(2)] = {NULL}; + PSY_OUT_CHANNEL* psyOutChannel[(2)] = {NULL}; + + for (ch=0; ch<nChannels; ch++) { + + /* init pointers */ + qcOutChannel[ch] = qcElement[elementId]->qcOutChannel[ch]; + psyOutChannel[ch] = psyOutElement[elementId]->psyOutChannel[ch]; + + for(sfbGrp=0; sfbGrp < psyOutChannel[ch]->sfbCnt; sfbGrp+= psyOutChannel[ch]->sfbPerGroup) { + for (sfb=psyOutChannel[ch]->maxSfbPerGroup; sfb<psyOutChannel[ch]->sfbPerGroup; sfb++) { + peData->peChannelData[ch].sfbPe[sfbGrp+sfb] = 0; + } + } + } + + /* for MS allow hole in the channel with less energy */ + if ( nChannels==2 && psyOutChannel[0]->lastWindowSequence==psyOutChannel[1]->lastWindowSequence ) { + + for (sfb=0; sfb<psyOutChannel[0]->maxSfbPerGroup; sfb++) { + for(sfbGrp=0; sfbGrp < psyOutChannel[0]->sfbCnt; sfbGrp+=psyOutChannel[0]->sfbPerGroup) { + if (psyOutElement[elementId]->toolsInfo.msMask[sfbGrp+sfb]) { + FIXP_DBL EnergyLd_L = qcOutChannel[0]->sfbWeightedEnergyLdData[sfbGrp+sfb]; + FIXP_DBL EnergyLd_R = qcOutChannel[1]->sfbWeightedEnergyLdData[sfbGrp+sfb]; + + /* allow hole in side channel ? */ + if ( (ahFlag[elementId][1][sfbGrp+sfb] != NO_AH) && + (((FL2FXCONST_DBL(-0.02065512648f)>>1) + (qcOutChannel[0]->sfbMinSnrLdData[sfbGrp+sfb]>>1)) + > ((EnergyLd_R>>1) - (EnergyLd_L>>1))) ) + { + ahFlag[elementId][1][sfbGrp+sfb] = NO_AH; + qcOutChannel[1]->sfbThresholdLdData[sfbGrp+sfb] = FL2FXCONST_DBL(0.015625f) + EnergyLd_R; + actPe -= peData->peChannelData[1].sfbPe[sfbGrp+sfb]>>PE_CONSTPART_SHIFT; + } + /* allow hole in mid channel ? */ + else if ( (ahFlag[elementId][0][sfbGrp+sfb] != NO_AH) && + (((FL2FXCONST_DBL(-0.02065512648f)>>1) + (qcOutChannel[1]->sfbMinSnrLdData[sfbGrp+sfb]>>1)) + > ((EnergyLd_L>>1) - (EnergyLd_R>>1))) ) + { + ahFlag[elementId][0][sfbGrp+sfb] = NO_AH; + qcOutChannel[0]->sfbThresholdLdData[sfbGrp+sfb] = FL2FXCONST_DBL(0.015625f) + EnergyLd_L; + actPe -= peData->peChannelData[0].sfbPe[sfbGrp+sfb]>>PE_CONSTPART_SHIFT; + } /* if (ahFlag) */ + } /* if MS */ + } /* sfbGrp */ + if (actPe <= desiredPe) { + return; /* stop if enough has been saved */ + } + } /* sfb */ + } /* MS possible ? */ + + /* more holes necessary? subsequently erase bands + starting with low energies */ + INT startSfb[2]; + FIXP_DBL avgEnLD64,minEnLD64; + INT ahCnt; + FIXP_DBL ahCntLD64; + INT enIdx; + FIXP_DBL enLD64[4]; + FIXP_DBL avgEn; + + /* do not go below startSfb */ + for (ch=0; ch<nChannels; ch++) { + if (psyOutChannel[ch]->lastWindowSequence != SHORT_WINDOW) + startSfb[ch] = AdjThrStateElement[elementId]->ahParam.startSfbL; + else + startSfb[ch] = AdjThrStateElement[elementId]->ahParam.startSfbS; + } + + /* calc avg and min energies of bands that avoid holes */ + avgEn = FL2FXCONST_DBL(0.0f); + minEnLD64 = FL2FXCONST_DBL(0.0f); + ahCnt = 0; + + for (ch=0; ch<nChannels; ch++) { + + sfbGrp=0; + sfb=startSfb[ch]; + + do { + for (; sfb<psyOutChannel[ch]->maxSfbPerGroup; sfb++) { + if ((ahFlag[elementId][ch][sfbGrp+sfb]!=NO_AH) && + (qcOutChannel[ch]->sfbWeightedEnergyLdData[sfbGrp+sfb] > qcOutChannel[ch]->sfbThresholdLdData[sfbGrp+sfb])){ + minEnLD64 = fixMin(minEnLD64,qcOutChannel[ch]->sfbEnergyLdData[sfbGrp+sfb]); + avgEn += qcOutChannel[ch]->sfbEnergy[sfbGrp+sfb] >> 6; + ahCnt++; + } + } + + sfbGrp += psyOutChannel[ch]->sfbPerGroup; + sfb=0; + + } while (sfbGrp < psyOutChannel[ch]->sfbCnt); + } + + if ( (avgEn == FL2FXCONST_DBL(0.0f)) || (ahCnt == 0) ) { + avgEnLD64 = FL2FXCONST_DBL(0.0f); + } + else { + avgEnLD64 = CalcLdData(avgEn); + ahCntLD64 = CalcLdInt(ahCnt); + avgEnLD64 = avgEnLD64 + FL2FXCONST_DBL(0.09375f) - ahCntLD64; /* compensate shift with 6 */ + } + + /* calc some energy borders between minEn and avgEn */ + /* for (enIdx=0; enIdx<4; enIdx++) */ + /* en[enIdx] = minEn * (float)FDKpow(avgEn/(minEn+FLT_MIN), (2*enIdx+1)/7.0f); */ + enLD64[0] = minEnLD64 + fMult((avgEnLD64-minEnLD64),FL2FXCONST_DBL(0.14285714285f)); + enLD64[1] = minEnLD64 + fMult((avgEnLD64-minEnLD64),FL2FXCONST_DBL(0.42857142857f)); + enLD64[2] = minEnLD64 + fMult((avgEnLD64-minEnLD64),FL2FXCONST_DBL(0.71428571428f)); + enLD64[3] = minEnLD64 + (avgEnLD64-minEnLD64); + + for (enIdx=0; enIdx<4; enIdx++) { + INT noReduction = 1; + + INT maxSfbPerGroup[2]; + INT sfbCnt[2]; + INT sfbPerGroup[2]; + + for(ch=0; ch<cm->elInfo[elementId].nChannelsInEl; ch++) { + maxSfbPerGroup[ch] = psyOutElement[elementId]->psyOutChannel[ch]->maxSfbPerGroup-1; + sfbCnt[ch] = psyOutElement[elementId]->psyOutChannel[ch]->sfbCnt; + sfbPerGroup[ch] = psyOutElement[elementId]->psyOutChannel[ch]->sfbPerGroup; + } + + do { + + noReduction = 1; + + for(ch=0; ch<cm->elInfo[elementId].nChannelsInEl; ch++) { + + INT sfb, sfbGrp; + + /* start with lowest energy border at highest sfb */ + if (maxSfbPerGroup[ch]>=startSfb[ch]) { /* sfb in next channel */ + sfb = maxSfbPerGroup[ch]--; + noReduction = 0; + + for (sfbGrp = 0; sfbGrp < sfbCnt[ch]; sfbGrp += sfbPerGroup[ch]) { + /* sfb energy below border ? */ + if (ahFlag[elementId][ch][sfbGrp+sfb] != NO_AH && qcOutChannel[ch]->sfbEnergyLdData[sfbGrp+sfb] < enLD64[enIdx]) { + /* allow hole */ + ahFlag[elementId][ch][sfbGrp+sfb] = NO_AH; + qcOutChannel[ch]->sfbThresholdLdData[sfbGrp+sfb] = FL2FXCONST_DBL(0.015625f) + qcOutChannel[ch]->sfbWeightedEnergyLdData[sfbGrp+sfb]; + actPe -= peData->peChannelData[ch].sfbPe[sfbGrp+sfb]>>PE_CONSTPART_SHIFT; + } + } /* sfbGrp */ + + if (actPe <= desiredPe) { + return; /* stop if enough has been saved */ + } + } /* sfb > 0 */ + } /* ch loop */ + + } while( (noReduction == 0) && (actPe > desiredPe) ); + + if (actPe <= desiredPe) { + return; /* stop if enough has been saved */ + } + + } /* enIdx loop */ + + } /* EOF DSE-suppression */ + } /* EOF for all elements... */ + +} + +/* reset avoid hole flags from AH_ACTIVE to AH_INACTIVE */ +static void FDKaacEnc_resetAHFlags( UCHAR ahFlag[(2)][MAX_GROUPED_SFB], + const int nChannels, + PSY_OUT_CHANNEL *psyOutChannel[(2)]) +{ + int ch, sfb, sfbGrp; + + for(ch=0; ch<nChannels; ch++) { + for (sfbGrp=0; sfbGrp < psyOutChannel[ch]->sfbCnt; sfbGrp+=psyOutChannel[ch]->sfbPerGroup) { + for (sfb=0; sfb<psyOutChannel[ch]->maxSfbPerGroup; sfb++) { + if ( ahFlag[ch][sfbGrp+sfb] == AH_ACTIVE) { + ahFlag[ch][sfbGrp+sfb] = AH_INACTIVE; + } + } + } + } +} + + +static FIXP_DBL CalcRedValPower(FIXP_DBL num, + FIXP_DBL denum, + INT* scaling ) +{ + FIXP_DBL value = FL2FXCONST_DBL(0.f); + + if (num>=FL2FXCONST_DBL(0.f)) { + value = fDivNorm( num, denum, scaling); + } + else { + value = -fDivNorm( -num, denum, scaling); + } + value = f2Pow(value, *scaling, scaling); + *scaling = DFRACT_BITS-1-*scaling; + + return value; +} + + +/***************************************************************************** +functionname: FDKaacEnc_adaptThresholdsToPe +description: two guesses for the reduction value and one final correction of the thresholds +*****************************************************************************/ +static void FDKaacEnc_adaptThresholdsToPe(CHANNEL_MAPPING* cm, + ATS_ELEMENT* AdjThrStateElement[(6)], + QC_OUT_ELEMENT* qcElement[(6)], + PSY_OUT_ELEMENT* psyOutElement[(6)], + const INT desiredPe, + const INT processElements, + const INT elementOffset) +{ + FIXP_DBL redValue[(6)]; + SCHAR redValScaling[(6)]; + UCHAR pAhFlag[(6)][(2)][MAX_GROUPED_SFB]; + FIXP_DBL pThrExp[(6)][(2)][MAX_GROUPED_SFB]; + int iter; + + INT constPartGlobal, noRedPeGlobal, nActiveLinesGlobal, redPeGlobal; + constPartGlobal = noRedPeGlobal = nActiveLinesGlobal = redPeGlobal = 0; + + int elementId; + + int nElements = elementOffset+processElements; + if(nElements > cm->nElements) { + nElements = cm->nElements; + } + + /* ------------------------------------------------------- */ + /* Part I: Initialize data structures and variables... */ + /* ------------------------------------------------------- */ + for (elementId = elementOffset;elementId<nElements;elementId++) { + if (cm->elInfo[elementId].elType != ID_DSE) { + + INT nChannels = cm->elInfo[elementId].nChannelsInEl; + PE_DATA *peData = &qcElement[elementId]->peData; + + /* thresholds to the power of redExp */ + FDKaacEnc_calcThreshExp(pThrExp[elementId], qcElement[elementId]->qcOutChannel, psyOutElement[elementId]->psyOutChannel, nChannels); + + /* lower the minSnr requirements for low energies compared to the average + energy in this frame */ + FDKaacEnc_adaptMinSnr(qcElement[elementId]->qcOutChannel, psyOutElement[elementId]->psyOutChannel, &AdjThrStateElement[elementId]->minSnrAdaptParam, nChannels); + + /* init ahFlag (0: no ah necessary, 1: ah possible, 2: ah active */ + FDKaacEnc_initAvoidHoleFlag(qcElement[elementId]->qcOutChannel, psyOutElement[elementId]->psyOutChannel, pAhFlag[elementId], &psyOutElement[elementId]->toolsInfo, nChannels, peData, &AdjThrStateElement[elementId]->ahParam); + + /* sum up */ + constPartGlobal += peData->constPart; + noRedPeGlobal += peData->pe; + nActiveLinesGlobal += fixMax((INT)peData->nActiveLines, 1); + + } /* EOF DSE-suppression */ + } /* EOF for all elements... */ + + /* ----------------------------------------------------------------------- */ + /* Part II: Calculate bit consumption of initial bit constraints setup */ + /* ----------------------------------------------------------------------- */ + for (elementId = elementOffset;elementId<nElements;elementId++) { + if (cm->elInfo[elementId].elType != ID_DSE) { + /* + redVal = ( 2 ^ ( (constPartGlobal-desiredPe) / (invRedExp*nActiveLinesGlobal) ) + - 2 ^ ( (constPartGlobal-noRedPeGlobal) / (invRedExp*nActiveLinesGlobal) ) ) + */ + + + INT nChannels = cm->elInfo[elementId].nChannelsInEl; + PE_DATA *peData = &qcElement[elementId]->peData; + + /* first guess of reduction value */ + int scale0=0, scale1=0; + FIXP_DBL tmp0 = CalcRedValPower( constPartGlobal-desiredPe, 4*nActiveLinesGlobal, &scale0 ); + FIXP_DBL tmp1 = CalcRedValPower( constPartGlobal-noRedPeGlobal, 4*nActiveLinesGlobal, &scale1 ); + + int scalMin = FDKmin(scale0, scale1)-1; + + redValue[elementId] = scaleValue(tmp0,(scalMin-scale0)) - scaleValue(tmp1,(scalMin-scale1)); + redValScaling[elementId] = scalMin; + + /* reduce thresholds */ + FDKaacEnc_reduceThresholdsCBR(qcElement[elementId]->qcOutChannel, psyOutElement[elementId]->psyOutChannel, pAhFlag[elementId], pThrExp[elementId], nChannels, redValue[elementId], redValScaling[elementId]); + + /* pe after first guess */ + FDKaacEnc_calcPe(psyOutElement[elementId]->psyOutChannel, qcElement[elementId]->qcOutChannel, peData, nChannels); + + redPeGlobal += peData->pe; + } /* EOF DSE-suppression */ + } /* EOF for all elements... */ + + /* -------------------------------------------------- */ + /* Part III: Iterate until bit constraints are met */ + /* -------------------------------------------------- */ + iter = 0; + while ((fixp_abs(redPeGlobal - desiredPe) > fMultI(FL2FXCONST_DBL(0.05f),desiredPe)) && (iter < 1)) { + + INT desiredPeNoAHGlobal; + INT redPeNoAHGlobal = 0; + INT constPartNoAHGlobal = 0; + INT nActiveLinesNoAHGlobal = 0; + + for (elementId = elementOffset;elementId<nElements;elementId++) { + if (cm->elInfo[elementId].elType != ID_DSE) { + + INT redPeNoAH, constPartNoAH, nActiveLinesNoAH; + INT nChannels = cm->elInfo[elementId].nChannelsInEl; + PE_DATA *peData = &qcElement[elementId]->peData; + + /* pe for bands where avoid hole is inactive */ + FDKaacEnc_FDKaacEnc_calcPeNoAH(&redPeNoAH, &constPartNoAH, &nActiveLinesNoAH, + peData, pAhFlag[elementId], psyOutElement[elementId]->psyOutChannel, nChannels); + + redPeNoAHGlobal += redPeNoAH; + constPartNoAHGlobal += constPartNoAH; + nActiveLinesNoAHGlobal += nActiveLinesNoAH; + } /* EOF DSE-suppression */ + } /* EOF for all elements... */ + + /* Calculate new redVal ... */ + if(desiredPe < redPeGlobal) { + + /* new desired pe without bands where avoid hole is active */ + desiredPeNoAHGlobal = desiredPe - (redPeGlobal - redPeNoAHGlobal); + + /* limit desiredPeNoAH to positive values, as the PE can not become negative */ + desiredPeNoAHGlobal = FDKmax(0,desiredPeNoAHGlobal); + + /* second guess (only if there are bands left where avoid hole is inactive)*/ + if (nActiveLinesNoAHGlobal > 0) { + for (elementId = elementOffset;elementId<nElements;elementId++) { + if (cm->elInfo[elementId].elType != ID_DSE) { + /* + redVal += ( 2 ^ ( (constPartNoAHGlobal-desiredPeNoAHGlobal) / (invRedExp*nActiveLinesNoAHGlobal) ) + - 2 ^ ( (constPartNoAHGlobal-redPeNoAHGlobal) / (invRedExp*nActiveLinesNoAHGlobal) ) ) + */ + int scale0 = 0; + int scale1 = 0; + + FIXP_DBL tmp0 = CalcRedValPower( constPartNoAHGlobal-desiredPeNoAHGlobal, 4*nActiveLinesNoAHGlobal, &scale0 ); + FIXP_DBL tmp1 = CalcRedValPower( constPartNoAHGlobal-redPeNoAHGlobal, 4*nActiveLinesNoAHGlobal, &scale1 ); + + int scalMin = FDKmin(scale0, scale1)-1; + + tmp0 = scaleValue(tmp0,(scalMin-scale0)) - scaleValue(tmp1,(scalMin-scale1)); + scale0 = scalMin; + + /* old reduction value */ + tmp1 = redValue[elementId]; + scale1 = redValScaling[elementId]; + + scalMin = fixMin(scale0,scale1)-1; + + /* sum up old and new reduction value */ + redValue[elementId] = scaleValue(tmp0,(scalMin-scale0)) + scaleValue(tmp1,(scalMin-scale1)); + redValScaling[elementId] = scalMin; + + } /* EOF DSE-suppression */ + } /* EOF for all elements... */ + } /* nActiveLinesNoAHGlobal > 0 */ + } + else { + /* desiredPe >= redPeGlobal */ + for (elementId = elementOffset;elementId<nElements;elementId++) { + if (cm->elInfo[elementId].elType != ID_DSE) { + + INT redVal_scale = 0; + FIXP_DBL tmp = fDivNorm((FIXP_DBL)redPeGlobal, (FIXP_DBL)desiredPe, &redVal_scale); + + /* redVal *= redPeGlobal/desiredPe; */ + redValue[elementId] = fMult(redValue[elementId], tmp); + redValScaling[elementId] -= redVal_scale; + + FDKaacEnc_resetAHFlags(pAhFlag[elementId], cm->elInfo[elementId].nChannelsInEl, psyOutElement[elementId]->psyOutChannel); + } /* EOF DSE-suppression */ + } /* EOF for all elements... */ + } + + redPeGlobal = 0; + /* Calculate new redVal's PE... */ + for (elementId = elementOffset;elementId<nElements;elementId++) { + if (cm->elInfo[elementId].elType != ID_DSE) { + + INT nChannels = cm->elInfo[elementId].nChannelsInEl; + PE_DATA *peData = &qcElement[elementId]->peData; + + /* reduce thresholds */ + FDKaacEnc_reduceThresholdsCBR(qcElement[elementId]->qcOutChannel, psyOutElement[elementId]->psyOutChannel, pAhFlag[elementId], pThrExp[elementId], nChannels, redValue[elementId], redValScaling[elementId]); + + /* pe after second guess */ + FDKaacEnc_calcPe(psyOutElement[elementId]->psyOutChannel, qcElement[elementId]->qcOutChannel, peData, nChannels); + redPeGlobal += peData->pe; + + } /* EOF DSE-suppression */ + } /* EOF for all elements... */ + + iter++; + } /* EOF while */ + + + /* ------------------------------------------------------- */ + /* Part IV: if still required, further reduce constraints */ + /* ------------------------------------------------------- */ + /* 1.0* 1.15* 1.20* + * desiredPe desiredPe desiredPe + * | | | + * ...XXXXXXXXXXXXXXXXXXXXXXXXXXX| | + * | | |XXXXXXXXXXX... + * | |XXXXXXXXXXX| + * --- A --- | --- B --- | --- C --- + * + * (X): redPeGlobal + * (A): FDKaacEnc_correctThresh() + * (B): FDKaacEnc_allowMoreHoles() + * (C): FDKaacEnc_reduceMinSnr() + */ + + /* correct thresholds to get closer to the desired pe */ + if ( redPeGlobal > desiredPe ) { + FDKaacEnc_correctThresh(cm, qcElement, psyOutElement, pAhFlag, pThrExp, redValue, redValScaling, + desiredPe - redPeGlobal, processElements, elementOffset); + + /* update PE */ + redPeGlobal = 0; + for(elementId=elementOffset;elementId<nElements;elementId++) { + if (cm->elInfo[elementId].elType != ID_DSE) { + + INT nChannels = cm->elInfo[elementId].nChannelsInEl; + PE_DATA *peData = &qcElement[elementId]->peData; + + /* pe after correctThresh */ + FDKaacEnc_calcPe(psyOutElement[elementId]->psyOutChannel, qcElement[elementId]->qcOutChannel, peData, nChannels); + redPeGlobal += peData->pe; + + } /* EOF DSE-suppression */ + } /* EOF for all elements... */ + } + + if ( redPeGlobal > desiredPe ) { + /* reduce pe by reducing minSnr requirements */ + FDKaacEnc_reduceMinSnr(cm, qcElement, psyOutElement, pAhFlag, + (fMultI(FL2FXCONST_DBL(0.15f),desiredPe) + desiredPe), + &redPeGlobal, processElements, elementOffset); + + /* reduce pe by allowing additional spectral holes */ + FDKaacEnc_allowMoreHoles(cm, qcElement, psyOutElement, AdjThrStateElement, pAhFlag, + desiredPe, redPeGlobal, processElements, elementOffset); + } + +} + + +/* similar to FDKaacEnc_adaptThresholdsToPe(), for VBR-mode */ +void FDKaacEnc_AdaptThresholdsVBR(QC_OUT_CHANNEL* qcOutChannel[(2)], + PSY_OUT_CHANNEL* psyOutChannel[(2)], + ATS_ELEMENT* AdjThrStateElement, + struct TOOLSINFO *toolsInfo, + PE_DATA *peData, + const INT nChannels) +{ + UCHAR pAhFlag[(2)][MAX_GROUPED_SFB]; + FIXP_DBL pThrExp[(2)][MAX_GROUPED_SFB]; + + /* thresholds to the power of redExp */ + FDKaacEnc_calcThreshExp(pThrExp, qcOutChannel, psyOutChannel, nChannels); + + /* lower the minSnr requirements for low energies compared to the average + energy in this frame */ + FDKaacEnc_adaptMinSnr(qcOutChannel, psyOutChannel, &AdjThrStateElement->minSnrAdaptParam, nChannels); + + /* init ahFlag (0: no ah necessary, 1: ah possible, 2: ah active */ + FDKaacEnc_initAvoidHoleFlag(qcOutChannel, psyOutChannel, pAhFlag, toolsInfo, + nChannels, peData, &AdjThrStateElement->ahParam); + + /* reduce thresholds */ + FDKaacEnc_reduceThresholdsVBR(qcOutChannel, psyOutChannel, pAhFlag, pThrExp, nChannels, + AdjThrStateElement->vbrQualFactor, + &AdjThrStateElement->chaosMeasureOld); + +} + + +/***************************************************************************** + + functionname: FDKaacEnc_calcBitSave + description: Calculates percentage of bit save, see figure below + returns: + input: parameters and bitres-fullness + output: percentage of bit save + +*****************************************************************************/ +/* + bitsave + maxBitSave(%)| clipLow + |---\ + | \ + | \ + | \ + | \ + |--------\--------------> bitres + | \ + minBitSave(%)| \------------ + clipHigh maxBitres +*/ +static FIXP_DBL FDKaacEnc_calcBitSave(FIXP_DBL fillLevel, + const FIXP_DBL clipLow, + const FIXP_DBL clipHigh, + const FIXP_DBL minBitSave, + const FIXP_DBL maxBitSave, + const FIXP_DBL bitsave_slope) +{ + FIXP_DBL bitsave; + + fillLevel = fixMax(fillLevel, clipLow); + fillLevel = fixMin(fillLevel, clipHigh); + + bitsave = maxBitSave - fMult((fillLevel-clipLow), bitsave_slope); + + return (bitsave); +} + +/***************************************************************************** + + functionname: FDKaacEnc_calcBitSpend + description: Calculates percentage of bit spend, see figure below + returns: + input: parameters and bitres-fullness + output: percentage of bit spend + +*****************************************************************************/ +/* + bitspend clipHigh + maxBitSpend(%)| /-----------maxBitres + | / + | / + | / + | / + | / + |----/-----------------> bitres + | / + minBitSpend(%)|--/ + clipLow +*/ +static FIXP_DBL FDKaacEnc_calcBitSpend(FIXP_DBL fillLevel, + const FIXP_DBL clipLow, + const FIXP_DBL clipHigh, + const FIXP_DBL minBitSpend, + const FIXP_DBL maxBitSpend, + const FIXP_DBL bitspend_slope) +{ + FIXP_DBL bitspend; + + fillLevel = fixMax(fillLevel, clipLow); + fillLevel = fixMin(fillLevel, clipHigh); + + bitspend = minBitSpend + fMult(fillLevel-clipLow, bitspend_slope); + + return (bitspend); +} + + +/***************************************************************************** + + functionname: FDKaacEnc_adjustPeMinMax() + description: adjusts peMin and peMax parameters over time + returns: + input: current pe, peMin, peMax, bitres size + output: adjusted peMin/peMax + +*****************************************************************************/ +static void FDKaacEnc_adjustPeMinMax(const INT currPe, + INT *peMin, + INT *peMax) +{ + FIXP_DBL minFacHi = FL2FXCONST_DBL(0.3f), maxFacHi = (FIXP_DBL)MAXVAL_DBL, minFacLo = FL2FXCONST_DBL(0.14f), maxFacLo = FL2FXCONST_DBL(0.07f); + INT diff; + + INT minDiff_fix = fMultI(FL2FXCONST_DBL(0.1666666667f), currPe); + + if (currPe > *peMax) + { + diff = (currPe-*peMax) ; + *peMin += fMultI(minFacHi,diff); + *peMax += fMultI(maxFacHi,diff); + } + else if (currPe < *peMin) + { + diff = (*peMin-currPe) ; + *peMin -= fMultI(minFacLo,diff); + *peMax -= fMultI(maxFacLo,diff); + } + else + { + *peMin += fMultI(minFacHi, (currPe - *peMin)); + *peMax -= fMultI(maxFacLo, (*peMax - currPe)); + } + + if ((*peMax - *peMin) < minDiff_fix) + { + INT peMax_fix = *peMax, peMin_fix = *peMin; + FIXP_DBL partLo_fix, partHi_fix; + + partLo_fix = (FIXP_DBL)fixMax(0, currPe - peMin_fix); + partHi_fix = (FIXP_DBL)fixMax(0, peMax_fix - currPe); + + peMax_fix = (INT)(currPe + fMultI(fDivNorm(partHi_fix, (partLo_fix+partHi_fix)), minDiff_fix)); + peMin_fix = (INT)(currPe - fMultI(fDivNorm(partLo_fix, (partLo_fix+partHi_fix)), minDiff_fix)); + peMin_fix = fixMax(0, peMin_fix); + + *peMax = peMax_fix; + *peMin = peMin_fix; + } +} + + + +/***************************************************************************** + + functionname: BitresCalcBitFac + description: calculates factor of spending bits for one frame + 1.0 : take all frame dynpart bits + >1.0 : take all frame dynpart bits + bitres + <1.0 : put bits in bitreservoir + returns: BitFac + input: bitres-fullness, pe, blockType, parameter-settings + output: + +*****************************************************************************/ +/* + bitfac(%) pemax + bitspend(%) | /-----------maxBitres + | / + | / + | / + | / + | / + |----/-----------------> pe + | / + bitsave(%) |--/ + pemin +*/ + +static FIXP_DBL FDKaacEnc_bitresCalcBitFac(const INT bitresBits, + const INT maxBitresBits, + const INT pe, + const INT lastWindowSequence, + const INT avgBits, + const FIXP_DBL maxBitFac, + ADJ_THR_STATE *AdjThr, + ATS_ELEMENT *adjThrChan) +{ + BRES_PARAM *bresParam; + INT pex; + + INT qmin, qbr, qbres, qmbr; + FIXP_DBL bitSave, bitSpend; + + FIXP_DBL bitresFac_fix, tmp_cst, tmp_fix; + FIXP_DBL pe_pers, bits_ratio, maxBrVal; + FIXP_DBL bitsave_slope, bitspend_slope, maxBitFac_tmp; + FIXP_DBL fillLevel_fix = (FIXP_DBL)0x7fffffff; + FIXP_DBL UNITY = (FIXP_DBL)0x7fffffff; + FIXP_DBL POINT7 = (FIXP_DBL)0x5999999A; + + if (maxBitresBits > bitresBits) { + fillLevel_fix = fDivNorm(bitresBits, maxBitresBits); + } + + if (lastWindowSequence != SHORT_WINDOW) + { + bresParam = &(AdjThr->bresParamLong); + bitsave_slope = (FIXP_DBL)0x3BBBBBBC; + bitspend_slope = (FIXP_DBL)0x55555555; + } + else + { + bresParam = &(AdjThr->bresParamShort); + bitsave_slope = (FIXP_DBL)0x2E8BA2E9; + bitspend_slope = (FIXP_DBL)0x7fffffff; + } + + pex = fixMax(pe, adjThrChan->peMin); + pex = fixMin(pex, adjThrChan->peMax); + + bitSave = FDKaacEnc_calcBitSave(fillLevel_fix, + bresParam->clipSaveLow, bresParam->clipSaveHigh, + bresParam->minBitSave, bresParam->maxBitSave, bitsave_slope); + + bitSpend = FDKaacEnc_calcBitSpend(fillLevel_fix, + bresParam->clipSpendLow, bresParam->clipSpendHigh, + bresParam->minBitSpend, bresParam->maxBitSpend, bitspend_slope); + + pe_pers = fDivNorm(pex - adjThrChan->peMin, adjThrChan->peMax - adjThrChan->peMin); + tmp_fix = fMult(((FIXP_DBL)bitSpend + (FIXP_DBL)bitSave), pe_pers); + bitresFac_fix = (UNITY>>1) - ((FIXP_DBL)bitSave>>1) + (tmp_fix>>1); qbres = (DFRACT_BITS-2); + + /* (float)bitresBits/(float)avgBits */ + bits_ratio = fDivNorm(bitresBits, avgBits, &qbr); + qbr = DFRACT_BITS-1-qbr; + + /* Add 0.7 in q31 to bits_ratio in qbr */ + /* 0.7f + (float)bitresBits/(float)avgBits */ + qmin = fixMin(qbr, (DFRACT_BITS-1)); + bits_ratio = bits_ratio >> (qbr - qmin); + tmp_cst = POINT7 >> ((DFRACT_BITS-1) - qmin); + maxBrVal = (bits_ratio>>1) + (tmp_cst>>1); qmbr = qmin - 1; + + /* bitresFac_fix = fixMin(bitresFac_fix, 0.7 + bitresBits/avgBits); */ + bitresFac_fix = bitresFac_fix >> (qbres - qmbr); qbres = qmbr; + bitresFac_fix = fixMin(bitresFac_fix, maxBrVal); + + /* Compare with maxBitFac */ + qmin = fixMin(Q_BITFAC, qbres); + bitresFac_fix = bitresFac_fix >> (qbres - qmin); + maxBitFac_tmp = maxBitFac >> (Q_BITFAC - qmin); + if(maxBitFac_tmp < bitresFac_fix) + { + bitresFac_fix = maxBitFac; + } + else + { + if(qmin < Q_BITFAC) + { + bitresFac_fix = bitresFac_fix << (Q_BITFAC-qmin); + } + else + { + bitresFac_fix = bitresFac_fix >> (qmin-Q_BITFAC); + } + } + + FDKaacEnc_adjustPeMinMax(pe, &adjThrChan->peMin, &adjThrChan->peMax); + + return bitresFac_fix; +} + + +/***************************************************************************** +functionname: FDKaacEnc_AdjThrNew +description: allocate ADJ_THR_STATE +*****************************************************************************/ +INT FDKaacEnc_AdjThrNew(ADJ_THR_STATE** phAdjThr, + INT nElements) +{ + INT err = 0; + INT i; + ADJ_THR_STATE* hAdjThr = GetRam_aacEnc_AdjustThreshold(); + if (hAdjThr==NULL) { + err = 1; + goto bail; + } + + for (i=0; i<nElements; i++) { + hAdjThr->adjThrStateElem[i] = GetRam_aacEnc_AdjThrStateElement(i); + if (hAdjThr->adjThrStateElem[i]==NULL) { + err = 1; + goto bail; + } + } + +bail: + *phAdjThr = hAdjThr; + return err; +} + + +/***************************************************************************** +functionname: FDKaacEnc_AdjThrInit +description: initialize ADJ_THR_STATE +*****************************************************************************/ +void FDKaacEnc_AdjThrInit(ADJ_THR_STATE *hAdjThr, + const INT meanPe, + ELEMENT_BITS *elBits[(6)], + INT nElements, + FIXP_DBL vbrQualFactor) +{ + INT i; + + FIXP_DBL POINT8 = FL2FXCONST_DBL(0.8f); + FIXP_DBL POINT6 = FL2FXCONST_DBL(0.6f); + + /* common for all elements: */ + /* parameters for bitres control */ + hAdjThr->bresParamLong.clipSaveLow = (FIXP_DBL)0x1999999a; /* FL2FXCONST_DBL(0.2f); */ + hAdjThr->bresParamLong.clipSaveHigh = (FIXP_DBL)0x7999999a; /* FL2FXCONST_DBL(0.95f); */ + hAdjThr->bresParamLong.minBitSave = (FIXP_DBL)0xf999999a; /* FL2FXCONST_DBL(-0.05f); */ + hAdjThr->bresParamLong.maxBitSave = (FIXP_DBL)0x26666666; /* FL2FXCONST_DBL(0.3f); */ + hAdjThr->bresParamLong.clipSpendLow = (FIXP_DBL)0x1999999a; /* FL2FXCONST_DBL(0.2f); */ + hAdjThr->bresParamLong.clipSpendHigh = (FIXP_DBL)0x7999999a; /* FL2FXCONST_DBL(0.95f); */ + hAdjThr->bresParamLong.minBitSpend = (FIXP_DBL)0xf3333333; /* FL2FXCONST_DBL(-0.10f); */ + hAdjThr->bresParamLong.maxBitSpend = (FIXP_DBL)0x33333333; /* FL2FXCONST_DBL(0.4f); */ + + hAdjThr->bresParamShort.clipSaveLow = (FIXP_DBL)0x199999a0; /* FL2FXCONST_DBL(0.2f); */ + hAdjThr->bresParamShort.clipSaveHigh = (FIXP_DBL)0x5fffffff; /* FL2FXCONST_DBL(0.75f); */ + hAdjThr->bresParamShort.minBitSave = (FIXP_DBL)0x00000000; /* FL2FXCONST_DBL(0.0f); */ + hAdjThr->bresParamShort.maxBitSave = (FIXP_DBL)0x199999a0; /* FL2FXCONST_DBL(0.2f); */ + hAdjThr->bresParamShort.clipSpendLow = (FIXP_DBL)0x199999a0; /* FL2FXCONST_DBL(0.2f); */ + hAdjThr->bresParamShort.clipSpendHigh = (FIXP_DBL)0x5fffffff; /* FL2FXCONST_DBL(0.75f); */ + hAdjThr->bresParamShort.minBitSpend = (FIXP_DBL)0xf9999998; /* FL2FXCONST_DBL(-0.05f); */ + hAdjThr->bresParamShort.maxBitSpend = (FIXP_DBL)0x40000000; /* FL2FXCONST_DBL(0.5f); */ + + /* specific for each element: */ + for (i=0; i<nElements; i++) { + ATS_ELEMENT* atsElem = hAdjThr->adjThrStateElem[i]; + MINSNR_ADAPT_PARAM *msaParam = &atsElem->minSnrAdaptParam; + INT chBitrate = elBits[i]->chBitrateEl; + + /* parameters for bitres control */ + atsElem->peMin = fMultI(POINT8, meanPe) >> 1; + atsElem->peMax = fMultI(POINT6, meanPe); + + /* for use in FDKaacEnc_reduceThresholdsVBR */ + atsElem->chaosMeasureOld = FL2FXCONST_DBL(0.3f); + + /* additional pe offset to correct pe2bits for low bitrates */ + atsElem->peOffset = 0; + + /* vbr initialisation */ + atsElem->vbrQualFactor = vbrQualFactor; + if (chBitrate < 32000) + { + atsElem->peOffset = fixMax(50, 100-fMultI((FIXP_DBL)0x666667, chBitrate)); + } + + /* avoid hole parameters */ + if (chBitrate > 20000) { + atsElem->ahParam.modifyMinSnr = TRUE; + atsElem->ahParam.startSfbL = 15; + atsElem->ahParam.startSfbS = 3; + } + else { + atsElem->ahParam.modifyMinSnr = FALSE; + atsElem->ahParam.startSfbL = 0; + atsElem->ahParam.startSfbS = 0; + } + + /* minSnr adaptation */ + msaParam->maxRed = FL2FXCONST_DBL(0.00390625f); /* 0.25f/64.0f */ + /* start adaptation of minSnr for avgEn/sfbEn > startRatio */ + msaParam->startRatio = FL2FXCONST_DBL(0.05190512648f); /* ld64(10.0f) */ + /* maximum minSnr reduction to minSnr^maxRed is reached for + avgEn/sfbEn >= maxRatio */ + /* msaParam->maxRatio = 1000.0f; */ + /*msaParam->redRatioFac = ((float)1.0f - msaParam->maxRed) / ((float)10.0f*log10(msaParam->startRatio/msaParam->maxRatio)/log10(2.0f)*(float)0.3010299956f);*/ + msaParam->redRatioFac = FL2FXCONST_DBL(-0.375f); /* -0.0375f * 10.0f */ + /*msaParam->redOffs = (float)1.0f - msaParam->redRatioFac * (float)10.0f * log10(msaParam->startRatio)/log10(2.0f) * (float)0.3010299956f;*/ + msaParam->redOffs = FL2FXCONST_DBL(0.021484375); /* 1.375f/64.0f */ + + /* init pe correction */ + atsElem->peCorrectionFactor_m = FL2FXCONST_DBL(0.5f); /* 1.0 */ + atsElem->peCorrectionFactor_e = 1; + + atsElem->dynBitsLast = -1; + atsElem->peLast = 0; + + /* init bits to pe factor */ + atsElem->bits2PeFactor_m = FL2FXCONST_DBL(1.18f/(1<<(1))); + atsElem->bits2PeFactor_e = 1; + } +} + + +/***************************************************************************** + functionname: FDKaacEnc_FDKaacEnc_calcPeCorrection + description: calc desired pe +*****************************************************************************/ +static void FDKaacEnc_FDKaacEnc_calcPeCorrection( + FIXP_DBL *const correctionFac_m, + INT *const correctionFac_e, + const INT peAct, + const INT peLast, + const INT bitsLast, + const FIXP_DBL bits2PeFactor_m, + const INT bits2PeFactor_e + ) +{ + if ( (bitsLast > 0) && (peAct < 1.5f*peLast) && (peAct > 0.7f*peLast) && + (FDKaacEnc_bits2pe2(bitsLast, fMult(FL2FXCONST_DBL(1.2f/2.f), bits2PeFactor_m), bits2PeFactor_e+1) > peLast) && + (FDKaacEnc_bits2pe2(bitsLast, fMult(FL2FXCONST_DBL(0.65f), bits2PeFactor_m), bits2PeFactor_e ) < peLast) ) + { + FIXP_DBL corrFac = *correctionFac_m; + + int scaling = 0; + FIXP_DBL denum = (FIXP_DBL)FDKaacEnc_bits2pe2(bitsLast, bits2PeFactor_m, bits2PeFactor_e); + FIXP_DBL newFac = fDivNorm((FIXP_DBL)peLast, denum, &scaling); + + /* dead zone, newFac and corrFac are scaled by 0.5 */ + if ((FIXP_DBL)peLast <= denum) { /* ratio <= 1.f */ + newFac = fixMax(scaleValue(fixMin( fMult(FL2FXCONST_DBL(1.1f/2.f), newFac), scaleValue(FL2FXCONST_DBL( 1.f/2.f), -scaling)), scaling), FL2FXCONST_DBL(0.85f/2.f) ); + } + else { /* ratio < 1.f */ + newFac = fixMax( fixMin( scaleValue(fMult(FL2FXCONST_DBL(0.9f/2.f), newFac), scaling), FL2FXCONST_DBL(1.15f/2.f) ), FL2FXCONST_DBL( 1.f/2.f) ); + } + + if ( ((newFac > FL2FXCONST_DBL(1.f/2.f)) && (corrFac < FL2FXCONST_DBL(1.f/2.f))) + || ((newFac < FL2FXCONST_DBL(1.f/2.f)) && (corrFac > FL2FXCONST_DBL(1.f/2.f)))) + { + corrFac = FL2FXCONST_DBL(1.f/2.f); + } + + /* faster adaptation towards 1.0, slower in the other direction */ + if ( (corrFac < FL2FXCONST_DBL(1.f/2.f) && newFac < corrFac) + || (corrFac > FL2FXCONST_DBL(1.f/2.f) && newFac > corrFac) ) + { + corrFac = fMult(FL2FXCONST_DBL(0.85f), corrFac) + fMult(FL2FXCONST_DBL(0.15f), newFac); + } + else { + corrFac = fMult(FL2FXCONST_DBL(0.7f), corrFac) + fMult(FL2FXCONST_DBL(0.3f), newFac); + } + + corrFac = fixMax( fixMin( corrFac, FL2FXCONST_DBL(1.15f/2.f) ), FL2FXCONST_DBL(0.85/2.f) ); + + *correctionFac_m = corrFac; + *correctionFac_e = 1; + } + else { + *correctionFac_m = FL2FXCONST_DBL(1.f/2.f); + *correctionFac_e = 1; + } +} + + +void FDKaacEnc_DistributeBits(ADJ_THR_STATE *adjThrState, + ATS_ELEMENT *AdjThrStateElement, + PSY_OUT_CHANNEL *psyOutChannel[(2)], + PE_DATA *peData, + INT *grantedPe, + INT *grantedPeCorr, + const INT nChannels, + const INT commonWindow, + const INT grantedDynBits, + const INT bitresBits, + const INT maxBitresBits, + const FIXP_DBL maxBitFac, + const INT bitDistributenMode) +{ + FIXP_DBL bitFactor; + INT noRedPe = peData->pe; + + /* prefer short windows for calculation of bitFactor */ + INT curWindowSequence = LONG_WINDOW; + if (nChannels==2) { + if ((psyOutChannel[0]->lastWindowSequence == SHORT_WINDOW) || + (psyOutChannel[1]->lastWindowSequence == SHORT_WINDOW)) { + curWindowSequence = SHORT_WINDOW; + } + } + else { + curWindowSequence = psyOutChannel[0]->lastWindowSequence; + } + + if (grantedDynBits >= 1) { + if (bitDistributenMode!=0) { + *grantedPe = FDKaacEnc_bits2pe2(grantedDynBits, AdjThrStateElement->bits2PeFactor_m, AdjThrStateElement->bits2PeFactor_e); + } + else + { + /* factor dependend on current fill level and pe */ + bitFactor = FDKaacEnc_bitresCalcBitFac(bitresBits, maxBitresBits, noRedPe, + curWindowSequence, grantedDynBits, maxBitFac, + adjThrState, + AdjThrStateElement + ); + + /* desired pe for actual frame */ + /* Worst case max of grantedDynBits is = 1024 * 5.27 * 2 */ + *grantedPe = FDKaacEnc_bits2pe2(grantedDynBits, + fMult(bitFactor, AdjThrStateElement->bits2PeFactor_m), AdjThrStateElement->bits2PeFactor_e+(DFRACT_BITS-1-Q_BITFAC) + ); + } + } + else { + *grantedPe = 0; /* prevent divsion by 0 */ + } + + /* correction of pe value */ + { + FDKaacEnc_FDKaacEnc_calcPeCorrection( + &AdjThrStateElement->peCorrectionFactor_m, + &AdjThrStateElement->peCorrectionFactor_e, + fixMin(*grantedPe, noRedPe), + AdjThrStateElement->peLast, + AdjThrStateElement->dynBitsLast, + AdjThrStateElement->bits2PeFactor_m, + AdjThrStateElement->bits2PeFactor_e + ); + } + + *grantedPeCorr = (INT)(fMult((FIXP_DBL)(*grantedPe<<Q_AVGBITS), AdjThrStateElement->peCorrectionFactor_m) >> (Q_AVGBITS-AdjThrStateElement->peCorrectionFactor_e)); + + /* update last pe */ + AdjThrStateElement->peLast = *grantedPe; + AdjThrStateElement->dynBitsLast = -1; + +} + +/***************************************************************************** +functionname: FDKaacEnc_AdjustThresholds +description: adjust thresholds +*****************************************************************************/ +void FDKaacEnc_AdjustThresholds(ATS_ELEMENT* AdjThrStateElement[(6)], + QC_OUT_ELEMENT* qcElement[(6)], + QC_OUT* qcOut, + PSY_OUT_ELEMENT* psyOutElement[(6)], + INT CBRbitrateMode, + CHANNEL_MAPPING* cm) +{ + int i; + if (CBRbitrateMode) + { + /* In case, no bits must be shifted between different elements, */ + /* an element-wise execution of the pe-dependent threshold- */ + /* adaption becomes necessary... */ + for (i=0; i<cm->nElements; i++) + { + ELEMENT_INFO elInfo = cm->elInfo[i]; + + if ((elInfo.elType == ID_SCE) || (elInfo.elType == ID_CPE) || + (elInfo.elType == ID_LFE)) + { + /* qcElement[i]->grantedPe = 2000; */ /* Use this only for debugging */ + //if (totalGrantedPeCorr < totalNoRedPe) { + if (qcElement[i]->grantedPe < qcElement[i]->peData.pe) + { + /* calc threshold necessary for desired pe */ + FDKaacEnc_adaptThresholdsToPe(cm, + AdjThrStateElement, + qcElement, + psyOutElement, + qcElement[i]->grantedPeCorr, + 1, /* Process only 1 element */ + i); /* Process exactly THIS element */ + + } + + } /* -end- if(ID_SCE || ID_CPE || ID_LFE) */ + + } /* -end- element loop */ + } + else { + for (i=0; i<cm->nElements; i++) + { + ELEMENT_INFO elInfo = cm->elInfo[i]; + + if ((elInfo.elType == ID_SCE) || (elInfo.elType == ID_CPE) || + (elInfo.elType == ID_LFE)) + { + /* for VBR-mode */ + FDKaacEnc_AdaptThresholdsVBR(qcElement[i]->qcOutChannel, + psyOutElement[i]->psyOutChannel, + AdjThrStateElement[i], + &psyOutElement[i]->toolsInfo, + &qcElement[i]->peData, + cm->elInfo[i].nChannelsInEl); + } /* -end- if(ID_SCE || ID_CPE || ID_LFE) */ + + } /* -end- element loop */ + + } + for (i=0; i<cm->nElements; i++) { + int ch,sfb,sfbGrp; + /* no weighting of threholds and energies for mlout */ + /* weight energies and thresholds */ + for (ch=0; ch<cm->elInfo[i].nChannelsInEl; ch++) { + QC_OUT_CHANNEL* pQcOutCh = qcElement[i]->qcOutChannel[ch]; + for (sfbGrp = 0;sfbGrp < psyOutElement[i]->psyOutChannel[ch]->sfbCnt; sfbGrp+=psyOutElement[i]->psyOutChannel[ch]->sfbPerGroup) { + for (sfb=0; sfb<psyOutElement[i]->psyOutChannel[ch]->maxSfbPerGroup; sfb++) { + pQcOutCh->sfbThresholdLdData[sfb+sfbGrp] += pQcOutCh->sfbEnFacLd[sfb+sfbGrp]; + } + } + } + } + +} + +void FDKaacEnc_AdjThrClose(ADJ_THR_STATE** phAdjThr) +{ + INT i; + ADJ_THR_STATE* hAdjThr = *phAdjThr; + + if (hAdjThr!=NULL) { + for (i=0; i<(6); i++) { + if (hAdjThr->adjThrStateElem[i]!=NULL) { + FreeRam_aacEnc_AdjThrStateElement(&hAdjThr->adjThrStateElem[i]); + } + } + FreeRam_aacEnc_AdjustThreshold(phAdjThr); + } +} + |