/*
 ** Copyright 2003-2010, VisualOn, Inc.
 **
 ** Licensed under the Apache License, Version 2.0 (the "License");
 ** you may not use this file except in compliance with the License.
 ** You may obtain a copy of the License at
 **
 **     http://www.apache.org/licenses/LICENSE-2.0
 **
 ** Unless required by applicable law or agreed to in writing, software
 ** distributed under the License is distributed on an "AS IS" BASIS,
 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 ** See the License for the specific language governing permissions and
 ** limitations under the License.
 */
/*******************************************************************************
	File:		psy_main.c

	Content:	Psychoacoustic major functions

*******************************************************************************/

#include "typedef.h"
#include "basic_op.h"
#include "oper_32b.h"
#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 "memalign.h"

/*                                    long       start       short       stop */
static Word16 blockType2windowShape[] = {KBD_WINDOW,SINE_WINDOW,SINE_WINDOW,KBD_WINDOW};

/*
  forward definitions
*/
static Word16 advancePsychLong(PSY_DATA* psyData,
                               TNS_DATA* tnsData,
                               PSY_CONFIGURATION_LONG *hPsyConfLong,
                               PSY_OUT_CHANNEL* psyOutChannel,
                               Word32 *pScratchTns,
                               const TNS_DATA *tnsData2,
                               const Word16 ch);

static Word16 advancePsychLongMS (PSY_DATA  psyData[MAX_CHANNELS],
                                  const PSY_CONFIGURATION_LONG *hPsyConfLong);

static Word16 advancePsychShort(PSY_DATA* psyData,
                                TNS_DATA* tnsData,
                                const PSY_CONFIGURATION_SHORT *hPsyConfShort,
                                PSY_OUT_CHANNEL* psyOutChannel,
                                Word32 *pScratchTns,
                                const TNS_DATA *tnsData2,
                                const Word16 ch);

static Word16 advancePsychShortMS (PSY_DATA  psyData[MAX_CHANNELS],
                                   const PSY_CONFIGURATION_SHORT *hPsyConfShort);


/*****************************************************************************
*
* function name: PsyNew
* description:  allocates memory for psychoacoustic
* returns:      an error code
* input:        pointer to a psych handle
*
*****************************************************************************/
Word16 PsyNew(PSY_KERNEL *hPsy, Word32 nChan, VO_MEM_OPERATOR *pMemOP)
{
  Word16 i;
  Word32 *mdctSpectrum;
  Word32 *scratchTNS;
  Word16 *mdctDelayBuffer;
  
  mdctSpectrum = (Word32 *)mem_malloc(pMemOP, nChan * FRAME_LEN_LONG * sizeof(Word32), 32, VO_INDEX_ENC_AAC);
  if(NULL == mdctSpectrum)
	  return 1;

  scratchTNS = (Word32 *)mem_malloc(pMemOP, nChan * FRAME_LEN_LONG * sizeof(Word32), 32, VO_INDEX_ENC_AAC);
  if(NULL == scratchTNS)
  {
	  return 1;
  }

  mdctDelayBuffer = (Word16 *)mem_malloc(pMemOP, nChan * BLOCK_SWITCHING_OFFSET * sizeof(Word16), 32, VO_INDEX_ENC_AAC);
  if(NULL == mdctDelayBuffer)
  {
	  return 1;
  }

  for (i=0; i<nChan; i++){
    hPsy->psyData[i].mdctDelayBuffer = mdctDelayBuffer + i*BLOCK_SWITCHING_OFFSET;      
    hPsy->psyData[i].mdctSpectrum = mdctSpectrum + i*FRAME_LEN_LONG;
  }

  hPsy->pScratchTns = scratchTNS;

  return 0;
}


/*****************************************************************************
*
* function name: PsyDelete
* description:  allocates memory for psychoacoustic
* returns:      an error code
*
*****************************************************************************/
Word16 PsyDelete(PSY_KERNEL  *hPsy, VO_MEM_OPERATOR *pMemOP)
{
  Word32 nch;

  if(hPsy)
  {
	if(hPsy->psyData[0].mdctDelayBuffer)
		mem_free(pMemOP, hPsy->psyData[0].mdctDelayBuffer, VO_INDEX_ENC_AAC);
      
    if(hPsy->psyData[0].mdctSpectrum)
		mem_free(pMemOP, hPsy->psyData[0].mdctSpectrum, VO_INDEX_ENC_AAC);

    for (nch=0; nch<MAX_CHANNELS; nch++){
	  hPsy->psyData[nch].mdctDelayBuffer = NULL;      
	  hPsy->psyData[nch].mdctSpectrum = NULL;
	}

	if(hPsy->pScratchTns)
	{
		mem_free(pMemOP, hPsy->pScratchTns, VO_INDEX_ENC_AAC);
		hPsy->pScratchTns = NULL;
	}
  }

  return 0;
}


/*****************************************************************************
*
* function name: PsyOutNew
* description:  allocates memory for psyOut struc
* returns:      an error code
* input:        pointer to a psych handle
*
*****************************************************************************/
Word16 PsyOutNew(PSY_OUT *hPsyOut, VO_MEM_OPERATOR *pMemOP)
{
  pMemOP->Set(VO_INDEX_ENC_AAC, hPsyOut, 0, sizeof(PSY_OUT));
  /*
    alloc some more stuff, tbd
  */
  return 0;
}

/*****************************************************************************
*
* function name: PsyOutDelete
* description:  allocates memory for psychoacoustic
* returns:      an error code
*
*****************************************************************************/
Word16 PsyOutDelete(PSY_OUT *hPsyOut, VO_MEM_OPERATOR *pMemOP)
{
  hPsyOut=NULL;
  return 0;
}


/*****************************************************************************
*
* function name: psyMainInit
* description:  initializes psychoacoustic
* returns:      an error code
*
*****************************************************************************/

Word16 psyMainInit(PSY_KERNEL *hPsy,
                   Word32 sampleRate,
                   Word32 bitRate,
                   Word16 channels,
                   Word16 tnsMask,
                   Word16 bandwidth)
{
  Word16 ch, err;
  Word32 channelBitRate = bitRate/channels;

  err = InitPsyConfigurationLong(channelBitRate,
                                 sampleRate,
                                 bandwidth,
                                 &(hPsy->psyConfLong));

  if (!err) {
      hPsy->sampleRateIdx = hPsy->psyConfLong.sampRateIdx;
	  err = InitTnsConfigurationLong(bitRate, sampleRate, channels,
                                   &hPsy->psyConfLong.tnsConf, &hPsy->psyConfLong, tnsMask&2);
  }

  if (!err)
    err = InitPsyConfigurationShort(channelBitRate,
                                    sampleRate,
                                    bandwidth,
                                    &hPsy->psyConfShort);
  if (!err) {
    err = InitTnsConfigurationShort(bitRate, sampleRate, channels,
                                    &hPsy->psyConfShort.tnsConf, &hPsy->psyConfShort, tnsMask&1);
  }

  if (!err)
    for(ch=0;ch < channels;ch++){
  
      InitBlockSwitching(&hPsy->psyData[ch].blockSwitchingControl,
                         bitRate, channels);

      InitPreEchoControl(hPsy->psyData[ch].sfbThresholdnm1,
                         hPsy->psyConfLong.sfbCnt,
                         hPsy->psyConfLong.sfbThresholdQuiet);
      hPsy->psyData[ch].mdctScalenm1 = 0;                                
    }

	return(err);
}

/*****************************************************************************
*
* function name: psyMain
* description:  psychoacoustic main function
* returns:      an error code
*
*    This function assumes that enough input data is in the modulo buffer.
*
*****************************************************************************/

Word16 psyMain(Word16                   nChannels,
               ELEMENT_INFO            *elemInfo,
               Word16                  *timeSignal, 
               PSY_DATA                 psyData[MAX_CHANNELS],
               TNS_DATA                 tnsData[MAX_CHANNELS],
               PSY_CONFIGURATION_LONG  *hPsyConfLong,
               PSY_CONFIGURATION_SHORT *hPsyConfShort,
               PSY_OUT_CHANNEL          psyOutChannel[MAX_CHANNELS],
               PSY_OUT_ELEMENT         *psyOutElement,
               Word32                  *pScratchTns,
			   Word32				   sampleRate)
{
  Word16 maxSfbPerGroup[MAX_CHANNELS];
  Word16 mdctScalingArray[MAX_CHANNELS];

  Word16 ch;   /* counts through channels          */
  Word16 sfb;  /* counts through scalefactor bands */
  Word16 line; /* counts through lines             */
  Word16 channels;
  Word16 maxScale;

  channels = elemInfo->nChannelsInEl;                            
  maxScale = 0;                                                  

  /* block switching */
  for(ch = 0; ch < channels; ch++) {
    BlockSwitching(&psyData[ch].blockSwitchingControl,
                   timeSignal+elemInfo->ChannelIndex[ch],
				   sampleRate,
                   nChannels);
  }

  /* synch left and right block type */
  SyncBlockSwitching(&psyData[0].blockSwitchingControl,
                     &psyData[1].blockSwitchingControl,
                     channels);

  /* transform
     and get maxScale (max mdctScaling) for all channels */
  for(ch=0; ch<channels; ch++) {
    Transform_Real(psyData[ch].mdctDelayBuffer,
                   timeSignal+elemInfo->ChannelIndex[ch],
                   nChannels,
                   psyData[ch].mdctSpectrum,
                   &(mdctScalingArray[ch]),
                   psyData[ch].blockSwitchingControl.windowSequence);
    maxScale = max(maxScale, mdctScalingArray[ch]);
  }

  /* common scaling for all channels */
  for (ch=0; ch<channels; ch++) {
    Word16 scaleDiff = maxScale - mdctScalingArray[ch];
     
    if (scaleDiff > 0) {
      Word32 *Spectrum = psyData[ch].mdctSpectrum;
	  for(line=0; line<FRAME_LEN_LONG; line++) {
        *Spectrum = (*Spectrum) >> scaleDiff;
		Spectrum++;
      }
    }
    psyData[ch].mdctScale = maxScale;                                    
  }

  for (ch=0; ch<channels; ch++) {
     
    if(psyData[ch].blockSwitchingControl.windowSequence != SHORT_WINDOW) {
      /* update long block parameter */
	  advancePsychLong(&psyData[ch],
                       &tnsData[ch],
                       hPsyConfLong,
                       &psyOutChannel[ch],
                       pScratchTns,
                       &tnsData[1 - ch],
                       ch);

      /* determine maxSfb */
      for (sfb=hPsyConfLong->sfbCnt-1; sfb>=0; sfb--) {
        for (line=hPsyConfLong->sfbOffset[sfb+1] - 1; line>=hPsyConfLong->sfbOffset[sfb]; line--) {
           
          if (psyData[ch].mdctSpectrum[line] != 0) break;
        }
        if (line >= hPsyConfLong->sfbOffset[sfb]) break;
      }
      maxSfbPerGroup[ch] = sfb + 1;

      /* Calc bandwise energies for mid and side channel
         Do it only if 2 channels exist */
       
      if (ch == 1)
        advancePsychLongMS(psyData, hPsyConfLong);
    }
    else {
      advancePsychShort(&psyData[ch],
                        &tnsData[ch],
                        hPsyConfShort,
                        &psyOutChannel[ch],
                        pScratchTns,
                        &tnsData[1 - ch],
                        ch);

      /* Calc bandwise energies for mid and side channel
         Do it only if 2 channels exist */
       
      if (ch == 1)
        advancePsychShortMS (psyData, hPsyConfShort);
    }
  }

  /* group short data */
  for(ch=0; ch<channels; ch++) {
     
    if (psyData[ch].blockSwitchingControl.windowSequence == SHORT_WINDOW) {
      groupShortData(psyData[ch].mdctSpectrum,
                     pScratchTns,
                     &psyData[ch].sfbThreshold,
                     &psyData[ch].sfbEnergy,
                     &psyData[ch].sfbEnergyMS,
                     &psyData[ch].sfbSpreadedEnergy,
                     hPsyConfShort->sfbCnt,
                     hPsyConfShort->sfbOffset,
                     hPsyConfShort->sfbMinSnr,
                     psyOutElement->groupedSfbOffset[ch],
                     &maxSfbPerGroup[ch],
                     psyOutElement->groupedSfbMinSnr[ch],
                     psyData[ch].blockSwitchingControl.noOfGroups,
                     psyData[ch].blockSwitchingControl.groupLen);
    }
  }


#if (MAX_CHANNELS>1)
  /*
    stereo Processing
  */
  if (channels == 2) {
    psyOutElement->toolsInfo.msDigest = MS_NONE;                 
    maxSfbPerGroup[0] = maxSfbPerGroup[1] = max(maxSfbPerGroup[0], maxSfbPerGroup[1]);

     
    if (psyData[0].blockSwitchingControl.windowSequence != SHORT_WINDOW)
      MsStereoProcessing(psyData[0].sfbEnergy.sfbLong,
                         psyData[1].sfbEnergy.sfbLong,
                         psyData[0].sfbEnergyMS.sfbLong,
                         psyData[1].sfbEnergyMS.sfbLong,
                         psyData[0].mdctSpectrum,
                         psyData[1].mdctSpectrum,
                         psyData[0].sfbThreshold.sfbLong,
                         psyData[1].sfbThreshold.sfbLong,
                         psyData[0].sfbSpreadedEnergy.sfbLong,
                         psyData[1].sfbSpreadedEnergy.sfbLong,
                         (Word16*)&psyOutElement->toolsInfo.msDigest,
                         (Word16*)psyOutElement->toolsInfo.msMask,
                         hPsyConfLong->sfbCnt,
                         hPsyConfLong->sfbCnt,
                         maxSfbPerGroup[0],
                         (const Word16*)hPsyConfLong->sfbOffset);
      else
        MsStereoProcessing(psyData[0].sfbEnergy.sfbLong,
                           psyData[1].sfbEnergy.sfbLong,
                           psyData[0].sfbEnergyMS.sfbLong,
                           psyData[1].sfbEnergyMS.sfbLong,
                           psyData[0].mdctSpectrum,
                           psyData[1].mdctSpectrum,
                           psyData[0].sfbThreshold.sfbLong,
                           psyData[1].sfbThreshold.sfbLong,
                           psyData[0].sfbSpreadedEnergy.sfbLong,
                           psyData[1].sfbSpreadedEnergy.sfbLong,
                           (Word16*)&psyOutElement->toolsInfo.msDigest,
                           (Word16*)psyOutElement->toolsInfo.msMask,
                           psyData[0].blockSwitchingControl.noOfGroups*hPsyConfShort->sfbCnt,
                           hPsyConfShort->sfbCnt,
                           maxSfbPerGroup[0],
                           (const Word16*)psyOutElement->groupedSfbOffset[0]);
  }

#endif /* (MAX_CHANNELS>1) */

  /*
    build output
  */
  for(ch=0;ch<channels;ch++) {
     
    if (psyData[ch].blockSwitchingControl.windowSequence != SHORT_WINDOW)
      BuildInterface(psyData[ch].mdctSpectrum,
                     psyData[ch].mdctScale,
                     &psyData[ch].sfbThreshold,
                     &psyData[ch].sfbEnergy,
                     &psyData[ch].sfbSpreadedEnergy,
                     psyData[ch].sfbEnergySum,
                     psyData[ch].sfbEnergySumMS,
                     psyData[ch].blockSwitchingControl.windowSequence,
                     blockType2windowShape[psyData[ch].blockSwitchingControl.windowSequence],
                     hPsyConfLong->sfbCnt,
                     hPsyConfLong->sfbOffset,
                     maxSfbPerGroup[ch],
                     hPsyConfLong->sfbMinSnr,
                     psyData[ch].blockSwitchingControl.noOfGroups,
                     psyData[ch].blockSwitchingControl.groupLen,
                     &psyOutChannel[ch]);
    else
      BuildInterface(psyData[ch].mdctSpectrum,
                     psyData[ch].mdctScale,
                     &psyData[ch].sfbThreshold,
                     &psyData[ch].sfbEnergy,
                     &psyData[ch].sfbSpreadedEnergy,
                     psyData[ch].sfbEnergySum,
                     psyData[ch].sfbEnergySumMS,
                     SHORT_WINDOW,
                     SINE_WINDOW,
                     psyData[0].blockSwitchingControl.noOfGroups*hPsyConfShort->sfbCnt,
                     psyOutElement->groupedSfbOffset[ch],
                     maxSfbPerGroup[ch],
                     psyOutElement->groupedSfbMinSnr[ch],
                     psyData[ch].blockSwitchingControl.noOfGroups,
                     psyData[ch].blockSwitchingControl.groupLen,
                     &psyOutChannel[ch]);
  }

  return(0); /* no error */
}

/*****************************************************************************
*
* function name: advancePsychLong
* description:  psychoacoustic for long blocks
*
*****************************************************************************/

static Word16 advancePsychLong(PSY_DATA* psyData,
                               TNS_DATA* tnsData,
                               PSY_CONFIGURATION_LONG *hPsyConfLong,
                               PSY_OUT_CHANNEL* psyOutChannel,
                               Word32 *pScratchTns,
                               const TNS_DATA* tnsData2,
                               const Word16 ch)
{
  Word32 i;
  Word32 normEnergyShift = (psyData->mdctScale + 1) << 1; /* in reference code, mdct spectrum must be multipied with 2, so +1 */
  Word32 clipEnergy = hPsyConfLong->clipEnergy >> normEnergyShift;
  Word32 *data0, *data1, tdata;

  /* low pass */
  data0 = psyData->mdctSpectrum + hPsyConfLong->lowpassLine;
  for(i=hPsyConfLong->lowpassLine; i<FRAME_LEN_LONG; i++) {
    *data0++ = 0;                                
  }

  /* Calc sfb-bandwise mdct-energies for left and right channel */
  CalcBandEnergy( psyData->mdctSpectrum,
                  hPsyConfLong->sfbOffset,
                  hPsyConfLong->sfbActive,
                  psyData->sfbEnergy.sfbLong,
                  &psyData->sfbEnergySum.sfbLong);

  /*
    TNS detect
  */
  TnsDetect(tnsData,
            hPsyConfLong->tnsConf,
            pScratchTns,
            (const Word16*)hPsyConfLong->sfbOffset,
            psyData->mdctSpectrum,
            0,
            psyData->blockSwitchingControl.windowSequence,
            psyData->sfbEnergy.sfbLong);

  /*  TnsSync */   
  if (ch == 1) {
    TnsSync(tnsData,
            tnsData2,
            hPsyConfLong->tnsConf,
            0,
            psyData->blockSwitchingControl.windowSequence);
  }

  /*  Tns Encoder */ 
  TnsEncode(&psyOutChannel->tnsInfo,
            tnsData,
            hPsyConfLong->sfbCnt,
            hPsyConfLong->tnsConf,
            hPsyConfLong->lowpassLine,
            psyData->mdctSpectrum,
            0,
            psyData->blockSwitchingControl.windowSequence);

  /* first part of threshold calculation */
  data0 = psyData->sfbEnergy.sfbLong;
  data1 = psyData->sfbThreshold.sfbLong;
  for (i=hPsyConfLong->sfbCnt; i; i--) {
    tdata = L_mpy_ls(*data0++, hPsyConfLong->ratio);
    *data1++ = min(tdata, clipEnergy);
  }

  /* Calc sfb-bandwise mdct-energies for left and right channel again */   
  if (tnsData->dataRaw.tnsLong.subBlockInfo.tnsActive!=0) {
    Word16 tnsStartBand = hPsyConfLong->tnsConf.tnsStartBand;                            
    CalcBandEnergy( psyData->mdctSpectrum,
                    hPsyConfLong->sfbOffset+tnsStartBand,
                    hPsyConfLong->sfbActive - tnsStartBand,
                    psyData->sfbEnergy.sfbLong+tnsStartBand,
                    &psyData->sfbEnergySum.sfbLong);
    
	data0 = psyData->sfbEnergy.sfbLong;
	tdata = psyData->sfbEnergySum.sfbLong;
	for (i=0; i<tnsStartBand; i++)
      tdata += *data0++;

	psyData->sfbEnergySum.sfbLong = tdata;
  }


  /* spreading energy */
  SpreadingMax(hPsyConfLong->sfbCnt,
               hPsyConfLong->sfbMaskLowFactor,
               hPsyConfLong->sfbMaskHighFactor,
               psyData->sfbThreshold.sfbLong);

  /* threshold in quiet */
  data0 = psyData->sfbThreshold.sfbLong;
  data1 = hPsyConfLong->sfbThresholdQuiet;
  for (i=hPsyConfLong->sfbCnt; i; i--)
  {
	  *data0 = max(*data0, (*data1 >> normEnergyShift));
	  data0++; data1++;
  }

  /* preecho control */   
  if (psyData->blockSwitchingControl.windowSequence == STOP_WINDOW) {
    data0 = psyData->sfbThresholdnm1;
	for (i=hPsyConfLong->sfbCnt; i; i--) {
      *data0++ = MAX_32;                              
    }
    psyData->mdctScalenm1 = 0;                                           
  }

  PreEchoControl( psyData->sfbThresholdnm1,
                  hPsyConfLong->sfbCnt,
                  hPsyConfLong->maxAllowedIncreaseFactor,
                  hPsyConfLong->minRemainingThresholdFactor,
                  psyData->sfbThreshold.sfbLong,
                  psyData->mdctScale,
                  psyData->mdctScalenm1);
  psyData->mdctScalenm1 = psyData->mdctScale;                            

   
  if (psyData->blockSwitchingControl.windowSequence== START_WINDOW) {
    data0 = psyData->sfbThresholdnm1;
	for (i=hPsyConfLong->sfbCnt; i; i--) {
      *data0++ = MAX_32;                              
    }
    psyData->mdctScalenm1 = 0;                                           
  }

  /* apply tns mult table on cb thresholds */
  ApplyTnsMultTableToRatios(hPsyConfLong->tnsConf.tnsRatioPatchLowestCb,
                            hPsyConfLong->tnsConf.tnsStartBand,
                            tnsData->dataRaw.tnsLong.subBlockInfo,
                            psyData->sfbThreshold.sfbLong);


  /* spreaded energy */
  data0 = psyData->sfbSpreadedEnergy.sfbLong;
  data1 = psyData->sfbEnergy.sfbLong;
  for (i=hPsyConfLong->sfbCnt; i; i--) {
    //psyData->sfbSpreadedEnergy.sfbLong[i] = psyData->sfbEnergy.sfbLong[i];       
	  *data0++ = *data1++;
  }

  /* spreading energy */
  SpreadingMax(hPsyConfLong->sfbCnt,
               hPsyConfLong->sfbMaskLowFactorSprEn, 
               hPsyConfLong->sfbMaskHighFactorSprEn,
               psyData->sfbSpreadedEnergy.sfbLong);

  return 0;
}

/*****************************************************************************
*
* function name: advancePsychLongMS
* description:   update mdct-energies for left add or minus right channel 
*				for long block
*
*****************************************************************************/
static Word16 advancePsychLongMS (PSY_DATA psyData[MAX_CHANNELS],
                                  const PSY_CONFIGURATION_LONG *hPsyConfLong)
{
  CalcBandEnergyMS(psyData[0].mdctSpectrum,
                   psyData[1].mdctSpectrum,
                   hPsyConfLong->sfbOffset,
                   hPsyConfLong->sfbActive,
                   psyData[0].sfbEnergyMS.sfbLong,
                   &psyData[0].sfbEnergySumMS.sfbLong,
                   psyData[1].sfbEnergyMS.sfbLong,
                   &psyData[1].sfbEnergySumMS.sfbLong);

  return 0;
}


/*****************************************************************************
*
* function name: advancePsychShort
* description:  psychoacoustic for short blocks
*
*****************************************************************************/

static Word16 advancePsychShort(PSY_DATA* psyData,
                                TNS_DATA* tnsData,
                                const PSY_CONFIGURATION_SHORT *hPsyConfShort,
                                PSY_OUT_CHANNEL* psyOutChannel,
                                Word32 *pScratchTns,
                                const TNS_DATA *tnsData2,
                                const Word16 ch)
{
  Word32 w;
  Word32 normEnergyShift = (psyData->mdctScale + 1) << 1; /* in reference code, mdct spectrum must be multipied with 2, so +1 */
  Word32 clipEnergy = hPsyConfShort->clipEnergy >> normEnergyShift;
  Word32 wOffset = 0;     
  Word32 *data0, *data1;

  for(w = 0; w < TRANS_FAC; w++) {
    Word32 i, tdata;

    /* low pass */
    data0 = psyData->mdctSpectrum + wOffset + hPsyConfShort->lowpassLine;
	for(i=hPsyConfShort->lowpassLine; i<FRAME_LEN_SHORT; i++){
      *data0++ = 0;                                      
    }

    /* Calc sfb-bandwise mdct-energies for left and right channel */
    CalcBandEnergy( psyData->mdctSpectrum+wOffset,
                    hPsyConfShort->sfbOffset,
                    hPsyConfShort->sfbActive,
                    psyData->sfbEnergy.sfbShort[w],
                    &psyData->sfbEnergySum.sfbShort[w]);
    /*
       TNS
    */
    TnsDetect(tnsData,
              hPsyConfShort->tnsConf,
              pScratchTns,
              (const Word16*)hPsyConfShort->sfbOffset,
              psyData->mdctSpectrum+wOffset,
              w,
              psyData->blockSwitchingControl.windowSequence,
              psyData->sfbEnergy.sfbShort[w]);

    /*  TnsSync */
    if (ch == 1) {
      TnsSync(tnsData,
              tnsData2,
              hPsyConfShort->tnsConf,
              w,
              psyData->blockSwitchingControl.windowSequence);
    }

    TnsEncode(&psyOutChannel->tnsInfo,
              tnsData,
              hPsyConfShort->sfbCnt,
              hPsyConfShort->tnsConf,
              hPsyConfShort->lowpassLine,
              psyData->mdctSpectrum+wOffset,
              w,
              psyData->blockSwitchingControl.windowSequence);

    /* first part of threshold calculation */
    data0 = psyData->sfbThreshold.sfbShort[w];
	data1 = psyData->sfbEnergy.sfbShort[w];
	for (i=hPsyConfShort->sfbCnt; i; i--) {
      tdata = L_mpy_ls(*data1++, hPsyConfShort->ratio);
      *data0++ = min(tdata, clipEnergy);
    }

    /* Calc sfb-bandwise mdct-energies for left and right channel again */     
    if (tnsData->dataRaw.tnsShort.subBlockInfo[w].tnsActive != 0) {
      Word16 tnsStartBand = hPsyConfShort->tnsConf.tnsStartBand;                            
      CalcBandEnergy( psyData->mdctSpectrum+wOffset,
                      hPsyConfShort->sfbOffset+tnsStartBand,
                      (hPsyConfShort->sfbActive - tnsStartBand),
                      psyData->sfbEnergy.sfbShort[w]+tnsStartBand,
                      &psyData->sfbEnergySum.sfbShort[w]);

      tdata = psyData->sfbEnergySum.sfbShort[w];
	  data0 = psyData->sfbEnergy.sfbShort[w];
	  for (i=tnsStartBand; i; i--)
        tdata += *data0++;

	  psyData->sfbEnergySum.sfbShort[w] = tdata;
    }

    /* spreading */
    SpreadingMax(hPsyConfShort->sfbCnt,
                 hPsyConfShort->sfbMaskLowFactor,
                 hPsyConfShort->sfbMaskHighFactor,
                 psyData->sfbThreshold.sfbShort[w]);


    /* threshold in quiet */
    data0 = psyData->sfbThreshold.sfbShort[w];
	data1 = hPsyConfShort->sfbThresholdQuiet;
	for (i=hPsyConfShort->sfbCnt; i; i--)
    {
		*data0 = max(*data0, (*data1 >> normEnergyShift));

		data0++; data1++;
	}


    /* preecho */     
    PreEchoControl( psyData->sfbThresholdnm1,
                    hPsyConfShort->sfbCnt,
                    hPsyConfShort->maxAllowedIncreaseFactor,
                    hPsyConfShort->minRemainingThresholdFactor,
                    psyData->sfbThreshold.sfbShort[w],
                    psyData->mdctScale,
                    w==0 ? psyData->mdctScalenm1 : psyData->mdctScale);

    /* apply tns mult table on cb thresholds */
    ApplyTnsMultTableToRatios( hPsyConfShort->tnsConf.tnsRatioPatchLowestCb,
                               hPsyConfShort->tnsConf.tnsStartBand,
                               tnsData->dataRaw.tnsShort.subBlockInfo[w],
                               psyData->sfbThreshold.sfbShort[w]);

    /* spreaded energy */
    data0 = psyData->sfbSpreadedEnergy.sfbShort[w];
	data1 = psyData->sfbEnergy.sfbShort[w];
	for (i=hPsyConfShort->sfbCnt; i; i--) {
	  *data0++ = *data1++;
    }
    SpreadingMax(hPsyConfShort->sfbCnt,
                 hPsyConfShort->sfbMaskLowFactorSprEn, 
                 hPsyConfShort->sfbMaskHighFactorSprEn,
                 psyData->sfbSpreadedEnergy.sfbShort[w]);

    wOffset += FRAME_LEN_SHORT;
  } /* for TRANS_FAC */

  psyData->mdctScalenm1 = psyData->mdctScale;              

  return 0;
}

/*****************************************************************************
*
* function name: advancePsychShortMS
* description:   update mdct-energies for left add or minus right channel 
*				for short block
*
*****************************************************************************/
static Word16 advancePsychShortMS (PSY_DATA psyData[MAX_CHANNELS],
                                   const PSY_CONFIGURATION_SHORT *hPsyConfShort)
{
  Word32 w, wOffset;
  wOffset = 0;                                   
  for(w=0; w<TRANS_FAC; w++) {
    CalcBandEnergyMS(psyData[0].mdctSpectrum+wOffset,
                     psyData[1].mdctSpectrum+wOffset,
                     hPsyConfShort->sfbOffset,
                     hPsyConfShort->sfbActive,
                     psyData[0].sfbEnergyMS.sfbShort[w],
                     &psyData[0].sfbEnergySumMS.sfbShort[w],
                     psyData[1].sfbEnergyMS.sfbShort[w],
                     &psyData[1].sfbEnergySumMS.sfbShort[w]);
    wOffset += FRAME_LEN_SHORT;
  }

  return 0;
}
