diff options
Diffstat (limited to 'libAACenc/src/psy_main.cpp')
-rw-r--r-- | libAACenc/src/psy_main.cpp | 1338 |
1 files changed, 1338 insertions, 0 deletions
diff --git a/libAACenc/src/psy_main.cpp b/libAACenc/src/psy_main.cpp new file mode 100644 index 0000000..bfc8b3b --- /dev/null +++ b/libAACenc/src/psy_main.cpp @@ -0,0 +1,1338 @@ +/******************************** MPEG Audio Encoder ************************** + + (C) Copyright Fraunhofer IIS (1999) + All Rights Reserved + + Please be advised that this software and/or program delivery is + Confidential Information of Fraunhofer and subject to and covered by the + + Fraunhofer IIS Software Evaluation Agreement + between Google Inc. and Fraunhofer + effective and in full force since March 1, 2012. + + You may use this software and/or program only under the terms and + conditions described in the above mentioned Fraunhofer IIS Software + Evaluation Agreement. Any other and/or further use requires a separate agreement. + + + This software and/or program is protected by copyright law and international + treaties. Any reproduction or distribution of this software and/or program, + or any portion of it, may result in severe civil and criminal penalties, and + will be prosecuted to the maximum extent possible under law. + + $Id$ + 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" + + +#ifdef PSY_MAIN_DEBUG_INFO +FDKFILE *fSpectrumOut; +#endif + +/* 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; + +#ifdef PSY_MAIN_DEBUG_INFO + fSpectrumOut = FDKfopen("psy_main_spectrum2.raw", "wb"); +#endif + + 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 */ + + + + + +#ifdef PSY_MAIN_DEBUG_INFO + for(ch = 0; ch < channels; ch++) { + FDKfwrite(psyData[ch]->mdctSpectrum, sizeof(FIXP_DBL), psyConf->granuleLength, fSpectrumOut); + } +#endif + + /* 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; + +#ifdef PSY_MAIN_DEBUG_INFO + if(fSpectrumOut!=NULL) + FDKfclose(fSpectrumOut); +#endif + + 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]); + } + } + } +} |