diff options
Diffstat (limited to 'libSACdec/src/sac_dec_lib.cpp')
-rw-r--r-- | libSACdec/src/sac_dec_lib.cpp | 1912 |
1 files changed, 1912 insertions, 0 deletions
diff --git a/libSACdec/src/sac_dec_lib.cpp b/libSACdec/src/sac_dec_lib.cpp new file mode 100644 index 0000000..56d8693 --- /dev/null +++ b/libSACdec/src/sac_dec_lib.cpp @@ -0,0 +1,1912 @@ +/* ----------------------------------------------------------------------------- +Software License for The Fraunhofer FDK AAC Codec Library for Android + +© Copyright 1995 - 2018 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 +----------------------------------------------------------------------------- */ + +/*********************** MPEG surround decoder library ************************* + + Author(s): + + Description: SAC Decoder Library Interface + +*******************************************************************************/ + +#include "sac_dec_lib.h" +#include "sac_dec_interface.h" +#include "sac_dec.h" +#include "sac_bitdec.h" +#include "FDK_matrixCalloc.h" + +#define MPS_DATA_BUFFER_SIZE (2048) + +/** + * \brief MPEG Surround data indication. + **/ +typedef enum { + MPEGS_ANCTYPE_FRAME = 0, /*!< MPEG Surround frame, see ISO/IEC 23003-1 */ + MPEGS_ANCTYPE_HEADER_AND_FRAME = 1, /*!< MPEG Surround header and MPEG + Surround frame, see ISO/IEC 23003-1 */ + MPEGS_ANCTYPE_RESERVED_1 = 2, /*!< reserved, see ISO/IEC 23003-1 */ + MPEGS_ANCTYPE_RESERVED_2 = 3 /*!< reserved, see ISO/IEC 23003-1*/ +} MPEGS_ANCTYPE; + +/** + * \brief MPEG Surround data segment indication. + **/ +typedef enum { + MPEGS_CONTINUE = 0, /*!< Indicates if data segment continues a data block. */ + MPEGS_STOP = 1, /*!< Indicates if data segment ends a data block. */ + MPEGS_START = 2, /*!< Indicates if data segment begins a data block. */ + MPEGS_START_STOP = + 3 /*!< Indicates if data segment begins and ends a data block. */ +} MPEGS_ANCSTARTSTOP; + +/** + * \brief MPEG Surround synchronizaiton state. + * + * CAUTION: Changing the enumeration values can break the sync mechanism + *because it is based on comparing the state values. + **/ +typedef enum { + MPEGS_SYNC_LOST = + 0, /*!< Indicates lost sync because of current discontinuity. */ + MPEGS_SYNC_FOUND = 1, /*!< Parsed a valid header and (re)intialization was + successfully completed. */ + MPEGS_SYNC_COMPLETE = 2 /*!< In sync and continuous. Found an independent + frame in addition to MPEGS_SYNC_FOUND. + Precondition: MPEGS_SYNC_FOUND. */ +} MPEGS_SYNCSTATE; + +/** + * \brief MPEG Surround operation mode. + **/ +typedef enum { + MPEGS_OPMODE_EMM = 0, /*!< Mode: Enhanced Matrix Mode (Blind) */ + MPEGS_OPMODE_MPS_PAYLOAD = 1, /*!< Mode: Normal, Stereo or Binaural */ + MPEGS_OPMODE_NO_MPS_PAYLOAD = 2 /*!< Mode: no MPEG Surround payload */ +} MPEGS_OPMODE; + +/** + * \brief MPEG Surround init flags. + **/ +typedef enum { + MPEGS_INIT_OK = 0x00000000, /*!< indicate correct initialization */ + MPEGS_INIT_ENFORCE_REINIT = + 0x00000001, /*!< indicate complete initialization */ + + MPEGS_INIT_CHANGE_OUTPUT_MODE = + 0x00000010, /*!< indicate change of the output mode */ + MPEGS_INIT_CHANGE_PARTIALLY_COMPLEX = + 0x00000020, /*!< indicate change of low power/high quality */ + MPEGS_INIT_CHANGE_TIME_FREQ_INTERFACE = + 0x00000040, /*!< indicate change of qmf/time interface */ + MPEGS_INIT_CHANGE_HEADER = 0x00000080, /*!< indicate change of header */ + + MPEGS_INIT_ERROR_PAYLOAD = + 0x00000100, /*!< indicate payload/ancType/ancStartStop error */ + + MPEGS_INIT_BS_INTERRUPTION = + 0x00001000, /*!< indicate bitstream interruption */ + MPEGS_INIT_CLEAR_HISTORY = + 0x00002000, /*!< indicate that all states shall be cleared */ + + /* Re-initialization of submodules */ + + MPEGS_INIT_CHANGE_CONCEAL_PARAMS = 0x00100000, /*!< indicate a change of at + least one error concealment + param */ + + /* No re-initialization needed, currently not used */ + MPEGS_INIT_CHANGE_BYPASS_MODE = + 0x01000000, /*!< indicate change of bypass mode */ + + /* Re-initialization needed, currently not used */ + MPEGS_INIT_ERROR_ANC_TYPE = 0x10000000, /*!< indicate ancType error*/ + MPEGS_INIT_ERROR_ANC_STARTSTOP = + 0x20000000 /*!< indicate ancStartStop error */ +} MPEGS_INIT_FLAGS; + +struct MpegSurroundDecoder { + HANDLE_FDK_QMF_DOMAIN pQmfDomain; + UCHAR mpsData[MPS_DATA_BUFFER_SIZE]; /* Buffer for MPS payload accross more + than one segment */ + INT mpsDataBits; /* Amount of bits in mpsData */ + /* MPEG Surround decoder */ + SPATIAL_SPECIFIC_CONFIG spatialSpecificConfig[1]; /* SSC delay line which is + used during decoding */ + spatialDec *pSpatialDec; + SPATIAL_SPECIFIC_CONFIG + spatialSpecificConfigBackup; /* SSC used while parsing */ + + /* Creation parameter */ + UCHAR mpegSurroundDecoderLevel; + /* Run-time parameter */ + UCHAR mpegSurroundSscIsGlobalCfg; /* Flag telling that the SSC + (::spatialSpecificConfig) is a + out-of-band configuration. */ + UCHAR mpegSurroundUseTimeInterface; + + SPATIAL_BS_FRAME + bsFrames[1]; /* Bitstream Structs that contain data read from the + SpatialFrame() bitstream element */ + BS_LL_STATE llState; /* Bit stream parser state memory */ + UCHAR bsFrameParse; /* Current parse frame context index */ + UCHAR bsFrameDecode; /* Current decode/apply frame context index */ + UCHAR bsFrameDelay; /* Amount of frames delay between parsing and processing. + Required i.e. for interpolation error concealment. */ + + /* User prameters */ + SPATIALDEC_PARAM mpegSurroundUserParams; + + /* Internal flags */ + SPATIAL_DEC_UPMIX_TYPE upmixType; + int initFlags[1]; + MPEGS_ANCSTARTSTOP ancStartStopPrev; + MPEGS_SYNCSTATE fOnSync[1]; + + /* Inital decoder configuration */ + SPATIAL_DEC_CONFIG decConfig; +}; + +static SACDEC_ERROR sscParseCheck(const SPATIAL_SPECIFIC_CONFIG *pSsc); + +/** + * \brief Get the number of QMF bands from the sampling frequency (in Hz) + **/ +static int mpegSurroundDecoder_GetNrOfQmfBands( + const SPATIAL_SPECIFIC_CONFIG *pSsc, UINT sampleRate) { + UINT samplingFrequency = sampleRate; + int qmfBands = 64; + + if (pSsc != NULL) { + switch (pSsc->coreCodec) { + case AOT_USAC: + if ((pSsc->stereoConfigIndex == 3)) { + static const UCHAR mapIdx2QmfBands[3] = {24, 32, 16}; + FDK_ASSERT((pSsc->coreSbrFrameLengthIndex >= 2) && + (pSsc->coreSbrFrameLengthIndex <= 4)); + qmfBands = mapIdx2QmfBands[pSsc->coreSbrFrameLengthIndex - 2]; + } + return qmfBands; + default: + samplingFrequency = pSsc->samplingFreq; + break; + } + } + + /* number of QMF bands depend on sampling frequency, see FDIS 23003-1:2006 + * Chapter 6.3.3 */ + if (samplingFrequency < 27713) { + qmfBands = 32; + } + if (samplingFrequency > 55426) { + qmfBands = 128; + } + + return qmfBands; +} + +/** + * \brief Analyse init flags + **/ +static int mpegSurroundDecoder_CalcInitFlags(SPATIAL_SPECIFIC_CONFIG *pSsc1, + SPATIAL_SPECIFIC_CONFIG *pSsc2, + int upmixTypeFlag, + int binauralQualityFlag, + int partiallyComplexFlag, + int *ctrlFlags) { + /* Analyse core coder */ + if (pSsc1->coreCodec != pSsc2->coreCodec) { + *ctrlFlags |= MASK_MPEGS_INIT_ALL_STATES; + *ctrlFlags |= MASK_MPEGS_INIT_ALL_PARAMS; + } else { + /* Analyse elements for initialization of space analysis qmf filterbank */ + if ((partiallyComplexFlag) || (pSsc1->treeConfig != pSsc2->treeConfig) || + (pSsc1->samplingFreq != pSsc2->samplingFreq)) { + *ctrlFlags |= MPEGS_INIT_STATES_ANA_QMF_FILTER; + *ctrlFlags |= MPEGS_INIT_STATES_ANA_HYB_FILTER; + } + + /* Analyse elements for initialization of space synthesis qmf filterbank */ + if ((upmixTypeFlag) || (partiallyComplexFlag) || + (pSsc1->treeConfig != pSsc2->treeConfig) || + (pSsc1->samplingFreq != pSsc2->samplingFreq) || + (pSsc1->bsFixedGainDMX != pSsc2->bsFixedGainDMX)) { + *ctrlFlags |= MPEGS_INIT_STATES_SYN_QMF_FILTER; + } + + /* Analyse elements for initialization of decorrelator */ + if ((upmixTypeFlag) || (partiallyComplexFlag) || + (pSsc1->treeConfig != pSsc2->treeConfig) || + (pSsc1->samplingFreq != pSsc2->samplingFreq) || + (pSsc1->decorrConfig != pSsc2->decorrConfig)) { + *ctrlFlags |= MPEGS_INIT_STATES_DECORRELATOR; + } + + /* Analyse elements for initialization of m1 and m2 calculation */ + if ((upmixTypeFlag) || (binauralQualityFlag) || + (pSsc1->treeConfig != pSsc2->treeConfig) || + (pSsc1->samplingFreq != pSsc2->samplingFreq)) + + { + *ctrlFlags |= MPEGS_INIT_STATES_M1M2; + } + + /* Analyse elements for initialization of GES */ + if ((upmixTypeFlag) || (pSsc1->treeConfig != pSsc2->treeConfig) || + (pSsc1->tempShapeConfig != pSsc2->tempShapeConfig)) { + *ctrlFlags |= MPEGS_INIT_STATES_GES; + } + + /* Analyse elements for initialization of FDreverb */ + if ((upmixTypeFlag) || (binauralQualityFlag) || (partiallyComplexFlag) || + (pSsc1->samplingFreq != pSsc2->samplingFreq) || + (pSsc1->nTimeSlots != pSsc2->nTimeSlots)) { + *ctrlFlags |= MPEGS_INIT_STATES_REVERB; + } + + /* Reset previous frame data whenever the config changes */ + if (*ctrlFlags & MPEGS_INIT_CONFIG) { + *ctrlFlags |= MPEGS_INIT_STATES_PARAM; + } + } + + return MPS_OK; +} + +/** + * \brief Reset MPEG Surround status info + **/ +static void updateMpegSurroundDecoderStatus( + CMpegSurroundDecoder *pMpegSurroundDecoder, int initFlags, + MPEGS_SYNCSTATE fOnSync, MPEGS_ANCSTARTSTOP ancStartStopPrev) { + pMpegSurroundDecoder->initFlags[pMpegSurroundDecoder->bsFrameDecode] |= + initFlags; + if ((pMpegSurroundDecoder->mpegSurroundSscIsGlobalCfg != 0) && + (pMpegSurroundDecoder->fOnSync[pMpegSurroundDecoder->bsFrameDecode] >= + MPEGS_SYNC_FOUND) && + (fOnSync < MPEGS_SYNC_FOUND)) { + pMpegSurroundDecoder->fOnSync[pMpegSurroundDecoder->bsFrameDecode] = + MPEGS_SYNC_FOUND; + } else { + pMpegSurroundDecoder->fOnSync[pMpegSurroundDecoder->bsFrameDecode] = + fOnSync; + } + pMpegSurroundDecoder->ancStartStopPrev = ancStartStopPrev; +} + +static SACDEC_ERROR mpegSurroundDecoder_Create( + CMpegSurroundDecoder **pMpegSurroundDecoder, int stereoConfigIndex, + HANDLE_FDK_QMF_DOMAIN pQmfDomain); + +SAC_INSTANCE_AVAIL +mpegSurroundDecoder_IsFullMpegSurroundDecoderInstanceAvailable( + CMpegSurroundDecoder *pMpegSurroundDecoder) { + SAC_INSTANCE_AVAIL instanceAvailable = SAC_INSTANCE_NOT_FULL_AVAILABLE; + + if (pMpegSurroundDecoder->pSpatialDec != NULL) { + instanceAvailable = SAC_INSTANCE_FULL_AVAILABLE; + } + + return instanceAvailable; +} + +SACDEC_ERROR mpegSurroundDecoder_Open( + CMpegSurroundDecoder **pMpegSurroundDecoder, int stereoConfigIndex, + HANDLE_FDK_QMF_DOMAIN pQmfDomain) { + SACDEC_ERROR error; + + error = mpegSurroundDecoder_Create(pMpegSurroundDecoder, stereoConfigIndex, + pQmfDomain); + + return error; +} + +/** + * \brief Renamed function from getUpmixType to check_UParam_Build_DecConfig. + * This function checks if user params, decoder config and SSC are valid + * and if the decoder build can handle all this settings. + * The upmix type may be modified by this function. + * It is called in initMpegSurroundDecoder() after the ssc parse check, + * to have all checks in one place and to ensure these checks are always + * performed if config changes (inband and out-of-band). + * + * \param pUserParams User data handle. + * \param pDecConfig decoder config handle. + * \param pSsc spatial specific config handle. + * \param pUpmixType upmix type which is set by this function + * + * \return MPS_OK on sucess, and else on failure. + */ +static SACDEC_ERROR check_UParam_Build_DecConfig( + SPATIALDEC_PARAM const *pUserParams, SPATIAL_DEC_CONFIG const *pDecConfig, + const SPATIAL_SPECIFIC_CONFIG *pSsc, SPATIAL_DEC_UPMIX_TYPE *pUpmixType) { + int dmxChannels, outChannels, maxNumOutChannels; + + FDK_ASSERT(pUserParams != NULL); + FDK_ASSERT(pUpmixType != NULL); + + /* checks if implementation can handle the Ssc */ + + switch (pSsc->treeConfig) { + case SPATIALDEC_MODE_RSVD7: /* 212 */ + dmxChannels = 1; + outChannels = 2; + break; + default: + return MPS_UNSUPPORTED_CONFIG; + } + + /* ------------------------------------------- */ + + /* Analyse pDecConfig params */ + switch (pDecConfig->binauralMode) { + case BINAURAL_NONE: + break; + default: + return MPS_UNSUPPORTED_CONFIG; + } + + switch (pDecConfig->decoderMode) { + case EXT_HQ_ONLY: + break; + default: + return MPS_UNSUPPORTED_CONFIG; + } + + switch (pDecConfig->maxNumOutputChannels) { + case OUTPUT_CHANNELS_DEFAULT: + /* No special restrictions -> Get the level restriction: */ + switch (pDecConfig->decoderLevel) { + case DECODER_LEVEL_0: + maxNumOutChannels = 2; + break; + default: + return MPS_UNSUPPORTED_CONFIG; + } + break; + case OUTPUT_CHANNELS_2_0: + maxNumOutChannels = 2; + break; + default: + return MPS_UNSUPPORTED_CONFIG; + } + /* ------------------------- */ + + /* check if we can handle user params */ + if (pUserParams->blindEnable == 1) { + return MPS_UNSUPPORTED_CONFIG; + } + { + switch ((SAC_DEC_OUTPUT_MODE)pUserParams->outputMode) { + case SACDEC_OUT_MODE_NORMAL: + if (maxNumOutChannels >= outChannels) { + *pUpmixType = UPMIX_TYPE_NORMAL; + } else { + { *pUpmixType = UPMIX_TYPE_BYPASS; } + } + break; + case SACDEC_OUT_MODE_STEREO: + if (dmxChannels == 1) { + if (outChannels == 2) { + *pUpmixType = UPMIX_TYPE_NORMAL; + } + } else { + *pUpmixType = UPMIX_TYPE_BYPASS; + } + break; + case SACDEC_OUT_MODE_6CHANNEL: + if (outChannels > 6) { + { *pUpmixType = UPMIX_TYPE_BYPASS; } + } else { + *pUpmixType = UPMIX_TYPE_NORMAL; + } + break; + default: + return MPS_UNSUPPORTED_CONFIG; + } + } + + return MPS_OK; +} + +/** + * \brief Init MPEG Surround decoder. + **/ +static SACDEC_ERROR initMpegSurroundDecoder( + CMpegSurroundDecoder *pMpegSurroundDecoder) { + SACDEC_ERROR err; + int initFlags = MPEGS_INIT_NONE, initFlagsDec; + int upmixTypeCurr = pMpegSurroundDecoder->upmixType; + + FDK_ASSERT(pMpegSurroundDecoder != NULL); + + SPATIAL_SPECIFIC_CONFIG *const pSSCinput = + &pMpegSurroundDecoder->spatialSpecificConfigBackup; + SPATIAL_SPECIFIC_CONFIG *const pSSCtarget = + &pMpegSurroundDecoder + ->spatialSpecificConfig[pMpegSurroundDecoder->bsFrameDecode]; + initFlagsDec = + pMpegSurroundDecoder->initFlags[pMpegSurroundDecoder->bsFrameDecode]; + + if (pSSCinput->coreCodec != AOT_USAC) { + /* here we check if we have a valid Ssc */ + err = sscParseCheck(pSSCinput); + if (err != MPS_OK) goto bail; + } + + /* here we check if Ssc matches build; also check UParams and DecConfig */ + /* if desired upmixType is changes */ + err = check_UParam_Build_DecConfig( + &pMpegSurroundDecoder->mpegSurroundUserParams, + &pMpegSurroundDecoder->decConfig, pSSCinput, + &pMpegSurroundDecoder->upmixType); + if (err != MPS_OK) goto bail; + + /* init config */ + if (initFlagsDec & MPEGS_INIT_CHANGE_HEADER) { + initFlags |= MPEGS_INIT_CONFIG; + } + /* init all states */ + if (initFlagsDec & MPEGS_INIT_CLEAR_HISTORY) { + initFlags |= MASK_MPEGS_INIT_ALL_STATES; + } + if (initFlagsDec & MPEGS_INIT_CHANGE_CONCEAL_PARAMS) { + initFlags |= MPEGS_INIT_PARAMS_ERROR_CONCEALMENT; + } + + if (initFlagsDec & MPEGS_INIT_ENFORCE_REINIT) { + /* init all states */ + initFlags |= MASK_MPEGS_INIT_ALL_STATES; + initFlags |= MASK_MPEGS_INIT_ALL_PARAMS; + } else { + /* analyse states which have to be initialized */ + mpegSurroundDecoder_CalcInitFlags( + pSSCtarget, pSSCinput, + (upmixTypeCurr != + pMpegSurroundDecoder->upmixType), /* upmixType changed */ + 0, (initFlagsDec & MPEGS_INIT_CHANGE_PARTIALLY_COMPLEX) ? 1 : 0, + &initFlags); + } + + { + int nrOfQmfBands; + FDKmemcpy(pSSCtarget, pSSCinput, sizeof(SPATIAL_SPECIFIC_CONFIG)); + + nrOfQmfBands = mpegSurroundDecoder_GetNrOfQmfBands( + pSSCtarget, pSSCtarget->samplingFreq); + err = FDK_SpatialDecInit( + pMpegSurroundDecoder->pSpatialDec, + &pMpegSurroundDecoder->bsFrames[pMpegSurroundDecoder->bsFrameDecode], + pSSCtarget, nrOfQmfBands, pMpegSurroundDecoder->upmixType, + &pMpegSurroundDecoder->mpegSurroundUserParams, initFlags); + + if (err != MPS_OK) goto bail; + + /* Signal that we got a header and can go on decoding */ + if (err == MPS_OK) { + initFlagsDec = MPEGS_INIT_OK; + { + pMpegSurroundDecoder->fOnSync[pMpegSurroundDecoder->bsFrameDecode] = + MPEGS_SYNC_FOUND; + } + } + } + +bail: + pMpegSurroundDecoder->initFlags[pMpegSurroundDecoder->bsFrameDecode] = + initFlagsDec; + return err; +} + +/** + * \brief Init MPEG Surround decoder. + **/ +SACDEC_ERROR mpegSurroundDecoder_Init( + CMpegSurroundDecoder *pMpegSurroundDecoder) { + SACDEC_ERROR err = MPS_OK; + + if (pMpegSurroundDecoder->initFlags[pMpegSurroundDecoder->bsFrameDecode]) { + err = initMpegSurroundDecoder(pMpegSurroundDecoder); + } + return err; +} + +/** + * \brief Open MPEG Surround decoder. + **/ +static SACDEC_ERROR mpegSurroundDecoder_Create( + CMpegSurroundDecoder **pMpegSurroundDecoder, int stereoConfigIndex, + HANDLE_FDK_QMF_DOMAIN pQmfDomain) { + SACDEC_ERROR err = MPS_OK; + CMpegSurroundDecoder *sacDec = NULL; + spatialDec *self = NULL; + + /* decoderLevel decoderMode maxNumOutputChannels binauralMode */ + static const SPATIAL_DEC_CONFIG decConfig = { + (CFG_LEVEL)(0), EXT_HQ_ONLY, OUTPUT_CHANNELS_DEFAULT, BINAURAL_NONE}; + + if (*pMpegSurroundDecoder == NULL) { + FDK_ALLOCATE_MEMORY_1D(*pMpegSurroundDecoder, 1, CMpegSurroundDecoder) + + for (int i = 0; i < 1; i++) { + err = SpatialDecCreateBsFrame(&(*pMpegSurroundDecoder)->bsFrames[i], + &(*pMpegSurroundDecoder)->llState); + if (err != MPS_OK) { + sacDec = *pMpegSurroundDecoder; + goto bail; + } + } + (*pMpegSurroundDecoder)->pQmfDomain = pQmfDomain; + + (*pMpegSurroundDecoder)->bsFrameDelay = 1; + (*pMpegSurroundDecoder)->bsFrameParse = 0; + (*pMpegSurroundDecoder)->bsFrameDecode = 0; + + return err; + } else { + sacDec = *pMpegSurroundDecoder; + } + + if (sacDec->pSpatialDec == NULL) { + if ((self = FDK_SpatialDecOpen(&decConfig, stereoConfigIndex)) == NULL) { + err = MPS_OUTOFMEMORY; + goto bail; + } + } else { + self = sacDec->pSpatialDec; + } + + self->pQmfDomain = sacDec->pQmfDomain; + + sacDec->pSpatialDec = self; + + /* default parameter set */ + sacDec->mpegSurroundUserParams.outputMode = SACDEC_OUT_MODE_NORMAL; + sacDec->mpegSurroundUserParams.blindEnable = 0; + sacDec->mpegSurroundUserParams.bypassMode = 0; + sacDec->mpegSurroundUserParams.concealMethod = 1; + sacDec->mpegSurroundUserParams.concealNumKeepFrames = 10; + sacDec->mpegSurroundUserParams.concealFadeOutSlopeLength = 5; + sacDec->mpegSurroundUserParams.concealFadeInSlopeLength = 5; + sacDec->mpegSurroundUserParams.concealNumReleaseFrames = 3; + sacDec->mpegSurroundSscIsGlobalCfg = 0; + sacDec->mpegSurroundUseTimeInterface = 1; + sacDec->mpegSurroundDecoderLevel = decConfig.decoderLevel; + + sacDec->upmixType = UPMIX_TYPE_NORMAL; + + /* signalize spatial decoder re-initalization */ + updateMpegSurroundDecoderStatus(sacDec, MPEGS_INIT_ENFORCE_REINIT, + MPEGS_SYNC_LOST, MPEGS_STOP); + + /* return decoder instance */ + *pMpegSurroundDecoder = sacDec; + sacDec->decConfig = decConfig; + + SpatialDecInitParserContext(sacDec->pSpatialDec); + + return err; + +bail: + if (sacDec != NULL) { + mpegSurroundDecoder_Close(sacDec); + } + *pMpegSurroundDecoder = NULL; + if (err == MPS_OK) { + return MPS_OUTOFMEMORY; + } else { + return err; + } +} + +/** + * \brief Config MPEG Surround decoder. + **/ +SACDEC_ERROR mpegSurroundDecoder_Config( + CMpegSurroundDecoder *pMpegSurroundDecoder, HANDLE_FDK_BITSTREAM hBs, + AUDIO_OBJECT_TYPE coreCodec, INT samplingRate, INT stereoConfigIndex, + INT coreSbrFrameLengthIndex, INT configBytes, const UCHAR configMode, + UCHAR *configChanged) { + SACDEC_ERROR err = MPS_OK; + + switch (coreCodec) { + case AOT_DRM_USAC: + case AOT_USAC: + if (configMode == AC_CM_DET_CFG_CHANGE) { + /* In config detection mode write spatial specific config parameters + * into temporarily allocated structure */ + SPATIAL_SPECIFIC_CONFIG spatialSpecificConfig; + err = SpatialDecParseMps212Config( + hBs, &spatialSpecificConfig, samplingRate, coreCodec, + stereoConfigIndex, coreSbrFrameLengthIndex); + } else { + err = SpatialDecParseMps212Config( + hBs, &pMpegSurroundDecoder->spatialSpecificConfigBackup, + samplingRate, coreCodec, stereoConfigIndex, + coreSbrFrameLengthIndex); + } + break; + case AOT_ER_AAC_ELD: + case AOT_ER_AAC_LD: + err = SpatialDecParseSpecificConfig( + hBs, &pMpegSurroundDecoder->spatialSpecificConfigBackup, configBytes, + coreCodec); + break; + default: + err = MPS_UNSUPPORTED_FORMAT; + break; + } + + if (err != MPS_OK) { + goto bail; + } + + if (configMode & AC_CM_DET_CFG_CHANGE) { + return err; + } + + if (configMode & AC_CM_ALLOC_MEM) { + if (*configChanged) { + if ((err = mpegSurroundDecoder_Open(&pMpegSurroundDecoder, + stereoConfigIndex, NULL))) { + return err; + } + } + } + + { + SPATIAL_SPECIFIC_CONFIG *sscParse = + &pMpegSurroundDecoder + ->spatialSpecificConfig[pMpegSurroundDecoder->bsFrameParse]; + + if (FDK_SpatialDecCompareSpatialSpecificConfigHeader( + &pMpegSurroundDecoder->spatialSpecificConfigBackup, sscParse)) { + pMpegSurroundDecoder->initFlags[pMpegSurroundDecoder->bsFrameParse] |= + MPEGS_INIT_CHANGE_HEADER; + /* Error resilience code */ + if (pMpegSurroundDecoder->pSpatialDec == NULL) { + err = MPS_NOTOK; + goto bail; + } + SpatialDecInitParserContext(pMpegSurroundDecoder->pSpatialDec); + pMpegSurroundDecoder->pSpatialDec->pConfigCurrent = + &pMpegSurroundDecoder + ->spatialSpecificConfig[pMpegSurroundDecoder->bsFrameDecode]; + } + } + + if (err == MPS_OK) { + /* We got a valid out-of-band configuration so label it accordingly. */ + pMpegSurroundDecoder->mpegSurroundSscIsGlobalCfg = 1; + } + +bail: + return err; +} + +/** + * \brief Determine MPEG Surround operation mode. + **/ +static MPEGS_OPMODE mpegSurroundOperationMode( + CMpegSurroundDecoder *pMpegSurroundDecoder, int mpsDataBits) { + MPEGS_OPMODE mode; + + { + if ((mpsDataBits > 0) && + (pMpegSurroundDecoder->mpegSurroundUserParams.blindEnable == 0)) { + mode = MPEGS_OPMODE_MPS_PAYLOAD; /* Mode: Normal, Stereo or Binaural */ + } else { + mode = MPEGS_OPMODE_NO_MPS_PAYLOAD; /* Mode: No MPEG Surround Payload */ + updateMpegSurroundDecoderStatus(pMpegSurroundDecoder, + MPEGS_INIT_ERROR_PAYLOAD, MPEGS_SYNC_LOST, + MPEGS_STOP); + } + } + + return (mode); +} + +/** + * \brief Check ssc for parse errors. + * This one is called in initMpegSurroundDecoder() + * to ensure checking of inband and out-of-band mps configs. + * Only parse errors checked here! Check for valid config is done + * in check_UParam_Build_DecConfig()! + * + * \param pSsc spatial specific config handle. + * + * \return MPS_OK on sucess, and else on parse error. + */ +static SACDEC_ERROR sscParseCheck(const SPATIAL_SPECIFIC_CONFIG *pSsc) { + SACDEC_ERROR err = MPS_OK; + + if (pSsc->samplingFreq > 96000) return MPS_PARSE_ERROR; + if (pSsc->samplingFreq < 8000) return MPS_PARSE_ERROR; + + switch (pSsc->freqRes) { + case SPATIALDEC_FREQ_RES_28: + case SPATIALDEC_FREQ_RES_20: + case SPATIALDEC_FREQ_RES_14: + case SPATIALDEC_FREQ_RES_10: + case SPATIALDEC_FREQ_RES_23: + case SPATIALDEC_FREQ_RES_15: + case SPATIALDEC_FREQ_RES_12: + case SPATIALDEC_FREQ_RES_9: + case SPATIALDEC_FREQ_RES_7: + case SPATIALDEC_FREQ_RES_5: + case SPATIALDEC_FREQ_RES_4: + break; + case SPATIALDEC_FREQ_RES_40: /* 40 doesn't exist in ISO/IEC 23003-1 */ + default: + return MPS_PARSE_ERROR; + } + + if ((pSsc->treeConfig < 0) || (pSsc->treeConfig > 7)) { + return MPS_PARSE_ERROR; + } + + if ((pSsc->quantMode < 0) || (pSsc->quantMode > 2)) { + return MPS_PARSE_ERROR; + } + + if (pSsc->tempShapeConfig == 3) { + return MPS_PARSE_ERROR; + } + + if (pSsc->decorrConfig == 3) { + return MPS_PARSE_ERROR; + } + + /* now we are sure there were no parsing errors */ + + return err; +} + +/** + * \brief Check number of time slots + * + * Basically the mps frame length must be a multiple of the core coder frame + * length. The below table shows all valid configurations in detail. See ISO/IEC + * 23003-1: "Table 4A - Allowed values for bsFrameLength in the Baseline MPEG + * Surround Profile" + * + * Downmix Coder Downmix Code Allowed values for bsFrameLength + * Allowed frame sizes for normal, downsampled and upsampled MPS Framelength + * (QMF Samples) + * + * AAC 1024 16 15, 31, 47, 63 1024 2048 3072 4096 + * downsampled MPS 32 31, 63 1024 2048 upsampled MPS + * 8 7, 15, 23, 31, 39, 47, 55, 63, 71 1024 2048 3072 4096 + * 5120 6144 7168 8192 9216 + * + * AAC 960 15 14, 29, 44, 59 960 1920 2880 3840 + * downsampled MPS 30 29, 59 960 1920 upsampled MPS + * 7,5 14, 29, 44, 59 1920 3840 5760 7680 + * + * HE-AAC 1024/2048 32 31, 63 2048 4096 downsampled MPS + * 64 63 2048 upsampled MPS + * 16 15, 31, 47, 63 2048 4096 6144 8192 + * + * HE-AAC 960/1920 30 29, 59 1920 3840 downsampled MPS + * 60 59 1920 upsampled MPS + * 15 14, 29, 44, 59 1920 3840 5760 7680 + * + * BSAC 16 15, 31, 47, 63 1024 2048 3072 4096 + * downsampled MPS 32 31, 63 1024 2048 upsampled MPS + * 8 7, 15, 23, 31, 39, 47, 55, 63, 71 1024 2048 3072 4096 + * 5120 6144 7168 8192 9216 + * + * BSAC with SBR 32 31, 63 2048 4096 downsampled MPS + * 64 63 2048 upsampled MPS + * 16 15, 31, 47, 63 2048 4096 6144 8192 + * + * AAC LD 512 8 7, 15, 23, 31, 39, 47, 55, 63, 71 + * 512 1024 1536 2048 2560 3072 3584 4096 4608 downsampled MPS + * 16 15, 31, 47, 63 512 1024 1536 2048 + * + * AAC ELD 512 8 7, 15, 23, 31, 39, 47, 55, 63, 71 + * 512 1024 1536 2048 2560 3072 3584 4096 4608 downsampled MPS + * 16 15, 31, 47, 63 512 1024 1536 2048 + * + * AAC ELD with SBR 512/1024 16 15, 31, 47, 63 1024 2048 3072 4096 + * downsampled MPS 32 31, 63 1024 2048 upsampled MPS + * 8 7, 15, 23, 31, 39, 47, 55, 63, 71 1024 2048 3072 4096 + * 5120 6144 7168 8192 9216 + * + * MPEG1/2 Layer II 18 17, 35, 53, 71 1152 2304 3456 4608 + * downsampled MPS 36 35, 71 1152 2304 + * + * MPEG1/2 Layer III 18 17, 35, 53, 71 1152 2304 3456 4608 + * downsampled MPS 36 35, 71 1152 2304 + * + * \param frameLength + * \param qmfBands + * \param timeSlots + * + * \return error code + */ +SACDEC_ERROR checkTimeSlots(int frameLength, int qmfBands, int timeSlots) { + int len; + int maxFrameLength; + + if (qmfBands == 64) { + /* normal MPEG Surround */ + switch (frameLength) { + case 960: + case 1920: + maxFrameLength = 3840; + break; + case 1024: + case 2048: + maxFrameLength = 4096; + break; + case 512: + case 1152: + maxFrameLength = 4608; + break; + default: + return MPS_PARSE_ERROR; + } + } else if (qmfBands == 32) { + /* downsampled MPEG Surround */ + switch (frameLength) { + case 960: + case 1920: + maxFrameLength = 1920; + break; + case 512: + case 1024: + case 2048: + maxFrameLength = 2048; + break; + case 1152: + maxFrameLength = 2304; + break; + default: + return MPS_PARSE_ERROR; + } + } else if (qmfBands == 128) { + /* upsampled MPEG Surround */ + switch (frameLength) { + case 1920: + maxFrameLength = 7680; + break; + case 1024: + maxFrameLength = 9216; + break; + case 2048: + maxFrameLength = 8192; + break; + case 512: + case 960: + case 1152: + /* no break, no support for upsampled MPEG Surround */ + default: + return MPS_PARSE_ERROR; + } + } else { + return MPS_PARSE_ERROR; + } + + len = frameLength; + + while (len <= maxFrameLength) { + if (len == timeSlots * qmfBands) { + return MPS_OK; + } + len += frameLength; + } + return MPS_PARSE_ERROR; +} + +/** + * \brief Check ssc for consistency (e.g. bit errors could cause trouble) + * First of currently two ssc-checks. + * This (old) one is called in mpegSurroundDecoder_Apply() + * only if inband mps config is contained in stream. + * + * New ssc check is split in two functions sscParseCheck() and + * check_UParam_Build_DecConfig(). sscParseCheck() checks only for correct + * parsing. check_UParam_Build_DecConfig() is used to check if we have a + * valid config. Both are called in initMpegSurroundDecoder() to ensure + * checking of inband and out-of-band mps configs. + * + * If this function can be integrated into the new functions. + * We can remove this one. + * + * \param pSsc spatial specific config handle. + * \param frameLength + * \param sampleRate + * + * \return MPS_OK on sucess, and else on failure. + */ +static SACDEC_ERROR sscCheckInBand(SPATIAL_SPECIFIC_CONFIG *pSsc, + int frameLength, int sampleRate) { + SACDEC_ERROR err = MPS_OK; + int qmfBands; + + FDK_ASSERT(pSsc != NULL); + + /* core fs and mps fs must match */ + if (pSsc->samplingFreq != sampleRate) { + err = MPS_PARSE_ERROR /* MPEGSDEC_SSC_PARSE_ERROR */; + } + + qmfBands = mpegSurroundDecoder_GetNrOfQmfBands(pSsc, pSsc->samplingFreq); + + if (checkTimeSlots(frameLength, qmfBands, pSsc->nTimeSlots) != MPS_OK) { + err = MPS_PARSE_ERROR; + } + + return err; +} + +SACDEC_ERROR +mpegSurroundDecoder_ConfigureQmfDomain( + CMpegSurroundDecoder *pMpegSurroundDecoder, + SAC_INPUT_CONFIG sac_dec_interface, UINT coreSamplingRate, + AUDIO_OBJECT_TYPE coreCodec) { + SACDEC_ERROR err = MPS_OK; + FDK_QMF_DOMAIN_GC *pGC = NULL; + + if (pMpegSurroundDecoder == NULL) { + return MPS_INVALID_HANDLE; + } + + FDK_ASSERT(pMpegSurroundDecoder->pSpatialDec); + + pGC = &pMpegSurroundDecoder->pQmfDomain->globalConf; + if (pMpegSurroundDecoder->mpegSurroundSscIsGlobalCfg) { + SPATIAL_SPECIFIC_CONFIG *pSSC = + &pMpegSurroundDecoder->spatialSpecificConfigBackup; + if (sac_dec_interface == SAC_INTERFACE_TIME) { + /* For SAC_INTERFACE_QMF these parameters are set by SBR. */ + pGC->nBandsAnalysis_requested = mpegSurroundDecoder_GetNrOfQmfBands( + pSSC, coreSamplingRate); /* coreSamplingRate == outputSamplingRate for + SAC_INTERFACE_TIME */ + pGC->nBandsSynthesis_requested = pGC->nBandsAnalysis_requested; + pGC->nInputChannels_requested = + fMax((UINT)pSSC->nInputChannels, (UINT)pGC->nInputChannels_requested); + } + pGC->nOutputChannels_requested = + fMax((UINT)pSSC->nOutputChannels, (UINT)pGC->nOutputChannels_requested); + } else { + if (sac_dec_interface == SAC_INTERFACE_TIME) { + /* For SAC_INTERFACE_QMF these parameters are set by SBR. */ + pGC->nBandsAnalysis_requested = mpegSurroundDecoder_GetNrOfQmfBands( + NULL, coreSamplingRate); /* coreSamplingRate == outputSamplingRate for + SAC_INTERFACE_TIME */ + pGC->nBandsSynthesis_requested = pGC->nBandsAnalysis_requested; + pGC->nInputChannels_requested = + pMpegSurroundDecoder->pSpatialDec->createParams.maxNumInputChannels; + } + pGC->nOutputChannels_requested = + pMpegSurroundDecoder->pSpatialDec->createParams.maxNumOutputChannels; + } + pGC->nQmfProcBands_requested = 64; + pGC->nQmfProcChannels_requested = + fMin((INT)pGC->nInputChannels_requested, + pMpegSurroundDecoder->pSpatialDec->createParams.maxNumInputChannels); + + if (coreCodec == AOT_ER_AAC_ELD) { + pGC->flags_requested |= QMF_FLAG_MPSLDFB; + } + + return err; +} + +/** + * \brief Decode MPEG Surround frame. + **/ +int mpegSurroundDecoder_ParseNoHeader( + CMpegSurroundDecoder *pMpegSurroundDecoder, HANDLE_FDK_BITSTREAM hBs, + int *pMpsDataBits, int fGlobalIndependencyFlag) { + SACDEC_ERROR err = MPS_OK; + SPATIAL_SPECIFIC_CONFIG *sscParse; + int bitsAvail, numSacBits; + + if (pMpegSurroundDecoder == NULL || hBs == NULL) { + return MPS_INVALID_HANDLE; + } + + sscParse = &pMpegSurroundDecoder + ->spatialSpecificConfig[pMpegSurroundDecoder->bsFrameParse]; + + bitsAvail = FDKgetValidBits(hBs); + + /* First spatial specific config is parsed into spatialSpecificConfigBackup, + * second spatialSpecificConfigBackup is copied into + * spatialSpecificConfig[bsFrameDecode] */ + if (pMpegSurroundDecoder->initFlags[pMpegSurroundDecoder->bsFrameParse]) { + FDKmemcpy(sscParse, &pMpegSurroundDecoder->spatialSpecificConfigBackup, + sizeof(SPATIAL_SPECIFIC_CONFIG)); + pMpegSurroundDecoder->fOnSync[pMpegSurroundDecoder->bsFrameParse] = + MPEGS_SYNC_FOUND; + } + + if (bitsAvail <= 0) { + err = MPS_PARSE_ERROR; + } else { + err = SpatialDecParseFrameData( + pMpegSurroundDecoder->pSpatialDec, + &pMpegSurroundDecoder->bsFrames[pMpegSurroundDecoder->bsFrameParse], + hBs, sscParse, (UPMIXTYPE)pMpegSurroundDecoder->upmixType, + fGlobalIndependencyFlag); + if (err == MPS_OK) { + pMpegSurroundDecoder->bsFrames[pMpegSurroundDecoder->bsFrameParse] + .newBsData = 1; + } + } + + numSacBits = bitsAvail - (INT)FDKgetValidBits(hBs); + + if (numSacBits > bitsAvail) { + pMpegSurroundDecoder->bsFrames[pMpegSurroundDecoder->bsFrameParse] + .newBsData = 0; + err = MPS_PARSE_ERROR; + } + + *pMpsDataBits -= numSacBits; + + return err; +} + +/** + * \brief Check, if ancType is valid. + **/ +static int isValidAncType(CMpegSurroundDecoder *pMpegSurroundDecoder, + int ancType) { + int ret = 1; + + if ((ancType != MPEGS_ANCTYPE_HEADER_AND_FRAME) && + (ancType != MPEGS_ANCTYPE_FRAME)) { + ret = 0; + } + + if (ret == 0) { + updateMpegSurroundDecoderStatus(pMpegSurroundDecoder, + MPEGS_INIT_ERROR_PAYLOAD, MPEGS_SYNC_LOST, + MPEGS_STOP); + } + + return (ret); +} + +/** + * \brief Check, if ancStartStop is valid. + **/ +static int isValidAncStartStop(CMpegSurroundDecoder *pMpegSurroundDecoder, + int ancStartStop) { + int ret = 1; + + switch (ancStartStop) { + case MPEGS_START: + /* Sequence start - start and continue - start not allowed */ + if ((pMpegSurroundDecoder->ancStartStopPrev == MPEGS_START) || + (pMpegSurroundDecoder->ancStartStopPrev == MPEGS_CONTINUE)) { + ret = 0; + } + break; + + case MPEGS_STOP: + /* MPS payload of the previous frame must be valid if current type is stop + Sequence startstop - stop and stop - stop not allowed + Sequence startstop - continue and stop - continue are allowed */ + if ((pMpegSurroundDecoder->ancStartStopPrev == MPEGS_STOP) || + (pMpegSurroundDecoder->ancStartStopPrev == MPEGS_START_STOP)) { + ret = 0; + } + break; + + case MPEGS_CONTINUE: + case MPEGS_START_STOP: + /* No error detection possible for this states */ + break; + } + + if (ret == 0) { + updateMpegSurroundDecoderStatus(pMpegSurroundDecoder, + MPEGS_INIT_ERROR_PAYLOAD, MPEGS_SYNC_LOST, + MPEGS_STOP); + } else { + pMpegSurroundDecoder->ancStartStopPrev = (MPEGS_ANCSTARTSTOP)ancStartStop; + } + + return (ret); +} + +int mpegSurroundDecoder_Parse(CMpegSurroundDecoder *pMpegSurroundDecoder, + HANDLE_FDK_BITSTREAM hBs, int *pMpsDataBits, + AUDIO_OBJECT_TYPE coreCodec, int sampleRate, + int frameSize, int fGlobalIndependencyFlag) { + SACDEC_ERROR err = MPS_OK; + SPATIAL_SPECIFIC_CONFIG *sscParse; + SPATIAL_BS_FRAME *bsFrame; + HANDLE_FDK_BITSTREAM hMpsBsData = NULL; + FDK_BITSTREAM mpsBsData; + int mpsDataBits = *pMpsDataBits; + int mpsBsBits; + MPEGS_ANCTYPE ancType; + MPEGS_ANCSTARTSTOP ancStartStop; + + if (pMpegSurroundDecoder == NULL) { + return MPS_INVALID_HANDLE; + } + + FDK_ASSERT(pMpegSurroundDecoder->pSpatialDec); + + mpsBsBits = FDKgetValidBits(hBs); + + sscParse = &pMpegSurroundDecoder + ->spatialSpecificConfig[pMpegSurroundDecoder->bsFrameParse]; + bsFrame = &pMpegSurroundDecoder->bsFrames[pMpegSurroundDecoder->bsFrameParse]; + + /* + Find operation mode of mpeg surround decoder: + - MPEGS_OPMODE_EMM: Mode: Enhanced Matrix Mode (Blind) + - MPEGS_OPMODE_MPS_PAYLOAD: Mode: Normal, Stereo or Binaural + - MPEGS_OPMODE_NO_MPS_PAYLOAD: Mode: No MpegSurround Payload + */ + { + /* Parse ancType and ancStartStop */ + ancType = (MPEGS_ANCTYPE)FDKreadBits(hBs, 2); + ancStartStop = (MPEGS_ANCSTARTSTOP)FDKreadBits(hBs, 2); + mpsDataBits -= 4; + + /* Set valid anc type flag, if ancType signals a payload with either header + * and frame or frame */ + if (isValidAncType(pMpegSurroundDecoder, ancType)) { + /* Set valid anc startstop flag, if transmitted sequence is not illegal */ + if (isValidAncStartStop(pMpegSurroundDecoder, ancStartStop)) { + switch (ancStartStop) { + case MPEGS_START: + /* Assuming that core coder frame size (AAC) is smaller than MPS + coder frame size. Save audio data for next frame. */ + if (mpsDataBits > MPS_DATA_BUFFER_SIZE * 8) { + err = MPS_NOTOK; + goto bail; + } + for (int i = 0; i < mpsDataBits / 8; i++) { + pMpegSurroundDecoder->mpsData[i] = FDKreadBits(hBs, 8); + } + pMpegSurroundDecoder->mpsDataBits = mpsDataBits; + break; + + case MPEGS_CONTINUE: + case MPEGS_STOP: + /* Assuming that core coder frame size (AAC) is smaller than MPS + coder frame size. Save audio data for next frame. */ + if ((pMpegSurroundDecoder->mpsDataBits + mpsDataBits) > + MPS_DATA_BUFFER_SIZE * 8) { + err = MPS_NOTOK; + goto bail; + } + for (int i = 0; i < mpsDataBits / 8; i++) { + pMpegSurroundDecoder + ->mpsData[(pMpegSurroundDecoder->mpsDataBits / 8) + i] = + FDKreadBits(hBs, 8); + } + pMpegSurroundDecoder->mpsDataBits += mpsDataBits; + FDKinitBitStream(&mpsBsData, pMpegSurroundDecoder->mpsData, + MAX_BUFSIZE_BYTES, + pMpegSurroundDecoder->mpsDataBits, BS_READER); + hMpsBsData = &mpsBsData; + break; + + case MPEGS_START_STOP: + pMpegSurroundDecoder->mpsDataBits = mpsDataBits; + hMpsBsData = hBs; + break; + + default: + FDK_ASSERT(0); + } + + if ((ancStartStop == MPEGS_STOP) || + (ancStartStop == MPEGS_START_STOP)) { + switch (ancType) { + case MPEGS_ANCTYPE_HEADER_AND_FRAME: { + int parseResult, bitsRead; + SPATIAL_SPECIFIC_CONFIG spatialSpecificConfigTmp = + pMpegSurroundDecoder->spatialSpecificConfigBackup; + + /* Parse spatial specific config */ + bitsRead = FDKgetValidBits(hMpsBsData); + + err = SpatialDecParseSpecificConfigHeader( + hMpsBsData, + &pMpegSurroundDecoder->spatialSpecificConfigBackup, coreCodec, + pMpegSurroundDecoder->upmixType); + + bitsRead = (bitsRead - FDKgetValidBits(hMpsBsData)); + parseResult = ((err == MPS_OK) ? bitsRead : -bitsRead); + + if (parseResult < 0) { + parseResult = -parseResult; + err = MPS_PARSE_ERROR; + } else if (err == MPS_OK) { + /* Check SSC for consistency (e.g. bit errors could cause + * trouble) */ + err = sscCheckInBand( + &pMpegSurroundDecoder->spatialSpecificConfigBackup, + frameSize, sampleRate); + } + if (err != MPS_OK) { + pMpegSurroundDecoder->spatialSpecificConfigBackup = + spatialSpecificConfigTmp; + break; + } + + pMpegSurroundDecoder->mpsDataBits -= parseResult; + + /* Initiate re-initialization, if header has changed */ + if (FDK_SpatialDecCompareSpatialSpecificConfigHeader( + &pMpegSurroundDecoder->spatialSpecificConfigBackup, + sscParse) == MPS_UNEQUAL_SSC) { + pMpegSurroundDecoder + ->initFlags[pMpegSurroundDecoder->bsFrameParse] |= + MPEGS_INIT_CHANGE_HEADER; + SpatialDecInitParserContext(pMpegSurroundDecoder->pSpatialDec); + /* We found a valid in-band configuration. Therefore any + * previous config is invalid now. */ + pMpegSurroundDecoder->mpegSurroundSscIsGlobalCfg = 0; + } + } + case MPEGS_ANCTYPE_FRAME: + + if (pMpegSurroundDecoder + ->initFlags[pMpegSurroundDecoder->bsFrameParse] & + MPEGS_INIT_ERROR_PAYLOAD) { + err = MPS_PARSE_ERROR; + break; + } + + /* First spatial specific config is parsed into + * spatialSpecificConfigBackup, second spatialSpecificConfigBackup + * is copied into spatialSpecificConfig[bsFrameDecode] */ + if (pMpegSurroundDecoder + ->initFlags[pMpegSurroundDecoder->bsFrameParse]) { + FDKmemcpy(sscParse, + &pMpegSurroundDecoder->spatialSpecificConfigBackup, + sizeof(SPATIAL_SPECIFIC_CONFIG)); + pMpegSurroundDecoder + ->fOnSync[pMpegSurroundDecoder->bsFrameParse] = + MPEGS_SYNC_FOUND; + } + + if (pMpegSurroundDecoder + ->fOnSync[pMpegSurroundDecoder->bsFrameParse] >= + MPEGS_SYNC_FOUND) { + int nbits = 0, bitsAvail; + + if (err != MPS_OK) { + break; + } + + bitsAvail = FDKgetValidBits(hMpsBsData); + + if (bitsAvail <= 0) { + err = MPS_PARSE_ERROR; + } else { + err = SpatialDecParseFrameData( + pMpegSurroundDecoder->pSpatialDec, bsFrame, hMpsBsData, + sscParse, (UPMIXTYPE)pMpegSurroundDecoder->upmixType, + fGlobalIndependencyFlag); + if (err == MPS_OK) { + bsFrame->newBsData = 1; + } + } + + nbits = bitsAvail - (INT)FDKgetValidBits(hMpsBsData); + + if ((nbits > bitsAvail) || + (nbits > pMpegSurroundDecoder->mpsDataBits) || + (pMpegSurroundDecoder->mpsDataBits > nbits + 7 && + !IS_LOWDELAY(coreCodec))) { + bsFrame->newBsData = 0; + err = MPS_PARSE_ERROR; + break; + } + pMpegSurroundDecoder->mpsDataBits -= nbits; + } + break; + + default: /* added to avoid compiler warning */ + err = MPS_NOTOK; + break; /* added to avoid compiler warning */ + } /* switch (ancType) */ + + if (err == MPS_OK) { + pMpegSurroundDecoder->ancStartStopPrev = ancStartStop; + } else { + updateMpegSurroundDecoderStatus(pMpegSurroundDecoder, + MPEGS_INIT_ERROR_PAYLOAD, + MPEGS_SYNC_LOST, MPEGS_STOP); + pMpegSurroundDecoder->mpsDataBits = 0; + } + } /* (ancStartStop == MPEGS_STOP) || (ancStartStop == MPEGS_START_STOP) + */ + } /* validAncStartStop */ + } /* validAncType */ + } + +bail: + + *pMpsDataBits -= (mpsBsBits - FDKgetValidBits(hBs)); + + return err; +} + +int mpegSurroundDecoder_Apply(CMpegSurroundDecoder *pMpegSurroundDecoder, + INT_PCM *input, PCM_MPS *pTimeData, + const int timeDataSize, int timeDataFrameSize, + int *nChannels, int *frameSize, int sampleRate, + AUDIO_OBJECT_TYPE coreCodec, + AUDIO_CHANNEL_TYPE channelType[], + UCHAR channelIndices[], + const FDK_channelMapDescr *const mapDescr) { + SACDEC_ERROR err = MPS_OK; + PCM_MPS *pTimeOut = pTimeData; + UINT initControlFlags = 0, controlFlags = 0; + int timeDataRequiredSize = 0; + int newData; + + if (pMpegSurroundDecoder == NULL) { + return MPS_INVALID_HANDLE; + } + + FDK_ASSERT(pMpegSurroundDecoder->pSpatialDec); + + if (!FDK_chMapDescr_isValid(mapDescr)) { + return MPS_INVALID_HANDLE; + } + + if ((*nChannels <= 0) || (*nChannels > 2)) { + return MPS_NOTOK; + } + + pMpegSurroundDecoder->pSpatialDec->pConfigCurrent = + &pMpegSurroundDecoder + ->spatialSpecificConfig[pMpegSurroundDecoder->bsFrameDecode]; + newData = pMpegSurroundDecoder->bsFrames[pMpegSurroundDecoder->bsFrameParse] + .newBsData; + + switch (mpegSurroundOperationMode(pMpegSurroundDecoder, 1000)) { + case MPEGS_OPMODE_MPS_PAYLOAD: + if (pMpegSurroundDecoder + ->initFlags[pMpegSurroundDecoder->bsFrameDecode]) { + err = initMpegSurroundDecoder(pMpegSurroundDecoder); + } + + if (err == MPS_OK) { + if ((pMpegSurroundDecoder + ->fOnSync[pMpegSurroundDecoder->bsFrameDecode] != + MPEGS_SYNC_COMPLETE) && + (pMpegSurroundDecoder->bsFrames[pMpegSurroundDecoder->bsFrameDecode] + .bsIndependencyFlag == 1)) { + /* We got a valid header and independently decodeable frame data. + -> Go to the next sync level and start processing. */ + pMpegSurroundDecoder->fOnSync[pMpegSurroundDecoder->bsFrameDecode] = + MPEGS_SYNC_COMPLETE; + } + } else { + /* We got a valid config header but found an error while parsing the + bitstream. Wait for the next independent frame and apply error + conealment in the meantime. */ + pMpegSurroundDecoder->fOnSync[pMpegSurroundDecoder->bsFrameDecode] = + MPEGS_SYNC_FOUND; + controlFlags |= MPEGS_CONCEAL; + err = MPS_OK; + } + /* + Concealment: + - Bitstream is available, no sync found during bitstream processing + - Bitstream is available, sync lost due to corrupted bitstream + - Bitstream is available, sync found but no independent frame + */ + if (pMpegSurroundDecoder->fOnSync[pMpegSurroundDecoder->bsFrameDecode] != + MPEGS_SYNC_COMPLETE) { + controlFlags |= MPEGS_CONCEAL; + } + break; + + case MPEGS_OPMODE_NO_MPS_PAYLOAD: + /* Concealment: No bitstream is available */ + controlFlags |= MPEGS_CONCEAL; + break; + + default: + err = MPS_NOTOK; + } + + if (err != MPS_OK) { + goto bail; + } + + /* + * Force BypassMode if choosen by user + */ + if (pMpegSurroundDecoder->mpegSurroundUserParams.bypassMode) { + controlFlags |= MPEGS_BYPASSMODE; + } + + if (pMpegSurroundDecoder->initFlags[pMpegSurroundDecoder->bsFrameDecode]) { + int startWithDfltCfg = 0; + /* + * Init with a default configuration if we came here and are still not + * initialized. + */ + if (pMpegSurroundDecoder->initFlags[pMpegSurroundDecoder->bsFrameDecode] & + MPEGS_INIT_ENFORCE_REINIT) { + /* Get default spatial specific config */ + if (FDK_SpatialDecInitDefaultSpatialSpecificConfig( + &pMpegSurroundDecoder->spatialSpecificConfigBackup, coreCodec, + *nChannels, sampleRate, + *frameSize / + mpegSurroundDecoder_GetNrOfQmfBands(NULL, sampleRate), + pMpegSurroundDecoder->mpegSurroundDecoderLevel, + pMpegSurroundDecoder->mpegSurroundUserParams.blindEnable)) { + err = MPS_NOTOK; + goto bail; + } + + /* Initiate re-initialization, if header has changed */ + if (FDK_SpatialDecCompareSpatialSpecificConfigHeader( + &pMpegSurroundDecoder->spatialSpecificConfigBackup, + &pMpegSurroundDecoder->spatialSpecificConfig + [pMpegSurroundDecoder->bsFrameDecode]) == MPS_UNEQUAL_SSC) { + pMpegSurroundDecoder->initFlags[pMpegSurroundDecoder->bsFrameDecode] |= + MPEGS_INIT_CHANGE_HEADER; + SpatialDecInitParserContext(pMpegSurroundDecoder->pSpatialDec); + } + + startWithDfltCfg = 1; + } + + /* First spatial specific config is parsed into spatialSpecificConfigBackup, + * second spatialSpecificConfigBackup is copied into spatialSpecificConfig + */ + err = initMpegSurroundDecoder(pMpegSurroundDecoder); + + if (startWithDfltCfg) { + /* initialized with default config, but no sync found */ + /* maybe use updateMpegSurroundDecoderStatus later on */ + pMpegSurroundDecoder->fOnSync[pMpegSurroundDecoder->bsFrameDecode] = + MPEGS_SYNC_LOST; + } + + /* Since we do not have state MPEGS_SYNC_COMPLETE apply concealment */ + controlFlags |= MPEGS_CONCEAL; + + if (err != MPS_OK) { + goto bail; + } + } + + /* + * Process MPEG Surround Audio + */ + initControlFlags = controlFlags; + + /* Check that provided output buffer is large enough. */ + timeDataRequiredSize = + (timeDataFrameSize * + pMpegSurroundDecoder->pSpatialDec->numOutputChannelsAT * + pMpegSurroundDecoder->pQmfDomain->globalConf.nBandsSynthesis) / + pMpegSurroundDecoder->pQmfDomain->globalConf.nBandsAnalysis; + if (timeDataSize < timeDataRequiredSize) { + err = MPS_OUTPUT_BUFFER_TOO_SMALL; + goto bail; + } + + if ((pMpegSurroundDecoder->pSpatialDec->pConfigCurrent->syntaxFlags & + SACDEC_SYNTAX_USAC) && + (pMpegSurroundDecoder->pSpatialDec->stereoConfigIndex > 1)) { + FDK_ASSERT(timeDataRequiredSize >= timeDataFrameSize * *nChannels); + /* Place samples comprising QMF time slots spaced at QMF output Band raster + * to allow slot wise processing */ + int timeDataFrameSizeOut = + (timeDataFrameSize * + pMpegSurroundDecoder->pQmfDomain->globalConf.nBandsSynthesis) / + pMpegSurroundDecoder->pQmfDomain->globalConf.nBandsAnalysis; + pMpegSurroundDecoder->pQmfDomain->globalConf.TDinput = + pTimeData + timeDataFrameSizeOut - timeDataFrameSize; + for (int i = *nChannels - 1; i >= 0; i--) { + FDKmemmove(pTimeData + (i + 1) * timeDataFrameSizeOut - timeDataFrameSize, + pTimeData + timeDataFrameSize * i, + sizeof(PCM_MPS) * timeDataFrameSize); + FDKmemclear(pTimeData + i * timeDataFrameSizeOut, + sizeof(PCM_MPS) * (timeDataFrameSizeOut - timeDataFrameSize)); + } + } else { + if (pMpegSurroundDecoder->mpegSurroundUseTimeInterface) { + FDKmemcpy(input, pTimeData, + sizeof(INT_PCM) * (*nChannels) * (*frameSize)); + pMpegSurroundDecoder->pQmfDomain->globalConf.TDinput = input; + } + } + + /* + * Process MPEG Surround Audio + */ + err = SpatialDecApplyFrame( + pMpegSurroundDecoder->pSpatialDec, + &pMpegSurroundDecoder->bsFrames[pMpegSurroundDecoder->bsFrameDecode], + pMpegSurroundDecoder->mpegSurroundUseTimeInterface ? INPUTMODE_TIME + : INPUTMODE_QMF_SBR, + pMpegSurroundDecoder->pQmfDomain->globalConf.TDinput, NULL, NULL, + pTimeOut, *frameSize, &controlFlags, *nChannels, mapDescr); + *nChannels = pMpegSurroundDecoder->pSpatialDec->numOutputChannelsAT; + + if (err != + MPS_OK) { /* A fatal error occured. Go back to start and try again: */ + updateMpegSurroundDecoderStatus(pMpegSurroundDecoder, + MPEGS_INIT_ENFORCE_REINIT, MPEGS_SYNC_LOST, + MPEGS_STOP); + *frameSize = + 0; /* Declare that framework can not use the data in pTimeOut. */ + } else { + if (((controlFlags & MPEGS_CONCEAL) && + !(initControlFlags & MPEGS_CONCEAL)) || + (pMpegSurroundDecoder->pSpatialDec->errInt != + MPS_OK)) { /* Account for errors that occured in + SpatialDecApplyFrame(): */ + updateMpegSurroundDecoderStatus(pMpegSurroundDecoder, + MPEGS_INIT_ERROR_PAYLOAD, MPEGS_SYNC_LOST, + MPEGS_STOP); + } + } + + if ((err == MPS_OK) && !(controlFlags & MPEGS_BYPASSMODE) && + !(pMpegSurroundDecoder->upmixType == UPMIX_TYPE_BYPASS)) { + SpatialDecChannelProperties(pMpegSurroundDecoder->pSpatialDec, channelType, + channelIndices, mapDescr); + } + +bail: + + if (newData) { + /* numParameterSetsPrev shall only be read in the decode process, because of + that we can update this state variable here */ + pMpegSurroundDecoder->pSpatialDec->numParameterSetsPrev = + pMpegSurroundDecoder->bsFrames[pMpegSurroundDecoder->bsFrameDecode] + .numParameterSets; + } + + return (err); +} + +/** + * \brief Free config dependent MPEG Surround memory. + **/ +SACDEC_ERROR mpegSurroundDecoder_FreeMem( + CMpegSurroundDecoder *pMpegSurroundDecoder) { + SACDEC_ERROR err = MPS_OK; + + if (pMpegSurroundDecoder != NULL) { + FDK_SpatialDecClose(pMpegSurroundDecoder->pSpatialDec); + pMpegSurroundDecoder->pSpatialDec = NULL; + } + + return err; +} + +/** + * \brief Close MPEG Surround decoder. + **/ +void mpegSurroundDecoder_Close(CMpegSurroundDecoder *pMpegSurroundDecoder) { + if (pMpegSurroundDecoder != NULL) { + FDK_SpatialDecClose(pMpegSurroundDecoder->pSpatialDec); + pMpegSurroundDecoder->pSpatialDec = NULL; + + for (int i = 0; i < 1; i++) { + SpatialDecCloseBsFrame(&pMpegSurroundDecoder->bsFrames[i]); + } + + FDK_FREE_MEMORY_1D(pMpegSurroundDecoder); + } +} + +#define SACDEC_VL0 2 +#define SACDEC_VL1 0 +#define SACDEC_VL2 0 + +int mpegSurroundDecoder_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_MPSDEC; +#ifdef __ANDROID__ + info->build_date = ""; + info->build_time = ""; +#else + info->build_date = __DATE__; + info->build_time = __TIME__; +#endif + info->title = "MPEG Surround Decoder"; + info->version = LIB_VERSION(SACDEC_VL0, SACDEC_VL1, SACDEC_VL2); + LIB_VERSION_STRING(info); + info->flags = 0 | CAPF_MPS_LD | CAPF_MPS_USAC | CAPF_MPS_HQ | + CAPF_MPS_1CH_IN | CAPF_MPS_2CH_OUT; /* end flags */ + + return 0; +} + +SACDEC_ERROR mpegSurroundDecoder_SetParam( + CMpegSurroundDecoder *pMpegSurroundDecoder, const SACDEC_PARAM param, + const INT value) { + SACDEC_ERROR err = MPS_OK; + SPATIALDEC_PARAM *pUserParams = NULL; + + /* check decoder handle */ + if (pMpegSurroundDecoder != NULL) { + /* init local shortcuts */ + pUserParams = &pMpegSurroundDecoder->mpegSurroundUserParams; + } else { + err = MPS_INVALID_HANDLE; + /* check the parameter values before exiting. */ + } + + /* apply param value */ + switch (param) { + case SACDEC_OUTPUT_MODE: + switch ((SAC_DEC_OUTPUT_MODE)value) { + case SACDEC_OUT_MODE_NORMAL: + case SACDEC_OUT_MODE_STEREO: + break; + default: + err = MPS_INVALID_PARAMETER; + } + if (err == MPS_OK) { + if (0) { + err = MPS_INVALID_PARAMETER; + } else if (pUserParams->outputMode != (UCHAR)value) { + pUserParams->outputMode = (UCHAR)value; + pMpegSurroundDecoder + ->initFlags[pMpegSurroundDecoder->bsFrameDecode] |= + MPEGS_INIT_CHANGE_OUTPUT_MODE; + } + } + break; + + case SACDEC_INTERFACE: + if (value < 0 || value > 1) { + err = MPS_INVALID_PARAMETER; + } + if (err != MPS_OK) { + goto bail; + } + if (pMpegSurroundDecoder->mpegSurroundUseTimeInterface != (UCHAR)value) { + pMpegSurroundDecoder->mpegSurroundUseTimeInterface = (UCHAR)value; + pMpegSurroundDecoder->initFlags[pMpegSurroundDecoder->bsFrameDecode] |= + MPEGS_INIT_CHANGE_TIME_FREQ_INTERFACE; + } + break; + + case SACDEC_BS_INTERRUPTION: + if ((err == MPS_OK) && (value != 0)) { + updateMpegSurroundDecoderStatus(pMpegSurroundDecoder, + MPEGS_INIT_BS_INTERRUPTION, + MPEGS_SYNC_LOST, MPEGS_STOP); + } + break; + + case SACDEC_CLEAR_HISTORY: + if ((err == MPS_OK) && (value != 0)) { + /* Just reset the states and go on. */ + updateMpegSurroundDecoderStatus(pMpegSurroundDecoder, + MPEGS_INIT_CLEAR_HISTORY, + MPEGS_SYNC_LOST, MPEGS_STOP); + } + break; + + case SACDEC_CONCEAL_NUM_KEEP_FRAMES: + if (value < 0) { /* Check valid value range */ + err = MPS_INVALID_PARAMETER; + } + if (err != MPS_OK) { + goto bail; + } + if (pUserParams->concealNumKeepFrames != (UINT)value) { + pUserParams->concealNumKeepFrames = (UINT)value; + pMpegSurroundDecoder->initFlags[pMpegSurroundDecoder->bsFrameDecode] |= + MPEGS_INIT_CHANGE_CONCEAL_PARAMS; + } + break; + + case SACDEC_CONCEAL_FADE_OUT_SLOPE_LENGTH: + if (value < 0) { /* Check valid value range */ + err = MPS_INVALID_PARAMETER; + } + if (err != MPS_OK) { + goto bail; + } + if (pUserParams->concealFadeOutSlopeLength != (UINT)value) { + pUserParams->concealFadeOutSlopeLength = (UINT)value; + pMpegSurroundDecoder->initFlags[pMpegSurroundDecoder->bsFrameDecode] |= + MPEGS_INIT_CHANGE_CONCEAL_PARAMS; + } + break; + + case SACDEC_CONCEAL_FADE_IN_SLOPE_LENGTH: + if (value < 0) { /* Check valid value range */ + err = MPS_INVALID_PARAMETER; + } + if (err != MPS_OK) { + goto bail; + } + if (pUserParams->concealFadeInSlopeLength != (UINT)value) { + pUserParams->concealFadeInSlopeLength = (UINT)value; + pMpegSurroundDecoder->initFlags[pMpegSurroundDecoder->bsFrameDecode] |= + MPEGS_INIT_CHANGE_CONCEAL_PARAMS; + } + break; + + case SACDEC_CONCEAL_NUM_RELEASE_FRAMES: + if (value < 0) { /* Check valid value range */ + err = MPS_INVALID_PARAMETER; + } + if (err != MPS_OK) { + goto bail; + } + if (pUserParams->concealNumReleaseFrames != (UINT)value) { + pUserParams->concealNumReleaseFrames = (UINT)value; + pMpegSurroundDecoder->initFlags[pMpegSurroundDecoder->bsFrameDecode] |= + MPEGS_INIT_CHANGE_CONCEAL_PARAMS; + } + break; + + default: + err = MPS_INVALID_PARAMETER; + break; + } /* switch(param) */ + +bail: + return err; +} + +SACDEC_ERROR mpegSurroundDecoder_IsPseudoLR( + CMpegSurroundDecoder *pMpegSurroundDecoder, int *bsPseudoLr) { + if (pMpegSurroundDecoder != NULL) { + const SPATIAL_SPECIFIC_CONFIG *sscDecode = + &pMpegSurroundDecoder + ->spatialSpecificConfig[pMpegSurroundDecoder->bsFrameDecode]; + *bsPseudoLr = (int)sscDecode->bsPseudoLr; + return MPS_OK; + } else + return MPS_INVALID_HANDLE; +} + +/** + * \brief Get the signal delay caused by the MPEG Surround decoder module. + **/ +UINT mpegSurroundDecoder_GetDelay(const CMpegSurroundDecoder *self) { + INT outputDelay = 0; + + if (self != NULL) { + const SPATIAL_SPECIFIC_CONFIG *sscDecode = + &self->spatialSpecificConfig[self->bsFrameDecode]; + AUDIO_OBJECT_TYPE coreCodec = sscDecode->coreCodec; + + /* See chapter 4.5 (delay and synchronization) of ISO/IEC FDIS 23003-1 and + chapter 5.4.3 of ISO/IEC FDIS 23003-2 for details on the following + figures. */ + + if (coreCodec > AOT_NULL_OBJECT) { + if (IS_LOWDELAY(coreCodec)) { + /* All low delay variants (ER-AAC-(E)LD): */ + outputDelay += 256; + } else if (!IS_USAC(coreCodec)) { + /* By the method of elimination this is the GA (AAC-LC, HE-AAC, ...) + * branch: */ + outputDelay += 320 + 257; /* cos to exp delay + QMF synthesis */ + if (self->mpegSurroundUseTimeInterface) { + outputDelay += 320 + 384; /* QMF and hybrid analysis */ + } + } + } + } + + return (outputDelay); +} |