blob: 6e19680359a69a092d3e834bc36d5901146a5b02 [file] [log] [blame]
/* -----------------------------------------------------------------------------
Software License for The Fraunhofer FDK AAC Codec Library for Android
© Copyright 1995 - 2018 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
----------------------------------------------------------------------------- */
/**************************** AAC encoder library ******************************
Author(s): M. Werner
Description: Threshold compensation
*******************************************************************************/
#include "adj_thr.h"
#include "sf_estim.h"
#include "aacEnc_ram.h"
#define NUM_NRG_LEVS (8)
#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);*/
/*
The bits2Pe factors are choosen for the case that some times
the crash recovery strategy will be activated once.
*/
#define AFTERBURNER_STATI 2
#define MAX_ALLOWED_EL_CHANNELS 2
typedef struct {
INT bitrate;
FIXP_DBL bits2PeFactor[AFTERBURNER_STATI][MAX_ALLOWED_EL_CHANNELS];
} BIT_PE_SFAC;
typedef struct {
INT sampleRate;
const BIT_PE_SFAC *pPeTab;
INT nEntries;
} BITS2PE_CFG_TAB;
#define FL2B2PE(value) FL2FXCONST_DBL((value) / (1 << 2))
static const BIT_PE_SFAC S_Bits2PeTab16000[] = {
/* bitrate| afterburner off | afterburner on | | nCh=1
| nCh=2 | nCh=1 | nCh=2 */
{10000,
{{FL2B2PE(1.60f), FL2B2PE(0.00f)}, {FL2B2PE(1.40f), FL2B2PE(0.00f)}}},
{24000,
{{FL2B2PE(1.80f), FL2B2PE(1.40f)}, {FL2B2PE(1.60f), FL2B2PE(1.20f)}}},
{32000,
{{FL2B2PE(1.80f), FL2B2PE(1.60f)}, {FL2B2PE(1.60f), FL2B2PE(1.40f)}}},
{48000,
{{FL2B2PE(1.60f), FL2B2PE(1.80f)}, {FL2B2PE(1.60f), FL2B2PE(1.60f)}}},
{64000,
{{FL2B2PE(1.20f), FL2B2PE(1.60f)}, {FL2B2PE(1.20f), FL2B2PE(1.60f)}}},
{96000,
{{FL2B2PE(1.40f), FL2B2PE(1.80f)}, {FL2B2PE(1.40f), FL2B2PE(1.60f)}}},
{128000,
{{FL2B2PE(1.40f), FL2B2PE(1.80f)}, {FL2B2PE(1.40f), FL2B2PE(1.80f)}}},
{148000,
{{FL2B2PE(1.40f), FL2B2PE(1.80f)}, {FL2B2PE(1.40f), FL2B2PE(1.40f)}}}};
static const BIT_PE_SFAC S_Bits2PeTab22050[] = {
/* bitrate| afterburner off | afterburner on | | nCh=1
| nCh=2 | nCh=1 | nCh=2 */
{16000,
{{FL2B2PE(1.60f), FL2B2PE(1.40f)}, {FL2B2PE(1.20f), FL2B2PE(0.80f)}}},
{24000,
{{FL2B2PE(1.60f), FL2B2PE(1.40f)}, {FL2B2PE(1.40f), FL2B2PE(1.00f)}}},
{32000,
{{FL2B2PE(1.40f), FL2B2PE(1.40f)}, {FL2B2PE(1.40f), FL2B2PE(1.20f)}}},
{48000,
{{FL2B2PE(1.20f), FL2B2PE(1.60f)}, {FL2B2PE(1.20f), FL2B2PE(1.40f)}}},
{64000,
{{FL2B2PE(1.60f), FL2B2PE(1.60f)}, {FL2B2PE(1.60f), FL2B2PE(1.40f)}}},
{96000,
{{FL2B2PE(1.80f), FL2B2PE(1.60f)}, {FL2B2PE(1.80f), FL2B2PE(1.60f)}}},
{128000,
{{FL2B2PE(1.80f), FL2B2PE(1.80f)}, {FL2B2PE(1.60f), FL2B2PE(1.60f)}}},
{148000,
{{FL2B2PE(1.40f), FL2B2PE(1.80f)}, {FL2B2PE(1.40f), FL2B2PE(1.60f)}}}};
static const BIT_PE_SFAC S_Bits2PeTab24000[] = {
/* bitrate| afterburner off | afterburner on | | nCh=1
| nCh=2 | nCh=1 | nCh=2 */
{16000,
{{FL2B2PE(1.40f), FL2B2PE(1.40f)}, {FL2B2PE(1.20f), FL2B2PE(0.80f)}}},
{24000,
{{FL2B2PE(1.60f), FL2B2PE(1.20f)}, {FL2B2PE(1.40f), FL2B2PE(1.00f)}}},
{32000,
{{FL2B2PE(1.40f), FL2B2PE(1.20f)}, {FL2B2PE(1.40f), FL2B2PE(0.80f)}}},
{48000,
{{FL2B2PE(1.40f), FL2B2PE(1.60f)}, {FL2B2PE(1.40f), FL2B2PE(1.40f)}}},
{64000,
{{FL2B2PE(1.60f), FL2B2PE(1.60f)}, {FL2B2PE(1.60f), FL2B2PE(1.40f)}}},
{96000,
{{FL2B2PE(1.80f), FL2B2PE(1.60f)}, {FL2B2PE(1.80f), FL2B2PE(1.60f)}}},
{128000,
{{FL2B2PE(1.40f), FL2B2PE(1.60f)}, {FL2B2PE(1.80f), FL2B2PE(1.80f)}}},
{148000,
{{FL2B2PE(1.40f), FL2B2PE(1.60f)}, {FL2B2PE(1.40f), FL2B2PE(1.80f)}}}};
static const BIT_PE_SFAC S_Bits2PeTab32000[] = {
/* bitrate| afterburner off | afterburner on | | nCh=1
| nCh=2 | nCh=1 | nCh=2 */
{16000,
{{FL2B2PE(1.20f), FL2B2PE(1.40f)}, {FL2B2PE(0.80f), FL2B2PE(0.80f)}}},
{24000,
{{FL2B2PE(1.40f), FL2B2PE(1.20f)}, {FL2B2PE(1.00f), FL2B2PE(0.60f)}}},
{32000,
{{FL2B2PE(1.20f), FL2B2PE(1.20f)}, {FL2B2PE(1.00f), FL2B2PE(0.80f)}}},
{48000,
{{FL2B2PE(1.40f), FL2B2PE(1.40f)}, {FL2B2PE(1.20f), FL2B2PE(1.20f)}}},
{64000,
{{FL2B2PE(1.60f), FL2B2PE(1.40f)}, {FL2B2PE(1.60f), FL2B2PE(1.20f)}}},
{96000,
{{FL2B2PE(1.60f), FL2B2PE(1.40f)}, {FL2B2PE(1.60f), FL2B2PE(1.40f)}}},
{128000,
{{FL2B2PE(1.80f), FL2B2PE(1.60f)}, {FL2B2PE(1.80f), FL2B2PE(1.60f)}}},
{148000,
{{FL2B2PE(1.80f), FL2B2PE(1.60f)}, {FL2B2PE(1.80f), FL2B2PE(1.60f)}}},
{160000,
{{FL2B2PE(1.80f), FL2B2PE(1.60f)}, {FL2B2PE(1.80f), FL2B2PE(1.60f)}}},
{200000,
{{FL2B2PE(1.40f), FL2B2PE(1.60f)}, {FL2B2PE(1.40f), FL2B2PE(1.60f)}}},
{320000,
{{FL2B2PE(3.20f), FL2B2PE(1.80f)}, {FL2B2PE(3.20f), FL2B2PE(1.80f)}}}};
static const BIT_PE_SFAC S_Bits2PeTab44100[] = {
/* bitrate| afterburner off | afterburner on | | nCh=1
| nCh=2 | nCh=1 | nCh=2 */
{16000,
{{FL2B2PE(1.20f), FL2B2PE(1.60f)}, {FL2B2PE(0.80f), FL2B2PE(1.00f)}}},
{24000,
{{FL2B2PE(1.00f), FL2B2PE(1.20f)}, {FL2B2PE(1.00f), FL2B2PE(0.80f)}}},
{32000,
{{FL2B2PE(1.20f), FL2B2PE(1.20f)}, {FL2B2PE(0.80f), FL2B2PE(0.60f)}}},
{48000,
{{FL2B2PE(1.20f), FL2B2PE(1.20f)}, {FL2B2PE(1.20f), FL2B2PE(0.80f)}}},
{64000,
{{FL2B2PE(1.40f), FL2B2PE(1.20f)}, {FL2B2PE(1.20f), FL2B2PE(1.00f)}}},
{96000,
{{FL2B2PE(1.60f), FL2B2PE(1.20f)}, {FL2B2PE(1.60f), FL2B2PE(1.20f)}}},
{128000,
{{FL2B2PE(1.60f), FL2B2PE(1.60f)}, {FL2B2PE(1.60f), FL2B2PE(1.40f)}}},
{148000,
{{FL2B2PE(1.60f), FL2B2PE(1.60f)}, {FL2B2PE(1.60f), FL2B2PE(1.60f)}}},
{160000,
{{FL2B2PE(1.60f), FL2B2PE(1.60f)}, {FL2B2PE(1.60f), FL2B2PE(1.60f)}}},
{200000,
{{FL2B2PE(1.80f), FL2B2PE(1.60f)}, {FL2B2PE(1.60f), FL2B2PE(1.60f)}}},
{320000,
{{FL2B2PE(3.20f), FL2B2PE(1.60f)}, {FL2B2PE(3.20f), FL2B2PE(1.60f)}}}};
static const BIT_PE_SFAC S_Bits2PeTab48000[] = {
/* bitrate| afterburner off | afterburner on | | nCh=1
| nCh=2 | nCh=1 | nCh=2 */
{16000,
{{FL2B2PE(1.40f), FL2B2PE(0.00f)}, {FL2B2PE(0.80f), FL2B2PE(0.00f)}}},
{24000,
{{FL2B2PE(1.40f), FL2B2PE(1.20f)}, {FL2B2PE(1.00f), FL2B2PE(0.80f)}}},
{32000,
{{FL2B2PE(1.00f), FL2B2PE(1.20f)}, {FL2B2PE(0.60f), FL2B2PE(0.80f)}}},
{48000,
{{FL2B2PE(1.20f), FL2B2PE(1.00f)}, {FL2B2PE(0.80f), FL2B2PE(0.80f)}}},
{64000,
{{FL2B2PE(1.20f), FL2B2PE(1.20f)}, {FL2B2PE(1.20f), FL2B2PE(1.00f)}}},
{96000,
{{FL2B2PE(1.60f), FL2B2PE(1.40f)}, {FL2B2PE(1.60f), FL2B2PE(1.20f)}}},
{128000,
{{FL2B2PE(1.60f), FL2B2PE(1.60f)}, {FL2B2PE(1.60f), FL2B2PE(1.40f)}}},
{148000,
{{FL2B2PE(1.60f), FL2B2PE(1.60f)}, {FL2B2PE(1.60f), FL2B2PE(1.40f)}}},
{160000,
{{FL2B2PE(1.60f), FL2B2PE(1.60f)}, {FL2B2PE(1.60f), FL2B2PE(1.40f)}}},
{200000,
{{FL2B2PE(1.20f), FL2B2PE(1.60f)}, {FL2B2PE(1.60f), FL2B2PE(1.40f)}}},
{320000,
{{FL2B2PE(3.20f), FL2B2PE(1.60f)}, {FL2B2PE(3.20f), FL2B2PE(1.60f)}}}};
static const BITS2PE_CFG_TAB bits2PeConfigTab[] = {
{16000, S_Bits2PeTab16000, sizeof(S_Bits2PeTab16000) / sizeof(BIT_PE_SFAC)},
{22050, S_Bits2PeTab22050, sizeof(S_Bits2PeTab22050) / sizeof(BIT_PE_SFAC)},
{24000, S_Bits2PeTab24000, sizeof(S_Bits2PeTab24000) / sizeof(BIT_PE_SFAC)},
{32000, S_Bits2PeTab32000, sizeof(S_Bits2PeTab32000) / sizeof(BIT_PE_SFAC)},
{44100, S_Bits2PeTab44100, sizeof(S_Bits2PeTab44100) / sizeof(BIT_PE_SFAC)},
{48000, S_Bits2PeTab48000,
sizeof(S_Bits2PeTab48000) / sizeof(BIT_PE_SFAC)}};
/* 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 */
/*****************************************************************************
functionname: FDKaacEnc_InitBits2PeFactor
description: retrieve bits2PeFactor from table
*****************************************************************************/
static void FDKaacEnc_InitBits2PeFactor(
FIXP_DBL *bits2PeFactor_m, INT *bits2PeFactor_e, const INT bitRate,
const INT nChannels, const INT sampleRate, const INT advancedBitsToPe,
const INT dZoneQuantEnable, const INT invQuant) {
/**** 1) Set default bits2pe factor ****/
FIXP_DBL bit2PE_m = FL2FXCONST_DBL(1.18f / (1 << (1)));
INT bit2PE_e = 1;
/**** 2) For AAC-(E)LD, make use of advanced bits to pe factor table ****/
if (advancedBitsToPe && nChannels <= (2)) {
int i;
const BIT_PE_SFAC *peTab = NULL;
INT size = 0;
/*** 2.1) Get correct table entry ***/
for (i = 0; i < (INT)(sizeof(bits2PeConfigTab) / sizeof(BITS2PE_CFG_TAB));
i++) {
if (sampleRate >= bits2PeConfigTab[i].sampleRate) {
peTab = bits2PeConfigTab[i].pPeTab;
size = bits2PeConfigTab[i].nEntries;
}
}
if ((peTab != NULL) && (size != 0)) {
INT startB = -1; /* bitrate entry in table that is the next-lower to
actual bitrate */
INT stopB = -1; /* bitrate entry in table that is the next-higher to
actual bitrate */
FIXP_DBL startPF =
FL2FXCONST_DBL(0.0f); /* bits2PE factor entry in table that is the
next-lower to actual bits2PE factor */
FIXP_DBL stopPF = FL2FXCONST_DBL(0.0f); /* bits2PE factor entry in table
that is the next-higher to
actual bits2PE factor */
FIXP_DBL slope = FL2FXCONST_DBL(
0.0f); /* the slope from the start bits2Pe entry to the next one */
const int qualityIdx = (invQuant == 0) ? 0 : 1;
if (bitRate >= peTab[size - 1].bitrate) {
/* Chosen bitrate is higher than the highest bitrate in table.
The slope for extrapolating the bits2PE factor must be zero.
Values are set accordingly. */
startB = peTab[size - 1].bitrate;
stopB =
bitRate +
1; /* Can be an arbitrary value greater than startB and bitrate. */
startPF = peTab[size - 1].bits2PeFactor[qualityIdx][nChannels - 1];
stopPF = peTab[size - 1].bits2PeFactor[qualityIdx][nChannels - 1];
} else {
for (i = 0; i < size - 1; i++) {
if ((peTab[i].bitrate <= bitRate) &&
(peTab[i + 1].bitrate > bitRate)) {
startB = peTab[i].bitrate;
stopB = peTab[i + 1].bitrate;
startPF = peTab[i].bits2PeFactor[qualityIdx][nChannels - 1];
stopPF = peTab[i + 1].bits2PeFactor[qualityIdx][nChannels - 1];
break;
}
}
}
/*** 2.2) Configuration available? ***/
if (startB != -1) {
/** 2.2.1) linear interpolate to actual PEfactor **/
FIXP_DBL bit2PE = 0;
const FIXP_DBL maxBit2PE = FL2FXCONST_DBL(3.f / 4.f);
/* bit2PE = ((stopPF-startPF)/(stopB-startB))*(bitRate-startB)+startPF;
*/
slope = fDivNorm(bitRate - startB, stopB - startB);
bit2PE = fMult(slope, stopPF - startPF) + startPF;
bit2PE = fMin(maxBit2PE, bit2PE);
/** 2.2.2) sanity check if bits2pe value is high enough **/
if (bit2PE >= (FL2FXCONST_DBL(0.35f) >> 2)) {
bit2PE_m = bit2PE;
bit2PE_e = 2; /* table is fixed scaled */
}
} /* br */
} /* sr */
} /* advancedBitsToPe */
if (dZoneQuantEnable) {
if (bit2PE_m >= (FL2FXCONST_DBL(0.6f)) >> bit2PE_e) {
/* Additional headroom for addition */
bit2PE_m >>= 1;
bit2PE_e += 1;
}
/* the quantTendencyCompensator compensates a lower bit consumption due to
* increasing the tendency to quantize low spectral values to the lower
* quantizer border for bitrates below a certain bitrate threshold --> see
* also function calcSfbDistLD in quantize.c */
if ((bitRate / nChannels > 32000) && (bitRate / nChannels <= 40000)) {
bit2PE_m += (FL2FXCONST_DBL(0.4f)) >> bit2PE_e;
} else if (bitRate / nChannels > 20000) {
bit2PE_m += (FL2FXCONST_DBL(0.3f)) >> bit2PE_e;
} else if (bitRate / nChannels >= 16000) {
bit2PE_m += (FL2FXCONST_DBL(0.3f)) >> bit2PE_e;
} else {
bit2PE_m += (FL2FXCONST_DBL(0.0f)) >> bit2PE_e;
}
}
/***** 3.) Return bits2pe factor *****/
*bits2PeFactor_m = bit2PE_m;
*bits2PeFactor_e = bit2PE_e;
}
/*****************************************************************************
functionname: FDKaacEnc_bits2pe2
description: convert from bits to pe
*****************************************************************************/
FDK_INLINE 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],
const QC_OUT_CHANNEL *const qcOutChannel[(2)],
const PSY_OUT_CHANNEL *const 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 *const qcOutChannel[(2)],
const PSY_OUT_CHANNEL *const psyOutChannel[(2)],
const MINSNR_ADAPT_PARAM *const 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;
FIXP_DBL msaParam_maxRed = msaParam->maxRed;
FIXP_DBL msaParam_startRatio = msaParam->startRatio;
FIXP_DBL msaParam_redRatioFac =
fMult(msaParam->redRatioFac, FL2FXCONST_DBL(0.3010299956f));
FIXP_DBL msaParam_redOffs = msaParam->redOffs;
for (ch = 0; ch < nChannels; ch++) {
/* calc average energy per scalefactor band */
nSfb = 0;
accu = FL2FXCONST_DBL(0.0f);
DWORD_ALIGNED(psyOutChannel[ch]->sfbEnergy);
for (sfbGrp = 0; sfbGrp < psyOutChannel[ch]->sfbCnt;
sfbGrp += psyOutChannel[ch]->sfbPerGroup) {
int maxSfbPerGroup = psyOutChannel[ch]->maxSfbPerGroup;
nSfb += maxSfbPerGroup;
for (sfb = 0; sfb < maxSfbPerGroup; sfb++) {
accu += psyOutChannel[ch]->sfbEnergy[sfbGrp + sfb] >> 6;
}
}
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 */
int maxSfbPerGroup = psyOutChannel[ch]->maxSfbPerGroup;
int sfbCnt = psyOutChannel[ch]->sfbCnt;
int sfbPerGroup = psyOutChannel[ch]->sfbPerGroup;
for (sfbGrp = 0; sfbGrp < sfbCnt; sfbGrp += sfbPerGroup) {
FIXP_DBL *RESTRICT psfbEnergyLdData =
&qcOutChannel[ch]->sfbEnergyLdData[sfbGrp];
FIXP_DBL *RESTRICT psfbMinSnrLdData =
&qcOutChannel[ch]->sfbMinSnrLdData[sfbGrp];
for (sfb = 0; sfb < maxSfbPerGroup; sfb++) {
FIXP_DBL sfbEnergyLdData = *psfbEnergyLdData++;
FIXP_DBL sfbMinSnrLdData = *psfbMinSnrLdData;
dbRatio = avgEnLD64 - sfbEnergyLdData;
int update = (msaParam_startRatio < dbRatio) ? 1 : 0;
minSnrRed = msaParam_redOffs + fMult(msaParam_redRatioFac,
dbRatio); /* scaled by 1.0f/64.0f*/
minSnrRed =
fixMax(minSnrRed, msaParam_maxRed); /* scaled by 1.0f/64.0f*/
minSnrRed = (fMult(sfbMinSnrLdData, minSnrRed)) << 6;
minSnrRed = fixMin(minSnrLimitLD64, minSnrRed);
*psfbMinSnrLdData++ = update ? minSnrRed : sfbMinSnrLdData;
}
}
}
}
/*****************************************************************************
functionname: FDKaacEnc_initAvoidHoleFlag
description: determine bands where avoid hole is not necessary resp. possible
*****************************************************************************/
static void FDKaacEnc_initAvoidHoleFlag(
QC_OUT_CHANNEL *const qcOutChannel[(2)],
const PSY_OUT_CHANNEL *const psyOutChannel[(2)],
UCHAR ahFlag[(2)][MAX_GROUPED_SFB], const struct TOOLSINFO *const toolsInfo,
const INT nChannels, const AH_PARAM *const 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++) {
QC_OUT_CHANNEL *const 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 *const 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];
const PSY_OUT_CHANNEL *const 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));
} /* if (toolsInfo->msMask[sfbGrp+sfb]) */
} /* sfb */
} /* sfbGrp */
} /* nChannels==2 */
/* init ahFlag (0: no ah necessary, 1: ah possible, 2: ah active */
for (ch = 0; ch < nChannels; ch++) {
QC_OUT_CHANNEL *qcOutChan = qcOutChannel[ch];
const PSY_OUT_CHANNEL *const 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 *const peData,
const PSY_OUT_CHANNEL *const psyOutChannel[(2)],
const QC_OUT_CHANNEL *const qcOutChannel[(2)],
const INT nChannels, const INT peOffset) {
INT ch;
for (ch = 0; ch < nChannels; ch++) {
const PSY_OUT_CHANNEL *const 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(
const PE_DATA *const peData,
const PSY_OUT_CHANNEL *const psyOutChannel[(2)],
QC_OUT_CHANNEL *const qcOutChannel[(2)],
const struct TOOLSINFO *const toolsInfo,
ATS_ELEMENT *const 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++) {
const PSY_OUT_CHANNEL *const 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, sfbGrp, nLinesSum = 0;
nrgSum14 = nrgSum12 = nrgSum34 = nrgTotal = FL2FXCONST_DBL(0.f);
/* calculate flatness of audible spectrum, i.e. spectrum above masking
* threshold. */
for (sfbGrp = 0; sfbGrp < psyOutChannel[ch]->sfbCnt;
sfbGrp += psyOutChannel[ch]->sfbPerGroup) {
for (sfb = 0; sfb < psyOutChannel[ch]->maxSfbPerGroup; sfb++) {
FIXP_DBL nrgFac12 = CalcInvLdData(
psyOutChan->sfbEnergyLdData[sfbGrp + sfb] >> 1); /* nrg^(1/2) */
FIXP_DBL nrgFac14 = CalcInvLdData(
psyOutChan->sfbEnergyLdData[sfbGrp + sfb] >> 2); /* nrg^(1/4) */
/* maximal number of bands is 64, results scaling factor 6 */
nLinesSum += peData->peChannelData[ch]
.sfbNLines[sfbGrp + sfb]; /* relevant lines */
nrgTotal +=
(psyOutChan->sfbEnergy[sfbGrp + 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) */
/* Note: nLinesSum cannot be larger than the number of total lines, thats
* taken care of in line_pe.cpp FDKaacEnc_prepareSfbPe() */
adjThrStateElement->chaosMeasureEnFac[ch] =
fMax(FL2FXCONST_DBL(0.1875f),
fDivNorm(nLinesSum, psyOutChan->sfbOffsets[psyOutChan->sfbCnt]));
usePatch = (adjThrStateElement->chaosMeasureEnFac[ch] >
FL2FXCONST_DBL(0.78125f));
exePatch = ((usePatch) && (adjThrStateElement->lastEnFacPatch[ch]));
for (sfbGrp = 0; sfbGrp < psyOutChannel[ch]->sfbCnt;
sfbGrp += psyOutChannel[ch]->sfbPerGroup) {
for (sfb = 0; sfb < psyOutChannel[ch]->maxSfbPerGroup; sfb++) {
INT sfbExePatch;
/* for MS coupled SFBs, also execute patch in side channel if done in
* mid channel */
if ((ch == 1) && (toolsInfo->msMask[sfbGrp + sfb])) {
sfbExePatch = exePatchM;
} else {
sfbExePatch = exePatch;
}
if ((sfbExePatch) &&
(psyOutChan->sfbEnergy[sfbGrp + sfb] > FL2FXCONST_DBL(0.f))) {
/* execute patch based on spectral flatness calculated above */
if (adjThrStateElement->chaosMeasureEnFac[ch] >
FL2FXCONST_DBL(0.8125f)) {
qcOutChannel[ch]->sfbEnFacLd[sfbGrp + sfb] =
((nrgFacLd_14 +
(psyOutChan->sfbEnergyLdData[sfbGrp + sfb] +
(psyOutChan->sfbEnergyLdData[sfbGrp + sfb] >> 1))) >>
1); /* sfbEnergy^(3/4) */
} else if (adjThrStateElement->chaosMeasureEnFac[ch] >
FL2FXCONST_DBL(0.796875f)) {
qcOutChannel[ch]->sfbEnFacLd[sfbGrp + sfb] =
((nrgFacLd_12 + psyOutChan->sfbEnergyLdData[sfbGrp + sfb]) >>
1); /* sfbEnergy^(2/4) */
} else {
qcOutChannel[ch]->sfbEnFacLd[sfbGrp + sfb] =
((nrgFacLd_34 +
(psyOutChan->sfbEnergyLdData[sfbGrp + sfb] >> 1)) >>
1); /* sfbEnergy^(1/4) */
}
qcOutChannel[ch]->sfbEnFacLd[sfbGrp + sfb] =
fixMin(qcOutChannel[ch]->sfbEnFacLd[sfbGrp + 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(const PSY_OUT_CHANNEL *const psyOutChannel[(2)],
const QC_OUT_CHANNEL *const qcOutChannel[(2)],
PE_DATA *const 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(
peChanData, 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 *const peData,
const PSY_OUT_CHANNEL *const psyOutChannel[(2)],
QC_OUT_CHANNEL *const qcOutChannel[(2)],
const struct TOOLSINFO *const toolsInfo,
ATS_ELEMENT *const 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
*****************************************************************************/
#define CONSTPART_HEADROOM 4
static void FDKaacEnc_FDKaacEnc_calcPeNoAH(
INT *const pe, INT *const constPart, INT *const nActiveLines,
const PE_DATA *const peData, const UCHAR ahFlag[(2)][MAX_GROUPED_SFB],
const PSY_OUT_CHANNEL *const 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++) {
const PE_CHANNEL_DATA *const 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] >> CONSTPART_HEADROOM;
nActiveLines_tmp += peChanData->sfbNActiveLines[sfbGrp + sfb];
}
}
}
}
/* correct scaled pe and constPart values */
*pe = pe_tmp >> PE_CONSTPART_SHIFT;
*constPart = constPart_tmp >> (PE_CONSTPART_SHIFT - CONSTPART_HEADROOM);
*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 *const qcOutChannel[(2)],
const PSY_OUT_CHANNEL *const psyOutChannel[(2)],
UCHAR ahFlag[(2)][MAX_GROUPED_SFB],
const FIXP_DBL thrExp[(2)][MAX_GROUPED_SFB], const INT nChannels,
const FIXP_DBL redVal_m, const SCHAR redVal_e) {
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_m) - redVal_e) -
1;
/* 4*log( sfbThrExp + redVal ) */
sfbThrReducedLdData =
CalcLdData(fAbs(scaleValue(sfbThrExp, minScale) +
scaleValue(redVal_m, redVal_e + minScale))) -
(FIXP_DBL)(minScale << (DFRACT_BITS - 1 - LD_DATA_SHIFT));
sfbThrReducedLdData <<= 2;
/* avoid holes */
if ((sfbThrReducedLdData >
(qcOutChan->sfbMinSnrLdData[sfbGrp + sfb] + sfbEnLdData)) &&
(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(
const PSY_OUT_CHANNEL *const psyOutChannel,
const FIXP_DBL *const 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 *const qcOutChannel[(2)],
const PSY_OUT_CHANNEL *const psyOutChannel[(2)],
UCHAR ahFlag[(2)][MAX_GROUPED_SFB],
const FIXP_DBL thrExp[(2)][MAX_GROUPED_SFB], const INT nChannels,
const FIXP_DBL vbrQualFactor, FIXP_DBL *const 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;
const 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++) {
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)
groupCnt = 0;
for (sfbGrp = 0; sfbGrp < psyOutChannel[0]->sfbCnt;
sfbGrp += psyOutChannel[0]->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(
const CHANNEL_MAPPING *const cm, QC_OUT_ELEMENT *const qcElement[((8))],
const PSY_OUT_ELEMENT *const psyOutElement[((8))],
UCHAR ahFlag[((8))][(2)][MAX_GROUPED_SFB],
const FIXP_DBL thrExp[((8))][(2)][MAX_GROUPED_SFB], const FIXP_DBL redVal_m,
const SCHAR redVal_e, 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[((8))][(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++) {
/* The reinterpret_cast is used to suppress a compiler warning. We know
* that qcElement[elementId]->qcOutChannel[ch]->quantSpec is sufficiently
* aligned, so the cast is safe */
sfbPeFactorsLdData[elementId][ch] =
reinterpret_cast<FIXP_DBL *>(reinterpret_cast<void *>(
qcElement[elementId]->qcOutChannel[ch]->quantSpec));
}
}
/* The reinterpret_cast is used to suppress a compiler warning. We know that
* qcElement[0]->dynMem_SfbNActiveLinesLdData is sufficiently aligned, so the
* cast is safe */
sfbNActiveLinesLdData = reinterpret_cast<FIXP_DBL(*)[(2)][MAX_GROUPED_SFB]>(
reinterpret_cast<void *>(qcElement[0]->dynMem_SfbNActiveLinesLdData));
/* 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++) {
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[elementId][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[elementId][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_m) {
/* 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_m) - redVal_e) -
1;
/* sumld = ld64( sfbThrExp + redVal ) */
FIXP_DBL sumLd =
CalcLdData(scaleValue(thrExp[elementId][ch][sfbGrp + sfb],
minScale) +
scaleValue(redVal_m, redVal_e + minScale)) -
(FIXP_DBL)(minScale << (DFRACT_BITS - 1 - LD_DATA_SHIFT));
if (sumLd < FL2FXCONST_DBL(0.f)) {
sfbPeFactorsLdData[elementId][ch][sfbGrp + sfb] =
sfbNActiveLinesLdData[elementId][ch][sfbGrp + sfb] -
sumLd;
} else {
if (sfbNActiveLinesLdData[elementId][ch][sfbGrp + sfb] >
(FL2FXCONST_DBL(-1.f) + sumLd)) {
sfbPeFactorsLdData[elementId][ch][sfbGrp + sfb] =
sfbNActiveLinesLdData[elementId][ch][sfbGrp + sfb] -
sumLd;
} else {
sfbPeFactorsLdData[elementId][ch][sfbGrp + sfb] =
sfbNActiveLinesLdData[elementId][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[elementId][ch][sfbGrp + sfb] -
FL2FXCONST_DBL((float)LD_DATA_SHIFT / LD_DATA_SCALING));
/* limit thrFactor to 60dB */
tmp = (deltaPe < 0) ? tmp : (-tmp);
thrFactorLdData =
fMin(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
*****************************************************************************/
static void FDKaacEnc_reduceMinSnr(
const CHANNEL_MAPPING *const cm, QC_OUT_ELEMENT *const qcElement[((8))],
const PSY_OUT_ELEMENT *const psyOutElement[((8))],
const UCHAR ahFlag[((8))][(2)][MAX_GROUPED_SFB], const INT desiredPe,
INT *const redPeGlobal, const INT processElements, const INT elementOffset)
{
INT ch, elementId, globalMaxSfb = 0;
const INT nElements = elementOffset + processElements;
INT newGlobalPe = *redPeGlobal;
if (newGlobalPe <= desiredPe) {
goto bail;
}
/* global maximum of maxSfbPerGroup */
for (elementId = elementOffset; elementId < nElements; elementId++) {
if (cm->elInfo[elementId].elType != ID_DSE) {
for (ch = 0; ch < cm->elInfo[elementId].nChannelsInEl; ch++) {
globalMaxSfb =
fMax(globalMaxSfb,
psyOutElement[elementId]->psyOutChannel[ch]->maxSfbPerGroup);
}
}
}
/* as long as globalPE is above desirePE reduce SNR to 1.0 dB, starting at
* highest SFB */
while ((newGlobalPe > desiredPe) && (--globalMaxSfb >= 0)) {
for (elementId = elementOffset; elementId < nElements; elementId++) {
if (cm->elInfo[elementId].elType != ID_DSE) {
PE_DATA *peData = &qcElement[elementId]->peData;
for (ch = 0; ch < cm->elInfo[elementId].nChannelsInEl; ch++) {
QC_OUT_CHANNEL *qcOutChan = qcElement[elementId]->qcOutChannel[ch];
PSY_OUT_CHANNEL *psyOutChan =
psyOutElement[elementId]->psyOutChannel[ch];
/* try to reduce SNR of channel's uppermost SFB(s) */
if (globalMaxSfb < psyOutChan->maxSfbPerGroup) {
INT sfb, deltaPe = 0;
for (sfb = globalMaxSfb; sfb < psyOutChan->sfbCnt;
sfb += psyOutChan->sfbPerGroup) {
if (ahFlag[elementId][ch][sfb] != NO_AH &&
qcOutChan->sfbMinSnrLdData[sfb] < SnrLdFac &&
(qcOutChan->sfbWeightedEnergyLdData[sfb] >
qcOutChan->sfbThresholdLdData[sfb] - SnrLdFac)) {
/* increase threshold to new minSnr of 1dB */
qcOutChan->sfbMinSnrLdData[sfb] = SnrLdFac;
qcOutChan->sfbThresholdLdData[sfb] =
qcOutChan->sfbWeightedEnergyLdData[sfb] + SnrLdFac;
/* calc new pe */
/* C2 + C3*ld(1/0.8) = 1.5 */
deltaPe -= peData->peChannelData[ch].sfbPe[sfb];
/* sfbPe = 1.5 * sfbNLines */
peData->peChannelData[ch].sfbPe[sfb] =
(3 * peData->peChannelData[ch].sfbNLines[sfb])
<< (PE_CONSTPART_SHIFT - 1);
deltaPe += peData->peChannelData[ch].sfbPe[sfb];
}
} /* sfb loop */
deltaPe >>= PE_CONSTPART_SHIFT;
peData->pe += deltaPe;
peData->peChannelData[ch].pe += deltaPe;
newGlobalPe += deltaPe;
} /* if globalMaxSfb < maxSfbPerGroup */
/* stop if enough has been saved */
if (newGlobalPe <= desiredPe) {
goto bail;
}
} /* ch loop */
} /* != ID_DSE */
} /* elementId loop */
} /* while ( newGlobalPe > desiredPe) && (--globalMaxSfb >= 0) ) */
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(
const CHANNEL_MAPPING *const cm, QC_OUT_ELEMENT *const qcElement[((8))],
const PSY_OUT_ELEMENT *const psyOutElement[((8))],
const ATS_ELEMENT *const AdjThrStateElement[((8))],
UCHAR ahFlag[((8))][(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 = psyOutChannel[0]->maxSfbPerGroup - 1; sfb >= 0; 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 ? */
} /* EOF DSE-suppression */
} /* EOF for all elements... */
if (actPe > desiredPe) {
/* more holes necessary? subsequently erase bands starting with low energies
*/
INT ch, sfb, sfbGrp;
INT minSfb, maxSfb;
INT enIdx, ahCnt, done;
INT startSfb[(8)];
INT sfbCnt[(8)];
INT sfbPerGroup[(8)];
INT maxSfbPerGroup[(8)];
FIXP_DBL avgEn;
FIXP_DBL minEnLD64;
FIXP_DBL avgEnLD64;
FIXP_DBL enLD64[NUM_NRG_LEVS];
INT avgEn_e;
/* get the scaling factor over all audio elements and channels */
maxSfb = 0;
for (elementId = elementOffset; elementId < nElements; elementId++) {
if (cm->elInfo[elementId].elType != ID_DSE) {
for (ch = 0; ch < cm->elInfo[elementId].nChannelsInEl; ch++) {
for (sfbGrp = 0;
sfbGrp < psyOutElement[elementId]->psyOutChannel[ch]->sfbCnt;
sfbGrp +=
psyOutElement[elementId]->psyOutChannel[ch]->sfbPerGroup) {
maxSfb +=
psyOutElement[elementId]->psyOutChannel[ch]->maxSfbPerGroup;
}
}
}
}
avgEn_e =
(DFRACT_BITS - fixnormz_D((LONG)fMax(0, maxSfb - 1))); /* ilog2() */
ahCnt = 0;
maxSfb = 0;
minSfb = MAX_SFB;
avgEn = FL2FXCONST_DBL(0.0f);
minEnLD64 = FL2FXCONST_DBL(0.0f);
for (elementId = elementOffset; elementId < nElements; elementId++) {
if (cm->elInfo[elementId].elType != ID_DSE) {
for (ch = 0; ch < cm->elInfo[elementId].nChannelsInEl; ch++) {
const INT chIdx = cm->elInfo[elementId].ChannelIndex[ch];
QC_OUT_CHANNEL *qcOutChannel = qcElement[elementId]->qcOutChannel[ch];
PSY_OUT_CHANNEL *psyOutChannel =
psyOutElement[elementId]->psyOutChannel[ch];
maxSfbPerGroup[chIdx] = psyOutChannel->maxSfbPerGroup;
sfbCnt[chIdx] = psyOutChannel->sfbCnt;
sfbPerGroup[chIdx] = psyOutChannel->sfbPerGroup;
maxSfb = fMax(maxSfb, psyOutChannel->maxSfbPerGroup);
if (psyOutChannel->lastWindowSequence != SHORT_WINDOW) {
startSfb[chIdx] = AdjThrStateElement[elementId]->ahParam.startSfbL;
} else {
startSfb[chIdx] = AdjThrStateElement[elementId]->ahParam.startSfbS;
}
minSfb = fMin(minSfb, startSfb[chIdx]);
sfbGrp = 0;
sfb = startSfb[chIdx];
do {
for (; sfb < psyOutChannel->maxSfbPerGroup; sfb++) {
if ((ahFlag[elementId][ch][sfbGrp + sfb] != NO_AH) &&
(qcOutChannel->sfbWeightedEnergyLdData[sfbGrp + sfb] >
qcOutChannel->sfbThresholdLdData[sfbGrp + sfb])) {
minEnLD64 = fixMin(minEnLD64,
qcOutChannel->sfbEnergyLdData[sfbGrp + sfb]);
avgEn += qcOutChannel->sfbEnergy[sfbGrp + sfb] >> avgEn_e;
ahCnt++;
}
}
sfbGrp += psyOutChannel->sfbPerGroup;
sfb = startSfb[chIdx];
} while (sfbGrp < psyOutChannel->sfbCnt);
}
} /* (cm->elInfo[elementId].elType != ID_DSE) */
} /* (elementId = elementOffset;elementId<nElements;elementId++) */
if ((avgEn == FL2FXCONST_DBL(0.0f)) || (ahCnt == 0)) {
avgEnLD64 = FL2FXCONST_DBL(0.0f);
} else {
avgEnLD64 = CalcLdData(avgEn) +
(FIXP_DBL)(avgEn_e << (DFRACT_BITS - 1 - LD_DATA_SHIFT)) -
CalcLdInt(ahCnt);
}
/* calc some energy borders between minEn and avgEn */
/* for (enIdx = 0; enIdx < NUM_NRG_LEVS; enIdx++) {
en[enIdx] = (2.0f*enIdx+1.0f)/(2.0f*NUM_NRG_LEVS-1.0f);
} */
enLD64[0] =
minEnLD64 + fMult((avgEnLD64 - minEnLD64), FL2FXCONST_DBL(0.06666667f));
enLD64[1] =
minEnLD64 + fMult((avgEnLD64 - minEnLD64), FL2FXCONST_DBL(0.20000000f));
enLD64[2] =
minEnLD64 + fMult((avgEnLD64 - minEnLD64), FL2FXCONST_DBL(0.33333334f));
enLD64[3] =
minEnLD64 + fMult((avgEnLD64 - minEnLD64), FL2FXCONST_DBL(0.46666667f));
enLD64[4] =
minEnLD64 + fMult((avgEnLD64 - minEnLD64), FL2FXCONST_DBL(0.60000002f));
enLD64[5] =
minEnLD64 + fMult((avgEnLD64 - minEnLD64), FL2FXCONST_DBL(0.73333335f));
enLD64[6] =
minEnLD64 + fMult((avgEnLD64 - minEnLD64), FL2FXCONST_DBL(0.86666667f));
enLD64[7] = minEnLD64 + (avgEnLD64 - minEnLD64);
done = 0;
enIdx = 0;
sfb = maxSfb - 1;
while (!done) {
for (elementId = elementOffset; elementId < nElements; elementId++) {
if (cm->elInfo[elementId].elType != ID_DSE) {
PE_DATA *peData = &qcElement[elementId]->peData;
for (ch = 0; ch < cm->elInfo[elementId].nChannelsInEl; ch++) {
const INT chIdx = cm->elInfo[elementId].ChannelIndex[ch];
QC_OUT_CHANNEL *qcOutChannel =
qcElement[elementId]->qcOutChannel[ch];
if (sfb >= startSfb[chIdx] && sfb < maxSfbPerGroup[chIdx]) {
for (sfbGrp = 0; sfbGrp < sfbCnt[chIdx];
sfbGrp += sfbPerGroup[chIdx]) {
/* sfb energy below border ? */
if (ahFlag[elementId][ch][sfbGrp + sfb] != NO_AH &&
qcOutChannel->sfbEnergyLdData[sfbGrp + sfb] <
enLD64[enIdx]) {
/* allow hole */
ahFlag[elementId][ch][sfbGrp + sfb] = NO_AH;
qcOutChannel->sfbThresholdLdData[sfbGrp + sfb] =
FL2FXCONST_DBL(0.015625f) +
qcOutChannel->sfbWeightedEnergyLdData[sfbGrp + sfb];
actPe -= peData->peChannelData[ch].sfbPe[sfbGrp + sfb] >>
PE_CONSTPART_SHIFT;
}
if (actPe <= desiredPe) {
return; /* stop if enough has been saved */
}
} /* sfbGrp */
} /* sfb */
} /* nChannelsInEl */
} /* ID_DSE */
} /* elementID */
sfb--;
if (sfb < minSfb) {
/* restart with next energy border */
sfb = maxSfb;
enIdx++;
if (enIdx >= NUM_NRG_LEVS) {
done = 1;
}
}
} /* done */
} /* (actPe <= desiredPe) */
}
/* reset avoid hole flags from AH_ACTIVE to AH_INACTIVE */
static void FDKaacEnc_resetAHFlags(
UCHAR ahFlag[(2)][MAX_GROUPED_SFB], const INT nChannels,
const PSY_OUT_CHANNEL *const 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);
return value;
}
/*****************************************************************************
functionname: FDKaacEnc_adaptThresholdsToPe
description: two guesses for the reduction value and one final correction of
the thresholds
*****************************************************************************/
static void FDKaacEnc_adaptThresholdsToPe(
const CHANNEL_MAPPING *const cm,
ATS_ELEMENT *const AdjThrStateElement[((8))],
QC_OUT_ELEMENT *const qcElement[((8))],
const PSY_OUT_ELEMENT *const psyOutElement[((8))], const INT desiredPe,
const INT maxIter2ndGuess, const INT processElements,
const INT elementOffset) {
FIXP_DBL reductionValue_m;
SCHAR reductionValue_e;
UCHAR(*pAhFlag)[(2)][MAX_GROUPED_SFB];
FIXP_DBL(*pThrExp)[(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;
}
/* The reinterpret_cast is used to suppress a compiler warning. We know that
* qcElement[0]->dynMem_Ah_Flag is sufficiently aligned, so the cast is safe
*/
pAhFlag = reinterpret_cast<UCHAR(*)[(2)][MAX_GROUPED_SFB]>(
reinterpret_cast<void *>(qcElement[0]->dynMem_Ah_Flag));
/* The reinterpret_cast is used to suppress a compiler warning. We know that
* qcElement[0]->dynMem_Thr_Exp is sufficiently aligned, so the cast is safe
*/
pThrExp = reinterpret_cast<FIXP_DBL(*)[(2)][MAX_GROUPED_SFB]>(
reinterpret_cast<void *>(qcElement[0]->dynMem_Thr_Exp));
/* ------------------------------------------------------- */
/* 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,
&AdjThrStateElement[elementId]->ahParam);
/* sum up */
constPartGlobal += peData->constPart;
noRedPeGlobal += peData->pe;
nActiveLinesGlobal += fixMax((INT)peData->nActiveLines, 1);
} /* EOF DSE-suppression */
} /* EOF for all elements... */
/*
First guess of reduction value:
avgThrExp = (float)pow(2.0f, (constPartGlobal - noRedPeGlobal)/(4.0f *
nActiveLinesGlobal)); redVal = (float)pow(2.0f, (constPartGlobal -
desiredPe)/(4.0f * nActiveLinesGlobal)) - avgThrExp; redVal = max(0.f,
redVal);
*/
int redVal_e, avgThrExp_e, result_e;
FIXP_DBL redVal_m, avgThrExp_m;
redVal_m = CalcRedValPower(constPartGlobal - desiredPe,
4 * nActiveLinesGlobal, &redVal_e);
avgThrExp_m = CalcRedValPower(constPartGlobal - noRedPeGlobal,
4 * nActiveLinesGlobal, &avgThrExp_e);
result_e = fMax(redVal_e, avgThrExp_e) + 1;
reductionValue_m = fMax(FL2FXCONST_DBL(0.f),
scaleValue(redVal_m, redVal_e - result_e) -
scaleValue(avgThrExp_m, avgThrExp_e - result_e));
reductionValue_e = result_e;
/* ----------------------------------------------------------------------- */
/* Part II: Calculate bit consumption of initial bit constraints setup */
/* ----------------------------------------------------------------------- */
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, reductionValue_m, reductionValue_e);
/* 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 < maxIter2ndGuess)) {
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 = fMax(0, desiredPeNoAHGlobal);
/* second guess (only if there are bands left where avoid hole is
* inactive)*/
if (nActiveLinesNoAHGlobal > 0) {
/*
avgThrExp = (float)pow(2.0f, (constPartNoAHGlobal - redPeNoAHGlobal) /
(4.0f * nActiveLinesNoAHGlobal)); redVal += (float)pow(2.0f,
(constPartNoAHGlobal - desiredPeNoAHGlobal) / (4.0f *
nActiveLinesNoAHGlobal)) - avgThrExp; redVal = max(0.0f, redVal);
*/
redVal_m = CalcRedValPower(constPartNoAHGlobal - desiredPeNoAHGlobal,
4 * nActiveLinesNoAHGlobal, &redVal_e);
avgThrExp_m = CalcRedValPower(constPartNoAHGlobal - redPeNoAHGlobal,
4 * nActiveLinesNoAHGlobal, &avgThrExp_e);
result_e = fMax(reductionValue_e, fMax(redVal_e, avgThrExp_e) + 1) + 1;
reductionValue_m =
fMax(FL2FXCONST_DBL(0.f),
scaleValue(reductionValue_m, reductionValue_e - result_e) +
scaleValue(redVal_m, redVal_e - result_e) -
scaleValue(avgThrExp_m, avgThrExp_e - result_e));
reductionValue_e = result_e;
} /* nActiveLinesNoAHGlobal > 0 */
} else {
/* redVal *= redPeGlobal/desiredPe; */
int sc0, sc1;
reductionValue_m = fMultNorm(
reductionValue_m,
fDivNorm((FIXP_DBL)redPeGlobal, (FIXP_DBL)desiredPe, &sc0), &sc1);
reductionValue_e += sc0 + sc1;
for (elementId = elementOffset; elementId < nElements; elementId++) {
if (cm->elInfo[elementId].elType != ID_DSE) {
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, reductionValue_m, reductionValue_e);
/* 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,
reductionValue_m, reductionValue_e,
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 */
static void FDKaacEnc_AdaptThresholdsVBR(
QC_OUT_CHANNEL *const qcOutChannel[(2)],
const PSY_OUT_CHANNEL *const psyOutChannel[(2)],
ATS_ELEMENT *const AdjThrStateElement,
const struct TOOLSINFO *const toolsInfo, const INT nChannels) {
UCHAR(*pAhFlag)[MAX_GROUPED_SFB];
FIXP_DBL(*pThrExp)[MAX_GROUPED_SFB];
/* allocate scratch memory */
C_ALLOC_SCRATCH_START(_pAhFlag, UCHAR, (2) * MAX_GROUPED_SFB)
C_ALLOC_SCRATCH_START(_pThrExp, FIXP_DBL, (2) * MAX_GROUPED_SFB)
pAhFlag = (UCHAR(*)[MAX_GROUPED_SFB])_pAhFlag;
pThrExp = (FIXP_DBL(*)[MAX_GROUPED_SFB])_pThrExp;
/* 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, &AdjThrStateElement->ahParam);
/* reduce thresholds */
FDKaacEnc_reduceThresholdsVBR(qcOutChannel, psyOutChannel, pAhFlag, pThrExp,
nChannels, AdjThrStateElement->vbrQualFactor,
&AdjThrStateElement->chaosMeasureOld);
/* free scratch memory */
C_ALLOC_SCRATCH_END(_pThrExp, FIXP_DBL, (2) * MAX_GROUPED_SFB)
C_ALLOC_SCRATCH_END(_pAhFlag, UCHAR, (2) * MAX_GROUPED_SFB)
}
/*****************************************************************************
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
*/
void FDKaacEnc_bitresCalcBitFac(const INT bitresBits, const INT maxBitresBits,
const INT pe, const INT lastWindowSequence,
const INT avgBits, const FIXP_DBL maxBitFac,
const ADJ_THR_STATE *const AdjThr,
ATS_ELEMENT *const adjThrChan,
FIXP_DBL *const pBitresFac,
INT *const pBitresFac_e) {
const BRES_PARAM *bresParam;
INT pex;
FIXP_DBL fillLevel;
INT fillLevel_e = 0;
FIXP_DBL bitresFac;
INT bitresFac_e;
FIXP_DBL bitSave, bitSpend;
FIXP_DBL bitsave_slope, bitspend_slope;
FIXP_DBL fillLevel_fix = MAXVAL_DBL;
FIXP_DBL slope = MAXVAL_DBL;
if (lastWindowSequence != SHORT_WINDOW) {
bresParam = &(AdjThr->bresParamLong);
bitsave_slope = FL2FXCONST_DBL(0.466666666);
bitspend_slope = FL2FXCONST_DBL(0.666666666);
} else {
bresParam = &(AdjThr->bresParamShort);
bitsave_slope = (FIXP_DBL)0x2E8BA2E9;
bitspend_slope = (FIXP_DBL)0x7fffffff;
}
// fillLevel = (float)(bitresBits+avgBits) / (float)(maxBitresBits + avgBits);
if (bitresBits < maxBitresBits) {
fillLevel_fix = fDivNorm(bitresBits, maxBitresBits);
}
pex = fMax(pe, adjThrChan->peMin);
pex = fMin(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);
slope = schur_div((pex - adjThrChan->peMin),
(adjThrChan->peMax - adjThrChan->peMin), 31);
/* scale down by 1 bit because the result of the following addition can be
* bigger than 1 (though smaller than 2) */
bitresFac = ((FIXP_DBL)(MAXVAL_DBL >> 1) - (bitSave >> 1));
bitresFac_e = 1; /* exp=1 */
bitresFac = fMultAddDiv2(bitresFac, slope, bitSpend + bitSave); /* exp=1 */
/*** limit bitresFac for small bitreservoir ***/
fillLevel = fDivNorm(bitresBits, avgBits, &fillLevel_e);
if (fillLevel_e < 0) {
fillLevel = scaleValue(fillLevel, fillLevel_e);
fillLevel_e = 0;
}
/* shift down value by 1 because of summation, ... */
fillLevel >>= 1;
fillLevel_e += 1;
/* ..., this summation: */
fillLevel += scaleValue(FL2FXCONST_DBL(0.7f), -fillLevel_e);
/* set bitresfactor to same exponent as fillLevel */
if (scaleValue(bitresFac, -fillLevel_e + 1) > fillLevel) {
bitresFac = fillLevel;
bitresFac_e = fillLevel_e;
}
/* limit bitresFac for high bitrates */
if (scaleValue(bitresFac, bitresFac_e - (DFRACT_BITS - 1 - 24)) > maxBitFac) {
bitresFac = maxBitFac;
bitresFac_e = (DFRACT_BITS - 1 - 24);
}
FDKaacEnc_adjustPeMinMax(pe, &adjThrChan->peMin, &adjThrChan->peMax);
/* output values */
*pBitresFac = bitresFac;
*pBitresFac_e = bitresFac_e;
}
/*****************************************************************************
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 *const hAdjThr, const INT meanPe, const INT invQuant,
const CHANNEL_MAPPING *const channelMapping, const INT sampleRate,
const INT totalBitrate, const INT isLowDelay,
const AACENC_BITRES_MODE bitResMode, const INT dZoneQuantEnable,
const INT bitDistributionMode, const FIXP_DBL vbrQualFactor) {
INT i;
FIXP_DBL POINT8 = FL2FXCONST_DBL(0.8f);
FIXP_DBL POINT6 = FL2FXCONST_DBL(0.6f);
if (bitDistributionMode == 1) {