aboutsummaryrefslogtreecommitdiffstats
path: root/libFDK/src/FDK_qmf_domain.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libFDK/src/FDK_qmf_domain.cpp')
-rw-r--r--libFDK/src/FDK_qmf_domain.cpp982
1 files changed, 982 insertions, 0 deletions
diff --git a/libFDK/src/FDK_qmf_domain.cpp b/libFDK/src/FDK_qmf_domain.cpp
new file mode 100644
index 0000000..4b78931
--- /dev/null
+++ b/libFDK/src/FDK_qmf_domain.cpp
@@ -0,0 +1,982 @@
+/* -----------------------------------------------------------------------------
+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
+----------------------------------------------------------------------------- */
+
+/******************* Library for basic calculation routines ********************
+
+ Author(s): Matthias Hildenbrand
+
+ Description: Module to efficiently handle QMF data for multiple channels and
+ to share the data between e.g. SBR and MPS
+
+*******************************************************************************/
+
+#include "FDK_qmf_domain.h"
+
+#include "common_fix.h"
+
+#define WORKBUFFER1_TAG 0
+#define WORKBUFFER2_TAG 1
+
+#define WORKBUFFER3_TAG 4
+#define WORKBUFFER4_TAG 5
+#define WORKBUFFER5_TAG 6
+#define WORKBUFFER6_TAG 7
+
+C_ALLOC_MEM_OVERLAY(QmfWorkBufferCore1, FIXP_DBL, QMF_WB_SECTION_SIZE,
+ SECT_DATA_L1, WORKBUFFER1_TAG)
+C_ALLOC_MEM_OVERLAY(QmfWorkBufferCore2, FIXP_DBL, QMF_WB_SECTION_SIZE,
+ SECT_DATA_L2, WORKBUFFER2_TAG)
+C_ALLOC_MEM_OVERLAY(QmfWorkBufferCore3, FIXP_DBL, QMF_WB_SECTION_SIZE,
+ SECT_DATA_L2, WORKBUFFER3_TAG)
+C_ALLOC_MEM_OVERLAY(QmfWorkBufferCore4, FIXP_DBL, QMF_WB_SECTION_SIZE,
+ SECT_DATA_L2, WORKBUFFER4_TAG)
+C_ALLOC_MEM_OVERLAY(QmfWorkBufferCore5, FIXP_DBL, QMF_WB_SECTION_SIZE,
+ SECT_DATA_L2, WORKBUFFER5_TAG)
+C_ALLOC_MEM_OVERLAY(QmfWorkBufferCore6, FIXP_DBL, QMF_WB_SECTION_SIZE,
+ SECT_DATA_L2, WORKBUFFER6_TAG)
+
+/*! Analysis states buffer. <br>
+ Dimension: #((8) + (1)) */
+C_ALLOC_MEM2(AnaQmfStates, FIXP_QAS, 10 * QMF_DOMAIN_MAX_ANALYSIS_QMF_BANDS,
+ ((8) + (1)))
+
+/*! Synthesis states buffer. <br>
+ Dimension: #((8) + (1)) */
+C_ALLOC_MEM2(SynQmfStates, FIXP_QSS, 9 * QMF_DOMAIN_MAX_SYNTHESIS_QMF_BANDS,
+ ((8) + (1)))
+
+/*! Pointer to real qmf data for each time slot. <br>
+ Dimension: #((8) + (1)) */
+C_ALLOC_MEM2(QmfSlotsReal, FIXP_DBL *,
+ QMF_DOMAIN_MAX_TIMESLOTS + QMF_DOMAIN_MAX_OV_TIMESLOTS,
+ ((8) + (1)))
+
+/*! Pointer to imaginary qmf data for each time slot. <br>
+ Dimension: #((8) + (1)) */
+C_ALLOC_MEM2(QmfSlotsImag, FIXP_DBL *,
+ QMF_DOMAIN_MAX_TIMESLOTS + QMF_DOMAIN_MAX_OV_TIMESLOTS,
+ ((8) + (1)))
+
+/*! QMF overlap buffer. <br>
+ Dimension: #((8) + (1)) */
+C_AALLOC_MEM2(QmfOverlapBuffer, FIXP_DBL,
+ 2 * QMF_DOMAIN_MAX_OV_TIMESLOTS * QMF_DOMAIN_MAX_QMF_PROC_BANDS,
+ ((8) + (1)))
+
+/*! Analysis states buffer. <br>
+ Dimension: #((8) + (1)) */
+C_ALLOC_MEM2(AnaQmfStates16, FIXP_QAS, 10 * QMF_DOMAIN_ANALYSIS_QMF_BANDS_16,
+ ((8) + (1)))
+
+/*! Analysis states buffer. <br>
+ Dimension: #((8) + (1)) */
+C_ALLOC_MEM2(AnaQmfStates24, FIXP_QAS, 10 * QMF_DOMAIN_ANALYSIS_QMF_BANDS_24,
+ ((8) + (1)))
+
+/*! Analysis states buffer. <br>
+ Dimension: #((8) + (1)) */
+C_ALLOC_MEM2(AnaQmfStates32, FIXP_QAS, 10 * QMF_DOMAIN_ANALYSIS_QMF_BANDS_32,
+ ((8) + (1)))
+
+/*! Pointer to real qmf data for each time slot. <br>
+ Dimension: #((8) + (1)) */
+C_ALLOC_MEM2(QmfSlotsReal16, FIXP_DBL *,
+ QMF_DOMAIN_TIMESLOTS_16 + QMF_DOMAIN_OV_TIMESLOTS_16, ((8) + (1)))
+
+/*! Pointer to real qmf data for each time slot. <br>
+ Dimension: #((8) + (1)) */
+C_ALLOC_MEM2(QmfSlotsReal32, FIXP_DBL *,
+ QMF_DOMAIN_TIMESLOTS_32 + QMF_DOMAIN_OV_TIMESLOTS_32, ((8) + (1)))
+
+/*! Pointer to imaginary qmf data for each time slot. <br>
+ Dimension: #((8) + (1)) */
+C_ALLOC_MEM2(QmfSlotsImag16, FIXP_DBL *,
+ QMF_DOMAIN_TIMESLOTS_16 + QMF_DOMAIN_OV_TIMESLOTS_16, ((8) + (1)))
+
+/*! Pointer to imaginary qmf data for each time slot. <br>
+ Dimension: #((8) + (1)) */
+C_ALLOC_MEM2(QmfSlotsImag32, FIXP_DBL *,
+ QMF_DOMAIN_TIMESLOTS_32 + QMF_DOMAIN_OV_TIMESLOTS_32, ((8) + (1)))
+
+/*! QMF overlap buffer. <br>
+ Dimension: #((8) + (1)) */
+C_AALLOC_MEM2(QmfOverlapBuffer16, FIXP_DBL,
+ 2 * QMF_DOMAIN_OV_TIMESLOTS_16 * QMF_DOMAIN_MAX_QMF_PROC_BANDS,
+ ((8) + (1)))
+
+/*! QMF overlap buffer. <br>
+ Dimension: #((8) + (1)) */
+C_AALLOC_MEM2(QmfOverlapBuffer32, FIXP_DBL,
+ 2 * QMF_DOMAIN_OV_TIMESLOTS_32 * QMF_DOMAIN_MAX_QMF_PROC_BANDS,
+ ((8) + (1)))
+
+static int FDK_QmfDomain_FreePersistentMemory(HANDLE_FDK_QMF_DOMAIN qd) {
+ int err = 0;
+ int ch;
+
+ for (ch = 0; ch < ((8) + (1)); ch++) {
+ if (qd->QmfDomainIn[ch].pAnaQmfStates) {
+ if (qd->globalConf.nBandsAnalysis == QMF_DOMAIN_ANALYSIS_QMF_BANDS_16) {
+ FreeAnaQmfStates16(&qd->QmfDomainIn[ch].pAnaQmfStates);
+ } else if (qd->globalConf.nBandsAnalysis ==
+ QMF_DOMAIN_ANALYSIS_QMF_BANDS_24) {
+ FreeAnaQmfStates24(&qd->QmfDomainIn[ch].pAnaQmfStates);
+ } else if (qd->globalConf.nBandsAnalysis ==
+ QMF_DOMAIN_ANALYSIS_QMF_BANDS_32) {
+ FreeAnaQmfStates32(&qd->QmfDomainIn[ch].pAnaQmfStates);
+ } else {
+ FreeAnaQmfStates(&qd->QmfDomainIn[ch].pAnaQmfStates);
+ }
+ }
+
+ if (qd->QmfDomainIn[ch].pOverlapBuffer) {
+ if (qd->globalConf.nQmfOvTimeSlots == QMF_DOMAIN_OV_TIMESLOTS_16) {
+ FreeQmfOverlapBuffer16(&qd->QmfDomainIn[ch].pOverlapBuffer);
+ } else if (qd->globalConf.nQmfOvTimeSlots == QMF_DOMAIN_OV_TIMESLOTS_32) {
+ FreeQmfOverlapBuffer32(&qd->QmfDomainIn[ch].pOverlapBuffer);
+ } else {
+ FreeQmfOverlapBuffer(&qd->QmfDomainIn[ch].pOverlapBuffer);
+ }
+ }
+
+ if (qd->QmfDomainIn[ch].hQmfSlotsReal) {
+ if (qd->globalConf.nQmfTimeSlots == QMF_DOMAIN_TIMESLOTS_16) {
+ FreeQmfSlotsReal16(&qd->QmfDomainIn[ch].hQmfSlotsReal);
+ } else if (qd->globalConf.nQmfTimeSlots == QMF_DOMAIN_TIMESLOTS_32) {
+ FreeQmfSlotsReal32(&qd->QmfDomainIn[ch].hQmfSlotsReal);
+ } else {
+ FreeQmfSlotsReal(&qd->QmfDomainIn[ch].hQmfSlotsReal);
+ }
+ }
+
+ if (qd->QmfDomainIn[ch].hQmfSlotsImag) {
+ if (qd->globalConf.nQmfTimeSlots == QMF_DOMAIN_TIMESLOTS_16) {
+ FreeQmfSlotsImag16(&qd->QmfDomainIn[ch].hQmfSlotsImag);
+ }
+ if (qd->globalConf.nQmfTimeSlots == QMF_DOMAIN_TIMESLOTS_32) {
+ FreeQmfSlotsImag32(&qd->QmfDomainIn[ch].hQmfSlotsImag);
+ } else {
+ FreeQmfSlotsImag(&qd->QmfDomainIn[ch].hQmfSlotsImag);
+ }
+ }
+ }
+
+ for (ch = 0; ch < ((8) + (1)); ch++) {
+ if (qd->QmfDomainOut[ch].pSynQmfStates) {
+ FreeSynQmfStates(&qd->QmfDomainOut[ch].pSynQmfStates);
+ }
+ }
+
+ return err;
+}
+
+static int FDK_QmfDomain_AllocatePersistentMemory(HANDLE_FDK_QMF_DOMAIN qd) {
+ int err = 0;
+ int ch;
+ HANDLE_FDK_QMF_DOMAIN_GC gc = &qd->globalConf;
+
+ if ((gc->nInputChannels > ((8) + (1))) || (gc->nOutputChannels > ((8) + (1))))
+ return err = 1;
+ for (ch = 0; ch < gc->nInputChannels; ch++) {
+ int size;
+
+ size = gc->nBandsAnalysis * 10;
+ if (size > 0) {
+ if (gc->nBandsAnalysis == QMF_DOMAIN_ANALYSIS_QMF_BANDS_16) {
+ if (NULL == (qd->QmfDomainIn[ch].pAnaQmfStates = GetAnaQmfStates16(ch)))
+ goto bail;
+ } else if (gc->nBandsAnalysis == QMF_DOMAIN_ANALYSIS_QMF_BANDS_24) {
+ if (NULL == (qd->QmfDomainIn[ch].pAnaQmfStates = GetAnaQmfStates24(ch)))
+ goto bail;
+ } else if (gc->nBandsAnalysis == QMF_DOMAIN_ANALYSIS_QMF_BANDS_32) {
+ if (NULL == (qd->QmfDomainIn[ch].pAnaQmfStates = GetAnaQmfStates32(ch)))
+ goto bail;
+ } else {
+ if (NULL == (qd->QmfDomainIn[ch].pAnaQmfStates = GetAnaQmfStates(ch)))
+ goto bail;
+ }
+ } else {
+ qd->QmfDomainIn[ch].pAnaQmfStates = NULL;
+ }
+
+ size = gc->nQmfOvTimeSlots + gc->nQmfTimeSlots;
+ if (size > 0) {
+ if (gc->nQmfTimeSlots == QMF_DOMAIN_TIMESLOTS_16) {
+ if (NULL == (qd->QmfDomainIn[ch].hQmfSlotsReal = GetQmfSlotsReal16(ch)))
+ goto bail;
+ if (NULL == (qd->QmfDomainIn[ch].hQmfSlotsImag = GetQmfSlotsImag16(ch)))
+ goto bail;
+ } else if (gc->nQmfTimeSlots == QMF_DOMAIN_TIMESLOTS_32) {
+ if (NULL == (qd->QmfDomainIn[ch].hQmfSlotsReal = GetQmfSlotsReal32(ch)))
+ goto bail;
+ if (NULL == (qd->QmfDomainIn[ch].hQmfSlotsImag = GetQmfSlotsImag32(ch)))
+ goto bail;
+ } else {
+ if (NULL == (qd->QmfDomainIn[ch].hQmfSlotsReal = GetQmfSlotsReal(ch)))
+ goto bail;
+ if (NULL == (qd->QmfDomainIn[ch].hQmfSlotsImag = GetQmfSlotsImag(ch)))
+ goto bail;
+ }
+ } else {
+ qd->QmfDomainIn[ch].hQmfSlotsReal = NULL;
+ qd->QmfDomainIn[ch].hQmfSlotsImag = NULL;
+ }
+
+ size = gc->nQmfOvTimeSlots * gc->nQmfProcBands * CMPLX_MOD;
+ if (size > 0) {
+ if (gc->nQmfOvTimeSlots == QMF_DOMAIN_OV_TIMESLOTS_16) {
+ if (NULL ==
+ (qd->QmfDomainIn[ch].pOverlapBuffer = GetQmfOverlapBuffer16(ch)))
+ goto bail;
+ } else if (gc->nQmfOvTimeSlots == QMF_DOMAIN_OV_TIMESLOTS_32) {
+ if (NULL ==
+ (qd->QmfDomainIn[ch].pOverlapBuffer = GetQmfOverlapBuffer32(ch)))
+ goto bail;
+ } else {
+ if (NULL ==
+ (qd->QmfDomainIn[ch].pOverlapBuffer = GetQmfOverlapBuffer(ch)))
+ goto bail;
+ }
+ } else {
+ qd->QmfDomainIn[ch].pOverlapBuffer = NULL;
+ }
+ }
+
+ for (ch = 0; ch < gc->nOutputChannels; ch++) {
+ int size = gc->nBandsSynthesis * 9;
+ if (size > 0) {
+ if (NULL == (qd->QmfDomainOut[ch].pSynQmfStates = GetSynQmfStates(ch)))
+ goto bail;
+ } else {
+ qd->QmfDomainOut[ch].pSynQmfStates = NULL;
+ }
+ }
+
+ return err;
+
+bail:
+ FDK_QmfDomain_FreePersistentMemory(qd);
+ return -1;
+}
+
+QMF_DOMAIN_ERROR FDK_QmfDomain_ClearPersistentMemory(
+ HANDLE_FDK_QMF_DOMAIN hqd) {
+ QMF_DOMAIN_ERROR err = QMF_DOMAIN_OK;
+ int ch, size;
+ if (hqd) {
+ HANDLE_FDK_QMF_DOMAIN_GC gc = &hqd->globalConf;
+
+ size = gc->nQmfOvTimeSlots * gc->nQmfProcBands * CMPLX_MOD;
+ for (ch = 0; ch < gc->nInputChannels; ch++) {
+ if (hqd->QmfDomainIn[ch].pOverlapBuffer) {
+ FDKmemclear(hqd->QmfDomainIn[ch].pOverlapBuffer,
+ size * sizeof(FIXP_DBL));
+ }
+ }
+ if (FDK_QmfDomain_InitFilterBank(hqd, 0)) {
+ err = QMF_DOMAIN_INIT_ERROR;
+ }
+ } else {
+ err = QMF_DOMAIN_INIT_ERROR;
+ }
+ return err;
+}
+
+/*
+ FDK_getWorkBuffer
+
+ Parameters:
+
+ pWorkBuffer i: array of pointers which point to different workbuffer
+ sections workBufferOffset i: offset in the workbuffer to the requested
+ memory memSize i: size of requested memory
+
+ Function:
+
+ The functions returns the address to the requested memory in the workbuffer.
+
+ The overall workbuffer is divided into several sections. There are
+ QMF_MAX_WB_SECTIONS sections of size QMF_WB_SECTION_SIZE. The function
+ selects the workbuffer section with the help of the workBufferOffset and than
+ it verifies whether the requested amount of memory fits into the selected
+ workbuffer section.
+
+ Returns:
+
+ address to workbuffer
+*/
+static FIXP_DBL *FDK_getWorkBuffer(FIXP_DBL **pWorkBuffer,
+ USHORT workBufferOffset,
+ USHORT workBufferSectSize, USHORT memSize) {
+ int idx1;
+ int idx2;
+ FIXP_DBL *pwb;
+
+ /* a section must be a multiple of the number of processing bands (currently
+ * always 64) */
+ FDK_ASSERT((workBufferSectSize % 64) == 0);
+
+ /* calculate offset within the section */
+ idx2 = workBufferOffset % workBufferSectSize;
+ /* calculate section number */
+ idx1 = (workBufferOffset - idx2) / workBufferSectSize;
+ /* maximum sectionnumber is QMF_MAX_WB_SECTIONS */
+ FDK_ASSERT(idx1 < QMF_MAX_WB_SECTIONS);
+
+ /* check, whether workbuffer is available */
+ FDK_ASSERT(pWorkBuffer[idx1] != NULL);
+
+ /* check, whether buffer fits into selected section */
+ FDK_ASSERT((idx2 + memSize) <= workBufferSectSize);
+
+ /* get requested address to workbuffer */
+ pwb = &pWorkBuffer[idx1][idx2];
+
+ return pwb;
+}
+
+static int FDK_QmfDomain_FeedWorkBuffer(HANDLE_FDK_QMF_DOMAIN qd, int ch,
+ FIXP_DBL **pWorkBuffer,
+ USHORT workBufferOffset,
+ USHORT workBufferSectSize, int size) {
+ int err = 0;
+ int mem_needed;
+
+ mem_needed = qd->QmfDomainIn[ch].workBuf_nBands *
+ qd->QmfDomainIn[ch].workBuf_nTimeSlots * CMPLX_MOD;
+ if (mem_needed > size) {
+ return (err = 1);
+ }
+ qd->QmfDomainIn[ch].pWorkBuffer = pWorkBuffer;
+ qd->QmfDomainIn[ch].workBufferOffset = workBufferOffset;
+ qd->QmfDomainIn[ch].workBufferSectSize = workBufferSectSize;
+
+ return err;
+}
+
+int FDK_QmfDomain_IsInitialized(const HANDLE_FDK_QMF_DOMAIN qd) {
+ FDK_ASSERT(qd != NULL);
+ return ((qd->QmfDomainIn[0].pAnaQmfStates == NULL) &&
+ (qd->QmfDomainOut[0].pSynQmfStates == NULL))
+ ? 0
+ : 1;
+}
+
+int FDK_QmfDomain_InitFilterBank(HANDLE_FDK_QMF_DOMAIN qd, UINT extra_flags) {
+ FDK_ASSERT(qd != NULL);
+ int err = 0;
+ int ch, ts;
+ HANDLE_FDK_QMF_DOMAIN_GC gc = &qd->globalConf;
+ int noCols = gc->nQmfTimeSlots;
+ int lsb = gc->nBandsAnalysis;
+ int usb = fMin((INT)gc->nBandsSynthesis, 64);
+ int nProcBands = gc->nQmfProcBands;
+ FDK_ASSERT(nProcBands % ALIGNMENT_DEFAULT == 0);
+
+ if (extra_flags & QMF_FLAG_MPSLDFB) {
+ gc->flags &= ~QMF_FLAG_CLDFB;
+ gc->flags |= QMF_FLAG_MPSLDFB;
+ }
+ for (ch = 0; ch < gc->nInputChannels; ch++) {
+ /* distribute memory to slots array */
+ FIXP_DBL *ptrOv =
+ qd->QmfDomainIn[ch].pOverlapBuffer; /* persistent memory for overlap */
+ if ((ptrOv == NULL) && (gc->nQmfOvTimeSlots != 0)) {
+ err = 1;
+ return err;
+ }
+ /* This assumes the workbuffer defined for ch0 is the big one being used to
+ * hold one full frame of QMF data. */
+ FIXP_DBL **ptr =
+ qd->QmfDomainIn[fMin(ch, fMax((INT)gc->nQmfProcChannels - 1, 0))]
+ .pWorkBuffer; /* non-persistent workbuffer */
+ USHORT workBufferOffset =
+ qd->QmfDomainIn[fMin(ch, fMax((INT)gc->nQmfProcChannels - 1, 0))]
+ .workBufferOffset;
+ USHORT workBufferSectSize =
+ qd->QmfDomainIn[fMin(ch, fMax((INT)gc->nQmfProcChannels - 1, 0))]
+ .workBufferSectSize;
+
+ if ((ptr == NULL) && (gc->nQmfTimeSlots != 0)) {
+ err = 1;
+ return err;
+ }
+
+ qd->QmfDomainIn[ch].pGlobalConf = gc;
+ for (ts = 0; ts < gc->nQmfOvTimeSlots; ts++) {
+ qd->QmfDomainIn[ch].hQmfSlotsReal[ts] = ptrOv;
+ ptrOv += nProcBands;
+ qd->QmfDomainIn[ch].hQmfSlotsImag[ts] = ptrOv;
+ ptrOv += nProcBands;
+ }
+ for (; ts < (gc->nQmfOvTimeSlots + gc->nQmfTimeSlots); ts++) {
+ qd->QmfDomainIn[ch].hQmfSlotsReal[ts] = FDK_getWorkBuffer(
+ ptr, workBufferOffset, workBufferSectSize, nProcBands);
+ workBufferOffset += nProcBands;
+ qd->QmfDomainIn[ch].hQmfSlotsImag[ts] = FDK_getWorkBuffer(
+ ptr, workBufferOffset, workBufferSectSize, nProcBands);
+ workBufferOffset += nProcBands;
+ }
+ err |= qmfInitAnalysisFilterBank(
+ &qd->QmfDomainIn[ch].fb, qd->QmfDomainIn[ch].pAnaQmfStates, noCols,
+ (qd->QmfDomainIn[ch].fb.lsb == 0) ? lsb : qd->QmfDomainIn[ch].fb.lsb,
+ (qd->QmfDomainIn[ch].fb.usb == 0) ? usb : qd->QmfDomainIn[ch].fb.usb,
+ gc->nBandsAnalysis, gc->flags | extra_flags);
+ }
+
+ for (ch = 0; ch < gc->nOutputChannels; ch++) {
+ FIXP_DBL outGain_m = qd->QmfDomainOut[ch].fb.outGain_m;
+ int outGain_e = qd->QmfDomainOut[ch].fb.outGain_e;
+ int outScale = qmfGetOutScalefactor(&qd->QmfDomainOut[ch].fb);
+ err |= qmfInitSynthesisFilterBank(
+ &qd->QmfDomainOut[ch].fb, qd->QmfDomainOut[ch].pSynQmfStates, noCols,
+ (qd->QmfDomainOut[ch].fb.lsb == 0) ? lsb : qd->QmfDomainOut[ch].fb.lsb,
+ (qd->QmfDomainOut[ch].fb.usb == 0) ? usb : qd->QmfDomainOut[ch].fb.usb,
+ gc->nBandsSynthesis, gc->flags | extra_flags);
+ if (outGain_m != (FIXP_DBL)0) {
+ qmfChangeOutGain(&qd->QmfDomainOut[ch].fb, outGain_m, outGain_e);
+ }
+ if (outScale) {
+ qmfChangeOutScalefactor(&qd->QmfDomainOut[ch].fb, outScale);
+ }
+ }
+
+ return err;
+}
+
+void FDK_QmfDomain_SaveOverlap(HANDLE_FDK_QMF_DOMAIN_IN qd_ch, int offset) {
+ FDK_ASSERT(qd_ch != NULL);
+ int ts;
+ HANDLE_FDK_QMF_DOMAIN_GC gc = qd_ch->pGlobalConf;
+ int ovSlots = gc->nQmfOvTimeSlots;
+ int nCols = gc->nQmfTimeSlots;
+ int nProcBands = gc->nQmfProcBands;
+ FIXP_DBL **qmfReal = qd_ch->hQmfSlotsReal;
+ FIXP_DBL **qmfImag = qd_ch->hQmfSlotsImag;
+ QMF_SCALE_FACTOR *pScaling = &qd_ch->scaling;
+
+ /* for high part it would be enough to save only used part of overlap area */
+ if (qmfImag != NULL) {
+ for (ts = offset; ts < ovSlots; ts++) {
+ FDKmemcpy(qmfReal[ts], qmfReal[nCols + ts],
+ sizeof(FIXP_DBL) * nProcBands);
+ FDKmemcpy(qmfImag[ts], qmfImag[nCols + ts],
+ sizeof(FIXP_DBL) * nProcBands);
+ }
+ } else {
+ for (ts = 0; ts < ovSlots; ts++) {
+ FDKmemcpy(qmfReal[ts], qmfReal[nCols + ts],
+ sizeof(FIXP_DBL) * nProcBands);
+ }
+ }
+ pScaling->ov_lb_scale = pScaling->lb_scale;
+}
+
+ /* Convert headroom bits to exponent */
+#define SCALE2EXP(s) (15 - (s))
+#define EXP2SCALE(e) (15 - (e))
+
+void FDK_QmfDomain_GetSlot(const HANDLE_FDK_QMF_DOMAIN_IN qd_ch, const int ts,
+ const int start_band, const int stop_band,
+ FIXP_DBL *pQmfOutReal, FIXP_DBL *pQmfOutImag,
+ const int exp_out) {
+ FDK_ASSERT(qd_ch != NULL);
+ FDK_ASSERT(pQmfOutReal != NULL);
+ HANDLE_FDK_QMF_DOMAIN_GC gc = qd_ch->pGlobalConf;
+ const FIXP_DBL *real = qd_ch->hQmfSlotsReal[ts];
+ const FIXP_DBL *imag = qd_ch->hQmfSlotsImag[ts];
+ const int ovSlots = gc->nQmfOvTimeSlots;
+ const int exp_lb = SCALE2EXP((ts < ovSlots) ? qd_ch->scaling.ov_lb_scale
+ : qd_ch->scaling.lb_scale);
+ const int exp_hb = SCALE2EXP(qd_ch->scaling.hb_scale);
+ const int lsb = qd_ch->fb.lsb;
+ const int usb = qd_ch->fb.usb;
+ int b = start_band;
+ int lb_sf, hb_sf;
+
+ int target_exp =
+ ALGORITHMIC_SCALING_IN_ANALYSIS_FILTERBANK + qd_ch->fb.filterScale;
+
+ FDK_ASSERT(ts < (gc->nQmfTimeSlots + gc->nQmfOvTimeSlots));
+ FDK_ASSERT(start_band >= 0);
+ FDK_ASSERT(stop_band <= gc->nQmfProcBands);
+
+ if (qd_ch->fb.no_channels == 24) {
+ target_exp -= 1;
+ }
+
+ /* Limit scaling factors to maximum negative value to avoid faulty behaviour
+ due to right-shifts. Corresponding asserts were observed during robustness
+ testing.
+ */
+ lb_sf = fMax(exp_lb - target_exp - exp_out, -31);
+ FDK_ASSERT(lb_sf < 32);
+ hb_sf = fMax(exp_hb - target_exp - exp_out, -31);
+ FDK_ASSERT(hb_sf < 32);
+
+ if (pQmfOutImag == NULL) {
+ for (; b < fMin(lsb, stop_band); b++) {
+ pQmfOutReal[b] = scaleValue(real[b], lb_sf);
+ }
+ for (; b < fMin(usb, stop_band); b++) {
+ pQmfOutReal[b] = scaleValue(real[b], hb_sf);
+ }
+ for (; b < stop_band; b++) {
+ pQmfOutReal[b] = (FIXP_DBL)0;
+ }
+ } else {
+ FDK_ASSERT(imag != NULL);
+ for (; b < fMin(lsb, stop_band); b++) {
+ pQmfOutReal[b] = scaleValue(real[b], lb_sf);
+ pQmfOutImag[b] = scaleValue(imag[b], lb_sf);
+ }
+ for (; b < fMin(usb, stop_band); b++) {
+ pQmfOutReal[b] = scaleValue(real[b], hb_sf);
+ pQmfOutImag[b] = scaleValue(imag[b], hb_sf);
+ }
+ for (; b < stop_band; b++) {
+ pQmfOutReal[b] = (FIXP_DBL)0;
+ pQmfOutImag[b] = (FIXP_DBL)0;
+ }
+ }
+}
+
+void FDK_QmfDomain_GetWorkBuffer(const HANDLE_FDK_QMF_DOMAIN_IN qd_ch,
+ const int ts, FIXP_DBL **ppQmfReal,
+ FIXP_DBL **ppQmfImag) {
+ FDK_ASSERT(qd_ch != NULL);
+ FDK_ASSERT(ppQmfReal != NULL);
+ FDK_ASSERT(ppQmfImag != NULL);
+ const int bands = qd_ch->workBuf_nBands;
+ FIXP_DBL **pWorkBuf = qd_ch->pWorkBuffer;
+ USHORT workBufferOffset = qd_ch->workBufferOffset;
+ USHORT workBufferSectSize = qd_ch->workBufferSectSize;
+
+ FDK_ASSERT(bands > 0);
+ FDK_ASSERT(ts < qd_ch->workBuf_nTimeSlots);
+
+ *ppQmfReal = FDK_getWorkBuffer(
+ pWorkBuf, workBufferOffset + (ts * CMPLX_MOD + 0) * bands,
+ workBufferSectSize, bands);
+ *ppQmfImag = FDK_getWorkBuffer(
+ pWorkBuf, workBufferOffset + (ts * CMPLX_MOD + 1) * bands,
+ workBufferSectSize, bands);
+}
+
+void FDK_QmfDomain_WorkBuffer2ProcChannel(
+ const HANDLE_FDK_QMF_DOMAIN_IN qd_ch) {
+ FDK_ASSERT(qd_ch != NULL);
+ HANDLE_FDK_QMF_DOMAIN_GC gc = qd_ch->pGlobalConf;
+ FIXP_DBL **pWorkBuf = qd_ch->pWorkBuffer;
+ USHORT workBufferOffset = qd_ch->workBufferOffset;
+ USHORT workBufferSectSize = qd_ch->workBufferSectSize;
+
+ if (FDK_getWorkBuffer(pWorkBuf, workBufferOffset, workBufferSectSize,
+ qd_ch->workBuf_nBands) ==
+ qd_ch->hQmfSlotsReal[gc->nQmfOvTimeSlots]) {
+ /* work buffer is part of processing channel => nothing to do */
+ return;
+ } else {
+ /* copy parked new QMF data to processing channel */
+ const int bands = qd_ch->workBuf_nBands;
+ const int slots = qd_ch->workBuf_nTimeSlots;
+ int ts;
+ for (ts = 0; ts < slots; ts++) {
+ FDKmemcpy(qd_ch->hQmfSlotsReal[gc->nQmfOvTimeSlots + ts],
+ FDK_getWorkBuffer(pWorkBuf, workBufferOffset,
+ workBufferSectSize, bands),
+ sizeof(FIXP_DBL) * bands); // parkBuf_to_anaMatrix
+ workBufferOffset += bands;
+ FDKmemcpy(qd_ch->hQmfSlotsImag[gc->nQmfOvTimeSlots + ts],
+ FDK_getWorkBuffer(pWorkBuf, workBufferOffset,
+ workBufferSectSize, bands),
+ sizeof(FIXP_DBL) * bands);
+ workBufferOffset += bands;
+ }
+ }
+}
+
+void FDK_QmfDomain_QmfData2HBE(HANDLE_FDK_QMF_DOMAIN_IN qd_ch,
+ FIXP_DBL **ppQmfReal, FIXP_DBL **ppQmfImag) {
+ FDK_ASSERT(qd_ch != NULL);
+ FDK_ASSERT(ppQmfReal != NULL);
+ FDK_ASSERT(ppQmfImag != NULL);
+ HANDLE_FDK_QMF_DOMAIN_GC gc = qd_ch->pGlobalConf;
+ FIXP_DBL **pWorkBuf = qd_ch->pWorkBuffer;
+ USHORT workBufferOffset = qd_ch->workBufferOffset;
+ USHORT workBufferSectSize = qd_ch->workBufferSectSize;
+
+ if (FDK_getWorkBuffer(pWorkBuf, workBufferOffset, workBufferSectSize,
+ qd_ch->workBuf_nBands) ==
+ qd_ch->hQmfSlotsReal[gc->nQmfOvTimeSlots]) { // left channel (anaMatrix)
+ int ts;
+ const int bands = gc->nBandsAnalysis;
+ const int slots = qd_ch->workBuf_nTimeSlots;
+ FDK_ASSERT(bands <= 64);
+ for (ts = 0; ts < slots; ts++) {
+ /* copy current data of processing channel */
+ FIXP_DBL tmp[64]; // one slot
+ /* real */
+ FDKmemcpy(tmp, qd_ch->hQmfSlotsReal[gc->nQmfOvTimeSlots + ts],
+ sizeof(FIXP_DBL) * bands); // anaMatrix_to_tmp
+ FDKmemcpy(qd_ch->hQmfSlotsReal[gc->nQmfOvTimeSlots + ts], ppQmfReal[ts],
+ sizeof(FIXP_DBL) * bands); // HBE_to_anaMatrix
+ FDKmemcpy(ppQmfReal[ts], tmp, sizeof(FIXP_DBL) * bands); // tmp_to_HBE
+ /* imag */
+ FDKmemcpy(tmp, qd_ch->hQmfSlotsImag[gc->nQmfOvTimeSlots + ts],
+ sizeof(FIXP_DBL) * bands);
+ FDKmemcpy(qd_ch->hQmfSlotsImag[gc->nQmfOvTimeSlots + ts], ppQmfImag[ts],
+ sizeof(FIXP_DBL) * bands);
+ FDKmemcpy(ppQmfImag[ts], tmp, sizeof(FIXP_DBL) * bands);
+ }
+ } else { // right channel (parkBuf)
+ const int bands = qd_ch->workBuf_nBands;
+ const int slots = qd_ch->workBuf_nTimeSlots;
+ int ts;
+ FDK_ASSERT(qd_ch->workBuf_nBands == gc->nBandsAnalysis);
+ for (ts = 0; ts < slots; ts++) {
+ /* copy HBE QMF data buffer to processing channel */
+ FDKmemcpy(qd_ch->hQmfSlotsReal[gc->nQmfOvTimeSlots + ts], ppQmfReal[ts],
+ sizeof(FIXP_DBL) * bands); // HBE_to_anaMatrix
+ FDKmemcpy(qd_ch->hQmfSlotsImag[gc->nQmfOvTimeSlots + ts], ppQmfImag[ts],
+ sizeof(FIXP_DBL) * bands);
+ /* copy parked new QMF data to HBE QMF data buffer */
+ FDKmemcpy(ppQmfReal[ts],
+ FDK_getWorkBuffer(pWorkBuf, workBufferOffset,
+ workBufferSectSize, bands),
+ sizeof(FIXP_DBL) * bands); // parkBuf_to_HBE
+ workBufferOffset += bands;
+ FDKmemcpy(ppQmfImag[ts],
+ FDK_getWorkBuffer(pWorkBuf, workBufferOffset,
+ workBufferSectSize, bands),
+ sizeof(FIXP_DBL) * bands);
+ workBufferOffset += bands;
+ }
+ }
+}
+
+void FDK_QmfDomain_ClearRequested(HANDLE_FDK_QMF_DOMAIN_GC hgc) {
+ hgc->qmfDomainExplicitConfig = 0;
+ hgc->flags_requested = 0;
+ hgc->nInputChannels_requested = 0;
+ hgc->nOutputChannels_requested = 0;
+ hgc->nBandsAnalysis_requested = 0;
+ hgc->nBandsSynthesis_requested = 0;
+ hgc->nQmfTimeSlots_requested = 0;
+ hgc->nQmfOvTimeSlots_requested = 0;
+ hgc->nQmfProcBands_requested = 0;
+ hgc->nQmfProcChannels_requested = 0;
+}
+
+static void FDK_QmfDomain_ClearConfigured(HANDLE_FDK_QMF_DOMAIN_GC hgc) {
+ hgc->flags = 0;
+ hgc->nInputChannels = 0;
+ hgc->nOutputChannels = 0;
+ hgc->nBandsAnalysis = 0;
+ hgc->nBandsSynthesis = 0;
+ hgc->nQmfTimeSlots = 0;
+ hgc->nQmfOvTimeSlots = 0;
+ hgc->nQmfProcBands = 0;
+ hgc->nQmfProcChannels = 0;
+}
+
+static void FDK_QmfDomain_ClearFilterBank(HANDLE_FDK_QMF_DOMAIN hqd) {
+ int ch;
+
+ for (ch = 0; ch < ((8) + (1)); ch++) {
+ FDKmemclear(&hqd->QmfDomainIn[ch].fb, sizeof(hqd->QmfDomainIn[ch].fb));
+ }
+
+ for (ch = 0; ch < ((8) + (1)); ch++) {
+ FDKmemclear(&hqd->QmfDomainOut[ch].fb, sizeof(hqd->QmfDomainIn[ch].fb));
+ }
+}
+
+QMF_DOMAIN_ERROR FDK_QmfDomain_Configure(HANDLE_FDK_QMF_DOMAIN hqd) {
+ FDK_ASSERT(hqd != NULL);
+ QMF_DOMAIN_ERROR err = QMF_DOMAIN_OK;
+ int i, size_main, size, size_temp = 0;
+
+ HANDLE_FDK_QMF_DOMAIN_GC hgc = &hqd->globalConf;
+ FIXP_DBL **pWorkBuffer = hgc->pWorkBuffer;
+
+ int hasChanged = 0;
+
+ if ((hgc->nQmfProcChannels_requested > 0) &&
+ (hgc->nQmfProcBands_requested != 64)) {
+ return QMF_DOMAIN_INIT_ERROR;
+ }
+ if (hgc->nBandsAnalysis_requested > hgc->nQmfProcBands_requested) {
+ /* In general the output of the qmf analysis is written to QMF memory slots
+ which size is defined by nQmfProcBands. nBandsSynthesis may be larger
+ than nQmfProcBands. This is e.g. the case if the QMF based resampler is
+ used.
+ */
+ return QMF_DOMAIN_INIT_ERROR;
+ }
+
+ /* 1. adjust change of processing channels by comparison of current and
+ * requested parameters */
+ if ((hgc->nQmfProcChannels != hgc->nQmfProcChannels_requested) ||
+ (hgc->nQmfProcBands != hgc->nQmfProcBands_requested) ||
+ (hgc->nQmfTimeSlots != hgc->nQmfTimeSlots_requested)) {
+ for (i = 0; i < hgc->nQmfProcChannels_requested; i++) {
+ hqd->QmfDomainIn[i].workBuf_nBands = hgc->nQmfProcBands_requested;
+ hgc->nQmfProcBands = hgc->nQmfProcBands_requested;
+
+ hqd->QmfDomainIn[i].workBuf_nTimeSlots = hgc->nQmfTimeSlots_requested;
+ }
+
+ hgc->nQmfProcChannels =
+ hgc->nQmfProcChannels_requested; /* keep highest value encountered so
+ far as allocated */
+
+ hasChanged = 1;
+ }
+
+ /* 2. reallocate persistent memory if necessary (analysis state-buffers,
+ * timeslot-pointer-array, overlap-buffers, synthesis state-buffers) */
+ if ((hgc->nInputChannels != hgc->nInputChannels_requested) ||
+ (hgc->nBandsAnalysis != hgc->nBandsAnalysis_requested) ||
+ (hgc->nQmfTimeSlots != hgc->nQmfTimeSlots_requested) ||
+ (hgc->nQmfOvTimeSlots != hgc->nQmfOvTimeSlots_requested) ||
+ (hgc->nOutputChannels != hgc->nOutputChannels_requested) ||
+ (hgc->nBandsSynthesis != hgc->nBandsSynthesis_requested) ||
+ (hgc->parkChannel != hgc->parkChannel_requested)) {
+ hgc->nInputChannels = hgc->nInputChannels_requested;
+ hgc->nBandsAnalysis = hgc->nBandsAnalysis_requested;
+ hgc->nQmfTimeSlots = hgc->nQmfTimeSlots_requested;
+ hgc->nQmfOvTimeSlots = hgc->nQmfOvTimeSlots_requested;
+ hgc->nOutputChannels = hgc->nOutputChannels_requested;
+ hgc->nBandsSynthesis = hgc->nBandsSynthesis_requested;
+ hgc->parkChannel = hgc->parkChannel_requested;
+
+ if (FDK_QmfDomain_AllocatePersistentMemory(hqd)) {
+ err = QMF_DOMAIN_OUT_OF_MEMORY;
+ goto bail;
+ }
+
+ /* 3. set request-flag for downsampled SBR */
+ if ((hgc->nBandsAnalysis == 32) && (hgc->nBandsSynthesis == 32) &&
+ !(hgc->flags & (QMF_FLAG_CLDFB | QMF_FLAG_MPSLDFB))) {
+ hgc->flags_requested |= QMF_FLAG_DOWNSAMPLED;
+ }
+ if ((hgc->flags_requested & QMF_FLAG_MPSLDFB) &&
+ (hgc->flags_requested & QMF_FLAG_CLDFB)) {
+ hgc->flags_requested &= ~QMF_FLAG_CLDFB;
+ }
+
+ hasChanged = 1;
+ }
+
+ /* 4. initialize tables and buffer for QMF-resampler */
+
+ /* 5. set requested flags */
+ if (hgc->flags != hgc->flags_requested) {
+ hgc->flags = hgc->flags_requested;
+ hasChanged = 1;
+ }
+
+ if (hasChanged) {
+ /* 6. recalculate and check size of required workbuffer-space */
+
+ if (hgc->parkChannel && (hqd->globalConf.nQmfProcChannels == 1)) {
+ /* configure temp QMF buffer for parking right channel MPS212 output,
+ * (USAC stereoConfigIndex 3 only) */
+ hqd->QmfDomainIn[1].workBuf_nBands = hqd->globalConf.nBandsAnalysis;
+ hqd->QmfDomainIn[1].workBuf_nTimeSlots = hqd->globalConf.nQmfTimeSlots;
+ size_temp = hqd->QmfDomainIn[1].workBuf_nBands *
+ hqd->QmfDomainIn[1].workBuf_nTimeSlots * CMPLX_MOD;
+ }
+
+ size_main = hqd->QmfDomainIn[0].workBuf_nBands *
+ hqd->QmfDomainIn[0].workBuf_nTimeSlots * CMPLX_MOD;
+
+ size = size_main * hgc->nQmfProcChannels + size_temp;
+
+ if (size > (QMF_MAX_WB_SECTIONS * QMF_WB_SECTION_SIZE)) {
+ err = QMF_DOMAIN_OUT_OF_MEMORY;
+ goto bail;
+ }
+
+ /* 7. allocate additional workbuffer if necessary */
+ if ((size > 0 /* *QMF_WB_SECTION_SIZE */) && (pWorkBuffer[0] == NULL)) {
+ /* get work buffer of size QMF_WB_SECTION_SIZE */
+ pWorkBuffer[0] = GetQmfWorkBufferCore6();
+ }
+
+ if ((size > 1 * QMF_WB_SECTION_SIZE) && (pWorkBuffer[1] == NULL)) {
+ /* get work buffer of size QMF_WB_SECTION_SIZE */
+ pWorkBuffer[1] = GetQmfWorkBufferCore1();
+ }
+
+ if ((size > 2 * QMF_WB_SECTION_SIZE) && (pWorkBuffer[2] == NULL)) {
+ /* get work buffer of size QMF_WB_SECTION_SIZE */
+ pWorkBuffer[2] = GetQmfWorkBufferCore3();
+ }
+
+ if ((size > 3 * QMF_WB_SECTION_SIZE) && (pWorkBuffer[3] == NULL)) {
+ /* get work buffer of size QMF_WB_SECTION_SIZE */
+ pWorkBuffer[3] = GetQmfWorkBufferCore4();
+ }
+
+ if ((size > 4 * QMF_WB_SECTION_SIZE) && (pWorkBuffer[4] == NULL)) {
+ /* get work buffer of size QMF_WB_SECTION_SIZE */
+ pWorkBuffer[4] = GetQmfWorkBufferCore5();
+ }
+
+ /* 8. distribute workbuffer over processing channels */
+ for (i = 0; i < hgc->nQmfProcChannels; i++) {
+ FDK_QmfDomain_FeedWorkBuffer(hqd, i, pWorkBuffer, size_main * i,
+ QMF_WB_SECTION_SIZE, size_main);
+ }
+ if (hgc->parkChannel) {
+ for (; i < hgc->nInputChannels; i++) {
+ FDK_QmfDomain_FeedWorkBuffer(hqd, 1, pWorkBuffer,
+ size_main * hgc->nQmfProcChannels,
+ QMF_WB_SECTION_SIZE, size_temp);
+ }
+ }
+
+ /* 9. (re-)init filterbank */
+ for (i = 0; i < hgc->nOutputChannels; i++) {
+ if ((hqd->QmfDomainOut[i].fb.lsb == 0) &&
+ (hqd->QmfDomainOut[i].fb.usb == 0)) {
+ /* Although lsb and usb are set in the SBR module, they are initialized
+ * at this point due to the case of using MPS without SBR. */
+ hqd->QmfDomainOut[i].fb.lsb = hgc->nBandsAnalysis_requested;
+ hqd->QmfDomainOut[i].fb.usb =
+ fMin((INT)hgc->nBandsSynthesis_requested, 64);
+ }
+ }
+ if (FDK_QmfDomain_InitFilterBank(hqd, 0)) {
+ err = QMF_DOMAIN_INIT_ERROR;
+ }
+ }
+
+bail:
+ if (err == QMF_DOMAIN_OUT_OF_MEMORY) {
+ FDK_QmfDomain_FreePersistentMemory(hqd);
+ FDK_QmfDomain_ClearConfigured(&hqd->globalConf);
+ }
+ return err;
+}
+
+static void FDK_QmfDomain_FreeWorkBuffer(HANDLE_FDK_QMF_DOMAIN hqd) {
+ FIXP_DBL **pWorkBuffer = hqd->globalConf.pWorkBuffer;
+
+ if (pWorkBuffer[0]) FreeQmfWorkBufferCore6(&pWorkBuffer[0]);
+ if (pWorkBuffer[1]) FreeQmfWorkBufferCore1(&pWorkBuffer[1]);
+ if (pWorkBuffer[2]) FreeQmfWorkBufferCore3(&pWorkBuffer[2]);
+ if (pWorkBuffer[3]) FreeQmfWorkBufferCore4(&pWorkBuffer[3]);
+ if (pWorkBuffer[4]) FreeQmfWorkBufferCore5(&pWorkBuffer[4]);
+}
+
+void FDK_QmfDomain_FreeMem(HANDLE_FDK_QMF_DOMAIN hqd) {
+ FDK_QmfDomain_FreeWorkBuffer(hqd);
+
+ FDK_QmfDomain_FreePersistentMemory(hqd);
+
+ FDK_QmfDomain_ClearFilterBank(hqd);
+
+ FDK_QmfDomain_ClearConfigured(&hqd->globalConf);
+
+ FDK_QmfDomain_ClearRequested(&hqd->globalConf);
+}
+
+void FDK_QmfDomain_Close(HANDLE_FDK_QMF_DOMAIN hqd) {
+ FDK_QmfDomain_FreeWorkBuffer(hqd);
+
+ FDK_QmfDomain_FreePersistentMemory(hqd);
+}