/* ----------------------------------------------------------------------------- Software License for The Fraunhofer FDK AAC Codec Library for Android © Copyright 1995 - 2020 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 ----------------------------------------------------------------------------- */ /**************************** SBR encoder library ****************************** Author(s): Description: *******************************************************************************/ #include "env_est.h" #include "tran_det.h" #include "qmf.h" #include "fram_gen.h" #include "bit_sbr.h" #include "cmondata.h" #include "sbrenc_ram.h" #include "genericStds.h" #define QUANT_ERROR_THRES 200 #define Y_NRG_SCALE 5 /* noCols = 32 -> shift(5) */ #define MAX_NRG_SLOTS_LD 16 static const UCHAR panTable[2][10] = {{0, 2, 4, 6, 8, 12, 16, 20, 24}, {0, 2, 4, 8, 12, 0, 0, 0, 0}}; static const UCHAR maxIndex[2] = {9, 5}; /****************************************************************************** Functionname: FDKsbrEnc_GetTonality ******************************************************************************/ /***************************************************************************/ /*! \brief Calculates complete energy per band from the energy values of the QMF subsamples. \brief quotaMatrix - calculated in FDKsbrEnc_CalculateTonalityQuotas() \brief noEstPerFrame - number of estimations per frame \brief startIndex - start index for the quota matrix \brief Energies - energy matrix \brief startBand - start band \brief stopBand - number of QMF bands \brief numberCols - number of QMF subsamples \return mean tonality of the 5 bands with the highest energy scaled by 2^(RELAXATION_SHIFT+2)*RELAXATION_FRACT ****************************************************************************/ static FIXP_DBL FDKsbrEnc_GetTonality(const FIXP_DBL *const *quotaMatrix, const INT noEstPerFrame, const INT startIndex, const FIXP_DBL *const *Energies, const UCHAR startBand, const INT stopBand, const INT numberCols) { UCHAR b, e, k; INT no_enMaxBand[SBR_MAX_ENERGY_VALUES] = {-1, -1, -1, -1, -1}; FIXP_DBL energyMax[SBR_MAX_ENERGY_VALUES] = { FL2FXCONST_DBL(0.0f), FL2FXCONST_DBL(0.0f), FL2FXCONST_DBL(0.0f), FL2FXCONST_DBL(0.0f), FL2FXCONST_DBL(0.0f)}; FIXP_DBL energyMaxMin = MAXVAL_DBL; /* min. energy in energyMax array */ UCHAR posEnergyMaxMin = 0; /* min. energy in energyMax array position */ FIXP_DBL tonalityBand[SBR_MAX_ENERGY_VALUES] = { FL2FXCONST_DBL(0.0f), FL2FXCONST_DBL(0.0f), FL2FXCONST_DBL(0.0f), FL2FXCONST_DBL(0.0f), FL2FXCONST_DBL(0.0f)}; FIXP_DBL globalTonality = FL2FXCONST_DBL(0.0f); FIXP_DBL energyBand[64]; INT maxNEnergyValues; /* max. number of max. energy values */ /*** Sum up energies for each band ***/ FDK_ASSERT(numberCols == 15 || numberCols == 16); /* numberCols is always 15 or 16 for ELD. In case of 16 bands, the energyBands are initialized with the [15]th column. The rest of the column energies are added in the next step. */ if (numberCols == 15) { for (b = startBand; b < stopBand; b++) { energyBand[b] = FL2FXCONST_DBL(0.0f); } } else { for (b = startBand; b < stopBand; b++) { energyBand[b] = Energies[15][b] >> 4; } } for (k = 0; k < 15; k++) { for (b = startBand; b < stopBand; b++) { energyBand[b] += Energies[k][b] >> 4; } } /*** Determine 5 highest band-energies ***/ maxNEnergyValues = fMin(SBR_MAX_ENERGY_VALUES, stopBand - startBand); /* Get min. value in energyMax array */ energyMaxMin = energyMax[0] = energyBand[startBand]; no_enMaxBand[0] = startBand; posEnergyMaxMin = 0; for (k = 1; k < maxNEnergyValues; k++) { energyMax[k] = energyBand[startBand + k]; no_enMaxBand[k] = startBand + k; if (energyMaxMin > energyMax[k]) { energyMaxMin = energyMax[k]; posEnergyMaxMin = k; } } for (b = startBand + maxNEnergyValues; b < stopBand; b++) { if (energyBand[b] > energyMaxMin) { energyMax[posEnergyMaxMin] = energyBand[b]; no_enMaxBand[posEnergyMaxMin] = b; /* Again, get min. value in energyMax array */ energyMaxMin = energyMax[0]; posEnergyMaxMin = 0; for (k = 1; k < maxNEnergyValues; k++) { if (energyMaxMin > energyMax[k]) { energyMaxMin = energyMax[k]; posEnergyMaxMin = k; } } } } /*** End determine 5 highest band-energies ***/ /* Get tonality values for 5 highest energies */ for (e = 0; e < maxNEnergyValues; e++) { tonalityBand[e] = FL2FXCONST_DBL(0.0f); for (k = 0; k < noEstPerFrame; k++) { tonalityBand[e] += quotaMatrix[startIndex + k][no_enMaxBand[e]] >> 1; } globalTonality += tonalityBand[e] >> 2; /* headroom of 2+1 (max. 5 additions) */ } return globalTonality; } /***************************************************************************/ /*! \brief Calculates energy form real and imaginary part of the QMF subsamples \return none ****************************************************************************/ LNK_SECTION_CODE_L1 static void FDKsbrEnc_getEnergyFromCplxQmfData( FIXP_DBL **RESTRICT energyValues, /*!< the result of the operation */ FIXP_DBL **RESTRICT realValues, /*!< the real part of the QMF subsamples */ FIXP_DBL **RESTRICT imagValues, /*!< the imaginary part of the QMF subsamples */ INT numberBands, /*!< number of QMF bands */ INT numberCols, /*!< number of QMF subsamples */ INT *qmfScale, /*!< sclefactor of QMF subsamples */ INT *energyScale) /*!< scalefactor of energies */ { int j, k; int scale; FIXP_DBL max_val = FL2FXCONST_DBL(0.0f); /* Get Scratch buffer */ C_ALLOC_SCRATCH_START(tmpNrg, FIXP_DBL, 32 * 64 / 2) /* Get max possible scaling of QMF data */ scale = DFRACT_BITS; for (k = 0; k < numberCols; k++) { scale = fixMin(scale, fixMin(getScalefactor(realValues[k], numberBands), getScalefactor(imagValues[k], numberBands))); } /* Tweak scaling stability for zero signal to non-zero signal transitions */ if (scale >= DFRACT_BITS - 1) { scale = (FRACT_BITS - 1 - *qmfScale); } /* prevent scaling of QMF values to -1.f */ scale = fixMax(0, scale - 1); /* Update QMF scale */ *qmfScale += scale; /* Calculate energy of each time slot pair, max energy and shift QMF values as far as possible to the left. */ { FIXP_DBL *nrgValues = tmpNrg; for (k = 0; k < numberCols; k += 2) { /* Load band vector addresses of 2 consecutive timeslots */ FIXP_DBL *RESTRICT r0 = realValues[k]; FIXP_DBL *RESTRICT i0 = imagValues[k]; FIXP_DBL *RESTRICT r1 = realValues[k + 1]; FIXP_DBL *RESTRICT i1 = imagValues[k + 1]; for (j = 0; j < numberBands; j++) { FIXP_DBL energy; FIXP_DBL tr0, tr1, ti0, ti1; /* Read QMF values of 2 timeslots */ tr0 = r0[j]; tr1 = r1[j]; ti0 = i0[j]; ti1 = i1[j]; /* Scale QMF Values and Calc Energy average of both timeslots */ tr0 <<= scale; ti0 <<= scale; energy = fPow2AddDiv2(fPow2Div2(tr0), ti0) >> 1; tr1 <<= scale; ti1 <<= scale; energy += fPow2AddDiv2(fPow2Div2(tr1), ti1) >> 1; /* Write timeslot pair energy to scratch */ *nrgValues++ = energy; max_val = fixMax(max_val, energy); /* Write back scaled QMF values */ r0[j] = tr0; r1[j] = tr1; i0[j] = ti0; i1[j] = ti1; } } } /* energyScale: scalefactor energies of current frame */ *energyScale = 2 * (*qmfScale) - 1; /* if qmfScale > 0: nr of right shifts otherwise nr of left shifts */ /* Scale timeslot pair energies and write to output buffer */ scale = CountLeadingBits(max_val); { FIXP_DBL *nrgValues = tmpNrg; for (k = 0; k> 1; k++) { scaleValues(energyValues[k], nrgValues, numberBands, scale); nrgValues += numberBands; } *energyScale += scale; } /* Free Scratch buffer */ C_ALLOC_SCRATCH_END(tmpNrg, FIXP_DBL, 32 * 64 / 2) } LNK_SECTION_CODE_L1 static void FDKsbrEnc_getEnergyFromCplxQmfDataFull( FIXP_DBL **RESTRICT energyValues, /*!< the result of the operation */ FIXP_DBL **RESTRICT realValues, /*!< the real part of the QMF subsamples */ FIXP_DBL **RESTRICT imagValues, /*!< the imaginary part of the QMF subsamples */ int numberBands, /*!< number of QMF bands */ int numberCols, /*!< number of QMF subsamples */ int *qmfScale, /*!< scalefactor of QMF subsamples */ int *energyScale) /*!< scalefactor of energies */ { int j, k; int scale; FIXP_DBL max_val = FL2FXCONST_DBL(0.0f); /* Get Scratch buffer */ C_ALLOC_SCRATCH_START(tmpNrg, FIXP_DBL, MAX_NRG_SLOTS_LD * 64) FDK_ASSERT(numberCols <= MAX_NRG_SLOTS_LD); FDK_ASSERT(numberBands <= 64); /* Get max possible scaling of QMF data */ scale = DFRACT_BITS; for (k = 0; k < numberCols; k++) { scale = fixMin(scale, fixMin(getScalefactor(realValues[k], numberBands), getScalefactor(imagValues[k], numberBands))); } /* Tweak scaling stability for zero signal to non-zero signal transitions */ if (scale >= DFRACT_BITS - 1) { scale = (FRACT_BITS - 1 - *qmfScale); } /* prevent scaling of QFM values to -1.f */ scale = fixMax(0, scale - 1); /* Update QMF scale */ *qmfScale += scale; /* Calculate energy of each time slot pair, max energy and shift QMF values as far as possible to the left. */ { FIXP_DBL *nrgValues = tmpNrg; for (k = 0; k < numberCols; k++) { /* Load band vector addresses of 1 timeslot */ FIXP_DBL *RESTRICT r0 = realValues[k]; FIXP_DBL *RESTRICT i0 = imagValues[k]; for (j = 0; j < numberBands; j++) { FIXP_DBL energy; FIXP_DBL tr0, ti0; /* Read QMF values of 1 timeslot */ tr0 = r0[j]; ti0 = i0[j]; /* Scale QMF Values and Calc Energy */ tr0 <<= scale; ti0 <<= scale; energy = fPow2AddDiv2(fPow2Div2(tr0), ti0); *nrgValues++ = energy; max_val = fixMax(max_val, energy); /* Write back scaled QMF values */ r0[j] = tr0; i0[j] = ti0; } } } /* energyScale: scalefactor energies of current frame */ *energyScale = 2 * (*qmfScale) - 1; /* if qmfScale > 0: nr of right shifts otherwise nr of left shifts */ /* Scale timeslot pair energies and write to output buffer */ scale = CountLeadingBits(max_val); { FIXP_DBL *nrgValues = tmpNrg; for (k = 0; k < numberCols; k++) { scaleValues(energyValues[k], nrgValues, numberBands, scale); nrgValues += numberBands; } *energyScale += scale; } /* Free Scratch buffer */ C_ALLOC_SCRATCH_END(tmpNrg, FIXP_DBL, MAX_NRG_SLOTS_LD * 64) } /***************************************************************************/ /*! \brief Quantisation of the panorama value (balance) \return the quantized pan value ****************************************************************************/ static INT mapPanorama(INT nrgVal, /*! integer value of the energy */ INT ampRes, /*! amplitude resolution [1.5/3dB] */ INT *quantError /*! quantization error of energy val*/ ) { int i; INT min_val, val; UCHAR panIndex; INT sign; sign = nrgVal > 0 ? 1 : -1; nrgVal *= sign; min_val = FDK_INT_MAX; panIndex = 0; for (i = 0; i < maxIndex[ampRes]; i++) { val = fixp_abs((nrgVal - (INT)panTable[ampRes][i])); if (val < min_val) { min_val = val; panIndex = i; } } *quantError = min_val; return panTable[ampRes][maxIndex[ampRes] - 1] + sign * panTable[ampRes][panIndex]; } /***************************************************************************/ /*! \brief Quantisation of the noise floor levels \return void ****************************************************************************/ static void sbrNoiseFloorLevelsQuantisation( SCHAR *RESTRICT iNoiseLevels, /*! quantized noise levels */ FIXP_DBL *RESTRICT NoiseLevels, /*! the noise levels. Exponent = LD_DATA_SHIFT */ INT coupling /*! the coupling flag */ ) { INT i; INT tmp, dummy; /* Quantisation, similar to sfb quant... */ for (i = 0; i < MAX_NUM_NOISE_VALUES; i++) { /* tmp = NoiseLevels[i] > (PFLOAT)30.0f ? 30: (INT) (NoiseLevels[i] + * (PFLOAT)0.5); */ /* 30>>LD_DATA_SHIFT = 0.46875 */ if ((FIXP_DBL)NoiseLevels[i] > FL2FXCONST_DBL(0.46875f)) { tmp = 30; } else { /* tmp = (INT)((FIXP_DBL)NoiseLevels[i] + (FL2FXCONST_DBL(0.5f)>>(*/ /* FRACT_BITS+ */ /* 6-1)));*/ /* tmp = tmp >> (DFRACT_BITS-1-LD_DATA_SHIFT); */ /* conversion to integer happens here */ /* rounding is done by shifting one bit less than necessary to the right, * adding '1' and then shifting the final bit */ tmp = ((((INT)NoiseLevels[i]) >> (DFRACT_BITS - 1 - LD_DATA_SHIFT))); /* conversion to integer */ if (tmp != 0) tmp += 1; } if (coupling) { tmp = tmp < -30 ? -30 : tmp; tmp = mapPanorama(tmp, 1, &dummy); } iNoiseLevels[i] = tmp; } } /***************************************************************************/ /*! \brief Calculation of noise floor for coupling \return void ****************************************************************************/ static void coupleNoiseFloor( FIXP_DBL *RESTRICT noise_level_left, /*! noise level left (modified)*/ FIXP_DBL *RESTRICT noise_level_right /*! noise level right (modified)*/ ) { FIXP_DBL cmpValLeft, cmpValRight; INT i; FIXP_DBL temp1, temp2; for (i = 0; i < MAX_NUM_NOISE_VALUES; i++) { /* Calculation of the power function using ld64: z = x^y; z' = CalcLd64(z) = y*CalcLd64(x)/64; z = CalcInvLd64(z'); */ cmpValLeft = NOISE_FLOOR_OFFSET_64 - noise_level_left[i]; cmpValRight = NOISE_FLOOR_OFFSET_64 - noise_level_right[i]; if (cmpValRight < FL2FXCONST_DBL(0.0f)) { temp1 = CalcInvLdData(NOISE_FLOOR_OFFSET_64 - noise_level_right[i]); } else { temp1 = CalcInvLdData(NOISE_FLOOR_OFFSET_64 - noise_level_right[i]); temp1 = temp1 << (DFRACT_BITS - 1 - LD_DATA_SHIFT - 1); /* INT to fract conversion of result, if input of CalcInvLdData is positiv */ } if (cmpValLeft < FL2FXCONST_DBL(0.0f)) { temp2 = CalcInvLdData(NOISE_FLOOR_OFFSET_64 - noise_level_left[i]); } else { temp2 = CalcInvLdData(NOISE_FLOOR_OFFSET_64 - noise_level_left[i]); temp2 = temp2 << (DFRACT_BITS - 1 - LD_DATA_SHIFT - 1); /* INT to fract conversion of result, if input of CalcInvLdData is positiv */ } if ((cmpValLeft < FL2FXCONST_DBL(0.0f)) && (cmpValRight < FL2FXCONST_DBL(0.0f))) { noise_level_left[i] = NOISE_FLOOR_OFFSET_64 - (CalcLdData( ((temp1 >> 1) + (temp2 >> 1)))); /* no scaling needed! both values are dfract */ noise_level_right[i] = CalcLdData(temp2) - CalcLdData(temp1); } if ((cmpValLeft >= FL2FXCONST_DBL(0.0f)) && (cmpValRight >= FL2FXCONST_DBL(0.0f))) { noise_level_left[i] = NOISE_FLOOR_OFFSET_64 - (CalcLdData(((temp1 >> 1) + (temp2 >> 1))) + FL2FXCONST_DBL(0.109375f)); /* scaled with 7/64 */ noise_level_right[i] = CalcLdData(temp2) - CalcLdData(temp1); } if ((cmpValLeft >= FL2FXCONST_DBL(0.0f)) && (cmpValRight < FL2FXCONST_DBL(0.0f))) { noise_level_left[i] = NOISE_FLOOR_OFFSET_64 - (CalcLdData(((temp1 >> (7 + 1)) + (temp2 >> 1))) + FL2FXCONST_DBL(0.109375f)); /* scaled with 7/64 */ noise_level_right[i] = (CalcLdData(temp2) + FL2FXCONST_DBL(0.109375f)) - CalcLdData(temp1); } if ((cmpValLeft < FL2FXCONST_DBL(0.0f)) && (cmpValRight >= FL2FXCONST_DBL(0.0f))) { noise_level_left[i] = NOISE_FLOOR_OFFSET_64 - (CalcLdData(((temp1 >> 1) + (temp2 >> (7 + 1)))) + FL2FXCONST_DBL(0.109375f)); /* scaled with 7/64 */ noise_level_right[i] = CalcLdData(temp2) - (CalcLdData(temp1) + FL2FXCONST_DBL(0.109375f)); /* scaled with 7/64 */ } } } /***************************************************************************/ /*! \brief Calculation of energy starting in lower band (li) up to upper band (ui) over slots (start_pos) to (stop_pos) \return void ****************************************************************************/ static FIXP_DBL getEnvSfbEnergy( INT li, /*! lower band */ INT ui, /*! upper band */ INT start_pos, /*! start slot */ INT stop_pos, /*! stop slot */ INT border_pos, /*! slots scaling border */ FIXP_DBL **YBuffer, /*! sfb energy buffer */ INT YBufferSzShift, /*! Energy buffer index scale */ INT scaleNrg0, /*! scaling of lower slots */ INT scaleNrg1) /*! scaling of upper slots */ { /* use dynamic scaling for outer energy loop; energies are critical and every bit is important */ int sc0, sc1, k, l; FIXP_DBL nrgSum, nrg1, nrg2, accu1, accu2; INT dynScale, dynScale1, dynScale2; if (ui - li == 0) dynScale = DFRACT_BITS - 1; else dynScale = CalcLdInt(ui - li) >> (DFRACT_BITS - 1 - LD_DATA_SHIFT); sc0 = fixMin(scaleNrg0, Y_NRG_SCALE); sc1 = fixMin(scaleNrg1, Y_NRG_SCALE); /* dynScale{1,2} is set such that the right shift below is positive */ dynScale1 = fixMin((scaleNrg0 - sc0), dynScale); dynScale2 = fixMin((scaleNrg1 - sc1), dynScale); nrgSum = accu1 = accu2 = (FIXP_DBL)0; for (k = li; k < ui; k++) { nrg1 = nrg2 = (FIXP_DBL)0; for (l = start_pos; l < border_pos; l++) { nrg1 += YBuffer[l >> YBufferSzShift][k] >> sc0; } for (; l < stop_pos; l++) { nrg2 += YBuffer[l >> YBufferSzShift][k] >> sc1; } accu1 = fAddSaturate(accu1, (nrg1 >> dynScale1)); accu2 = fAddSaturate(accu2, (nrg2 >> dynScale2)); } /* This shift factor is always positive. See comment above. */ nrgSum += (accu1 >> fixMin((scaleNrg0 - sc0 - dynScale1), (DFRACT_BITS - 1))) + (accu2 >> fixMin((scaleNrg1 - sc1 - dynScale2), (DFRACT_BITS - 1))); return nrgSum; } /***************************************************************************/ /*! \brief Energy compensation in missing harmonic mode \return void ****************************************************************************/ static FIXP_DBL mhLoweringEnergy(FIXP_DBL nrg, INT M) { /* Compensating for the fact that we in the decoder map the "average energy to every QMF band, and use this when we calculate the boost-factor. Since the mapped energy isn't the average energy but the maximum energy in case of missing harmonic creation, we will in the boost function calculate that too much limiting has been applied and hence we will boost the signal although it isn't called for. Hence we need to compensate for this by lowering the transmitted energy values for the sines so they will get the correct level after the boost is applied. */ if (M > 2) { INT tmpScale; tmpScale = CountLeadingBits(nrg); nrg <<= tmpScale; nrg = fMult(nrg, FL2FXCONST_DBL(0.398107267f)); /* The maximum boost is 1.584893, so the maximum attenuation should be square(1/1.584893) = 0.398107267 */ nrg >>= tmpScale; } else { if (M > 1) { nrg >>= 1; } } return nrg; } /***************************************************************************/ /*! \brief Energy compensation in none missing harmonic mode \return void ****************************************************************************/ static FIXP_DBL nmhLoweringEnergy(FIXP_DBL nrg, const FIXP_DBL nrgSum, const INT nrgSum_scale, const INT M) { if (nrg > FL2FXCONST_DBL(0)) { int sc = 0; /* gain = nrgSum / (nrg*(M+1)) */ FIXP_DBL gain = fMult(fDivNorm(nrgSum, nrg, &sc), GetInvInt(M + 1)); sc += nrgSum_scale; /* reduce nrg if gain smaller 1.f */ if (!((sc >= 0) && (gain > ((FIXP_DBL)MAXVAL_DBL >> sc)))) { nrg = fMult(scaleValue(gain, sc), nrg); } } return nrg; } /***************************************************************************/ /*! \brief calculates the envelope values from the energies, depending on framing and stereo mode \return void ****************************************************************************/ static void calculateSbrEnvelope( FIXP_DBL **RESTRICT YBufferLeft, /*! energy buffer left */ FIXP_DBL **RESTRICT YBufferRight, /*! energy buffer right */ int *RESTRICT YBufferScaleLeft, /*! scale energy buffer left */ int *RESTRICT YBufferScaleRight, /*! scale energy buffer right */ const SBR_FRAME_INFO *frame_info, /*! frame info vector */ SCHAR *RESTRICT sfb_nrgLeft, /*! sfb energy buffer left */ SCHAR *RESTRICT sfb_nrgRight, /*! sfb energy buffer right */ HANDLE_SBR_CONFIG_DATA h_con, /*! handle to config data */ HANDLE_ENV_CHANNEL h_sbr, /*! envelope channel handle */ SBR_STEREO_MODE stereoMode, /*! stereo coding mode */ INT *maxQuantError, /*! maximum quantization error, for panorama. */ int YBufferSzShift) /*! Energy buffer index scale */ { int env, j, m = 0; INT no_of_bands, start_pos, stop_pos, li, ui; FREQ_RES freq_res; INT ca = 2 - h_sbr->encEnvData.init_sbr_amp_res; INT oneBitLess = 0; if (ca == 2) oneBitLess = 1; /* LD_DATA_SHIFT => ld64 scaling; one bit less for rounding */ INT quantError; INT nEnvelopes = frame_info->nEnvelopes; INT short_env = frame_info->shortEnv - 1; INT timeStep = h_sbr->sbrExtractEnvelope.time_step; INT commonScale, scaleLeft0, scaleLeft1; INT scaleRight0 = 0, scaleRight1 = 0; commonScale = fixMin(YBufferScaleLeft[0], YBufferScaleLeft[1]); if (stereoMode == SBR_COUPLING) { commonScale = fixMin(commonScale, YBufferScaleRight[0]); commonScale = fixMin(commonScale, YBufferScaleRight[1]); } commonScale = commonScale - 7; scaleLeft0 = YBufferScaleLeft[0] - commonScale; scaleLeft1 = YBufferScaleLeft[1] - commonScale; FDK_ASSERT((scaleLeft0 >= 0) && (scaleLeft1 >= 0)); if (stereoMode == SBR_COUPLING) { scaleRight0 = YBufferScaleRight[0] - commonScale; scaleRight1 = YBufferScaleRight[1] - commonScale; FDK_ASSERT((scaleRight0 >= 0) && (scaleRight1 >= 0)); *maxQuantError = 0; } for (env = 0; env < nEnvelopes; env++) { FIXP_DBL pNrgLeft[32]; FIXP_DBL pNrgRight[32]; int envNrg_scale; FIXP_DBL envNrgLeft = FL2FXCONST_DBL(0.0f); FIXP_DBL envNrgRight = FL2FXCONST_DBL(0.0f); int missingHarmonic[32]; int count[32]; start_pos = timeStep * frame_info->borders[env]; stop_pos = timeStep * frame_info->borders[env + 1]; freq_res = frame_info->freqRes[env]; no_of_bands = h_con->nSfb[freq_res]; envNrg_scale = DFRACT_BITS - fNormz((FIXP_DBL)no_of_bands); if (env == short_env) { j = fMax(2, timeStep); /* consider at least 2 QMF slots less for short envelopes (envelopes just before transients) */ if ((stop_pos - start_pos - j) > 0) { stop_pos = stop_pos - j; } } for (j = 0; j < no_of_bands; j++) { FIXP_DBL nrgLeft = FL2FXCONST_DBL(0.0f); FIXP_DBL nrgRight = FL2FXCONST_DBL(0.0f); li = h_con->freqBandTable[freq_res][j]; ui = h_con->freqBandTable[freq_res][j + 1]; if (freq_res == FREQ_RES_HIGH) { if (j == 0 && ui - li > 1) { li++; } } else { if (j == 0 && ui - li > 2) { li++; } } /* Find out whether a sine will be missing in the scale-factor band that we're currently processing. */ missingHarmonic[j] = 0; if (h_sbr->encEnvData.addHarmonicFlag) { if (freq_res == FREQ_RES_HIGH) { if (h_sbr->encEnvData .addHarmonic[j]) { /*A missing sine in the current band*/ missingHarmonic[j] = 1; } } else { INT i; INT startBandHigh = 0; INT stopBandHigh = 0; while (h_con->freqBandTable[FREQ_RES_HIGH][startBandHigh] < h_con->freqBandTable[FREQ_RES_LOW][j]) startBandHigh++; while (h_con->freqBandTable[FREQ_RES_HIGH][stopBandHigh] < h_con->freqBandTable[FREQ_RES_LOW][j + 1]) stopBandHigh++; for (i = startBandHigh; i < stopBandHigh; i++) { if (h_sbr->encEnvData.addHarmonic[i]) { missingHarmonic[j] = 1; } } } } /* If a sine is missing in a scalefactorband, with more than one qmf channel use the nrg from the channel with the largest nrg rather than the mean. Compensate for the boost calculation in the decdoder. */ int border_pos = fixMin(stop_pos, h_sbr->sbrExtractEnvelope.YBufferWriteOffset << YBufferSzShift); if (missingHarmonic[j]) { int k; count[j] = stop_pos - start_pos; nrgLeft = FL2FXCONST_DBL(0.0f); for (k = li; k < ui; k++) { FIXP_DBL tmpNrg; tmpNrg = getEnvSfbEnergy(k, k + 1, start_pos, stop_pos, border_pos, YBufferLeft, YBufferSzShift, scaleLeft0, scaleLeft1); nrgLeft = fixMax(nrgLeft, tmpNrg); } /* Energy lowering compensation */ nrgLeft = mhLoweringEnergy(nrgLeft, ui - li); if (stereoMode == SBR_COUPLING) { nrgRight = FL2FXCONST_DBL(0.0f); for (k = li; k < ui; k++) { FIXP_DBL tmpNrg; tmpNrg = getEnvSfbEnergy(k, k + 1, start_pos, stop_pos, border_pos, YBufferRight, YBufferSzShift, scaleRight0, scaleRight1); nrgRight = fixMax(nrgRight, tmpNrg); } /* Energy lowering compensation */ nrgRight = mhLoweringEnergy(nrgRight, ui - li); } } /* end missingHarmonic */ else { count[j] = (stop_pos - start_pos) * (ui - li); nrgLeft = getEnvSfbEnergy(li, ui, start_pos, stop_pos, border_pos, YBufferLeft, YBufferSzShift, scaleLeft0, scaleLeft1); if (stereoMode == SBR_COUPLING) { nrgRight = getEnvSfbEnergy(li, ui, start_pos, stop_pos, border_pos, YBufferRight, YBufferSzShift, scaleRight0, scaleRight1); } } /* !missingHarmonic */ /* save energies */ pNrgLeft[j] = nrgLeft; pNrgRight[j] = nrgRight; envNrgLeft += (nrgLeft >> envNrg_scale); envNrgRight += (nrgRight >> envNrg_scale); } /* j */ for (j = 0; j < no_of_bands; j++) { FIXP_DBL nrgLeft2 = FL2FXCONST_DBL(0.0f); FIXP_DBL nrgLeft = pNrgLeft[j]; FIXP_DBL nrgRight = pNrgRight[j]; /* None missing harmonic Energy lowering compensation */ if (!missingHarmonic[j] && h_sbr->fLevelProtect) { /* in case of missing energy in base band, reduce reference energy to prevent overflows in decoder output */ nrgLeft = nmhLoweringEnergy(nrgLeft, envNrgLeft, envNrg_scale, no_of_bands); if (stereoMode == SBR_COUPLING) { nrgRight = nmhLoweringEnergy(nrgRight, envNrgRight, envNrg_scale, no_of_bands); } } if (stereoMode == SBR_COUPLING) { /* calc operation later with log */ nrgLeft2 = nrgLeft; nrgLeft = (nrgRight + nrgLeft) >> 1; } /* nrgLeft = f20_log2(nrgLeft / (PFLOAT)(count * 64))+(PFLOAT)44; */ /* If nrgLeft == 0 then the Log calculations below do fail. */ if (nrgLeft > FL2FXCONST_DBL(0.0f)) { FIXP_DBL tmp0, tmp1, tmp2, tmp3; INT tmpScale; tmpScale = CountLeadingBits(nrgLeft); nrgLeft = nrgLeft << tmpScale; tmp0 = CalcLdData(nrgLeft); /* scaled by 1/64 */ tmp1 = ((FIXP_DBL)(commonScale + tmpScale)) << (DFRACT_BITS - 1 - LD_DATA_SHIFT - 1); /* scaled by 1/64 */ tmp2 = ((FIXP_DBL)(count[j] * 64)) << (DFRACT_BITS - 1 - 14 - 1); tmp2 = CalcLdData(tmp2); /* scaled by 1/64 */ tmp3 = FL2FXCONST_DBL(0.6875f - 0.21875f - 0.015625f) >> 1; /* scaled by 1/64 */ nrgLeft = ((tmp0 - tmp2) >> 1) + (tmp3 - tmp1); } else { nrgLeft = FL2FXCONST_DBL(-1.0f); } /* ld64 to integer conversion */ nrgLeft = fixMin(fixMax(nrgLeft, FL2FXCONST_DBL(0.0f)), (FL2FXCONST_DBL(0.5f) >> oneBitLess)); nrgLeft = (FIXP_DBL)(LONG)nrgLeft >> (DFRACT_BITS - 1 - LD_DATA_SHIFT - 1 - oneBitLess - 1); sfb_nrgLeft[m] = ((INT)nrgLeft + 1) >> 1; /* rounding */ if (stereoMode == SBR_COUPLING) { FIXP_DBL scaleFract; int sc0, sc1; nrgLeft2 = fixMax((FIXP_DBL)0x1, nrgLeft2); nrgRight = fixMax((FIXP_DBL)0x1, nrgRight); sc0 = CountLeadingBits(nrgLeft2); sc1 = CountLeadingBits(nrgRight); scaleFract = ((FIXP_DBL)(sc0 - sc1)) << (DFRACT_BITS - 1 - LD_DATA_SHIFT); /* scale value in ld64 representation */ nrgRight = CalcLdData(nrgLeft2 << sc0) - CalcLdData(nrgRight << sc1) - scaleFract; /* ld64 to integer conversion */ nrgRight = (FIXP_DBL)(LONG)(nrgRight) >> (DFRACT_BITS - 1 - LD_DATA_SHIFT - 1 - oneBitLess); nrgRight = (nrgRight + (FIXP_DBL)1) >> 1; /* rounding */ sfb_nrgRight[m] = mapPanorama( nrgRight, h_sbr->encEnvData.init_sbr_amp_res, &quantError); *maxQuantError = fixMax(quantError, *maxQuantError); } m++; } /* j */ /* Do energy compensation for sines that are present in two QMF-bands in the original, but will only occur in one band in the decoder due to the synthetic sine coding.*/ if (h_con->useParametricCoding) { m -= no_of_bands; for (j = 0; j < no_of_bands; j++) { if (freq_res == FREQ_RES_HIGH && h_sbr->sbrExtractEnvelope.envelopeCompensation[j]) { sfb_nrgLeft[m] -= (ca * fixp_abs( (INT)h_sbr->sbrExtractEnvelope.envelopeCompensation[j])); } sfb_nrgLeft[m] = fixMax(0, sfb_nrgLeft[m]); m++; } } /* useParametricCoding */ } /* env loop */ } /***************************************************************************/ /*! \brief calculates the noise floor and the envelope values from the energies, depending on framing and stereo mode FDKsbrEnc_extractSbrEnvelope is the main function for encoding and writing the envelope and the noise floor. The function includes the following processes: -Analysis subband filtering. -Encoding SA and pan parameters (if enabled). -Transient detection. ****************************************************************************/ LNK_SECTION_CODE_L1 void FDKsbrEnc_extractSbrEnvelope1( HANDLE_SBR_CONFIG_DATA h_con, /*! handle to config data */ HANDLE_SBR_HEADER_DATA sbrHeaderData, HANDLE_SBR_BITSTREAM_DATA sbrBitstreamData, HANDLE_ENV_CHANNEL hEnvChan, HANDLE_COMMON_DATA hCmonData, SBR_ENV_TEMP_DATA *eData, SBR_FRAME_TEMP_DATA *fData) { HANDLE_SBR_EXTRACT_ENVELOPE sbrExtrEnv = &hEnvChan->sbrExtractEnvelope; if (sbrExtrEnv->YBufferSzShift == 0) FDKsbrEnc_getEnergyFromCplxQmfDataFull( &sbrExtrEnv->YBuffer[sbrExtrEnv->YBufferWriteOffset], sbrExtrEnv->rBuffer + sbrExtrEnv->rBufferReadOffset, sbrExtrEnv->iBuffer + sbrExtrEnv->rBufferReadOffset, h_con->noQmfBands, sbrExtrEnv->no_cols, &hEnvChan->qmfScale, &sbrExtrEnv->YBufferScale[1]); else FDKsbrEnc_getEnergyFromCplxQmfData( &sbrExtrEnv->YBuffer[sbrExtrEnv->YBufferWriteOffset], sbrExtrEnv->rBuffer + sbrExtrEnv->rBufferReadOffset, sbrExtrEnv->iBuffer + sbrExtrEnv->rBufferReadOffset, h_con->noQmfBands, sbrExtrEnv->no_cols, &hEnvChan->qmfScale, &sbrExtrEnv->YBufferScale[1]); /* Energie values = * sbrExtrEnv->YBuffer[sbrExtrEnv->YBufferWriteOffset][x].floatVal * * (1<<2*7-sbrExtrEnv->YBufferScale[1]) */ /* Precalculation of Tonality Quotas COEFF Transform OK */ FDKsbrEnc_CalculateTonalityQuotas( &hEnvChan->TonCorr, sbrExtrEnv->rBuffer, sbrExtrEnv->iBuffer, h_con->freqBandTable[HI][h_con->nSfb[HI]], hEnvChan->qmfScale); if (h_con->sbrSyntaxFlags & SBR_SYNTAX_LOW_DELAY) { FIXP_DBL tonality = FDKsbrEnc_GetTonality( hEnvChan->TonCorr.quotaMatrix, hEnvChan->TonCorr.numberOfEstimatesPerFrame, hEnvChan->TonCorr.startIndexMatrix, sbrExtrEnv->YBuffer + sbrExtrEnv->YBufferWriteOffset, h_con->freqBandTable[HI][0] + 1, h_con->noQmfBands, sbrExtrEnv->no_cols); hEnvChan->encEnvData.ton_HF[1] = hEnvChan->encEnvData.ton_HF[0]; hEnvChan->encEnvData.ton_HF[0] = tonality; /* tonality is scaled by 2^19/0.524288f (fract part of RELAXATION) */ hEnvChan->encEnvData.global_tonality = (hEnvChan->encEnvData.ton_HF[0] >> 1) + (hEnvChan->encEnvData.ton_HF[1] >> 1); } /* Transient detection COEFF Transform OK */ if (h_con->sbrSyntaxFlags & SBR_SYNTAX_LOW_DELAY) { FDKsbrEnc_fastTransientDetect(&hEnvChan->sbrFastTransientDetector, sbrExtrEnv->YBuffer, sbrExtrEnv->YBufferScale, sbrExtrEnv->YBufferWriteOffset, eData->transient_info); } else { FDKsbrEnc_transientDetect( &hEnvChan->sbrTransientDetector, sbrExtrEnv->YBuffer, sbrExtrEnv->YBufferScale, eData->transient_info, sbrExtrEnv->YBufferWriteOffset, sbrExtrEnv->YBufferSzShift, sbrExtrEnv->time_step, hEnvChan->SbrEnvFrame.frameMiddleSlot); } /* Generate flags for 2 env in a FIXFIX-frame. Remove this function to get always 1 env per FIXFIX-frame. */ /* frame Splitter COEFF Transform OK */ FDKsbrEnc_frameSplitter( sbrExtrEnv->YBuffer, sbrExtrEnv->YBufferScale, &hEnvChan->sbrTransientDetector, h_con->freqBandTable[1], eData->transient_info, sbrExtrEnv->YBufferWriteOffset, sbrExtrEnv->YBufferSzShift, h_con->nSfb[1], sbrExtrEnv->time_step, sbrExtrEnv->no_cols, &hEnvChan->encEnvData.global_tonality); } /***************************************************************************/ /*! \brief calculates the noise floor and the envelope values from the energies, depending on framing and stereo mode FDKsbrEnc_extractSbrEnvelope is the main function for encoding and writing the envelope and the noise floor. The function includes the following processes: -Determine time/frequency division of current granule. -Sending transient info to bitstream. -Set amp_res to 1.5 dB if the current frame contains only one envelope. -Lock dynamic bandwidth frequency change if the next envelope not starts on a frame boundary. -MDCT transposer (needed to detect where harmonics will be missing). -Spectrum Estimation (used for pulse train and missing harmonics detection). -Pulse train detection. -Inverse Filtering detection. -Waveform Coding. -Missing Harmonics detection. -Extract envelope of current frame. -Noise floor estimation. -Noise floor quantisation and coding. -Encode envelope of current frame. -Send the encoded data to the bitstream. -Write to bitstream. ****************************************************************************/ LNK_SECTION_CODE_L1 void FDKsbrEnc_extractSbrEnvelope2( HANDLE_SBR_CONFIG_DATA h_con, /*! handle to config data */ HANDLE_SBR_HEADER_DATA sbrHeaderData, HANDLE_PARAMETRIC_STEREO hParametricStereo, HANDLE_SBR_BITSTREAM_DATA sbrBitstreamData, HANDLE_ENV_CHANNEL h_envChan0, HANDLE_ENV_CHANNEL h_envChan1, HANDLE_COMMON_DATA hCmonData, SBR_ENV_TEMP_DATA *eData, SBR_FRAME_TEMP_DATA *fData, int clearOutput) { HANDLE_ENV_CHANNEL h_envChan[MAX_NUM_CHANNELS] = {h_envChan0, h_envChan1}; int ch, i, j, c, YSzShift = h_envChan[0]->sbrExtractEnvelope.YBufferSzShift; SBR_STEREO_MODE stereoMode = h_con->stereoMode; int nChannels = h_con->nChannels; const int *v_tuning; static const int v_tuningHEAAC[6] = {0, 2, 4, 0, 0, 0}; static const int v_tuningELD[6] = {0, 2, 3, 0, 0, 0}; if (h_con->sbrSyntaxFlags & SBR_SYNTAX_LOW_DELAY) v_tuning = v_tuningELD; else v_tuning = v_tuningHEAAC; /* Select stereo mode. */ if (stereoMode == SBR_COUPLING) { if (eData[0].transient_info[1] && eData[1].transient_info[1]) { eData[0].transient_info[0] = fixMin(eData[1].transient_info[0], eData[0].transient_info[0]); eData[1].transient_info[0] = eData[0].transient_info[0]; } else { if (eData[0].transient_info[1] && !eData[1].transient_info[1]) { eData[1].transient_info[0] = eData[0].transient_info[0]; } else { if (!eData[0].transient_info[1] && eData[1].transient_info[1]) eData[0].transient_info[0] = eData[1].transient_info[0]; else { eData[0].transient_info[0] = fixMax(eData[1].transient_info[0], eData[0].transient_info[0]); eData[1].transient_info[0] = eData[0].transient_info[0]; } } } } /* Determine time/frequency division of current granule */ eData[0].frame_info = FDKsbrEnc_frameInfoGenerator( &h_envChan[0]->SbrEnvFrame, eData[0].transient_info, sbrBitstreamData->rightBorderFIX, h_envChan[0]->sbrExtractEnvelope.pre_transient_info, h_envChan[0]->encEnvData.ldGrid, v_tuning); h_envChan[0]->encEnvData.hSbrBSGrid = &h_envChan[0]->SbrEnvFrame.SbrGrid; /* AAC LD patch for transient prediction */ if (h_envChan[0]->encEnvData.ldGrid && eData[0].transient_info[2]) { /* if next frame will start with transient, set shortEnv to * numEnvelopes(shortend Envelope = shortEnv-1)*/ h_envChan[0]->SbrEnvFrame.SbrFrameInfo.shortEnv = h_envChan[0]->SbrEnvFrame.SbrFrameInfo.nEnvelopes; } switch (stereoMode) { case SBR_LEFT_RIGHT: case SBR_SWITCH_LRC: eData[1].frame_info = FDKsbrEnc_frameInfoGenerator( &h_envChan[1]->SbrEnvFrame, eData[1].transient_info, sbrBitstreamData->rightBorderFIX, h_envChan[1]->sbrExtractEnvelope.pre_transient_info, h_envChan[1]->encEnvData.ldGrid, v_tuning); h_envChan[1]->encEnvData.hSbrBSGrid = &h_envChan[1]->SbrEnvFrame.SbrGrid; if (h_envChan[1]->encEnvData.ldGrid && eData[1].transient_info[2]) { /* if next frame will start with transient, set shortEnv to * numEnvelopes(shortend Envelope = shortEnv-1)*/ h_envChan[1]->SbrEnvFrame.SbrFrameInfo.shortEnv = h_envChan[1]->SbrEnvFrame.SbrFrameInfo.nEnvelopes; } /* compare left and right frame_infos */ if (eData[0].frame_info->nEnvelopes != eData[1].frame_info->nEnvelopes) { stereoMode = SBR_LEFT_RIGHT; } else { for (i = 0; i < eData[0].frame_info->nEnvelopes + 1; i++) { if (eData[0].frame_info->borders[i] != eData[1].frame_info->borders[i]) { stereoMode = SBR_LEFT_RIGHT; break; } } for (i = 0; i < eData[0].frame_info->nEnvelopes; i++) { if (eData[0].frame_info->freqRes[i] != eData[1].frame_info->freqRes[i]) { stereoMode = SBR_LEFT_RIGHT; break; } } if (eData[0].frame_info->shortEnv != eData[1].frame_info->shortEnv) { stereoMode = SBR_LEFT_RIGHT; } } break; case SBR_COUPLING: eData[1].frame_info = eData[0].frame_info; h_envChan[1]->encEnvData.hSbrBSGrid = &h_envChan[0]->SbrEnvFrame.SbrGrid; break; case SBR_MONO: /* nothing to do */ break; default: FDK_ASSERT(0); } for (ch = 0; ch < nChannels; ch++) { HANDLE_ENV_CHANNEL hEnvChan = h_envChan[ch]; HANDLE_SBR_EXTRACT_ENVELOPE sbrExtrEnv = &hEnvChan->sbrExtractEnvelope; SBR_ENV_TEMP_DATA *ed = &eData[ch]; /* Send transient info to bitstream and store for next call */ sbrExtrEnv->pre_transient_info[0] = ed->transient_info[0]; /* tran_pos */ sbrExtrEnv->pre_transient_info[1] = ed->transient_info[1]; /* tran_flag */ hEnvChan->encEnvData.noOfEnvelopes = ed->nEnvelopes = ed->frame_info->nEnvelopes; /* number of envelopes of current frame */ hEnvChan->encEnvData.currentAmpResFF = (AMP_RES)h_con->initAmpResFF; /* Check if the current frame is divided into one envelope only. If so, set the amplitude resolution to 1.5 dB, otherwise may set back to chosen value */ if ((hEnvChan->encEnvData.hSbrBSGrid->frameClass == FIXFIX) && (ed->nEnvelopes == 1)) { AMP_RES currentAmpResFF = SBR_AMP_RES_1_5; if (h_con->sbrSyntaxFlags & SBR_SYNTAX_LOW_DELAY) { /* Note: global_tonality_float_value == ((float)hEnvChan->encEnvData.global_tonality/((INT64)(1)<<(31-(19+2)))/0.524288*(2.0/3.0))); threshold_float_value == ((float)h_con->thresholdAmpResFF_m/((INT64)(1)<<(31-(h_con->thresholdAmpResFF_e)))/0.524288*(2.0/3.0))); */ /* decision of SBR_AMP_RES */ if (fIsLessThan(/* global_tonality > threshold ? */ h_con->thresholdAmpResFF_m, h_con->thresholdAmpResFF_e, hEnvChan->encEnvData.global_tonality, RELAXATION_SHIFT + 2)) { hEnvChan->encEnvData.currentAmpResFF = SBR_AMP_RES_1_5; } else { hEnvChan->encEnvData.currentAmpResFF = SBR_AMP_RES_3_0; } currentAmpResFF = hEnvChan->encEnvData.currentAmpResFF; } if (currentAmpResFF != hEnvChan->encEnvData.init_sbr_amp_res) { FDKsbrEnc_InitSbrHuffmanTables( &hEnvChan->encEnvData, &hEnvChan->sbrCodeEnvelope, &hEnvChan->sbrCodeNoiseFloor, currentAmpResFF); } } else { if (sbrHeaderData->sbr_amp_res != hEnvChan->encEnvData.init_sbr_amp_res) { FDKsbrEnc_InitSbrHuffmanTables( &hEnvChan->encEnvData, &hEnvChan->sbrCodeEnvelope, &hEnvChan->sbrCodeNoiseFloor, sbrHeaderData->sbr_amp_res); } } if (!clearOutput) { /* Tonality correction parameter extraction (inverse filtering level, noise floor additional sines). */ FDKsbrEnc_TonCorrParamExtr( &hEnvChan->TonCorr, hEnvChan->encEnvData.sbr_invf_mode_vec, ed->noiseFloor, &hEnvChan->encEnvData.addHarmonicFlag, hEnvChan->encEnvData.addHarmonic, sbrExtrEnv->envelopeCompensation, ed->frame_info, ed->transient_info, h_con->freqBandTable[HI], h_con->nSfb[HI], hEnvChan->encEnvData.sbr_xpos_mode, h_con->sbrSyntaxFlags); } /* Low energy in low band fix */ if (hEnvChan->sbrTransientDetector.prevLowBandEnergy < hEnvChan->sbrTransientDetector.prevHighBandEnergy && hEnvChan->sbrTransientDetector.prevHighBandEnergy > FL2FX_DBL(0.03) /* The fix needs the non-fast transient detector running. It sets prevLowBandEnergy and prevHighBandEnergy. */ && !(h_con->sbrSyntaxFlags & SBR_SYNTAX_LOW_DELAY)) { hEnvChan->fLevelProtect = 1; for (i = 0; i < MAX_NUM_NOISE_VALUES; i++) hEnvChan->encEnvData.sbr_invf_mode_vec[i] = INVF_HIGH_LEVEL; } else { hEnvChan->fLevelProtect = 0; } hEnvChan->encEnvData.sbr_invf_mode = hEnvChan->encEnvData.sbr_invf_mode_vec[0]; hEnvChan->encEnvData.noOfnoisebands = hEnvChan->TonCorr.sbrNoiseFloorEstimate.noNoiseBands; } /* ch */ /* Save number of scf bands per envelope */ for (ch = 0; ch < nChannels; ch++) { for (i = 0; i < eData[ch].nEnvelopes; i++) { h_envChan[ch]->encEnvData.noScfBands[i] = (eData[ch].frame_info->freqRes[i] == FREQ_RES_HIGH ? h_con->nSfb[FREQ_RES_HIGH] : h_con->nSfb[FREQ_RES_LOW]); } } if (h_con->sbrSyntaxFlags & SBR_SYNTAX_LOW_DELAY && stereoMode == SBR_SWITCH_LRC && h_envChan[0]->encEnvData.currentAmpResFF != h_envChan[1]->encEnvData.currentAmpResFF) { stereoMode = SBR_LEFT_RIGHT; } /* Extract envelope of current frame. */ switch (stereoMode) { case SBR_MONO: calculateSbrEnvelope(h_envChan[0]->sbrExtractEnvelope.YBuffer, NULL, h_envChan[0]->sbrExtractEnvelope.YBufferScale, NULL, eData[0].frame_info, eData[0].sfb_nrg, NULL, h_con, h_envChan[0], SBR_MONO, NULL, YSzShift); break; case SBR_LEFT_RIGHT: calculateSbrEnvelope(h_envChan[0]->sbrExtractEnvelope.YBuffer, NULL, h_envChan[0]->sbrExtractEnvelope.YBufferScale, NULL, eData[0].frame_info, eData[0].sfb_nrg, NULL, h_con, h_envChan[0], SBR_MONO, NULL, YSzShift); calculateSbrEnvelope(h_envChan[1]->sbrExtractEnvelope.YBuffer, NULL, h_envChan[1]->sbrExtractEnvelope.YBufferScale, NULL, eData[1].frame_info, eData[1].sfb_nrg, NULL, h_con, h_envChan[1], SBR_MONO, NULL, YSzShift); break; case SBR_COUPLING: calculateSbrEnvelope(h_envChan[0]->sbrExtractEnvelope.YBuffer, h_envChan[1]->sbrExtractEnvelope.YBuffer, h_envChan[0]->sbrExtractEnvelope.YBufferScale, h_envChan[1]->sbrExtractEnvelope.YBufferScale, eData[0].frame_info, eData[0].sfb_nrg, eData[1].sfb_nrg, h_con, h_envChan[0], SBR_COUPLING, &fData->maxQuantError, YSzShift); break; case SBR_SWITCH_LRC: calculateSbrEnvelope(h_envChan[0]->sbrExtractEnvelope.YBuffer, NULL, h_envChan[0]->sbrExtractEnvelope.YBufferScale, NULL, eData[0].frame_info, eData[0].sfb_nrg, NULL, h_con, h_envChan[0], SBR_MONO, NULL, YSzShift); calculateSbrEnvelope(h_envChan[1]->sbrExtractEnvelope.YBuffer, NULL, h_envChan[1]->sbrExtractEnvelope.YBufferScale, NULL, eData[1].frame_info, eData[1].sfb_nrg, NULL, h_con, h_envChan[1], SBR_MONO, NULL, YSzShift); calculateSbrEnvelope(h_envChan[0]->sbrExtractEnvelope.YBuffer, h_envChan[1]->sbrExtractEnvelope.YBuffer, h_envChan[0]->sbrExtractEnvelope.YBufferScale, h_envChan[1]->sbrExtractEnvelope.YBufferScale, eData[0].frame_info, eData[0].sfb_nrg_coupling, eData[1].sfb_nrg_coupling, h_con, h_envChan[0], SBR_COUPLING, &fData->maxQuantError, YSzShift); break; } /* Noise floor quantisation and coding. */ switch (stereoMode) { case SBR_MONO: sbrNoiseFloorLevelsQuantisation(eData[0].noise_level, eData[0].noiseFloor, 0); FDKsbrEnc_codeEnvelope(eData[0].noise_level, fData->res, &h_envChan[0]->sbrCodeNoiseFloor, h_envChan[0]->encEnvData.domain_vec_noise, 0, (eData[0].frame_info->nEnvelopes > 1 ? 2 : 1), 0, sbrBitstreamData->HeaderActive); break; case SBR_LEFT_RIGHT: sbrNoiseFloorLevelsQuantisation(eData[0].noise_level, eData[0].noiseFloor, 0); FDKsbrEnc_codeEnvelope(eData[0].noise_level, fData->res, &h_envChan[0]->sbrCodeNoiseFloor, h_envChan[0]->encEnvData.domain_vec_noise, 0, (eData[0].frame_info->nEnvelopes > 1 ? 2 : 1), 0, sbrBitstreamData->HeaderActive); sbrNoiseFloorLevelsQuantisation(eData[1].noise_level, eData[1].noiseFloor, 0); FDKsbrEnc_codeEnvelope(eData[1].noise_level, fData->res, &h_envChan[1]->sbrCodeNoiseFloor, h_envChan[1]->encEnvData.domain_vec_noise, 0, (eData[1].frame_info->nEnvelopes > 1 ? 2 : 1), 0, sbrBitstreamData->HeaderActive); break; case SBR_COUPLING: coupleNoiseFloor(eData[0].noiseFloor, eData[1].noiseFloor); sbrNoiseFloorLevelsQuantisation(eData[0].noise_level, eData[0].noiseFloor, 0); FDKsbrEnc_codeEnvelope(eData[0].noise_level, fData->res, &h_envChan[0]->sbrCodeNoiseFloor, h_envChan[0]->encEnvData.domain_vec_noise, 1, (eData[0].frame_info->nEnvelopes > 1 ? 2 : 1), 0, sbrBitstreamData->HeaderActive); sbrNoiseFloorLevelsQuantisation(eData[1].noise_level, eData[1].noiseFloor, 1); FDKsbrEnc_codeEnvelope(eData[1].noise_level, fData->res, &h_envChan[1]->sbrCodeNoiseFloor, h_envChan[1]->encEnvData.domain_vec_noise, 1, (eData[1].frame_info->nEnvelopes > 1 ? 2 : 1), 1, sbrBitstreamData->HeaderActive); break; case SBR_SWITCH_LRC: sbrNoiseFloorLevelsQuantisation(eData[0].noise_level, eData[0].noiseFloor, 0); sbrNoiseFloorLevelsQuantisation(eData[1].noise_level, eData[1].noiseFloor, 0); coupleNoiseFloor(eData[0].noiseFloor, eData[1].noiseFloor); sbrNoiseFloorLevelsQuantisation(eData[0].noise_level_coupling, eData[0].noiseFloor, 0); sbrNoiseFloorLevelsQuantisation(eData[1].noise_level_coupling, eData[1].noiseFloor, 1); break; } /* Encode envelope of current frame. */ switch (stereoMode) { case SBR_MONO: sbrHeaderData->coupling = 0; h_envChan[0]->encEnvData.balance = 0; FDKsbrEnc_codeEnvelope( eData[0].sfb_nrg, eData[0].frame_info->freqRes, &h_envChan[0]->sbrCodeEnvelope, h_envChan[0]->encEnvData.domain_vec, sbrHeaderData->coupling, eData[0].frame_info->nEnvelopes, 0, sbrBitstreamData->HeaderActive); break; case SBR_LEFT_RIGHT: sbrHeaderData->coupling = 0; h_envChan[0]->encEnvData.balance = 0; h_envChan[1]->encEnvData.balance = 0; FDKsbrEnc_codeEnvelope( eData[0].sfb_nrg, eData[0].frame_info->freqRes, &h_envChan[0]->sbrCodeEnvelope, h_envChan[0]->encEnvData.domain_vec, sbrHeaderData->coupling, eData[0].frame_info->nEnvelopes, 0, sbrBitstreamData->HeaderActive); FDKsbrEnc_codeEnvelope( eData[1].sfb_nrg, eData[1].frame_info->freqRes, &h_envChan[1]->sbrCodeEnvelope, h_envChan[1]->encEnvData.domain_vec, sbrHeaderData->coupling, eData[1].frame_info->nEnvelopes, 0, sbrBitstreamData->HeaderActive); break; case SBR_COUPLING: sbrHeaderData->coupling = 1; h_envChan[0]->encEnvData.balance = 0; h_envChan[1]->encEnvData.balance = 1; FDKsbrEnc_codeEnvelope( eData[0].sfb_nrg, eData[0].frame_info->freqRes, &h_envChan[0]->sbrCodeEnvelope, h_envChan[0]->encEnvData.domain_vec, sbrHeaderData->coupling, eData[0].frame_info->nEnvelopes, 0, sbrBitstreamData->HeaderActive); FDKsbrEnc_codeEnvelope( eData[1].sfb_nrg, eData[1].frame_info->freqRes, &h_envChan[1]->sbrCodeEnvelope, h_envChan[1]->encEnvData.domain_vec, sbrHeaderData->coupling, eData[1].frame_info->nEnvelopes, 1, sbrBitstreamData->HeaderActive); break; case SBR_SWITCH_LRC: { INT payloadbitsLR; INT payloadbitsCOUPLING; SCHAR sfbNrgPrevTemp[MAX_NUM_CHANNELS][MAX_FREQ_COEFFS]; SCHAR noisePrevTemp[MAX_NUM_CHANNELS][MAX_NUM_NOISE_COEFFS]; INT upDateNrgTemp[MAX_NUM_CHANNELS]; INT upDateNoiseTemp[MAX_NUM_CHANNELS]; INT domainVecTemp[MAX_NUM_CHANNELS][MAX_ENVELOPES]; INT domainVecNoiseTemp[MAX_NUM_CHANNELS][MAX_ENVELOPES]; INT tempFlagRight = 0; INT tempFlagLeft = 0; /* Store previous values, in order to be able to "undo" what is being done. */ for (ch = 0; ch < nChannels; ch++) { FDKmemcpy(sfbNrgPrevTemp[ch], h_envChan[ch]->sbrCodeEnvelope.sfb_nrg_prev, MAX_FREQ_COEFFS * sizeof(SCHAR)); FDKmemcpy(noisePrevTemp[ch], h_envChan[ch]->sbrCodeNoiseFloor.sfb_nrg_prev, MAX_NUM_NOISE_COEFFS * sizeof(SCHAR)); upDateNrgTemp[ch] = h_envChan[ch]->sbrCodeEnvelope.upDate; upDateNoiseTemp[ch] = h_envChan[ch]->sbrCodeNoiseFloor.upDate; /* forbid time coding in the first envelope in case of a different previous stereomode */ if (sbrHeaderData->prev_coupling) { h_envChan[ch]->sbrCodeEnvelope.upDate = 0; h_envChan[ch]->sbrCodeNoiseFloor.upDate = 0; } } /* ch */ /* Code ordinary Left/Right stereo */ FDKsbrEnc_codeEnvelope(eData[0].sfb_nrg, eData[0].frame_info->freqRes, &h_envChan[0]->sbrCodeEnvelope, h_envChan[0]->encEnvData.domain_vec, 0, eData[0].frame_info->nEnvelopes, 0, sbrBitstreamData->HeaderActive); FDKsbrEnc_codeEnvelope(eData[1].sfb_nrg, eData[1].frame_info->freqRes, &h_envChan[1]->sbrCodeEnvelope, h_envChan[1]->encEnvData.domain_vec, 0, eData[1].frame_info->nEnvelopes, 0, sbrBitstreamData->HeaderActive); c = 0; for (i = 0; i < eData[0].nEnvelopes; i++) { for (j = 0; j < h_envChan[0]->encEnvData.noScfBands[i]; j++) { h_envChan[0]->encEnvData.ienvelope[i][j] = eData[0].sfb_nrg[c]; h_envChan[1]->encEnvData.ienvelope[i][j] = eData[1].sfb_nrg[c]; c++; } } FDKsbrEnc_codeEnvelope(eData[0].noise_level, fData->res, &h_envChan[0]->sbrCodeNoiseFloor, h_envChan[0]->encEnvData.domain_vec_noise, 0, (eData[0].frame_info->nEnvelopes > 1 ? 2 : 1), 0, sbrBitstreamData->HeaderActive); for (i = 0; i < MAX_NUM_NOISE_VALUES; i++) h_envChan[0]->encEnvData.sbr_noise_levels[i] = eData[0].noise_level[i]; FDKsbrEnc_codeEnvelope(eData[1].noise_level, fData->res, &h_envChan[1]->sbrCodeNoiseFloor, h_envChan[1]->encEnvData.domain_vec_noise, 0, (eData[1].frame_info->nEnvelopes > 1 ? 2 : 1), 0, sbrBitstreamData->HeaderActive); for (i = 0; i < MAX_NUM_NOISE_VALUES; i++) h_envChan[1]->encEnvData.sbr_noise_levels[i] = eData[1].noise_level[i]; sbrHeaderData->coupling = 0; h_envChan[0]->encEnvData.balance = 0; h_envChan[1]->encEnvData.balance = 0; payloadbitsLR = FDKsbrEnc_CountSbrChannelPairElement( sbrHeaderData, hParametricStereo, sbrBitstreamData, &h_envChan[0]->encEnvData, &h_envChan[1]->encEnvData, hCmonData, h_con->sbrSyntaxFlags); /* swap saved stored with current values */ for (ch = 0; ch < nChannels; ch++) { INT itmp; for (i = 0; i < MAX_FREQ_COEFFS; i++) { /* swap sfb energies */ itmp = h_envChan[ch]->sbrCodeEnvelope.sfb_nrg_prev[i]; h_envChan[ch]->sbrCodeEnvelope.sfb_nrg_prev[i] = sfbNrgPrevTemp[ch][i]; sfbNrgPrevTemp[ch][i] = itmp; } for (i = 0; i < MAX_NUM_NOISE_COEFFS; i++) { /* swap noise energies */ itmp = h_envChan[ch]->sbrCodeNoiseFloor.sfb_nrg_prev[i]; h_envChan[ch]->sbrCodeNoiseFloor.sfb_nrg_prev[i] = noisePrevTemp[ch][i]; noisePrevTemp[ch][i] = itmp; } /* swap update flags */ itmp = h_envChan[ch]->sbrCodeEnvelope.upDate; h_envChan[ch]->sbrCodeEnvelope.upDate = upDateNrgTemp[ch]; upDateNrgTemp[ch] = itmp; itmp = h_envChan[ch]->sbrCodeNoiseFloor.upDate; h_envChan[ch]->sbrCodeNoiseFloor.upDate = upDateNoiseTemp[ch]; upDateNoiseTemp[ch] = itmp; /* save domain vecs */ FDKmemcpy(domainVecTemp[ch], h_envChan[ch]->encEnvData.domain_vec, sizeof(INT) * MAX_ENVELOPES); FDKmemcpy(domainVecNoiseTemp[ch], h_envChan[ch]->encEnvData.domain_vec_noise, sizeof(INT) * MAX_ENVELOPES); /* forbid time coding in the first envelope in case of a different previous stereomode */ if (!sbrHeaderData->prev_coupling) { h_envChan[ch]->sbrCodeEnvelope.upDate = 0; h_envChan[ch]->sbrCodeNoiseFloor.upDate = 0; } } /* ch */ /* Coupling */ FDKsbrEnc_codeEnvelope( eData[0].sfb_nrg_coupling, eData[0].frame_info->freqRes, &h_envChan[0]->sbrCodeEnvelope, h_envChan[0]->encEnvData.domain_vec, 1, eData[0].frame_info->nEnvelopes, 0, sbrBitstreamData->HeaderActive); FDKsbrEnc_codeEnvelope( eData[1].sfb_nrg_coupling, eData[1].frame_info->freqRes, &h_envChan[1]->sbrCodeEnvelope, h_envChan[1]->encEnvData.domain_vec, 1, eData[1].frame_info->nEnvelopes, 1, sbrBitstreamData->HeaderActive); c = 0; for (i = 0; i < eData[0].nEnvelopes; i++) { for (j = 0; j < h_envChan[0]->encEnvData.noScfBands[i]; j++) { h_envChan[0]->encEnvData.ienvelope[i][j] = eData[0].sfb_nrg_coupling[c]; h_envChan[1]->encEnvData.ienvelope[i][j] = eData[1].sfb_nrg_coupling[c]; c++; } } FDKsbrEnc_codeEnvelope(eData[0].noise_level_coupling, fData->res, &h_envChan[0]->sbrCodeNoiseFloor, h_envChan[0]->encEnvData.domain_vec_noise, 1, (eData[0].frame_info->nEnvelopes > 1 ? 2 : 1), 0, sbrBitstreamData->HeaderActive); for (i = 0; i < MAX_NUM_NOISE_VALUES; i++) h_envChan[0]->encEnvData.sbr_noise_levels[i] = eData[0].noise_level_coupling[i]; FDKsbrEnc_codeEnvelope(eData[1].noise_level_coupling, fData->res, &h_envChan[1]->sbrCodeNoiseFloor, h_envChan[1]->encEnvData.domain_vec_noise, 1, (eData[1].frame_info->nEnvelopes > 1 ? 2 : 1), 1, sbrBitstreamData->HeaderActive); for (i = 0; i < MAX_NUM_NOISE_VALUES; i++) h_envChan[1]->encEnvData.sbr_noise_levels[i] = eData[1].noise_level_coupling[i]; sbrHeaderData->coupling = 1; h_envChan[0]->encEnvData.balance = 0; h_envChan[1]->encEnvData.balance = 1; tempFlagLeft = h_envChan[0]->encEnvData.addHarmonicFlag; tempFlagRight = h_envChan[1]->encEnvData.addHarmonicFlag; payloadbitsCOUPLING = FDKsbrEnc_CountSbrChannelPairElement( sbrHeaderData, hParametricStereo, sbrBitstreamData, &h_envChan[0]->encEnvData, &h_envChan[1]->encEnvData, hCmonData, h_con->sbrSyntaxFlags); h_envChan[0]->encEnvData.addHarmonicFlag = tempFlagLeft; h_envChan[1]->encEnvData.addHarmonicFlag = tempFlagRight; if (payloadbitsCOUPLING < payloadbitsLR) { /* copy coded coupling envelope and noise data to l/r */ for (ch = 0; ch < nChannels; ch++) { SBR_ENV_TEMP_DATA *ed = &eData[ch]; FDKmemcpy(ed->sfb_nrg, ed->sfb_nrg_coupling, MAX_NUM_ENVELOPE_VALUES * sizeof(SCHAR)); FDKmemcpy(ed->noise_level, ed->noise_level_coupling, MAX_NUM_NOISE_VALUES * sizeof(SCHAR)); } sbrHeaderData->coupling = 1; h_envChan[0]->encEnvData.balance = 0; h_envChan[1]->encEnvData.balance = 1; } else { /* restore saved l/r items */ for (ch = 0; ch < nChannels; ch++) { FDKmemcpy(h_envChan[ch]->sbrCodeEnvelope.sfb_nrg_prev, sfbNrgPrevTemp[ch], MAX_FREQ_COEFFS * sizeof(SCHAR)); h_envChan[ch]->sbrCodeEnvelope.upDate = upDateNrgTemp[ch]; FDKmemcpy(h_envChan[ch]->sbrCodeNoiseFloor.sfb_nrg_prev, noisePrevTemp[ch], MAX_NUM_NOISE_COEFFS * sizeof(SCHAR)); FDKmemcpy(h_envChan[ch]->encEnvData.domain_vec, domainVecTemp[ch], sizeof(INT) * MAX_ENVELOPES); FDKmemcpy(h_envChan[ch]->encEnvData.domain_vec_noise, domainVecNoiseTemp[ch], sizeof(INT) * MAX_ENVELOPES); h_envChan[ch]->sbrCodeNoiseFloor.upDate = upDateNoiseTemp[ch]; } sbrHeaderData->coupling = 0; h_envChan[0]->encEnvData.balance = 0; h_envChan[1]->encEnvData.balance = 0; } } break; } /* switch */ /* tell the envelope encoders how long it has been, since we last sent a frame starting with a dF-coded envelope */ if (stereoMode == SBR_MONO) { if (h_envChan[0]->encEnvData.domain_vec[0] == TIME) h_envChan[0]->sbrCodeEnvelope.dF_edge_incr_fac++; else h_envChan[0]->sbrCodeEnvelope.dF_edge_incr_fac = 0; } else { if (h_envChan[0]->encEnvData.domain_vec[0] == TIME || h_envChan[1]->encEnvData.domain_vec[0] == TIME) { h_envChan[0]->sbrCodeEnvelope.dF_edge_incr_fac++; h_envChan[1]->sbrCodeEnvelope.dF_edge_incr_fac++; } else { h_envChan[0]->sbrCodeEnvelope.dF_edge_incr_fac = 0; h_envChan[1]->sbrCodeEnvelope.dF_edge_incr_fac = 0; } } /* Send the encoded data to the bitstream */ for (ch = 0; ch < nChannels; ch++) { SBR_ENV_TEMP_DATA *ed = &eData[ch]; c = 0; for (i = 0; i < ed->nEnvelopes; i++) { for (j = 0; j < h_envChan[ch]->encEnvData.noScfBands[i]; j++) { h_envChan[ch]->encEnvData.ienvelope[i][j] = ed->sfb_nrg[c]; c++; } } for (i = 0; i < MAX_NUM_NOISE_VALUES; i++) { h_envChan[ch]->encEnvData.sbr_noise_levels[i] = ed->noise_level[i]; } } /* ch */ /* Write bitstream */ if (nChannels == 2) { FDKsbrEnc_WriteEnvChannelPairElement( sbrHeaderData, hParametricStereo, sbrBitstreamData, &h_envChan[0]->encEnvData, &h_envChan[1]->encEnvData, hCmonData, h_con->sbrSyntaxFlags); } else { FDKsbrEnc_WriteEnvSingleChannelElement( sbrHeaderData, hParametricStereo, sbrBitstreamData, &h_envChan[0]->encEnvData, hCmonData, h_con->sbrSyntaxFlags); } /* * Update buffers. */ for (ch = 0; ch < nChannels; ch++) { int YBufferLength = h_envChan[ch]->sbrExtractEnvelope.no_cols >> h_envChan[ch]->sbrExtractEnvelope.YBufferSzShift; for (i = 0; i < h_envChan[ch]->sbrExtractEnvelope.YBufferWriteOffset; i++) { FDKmemcpy(h_envChan[ch]->sbrExtractEnvelope.YBuffer[i], h_envChan[ch]->sbrExtractEnvelope.YBuffer[i + YBufferLength], sizeof(FIXP_DBL) * 64); } h_envChan[ch]->sbrExtractEnvelope.YBufferScale[0] = h_envChan[ch]->sbrExtractEnvelope.YBufferScale[1]; } sbrHeaderData->prev_coupling = sbrHeaderData->coupling; } /***************************************************************************/ /*! \brief creates an envelope extractor handle \return error status ****************************************************************************/ INT FDKsbrEnc_CreateExtractSbrEnvelope(HANDLE_SBR_EXTRACT_ENVELOPE hSbrCut, INT channel, INT chInEl, UCHAR *dynamic_RAM) { INT i; FIXP_DBL *rBuffer, *iBuffer; INT n; FIXP_DBL *YBufferDyn; FDKmemclear(hSbrCut, sizeof(SBR_EXTRACT_ENVELOPE)); if (NULL == (hSbrCut->p_YBuffer = GetRam_Sbr_envYBuffer(channel))) { goto bail; } for (i = 0; i < (32 >> 1); i++) { hSbrCut->YBuffer[i] = hSbrCut->p_YBuffer + (i * 64); } YBufferDyn = GetRam_Sbr_envYBuffer(chInEl, dynamic_RAM); for (n = 0; i < 32; i++, n++) { hSbrCut->YBuffer[i] = YBufferDyn + (n * 64); } rBuffer = GetRam_Sbr_envRBuffer(0, dynamic_RAM); iBuffer = GetRam_Sbr_envIBuffer(0, dynamic_RAM); for (i = 0; i < 32; i++) { hSbrCut->rBuffer[i] = rBuffer + (i * 64); hSbrCut->iBuffer[i] = iBuffer + (i * 64); } return 0; bail: FDKsbrEnc_deleteExtractSbrEnvelope(hSbrCut); return -1; } /***************************************************************************/ /*! \brief Initialize an envelope extractor instance. \return error status ****************************************************************************/ INT FDKsbrEnc_InitExtractSbrEnvelope(HANDLE_SBR_EXTRACT_ENVELOPE hSbrCut, int no_cols, int no_rows, int start_index, int time_slots, int time_step, int tran_off, ULONG statesInitFlag, int chInEl, UCHAR *dynamic_RAM, UINT sbrSyntaxFlags) { int YBufferLength, rBufferLength; int i; if (sbrSyntaxFlags & SBR_SYNTAX_LOW_DELAY) { int off = TRANSIENT_OFFSET_LD; hSbrCut->YBufferWriteOffset = (no_cols >> 1) + off * time_step; } else { hSbrCut->YBufferWriteOffset = tran_off * time_step; } hSbrCut->rBufferReadOffset = 0; YBufferLength = hSbrCut->YBufferWriteOffset + no_cols; rBufferLength = no_cols; hSbrCut->pre_transient_info[0] = 0; hSbrCut->pre_transient_info[1] = 0; hSbrCut->no_cols = no_cols; hSbrCut->no_rows = no_rows; hSbrCut->start_index = start_index; hSbrCut->time_slots = time_slots; hSbrCut->time_step = time_step; FDK_ASSERT(no_rows <= 64); /* Use half the Energy values if time step is 2 or greater */ if (time_step >= 2) hSbrCut->YBufferSzShift = 1; else hSbrCut->YBufferSzShift = 0; YBufferLength >>= hSbrCut->YBufferSzShift; hSbrCut->YBufferWriteOffset >>= hSbrCut->YBufferSzShift; FDK_ASSERT(YBufferLength <= 32); FIXP_DBL *YBufferDyn = GetRam_Sbr_envYBuffer(chInEl, dynamic_RAM); INT n = 0; for (i = (32 >> 1); i < 32; i++, n++) { hSbrCut->YBuffer[i] = YBufferDyn + (n * 64); } if (statesInitFlag) { for (i = 0; i < YBufferLength; i++) { FDKmemclear(hSbrCut->YBuffer[i], 64 * sizeof(FIXP_DBL)); } } for (i = 0; i < rBufferLength; i++) { FDKmemclear(hSbrCut->rBuffer[i], 64 * sizeof(FIXP_DBL)); FDKmemclear(hSbrCut->iBuffer[i], 64 * sizeof(FIXP_DBL)); } FDKmemclear(hSbrCut->envelopeCompensation, sizeof(UCHAR) * MAX_FREQ_COEFFS); if (statesInitFlag) { hSbrCut->YBufferScale[0] = hSbrCut->YBufferScale[1] = FRACT_BITS - 1; } return (0); } /***************************************************************************/ /*! \brief deinitializes an envelope extractor handle \return void ****************************************************************************/ void FDKsbrEnc_deleteExtractSbrEnvelope(HANDLE_SBR_EXTRACT_ENVELOPE hSbrCut) { if (hSbrCut) { FreeRam_Sbr_envYBuffer(&hSbrCut->p_YBuffer); } } INT FDKsbrEnc_GetEnvEstDelay(HANDLE_SBR_EXTRACT_ENVELOPE hSbr) { return hSbr->no_rows * ((hSbr->YBufferWriteOffset) * 2 /* mult 2 because nrg's are grouped half */ - hSbr->rBufferReadOffset); /* in reference hold half spec and calc nrg's on overlapped spec */ }