/***************************  Fraunhofer IIS FDK Tools  ***********************

                        (C) Copyright Fraunhofer IIS (2004)
                               All Rights Reserved

    Please be advised that this software and/or program delivery is
    Confidential Information of Fraunhofer and subject to and covered by the

    Fraunhofer IIS Software Evaluation Agreement
    between Google Inc. and  Fraunhofer
    effective and in full force since March 1, 2012.

    You may use this software and/or program only under the terms and
    conditions described in the above mentioned Fraunhofer IIS Software
    Evaluation Agreement. Any other and/or further use requires a separate agreement.


   $Id$
   Author(s): Andreas Ehret
   Description: SBR encoder top level processing.

   This software and/or program is protected by copyright law and international
   treaties. Any reproduction or distribution of this software and/or program,
   or any portion of it, may result in severe civil and criminal penalties, and
   will be prosecuted to the maximum extent possible under law.

******************************************************************************/
#include "sbr_encoder.h"

#include "sbr_ram.h"
#include "sbr_rom.h"
#include "sbrenc_freq_sca.h"
#include "env_bit.h"
#include "cmondata.h"
#include "sbr_misc.h"
#include "sbr.h"
#include "qmf.h"

#include "ps_main.h"

#define SBRENCODER_LIB_VL0 3
#define SBRENCODER_LIB_VL1 2
#define SBRENCODER_LIB_VL2 0



/***************************************************************************/
/*
 * SBR Delay balancing definitions.
 */

/*
      input buffer (1ch)

      |------------ 1537   -------------|-----|---------- 2048 -------------|
           (core2sbr delay     )          ds     (read, core and ds area)
*/

#define DOWN_SMPL_FAC        (2)

#define SFL(fl)            (fl*DOWN_SMPL_FAC)   /* SBR frame length (hardcoded to downsample factor of 2) */
#define STS(fl)            (SFL(fl)/64)  /* SBR Time Slots */

#define DELAY_QMF_ANA      (640 - 64)    /* Full bandwidth */
#define DELAY_QMF_ANAELD   (32)
#define DELAY_HYB_ANA      (10*64)       /* + 0.5 */
#define DELAY_HYB_SYN      (6*64 - 32)
#define DELAY_QMF_SYNELD   (32)
#define DELAY_DEC_QMF      (6*64)        /* Decoder QMF overlap */
#define DELAY_QMF_SYN      (2)           /* NO_POLY/2 */
#define DELAY_QMF_DS       (32)          /* QMF synthesis for downsampled time signal */

/* Delay in QMF paths */
#define DELAY_SBR(fl)      (DELAY_QMF_ANA + (64*STS(fl)-1) + DELAY_QMF_SYN)
#define DELAY_PS(fl)       (DELAY_QMF_ANA + DELAY_HYB_ANA + DELAY_DEC_QMF + (64*STS(fl)-1) + DELAY_HYB_SYN + DELAY_QMF_SYN)
#define DELAY_ELDSBR(fl)   (DELAY_QMF_ANAELD + (((fl)+((fl)/2))*2 - 1) + DELAY_QMF_SYNELD)

/* Delay differences for SBR and SBR+PS */
    #define MAX_DS_FILTER_DELAY (34)                                      /* the additional max downsampler filter delay (source fs) */
#define DELAY_AAC2SBR(fl)      ((/*RESAMPLER +*/ /*(DELAY_AAC(fl)*2) + */ DELAY_QMF_ANA + DELAY_DEC_QMF + DELAY_QMF_SYN) - DELAY_SBR(fl)) /* 1537 */
#define DELAY_ELD2SBR(fl)      ((/*RESAMPLER +*/ /*(DELAY_ELD(fl)*2) + */ DELAY_QMF_ANAELD + DELAY_QMF_SYNELD) - DELAY_ELDSBR(fl))

#define DELAY_AAC2PS(fl)       ((DELAY_QMF_ANA + DELAY_QMF_DS + /*(DELAY_AAC(fl)*2)*/ + DELAY_QMF_ANA + DELAY_DEC_QMF + DELAY_HYB_SYN + DELAY_QMF_SYN) - DELAY_PS(fl)) /* 2048 - 463*2 */

/* Assumption: that the sample delay resulting of of DELAY_AAC2PS is always smaller than the sample delay implied by DELAY_AAC2SBR */
#define MAX_SAMPLE_DELAY       (DELAY_AAC2SBR(1024) + MAX_DS_FILTER_DELAY)

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



#define INVALID_TABLE_IDX -1

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

  \brief  Selects the SBR tuning settings to use dependent on number of
          channels, bitrate, sample rate and core coder

  \return Index to the appropriate table

****************************************************************************/
#define DISTANCE_CEIL_VALUE 5000000
static INT
getSbrTuningTableIndex(UINT bitrate,    /*! the total bitrate in bits/sec */
                       UINT numChannels,/*! the number of channels for the core coder */
                       UINT sampleRate,  /*! the sampling rate of the core coder */
                       AUDIO_OBJECT_TYPE core,
                       UINT *pBitRateClosest
                       )
{
  int i, paramSetTop, bitRateClosestLowerIndex=-1, bitRateClosestUpperIndex=-1, found = 0;
  UINT bitRateClosestUpper = 0, bitRateClosestLower=DISTANCE_CEIL_VALUE;

  FDK_ASSERT(SBRENC_TUNING_SIZE == sizeof(sbrTuningTable)/sizeof(sbrTuningTable[0]));

  if (core == AOT_ER_AAC_ELD) {
    paramSetTop = SBRENC_TUNING_SIZE;
    i = SBRENC_AACLC_TUNING_SIZE;
  } else {
    paramSetTop = SBRENC_AACLC_TUNING_SIZE;
    i = 0;
  }

  for (; i < paramSetTop ; i++) {
    if ( numChannels == sbrTuningTable [i].numChannels 
      && sampleRate == sbrTuningTable [i].sampleRate )
    {
      found = 1;
      if ((bitrate >= sbrTuningTable [i].bitrateFrom) &&
          (bitrate < sbrTuningTable [i].bitrateTo)) {
            bitRateClosestLower = bitrate;
            bitRateClosestUpper = bitrate;
            //FDKprintf("entry %d\n", i);
        return i ;
      } else {
        if ( sbrTuningTable [i].bitrateFrom > bitrate ) {
          if (sbrTuningTable [i].bitrateFrom < bitRateClosestLower) {
            bitRateClosestLower = sbrTuningTable [i].bitrateFrom;
            bitRateClosestLowerIndex = i;
          }
        }
        if ( sbrTuningTable [i].bitrateTo <= bitrate ) {
          if (sbrTuningTable [i].bitrateTo > bitRateClosestUpper) {
            bitRateClosestUpper = sbrTuningTable [i].bitrateTo-1;
            bitRateClosestUpperIndex = i;
          }
        }
      }
    }
  }

  if (pBitRateClosest != NULL)
  {
    /* Is there was at least one matching tuning entry found then pick the least distance bit rate */
    if (found)
    {
      int distanceUpper=DISTANCE_CEIL_VALUE, distanceLower=DISTANCE_CEIL_VALUE;
      if (bitRateClosestLowerIndex >= 0) {
        distanceLower = sbrTuningTable [bitRateClosestLowerIndex].bitrateFrom - bitrate;
      }
      if (bitRateClosestUpperIndex >= 0) {
        distanceUpper = bitrate - sbrTuningTable [bitRateClosestUpperIndex].bitrateTo;
      }
      if ( distanceUpper < distanceLower )
      {
        *pBitRateClosest = bitRateClosestUpper;
      } else {
        *pBitRateClosest = bitRateClosestLower;
      }    
    } else {
      *pBitRateClosest = 0;
    }
  }

  return INVALID_TABLE_IDX;
}

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

  \brief  Selects the PS tuning settings to use dependent on bitrate
  and core coder

  \return Index to the appropriate table

****************************************************************************/
static INT
getPsTuningTableIndex(UINT bitrate, UINT *pBitRateClosest){

  INT i, paramSets = sizeof (psTuningTable) / sizeof (psTuningTable [0]);
  int bitRateClosestLowerIndex=-1, bitRateClosestUpperIndex=-1;
  UINT bitRateClosestUpper = 0, bitRateClosestLower=DISTANCE_CEIL_VALUE;

  for (i = 0 ; i < paramSets ; i++)  {
    if ((bitrate >= psTuningTable [i].bitrateFrom) &&
        (bitrate < psTuningTable [i].bitrateTo)) {
      return i ;
    } else {
      if ( psTuningTable [i].bitrateFrom > bitrate ) {
        if (psTuningTable [i].bitrateFrom < bitRateClosestLower) {
          bitRateClosestLower = psTuningTable [i].bitrateFrom;
          bitRateClosestLowerIndex = i;
        }
      }
      if ( psTuningTable [i].bitrateTo <= bitrate ) {
        if (psTuningTable [i].bitrateTo > bitRateClosestUpper) {
          bitRateClosestUpper = psTuningTable [i].bitrateTo-1;
          bitRateClosestUpperIndex = i;
        }
      }
    }
  }

  if (pBitRateClosest != NULL)
  {
    int distanceUpper=DISTANCE_CEIL_VALUE, distanceLower=DISTANCE_CEIL_VALUE;
    if (bitRateClosestLowerIndex >= 0) {
      distanceLower = sbrTuningTable [bitRateClosestLowerIndex].bitrateFrom - bitrate;
    }
    if (bitRateClosestUpperIndex >= 0) {
      distanceUpper = bitrate - sbrTuningTable [bitRateClosestUpperIndex].bitrateTo;
    }
    if ( distanceUpper < distanceLower )
    {
      *pBitRateClosest = bitRateClosestUpper;
    } else {
      *pBitRateClosest = bitRateClosestLower;
    }
  }

  return INVALID_TABLE_IDX;
}


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

  \brief  tells us, if for the given coreCoder, bitrate, number of channels
          and input sampling rate an SBR setting is available. If yes, it
          tells us also the core sampling rate we would need to run with

  \return a flag indicating success: yes (1) or no (0)

****************************************************************************/
static UINT
FDKsbrEnc_IsSbrSettingAvail (UINT bitrate,          /*! the total bitrate in bits/sec */
                   UINT vbrMode,          /*! the vbr paramter, 0 means constant bitrate */
                   UINT numOutputChannels,/*! the number of channels for the core coder */
                   UINT sampleRateInput,   /*! the input sample rate [in Hz] */
                   AUDIO_OBJECT_TYPE core
                   )
{
  INT idx = INVALID_TABLE_IDX;
  UINT sampleRateCore;

  if (sampleRateInput < 16000)
    return 0;

  if (bitrate==0) {
    /* map vbr quality to bitrate */
    if (vbrMode < 30)
      bitrate = 24000;
    else if (vbrMode < 40)
      bitrate = 28000;
    else if (vbrMode < 60)
      bitrate = 32000;
    else if (vbrMode < 75)
      bitrate = 40000;
    else
      bitrate = 48000;
    bitrate *= numOutputChannels;
  }

  /* try DOWN_SMPL_FAC of the input sampling rate */
  sampleRateCore = sampleRateInput/DOWN_SMPL_FAC;
  idx = getSbrTuningTableIndex(bitrate, numOutputChannels, sampleRateCore, core, NULL);

  return (idx == INVALID_TABLE_IDX ? 0 : 1);
}


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

  \brief  Adjusts the SBR settings according to the chosen core coder
          settings which are accessible via config->codecSettings

  \return A flag indicating success: yes (1) or no (0)

****************************************************************************/
static UINT
FDKsbrEnc_AdjustSbrSettings (const sbrConfigurationPtr config, /*! output, modified */
                   UINT bitRate,             /*! the total bitrate in bits/sec */
                   UINT numChannels,         /*! the core coder number of channels */
                   UINT fsCore,              /*! the core coder sampling rate in Hz */
                   UINT transFac,            /*! the short block to long block ratio */
                   UINT standardBitrate,     /*! the standard bitrate per channel in bits/sec */
                   UINT vbrMode,             /*! the vbr paramter, 0 poor quality .. 100 high quality*/
                   UINT useSpeechConfig,     /*!< adapt tuning parameters for speech ? */
                   UINT lcsMode,             /*! the low complexity stereo mode */
                   UINT bParametricStereo,   /*!< use parametric stereo */
                   AUDIO_OBJECT_TYPE core)   /* Core audio codec object type */
{
  INT idx = INVALID_TABLE_IDX;
  UINT sampleRate;

  /* set the codec settings */
  config->codecSettings.bitRate         = bitRate;
  config->codecSettings.nChannels       = numChannels;
  config->codecSettings.sampleFreq      = fsCore;
  config->codecSettings.transFac        = transFac;
  config->codecSettings.standardBitrate = standardBitrate;
  sampleRate  = fsCore * DOWN_SMPL_FAC;

  if (bitRate==0) {
    /* map vbr quality to bitrate */
    if (vbrMode < 30)
      bitRate = 24000;
    else if (vbrMode < 40)
      bitRate = 28000;
    else if (vbrMode < 60)
      bitRate = 32000;
    else if (vbrMode < 75)
      bitRate = 40000;
    else
      bitRate = 48000;
    bitRate *= numChannels;
    /* fix to enable mono vbrMode<40 @ 44.1 of 48kHz */
    if (numChannels==1) {
      if (sampleRate==44100 || sampleRate==48000) {
        if (vbrMode<40) bitRate = 32000;
      }
    }
  }

  idx = getSbrTuningTableIndex(bitRate,numChannels,fsCore, core, NULL);

  if (idx != INVALID_TABLE_IDX) {
    config->startFreq       = sbrTuningTable[idx].startFreq ;
    config->stopFreq        = sbrTuningTable[idx].stopFreq ;
    if (useSpeechConfig) {
      config->startFreq     = sbrTuningTable[idx].startFreqSpeech;
      config->stopFreq      = sbrTuningTable[idx].stopFreqSpeech;
    }

    config->sbr_noise_bands = sbrTuningTable[idx].numNoiseBands ;
    if (core == AOT_ER_AAC_ELD)
      config->init_amp_res_FF = SBR_AMP_RES_1_5;
    config->noiseFloorOffset= sbrTuningTable[idx].noiseFloorOffset;

    config->ana_max_level   = sbrTuningTable[idx].noiseMaxLevel ;
    config->stereoMode      = sbrTuningTable[idx].stereoMode ;
    config->freqScale       = sbrTuningTable[idx].freqScale ;

    /* adjust usage of parametric coding dependent on bitrate and speech config flag */
    if (useSpeechConfig)
      config->parametricCoding  = 0;

    if (core == AOT_ER_AAC_ELD) {
      if (bitRate < 28000)
        config->init_amp_res_FF = SBR_AMP_RES_3_0;
      config->SendHeaderDataTime = -1;
    }

    if (numChannels == 1) {
      if (bitRate < 16000) {
        config->parametricCoding  = 0;
      }
    }
    else {
      if (bitRate < 20000) {
        config->parametricCoding  = 0;
      }
    }

    config->useSpeechConfig = useSpeechConfig;

    /* PS settings */
    config->bParametricStereo = bParametricStereo;

    return 1 ;
  }
  else {
    return 0 ;
  }
}

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

 functionname: FDKsbrEnc_InitializeSbrDefaults
 description:  initializes the SBR confifuration
 returns:      error status
 input:        - core codec type,
               - fac of SBR to core frame length,
               - core frame length
 output:       initialized SBR configuration

*****************************************************************************/
static UINT
FDKsbrEnc_InitializeSbrDefaults (sbrConfigurationPtr config,
                                 INT                 coreSbrFrameLenFac,
                                 UINT                codecGranuleLen)
{
    if ( (coreSbrFrameLenFac != 2) ||
         (codecGranuleLen*coreSbrFrameLenFac > QMF_CHANNELS*QMF_MAX_TIME_SLOTS) )
       return(1);

    config->SendHeaderDataTime     = 1000;
    config->useWaveCoding          = 0;
    config->crcSbr                 = 0;
    config->dynBwSupported         = 1;
    config->tran_thr               = 13000;
    config->parametricCoding       = 1;

    config->sbrFrameSize  = codecGranuleLen * coreSbrFrameLenFac;


    /* sbr default parameters */
    config->sbr_data_extra         = 0;
    config->amp_res                = SBR_AMP_RES_3_0 ;
    config->tran_fc                = 0 ;
    config->tran_det_mode          = 1 ;
    config->spread                 = 1 ;
    config->stat                   = 0 ;
    config->e                      = 1 ;
    config->deltaTAcrossFrames     = 1 ;
    config->dF_edge_1stEnv         = FL2FXCONST_DBL(0.3f) ;
    config->dF_edge_incr           = FL2FXCONST_DBL(0.3f) ;

    config->sbr_invf_mode   = INVF_SWITCHED;
    config->sbr_xpos_mode   = XPOS_LC;
    config->sbr_xpos_ctrl   = SBR_XPOS_CTRL_DEFAULT;
    config->sbr_xpos_level  = 0;
    config->useSaPan        = 0;
    config->dynBwEnabled    = 0;
    config->bDownSampledSbr = 0;


    /* the following parameters are overwritten by the FDKsbrEnc_AdjustSbrSettings() function since
       they are included in the tuning table */
    config->stereoMode             = SBR_SWITCH_LRC;
    config->ana_max_level          = 6;
    config->noiseFloorOffset       = 0;
    config->startFreq              = 5; /*  5.9 respectively  6.0 kHz at fs = 44.1/48 kHz */
    config->stopFreq               = 9; /* 16.2 respectively 16.8 kHz at fs = 44.1/48 kHz */


    /* header_extra_1 */
    config->freqScale       = SBR_FREQ_SCALE_DEFAULT;
    config->alterScale      = SBR_ALTER_SCALE_DEFAULT;
    config->sbr_noise_bands = SBR_NOISE_BANDS_DEFAULT;

    /* header_extra_2 */
    config->sbr_limiter_bands    = SBR_LIMITER_BANDS_DEFAULT;
    config->sbr_limiter_gains    = SBR_LIMITER_GAINS_DEFAULT;
    config->sbr_interpol_freq    = SBR_INTERPOL_FREQ_DEFAULT;
    config->sbr_smoothing_length = SBR_SMOOTHING_LENGTH_DEFAULT;

    return 1;
}


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

 functionname: DeleteEnvChannel
 description:  frees memory of one SBR channel
 returns:      -
 input:        handle of channel
 output:       released handle

*****************************************************************************/
static void
deleteEnvChannel (HANDLE_ENV_CHANNEL hEnvCut)
{
  if (hEnvCut) {

    /* deleteFrameInfoGenerator (&hEnvCut->SbrEnvFrame); */



    /* deleteSbrCodeEnvelope (&hEnvCut->sbrCodeEnvelope); */

    /* deleteSbrCodeEnvelope (&hEnvCut->sbrCodeNoiseFloor); */

    FDKsbrEnc_DeleteTonCorrParamExtr(&hEnvCut->TonCorr);

    FDKsbrEnc_deleteExtractSbrEnvelope (&hEnvCut->sbrExtractEnvelope);

    //FreeRam_EnvChannel(phEnvCut);
  }

}


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

 functionname: sbrEncoder_ChannelClose
 description:  close the channel coding handle
 returns:
 input:        phSbrChannel
 output:

*****************************************************************************/
static void
sbrEncoder_ChannelClose(HANDLE_SBR_CHANNEL hSbrChannel)
{
  if (hSbrChannel != NULL)
  {
    deleteEnvChannel (&hSbrChannel->hEnvChannel);
  }
}

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

 functionname: sbrEncoder_ElementClose
 description:  close the channel coding handle
 returns:
 input:        phSbrChannel
 output:

*****************************************************************************/
static void
sbrEncoder_ElementClose(HANDLE_SBR_ELEMENT *phSbrElement)
{
  HANDLE_SBR_ELEMENT hSbrElement = *phSbrElement;

  if (hSbrElement!=NULL) {
    if (hSbrElement->sbrConfigData.v_k_master)
      FreeRam_Sbr_v_k_master(&hSbrElement->sbrConfigData.v_k_master);
    if (hSbrElement->sbrConfigData.freqBandTable[LO])
      FreeRam_Sbr_freqBandTableLO(&hSbrElement->sbrConfigData.freqBandTable[LO]);
    if (hSbrElement->sbrConfigData.freqBandTable[HI])
      FreeRam_Sbr_freqBandTableHI(&hSbrElement->sbrConfigData.freqBandTable[HI]);

    FreeRam_SbrElement(phSbrElement);
  }
  return ;

}


void sbrEncoder_Close (HANDLE_SBR_ENCODER *phSbrEncoder)
{
  HANDLE_SBR_ENCODER hSbrEncoder = *phSbrEncoder;

  if (hSbrEncoder != NULL)
  {
    int el, ch;

    for (el=0; el<(6); el++)
    {
      if (hSbrEncoder->sbrElement[el]!=NULL) {
        sbrEncoder_ElementClose(&hSbrEncoder->sbrElement[el]);
      }
    }

    /* Close sbr Channels */
    for (ch=0; ch<(6); ch++)
    {
      if (hSbrEncoder->pSbrChannel[ch]) {
        sbrEncoder_ChannelClose(hSbrEncoder->pSbrChannel[ch]);
        FreeRam_SbrChannel(&hSbrEncoder->pSbrChannel[ch]);
      }

      if (hSbrEncoder->QmfAnalysis[ch].FilterStates)
        FreeRam_Sbr_QmfStatesAnalysis((FIXP_QAS**)&hSbrEncoder->QmfAnalysis[ch].FilterStates);


    }

    if (hSbrEncoder->hParametricStereo)
      PSEnc_Destroy(&hSbrEncoder->hParametricStereo);
    if (hSbrEncoder->qmfSynthesisPS.FilterStates)
      FreeRam_PsQmfStatesSynthesis((FIXP_DBL**)&hSbrEncoder->qmfSynthesisPS.FilterStates);

    /* Release Overlay */
    FreeRam_SbrDynamic_RAM((FIXP_DBL**)&hSbrEncoder->pSBRdynamic_RAM);


    FreeRam_SbrEncoder(phSbrEncoder);
  }

}

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

 functionname: updateFreqBandTable
 description:  updates vk_master
 returns:      -
 input:        config handle
 output:       error info

*****************************************************************************/
static INT updateFreqBandTable(HANDLE_SBR_CONFIG_DATA  sbrConfigData,
                               HANDLE_SBR_HEADER_DATA  sbrHeaderData,
                               INT noQmfChannels)
{
  INT k0, k2;

  if(FDKsbrEnc_FindStartAndStopBand(sbrConfigData->sampleFreq,
                                    noQmfChannels,
                                    sbrHeaderData->sbr_start_frequency,
                                    sbrHeaderData->sbr_stop_frequency,
                                    sbrHeaderData->sampleRateMode,
                                    &k0, &k2))
    return(1);


  if(FDKsbrEnc_UpdateFreqScale(sbrConfigData->v_k_master, &sbrConfigData->num_Master,
                     k0, k2, sbrHeaderData->freqScale,
                     sbrHeaderData->alterScale))
    return(1);


  sbrHeaderData->sbr_xover_band=0;


  if(FDKsbrEnc_UpdateHiRes(sbrConfigData->freqBandTable[HI],
                           &sbrConfigData->nSfb[HI],
                           sbrConfigData->v_k_master,
                           sbrConfigData->num_Master ,
                           &sbrHeaderData->sbr_xover_band,
                           sbrHeaderData->sampleRateMode,
                           noQmfChannels))
    return(1);


  FDKsbrEnc_UpdateLoRes(sbrConfigData->freqBandTable[LO],
                        &sbrConfigData->nSfb[LO],
                        sbrConfigData->freqBandTable[HI],
                        sbrConfigData->nSfb[HI]);

  sbrConfigData->xOverFreq = (sbrConfigData->freqBandTable[LOW_RES][0] * sbrConfigData->sampleFreq / noQmfChannels+1)>>1;

  return (0);
}


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

 functionname: resetEnvChannel
 description:  resets parameters and allocates memory
 returns:      error status
 input:
 output:       hEnv

*****************************************************************************/
static INT resetEnvChannel (HANDLE_SBR_CONFIG_DATA  sbrConfigData,
                            HANDLE_SBR_HEADER_DATA  sbrHeaderData,
                            HANDLE_ENV_CHANNEL      hEnv)
{
  /* note !!! hEnv->encEnvData.noOfnoisebands will be updated later in function FDKsbrEnc_extractSbrEnvelope !!!*/
  hEnv->TonCorr.sbrNoiseFloorEstimate.noiseBands =  sbrHeaderData->sbr_noise_bands;


  if(FDKsbrEnc_ResetTonCorrParamExtr(&hEnv->TonCorr,
                           sbrConfigData->xposCtrlSwitch,
                           sbrConfigData->freqBandTable[HI][0],
                           sbrConfigData->v_k_master,
                           sbrConfigData->num_Master,
                           sbrConfigData->sampleFreq,
                           sbrConfigData->freqBandTable,
                           sbrConfigData->nSfb,
                           sbrConfigData->noQmfBands))
    return(1);

  hEnv->sbrCodeNoiseFloor.nSfb[LO] = hEnv->TonCorr.sbrNoiseFloorEstimate.noNoiseBands;
  hEnv->sbrCodeNoiseFloor.nSfb[HI] = hEnv->TonCorr.sbrNoiseFloorEstimate.noNoiseBands;

  hEnv->sbrCodeEnvelope.nSfb[LO] = sbrConfigData->nSfb[LO];
  hEnv->sbrCodeEnvelope.nSfb[HI] = sbrConfigData->nSfb[HI];

  hEnv->encEnvData.noHarmonics = sbrConfigData->nSfb[HI];

  hEnv->sbrCodeEnvelope.upDate = 0;
  hEnv->sbrCodeNoiseFloor.upDate = 0;

  return (0);
}

/* ****************************** FDKsbrEnc_SbrGetXOverFreq ******************************/
/**
 * @fn
 * @brief       calculates the closest possible crossover frequency
 * @return      the crossover frequency SBR accepts
 *
 */
static INT
FDKsbrEnc_SbrGetXOverFreq(HANDLE_SBR_ELEMENT  hEnv,        /*!< handle to SBR encoder instance */
                          INT                 xoverFreq)   /*!< from core coder suggested crossover frequency */
{
  INT band;
  INT lastDiff, newDiff;
  INT cutoffSb;

  UCHAR *RESTRICT pVKMaster = hEnv->sbrConfigData.v_k_master;

  /* Check if there is a matching cutoff frequency in the master table */
  cutoffSb = (4*xoverFreq * hEnv->sbrConfigData.noQmfBands / hEnv->sbrConfigData.sampleFreq + 1)>>1;
  lastDiff = cutoffSb;
  for (band = 0; band < hEnv->sbrConfigData.num_Master; band++) {

    newDiff = fixp_abs((INT)pVKMaster[band] - cutoffSb);

    if(newDiff >= lastDiff) {
      band--;
      break;
    }

    lastDiff = newDiff;
  }

  return ((pVKMaster[band] * hEnv->sbrConfigData.sampleFreq/hEnv->sbrConfigData.noQmfBands+1)>>1);
}

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

 functionname: FDKsbrEnc_EnvEncodeFrame
 description: performs the sbr envelope calculation for one element
 returns:
 input:
 output:

*****************************************************************************/
INT
FDKsbrEnc_EnvEncodeFrame(HANDLE_SBR_ENCODER   hEnvEncoder,
                         int                  iElement,
                         INT_PCM             *samples,                 /*!< time samples, always interleaved */
                         UINT                 timeInStride,            /*!< time buffer channel interleaving stride */
                         UINT                *sbrDataBits,             /*!< Size of SBR payload  */
                         UCHAR               *sbrData,                 /*!< SBR payload  */
                         int                  clearOutput              /*!< Do not consider any input signal */
                        )
{
  HANDLE_SBR_ELEMENT hSbrElement = hEnvEncoder->sbrElement[iElement];
  FDK_CRCINFO  crcInfo;
  INT    crcReg;
  INT    ch;
  INT    band;
  INT    cutoffSb;
  INT    newXOver;

  if (hEnvEncoder == NULL)
    return -1;

  hSbrElement = hEnvEncoder->sbrElement[iElement];

  if (hSbrElement == NULL)
    return -1;


  /* header bitstream handling */
  HANDLE_SBR_BITSTREAM_DATA sbrBitstreamData = &hSbrElement->sbrBitstreamData;

  INT psHeaderActive = 0;
  sbrBitstreamData->HeaderActive = 0;

  /* Anticipate PS header because of internal PS bitstream delay in order to be in sync with SBR header. */
  if ( sbrBitstreamData->CountSendHeaderData==(sbrBitstreamData->NrSendHeaderData-1) )
  {
      psHeaderActive = 1;
  }

  /* Signal SBR header to be written into bitstream */
  if ( sbrBitstreamData->CountSendHeaderData==0  )
  {
     sbrBitstreamData->HeaderActive = 1;
  }

  /* Increment header interval counter */
  if (sbrBitstreamData->NrSendHeaderData == 0) {
    sbrBitstreamData->CountSendHeaderData = 1;
  }
  else {
    if (sbrBitstreamData->CountSendHeaderData >= 0) {
      sbrBitstreamData->CountSendHeaderData++;
      sbrBitstreamData->CountSendHeaderData %= sbrBitstreamData->NrSendHeaderData;
    }
  }

  if (hSbrElement->CmonData.dynBwEnabled ) {
    INT i;
    for ( i = 4; i > 0; i-- )
      hSbrElement->dynXOverFreqDelay[i] = hSbrElement->dynXOverFreqDelay[i-1];

    hSbrElement->dynXOverFreqDelay[0] = hSbrElement->CmonData.dynXOverFreqEnc;
    if (hSbrElement->dynXOverFreqDelay[1] > hSbrElement->dynXOverFreqDelay[2])
      newXOver = hSbrElement->dynXOverFreqDelay[2];
    else
      newXOver = hSbrElement->dynXOverFreqDelay[1];

    /* has the crossover frequency changed? */
    if ( hSbrElement->sbrConfigData.dynXOverFreq != newXOver ) {

      /* get corresponding master band */
      cutoffSb = ((4* newXOver * hSbrElement->sbrConfigData.noQmfBands
                  / hSbrElement->sbrConfigData.sampleFreq)+1)>>1;

      for ( band = 0; band < hSbrElement->sbrConfigData.num_Master; band++ ) {
        if ( cutoffSb == hSbrElement->sbrConfigData.v_k_master[band] )
          break;
      }
      FDK_ASSERT( band < hSbrElement->sbrConfigData.num_Master );

      hSbrElement->sbrConfigData.dynXOverFreq = newXOver;
      hSbrElement->sbrHeaderData.sbr_xover_band = band;
      hSbrElement->sbrBitstreamData.HeaderActive=1;
      psHeaderActive = 1; /* ps header is one frame delayed */

      /*
        update vk_master table
      */
      if(updateFreqBandTable(&hSbrElement->sbrConfigData,
                             &hSbrElement->sbrHeaderData,
                              hSbrElement->sbrConfigData.noQmfBands))
        return(1);


      /* reset SBR channels */
      INT nEnvCh = hSbrElement->sbrConfigData.nChannels;
      for ( ch = 0; ch < nEnvCh; ch++ ) {
        if(resetEnvChannel (&hSbrElement->sbrConfigData,
                            &hSbrElement->sbrHeaderData,
                            &hSbrElement->sbrChannel[ch]->hEnvChannel))
          return(1);

      }
    }
  }

  /*
    allocate space for dummy header and crc
  */
  crcReg = FDKsbrEnc_InitSbrBitstream(&hSbrElement->CmonData,
                                       hSbrElement->payloadDelayLine[hEnvEncoder->nBitstrDelay],
                                       MAX_PAYLOAD_SIZE*sizeof(UCHAR),
                                      &crcInfo,
                                       hSbrElement->sbrConfigData.sbrSyntaxFlags);

  INT error = noError;

  /* Temporal Envelope Data */
  SBR_FRAME_TEMP_DATA _fData;
  SBR_FRAME_TEMP_DATA *fData = &_fData;
  SBR_ENV_TEMP_DATA eData[MAX_NUM_CHANNELS];

  /* Init Temporal Envelope Data */
  {
    int i;

    FDKmemclear(&eData[0], sizeof(SBR_ENV_TEMP_DATA));
    FDKmemclear(&eData[1], sizeof(SBR_ENV_TEMP_DATA));
    FDKmemclear(fData, sizeof(SBR_FRAME_TEMP_DATA));

    for(i=0; i<MAX_NUM_NOISE_VALUES; i++)
      fData->res[i] = FREQ_RES_HIGH;
  }


  if (!clearOutput)
  {
    /*
     * Transform audio data into QMF domain
     */
    for(ch = 0; ch < hSbrElement->sbrConfigData.nChannels; ch++)
    {
      HANDLE_ENV_CHANNEL h_envChan = &hSbrElement->sbrChannel[ch]->hEnvChannel;
      HANDLE_SBR_EXTRACT_ENVELOPE sbrExtrEnv = &h_envChan->sbrExtractEnvelope;

      if(hSbrElement->elInfo.fParametricStereo == 0)
      {
        C_ALLOC_SCRATCH_START(qmfWorkBuffer, FIXP_DBL, QMF_CHANNELS*2);
        QMF_SCALE_FACTOR tmpScale;
        FIXP_DBL **pQmfReal, **pQmfImag;


        /* Obtain pointers to QMF buffers. */
        pQmfReal = sbrExtrEnv->rBuffer;
        pQmfImag = sbrExtrEnv->iBuffer;

        qmfAnalysisFiltering( hSbrElement->hQmfAnalysis[ch],
                               pQmfReal,
                               pQmfImag,
                              &tmpScale,
                               samples + hSbrElement->elInfo.ChannelIndex[ch],
                               timeInStride,
                               qmfWorkBuffer );

        C_ALLOC_SCRATCH_END(qmfWorkBuffer, FIXP_DBL, QMF_CHANNELS*2);

        h_envChan->qmfScale = tmpScale.lb_scale + 7;

      } /* fParametricStereo == 0 */


      /*
        Parametric Stereo processing
      */
      if (hSbrElement->elInfo.fParametricStereo)
      {

        /* Limit Parametric Stereo to one instance */
        FDK_ASSERT(ch == 0);


        if(error == noError){
          /* parametric stereo processing:
             - input:
               o left and right time domain samples
             - processing:
               o stereo qmf analysis
               o stereo hybrid analysis
               o ps parameter extraction
               o downmix + hybrid synthesis
             - output:
               o downmixed qmf data is written to sbrExtrEnv->rBuffer and sbrExtrEnv->iBuffer
          */
          SCHAR qmfScale;
          INT_PCM* pSamples[2] = {samples + hSbrElement->elInfo.ChannelIndex[0],samples + hSbrElement->elInfo.ChannelIndex[1]};
          error = FDKsbrEnc_PSEnc_ParametricStereoProcessing( hEnvEncoder->hParametricStereo,
                                                              pSamples,
                                                              timeInStride,
                                                              hSbrElement->hQmfAnalysis,
                                                              sbrExtrEnv->rBuffer,
                                                              sbrExtrEnv->iBuffer,
                                                              samples + hSbrElement->elInfo.ChannelIndex[ch],
                                                             &hEnvEncoder->qmfSynthesisPS,
                                                             &qmfScale,
                                                              psHeaderActive );
          if (noError != error)
          {
            error = handBack(error);
          }
          h_envChan->qmfScale = (int)qmfScale;
        }


      } /* if (hEnvEncoder->hParametricStereo) */

      /*

         Extract Envelope relevant things from QMF data

      */
      FDKsbrEnc_extractSbrEnvelope1(
                                &hSbrElement->sbrConfigData,
                                &hSbrElement->sbrHeaderData,
                                &hSbrElement->sbrBitstreamData,
                                 h_envChan,
                                &hSbrElement->CmonData,
                                &eData[ch],
                                 fData
                                 );

    } /* hEnvEncoder->sbrConfigData.nChannels */
 }

  /*
     Process Envelope relevant things and calculate envelope data and write payload
  */
  FDKsbrEnc_extractSbrEnvelope2(
                                &hSbrElement->sbrConfigData,
                                &hSbrElement->sbrHeaderData,
                                (hSbrElement->elInfo.fParametricStereo) ? hEnvEncoder->hParametricStereo : NULL,
                                &hSbrElement->sbrBitstreamData,
                                &hSbrElement->sbrChannel[0]->hEnvChannel,
                                &hSbrElement->sbrChannel[1]->hEnvChannel,
                                &hSbrElement->CmonData,
                                 eData,
                                 fData,
                                 clearOutput
                               );

  /*
    format payload, calculate crc
  */
  FDKsbrEnc_AssembleSbrBitstream(&hSbrElement->CmonData, &crcInfo, crcReg, hSbrElement->sbrConfigData.sbrSyntaxFlags);

  /*
    save new payload, set to zero length if greater than MAX_PAYLOAD_SIZE
  */
  hSbrElement->payloadDelayLineSize[hEnvEncoder->nBitstrDelay] = FDKgetValidBits(&hSbrElement->CmonData.sbrBitbuf);

  if(hSbrElement->payloadDelayLineSize[hEnvEncoder->nBitstrDelay] > (MAX_PAYLOAD_SIZE<<3))
    hSbrElement->payloadDelayLineSize[hEnvEncoder->nBitstrDelay]=0;

  /* While filling the Delay lines, sbrData is NULL */
  if (sbrData) {
    *sbrDataBits = hSbrElement->payloadDelayLineSize[0];
    FDKmemcpy(sbrData, hSbrElement->payloadDelayLine[0], (hSbrElement->payloadDelayLineSize[0]+7)>>3);


  }


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

  if (hEnvEncoder->fTimeDomainDownsampling)
  {
    int ch;
    int nChannels = hSbrElement->sbrConfigData.nChannels;

    for (ch=0; ch < nChannels; ch++)
    {
      INT nOutSamples;

      FDKaacEnc_Downsample(&hSbrElement->sbrChannel[ch]->downSampler,
                            samples + hSbrElement->elInfo.ChannelIndex[ch] + hEnvEncoder->bufferOffset,
                            hSbrElement->sbrConfigData.frameSize,
                            timeInStride,
                            samples + hSbrElement->elInfo.ChannelIndex[ch],
                           &nOutSamples,
                            hEnvEncoder->nChannels);
    }
  } /* downsample */


  return (0);
}

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

 functionname: createEnvChannel
 description:  initializes parameters and allocates memory
 returns:      error status
 input:
 output:       hEnv

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

static INT
createEnvChannel (HANDLE_ENV_CHANNEL     hEnv,
                  INT                    channel
                 ,UCHAR*                 dynamic_RAM
                 )
{
  FDKmemclear(hEnv,sizeof (struct ENV_CHANNEL));

  if ( FDKsbrEnc_CreateTonCorrParamExtr(&hEnv->TonCorr,
                                         channel) )
  {
    return(1);
  }

  if ( FDKsbrEnc_CreateExtractSbrEnvelope (&hEnv->sbrExtractEnvelope,
                               channel
                              ,/*chan*/0
                              ,dynamic_RAM
                              ) )
  {
    return(1);
  }

  return 0;
}

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

 functionname: initEnvChannel
 description:  initializes parameters
 returns:      error status
 input:
 output:

*****************************************************************************/
static INT
initEnvChannel (  HANDLE_SBR_CONFIG_DATA sbrConfigData,
                  HANDLE_SBR_HEADER_DATA sbrHeaderData,
                  HANDLE_ENV_CHANNEL     hEnv,
                  sbrConfigurationPtr    params,
                  ULONG                  statesInitFlag
                 ,INT                    chanInEl
                 ,UCHAR*                 dynamic_RAM
                 )
{
  int frameShift, tran_off=0;
  INT e;
  INT tran_fc;
  INT timeSlots, timeStep, startIndex;
  INT noiseBands[2] = { 3, 3 };

  e = 1 << params->e;

  FDK_ASSERT(params->e >= 0);

  hEnv->encEnvData.freq_res_fixfix = 1;
  hEnv->fLevelProtect = 0;

  hEnv->encEnvData.ldGrid = (sbrConfigData->sbrSyntaxFlags & SBR_SYNTAX_LOW_DELAY) ? 1 : 0;

  hEnv->encEnvData.sbr_xpos_mode = (XPOS_MODE)params->sbr_xpos_mode;

  if (hEnv->encEnvData.sbr_xpos_mode == XPOS_SWITCHED) {
    /*
       no other type than XPOS_MDCT or XPOS_SPEECH allowed,
       but enable switching
    */
    sbrConfigData->switchTransposers = TRUE;
    hEnv->encEnvData.sbr_xpos_mode = XPOS_MDCT;
  }
  else {
    sbrConfigData->switchTransposers = FALSE;
  }

  hEnv->encEnvData.sbr_xpos_ctrl = params->sbr_xpos_ctrl;


  /* extended data */
  if(params->parametricCoding) {
    hEnv->encEnvData.extended_data = 1;
  }
  else {
    hEnv->encEnvData.extended_data = 0;
  }

  hEnv->encEnvData.extension_size = 0;

  startIndex = QMF_FILTER_PROTOTYPE_SIZE - sbrConfigData->noQmfBands;

  switch (params->sbrFrameSize) {
  case 2304:
    timeSlots = 18;
    break;
  case 2048:
  case 1024:
    timeSlots = 16;
    break;
  case 1920:
  case 960:
    timeSlots = 15;
    break;
  case 1152:
    timeSlots = 9;
    break;
  default:
    return (1); /* Illegal frame size */
  }

  timeStep = sbrConfigData->noQmfSlots / timeSlots;

  if ( FDKsbrEnc_InitTonCorrParamExtr(params->sbrFrameSize,
                                     &hEnv->TonCorr,
                                      sbrConfigData,
                                      timeSlots,
                                      params->sbr_xpos_ctrl,
                                      params->ana_max_level,
                                      sbrHeaderData->sbr_noise_bands,
                                      params->noiseFloorOffset,
                                      params->useSpeechConfig) )
    return(1);

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

  noiseBands[0] = hEnv->encEnvData.noOfnoisebands;
  noiseBands[1] = hEnv->encEnvData.noOfnoisebands;

  hEnv->encEnvData.sbr_invf_mode = (INVF_MODE)params->sbr_invf_mode;

  if (hEnv->encEnvData.sbr_invf_mode == INVF_SWITCHED) {
    hEnv->encEnvData.sbr_invf_mode = INVF_MID_LEVEL;
    hEnv->TonCorr.switchInverseFilt = TRUE;
  }
  else {
    hEnv->TonCorr.switchInverseFilt = FALSE;
  }


  tran_fc  = params->tran_fc;

  if (tran_fc == 0)
    tran_fc = fixMin (5000, FDKsbrEnc_getSbrStartFreqRAW (sbrHeaderData->sbr_start_frequency,64,sbrConfigData->sampleFreq));


  tran_fc = (tran_fc*4*sbrConfigData->noQmfBands/sbrConfigData->sampleFreq + 1)>>1;

  if (sbrConfigData->sbrSyntaxFlags & SBR_SYNTAX_LOW_DELAY) {
    frameShift = LD_PRETRAN_OFF;
    tran_off = LD_PRETRAN_OFF + FRAME_MIDDLE_SLOT_512LD*timeStep;
  } else
  {
    frameShift = 0;
    switch (params->sbrFrameSize) {
      /* The factor of 2 is by definition. */
      case 2048: tran_off = 8 + FRAME_MIDDLE_SLOT_2048 * timeStep; break;
      case 1920: tran_off = 7 + FRAME_MIDDLE_SLOT_1920 * timeStep; break;
      default: return 1; break;
    }
  }
  if ( FDKsbrEnc_InitExtractSbrEnvelope (&hEnv->sbrExtractEnvelope,
                               sbrConfigData->noQmfSlots,
                               sbrConfigData->noQmfBands, startIndex,
                               timeSlots, timeStep, tran_off,
                               statesInitFlag
                              ,chanInEl
                              ,dynamic_RAM
                              ,sbrConfigData->sbrSyntaxFlags
                               ) )
    return(1);

  if(FDKsbrEnc_InitSbrCodeEnvelope (&hEnv->sbrCodeEnvelope,
                            sbrConfigData->nSfb,
                            params->deltaTAcrossFrames,
                            params->dF_edge_1stEnv,
                            params->dF_edge_incr))
    return(1);

  if(FDKsbrEnc_InitSbrCodeEnvelope (&hEnv->sbrCodeNoiseFloor,
                            noiseBands,
                            params->deltaTAcrossFrames,
                            0,0))
    return(1);

  sbrConfigData->initAmpResFF = params->init_amp_res_FF;

  if(FDKsbrEnc_InitSbrHuffmanTables (&hEnv->encEnvData,
                           &hEnv->sbrCodeEnvelope,
                           &hEnv->sbrCodeNoiseFloor,
                           sbrHeaderData->sbr_amp_res))
   return(1);

  FDKsbrEnc_initFrameInfoGenerator (&hEnv->SbrEnvFrame,
                            params->spread,
                            e,
                            params->stat,
                            timeSlots,
                            hEnv->encEnvData.freq_res_fixfix
                            ,hEnv->encEnvData.ldGrid
                            );

  if(FDKsbrEnc_InitSbrTransientDetector (&hEnv->sbrTransientDetector,
                                          sbrConfigData->frameSize,
                                          sbrConfigData->sampleFreq,
                                          params,
                                          tran_fc,
                                          sbrConfigData->noQmfSlots,
                                          sbrConfigData->noQmfBands,
                                          hEnv->sbrExtractEnvelope.YBufferWriteOffset,
                                          hEnv->sbrExtractEnvelope.YBufferSzShift,
                                          frameShift,
                                          tran_off
                                          ))
  return(1);


  sbrConfigData->xposCtrlSwitch = params->sbr_xpos_ctrl;

  hEnv->encEnvData.noHarmonics = sbrConfigData->nSfb[HI];
  hEnv->encEnvData.addHarmonicFlag = 0;

  return (0);
}

INT sbrEncoder_Open(
        HANDLE_SBR_ENCODER  *phSbrEncoder,
        INT                  nElements,
        INT                  nChannels,
        INT                  supportPS
        )
{
  INT i;
  INT errorStatus = 1;
  HANDLE_SBR_ENCODER hSbrEncoder = NULL;

  if (phSbrEncoder==NULL
     )
  {
    goto bail;
  }

  hSbrEncoder = GetRam_SbrEncoder();
  if (hSbrEncoder==NULL) {
    goto bail;
  }
  FDKmemclear(hSbrEncoder, sizeof(SBR_ENCODER));

      hSbrEncoder->pSBRdynamic_RAM = (UCHAR*)GetRam_SbrDynamic_RAM();
      hSbrEncoder->dynamicRam = hSbrEncoder->pSBRdynamic_RAM;


  for (i=0; i<nElements; i++) {
    hSbrEncoder->sbrElement[i] = GetRam_SbrElement(i);
    if (hSbrEncoder->sbrElement[i]==NULL) {
        goto bail;
    }
    FDKmemclear(hSbrEncoder->sbrElement[i], sizeof(SBR_ELEMENT));
    hSbrEncoder->sbrElement[i]->sbrConfigData.freqBandTable[LO] =  GetRam_Sbr_freqBandTableLO(i);
    hSbrEncoder->sbrElement[i]->sbrConfigData.freqBandTable[HI] =  GetRam_Sbr_freqBandTableHI(i);
    hSbrEncoder->sbrElement[i]->sbrConfigData.v_k_master        =  GetRam_Sbr_v_k_master(i);
    if ( (hSbrEncoder->sbrElement[i]->sbrConfigData.freqBandTable[LO]==NULL) ||
         (hSbrEncoder->sbrElement[i]->sbrConfigData.freqBandTable[HI]==NULL) ||
         (hSbrEncoder->sbrElement[i]->sbrConfigData.v_k_master==NULL) )
    {
        goto bail;
    }
  }

  for (i=0; i<nChannels; i++) {
    hSbrEncoder->pSbrChannel[i] = GetRam_SbrChannel(i);
    if (hSbrEncoder->pSbrChannel[i]==NULL) {
        goto bail;
    }

    if ( createEnvChannel(&hSbrEncoder->pSbrChannel[i]->hEnvChannel,
                           i
                          ,hSbrEncoder->dynamicRam
                           ) )
    {
        goto bail;
    }

  }

  for (i=0; i<fixMax(nChannels,(supportPS)?2:0); i++) {
    hSbrEncoder->QmfAnalysis[i].FilterStates = GetRam_Sbr_QmfStatesAnalysis(i);
    if (hSbrEncoder->QmfAnalysis[i].FilterStates==NULL) {
        goto bail;
    }
  }

  if (supportPS) {
    if (PSEnc_Create(&hSbrEncoder->hParametricStereo))
    {
      goto bail;
    }

    hSbrEncoder->qmfSynthesisPS.FilterStates = GetRam_PsQmfStatesSynthesis();
    if (hSbrEncoder->qmfSynthesisPS.FilterStates==NULL) {
      goto bail;
    }
  }  /* supportPS */

  *phSbrEncoder = hSbrEncoder;

  errorStatus = 0;
  return errorStatus;

bail:
  /* Close SBR encoder instance */
  sbrEncoder_Close(&hSbrEncoder);
  return errorStatus;
}

static
INT FDKsbrEnc_Reallocate(
                    HANDLE_SBR_ENCODER   hSbrEncoder,
                    SBR_ELEMENT_INFO     elInfo[(6)],
                    const INT            noElements)
{
  INT totalCh = 0;
  INT totalQmf = 0;
  INT coreEl;
  INT el=-1;

  hSbrEncoder->lfeChIdx = -1; /* default value, until lfe found */

  for (coreEl=0; coreEl<noElements; coreEl++)
  {
    /* SBR only handles SCE and CPE's */
    if (elInfo[coreEl].elType == ID_SCE || elInfo[coreEl].elType == ID_CPE) {
      el++;
    } else {
      if (elInfo[coreEl].elType == ID_LFE) {
          hSbrEncoder->lfeChIdx = elInfo[coreEl].ChannelIndex[0];
      }
      continue;
    }

    SBR_ELEMENT_INFO    *pelInfo = &elInfo[coreEl];
    HANDLE_SBR_ELEMENT  hSbrElement = hSbrEncoder->sbrElement[el];

    int ch;
    for ( ch = 0; ch < pelInfo->nChannelsInEl; ch++ ) {
      hSbrElement->sbrChannel[ch] = hSbrEncoder->pSbrChannel[totalCh];
      totalCh++;
    }
    /* analysis QMF */
    for ( ch = 0; ch < ((pelInfo->fParametricStereo)?2:pelInfo->nChannelsInEl); ch++ ) {
      hSbrElement->elInfo.ChannelIndex[ch] = pelInfo->ChannelIndex[ch];
      hSbrElement->hQmfAnalysis[ch] = &hSbrEncoder->QmfAnalysis[totalQmf++];
    }

    /* Copy Element info */
    hSbrElement->elInfo.elType            = pelInfo->elType;
    hSbrElement->elInfo.instanceTag       = pelInfo->instanceTag;
    hSbrElement->elInfo.nChannelsInEl     = pelInfo->nChannelsInEl;
    hSbrElement->elInfo.fParametricStereo = pelInfo->fParametricStereo;
  } /* coreEl */

  return 0;
}



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

 functionname: FDKsbrEnc_EnvInit
 description:  initializes parameters
 returns:      error status
 input:
 output:       hEnv

*****************************************************************************/
static
INT FDKsbrEnc_EnvInit (
                       HANDLE_SBR_ELEMENT   hSbrElement,
                       sbrConfigurationPtr params,
                       INT      *coreBandWith,
                       AUDIO_OBJECT_TYPE aot,
                       int       nBitstrDelay,
                       int       nElement,
                       ULONG     statesInitFlag
                      ,UCHAR    *dynamic_RAM
                      )
{
  UCHAR    *bitstreamBuffer;
  int ch, i;

  if ((params->codecSettings.nChannels < 1) || (params->codecSettings.nChannels > MAX_NUM_CHANNELS)){
    return(1);
  }

  /* initialize the encoder handle  and structs*/
  bitstreamBuffer = hSbrElement->payloadDelayLine[nBitstrDelay];

  /* init and set syntax flags */
  hSbrElement->sbrConfigData.sbrSyntaxFlags = 0;

  switch (aot) {
  case AOT_DRM_MPEG_PS:
  case AOT_DRM_SBR:
    hSbrElement->sbrConfigData.sbrSyntaxFlags |= SBR_SYNTAX_SCALABLE;
    hSbrElement->sbrConfigData.sbrSyntaxFlags |= SBR_SYNTAX_DRM_CRC;
    hSbrElement->sbrConfigData.sbrSyntaxFlags |= SBR_SYNTAX_CRC;
    break;
  case AOT_ER_AAC_ELD:
    hSbrElement->sbrConfigData.sbrSyntaxFlags |= SBR_SYNTAX_LOW_DELAY;
    break;
  default:
    break;
  }
  if (params->crcSbr) {
    hSbrElement->sbrConfigData.sbrSyntaxFlags |= SBR_SYNTAX_CRC;
  }

  hSbrElement->sbrConfigData.noQmfBands = QMF_CHANNELS;
  hSbrElement->sbrConfigData.noQmfSlots = params->sbrFrameSize/hSbrElement->sbrConfigData.noQmfBands;

  FDKinitBitStream(&hSbrElement->CmonData.sbrBitbuf, bitstreamBuffer, MAX_PAYLOAD_SIZE*sizeof(UCHAR), 0, BS_WRITER);

  /*
    now initialize sbrConfigData, sbrHeaderData and sbrBitstreamData,
  */
  hSbrElement->sbrConfigData.nChannels = params->codecSettings.nChannels;

  if(params->codecSettings.nChannels == 2)
     hSbrElement->sbrConfigData.stereoMode = params->stereoMode;
  else
     hSbrElement->sbrConfigData.stereoMode = SBR_MONO;

  hSbrElement->sbrConfigData.frameSize   = params->sbrFrameSize;

  /* implicit rule for sampleRateMode */
  /* run in "multirate" mode where sbr fs is 2 * codec fs */
  hSbrElement->sbrHeaderData.sampleRateMode = DUAL_RATE;
  hSbrElement->sbrConfigData.sampleFreq = 2 * params->codecSettings.sampleFreq;

  hSbrElement->sbrBitstreamData.CountSendHeaderData = 0;
  if (params->SendHeaderDataTime > 0 ) {

    hSbrElement->sbrBitstreamData.NrSendHeaderData = (INT)(params->SendHeaderDataTime * hSbrElement->sbrConfigData.sampleFreq
                                               / (1000 * hSbrElement->sbrConfigData.frameSize));
    hSbrElement->sbrBitstreamData.NrSendHeaderData = fixMax(hSbrElement->sbrBitstreamData.NrSendHeaderData,1);
  }
  else {
   hSbrElement->sbrBitstreamData.NrSendHeaderData = 0;
  }

  hSbrElement->sbrHeaderData.sbr_data_extra = params->sbr_data_extra;
  hSbrElement->sbrBitstreamData.HeaderActive = 0;
  hSbrElement->sbrHeaderData.sbr_start_frequency = params->startFreq;
  hSbrElement->sbrHeaderData.sbr_stop_frequency  = params->stopFreq;
  hSbrElement->sbrHeaderData.sbr_xover_band = 0;
  hSbrElement->sbrHeaderData.sbr_lc_stereo_mode = 0;

  /* data_extra */
  if (params->sbr_xpos_ctrl!= SBR_XPOS_CTRL_DEFAULT)
     hSbrElement->sbrHeaderData.sbr_data_extra = 1;

  hSbrElement->sbrHeaderData.sbr_amp_res = (AMP_RES)params->amp_res;

  /* header_extra_1 */
  hSbrElement->sbrHeaderData.freqScale  = params->freqScale;
  hSbrElement->sbrHeaderData.alterScale = params->alterScale;
  hSbrElement->sbrHeaderData.sbr_noise_bands = params->sbr_noise_bands;
  hSbrElement->sbrHeaderData.header_extra_1 = 0;

  if ((params->freqScale != SBR_FREQ_SCALE_DEFAULT) ||
      (params->alterScale != SBR_ALTER_SCALE_DEFAULT) ||
      (params->sbr_noise_bands != SBR_NOISE_BANDS_DEFAULT))
  {
   hSbrElement->sbrHeaderData.header_extra_1 = 1;
  }

  /* header_extra_2 */
  hSbrElement->sbrHeaderData.sbr_limiter_bands = params->sbr_limiter_bands;
  hSbrElement->sbrHeaderData.sbr_limiter_gains = params->sbr_limiter_gains;

  if ((hSbrElement->sbrConfigData.sampleFreq > 48000) &&
      (hSbrElement->sbrHeaderData.sbr_start_frequency >= 9))
  {
    hSbrElement->sbrHeaderData.sbr_limiter_gains = SBR_LIMITER_GAINS_INFINITE;
  }

  hSbrElement->sbrHeaderData.sbr_interpol_freq = params->sbr_interpol_freq;
  hSbrElement->sbrHeaderData.sbr_smoothing_length = params->sbr_smoothing_length;
  hSbrElement->sbrHeaderData.header_extra_2 = 0;

  if ((params->sbr_limiter_bands != SBR_LIMITER_BANDS_DEFAULT) ||
      (params->sbr_limiter_gains != SBR_LIMITER_GAINS_DEFAULT) ||
      (params->sbr_interpol_freq != SBR_INTERPOL_FREQ_DEFAULT) ||
      (params->sbr_smoothing_length != SBR_SMOOTHING_LENGTH_DEFAULT))
  {
     hSbrElement->sbrHeaderData.header_extra_2 = 1;
  }

   /* other switches */
  hSbrElement->sbrConfigData.useWaveCoding             = params->useWaveCoding;
  hSbrElement->sbrConfigData.useParametricCoding       = params->parametricCoding;

  /* init freq band table */
  if(updateFreqBandTable(&hSbrElement->sbrConfigData,
                         &hSbrElement->sbrHeaderData,
                         hSbrElement->sbrConfigData.noQmfBands))
  {
    return(1);
  }

  /* now create envelope ext and QMF for each available channel */
  for ( ch = 0; ch < hSbrElement->sbrConfigData.nChannels; ch++ ) {

     if ( initEnvChannel(&hSbrElement->sbrConfigData,
                         &hSbrElement->sbrHeaderData,
                         &hSbrElement->sbrChannel[ch]->hEnvChannel,
                          params,
                          statesInitFlag
                        ,ch
                        ,dynamic_RAM
                         ) )
     {
       return(1);
     }


  } /* nChannels */

  /* reset and intialize analysis qmf */
  for ( ch = 0; ch < ((hSbrElement->elInfo.fParametricStereo)?2:hSbrElement->sbrConfigData.nChannels); ch++ )
  {
    int err;
    UINT qmfFlags = (hSbrElement->sbrConfigData.sbrSyntaxFlags & SBR_SYNTAX_LOW_DELAY) ? QMF_FLAG_CLDFB : 0;
    if (statesInitFlag)
      qmfFlags &= ~QMF_FLAG_KEEP_STATES;
    else
      qmfFlags |=  QMF_FLAG_KEEP_STATES;

    err = qmfInitAnalysisFilterBank( hSbrElement->hQmfAnalysis[ch],
                                     (FIXP_QAS*)hSbrElement->hQmfAnalysis[ch]->FilterStates,
                                     hSbrElement->sbrConfigData.noQmfSlots,
                                     hSbrElement->sbrConfigData.noQmfBands,
                                     hSbrElement->sbrConfigData.noQmfBands,
                                     hSbrElement->sbrConfigData.noQmfBands,
                                     qmfFlags );
  }

  /*  */
  hSbrElement->CmonData.xOverFreq = hSbrElement->sbrConfigData.xOverFreq;
  hSbrElement->CmonData.dynBwEnabled = (params->dynBwSupported && params->dynBwEnabled);
  hSbrElement->CmonData.dynXOverFreqEnc = FDKsbrEnc_SbrGetXOverFreq( hSbrElement, hSbrElement->CmonData.xOverFreq);
  for ( i = 0; i < 5; i++ )
      hSbrElement->dynXOverFreqDelay[i] = hSbrElement->CmonData.dynXOverFreqEnc;
  hSbrElement->CmonData.sbrNumChannels  = hSbrElement->sbrConfigData.nChannels;
  hSbrElement->sbrConfigData.dynXOverFreq = hSbrElement->CmonData.xOverFreq;

  /* Update Bandwith to be passed to the core encoder */
  *coreBandWith = hSbrElement->CmonData.xOverFreq;

  return(0);
 }

INT sbrEncoder_GetInBufferSize(int noChannels)
{
  INT temp;

  temp = (1024*DOWN_SMPL_FAC);
  temp += 1024 + MAX_SAMPLE_DELAY;
  temp *= noChannels;
  temp *= sizeof(INT_PCM);
  return temp;
}

/*
 * Encode Dummy SBR payload frames to fill the delay lines.
 */
static
INT FDKsbrEnc_DelayCompensation (
                                  HANDLE_SBR_ENCODER hEnvEnc,
                                  INT_PCM *timeBuffer
                                 )
{
    int n, el;

    for (n=hEnvEnc->nBitstrDelay; n>0; n--)
    {
      for (el=0; el<hEnvEnc->noElements; el++)
      {
        if (FDKsbrEnc_EnvEncodeFrame(
                                     hEnvEnc,
                                     el,
                                     timeBuffer + hEnvEnc->downsampledOffset,
                                     hEnvEnc->sbrElement[el]->sbrConfigData.nChannels,
                                     NULL,
                                     NULL,
                                     1
                                    ))
          return -1;
          sbrEncoder_UpdateBuffers(hEnvEnc, timeBuffer);
      }
    }
	  return 0;
}

UINT sbrEncoder_LimitBitRate(UINT bitRate, UINT numChannels, UINT coreSampleRate, AUDIO_OBJECT_TYPE aot)
{
  UINT newBitRate;
  INT index;

  FDK_ASSERT(numChannels > 0 && numChannels <= 2);
  if (aot == AOT_PS) {
    if (numChannels == 2) {
      index = getPsTuningTableIndex(bitRate, &newBitRate);
      if (index == INVALID_TABLE_IDX) {
        bitRate = newBitRate;
      }
      /* Set numChannels to 1 because for PS we need a SBR SCE (mono) element. */
      numChannels = 1;
    } else {
      return 0;
    }
  }
  index = getSbrTuningTableIndex(bitRate, numChannels, coreSampleRate, aot, &newBitRate);
  if (index != INVALID_TABLE_IDX) {
    newBitRate = bitRate;
  }

  return newBitRate;
}


INT sbrEncoder_Init(
                     HANDLE_SBR_ENCODER hSbrEncoder,
                     SBR_ELEMENT_INFO elInfo[(6)],
                     int              noElements,
                     INT_PCM *inputBuffer,
                     INT   *coreBandwidth,
                     INT   *inputBufferOffset,
                     INT   *numChannels,
                     INT   *sampleRate,
                     INT   *frameLength,
                     AUDIO_OBJECT_TYPE *aot,
                     int *delay,
                     int  transformFactor,
                     ULONG  statesInitFlag
                    )
{
    HANDLE_ERROR_INFO errorInfo = noError;
    sbrConfiguration sbrConfig[(6)];
    INT error = 0;
    INT lowestBandwidth;
    /* Save input parameters */
    INT inputSampleRate = *sampleRate;
    int coreFrameLength = *frameLength;
    int inputBandWidth = *coreBandwidth;
    int inputChannels = *numChannels;

    int downsampledOffset = 0;
    int sbrOffset = 0;
    int downsamplerDelay = 0;
    int downsample = 0;
    int nBitstrDelay = 0;
    int lowestSbrStartFreq, lowestSbrStopFreq;
    int lowDelay = 0;
    int usePs = 0;

    /* check whether SBR setting is available for the current encoder configuration (bitrate, samplerate) */
    if ( (*aot==AOT_PS) || (*aot==AOT_MP2_PS) || (*aot==AOT_DABPLUS_PS) || (*aot==AOT_DRM_MPEG_PS) ) {
        usePs = 1;
    }
    if ( (*aot==AOT_ER_AAC_ELD) ) {
        lowDelay = 1;
    }
    else if ( (*aot==AOT_ER_AAC_LD) ) {
        error = 1;
        goto bail;
    }

    /* Parametric Stereo */
    if ( usePs ) {
      if ( *numChannels == 2 && noElements == 1) {
        /* Override Element type in case of Parametric stereo */
        elInfo[0].elType = ID_SCE;
        elInfo[0].fParametricStereo = 1;
        elInfo[0].nChannelsInEl = 1;
        /* core encoder gets downmixed mono signal */
        *numChannels  = 1;
      } else {
        switch (*aot) {
          case AOT_MP2_PS:
            *aot = AOT_MP2_SBR;
            break;
          case AOT_DABPLUS_PS:
            *aot = AOT_DABPLUS_SBR;
            break;
          case AOT_DRM_MPEG_PS:
            *aot = AOT_DRM_SBR;
            break;
          case AOT_PS:
          default:
            *aot = AOT_SBR;
        }
        usePs = 0;
      }
    } /* usePs */

    /* check whether SBR setting is available for the current encoder configuration (bitrate, samplerate) */
    {
      int delayDiff = 0;
      int el, coreEl;

      /* Check if every element config is feasible */
      for (coreEl=0; coreEl<noElements; coreEl++)
      {
        /* SBR only handles SCE and CPE's */
        if (elInfo[coreEl].elType != ID_SCE && elInfo[coreEl].elType != ID_CPE) {
          continue;
        }
        /* check if desired configuration is available */
        if ( !FDKsbrEnc_IsSbrSettingAvail (elInfo[coreEl].bitRate, 0, elInfo[coreEl].nChannelsInEl, inputSampleRate, *aot) )
        {
            /* otherwise - change to AAC-LC */
            switch (*aot) {
              case AOT_MP2_SBR:
              case AOT_MP2_PS:
                *aot = AOT_MP2_AAC_LC;
                break;
              case AOT_DABPLUS_SBR:
              case AOT_DABPLUS_PS:
                *aot = AOT_DABPLUS_AAC_LC;
                break;
              case AOT_DRM_SBR:
              case AOT_DRM_MPEG_PS:
                *aot = AOT_DRM_AAC;
                break;
              case AOT_ER_AAC_ELD:
                break;
              case AOT_SBR:
              case AOT_PS:
              default:
                *aot = AOT_AAC_LC;
            }
            error = 1;
            goto bail;
        }
      }

      *sampleRate /= DOWN_SMPL_FAC;

      /* Determine Delay balancing and new encoder delay */
      if (lowDelay) {
        downsample = 1;  /* activate downsampler */
        delayDiff = (*delay*DOWN_SMPL_FAC) + DELAY_ELD2SBR(coreFrameLength);
        *delay = DELAY_ELDSBR(coreFrameLength);
      }
      else if (usePs) {
        delayDiff = (*delay*DOWN_SMPL_FAC) + DELAY_AAC2PS(coreFrameLength);
        *delay = DELAY_PS(coreFrameLength);
      }
      else {
        downsample = 1;  /* activate downsampler */
        delayDiff = (*delay*DOWN_SMPL_FAC) + DELAY_AAC2SBR(coreFrameLength);
        *delay = DELAY_SBR(coreFrameLength);
      }

      /* Take care about downsampled data bound to the SBR path */
      if (!downsample && delayDiff > 0) {
        /*
         * We must tweak the balancing into a situation where the downsampled path
         * is the one to be delayed, because delaying the QMF domain input, also delays
         * the downsampled audio, counteracting to the purpose of delay balancing.
         */
        while ( delayDiff > 0 )
        {
          /* Encoder delay increases */
          *delay += coreFrameLength*DOWN_SMPL_FAC;
          /* Add one frame delay to SBR path */
          delayDiff -= coreFrameLength*DOWN_SMPL_FAC;
          nBitstrDelay += 1;
        }
      } else {
        *delay += fixp_abs(delayDiff);
      }

      if (delayDiff < 0) {
        /* Delay AAC data */
        delayDiff = -delayDiff;
        /* Multiply downsampled offset by AAC core channels. Divide by 2 because of half samplerate of downsampled data. */
        downsampledOffset = (delayDiff*(*numChannels))/DOWN_SMPL_FAC;
        sbrOffset = 0;
      } else {
        /* Delay SBR input */
        if ( delayDiff > (int)coreFrameLength*DOWN_SMPL_FAC )
        {
          /* Do bitstream frame-wise delay balancing if we have more than SBR framelength samples delay difference */
          delayDiff -= coreFrameLength*DOWN_SMPL_FAC;
          nBitstrDelay = 1;
        }
        /* Multiply input offset by input channels */
        sbrOffset = delayDiff*(*numChannels);
        downsampledOffset = 0;
      }

      hSbrEncoder->nBitstrDelay = nBitstrDelay;
      hSbrEncoder->nChannels = *numChannels;
      hSbrEncoder->frameSize = *frameLength*DOWN_SMPL_FAC;
      hSbrEncoder->fTimeDomainDownsampling = downsample;
      hSbrEncoder->estimateBitrate = 0;
      hSbrEncoder->inputDataDelay = 0;


      /* Open SBR elements */
      el = -1;
      lowestSbrStartFreq = lowestSbrStopFreq = 9999;
      lowestBandwidth = 99999;

      /* Loop through each core encoder element and get a matching SBR element config */
      for (coreEl=0; coreEl<noElements; coreEl++)
      {
        /* SBR only handles SCE and CPE's */
        if (elInfo[coreEl].elType == ID_SCE || elInfo[coreEl].elType == ID_CPE) {
          el++;
        } else {
          continue;
        }

        /* Set parametric Stereo Flag. */
        if (usePs) {
          elInfo[coreEl].fParametricStereo = 1;
        } else {
          elInfo[coreEl].fParametricStereo = 0;
        }

        /*
         * Init sbrConfig structure
         */
        FDKsbrEnc_InitializeSbrDefaults ( &sbrConfig[el],
                                           DOWN_SMPL_FAC,
                                           coreFrameLength);
        /*
         * Modify sbrConfig structure according to Element parameters
         */
        FDKsbrEnc_AdjustSbrSettings ( &sbrConfig[el],
                                       elInfo[coreEl].bitRate,
                                       elInfo[coreEl].nChannelsInEl,
                                      *sampleRate,
                                       transformFactor,
                                       24000,
                                       0,
                                       0,                                            /* useSpeechConfig */
                                       0,                                            /* lcsMode */
                                       usePs, /* bParametricStereo */
                                       *aot);

        /* Find common frequency border for all SBR elements */
        lowestSbrStartFreq = fixMin(lowestSbrStartFreq, sbrConfig[el].startFreq);
        lowestSbrStopFreq = fixMin(lowestSbrStopFreq, sbrConfig[el].stopFreq);


      } /* first element loop */

      /* Set element count (can be less than core encoder element count) */
      hSbrEncoder->noElements = el+1;

      FDKsbrEnc_Reallocate(hSbrEncoder,
                           elInfo,
                           noElements);

      for (el=0; el<hSbrEncoder->noElements; el++) {

        int bandwidth = *coreBandwidth;

        /* Use lowest common bandwidth */
        sbrConfig[el].startFreq = lowestSbrStartFreq;
        sbrConfig[el].stopFreq = lowestSbrStopFreq;

        /* initialize SBR element, and get core bandwidth */
        error = FDKsbrEnc_EnvInit(hSbrEncoder->sbrElement[el],
                                  &sbrConfig[el],
                                  &bandwidth,
                                  *aot,
                                   nBitstrDelay,
                                   el,
                                   statesInitFlag
                                  ,hSbrEncoder->dynamicRam
                                  );

        if (error != 0) {
          goto bail;
        }

        /* Get lowest core encoder bandwidth to be returned later. */
        lowestBandwidth = fixMin(lowestBandwidth, bandwidth);

      } /* second element loop */

      /* Initialize a downsampler for each channel in each SBR element */
      if (hSbrEncoder->fTimeDomainDownsampling)
      {
        for (el=0; el<hSbrEncoder->noElements; el++)
        {
          HANDLE_SBR_ELEMENT hSbrEl = hSbrEncoder->sbrElement[el];
          INT Wc, ch;

          /* Calculated required normalized cutoff frequency (Wc = 1.0 -> lowestBandwidth = inputSampleRate/2) */
          Wc = (2*lowestBandwidth)*1000 / inputSampleRate;

          for (ch=0; ch<hSbrEl->elInfo.nChannelsInEl; ch++)
          {
            FDKaacEnc_InitDownsampler (&hSbrEl->sbrChannel[ch]->downSampler, Wc, DOWN_SMPL_FAC);
          }

          FDK_ASSERT (hSbrEl->sbrChannel[0]->downSampler.delay <=MAX_DS_FILTER_DELAY && hSbrEl->sbrChannel[0]->downSampler.delay <=MAX_DS_FILTER_DELAY);
          downsamplerDelay = hSbrEl->sbrChannel[0]->downSampler.delay;
        } /* third element loop */

        /* lfe */
        FDKaacEnc_InitDownsampler (&hSbrEncoder->lfeDownSampler, 0, DOWN_SMPL_FAC);

        /* Add the resampler additional delay to get the final delay and buffer offset values. */
        if (sbrOffset > 0 || downsampledOffset <= ((downsamplerDelay * (*numChannels))/DOWN_SMPL_FAC)) {
          sbrOffset += (downsamplerDelay - downsampledOffset) * (*numChannels) ;
          *delay += downsamplerDelay - downsampledOffset;
          downsampledOffset = 0;
        } else {
          downsampledOffset -= (downsamplerDelay * (*numChannels))/DOWN_SMPL_FAC;
          sbrOffset = 0;
        }

        hSbrEncoder->inputDataDelay = downsamplerDelay;
      }


      /* Assign core encoder Bandwidth */
      *coreBandwidth = lowestBandwidth;

      /* Estimate sbr bitrate, 2.5 kBit/s per sbr channel */
      hSbrEncoder->estimateBitrate += 2500 * (*numChannels);

      /* initialize parametric stereo */
      if (usePs)
      {
        PSENC_CONFIG psEncConfig;
        FDK_ASSERT(hSbrEncoder->noElements == 1);
        INT psTuningTableIdx = getPsTuningTableIndex(elInfo[0].bitRate, NULL);

        psEncConfig.frameSize           = *frameLength; //sbrConfig.sbrFrameSize;
        psEncConfig.qmfFilterMode       = 0;
        psEncConfig.sbrPsDelay          = 0;

        /* tuning parameters */
        if (psTuningTableIdx  != INVALID_TABLE_IDX) {
          psEncConfig.nStereoBands           = psTuningTable[psTuningTableIdx].nStereoBands;
          psEncConfig.maxEnvelopes           = psTuningTable[psTuningTableIdx].nEnvelopes;
          psEncConfig.iidQuantErrorThreshold = (FIXP_DBL)psTuningTable[psTuningTableIdx].iidQuantErrorThreshold;

          /* calculation is not quite linear, increased number of envelopes causes more bits */
          /* assume avg. 50 bits per frame for 10 stereo bands / 1 envelope configuration */
          hSbrEncoder->estimateBitrate += ( (((*sampleRate) * 5 * psEncConfig.nStereoBands * psEncConfig.maxEnvelopes) / hSbrEncoder->frameSize));

        } else {
          error = ERROR(CDI, "Invalid ps tuning table index.");
          goto bail;
        }

        qmfInitSynthesisFilterBank(&hSbrEncoder->qmfSynthesisPS,
                                    (FIXP_DBL*)hSbrEncoder->qmfSynthesisPS.FilterStates,
                                    hSbrEncoder->sbrElement[0]->sbrConfigData.noQmfSlots,
                                    hSbrEncoder->sbrElement[0]->sbrConfigData.noQmfBands>>1,
                                    hSbrEncoder->sbrElement[0]->sbrConfigData.noQmfBands>>1,
                                    hSbrEncoder->sbrElement[0]->sbrConfigData.noQmfBands>>1,
                                    (statesInitFlag) ? 0 : QMF_FLAG_KEEP_STATES);

        if(errorInfo == noError){
          /* update delay */
          psEncConfig.sbrPsDelay = FDKsbrEnc_GetEnvEstDelay(&hSbrEncoder->sbrElement[0]->sbrChannel[0]->hEnvChannel.sbrExtractEnvelope);

          if(noError != (errorInfo = PSEnc_Init( hSbrEncoder->hParametricStereo,
                                                &psEncConfig,
                                                 hSbrEncoder->sbrElement[0]->sbrConfigData.noQmfSlots,
                                                 hSbrEncoder->sbrElement[0]->sbrConfigData.noQmfBands
                                                ,hSbrEncoder->dynamicRam
                                                 )))
          {
            errorInfo = handBack(errorInfo);
          }
        }
      }

      hSbrEncoder->downsampledOffset = downsampledOffset;
      hSbrEncoder->downmixSize = coreFrameLength*(*numChannels);
      hSbrEncoder->bufferOffset = sbrOffset;
      /* Delay Compensation: fill bitstream delay buffer with zero input signal */
      if ( hSbrEncoder->nBitstrDelay > 0 )
      {
        error = FDKsbrEnc_DelayCompensation (hSbrEncoder, inputBuffer);
        if (error != 0)
          goto bail;
      }

      /* Set Output frame length */
      *frameLength = coreFrameLength*DOWN_SMPL_FAC;
      /* Input buffer offset */
      *inputBufferOffset = fixMax(sbrOffset, downsampledOffset);


    }

    return error;

bail:
    /* Restore input settings */
    *sampleRate = inputSampleRate;
    *frameLength = coreFrameLength;
    *numChannels = inputChannels;
    *coreBandwidth = inputBandWidth;

    return error;
 }


INT
sbrEncoder_EncodeFrame(  HANDLE_SBR_ENCODER   hSbrEncoder,
                         INT_PCM             *samples,
                         UINT                 timeInStride,
                         UINT                 sbrDataBits[(6)],
                         UCHAR                sbrData[(6)][MAX_PAYLOAD_SIZE]
                        )
{
  INT error;
  int el;

  for (el=0; el<hSbrEncoder->noElements; el++)
  {
    if (hSbrEncoder->sbrElement[el] != NULL)
    {
      error = FDKsbrEnc_EnvEncodeFrame(
                                        hSbrEncoder,
                                        el,
                                        samples + hSbrEncoder->downsampledOffset,
                                        timeInStride,
                                       &sbrDataBits[el],
                                        sbrData[el],
                                        0
                                       );
      if (error)
        return error;
    }
  }

  if ( (hSbrEncoder->lfeChIdx!=-1) && (hSbrEncoder->fTimeDomainDownsampling) )
  {
      INT nOutSamples;

      FDKaacEnc_Downsample(&hSbrEncoder->lfeDownSampler,
                            samples + hSbrEncoder->downsampledOffset + hSbrEncoder->bufferOffset + hSbrEncoder->lfeChIdx,
                            hSbrEncoder->frameSize,
                            timeInStride,
                            samples + hSbrEncoder->downsampledOffset + hSbrEncoder->lfeChIdx,
                           &nOutSamples,
                            hSbrEncoder->nChannels);
  } /* lfe downsampler */

  return 0;
}


INT sbrEncoder_UpdateBuffers(
                            HANDLE_SBR_ENCODER hSbrEncoder,
                            INT_PCM *timeBuffer
                            )
 {
    if ( hSbrEncoder->downsampledOffset > 0 ) {
      /* Move delayed downsampled data */
      FDKmemcpy ( timeBuffer,
                  timeBuffer + hSbrEncoder->downmixSize,
                  sizeof(INT_PCM) * (hSbrEncoder->downsampledOffset) );
    } else {
      /* Move delayed input data */
      FDKmemcpy ( timeBuffer,
        timeBuffer + hSbrEncoder->nChannels * hSbrEncoder->frameSize,
                  sizeof(INT_PCM) * hSbrEncoder->bufferOffset );
    }
    if ( hSbrEncoder->nBitstrDelay > 0  )
    {
      int el;

      for (el=0; el<hSbrEncoder->noElements; el++)
      {
        FDKmemmove ( hSbrEncoder->sbrElement[el]->payloadDelayLine[0],
                     hSbrEncoder->sbrElement[el]->payloadDelayLine[1],
                     sizeof(UCHAR) * (hSbrEncoder->nBitstrDelay*MAX_PAYLOAD_SIZE) );

        FDKmemmove( &hSbrEncoder->sbrElement[el]->payloadDelayLineSize[0],
                    &hSbrEncoder->sbrElement[el]->payloadDelayLineSize[1],
                    sizeof(UINT) * (hSbrEncoder->nBitstrDelay) );
      }
    }
    return 0;
 }


INT sbrEncoder_GetEstimateBitrate(HANDLE_SBR_ENCODER hSbrEncoder)
{
  INT estimateBitrate = 0;

  if(hSbrEncoder) {
    estimateBitrate += hSbrEncoder->estimateBitrate;
  }

  return estimateBitrate;
}

INT sbrEncoder_GetInputDataDelay(HANDLE_SBR_ENCODER hSbrEncoder)
{
  INT delay = -1;

  if(hSbrEncoder) {
    delay = hSbrEncoder->inputDataDelay;
  }
  return delay;
}


INT sbrEncoder_GetLibInfo( LIB_INFO *info )
{
  int i;

  if (info == NULL) {
    return -1;
  }
  /* search for next free tab */
  for (i = 0; i < FDK_MODULE_LAST; i++) {
    if (info[i].module_id == FDK_NONE) break;
  }
  if (i == FDK_MODULE_LAST) {
    return -1;
  }
  info += i;

  info->module_id = FDK_SBRENC;
  info->version = LIB_VERSION(SBRENCODER_LIB_VL0, SBRENCODER_LIB_VL1, SBRENCODER_LIB_VL2);
  LIB_VERSION_STRING(info);
  info->build_date = __DATE__;
  info->build_time = __TIME__;
  info->title = "SBR Encoder";

  /* Set flags */
  info->flags = 0
    | CAPF_SBR_HQ
    | CAPF_SBR_PS_MPEG
    ;
  /* End of flags */

  return 0;
}