| |
| /* ----------------------------------------------------------------------------------------------------------- |
| Software License for The Fraunhofer FDK AAC Codec Library for Android |
| |
| © Copyright 1995 - 2012 Fraunhofer-Gesellschaft zur Förderung der angewandten Forschung e.V. |
| All rights reserved. |
| |
| 1. INTRODUCTION |
| The Fraunhofer FDK AAC Codec Library for Android ("FDK AAC Codec") is software that implements |
| the MPEG Advanced Audio Coding ("AAC") encoding and decoding scheme for digital audio. |
| This FDK AAC Codec software is intended to be used on a wide variety of Android devices. |
| |
| AAC's HE-AAC and HE-AAC v2 versions are regarded as today's most efficient general perceptual |
| audio codecs. AAC-ELD is considered the best-performing full-bandwidth communications codec by |
| independent studies and is widely deployed. AAC has been standardized by ISO and IEC as part |
| of the MPEG specifications. |
| |
| Patent licenses for necessary patent claims for the FDK AAC Codec (including those of Fraunhofer) |
| may be obtained through Via Licensing (www.vialicensing.com) or through the respective patent owners |
| individually for the purpose of encoding or decoding bit streams in products that are compliant with |
| the ISO/IEC MPEG audio standards. Please note that most manufacturers of Android devices already license |
| these patent claims through Via Licensing or directly from the patent owners, and therefore FDK AAC Codec |
| software may already be covered under those patent licenses when it is used for those licensed purposes only. |
| |
| Commercially-licensed AAC software libraries, including floating-point versions with enhanced sound quality, |
| are also available from Fraunhofer. Users are encouraged to check the Fraunhofer website for additional |
| applications information and documentation. |
| |
| 2. COPYRIGHT LICENSE |
| |
| Redistribution and use in source and binary forms, with or without modification, are permitted without |
| payment of copyright license fees provided that you satisfy the following conditions: |
| |
| You must retain the complete text of this software license in redistributions of the FDK AAC Codec or |
| your modifications thereto in source code form. |
| |
| You must retain the complete text of this software license in the documentation and/or other materials |
| provided with redistributions of the FDK AAC Codec or your modifications thereto in binary form. |
| You must make available free of charge copies of the complete source code of the FDK AAC Codec and your |
| modifications thereto to recipients of copies in binary form. |
| |
| The name of Fraunhofer may not be used to endorse or promote products derived from this library without |
| prior written permission. |
| |
| You may not charge copyright license fees for anyone to use, copy or distribute the FDK AAC Codec |
| software or your modifications thereto. |
| |
| Your modified versions of the FDK AAC Codec must carry prominent notices stating that you changed the software |
| and the date of any change. For modified versions of the FDK AAC Codec, the term |
| "Fraunhofer FDK AAC Codec Library for Android" must be replaced by the term |
| "Third-Party Modified Version of the Fraunhofer FDK AAC Codec Library for Android." |
| |
| 3. NO PATENT LICENSE |
| |
| NO EXPRESS OR IMPLIED LICENSES TO ANY PATENT CLAIMS, including without limitation the patents of Fraunhofer, |
| ARE GRANTED BY THIS SOFTWARE LICENSE. Fraunhofer provides no warranty of patent non-infringement with |
| respect to this software. |
| |
| You may use this FDK AAC Codec software or modifications thereto only for purposes that are authorized |
| by appropriate patent licenses. |
| |
| 4. DISCLAIMER |
| |
| This FDK AAC Codec software is provided by Fraunhofer on behalf of the copyright holders and contributors |
| "AS IS" and WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, including but not limited to the implied warranties |
| of merchantability and fitness for a particular purpose. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR |
| CONTRIBUTORS BE LIABLE for any direct, indirect, incidental, special, exemplary, or consequential damages, |
| including but not limited to procurement of substitute goods or services; loss of use, data, or profits, |
| or business interruption, however caused and on any theory of liability, whether in contract, strict |
| liability, or tort (including negligence), arising in any way out of the use of this software, even if |
| advised of the possibility of such damage. |
| |
| 5. CONTACT INFORMATION |
| |
| Fraunhofer Institute for Integrated Circuits IIS |
| Attention: Audio and Multimedia Departments - FDK AAC LL |
| Am Wolfsmantel 33 |
| 91058 Erlangen, Germany |
| |
| www.iis.fraunhofer.de/amm |
| amm-info@iis.fraunhofer.de |
| ----------------------------------------------------------------------------------------------------------- */ |
| |
| /******************************** MPEG Audio Encoder ************************** |
| |
| Initial author: M.Werner |
| contents/description: Psychoaccoustic major function block |
| |
| ******************************************************************************/ |
| |
| #include "psy_const.h" |
| |
| #include "block_switch.h" |
| #include "transform.h" |
| #include "spreading.h" |
| #include "pre_echo_control.h" |
| #include "band_nrg.h" |
| #include "psy_configuration.h" |
| #include "psy_data.h" |
| #include "ms_stereo.h" |
| #include "interface.h" |
| #include "psy_main.h" |
| #include "grp_data.h" |
| #include "tns_func.h" |
| #include "pns_func.h" |
| #include "tonality.h" |
| #include "aacEnc_ram.h" |
| #include "intensity.h" |
| |
| |
| |
| /* blending to reduce gibbs artifacts */ |
| #define FADE_OUT_LEN 6 |
| static const FIXP_DBL fadeOutFactor[FADE_OUT_LEN] = {1840644096, 1533870080, 1227096064, 920322048, 613548032, 306774016}; |
| |
| /* forward definitions */ |
| |
| |
| static inline int isLowDelay( AUDIO_OBJECT_TYPE aot ) |
| { |
| return (aot==AOT_ER_AAC_LD || aot==AOT_ER_AAC_ELD); |
| } |
| |
| /***************************************************************************** |
| |
| functionname: FDKaacEnc_PsyNew |
| description: allocates memory for psychoacoustic |
| returns: an error code |
| input: pointer to a psych handle |
| |
| *****************************************************************************/ |
| AAC_ENCODER_ERROR FDKaacEnc_PsyNew(PSY_INTERNAL **phpsy, |
| const INT nElements, |
| const INT nChannels |
| ,UCHAR *dynamic_RAM |
| ) |
| { |
| AAC_ENCODER_ERROR ErrorStatus; |
| PSY_INTERNAL *hPsy; |
| INT i; |
| |
| hPsy = GetRam_aacEnc_PsyInternal(); |
| *phpsy = hPsy; |
| if (hPsy == NULL) { |
| ErrorStatus = AAC_ENC_NO_MEMORY; |
| goto bail; |
| } |
| |
| for (i=0; i<nElements; i++) { |
| /* PSY_ELEMENT */ |
| hPsy->psyElement[i] = GetRam_aacEnc_PsyElement(i); |
| if (hPsy->psyElement[i] == NULL) { |
| ErrorStatus = AAC_ENC_NO_MEMORY; |
| goto bail; |
| } |
| } |
| |
| for (i=0; i<nChannels; i++) { |
| /* PSY_STATIC */ |
| hPsy->pStaticChannels[i] = GetRam_aacEnc_PsyStatic(i); |
| if (hPsy->pStaticChannels[i]==NULL) { |
| ErrorStatus = AAC_ENC_NO_MEMORY; |
| goto bail; |
| } |
| /* AUDIO INPUT BUFFER */ |
| hPsy->pStaticChannels[i]->psyInputBuffer = GetRam_aacEnc_PsyInputBuffer(i); |
| if (hPsy->pStaticChannels[i]->psyInputBuffer==NULL) { |
| ErrorStatus = AAC_ENC_NO_MEMORY; |
| goto bail; |
| } |
| } |
| |
| /* reusable psych memory */ |
| hPsy->psyDynamic = GetRam_aacEnc_PsyDynamic(0, dynamic_RAM); |
| |
| return AAC_ENC_OK; |
| |
| bail: |
| FDKaacEnc_PsyClose(phpsy, NULL); |
| |
| return ErrorStatus; |
| } |
| |
| /***************************************************************************** |
| |
| functionname: FDKaacEnc_PsyOutNew |
| description: allocates memory for psyOut struc |
| returns: an error code |
| input: pointer to a psych handle |
| |
| *****************************************************************************/ |
| AAC_ENCODER_ERROR FDKaacEnc_PsyOutNew(PSY_OUT **phpsyOut, |
| const INT nElements, |
| const INT nChannels, |
| const INT nSubFrames |
| ,UCHAR *dynamic_RAM |
| ) |
| { |
| AAC_ENCODER_ERROR ErrorStatus; |
| int n, i; |
| int elInc = 0, chInc = 0; |
| |
| for (n=0; n<nSubFrames; n++) { |
| phpsyOut[n] = GetRam_aacEnc_PsyOut(n); |
| |
| if (phpsyOut[n] == NULL) { |
| ErrorStatus = AAC_ENC_NO_MEMORY; |
| goto bail; |
| } |
| |
| for (i=0; i<nChannels; i++) { |
| phpsyOut[n]->pPsyOutChannels[i] = GetRam_aacEnc_PsyOutChannel(chInc++); |
| } |
| |
| for (i=0; i<nElements; i++) { |
| phpsyOut[n]->psyOutElement[i] = GetRam_aacEnc_PsyOutElements(elInc++); |
| if (phpsyOut[n]->psyOutElement[i] == NULL) { |
| ErrorStatus = AAC_ENC_NO_MEMORY; |
| goto bail; |
| } |
| } |
| } /* nSubFrames */ |
| |
| return AAC_ENC_OK; |
| |
| bail: |
| FDKaacEnc_PsyClose(NULL, phpsyOut); |
| return ErrorStatus; |
| } |
| |
| |
| AAC_ENCODER_ERROR FDKaacEnc_psyInitStates(PSY_INTERNAL *hPsy, |
| PSY_STATIC* psyStatic, |
| AUDIO_OBJECT_TYPE audioObjectType) |
| { |
| /* init input buffer */ |
| FDKmemclear(psyStatic->psyInputBuffer, MAX_INPUT_BUFFER_SIZE*sizeof(INT_PCM)); |
| |
| FDKaacEnc_InitBlockSwitching(&psyStatic->blockSwitchingControl, |
| isLowDelay(audioObjectType) |
| ); |
| |
| return AAC_ENC_OK; |
| } |
| |
| |
| AAC_ENCODER_ERROR FDKaacEnc_psyInit(PSY_INTERNAL *hPsy, |
| PSY_OUT **phpsyOut, |
| const INT nSubFrames, |
| const INT nMaxChannels, |
| const AUDIO_OBJECT_TYPE audioObjectType, |
| CHANNEL_MAPPING *cm) |
| { |
| AAC_ENCODER_ERROR ErrorStatus = AAC_ENC_OK; |
| int i, ch, n, chInc = 0, resetChannels = 3; |
| |
| if ( (nMaxChannels>2) && (cm->nChannels==2) ) { |
| chInc = 1; |
| FDKaacEnc_psyInitStates(hPsy, hPsy->pStaticChannels[0], audioObjectType); |
| } |
| |
| if ( (nMaxChannels==2) ) { |
| resetChannels = 0; |
| } |
| |
| for (i=0; i<cm->nElements; i++) { |
| for (ch=0; ch<cm->elInfo[i].nChannelsInEl; ch++) { |
| if (cm->elInfo[i].elType!=ID_LFE) { |
| hPsy->psyElement[i]->psyStatic[ch] = hPsy->pStaticChannels[chInc]; |
| if (chInc>=resetChannels) { |
| FDKaacEnc_psyInitStates(hPsy, hPsy->psyElement[i]->psyStatic[ch], audioObjectType); |
| } |
| hPsy->psyElement[i]->psyStatic[ch]->isLFE = 0; |
| } |
| else { |
| hPsy->psyElement[i]->psyStatic[ch] = hPsy->pStaticChannels[nMaxChannels-1]; |
| hPsy->psyElement[i]->psyStatic[ch]->isLFE = 1; |
| } |
| chInc++; |
| } |
| } |
| |
| for (n=0; n<nSubFrames; n++) { |
| chInc = 0; |
| for (i=0; i<cm->nElements; i++) { |
| for (ch=0; ch<cm->elInfo[i].nChannelsInEl; ch++) { |
| phpsyOut[n]->psyOutElement[i]->psyOutChannel[ch] = phpsyOut[n]->pPsyOutChannels[chInc++]; |
| } |
| } |
| } |
| |
| return ErrorStatus; |
| } |
| |
| |
| /***************************************************************************** |
| |
| functionname: FDKaacEnc_psyMainInit |
| description: initializes psychoacoustic |
| returns: an error code |
| |
| *****************************************************************************/ |
| |
| AAC_ENCODER_ERROR FDKaacEnc_psyMainInit(PSY_INTERNAL *hPsy, |
| AUDIO_OBJECT_TYPE audioObjectType, |
| CHANNEL_MAPPING *cm, |
| INT sampleRate, |
| INT granuleLength, |
| INT bitRate, |
| INT tnsMask, |
| INT bandwidth, |
| INT usePns, |
| INT useIS, |
| UINT syntaxFlags, |
| ULONG initFlags) |
| { |
| AAC_ENCODER_ERROR ErrorStatus; |
| int i, ch; |
| int channelsEff = cm->nChannelsEff; |
| int tnsChannels = 0; |
| FB_TYPE filterBank; |
| |
| |
| switch(FDKaacEnc_GetMonoStereoMode(cm->encMode)) { |
| /* ... and map to tnsChannels */ |
| case EL_MODE_MONO: tnsChannels = 1; break; |
| case EL_MODE_STEREO: tnsChannels = 2; break; |
| default: tnsChannels = 0; |
| } |
| |
| switch (audioObjectType) |
| { |
| default: filterBank = FB_LC; break; |
| case AOT_ER_AAC_LD: filterBank = FB_LD; break; |
| case AOT_ER_AAC_ELD: filterBank = FB_ELD; break; |
| } |
| |
| hPsy->granuleLength = granuleLength; |
| |
| ErrorStatus = FDKaacEnc_InitPsyConfiguration(bitRate/channelsEff, sampleRate, bandwidth, LONG_WINDOW, hPsy->granuleLength, useIS, &(hPsy->psyConf[0]), filterBank); |
| if (ErrorStatus != AAC_ENC_OK) |
| return ErrorStatus; |
| |
| ErrorStatus = FDKaacEnc_InitTnsConfiguration( |
| (bitRate*tnsChannels)/channelsEff, |
| sampleRate, |
| tnsChannels, |
| LONG_WINDOW, |
| hPsy->granuleLength, |
| (syntaxFlags&AC_SBR_PRESENT)?1:0, |
| &(hPsy->psyConf[0].tnsConf), |
| &hPsy->psyConf[0], |
| (INT)(tnsMask&2), |
| (INT)(tnsMask&8) ); |
| |
| if (ErrorStatus != AAC_ENC_OK) |
| return ErrorStatus; |
| |
| if (granuleLength > 512) { |
| ErrorStatus = FDKaacEnc_InitPsyConfiguration(bitRate/channelsEff, sampleRate, bandwidth, SHORT_WINDOW, hPsy->granuleLength, useIS, &hPsy->psyConf[1], filterBank); |
| if (ErrorStatus != AAC_ENC_OK) |
| return ErrorStatus; |
| |
| ErrorStatus = FDKaacEnc_InitTnsConfiguration( |
| (bitRate*tnsChannels)/channelsEff, |
| sampleRate, |
| tnsChannels, |
| SHORT_WINDOW, |
| hPsy->granuleLength, |
| (syntaxFlags&AC_SBR_PRESENT)?1:0, |
| &hPsy->psyConf[1].tnsConf, |
| &hPsy->psyConf[1], |
| (INT)(tnsMask&1), |
| (INT)(tnsMask&4) ); |
| |
| if (ErrorStatus != AAC_ENC_OK) |
| return ErrorStatus; |
| |
| } |
| |
| |
| for (i=0; i<cm->nElements; i++) { |
| for (ch=0; ch<cm->elInfo[i].nChannelsInEl; ch++) { |
| if (initFlags) { |
| /* reset states */ |
| FDKaacEnc_psyInitStates(hPsy, hPsy->psyElement[i]->psyStatic[ch], audioObjectType); |
| } |
| |
| FDKaacEnc_InitPreEchoControl(hPsy->psyElement[i]->psyStatic[ch]->sfbThresholdnm1, |
| &hPsy->psyElement[i]->psyStatic[ch]->calcPreEcho, |
| hPsy->psyConf[0].sfbCnt, |
| hPsy->psyConf[0].sfbPcmQuantThreshold, |
| &hPsy->psyElement[i]->psyStatic[ch]->mdctScalenm1); |
| } |
| } |
| |
| ErrorStatus = FDKaacEnc_InitPnsConfiguration(&hPsy->psyConf[0].pnsConf, |
| bitRate/channelsEff, |
| sampleRate, |
| usePns, |
| hPsy->psyConf[0].sfbCnt, |
| hPsy->psyConf[0].sfbOffset, |
| cm->elInfo[0].nChannelsInEl, |
| (hPsy->psyConf[0].filterbank == FB_LC)); |
| if (ErrorStatus != AAC_ENC_OK) |
| return ErrorStatus; |
| |
| ErrorStatus = FDKaacEnc_InitPnsConfiguration(&hPsy->psyConf[1].pnsConf, |
| bitRate/channelsEff, |
| sampleRate, |
| usePns, |
| hPsy->psyConf[1].sfbCnt, |
| hPsy->psyConf[1].sfbOffset, |
| cm->elInfo[1].nChannelsInEl, |
| (hPsy->psyConf[1].filterbank == FB_LC)); |
| return ErrorStatus; |
| } |
| |
| |
| static |
| void FDKaacEnc_deinterleaveInputBuffer(INT_PCM *pOutputSamples, |
| INT_PCM *pInputSamples, |
| INT nSamples, |
| INT nChannels) |
| { |
| INT k; |
| /* deinterlave input samples and write to output buffer */ |
| for (k=0; k<nSamples; k++) { |
| pOutputSamples[k] = pInputSamples[k*nChannels]; |
| } |
| } |
| |
| |
| |
| /***************************************************************************** |
| |
| functionname: FDKaacEnc_psyMain |
| description: psychoacoustic |
| returns: an error code |
| |
| This function assumes that enough input data is in the modulo buffer. |
| |
| *****************************************************************************/ |
| |
| AAC_ENCODER_ERROR FDKaacEnc_psyMain(INT channels, |
| PSY_ELEMENT *psyElement, |
| PSY_DYNAMIC *psyDynamic, |
| PSY_CONFIGURATION *psyConf, |
| PSY_OUT_ELEMENT *RESTRICT psyOutElement, |
| INT_PCM *pInput, |
| INT *chIdx, |
| INT totalChannels |
| ) |
| { |
| INT commonWindow = 1; |
| INT maxSfbPerGroup[(2)]; |
| INT mdctSpectrum_e; |
| INT ch; /* counts through channels */ |
| INT w; /* counts through windows */ |
| INT sfb; /* counts through scalefactor bands */ |
| INT line; /* counts through lines */ |
| |
| PSY_CONFIGURATION *RESTRICT hPsyConfLong = &psyConf[0]; |
| PSY_CONFIGURATION *RESTRICT hPsyConfShort = &psyConf[1]; |
| PSY_OUT_CHANNEL **RESTRICT psyOutChannel = psyOutElement->psyOutChannel; |
| FIXP_SGL sfbTonality[(2)][MAX_SFB_LONG]; |
| |
| PSY_STATIC **RESTRICT psyStatic = psyElement->psyStatic; |
| |
| PSY_DATA *RESTRICT psyData[(2)]; |
| TNS_DATA *RESTRICT tnsData[(2)]; |
| PNS_DATA *RESTRICT pnsData[(2)]; |
| |
| INT zeroSpec = TRUE; /* means all spectral lines are zero */ |
| |
| INT blockSwitchingOffset; |
| |
| PSY_CONFIGURATION *RESTRICT hThisPsyConf[(2)]; |
| INT windowLength[(2)]; |
| INT nWindows[(2)]; |
| INT wOffset; |
| |
| INT maxSfb[(2)]; |
| INT *pSfbMaxScaleSpec[(2)]; |
| FIXP_DBL *pSfbEnergy[(2)]; |
| FIXP_DBL *pSfbSpreadEnergy[(2)]; |
| FIXP_DBL *pSfbEnergyLdData[(2)]; |
| FIXP_DBL *pSfbEnergyMS[(2)]; |
| FIXP_DBL *pSfbThreshold[(2)]; |
| |
| INT isShortWindow[(2)]; |
| |
| |
| if (hPsyConfLong->filterbank == FB_LC) { |
| blockSwitchingOffset = psyConf->granuleLength + (9*psyConf->granuleLength/(2*TRANS_FAC)); |
| } else { |
| blockSwitchingOffset = psyConf->granuleLength; |
| } |
| |
| for(ch = 0; ch < channels; ch++) |
| { |
| psyData[ch] = &psyDynamic->psyData[ch]; |
| tnsData[ch] = &psyDynamic->tnsData[ch]; |
| pnsData[ch] = &psyDynamic->pnsData[ch]; |
| |
| psyData[ch]->mdctSpectrum = psyOutChannel[ch]->mdctSpectrum; |
| } |
| |
| /* block switching */ |
| if (hPsyConfLong->filterbank != FB_ELD) |
| { |
| int err; |
| |
| for(ch = 0; ch < channels; ch++) |
| { |
| C_ALLOC_SCRATCH_START(timeSignal, INT_PCM, (1024)); |
| psyStatic[ch]->blockSwitchingControl.timeSignal = timeSignal; |
| |
| /* deinterleave input data and use for block switching */ |
| FDKaacEnc_deinterleaveInputBuffer( psyStatic[ch]->blockSwitchingControl.timeSignal, |
| &pInput[chIdx[ch]], |
| psyConf->granuleLength, |
| totalChannels); |
| |
| |
| FDKaacEnc_BlockSwitching (&psyStatic[ch]->blockSwitchingControl, |
| psyConf->granuleLength |
| ,psyStatic[ch]->isLFE |
| ); |
| |
| |
| /* fill up internal input buffer, to 2xframelength samples */ |
| FDKmemcpy(psyStatic[ch]->psyInputBuffer+blockSwitchingOffset, |
| psyStatic[ch]->blockSwitchingControl.timeSignal, |
| (2*psyConf->granuleLength-blockSwitchingOffset)*sizeof(INT_PCM)); |
| |
| C_ALLOC_SCRATCH_END(timeSignal, INT_PCM, (1024)); |
| } |
| |
| /* synch left and right block type */ |
| err = FDKaacEnc_SyncBlockSwitching(&psyStatic[0]->blockSwitchingControl, |
| &psyStatic[1]->blockSwitchingControl, |
| channels, |
| commonWindow); |
| |
| if (err) { |
| return AAC_ENC_UNSUPPORTED_AOT; /* mixed up LC and LD */ |
| } |
| |
| } |
| else { |
| for(ch = 0; ch < channels; ch++) |
| { |
| /* deinterleave input data and use for block switching */ |
| FDKaacEnc_deinterleaveInputBuffer( psyStatic[ch]->psyInputBuffer + blockSwitchingOffset, |
| &pInput[chIdx[ch]], |
| psyConf->granuleLength, |
| totalChannels); |
| } |
| } |
| |
| for(ch = 0; ch < channels; ch++) |
| isShortWindow[ch]=(psyStatic[ch]->blockSwitchingControl.lastWindowSequence == SHORT_WINDOW); |
| |
| /* set parameters according to window length */ |
| for(ch = 0; ch < channels; ch++) |
| { |
| if(isShortWindow[ch]) { |
| hThisPsyConf[ch] = hPsyConfShort; |
| windowLength[ch] = psyConf->granuleLength/TRANS_FAC; |
| nWindows[ch] = TRANS_FAC; |
| maxSfb[ch] = MAX_SFB_SHORT; |
| |
| pSfbMaxScaleSpec[ch] = psyData[ch]->sfbMaxScaleSpec.Short[0]; |
| pSfbEnergy[ch] = psyData[ch]->sfbEnergy.Short[0]; |
| pSfbSpreadEnergy[ch] = psyData[ch]->sfbSpreadEnergy.Short[0]; |
| pSfbEnergyLdData[ch] = psyData[ch]->sfbEnergyLdData.Short[0]; |
| pSfbEnergyMS[ch] = psyData[ch]->sfbEnergyMS.Short[0]; |
| pSfbThreshold[ch] = psyData[ch]->sfbThreshold.Short[0]; |
| |
| } else |
| { |
| hThisPsyConf[ch] = hPsyConfLong; |
| windowLength[ch] = psyConf->granuleLength; |
| nWindows[ch] = 1; |
| maxSfb[ch] = MAX_GROUPED_SFB; |
| |
| pSfbMaxScaleSpec[ch] = psyData[ch]->sfbMaxScaleSpec.Long; |
| pSfbEnergy[ch] = psyData[ch]->sfbEnergy.Long; |
| pSfbSpreadEnergy[ch] = psyData[ch]->sfbSpreadEnergy.Long; |
| pSfbEnergyLdData[ch] = psyData[ch]->sfbEnergyLdData.Long; |
| pSfbEnergyMS[ch] = psyData[ch]->sfbEnergyMS.Long; |
| pSfbThreshold[ch] = psyData[ch]->sfbThreshold.Long; |
| } |
| } |
| |
| /* Transform and get mdctScaling for all channels and windows. */ |
| for(ch = 0; ch < channels; ch++) |
| { |
| /* update number of active bands */ |
| if (psyStatic[ch]->isLFE) { |
| psyData[ch]->sfbActive = hThisPsyConf[ch]->sfbActiveLFE; |
| psyData[ch]->lowpassLine = hThisPsyConf[ch]->lowpassLineLFE; |
| } else |
| { |
| psyData[ch]->sfbActive = hThisPsyConf[ch]->sfbActive; |
| psyData[ch]->lowpassLine = hThisPsyConf[ch]->lowpassLine; |
| } |
| |
| for(w = 0; w < nWindows[ch]; w++) { |
| |
| wOffset = w*windowLength[ch]; |
| |
| FDKaacEnc_Transform_Real( psyStatic[ch]->psyInputBuffer + wOffset, |
| psyData[ch]->mdctSpectrum+wOffset, |
| psyStatic[ch]->blockSwitchingControl.lastWindowSequence, |
| psyStatic[ch]->blockSwitchingControl.windowShape, |
| &psyStatic[ch]->blockSwitchingControl.lastWindowShape, |
| psyConf->granuleLength, |
| &mdctSpectrum_e, |
| hThisPsyConf[ch]->filterbank |
| ,psyStatic[ch]->overlapAddBuffer |
| ); |
| |
| /* Low pass / highest sfb */ |
| FDKmemclear(&psyData[ch]->mdctSpectrum[psyData[ch]->lowpassLine+wOffset], |
| (windowLength[ch]-psyData[ch]->lowpassLine)*sizeof(FIXP_DBL)); |
| |
| if (hPsyConfLong->filterbank != FB_LC) { |
| /* Do blending to reduce gibbs artifacts */ |
| for (int i=0; i<FADE_OUT_LEN; i++) { |
| psyData[ch]->mdctSpectrum[psyData[ch]->lowpassLine+wOffset - FADE_OUT_LEN + i] = fMult(psyData[ch]->mdctSpectrum[psyData[ch]->lowpassLine+wOffset - FADE_OUT_LEN + i], fadeOutFactor[i]); |
| } |
| } |
| |
| |
| /* Check for zero spectrum. These loops will usually terminate very, very early. */ |
| for(line=0; (line<psyData[ch]->lowpassLine) && (zeroSpec==TRUE); line++) { |
| if (psyData[ch]->mdctSpectrum[line+wOffset] != (FIXP_DBL)0) { |
| zeroSpec = FALSE; |
| break; |
| } |
| } |
| |
| } /* w loop */ |
| |
| psyData[ch]->mdctScale = mdctSpectrum_e; |
| |
| /* rotate internal time samples */ |
| FDKmemmove(psyStatic[ch]->psyInputBuffer, |
| psyStatic[ch]->psyInputBuffer+psyConf->granuleLength, |
| psyConf->granuleLength*sizeof(INT_PCM)); |
| |
| |
| /* ... and get remaining samples from input buffer */ |
| FDKaacEnc_deinterleaveInputBuffer( psyStatic[ch]->psyInputBuffer+psyConf->granuleLength, |
| &pInput[ (2*psyConf->granuleLength-blockSwitchingOffset)*totalChannels + chIdx[ch] ], |
| blockSwitchingOffset-psyConf->granuleLength, |
| totalChannels); |
| |
| } /* ch */ |
| |
| /* Do some rescaling to get maximum possible accuracy for energies */ |
| if ( zeroSpec == FALSE) { |
| |
| /* Calc possible spectrum leftshift for each sfb (1 means: 1 bit left shift is possible without overflow) */ |
| INT minSpecShift = MAX_SHIFT_DBL; |
| INT nrgShift = MAX_SHIFT_DBL; |
| INT finalShift = MAX_SHIFT_DBL; |
| FIXP_DBL currNrg = 0; |
| FIXP_DBL maxNrg = 0; |
| |
| for(ch = 0; ch < channels; ch++) { |
| for(w = 0; w < nWindows[ch]; w++) { |
| wOffset = w*windowLength[ch]; |
| FDKaacEnc_CalcSfbMaxScaleSpec(psyData[ch]->mdctSpectrum+wOffset, |
| hThisPsyConf[ch]->sfbOffset, |
| pSfbMaxScaleSpec[ch]+w*maxSfb[ch], |
| psyData[ch]->sfbActive); |
| |
| for (sfb = 0; sfb<psyData[ch]->sfbActive; sfb++) |
| minSpecShift = fixMin(minSpecShift, (pSfbMaxScaleSpec[ch]+w*maxSfb[ch])[sfb]); |
| } |
| |
| } |
| |
| /* Calc possible energy leftshift for each sfb (1 means: 1 bit left shift is possible without overflow) */ |
| for(ch = 0; ch < channels; ch++) { |
| for(w = 0; w < nWindows[ch]; w++) { |
| wOffset = w*windowLength[ch]; |
| currNrg = FDKaacEnc_CheckBandEnergyOptim(psyData[ch]->mdctSpectrum+wOffset, |
| pSfbMaxScaleSpec[ch]+w*maxSfb[ch], |
| hThisPsyConf[ch]->sfbOffset, |
| psyData[ch]->sfbActive, |
| pSfbEnergy[ch]+w*maxSfb[ch], |
| pSfbEnergyLdData[ch]+w*maxSfb[ch], |
| minSpecShift-4); |
| |
| maxNrg = fixMax(maxNrg, currNrg); |
| } |
| } |
| |
| if ( maxNrg != (FIXP_DBL)0 ) { |
| nrgShift = (CountLeadingBits(maxNrg)>>1) + (minSpecShift-4); |
| } |
| |
| /* 2check: Hasn't this decision to be made for both channels? */ |
| /* For short windows 1 additional bit headroom is necessary to prevent overflows when summing up energies in FDKaacEnc_groupShortData() */ |
| if(isShortWindow[0]) nrgShift--; |
| |
| /* both spectrum and energies mustn't overflow */ |
| finalShift = fixMin(minSpecShift, nrgShift); |
| |
| /* do not shift more than 3 bits more to the left than signal without blockfloating point |
| * would be to avoid overflow of scaled PCM quantization thresholds */ |
| if (finalShift > psyData[0]->mdctScale + 3 ) |
| finalShift = psyData[0]->mdctScale + 3; |
| |
| FDK_ASSERT(finalShift >= 0); /* right shift is not allowed */ |
| |
| /* correct sfbEnergy and sfbEnergyLdData with new finalShift */ |
| FIXP_DBL ldShift = finalShift * FL2FXCONST_DBL(2.0/64); |
| for(ch = 0; ch < channels; ch++) { |
| for(w = 0; w < nWindows[ch]; w++) { |
| for(sfb=0; sfb<psyData[ch]->sfbActive; sfb++) { |
| INT scale = fixMax(0, (pSfbMaxScaleSpec[ch]+w*maxSfb[ch])[sfb]-4); |
| scale = fixMin((scale-finalShift)<<1, DFRACT_BITS-1); |
| if (scale >= 0) (pSfbEnergy[ch]+w*maxSfb[ch])[sfb] >>= (scale); |
| else (pSfbEnergy[ch]+w*maxSfb[ch])[sfb] <<= (-scale); |
| (pSfbThreshold[ch]+w*maxSfb[ch])[sfb] = fMult((pSfbEnergy[ch]+w*maxSfb[ch])[sfb], C_RATIO); |
| (pSfbEnergyLdData[ch]+w*maxSfb[ch])[sfb] += ldShift; |
| } |
| } |
| } |
| |
| if ( finalShift != 0 ) { |
| for (ch = 0; ch < channels; ch++) { |
| for(w = 0; w < nWindows[ch]; w++) { |
| wOffset = w*windowLength[ch]; |
| for(line=0; line<psyData[ch]->lowpassLine; line++) { |
| psyData[ch]->mdctSpectrum[line+wOffset] <<= finalShift; |
| } |
| /* update sfbMaxScaleSpec */ |
| for (sfb = 0; sfb<psyData[ch]->sfbActive; sfb++) |
| (pSfbMaxScaleSpec[ch]+w*maxSfb[ch])[sfb] -= finalShift; |
| } |
| /* update mdctScale */ |
| psyData[ch]->mdctScale -= finalShift; |
| } |
| } |
| |
| } else { |
| /* all spectral lines are zero */ |
| for (ch = 0; ch < channels; ch++) { |
| psyData[ch]->mdctScale = 0; /* otherwise mdctScale would be for example 7 and PCM quantization thresholds would be shifted |
| * 14 bits to the right causing some of them to become 0 (which causes problems later) */ |
| /* clear sfbMaxScaleSpec */ |
| for(w = 0; w < nWindows[ch]; w++) { |
| for (sfb = 0; sfb<psyData[ch]->sfbActive; sfb++) { |
| (pSfbMaxScaleSpec[ch]+w*maxSfb[ch])[sfb] = 0; |
| (pSfbEnergy[ch]+w*maxSfb[ch])[sfb] = (FIXP_DBL)0; |
| (pSfbEnergyLdData[ch]+w*maxSfb[ch])[sfb] = FL2FXCONST_DBL(-1.0f); |
| (pSfbThreshold[ch]+w*maxSfb[ch])[sfb] = (FIXP_DBL)0; |
| } |
| } |
| } |
| } |
| |
| /* Advance psychoacoustics: Tonality and TNS */ |
| if (psyStatic[0]->isLFE) { |
| tnsData[0]->dataRaw.Long.subBlockInfo.tnsActive = 0; |
| } |
| else |
| { |
| |
| for(ch = 0; ch < channels; ch++) { |
| if (!isShortWindow[ch]) { |
| /* tonality */ |
| FDKaacEnc_CalculateFullTonality( psyData[ch]->mdctSpectrum, |
| pSfbMaxScaleSpec[ch], |
| pSfbEnergyLdData[ch], |
| sfbTonality[ch], |
| psyData[ch]->sfbActive, |
| hThisPsyConf[ch]->sfbOffset, |
| hThisPsyConf[ch]->pnsConf.usePns); |
| } |
| } |
| |
| if (hPsyConfLong->tnsConf.tnsActive || hPsyConfShort->tnsConf.tnsActive) { |
| INT tnsActive[TRANS_FAC]; |
| INT nrgScaling[2] = {0,0}; |
| INT tnsSpecShift = 0; |
| |
| for(ch = 0; ch < channels; ch++) { |
| for(w = 0; w < nWindows[ch]; w++) { |
| |
| wOffset = w*windowLength[ch]; |
| /* TNS */ |
| FDKaacEnc_TnsDetect( |
| tnsData[ch], |
| &hThisPsyConf[ch]->tnsConf, |
| &psyOutChannel[ch]->tnsInfo, |
| hThisPsyConf[ch]->sfbCnt, |
| psyData[ch]->mdctSpectrum+wOffset, |
| w, |
| psyStatic[ch]->blockSwitchingControl.lastWindowSequence |
| ); |
| } |
| } |
| |
| if (channels == 2) { |
| FDKaacEnc_TnsSync( |
| tnsData[1], |
| tnsData[0], |
| &psyOutChannel[1]->tnsInfo, |
| &psyOutChannel[0]->tnsInfo, |
| |
| psyStatic[1]->blockSwitchingControl.lastWindowSequence, |
| psyStatic[0]->blockSwitchingControl.lastWindowSequence, |
| &hThisPsyConf[1]->tnsConf); |
| } |
| |
| FDK_ASSERT(commonWindow=1); /* all checks for TNS do only work for common windows (which is always set)*/ |
| for(w = 0; w < nWindows[0]; w++) |
| { |
| if (isShortWindow[0]) |
| tnsActive[w] = tnsData[0]->dataRaw.Short.subBlockInfo[w].tnsActive || |
| ((channels == 2) ? tnsData[1]->dataRaw.Short.subBlockInfo[w].tnsActive : 0); |
| else |
| tnsActive[w] = tnsData[0]->dataRaw.Long.subBlockInfo.tnsActive || |
| ((channels == 2) ? tnsData[1]->dataRaw.Long.subBlockInfo.tnsActive : 0); |
| } |
| |
| for(ch = 0; ch < channels; ch++) { |
| if (tnsActive[0] && !isShortWindow[ch]) { |
| /* Scale down spectrum if tns is active in one of the two channels with same lastWindowSequence */ |
| /* first part of threshold calculation; it's not necessary to update sfbMaxScaleSpec */ |
| INT shift = 1; |
| for(sfb=0; sfb<hThisPsyConf[ch]->lowpassLine; sfb++) { |
| psyData[ch]->mdctSpectrum[sfb] = psyData[ch]->mdctSpectrum[sfb] >> shift; |
| } |
| |
| /* update thresholds */ |
| for (sfb=0; sfb<psyData[ch]->sfbActive; sfb++) { |
| pSfbThreshold[ch][sfb] >>= (2*shift); |
| } |
| |
| psyData[ch]->mdctScale += shift; /* update mdctScale */ |
| |
| /* calc sfbEnergies after tnsEncode again ! */ |
| |
| } |
| } |
| |
| for(ch = 0; ch < channels; ch++) { |
| for(w = 0; w < nWindows[ch]; w++) |
| { |
| wOffset = w*windowLength[ch]; |
| FDKaacEnc_TnsEncode( |
| &psyOutChannel[ch]->tnsInfo, |
| tnsData[ch], |
| hThisPsyConf[ch]->sfbCnt, |
| &hThisPsyConf[ch]->tnsConf, |
| hThisPsyConf[ch]->sfbOffset[psyData[ch]->sfbActive],/*hThisPsyConf[ch]->lowpassLine*/ /* filter stops before that line ! */ |
| psyData[ch]->mdctSpectrum+wOffset, |
| w, |
| psyStatic[ch]->blockSwitchingControl.lastWindowSequence); |
| |
| if(tnsActive[w]) { |
| /* Calc sfb-bandwise mdct-energies for left and right channel again, */ |
| /* if tns active in current channel or in one channel with same lastWindowSequence left and right */ |
| FDKaacEnc_CalcSfbMaxScaleSpec(psyData[ch]->mdctSpectrum+wOffset, |
| hThisPsyConf[ch]->sfbOffset, |
| pSfbMaxScaleSpec[ch]+w*maxSfb[ch], |
| psyData[ch]->sfbActive); |
| } |
| } |
| } |
| |
| for(ch = 0; ch < channels; ch++) { |
| for(w = 0; w < nWindows[ch]; w++) { |
| |
| if (tnsActive[w]) { |
| |
| if (isShortWindow[ch]) { |
| FDKaacEnc_CalcBandEnergyOptimShort(psyData[ch]->mdctSpectrum+w*windowLength[ch], |
| pSfbMaxScaleSpec[ch]+w*maxSfb[ch], |
| hThisPsyConf[ch]->sfbOffset, |
| psyData[ch]->sfbActive, |
| pSfbEnergy[ch]+w*maxSfb[ch]); |
| } |
| else { |
| nrgScaling[ch] = /* with tns, energy calculation can overflow; -> scaling */ |
| FDKaacEnc_CalcBandEnergyOptimLong(psyData[ch]->mdctSpectrum, |
| pSfbMaxScaleSpec[ch], |
| hThisPsyConf[ch]->sfbOffset, |
| psyData[ch]->sfbActive, |
| pSfbEnergy[ch], |
| pSfbEnergyLdData[ch]); |
| tnsSpecShift = fixMax(tnsSpecShift, nrgScaling[ch]); /* nrgScaling is set only if nrg would have an overflow */ |
| } |
| } /* if tnsActive */ |
| } |
| } /* end channel loop */ |
| |
| /* adapt scaling to prevent nrg overflow, only for long blocks */ |
| for(ch = 0; ch < channels; ch++) { |
| if ( (tnsSpecShift!=0) && !isShortWindow[ch] ) { |
| /* scale down spectrum, nrg's and thresholds, if there was an overflow in sfbNrg calculation after tns */ |
| for(line=0; line<hThisPsyConf[ch]->lowpassLine; line++) { |
| psyData[ch]->mdctSpectrum[line] >>= tnsSpecShift; |
| } |
| INT scale = (tnsSpecShift-nrgScaling[ch])<<1; |
| for(sfb=0; sfb<psyData[ch]->sfbActive; sfb++) { |
| pSfbEnergyLdData[ch][sfb] -= scale*FL2FXCONST_DBL(1.0/LD_DATA_SCALING); |
| pSfbEnergy[ch][sfb] >>= scale; |
| pSfbThreshold[ch][sfb] >>= (tnsSpecShift<<1); |
| } |
| psyData[ch]->mdctScale += tnsSpecShift; /* update mdctScale; not necessary to update sfbMaxScaleSpec */ |
| |
| } |
| } /* end channel loop */ |
| |
| } /* TNS active */ |
| } /* !isLFE */ |
| |
| |
| |
| |
| |
| |
| /* Advance thresholds */ |
| for(ch = 0; ch < channels; ch++) { |
| INT headroom; |
| |
| FIXP_DBL clipEnergy; |
| INT energyShift = psyData[ch]->mdctScale*2 ; |
| INT clipNrgShift = energyShift - THR_SHIFTBITS ; |
| |
| if(isShortWindow[ch]) |
| headroom = 6; |
| else |
| headroom = 0; |
| |
| if (clipNrgShift >= 0) |
| clipEnergy = hThisPsyConf[ch]->clipEnergy >> clipNrgShift ; |
| else if (clipNrgShift>=-headroom) |
| clipEnergy = hThisPsyConf[ch]->clipEnergy << -clipNrgShift ; |
| else |
| clipEnergy = (FIXP_DBL)MAXVAL_DBL ; |
| |
| for(w = 0; w < nWindows[ch]; w++) |
| { |
| INT i; |
| /* limit threshold to avoid clipping */ |
| for (i=0; i<psyData[ch]->sfbActive; i++) { |
| *(pSfbThreshold[ch]+w*maxSfb[ch]+i) = fixMin(*(pSfbThreshold[ch]+w*maxSfb[ch]+i), clipEnergy); |
| } |
| |
| /* spreading */ |
| FDKaacEnc_SpreadingMax(psyData[ch]->sfbActive, |
| hThisPsyConf[ch]->sfbMaskLowFactor, |
| hThisPsyConf[ch]->sfbMaskHighFactor, |
| pSfbThreshold[ch]+w*maxSfb[ch]); |
| |
| |
| /* PCM quantization threshold */ |
| energyShift += PCM_QUANT_THR_SCALE; |
| if (energyShift>=0) { |
| energyShift = fixMin(DFRACT_BITS-1,energyShift); |
| for (i=0; i<psyData[ch]->sfbActive;i++) { |
| *(pSfbThreshold[ch]+w*maxSfb[ch]+i) = fixMax(*(pSfbThreshold[ch]+w*maxSfb[ch]+i) >> THR_SHIFTBITS, |
| (hThisPsyConf[ch]->sfbPcmQuantThreshold[i] >> energyShift)); |
| } |
| } else { |
| energyShift = fixMin(DFRACT_BITS-1,-energyShift); |
| for (i=0; i<psyData[ch]->sfbActive;i++) { |
| *(pSfbThreshold[ch]+w*maxSfb[ch]+i) = fixMax(*(pSfbThreshold[ch]+w*maxSfb[ch]+i) >> THR_SHIFTBITS, |
| (hThisPsyConf[ch]->sfbPcmQuantThreshold[i] << energyShift)); |
| } |
| } |
| |
| if (!psyStatic[ch]->isLFE) |
| { |
| /* preecho control */ |
| if(psyStatic[ch]->blockSwitchingControl.lastWindowSequence == STOP_WINDOW) { |
| /* prevent FDKaacEnc_PreEchoControl from comparing stop |
| thresholds with short thresholds */ |
| for (i=0; i<psyData[ch]->sfbActive;i++) { |
| psyStatic[ch]->sfbThresholdnm1[i] = (FIXP_DBL)MAXVAL_DBL; |
| } |
| |
| psyStatic[ch]->mdctScalenm1 = 0; |
| psyStatic[ch]->calcPreEcho = 0; |
| } |
| |
| FDKaacEnc_PreEchoControl( psyStatic[ch]->sfbThresholdnm1, |
| psyStatic[ch]->calcPreEcho, |
| psyData[ch]->sfbActive, |
| hThisPsyConf[ch]->maxAllowedIncreaseFactor, |
| hThisPsyConf[ch]->minRemainingThresholdFactor, |
| pSfbThreshold[ch]+w*maxSfb[ch], |
| psyData[ch]->mdctScale, |
| &psyStatic[ch]->mdctScalenm1); |
| |
| psyStatic[ch]->calcPreEcho = 1; |
| |
| if(psyStatic[ch]->blockSwitchingControl.lastWindowSequence == START_WINDOW) |
| { |
| /* prevent FDKaacEnc_PreEchoControl in next frame to compare start |
| thresholds with short thresholds */ |
| for (i=0; i<psyData[ch]->sfbActive;i++) { |
| psyStatic[ch]->sfbThresholdnm1[i] = (FIXP_DBL)MAXVAL_DBL; |
| } |
| |
| psyStatic[ch]->mdctScalenm1 = 0; |
| psyStatic[ch]->calcPreEcho = 0; |
| } |
| |
| } |
| |
| /* spread energy to avoid hole detection */ |
| FDKmemcpy(pSfbSpreadEnergy[ch]+w*maxSfb[ch], pSfbEnergy[ch]+w*maxSfb[ch], psyData[ch]->sfbActive*sizeof(FIXP_DBL)); |
| |
| FDKaacEnc_SpreadingMax(psyData[ch]->sfbActive, |
| hThisPsyConf[ch]->sfbMaskLowFactorSprEn, |
| hThisPsyConf[ch]->sfbMaskHighFactorSprEn, |
| pSfbSpreadEnergy[ch]+w*maxSfb[ch]); |
| } |
| } |
| |
| /* Calc bandwise energies for mid and side channel. Do it only if 2 channels exist */ |
| if (channels==2) { |
| for(w = 0; w < nWindows[1]; w++) { |
| wOffset = w*windowLength[1]; |
| FDKaacEnc_CalcBandNrgMSOpt(psyData[0]->mdctSpectrum+wOffset, |
| psyData[1]->mdctSpectrum+wOffset, |
| pSfbMaxScaleSpec[0]+w*maxSfb[0], |
| pSfbMaxScaleSpec[1]+w*maxSfb[1], |
| hThisPsyConf[1]->sfbOffset, |
| psyData[0]->sfbActive, |
| pSfbEnergyMS[0]+w*maxSfb[0], |
| pSfbEnergyMS[1]+w*maxSfb[1], |
| (psyStatic[1]->blockSwitchingControl.lastWindowSequence != SHORT_WINDOW), |
| psyData[0]->sfbEnergyMSLdData, |
| psyData[1]->sfbEnergyMSLdData); |
| } |
| } |
| |
| /* group short data (maxSfb[ch] for short blocks is determined here) */ |
| for(ch=0;ch<channels;ch++) |
| { |
| INT noSfb, i; |
| if(isShortWindow[ch]) |
| { |
| int sfbGrp; |
| noSfb = psyStatic[ch]->blockSwitchingControl.noOfGroups * hPsyConfShort->sfbCnt; |
| /* At this point, energies and thresholds are copied/regrouped from the ".Short" to the ".Long" arrays */ |
| FDKaacEnc_groupShortData( psyData[ch]->mdctSpectrum, |
| &psyData[ch]->sfbThreshold, |
| &psyData[ch]->sfbEnergy, |
| &psyData[ch]->sfbEnergyMS, |
| &psyData[ch]->sfbSpreadEnergy, |
| hPsyConfShort->sfbCnt, |
| psyData[ch]->sfbActive, |
| hPsyConfShort->sfbOffset, |
| hPsyConfShort->sfbMinSnrLdData, |
| psyData[ch]->groupedSfbOffset, |
| &maxSfbPerGroup[ch], |
| psyOutChannel[ch]->sfbMinSnrLdData, |
| psyStatic[ch]->blockSwitchingControl.noOfGroups, |
| psyStatic[ch]->blockSwitchingControl.groupLen, |
| psyConf[1].granuleLength); |
| |
| |
| /* calculate ldData arrays (short values are in .Long-arrays after FDKaacEnc_groupShortData) */ |
| for (sfbGrp = 0; sfbGrp < noSfb; sfbGrp += hPsyConfShort->sfbCnt) { |
| LdDataVector(&psyData[ch]->sfbEnergy.Long[sfbGrp], &psyOutChannel[ch]->sfbEnergyLdData[sfbGrp], psyData[ch]->sfbActive); |
| } |
| |
| /* calc sfbThrld and set Values smaller 2^-31 to 2^-33*/ |
| for (sfbGrp = 0; sfbGrp < noSfb; sfbGrp += hPsyConfShort->sfbCnt) { |
| LdDataVector(&psyData[ch]->sfbThreshold.Long[sfbGrp], &psyOutChannel[ch]->sfbThresholdLdData[sfbGrp], psyData[ch]->sfbActive); |
| for (sfb=0;sfb<psyData[ch]->sfbActive;sfb++) { |
| psyOutChannel[ch]->sfbThresholdLdData[sfbGrp+sfb] = |
| fixMax(psyOutChannel[ch]->sfbThresholdLdData[sfbGrp+sfb], FL2FXCONST_DBL(-0.515625f)); |
| } |
| } |
| |
| if ( channels==2 ) { |
| for (sfbGrp = 0; sfbGrp < noSfb; sfbGrp += hPsyConfShort->sfbCnt) { |
| LdDataVector(&psyData[ch]->sfbEnergyMS.Long[sfbGrp], &psyData[ch]->sfbEnergyMSLdData[sfbGrp], psyData[ch]->sfbActive); |
| } |
| } |
| |
| FDKmemcpy(psyOutChannel[ch]->sfbOffsets, psyData[ch]->groupedSfbOffset, (MAX_GROUPED_SFB+1)*sizeof(INT)); |
| |
| } else { |
| /* maxSfb[ch] for long blocks */ |
| for (sfb = psyData[ch]->sfbActive-1; sfb >= 0; sfb--) { |
| for (line = hPsyConfLong->sfbOffset[sfb+1]-1; line >= hPsyConfLong->sfbOffset[sfb]; line--) { |
| if (psyData[ch]->mdctSpectrum[line] != FL2FXCONST_SGL(0.0f)) break; |
| } |
| if (line > hPsyConfLong->sfbOffset[sfb]) break; |
| } |
| maxSfbPerGroup[ch] = sfb + 1; |
| /* ensure at least one section in ICS; workaround for existing decoder crc implementation */ |
| maxSfbPerGroup[ch] = fixMax(fixMin(5,psyData[ch]->sfbActive),maxSfbPerGroup[ch]); |
| |
| /* sfbNrgLdData is calculated in FDKaacEnc_advancePsychLong, copy in psyOut structure */ |
| FDKmemcpy(psyOutChannel[ch]->sfbEnergyLdData, psyData[ch]->sfbEnergyLdData.Long, psyData[ch]->sfbActive*sizeof(FIXP_DBL)); |
| |
| FDKmemcpy(psyOutChannel[ch]->sfbOffsets, hPsyConfLong->sfbOffset, (MAX_GROUPED_SFB+1)*sizeof(INT)); |
| |
| /* sfbMinSnrLdData modified in adjust threshold, copy necessary */ |
| FDKmemcpy(psyOutChannel[ch]->sfbMinSnrLdData, hPsyConfLong->sfbMinSnrLdData, psyData[ch]->sfbActive*sizeof(FIXP_DBL)); |
| |
| /* sfbEnergyMSLdData ist already calculated in FDKaacEnc_CalcBandNrgMSOpt; only in long case */ |
| |
| /* calc sfbThrld and set Values smaller 2^-31 to 2^-33*/ |
| LdDataVector(psyData[ch]->sfbThreshold.Long, psyOutChannel[ch]->sfbThresholdLdData, psyData[ch]->sfbActive); |
| for (i=0;i<psyData[ch]->sfbActive;i++) { |
| psyOutChannel[ch]->sfbThresholdLdData[i] = |
| fixMax(psyOutChannel[ch]->sfbThresholdLdData[i], FL2FXCONST_DBL(-0.515625f)); |
| } |
| |
| |
| } |
| |
| |
| } |
| |
| |
| /* |
| Intensity parameter intialization. |
| */ |
| for(ch=0;ch<channels;ch++) { |
| FDKmemclear(psyOutChannel[ch]->isBook, MAX_GROUPED_SFB*sizeof(INT)); |
| FDKmemclear(psyOutChannel[ch]->isScale, MAX_GROUPED_SFB*sizeof(INT)); |
| } |
| |
| for(ch=0;ch<channels;ch++) { |
| INT win = (isShortWindow[ch]?1:0); |
| if (!psyStatic[ch]->isLFE) |
| { |
| /* PNS Decision */ |
| FDKaacEnc_PnsDetect( &(psyConf[0].pnsConf), |
| pnsData[ch], |
| psyStatic[ch]->blockSwitchingControl.lastWindowSequence, |
| psyData[ch]->sfbActive, |
| maxSfbPerGroup[ch], /* count of Sfb which are not zero. */ |
| psyOutChannel[ch]->sfbThresholdLdData, |
| psyConf[win].sfbOffset, |
| psyData[ch]->mdctSpectrum, |
| psyData[ch]->sfbMaxScaleSpec.Long, |
| sfbTonality[ch], |
| psyOutChannel[ch]->tnsInfo.order[0][0], |
| tnsData[ch]->dataRaw.Long.subBlockInfo.predictionGain, |
| tnsData[ch]->dataRaw.Long.subBlockInfo.tnsActive, |
| psyOutChannel[ch]->sfbEnergyLdData, |
| psyOutChannel[ch]->noiseNrg ); |
| } /* !isLFE */ |
| } |
| |
| /* |
| stereo Processing |
| */ |
| if(channels == 2) |
| { |
| psyOutElement->toolsInfo.msDigest = MS_NONE; |
| psyOutElement->commonWindow = commonWindow; |
| if (psyOutElement->commonWindow) |
| maxSfbPerGroup[0] = maxSfbPerGroup[1] = |
| fixMax(maxSfbPerGroup[0], maxSfbPerGroup[1]); |
| |
| if(psyStatic[0]->blockSwitchingControl.lastWindowSequence != SHORT_WINDOW) |
| { |
| /* PNS preprocessing depending on ms processing: PNS not in Short Window! */ |
| FDKaacEnc_PreProcessPnsChannelPair( |
| psyData[0]->sfbActive, |
| (&psyData[0]->sfbEnergy)->Long, |
| (&psyData[1]->sfbEnergy)->Long, |
| psyOutChannel[0]->sfbEnergyLdData, |
| psyOutChannel[1]->sfbEnergyLdData, |
| psyData[0]->sfbEnergyMS.Long, |
| &(psyConf[0].pnsConf), |
| pnsData[0], |
| pnsData[1]); |
| |
| FDKaacEnc_IntensityStereoProcessing( |
| psyData[0]->sfbEnergy.Long, |
| psyData[1]->sfbEnergy.Long, |
| psyData[0]->mdctSpectrum, |
| psyData[1]->mdctSpectrum, |
| psyData[0]->sfbThreshold.Long, |
| psyData[1]->sfbThreshold.Long, |
| psyOutChannel[1]->sfbThresholdLdData, |
| psyData[0]->sfbSpreadEnergy.Long, |
| psyData[1]->sfbSpreadEnergy.Long, |
| psyOutChannel[0]->sfbEnergyLdData, |
| psyOutChannel[1]->sfbEnergyLdData, |
| &psyOutElement->toolsInfo.msDigest, |
| psyOutElement->toolsInfo.msMask, |
| psyConf[0].sfbCnt, |
| psyConf[0].sfbCnt, |
| maxSfbPerGroup[0], |
| psyConf[0].sfbOffset, |
| psyConf[0].allowIS && commonWindow, |
| psyOutChannel[1]->isBook, |
| psyOutChannel[1]->isScale, |
| pnsData); |
| |
| FDKaacEnc_MsStereoProcessing( |
| psyData, |
| psyOutChannel, |
| psyOutChannel[1]->isBook, |
| &psyOutElement->toolsInfo.msDigest, |
| psyOutElement->toolsInfo.msMask, |
| psyData[0]->sfbActive, |
| psyData[0]->sfbActive, |
| maxSfbPerGroup[0], |
| psyOutChannel[0]->sfbOffsets); |
| |
| /* PNS postprocessing */ |
| FDKaacEnc_PostProcessPnsChannelPair(psyData[0]->sfbActive, |
| &(psyConf[0].pnsConf), |
| pnsData[0], |
| pnsData[1], |
| psyOutElement->toolsInfo.msMask, |
| &psyOutElement->toolsInfo.msDigest); |
| |
| } else { |
| FDKaacEnc_IntensityStereoProcessing( |
| psyData[0]->sfbEnergy.Long, |
| psyData[1]->sfbEnergy.Long, |
| psyData[0]->mdctSpectrum, |
| psyData[1]->mdctSpectrum, |
| psyData[0]->sfbThreshold.Long, |
| psyData[1]->sfbThreshold.Long, |
| psyOutChannel[1]->sfbThresholdLdData, |
| psyData[0]->sfbSpreadEnergy.Long, |
| psyData[1]->sfbSpreadEnergy.Long, |
| psyOutChannel[0]->sfbEnergyLdData, |
| psyOutChannel[1]->sfbEnergyLdData, |
| &psyOutElement->toolsInfo.msDigest, |
| psyOutElement->toolsInfo.msMask, |
| psyStatic[0]->blockSwitchingControl.noOfGroups*hPsyConfShort->sfbCnt, |
| psyConf[1].sfbCnt, |
| maxSfbPerGroup[0], |
| psyData[0]->groupedSfbOffset, |
| psyConf[0].allowIS && commonWindow, |
| psyOutChannel[1]->isBook, |
| psyOutChannel[1]->isScale, |
| pnsData); |
| |
| /* it's OK to pass the ".Long" arrays here. They contain grouped short data since FDKaacEnc_groupShortData() */ |
| FDKaacEnc_MsStereoProcessing( psyData, |
| psyOutChannel, |
| psyOutChannel[1]->isBook, |
| &psyOutElement->toolsInfo.msDigest, |
| psyOutElement->toolsInfo.msMask, |
| psyStatic[0]->blockSwitchingControl.noOfGroups*hPsyConfShort->sfbCnt, |
| hPsyConfShort->sfbCnt, |
| maxSfbPerGroup[0], |
| psyOutChannel[0]->sfbOffsets); |
| } |
| } |
| |
| /* |
| PNS Coding |
| */ |
| for(ch=0;ch<channels;ch++) { |
| if (psyStatic[ch]->isLFE) { |
| /* no PNS coding */ |
| for(sfb = 0; sfb < psyData[ch]->sfbActive; sfb++) { |
| psyOutChannel[ch]->noiseNrg[sfb] = NO_NOISE_PNS; |
| } |
| } else |
| { |
| FDKaacEnc_CodePnsChannel(psyData[ch]->sfbActive, |
| &(psyConf[ch].pnsConf), |
| pnsData[ch]->pnsFlag, |
| psyData[ch]->sfbEnergyLdData.Long, |
| psyOutChannel[ch]->noiseNrg, /* this is the energy that will be written to the bitstream */ |
| psyOutChannel[ch]->sfbThresholdLdData); |
| } |
| } |
| |
| /* |
| build output |
| */ |
| for(ch=0;ch<channels;ch++) |
| { |
| INT j, grp, mask; |
| |
| psyOutChannel[ch]->maxSfbPerGroup = maxSfbPerGroup[ch]; |
| psyOutChannel[ch]->mdctScale = psyData[ch]->mdctScale; |
| |
| if(isShortWindow[ch]==0) { |
| |
| psyOutChannel[ch]->sfbCnt = hPsyConfLong->sfbActive; |
| psyOutChannel[ch]->sfbPerGroup = hPsyConfLong->sfbActive; |
| psyOutChannel[ch]->lastWindowSequence = psyStatic[ch]->blockSwitchingControl.lastWindowSequence; |
| psyOutChannel[ch]->windowShape = psyStatic[ch]->blockSwitchingControl.windowShape; |
| } |
| else { |
| INT sfbCnt = psyStatic[ch]->blockSwitchingControl.noOfGroups*hPsyConfShort->sfbCnt; |
| |
| psyOutChannel[ch]->sfbCnt = sfbCnt; |
| psyOutChannel[ch]->sfbPerGroup = hPsyConfShort->sfbCnt; |
| psyOutChannel[ch]->lastWindowSequence = SHORT_WINDOW; |
| psyOutChannel[ch]->windowShape = SINE_WINDOW; |
| } |
| |
| /* generate grouping mask */ |
| mask = 0; |
| for (grp = 0; grp < psyStatic[ch]->blockSwitchingControl.noOfGroups; grp++) |
| { |
| mask <<= 1; |
| for (j=1; j<psyStatic[ch]->blockSwitchingControl.groupLen[grp]; j++) { |
| mask = (mask<<1) | 1 ; |
| } |
| } |
| psyOutChannel[ch]->groupingMask = mask; |
| |
| /* build interface */ |
| FDKmemcpy(psyOutChannel[ch]->groupLen,psyStatic[ch]->blockSwitchingControl.groupLen,MAX_NO_OF_GROUPS*sizeof(INT)); |
| FDKmemcpy(psyOutChannel[ch]->sfbEnergy,(&psyData[ch]->sfbEnergy)->Long, MAX_GROUPED_SFB*sizeof(FIXP_DBL)); |
| FDKmemcpy(psyOutChannel[ch]->sfbSpreadEnergy,(&psyData[ch]->sfbSpreadEnergy)->Long, MAX_GROUPED_SFB*sizeof(FIXP_DBL)); |
| // FDKmemcpy(psyOutChannel[ch]->mdctSpectrum, psyData[ch]->mdctSpectrum, (1024)*sizeof(FIXP_DBL)); |
| } |
| |
| return AAC_ENC_OK; |
| } |
| |
| |
| void FDKaacEnc_PsyClose(PSY_INTERNAL **phPsyInternal, |
| PSY_OUT **phPsyOut) |
| { |
| int n, i; |
| |
| |
| if(phPsyInternal!=NULL) { |
| PSY_INTERNAL *hPsyInternal = *phPsyInternal; |
| |
| if (hPsyInternal) |
| { |
| for (i=0; i<(6); i++) { |
| if (hPsyInternal->pStaticChannels[i]) { |
| if (hPsyInternal->pStaticChannels[i]->psyInputBuffer) |
| FreeRam_aacEnc_PsyInputBuffer(&hPsyInternal->pStaticChannels[i]->psyInputBuffer); /* AUDIO INPUT BUFFER */ |
| |
| FreeRam_aacEnc_PsyStatic(&hPsyInternal->pStaticChannels[i]); /* PSY_STATIC */ |
| } |
| } |
| |
| for (i=0; i<(6); i++) { |
| if (hPsyInternal->psyElement[i]) |
| FreeRam_aacEnc_PsyElement(&hPsyInternal->psyElement[i]); /* PSY_ELEMENT */ |
| } |
| |
| |
| FreeRam_aacEnc_PsyInternal(phPsyInternal); |
| } |
| } |
| |
| if (phPsyOut!=NULL) { |
| for (n=0; n<(1); n++) { |
| if (phPsyOut[n]) |
| { |
| for (i=0; i<(6); i++) { |
| if (phPsyOut[n]->pPsyOutChannels[i]) |
| FreeRam_aacEnc_PsyOutChannel(&phPsyOut[n]->pPsyOutChannels[i]); /* PSY_OUT_CHANNEL */ |
| } |
| |
| for (i=0; i<(6); i++) { |
| if (phPsyOut[n]->psyOutElement[i]) |
| FreeRam_aacEnc_PsyOutElements(&phPsyOut[n]->psyOutElement[i]); /* PSY_OUT_ELEMENTS */ |
| } |
| |
| FreeRam_aacEnc_PsyOut(&phPsyOut[n]); |
| } |
| } |
| } |
| } |