
/* -----------------------------------------------------------------------------------------------------------
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
----------------------------------------------------------------------------------------------------------- */

/*!
  \file
  \brief  SBR decoder frontend
  This module provides a frontend to the SBR decoder. The function openSBR() is called for
  initialization. The function sbrDecoder_Apply() is called for each frame. sbr_Apply() will call the
  required functions to decode the raw SBR data (provided by env_extr.cpp), to decode the envelope data and noise floor levels [decodeSbrData()],
  and to finally apply SBR to the current frame [sbr_dec()].

  \sa sbrDecoder_Apply(), \ref documentationOverview
*/

/*!
  \page documentationOverview Overview of important information resources and source code documentation

  The primary source code documentation is based on generated and cross-referenced HTML files using
  <a HREF="http://www.doxygen.org">doxygen</a>. As part of this documentation
  you can find more extensive descriptions about key concepts and algorithms at the following locations:

  <h2>Programming</h2>

  \li Buffer management: sbrDecoder_Apply() and sbr_dec()
  \li Internal scale factors to maximize SNR on fixed point processors: #QMF_SCALE_FACTOR
  \li Special mantissa-exponent format: Created in requantizeEnvelopeData() and used in calculateSbrEnvelope()

  <h2>Algorithmic details</h2>
  \li About the SBR data format: \ref SBR_HEADER_ELEMENT and \ref SBR_STANDARD_ELEMENT
  \li Details about the bitstream decoder: env_extr.cpp
  \li Details about the QMF filterbank and the provided polyphase implementation: qmf_dec.cpp
  \li Details about the transposer: lpp_tran.cpp
  \li Details about the envelope adjuster: env_calc.cpp

*/

#include "sbrdecoder.h"

#include "FDK_bitstream.h"

#include "sbrdec_freq_sca.h"
#include "env_extr.h"
#include "sbr_dec.h"
#include "env_dec.h"
#include "sbr_crc.h"
#include "sbr_ram.h"
#include "sbr_rom.h"
#include "lpp_tran.h"
#include "transcendent.h"


#include "sbrdec_drc.h"

#include "psbitdec.h"


/* Decoder library info */
#define SBRDECODER_LIB_VL0 2
#define SBRDECODER_LIB_VL1 2
#define SBRDECODER_LIB_VL2 3
#define SBRDECODER_LIB_TITLE "SBR Decoder"
#define SBRDECODER_LIB_BUILD_DATE __DATE__
#define SBRDECODER_LIB_BUILD_TIME __TIME__




static UCHAR getHeaderSlot( UCHAR currentSlot, UCHAR hdrSlotUsage[(1)+1] )
{
  UINT  occupied = 0;
  int   s;
  UCHAR slot = hdrSlotUsage[currentSlot];

  FDK_ASSERT((1)+1 < 32);

  for (s = 0; s < (1)+1; s++) {
    if ( (hdrSlotUsage[s] == slot)
      && (s != slot) ) {
      occupied = 1;
      break;
    }
  }

  if (occupied) {
    occupied = 0;

    for (s = 0; s < (1)+1; s++) {
      occupied |= 1 << hdrSlotUsage[s];
    }
    for (s = 0; s < (1)+1; s++) {
      if ( !(occupied & 0x1) ) {
        slot = s;
        break;
      }
      occupied >>= 1;
    }
  }

  return slot;
}

static void copySbrHeader( HANDLE_SBR_HEADER_DATA hDst, const HANDLE_SBR_HEADER_DATA hSrc )
{
  /* copy the whole header memory (including pointers) */
  FDKmemcpy( hDst, hSrc, sizeof(SBR_HEADER_DATA) );

  /* update pointers */
  hDst->freqBandData.freqBandTable[0]  = hDst->freqBandData.freqBandTableLo;
  hDst->freqBandData.freqBandTable[1] = hDst->freqBandData.freqBandTableHi;
}


/*!
  \brief Reset SBR decoder.

  Reset should only be called if SBR has been sucessfully detected by
  an appropriate checkForPayload() function.

  \return Error code.
*/
static
SBR_ERROR sbrDecoder_ResetElement (
        HANDLE_SBRDECODER    self,
        int                  sampleRateIn,
        int                  sampleRateOut,
        int                  samplesPerFrame,
        const MP4_ELEMENT_ID elementID,
        const int            elementIndex,
        const int            overlap
        )
{
  SBR_ERROR sbrError = SBRDEC_OK;
  HANDLE_SBR_HEADER_DATA hSbrHeader;
  UINT qmfFlags = 0;

  int i, synDownsampleFac;

  /* Check in/out samplerates */
  if ( sampleRateIn < 6400
    || sampleRateIn > 48000
     )
  {
    sbrError = SBRDEC_UNSUPPORTED_CONFIG;
    goto bail;
  }

  if ( sampleRateOut > 96000 )
  {
    sbrError = SBRDEC_UNSUPPORTED_CONFIG;
    goto bail;
  }

  /* Set QMF mode flags */
  if (self->flags & SBRDEC_LOW_POWER)
    qmfFlags |= QMF_FLAG_LP;

  if (self->coreCodec == AOT_ER_AAC_ELD) {
    if (self->flags & SBRDEC_LD_MPS_QMF) {
      qmfFlags |=  QMF_FLAG_MPSLDFB;
    } else {
      qmfFlags |=  QMF_FLAG_CLDFB;
    }
  }

  /* Set downsampling factor for synthesis filter bank */
  if (sampleRateOut == 0)
  {
    /* no single rate mode */
      sampleRateOut = sampleRateIn<<1; /* In case of implicit signalling, assume dual rate SBR */
  }

  if ( sampleRateIn == sampleRateOut ) {
    synDownsampleFac = 2;
  } else {
    synDownsampleFac = 1;
  }

  self->synDownsampleFac = synDownsampleFac;
  self->sampleRateOut = sampleRateOut;

  {
    int i;

    for (i = 0; i < (1)+1; i++)
    {
      hSbrHeader = &(self->sbrHeader[elementIndex][i]);

      /* init a default header such that we can at least do upsampling later */
      sbrError = initHeaderData(
              hSbrHeader,
              sampleRateIn,
              sampleRateOut,
              samplesPerFrame,
              self->flags
              );
    }
  }

  if (sbrError != SBRDEC_OK) {
    goto bail;
  }

  /* Init SBR channels going to be assigned to a SBR element */
  {
    int ch;

    for (ch=0; ch<self->pSbrElement[elementIndex]->nChannels; ch++)
    {
      /* and create sbrDec */
      sbrError = createSbrDec (self->pSbrElement[elementIndex]->pSbrChannel[ch],
                          hSbrHeader,
                         &self->pSbrElement[elementIndex]->transposerSettings,
                          synDownsampleFac,
                          qmfFlags,
                          self->flags,
                          overlap,
                          ch );

      if (sbrError != SBRDEC_OK) {
        goto bail;
      }
    }
  }

  //FDKmemclear(sbr_OverlapBuffer, sizeof(sbr_OverlapBuffer));

  if (self->numSbrElements == 1) {
    switch ( self->coreCodec ) {
    case AOT_AAC_LC:
    case AOT_SBR:
    case AOT_PS:
    case AOT_ER_AAC_SCAL:
    case AOT_DRM_AAC:
    case AOT_DRM_SURROUND:
      if (CreatePsDec ( &self->hParametricStereoDec, samplesPerFrame )) {
        sbrError = SBRDEC_CREATE_ERROR;
        goto bail;
      }
      break;
    default:
      break;
    }
  }

  /* Init frame delay slot handling */
  self->pSbrElement[elementIndex]->useFrameSlot = 0;
  for (i = 0; i < ((1)+1); i++) {
    self->pSbrElement[elementIndex]->useHeaderSlot[i] = i;
  }

bail:

  return sbrError;
}


SBR_ERROR sbrDecoder_Open ( HANDLE_SBRDECODER  * pSelf )
{
  HANDLE_SBRDECODER    self = NULL;
  SBR_ERROR sbrError = SBRDEC_OK;

  /* Get memory for this instance */
  self = GetRam_SbrDecoder();
  if (self == NULL) {
    sbrError = SBRDEC_MEM_ALLOC_FAILED;
    goto bail;
  }

  self->workBuffer1 = GetRam_SbrDecWorkBuffer1();
  self->workBuffer2 = GetRam_SbrDecWorkBuffer2();

  if (  self->workBuffer1 == NULL
     || self->workBuffer2 == NULL )
  {
    sbrError = SBRDEC_MEM_ALLOC_FAILED;
    goto bail;
  }

  /*
  Already zero because of calloc
  self->numSbrElements = 0;
  self->numSbrChannels = 0;
  self->codecFrameSize = 0;
  */

  self->numDelayFrames = (1);  /* set to the max value by default */

  *pSelf = self;

bail:
  return sbrError;
}

/**
 * \brief determine if the given core codec AOT can be processed or not.
 * \param coreCodec core codec audio object type.
 * \return 1 if SBR can be processed, 0 if SBR cannot be processed/applied.
 */
static
int sbrDecoder_isCoreCodecValid(AUDIO_OBJECT_TYPE coreCodec)
{
  switch (coreCodec) {
    case AOT_AAC_LC:
    case AOT_SBR:
    case AOT_PS:
    case AOT_ER_AAC_SCAL:
    case AOT_ER_AAC_ELD:
      return 1;
    default:
      return 0;
  }
}

static
void sbrDecoder_DestroyElement (
        HANDLE_SBRDECODER       self,
        const int               elementIndex
        )
{
  if (self->pSbrElement[elementIndex] != NULL) {
    int ch;

    for (ch=0; ch<SBRDEC_MAX_CH_PER_ELEMENT; ch++) {
      if (self->pSbrElement[elementIndex]->pSbrChannel[ch] != NULL) {
        deleteSbrDec( self->pSbrElement[elementIndex]->pSbrChannel[ch] );
        FreeRam_SbrDecChannel( &self->pSbrElement[elementIndex]->pSbrChannel[ch] );
        self->numSbrChannels -= 1;
      }
    }
    FreeRam_SbrDecElement( &self->pSbrElement[elementIndex] );
    self->numSbrElements -= 1;
  }
}


SBR_ERROR sbrDecoder_InitElement (
        HANDLE_SBRDECODER       self,
        const int               sampleRateIn,
        const int               sampleRateOut,
        const int               samplesPerFrame,
        const AUDIO_OBJECT_TYPE coreCodec,
        const MP4_ELEMENT_ID    elementID,
        const int               elementIndex
        )
{
  SBR_ERROR sbrError = SBRDEC_OK;
  int chCnt=0;
  int nSbrElementsStart = self->numSbrElements;

  /* Check core codec AOT */
  if (! sbrDecoder_isCoreCodecValid(coreCodec) || elementIndex >= (4)) {
    sbrError = SBRDEC_UNSUPPORTED_CONFIG;
    goto bail;
  }

  if ( elementID != ID_SCE && elementID != ID_CPE && elementID != ID_LFE )
  {
    sbrError = SBRDEC_UNSUPPORTED_CONFIG;
    goto bail;
  }

  if (  self->sampleRateIn == sampleRateIn
     && self->codecFrameSize == samplesPerFrame
     && self->coreCodec == coreCodec
     && self->pSbrElement[elementIndex] != NULL
     && self->pSbrElement[elementIndex]->elementID == elementID
     )
  {
     /* Nothing to do */
     return SBRDEC_OK;
  }

  self->sampleRateIn = sampleRateIn;
  self->codecFrameSize = samplesPerFrame;
  self->coreCodec = coreCodec;

  self->flags = 0;
  self->flags |= (coreCodec == AOT_ER_AAC_ELD) ? SBRDEC_ELD_GRID : 0;

  /* Init SBR elements */
  {
    int elChannels, ch;

    if (self->pSbrElement[elementIndex] == NULL) {
      self->pSbrElement[elementIndex] = GetRam_SbrDecElement(elementIndex);
      if (self->pSbrElement[elementIndex] == NULL) {
        sbrError = SBRDEC_MEM_ALLOC_FAILED;
        goto bail;
      }
      self->numSbrElements ++;
    } else {
      self->numSbrChannels -= self->pSbrElement[elementIndex]->nChannels;
    }

    /* Save element ID for sanity checks and to have a fallback for concealment. */
    self->pSbrElement[elementIndex]->elementID = elementID;

    /* Determine amount of channels for this element */
    switch (elementID) {
      case ID_NONE:
      case ID_CPE: elChannels=2;
        break;
      case ID_LFE:
      case ID_SCE: elChannels=1;
        break;
      default: elChannels=0;
        break;
    }

    /* Handle case of Parametric Stereo */
    if ( elementIndex == 0 && elementID == ID_SCE ) {
      switch (coreCodec) {
        case AOT_AAC_LC:
        case AOT_SBR:
        case AOT_PS:
        case AOT_ER_AAC_SCAL:
        case AOT_DRM_AAC:
        case AOT_DRM_SURROUND:
          elChannels = 2;
          break;
        default:
          break;
      }
    }

    self->pSbrElement[elementIndex]->nChannels = elChannels;

    for (ch=0; ch<elChannels; ch++)
    {
      if (self->pSbrElement[elementIndex]->pSbrChannel[ch] == NULL) {
        self->pSbrElement[elementIndex]->pSbrChannel[ch] = GetRam_SbrDecChannel(chCnt);
        if (self->pSbrElement[elementIndex]->pSbrChannel[ch] == NULL) {
          sbrError = SBRDEC_MEM_ALLOC_FAILED;
          goto bail;
        }
      }
      self->numSbrChannels ++;

      sbrDecoder_drcInitChannel( &self->pSbrElement[elementIndex]->pSbrChannel[ch]->SbrDec.sbrDrcChannel );

      /* Add reference pointer to workbuffers. */
      self->pSbrElement[elementIndex]->pSbrChannel[ch]->SbrDec.WorkBuffer1 = self->workBuffer1;
      self->pSbrElement[elementIndex]->pSbrChannel[ch]->SbrDec.WorkBuffer2 = self->workBuffer2;
      chCnt++;
    }
    if (elChannels == 1 && self->pSbrElement[elementIndex]->pSbrChannel[ch] != NULL) {
      deleteSbrDec( self->pSbrElement[elementIndex]->pSbrChannel[ch] );
      FreeRam_SbrDecChannel( &self->pSbrElement[elementIndex]->pSbrChannel[ch] );
    }
  }

  /* clear error flags for all delay slots */
  FDKmemclear(self->pSbrElement[elementIndex]->frameErrorFlag, ((1)+1)*sizeof(UCHAR));

  /* Initialize this instance */
  sbrError = sbrDecoder_ResetElement(
          self,
          sampleRateIn,
          sampleRateOut,
          samplesPerFrame,
          elementID,
          elementIndex,
          (coreCodec == AOT_ER_AAC_ELD) ? 0 : (6)
          );



bail:
  if (sbrError != SBRDEC_OK) {
    if (nSbrElementsStart < self->numSbrElements) {
      /* Free the memory allocated for this element */
      sbrDecoder_DestroyElement( self, elementIndex );
    } else if (self->pSbrElement[elementIndex] != NULL) {
      /* Set error flag to trigger concealment */
      self->pSbrElement[elementIndex]->frameErrorFlag[self->pSbrElement[elementIndex]->useFrameSlot] = 1;
    }
  }

  return sbrError;
}

/**
 * \brief Apply decoded SBR header for one element.
 * \param self SBR decoder instance handle
 * \param hSbrHeader SBR header handle to be processed.
 * \param hSbrChannel pointer array to the SBR element channels corresponding to the SBR header.
 * \param headerStatus header status value returned from SBR header parser.
 * \param numElementChannels amount of channels for the SBR element whos header is to be processed.
 */
static
SBR_ERROR sbrDecoder_HeaderUpdate(
        HANDLE_SBRDECODER self,
        HANDLE_SBR_HEADER_DATA hSbrHeader,
        SBR_HEADER_STATUS headerStatus,
        HANDLE_SBR_CHANNEL hSbrChannel[],
        const int numElementChannels
        )
{
  SBR_ERROR errorStatus = SBRDEC_OK;

  /*
    change of control data, reset decoder
  */
  errorStatus = resetFreqBandTables(hSbrHeader, self->flags);

  if (errorStatus == SBRDEC_OK) {
    if (hSbrHeader->syncState == UPSAMPLING && headerStatus != HEADER_RESET)
    {
      /* As the default header would limit the frequency range,
         lowSubband and highSubband must be patched. */
      hSbrHeader->freqBandData.lowSubband = hSbrHeader->numberOfAnalysisBands;
      hSbrHeader->freqBandData.highSubband = hSbrHeader->numberOfAnalysisBands;
    }

    /* Trigger a reset before processing this slot */
    hSbrHeader->status |= SBRDEC_HDR_STAT_RESET;
  }

  return errorStatus;
}

INT sbrDecoder_Header (
        HANDLE_SBRDECODER       self,
        HANDLE_FDK_BITSTREAM    hBs,
        const INT sampleRateIn,
        const INT sampleRateOut,
        const INT samplesPerFrame,
        const AUDIO_OBJECT_TYPE coreCodec,
        const MP4_ELEMENT_ID    elementID,
        const INT               elementIndex
        )
{
  SBR_HEADER_STATUS headerStatus;
  HANDLE_SBR_HEADER_DATA hSbrHeader;
  SBR_ERROR sbrError = SBRDEC_OK;
  int headerIndex;

  if ( self == NULL || elementIndex > (4) )
  {
    return SBRDEC_UNSUPPORTED_CONFIG;
  }

  if (! sbrDecoder_isCoreCodecValid(coreCodec)) {
    return SBRDEC_UNSUPPORTED_CONFIG;
  }

  sbrError = sbrDecoder_InitElement(
          self,
          sampleRateIn,
          sampleRateOut,
          samplesPerFrame,
          coreCodec,
          elementID,
          elementIndex
          );

  if (sbrError != SBRDEC_OK) {
    goto bail;
  }

  headerIndex = getHeaderSlot(self->pSbrElement[elementIndex]->useFrameSlot,
                              self->pSbrElement[elementIndex]->useHeaderSlot);
  hSbrHeader = &(self->sbrHeader[elementIndex][headerIndex]);

  headerStatus = sbrGetHeaderData ( hSbrHeader,
                                    hBs,
                                    self->flags,
                                    0);


  {
    SBR_DECODER_ELEMENT *pSbrElement;

    pSbrElement = self->pSbrElement[elementIndex];

    /* Sanity check */
    if (pSbrElement != NULL) {
      if ( (elementID == ID_CPE && pSbrElement->nChannels != 2)
        || (elementID != ID_CPE && pSbrElement->nChannels != 1) )
      {
        return SBRDEC_UNSUPPORTED_CONFIG;
      }
      if ( headerStatus == HEADER_RESET ) {

        sbrError = sbrDecoder_HeaderUpdate(
              self,
              hSbrHeader,
              headerStatus,
              pSbrElement->pSbrChannel,
              pSbrElement->nChannels
              );

        if (sbrError == SBRDEC_OK) {
          hSbrHeader->syncState = SBR_HEADER;
          hSbrHeader->status   |= SBRDEC_HDR_STAT_UPDATE;
        }
        /* else {
          Since we already have overwritten the old SBR header the only way out is UPSAMPLING!
          This will be prepared in the next step.
        } */
      }
    }
  }
bail:
  return sbrError;
}


SBR_ERROR sbrDecoder_SetParam (HANDLE_SBRDECODER   self,
                               const SBRDEC_PARAM  param,
                               const INT           value )
{
  SBR_ERROR errorStatus = SBRDEC_OK;

  /* configure the subsystems */
  switch (param)
  {
  case SBR_SYSTEM_BITSTREAM_DELAY:
    if (value < 0 || value > (1)) {
      errorStatus = SBRDEC_SET_PARAM_FAIL;
      break;
    }
    if (self == NULL) {
      errorStatus = SBRDEC_NOT_INITIALIZED;
    } else {
      self->numDelayFrames = (UCHAR)value;
    }
    break;
  case SBR_QMF_MODE:
    if (self == NULL) {
      errorStatus = SBRDEC_NOT_INITIALIZED;
    } else {
      if (value == 1) {
        self->flags |= SBRDEC_LOW_POWER;
      } else {
        self->flags &= ~SBRDEC_LOW_POWER;
      }
    }
    break;
  case SBR_LD_QMF_TIME_ALIGN:
    if (self == NULL) {
      errorStatus = SBRDEC_NOT_INITIALIZED;
    } else {
      if (value == 1) {
        self->flags |= SBRDEC_LD_MPS_QMF;
      } else {
        self->flags &= ~SBRDEC_LD_MPS_QMF;
      }
    }
    break;
  case SBR_BS_INTERRUPTION:
    {
      int elementIndex;

      if (self == NULL) {
        errorStatus = SBRDEC_NOT_INITIALIZED;
        break;
      }

      /* Loop over SBR elements */
      for (elementIndex = 0; elementIndex < self->numSbrElements; elementIndex++)
      {
        HANDLE_SBR_HEADER_DATA hSbrHeader;
        int headerIndex = getHeaderSlot(self->pSbrElement[elementIndex]->useFrameSlot,
                                        self->pSbrElement[elementIndex]->useHeaderSlot);

        hSbrHeader = &(self->sbrHeader[elementIndex][headerIndex]);

        /* Set sync state UPSAMPLING for the corresponding slot.
           This switches off bitstream parsing until a new header arrives. */
        hSbrHeader->syncState = UPSAMPLING;
        hSbrHeader->status   |= SBRDEC_HDR_STAT_UPDATE;
      }
    }
    break;
  default:
    errorStatus = SBRDEC_SET_PARAM_FAIL;
    break;
  }  /* switch(param) */

  return (errorStatus);
}

static
SBRDEC_DRC_CHANNEL * sbrDecoder_drcGetChannel( const HANDLE_SBRDECODER self, const INT channel )
{
  SBRDEC_DRC_CHANNEL *pSbrDrcChannelData = NULL;
  int elementIndex, elChanIdx=0, numCh=0;

  for (elementIndex = 0; (elementIndex < (4)) && (numCh <= channel); elementIndex++)
  {
    SBR_DECODER_ELEMENT *pSbrElement = self->pSbrElement[elementIndex];
    int c, elChannels;

    elChanIdx = 0;
    if (pSbrElement == NULL) break;

    /* Determine amount of channels for this element */
    switch (pSbrElement->elementID) {
      case ID_CPE: elChannels = 2;
        break;
      case ID_LFE:
      case ID_SCE: elChannels = 1;
        break;
      case ID_NONE:
      default: elChannels = 0;
        break;
    }

    /* Limit with actual allocated element channels */
    elChannels = FDKmin(elChannels, pSbrElement->nChannels);

    for (c = 0; (c < elChannels) && (numCh <= channel); c++) {
      if (pSbrElement->pSbrChannel[elChanIdx] != NULL) {
        numCh++;
        elChanIdx++;
      }
    }
  }
  elementIndex -= 1;
  elChanIdx -= 1;

  if (elChanIdx < 0 || elementIndex < 0) {
    return NULL;
  }

  if ( self->pSbrElement[elementIndex] != NULL ) {
    if ( self->pSbrElement[elementIndex]->pSbrChannel[elChanIdx] != NULL )
    {
      pSbrDrcChannelData = &self->pSbrElement[elementIndex]->pSbrChannel[elChanIdx]->SbrDec.sbrDrcChannel;
    }
  }

  return (pSbrDrcChannelData);
}

SBR_ERROR sbrDecoder_drcFeedChannel ( HANDLE_SBRDECODER  self,
                                      INT                ch,
                                      UINT               numBands,
                                      FIXP_DBL          *pNextFact_mag,
                                      INT                nextFact_exp,
                                      SHORT              drcInterpolationScheme,
                                      UCHAR              winSequence,
                                      USHORT            *pBandTop )
{
  SBRDEC_DRC_CHANNEL *pSbrDrcChannelData = NULL;
  int band, isValidData = 0;

  if (self == NULL) {
    return SBRDEC_NOT_INITIALIZED;
  }
  if (ch > (6) || pNextFact_mag == NULL) {
    return SBRDEC_SET_PARAM_FAIL;
  }

  /* Search for gain values different to 1.0f */
  for (band = 0; band < numBands; band += 1) {
    if ( !((pNextFact_mag[band] == FL2FXCONST_DBL(0.5))  && (nextFact_exp == 1))
      && !((pNextFact_mag[band] == (FIXP_DBL)MAXVAL_DBL) && (nextFact_exp == 0)) ) {
      isValidData = 1;
      break;
    }
  }

  /* Find the right SBR channel */
  pSbrDrcChannelData = sbrDecoder_drcGetChannel( self, ch );

  if ( pSbrDrcChannelData != NULL ) {
    if ( pSbrDrcChannelData->enable || isValidData )
  { /* Activate processing only with real and valid data */
    int i;

    pSbrDrcChannelData->enable   = 1;
    pSbrDrcChannelData->numBandsNext = numBands;

    pSbrDrcChannelData->winSequenceNext            = winSequence;
    pSbrDrcChannelData->drcInterpolationSchemeNext = drcInterpolationScheme;
    pSbrDrcChannelData->nextFact_exp               = nextFact_exp;

    for (i = 0; i < (int)numBands; i++) {
      pSbrDrcChannelData->bandTopNext[i]  = pBandTop[i];
      pSbrDrcChannelData->nextFact_mag[i] = pNextFact_mag[i];
    }
  }
  }

  return SBRDEC_OK;
}


void sbrDecoder_drcDisable ( HANDLE_SBRDECODER  self,
                             INT                ch )
{
  SBRDEC_DRC_CHANNEL *pSbrDrcChannelData = NULL;

  if ( (self == NULL)
    || (ch > (6))
    || (self->numSbrElements == 0)
    || (self->numSbrChannels == 0) ) {
    return;
  }

  /* Find the right SBR channel */
  pSbrDrcChannelData = sbrDecoder_drcGetChannel( self, ch );

  if ( pSbrDrcChannelData != NULL ) {
    sbrDecoder_drcInitChannel( pSbrDrcChannelData );
  }
}



SBR_ERROR sbrDecoder_Parse(
        HANDLE_SBRDECODER self,
        HANDLE_FDK_BITSTREAM  hBs,
        int *count,
        int  bsPayLen,
        int  crcFlag,
        MP4_ELEMENT_ID prevElement,
        int elementIndex,
        int fGlobalIndependencyFlag
        )
{
  SBR_DECODER_ELEMENT   *hSbrElement;
  HANDLE_SBR_HEADER_DATA hSbrHeader;
  HANDLE_SBR_CHANNEL    *pSbrChannel;

  SBR_FRAME_DATA *hFrameDataLeft;
  SBR_FRAME_DATA *hFrameDataRight;

  SBR_ERROR errorStatus = SBRDEC_OK;
  SBR_SYNC_STATE initialSyncState;
  SBR_HEADER_STATUS headerStatus = HEADER_NOT_PRESENT;

  INT  startPos;
  INT  CRCLen = 0;

  int  stereo;
  int  fDoDecodeSbrData = 1;

  int lastSlot, lastHdrSlot = 0, thisHdrSlot;

  /* Remember start position of  SBR element */
  startPos = FDKgetValidBits(hBs);

  /* SBR sanity checks */
  if ( self == NULL || self->pSbrElement[elementIndex] == NULL ) {
    errorStatus = SBRDEC_NOT_INITIALIZED;
    goto bail;
  } 

  hSbrElement = self->pSbrElement[elementIndex];

  lastSlot    = (hSbrElement->useFrameSlot > 0) ? hSbrElement->useFrameSlot-1 : self->numDelayFrames;
  lastHdrSlot =  hSbrElement->useHeaderSlot[lastSlot];
  thisHdrSlot =  getHeaderSlot( hSbrElement->useFrameSlot, hSbrElement->useHeaderSlot );  /* Get a free header slot not used by frames not processed yet. */

  /* Assign the free slot to store a new header if there is one. */
  hSbrHeader = &self->sbrHeader[elementIndex][thisHdrSlot];

  pSbrChannel = hSbrElement->pSbrChannel;
  stereo = (hSbrElement->elementID == ID_CPE) ? 1 : 0;

  hFrameDataLeft  = &self->pSbrElement[elementIndex]->pSbrChannel[0]->frameData[hSbrElement->useFrameSlot];
  hFrameDataRight = &self->pSbrElement[elementIndex]->pSbrChannel[1]->frameData[hSbrElement->useFrameSlot];

  initialSyncState = hSbrHeader->syncState;

  /* reset PS flag; will be set after PS was found */
  self->flags &= ~SBRDEC_PS_DECODED;

  if (hSbrHeader->status & SBRDEC_HDR_STAT_UPDATE) {
    /* Got a new header from extern (e.g. from an ASC) */
    headerStatus = HEADER_OK;
    hSbrHeader->status &= ~SBRDEC_HDR_STAT_UPDATE;
  }
  else if (thisHdrSlot != lastHdrSlot) {
    /* Copy the last header into this slot otherwise the
       header compare will trigger more HEADER_RESETs than needed. */
    copySbrHeader( hSbrHeader, &self->sbrHeader[elementIndex][lastHdrSlot] );
  }

  /*
     Check if bit stream data is valid and matches the element context
  */
  if ( ((prevElement != ID_SCE) && (prevElement != ID_CPE)) || prevElement != hSbrElement->elementID) {
    /* In case of LFE we also land here, since there is no LFE SBR element (do upsampling only) */
    fDoDecodeSbrData = 0;
  }

  if (fDoDecodeSbrData)
  {
    if ((INT)FDKgetValidBits(hBs) <= 0) {
      fDoDecodeSbrData = 0;
    }
  }

  /*
     SBR CRC-check
  */
  if (fDoDecodeSbrData)
  {
    if (crcFlag == 1) {
      switch (self->coreCodec) {
      case AOT_ER_AAC_ELD:
        FDKpushFor (hBs, 10);
        /* check sbrcrc later: we don't know the payload length now */
        break;
      default:
        CRCLen = bsPayLen - 10;                     /* change: 0 => i */
        if (CRCLen < 0) {
          fDoDecodeSbrData = 0;
        } else {
          fDoDecodeSbrData = SbrCrcCheck (hBs, CRCLen);
        }
        break;
      }
    }
  } /* if (fDoDecodeSbrData) */

  /*
     Read in the header data and issue a reset if change occured
  */
  if (fDoDecodeSbrData)
  {
    int sbrHeaderPresent;

    {
      sbrHeaderPresent = FDKreadBit(hBs);
    }

    if ( sbrHeaderPresent ) {
      headerStatus = sbrGetHeaderData (hSbrHeader,
                                       hBs,
                                       self->flags,
                                       1);
    }

    if (headerStatus == HEADER_RESET)
    {
      errorStatus = sbrDecoder_HeaderUpdate(
            self,
            hSbrHeader,
            headerStatus,
            pSbrChannel,
            hSbrElement->nChannels 
            );

      if (errorStatus == SBRDEC_OK) {
        hSbrHeader->syncState = SBR_HEADER;
      } else {
        hSbrHeader->syncState = SBR_NOT_INITIALIZED;
      }
    }

    if (errorStatus != SBRDEC_OK) {
      fDoDecodeSbrData = 0;
    }
  } /* if (fDoDecodeSbrData) */

  /*
    Print debugging output only if state has changed
  */

  /* read frame data */
  if ((hSbrHeader->syncState >= SBR_HEADER) && fDoDecodeSbrData) {
    int sbrFrameOk;
    /* read the SBR element data */
    if (stereo) {
      sbrFrameOk = sbrGetChannelPairElement(hSbrHeader,
                                            hFrameDataLeft,
                                            hFrameDataRight,
                                            hBs,
                                            self->flags,
                                            self->pSbrElement[elementIndex]->transposerSettings.overlap);
    }
    else {
      if (self->hParametricStereoDec != NULL) {
        /* update slot index for PS bitstream parsing */
        self->hParametricStereoDec->bsLastSlot = self->hParametricStereoDec->bsReadSlot;
        self->hParametricStereoDec->bsReadSlot = hSbrElement->useFrameSlot;
      }
      sbrFrameOk = sbrGetSingleChannelElement(hSbrHeader,
                                              hFrameDataLeft,
                                              hBs,
                                              self->hParametricStereoDec,
                                              self->flags,
                                              self->pSbrElement[elementIndex]->transposerSettings.overlap);
    }
    if (!sbrFrameOk) {
      fDoDecodeSbrData = 0;
    }
    else {
      INT valBits;

      if (bsPayLen > 0) {
        valBits = bsPayLen - ((INT)startPos - (INT)FDKgetValidBits(hBs));
      } else {
        valBits = (INT)FDKgetValidBits(hBs);
      }

      if ( crcFlag == 1 ) {
        switch (self->coreCodec) {
        case AOT_ER_AAC_ELD:
          {
            /* late crc check for eld */
            INT payloadbits = (INT)startPos - (INT)FDKgetValidBits(hBs) - startPos;
            INT crcLen      = payloadbits - 10;
            FDKpushBack(hBs, payloadbits);
            fDoDecodeSbrData      = SbrCrcCheck (hBs, crcLen);
            FDKpushFor(hBs, crcLen);
          }
          break;
        default:
          break;
        }
      }

      /* sanity check of remaining bits */
      if (valBits < 0) {
        fDoDecodeSbrData = 0;
      } else {
        switch (self->coreCodec) {
        case AOT_SBR:
        case AOT_PS:
        case AOT_AAC_LC:
          {
            /* This sanity check is only meaningful with General Audio bitstreams */
            int alignBits = valBits & 0x7;

            if (valBits > alignBits) {
              fDoDecodeSbrData = 0;
            }
          }
          break;
        default:
          /* No sanity check available */
          break;
        }
      }
    }
  }

  if (!fDoDecodeSbrData) {
    /* Set error flag for this slot to trigger concealment */
    self->pSbrElement[elementIndex]->frameErrorFlag[hSbrElement->useFrameSlot] = 1;
    errorStatus = SBRDEC_PARSE_ERROR;
  } else {
    /* Everything seems to be ok so clear the error flag */
    self->pSbrElement[elementIndex]->frameErrorFlag[hSbrElement->useFrameSlot] = 0;
  }

  if (!stereo) {
    /* Turn coupling off explicitely to avoid access to absent right frame data
       that might occur with corrupt bitstreams. */
    hFrameDataLeft->coupling = COUPLING_OFF;
  }

bail:
  if (errorStatus == SBRDEC_OK) {
    if (headerStatus == HEADER_NOT_PRESENT) {
      /* Use the old header for this frame */
      hSbrElement->useHeaderSlot[hSbrElement->useFrameSlot] = lastHdrSlot;
    } else {
      /* Use the new header for this frame */
      hSbrElement->useHeaderSlot[hSbrElement->useFrameSlot] = thisHdrSlot;
    }

    /* Move frame pointer to the next slot which is up to be decoded/applied next */
    hSbrElement->useFrameSlot = (hSbrElement->useFrameSlot+1) % (self->numDelayFrames+1);
  }

  *count -= startPos - FDKgetValidBits(hBs);

  return errorStatus;
}


/**
 * \brief Render one SBR element into time domain signal.
 * \param self SBR decoder handle
 * \param timeData pointer to output buffer
 * \param interleaved flag indicating interleaved channel output
 * \param channelMapping pointer to UCHAR array where next 2 channel offsets are stored. 
 * \param elementIndex enumerating index of the SBR element to render.
 * \param numInChannels number of channels from core coder (reading stride).
 * \param numOutChannels pointer to a location to return number of output channels.
 * \param psPossible flag indicating if PS is possible or not.
 * \return SBRDEC_OK if successfull, else error code
 */
static SBR_ERROR
sbrDecoder_DecodeElement (
        HANDLE_SBRDECODER    self,
        INT_PCM             *timeData,
        const int            interleaved,
        const UCHAR         *channelMapping,
        const int            elementIndex,
        const int            numInChannels,
        int                 *numOutChannels,
        const int            psPossible
        )
{
  SBR_DECODER_ELEMENT *hSbrElement = self->pSbrElement[elementIndex];
  HANDLE_SBR_CHANNEL    *pSbrChannel = self->pSbrElement[elementIndex]->pSbrChannel;
  HANDLE_SBR_HEADER_DATA hSbrHeader = &self->sbrHeader[elementIndex][hSbrElement->useHeaderSlot[hSbrElement->useFrameSlot]];
  HANDLE_PS_DEC h_ps_d = self->hParametricStereoDec;

  /* get memory for frame data from scratch */
  SBR_FRAME_DATA *hFrameDataLeft  = &hSbrElement->pSbrChannel[0]->frameData[hSbrElement->useFrameSlot];
  SBR_FRAME_DATA *hFrameDataRight = &hSbrElement->pSbrChannel[1]->frameData[hSbrElement->useFrameSlot];

  SBR_ERROR errorStatus = SBRDEC_OK;


  INT  strideIn, strideOut, offset0, offset1;
  INT  codecFrameSize = self->codecFrameSize;

  int  stereo = (hSbrElement->elementID == ID_CPE) ? 1 : 0;
  int  numElementChannels = hSbrElement->nChannels; /* Number of channels of the current SBR element */

  /* Update the header error flag */
  hSbrHeader->frameErrorFlag = hSbrElement->frameErrorFlag[hSbrElement->useFrameSlot];

  /*
     Prepare filterbank for upsampling if no valid bit stream data is available.
   */
  if ( hSbrHeader->syncState == SBR_NOT_INITIALIZED )
  {
    errorStatus = initHeaderData(
            hSbrHeader,
            self->sampleRateIn,
            self->sampleRateOut,
            codecFrameSize,
            self->flags
            );

    if (errorStatus != SBRDEC_OK) {
      return errorStatus;
    }

    hSbrHeader->syncState = UPSAMPLING;

    errorStatus = sbrDecoder_HeaderUpdate(
            self,
            hSbrHeader,
            HEADER_NOT_PRESENT,
            pSbrChannel,
            hSbrElement->nChannels
            );

    if (errorStatus != SBRDEC_OK) {
      hSbrHeader->syncState = SBR_NOT_INITIALIZED;
      return errorStatus;
    }
  }

  /* reset */
  if (hSbrHeader->status & SBRDEC_HDR_STAT_RESET) {
    int ch;
    for (ch = 0 ; ch < numElementChannels; ch++) {
      SBR_ERROR errorStatusTmp = SBRDEC_OK;

      errorStatusTmp = resetSbrDec (
             &pSbrChannel[ch]->SbrDec,
              hSbrHeader,
             &pSbrChannel[ch]->prevFrameData,
              self->flags & SBRDEC_LOW_POWER,
              self->synDownsampleFac
              );

      if (errorStatusTmp != SBRDEC_OK) {
        errorStatus = errorStatusTmp;
      }
    }
    hSbrHeader->status &= ~SBRDEC_HDR_STAT_RESET;
  }

  /* decoding */
  if ( (hSbrHeader->syncState == SBR_ACTIVE)
    || ((hSbrHeader->syncState == SBR_HEADER) && (hSbrHeader->frameErrorFlag == 0)) )
  {
    errorStatus = SBRDEC_OK;

    decodeSbrData (hSbrHeader,
                   hFrameDataLeft,
                  &pSbrChannel[0]->prevFrameData,
                   (stereo) ? hFrameDataRight : NULL,
                   (stereo) ? &pSbrChannel[1]->prevFrameData : NULL);


    /* Now we have a full parameter set and can do parameter
       based concealment instead of plain upsampling. */
    hSbrHeader->syncState = SBR_ACTIVE;
  }

  /* decode PS data if available */
  if (h_ps_d != NULL && psPossible) {
    int applyPs = 1;

    /* define which frame delay line slot to process */
    h_ps_d->processSlot = hSbrElement->useFrameSlot;

    applyPs = DecodePs(h_ps_d, hSbrHeader->frameErrorFlag);
    self->flags |= (applyPs) ? SBRDEC_PS_DECODED : 0;
  }

  /* Set strides for reading and writing */
  if (interleaved) {
    strideIn = numInChannels;
    if ( psPossible )
      strideOut = (numInChannels < 2) ? 2 : numInChannels;
    else
      strideOut = numInChannels;
    offset0 = channelMapping[0];
    offset1 = channelMapping[1];
  } else {
    strideIn  = 1;
    strideOut = 1;
    offset0 = channelMapping[0]*2*codecFrameSize;
    offset1 = channelMapping[1]*2*codecFrameSize;
  }

  /* use same buffers for left and right channel and apply PS per timeslot */
  /* Process left channel */
//FDKprintf("self->codecFrameSize %d\t%d\n",self->codecFrameSize,self->sampleRateIn);
  sbr_dec (&pSbrChannel[0]->SbrDec,
            timeData + offset0,
            timeData + offset0,
           &pSbrChannel[1]->SbrDec,
            timeData + offset1,
            strideIn,
            strideOut,
            hSbrHeader,
            hFrameDataLeft,
           &pSbrChannel[0]->prevFrameData,
            (hSbrHeader->syncState == SBR_ACTIVE),
            h_ps_d,
            self->flags
          );

  if (stereo) {
    /* Process right channel */
    sbr_dec (&pSbrChannel[1]->SbrDec,
              timeData + offset1,
              timeData + offset1,
              NULL,
              NULL,
              strideIn,
              strideOut,
              hSbrHeader,
              hFrameDataRight,
             &pSbrChannel[1]->prevFrameData,
              (hSbrHeader->syncState == SBR_ACTIVE),
              NULL,
              self->flags
            );
  }

  if (h_ps_d != NULL) {
    /* save PS status for next run */
    h_ps_d->psDecodedPrv = (self->flags & SBRDEC_PS_DECODED) ? 1 : 0 ;
  }

  if ( psPossible 
    )
  {
    FDK_ASSERT(strideOut > 1);
    if ( !(self->flags & SBRDEC_PS_DECODED) ) {
      /* A decoder which is able to decode PS has to produce a stereo output even if no PS data is availble. */
      /* So copy left channel to right channel.                                                              */
      if (interleaved) {
        INT_PCM *ptr;
        INT i;
        FDK_ASSERT(strideOut == 2);

        ptr = timeData;
        for (i = codecFrameSize; i--; ) 
        {
          INT_PCM tmp; /* This temporal variable is required because some compilers can't do *ptr++ = *ptr++ correctly. */
          tmp = *ptr++; *ptr++ = tmp;
          tmp = *ptr++; *ptr++ = tmp;
        }
      } else {
        FDKmemcpy( timeData+2*codecFrameSize, timeData, 2*codecFrameSize*sizeof(INT_PCM) );
      }
    }
    *numOutChannels = 2;  /* Output minimum two channels when PS is enabled. */
  }

  return errorStatus;
}


SBR_ERROR sbrDecoder_Apply ( HANDLE_SBRDECODER   self,
                             INT_PCM            *timeData,
                             int                *numChannels,
                             int                *sampleRate,
                             const UCHAR         channelMapping[(6)],
                             const int           interleaved,
                             const int           coreDecodedOk,
                             UCHAR              *psDecoded )
{
  SBR_ERROR errorStatus = SBRDEC_OK;

  int   psPossible = 0;
  int   sbrElementNum;
  int   numCoreChannels = *numChannels;
  int   numSbrChannels  = 0;

  psPossible = *psDecoded;

  if (self->numSbrElements < 1) {
    /* exit immediately to avoid access violations */
    return SBRDEC_CREATE_ERROR;
  }

  /* Sanity check of allocated SBR elements. */
  for (sbrElementNum=0; sbrElementNum<self->numSbrElements; sbrElementNum++) {
    if (self->pSbrElement[sbrElementNum] == NULL) {
      return SBRDEC_CREATE_ERROR;
    }
  }

  if (self->numSbrElements != 1 || self->pSbrElement[0]->elementID != ID_SCE) {
    psPossible = 0;
  }


  /* In case of non-interleaved time domain data and upsampling, make room for bigger SBR output. */
  if (self->synDownsampleFac == 1 && interleaved == 0) {
    int c, outputFrameSize;

    outputFrameSize =
            self->pSbrElement[0]->pSbrChannel[0]->SbrDec.SynthesisQMF.no_channels
            * self->pSbrElement[0]->pSbrChannel[0]->SbrDec.SynthesisQMF.no_col;

    for (c=numCoreChannels-1; c>0; c--) {
      FDKmemmove(timeData + c*outputFrameSize, timeData + c*self->codecFrameSize , self->codecFrameSize*sizeof(INT_PCM));
    }
  }


  /* Make sure that even if no SBR data was found/parsed *psDecoded is returned 1 if psPossible was 0. */
  if (psPossible == 0) {
    self->flags &= ~SBRDEC_PS_DECODED;
  }

  /* Loop over SBR elements */
  for (sbrElementNum = 0; sbrElementNum<self->numSbrElements; sbrElementNum++)
  {
    int numElementChan;

    if (psPossible && self->pSbrElement[sbrElementNum]->pSbrChannel[1] == NULL) {
      errorStatus = SBRDEC_UNSUPPORTED_CONFIG;
      goto bail;
    }

    numElementChan = (self->pSbrElement[sbrElementNum]->elementID == ID_CPE) ? 2 : 1;

    /* If core signal is bad then force upsampling */
    if ( ! coreDecodedOk ) {
      self->pSbrElement[sbrElementNum]->frameErrorFlag[self->pSbrElement[sbrElementNum]->useFrameSlot] = 1;
    }

    errorStatus = sbrDecoder_DecodeElement (
                                 self,
                                 timeData,
                                 interleaved,
                                 channelMapping,
                                 sbrElementNum,
                                 numCoreChannels,
                                &numElementChan,
                                 psPossible
                               );

    if (errorStatus != SBRDEC_OK) {
      goto bail;
    }

    numSbrChannels += numElementChan;
    channelMapping += numElementChan;

    if (numSbrChannels >= numCoreChannels) {
      break;
    }
  }

  /* Update numChannels and samplerate */
  *numChannels = numSbrChannels;
  *sampleRate = self->sampleRateOut;
  *psDecoded = (self->flags & SBRDEC_PS_DECODED) ? 1 : 0;



bail:

  return errorStatus;
}


SBR_ERROR sbrDecoder_Close ( HANDLE_SBRDECODER *pSelf )
{
  HANDLE_SBRDECODER self = *pSelf;
  int i;

  if (self != NULL)
  {
    if (self->hParametricStereoDec != NULL) {
      DeletePsDec ( &self->hParametricStereoDec );
    }

    if (self->workBuffer1 != NULL) {
      FreeRam_SbrDecWorkBuffer1(&self->workBuffer1);
    }
    if (self->workBuffer2 != NULL) {
      FreeRam_SbrDecWorkBuffer2(&self->workBuffer2);
    }

    for (i = 0; i < (4); i++) {
      sbrDecoder_DestroyElement( self, i );
    }

    FreeRam_SbrDecoder(pSelf);
  }

  return SBRDEC_OK;
}


INT sbrDecoder_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_SBRDEC;
  info->version = LIB_VERSION(SBRDECODER_LIB_VL0, SBRDECODER_LIB_VL1, SBRDECODER_LIB_VL2);
  LIB_VERSION_STRING(info);
  info->build_date = (char *)SBRDECODER_LIB_BUILD_DATE;
  info->build_time = (char *)SBRDECODER_LIB_BUILD_TIME;
  info->title      = (char *)SBRDECODER_LIB_TITLE;

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

  return 0;
}

