summaryrefslogtreecommitdiffstats
path: root/libSBRdec/src/sbrdecoder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libSBRdec/src/sbrdecoder.cpp')
-rw-r--r--libSBRdec/src/sbrdecoder.cpp1448
1 files changed, 1448 insertions, 0 deletions
diff --git a/libSBRdec/src/sbrdecoder.cpp b/libSBRdec/src/sbrdecoder.cpp
new file mode 100644
index 0000000..dc824cb
--- /dev/null
+++ b/libSBRdec/src/sbrdecoder.cpp
@@ -0,0 +1,1448 @@
+/****************************************************************************
+
+ (C) Copyright Fraunhofer IIS (2005)
+ All Rights Reserved
+
+ Please be advised that this software and/or program delivery is
+ Confidential Information of Fraunhofer and subject to and covered by the
+
+ Fraunhofer IIS Software Evaluation Agreement
+ between Google Inc. and Fraunhofer
+ effective and in full force since March 1, 2012.
+
+ You may use this software and/or program only under the terms and
+ conditions described in the above mentioned Fraunhofer IIS Software
+ Evaluation Agreement. Any other and/or further use requires a separate agreement.
+
+
+ This software and/or program is protected by copyright law and international
+ treaties. Any reproduction or distribution of this software and/or program,
+ or any portion of it, may result in severe civil and criminal penalties, and
+ will be prosecuted to the maximum extent possible under law.
+
+ $Id$
+
+*******************************************************************************/
+/*!
+ \file
+ \brief SBR decoder frontend $Revision: 36841 $
+ 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 1
+#define SBRDECODER_LIB_VL2 0
+#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 > 24000
+ )
+ {
+ sbrError = SBRDEC_UNSUPPORTED_CONFIG;
+ goto bail;
+ }
+
+ if ( sampleRateOut > 48000 )
+ {
+ 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;
+
+ /* 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) {
+ /* Free the memory allocated for this element */
+ sbrDecoder_DestroyElement( self, elementIndex );
+ }
+
+ 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;
+ /* 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;
+
+ if (self == NULL) {
+ return SBRDEC_NOT_INITIALIZED;
+ }
+ if (ch > (6) || pNextFact_mag == NULL) {
+ return SBRDEC_SET_PARAM_FAIL;
+ }
+
+ /* Find the right SBR channel */
+ pSbrDrcChannelData = sbrDecoder_drcGetChannel( self, ch );
+
+ if ( pSbrDrcChannelData != NULL ) {
+ 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 ) {
+ pSbrDrcChannelData->enable = 0;
+ }
+}
+
+
+
+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
+ );
+
+ hSbrHeader->syncState = UPSAMPLING;
+
+ errorStatus = sbrDecoder_HeaderUpdate(
+ self,
+ hSbrHeader,
+ HEADER_NOT_PRESENT,
+ pSbrChannel,
+ hSbrElement->nChannels
+ );
+ }
+
+ /* 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;
+
+ /* 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;
+}
+