/* ----------------------------------------------------------------------------------------------------------- Software License for The Fraunhofer FDK AAC Codec Library for Android © Copyright 1995 - 2013 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 ----------------------------------------------------------------------------------------------------------- */ /*! \file \brief Envelope calculation The envelope adjustor compares the energies present in the transposed highband to the reference energies conveyed with the bitstream. The highband is amplified (sometimes) or attenuated (mostly) to the desired level. The spectral shape of the reference energies can be changed several times per frame if necessary. Each set of energy values corresponding to a certain range in time will be called an envelope here. The bitstream supports several frequency scales and two resolutions. Normally, one or more QMF-subbands are grouped to one SBR-band. An envelope contains reference energies for each SBR-band. In addition to the energy envelopes, noise envelopes are transmitted that define the ratio of energy which is generated by adding noise instead of transposing the lowband. The noise envelopes are given in a coarser time and frequency resolution. If a signal contains strong tonal components, synthetic sines can be generated in individual SBR bands. An overlap buffer of 6 QMF-timeslots is used to allow a more flexible alignment of the envelopes in time that is not restricted to the core codec's frame borders. Therefore the envelope adjustor has access to the spectral data of the current frame as well as the last 6 QMF-timeslots of the previous frame. However, in average only the data of 1 frame is being processed as the adjustor is called once per frame. Depending on the frequency range set in the bitstream, only QMF-subbands between lowSubband and highSubband are adjusted. Scaling of spectral data to maximize SNR (see #QMF_SCALE_FACTOR) as well as a special Mantissa-Exponent format ( see calculateSbrEnvelope() ) are being used. The main entry point for this modules is calculateSbrEnvelope(). \sa sbr_scale.h, #QMF_SCALE_FACTOR, calculateSbrEnvelope(), \ref documentationOverview */ #include "env_calc.h" #include "sbrdec_freq_sca.h" #include "env_extr.h" #include "transcendent.h" #include "sbr_ram.h" #include "sbr_rom.h" #include "genericStds.h" /* need FDKpow() for debug outputs */ #if defined(__arm__) #include "arm/env_calc_arm.cpp" #endif typedef struct { FIXP_DBL nrgRef[MAX_FREQ_COEFFS]; FIXP_DBL nrgEst[MAX_FREQ_COEFFS]; FIXP_DBL nrgGain[MAX_FREQ_COEFFS]; FIXP_DBL noiseLevel[MAX_FREQ_COEFFS]; FIXP_DBL nrgSine[MAX_FREQ_COEFFS]; SCHAR nrgRef_e[MAX_FREQ_COEFFS]; SCHAR nrgEst_e[MAX_FREQ_COEFFS]; SCHAR nrgGain_e[MAX_FREQ_COEFFS]; SCHAR noiseLevel_e[MAX_FREQ_COEFFS]; SCHAR nrgSine_e[MAX_FREQ_COEFFS]; } ENV_CALC_NRGS; /*static*/ void equalizeFiltBufferExp(FIXP_DBL *filtBuffer, SCHAR *filtBuffer_e, FIXP_DBL *NrgGain, SCHAR *NrgGain_e, int subbands); /*static*/ void calcNrgPerSubband(FIXP_DBL **analysBufferReal, FIXP_DBL **analysBufferImag, int lowSubband, int highSubband, int start_pos, int next_pos, SCHAR frameExp, FIXP_DBL *nrgEst, SCHAR *nrgEst_e ); /*static*/ void calcNrgPerSfb(FIXP_DBL **analysBufferReal, FIXP_DBL **analysBufferImag, int nSfb, UCHAR *freqBandTable, int start_pos, int next_pos, SCHAR input_e, FIXP_DBL *nrg_est, SCHAR *nrg_est_e ); /*static*/ void calcSubbandGain(FIXP_DBL nrgRef, SCHAR nrgRef_e, ENV_CALC_NRGS* nrgs, int c, FIXP_DBL tmpNoise, SCHAR tmpNoise_e, UCHAR sinePresentFlag, UCHAR sineMapped, int noNoiseFlag); /*static*/ void calcAvgGain(ENV_CALC_NRGS* nrgs, int lowSubband, int highSubband, FIXP_DBL *sumRef_m, SCHAR *sumRef_e, FIXP_DBL *ptrAvgGain_m, SCHAR *ptrAvgGain_e); /*static*/ void adjustTimeSlotLC(FIXP_DBL *ptrReal, ENV_CALC_NRGS* nrgs, UCHAR *ptrHarmIndex, int lowSubbands, int noSubbands, int scale_change, int noNoiseFlag, int *ptrPhaseIndex, int fCldfb); /*static*/ void adjustTimeSlotHQ(FIXP_DBL *ptrReal, FIXP_DBL *ptrImag, HANDLE_SBR_CALCULATE_ENVELOPE h_sbr_cal_env, ENV_CALC_NRGS* nrgs, int lowSubbands, int noSubbands, int scale_change, FIXP_SGL smooth_ratio, int noNoiseFlag, int filtBufferNoiseShift); /*! \brief Map sine flags from bitstream to QMF bands The bitstream carries only 1 sine flag per band and frame. This function maps every sine flag from the bitstream to a specific QMF subband and to a specific envelope where the sine shall start. The result is stored in the vector sineMapped which contains one entry per QMF subband. The value of an entry specifies the envelope where a sine shall start. A value of #MAX_ENVELOPES indicates that no sine is present in the subband. The missing harmonics flags from the previous frame (harmFlagsPrev) determine if a sine starts at the beginning of the frame or at the transient position. Additionally, the flags in harmFlagsPrev are being updated by this function for the next frame. */ /*static*/ void mapSineFlags(UCHAR *freqBandTable, /*!< Band borders (there's only 1 flag per band) */ int nSfb, /*!< Number of bands in the table */ UCHAR *addHarmonics, /*!< vector with 1 flag per sfb */ int *harmFlagsPrev, /*!< Packed 'addHarmonics' */ int tranEnv, /*!< Transient position */ SCHAR *sineMapped) /*!< Resulting vector of sine start positions for each QMF band */ { int i; int lowSubband2 = freqBandTable[0]<<1; int bitcount = 0; int oldflags = *harmFlagsPrev; int newflags = 0; /* Format of harmFlagsPrev: first word = flags for highest 16 sfb bands in use second word = flags for next lower 16 sfb bands (if present) third word = flags for lowest 16 sfb bands (if present) Up to MAX_FREQ_COEFFS sfb bands can be flagged for a sign. The lowest bit of the first word corresponds to the _highest_ sfb band in use. This is ensures that each flag is mapped to the same QMF band even after a change of the crossover-frequency. */ /* Reset the output vector first */ FDKmemset(sineMapped, MAX_ENVELOPES,MAX_FREQ_COEFFS); /* MAX_ENVELOPES means 'no sine' */ freqBandTable += nSfb; addHarmonics += nSfb-1; for (i=nSfb; i!=0; i--) { int ui = *freqBandTable--; /* Upper limit of the current scale factor band. */ int li = *freqBandTable; /* Lower limit of the current scale factor band. */ if ( *addHarmonics-- ) { /* There is a sine in this band */ unsigned int mask = 1 << bitcount; newflags |= mask; /* Set flag */ /* If there was a sine in the last frame, let it continue from the first envelope on else start at the transient position. */ sineMapped[(ui+li-lowSubband2) >> 1] = ( oldflags & mask ) ? 0 : tranEnv; } if ((++bitcount == 16) || i==1) { bitcount = 0; *harmFlagsPrev++ = newflags; oldflags = *harmFlagsPrev; /* Fetch 16 of the old flags */ newflags = 0; } } } /*! \brief Reduce gain-adjustment induced aliasing for real valued filterbank. */ /*static*/ void aliasingReduction(FIXP_DBL* degreeAlias, /*!< estimated aliasing for each QMF channel */ ENV_CALC_NRGS* nrgs, int* useAliasReduction, /*!< synthetic sine engergy for each subband, used as flag */ int noSubbands) /*!< number of QMF channels to process */ { FIXP_DBL* nrgGain = nrgs->nrgGain; /*!< subband gains to be modified */ SCHAR* nrgGain_e = nrgs->nrgGain_e; /*!< subband gains to be modified (exponents) */ FIXP_DBL* nrgEst = nrgs->nrgEst; /*!< subband energy before amplification */ SCHAR* nrgEst_e = nrgs->nrgEst_e; /*!< subband energy before amplification (exponents) */ int grouping = 0, index = 0, noGroups, k; int groupVector[MAX_FREQ_COEFFS]; /* Calculate grouping*/ for (k = 0; k < noSubbands-1; k++ ){ if ( (degreeAlias[k + 1] != FL2FXCONST_DBL(0.0f)) && useAliasReduction[k] ) { if(grouping==0){ groupVector[index++] = k; grouping = 1; } else{ if(groupVector[index-1] + 3 == k){ groupVector[index++] = k + 1; grouping = 0; } } } else{ if(grouping){ if(useAliasReduction[k]) groupVector[index++] = k + 1; else groupVector[index++] = k; grouping = 0; } } } if(grouping){ groupVector[index++] = noSubbands; } noGroups = index >> 1; /*Calculate new gain*/ for (int group = 0; group < noGroups; group ++) { FIXP_DBL nrgOrig = FL2FXCONST_DBL(0.0f); /* Original signal energy in current group of bands */ SCHAR nrgOrig_e = 0; FIXP_DBL nrgAmp = FL2FXCONST_DBL(0.0f); /* Amplified signal energy in group (using current gains) */ SCHAR nrgAmp_e = 0; FIXP_DBL nrgMod = FL2FXCONST_DBL(0.0f); /* Signal energy in group when applying modified gains */ SCHAR nrgMod_e = 0; FIXP_DBL groupGain; /* Total energy gain in group */ SCHAR groupGain_e; FIXP_DBL compensation; /* Compensation factor for the energy change when applying modified gains */ SCHAR compensation_e; int startGroup = groupVector[2*group]; int stopGroup = groupVector[2*group+1]; /* Calculate total energy in group before and after amplification with current gains: */ for(k = startGroup; k < stopGroup; k++){ /* Get original band energy */ FIXP_DBL tmp = nrgEst[k]; SCHAR tmp_e = nrgEst_e[k]; FDK_add_MantExp(tmp, tmp_e, nrgOrig, nrgOrig_e, &nrgOrig, &nrgOrig_e); /* Multiply band energy with current gain */ tmp = fMult(tmp,nrgGain[k]); tmp_e = tmp_e + nrgGain_e[k]; FDK_add_MantExp(tmp, tmp_e, nrgAmp, nrgAmp_e, &nrgAmp, &nrgAmp_e); } /* Calculate total energy gain in group */ FDK_divide_MantExp(nrgAmp, nrgAmp_e, nrgOrig, nrgOrig_e, &groupGain, &groupGain_e); for(k = startGroup; k < stopGroup; k++){ FIXP_DBL tmp; SCHAR tmp_e; FIXP_DBL alpha = degreeAlias[k]; if (k < noSubbands - 1) { if (degreeAlias[k + 1] > alpha) alpha = degreeAlias[k + 1]; } /* Modify gain depending on the degree of aliasing */ FDK_add_MantExp( fMult(alpha,groupGain), groupGain_e, fMult(/*FL2FXCONST_DBL(1.0f)*/ (FIXP_DBL)MAXVAL_DBL - alpha,nrgGain[k]), nrgGain_e[k], &nrgGain[k], &nrgGain_e[k] ); /* Apply modified gain to original energy */ tmp = fMult(nrgGain[k],nrgEst[k]); tmp_e = nrgGain_e[k] + nrgEst_e[k]; /* Accumulate energy with modified gains applied */ FDK_add_MantExp( tmp, tmp_e, nrgMod, nrgMod_e, &nrgMod, &nrgMod_e ); } /* Calculate compensation factor to retain the energy of the amplified signal */ FDK_divide_MantExp(nrgAmp, nrgAmp_e, nrgMod, nrgMod_e, &compensation, &compensation_e); /* Apply compensation factor to all gains of the group */ for(k = startGroup; k < stopGroup; k++){ nrgGain[k] = fMult(nrgGain[k],compensation); nrgGain_e[k] = nrgGain_e[k] + compensation_e; } } } /* Convert headroom bits to exponent */ #define SCALE2EXP(s) (15-(s)) #define EXP2SCALE(e) (15-(e)) /*! \brief Apply spectral envelope to subband samples This function is called from sbr_dec.cpp in each frame. To enhance accuracy and due to the usage of tables for squareroots and inverse, some calculations are performed with the operands being split into mantissa and exponent. The variable names in the source code carry the suffixes _m and _e respectively. The control data in #hFrameData containts envelope data which is represented by this format but stored in single words. (See requantizeEnvelopeData() for details). This data is unpacked within calculateSbrEnvelope() to follow the described suffix convention. The actual value (comparable to the corresponding float-variable in the research-implementation) of a mantissa/exponent-pair can be calculated as \f$value = value\_m * 2^{value\_e} \f$ All energies and noise levels decoded from the bitstream suit for an original signal magnitude of \f$\pm 32768 \f$ rather than \f$\pm 1\f$. Therefore, the scale factor hb_scale passed into this function will be converted to an 'input exponent' (#input_e), which fits the internal representation. Before the actual processing, an exponent #adj_e for resulting adjusted samples is derived from the maximum reference energy. Then, for each envelope, the following steps are performed: \li Calculate energy in the signal to be adjusted. Depending on the the value of #interpolFreq (interpolation mode), this is either done seperately for each QMF-subband or for each SBR-band. The resulting energies are stored in #nrgEst_m[#MAX_FREQ_COEFFS] (mantissas) and #nrgEst_e[#MAX_FREQ_COEFFS] (exponents). \li Calculate gain and noise level for each subband:
\f$gain = \sqrt{ \frac{nrgRef}{nrgEst} \cdot (1 - noiseRatio) } \hspace{2cm} noise = \sqrt{ nrgRef \cdot noiseRatio } \f$