
/* -----------------------------------------------------------------------------------------------------------
Software License for The Fraunhofer FDK AAC Codec Library for Android

© Copyright  1995 - 2013 Fraunhofer-Gesellschaft zur Förderung der angewandten Forschung e.V.
  All rights reserved.

 1.    INTRODUCTION
The Fraunhofer FDK AAC Codec Library for Android ("FDK AAC Codec") is software that implements
the MPEG Advanced Audio Coding ("AAC") encoding and decoding scheme for digital audio.
This FDK AAC Codec software is intended to be used on a wide variety of Android devices.

AAC's HE-AAC and HE-AAC v2 versions are regarded as today's most efficient general perceptual
audio codecs. AAC-ELD is considered the best-performing full-bandwidth communications codec by
independent studies and is widely deployed. AAC has been standardized by ISO and IEC as part
of the MPEG specifications.

Patent licenses for necessary patent claims for the FDK AAC Codec (including those of Fraunhofer)
may be obtained through Via Licensing (www.vialicensing.com) or through the respective patent owners
individually for the purpose of encoding or decoding bit streams in products that are compliant with
the ISO/IEC MPEG audio standards. Please note that most manufacturers of Android devices already license
these patent claims through Via Licensing or directly from the patent owners, and therefore FDK AAC Codec
software may already be covered under those patent licenses when it is used for those licensed purposes only.

Commercially-licensed AAC software libraries, including floating-point versions with enhanced sound quality,
are also available from Fraunhofer. Users are encouraged to check the Fraunhofer website for additional
applications information and documentation.

2.    COPYRIGHT LICENSE

Redistribution and use in source and binary forms, with or without modification, are permitted without
payment of copyright license fees provided that you satisfy the following conditions:

You must retain the complete text of this software license in redistributions of the FDK AAC Codec or
your modifications thereto in source code form.

You must retain the complete text of this software license in the documentation and/or other materials
provided with redistributions of the FDK AAC Codec or your modifications thereto in binary form.
You must make available free of charge copies of the complete source code of the FDK AAC Codec and your
modifications thereto to recipients of copies in binary form.

The name of Fraunhofer may not be used to endorse or promote products derived from this library without
prior written permission.

You may not charge copyright license fees for anyone to use, copy or distribute the FDK AAC Codec
software or your modifications thereto.

Your modified versions of the FDK AAC Codec must carry prominent notices stating that you changed the software
and the date of any change. For modified versions of the FDK AAC Codec, the term
"Fraunhofer FDK AAC Codec Library for Android" must be replaced by the term
"Third-Party Modified Version of the Fraunhofer FDK AAC Codec Library for Android."

3.    NO PATENT LICENSE

NO EXPRESS OR IMPLIED LICENSES TO ANY PATENT CLAIMS, including without limitation the patents of Fraunhofer,
ARE GRANTED BY THIS SOFTWARE LICENSE. Fraunhofer provides no warranty of patent non-infringement with
respect to this software.

You may use this FDK AAC Codec software or modifications thereto only for purposes that are authorized
by appropriate patent licenses.

4.    DISCLAIMER

This FDK AAC Codec software is provided by Fraunhofer on behalf of the copyright holders and contributors
"AS IS" and WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, including but not limited to the implied warranties
of merchantability and fitness for a particular purpose. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
CONTRIBUTORS BE LIABLE for any direct, indirect, incidental, special, exemplary, or consequential damages,
including but not limited to procurement of substitute goods or services; loss of use, data, or profits,
or business interruption, however caused and on any theory of liability, whether in contract, strict
liability, or tort (including negligence), arising in any way out of the use of this software, even if
advised of the possibility of such damage.

5.    CONTACT INFORMATION

Fraunhofer Institute for Integrated Circuits IIS
Attention: Audio and Multimedia Departments - FDK AAC LL
Am Wolfsmantel 33
91058 Erlangen, Germany

www.iis.fraunhofer.de/amm
amm-info@iis.fraunhofer.de
----------------------------------------------------------------------------------------------------------- */

#include "env_est.h"
#include "tran_det.h"

#include "qmf.h"

#include "fram_gen.h"
#include "bit_sbr.h"
#include "cmondata.h"
#include "sbr_ram.h"


#include "genericStds.h"

#define QUANT_ERROR_THRES 200
#define Y_NRG_SCALE 5 /* noCols = 32 -> shift(5) */


static const UCHAR panTable[2][10] = { { 0, 2, 4, 6, 8,12,16,20,24},
                                       { 0, 2, 4, 8,12, 0, 0, 0, 0 } };
static const UCHAR maxIndex[2] = {9, 5};


/***************************************************************************/
/*!

  \brief      Calculates energy form real and imaginary part of
              the QMF subsamples

  \return     none

****************************************************************************/
LNK_SECTION_CODE_L1
static void
FDKsbrEnc_getEnergyFromCplxQmfData(FIXP_DBL **RESTRICT energyValues,/*!< the result of the operation */
                                   FIXP_DBL **RESTRICT realValues,  /*!< the real part of the QMF subsamples */
                                   FIXP_DBL **RESTRICT imagValues,  /*!< the imaginary part of the QMF subsamples */
                                   INT   numberBands,               /*!< number of QMF bands */
                                   INT   numberCols,                /*!< number of QMF subsamples */
                                   INT   *qmfScale,                 /*!< sclefactor of QMF subsamples */
                                   INT   *energyScale)              /*!< scalefactor of energies */
{
  int j, k;
  int scale;
  FIXP_DBL max_val = FL2FXCONST_DBL(0.0f);

  /* Get Scratch buffer */
  C_ALLOC_SCRATCH_START(tmpNrg, FIXP_DBL, QMF_CHANNELS*QMF_MAX_TIME_SLOTS/2);

  /* Get max possible scaling of QMF data */
  scale = DFRACT_BITS;
  for (k=0; k<numberCols; k++) {
    scale = fixMin(scale, fixMin(getScalefactor(realValues[k], numberBands), getScalefactor(imagValues[k], numberBands)));
  }

  /* Tweak scaling stability for zero signal to non-zero signal transitions */
  if (scale >= DFRACT_BITS-1) {
    scale = (FRACT_BITS-1-*qmfScale);
  }
  /* prevent scaling of QFM values to -1.f */
  scale = fixMax(0,scale-1);

  /* Update QMF scale */
  *qmfScale += scale;

  /*
     Calculate energy of each time slot pair, max energy
     and shift QMF values as far as possible to the left.
   */
  {
    FIXP_DBL *nrgValues = tmpNrg;
    for (k=0; k<numberCols; k+=2)
    {
      /* Load band vector addresses of 2 consecutive timeslots */
      FIXP_DBL *RESTRICT r0 = realValues[k];
      FIXP_DBL *RESTRICT i0 = imagValues[k];
      FIXP_DBL *RESTRICT r1 = realValues[k+1];
      FIXP_DBL *RESTRICT i1 = imagValues[k+1];
      for (j=0; j<numberBands; j++)
      {
        FIXP_DBL  energy;
        FIXP_DBL  tr0,tr1,ti0,ti1;

        /* Read QMF values of 2 timeslots */
        tr0 = r0[j]; tr1 = r1[j]; ti0 = i0[j]; ti1 = i1[j];

        /* Scale QMF Values and Calc Energy of both timeslots */
        tr0 <<= scale;
        ti0 <<= scale;
        energy = fPow2AddDiv2(fPow2Div2(tr0), ti0) >> 1;

        tr1 <<= scale;
        ti1 <<= scale;
        energy += fPow2AddDiv2(fPow2Div2(tr1), ti1) >> 1;

        /* Write timeslot pair energy to scratch */
        *nrgValues++ = energy;
        max_val = fixMax(max_val, energy);

        /* Write back scaled QMF values */
        r0[j] = tr0; r1[j] = tr1; i0[j] = ti0; i1[j] = ti1;
      }
    }
  }
  /* energyScale: scalefactor energies of current frame */
  *energyScale = 2*(*qmfScale)-1;       /* if qmfScale > 0: nr of right shifts otherwise nr of left shifts */

  /* Scale timeslot pair energies and write to output buffer */
  scale = CountLeadingBits(max_val);
  {
  	FIXP_DBL *nrgValues = tmpNrg;
    for (k=0; k<numberCols>>1; k++) {
      scaleValues(energyValues[k], nrgValues, numberBands, scale);
      nrgValues += numberBands;
    }
    *energyScale += scale;
  }

  /* Free Scratch buffer */
  C_ALLOC_SCRATCH_END(tmpNrg, FIXP_DBL, QMF_CHANNELS*QMF_MAX_TIME_SLOTS/2);
}

LNK_SECTION_CODE_L1
static void
FDKsbrEnc_getEnergyFromCplxQmfDataFull(FIXP_DBL **RESTRICT energyValues,/*!< the result of the operation */
                                       FIXP_DBL **RESTRICT realValues,  /*!< the real part of the QMF subsamples */
                                       FIXP_DBL **RESTRICT imagValues,  /*!< the imaginary part of the QMF subsamples */
                                       int   numberBands,               /*!< number of QMF bands */
                                       int   numberCols,                /*!< number of QMF subsamples */
                                       int   *qmfScale,                 /*!< sclefactor of QMF subsamples */
                                       int   *energyScale)              /*!< scalefactor of energies */
{
  int j, k;
  int scale;
  FIXP_DBL max_val = FL2FXCONST_DBL(0.0f);

  /* Get Scratch buffer */
  C_ALLOC_SCRATCH_START(tmpNrg, FIXP_DBL, QMF_MAX_TIME_SLOTS*QMF_CHANNELS/2);

  FDK_ASSERT(numberBands <= QMF_CHANNELS);
  FDK_ASSERT(numberCols <= QMF_MAX_TIME_SLOTS/2);

  /* Get max possible scaling of QMF data */
  scale = DFRACT_BITS;
  for (k=0; k<numberCols; k++) {
    scale = fixMin(scale, fixMin(getScalefactor(realValues[k], numberBands), getScalefactor(imagValues[k], numberBands)));
  }

  /* Tweak scaling stability for zero signal to non-zero signal transitions */
  if (scale >= DFRACT_BITS-1) {
    scale = (FRACT_BITS-1-*qmfScale);
  }
  /* prevent scaling of QFM values to -1.f */
  scale = fixMax(0,scale-1);

  /* Update QMF scale */
  *qmfScale += scale;

  /*
     Calculate energy of each time slot pair, max energy
     and shift QMF values as far as possible to the left.
   */
  {
    FIXP_DBL *nrgValues = tmpNrg;
    for (k=0; k<numberCols; k++)
    {
      /* Load band vector addresses of 2 consecutive timeslots */
      FIXP_DBL *RESTRICT r0 = realValues[k];
      FIXP_DBL *RESTRICT i0 = imagValues[k];
      for (j=0; j<numberBands; j++)
      {
        FIXP_DBL  energy;
        FIXP_DBL  tr0,ti0;

        /* Read QMF values of 2 timeslots */
        tr0 = r0[j]; ti0 = i0[j];

        /* Scale QMF Values and Calc Energy of both timeslots */
        tr0 <<= scale;
        ti0 <<= scale;
        energy = fPow2AddDiv2(fPow2Div2(tr0), ti0);
        *nrgValues++ = energy;

        max_val = fixMax(max_val, energy);

        /* Write back scaled QMF values */
        r0[j] = tr0; i0[j] = ti0;
      }
    }
  }
  /* energyScale: scalefactor energies of current frame */
  *energyScale = 2*(*qmfScale)-1;       /* if qmfScale > 0: nr of right shifts otherwise nr of left shifts */

  /* Scale timeslot pair energies and write to output buffer */
  scale = CountLeadingBits(max_val);
  {
  	FIXP_DBL *nrgValues = tmpNrg;
    for (k=0; k<numberCols; k++) {
      scaleValues(energyValues[k], nrgValues, numberBands, scale);
      nrgValues += numberBands;
    }
    *energyScale += scale;
  }

  /* Free Scratch buffer */
  C_ALLOC_SCRATCH_END(tmpNrg, FIXP_DBL, QMF_MAX_TIME_SLOTS*QMF_CHANNELS/2);
}

/***************************************************************************/
/*!

  \brief  Quantisation of the panorama value (balance)

  \return the quantized pan value

****************************************************************************/
static INT
mapPanorama(INT nrgVal,     /*! integer value of the energy */
            INT ampRes,     /*! amplitude resolution [1.5/3dB] */
            INT *quantError /*! quantization error of energy val*/
           )
{
  int i;
  INT min_val, val;
  UCHAR panIndex;
  INT sign;

  sign = nrgVal > 0 ? 1 : -1;

  nrgVal *= sign;

  min_val = FDK_INT_MAX;
  panIndex = 0;
  for (i = 0; i < maxIndex[ampRes]; i++) {
    val = fixp_abs ((nrgVal - (INT)panTable[ampRes][i]));

    if (val < min_val) {
      min_val = val;
      panIndex = i;
    }
  }

  *quantError=min_val;

  return panTable[ampRes][maxIndex[ampRes]-1] + sign * panTable[ampRes][panIndex];
}


/***************************************************************************/
/*!

  \brief  Quantisation of the noise floor levels

  \return void

****************************************************************************/
static void
sbrNoiseFloorLevelsQuantisation(SCHAR    *RESTRICT iNoiseLevels, /*! quantized noise levels */
                                FIXP_DBL *RESTRICT NoiseLevels,  /*! the noise levels  */
                                INT       coupling               /*! the coupling flag */
                               )
{
  INT i;
  INT tmp, dummy;

  /* Quantisation, similar to sfb quant... */
  for (i = 0; i < MAX_NUM_NOISE_VALUES; i++) {
    /* tmp = NoiseLevels[i] > (PFLOAT)30.0f ? 30: (INT) (NoiseLevels[i] + (PFLOAT)0.5); */
    /* 30>>6 = 0.46875 */
    if ((FIXP_DBL)NoiseLevels[i] > FL2FXCONST_DBL(0.46875f)) {
      tmp = 30;
    }
    else {
      /* tmp = (INT)((FIXP_DBL)NoiseLevels[i] + (FL2FXCONST_DBL(0.5f)>>(*/ /* FRACT_BITS+ */ /* 6-1)));*/
      /* tmp = tmp >> (DFRACT_BITS-1-6); */ /* conversion to integer happens here */
      /* rounding is done by shifting one bit less than necessary to the right, adding '1' and then shifting the final bit */
      tmp = ((((INT)NoiseLevels[i])>>(DFRACT_BITS-1-LD_DATA_SHIFT)) ); /* conversion to integer */
			 if (tmp != 0)
			   tmp += 1;
    }

    if (coupling) {
      tmp = tmp < -30 ? -30 : tmp;
      tmp = mapPanorama (tmp,1,&dummy);
    }
    iNoiseLevels[i] = tmp;
  }
}

/***************************************************************************/
/*!

  \brief  Calculation of noise floor for coupling

  \return void

****************************************************************************/
static void
coupleNoiseFloor(FIXP_DBL *RESTRICT noise_level_left, /*! noise level left  (modified)*/
                 FIXP_DBL *RESTRICT noise_level_right /*! noise level right (modified)*/
                )
{
  FIXP_DBL cmpValLeft,cmpValRight;
  INT i;
  FIXP_DBL temp1,temp2;

  for (i = 0; i < MAX_NUM_NOISE_VALUES; i++) {

    /* Calculation of the power function using ld64:
       z  = x^y;
       z' = CalcLd64(z) = y*CalcLd64(x)/64;
       z  = CalcInvLd64(z');
    */
    cmpValLeft  = NOISE_FLOOR_OFFSET_64 - noise_level_left[i];
    cmpValRight = NOISE_FLOOR_OFFSET_64 - noise_level_right[i];

    if (cmpValRight < FL2FXCONST_DBL(0.0f)) {
      temp1 = CalcInvLdData(NOISE_FLOOR_OFFSET_64 - noise_level_right[i]);
    }
    else {
      temp1 = CalcInvLdData(NOISE_FLOOR_OFFSET_64 - noise_level_right[i]);
      temp1 = temp1 << (DFRACT_BITS-1-LD_DATA_SHIFT-1);  /* INT to fract conversion of result, if input of CalcInvLdData is positiv */
    }

    if (cmpValLeft < FL2FXCONST_DBL(0.0f)) {
      temp2 = CalcInvLdData(NOISE_FLOOR_OFFSET_64 - noise_level_left[i]);
    }
    else {
      temp2 = CalcInvLdData(NOISE_FLOOR_OFFSET_64 - noise_level_left[i]);
      temp2 = temp2 << (DFRACT_BITS-1-LD_DATA_SHIFT-1);  /* INT to fract conversion of result, if input of CalcInvLdData is positiv */
    }


    if ((cmpValLeft < FL2FXCONST_DBL(0.0f)) && (cmpValRight < FL2FXCONST_DBL(0.0f))) {
      noise_level_left[i]  = NOISE_FLOOR_OFFSET_64 - (CalcLdData(((temp1>>1) + (temp2>>1)))); /* no scaling needed! both values are dfract */
      noise_level_right[i] = CalcLdData(temp2) - CalcLdData(temp1);
    }

    if ((cmpValLeft >= FL2FXCONST_DBL(0.0f)) && (cmpValRight >= FL2FXCONST_DBL(0.0f))) {
      noise_level_left[i]  = NOISE_FLOOR_OFFSET_64 - (CalcLdData(((temp1>>1) + (temp2>>1))) + FL2FXCONST_DBL(0.109375f)); /* scaled with 7/64 */
      noise_level_right[i] = CalcLdData(temp2) - CalcLdData(temp1);
    }

    if ((cmpValLeft >= FL2FXCONST_DBL(0.0f)) && (cmpValRight < FL2FXCONST_DBL(0.0f))) {
      noise_level_left[i]  = NOISE_FLOOR_OFFSET_64 - (CalcLdData(((temp1>>(7+1)) + (temp2>>1))) + FL2FXCONST_DBL(0.109375f)); /* scaled with 7/64 */
      noise_level_right[i] = (CalcLdData(temp2) + FL2FXCONST_DBL(0.109375f)) - CalcLdData(temp1);
    }

    if ((cmpValLeft < FL2FXCONST_DBL(0.0f)) && (cmpValRight >= FL2FXCONST_DBL(0.0f))) {
      noise_level_left[i]  = NOISE_FLOOR_OFFSET_64 - (CalcLdData(((temp1>>1) + (temp2>>(7+1)))) + FL2FXCONST_DBL(0.109375f)); /* scaled with 7/64 */
      noise_level_right[i] = CalcLdData(temp2) - (CalcLdData(temp1) + FL2FXCONST_DBL(0.109375f)); /* scaled with 7/64 */
    }
  }
}

/***************************************************************************/
/*!

  \brief  Calculation of energy starting in lower band (li) up to upper band (ui)
          over slots (start_pos) to (stop_pos)

  \return void

****************************************************************************/
static FIXP_DBL
getEnvSfbEnergy(INT li,              /*! lower band */
                INT ui,              /*! upper band */
                INT start_pos,       /*! start slot */
                INT stop_pos,        /*! stop slot */
                INT border_pos,      /*! slots scaling border */
                FIXP_DBL **YBuffer,  /*! sfb energy buffer */
                INT YBufferSzShift,  /*! Energy buffer index scale */
                INT scaleNrg0,       /*! scaling of lower slots */
                INT scaleNrg1)       /*! scaling of upper slots */
{
  /* use dynamic scaling for outer energy loop;
     energies are critical and every bit is important */
  int sc0, sc1, k, l;

  FIXP_DBL nrgSum, nrg1, nrg2, accu1, accu2;
  INT dynScale, dynScale1, dynScale2;
  if(ui-li==0) dynScale = DFRACT_BITS-1;
  else
  dynScale = CalcLdInt(ui-li)>>(DFRACT_BITS-1-LD_DATA_SHIFT);

  sc0 = fixMin(scaleNrg0,Y_NRG_SCALE); sc1 = fixMin(scaleNrg1,Y_NRG_SCALE);
  /* dynScale{1,2} is set such that the right shift below is positive */
  dynScale1 = fixMin((scaleNrg0-sc0),dynScale);
  dynScale2 = fixMin((scaleNrg1-sc1),dynScale);
  nrgSum = accu1 = accu2 = (FIXP_DBL)0;

  for (k = li; k < ui; k++) {
    nrg1 = nrg2 = (FIXP_DBL)0;
    for (l = start_pos; l < border_pos; l++) {
      nrg1 += YBuffer[l>>YBufferSzShift][k] >> sc0;
    }
    for (; l < stop_pos; l++) {
      nrg2 += YBuffer[l>>YBufferSzShift][k] >> sc1;
    }
    accu1 += (nrg1>>dynScale1);
    accu2 += (nrg2>>dynScale2);
  }
  /* This shift factor is always positive. See comment above. */
  nrgSum += ( accu1 >> fixMin((scaleNrg0-sc0-dynScale1),(DFRACT_BITS-1)) )
          +  ( accu2 >> fixMin((scaleNrg1-sc1-dynScale2),(DFRACT_BITS-1)) );

  return nrgSum;
}

/***************************************************************************/
/*!

  \brief  Energy compensation in missing harmonic mode

  \return void

****************************************************************************/
static FIXP_DBL
mhLoweringEnergy(FIXP_DBL nrg, INT M)
{
  /*
     Compensating for the fact that we in the decoder map the "average energy to every QMF
     band, and use this when we calculate the boost-factor. Since the mapped energy isn't
     the average energy but the maximum energy in case of missing harmonic creation, we will
     in the boost function calculate that too much limiting has been applied and hence we will
     boost the signal although it isn't called for. Hence we need to compensate for this by
     lowering the transmitted energy values for the sines so they will get the correct level
     after the boost is applied.
  */
  if(M > 2){
    INT tmpScale;
    tmpScale = CountLeadingBits(nrg);
    nrg <<= tmpScale;
    nrg = fMult(nrg, FL2FXCONST_DBL(0.398107267f)); /* The maximum boost is 1.584893, so the maximum attenuation should be square(1/1.584893) = 0.398107267 */
    nrg >>= tmpScale;
  }
  else{
    if(M > 1){
      nrg >>= 1;
    }
  }

  return nrg;
}

/***************************************************************************/
/*!

  \brief  Energy compensation in none missing harmonic mode

  \return void

****************************************************************************/
static FIXP_DBL nmhLoweringEnergy(
        FIXP_DBL nrg,
        const FIXP_DBL nrgSum,
        const INT nrgSum_scale,
        const INT M
        )
{
  if (nrg>FL2FXCONST_DBL(0)) {
    int sc=0;
    /* gain = nrgSum / (nrg*(M+1)) */
    FIXP_DBL gain = fMult(fDivNorm(nrgSum, nrg, &sc), GetInvInt(M+1));
    sc += nrgSum_scale;

    /* reduce nrg if gain smaller 1.f */
    if ( !((sc>=0) && ( gain > ((FIXP_DBL)MAXVAL_DBL>>sc) )) ) {
      nrg = fMult(scaleValue(gain,sc), nrg);
    }
  }
  return nrg;
}

/***************************************************************************/
/*!

  \brief  calculates the envelope values from the energies, depending on
          framing and stereo mode

  \return void

****************************************************************************/
static void
calculateSbrEnvelope (FIXP_DBL **RESTRICT YBufferLeft,  /*! energy buffer left */
                      FIXP_DBL **RESTRICT YBufferRight, /*! energy buffer right */
                      int *RESTRICT YBufferScaleLeft,   /*! scale energy buffer left */
                      int *RESTRICT YBufferScaleRight,  /*! scale energy buffer right */
                      const SBR_FRAME_INFO *frame_info, /*! frame info vector */
                      SCHAR *RESTRICT sfb_nrgLeft,      /*! sfb energy buffer left */
                      SCHAR *RESTRICT sfb_nrgRight,     /*! sfb energy buffer right */
                      HANDLE_SBR_CONFIG_DATA h_con,     /*! handle to config data   */
                      HANDLE_ENV_CHANNEL h_sbr,         /*! envelope channel handle */
                      SBR_STEREO_MODE stereoMode,       /*! stereo coding mode */
                      INT* maxQuantError,               /*! maximum quantization error, for panorama. */
                      int YBufferSzShift)               /*! Energy buffer index scale */

{
  int i, j, m = 0;
  INT no_of_bands, start_pos, stop_pos, li, ui;
  FREQ_RES freq_res;

  INT ca = 2 - h_sbr->encEnvData.init_sbr_amp_res;
  INT oneBitLess = 0;
  if (ca == 2)
    oneBitLess = 1; /* LD_DATA_SHIFT => ld64 scaling; one bit less for rounding */

  INT quantError;
  INT nEnvelopes = frame_info->nEnvelopes;
  INT short_env = frame_info->shortEnv - 1;
  INT timeStep = h_sbr->sbrExtractEnvelope.time_step;
  INT commonScale,scaleLeft0,scaleLeft1;
  INT scaleRight0=0,scaleRight1=0;

  commonScale = fixMin(YBufferScaleLeft[0],YBufferScaleLeft[1]);

  if (stereoMode == SBR_COUPLING) {
    commonScale = fixMin(commonScale,YBufferScaleRight[0]);
    commonScale = fixMin(commonScale,YBufferScaleRight[1]);
  }

  commonScale = commonScale - 7;

  scaleLeft0 = YBufferScaleLeft[0] - commonScale;
  scaleLeft1 = YBufferScaleLeft[1] - commonScale ;
  FDK_ASSERT ((scaleLeft0 >= 0) && (scaleLeft1 >= 0));

  if (stereoMode == SBR_COUPLING) {
    scaleRight0 = YBufferScaleRight[0] - commonScale;
    scaleRight1 = YBufferScaleRight[1] - commonScale;
    FDK_ASSERT ((scaleRight0 >= 0) && (scaleRight1 >= 0));
    *maxQuantError = 0;
  }

  for (i = 0; i < nEnvelopes; i++) {

    FIXP_DBL pNrgLeft[QMF_MAX_TIME_SLOTS];
    FIXP_DBL pNrgRight[QMF_MAX_TIME_SLOTS];
    int envNrg_scale;
    FIXP_DBL envNrgLeft  = FL2FXCONST_DBL(0.0f);
    FIXP_DBL envNrgRight = FL2FXCONST_DBL(0.0f);
    int      missingHarmonic[QMF_MAX_TIME_SLOTS];
    int      count[QMF_MAX_TIME_SLOTS];

    start_pos = timeStep * frame_info->borders[i];
    stop_pos = timeStep * frame_info->borders[i + 1];
    freq_res = frame_info->freqRes[i];
    no_of_bands = h_con->nSfb[freq_res];
    envNrg_scale = DFRACT_BITS-fNormz((FIXP_DBL)no_of_bands);

    if (i == short_env) {
      stop_pos -= fixMax(2, timeStep);  /* consider at least 2 QMF slots less for short envelopes (envelopes just before transients) */
    }

    for (j = 0; j < no_of_bands; j++) {
      FIXP_DBL nrgLeft  = FL2FXCONST_DBL(0.0f);
      FIXP_DBL nrgRight = FL2FXCONST_DBL(0.0f);

      li = h_con->freqBandTable[freq_res][j];
      ui = h_con->freqBandTable[freq_res][j + 1];

      if(freq_res == FREQ_RES_HIGH){
        if(j == 0 && ui-li > 1){
          li++;
        }
      }
      else{
        if(j == 0 && ui-li > 2){
          li++;
        }
      }

      /*
        Find out whether a sine will be missing in the scale-factor
        band that we're currently processing.
      */
      missingHarmonic[j] = 0;

      if(h_sbr->encEnvData.addHarmonicFlag){

        if(freq_res == FREQ_RES_HIGH){
          if(h_sbr->encEnvData.addHarmonic[j]){    /*A missing sine in the current band*/
            missingHarmonic[j] = 1;
          }
        }
        else{
          INT i;
          INT startBandHigh = 0;
          INT stopBandHigh = 0;

          while(h_con->freqBandTable[FREQ_RES_HIGH][startBandHigh] < h_con->freqBandTable[FREQ_RES_LOW][j])
            startBandHigh++;
          while(h_con->freqBandTable[FREQ_RES_HIGH][stopBandHigh] < h_con->freqBandTable[FREQ_RES_LOW][j + 1])
            stopBandHigh++;

          for(i = startBandHigh; i<stopBandHigh; i++){
            if(h_sbr->encEnvData.addHarmonic[i]){
              missingHarmonic[j] = 1;
            }
          }
        }
      }

      /*
        If a sine is missing in a scalefactorband, with more than one qmf channel
        use the nrg from the channel with the largest nrg rather than the mean.
        Compensate for the boost calculation in the decdoder.
      */
      int border_pos = fixMin(stop_pos, h_sbr->sbrExtractEnvelope.YBufferWriteOffset<<YBufferSzShift);

      if(missingHarmonic[j]){

        int k;
        count[j] = stop_pos - start_pos;
        nrgLeft = FL2FXCONST_DBL(0.0f);

        for (k = li; k < ui; k++) {
          FIXP_DBL tmpNrg;
          tmpNrg = getEnvSfbEnergy(k,
                                   k+1,
                                   start_pos,
                                   stop_pos,
                                   border_pos,
                                   YBufferLeft,
                                   YBufferSzShift,
                                   scaleLeft0,
                                   scaleLeft1);

          nrgLeft = fixMax(nrgLeft, tmpNrg);
        }

        /* Energy lowering compensation */
        nrgLeft = mhLoweringEnergy(nrgLeft, ui-li);

        if (stereoMode == SBR_COUPLING) {

          nrgRight = FL2FXCONST_DBL(0.0f);

          for (k = li; k < ui; k++) {
            FIXP_DBL tmpNrg;
            tmpNrg = getEnvSfbEnergy(k,
                                     k+1,
                                     start_pos,
                                     stop_pos,
                                     border_pos,
                                     YBufferRight,
                                     YBufferSzShift,
                                     scaleRight0,
                                     scaleRight1);

            nrgRight = fixMax(nrgRight, tmpNrg);
          }

          /* Energy lowering compensation */
          nrgRight = mhLoweringEnergy(nrgRight, ui-li);
        }
      } /* end missingHarmonic */
      else{
        count[j] = (stop_pos - start_pos) * (ui - li);

        nrgLeft = getEnvSfbEnergy(li,
                                  ui,
                                  start_pos,
                                  stop_pos,
                                  border_pos,
                                  YBufferLeft,
                                  YBufferSzShift,
                                  scaleLeft0,
                                  scaleLeft1);

        if (stereoMode == SBR_COUPLING) {
          nrgRight = getEnvSfbEnergy(li,
                                     ui,
                                     start_pos,
                                     stop_pos,
                                     border_pos,
                                     YBufferRight,
                                     YBufferSzShift,
                                     scaleRight0,
                                     scaleRight1);
        }
      } /* !missingHarmonic */

      /* save energies */
      pNrgLeft[j]  = nrgLeft;
      pNrgRight[j] = nrgRight;
      envNrgLeft  += (nrgLeft>>envNrg_scale);
      envNrgRight += (nrgRight>>envNrg_scale);
    } /* j */

    for (j = 0; j < no_of_bands; j++) {

      FIXP_DBL nrgLeft2 = FL2FXCONST_DBL(0.0f);
      FIXP_DBL nrgLeft  = pNrgLeft[j];
      FIXP_DBL nrgRight = pNrgRight[j];

      /* None missing harmonic Energy lowering compensation */
      if(!missingHarmonic[j] && h_sbr->fLevelProtect) {
        /* in case of missing energy in base band,
           reduce reference energy to prevent overflows in decoder output */
        nrgLeft = nmhLoweringEnergy(nrgLeft, envNrgLeft, envNrg_scale, no_of_bands);
        if (stereoMode == SBR_COUPLING) {
          nrgRight = nmhLoweringEnergy(nrgRight, envNrgRight, envNrg_scale, no_of_bands);
        }
      }

      if (stereoMode == SBR_COUPLING) {
        /* calc operation later with log */
        nrgLeft2 = nrgLeft;
        nrgLeft = (nrgRight + nrgLeft) >> 1;
      }

      /* nrgLeft = f20_log2(nrgLeft / (PFLOAT)(count * h_sbr->sbrQmf.no_channels))+(PFLOAT)44; */
      /* If nrgLeft == 0 then the Log calculations below do fail. */
      if (nrgLeft > FL2FXCONST_DBL(0.0f))
      {
        FIXP_DBL tmp0,tmp1,tmp2,tmp3;
        INT tmpScale;

        tmpScale = CountLeadingBits(nrgLeft);
        nrgLeft = nrgLeft << tmpScale;

        tmp0 = CalcLdData(nrgLeft);                                                       /* scaled by 1/64 */
        tmp1 = ((FIXP_DBL) (commonScale+tmpScale)) << (DFRACT_BITS-1-LD_DATA_SHIFT-1);    /* scaled by 1/64 */
        tmp2 = ((FIXP_DBL)(count[j]*h_con->noQmfBands)) << (DFRACT_BITS-1-14-1);
        tmp2 = CalcLdData(tmp2);                                                          /* scaled by 1/64 */
        tmp3 = FL2FXCONST_DBL(0.6875f-0.21875f-0.015625f)>>1;                             /* scaled by 1/64 */

        nrgLeft = ((tmp0-tmp2)>>1) + (tmp3 - tmp1);
      } else {
        nrgLeft = FL2FXCONST_DBL(-1.0f);
      }

      /* ld64 to integer conversion */
      nrgLeft = fixMin(fixMax(nrgLeft,FL2FXCONST_DBL(0.0f)),(FL2FXCONST_DBL(0.5f)>>oneBitLess));
      nrgLeft = (FIXP_DBL)(LONG)nrgLeft >> (DFRACT_BITS-1-LD_DATA_SHIFT-1-oneBitLess-1);
      sfb_nrgLeft[m] = ((INT)nrgLeft+1)>>1; /* rounding */

      if (stereoMode == SBR_COUPLING) {
        FIXP_DBL scaleFract;
        int sc0, sc1;

        nrgLeft2 = fixMax((FIXP_DBL)0x1, nrgLeft2);
        nrgRight = fixMax((FIXP_DBL)0x1, nrgRight);

        sc0 = CountLeadingBits(nrgLeft2);
        sc1 = CountLeadingBits(nrgRight);

        scaleFract = ((FIXP_DBL)(sc0-sc1)) << (DFRACT_BITS-1-LD_DATA_SHIFT); /* scale value in ld64 representation */
        nrgRight = CalcLdData(nrgLeft2<<sc0) - CalcLdData(nrgRight<<sc1) - scaleFract;

        /* ld64 to integer conversion */
        nrgRight = (FIXP_DBL)(LONG)(nrgRight) >> (DFRACT_BITS-1-LD_DATA_SHIFT-1-oneBitLess);
        nrgRight  = (nrgRight+(FIXP_DBL)1)>>1; /* rounding */

        sfb_nrgRight[m] = mapPanorama (nrgRight,h_sbr->encEnvData.init_sbr_amp_res,&quantError);

        *maxQuantError = fixMax(quantError, *maxQuantError);
      }

      m++;
    } /* j */

     /* Do energy compensation for sines that are present in two
         QMF-bands in the original, but will only occur in one band in
         the decoder due to the synthetic sine coding.*/
    if (h_con->useParametricCoding) {
      m-=no_of_bands;
      for (j = 0; j < no_of_bands; j++) {
        if (freq_res==FREQ_RES_HIGH && h_sbr->sbrExtractEnvelope.envelopeCompensation[j]){
          sfb_nrgLeft[m] -= (ca * fixp_abs((INT)h_sbr->sbrExtractEnvelope.envelopeCompensation[j]));
        }
        sfb_nrgLeft[m] = fixMax(0, sfb_nrgLeft[m]);
        m++;
      }
    } /* useParametricCoding */

  } /* i*/
}

/***************************************************************************/
/*!

  \brief  calculates the noise floor and the envelope values from the
          energies, depending on framing and stereo mode

  FDKsbrEnc_extractSbrEnvelope is the main function for encoding and writing the
  envelope and the noise floor. The function includes the following processes:

  -Analysis subband filtering.
  -Encoding SA and pan parameters (if enabled).
  -Transient detection.

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

LNK_SECTION_CODE_L1
void
FDKsbrEnc_extractSbrEnvelope1 (
                    HANDLE_SBR_CONFIG_DATA h_con, /*! handle to config data   */
                    HANDLE_SBR_HEADER_DATA sbrHeaderData,
                    HANDLE_SBR_BITSTREAM_DATA sbrBitstreamData,
                    HANDLE_ENV_CHANNEL hEnvChan,
                    HANDLE_COMMON_DATA hCmonData,
                    SBR_ENV_TEMP_DATA   *eData,
                    SBR_FRAME_TEMP_DATA *fData
                   )
{

  HANDLE_SBR_EXTRACT_ENVELOPE sbrExtrEnv = &hEnvChan->sbrExtractEnvelope;

  if (sbrExtrEnv->YBufferSzShift == 0)
    FDKsbrEnc_getEnergyFromCplxQmfDataFull(&sbrExtrEnv->YBuffer[sbrExtrEnv->YBufferWriteOffset],
                                           sbrExtrEnv->rBuffer + sbrExtrEnv->rBufferReadOffset,
                                           sbrExtrEnv->iBuffer + sbrExtrEnv->rBufferReadOffset,
                                           h_con->noQmfBands,
                                           sbrExtrEnv->no_cols,
                                          &hEnvChan->qmfScale,
                                          &sbrExtrEnv->YBufferScale[1]);
  else
    FDKsbrEnc_getEnergyFromCplxQmfData(&sbrExtrEnv->YBuffer[sbrExtrEnv->YBufferWriteOffset],
                                       sbrExtrEnv->rBuffer + sbrExtrEnv->rBufferReadOffset,
                                       sbrExtrEnv->iBuffer + sbrExtrEnv->rBufferReadOffset,
                                       h_con->noQmfBands,
                                       sbrExtrEnv->no_cols,
                                      &hEnvChan->qmfScale,
                                      &sbrExtrEnv->YBufferScale[1]);



  /*
    Precalculation of Tonality Quotas  COEFF Transform OK
  */
  FDKsbrEnc_CalculateTonalityQuotas(&hEnvChan->TonCorr,
                                     sbrExtrEnv->rBuffer,
                                     sbrExtrEnv->iBuffer,
                                     h_con->freqBandTable[HI][h_con->nSfb[HI]],
                                     hEnvChan->qmfScale);



  /*
    Transient detection COEFF Transform OK
  */
  FDKsbrEnc_transientDetect(&hEnvChan->sbrTransientDetector,
                             sbrExtrEnv->YBuffer,
                             sbrExtrEnv->YBufferScale,
                             eData->transient_info,
                             sbrExtrEnv->YBufferWriteOffset,
                             sbrExtrEnv->YBufferSzShift,
                             sbrExtrEnv->time_step,
                             hEnvChan->SbrEnvFrame.frameMiddleSlot);



  /*
    Generate flags for 2 env in a FIXFIX-frame.
    Remove this function to get always 1 env per FIXFIX-frame.
  */

  /*
    frame Splitter COEFF Transform OK
  */
  FDKsbrEnc_frameSplitter(sbrExtrEnv->YBuffer,
                          sbrExtrEnv->YBufferScale,
                         &hEnvChan->sbrTransientDetector,
                          h_con->freqBandTable[1],
                          eData->transient_info,
                          sbrExtrEnv->YBufferWriteOffset,
                          sbrExtrEnv->YBufferSzShift,
                          h_con->nSfb[1],
                          sbrExtrEnv->time_step,
                          sbrExtrEnv->no_cols);


}

/***************************************************************************/
/*!

  \brief  calculates the noise floor and the envelope values from the
          energies, depending on framing and stereo mode

  FDKsbrEnc_extractSbrEnvelope is the main function for encoding and writing the
  envelope and the noise floor. The function includes the following processes:

  -Determine time/frequency division of current granule.
  -Sending transient info to bitstream.
  -Set amp_res to 1.5 dB if the current frame contains only one envelope.
  -Lock dynamic bandwidth frequency change if the next envelope not starts on a
  frame boundary.
  -MDCT transposer (needed to detect where harmonics will be missing).
  -Spectrum Estimation (used for pulse train and missing harmonics detection).
  -Pulse train detection.
  -Inverse Filtering detection.
  -Waveform Coding.
  -Missing Harmonics detection.
  -Extract envelope of current frame.
  -Noise floor estimation.
  -Noise floor quantisation and coding.
  -Encode envelope of current frame.
  -Send the encoded data to the bitstream.
  -Write to bitstream.

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

LNK_SECTION_CODE_L1
void
FDKsbrEnc_extractSbrEnvelope2 (
                    HANDLE_SBR_CONFIG_DATA h_con, /*! handle to config data   */
                    HANDLE_SBR_HEADER_DATA sbrHeaderData,
                    HANDLE_PARAMETRIC_STEREO    hParametricStereo,
                    HANDLE_SBR_BITSTREAM_DATA sbrBitstreamData,
                    HANDLE_ENV_CHANNEL   h_envChan0,
                    HANDLE_ENV_CHANNEL   h_envChan1,
                    HANDLE_COMMON_DATA   hCmonData,
                    SBR_ENV_TEMP_DATA   *eData,
                    SBR_FRAME_TEMP_DATA *fData,
                    int                  clearOutput
                   )
{
  HANDLE_ENV_CHANNEL h_envChan[MAX_NUM_CHANNELS] = {h_envChan0, h_envChan1};
  int ch, i, j, c, YSzShift = h_envChan[0]->sbrExtractEnvelope.YBufferSzShift;

  SBR_STEREO_MODE stereoMode = h_con->stereoMode;
  int nChannels = h_con->nChannels;
  const int *v_tuning;
  static const int v_tuningHEAAC[6] = { 0, 2, 4, 0, 0, 0 };

  static const int v_tuningELD[6] = { 0, 2, 3, 0, 0, 0 };

  if (h_con->sbrSyntaxFlags & SBR_SYNTAX_LOW_DELAY)
    v_tuning = v_tuningELD;
  else
    v_tuning = v_tuningHEAAC;


  /*
    Select stereo mode.
  */
  if (stereoMode == SBR_COUPLING) {
    if (eData[0].transient_info[1] && eData[1].transient_info[1]) {
      eData[0].transient_info[0] = fixMin(eData[1].transient_info[0], eData[0].transient_info[0]);
      eData[1].transient_info[0] = eData[0].transient_info[0];
    }
    else {
      if (eData[0].transient_info[1] && !eData[1].transient_info[1]) {
        eData[1].transient_info[0] = eData[0].transient_info[0];
      }
      else {
        if (!eData[0].transient_info[1] && eData[1].transient_info[1])
          eData[0].transient_info[0] = eData[1].transient_info[0];
        else {
          eData[0].transient_info[0] = fixMax(eData[1].transient_info[0], eData[0].transient_info[0]);
          eData[1].transient_info[0] = eData[0].transient_info[0];
        }
      }
    }
  }

  /*
    Determine time/frequency division of current granule
  */
  eData[0].frame_info = FDKsbrEnc_frameInfoGenerator(&h_envChan[0]->SbrEnvFrame,
                                                     eData[0].transient_info,
                                                     h_envChan[0]->sbrExtractEnvelope.pre_transient_info,
                                                     h_envChan[0]->encEnvData.ldGrid,
                                                     v_tuning);

  h_envChan[0]->encEnvData.hSbrBSGrid = &h_envChan[0]->SbrEnvFrame.SbrGrid;

  /* AAC LD patch for transient prediction */
  if (h_envChan[0]->encEnvData.ldGrid && eData[0].transient_info[2]) {
    /* if next frame will start with transient, set shortEnv to numEnvelopes(shortend Envelope = shortEnv-1)*/
    h_envChan[0]->SbrEnvFrame.SbrFrameInfo.shortEnv = h_envChan[0]->SbrEnvFrame.SbrFrameInfo.nEnvelopes;
  }


  switch (stereoMode) {
  case SBR_LEFT_RIGHT:
  case SBR_SWITCH_LRC:
    eData[1].frame_info = FDKsbrEnc_frameInfoGenerator(&h_envChan[1]->SbrEnvFrame,
                                                       eData[1].transient_info,
                                                       h_envChan[1]->sbrExtractEnvelope.pre_transient_info,
                                                       h_envChan[1]->encEnvData.ldGrid,
                                                       v_tuning);

    h_envChan[1]->encEnvData.hSbrBSGrid = &h_envChan[1]->SbrEnvFrame.SbrGrid;

    if (h_envChan[1]->encEnvData.ldGrid && eData[1].transient_info[2]) {
      /* if next frame will start with transient, set shortEnv to numEnvelopes(shortend Envelope = shortEnv-1)*/
      h_envChan[1]->SbrEnvFrame.SbrFrameInfo.shortEnv = h_envChan[1]->SbrEnvFrame.SbrFrameInfo.nEnvelopes;
    }

    /* compare left and right frame_infos */
    if (eData[0].frame_info->nEnvelopes != eData[1].frame_info->nEnvelopes) {
      stereoMode = SBR_LEFT_RIGHT;
    } else {
      for (i = 0; i < eData[0].frame_info->nEnvelopes + 1; i++) {
        if (eData[0].frame_info->borders[i] != eData[1].frame_info->borders[i]) {
          stereoMode = SBR_LEFT_RIGHT;
          break;
        }
      }
      for (i = 0; i < eData[0].frame_info->nEnvelopes; i++) {
        if (eData[0].frame_info->freqRes[i] != eData[1].frame_info->freqRes[i]) {
          stereoMode = SBR_LEFT_RIGHT;
          break;
        }
      }
      if (eData[0].frame_info->shortEnv != eData[1].frame_info->shortEnv) {
        stereoMode = SBR_LEFT_RIGHT;
      }
    }
    break;
  case SBR_COUPLING:
    eData[1].frame_info = eData[0].frame_info;
    h_envChan[1]->encEnvData.hSbrBSGrid = &h_envChan[0]->SbrEnvFrame.SbrGrid;
    break;
  case SBR_MONO:
    /* nothing to do */
    break;
  default:
    FDK_ASSERT (0);
  }


  for (ch = 0; ch < nChannels;ch++)
  {
    HANDLE_ENV_CHANNEL hEnvChan = h_envChan[ch];
    HANDLE_SBR_EXTRACT_ENVELOPE sbrExtrEnv = &hEnvChan->sbrExtractEnvelope;
    SBR_ENV_TEMP_DATA *ed = &eData[ch];


    /*
       Send transient info to bitstream and store for next call
    */
    sbrExtrEnv->pre_transient_info[0] = ed->transient_info[0];/* tran_pos */
    sbrExtrEnv->pre_transient_info[1] = ed->transient_info[1];/* tran_flag */
    hEnvChan->encEnvData.noOfEnvelopes = ed->nEnvelopes = ed->frame_info->nEnvelopes;     /* number of envelopes of current frame */

    /*
      Check if the current frame is divided into one envelope only. If so, set the amplitude
      resolution to 1.5 dB, otherwise may set back to chosen value
    */
   if( ( hEnvChan->encEnvData.hSbrBSGrid->frameClass == FIXFIX )
        && ( ed->nEnvelopes == 1 ) )
   {

     if (hEnvChan->encEnvData.ldGrid)
       hEnvChan->encEnvData.currentAmpResFF = (AMP_RES)h_con->initAmpResFF;
     else
       hEnvChan->encEnvData.currentAmpResFF = SBR_AMP_RES_1_5;

     if ( hEnvChan->encEnvData.currentAmpResFF != hEnvChan->encEnvData.init_sbr_amp_res) {

        FDKsbrEnc_InitSbrHuffmanTables(&hEnvChan->encEnvData,
                                       &hEnvChan->sbrCodeEnvelope,
                                       &hEnvChan->sbrCodeNoiseFloor,
                                       hEnvChan->encEnvData.currentAmpResFF);
      }
    }
    else {
      if(sbrHeaderData->sbr_amp_res != hEnvChan->encEnvData.init_sbr_amp_res ) {

        FDKsbrEnc_InitSbrHuffmanTables(&hEnvChan->encEnvData,
                                       &hEnvChan->sbrCodeEnvelope,
                                       &hEnvChan->sbrCodeNoiseFloor,
                                       sbrHeaderData->sbr_amp_res);
      }
    }

    if (!clearOutput) {

      /*
        Tonality correction parameter extraction (inverse filtering level, noise floor additional sines).
      */
      FDKsbrEnc_TonCorrParamExtr(&hEnvChan->TonCorr,
                                  hEnvChan->encEnvData.sbr_invf_mode_vec,
                                  ed->noiseFloor,
                                 &hEnvChan->encEnvData.addHarmonicFlag,
                                  hEnvChan->encEnvData.addHarmonic,
                                  sbrExtrEnv->envelopeCompensation,
                                  ed->frame_info,
                                  ed->transient_info,
                                  h_con->freqBandTable[HI],
                                  h_con->nSfb[HI],
                                  hEnvChan->encEnvData.sbr_xpos_mode,
                                  h_con->sbrSyntaxFlags);

    }

    /* Low energy in low band fix */
    if ( hEnvChan->sbrTransientDetector.prevLowBandEnergy < hEnvChan->sbrTransientDetector.prevHighBandEnergy && hEnvChan->sbrTransientDetector.prevHighBandEnergy > FL2FX_DBL(0.03))
    {
      int i;

      hEnvChan->fLevelProtect = 1;

      for (i=0; i<MAX_NUM_NOISE_VALUES; i++)
        hEnvChan->encEnvData.sbr_invf_mode_vec[i] = INVF_HIGH_LEVEL;
    } else {
      hEnvChan->fLevelProtect = 0;
    }

    hEnvChan->encEnvData.sbr_invf_mode = hEnvChan->encEnvData.sbr_invf_mode_vec[0];

    hEnvChan->encEnvData.noOfnoisebands = hEnvChan->TonCorr.sbrNoiseFloorEstimate.noNoiseBands;


  } /* ch */



   /*
      Save number of scf bands per envelope
    */
  for (ch = 0; ch < nChannels;ch++) {
    for (i = 0; i < eData[ch].nEnvelopes; i++){
      h_envChan[ch]->encEnvData.noScfBands[i] =
      (eData[ch].frame_info->freqRes[i] == FREQ_RES_HIGH ? h_con->nSfb[FREQ_RES_HIGH] : h_con->nSfb[FREQ_RES_LOW]);
    }
  }

  /*
    Extract envelope of current frame.
  */
  switch (stereoMode) {
  case SBR_MONO:
     calculateSbrEnvelope (h_envChan[0]->sbrExtractEnvelope.YBuffer, NULL,
                          h_envChan[0]->sbrExtractEnvelope.YBufferScale, NULL,
                          eData[0].frame_info, eData[0].sfb_nrg, NULL,
                          h_con, h_envChan[0], SBR_MONO, NULL, YSzShift);
    break;
  case SBR_LEFT_RIGHT:
    calculateSbrEnvelope (h_envChan[0]->sbrExtractEnvelope.YBuffer, NULL,
                          h_envChan[0]->sbrExtractEnvelope.YBufferScale, NULL,
                          eData[0].frame_info, eData[0].sfb_nrg, NULL,
                          h_con, h_envChan[0], SBR_MONO, NULL, YSzShift);
    calculateSbrEnvelope (h_envChan[1]->sbrExtractEnvelope.YBuffer, NULL,
                          h_envChan[1]->sbrExtractEnvelope.YBufferScale, NULL,
                          eData[1].frame_info,eData[1].sfb_nrg, NULL,
                          h_con, h_envChan[1], SBR_MONO, NULL, YSzShift);
    break;
  case SBR_COUPLING:
    calculateSbrEnvelope (h_envChan[0]->sbrExtractEnvelope.YBuffer, h_envChan[1]->sbrExtractEnvelope.YBuffer,
                          h_envChan[0]->sbrExtractEnvelope.YBufferScale, h_envChan[1]->sbrExtractEnvelope.YBufferScale,
                          eData[0].frame_info, eData[0].sfb_nrg, eData[1].sfb_nrg,
                          h_con, h_envChan[0], SBR_COUPLING, &fData->maxQuantError, YSzShift);
    break;
  case SBR_SWITCH_LRC:
    calculateSbrEnvelope (h_envChan[0]->sbrExtractEnvelope.YBuffer, NULL,
                          h_envChan[0]->sbrExtractEnvelope.YBufferScale, NULL,
                          eData[0].frame_info, eData[0].sfb_nrg, NULL,
                          h_con, h_envChan[0], SBR_MONO, NULL, YSzShift);
    calculateSbrEnvelope (h_envChan[1]->sbrExtractEnvelope.YBuffer, NULL,
                          h_envChan[1]->sbrExtractEnvelope.YBufferScale, NULL,
                          eData[1].frame_info, eData[1].sfb_nrg, NULL,
                          h_con, h_envChan[1], SBR_MONO,NULL, YSzShift);
    calculateSbrEnvelope (h_envChan[0]->sbrExtractEnvelope.YBuffer, h_envChan[1]->sbrExtractEnvelope.YBuffer,
                          h_envChan[0]->sbrExtractEnvelope.YBufferScale, h_envChan[1]->sbrExtractEnvelope.YBufferScale,
                          eData[0].frame_info, eData[0].sfb_nrg_coupling, eData[1].sfb_nrg_coupling,
                          h_con, h_envChan[0], SBR_COUPLING, &fData->maxQuantError, YSzShift);
    break;
  }



  /*
    Noise floor quantisation and coding.
  */

  switch (stereoMode) {
  case SBR_MONO:
    sbrNoiseFloorLevelsQuantisation(eData[0].noise_level, eData[0].noiseFloor, 0);

    FDKsbrEnc_codeEnvelope(eData[0].noise_level, fData->res,
                           &h_envChan[0]->sbrCodeNoiseFloor,
                           h_envChan[0]->encEnvData.domain_vec_noise, 0,
                           (eData[0].frame_info->nEnvelopes > 1 ? 2 : 1), 0,
                           sbrBitstreamData->HeaderActive);

    break;
  case SBR_LEFT_RIGHT:
    sbrNoiseFloorLevelsQuantisation(eData[0].noise_level,eData[0].noiseFloor, 0);

    FDKsbrEnc_codeEnvelope (eData[0].noise_level, fData->res,
                  &h_envChan[0]->sbrCodeNoiseFloor,
                  h_envChan[0]->encEnvData.domain_vec_noise, 0,
                  (eData[0].frame_info->nEnvelopes > 1 ? 2 : 1), 0,
                  sbrBitstreamData->HeaderActive);

    sbrNoiseFloorLevelsQuantisation(eData[1].noise_level,eData[1].noiseFloor, 0);

    FDKsbrEnc_codeEnvelope (eData[1].noise_level, fData->res,
                  &h_envChan[1]->sbrCodeNoiseFloor,
                  h_envChan[1]->encEnvData.domain_vec_noise, 0,
                  (eData[1].frame_info->nEnvelopes > 1 ? 2 : 1), 0,
                  sbrBitstreamData->HeaderActive);

    break;

  case SBR_COUPLING:
    coupleNoiseFloor(eData[0].noiseFloor,eData[1].noiseFloor);

    sbrNoiseFloorLevelsQuantisation(eData[0].noise_level,eData[0].noiseFloor, 0);

    FDKsbrEnc_codeEnvelope (eData[0].noise_level, fData->res,
                  &h_envChan[0]->sbrCodeNoiseFloor,
                  h_envChan[0]->encEnvData.domain_vec_noise, 1,
                  (eData[0].frame_info->nEnvelopes > 1 ? 2 : 1), 0,
                  sbrBitstreamData->HeaderActive);

    sbrNoiseFloorLevelsQuantisation(eData[1].noise_level,eData[1].noiseFloor, 1);

    FDKsbrEnc_codeEnvelope (eData[1].noise_level, fData->res,
                  &h_envChan[1]->sbrCodeNoiseFloor,
                  h_envChan[1]->encEnvData.domain_vec_noise, 1,
                  (eData[1].frame_info->nEnvelopes > 1 ? 2 : 1), 1,
                  sbrBitstreamData->HeaderActive);

    break;
  case SBR_SWITCH_LRC:
    sbrNoiseFloorLevelsQuantisation(eData[0].noise_level,eData[0].noiseFloor, 0);
    sbrNoiseFloorLevelsQuantisation(eData[1].noise_level,eData[1].noiseFloor, 0);
    coupleNoiseFloor(eData[0].noiseFloor,eData[1].noiseFloor);
    sbrNoiseFloorLevelsQuantisation(eData[0].noise_level_coupling,eData[0].noiseFloor, 0);
    sbrNoiseFloorLevelsQuantisation(eData[1].noise_level_coupling,eData[1].noiseFloor, 1);
    break;
  }



  /*
    Encode envelope of current frame.
  */
  switch (stereoMode) {
  case SBR_MONO:
    sbrHeaderData->coupling = 0;
    h_envChan[0]->encEnvData.balance = 0;
    FDKsbrEnc_codeEnvelope (eData[0].sfb_nrg, eData[0].frame_info->freqRes,
                  &h_envChan[0]->sbrCodeEnvelope,
                  h_envChan[0]->encEnvData.domain_vec,
                  sbrHeaderData->coupling,
                  eData[0].frame_info->nEnvelopes, 0,
                  sbrBitstreamData->HeaderActive);
    break;
  case SBR_LEFT_RIGHT:
    sbrHeaderData->coupling = 0;

    h_envChan[0]->encEnvData.balance = 0;
    h_envChan[1]->encEnvData.balance = 0;


    FDKsbrEnc_codeEnvelope (eData[0].sfb_nrg, eData[0].frame_info->freqRes,
                  &h_envChan[0]->sbrCodeEnvelope,
                  h_envChan[0]->encEnvData.domain_vec,
                  sbrHeaderData->coupling,
                  eData[0].frame_info->nEnvelopes, 0,
                  sbrBitstreamData->HeaderActive);
    FDKsbrEnc_codeEnvelope (eData[1].sfb_nrg, eData[1].frame_info->freqRes,
                  &h_envChan[1]->sbrCodeEnvelope,
                  h_envChan[1]->encEnvData.domain_vec,
                  sbrHeaderData->coupling,
                  eData[1].frame_info->nEnvelopes, 0,
                  sbrBitstreamData->HeaderActive);
    break;
  case SBR_COUPLING:
    sbrHeaderData->coupling = 1;
    h_envChan[0]->encEnvData.balance = 0;
    h_envChan[1]->encEnvData.balance = 1;

    FDKsbrEnc_codeEnvelope (eData[0].sfb_nrg, eData[0].frame_info->freqRes,
                  &h_envChan[0]->sbrCodeEnvelope,
                  h_envChan[0]->encEnvData.domain_vec,
                  sbrHeaderData->coupling,
                  eData[0].frame_info->nEnvelopes, 0,
                  sbrBitstreamData->HeaderActive);
    FDKsbrEnc_codeEnvelope (eData[1].sfb_nrg, eData[1].frame_info->freqRes,
                  &h_envChan[1]->sbrCodeEnvelope,
                  h_envChan[1]->encEnvData.domain_vec,
                  sbrHeaderData->coupling,
                  eData[1].frame_info->nEnvelopes, 1,
                  sbrBitstreamData->HeaderActive);
    break;
  case SBR_SWITCH_LRC:
    {
      INT payloadbitsLR;
      INT payloadbitsCOUPLING;

      SCHAR sfbNrgPrevTemp[MAX_NUM_CHANNELS][MAX_FREQ_COEFFS];
      SCHAR noisePrevTemp[MAX_NUM_CHANNELS][MAX_NUM_NOISE_COEFFS];
      INT upDateNrgTemp[MAX_NUM_CHANNELS];
      INT upDateNoiseTemp[MAX_NUM_CHANNELS];
      INT domainVecTemp[MAX_NUM_CHANNELS][MAX_ENVELOPES];
      INT domainVecNoiseTemp[MAX_NUM_CHANNELS][MAX_ENVELOPES];

      INT tempFlagRight = 0;
      INT tempFlagLeft = 0;

      /*
         Store previous values, in order to be able to "undo" what is being done.
      */

      for(ch = 0; ch < nChannels;ch++){
        FDKmemcpy (sfbNrgPrevTemp[ch], h_envChan[ch]->sbrCodeEnvelope.sfb_nrg_prev,
              MAX_FREQ_COEFFS * sizeof (SCHAR));

        FDKmemcpy (noisePrevTemp[ch], h_envChan[ch]->sbrCodeNoiseFloor.sfb_nrg_prev,
              MAX_NUM_NOISE_COEFFS * sizeof (SCHAR));

        upDateNrgTemp[ch] = h_envChan[ch]->sbrCodeEnvelope.upDate;
        upDateNoiseTemp[ch] = h_envChan[ch]->sbrCodeNoiseFloor.upDate;

        /*
          forbid time coding in the first envelope in case of a different
          previous stereomode
        */
        if(sbrHeaderData->prev_coupling){
          h_envChan[ch]->sbrCodeEnvelope.upDate = 0;
          h_envChan[ch]->sbrCodeNoiseFloor.upDate = 0;
        }
      } /* ch */


      /*
         Code ordinary Left/Right stereo
      */
      FDKsbrEnc_codeEnvelope (eData[0].sfb_nrg, eData[0].frame_info->freqRes,
                    &h_envChan[0]->sbrCodeEnvelope,
                    h_envChan[0]->encEnvData.domain_vec, 0,
                    eData[0].frame_info->nEnvelopes, 0,
                    sbrBitstreamData->HeaderActive);
      FDKsbrEnc_codeEnvelope (eData[1].sfb_nrg, eData[1].frame_info->freqRes,
                    &h_envChan[1]->sbrCodeEnvelope,
                    h_envChan[1]->encEnvData.domain_vec, 0,
                    eData[1].frame_info->nEnvelopes, 0,
                    sbrBitstreamData->HeaderActive);

      c = 0;
      for (i = 0; i < eData[0].nEnvelopes; i++) {
        for (j = 0; j < h_envChan[0]->encEnvData.noScfBands[i]; j++)
          {
            h_envChan[0]->encEnvData.ienvelope[i][j] = eData[0].sfb_nrg[c];
            h_envChan[1]->encEnvData.ienvelope[i][j] = eData[1].sfb_nrg[c];
            c++;
          }
      }



      FDKsbrEnc_codeEnvelope (eData[0].noise_level, fData->res,
                    &h_envChan[0]->sbrCodeNoiseFloor,
                    h_envChan[0]->encEnvData.domain_vec_noise, 0,
                    (eData[0].frame_info->nEnvelopes > 1 ? 2 : 1), 0,
                    sbrBitstreamData->HeaderActive);


      for (i = 0; i < MAX_NUM_NOISE_VALUES; i++)
        h_envChan[0]->encEnvData.sbr_noise_levels[i] = eData[0].noise_level[i];


      FDKsbrEnc_codeEnvelope (eData[1].noise_level, fData->res,
                    &h_envChan[1]->sbrCodeNoiseFloor,
                    h_envChan[1]->encEnvData.domain_vec_noise, 0,
                    (eData[1].frame_info->nEnvelopes > 1 ? 2 : 1), 0,
                    sbrBitstreamData->HeaderActive);

      for (i = 0; i < MAX_NUM_NOISE_VALUES; i++)
        h_envChan[1]->encEnvData.sbr_noise_levels[i] = eData[1].noise_level[i];


      sbrHeaderData->coupling = 0;
      h_envChan[0]->encEnvData.balance = 0;
      h_envChan[1]->encEnvData.balance = 0;

      payloadbitsLR = FDKsbrEnc_CountSbrChannelPairElement (sbrHeaderData,
                                                  hParametricStereo,
                                                  sbrBitstreamData,
                                                  &h_envChan[0]->encEnvData,
                                                  &h_envChan[1]->encEnvData,
                                                  hCmonData,
                                                  h_con->sbrSyntaxFlags);

      /*
        swap saved stored with current values
      */
      for(ch = 0; ch < nChannels;ch++){
        INT   itmp;
        for(i=0;i<MAX_FREQ_COEFFS;i++){
          /*
            swap sfb energies
          */
          itmp =  h_envChan[ch]->sbrCodeEnvelope.sfb_nrg_prev[i];
          h_envChan[ch]->sbrCodeEnvelope.sfb_nrg_prev[i]=sfbNrgPrevTemp[ch][i];
          sfbNrgPrevTemp[ch][i]=itmp;
        }
        for(i=0;i<MAX_NUM_NOISE_COEFFS;i++){
          /*
            swap noise energies
          */
          itmp =  h_envChan[ch]->sbrCodeNoiseFloor.sfb_nrg_prev[i];
          h_envChan[ch]->sbrCodeNoiseFloor.sfb_nrg_prev[i]=noisePrevTemp[ch][i];
          noisePrevTemp[ch][i]=itmp;
       }
        /* swap update flags */
        itmp  = h_envChan[ch]->sbrCodeEnvelope.upDate;
        h_envChan[ch]->sbrCodeEnvelope.upDate=upDateNrgTemp[ch];
        upDateNrgTemp[ch] = itmp;

        itmp =  h_envChan[ch]->sbrCodeNoiseFloor.upDate;
        h_envChan[ch]->sbrCodeNoiseFloor.upDate=upDateNoiseTemp[ch];
        upDateNoiseTemp[ch]=itmp;

        /*
            save domain vecs
        */
        FDKmemcpy(domainVecTemp[ch],h_envChan[ch]->encEnvData.domain_vec,sizeof(INT)*MAX_ENVELOPES);
        FDKmemcpy(domainVecNoiseTemp[ch],h_envChan[ch]->encEnvData.domain_vec_noise,sizeof(INT)*MAX_ENVELOPES);

        /*
          forbid time coding in the first envelope in case of a different
          previous stereomode
        */

        if(!sbrHeaderData->prev_coupling){
          h_envChan[ch]->sbrCodeEnvelope.upDate = 0;
          h_envChan[ch]->sbrCodeNoiseFloor.upDate = 0;
        }
      } /* ch */


      /*
         Coupling
       */

      FDKsbrEnc_codeEnvelope (eData[0].sfb_nrg_coupling, eData[0].frame_info->freqRes,
                    &h_envChan[0]->sbrCodeEnvelope,
                    h_envChan[0]->encEnvData.domain_vec, 1,
                    eData[0].frame_info->nEnvelopes, 0,
                    sbrBitstreamData->HeaderActive);

      FDKsbrEnc_codeEnvelope (eData[1].sfb_nrg_coupling, eData[1].frame_info->freqRes,
                    &h_envChan[1]->sbrCodeEnvelope,
                    h_envChan[1]->encEnvData.domain_vec, 1,
                    eData[1].frame_info->nEnvelopes, 1,
                    sbrBitstreamData->HeaderActive);


      c = 0;
      for (i = 0; i < eData[0].nEnvelopes; i++) {
        for (j = 0; j < h_envChan[0]->encEnvData.noScfBands[i]; j++) {
          h_envChan[0]->encEnvData.ienvelope[i][j] = eData[0].sfb_nrg_coupling[c];
          h_envChan[1]->encEnvData.ienvelope[i][j] = eData[1].sfb_nrg_coupling[c];
          c++;
        }
      }

      FDKsbrEnc_codeEnvelope (eData[0].noise_level_coupling, fData->res,
                    &h_envChan[0]->sbrCodeNoiseFloor,
                    h_envChan[0]->encEnvData.domain_vec_noise, 1,
                    (eData[0].frame_info->nEnvelopes > 1 ? 2 : 1), 0,
                     sbrBitstreamData->HeaderActive);

      for (i = 0; i < MAX_NUM_NOISE_VALUES; i++)
        h_envChan[0]->encEnvData.sbr_noise_levels[i] = eData[0].noise_level_coupling[i];


      FDKsbrEnc_codeEnvelope (eData[1].noise_level_coupling, fData->res,
                    &h_envChan[1]->sbrCodeNoiseFloor,
                    h_envChan[1]->encEnvData.domain_vec_noise, 1,
                    (eData[1].frame_info->nEnvelopes > 1 ? 2 : 1), 1,
                    sbrBitstreamData->HeaderActive);

      for (i = 0; i < MAX_NUM_NOISE_VALUES; i++)
        h_envChan[1]->encEnvData.sbr_noise_levels[i] = eData[1].noise_level_coupling[i];

      sbrHeaderData->coupling = 1;

      h_envChan[0]->encEnvData.balance  = 0;
      h_envChan[1]->encEnvData.balance  = 1;

      tempFlagLeft  = h_envChan[0]->encEnvData.addHarmonicFlag;
      tempFlagRight = h_envChan[1]->encEnvData.addHarmonicFlag;

      payloadbitsCOUPLING =
        FDKsbrEnc_CountSbrChannelPairElement (sbrHeaderData,
                                    hParametricStereo,
                                    sbrBitstreamData,
                                    &h_envChan[0]->encEnvData,
                                    &h_envChan[1]->encEnvData,
                                    hCmonData,
                                    h_con->sbrSyntaxFlags);


      h_envChan[0]->encEnvData.addHarmonicFlag = tempFlagLeft;
      h_envChan[1]->encEnvData.addHarmonicFlag = tempFlagRight;

      if (payloadbitsCOUPLING < payloadbitsLR) {

          /*
            copy coded coupling envelope and noise data to l/r
          */
          for(ch = 0; ch < nChannels;ch++){
            SBR_ENV_TEMP_DATA *ed = &eData[ch];
            FDKmemcpy (ed->sfb_nrg, ed->sfb_nrg_coupling,
                  MAX_NUM_ENVELOPE_VALUES * sizeof (SCHAR));
            FDKmemcpy (ed->noise_level, ed->noise_level_coupling,
                  MAX_NUM_NOISE_VALUES * sizeof (SCHAR));
          }

          sbrHeaderData->coupling = 1;
          h_envChan[0]->encEnvData.balance  = 0;
          h_envChan[1]->encEnvData.balance  = 1;
      }
      else{
          /*
            restore saved l/r items
          */
          for(ch = 0; ch < nChannels;ch++){

            FDKmemcpy (h_envChan[ch]->sbrCodeEnvelope.sfb_nrg_prev,
                    sfbNrgPrevTemp[ch], MAX_FREQ_COEFFS * sizeof (SCHAR));

            h_envChan[ch]->sbrCodeEnvelope.upDate = upDateNrgTemp[ch];

            FDKmemcpy (h_envChan[ch]->sbrCodeNoiseFloor.sfb_nrg_prev,
                    noisePrevTemp[ch], MAX_NUM_NOISE_COEFFS * sizeof (SCHAR));

            FDKmemcpy (h_envChan[ch]->encEnvData.domain_vec,domainVecTemp[ch],sizeof(INT)*MAX_ENVELOPES);
            FDKmemcpy (h_envChan[ch]->encEnvData.domain_vec_noise,domainVecNoiseTemp[ch],sizeof(INT)*MAX_ENVELOPES);

            h_envChan[ch]->sbrCodeNoiseFloor.upDate = upDateNoiseTemp[ch];
          }

          sbrHeaderData->coupling = 0;
          h_envChan[0]->encEnvData.balance  = 0;
          h_envChan[1]->encEnvData.balance  = 0;
        }
    }
    break;
  } /* switch */


  /* tell the envelope encoders how long it has been, since we last sent
     a frame starting with a dF-coded envelope */
  if (stereoMode == SBR_MONO ) {
    if (h_envChan[0]->encEnvData.domain_vec[0] == TIME)
      h_envChan[0]->sbrCodeEnvelope.dF_edge_incr_fac++;
    else
      h_envChan[0]->sbrCodeEnvelope.dF_edge_incr_fac = 0;
  }
  else {
    if (h_envChan[0]->encEnvData.domain_vec[0] == TIME ||
        h_envChan[1]->encEnvData.domain_vec[0] == TIME) {
      h_envChan[0]->sbrCodeEnvelope.dF_edge_incr_fac++;
      h_envChan[1]->sbrCodeEnvelope.dF_edge_incr_fac++;
    }
    else {
      h_envChan[0]->sbrCodeEnvelope.dF_edge_incr_fac = 0;
      h_envChan[1]->sbrCodeEnvelope.dF_edge_incr_fac = 0;
    }
  }

  /*
    Send the encoded data to the bitstream
  */
  for(ch = 0; ch < nChannels;ch++){
    SBR_ENV_TEMP_DATA *ed = &eData[ch];
    c = 0;
    for (i = 0; i < ed->nEnvelopes; i++) {
      for (j = 0; j < h_envChan[ch]->encEnvData.noScfBands[i]; j++) {
        h_envChan[ch]->encEnvData.ienvelope[i][j] = ed->sfb_nrg[c];

        c++;
      }
    }
    for (i = 0; i < MAX_NUM_NOISE_VALUES; i++){
      h_envChan[ch]->encEnvData.sbr_noise_levels[i] = ed->noise_level[i];
    }
  }/* ch */


  /*
    Write bitstream
  */
  if (nChannels == 2) {
    FDKsbrEnc_WriteEnvChannelPairElement(sbrHeaderData,
                               hParametricStereo,
                               sbrBitstreamData,
                               &h_envChan[0]->encEnvData,
                               &h_envChan[1]->encEnvData,
                               hCmonData,
                               h_con->sbrSyntaxFlags);
  }
  else {
    FDKsbrEnc_WriteEnvSingleChannelElement(sbrHeaderData,
                                 hParametricStereo,
                                 sbrBitstreamData,
                                 &h_envChan[0]->encEnvData,
                                 hCmonData,
                                 h_con->sbrSyntaxFlags);
  }

  /*
   * Update buffers.
   */
  for (ch=0; ch<nChannels; ch++)
  {
      int YBufferLength = h_envChan[ch]->sbrExtractEnvelope.no_cols >> h_envChan[ch]->sbrExtractEnvelope.YBufferSzShift;
      for (i = 0; i < h_envChan[ch]->sbrExtractEnvelope.YBufferWriteOffset; i++) {
         FDKmemcpy(h_envChan[ch]->sbrExtractEnvelope.YBuffer[i],
                   h_envChan[ch]->sbrExtractEnvelope.YBuffer[i + YBufferLength],
                   sizeof(FIXP_DBL)*QMF_CHANNELS);
      }
      h_envChan[ch]->sbrExtractEnvelope.YBufferScale[0] = h_envChan[ch]->sbrExtractEnvelope.YBufferScale[1];
  }

  sbrHeaderData->prev_coupling = sbrHeaderData->coupling;
}

/***************************************************************************/
/*!

  \brief  creates an envelope extractor handle

  \return error status

****************************************************************************/
INT
FDKsbrEnc_CreateExtractSbrEnvelope (HANDLE_SBR_EXTRACT_ENVELOPE  hSbrCut,
                                    INT channel
                                   ,INT chInEl
                                   ,UCHAR* dynamic_RAM
                         )
{
  INT i;
  FIXP_DBL* YBuffer = GetRam_Sbr_envYBuffer(channel);

  FDKmemclear(hSbrCut,sizeof(SBR_EXTRACT_ENVELOPE));
  hSbrCut->p_YBuffer = YBuffer;


  for (i = 0; i < (QMF_MAX_TIME_SLOTS>>1); i++) {
    hSbrCut->YBuffer[i] = YBuffer + (i*QMF_CHANNELS);
  }
  FIXP_DBL *YBufferDyn = GetRam_Sbr_envYBuffer(chInEl, dynamic_RAM);
  INT n=0;
  for (; i < QMF_MAX_TIME_SLOTS; i++,n++) {
    hSbrCut->YBuffer[i] = YBufferDyn + (n*QMF_CHANNELS);
  }

  FIXP_DBL* rBuffer = GetRam_Sbr_envRBuffer(0, dynamic_RAM);
  FIXP_DBL* iBuffer = GetRam_Sbr_envIBuffer(0, dynamic_RAM);

  for (i = 0; i < QMF_MAX_TIME_SLOTS; i++) {
    hSbrCut->rBuffer[i] = rBuffer + (i*QMF_CHANNELS);
    hSbrCut->iBuffer[i] = iBuffer + (i*QMF_CHANNELS);
  }

  return 0;
}


/***************************************************************************/
/*!

  \brief  Initialize an envelope extractor instance.

  \return error status

****************************************************************************/
INT
FDKsbrEnc_InitExtractSbrEnvelope (HANDLE_SBR_EXTRACT_ENVELOPE  hSbrCut,
                                  int no_cols,
                                  int no_rows,
                                  int start_index,
                                  int time_slots,
                                  int time_step,
                                  int tran_off,
                                  ULONG statesInitFlag
                                 ,int chInEl
                                 ,UCHAR* dynamic_RAM
                                 ,UINT sbrSyntaxFlags
                                  )
{
  int YBufferLength, rBufferLength;
  int i;

  if (sbrSyntaxFlags & SBR_SYNTAX_LOW_DELAY) {
    int off = TRANSIENT_OFFSET_LD;
#ifndef FULL_DELAY
    hSbrCut->YBufferWriteOffset = (no_cols>>1)+off*time_step;
#else
    hSbrCut->YBufferWriteOffset = no_cols+off*time_step;
#endif
  } else
  {
    hSbrCut->YBufferWriteOffset = tran_off*time_step;
  }
  hSbrCut->rBufferReadOffset  = 0;


  YBufferLength = hSbrCut->YBufferWriteOffset + no_cols;
  rBufferLength = no_cols;

  hSbrCut->pre_transient_info[0] = 0;
  hSbrCut->pre_transient_info[1] = 0;


  hSbrCut->no_cols = no_cols;
  hSbrCut->no_rows = no_rows;
  hSbrCut->start_index = start_index;

  hSbrCut->time_slots = time_slots;
  hSbrCut->time_step = time_step;

  FDK_ASSERT(no_rows        <=   QMF_CHANNELS);

  /* Use half the Energy values if time step is 2 or greater */
  if (time_step >= 2)
    hSbrCut->YBufferSzShift = 1;
  else
    hSbrCut->YBufferSzShift = 0;

  YBufferLength               >>= hSbrCut->YBufferSzShift;
  hSbrCut->YBufferWriteOffset >>= hSbrCut->YBufferSzShift;

  FDK_ASSERT(YBufferLength<=QMF_MAX_TIME_SLOTS);

  FIXP_DBL *YBufferDyn = GetRam_Sbr_envYBuffer(chInEl, dynamic_RAM);
  INT n=0;
  for (i=(QMF_MAX_TIME_SLOTS>>1); i < QMF_MAX_TIME_SLOTS; i++,n++) {
    hSbrCut->YBuffer[i] = YBufferDyn + (n*QMF_CHANNELS);
  }

  if(statesInitFlag) {
    for (i=0; i<YBufferLength; i++) {
      FDKmemclear( hSbrCut->YBuffer[i],QMF_CHANNELS*sizeof(FIXP_DBL));
    }
  }

  for (i = 0; i < rBufferLength; i++) {
    FDKmemclear( hSbrCut->rBuffer[i],QMF_CHANNELS*sizeof(FIXP_DBL));
    FDKmemclear( hSbrCut->iBuffer[i],QMF_CHANNELS*sizeof(FIXP_DBL));
  }

  FDKmemclear (hSbrCut->envelopeCompensation,sizeof(UCHAR)*MAX_FREQ_COEFFS);

  if(statesInitFlag) {
    hSbrCut->YBufferScale[0] = hSbrCut->YBufferScale[1] = FRACT_BITS-1;
  }

  return (0);
}




/***************************************************************************/
/*!

  \brief  deinitializes an envelope extractor handle

  \return void

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

void
FDKsbrEnc_deleteExtractSbrEnvelope (HANDLE_SBR_EXTRACT_ENVELOPE hSbrCut)
{

  if (hSbrCut) {
    FreeRam_Sbr_envYBuffer(&hSbrCut->p_YBuffer);
  }
}

INT
FDKsbrEnc_GetEnvEstDelay(HANDLE_SBR_EXTRACT_ENVELOPE hSbr)
{
  return hSbr->no_rows*((hSbr->YBufferWriteOffset)*2     /* mult 2 because nrg's are grouped half */
                        - hSbr->rBufferReadOffset );       /* in reference hold half spec and calc nrg's on overlapped spec */

}




